在继续学习新的内容时仅针对学过的章节做一点总结,最好是比较一下Unity3D。不对这两种引擎本身做褒贬。相对的,U3D的一些知识却能帮助去理解UE4的Actor和Componet。Unity3D一开始就要接触到GameObject以及Component,然后会通过代码来获取和设置GameObject的属性,或者是获取与设置Component的属性。
U3D的脚本是CS文件,对于U3D而言,同样也是组件,需要与物体绑定以执行功能;
而UE4的C++类中实现U3D组件功能的却是ComponentActor,SceneActor等等的类。
U3D的脚本文件具有生命周期,Awake() Start() Update() OnDistory() 等;
UE4的类也具有生命周期:BeginPlay() Tick() 并且对于UObject与OnDestroyCondition(),
对于Actor有Destroy方法
U3D的GameObject类是所有对象的基类,
UE4的基类是UObject和AActor,其中AActor又继承自UObject
U3D提供了游戏对象的常用操作:
查找:Find()、FindGameObjectWithTAg、FindGameObjectsWithTag()等
创建:New GameObject ,或者是Instantiate
获取组件信息:GetComponent<ComponentName>()
Transform为固定组件,
gameObject为脚本所绑定的物体
UE4中Actor的操作呢?
对于Actor类可以在类中直接操作,需查看Actor相关文档
关注了如何创建和销毁一个Actor,GetWolrd()->SpawnActor,
而且明确规定了Object或者Actor不能够通过new的方式来创建
在代码中为Actor添加组件。
对于ActorComponent或者是SceneCompont,可以通过GetOwner获取当前所绑定的Actor
接下来继续Chapter4剩下的内容,偏重于实操:
单词本:
Inventory:库存,清单,财产目录
An InventoryComponent enables its containing Actor to store InventoryActors in its inventory, and place them back into the game world.
Creating an InventoryComponent for an RPG
说明:这个实例用到了本书后两章讲解的内容Handling Events and Delegates,以及Input and Collision。不要求立即照着做
自定义一个清单组件,用于使得一个Actor把一些资源类目放到清单中,并且从清单中再把资源放到游戏世界中。第一版的这里是看不了的,比较的乱。第二版提供了详细的插图作为参考。这一部分的代码过于硬核。故先简述逻辑。
粘一段移动的代码,自行体会下有多硬核
现在主推的比较合理的做法:
创建InventoryCompont清单组件,这是一个ActorComponent,里面含有一个Tarray<InVentoryActor* >
用于存储放到清单中的物体
创建InVentoryActor,这个为要拾取的物体,该物体具有PickUp和PutDown操作
创建一个Character,包含清单组件,在Character的碰撞事件中判断是是否是InVentoryActor
,是的话,就调用InVentoryActor的PickUp操作,
另外自定义DropDown按键事件,按下时会调用InVentoryActor的PutDown操作
注意事项:为了使能碰撞事件,需要StaticMesh具有碰撞体
//InventoryActor.h
#pragma once
#include "CoreMinimal.h"
#include "Engine/StaticMeshActor.h"
#include "InventoryActor.generated.h"
UCLASS()
class CHAPTER_04_API AInventoryActor : public AStaticMeshActor
{
GENERATED_BODY()
AInventoryActor();
public:
virtual void PickUp();
virtual void PutDown(FTransform TargetLocation);
};
//InventoryActor.cpp
AInventoryActor::AInventoryActor()
:Super(){
PrimaryActorTick.bCanEverTick = true;
auto MeshAsset=ConstructorHelpers::FObjectFinder<UStaticMesh>(
TEXT("StaticMesh'/Game/SM_Cube.SM_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
#pragma once
#include "CoreMinimal.h"
#include "InventoryActor.h"
#include "Components/ActorComponent.h"
#include "InventoryComponent.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class CHAPTER_04_API UInventoryComponent : public UActorComponent
{
GENERATED_BODY()
public:
UInventoryComponent();
protected:
virtual void BeginPlay() override;
public:
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
UPROPERTY()
TArray<AInventoryActor*>CurrentInventory;
UFUNCTION()
int32 AddToInventory(AInventoryActor* ActorToAdd);
UFUNCTION()
void RemoveFromInventory(AInventoryActor* ActorToRemove);
};
//InventoryComponent.Cpp
int32 UInventoryComponent::AddToInventory(AInventoryActor * ActorToAdd)
{
return CurrentInventory.Add(ActorToAdd);
}
void UInventoryComponent::RemoveFromInventory(AInventoryActor * ActorToRemove)
{
CurrentInventory.Remove(ActorToRemove);
}
//InventoryCharacter.h
#pragma once
#include "CoreMinimal.h"
#include "InventoryComponent.h"
#include "InventoryActor.h"
#include "GameFramework/Character.h"
#include "InventoryCharacter.generated.h"
UCLASS()
class CHAPTER_04_API AInventoryCharacter : public ACharacter
{
...
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);
...
};
//InventoryCharacter.cpp
#include "InventoryCharacter.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Components/InputComponent.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");
}
// Called to bind functionality to input
void AInventoryCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAction("DropItem", IE_Pressed, this, &AInventoryCharacter::DropItem);
PlayerInputComponent->BindAxis("MoveForward", this, &AInventoryCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AInventoryCharacter::MoveRight);
PlayerInputComponent->BindAxis("CameraPitch", this, &AInventoryCharacter::AddControllerPitchInput);
PlayerInputComponent->BindAxis("CameraYaw", this, &AInventoryCharacter::AddControllerYawInput);
}
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());
Item->PutDown(PutDownLocation);
}
void AInventoryCharacter::NotifyHit(UPrimitiveComponent * MyComp, AActor * Other, UPrimitiveComponent * OtherComp, bool bSelfMoved, FVector HitLocation, FVector HitNormal, FVector NormalImpulse, const FHitResult & Hit)
{
AInventoryActor* InventoryItem = Cast<AInventoryActor>(Other);
if(InventoryItem!=nullptr){
TakeItem(InventoryItem);
GEngine->AddOnScreenDebugMessage(-1, 3.f, FColor::Red, TEXT("PickUp"));
}
}
void AInventoryCharacter::TakeItem(AInventoryActor * InventoryItem)
{
InventoryItem->PickUp();
MyInventory->AddToInventory(InventoryItem);
}
void AInventoryCharacter::MoveForward(float AxisValue)
{
FVector MoveDirection = GetActorForwardVector();
AddMovementInput(MoveDirection,AxisValue);
}
void AInventoryCharacter::MoveRight(float AxisValue)
{
FVector MoveDirection = GetActorRightVector();
AddMovementInput(MoveDirection, AxisValue);
}
再来分析下代码:逻辑结构就像下图
TArray集合提供了add/remove用于增删元素,在PickUp操作中,主要是Disable Ticking、Hide Actor 、Disable collision,最后设PutDown操作中则是SetActorTickEnabled, SetActorHiddenInGame, 和 SetActorEnableCollision,最后是重新设置Actor的位置。
在Character中,主要是通过重写NotifyHit,当碰撞发生时,获取碰撞体的信息,如果是要拾取的物体,则调用该物体的PickUp,并且把该物体存储到清单中。
Creating an OrbitingMovement Component和Creating a building that spawns units
这两小节保留,书上讲的有点浅了,涉及3D数学,对于3D数学的玩法,日后需要专门的整理一遍。