UE4_20 Scripting with C++ 03

在继续学习新的内容时仅针对学过的章节做一点总结,最好是比较一下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数学的玩法,日后需要专门的整理一遍。

猜你喜欢

转载自blog.csdn.net/weixin_33232568/article/details/89210790