Bevor du anfängst
Stellen Sie sicher, dass Sie die folgenden Ziele im vorherigen Abschnitt Gegenstände und Daten verwalten erreicht haben:
Richten Sie eine Gegenstandsdatenstruktur, die Klasse
UDataAsset, eine Daten-Asset-Instanz vom Typ Verbrauchsgegenstand namensDA_Pickup_001und eine Datentabelle ein.
Erstellen Sie eine neue Abholpunktklasse
Bisher haben Sie gelernt, wie man die Struktur und die Daten eines Gegenstands definiert und speichert. In diesem Abschnitt werden Sie lernen, wie Sie diese Daten in einen Abholpunkt in-game umwandeln, eine konkrete Darstellung der Tabellendaten, mit denen der Spieler interagieren und einen Effekt erzielen können. Ein Abholpunkt könnte ein ausrüstbares Gadget sein, eine Box, die ihnen Materialien gibt, oder ein Powerup, das ihnen einen vorübergehenden Boost verleiht.
Um mit der Einrichtung einer Abholpunkt-Klasse mit anfänglichen Deklarationen zu beginnen, gehen Sie wie folgt vor:
Gehen Sie im Unreal Editor zu Werkzeuge > Neue C++-Klassen. Wählen Sie Actor als Parent-Klasse aus und geben Sie der Klasse den Namen
PickupBase. Klicken Sie auf Klasse erstellen.Öffnen Sie in Visual Studio
PickupBase.hund fügen Sie die folgenden Anweisungen am Anfang der Datei hinzu:#include ”Components/SphereComponent.h”. Sie fügen eine Kugelkomponente zum Abholpunkt hinzu, um Kollisionen zwischen Spieler und Abholpunkt zu erkennen.#include ”AdventureCharacter.h”. Fügen Sie eine Referenz auf Ihre First-Person-Charakterklasse hinzu, damit Sie auf Überlappungen zwischen dem Abholpunkt und Charakteren dieser Klasse prüfen können. (Dieses Tutorial verwendetAdventureCharacter.)Eine Vorwärts-Deklaration für
UItemDefinition. Das ist der assoziierte Daten-Asset Gegenstand, auf das sich jeder Abholpunkt bezieht.
C++// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Components/SphereComponent.h" #include "CoreMinimal.h" #include "AdventureCharacter.h" #include "GameFramework/Actor.h" #include "PickupBase.generated.h"Fügen Sie im
UCLASS()-Makro oberhalb derAPickupBaseKlassendefinition die BezeichnerBlueprintTypeundBlueprintablehinzu, um sie als Basis-Klasse für die Erstellung von Blueprints bereitzustellen.C++UCLASS(BlueprintType, Blueprintable) class ADVENTUREGAME_API APickupBase : public AActor {Fügen Sie in
PickupBase.cppein#includefürItemDefinition.hhinzu.C++// Copyright Epic Games, Inc. All Rights Reserved. #include "PickupBase.h" #include "ItemDefinition.h"
Initialisieren Sie den Abholpunkt mit Tabellendaten
Ihr Abholpunkt ist nur ein leer Actor, und wenn das Spiel beginnt, müssen Sie ihm die Daten geben, die er braucht, um richtig zu funktionieren. Der Abholpunkt sollte eine Reihe von Werten aus der Daten-Tabelle abrufen und diese Werte in einem ItemDefinition Daten-Asset (dem „Referenzgegenstand“) speichern.
Daten aus einer Daten-Tabelle abrufen
Deklarieren Sie in PickupBase.h im public-Abschnitt eine neue void-Funktion namens InitializePickup(). Sie werden diese Funktion verwenden, um den Abholpunkt mit Werten aus der Daten-Tabelle zu initialisieren.
// Initializes this pickup with values from the data table.
void InitializePickup();Um Daten aus der Tabelle abzurufen, benötigt der Abholpunkt-Blueprint zwei Eigenschaften: die Asset Daten-Tabelle und den Zeilennamen (den Sie so eingestellt haben, dass er mit der Gegenstands-ID identisch ist).
Deklarieren Sie im protected-Abschnitt eine FName--Eigenschaft namens PickupItemID. Geben Sie ihr den Bezeichner EditInstanceOnly und Category = “Pickup | Item Table”. Das ist die ID dieses Abholpunkt in der zugehörigen Daten-Tabelle.
// The ID of this pickup in the associated data table.
UPROPERTY(EditInstanceOnly, Category = "Pickup | Item Table")
FName PickupItemID;Abholpunkte sollten keine Standard-Gegenstands-ID haben, daher ermöglicht der Bezeichner EditInstanceOnly die Bearbeitung dieser Eigenschaft in Instanzen von Abholpunkten in der Welt, aber nicht im Archetyp (oder Klassenstandard).
Im Text Category bildet der vertikale Balken (|) einen verschachtelten Unterabschnitt. In diesem Beispiel erstellt die Unreal Engine einen Abholpunkt-Abschnitt mit einem Unterabschnitt namens Gegenstandstabelle im Details-Panel des Assets.
Als nächstes deklarieren Sie ein TSoftObjectPtr zu einer UDataTable mit dem Namen PickupDataTable. Geben Sie ihr die gleichen Bezeichner wie der PickupItemID. Dies ist die Daten-Tabelle, die der Abholpunkt verwendet, um seine Daten zu erhalten.
Die Daten-Tabelle wird möglicherweise nicht zur Laufzeit geladen, daher sollten Sie hier ein TSoftObjectPtr verwenden, damit Sie es asynchron laden können.
Speichern Sie die Header-Datei und wechseln Sie zu PickupBase.cpp, um InitializePickup() zu implementieren.
Innerhalb der Funktion wird in einer if-Anweisung, geprüft, ob die angegebene PickupDataTable gültig ist und ob PickupItemID einen Wert hat.
/**
* Initializes the pickup with default values by retrieving them from the associated data table.
*/
void APickupBase::InitializePickup()
{
if (PickupDataTable && !PickupItemID.IsNone())
{
}
}Fügen Sie in der if-Anweisung Code hinzu, um die Zeile mit den Werten aus der Datentabelle abzurufen. Deklarieren Sie einen const FItemData-Pointer namens ItemDataRow und legen Sie ihn auf das Ergebnis des Aufrufs von FindRow() auf Ihrer PickupDataTable fest. Geben Sie FItemData als Typ der zu suchenden Zeile an.
const FItemData* ItemDataRow = PickupDataTable->FindRow<FItemData>();FindRow() braucht zwei Argumente:
Der Name einer
FNameZeile, die Sie finden wollen. Übergeben Sie diePickupItemIDals Zeilenname.Ein Kontext String vom Typ
FString, den Sie für das Debugging verwenden können, wenn die Zeile nicht gefunden wird. Sie könnenText(„My Kontext here.“)verwenden, um einen Kontext String hinzuzufügen, oderToString(), um die Gegenstands ID in einen Kontext String zu konvertieren.
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());
}Einen Referenzgegenstand erstellen
Nachdem Sie die Zeilendaten des Abholpunkts abgerufen haben, erstellen und initialisieren Sie ein Daten-Asset vom Typ ReferenceItem, das diese Informationen enthält.
Durch das Speichern der Daten in einem Referenzgegenstand wie diesem kann die Unreal Engine einfach auf diese Daten verweisen, wenn sie etwas über den Gegenstand wissen muss, anstatt weitere Tabellendaten nachzuschlagen, was weniger effizient ist.
Deklarieren Sie in PickupBase.h im protected-Abschnitt einen TObjectPtr zu einer UItemDefinition namens ReferenceItem. Das ist ein Daten-Asset, in dem die Daten des Abholpunkts gespeichert werden. Geben Sie ihm den Bezeichner VisibleAnywhere und Category = “Pickup | Reference Item”.
// Data asset associated with this item.
UPROPERTY(VisibleAnywhere, Category = "Pickup | Reference Item")
TObjectPtr<UItemDefinition> ReferenceItem;Speichern die Header-Datei und wechseln Sie zurück zu PickupBase.cpp.
Legen Sie in InitializePickup() nach dem FindRow()-Aufruf ReferenceItem auf ein NewObject vom Typs UItemDefinition fest.
In der Unreal Engine ist NewObject<T>() eine vorgefertigte Funktion zur dynamischen Erstellung von UObject-abgeleiteten Instanzen zur Laufzeit. Sie gibt einen Pointer auf das neue Objekt zurück. Sie hat üblicherweise die folgende Syntax:
T* Objekt = NewObject<T>(Outer, Klasse);
Dabei ist T der Typ des UObject, das Sie erstellen, Outer ist der Eigentümer dieses Objekts, und Class ist die Klasse des Objekts, das Sie erstellen. Das Argument Class ist oft T::StaticClass(), das einen UClass-Pointer liefert, der den Klassentyp von Ts darstellt. Allerdings können Sie oft beide Argumente weglassen, da die UE davon ausgeht, dass Outer die aktuelle Klasse ist und T verwendet, um auf die UClass abzuleiten.
Übergeben Sie this als äußere Klasse und die UItemDefinition::StaticClass() als den Klassentyp, um eine Basis-UItemDefinition zu erstellen.
ReferenceItem = NewObject<UItemDefinition>(this, UItemDefinition::StaticClass());Um die Informationen des Abholpunkts in ReferenceItem zu kopieren, setzen Sie jedes Feld in ReferenceItem auf das assoziierte Feld aus ItemDataRow. Für das WorldMesh beziehen Sie die WorldMesh-Eigenschaft aus der ItemBase, auf die in ItemDataRow verwiesen wird.
ReferenceItem = NewObject<UItemDefinition>(this, UItemDefinition::StaticClass());
ReferenceItem->ID = ItemDataRow->ID;
ReferenceItem->ItemType = ItemDataRow->ItemType;
ReferenceItem->ItemText = ItemDataRow->ItemText;
ReferenceItem->WorldMesh = ItemDataRow->ItemBase->WorldMesh;InitializePickup() aufrufen
In BeginPlay() rufen Sie InitializePickup() auf, um den Abholpunkt zu initialisieren, wenn das Spiel beginnt.
// Called when the game starts or when spawned
void APickupBase::BeginPlay()
{
Super::BeginPlay();
// Initialize this pickup with default values
InitializePickup();
}Speichern Sie die Datei. PickupBase.cpp sollte nun so aussehen:
// Copyright Epic Games, Inc. All Rights Reserved.
#include "PickupBase.h"
// Sets default values
APickupBase::APickupBase()
{
// Set this actor to call Tick() every frame.
PrimaryActorTick.bCanEverTick = true;
}
In-Game Funktionalität erstellen
Ihr Abholpunkt verfügt über die erforderlichen Gegenstandsdaten, muss aber noch wissen, wie er in der Spielwelt erscheint und funktioniert. Er benötigt ein Mesh, das der Spieler sehen kann, ein Kollisionvolumen, um zu bestimmen, wenn der Spieler ihn berührt, und eine Logik, die dafür sorgt, dass der Abholpunkt verschwindet (als ob der Spieler sie aufgehoben hätte) und nach einer bestimmten Zeit respawnen.
Fügen Sie eine Mesh Komponente hinzu
Genau wie beim Einrichten des Spielercharakters in First-Person Kamera, Mesh und Animation hinzufügen werden Sie die CreateDefaultSubobject-Vorlagenfunktion verwenden, um ein statisches Mesh-Objekt als Child Komponente Ihrer Abholpunkt Klasse zu erstellen und dann das Mesh des Gegenstand auf diese Komponente anzuwenden.
Deklarieren Sie in PickupBase.h im Abschnitt protected ein TObjectPtr für eine UStaticMeshComponent mit dem Namen PickupMeshComponent. Dieses Mesh stellt den Abholpunkt in der Welt dar.
Sie werden Code verwenden, um das Mesh des Daten-Assets dieser Eigenschaft zuzuweisen, also geben Sie ihm den Bezeichner VisibleDefaultsOnly und Category = “Pickup | Mesh”, damit es im Unreal Editor sichtbar, aber nicht bearbeitbar ist.
// The mesh component to represent this pickup in the world.
UPROPERTY(VisibleDefaultsOnly, Category = "Pickup | Mesh")
TObjectPtr<UStaticMeshComponent> PickupMeshComponent;Speichern Sie die Header-Datei und wechseln Sie zu PickupBase.cpp.
Legen Sie in der Konstruktionsfunktion APickupBase den Pointer PickupMeshComponent auf das Ergebnis des Aufrufs CreateDefaultSubobject() vom Typ UStaticMeshComponent fest. Im Argument Text benennen Sie das Objekt „PickupMesh“.
Um dann sicherzustellen, dass das Mesh ordnungsgemäß instanziiert wurde, prüfen Sie, ob PickupMeshComponent nicht null ist.
// Sets default values
APickupBase::APickupBase()
{
// 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 pickup's mesh component
PickupMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("PickupMesh"));
check(PickupMeshComponent != nullptr);
}Gehen Sie zur Implementierung von InitializePickup().
Bevor Sie das WorldMesh auf die Mesh-Komponente des Abholpunkts anwenden, müssen Sie überprüfen, ob das Mesh geladen ist, da Sie es mit einem TSoftObjectPtr definiert haben.
Deklarieren Sie zunächst einen neuen UItemDefinition Pointer namens TempItemDefinition und setzen Sie ihn auf das Ergebnis des Aufrufs ItemDataRow->ItemBase.Get().
UItemDefinition* TempItemDefinition = ItemDataRow->ItemBase.Get();Prüfen Sie dann in einer if-Anweisung, ob das WorldMesh derzeit geladen ist, indem Sie WorldMesh.IsValid() aufrufen.
// Check if the mesh is currently loaded by calling IsValid().
if (TempItemDefinition->WorldMesh.IsValid()) {
}Ist dies der Fall, legen Sie die PickupMeshComponent durch Aufruf von SetStaticMesh() fest, und rufen das WorldMesh mit Get() ab:
// Check if the mesh is currently loaded by calling IsValid().
if (TempItemDefinition->WorldMesh.IsValid()) {
// Set the pickup's mesh to the associated item's mesh
PickupMeshComponent->SetStaticMesh(TempItemDefinition->WorldMesh.Get());
}Ist das Mesh nicht geladen, erzwingen Sie das Laden, indem Sie LoadSynchronous() für das Mesh aufrufen. Diese Funktion lädt gibt einen Asset-Pointer auf das Objekt zurück.
Nach der if-Anweisung deklarieren Sie in einer else-Anweisung einen neuen UStaticMesh-Pointer mit dem Namen WorldMesh und legen Sie diesen fest, indem Sie WorldMesh.LoadSynchronous() aurufen.
Legen Sie dann die PickupMeshComponent mit SetStaticMesh() fest.
else {
// If the mesh isn't loaded, load it by calling LoadSynchronous().
UStaticMesh* WorldMesh = TempItemDefinition->WorldMesh.LoadSynchronous();
PickupMeshComponent->SetStaticMesh(WorldMesh);
}Nach der Anweisung else machen Sie die PickupMeshComponent Anweisung sichtbar mit SetVisiblity(true):
// Set the mesh to visible.
PickupMeshComponent->SetVisibility(true);Fügen Sie eine Kollisionsform hinzu
Fügen Sie eine Kugel-Komponente hinzu, die als Kollisionsvolumen dient, und aktivieren Sie dann die Kollisionsabfrage für diese Komponente.
Deklarieren Sie in PickupBase.h im Abschnitt protected ein TObjectPtr zu einer USphereComponent namens SphereComponent. Diese Kugel Komponente wird zur Kollisionserkennung verwendet. Geben Sie ihr den Bezeichner EditAnywhere, BlueprintReadOnly und Category = “Pickup | Components”.
// Sphere Component that defines the collision radius of this pickup for interaction purposes.
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Pickup | Components")
TObjectPtr<USphereComponent> SphereComponent;Speichern Sie die Header-Datei und wechseln Sie zu PickupBase.cpp.
Legen Sie in der Konstruktionsfunktion APickupBase, nachdem Sie PickupMeshComponent festgelegt haben, SphereComponent auf das Ergebnis des Aufrufs CreateDefaultSubobject, mit USphereComponent als Typ und “SphereComponent” als Name fest. Fügen Sie anschließend eine Null check hinzu.
// Create this pickup's sphere component
SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
check(SphereComponent != nullptr);Da Sie nun beide Komponenten haben, verwenden Sie SetupAttachment(), um die PickupMeshComponent an die SphereComponent anzuhängen:
// Attach the sphere component to the mesh component
SphereComponent->SetupAttachment(PickupMeshComponent);Nachdem Sie die SphereComponent an die MeshComponent angehängt haben, legen Sie die Größe der Kugel mit SetSphereRadius() fest. Dieser Wert sollte so gewählt werden, dass der Abholpunkt-Collider groß genug ist, um mit ihm zu kollidieren, aber nicht so groß, dass der Charakter versehentlich gegen ihn stößt.
// Set the sphere's collision radius
SphereComponent->SetSphereRadius(32.f);Machen Sie in InitializePickup() nach der Zeile SetVisibility(true) die SphereComponent kollidierbar, indem Sie SetCollisionEnabled() aufrufen.
Diese Funktion nimmt ein Enum (ECollisionEnabled), das der Engine mitteilt, welcher Typ von Kollision zu verwenden ist. Sie möchten, dass der Charakter in der Lage ist, mit dem Pickup zu kollidieren und Kollisionsabfragen auszulösen, aber der Pickup sollte keine Physik haben, die ihn wegspringen lässt, wenn er getroffen wird, also übergeben Sie die Option ECollisionEnabled::QueryOnly.
// Set the mesh to visible and collidable.
PickupMeshComponent->SetVisibility(true);
SphereComponent->SetCollisionEnabled(ECollisionEnabled::QueryOnly); PickupBase.cpp sollte nun so aussehen:
// Copyright Epic Games, Inc. All Rights Reserved.
#include "PickupBase.h"
#include "ItemDefinition.h"
// Sets default values
APickupBase::APickupBase()
{
// 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;
Abholpunkt-Kollisionen simulieren
Da Ihr Abholpunkt nun eine Kollisionsform hat, fügen Sie eine Logik hinzu, um eine Kollision zwischen dem Abholpunkt und dem Spieler zu erkennen und den Abholpunkt verschwinden zu lassen, wenn er mit ihm kollidiert.
Richten Sie den Kollisionsevent ein
In PickupBase.h deklarieren Sie im Abschnitt protected eine void-Funktion mit dem Namen OnSphereBeginOverlap().
Jede Komponente, die von UPrimitiveComponent erbt, wie z. B. USphereComponent, kann diese Funktion implementieren, um Code auszuführen, wenn sich die Komponente mit einem anderen Actor überschneidet. Diese Funktion benötigt mehrere Parameter, die Sie nicht verwenden werden; Sie werden nur die folgenden übergeben:
UPrimitiveComponent* OverlappedComponent: Die Komponente, die überlappt wurde.AActor* OtherActor: Der Actor, der diese Komponente überlappt.UPrimitiveComponent* OtherComp: Die überlappte Komponente des Actors.int32 OtherBodyIndex: Der Index der überlappten Komponentebool bFromSweep, const FHitResult& SweepResult: Informationen über die Kollision, z. B. wo und in welchem Winkel sie stattfand.
// Code for when something overlaps the SphereComponent.
UFUNCTION()
void OnSphereBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);Speichern Sie die Header-Datei und wechseln Sie zu PickupBase.cpp.
Die Kollisions-Events der Unreal Engine sind mit dynamischen Multicast-Delegierten implementiert. In der UE ermöglicht dieses Delegatensystem, dass ein Objekt mehrere Funktionen auf einmal aufruft, ähnlich wie beim Senden einer Nachricht an eine Mailingliste, bei der die Abonnenten diese anderen Funktionen sind. Wenn wir Funktionen an den Delegaten binden, ist das so, als ob wir sie in unsere Mailingliste aufnehmen würden. Der „Delegierte“ ist unser Event, in diesem Fall eine Kollision zwischen dem Spieler und dem Abholpunkt. Wenn das Event eintritt, ruft die Unreal Engine alle an dieses Event gebundenen Funktionen auf.
Die Unreal Engine enthält einige andere Bindungsfunktionen, aber Sie sollten AddDynamic() verwenden, weil Ihr Delegate, OnComponentBeginOverlap, ein dynamischer Delegate ist. Außerdem binden Sie eine UFUNCTION in eine UObject-Klasse ein, was AddDynamic() für die Reflexions-Unterstützung erfordert. Weitere Informationen über dynamische Multicast-Delegierte finden Sie unter Multi-cast Delegates.
Verwenden Sie in PickupBase.cpp unter InitializePickup() das AddDynamic-Makro, um OnSphereBeginOverlap() an das OnComponentBeginOverlap-Event der Kugelkomponente zu binden.
// Register the Overlap Event
SphereComponent->OnComponentBeginOverlap.AddDynamic(this, &APickupBase::OnSphereBeginOverlap);Speichern Sie Ihre Arbeit. Jetzt wird OnSphereBeginOverlap() ausgeführt, wenn ein Charakter mit der Kugelkomponente des Abholpunkts kollidiert.
Verstecken Sie den Abholpunkt nach einer Kollision
Implementieren Sie in PickupBase.cpp OnSphereBeginOverlap(), um Ihren Abholpunkt verschwinden zu lassen, damit es so aussieht, als hätte der Spieler ihn aufgenommen.
Fügen Sie zunächst eine Debug-Meldung hinzu, die signalisiert, wenn diese Funktion ausgelöst wird.
void APickupBase::OnSphereBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Yellow, TEXT("Attempting a pickup collision"));
}Da diese Funktion immer dann ausgeführt wird, wenn ein anderer Actor mit dem Abholpunkt kollidiert, müssen Sie sicherstellen, dass es Ihr First-Person-Charakter ist, der kollidiert.
Deklarieren Sie einen neuen AAdventureCharacter-Zeiger mit dem Namen Charakter und setzen Sie ihn durch Casting von OtherActor auf den Namen Ihrer Character-Klasse (dieses Tutorial verwendet AAdventureCharacter).
// Checking if it's an AdventureCharacter overlapping
AAdventureCharacter* Character = Cast<AAdventureCharacter>(OtherActor);Prüfen Sie in einer if-Anweisung, ob Charakter nicht null ist. Null zeigt an, dass die Besetzung fehlgeschlagen ist und dass OtherActor keine Art von AAdventureCharacter ist.
Heben Sie innerhalb der if-Anweisung die Registrierung von OnComponentBeginOverlap für diese Funktion auf, indem Sie RemoveAll() aufrufen, damit sie nicht wiederholt ausgelöst wird. Damit ist die Kollision beendet.
if (Character != nullptr)
{
// Unregister from the Overlap Event so it is no longer triggered
SphereComponent->OnComponentBeginOverlap.RemoveAll(this);
}Dann setzen Sie die PickupMeshComponent mit SetVisibility(false) auf unsichtbar und setzen sowohl das Pickup-Mesh als auch die Kugelkomponente mit SetCollisionEnabled() auf nicht kollidierbar, wobei Sie die Option NoCollision übergeben.
if (Character != nullptr)
{
// Unregister from the Overlap Event so it is no longer triggered
SphereComponent->OnComponentBeginOverlap.RemoveAll(this);
// Set this pickup to be invisible and disable collision
PickupMeshComponent->SetVisibility(false);
PickupMeshComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
SphereComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}Speichern Sie PickupBase.cpp.
Respawn des Abholpunkt veranlassen
Da der Charakter nun nicht mehr mit dem Abholpunkt interagieren kann, sollte er nach einer bestimmten Zeit wieder respawnen.
Deklarieren Sie in PickupBase.h im protected-Abschnitt eine bool-Variable namens bShouldRespawn. Damit können Sie Respawns ein- oder ausschalten.
Deklarieren Sie einen float mit dem Namen RespawnTime, die mit 4.0f initialisiert wird. Das ist die Zeit, die man warten muss, bis der Abholpunkt wieder respawnt.
Geben Sie den beiden Eigenschaften EditAnywhere, BlueprintReadOnly und Category = „Pickup | Respawn“ Bezeichner.
// Whether this pickup should respawn after being picked up.
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Pickup | Respawn")
bool bShouldRespawn;
// The time in seconds to wait before respawning this pickup.
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Pickup | Respawn")
float RespawnTime = 4.0f;Deklarieren Sie ein FTimerHandle mit dem Namen RespawnTimerHandle.
// Timer handle to distinguish the respawn timer.
FTimerHandle RespawnTimerHandle;In der Unreal Engine werden die Gameplay-Timer von FTimerManager verwaltet. Diese Klasse enthält die Funktion SetTimer(), die eine Funktion oder einen Delegate nach einer festgelegten Verzögerung aufruft. Die Funktionen von FTimerManager verwenden einen FTimerHandle, um die Funktion zu starten, anzuhalten, fortzusetzen oder eine Schleife zu bilden. Sie verwenden RespawnTimerHandle, um zu signalisieren, wann der Abholpunkt wieder respawnen soll. Weitere Informationen zur Verwendung des Timer-Managers finden Sie unter Gameplay-Timer.
Speichern Sie die Header-Datei und wechseln Sie zu PickupBase.cpp.
Um den Abholpunkt-Respawn zu implementieren, verwenden Sie den Timer Manager, um einen Timer zu setzen, der InitializePickup() nach einer kurzen Wartezeit aufruft.
Sie wollen den Abholpunkt nur respawnen, wenn Respawns aktiviert sind; fügen Sie also am Ende von OnSphereBeginOverlap eine if-Anweisung ein, die prüft, ob bShouldRespawn True ist.
if (bShouldRespawn)
{
}Holen Sie in der if-Anweisung den Timer-Manager mit GetWorldTimerManager() und rufen Sie SetTimer() für den Timer-Manager auf. Diese Funktion hat die folgende Syntax:
SetTimer(InOutHandle, Objekt, InFuncName, InRate, bLoop, InFirstDelay);
Wobei:
InOutHandleist dasFTimerHandle, das den Timer steuert (IhrRespawnTimerHandle).Objectist dasUObject, das die Funktion besitzt, die Sie aufrufen. Nutzen Sie das hier.InFuncNameist ein Pointer auf die Funktion, die Sie aufrufen wollen (in diesem FallInitializePickup()).InRateist ein Float-Wert, der die Zeit in Sekunden angibt, die vor dem Aufruf Ihrer Funktion gewartet werden soll.bLoopbewirkt, dass sich der Timer entweder alleZeitSekunden wiederholt (True) oder nur einmal ausgelöst wird (False).InFirstDelay(optional) ist eine anfängliche Zeitverzögerung vor dem ersten Funktionsaufruf in einer Zeitschleife. Wenn nicht angegeben, verwendet die UEInRateals Verzögerung.
Sie wollen InitializePickup() nur einmal aufrufen, um den Abholpunkt zu ersetzen, also setzen Sie bLoop auf False.
Legen Sie Ihre bevorzugte Respawn-Zeit fest; in diesem Tutorial wird der Abholpunkt nach vier Sekunden ohne anfängliche Verzögerung respawnt.
// If the pickup should respawn, wait an fRespawnTime amount of seconds before calling InitializePickup() to respawn it
if (bShouldRespawn)
{
GetWorldTimerManager().SetTimer(RespawnTimerHandle, this, &APickupBase::InitializePickup, RespawnTime, false, 0);
}Ihre vollständige Funktion OnSphereBeginOverlap() sollte wie folgt aussehen:
/**
* Broadcasts an event when a character overlaps this pickup's SphereComponent. Sets the pickup to invisible and uninteractable, and respawns it after a set time.
* @param OverlappedComponent - the component that was overlapped.
* @param OtherActor - the Actor overlapping this component.
* @param OtherComp - the Actor's component that overlapped this component.
* @param OtherBodyIndex - the index of the overlapped component.
* @param bFromSweep - whether the overlap was generated from a sweep.
* @param SweepResult - contains info about the overlap such as surface normals and faces.
*/
void APickupBase::OnSphereBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
Speichern Sie Ihren Code und kompilieren Sie ihn mit Visual Studio.
Abholpunkte in Ihrem Level implementieren
Nun, da Sie den Code für Ihre Abholpunkte definiert haben, ist es an der Zeit, sie in Ihrem Spiel zu testen!
Um Ihrem Level Abholpunkte hinzuzufügen, gehen Sie folgendermaßen vor:
Gehen Sie im Unreal Editor im Asset-Baum des Inhaltsbrowsers zu Inhalt > FirstPerson > Blueprints.
Erstellen Sie im Ordner Blueprints einen neuen Child-Ordner namens Abholpunkte, um Ihre Abholpunkte-Klassen zu speichern.
Gehen Sie im Asset-Baum zu Ihrem C++-Klassen-Ordner. Klicken Sie mit der rechten Maustaste auf Ihre PickupBase-Klasse, um einen Blueprint aus dieser Klasse zu erstellen.
Geben Sie ihr den Namen
BP_PickupBase.Wählen Sie als Pfad Inhalt/FirstPerson/Blueprints/Abholpunkte und klicken Sie auf Abholpunkt-Basis-Klasse erstellen.
Gehen Sie zurück zu Ihrem Ordner Blueprints > Abholpunkte. Ziehen Sie Ihren
BP_PickupBase-Blueprint in den Level. Eine Instanz von PickupBase erscheint in Ihrem Level und wird automatisch im Outliner-Panel ausgewählt. Allerdings hat sie noch kein Mesh.Wenn der Actor
BP_PickupBasenoch ausgewählt ist, legen Sie im Details-Panel die folgenden Eigenschaften fest:Setzen Sie Abholpunkt Gegenstand-ID auf
pickup_001.Setzen Sie Abholpunkt Datentabelle auf
DT_PickupData.Setzen Sie Sollte Respawnen auf True.
Wenn Sie auf Spielen klicken, um Ihr Spiel auszuprobieren, sollte Ihr Abholpunkt die Abholpunkt Gegenstand-ID verwenden, um die Daten-Tabelle abzufragen und die mit pickup_001 verbundenen Daten abzurufen. Der Abholpunkt verwendet Tabellendaten und die Referenz auf Ihr Daten-Asset DA_Pickup_001, um ein Referenzelement zu initialisieren und dessen statisches Mesh zu laden.
Wenn Sie über den Abholpunkt laufen, sollten Sie sehen, wie er verschwindet und vier Sekunden später wieder auftaucht.
Anderen Abholpunkt auswählen
Wenn Sie die Abholpunkt Gegenstand-ID auf einen anderen Wert setzen, ruft Ihre Abholpunkt stattdessen Daten aus dieser Zeile in der Tabelle ab.
Führen Sie die folgenden Schritte aus, um mit dem Wechsel der Abholpunkt Gegenstand-ID zu experimentieren:
Erstellen Sie ein neues Daten-Asset mit dem Namen DA_Pickup_002. Setze in diesem Asset die folgenden Eigenschaften:
ID: pickup_002
Gegenstandstyp: Verbrauchsgegenstand
Name: Test Name 2
BeschreibungBeschreibung: Testbeschreibung 2
Welt-Mesh:
SM_ChamferCube
Fügen Sie eine neue Zeile in die Tabelle
DT_PickupDataein und geben Sie die Informationen zum Daten-Asset in die Felder der neuen Zeile ein.Ändern Sie im Actor
BP_PickupBasedie Abholpunkt Gegenstand-ID inpickup_002.
Wenn Sie auf Spielen klicken, um Ihr Spiel zu testen, sollte der Abholpunkt stattdessen mit den Werten von DA_Pickup_002 spawnen!
Abholpunkt-Actors im Editor aktualisieren
Ihre Abholpunkte funktionieren in-game, aber es kann schwierig sein, sie im Editor zu visualisieren, da sie kein Standard-Mesh haben.
Um dies zu beheben, verwenden Sie die Funktion PostEditChangeProperty(). Dies ist eine In-Editor-Funktion, die die Unreal Engine aufruft, wenn der Editor eine Eigenschaft ändert, so dass Sie sie verwenden können, um Ihre Actors im Viewport auf dem neuesten Stand zu halten, wenn sich ihre Eigenschaften ändern. So können Sie beispielsweise ein UI-Element aktualisieren, wenn Sie die Standard-Gesundheitswerte eines Spielers ändern, oder eine Kugel skalieren, wenn Sie sie näher an den Ursprung heranführen oder von ihm wegbewegen.
In diesem Projekt werden Sie es verwenden, um das neue statische Mesh des Abholpunkts anzuwenden, wenn sich die Abholpunkt Gegenstand-ID ändert. Auf diese Weise können Sie Ihren Abholpunkt-Typ ändern und sehen, dass er sofort im Viewport aktualisiert wird, ohne auf Spielen klicken zu müssen!
Gehen Sie folgendermaßen vor, um Änderungen an Ihren Abholpunkten sofort im Editor anzuzeigen:
Deklarieren Sie in
PickupBase.himprotected-Abschnitt ein#if WITH_EDITOR-Makro. Dieses Makro teilt dem Unreal Header Tool mit, dass alles, was darin enthalten ist, nur für Editor-Builds gepackt und nicht für Veröffentlichungs-Versionen des Spiels kompiliert werden soll. Beenden Sie dieses Makro mit einer#endif-Anweisung.C++#if WITH_EDITOR #endifDeklarieren Sie im Makro einen virtuellen void-Funktion-Override für
PostEditChangeProperty(). Diese Funktion nimmt eine Referenz auf dasFPropertyChangedEvent, das Informationen über die geänderte Eigenschaft, die Art der Änderung und mehr enthält. Speichern Sie die Header-Datei.C++#if WITH_EDITOR // Runs whenever a property on this object is changed in the editor virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; #endifImplementieren Sie in
PickupBase.cppdiePostEditChangeProperty()- Funktion. Beginnen Sie mit dem Aufruf der FunktionSuper::PostEditChangeProperty(), um alle Eigenschaftsänderungen der Parent-Klasse zu behandeln.C++/** * Updates this pickup whenever a property is changed. * @param PropertyChangedEvent - contains info about the property that was changed. */ void APickupBase::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { // Handle parent class property changes Super::PostEditChangeProperty(PropertyChangedEvent); }Erstellen Sie eine neue Variable
const FNamemit dem NamenChangedProperty, um den Namen der geänderten Eigenschaft zu speichern.C++// Handle parent class property changes Super::PostEditChangeProperty(PropertyChangedEvent); const FName ChangedPropertyName;Um zu überprüfen, ob das
PropertyChangedEventeineEigenschaftenthält, und diese Eigenschaft zu speichern, verwenden Sie einen ternären Operator mitPropertyChangedEvent.Propertyals Bedingung. Legen SieChangedPropertyNameaufPropertyChangedEvent.Property->GetFName()fest, wenn die Bedingung True ist, und setzen Sie ihn aufNAME_None, wenn False.C++// If a property was changed, get the name of the changed property. Otherwise use none. const FName ChangedPropertyName = PropertyChangedEvent.Property ? PropertyChangedEvent.Property->GetFName() : NAME_None;NAME_Noneist eine globale Unreal-Engine-Konstante des TypsFName, und bedeutet „kein gültiger Name“ oder „null name“.Da Sie nun den Namen der Eigenschaft kennen, können Sie Unreal Engine veranlassen, das Mesh zu aktualisieren, wenn erkannt wurde, dass sich die ID geändert hat.
Prüfen Sie in einer
if-Anweisung, obChangePropertyNamegleich dem Ergebnis des Aufrufs vonGET_MEMBER_NAME_CHECKED()ist, wobei Sie dieseAPickupBase-Klasse und diePickupItemIDübergeben. Dieses Makro führt eine Prüfung der Kompilierzeit durch, um sicherzustellen, dass die übergebene Eigenschaft in der übergebenen Klasse vorhanden ist.Sie werden auch Werte aus der Daten-Tabelle abrufen, also überprüfen Sie auch, ob die Tabelle gültig ist, bevor Sie die
if-Anweisung eingeben.C++// Verify that the changed property exists in this class and that the PickupDataTable is valid. if (ChangedPropertyName == GET_MEMBER_NAME_CHECKED(APickupBase, PickupItemID) && PickupDataTable) { }Rufen Sie innerhalb der
if-Anweisung die mit dieser Abholung verknüpfte Datenzeile auf dieselbe Weise ab, wie Sie es inInitializePickup()getan haben, indem SieFindRowaufrufen.Diesmal, um sicherzustellen, dass die
PickupItemIDin der Tabelle ist, bevor Sie fortfahren, setzen Sie dieFindRow-Zeile in eine verschachtelteif-Anweisung.C++// Verify that the changed property exists in this class and that the PickupDataTable is valid. if (ChangedPropertyName == GET_MEMBER_NAME_CHECKED(APickupBase, PickupItemID) && PickupDataTable) { // Retrieve the associated ItemData for this pickup. if (const FItemData* ItemDataRow = PickupDataTable->FindRow<FItemData>(PickupItemID, PickupItemID.ToString())) { } }Wenn UE die Zeilendaten erfolgreich gefunden hat, erstellen Sie eine Variable
TempItemDefinition, um das Daten-Asset (das das neue Mesh enthält) zu speichern, auf das inItemDataRowverwiesen wird.C++if (const FItemData* ItemDataRow = PickupDataTable->FindRow<FItemData>(PickupItemID, PickupItemID.ToString())) { UItemDefinition* TempItemDefinition = ItemDataRow->ItemBase;Um das Mesh zu aktualisieren, verwenden Sie
SetStaticMeshauf derPickupMeshComponentund übergeben dasWorldMeshdes temporären Daten-Assets.C++// Set the pickup's mesh to the associated item's mesh PickupMeshComponent->SetStaticMesh(TempItemDefinition->WorldMesh.Get());Legen Sie den Kollisionsradius der Kugel-Komponente mit
SetSphereRadius(32.f)fest.C++// Set the sphere's collision radius SphereComponent->SetSphereRadius(32.f);Speichern Sie Ihren Code und kompilieren Sie ihn mit Visual Studio.
Ihre vollständige PostEditChangeProperty()-Funktion sollte wie folgt aussehen:
/**
* Updates this pickup whenever a property is changed.
* @param PropertyChangedEvent - contains info about the property that was changed.
*/
void APickupBase::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
// Handle parent class property changes
Super::PostEditChangeProperty(PropertyChangedEvent);
// If a property was changed, get the name of the changed property. Otherwise use none.
Zurück im Editor, stellen Sie im Outliner sicher, dass Ihr BP_PickupBase-Actor ausgewählt ist. Ändern Sie die Abholpunkt Gegenstand-ID von pickup_001 in pickup_002 und ändern Sie sie dann wieder zurück. Wenn Sie die ID ändern, wird das Mesh Ihres Abholpunkts im Viewport aktualisiert.
Wenn Sie mit anderen Meshs experimentieren, müssen Sie möglicherweise das Spiel einmal spielen, um das vollständige Laden eines neuen Meshs zu erzwingen, bevor Sie es im Viewport des Editors sehen können.
Als Nächstes
Als Nächstes werden Sie Ihre Abholpunkt-Klasse erweitern, um ein benutzerdefiniertes Gadget zu erstellen und es mit Ihrem Charakter auszurüsten!
Ihren Charakter ausrüsten
Lernen Sie, mit C++ benutzerdefinierte ausrüstbare Gegenstände zu erstellen und an Ihren Charakter anzukoppeln.
Vollständiger Code
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "Components/SphereComponent.h"
#include "CoreMinimal.h"
#include "AdventureCharacter.h"
#include "GameFramework/Actor.h"
#include "PickupBase.generated.h"
// Copyright Epic Games, Inc. All Rights Reserved.
#include "PickupBase.h"
#include "ItemDefinition.h"
// Sets default values
APickupBase::APickupBase()
{
// 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;