在编辑器中创建新的C++类LMagicProjectile(继承Actor)
在头文件中添加如下组件:
class ROGUELIKEACTION_API ALMagicProjectile : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ALMagicProjectile();
protected:
// 魔法弹的模型
UPROPERTY(VisibleAnywhere)
USphereComponent * SphereComponent;
// 投掷体运动系统
UPROPERTY(VisibleAnywhere)
UProjectileMovementComponent * ProjectileMovementComponent;
// 粒子系统
UPROPERTY(VisibleAnywhere)
UParticleSystemComponent * EffectComponent;
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
在.cpp文件的构造函数中添加初始化的代码:
ALMagicProjectile::ALMagicProjectile()
{
// 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;
SphereComponent = CreateDefaultSubobject<USphereComponent>("SphereComponent");
// 根据名称指定自定义的碰撞通道设置,随后会在编辑器中定义
SphereComponent->SetCollisionProfileName("Projectile");
RootComponent = SphereComponent;
ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>("ProjectileMovementComponent");
// 运动初速度
ProjectileMovementComponent->InitialSpeed = 1000.0f;
// 球体每帧都会旋转以适应当前运动的方向
ProjectileMovementComponent->bRotationFollowsVelocity = true;
// 使用局部坐标系定义
ProjectileMovementComponent->bInitialVelocityInLocalSpace = true;
// 粒子系统
EffectComponent = CreateDefaultSubobject<UParticleSystemComponent>("EffectComponent");
EffectComponent->SetupAttachment(SphereComponent);
}
跟上一节中的Character一样,从C++类中派生出新的蓝图类MagicProjectileBP
打开蓝图类,在粒子系统组件中选择在上一节中下载好的P_Gideon_Primary_Projectile模板
首先,在Character类中添加两个新组件
protected:
// 魔法攻击发射的子弹类型,C++只负责定义,之后在继承的蓝图类中指定具体是哪种子弹
UPROPERTY(EditAnywhere, Category="Attack")
TSubclassOf<AActor> ProjectileClass;
// 发动攻击时的人物动画,也是蓝图中具体指定
UPROPERTY(EditAnywhere, Category="Attack")
UAnimMontage * AttackAnim;
打开角色蓝图,将ProjectClass指定为MagicProjectileBP,AttackAnim指定为Primary_Attack_A_Medium_Montage
首先,实现执行攻击动画与生成魔法弹的PrimaryAttack函数:
void ALCharacter::PrimaryAttack()
{
// 执行攻击动画
PlayAnimMontage(AttackAnim);
// 获取人物模型中手的坐标,GetSocketLocation可以获取骨骼中的插件
const FVector HandLocation = GetMesh()->GetSocketLocation("Muzzle_01");
const FTransform SpawnTM = FTransform(GetControlRotation(), HandLocation);
FActorSpawnParameters SpawnParams;
// 无论任何情况都生成(无视重叠,碰撞,覆盖...)
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
// 将角色自身作为触发者传入,以便子弹判断正确的交互对象
SpawnParams.Instigator = this;
// 在世界中生成
GetWorld()->SpawnActor<AActor>(ProjectileClass, SpawnTM, SpawnParams);
}
但是,我们希望最终的效果是人物完成攻击动作后才会发射子弹,而上述代码会造成人物动作刚开始执行,子弹就飞了出去,所以一个暂定的办法是设置一个定时器,让子弹生成操作延时触发:
Character.h
protected:
// 头文件中添加新属性
FTimerHandle TimerHandle_PrimaryAttack;
Character.cpp
void ALCharacter::PrimaryAttack()
{
// 先执行攻击动画
PlayAnimMontage(AttackAnim);
// 延时触发生成,延时器绑定需要触发的函数
GetWorldTimerManager().SetTimer(TimerHandle_PrimaryAttack, this, &ALCharacter::PrimaryAttack_TimeElapsed, 0.2f);
}
// 函数名和SetTimer中的对应
void ALCharacter::PrimaryAttack_TimeElapsed()
{
// 生成角度和坐标
// 获取人物模型中手的坐标,GetSocketLocation可以获取骨骼中的插件
const FVector HandLocation = GetMesh()->GetSocketLocation("Muzzle_01");
const FTransform SpawnTM = FTransform(GetControlRotation(), HandLocation);
FActorSpawnParameters SpawnParams;
// 无论任何情况都生成(无视重叠,碰撞,覆盖...)
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
// 将角色自身作为触发者传入,以便子弹判断正确的交互对象(不要和自身交互)
SpawnParams.Instigator = this;
// 在世界中生成对应子弹类型
GetWorld()->SpawnActor<AActor>(ProjectileClass, SpawnTM, SpawnParams);
}
在PlayerCharacter.cpp文件的SetupPlayerInputComponent绑定新的Action:
// 射击
PlayerInputComponent->BindAction("PrimaryAttack", IE_Pressed, this, &ALCharacter::PrimaryAttack);
在项目设置中进行操作映射: