本页面介绍了一些高级虚幻引擎编程概念。下面的说明假定你熟悉Unity C#并希望学习虚幻引擎C++。话虽如此,但在大部分情况下,你还可以使用蓝图并获得相同结果,因此我们尽可能同时添加了C++和蓝图的示例。
现在来看一些最常见的Gameplay编程模式,以及如何在虚幻引擎中实现它们。下面的示例介绍了一些常见Unity函数,以及如何在虚幻引擎中实现相同功能。
实例化GameObject/生成Actor
在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";
}
虚幻引擎有两个不同的函数来实例化对象:
NewObject用于创建新的UObject类型。SpawnActor用于生成AActor类型。
UObject和NewObject
在虚幻引擎中创建 UObject 的子类与在Unity中创建 ScriptableObject 的子类非常类似。对于不需要生成到世界中或像Actor那样绑定了组件的Gameplay类,这些很有用。
在Unity中,如果你创建自己的 ScriptableObject 子类,你会如下所示将其实例化:
MyScriptableObject NewSO = ScriptableObject.CreateInstance<MyScriptableObject>();
在虚幻引擎中,如果你创建自己的 UObject 派生类型,你可以如下所示将其实例化:
UMyObject* NewObj = NewObject<UMyObject>();
AActor和SpawnActor
Actor使用World(C++中的 UWorld)对象上的 SpawnActor 方法生成。一些UObject提供了 GetWorld 方法(例如,所有Actor都如此)。你会采用此方法获取World对象。
请注意,在下面的示例中,我们传入了我们想生成的Actor的类,而不是传入另一个Actor。在我们的示例中,该类可以是AMyEnemy的任意子类。
要是你想创建另一个对象的副本,就像Unity中的Instantiate函数那样,你该怎么做呢?
NewObject 和 SpawnActor 函数也能给一个"模板"对象来工作。虚幻引擎将创建该对象的副本,而不是从头创建新对象。这将复制其所有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);
}
你可能会好奇这里的“从头开始”到底是什么意思。你创建的每个对象类都有一个默认模板,其中包含其属性和组件的默认值。如果你不重载这些属性,也没有提供你自己的模板,虚幻引擎将使用这些默认值来构造你的对象。为了更好地说明这一点,我们首先来看一下MonoBehaviour示例:
public class MyComponent : MonoBehaviour
{
public int MyIntProp = 42;
public SphereCollider MyCollisionComp = null;
void Start()
{
// 如果尚无碰撞组件,则进行创建
if (MyCollisionComp == null)
{
MyCollisionComp = gameObject.AddComponent<SphereCollider>();
MyCollisionComp.center = Vector3.zero;
MyCollisionComp.radius = 20.0f;
}
}
}
在上面的示例中,我们有一个默认为42的 int 属性,以及默认为半径20的 SphereCollider 组件。
我们可以在虚幻引擎中使用对象的构造函数实现相同的效果:
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)
{
// ...
}
虚幻引擎C++
UPrimitiveComponent* Primitive = MyActor->GetComponentByClass(UPrimitiveComponent::StaticClass());
USphereComponent* SphereCollider = Cast<USphereComponent>(Primitive);
if (SphereCollider != nullptr)
{
// ...
}
销毁GameObject/Actor
Unity
|
C++
|
蓝图 ![]() |
销毁GameObject/Actor(延迟1秒)
Unity
|
C++
|
蓝图
|
禁用GameObject/Actor
Unity
|
C++
|
蓝图
|
从组件访问GameObject/Actor
Unity
|
C++
|
蓝图
|
从GameObject/Actor访问组件
Unity C#
MyComponent MyComp = gameObject.GetComponent<MyComponent>();
虚幻引擎C++
UMyComponent* MyComp = MyActor->FindComponentByClass<UMyComponent>();
蓝图
查找GameObject/Actor
Unity C#
// 按名称查找GameObject
GameObject MyGO = GameObject.Find("MyNamedGameObject");
// 按类型查找Object
MyComponent[] Components = Object.FindObjectsOfType(typeof(MyComponent)) as MyComponent[];
foreach (MyComponent Component in Components)
{
// ...
}
// 按标签查找GameObject
GameObject[] GameObjects = GameObject.FindGameObjectsWithTag("MyTag");
foreach (GameObject GO in GameObjects)
{
// ...
}
虚幻引擎C++
// 按名称查找Actor(也适用于UObject)
AActor* MyActor = FindObject<AActor>(nullptr, TEXT("MyNamedActor"));
// 按类型查找Actor(需要UWorld对象)
for (TActorIterator<AMyActor> It(GetWorld()); It; ++It)
{
AMyActor* MyActor = *It;
// ...
}
蓝图
虚幻引擎C++
// 按类型查找UObject
for (TObjectIterator<UMyObject> It; It; ++it)
{
UMyObject* MyObject = *It;
// ...
}
// 按标签查找Actor(也适用于ActorComponent,需要改用TObjectIterator)
for (TActorIterator<AActor> It(GetWorld()); It; ++It)
{
AActor* Actor = *It;
if (Actor->ActorHasTag(FName(TEXT("Mytag"))))
{
// ...
}
}
蓝图
向GameObject/Actor添加标签
Unity C#
MyGameObject.tag = "MyTag";
虚幻引擎C++
// Actor可以有多个标签
MyActor.Tags.AddUnique(TEXT("MyTag"));
蓝图
向MonoBehaviour/ActorComponent添加标签
Unity C#
// 这会更改它绑定到的GameObject上的标签
MyComponent.tag = "MyTag";
虚幻引擎C++
// 组件有自己的标签数组
MyComponent.ComponentTags.AddUnique(TEXT("MyTag"));
比较GameObject/Actor和MonoBehaviour/ActorComponent上的标签
Unity C#
if (MyGameObject.CompareTag("MyTag"))
{
// ...
}
// 检查它绑定到的GameObject上的标签
if (MyComponent.CompareTag("MyTag"))
{
// ...
}
虚幻引擎C++
// 检查某个Actor是否有此标签
if (MyActor->ActorHasTag(FName(TEXT("MyTag"))))
{
// ...
}
蓝图
虚幻引擎C++
// 检查某个ActorComponent是否有此标签
if (MyComponent->ComponentHasTag(FName(TEXT("MyTag"))))
{
// ...
}
蓝图
物理:刚体与图元组件
在Unity中,假如要为GameObject赋予物理特征,首先必须为其提供刚体组件。
在虚幻引擎中,任何图元组件(C++中的 UPrimitiveComponent)都可以是物理对象。一些常见图元组件如下:
- 形状组件(胶囊体、球体和盒体)
- 静态网格体组件
- 骨骼网格体组件
Unity将碰撞和可视性划分到不同的组件中,虚幻引擎则将"潜在的物理碰撞"和"潜在的可视效果"组合到了单个图元组件中。凡是在世界中具有几何体的组件,只要能通过物理方式渲染或交互,都是 PrimitiveComponent 的子类。
层与通道
虚幻引擎中的碰撞通道对应Unity中的层。如需详细信息,请参阅碰撞过滤。
RayCast与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;
}
虚幻引擎C++
APawn* AMyPlayerController::FindPawnCameraIsLookingAt()
{
// 你可以在这里自定义有关追踪的各种属性
FCollisionQueryParams Params;
// 忽略玩家的Pawn
Params.AddIgnoredActor(GetPawn());
// 击中结果由线路追踪填充
FHitResult Hit;
// 来自摄像机的光线投射,仅与Pawn碰撞(它们在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包含指向追踪所击中的Actor的弱指针
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)
{
// ...
}
}
虚幻引擎C++
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()
// 我的触发器组件
UPROPERTY()
UPrimitiveComponent* Trigger;
AMyActor()
{
Trigger = CreateDefaultSubobject<USphereComponent>(TEXT("TriggerCollider"));
// 两个碰撞物都需要将此项设置为true,才能触发事件
Trigger.bGenerateOverlapEvents = true;
// 设置碰撞物的碰撞模式
// 此模式仅为光线投射、扫描和重叠启用碰撞物
Trigger.SetCollisionEnabled(ECollisionEnabled::QueryOnly);
}
virtual void NotifyActorBeginOverlap(AActor* Other) override;
virtual void NotifyActorEndOverlap(AActor* Other) override;
};
虚幻引擎蓝图
如需详细了解如何设置碰撞响应,请参阅碰撞页面。
刚体运动
Unity C#
public class MyComponent : MonoBehaviour
{
void Start()
{
rigidbody.isKinematic = true;
rigidbody.velocity = transform.forward * 10.0f;
}
}
在虚幻引擎中,碰撞组件和刚体组件是同一个组件。其基类是 UPrimitiveComponent ,它有许多子类(USphereComponent 、 UCapsuleComponent 等)可满足你的需要。
虚幻引擎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");
// ...
}
}
虚幻引擎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);
};
蓝图
你的项目设置中的输入属性可能如下所示:
如需详细了解如何设置虚幻引擎项目的输入,请参阅输入页面。
