このページでは、Unreal Engine の先進的なプログラミング コンセプトについて説明します。以下の説明は、Unity の C# の経験がある上で Unreal Engine の C++ を学ぶことを前提にしています。多くの場合、ブループリントを使って同じ結果が得られるため、可能な限り C++ とブループリントの両方の例を挙げています。
よく使われるゲームプレイ プログラミング パターンと、Unreal Engine でそれをどのように実行するかについて説明します。以下に、Unity でよく使う関数と、Unreal Engine で同じ機能を実装する方法の例を挙げます。
ゲームオブジェクトをインスタンス化する / アクタをスポーンする
Unity では Instantiate
関数を使ってオブジェクトのインスタンスを新たに作成します。この関数は UnityEngine.Object
型 (GameObject や MonoBehaviour など) を受け取り、コピーを作ります。
public GameObject EnemyPrefab;
public Vector3 SpawnPosition;
public Quaternion SpawnRotation;
void Start()
{
GameObject NewGO = (GameObject)Instantiate(EnemyPrefab, SpawnPosition, SpawnRotation);
NewGO.name = "MyNewGameObject";
}
Unreal Engine でオブジェクトをインスタンス化するには次の 2 つの関数があります。
NewObject
は新しくUObject
型を作成します。SpawnActor
はAActor
型をスポーンします。
UObject と NewObject
Unreal Engine の UObject
のサブクラス化と Unity の ScriptableObject
のサブクラス化はよく似ています。ワールドへスポーンする必要がなくアクタのようにアタッチされたコンポーネントも必要ないゲームプレイ クラスに便利に利用できます。
Unity で ScriptableObject
の独自のサブクラスを作成する場合、以下のようにインスタンス化します。
MyScriptableObject NewSO = ScriptableObject.CreateInstance<MyScriptableObject>();
Unreal Engine で独自の UObject
派生型を作成する場合、以下のようにインスタンス化します。
UMyObject* NewObj = NewObject<UMyObject>();
AActors と SpawnActor
アクタは SpawnActor
メソッドでワールド オブジェクト (C++ では UWorld
) にスポーンされます。一部の UObject には GetWorld
メソッドが用意されています (たとえば、GetWorld
メソッドはすべてのアクタで用意されています)。この方法でワールド オブジェクトを取得できます。
以下の例では、もう一つのアクタを渡す代わりに、スポーンしたいアクタのクラスを渡します。 この例では、クラスは AMyEnemy の任意のサブクラスです。
Unity で Instantiate を使用すればできるように、別のオブジェクトのコピーを作る場合はどうすればよいでしょうか。
NewObject
関数および SpawnActor
関数と使用できるテンプレート オブジェクトがあります。Unreal Engine は、新規オブジェクトをゼロから作成するのではなく、テンプレート オブジェクトをコピーして作成します。このとき UPROPERTY とコンポーネントがすべてコピーされます。
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);
}
この場合の「ゼロから」とはどういうことか疑問に思われるかもしれません。作成する各オブジェクト クラスには、プロパティとコンポーネントのデフォルト値を含むデフォルトのテンプレートがあります。それらのプロパティを書き換えたり独自のテンプレートを提供したりしなければ、Unreal Engine はデフォルト値を使ってオブジェクトを構築します。これを説明するために、MonoBehaviour の例を示します。
public class MyComponent : MonoBehaviour
{
public int MyIntProp = 42;
public SphereCollider MyCollisionComp = null;
void Start()
{
// Create the collision component if we don't already have one
if (MyCollisionComp == null)
{
MyCollisionComp = gameObject.AddComponent<SphereCollider>();
MyCollisionComp.center = Vector3.zero;
MyCollisionComp.radius = 20.0f;
}
}
}
上記の例には、デフォルト値が 42 の int
プロパティとデフォルト値が半径 20 の SphereCollider
コンポーネントがあります。
Unreal ではオブジェクトのコンストラクタを使って同じものを実現できます。
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;
}
};
AMyActor
のコンストラクタで、クラスにデフォルトのプロパティ値を設定しています。CreateDefaultSubobject
関数を使っていることに注意してください。この関数を使うと、コンポーネントを作成してデフォルト プロパティを割り当てることができます。この関数で作成したサブオブジェクト全体がデフォルト テンプレートとして機能し、サブクラスかブループリントで修正できます。
ある型から別の型にキャストする
この場合、あると分かっているコンポーネントを取得して、それを特定の型にキャストし、条件付きで何かを実行します。
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)
{
// ...
}
ゲームオブジェクト / アクタを破壊する
Unity
|
C++
|
ブループリント ![]() |
ゲームオブジェクト / アクタを破壊する (遅延 1 秒)
Unity
|
C++
|
ブループリント ![]() |
ゲームオブジェクト / アクタを無効化する
Unity
|
C++
|
ブループリント ![]() |
コンポーネントからゲームオブジェクト / アクタにアクセスする
Unity
|
C++
|
ブループリント ![]() |
ゲームオブジェクト / アクタからコンポーネントにアクセスする
Unity C#
MyComponent MyComp = gameObject.GetComponent<MyComponent>();
Unreal Engine C++
UMyComponent* MyComp = MyActor->FindComponentByClass<UMyComponent>();
ブループリント

