前言:
笔者目前在校本科大三,目标方向是人工智能、计算机视觉。上一个OpenCV学习笔记专栏已完结,在学习完OpenCV后,我继续学习C++,并用纯C++做UE4项目的方式继续提升自己的水平。
梁迪老师的水平非常高,他的课程本来也无需笔记:课程本身即为最好的笔记。但由于我天赋有限,还是边看边记,以防遗忘——知识点太多,步骤太繁杂了。在学习过程中,我也偶有思考,思索为什么某个方法老师要这样做。所以,一是为了记录,二是为了分享,才有了这个专栏。
内容方面,由于我在开启这个专栏时,此项目已经做完很多了。所以,前期的一些大篇幅叙述的知识,可能在后期应用中一带而过。以及,前期的一些知识,后期会重新剖析,并加上我的个人理解。
另外,若有学术交流/学业交流意愿,可以邮件联系1246210283@qq.com,希望一齐进步。
本篇学习内容:
34.右手插槽
35.手上物品切换
36.动作切换
34.右手插槽
(1)在Character中定义一个变量
//手上物品
UPROPERTY(VisibleDefaultsOnly, Category = "SlAi")
class UChildActorComponent* HandObject;//可以绑定一个Actor在这个Character里
(2)绑定模型
//实例化手上物品(构造函数中)
HandObject = CreateDefaultSubobject<UChildActorComponent>(TEXT("HandObject")); //接下来将其绑定到骨骼的手上
//把手持物品组件绑定到第三人称模型,绑定规则:贴附到目标但不包括大小变换(BeginPlay函数中)
HandObject->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, FName("RHSocket"));
(3)创建HandObject类和8个物品对应的子类,继承自HandObject
(4)先在HandObject中做一些准备工作
ASlAiHandObject::ASlAiHandObject()
{
// 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;
//实例化根组件
RootScene = CreateDefaultSubobject<USceneComponent>(TEXT("RootScene"));
RootComponent = RootScene;
//创建静态模型组件
BaseMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("BaseMesh"));
BaseMesh->SetupAttachment(RootComponent);
BaseMesh->SetCollisionProfileName(FName("NoCollision"));
//实现碰撞组件
AffectCollision = CreateDefaultSubobject<UBoxComponent>(TEXT("AffectCollision"));
AffectCollision->SetupAttachment(RootComponent);
AffectCollision->SetCollisionProfileName(FName("Tool"));
//这个Tool的碰撞文件是在UE4编辑器的项目设置内新建的
//初始时关闭Overlay检测
AffectCollision->bGenerateOverlapEvents = false; //不允许进行交互检测 为了避免树碰到人的斧子就自己掉血
//绑定检测方法到碰撞体
FScriptDelegate OverlayBegin; //这个委托绑定到一个六参数函数,参数的类型和OnOverlayBegin一致
OverlayBegin.BindUFunction(this, "OnOverlayBegin");
AffectCollision->OnComponentBeginOverlap.Add(OverlayBegin);
FScriptDelegate OverlayEnd;
OverlayEnd.BindUFunction(this, "OnOverlayEnd");
AffectCollision->OnComponentEndOverlap.Add(OverlayEnd);
}
(5)关于子类的实现,以HandAxe为例:
ASlAiHandAxe::ASlAiHandAxe()
{
//给模型组件添加模型
static ConstructorHelpers::FObjectFinder<UStaticMesh> StaticBaseMesh(
TEXT("StaticMesh'/Game/Res/PolygonAdventure/Meshes/SM_Wep_Axe_01.SM_Wep_Axe_01'"));
BaseMesh->SetStaticMesh(StaticBaseMesh.Object);
//设置模型的位移和旋转 这些数据需要一点一点调试得出
//正常项目开发是写一些C++类,随后用蓝图继承这些类,然后在UE4编辑器内调试
BaseMesh->SetRelativeLocation(FVector(-28.f, -4.9f, 3.23f));
BaseMesh->SetRelativeRotation(FRotator(0.f, -90.f, 90.f));
BaseMesh->SetRelativeScale3D(FVector(1.f, 1.f, 1.f));
//设置碰撞体属性
AffectCollision->SetRelativeLocation(FVector(32.f, -5.f, 3.f));
AffectCollision->SetRelativeScale3D(FVector(0.375f, 0.5f, 0.125f));
}
void ASlAiHandAxe::BeginPlay()
{
Super::BeginPlay();
//定义物品序号
ObjectIndex = 5;
}
(6)类似HandAxe写完剩下的7个物品
35.手上物品切换
添加物品到手上,是通过Character的BeginPlay中HandObject->SetChildActorClass(ASlAiHandObject::StaticClass());
来实现的。
现在需要通过获取物品ID,来添加不同的物品到手上。
(1)在HandObject中创建一个工厂方法static TSubclassOf<AActor> SpawnHandObject(int ObjectID);
TSubclassOf<AActor> ASlAiHandObject::SpawnHandObject(int ObjectID)
{
//其实可以通过反射来做。switch是一个不友好的形式
switch (ObjectID) {
case 0:
return ASlAiHandObject::StaticClass();
case 1:
return ASlAiHandWood::StaticClass();
...
}
...
}
(2)修改Character的BeginPlay的代码:
//添加Actor到HandObject
HandObject->SetChildActorClass(ASlAiHandObject::SpawnHandObject(0)); //游戏开始时默认手上是空物体
(3)完善Controller中的鼠标滚轮事件:
//更改character的手持物品
void ASlAiPlayerController::ChangeHandObject()
{
//生成手持物品
SPCharacter->ChangeHandObject(ASlAiHandObject::SpawnHandObject(SPState->GetCurrentHandObjectIndex()));
}
其中的SPCharacter->ChangeHandObject:
void ASlAiPlayerCharacter::ChangeHandObject(TSubclassOf<AActor> HandObjectClass)
{
//设置物品到HandObject
HandObject->SetChildActorClass(HandObjectClass);
}
36.动作切换
在人物手持物品不同时,设置不同的动作。
(1)在PlayerState中实现GetCurrentObjectType()函数:
//获取当前手持物品的物品类型
EObjectType::Type ASlAiPlayerState::GetCurrentObjectType()
{
TSharedPtr<ObjectAttribute> ObjectAttr;
ObjectAttr = *SlAiDataHandle::Get()->ObjectAttrMap.Find(GetCurrentHandObjectIndex());
return ObjectAttr->ObjectType;
}
(2)在Controller中修改左右手预动作
void ASlAiPlayerController::ChangePreUpperType(EUpperBody::Type RightType = EUpperBody::None)
{
//实时修改左键预动作和右键预动作,随后这两个值会赋给Character的上半身动画状态UpperType,在Anim类中会根据UpperType来播放不同的动画
//根据当前手持物品的类型来修改预动作
switch (SPState->GetCurrentObjectType())
{
case EObjectType::Normal:
//空物体以及木头、石头
LeftUpperType = EUpperBody::Punch;
RightUpperType = RightType;
break;
...
}
...
}
(3)在Controller的Tick函数中调用
void ASlAiPlayerController::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
//临时代码
ChangePreUpperType(EUpperBody::None);
}