在前面,我们实现了火球术火球的制作,能够在释放火球术时,角色将播放释放技能动画,并实现了对火球的目标的服务器同步功能。
我们先回忆一下之前完成的内容。
在前面,我们先做了一个Actor,用于承载发射的火球,名称为:Projectile
,里面包含碰撞体和子弹移动组件,后面可以用于碰撞检测和子弹移动的支持。
我们又创建了基于技能的专门用于发射类似于火球这种发射物的技能类,命名为:ProjectileSpell
,它在后续会专门用于发射技能。在技能里面,制作了一个生成发射物体的函数SpawnProjectile()
,它内部主要实现为生成需要发射的物体,并设置变量,可以设置一个类,它会根据类生成对应的物体,比如你需要角色技能发射一个火球,那么设置火球的类即可。
然后我们使用技能内置的蒙太奇播放,然后使用动画通知通知角色接收GameplayTag标签触发对应的通知,在接收到需要发射火球的通知消息时,去执行SpawnProjectile()
函数。
在同步这里,我们创建了一个Ability Task类,专门用于将客户端鼠标拾取的位置上传到服务器端,用于同步。在内部实现使用到了预测窗口来标注需要预测的范围,并使用TargetData将目标位置上传到了服务器。
接下来,我们将实现角色在释放技能时,修改火球的移动方向,并处理角色遮挡相机的bug,然后增加一个不需要选中敌人就可以攻击的方式。
修改火球的转向
在ProjectileSpell
技能类里,我们在生成火球时,在里面传递一个位置,用于修改火球的发射方向,我们之前的方法是获取角色朝向,然后根据角色的朝向
在SpawnProjectile函数增加一个传入参数,用于确定攻击目标位置
UFUNCTION(BlueprintCallable, Category="Projectile")
void SpawnProjectile(const FVector& ProjectileTargetLocation);
在函数实现这里,首先获取目标位置和角色位置的朝向,然后转为旋转方向,并设置Pitch(水平旋转)为0,转换为四元数设置给火球。
const FVector SocketLocation = CombatInterface->GetCombatSocketLocation();
FRotator Rotation = (ProjectileTargetLocation - SocketLocation).Rotation(); //将方向转为旋转
Rotation.Pitch = 0.f; //设置Pitch为0,转向的朝向将平行于地面
FTransform SpawnTransform;
SpawnTransform.SetLocation(CombatInterface->GetCombatSocketLocation());
SpawnTransform.SetRotation(Rotation.Quaternion());
编译打开UE,将鼠标拾取的位置存储为变量,方便后面使用
在触发事件创建发射物时,将位置传入
这样就可以测试了,火球的方向正确。
完整蓝图
现在还有个问题,就是在客户端上面点击时,火球无法出现,这是因为创建火球和结束技能时同时创建的,导致被吃掉了,现在测试,我们先不执行EndAbility节点,这样,起码能有一个完整显示的技能。
解决角色遮挡相机的问题
要解决这个问题,首先查找谁会遮挡相机,我们需要在节点的碰撞预设查找。
首先角色的碰撞体,会出现这个问题
在角色的模型上面,也有相同的问题
我们需要将它们与相机的碰撞设置为忽略,它们将不再检测与相机的碰撞。
我们在角色的基类这里,一劳永逸的解决这个问题,在构造函数这里,使用通过设置碰撞,通道选择相机,碰撞设置为忽略
//设置角色不会和相机碰撞
GetCapsuleComponent()->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore);
GetMesh()->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore);
修改通过点击地面即可释放技能
为了防止移动和攻击冲突,接下来,我们将修改鼠标左键的操作,默认点击是移动,按住Shift键+鼠标左键则为攻击当前地点。
首先实现Shift按键事件,增加一个InputAction
值类型修改为一维浮点类型,Shift键只需要一维即可,记录它按下和抬起
将其添加到输入映射上下文中,我们需要将左右两个Shift键都添加进去
这个添加完了,我们需要实现对应的操作,打开PlayerController,添加一个属性,用于设置ShiftAction
UPROPERTY(EditAnywhere, Category="Input")
TObjectPtr<UInputAction> ShiftAction;
接下来,我们创建绑定ShiftAction的回调函数,按下和抬起,并添加一个变量用于标示当前状态释放处于按下状态
void ShiftPressed() { bShiftKeyDown = true; };
void ShiftReleased() { bShiftKeyDown = false; };
bool bShiftKeyDown = false;
绑定对应的输入事件回调
void APlayerControllerBase::SetupInputComponent()
{
Super::SetupInputComponent();
UInputComponentBase* EnhancedInputComponent = CastChecked<UInputComponentBase>(InputComponent); //获取到增强输入组件
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &APlayerControllerBase::Move); //绑定移动事件
//绑定Shift按键事件
EnhancedInputComponent->BindAction(ShiftAction, ETriggerEvent::Started, this, &ThisClass::ShiftPressed);
EnhancedInputComponent->BindAction(ShiftAction, ETriggerEvent::Completed, this, &ThisClass::ShiftReleased);
EnhancedInputComponent->BindAbilityAction(InputConfig, this, &ThisClass::AbilityInputTagPressed,&ThisClass::AbilityInputTagReleased, &ThisClass::AbilityInputTagHold);
}
我们需要在Hold事件和Released事件回调中修改判断,之前只是判断如果当前选中了攻击目标,则释放技能,现在则需要加上是否按住了Shift键
接着编译打开PlayerController的蓝图,设置上Shift键位的Action,运行测试,按住Shift键后触发技能。