ゲームオブジェクト / アクタを検索する
Unity C#
// ゲームオブジェクトを名前で検索します
GameObject MyGO = GameObject.Find("MyNamedGameObject");
// Find Objects by type
MyComponent[] Components = Object.FindObjectsOfType(typeof(MyComponent)) as MyComponent[];
foreach (MyComponent Component in Components)
{
// ...
}
// Find GameObjects by tag
GameObject[] GameObjects = GameObject.FindGameObjectsWithTag("MyTag");
foreach (GameObject GO in GameObjects)
{
// ...
}
Unreal Engine C++
// Find Actor by name (also works on UObjects)
AActor* MyActor = FindObject<AActor>(nullptr, TEXT("MyNamedActor"));
// Find Actors by type (needs a UWorld object)
for (TActorIterator<AMyActor> It(GetWorld()); It; ++It)
{
AMyActor* MyActor = *It;
// ...
}
ブループリント

Unreal Engine C++
// Find UObjects by type
for (TObjectIterator<UMyObject> It; It; ++it)
{
UMyObject* MyObject = *It;
// ...
}
// Find Actors by tag (also works on ActorComponents, use TObjectIterator instead)
for (TActorIterator<AActor> It(GetWorld()); It; ++It)
{
AActor* Actor = *It;
if (Actor->ActorHasTag(FName(TEXT("Mytag"))))
{
// ...
}
}
ブループリント

ゲームオブジェクト / アクタにタグを追加する
Unity C#
MyGameObject.tag = "MyTag";
Unreal Engine C++
// Actors can have multiple tags
MyActor.Tags.AddUnique(TEXT("MyTag"));
ブループリント

MonoBehaviour / ActorComponent にタグを追加する
Unity C#
// This changes the tag on the GameObject it is attached to
MyComponent.tag = "MyTag";
Unreal Engine C++
// Components have their own array of tags
MyComponent.ComponentTags.AddUnique(TEXT("MyTag"));
ゲームオブジェクト / アクタと MonoBehaviour / ActorComponent のタグの比較
Unity C#
if (MyGameObject.CompareTag("MyTag"))
{
// ...
}
// Checks the tag on the GameObject it is attached to
if (MyComponent.CompareTag("MyTag"))
{
// ...
}
Unreal Engine C++
// Checks if an Actor has this tag
if (MyActor->ActorHasTag(FName(TEXT("MyTag"))))
{
// ...
}
ブループリント

Unreal Engine C++
// Checks if an ActorComponent has this tag
if (MyComponent->ComponentHasTag(FName(TEXT("MyTag"))))
{
// ...
}
ブループリント

物理:リジッドボディとプリミティブ コンポーネントの比較
Unity でゲームオブジェクトに物理特性を与えるには、まずリジッドボディ コンポーネントを与える必要があります。
Unreal Engine ではプリミティブ コンポーネント (C++ では UPrimitiveComponent
) が物理オブジェクトになります。以下に一般的なプリミティブ コンポーネントを挙げます。
- 形状コンポーネント (カプセル、球体、ボックス)
- スタティックメッシュ コンポーネント
- スケルタルメッシュ コンポーネント
コリジョンやビジュアライゼーションの役割が別々のコンポーネントに分かれている Unity とは異なり、Unreal Engine では「物理化」と「可視化」が 1 つのプリミティブ コンポーネントにまとまっています。ワールドで形状を持ち、レンダリングされたり物理的にやり取りしたりするコンポーネントは、PrimitiveComponent
のサブクラスです。
レイヤーとチャンネルの比較
Unreal Engine のコリジョン チャンネルは Unity のレイヤーに相当します。詳細については、「コリジョン フィルタリング」を参照してください。
レイキャストとレイトレースの比較
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()
{
// You can use this to customize various properties about the trace
FCollisionQueryParams Params;
// Ignore the player's pawn
Params.AddIgnoredActor(GetPawn());
// The hit result gets populated by the line trace
FHitResult Hit;
// Raycast out from the camera, only collide with pawns (they are on the ECC_Pawn collision channel)
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 contains a weak pointer to the Actor that the trace hit
return Cast<APawn>(Hit.Actor.Get());
}
return nullptr;
}
ブループリント
画像をクリックするとフル サイズで表示されます。
トリガー ボリューム
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()
// My trigger component
UPROPERTY()
UPrimitiveComponent* Trigger;
AMyActor()
{
Trigger = CreateDefaultSubobject<USphereComponent>(TEXT("TriggerCollider"));
// Both colliders need to have this set to true for events to fire
Trigger.bGenerateOverlapEvents = true;
// Set the collision mode for the collider
// This mode will only enable the collider for raycasts, sweeps, and overlaps
Trigger.SetCollisionEnabled(ECollisionEnabled::QueryOnly);
}
virtual void NotifyActorBeginOverlap(AActor* Other) override;
virtual void NotifyActorEndOverlap(AActor* Other) override;
};
Unreal Engine ブループリント

コリジョン反応の設定の詳細については、「コリジョン」ページを参照してください。
キネマティック リジッドボディ
Unity C#
public class MyComponent : MonoBehaviour
{
void Start()
{
rigidbody.isKinematic = true;
rigidbody.velocity = transform.forward * 10.0f;
}
}
Unreal Engine ではコリジョン コンポーネントとリジッドボディ コンポーネントが一つになっています。この基本クラスは UPrimitiveComponent
であり、ニーズに合うように多数のサブクラス (USphereComponent
や UCapsuleComponent
など) があります。
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);
}
};
入力イベント
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);
};
ブループリント

以下は、プロジェクト設定で入力プロパティがどのように表示されるかを示します。

Unreal Engine プロジェクトの入力のセットアップ方法については、「Input」を参照してください。