Base class PickupItem

We need to define how a pickup item looks in code. Each pickup item will derive from a common base class. Let's construct the base class for a PickupItem class now.

The PickupItem base class should inherit from the AActor class. Similar to how we created multiple NPC blueprints from the base NPC class, we can create multiple PickupItem blueprints from a single PickupItem base class, as shown in the following screenshot:

Base class PickupItem

Once you have created the PickupItem class, open its code in Visual Studio.

The APickupItem class will need quite a few members, as follows:

  • An FString variable for the name of the item being picked up
  • An int32 variable for the quantity of the item being picked up
  • A USphereComponent variable for the sphere that you will collide with for the item to be picked up
  • A UStaticMeshComponent variable to hold the actual Mesh
  • A UTexture2D variable for the icon that represents the item
  • A pointer for the HUD (which we will initialize later)

This is how the code in PickupItem.h looks:

UCLASS()
class GOLDENEGG_API APickupItem : public AActor
{
  GENERATED_UCLASS_BODY()

  // The name of the item you are getting
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Item)
  FString Name;

  // How much you are getting
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Item)
  int32 Quantity;

  // the sphere you collide with to pick item up
  UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category =  Item)
  TSubobjectPtr<USphereComponent> ProxSphere;

  // The mesh of the item
  UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category =  Item)
  TSubobjectPtr<UStaticMeshComponent> Mesh;

  // The icon that represents the object in UI/canvas
  UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Item)
  UTexture2D* Icon;

  // When something comes inside ProxSphere, this function runs
  UFUNCTION(BlueprintNativeEvent, Category = Collision)
  void Prox( AActor* OtherActor, UPrimitiveComponent* OtherComp,  int32 OtherBodyIndex, bool bFromSweep, const FHitResult &  SweepResult );
};

The point of all these UPROPERTY() declarations is to make APickupItem completely configurable by blueprints. For example, the items in the Pickup category will be displayed as follows in the blueprints editor:

Base class PickupItem

In the PickupItem.cpp file, we complete the constructor for the APickupItem class, as shown in the following code:

APickupItem::APickupItem(const class FPostConstructInitializeProperties& PCIP) : Super(PCIP)
{
  Name = "UNKNOWN ITEM";
  Quantity = 0;

  // initialize the unreal objects
  ProxSphere = PCIP.CreateDefaultSubobject<USphereComponent>(this,  TEXT("ProxSphere"));
  Mesh = PCIP.CreateDefaultSubobject<UStaticMeshComponent>(this,  TEXT("Mesh"));

  // make the root object the Mesh
  RootComponent = Mesh;
  Mesh->SetSimulatePhysics(true);

  // Code to make APickupItem::Prox() run when this
  // object's proximity sphere overlaps another actor.
  ProxSphere->OnComponentBeginOverlap.AddDynamic(this,  &APickupItem::Prox);
  ProxSphere->AttachTo( Mesh ); // very important!	
}

In the first two lines, we perform an initialization of Name and Quantity to values that should stand out to the game designer as being uninitialized. I used block capitals so that the designer can clearly see that the variable has never been initialized before.

We then initialize the ProxSphere and Mesh components using PCIP.CreateDefaultSubobject. The freshly initialized objects might have some of their default values initialized, but Mesh will start out empty. You will have to load the actual mesh later, inside blueprints.

For the mesh, we set it to simulate realistic physics so that pickup items will bounce and roll around if they are dropped or moved. Pay special attention to the line ProxSphere->AttachTo( Mesh ). This line tells you to make sure the pickup item's ProxSphere component is attached to the Mesh root component. This means that when the mesh moves in the level, ProxSphere follows. If you forget this step (or if you did it the other way around), then ProxSphere will not follow the mesh when it bounces.

The root component

In the preceding code, we assigned RootComponent of APickupItem to the Mesh object. The RootComponent member is a part of the AActor base class, so every AActor and its derivatives has a root component. The root component is basically meant to be the core of the object, and also defines how you collide with the object. The RootComponent object is defined in the Actor.h file, as shown in the following code:

/**
 * Collision primitive that defines the transform (location,  rotation, scale) of this Actor.
 */
UPROPERTY()
class USceneComponent* RootComponent;

So the UE4 creators intended RootComponent to always be a reference to the collision primitive. Sometimes the collision primitive can be capsule shaped, other times it can be spherical or even box shaped, or it can be arbitrarily shaped, as in our case, with the mesh. It's rare that a character should have a box-shaped root component, however, because the corners of the box can get caught on walls. Round shapes are usually preferred. The RootComponent property shows up in the blueprints, where you can see and manipulate it.

The root component

You can edit the ProxSphere root component from its blueprints once you create a blueprint based on the PickupItem class

Finally, the Prox_Implementation function gets implemented, as follows:

void APickupItem::Prox_Implementation( AActor* OtherActor,  UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool  bFromSweep, const FHitResult & SweepResult )
{
  // if the overlapped actor is NOT the player,
  // you simply should return
  if( Cast<AAvatar>( OtherActor ) == nullptr )
  {
    return;
  }

  // Get a reference to the player avatar, to give him
  // the item
  AAvatar *avatar = Cast<AAvatar>(  UGameplayStatics::GetPlayerPawn( GetWorld(), 0 ) );

  // Let the player pick up item
  // Notice use of keyword this!
  // That is how _this_ Pickup can refer to itself.
  avatar->Pickup( this );

  // Get a reference to the controller
  APlayerController* PController = GetWorld()- >GetFirstPlayerController();

  // Get a reference to the HUD from the controller
  AMyHUD* hud = Cast<AMyHUD>( PController->GetHUD() );
  hud->addMessage( Message( Icon, FString("Picked up ") + FString::FromInt(Quantity) + FString(" ") + Name, 5.f, FColor::White, FColor::Black ) );

  Destroy();
}

A couple of tips here that are pretty important: first, we have to access a couple of globals to get the objects we need. There are three main objects we'll be accessing through these functions that manipulate the HUD: the controller (APlayerController), the HUD (AMyHUD), and the player himself (AAvatar). There is only one of each of these three types of objects in the game instance. UE4 has made finding them easy.

Getting the avatar

The player class object can be found at any time from any place in the code by simply calling the following code:

AAvatar *avatar = Cast<AAvatar>(
  UGameplayStatics::GetPlayerPawn( GetWorld(), 0 ) );

We then pass him the item by calling the AAvatar::Pickup() function defined earlier.

Because the PlayerPawn object is really an AAvatar instance, we cast the result to the AAvatar class, using the Cast<AAvatar> command. The UGameplayStatics family of functions are accessible anywhere in your code—they are global functions.

Getting the player controller

Retrieving the player controller is from a superglobal as well:

APlayerController* PController =
  GetWorld()->GetFirstPlayerController();

The GetWorld() function is actually defined in the UObject base class. Since all UE4 objects derive from UObject, any object in the game actually has access to the world object.

Getting the HUD

Although this organization might seem strange at first, the HUD is actually attached to the player's controller. You can retrieve the HUD as follows:

AMyHUD* hud = Cast<AMyHUD>( PController->GetHUD() );

We cast the HUD object since we previously set the HUD to being an AMyHUD instance in blueprints. Since we will be using the HUD often, we can actually store a permanent pointer to the HUD inside our APickupItem class. We will discuss this point later.

Next, we implement AAvatar::Pickup, which adds an object of the type APickupItem to Avatar's backpack:

void AAvatar::Pickup( APickupItem *item )
{
  if( Backpack.Find( item->Name ) )
  {
    // the item was already in the pack.. increase qty of it
    Backpack[ item->Name ] += item->Quantity;
  }
  else
  {
    // the item wasn't in the pack before, add it in now
    Backpack.Add(item->Name, item->Quantity);
    // record ref to the tex the first time it is picked up
    Icons.Add(item->Name, item->Icon);
  }
}

In the preceding code, we check whether the pickup item that the player just got is already in his pack. If it is, we increase its quantity. If it is not in his pack, we add it to both his pack and the Icons mapping.

To add the pickup items to the pack, use the following line of code:

avatar->Pickup( this );

The APickupItem::Prox_Implementation is the way this member function will get called.

Now, we need to display the contents of our backpack in the HUD when the player presses I.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.22.181.47