本章包含以下内容:
-
在C++中创建一个自定义的Actor
-
使用SpawnActor来实例化Actor
-
创建一个UFUNCTION
-
使用Destroy和计时器来销毁Actor
-
使用SetLifeSpan来使Actor在一段时间后销毁
-
通过组件实现Actor功能
-
使用FObjectFinder读取资源到组件中
-
通过继承实现Actor功能
-
附加组件来创建层次结构
-
创建一个自定义的ActorComponent
-
创建一个自定义的SceneComponent
-
为RPG创建一个InventoryComponent
-
创建一个OrbitingMovementComponent
-
创建一个产生单位的建筑
一、在C++中创建一个自定义的Actor
前面有提到如何创建Actor:
创建完之后可见编译器中出现了文件。
二、使用SpawnActor来实例化Actor
接下来我们在gameMode里面来生成MyActor:
添加BeginPlay()并且调用生成Actor的方法:
运行之后我们看到屏幕打印了提示并且面板上也多了MyActor的实例说明生成成功了。
三、创建一个UFUNCTION
以前面创建的MyActor为例子
添加代码:
我们拖拽一个到地图中并且在关卡蓝图获得其引用:
我们可以看到在蓝图中可以调用这个方法~
UFUNCTION ()实际上是一个 c++ 函数,但是有额外的元数据使它可以被 Blueprints 访问。 这对于允许设计人员访问程序开发人员编写的函数是非常有用的。
四、使用Destroy和计时器来销毁Actor
在GameMode中添加代码:
修改一下生成的函数:
执行之后结果是,生成一个MyActor,10s 后这个Actor销毁掉
五、使用SetLifeSpan来使Actor在一段时间后销毁
MyActor中添加代码:
编译完成后我们拖拽一个MyActor进去场景中
我们会发现这个场景中的所有MyActor 都会在5s之后被销毁掉。
六、通过组件实现Actor功能
没有组件的自定义Actor没有位置,也不能附加到其他Actor身上。 如果没有根组件,则 Actor 没有基转换,因此没有位置。 因此,大多数参与者要求至少有一个组件是有用的。我们可以通过组合创建自定义的 Actors,方法是向 Actor 添加一些组件,其中每个组件提供一些所需的功能。
在MyActor中添加代码:
编译完成后点击场景中的MyActor可以发现他具有了旋转量,位置信息等其他属性 ,这些属性来自于 StaticMeshComponent组件
添加到类声明中的 UPROPERTY 宏是一个指针,用于保存我们用作 Actor 子对象的组件:
使用 UPROPERTY ()宏可以确保在指针中声明的对象被认为是引用的,并且不会在我们没操作时被垃圾收集(即删除) ,从而留下悬空的指针。
七、使用FObjectFinder读取资源到组件中
资源引用信息查找的方法:
这边首先开启一下引擎内容的显示
在内容浏览器找到cube
右键菜单点击复制引用 即可复制 StaticMesh’/Engine/BasicShapes/Cube.Cube’ 剪切板
在MyActor中添加代码:
我们可以看到C++类的样式就变成了Cube的样子:
总结:
我们创建 FObjectFinder 类的一个实例,传入我们试图加载的资源类型作为模板参数。
FObjectFinder是一个帮助我们加载资源的类模板。 当我们构造它时,我们传入一个字符串,该字符串包含我们试图加载的资源的路径。
字符串的格式为"{ ObjectType }’/path/to/Asset.Asset’” 注意字符串中单引号的使用。
要获取编辑器中已经存在的资产的字符串,可以在 Content Browser 中右键单击该资产并选择 Copy Reference。 这样你就可以把字符串粘贴到你的代码中.
我们使用来自 c++ 11的 auto 关键字,以避免在它的声明中输入我们的整个对象类型; 编译器为我们推断出它。 如果没有 auto,我们使用以下代码:
可以做一些对比。
FObjectFinder类有一个名为 Object 的属性,该属性要么具有指向所需资源的指针,要么在找不到该资源时为 NULL。
这意味着我们可以根据 nullptr 检查它,如果它不是 null,那么使用 SetStaticMesh 将它分配给 Mesh。
八、通过继承实现Actor功能
很容易理解:
- 使用继承来创建一个新的Actor类那么这个类可以实现父类的功能
- 使用重写可以覆盖原来的功能进行改造
- 继承是实现自定义 Actor 的第二种方法。 这通常用于创建一个新的子类,该子类将成员变量、函数或组件添加到现有的 Actor 类中。
九、附加组件来创建层次结构
在从组件创建自定义 Actors 时,重要的是要考虑附加的概念。 附加组件会创建一种关系,其中应用于父组件的转换也会影响附加到父组件的组件。
创建一个新的Actor:
添加代码:
查看编辑器:
验证 Actor 是否具有层次结构中的组件,以及第二个框的大小是否更小:
十、创建一个自定义的ActorComponent
ActorComponent是实现应该在Actor之间共享的公共功能的简单方法。 Actor组件不会被呈现,但是仍然可以执行诸如订阅事件或者与Actor内部的其他组件通信之类的操作。
创建一个ActorComponent:
添加代码:
打开编辑器,然后,在 Details 选项卡 将 Transform 组件的 Mobility 属性设置为 Moveable:
随便创建一个空的Actor添加组件 一个Cube和自定义组件
运行之后我们就会看到这个Actor随机移动的现象。
十一、创建一个自定义的SceneComponent
SceneComponent 具有好几种作用:
-
用来标定位置
-
用来装载其他组件
-
实现比较多的组合功能
我们创建一个SceneComponent用来生成Actor:
添加代码:
编译之后打开编辑器,将组件添加进Actor ,并且设置生成目标类:
使用蓝图调用方法:
运行之后可见:
一个之前创建过的NewCreateActor生成了出来。
十二、为RPG创建一个InventoryComponent(背包组件)
这个例子中会涉及创建的类会比较多,所以新建一个文件夹(RPG)来创建这些类:
创建新C++类:
添加代码:
InventoryActor.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Engine/StaticMeshActor.h"
#include "InventoryActor.generated.h"
/**
*
*/
UCLASS()
class NEWTUTORIALPROJECT_API AInventoryActor : public AStaticMeshActor
{
GENERATED_BODY()
public:
AInventoryActor();
virtual void PickUp();
virtual void PutDown(FTransform TargetLocation);
};
InventoryActor.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "InventoryActor.h"
#include "ConstructorHelpers.h"
#include "Engine/CollisionProfile.h"
#include "Components/StaticMeshComponent.h"
AInventoryActor::AInventoryActor():Super()
{
PrimaryActorTick.bCanEverTick = true;
auto MeshAsset = ConstructorHelpers::FObjectFinder<UStaticMesh>(TEXT("StaticMesh'/Engine/BasicShapes/Cube.Cube'"));
if (MeshAsset.Object != nullptr)
{
GetStaticMeshComponent()->SetStaticMesh(MeshAsset.Object);
GetStaticMeshComponent()->SetCollisionProfileName(UCollisionProfile::Pawn_ProfileName);
}
GetStaticMeshComponent()->SetMobility(EComponentMobility::Movable);
SetActorEnableCollision(true);
}
void AInventoryActor::PickUp()
{
SetActorTickEnabled(false);
SetActorHiddenInGame(true);
SetActorEnableCollision(false);
}
void AInventoryActor::PutDown(FTransform TargetLocation)
{
SetActorTickEnabled(true);
SetActorHiddenInGame(false);
SetActorEnableCollision(true);
SetActorLocation(TargetLocation.GetLocation());
}
InventoryComponent.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "InventoryActor.h"
#include "InventoryComponent.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class NEWTUTORIALPROJECT_API UInventoryComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UInventoryComponent();
UPROPERTY()
TArray<AInventoryActor*> CurrentInventory;
UFUNCTION()
int32 AddToInventory(AInventoryActor* ActorToAdd);
UFUNCTION()
void RemoveFromInventory(AInventoryActor* ActorToRemove);
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
};
InventoryComponent.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "InventoryComponent.h"
// Sets default values for this component's properties
UInventoryComponent::UInventoryComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
// ...
}
int32 UInventoryComponent::AddToInventory(AInventoryActor* ActorToAdd)
{
return CurrentInventory.Add(ActorToAdd);
}
void UInventoryComponent::RemoveFromInventory(AInventoryActor* ActorToRemove)
{
CurrentInventory.Remove(ActorToRemove);
}
// Called when the game starts
void UInventoryComponent::BeginPlay()
{
Super::BeginPlay();
// ...
}
// Called every frame
void UInventoryComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// ...
}
InventoryCharacter.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "InventoryActor.h"
#include "InventoryComponent.h"
#include "InventoryCharacter.generated.h"
UCLASS()
class NEWTUTORIALPROJECT_API AInventoryCharacter : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AInventoryCharacter();
UPROPERTY()
UInventoryComponent* MyInventory;
UFUNCTION()
void DropItem();
UFUNCTION()
void TakeItem(AInventoryActor* InventoryItem);
UFUNCTION()
virtual void NotifyHit(class UPrimitiveComponent* MyComp, AActor* Other, class UPrimitiveComponent* OtherComp,bool bSelfMoved, FVector HitLocation, FVector HitNormal, FVector NormalImpulse, const FHitResult&Hit) override;
UFUNCTION()
void MoveForward(float AxisValue);
UFUNCTION()
void MoveRight(float AxisValue);
UFUNCTION()
void PitchCamera(float AxisValue);
UFUNCTION()
void YawCamera(float AxisValue);
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
private:
FVector MovementInput;
FVector CameraInput;
};
InventoryCharacter.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "InventoryCharacter.h"
#include "Components/InputComponent.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/CharacterMovementComponent.h"
// Sets default values
AInventoryCharacter::AInventoryCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
MyInventory = CreateDefaultSubobject<UInventoryComponent>("MyInventory");
}
void AInventoryCharacter::DropItem()
{
if (MyInventory->CurrentInventory.Num() == 0)
{
return;
}
AInventoryActor* Item = MyInventory->CurrentInventory.Last();
MyInventory->RemoveFromInventory(Item);
FVector ItemOrigin;
FVector ItemBounds;
Item->GetActorBounds(false, ItemOrigin, ItemBounds);
FTransform PutDownLocation = GetTransform() + FTransform(RootComponent->GetForwardVector() * ItemBounds.GetMax());
Item->PutDown(PutDownLocation);
}
void AInventoryCharacter::TakeItem(AInventoryActor* InventoryItem)
{
InventoryItem->PickUp();
MyInventory->AddToInventory(InventoryItem);
}
void AInventoryCharacter::NotifyHit(class UPrimitiveComponent* MyComp, AActor* Other, class UPrimitiveComponent* OtherComp, bool bSelfMoved, FVector HitLocation, FVector HitNormal, FVector NormalImpulse, const FHitResult&Hit)
{
AInventoryActor* InventoryItem = Cast<AInventoryActor>(Other);
if (InventoryItem != nullptr)
{
TakeItem(InventoryItem);
}
}
void AInventoryCharacter::MoveForward(float AxisValue)
{
MovementInput.X = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
}
void AInventoryCharacter::MoveRight(float AxisValue)
{
MovementInput.Y = FMath::Clamp<float>(AxisValue, -1.0f, 1.0f);
}
void AInventoryCharacter::PitchCamera(float AxisValue)
{
CameraInput.Y = AxisValue;
}
void AInventoryCharacter::YawCamera(float AxisValue)
{
CameraInput.X = AxisValue;
}
// Called when the game starts or when spawned
void AInventoryCharacter::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void AInventoryCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
if (!MovementInput.IsZero())
{
MovementInput *= 100;
//Scale our movement input axis values by 100 units
// per second
FVector InputVector = FVector(0, 0, 0);
InputVector += GetActorForwardVector()* MovementInput.X *DeltaTime;
InputVector += GetActorRightVector()* MovementInput.Y *DeltaTime;
/* GEngine->AddOnScreenDebugMessage(-1, 1,
FColor::Red,
FString::Printf(TEXT("x- %f, y - %f, z - %f"),
InputVector.X, InputVector.Y, InputVector.Z)); */
AddMovementInput(InputVector);
}
if (!CameraInput.IsNearlyZero())
{
FRotator NewRotation = GetActorRotation();
NewRotation.Pitch += CameraInput.Y;
NewRotation.Yaw += CameraInput.X;
APlayerController* MyPlayerController =Cast<APlayerController>(GetController());
if (MyPlayerController != nullptr)
{
MyPlayerController->AddYawInput(CameraInput.X);
MyPlayerController->AddPitchInput(CameraInput.Y);
}
SetActorRotation(NewRotation);
}
}
// Called to bind functionality to input
void AInventoryCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAction("DropItem",EInputEvent::IE_Pressed, this,&AInventoryCharacter::DropItem);
// Movement
PlayerInputComponent->BindAxis("MoveForward", this,&AInventoryCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this,&AInventoryCharacter::MoveRight);
PlayerInputComponent->BindAxis("CameraPitch", this,&AInventoryCharacter::PitchCamera);
PlayerInputComponent->BindAxis("CameraYaw", this,&AInventoryCharacter::YawCamera);
}
InventotyGameMode.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "InventoryCharacter.h"
#include "InventoryGameMode.generated.h"
/**
*
*/
UCLASS()
class NEWTUTORIALPROJECT_API AInventoryGameMode : public AGameModeBase
{
GENERATED_BODY()
public:
AInventoryGameMode();
};
InventotyGameMode.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "InventoryGameMode.h"
AInventoryGameMode::AInventoryGameMode()
{
DefaultPawnClass = AInventoryCharacter::StaticClass();
}
项目设置中设置按键输入:
在场景中摆放2个InventoryActor,
运行程序之后我们可以移动角色去触碰2个方块,可以看到方块被捡起,而按下E则可以将之前捡起的方块又放到场景中:
十三、创建一个OrbitingMovementComponent
这个例子中我们会创建一个轨道运动的组件
这个组件类似于 RotatingMovementComponent,因为它的设计目的是使用特定的方式移动它所包含的组件。 在这种情况下,它将移动任何附加组件在一个轨道上围绕一个固定的点在一个固定的距离。
例如,一个盾牌,围绕一个角色旋转的动作。
创建新C++类:
添加代码:
OrbitingMovementComponent.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Components/SceneComponent.h"
#include "OrbitingMovementComponent.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class NEWTUTORIALPROJECT_API UOrbitingMovementComponent : public USceneComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UOrbitingMovementComponent();
UPROPERTY()
bool RotateToFaceOutwards;
UPROPERTY()
float RotationSpeed;
UPROPERTY()
float OrbitDistance;
float CurrentValue;
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
};
OrbitingMovementComponent.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "OrbitingMovementComponent.h"
// Sets default values for this component's properties
UOrbitingMovementComponent::UOrbitingMovementComponent()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = true;
// ...
RotationSpeed = 5;
OrbitDistance = 100;
CurrentValue = 0;
RotateToFaceOutwards = true;
}
// Called when the game starts
void UOrbitingMovementComponent::BeginPlay()
{
Super::BeginPlay();
// ...
}
// Called every frame
void UOrbitingMovementComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// ...
float CurrentValueInRadians = FMath::DegreesToRadians<float>(CurrentValue);
SetRelativeLocation(FVector(OrbitDistance * FMath::Cos(CurrentValueInRadians), OrbitDistance * FMath::Sin(CurrentValueInRadians), RelativeLocation.Z));
if (RotateToFaceOutwards)
{
FVector LookDir = (RelativeLocation).GetSafeNormal(); FRotator LookAtRot = LookDir.Rotation(); SetRelativeRotation(LookAtRot);
}
CurrentValue = FMath::Fmod(CurrentValue + (RotationSpeed *DeltaTime), 360);
}
我们可以创建一个空的蓝图Actor来进行测试:
运行后结果如下:
我们可以看到两个方块绕某个点在旋转
十四、创建一个产生单位的建筑
创建两个Actor:
添加代码:
NewCreateActor.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/StaticMeshComponent.h"
#include "Particles/ParticleSystemComponent.h"
#include "Engine/EngineTypes.h"
#include "NewCreateActor.generated.h"
UCLASS()
class NEWTUTORIALPROJECT_API ANewCreateActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ANewCreateActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UPROPERTY()
UStaticMeshComponent* BuildingMesh;
UPROPERTY()
UParticleSystemComponent* SpawnPoint;
UPROPERTY()
UClass* UnitToSpawn;
UPROPERTY()
float SpawnInterval;
UFUNCTION()
void SpawnUnit();
UFUNCTION()
void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
UPROPERTY()
FTimerHandle SpawnTimerHandle;
};
NewCreateActor.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "NewCreateActor.h"
#include "ConstructorHelpers.h"
#include "BarracksUnit.h"
// Sets default values
ANewCreateActor::ANewCreateActor()
{
// 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;
BuildingMesh = CreateDefaultSubobject<UStaticMeshComponent>("BuildingMesh");
SpawnPoint = CreateDefaultSubobject<UParticleSystemComponent>("SpawnPoint");
SpawnInterval = 10;
auto MeshAsset = ConstructorHelpers::FObjectFinder<UStaticMesh>(TEXT("StaticMesh'/Engine/BasicShapes/Cube.Cube'"));
if (MeshAsset.Object != nullptr)
{
BuildingMesh->SetStaticMesh(MeshAsset.Object);
}
auto ParticleSystem =ConstructorHelpers::FObjectFinder<UParticleSystem>(TEXT("ParticleSystem'/Engine/Tutorial/SubEditors/TutorialAssets/TutorialParticleSystem.TutorialParticleSystem'"));
if (ParticleSystem.Object != nullptr)
{
SpawnPoint->SetTemplate(ParticleSystem.Object);
}
SpawnPoint->SetRelativeScale3D(FVector(0.5, 0.5, 0.5));
UnitToSpawn = ABarracksUnit::StaticClass();
}
// Called when the game starts or when spawned
void ANewCreateActor::BeginPlay()
{
Super::BeginPlay();
RootComponent = BuildingMesh;
SpawnPoint->AttachToComponent(RootComponent,FAttachmentTransformRules::SnapToTargetIncludingScale);
SpawnPoint->SetRelativeLocation(FVector(150, 0, 0));
GetWorld()->GetTimerManager().SetTimer(SpawnTimerHandle, this, &ANewCreateActor::SpawnUnit, SpawnInterval, true);
}
// Called every frame
void ANewCreateActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ANewCreateActor::SpawnUnit()
{
FVector SpawnLocation = SpawnPoint->GetComponentLocation();
GetWorld()->SpawnActor(UnitToSpawn, &SpawnLocation);
}
void ANewCreateActor::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
Super::EndPlay(EndPlayReason);
GetWorld()->GetTimerManager().ClearTimer(SpawnTimerHandle);
}
BarracksUnit.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Particles/ParticleSystemComponent.h"
#include "BarracksUnit.generated.h"
UCLASS()
class NEWTUTORIALPROJECT_API ABarracksUnit : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ABarracksUnit();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UPROPERTY()
UParticleSystemComponent* VisualRepresentation;
};
BarracksUnit.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "BarracksUnit.h"
#include "ConstructorHelpers.h"
// Sets default values
ABarracksUnit::ABarracksUnit()
{
// 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;
VisualRepresentation =CreateDefaultSubobject<UParticleSystemComponent>("SpawnPoint");
auto ParticleSystem =ConstructorHelpers::FObjectFinder<UParticleSystem>(TEXT("ParticleSystem'/Engine/Tutorial/SubEditors/TutorialAssets/TutorialParticleSystem.TutorialParticleSystem'"));
if (ParticleSystem.Object != nullptr)
{
VisualRepresentation->SetTemplate(ParticleSystem.Object);
}
VisualRepresentation->SetRelativeScale3D(FVector(0.5, 0.5, 0.5));
SpawnCollisionHandlingMethod =ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
}
// Called when the game starts or when spawned
void ABarracksUnit::BeginPlay()
{
Super::BeginPlay();
VisualRepresentation->AttachToComponent(RootComponent, FAttachmentTransformRules::SnapToTargetIncludingScale);
}
// Called every frame
void ABarracksUnit::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
SetActorLocation(GetActorLocation() + FVector(10, 0, 0));
}
运行结果如下: