Antes de começar
Certifique-se de ter um projeto da Unreal Engine baseado em C++.
Esta página continua do curso Como adicionar câmera, malha e animação em primeira pessoa; porém, você pode concluir este independentemente do restante da série de tutoriais Como programar um jogo de aventura em primeira pessoa se quiser apenas aprender sobre jogabilidade baseada em dados.
Organização de dados em jogos
O modo como você organiza e representa os dados é uma parte importante do design de jogos. Itens interativos podem ter qualidades muito diferentes e podem existir no jogo ou apenas como um dados. Você poderia representar esses dados criando classes e Blueprints separados para cada tipo de item, distribuindo e comunicando os dados entre diferentes atores. Porém, isso se torna ineficaz à medida que o jogo e a quantidade de dados armazenados aumentam.
Uma abordagem melhor é a jogabilidade orientada por dados. Em vez de valores fixos, você organizará os dados em uma posição centralizada gerenciados pelos sistemas no jogo. A jogabilidade orientada por dados permite que você carregue o que precisar na hora certa. Por exemplo, muitos jogos usam documentos de planilhas para organizar o diálogo, pois é mais fácil para um sistema extrair uma linha específica de diálogo em vez de armazenar potencialmente centenas de linhas em cada personagem.
Nesta seção, você aprenderá a usar essa metodologia para começar a criar itens personalizados.
Elementos de jogabilidade orientada por dados
Antes de começar a criar itens básicos, é importante considerar o que define um "item". Como um item pode ser qualquer elemento com que o jogador interaja, ele deve ter um conjunto mínimo de propriedades válidas para qualquer tipo de item. Essa configuração será feita em uma estrutura de dados de item. Você também deve ter um local centralizado para organizar e exibir os dados desse item. Para isso, você usará um ativo de tabela de dados.
A estrutura de dados de item atua como um modelo que define o tipo de dado do item, enquanto a tabela de dados e os ativos de dados armazenam as entradas de dados reais com base na estrutura.
O diagrama a seguir mostra os quatro elementos de jogabilidade baseada em dados que você criará nesta parte do tutorial. Depois de configurar os quatro elementos, você revisitará este diagrama em mais detalhes para ajudar a resumir o que você criou.
Primeiro, você criará dois arquivos para definir os dados do item:
ItemData.h: Um contêiner para a declaração da estrutura de dados de item (FItemData)ItemDefinition.h: Uma classe que herda deUDataAssetpara que seus dados de item possam ser usados no Unreal Editor.
Como a estrutura de dados de item não herda de UObject e não pode ser instanciada em um nível, você também precisa da classe de ativo de dados para usar e referenciar no editor.
Em seguida, no Unreal Editor, você criará uma tabela de dados e uma instância de ativo de dados com base em ItemDefinition.
Defina os dados do item
A estrutura de dados de item define os dados ou as propriedades que cada item na tabela de dados deve ter, agindo como as colunas da tabela.
A estrutura de dados de item terá as seguintes propriedades:
ID: um nome exclusivo para o item, útil ao referenciar as linhas da tabela posteriormente.
Tipo de item: o tipo deste item (neste caso, você definirá os tipos "Tool" (Ferramenta) e "Consumable" (Consumível)).
Texto do item: os dados textuais sobre o item, incluindo nome e descrição.
Base do item: o ativo de dados ItemDefinition associado a esse item.
Se quiser criar seus próprios campos de tabela (colunas), lembre-se de que os campos da tabela de dados podem ser de qualquer tipo compatível com UPROPERTY().
Crie um contêiner do arquivo de cabeçalho para a estrutura
Configure uma nova pasta e um novo arquivo de cabeçalho (.h) para armazenar a definição da estrutura de dados de item.
Você pode criar a estrutura de FItemData dentro de ItemDefinition.h, mas colocar a estrutura em um arquivo separado ajuda a organizar os elementos de dados e permite a reutilização.
Para definir um arquivo de cabeçalho como contêiner da estrutura de dados de item, siga estas etapas:
No Unreal Editor, acesse Ferramentas, Nova classe C++.
Na janela Escolher classe pai, selecione Nenhuma como a classe pai e clique em Próxima.
Ao lado do caminho, clique no ícone de pasta. Na pasta
Origem/[NomeDoProjeto], crie uma pasta chamadaDatapara armazenar a classe.Dê à classe o nome
ItemDatae clique em Criar classe.Se a Unreal Engine não abrir os arquivos de classe automaticamente, abra o projeto e o
ItemData.hno Visual Studio.Exclua todo o texto de
ItemData.cpp, salve e feche o arquivo. Você não vai usá-lo.Em
ItemData.h, exclua tudo abaixo da linha#include "CoreMinimal.h".O cabeçalho
CoreMinimal.hinclui tipos básicos comoFName,FStringe outros tipos necessários para definir os dados.Sobre
ItemData.h, adicione#pragma oncee as seguintes declarações "include":#include "Engine/DataTable.h": Necessário para que a estrutura herde deFTableRowBase.#include "ItemData.generated.h": Exigido pela Unreal Header Tool. Certifique-se de que a declaração "include" vem por último para que o código seja compilado corretamente.
C++#pragma once #include "CoreMinimal.h" #include "Engine/DataTable.h" #include "ItemData.generated.h"Adicione uma declaração de encaminhamento para uma classe chamada
UItemDefinition. Esse será o ativo de dados com o qual você poderá trabalhar no editor.C++class UItemDefinition;
Defina as propriedades do item
Como não há tipos de variável existentes para um tipo de item e dados de texto, você precisará defini-los.
Para definir uma enumeração de tipo de item, siga estas etapas:
Crie uma nova
classe de enumeraçãoque liste todos os tipos de item possíveis. Neste tutorial, você criará itens do tipo Tool e Consumable.Nomeie a classe de enumeração como
EItemTypee digiteuint8. Adicione a macroUENUM()acima dela para declarar essa enumeração para a Unreal Header Tool.C++// Defines the type of the item. UENUM() enum class EItemType : uint8 { };Adicione dois valores personalizados a esta enumeração:
Tool: adicionando uma macroUMETA()comDisplayName = "Tool".Consumable: adicionando uma macroUMETA()comDisplayName = "Consumable".
C++// Defines the type of the item. UENUM() enum class EItemType : uint8 { Tool UMETA(DisplayName = "Tool"), Consumable UMETA(DisplayName = "Consumable") };
Esses tipos de item são personalizados e não integrados à Unreal Engine; assim que você aprender o básico, poderá fazer o que quiser. (Talvez um QuestItem, Currency ou Disguise?)
Para definir uma estrutura de texto de item, siga estas etapas:
Após
EItemType, crie uma estruturaFItemTextcom uma macroUSTRUCT(). Essa estrutura contém dados de texto sobre o item.C++// Contains textual data about the item. USTRUCT() struct FItemText { };Em
FItemText, adicione a macroGENERATED_BODY().Em seguida, adicione duas propriedades
FText, chamadasNameeDescription, para armazenar o nome e a descrição desse item. Adicione a macroUPROPERTY()a cada um comEditAnywherecomo argumento.C++// Contains textual data about the item. USTRUCT() struct FItemText { GENERATED_BODY() // The text name of the item. UPROPERTY(EditAnywhere) FText Name;
Crie a estrutura de dados de item
Após adicionar as declarações de pré-requisitos, crie a estrutura de dados de item com as propriedades do item. Essas propriedades se tornam os campos da tabela de dados.
Definia uma estrutura chamada FItemData que herda de FTableRowBase. Adicione o especificador public para permitir a visibilidade em qualquer lugar e adicione GENERATED_BODY() à Unreal Header Tool.
// Defines a basic item that can be used in a data table.
USTRUCT()
struct FItemData : public FTableRowBase
{
GENERATED_BODY()
};FTableRowBase é uma estrutura base fornecida pela Unreal Engine que permite usar USTRUCTs personalizados em um ativo de tabela de dados. A Unreal a usa para saber como serializar sua estrutura de linha, para oferecer suporte à importação e exportação de dados de arquivos CSV/JSON e para garantir a segurança de tipos ao extrair dados da tabela.
Na estrutura FItemData, adicione as seguintes declarações:
Um
FNamechamadoID. Cada linha em uma tabela de dados precisa de umFNameassociado para referenciar.Uma
enumeração de EItemTypechamadaItemType. Essa é a enumeração dos tipos de item que você declarou antes.Uma estrutura de
FItemTextchamadaItemText. Essa é a estrutura de dados de texto que você declarou antes.
Adicione a macro UPROPERTY() a cada declaração com os argumentos EditAnywhere e Category = "ItemData".
// The ID name of this item for referencing in a table row.
UPROPERTY(EditAnywhere, Category = "Item Data")
FName ID;
// The type of the item.
UPROPERTY(EditAnywhere, Category = "Item Data")
EItemType ItemType;
// Text struct including the item name and description.
UPROPERTY(EditAnywhere, Category = "Item Data")
Adicione mais uma declaração: um TObjectPtr a uma UItemDefinition chamada ItemBase. Defina a mesma macro UPROPERTY das outras propriedades na estrutura.
// The Data Asset item definition associated with this item.
UPROPERTY(EditAnywhere, Category = "Item Data")
TObjectPtr<UItemDefinition> ItemBase;TObjectPtr é um tipo de ponteiro inteligente na Unreal Engine que é uma maneira mais segura de referenciar tipos derivados de UObject. Ele é um substituto para ponteiros UObject brutos que reconhece o editor e pode ser usado na coleta de lixo. É uma referência fixa, então mantém o objeto carregado em tempo de execução.
Na próxima etapa, você criará uma classe UDataAsset chamada ItemDefinition. Na tabela de dados, você usará o campo ItemBase para referenciar a instâncias do ativo de dados de ItemDefinition.
A estrutura FItemData deve ficar assim:
// Defines a basic item that can be used in a data table.
USTRUCT()
struct FItemData : public FTableRowBase
{
GENERATED_BODY()
// The ID name of this item for referencing in a table row.
UPROPERTY(EditAnywhere, Category = "Item Data")
FName ID;
Salve o código.
Crie uma definição de item de ativo de dados
Até agora, você definiu os dados do item ou o tipo de dado que aparecem na tabela de dados. Em seguida, implemente a classe UItemDefinition para a qual fez uma declaração de encaminhamento em ItemData.h.
Essa classe herda de UDataAsset e, portanto, é um UObject; ou seja, você pode criar e usar instâncias dela diretamente no editor sem precisar usar código. Você preencherá a tabela de dados com instâncias da classe UItemDefinition.
Para criar a classe de DataAsset ItemDefinition (ItemDefinition.h), siga estas etapas:
No Unreal Editor, acesse Ferramentas, Nova classe C++.
Na janela Escolher classe pai, clique Todas as classes.
Pesquise e selecione DataAsset como a classe pai e clique em Próxima.
Dê à classe o nome de
ItemDefinition(o que corresponde à declaração de encaminhamento feita emItemData.h), e clique em Criar classe.
O VS deve abrir automaticamente seus novos arquivos de classe .h e .cpp . Caso contrário, atualize o VS e abra os arquivos manualmente. Você trabalhará apenas no arquivo .h, então pode fechar o arquivo .cpp se desejar.
Em ItemDefinition.h, adicione uma "include" a ItemData.h. porque pode ser necessário reutilizar as propriedades ItemType e ItemText declaradas no arquivo.
#include "CoreMinimal.h"
#include "Data/ItemData.h"
#include "ItemDefinition.generated.h"Em ItemDefinition.h, na macro UCLASS() acima da definição de classe, adicione os especificadores BlueprintType e Blueprintable para expor isso como uma classe base para a criação de Blueprints.
// Defines a basic item with a static mesh that can be built from the editor.
UCLASS(BlueprintType, Blueprintable)
class FIRSTPERSON_API UItemDefinition : public UDataAsset
{
GENERATED_BODY()
public:
// Default constructor for the class.
UItemDefinition();
};Na seção pública, copie as declarações FName ID, EItemType ItemType e FItemText ItemText de ItemData.h.
A definição de item recebe os mesmos dados que a estrutura FItemData; portanto, você não precisa referenciar a tabela original quando quiser obter informações sobre o item.
public:
// The ID name of this item for referencing in a table row.
UPROPERTY(EditAnywhere, Category = "Item Data")
FName ID;
// The type of this item.
UPROPERTY(EditAnywhere, Category = "Item Data")
EItemType ItemType;
Após ItemText, declare um TSoftObjectPtr do tipo UStaticMesh com o nome WorldMesh. Você usará essa malha estática para exibir este item no mundo.
TSoftObjectPtr é um tipo especial de ponteiro fraco que atua como uma representação de string de um caminho para um ativo que é carregado apenas quando necessário. Em geral, ao declarar uma propriedade de ponteiro UObject que faz referência a um ativo, esse ativo é carregado quando o objeto que contém a propriedade é carregado. Isso pode fazer com que todos os ativos sejam carregados na inicialização do jogo, causando uma enorme sobrecarga e lentidão. TSoftObjectPtr é útil para ativos grandes, como malhas, que só devem ser carregadas sob demanda. Consulte mais informações em Carregamento assíncrono de ativos.
Adicione a mesma macro UPROPERTY(EditAnywhere, Category = "Item Data") a esta propriedade.
// Text struct including the item name and description.
UPROPERTY(EditAnywhere, Category = "Item Data")
FItemText ItemText;
// The Static Mesh used to display this item in the world.
UPROPERTY(EditAnywhere, Category = "Item Data")
TSoftObjectPtr<UStaticMesh> WorldMesh;
As linhas da tabela de dados são semelhantes às linhas de CSV, pois contêm dados de texto e não ativos completos. Para otimizar o gerenciamento de dados, recomendamos agrupar informações como a malha, o material e as animações de um item em um DataAsset, pois esse é o local central onde residem todos os dados sobre um item específico. Portanto, a propriedade de malha estática do item está em UItemDefinition e não na estrutura FItemData.
A classe UItemDefinition deve ficar assim:
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Data/ItemData.h"
#include "ItemDefinition.generated.h"
/**
* Defines a basic item with a static mesh that can be built from the editor.
Salve o código e compile pelo Visual Studio.
Crie uma instância de ativo de dados
Com os dados do item (a estrutura FItemData de ItemData.h) e a definição de item (classe UItemDefinition) definidos, você tem todas as partes necessárias para criar instâncias de item e tabela de dados.
Primeiro, crie um ativo de dados para um novo item de coleta de projétil; em seguida, crie uma tabela de dados e preencha-a com as informações do seu ativo de dados.
Para criar um item de ativo de dados a partir da classe ItemDefinition, siga estas etapas:
No Navegador de Conteúdo, acesse a pasta Conteúdo, FirstPerson, clicar em Adicionar ou clique com o botão direito do mouse em uma área em branco na Visualização de ativos, selecione Nova pasta e dê a ela o nome de
Data.É aqui que você armazena e organiza os ativos de dados no jogo.
Na pasta
Data, clique em Adicionar ou clique com o botão direito em uma área em branco na visualização de ativos e selecione Diversos, Ativo de dados.Certifique-se de selecionar a opção "Ativo de dados" com o ícone de gráfico de pizza.
Na janela Pick Class For Data Asset Instance (Selecionar classe para instância do ativo de dados), selecione Definição de item (a classe C++ que você definiu antes), e clique em Selecionar. Dê ao novo ativo de dados o nome de
DA_Pickup_001.Clique duas vezes em
DA_Pickup_001para abri-lo. No painel Detalhes , você verá todas as propriedades definidas emItemDefinition.h.Digite
pickup_001como ID.Defina Tipo de item como Consumable.
Expanda o texto do item e digite qualquer nome e descrição.
Defina a malha do mundo como
SM_FoamBullet.Clique em Salvar próximo ao canto superior esquerdo da janela para salvar o ativo de dados.
Defina uma tabela de dados
Agora que você tem pelo menos um ativo de dados para preencher uma tabela de dados, já pode criar a tabela.
Cada linha na tabela de dados é uma instância preenchida da estrutura ItemData.
Para criar uma tabela de dados, siga estas etapas:
No Navegador de Conteúdo,na pasta
Data, clique com o botão direito em uma área em branco e selecione Diversos, Tabela de dados.Na janela Pick Row Structure (Escolher estrutura da linha), selecione ItemData (a estrutura
FItemDatadefinida emItemData.h), e clique em OK.Dê à nova tabela o nome de
DT_PickupDatae clique duas vezes nela para abri-la.
A tabela de dados está vazia no início. Porém, você verá as propriedades que definiu em FItemData como cabeçalhos na parte superior da tabela e uma coluna adicional chamada Nome da linha.
Para adicionar o ativo de dados do item de coleta como uma linha na tabela, siga estas etapas:
Clique em Adicionar para adicionar uma nova linha à tabela. O editor de tabela de dados lista as entradas de linha no canto superior esquerdo da aba Tabela de dados.
Clique duas vezes no nome da linha
NewRowe altere-o parapickup_001(o ID do seu ativo de dados).Você pode usar qualquer
FNamecomo o nome da linha; porém, para facilitar a referenciação da linha no código, deixe o nome da linha igual ao ID do ativo de dados.No painel Editor de Linha, digite os mesmos valores definidos no ativo de dados
DA_Pickup_001nos campos ID, Tipo de item e Texto do item.Defina Base do item como
DA_Pickup_001DataAsset.Salve a tabela de dados.
E é isso! Confira novamente o diagrama dos elementos de jogabilidade orientada a dados que você criou nesta etapa e veja como eles estão conectados:
Você criou uma tabela de dados que obtém suas colunas da estrutura FItemData. Você preencheu a tabela com uma linha que contém os dados da instância do ativo de dados ItemDefinition do tipo consumível que criou e usou o ponteiro ItemBase para referenciar o ativo de dados em si. Por fim, a instância do ativo de dados obteve propriedades da classe pai do ativo de dados UItemDefinition que você criou.
Próxima
Na próxima seção, você aprenderá a estender essa definição de item para criar uma classe de coleta personalizar e instanciá-la no nível.
Código completo
#pragma once
#include "CoreMinimal.h"
#include "Engine/DataTable.h"
#include "ItemData.generated.h"
class UItemDefinition;
/**
* Defines the type of the item.
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Data/ItemData.h"
#include "ItemDefinition.generated.h"
/**
* Defines a basic item with a static mesh that can be built from the editor.