Diese Seite behandelt einige erweiterte Konzepte der Unreal-Engine-Programmierung. Bei den folgenden Erläuterungen gehen wir davon aus, dass Sie mit C# für Unity vertraut sind und C++ für die Unreal Engine lernen möchten. Allerdings können Sie für die meisten Dinge auch Blueprint verwenden und die gleichen Ergebnisse erhalten. Deshalb haben wir Beispiele sowohl in C++ als auch in Blueprint hinzugefügt, wo immer es möglich war.
Lassen Sie uns über einige der häufigsten Gameplay-Programmierungsmuster sprechen und wie man sie in der Unreal Engine angeht. Die folgenden Beispiele behandeln einige gängige Funktionen von Unity und zeigen, wie Sie die gleiche Funktionalität in der Unreal Engine implementieren können.
Instantiating GameObject / Spawnender Actor
In Unity haben Sie die Funktion Instantiate verwendet, um neue Instanzen von Objekten zu erstellen. Diese Funktion nimmt einen beliebigen Typ von UnityEngine.Object (GameObject, MonoBehaviour usw.) entgegen und erstellt eine Kopie davon.
public GameObject EnemyPrefab;
public Vector3 SpawnPosition;
public Quaternion SpawnRotation;
void Start()
{
GameObject NewGO = (GameObject)Instantiate(EnemyPrefab, SpawnPosition, SpawnRotation);
NewGO.name = "MyNewGameObject";
}
Die Unreal Engine verfügt über zwei verschiedene Funktionen, um Objekte zu instantiieren:
NewObjecterstellt neueUObject-Typen.SpawnActorspawntAActor-Typen.
UObjects und NewObject
Das Ableiten von Subklassen aus UObject in der Unreal Engine funktioniert ähnlich wie das Ableiten von Subklassen aus ScriptableObject in Unity. Diese sind nützlich für Gameplay-Klassen, die nicht in der Welt spawnen oder angekoppelte Komponenten haben müssen, wie es bei Actors der Fall ist.
Wenn Sie in Unity Ihre eigene Subklasse von ScriptableObject erstellen wollen, würden Sie sie wie folgt instantiieren:
MyScriptableObject NewSO = ScriptableObject.CreateInstance<MyScriptableObject>();
Wenn Sie in der Unreal Engine Ihren eigenen abgeleiteten Typ UObject erstellen, können Sie ihn wie folgt instantiieren:
UMyObject* NewObj = NewObject<UMyObject>();
AActors und SpawnActor
Actors werden mit der Methode SpawnActor auf einem World-Objekt (UWorld in C++) gespawnt. Einige UObjects stellen Ihnen eine GetWorld-Methode zur Verfügung (so zum Beispiel alle Actors). Mit dieser Methode holen Sie ein World-Objekt.
Beachten Sie im Beispiel unten, dass wir nicht einen weiteren Actor passieren, sondern die Klasse des Actors, den wir spawnen wollen. In unserem Beispiel kann diese Klasse jede Unterklasse von AMyEnemy sein.
Was, wenn Sie eine Kopie eines anderen Objekts erstellen wollen, wie das beim Instantiieren in Unity möglich ist?
Den Funktionen NewObject und SpawnActor kann auch ein Vorlage-Objekt für die Arbeit übergeben werden. Die Unreal Engine erstellt eine Kopie des Objekts, anstatt ein völlig neues Objekt von Grund auf zu erstellen. Dadurch werden alle seine UPROPERTYs und Komponenten herüberkopiert.
AMyActor* CreateCloneOfMyActor(AMyActor* ExistingActor, FVector SpawnLocation, FRotator SpawnRotation)
{
UWorld* World = ExistingActor->GetWorld();
FActorSpawnParameters SpawnParams;
SpawnParams.Template = ExistingActor;
World->SpawnActor<AMyActor>(ExistingActor->GetClass(), SpawnLocation, SpawnRotation, SpawnParams);
}
Sie fragen sich vielleicht, was „von Grund auf“ in diesem Kontext bedeutet. Jede Objektklasse, die Sie erstellen, hat eine Standardvorlage, die Standardwerte für ihre Eigenschaften und Komponenten enthält. Wenn Sie diese Eigenschaften nicht überschreiben und keine eigene Vorlage bereitstellen, verwendet die Unreal Engine diese Standardwerte für die Konstruktion Ihres Objekts. Um das zu verdeutlichen, schauen wir uns zunächst ein Beispiel für ein MonoBehaviour an:
public class MyComponent : MonoBehaviour
{
public int MyIntProp = 42;
public SphereCollider MyCollisionComp = null;
void Start()
{
// Erstellen Sie die Kollisionskomponente, wenn wir noch keine haben.
if (MyCollisionComp == null)
{
MyCollisionComp = gameObject.AddComponent<SphereCollider>();
MyCollisionComp.center = Vector3.zero;
MyCollisionComp.radius = 20.0f;
}
}
}
Im obigen Beispiel haben wir eine int-Eigenschaft, die standardmäßig auf 42 steht, und eine SphereCollider-Komponente, die standardmäßig einen Radius von 20 hat.
Wir können dies in Unreal erreichen, indem wir den Constructor des Objekts verwenden:
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
UPROPERTY()
int32 MyIntProp;
UPROPERTY()
USphereComponent* MyCollisionComp;
AMyActor()
{
MyIntProp = 42;
MyCollisionComp = CreateDefaultSubobject<USphereComponent>(FName(TEXT("CollisionComponent"));
MyCollisionComp->RelativeLocation = FVector::ZeroVector;
MyCollisionComp->SphereRadius = 20.0f;
}
};
Die Standardwerte für die Eigenschaften der Klasse legen wir im Constructor von AMyActor fest. Beachten Sie die Verwendung der Funktion CreateDefaultSubobject; wir können diese Funktion verwenden, um Komponenten zu erstellen und ihnen Standardeigenschaften zuzuweisen. Alle mit dieser Funktion erstellten Unterobjekte dienen als Standardvorlage, so dass wir sie in einer Subklasse oder einem Blueprint ändern können.
Casting von einem Typ zu einem anderen
In diesem Fall nehmen wir eine Komponente, die wir bereits haben, und casten diese dann auf einen bestimmten Typ, um unter bestimmten Umständen etwas zu tun.
Unity C#
Collider collider = gameObject.GetComponent<Collider>;
SphereCollider sphereCollider = collider as SphereCollider;
if (sphereCollider != null)
{
// ...
}
Unreal Engine C++
UPrimitiveComponent* Primitive = MyActor->GetComponentByClass(UPrimitiveComponent::StaticClass());
USphereComponent* SphereCollider = Cast<USphereComponent>(Primitive);
if (SphereCollider != nullptr)
{
// ...
}
Zerstören von GameObject / Actor
Unity
|
C++
|
Blueprint ![]() |
Zerstören von GameObject / Actor (mit 1 Sekunde Verzögerung)
Unity
|
C++
|
Blueprint
|
Deaktivieren von GameObject / Acor
Unity
|
C++
|
Blueprint
|
Zugriff auf GameObject / Actor aus einer Komponente
Unity
|
C++
|
Blueprint
|
Zugriff auf eine Komponente aus GameObject / Actor
Unity C#
MyComponent MyComp = gameObject.GetComponent<MyComponent>();
Unreal Engine C++
UMyComponent* MyComp = MyActor->FindComponentByClass<UMyComponent>();
Blueprint
Finden von GameObjects / Actors
Unity C#
// GameObject nach Name finden
GameObject MyGO = GameObject.Find("MyNamedGameObject");
// Objekte anhand des Typs finden
MyComponent[] Components = Object.FindObjectsOfType(typeof(MyComponent)) as MyComponent[];
foreach (MyComponent Component in Components)
{
// ...
}
// GameObjects anhand eines Tags finden
GameObject[] GameObjects = GameObject.FindGameObjectsWithTag("MyTag");
foreach (GameObject GO in GameObjects)
{
// ...
}
Unreal Engine C++
// Actor anhand des Namens finden (funktioniert auch bei UObjects)
AActor* MyActor = FindObject<AActor>(nullptr, TEXT("MyNamedActor"));
// Actors anhand des Typs finden (benötigt ein UWorld-Objekt)
for (TActorIterator<AMyActor> It(GetWorld()); It; ++It)
{
AMyActor* MyActor = *It;
// ...
}
Blueprint
Unreal Engine C++
// UObjects anhand des Typs finden
for (TObjectIterator<UMyObject> It; It; ++it)
{
UMyObject* MyObject = *It;
// ...
}
// Actors anhand eines Tags finden (funktioniert auch bei ActorComponents, stattdessen TObjectIterator verwenden)
for (TActorIterator<AActor> It(GetWorld()); It; ++It)
{
AActor* Actor = *It;
if (Actor->ActorHasTag(FName(TEXT("Mytag"))))
{
// ...
}
}
Blueprint
Hinzufügen von Tags zu GameObjects / Actors
Unity C#
MyGameObject.tag = "MyTag";
Unreal Engine C++
// Actors können mehrere Tags haben
MyActor.Tags.AddUnique(TEXT("MyTag"));
Blueprint
Hinzufügen von Tags zu MonoBehaviours / ActorComponents
Unity C#
// Dies ändert das Tag des Spielobjekts, an das es angekoppelt ist
MyComponent.tag = "MyTag";
Unreal Engine C++
// Komponenten besitzen ein eigenes Array von Tags
MyComponent.ComponentTags.AddUnique(TEXT("MyTag"));
Vergleich von Tags für GameObjects / Actors und MonoBehaviours / ActorComponents
Unity C#
if (MyGameObject.CompareTag("MyTag"))
{
// ...
}
// Prüft das Tag des GameObject, an das es angekoppelt ist
if (MyComponent.CompareTag("MyTag"))
{
// ...
}
Unreal Engine C++
// Prüft, ob ein Actor dieses Tag hat
if (MyActor->ActorHasTag(FName(TEXT("MyTag"))))
{
// ...
}
Blueprint
Unreal Engine C++
// Prüft, ob eine ActorComponent dieses Tag hat
if (MyComponent->ComponentHasTag(FName(TEXT("MyTag"))))
{
// ...
}
Blueprint
Physik: RigidBody vs. Grundkörper-Komponente
In Unity müssen Sie, um einem GameObject Physik-Charakteristiken zu verleihen, diesem zuerst eine RigidBody-Komponente zuweisen.
In der Unreal Engine kann jede Grundkörper-Komponente (UPrimitiveComponent in C++) ein physikalisches Objekt sein. Einige häufige Grundkörper-Komponenten sind:
- Form-Komponenten (Kapsel, Kugel und Box)
- Statisches-Mesh-Komponenten
- Skelett-Mesh-Komponenten
Im Gegensatz zu Unity, das die Zuständigkeiten für Kollisionen und Visualisierungen in separate Komponenten aufteilt, vereint die Unreal Engine die Konzepte „potenziell physikalisch“ und „potenziell sichtbar“ in einer einzigen Grundkörper-Komponente. Jede Komponente, die eine beliebige Geometrie in der Welt hat, die entweder gerendert oder mit der physikalisch interagiert werden kann, ist eine Subklasse von PrimitiveComponent.
Ebenen vs. Kanäle
Kollisionskanäle sind das Äquivalent zu Ebenen in Unity in der Unreal Engine. Mehr darüber erfahren Sie unter Collision Filtering (Kollisionsfilterung).
RayCast vs. RayTrace
Unity C#
GameObject FindGOCameraIsLookingAt()
{
Vector3 Start = Camera.main.transform.position;
Vector3 Direction = Camera.main.transform.forward;
float Distance = 100.0f;
int LayerBitMask = 1 << LayerMask.NameToLayer("Pawn");
RaycastHit Hit;
bool bHit = Physics.Raycast(Start, Direction, out Hit, Distance, LayerBitMask);
if (bHit)
{
return Hit.collider.gameObject;
}
return null;
}
Unreal Engine C++
APawn* AMyPlayerController::FindPawnCameraIsLookingAt()
{
// Sie können dies verwenden, um verschiedene Eigenschaften des Traces anzupassen
FCollisionQueryParams Params;
// Ignorieren Sie den Pawn des Spielers
Params.AddIgnoredActor(GetPawn());
// Das Trefferergebnis wird durch den Linien-Trace bevölkert
FHitResult Hit;
// Raycast von der Kamera abgehend, nur mit Pawns kollidieren (sie befinden sich auf dem Kollisionskanal ECC_Pawn)
FVector Start = PlayerCameraManager->GetCameraLocation();
FVector End = Start + (PlayerCameraManager->GetCameraRotation().Vector() * 1000.0f);
bool bHit = GetWorld()->LineTraceSingle(Hit, Start, End, ECC_Pawn, Params);
if (bHit)
{
// Hit.Actor enthält einen schwachen Pointer auf den Actor, den der Trace getroffen hat
return Cast<APawn>(Hit.Actor.Get());
}
return nullptr;
}
Blueprint
Bild für Großansicht anklicken.
Trigger Volumes
Unity C#
public class MyComponent : MonoBehaviour
{
void Start()
{
collider.isTrigger = true;
}
void OnTriggerEnter(Collider Other)
{
// ...
}
void OnTriggerExit(Collider Other)
{
// ...
}
}
Unreal Engine C++
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
// Meine Trigger-Komponente
UPROPERTY()
UPrimitiveComponent* Trigger;
AMyActor()
{
Trigger = CreateDefaultSubobject<USphereComponent>(TEXT("TriggerCollider"));
// Beide Collider müssen auf True gesetzt sein, damit die Events ausgelöst werden
Trigger.bGenerateOverlapEvents = true;
// Kollisionsmodus für den Collider festlegen
// In diesem Modus wird der Collider nur für Raycasts, Sweeps und Überlappungen aktiviert
Trigger.SetCollisionEnabled(ECollisionEnabled::QueryOnly);
}
virtual void NotifyActorBeginOverlap(AActor* Other) override;
virtual void NotifyActorEndOverlap(AActor* Other) override;
};
Unreal Engine Blueprint
Mehr über das Einrichten von Kollisionsreaktionen erfähen Sie auf der Seite Collision.
Kinematic Rigidbodies
Unity C#
public class MyComponent : MonoBehaviour
{
void Start()
{
rigidbody.isKinematic = true;
rigidbody.velocity = transform.forward * 10.0f;
}
}
In der Unreal Engine sind die Kollisionskomponente und die Rigidbody-Komponente eins. Die Basis-Klasse dafür ist UPrimitiveComponent, die viele Subklassen hat (USphereComponent, UCapsuleComponent usw.), um Ihren Bedürfnissen gerecht zu werden.
Unreal Engine C++
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
UPROPERTY()
UPrimitiveComponent* PhysicalComp;
AMyActor()
{
PhysicalComp = CreateDefaultSubobject<USphereComponent>(TEXT("CollisionAndPhysics"));
PhysicalComp->SetSimulatePhysics(false);
PhysicalComp->SetPhysicsLinearVelocity(GetActorRotation().Vector() * 100.0f);
}
};
Input Events
Unity C#
public class MyPlayerController : MonoBehaviour
{
void Update()
{
if (Input.GetButtonDown("Fire"))
{
// ...
}
float Horiz = Input.GetAxis("Horizontal");
float Vert = Input.GetAxis("Vertical");
// ...
}
}
Unreal Engine C++:
UCLASS()
class AMyPlayerController : public APlayerController
{
GENERATED_BODY()
void SetupInputComponent()
{
Super::SetupInputComponent();
InputComponent->BindAction("Fire", IE_Pressed, this, &AMyPlayerController::HandleFireInputEvent);
InputComponent->BindAxis("Horizontal", this, &AMyPlayerController::HandleHorizontalAxisInputEvent);
InputComponent->BindAxis("Vertical", this, &AMyPlayerController::HandleVerticalAxisInputEvent);
}
void HandleFireInputEvent();
void HandleHorizontalAxisInputEvent(float Value);
void HandleVerticalAxisInputEvent(float Value);
};
Blueprint
So könnten die Input-Eigenschaften in Ihren Projekt-Einstellungen aussehen:
Wenn Sie mehr darüber erfahren möchten, wie Sie Input für Ihr Unreal-Engine-Projekt einrichten, lesen Sie die Seite Input.
