【UE4C++ ActionRougelike-03】可交互对象

一、爆炸油桶

1、创建油桶类

新建油桶类SExplosionBarrel,声明所需组件

//SExplosionBarrel.h

UCLASS()

class ACTIONROGUELIKE_API ASExplosionBarrel : public AActor

{

protected:

//静态网格体

UPROPERTY(VisibleAnywhere)

UStaticMeshComponent* MeshComp;

//爆炸作用力

UPROPERTY(VisibleAnywhere)

URadialForceComponent* ForceComp;

//发生碰撞处理函数

UFUNCTION(BlueprintCallable)

void OnActorHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);

}

构造函数中创建对应组件,并注册组件的碰撞事件

//SExplosionBarrel.cpp

ASExplosionBarrel::ASExplosionBarrel()

{

//创建静态网格体

MeshComp = CreateDefaultSubobject("MeshComp");

//开启"模拟物理"

MeshComp->SetSimulatePhysics(true);

//设置碰撞预设为PhysicsActor

MeshComp->SetCollisionProfileName(UCollisionProfile::PhysicsActor_ProfileName);

RootComponent = MeshComp;

//创建爆炸作用力

ForceComp = CreateDefaultSubobject("ForceComp");

ForceComp->SetupAttachment(MeshComp);

ForceComp->Radius = 500.0f; //作用力范围

ForceComp->ImpulseStrength = 500.0f; //作用力大小

ForceComp->bImpulseVelChange = true; //作用力效果忽略质量大小

//注册组件的碰撞事件,绑定到OnActorHit函数上

MeshComp->OnComponentHit.AddDynamic(this, &ASExplosionBarrel::OnActorHit);

}

void ASExplosionBarrel::OnActorHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)

{

UE_LOG(LogTemp, Log, TEXT("OnActorHit in Explosive Barrel"));

//ForceComp释放作用力

ForceComp->FireImpulse();

}

2、效果演示

二、宝箱

创建C++接口来实现与Actor进行交互,将交互功能封装成一个交互组件

1、创建玩家交互接口

创建交互接口类SInteractionInterface,声明交互函数Interact()

//SInteractionInterface.h

//提供交互的API

class ACTIONROGUELIKE_API ISInteractionInterface

{

public:

//交互功能函数,参数表示发起交互的对象

UFUNCTION(BlueprintCallable,BlueprintNativeEvent)

void Interact(APawn* InstigatorPawn);

};

2、创建宝箱类

创建宝箱类STreasureChestItem,声明所需组件

//STreasureChestItem.h

//继承交互接口类

class ACTIONROGUELIKE_API ASTreasureChestItem : public AActor, public ISInteractionInterface

{

public:

UPROPERTY(EditAnywhere)

float OpenPitch;

//重写互动接口的互动方法

void Interact_Implementation(APawn* InstigatorPawn) override;

protected:

//宝箱体Mesh

UPROPERTY(VisibleAnywhere)

UStaticMeshComponent* BaseMesh;

//宝箱盖子Mesh

UPROPERTY(VisibleAnywhere, BlueprintReadOnly)

UStaticMeshComponent* LidMesh;

}

创建蓝图类TreasureChest_BP,并设置对应Mesh,在构造函数中创建宝箱类所需组件,并实现宝箱类继承的交互方法

//STreasureChestItem.cpp

ASTreasureChestItem::ASTreasureChestItem()

{

//创建宝箱底部Mesh

BaseMesh = CreateDefaultSubobject("BaseMesh");

RootComponent = BaseMesh;

//创建宝箱盖子Mesh

LidMesh = CreateDefaultSubobject("LidMesh");

LidMesh->SetupAttachment(BaseMesh);

//打开宝箱时盖子倾斜角度

OpenPitch = 110.0f;

}

//实现宝箱的交互方法

void ASTreasureChestItem::Interact_Implementation(APawn* InstigatorPawn)

{

//修改盖子的Rotation,达到宝箱打开的效果

LidMesh->SetRelativeRotation(FRotator(OpenPitch, 0, 0));

}

3、创建玩家交互组件

此时还差实现玩家能够碰撞查询到玩家面前可交互的Actor是什么,然后与Actor进行交互。将这一功能从Character类中抽离出来实现交互组件,实现交互功能的解耦,也避免Character类越来越庞大。

新建一个ActorComponent类SInteractionComponent表示交互组件,交互组件中声明一个公共方法来表示进行交互。

//SInteractionComponent.h

class ACTIONROGUELIKE_API USInteractionComponent : public UActorComponent

{

public:

//主动进行交互方法

void PrimaryInteract();

}

在.cpp中实现交互方法,通过射线检测,检测角色从眼部开始的射线相交的Actor,如果该Actor实现了交互接口类,则可以进行交互。

//SInteractionComponent.cpp

//主动进行交互

void USInteractionComponent::PrimaryInteract()

{

//碰撞查询参数

FCollisionObjectQueryParams ObjectQueryParams;

ObjectQueryParams.AddObjectTypesToQuery(ECC_WorldDynamic); //WorldDynamic类型时检测

AActor* Owner = GetOwner(); //获取角色拥有者

//EyeLocation作为射线检测的起点,EyeLocation+EyeRotation作为射线检测的终点

FVector EyeLocation;

FRotator EyeRotation;

//实参传递,修改Location和Rotation,将EyeLocation作为Start

Owner->GetActorEyesViewPoint(EyeLocation, EyeRotation);

//设置End

FVector End = EyeLocation + (EyeRotation.Vector()*1000);

//FHitResult Hit; //碰撞结果

//射线检测;参数: 命中结果(FHitReslut)、开始位置、终止位置、碰撞查询参数(FCollisionObjectQueryParams)

//GetWorld()->LineTraceSingleByObjectType(Hit, EyeLocation, End, ObjectQueryParams);

TArray Hits;

FCollisionShape Shape;

Shape.SetSphere(30.0f);

//最后会得到Hits的TArray,其中每个Hit包含这次Hit的结果信息

bool bBlockHit = GetWorld()->SweepMultiByObjectType(Hits, EyeLocation, End, FQuat::Identity, ObjectQueryParams, Shape);

FColor LinearColors = bBlockHit ? FColor::Green : FColor::Red; //发生碰撞时射线检测为绿,否则为红

for (FHitResult Hit : Hits)

{

AActor* HitActor = Hit.GetActor();

if (HitActor)

{

//如果HitActor实现了交互接口类USInteractionInterface

if (HitActor->Implements())

{

//Owner强转为APawn

APawn* MyPawn = Cast(Owner);

//调用实现的接口

ISInteractionInterface::Execute_Interact(HitActor, MyPawn);

}

//遇到首个交互体后跳出循环

break;

}

}

//射线检测可视化

DrawDebugLine(GetWorld(), EyeLocation, End, LinearColors, false, 2.0f, 0, 2.0f);

}

同之前一样在项目设置->Input中绑定按键与输入事件,在MyCharacter.cpp中绑定事件与处理函数

//MyCharacter.cpp

void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)

{

PlayerInputComponent->BindAction(TEXT("PickUp"), IE_Pressed, this, &AMyCharacter::PickUp);

}

void AMyCharacter::PickUp()

{

if (InteractionComp) {

InteractionComp->PrimaryInteract();

}

}

4、效果演示

三、攻击动画优化

1、添加攻击动画

MyCharacter.h中声明攻击动画AttackAnim(UAnimMontage*)

//AMyCharacter.h

class ACTIONROGUELIKE_API AMyCharacter : public ACharacter

{

//交互组件

UPROPERTY(VisibleAnywhere, Category = "Attack")

USInteractionComponent* InteractionComp;

//攻击动画

UPROPERTY(EditAnywhere, Category = "Attack")

UAnimMontage* AttackAnim;

};

MyCharacter.cpp在PrimaryAttack函数中添加播放动画

//AMyCharacter.cpp

void AMyCharacter::PrimaryAttack()

{

//播放攻击动画

PlayAnimMontage(AttackAnim);

}

效果如下:

问题:魔法攻击类生成位置在初始手部的插槽,而不是完成攻击动画后手部插槽的位置

如何解决?

通过一个Timer设置延迟生成攻击效果动画通知/动画事件生成攻击效果

2、延迟攻击特效生成

MyCharacter.h声明定时器TimerHandle_PrimaryAttack和定时器结束处理函数PrimaryAttack_TimeElapsed()

//MyCharacter.h

class ACTIONROGUELIKE_API AMyCharacter : public ACharacter

{

//攻击时的TimerHandle,处理动画导致的攻击生成位置错误

FTimerHandle TimerHandle_PrimaryAttack;

void PrimaryAttack_TimeElapsed();

};

MyCharacter.cpp在PrimaryAttack()函数中播放攻击动画,并设置定时器结束后生成攻击特效

//MyCharacter.cpp

void AMyCharacter::PrimaryAttack()

{

//播放攻击动画

PlayAnimMontage(AttackAnim);

//设置定时器,延迟生成攻击特效,0.2s后调用PrimaryAttack_TimeElapsed()

GetWorldTimerManager().SetTimer(TimerHandle_PrimaryAttack, this, &AMyCharacter::PrimaryAttack_TimeElapsed, 0.2f);

//销毁定时器,当角色抬手攻击时死亡,定时器销毁,不会执行PrimaryAttack_TimeElapsed()

//GetWorldTimerManager.ClearTimer(TimerHandle_PrimaryAttack);

}

//生成攻击特效

void AMyCharacter::PrimaryAttack_TimeElapsed()

{

//获取骨骼体手部的插槽作为魔法攻击类的生成点

FVector HandLocation = GetMesh()->GetSocketLocation("Muzzle_01");

//生成朝向:控制器当前的旋转量 生成位置:手部插槽

FTransform SpawnTM = FTransform(GetControlRotation(),HandLocation);

//参数设置

FActorSpawnParameters SpawnParams;

//设置为:总是生成;粒子在角色手部插槽生成,可能会与角色发生碰撞

SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;

//从World中的SpawnTM按SpawnParams设置生成Projectileclass类

GetWorld()->SpawnActor(Projectileclass ,SpawnTM, SpawnParams);

}

3、效果演示

推荐文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: