Antes de começar
Certifique-se de ter concluído os seguintes objetivos nas seções anteriores: de Como programar um jogo de aventura em primeira pessoa:
Crie um personagem de jogador em primeira pessoa em C++ em Como criar um personagem jogável com ações de entrada.
Configure elementos de jogabilidade baseados em dados para gerenciar dados de itens em Gerencie itens e dados.
Criamos um item de coleta e o adicionamos ao seu nível em Crie um item de coleta que ressurge.
Criar Itens de Referência com uma Nova Função CreateItemCopy
Antes de começar a criar um novo item de coleta equipável, você precisará primeiro modificar suas classes ItemDefinition e PickupBase para oferecer suporte à captura de um item de referência de uma variedade maior de tipos de itens.
Na função InitializePickup() na sua classe PickupBase, você define um ReferenceItem do tipo UItemDefinition. Isso é muito restritivo; definir um item de referência dessa forma não incluirá as propriedades adicionais que você adicionará a quaisquer novas classes de itens especializadas derivadas de UItemDefinition.
Para resolver isso, você criará uma nova função virtual em ItemDefinition que cria e retorna uma cópia desse item. Como é uma função virtual, você pode substituí-la em qualquer classe que herde de UItemDefinition. Quando PickupBase chama a função, o compilador determina a função correta a ser chamada com base na classe a partir da qual ela foi chamada.
Adicionar esta função à classe pai ItemDefinition garante que ela esteja disponível caso você decida continuar estendendo seu projeto para incluir mais tipos de itens que herdam de UItemDefinition.
Para definir uma nova função CreateItemCopy() para criar itens de referência, siga estas etapas:
Abra
ItemDefinition.h. Na seçãopública, declare uma nova função const virtual denominadaCreateItemCopy()que retorna um ponteiroUItemDefinition.C++// Creates and returns a copy of the item. virtual UItemDefinition* CreateItemCopy() const;Em
ItemDefinition.cpp, implemente a funçãoCreateItemCopy(). Dentro dela, crie um novo ponteiro de objetoUItemDefinitiondenominadoItemCopyusandoStaticClass().C++UItemDefinition* UItemDefinition::CreateItemCopy() const { UItemDefinition* ItemCopy = NewObject<UItemDefinition>(StaticClass()); }O Visual Studio desambigua
UItemDefinition::StaticClass()paraStaticClass().Atribua cada campo de
ItemCopyaos campos desta classe e então retorneItemCopy:C++/** * Creates and returns a copy of this Item Definition. * @return a copy of the item. */ UItemDefinition* UItemDefinition::CreateItemCopy() const { UItemDefinition* ItemCopy = NewObject<UItemDefinition>(StaticClass()); ItemCopy->ID = this->ID; ItemCopy->ItemType = this->ItemType;
Em seguida, otimize sua função InitializePickup() removendo o código que configura manualmente ReferenceItem e substitua esse código por uma chamada CreateItemCopy().
Para atualizar InitializePickup() com sua nova função CreateItemCopy(), siga estas etapas:
Abra
PickupBase.cppe acesseInitializePickup().Exclua estas cinco linhas que definem e configuram
ReferenceItem:C++ReferenceItem = NewObject<UItemDefinition>(this, UItemDefinition::StaticClass()); ReferenceItem->ID = ItemDataRow->ID; ReferenceItem->ItemType = ItemDataRow->ItemType; ReferenceItem->ItemText = ItemDataRow->ItemText; ReferenceItem->WorldMesh = ItemDataRow->ItemBase->WorldMesh;Em
ReferenceItemchamandoTempItemDefinition->CreateItemCopy():C++// Create a copy of the item with the class type ReferenceItem = TempItemDefinition->CreateItemCopy();
Salve PickupBase.cpp. Sua função InitializePickup() agora deve ficar assim:
if (PickupDataTable && !PickupItemID.IsNone())
{
// Retrieve the item data associated with this pickup from the data table
const FItemData* ItemDataRow = PickupDataTable->FindRow<FItemData>(PickupItemID, PickupItemID.ToString());
UItemDefinition* TempItemDefinition = ItemDataRow->ItemBase.Get();
// Create a copy of the item with the class type
ReferenceItem = TempItemDefinition->CreateItemCopy();
}Definir Dados de Ferramentas Equipáveis
Na seção anterior, você aprendeu como criar objetos de coleta interativos em seu nível que são representações concretas de dados da tabela. Nesta seção, você aprenderá a construir ferramentas para equipar seu personagem.
Para definir uma nova ferramenta equipável, você criará:
EquippableToolDefinition: uma classe de ativo de dados derivada deItemDefinitionque armazena os dados da ferramenta.EquippableToolBase: uma classe Ator para representar a ferramenta no jogo. Ela fornece ao seu personagem as animações, os mapeamentos de entrada e a malha para que ele possa segurar e operar a ferramenta.
Para que seu personagem possa pegar e equipar ferramentas, você adicionará:
Um lugar para armazenar itens.
Uma maneira de saber o tipo de cada item em seu inventário.
Uma maneira de equipar ferramentas.
Lembre-se, o ator EquippableToolBase representa a ferramenta que seu personagem está segurando e usando, enquanto o ator PickupBase representa o item de coleta em seu nível. Seu personagem precisa colidir com o item coletado antes de poder equipá-lo, então você também modificará PickupBase para conceder o item ao personagem após uma colisão bem-sucedida.
Em seguida, você combinará sua nova classe de ferramenta com as coletas e a Tabela de Dados que você já construiu para criar um lançador de dardos personalizado e anexá-lo ao seu personagem!
Primeiro, você definirá os dados da sua ferramenta em uma nova classe ItemDefinition.
Para criar uma nova classe EquippableToolDefinition, siga estas etapas:
No Unreal Editor, acesse Ferramentas > Nova classe C++. Acesse Todas as classes, procure e selecione ItemDefinition como a classe pai e clique em Avançar.
Nomeie a classe
EquippableToolDefinitione clique em Criar classe.No Visual Studio, no topo de
EquippableToolDefinition.h, adicione um include para"ItemDefinition.h", então adicione as seguintes declarações de encaminhamento:classe UInputMappingContext: cada ferramenta equipável deve conter uma referência a um Contexto de Mapeamento de Entrada que você aplicará ao personagem que empunha essa ferramenta.classe AEquippableToolBase: o ator que representa suas ferramentas no jogo. Você criará isso na próxima etapa.C++#pragma once #include "CoreMinimal.h" #include "ItemDefinition.h" #include "EquippableToolDefinition.generated.h" class AEquippableToolBase; class UInputMappingContext; UCLASS(BlueprintType, Blueprintable)
Na seção
pública, adicione uma propriedadeTSubclassOfdo tipoAEquippableToolBasedenominadaToolAsset. Dê a isso uma macroUPROPERTY()comEditDefaultsOnly.C++// The tool actor associated with this item UPROPERTY(EditDefaultsOnly) TSubclassOf<AEquippableToolBase> ToolAsset;TSubclassOf<AEquippableToolBase>é um agrupador de modelo em torno deUClassque permite que você referenciar subclasses do Blueprint deAEquippableToolBase, garantindo ao mesmo tempo a segurança do tipo. É útil em cenários de jogabilidade onde você deseja gerar diferentes tipos de atores dinamicamente.Você usará o
ToolAssetpara gerar dinamicamente um ator de ferramenta quando ele for equipado no seu personagem.Declare uma substituição para a função
CreateItemCopy()que você declarou emUItemDefinition. Esta substituição cria e retorna uma cópia da classeUEquippableToolDefinition.Seu arquivo
EquippableToolDefinition.hcompleto deve ficar aqui:C++// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "ItemDefinition.h" #include "EquippableToolDefinition.generated.h" class AEquippableToolBase; class UInputMappingContext;Em
EquippableToolDefinition.cpp, implemente a funçãoCreateItemCopy(). Deve ser semelhante à funçãoCreateItemCopy()emItemDefinition.cpp, com a diferença de que, agora, você também copiaráToolAsset.C++// Copyright Epic Games, Inc. All Rights Reserved. #include "EquippableToolDefinition.h" UEquippableToolDefinition* UEquippableToolDefinition::CreateItemCopy() const { UEquippableToolDefinition* ItemCopy = NewObject<UEquippableToolDefinition>(StaticClass()); ItemCopy->ID = this->ID; ItemCopy->ItemType = this->ItemType;
Salve os dois arquivos de classe EquippableToolDefinition.
Configurar um Ator de Ferramenta Equipável
Em seguida, comece a construir seu ator ferramenta equipável. Esta é a representação no jogo que adiciona as animações, os controles e a malha da ferramenta ao personagem.
Para criar e configurar um novo ator base de ferramenta equipável, siga estas etapas:
No Unreal Editor, acesse Ferramentas > Nova classe C++. Selecione Ator como a classe pai e nomeie a classe EquippableToolBase.
Clique em Criar classe. O Unreal Engine abre automaticamente os arquivos da sua nova classe no VS.
No topo de
EquippableToolBase.h, declare adiante a classeAAdventureCharactere a classeUInputAction. A ferramenta equipável precisa saber a qual Personagem ela está equipada para poder vincular Ações de Entrada específicas da ferramenta a esse Personagem.Na macro
UCLASSda declaração de classe, adicione os especificadoresBlueprintTypeeBlueprintablepara expor esta classe aos Blueprints.C++UCLASS(BlueprintType, Blueprintable) class ADVENTUREGAME_API AEquippableToolBase : public AActor
Declarar Animações de Ferramentas
Em EquippableToolBase.h, na seção pública, adicione duas propriedades TObjectPtr às propriedades UAnimBlueprint denominadas FirstPersonToolAnim e ThirdPersonToolAnim. Estas são as animações em primeira e terceira pessoa que o personagem usa quando equipado com esta ferramenta.
Dê a essas propriedades uma macro UPROPERTY() com EditAnywhere e BlueprintReadOnly.
// First Person animations
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TObjectPtr<UAnimBlueprint> FirstPersonToolAnim;
// Third Person animations
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TObjectPtr<UAnimBlueprint> ThirdPersonToolAnim;Criar a Malha da Ferramenta
Em EquippableToolBase.h, na seção pública, adicione um TObjectPtr a um USkeletalMeshComponent denominado ToolMeshComponent. Esta é a malha esquelética da ferramenta que o personagem vê quando equipada. Dê a ele uma macro UPROPERTY() com EditAnywhere e BlueprintReadOnly.
// Tool Skeletal Mesh
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TObjectPtr<USkeletalMeshComponent> ToolMeshComponent;Em EquippableToolBase.cpp, modifique a função de construtor AEquippableToolBase() para criar um USkeletalMeshComponent padrão e atribuí-lo a ToolMeshComponent. Em seguida, verifique se o ToolMeshComponent não é nulo para garantir que sua ferramenta tenha um modelo quando for carregada.
AEquippableToolBase::AEquippableToolBase()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// Create this tool's mesh component
ToolMeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("ToolMesh"));
check(ToolMeshComponent != nullptr);
}
Como Declarar o Proprietários da Ferramenta
Em EquippableToolBase.h, na seção pública, crie um TObjectPtr para uma instância da sua classe Personagem denominada OwningCharacter. Dê a ele uma macro UPROPERTY() com EditAnywhere e BlueprintReadOnly.
Este é o personagem no qual esta ferramenta está equipada atualmente.
// The character holding this tool
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TObjectPtr<AAdventureCharacter> OwningCharacter;Declare a entrada e uma função de uso de ferramenta
Sua ferramenta vem com um Contexto de Mapeamento de Entrada e uma Ação de Entrada que ela precisa fornecer ao personagem.
Para adicionar o Contexto de mapeamento de entrada, na seção pública, declare TObjectPtr para um UInputMappingContext denominado ToolMappingContext. Dê a ele uma macro UPROPERTY() com EditAnywhere e BlueprintReadOnly.
// The input mapping context associated with this tool
UPROPERTY(EditAnywhere, BlueprintReadOnly)
TObjectPtr<UInputMappingContext> ToolMappingContext;Semelhante à quando você implementou controles de movimento, você adicionará uma função que implementa uma ação de uso de ferramenta e uma nova função que vincula uma ação de entrada à função.
Em EquippableToolBase.h, na seção pública, declare duas funções void virtuais denominadas Use() e BindInputAction().
Ao implementar os controles de movimento dos personagens, você usou a função BindAction() do InputComponent, que exige que você passe o nome exato da função alvo. Você ainda não sabe o nome completo da função, então precisa de uma função BindInputAction() personalizada que pode implementar em cada subclasse EquippableToolBase para chamar BindAction, transmitindo [ToolChildClass]::Use.
A função BindInputAction() recebe um ponteiro UInputAction constante e vincula a ação de entrada fornecida à função Use() do personagem.
// Use the tool
UFUNCTION()
virtual void Use();
// Binds the Use function to the owning character
UFUNCTION()
virtual void BindInputAction(const UInputAction* ActionToBind);Em EquippableToolBase.cpp, implemente as funções Use() e BindInputAction(). Elas não farão nada na classe pai, então você pode deixá-las em branco por enquanto. Você adicionará lógica a elas ao criar subclasses EquippableToolBase; por exemplo, a função Use() deve incluir ações específicas da ferramenta, como lançar um projétil ou abrir uma porta.
void AEquippableToolBase::Use()
{
}
void AEquippableToolBase::BindInputAction(const UInputAction* ActionToBind)
{
}
Salve seu código e compile-o a partir do VS.
Seu arquivo EquippableToolBase.h agora deve ficar assim:
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "EquippableToolBase.generated.h"
class AAdventureCharacter;
class UInputAction;
EquippableToolBase.cpp agora deve ter a seguinte aparência:
// Copyright Epic Games, Inc. All Rights Reserved.
#include "EquippableToolBase.h"
#include "AdventureCharacter.h"
AEquippableToolBase::AEquippableToolBase()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
Conceder Itens a um Personagem
Você definiu ferramentas que seu personagem pode usar, mas ele ainda não pode equipá-las. Em seguida, você adicionará um sistema de inventário para que o personagem possa armazenar e equipar itens ao pegá-los.
Criar um Componente de Inventário
O inventário do seu personagem deve adicionar funcionalidade ao personagem, mas não existir no mundo do jogo, então você usará uma classe Componente de ator para definir um inventário que sabe quais itens um personagem tem, pode trocar ferramentas e pode impedir que o personagem obtenha mais de uma ferramenta igual.
No Unreal Editor, acesse Ferramentas > Nova classe C++. Selecione Componente de ator como a classe pai e nomeie a classe InventoryComponent.
Clique em Criar classe.
No VS, no topo de InventoryComponent.h, declare adiante uma UEquippableToolDefinition. Esta é a classe que você armazenará em seu inventário.
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "InventoryComponent.generated.h"
class UEquippableToolDefinition;
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class ADVENTUREGAME_API UInventoryComponent : public UActorComponent
{
GENERATED_BODY()
Na seção pública, declare um novo TArray de ponteiros UEquippableToolDefinition denominado ToolInventory. Dê a isso a macro UPROPERTY() com VisibleAnywhere, BlueprintReadOnly e Category = Tools.
public:
// Sets default values for this component's properties
UInventoryComponent();
// The array of tools stored in this inventory.
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Tools)
TArray<UEquippableToolDefinition*> ToolInventory;Este inventário armazena apenas ferramentas, mas você pode expandi-lo para incluir qualquer tipo de item que desejar. Uma implementação mais genérica armazenaria apenas valores UItemDefinition ou TSubclassOf<UItemDefinition> para criar um inventário mais complexo com interface do usuário, ícones, efeitos sonoros, custo e outras propriedades do item.
Seu arquivo InventoryComponent.h completo agora deve ficar assim:
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "InventoryComponent.generated.h"
class UEquippableToolDefinition;
Adicionar Declarações de Ferramentas e Inventário ao Personagem
Agora que você tem um lugar para armazenar seus itens, está pronto para atualizar seu personagem com uma lógica que lhe concede itens.
Para começar, no topo do arquivo .h do seu personagem, declare adiante as classes AEquippableToolBase, UEquippableToolDefinition e UInventoryComponent.
#include "CoreMinimal.h"
#include "Camera/CameraComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/Character.h"
#include "InputActionValue.h"
#include "AdventureCharacter.generated.h"
class AEquippableToolBase;
class UAnimBlueprint;
Na seção protegida, declare um TObjectPtr para uma UInputAction denominada UseAction. Esta é a ação de entrada "usar ferramenta" que você vinculará à função Use() da ferramenta.
// Use Input Actions
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input)
TObjectPtr<UInputAction> UseAction;Criar o Componente de Inventário do Personagem
No arquivo .h do personagem, na seção pública, declare um TObjectPtr como um UInventoryComponent denominado InventoryComponent. Dê a ele uma macro UPROPERTY() com VisibleAnywhere e Category = Inventory.
// Inventory Component
UPROPERTY(VisibleAnywhere, Category = Inventory)
TObjectPtr<UInventoryComponent> InventoryComponent;Na função de construtor do personagem, depois de criar o subobjeto do Componente de malha, crie um subobjeto padrão UInventoryComponent denominado InventoryComponent. Isso garante que seu inventário esteja configurado corretamente quando o personagem aparecer.
// Create an inventory component for the owning player
InventoryComponent = CreateDefaultSubobject<UInventoryComponent>(TEXT("InventoryComponent"));Verificar Inventário Existente
Antes de anexar a ferramenta, verifique se o jogador já a possui para não equipá-la várias vezes.
No arquivo .h do personagem, na seção pública, declare uma função denominada IsToolAlreadyOwned() que recebe um ponteiro UEquippableToolDefinition e retorna verdadeiro se a ferramenta já existir no inventário do jogador.
// Returns whether or not the player already owns this tool
UFUNCTION()
bool IsToolAlreadyOwned(UEquippableToolDefinition* ToolDefinition);No arquivo .cpp do personagem, AdventureCharacter.cpp, implemente a função IsToolAlreadyOwned(). Lá dentro, em um loop for, obtenha cada ferramenta no inventário do personagem acessando a matriz InventoryComponent->ToolInventory.
bool AAdventureCharacter::IsToolAlreadyOwned(UEquippableToolDefinition* ToolDefinition)
{
// Check that the character does not yet have this particular tool
for (UEquippableToolDefinition* InventoryItem : InventoryComponent->ToolInventory)
{
}
}
Em seguida, em uma instrução if, verifique se o ToolDefinition->ID da ferramenta transmitida para esta função corresponde ao InventoryItem->ID. Se sim, retorne verdadeiro, pois o personagem já possui esta ferramenta. Caso contrário, após o loop for, retorne falso porque ToolDefinition não correspondeu a nenhum item de inventário existente e, portanto, é um novo item.
bool AAdventureCharacter::IsToolAlreadyOwned(UEquippableToolDefinition* ToolDefinition)
{
// Check that the character does not yet have this particular tool
for (UEquippableToolDefinition* InventoryItem : InventoryComponent->ToolInventory)
{
if (ToolDefinition->ID == InventoryItem->ID)
{
return true;
}
}
Anexar uma Ferramenta
No arquivo .h do seu personagem, na seção pública, declare uma função denominada AttachTool() que recebe um ponteiro UEquippableToolDefinition. Esta função tenta equipar a ferramenta dentro da ToolDefinition para o jogador.
// Attaches and equips a tool to the player
UFUNCTION()
void AttachTool(UEquippableToolDefinition* ToolDefinition);Na seção protegida, declare um TObjectPtr para uma AEquippableToolBase denominada EquippedTool. Dê a ele os especificadores VisibleAnywhere, BlueprintReadOnly e Category = Tools UPROPERTY().
// The currently-equipped tool
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Tools)
TObjectPtr<AEquippableToolBase> EquippedTool;No arquivo .cpp do personagem, implemente AttachTool(). Primeiro, em uma instrução if, verifique se o personagem já tem a ferramenta chamada IsToolAlreadyOwned().
void AAdventureCharacter::AttachTool(UEquippableToolDefinition* ToolDefinition)
{
// Only equip this tool if it isn't already owned
if (not IsToolAlreadyOwned(ToolDefinition))
{
}
}
Gerar um Item
A ferramenta AEquippableToolBase armazenada dentro de ToolDefinition é um Ator, portanto, ela pode não ser carregada quando AttachTool() é chamada. Para lidar com isso, você vai gerar uma nova instância da ferramenta usando a função SpawnActor().
SpawnActor() faz parte do objeto UWorld, que é o objeto principal que representa o mapa e os atores nele. Acesse-o chamando a função GetWorld() em qualquer Ator.
Na instrução if, declare um novo ponteiro AEquippableToolBase denominado ToolToEquip. Defina isso como igual ao resultado da chamada de GetWorld()->SpawnActor(), transmitindo ToolDefinition->ToolAsset como o ator a ser gerado e this->GetActorTransform() como o local para gerá-lo.
// Only equip this tool if it isn't already owned
if (not IsToolAlreadyOwned(ToolDefinition))
{
// Spawn a new instance of the tool to equip
AEquippableToolBase* ToolToEquip = GetWorld()->SpawnActor<AEquippableToolBase>(ToolDefinition->ToolAsset, this->GetActorTransform());
}Quando você passa ToolDefinition->ToolAsset para SpawnActor, o UE sabe que deve olhar o tipo de classe do ToolAsset e gerar esse tipo de Ator. (ToolAsset é o ator EquippableToolBase associado a essa ToolDefinition.)
Anexar um Item ao Personagem
Para anexar a ferramenta gerada ao seu personagem, declare uma nova FAttachementTransformRules denominada AttachementRules.
FAttachementTransformRules é uma struct que define como lidar com localização, rotação e escala ao anexar. São necessários EAttachmentRules e um bool InWeldSimulatedBodies no final para informar ao UE se a física está envolvida. Quando verdadeiro, o UE solda ambos os corpos para que eles possam interagir como um só quando se movimentam. Algumas regras de anexação populares incluem KeepRelative (manter transformação relativa ao pai), KeepWorld (manter transformação do mundo) e SnapToTarget (encaixar na transformação do pai).
Na sua definição de AttachmentRoles, adicione EAttachmentRule::SnapToTarget e verdadeiro.
// Attach the tool to the First Person Character
FAttachmentTransformRules AttachmentRules(EAttachmentRule::SnapToTarget, true);Em seguida, chame ToolToEquip->AttachToActor() para anexar a ferramenta ao personagem, seguido por ToolToEquip->AttachToComponent() para anexar a ferramenta ao soquete do lado direito do componente de malha em primeira pessoa.
AttachToActor anexa um Ator a um Ator pai alvo, e AttachToComponent anexa o componente raiz de um Ator ao componente alvo. Eles têm a seguinte sintaxe:
MyActor->AttachToActor(ParentActor, AttachmentRules, OptionalSocketName)
// Attach the tool to this character, and then the right hand of their first-person mesh
ToolToEquip->AttachToActor(this, AttachmentRules);
ToolToEquip->AttachToComponent(FirstPersonMeshComponent, AttachmentRules, FName(TEXT("HandGrip_R")));Adicionar as Animações do Item ao Personagem
Defina as animações nas malhas de primeira e terceira pessoa usando SetAnimInstanceClass(), transmitindo as animações de primeira e terceira pessoa da ferramenta.
// Set the animations on the character's meshes.
FirstPersonMeshComponent->SetAnimInstanceClass(ToolToEquip->FirstPersonToolAnim->GeneratedClass);
GetMesh()->SetAnimInstanceClass(ToolToEquip->ThirdPersonToolAnim->GeneratedClass);SetAnimInstanceClass altera dinamicamente o Blueprint de animação em tempo de execução para uma malha esquelética e é comumente usado ao equipar itens e armas com diferentes conjuntos de animações. GeneratedClass obtém a classe AnimInstance real gerada a partir do Blueprint.
Adicionar o Item ao Inventário
Adicione a ferramenta ao inventário do personagem usando ToolInventory.Add()
// Add the tool to this character's inventory
InventoryComponent->ToolInventory.Add(ToolDefinition);
Agora que a ferramenta está anexada, defina ToolToEquip->OwningCharacter para este personagem.
ToolToEquip->OwningCharacter = this;Você terminou de anexar a nova ferramenta ao personagem, então defina EquippedTool como ToolToEquip.
EquippedTool = ToolToEquip;Adicionar os Controles de um Item ao Personagem
Em seguida, adicione a Ação de entrada e o Contexto de Mapeamento de Entrada da ferramenta ao personagem.
Você implementará isso de forma semelhante a como configura AAdventureCharacter::BeginPlay() na seção Vincular o mapeamento de entrada ao personagem de Como configurar o movimento do personagem: obtendo o PlayerController e, em seguida, o subsistema local de entrada aprimorado, usando instruções if para verificar ponteiros nulos conforme você avança.
// Get the player controller for this character
if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->AddMappingContext(ToolToEquip->ToolMappingContext, 1);
}
}
Desta vez, ao adicionar o Contexto de Mapeamento de Entrada da ferramenta ao subsistema do jogador, defina a prioridade como 1. A prioridade do contexto de mapeamento principal do jogador (FirstPersonContext) é menor (0), então quando ambos os contextos de mapeamento têm a mesma vinculação chave, a vinculação de entrada em ToolToEquip->ToolMappingContext tem prioridade sobre FirstPersonContext.
Após adicionar o contexto de mapeamento, chame ToolToEquip->BindInputAction() transmitindo o UseAction para vincular a ação de entrada do personagem à função Use() da ferramenta.
// Get the player controller for this character
if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
{
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
{
Subsystem->AddMappingContext(ToolToEquip->ToolMappingContext, 1);
}
ToolToEquip->BindInputAction(UseAction);
}
Sua função AttachTool() completa deve ser semelhante à seguinte:
void AAdventureCharacter::AttachTool(UEquippableToolDefinition* ToolDefinition)
{
// Only equip this tool if it isn't already owned
if (not IsToolAlreadyOwned(ToolDefinition))
{
// Spawn a new instance of the tool to equip
AEquippableToolBase* ToolToEquip = GetWorld()->SpawnActor<AEquippableToolBase>(ToolDefinition->ToolAsset, this->GetActorTransform());
// Attach the tool to the First Person Character
Suporte a Diferentes Tipos de Itens com GiveItem()
Você tem uma maneira de anexar ferramentas, mas como os itens de coleta e suas definições podem conter mais do que apenas ferramentas, é necessária uma maneira de saber com que tipo de item seu personagem está interagindo antes de chamar AttachTool().
Crie uma função GiveItem() para executar diferentes ações com base no tipo de ItemDefinition transmitido. Você declarou diferentes tipos de itens com a enum EItemType em ItemData.h e você pode usar esses dados agora para diferenciar entre diferentes definições de itens.
Em AdventureCharacter.h, na seção pública, declare uma função denominada GiveItem() que recebe um ponteiro UItemDefinition(). Outras classes chamam essa função quando tentam dar um item ao jogador.
// Public function that other classes can call to attempt to give an item to the player
UFUNCTION()
void GiveItem(UItemDefinition* ItemDefinition);Em AdventureCharacter.cpp, implemente GiveItem(). Comece declarando uma instrução switch que define casos com base no ItemType do item transmitido para esta função.
void AAdventureCharacter::GiveItem(UItemDefinition* ItemDefinition)
{
// Case based on the type of the item
switch (ItemDefinition->ItemType)
{
}
}
Dentro da instrução switch, declare casos para EItemType::Tool, EItemType::Consumable e um caso padrão. Neste tutorial, você está implementando apenas o item do tipo Ferramenta, portanto, nos casos Consumível e padrão, registre o tipo de item e saia do caso de troca.
// Case based on the type of the item
switch (ItemDefinition->ItemType)
{
case EItemType::Tool:
{
}
case EItemType::Consumable:
{
// Not yet implemented
break;
Dentro do caso Ferramenta, converter ItemDefinition para um ponteiro UEquippableToolDefinition denominado ToolDefinition.
Em seguida, certifique-se de que a conversão seja bem-sucedida verificando se ToolDefinition é nulo. Se não for nulo, chame AttachTool() para anexar a ferramenta ao personagem. Caso contrário, imprima o erro e interrompa-o.
case EItemType::Tool:
{
// If the item is a tool, attempt to cast and attach it to the character
UEquippableToolDefinition* ToolDefinition = Cast<UEquippableToolDefinition>(ItemDefinition);
if (ToolDefinition != nullptr)
{
AttachTool(ToolDefinition);
}
Sua função GiveItem() completa deve ser semelhante ao seguinte:
void AAdventureCharacter::GiveItem(UItemDefinition* ItemDefinition)
{
// Case based on the type of the item
switch (ItemDefinition->ItemType)
{
case EItemType::Tool:
{
// If the item is a tool, attempt to cast and attach it to the character
Por fim, você precisa de um gatilho no jogo para definir sua lógica de concessão de itens em ação. Quando um personagem colide com uma coleta, a coleta deve chamar a função GiveItem() do personagem para conceder o ReferenceItem da coleta ao personagem.
Para fazer isso, abra PickupBase.cpp.
Em OnSphereBeginOverlap(), depois de verificar se o personagem é válido, chame Character->GiveItem(ReferenceItem) para conceder o item ao seu personagem.
// Checking if it is a First Person Character overlapping
AAdventureCharacter* Character = Cast<AAdventureCharacter>(OtherActor);
if (Character != nullptr)
{
// Give the item to the character
Character->GiveItem(ReferenceItem);
// Unregister from the Overlap Event so it is no longer triggered
SphereComponent->OnComponentBeginOverlap.RemoveAll(this);
Agora que você configurou os dados da ferramenta e o ator, é possível usá-los para construir uma ferramenta real para seu personagem se equipar no jogo!
Implementar um Lançador de Dardos
Como sua primeira ferramenta equipável, você criará um lançador de dardos que pode lançar projéteis. Nesta seção, você começará criando a ferramenta que seu personagem vai segurar e usar. Na próxima seção deste tutorial, você implementará a lógica de projéteis.
Configurar uma nova Classe DartLauncher
No Unreal Editor, acesse Ferramentas > Nova classe C++.
Acesse Todas as Classes, procure e selecione EquippableToolBase como a classe pai e nomeie a classe DartLauncher. Crie uma nova pasta denominada Ferramentas para armazenar o código das suas ferramentas. Clique em Criar classe.
No Visual Studio, no topo de DartLauncher.h:
Adicione uma instrução include para
"[ProjectName]/EquippableToolBase.h".Adicione os especificadores
BlueprintTypeeBlueprintableà macroUCLASS().Na seção
pública, declare substituições para as funçõesUse()eBindInputAction()deAEquippableToolBase.
Sua classe DartLauncher.h completa deve ser semelhante ao seguinte:
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "AdventureGame/EquippableToolBase.h"
#include "DartLauncher.generated.h"
UCLASS(BlueprintType, Blueprintable)
class ADVENTUREGAME_API ADartLauncher : public AEquippableToolBase
Em DartLauncher.cpp, no topo do arquivo, adicione uma instrução include para "[ProjectName]/AdventureCharacter.h". Você precisará disso na função BindInputAction().
#include "DartLauncher.h"
#include "AdventureGame/AdventureCharacter.h"Implementar Controles de Ferramentas
Agora que você está trabalhando em uma ferramenta específica e sabe qual função está vinculando, pode implementar BindInputAction().
Primeiro, implemente a função Use(). Dentro de Use(), adicione uma mensagem de depuração que notifica quando o jogador aciona a função.
#include "DartLauncher.h"
#include "AdventureGame/AdventureCharacter.h"
void ADartLauncher::Use()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("Using the dart launcher!"));
}
Em seguida, implemente a função BindInputAction(). Dentro da função, em uma instrução if, obtenha o controle do jogador para OwningCharacter usando GetController() e, em seguida, converta-o para APlayerController. Isso é semelhante a como você adicionou um contexto de mapeamento na função AAdventureCharacter::BeginPlay().
void ADartLauncher::BindInputAction(const UInputAction* InputToBind)
{
// Set up action bindings
if (APlayerController* PlayerController = Cast<APlayerController>(OwningCharacter->GetController()))
{
}
}
Assim como você vinculou suas ações de movimento na seção Como Vincular Ações de Movimento em Como configurar o movimento do personagem: em outra instrução if, declare um novo ponteiro UEnhancedInputComponent denominado EnhancedInputComponent. Defina isso como igual ao resultado da chamada de Cast() no PlayerInputComponent transmitido para esta função durante a conversão para UEnhancedInputComponent.
Por fim, use BindAction para vincular a ação ADartLauncher::Use à ação InputToBind que é transmitida para esta função usando BindAction(). Isso vincula o InputAction a Use(); para que, quando a ação fornecida acontecer, Use() seja chamado.
// Set up action bindings
if (APlayerController* PlayerController = Cast<APlayerController>(OwningCharacter->GetController()))
{
if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerController->InputComponent))
{
// Fire
EnhancedInputComponent->BindAction(InputToBind, ETriggerEvent::Triggered, this, &ADartLauncher::Use);
}
}
Ao configurar o movimento do seu personagem, você usou CastChecked<>, que trava o jogo se falhar. Aqui, você não quer parar o jogo se os controles de coleta não forem inicializados corretamente, então use Cast<>. Use CastChecked<> somente quando uma conversão com falha indicar um bug sério.
Salve o código e compile-o.
Sua função BindInputAction() e sua classe DartLauncher.cpp completa agora devem ficar assim:
// Copyright Epic Games, Inc. All Rights Reserved.
#include "DartLauncher.h"
#include "AdventureGame/AdventureCharacter.h"
void ADartLauncher::Use()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("Using the dart launcher!"));
}
Adaptar um Blueprint de Animação para seu Personagem
O modelo em primeira pessoa inclui uma amostra de Blueprint de animação para itens do tipo arma, mas você precisará fazer algumas alterações no modelo para que ele funcione com seu lançador de dardos.
Para adaptar um Blueprint de animação existente para seu personagem, siga os seguintes passos:
No Navegador de Conteúdo, acesse a pasta Conteúdo > Variant_Shooter > Animação, clique com o botão direito do mouse no Blueprint de animação
ABP_FP_Pistole selecione Duplicar.Nomeie esta cópia como ABP_FP_DartLauncher, arraste-a para a pasta Conteúdo > FirstPerson > Animações e selecione Mover para cá.
ABP_TP_Pistolnão usa nenhuma lógica específica para o personagemBP_FPShooter; você não precisa modificá-la para seu personagem.Próximo ao topo de Event Graph, amplie o grupo de nós que começa com um nó Início da Reprodução do Blueprint de Eventos.
Este Blueprint obtém as variáveis Malha de primeira pessoa e Câmera de primeira pessoa em
BP_FPShooter, então você vai alterá-lo para usar o Blueprint do seu personagem (este tutorial usaBP_AdventureCharacter).Clique em cada um desses nós e pressione Excluir:
Converter para BP_FPShooter
Malha de primeira pessoa
Câmera em primeira pessoa
Clique com o botão direito do mouse no Event Graph e, em seguida, procure e selecione um nó Converter para BP_AdventureCharacter.
Conecte os pins de execução de Início da Reprodução do Blueprint de Eventos ao novo nó e, depois, ao nó Definir malha de primeira pessoa.
Conecte o pin valor de retorno do nó Tentar obter proprietário de pawn ao pin Objeto do nó Converter para.
Para criar um novo nó a partir de Converter para BP_AdventureCharacter, clique no pin Como BP meu personagem de aventura e arraste para um local vazio no gráfico.
Pesquise, obtenha a malha e selecione-o em Variáveis > Personagem. Conecte o pin Malha do novo nó ao nó Definir malha de primeira pessoa.
Para a câmera, arraste outro nó do pin Como BP meu personagem de primeira pessoa e pesquise e selecione Obter componente por classe.
Certifique-se de criar um nó Obter componente por classe com "por" em letras minúsculas.
No novo nó, defina a Classe do componente como Componente de câmera. Em seguida, conecte o pin valor de retorno ao nó Definir câmera em primeira pessoa.
Salve seu Blueprint ABP_FP_DartLauncher e compile-o.
Definir Controles do Lançador de Dardos
Seu lançador de dardos precisa de uma ação de entrada e um contexto de mapeamento de entrada para que o personagem possa atirar projéteis a partir da ferramenta.
Para criar controles de jogador para sua ferramenta lançador de dardos, siga estas etapas:
No Navegador de Conteúdo, acesse a pasta Entrada > Ações.
Crie e configure uma ação de entrada "usar ferramenta":
Clique em Adicionar, acesse Entrada e selecione Ação de entrada. Nomeie-a como IA_UseTool.
Clique duas vezes em IA_UseTool para abri-la. No painel Detalhes, certifique-se de que o Tipo de valor seja Digital (bool).
Ao lado de Gatilhos, clique no botão de adição (+) e selecione Pressionado na lista de gatilhos.
Salve e feche a ação de entrada.
De volta ao Navegador de Conteúdo, acesse a pasta Entrada.
Crie e configure um novo Contexto de Mapeamento de Entrada que mapeie o botão esquerdo do mouse e o gatilho direito do gamepad para a ação Usar do lançador de dardos:
Crie um novo Contexto de Mapeamento de Entrada denominado IMC_DartLauncher.
Abra o IMC_DartLauncher. Clique no botão de adição ao lado de Mapeamentos.
Na lista suspensa, selecione
IA_UseTool.Clique na seta para expandir o mapeamento. Clique no botão do teclado e pressione o botão esquerdo do mouse para vincular esse botão a
IA_UseTool.Ao lado de IA_UseTool, clique no botão de adição para adicionar outra vinculação. Na lista suspensa, expanda Gamepad e selecione Eixo do gatilho direito do controle.
Salve e feche o Contexto de Mapeamento de Entrada.
Criar o Blueprint do DartLauncher
De volta ao editor principal, na árvore de ativos do Navegador de Conteúdo, acesse a pasta Classes C++, clique com o botão direito do mouse na classe DartLauncher e crie uma nova classe de Blueprint.
Nomeie-a como BP_DartLauncher. Na pasta FirstPerson > Blueprints, crie uma nova pasta denominada Ferramentas para armazenar seus itens equipáveis e, em seguida, termine de criar o Blueprint.
No painel Detalhes do Blueprint, defina as seguintes propriedades padrão:
Defina a animação de ferramenta de primeira pessoa como
ABP_FP_DartLauncher.Defina a animação de ferramenta de terceira pessoa como
ABP_TP_Pistol.Defina o contexto de mapeamento de ferramentas como
IMC_DartLauncher.
Na aba Componentes, selecione o Componente de malha de ferramenta e defina o Ativo de malha do esqueleto como SKM_Pistol.
Criar o Ativo de Dados do Lançador de Dardos
Para criar um Ativo de Dados para armazenar os dados deste Blueprint, siga estas etapas:
No Navegador de Conteúdo, na pasta FirstPerson > Dados, crie um novo Ativo de Dados e escolha Definição de Ferramenta Equipável como a instância do Ativo de Dados. Nomeie este ativo
DA_DartLauncher.Dentro do
DA_DartLauncher, no painel Detalhes, defina as seguintes propriedades:Defina Ativo de Ferramenta como
BP_DartLauncher.Defina o ID como tool_001.
Defina o Tipo de item como Ferramenta.
Defina Malha do mundo como
SM_Pistol.
Insira um Nome e uma Descrição.
Salve seu ativo de dados.
Criar uma Tabela de Dados para Ferramentas
Embora essa ferramenta possa ser colocada na sua tabela DT_PickupData, é útil organizar suas tabelas para rastrear coisas específicas. Por exemplo, você pode ter tabelas diferentes para itens que classes específicas podem equipar, ou tabelas de itens que diferentes inimigos deixam cair quando derrotados. Neste tutorial, você terá uma tabela para consumíveis e uma tabela para ferramentas.
Para criar uma nova Tabela de Dados para rastrear seus itens de ferramentas, siga estas etapas:
No Navegador de Conteúdo, acesse a pasta FirstPerson > Dados e crie uma nova Tabela de Dados.
Selecione ItemData como a estrutura da linha.
Nomeie a tabela DT_ToolData e clique duas vezes nela para abri-la.
Dentro de DT_ToolData, clique em Adicionar para criar uma nova linha para seu lançador de dardos.
Com a nova linha selecionada, defina os seguintes campos:
Defina o Nome da linha e o ID como tool_001.
Defina o Tipo de item como Ferramenta.
Defina o Item-base como
DA_DartLauncher.
Salve e feche a Tabela de Dados.
Testar uma Coleta do Lançador de Dardos no Jogo
Você modificou sua classe de coleta para conceder um item ao usuário, criou uma classe de item equipável que dá ao jogador uma nova malha, animações e controles, e configurou uma ferramenta de lançamento de dardos. É hora de juntar tudo e criar o item de coleta no jogo que aciona toda a lógica de itens equipáveis que você configurou nesta parte do tutorial.
No Navegador de Conteúdo, acesse Conteúdo > FirstPerson > Blueprints e arraste um novo BP_PickupBase para o nível. Defina o ID do item de coleta como tool_001 e a Tabela de ados de coleta como DT_ToolData.
Clique em Jogar para testar seu jogo. Quando o jogo começar, sua coleta deverá ser inicializada no lançador de dardos. Quando você passar por cima da sua coleta, seu personagem deve começar a segurar a ferramenta!
Próxima
Na seção final, você implementará a física de projéteis no seu lançador de dardos e fará com que ele lance dardos de espuma!
Implementar um projétil
Aprenda a usar C++ para implementar projéteis e gerá-los durante o jogo.
Código completo
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "ItemData.h"
#include "ItemDefinition.generated.h"
/**
* Defines a basic item with a static mesh that can be built from the editor.
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "ItemDefinition.h"
#include "EquippableToolDefinition.generated.h"
class AEquippableToolBase;
class UInputMappingContext;
// Copyright Epic Games, Inc. All Rights Reserved.
#include "EquippableToolDefinition.h"
UEquippableToolDefinition* UEquippableToolDefinition::CreateItemCopy() const
{
UEquippableToolDefinition* ItemCopy = NewObject<UEquippableToolDefinition>(StaticClass());
ItemCopy->ID = this->ID;
ItemCopy->ItemType = this->ItemType;
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "InventoryComponent.generated.h"
class UEquippableToolDefinition;
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "AdventureGame/EquippableToolBase.h"
#include "DartLauncher.generated.h"
UCLASS(BlueprintType, Blueprintable)
class ADVENTUREGAME_API ADartLauncher : public AEquippableToolBase
// Copyright Epic Games, Inc. All Rights Reserved.
#include "DartLauncher.h"
#include "AdventureGame/AdventureCharacter.h"
void ADartLauncher::Use()
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("Using the dart launcher!"));
}
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "EquippableToolBase.generated.h"
class AAdventureCharacter;
class UInputAction;
// Copyright Epic Games, Inc. All Rights Reserved.
#include "EquippableToolBase.h"
#include "AdventureCharacter.h"
AEquippableToolBase::AEquippableToolBase()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Camera/CameraComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/Character.h"
#include "InputActionValue.h"
// Copyright Epic Games, Inc. All Rights Reserved.
#include "AdventureCharacter.h"
#include "EquippableToolBase.h"
#include "EquippableToolDefinition.h"
#include "ItemDefinition.h"
#include "InventoryComponent.h"
// Sets default values
AAdventureCharacter::AAdventureCharacter()