目录
1. 创建魔法粒子
在UE中从Actor派生出一个公共class,命名为SurMagicProjectile,表示这个类用于实现魔法投掷物攻击。创建完毕后在.h文件中添加三个用于计算碰撞和显示特效的组件,并在.cpp中创建实例:
// SurMagicProjectile.h
class USphereComponent;
class UProjectileMovementComponent;
class UParticleSystemComponent;
UCLASS()
class SURKEAUE_API ASurMagicProjectile : public AActor
{
protected:
// 球体,用于计算碰撞
UPROPERTY(VisibleAnywhere)
USphereComponent* SphereComp;
// 投射体,控制球体的运动
UPROPERTY(VisibleAnywhere)
UProjectileMovementComponent* MovementComp;
// 粒子系统,控制特效
UPROPERTY(VisibleAnywhere)
UParticleSystemComponent* EffectComp;
};
// SurMagicProjectile.cpp
#include "Components/SphereComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Particles/ParticleSystemComponent.h"
ASurMagicProjectile::ASurMagicProjectile()
{
SphereComp = CreateDefaultSubobject<USphereComponent>("SphereComp");
RootComponent = SphereComp;
MovementComp = CreateDefaultSubobject<UProjectileMovementComponent>("MovementComp");
MovementComp->InitialSpeed = 1000.0f;
MovementComp->bRotationFollowsVelocity = true;
MovementComp->bInitialVelocityInLocalSpace = true;
EffectComp = CreateDefaultSubobject<UParticleSystemComponent>("EffectComp");
EffectComp->SetupAttachment(SphereComp);
}
代码编写完毕后,需要在UE中创建蓝图类,步骤同之前创建的Player。
然后打开MagicProjectile,在粒子系统控件EffectComp的“粒子”属性中随意选择一种模板,我这里选择的是P_Gideon_Primary_Projectile。在选择模板后我们可以直接在界面中间的视口中看到粒子特效的样子。
2. 控制粒子生成
我们需要通过鼠标点击或键盘按下,来控制生成上述魔法粒子。要实现这个功能,可以参考前文有关控制人物移动的操作,需要在SurCharacter.cpp的SetupPlayerInputComponent()函数中绑定一个新动作。不同于控制移动的轴绑定,对于按下按钮这种动作需要使用操作绑定,即BindAction()函数:
// 绑定按键动作("UE中调用的名称";触发的时机如按下或释放;对象;具体方法实现)
PlayerInputComponent->BindAction("PrimaryAttack", IE_Pressed, this, &ASurCharacter::PrimaryAttack);
为此,需要增加一个控制释放攻击的函数PrimaryAttack()。这个函数的核心是GetWorld()在当前世界下SpawnActor()来生成上面创建的魔法粒子。其中传入三个对象参数分别为:要生成的对象的class、所有Actor基本的Transform变换属性(控制Actor的位置和缩放)、生成的相关参数设置,更详细的使用可以参考官方文档。
void ASurCharacter::PrimaryAttack() {
// Spawn Transform Matrix, spawn的变换矩阵
// 朝向角色方向,在角色的中心位置生成
FTransform SpawnTM = FTransform(GetActorRotation(),GetActorLocation());
// 参数设置。
// 此处设置碰撞检测规则为:即使碰撞也总是生成,因为粒子在角色中间生成必然碰撞
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
// 所有能放置或生成的对象都是Actor
GetWorld()->SpawnActor<AActor>(ProjectileClass, SpawnTM, SpawnParams);
}
在上面的代码中,传入SpawnActor()的第一个参数是ProjectileClass,这是一个需要在.h定义的AActor子类。同时,利用PROPERTY宏定义将其暴露在UE中,这样就可以在Player蓝图类的细节面板中,像选择参数那样直接选择MagicProjection实例进行调用。UPROPERTY的说明见官方文档,.h文件新增的内容如下:
UCLASS()
class SURKEAUE_API ASurCharacter : public ACharacter
{
protected:
// 投射体子类
UPROPERTY(EditAnywhere)
TSubclassOf<AActor> ProjectileClass;
void PrimaryAttack();
};
打开UE,将PrimaryAttack绑定为鼠标左键;同时将Player -> Sur Character中的ProjectileClass选择为第一步创建的蓝图类MagicProjectile。
现在运行关卡,点击鼠标左键就可以从角色的中心发射出魔法粒子。
3. 更改粒子发射位置
最佳的攻击演出效果,应该是从角色的手掌发出,所以首先需要找到骨骼体模型中手的位置。双击Player使用的网格体可以打开Gideon的模型,再点击右上角可以切换为骨骼。界面左侧会出现所有的骨骼和Socket,可以找到角色右手的位置为“Muzzle_01”。
要找到这个位置,UE提供了相应函数供开发者调用:
// 获取模型右手位置
FVector RightHandLoc = GetMesh()->GetSocketLocation("Muzzle_01");
现在角色就可以从右手发射魔法粒子了,后续还会配合动画播放,将会实现角色伸手发射粒子攻击的演出效果。但目前发射的粒子不能产生物理碰撞,如果向墙壁发射,粒子会直接穿过墙壁。
4. 实现物理碰撞
首先,将MagicProjectile的MovementComp组件的“发射重力范围”设置为0,这样发射的粒子可以不收重力下落而保持直线运动。然后在项目设置 -> 碰撞 -> Preset中新建名为“Projectile”的配置,用于魔法粒子碰撞检测。更多关于碰撞的内容可查看官方文档。
然后在MagicProjectile中的“碰撞”属性中设置使用该配置,或者直接使用如下C++代码实现:
SphereComp->SetCollisionProfileName("Projectile");
现在角色发射的粒子就可以和墙壁发生碰撞了。
5. 完整代码
SurCharacter.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "SurCharacter.generated.h"
class USpringArmComponent;
class UCameraComponent;
UCLASS()
class SURKEAUE_API ASurCharacter : public ACharacter
{
GENERATED_BODY()
public:
ASurCharacter();
protected:
//弹簧臂组件
UPROPERTY(VisibleAnywhere)
USpringArmComponent* SpringArmComp;
//相机组件
UPROPERTY(VisibleAnywhere)
UCameraComponent* CameraComp;
// 投射体子类
UPROPERTY(EditAnywhere)
TSubclassOf<AActor> ProjectileClass;
virtual void BeginPlay() override;
void MoveForward(float value);
void MoveRight(float value);
void PrimaryAttack();
public:
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
};
SurCharacter.cpp
#include "SurCharacter.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
ASurCharacter::ASurCharacter()
{
PrimaryActorTick.bCanEverTick = true;
SpringArmComp = CreateDefaultSubobject<USpringArmComponent>("SpringArmComp");
SpringArmComp->bUsePawnControlRotation = true;
SpringArmComp->SetupAttachment(RootComponent);
CameraComp = CreateDefaultSubobject<UCameraComponent>("CameraComp");
CameraComp->SetupAttachment(SpringArmComp);
GetCharacterMovement()->bOrientRotationToMovement = true;
bUseControllerRotationYaw = false;
}
void ASurCharacter::BeginPlay()
{
Super::BeginPlay();
}
// 角色向前移动
void ASurCharacter::MoveForward(float value)
{
FRotator ControlRot = GetControlRotation();
// 转向只关注水平Yaw方向,因此置0防止影响
ControlRot.Pitch = 0;
ControlRot.Roll = 0;
// 获取相机(鼠标控制器)的朝向,并朝这个方向移动
AddMovementInput(ControlRot.Vector(), value);
}
// 角色向右移动
void ASurCharacter::MoveRight(float value)
{
FRotator ControlRot = GetControlRotation();
ControlRot.Pitch = 0;
ControlRot.Roll = 0;
// 获取相机(鼠标控制器)的朝向,转向右侧,并朝这个方向移动
FVector RightVector = FRotationMatrix(ControlRot).GetScaledAxis(EAxis::Y);
AddMovementInput(RightVector, value);
}
// 左键攻击
void ASurCharacter::PrimaryAttack() {
// 获取模型右手位置
FVector RightHandLoc = GetMesh()->GetSocketLocation("Muzzle_01");
// Spawn Transform Matrix, spawn的变换矩阵
// 朝向角色方向,在角色的右手位置生成
FTransform SpawnTM = FTransform(GetActorRotation(), RightHandLoc);
// 参数设置。
// 此处设置碰撞检测规则为:即使碰撞也总是生成,因为粒子在角色中间生成必然碰撞
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
// 所有能放置或生成的对象都是Actor
GetWorld()->SpawnActor<AActor>(ProjectileClass, SpawnTM, SpawnParams);
}
void ASurCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ASurCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
// “UE中调用的名称”,this指针表示移动这个角色,&自定义移动方法
PlayerInputComponent->BindAxis("MoveForward", this, &ASurCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &ASurCharacter::MoveRight);
PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
// 绑定按键动作("UE中调用的名称";触发的时机如按下或释放;对象;具体方法实现)
PlayerInputComponent->BindAction("PrimaryAttack", IE_Pressed, this, &ASurCharacter::PrimaryAttack);
}
SurMagicProjectile.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SurMagicProjectile.generated.h"
class USphereComponent;
class UProjectileMovementComponent;
class UParticleSystemComponent;
UCLASS()
class SURKEAUE_API ASurMagicProjectile : public AActor
{
GENERATED_BODY()
public:
ASurMagicProjectile();
protected:
// 球体,用于计算碰撞
UPROPERTY(VisibleAnywhere)
USphereComponent* SphereComp;
// 投射体,控制球体的运动
UPROPERTY(VisibleAnywhere)
UProjectileMovementComponent* MovementComp;
// 粒子系统,控制特效
UPROPERTY(VisibleAnywhere)
UParticleSystemComponent* EffectComp;
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
};
SurMagicProjectile.cpp
#include "SurMagicProjectile.h"
#include "Components/SphereComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Particles/ParticleSystemComponent.h"
ASurMagicProjectile::ASurMagicProjectile()
{
PrimaryActorTick.bCanEverTick = true;
SphereComp = CreateDefaultSubobject<USphereComponent>("SphereComp");
SphereComp->SetCollisionProfileName("Projectile");
RootComponent = SphereComp;
MovementComp = CreateDefaultSubobject<UProjectileMovementComponent>("MovementComp");
MovementComp->InitialSpeed = 1000.0f;
MovementComp->bRotationFollowsVelocity = true;
MovementComp->bInitialVelocityInLocalSpace = true;
EffectComp = CreateDefaultSubobject<UParticleSystemComponent>("EffectComp");
EffectComp->SetupAttachment(SphereComp);
}
void ASurMagicProjectile::BeginPlay()
{
Super::BeginPlay();
}
void ASurMagicProjectile::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}