文章目录
一、代码结构
- 为了避免将所有事务都写在角色代码中,让其变得非常臃肿而难以维护,我们考虑到角色的所有技能都是右手释放的,所以右手发射这段代码是可复用的
- 根据以上思路,我们可以在角色代码中只需要保留任务逻辑生成投掷物和运行轨迹的逻辑,其余各部分的不同,放在单独的类中实现即可。
cpp
void ASurCharacter::SpawnProjectile(TSubclassOf<AActor> ClassToSpawn)
{
if (ensureAlways(ClassToSpawn)) {
...
// 生成传入的类
GetWorld()->SpawnActor<AActor>(ClassToSpawn, SpawnTM, SpawnParams);
}
}
- 而在原来的攻击实现函数中,我们只需要执行对应投掷物的生成即可。
cpp
void ASurCharacter::PrimaryAttack_TimeElapsed() {
SpawnProjectile(ProjectileClass);
}
- 截至目前,攻击释放的逻辑如下:
(1)按下按键,绑定到攻击
cpp
PlayerInputComponent->BindAction("PrimaryAttack", IE_Pressed, this, &ASCharacter::PrimaryAttack);
(2)调用攻击函数
cpp
void ASCharacter::PrimaryAttack()
{
//PlayAnimMontage(AttackAnim);
StartAttackEffects();
GetWorldTimerManager().SetTimer(TimerHandle_PrimaryAttack, this, &ASCharacter::PrimaryAttack_TimeElapsed, AttackAnimDelay);
//GetWorldTimerManager().ClearTimer(TimerHandle_PrimaryAttack); if the character died
}
(3)开始粒子发射效果展示
cpp
void ASCharacter::StartAttackEffects()
{
PlayAnimMontage(AttackAnim);
//在指定的网格组件(通过GetMesh()获得)上的指定socket(HandSocketName)处,精确放置(无偏移和旋转)一个粒子效果(CastingEffect)
UGameplayStatics::SpawnEmitterAttached(CastingEffect, GetMesh(), HandSocketName, FVector::ZeroVector, FRotator::ZeroRotator, EAttachLocation::SnapToTarget);
}
(4)抬手时间粒子发射延迟时间到后生成粒子
cpp
void ASCharacter::PrimaryAttack_TimeElapsed()
{
SpawnProjectile(ProjectileClass);
}
(5)粒子生成具体参数(起始位置,朝向等)
cpp
void ASurCharacter::SpawnProjectile(TSubclassOf<AActor> ClassToSpawn)
{
if (ensureAlways(ClassToSpawn)) {
...
// 生成传入的类
GetWorld()->SpawnActor<AActor>(ClassToSpawn, SpawnTM, SpawnParams);
}
}
(6)粒子的发射过程由对应的类具体实现
UGameplayStatics::SpawnEmitterAttached
用于创建和附加一个粒子效果(通常是视觉效果如爆炸、火花、烟雾等)到一个游戏对象。具体参数如下:
cpp
UGameplayStatics::SpawnEmitterAttached(CastingEffect, GetMesh(), HandSocketName, FVector::ZeroVector, FRotator::ZeroRotator, EAttachLocation::SnapToTarget);
(1)CastingEffect
:是要生成的粒子效果的引用。通常,这个参数是一个预先定义好的粒子系统模板的指针或者引用,需要在UE编辑器中创建这个粒子效果。
(2)GetMesh()
:返回一个USkeletalMeshComponent
的引用,这是想要将粒子效果附加到的网格组件 。通常这个网格是角色或者对象的可视化表现。
(3)HandSocketName
:是一个指定粒子效果应该附加到网格的哪个部分的名称。例如,如果你有一个角色,你想在他的手部位置播放粒子效果,你就可以使用一个定义在手部的socket
的名称。
(4)FVector::ZeroVector
:是一个三维向量 ,表示粒子效果相对于socket
位置的偏移。FVector::ZeroVector
表示没有偏移,粒子效果将精确地在socket
的位置生成。
(5)FRotator::ZeroRotator
:是一个旋转器 (rotator
),用于定义粒子效果的朝向。FRotator::ZeroRotator
表示没有旋转,粒子效果将按照网格或socket
的默认方向进行对齐。
(6)EAttachLocation::SnapToTarget
:是一个枚举值 ,定义了粒子效果附加到网格的方式。SnapToTarget
意味着粒子效果将精确地对齐到目标socket的位置和旋转。这对于需要精确位置控制的效果(如本例中手中的魔法效果)很有用。
二、实现黑洞粒子
- 准备工作:复制一份之前
PrimaryAttack
的代码,改为黑洞粒子的BlackHoleAttack
和BlackHoleAttack_TimeElapsed
。在SetupPlayerInputComponent
添加攻击按键绑定后,再在UE里实现绑定。 - 首先创建黑洞粒子子类并暴露于蓝图中,这样的定义允许开发者在不改变代码的情况下通过编辑器灵活配置和使用不同的类,减少了硬编码需求。
TSubclassOf<AActor>
是一个模板类,用于指定一个特定的基类。这意味着BlackHoleProjectileClass
可以存储任何AActor
或其子类的类型信息。例如如果有多个投掷物类(比如不同种类的炸弹、箭矢等),可以在编辑器中选择一个具体的类来实例化和使用。
cpp
// SCharacter.h
UPROPERTY(EditAnywhere, Category = "Attack")
TSubclassOf<AActor> BlackHoleProjectileClass;
// SCharacter.cpp
void ASurCharacter::BlackHoleAttack_TimeElapsed() {
SpawnProjectile(BlackHoleProjectileClass);
}
- 现在考虑代码功能复用,设置普通投掷物基类,其他扩展类继承于此。在UE中从
Actor
派生一个SurProjectileBase
类,使用ABSTRACT
宏将该类声明为"抽象基类 ",使其不显示在UE的下拉窗中,防止我们在关卡中添加此类的Actor
cpp
// ASurProjectileBase.h
UCLASS(ABSTRACT)
class ACTIONROGUELIKE_API ASProjectileBase : public AActor
// ASurProjectileBase.cpp
ASurProjectileBase::ASurProjectileBase()
{
SphereComp = CreateDefaultSubobject<USphereComponent>("SphereComp");
SphereComp->SetCollisionProfileName("Projectile");
RootComponent = SphereComp;
MoveComp = CreateDefaultSubobject<UProjectileMovementComponent>("ProjectileMoveComp");
MoveComp->bRotationFollowsVelocity = true;
MoveComp->bInitialVelocityInLocalSpace = true;
MoveComp->ProjectileGravityScale = 0.0f;
MoveComp->InitialSpeed = 500;
EffectComp = CreateDefaultSubobject<UParticleSystemComponent>("EffectComp");
EffectComp->SetupAttachment(RootComponent);
}
- 在蓝图中实现黑洞功能。创建继承自
SurProjectileBase
的Proj_BlackHole
蓝图。添加Physics
中的径向力组件RadialForce
,这个组件可以实现在一个范围内 产生力。在属性"径向力组件"中设置"要影响的对象",点击右边小三角形将Pawn
删除,这样就不会把我们自己吸进去。然后,设置半径 为500,设置"力"属性为-2,000,000。 - 不希望这个黑洞在移动时会被物体阻挡,所以修改碰撞预设为
Custom
和相关碰撞响应 - 这是一个有时间限制 的技能,设置
Actor
属性的"初始生命周期"为5秒,即要添加动画的时长,并设置其自定义时间膨胀(要匹配动画时长)