上一篇文章,我们创建了次级属性,并实现了对次级属性的初始化,而且因为使用的Infinite模式,具有无限的持续时间,它能够跟随基础属性变动而实时修改数值。
接下来,我们将为角色和敌人增加等级,将等级放到character身上,并实现使用Modifier Magnitude Calculation (MMC)进行自定义计算数值。(当然,等级属性也能够放到AS里面,这里为了学习,就作为一个单独的属性,看个人爱好了)
实现步骤:
- 首先在PlayerState上面给玩家角色增加等级属性,在怪物自身增加等级属性
- 创建一个接口,用于获取等级属性的值,在MMC里面可以通过接口去获取,而不需要判断到底哪个类了
- 创建MMC实现对最大血量和最大蓝量基于等级的提升
在这一篇里面,我们先实现一下前面的两个步骤。
创建等级属性
首先,玩家角色的等级需要创建在PlayerState里面,如果玩家角色Actor被销毁掉,等级信息也能够被保存下来。
UPROPERTY(VisibleAnywhere, ReplicatedUsing=OnRep_Level)
int32 Level;
设置OnRep
UFUNCTION()
void OnRep_Level(int32 OldLevel);
void APlayerStateBase::OnRep_Level(int32 OldLevel)
{
// GAMEPLAYATTRIBUTE_REPNOTIFY(UAttributeSetBase, Level, OldLevel);
}
相应的,我们也需要等级信息和服务器同步,我们需要覆写GetLifetimeReplicatedProps,在服务器注册属性
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
cpp里的实现就是将Level属性注册到服务器,在服务器有改动时,会自动复制到本地,如果输入了DOREPLIFETIME提示报错,记得alt+Enter键自动添加引用
void APlayerStateBase::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(APlayerStateBase, Level);
}
注册的宏经常使用的有三种:
- DOREPLIFETIME
- DOREPLIFETIME_CONDITION
- DOREPLIFETIME_CONDITION_NOTIFY
它们三种是一次递进的,
第一种就是基础的注册宏,在服务器变化时,会同步所有的客户端,
第二种增加了CONDITION 也就是条件,条件的参数有多种,按需设置
第三种增加了NOTIFY
如果使用DOREPLIFETIME ,后面两项是有默认值的 CONDITION的默认值是COND_None,NOTIFY的默认值是REPNOTIFY_OnChanged就是本地属性变动时,才会调用RepNotify函数
在英雄角色上创建属性并实现了和服务器同步,记住,这个是在PlayerState上实现的,不是玩家的Character。
ai控制的敌人身上就简单多了,直接才EnemyBase身上创建一个等级属性即可
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Character Class Defaults")
int32 Level = 1;
创建接口
打开ue,在敌人接口旁边创建一个新的接口,这个敌人的为敌人专门使用的,玩家角色无法使用,那我们再创建一个战斗通用的接口
右键选择新建c++类,找到UE接口
创建战斗专门接口,
创建完成,点否,我们在编辑器编译,不用ue
然后关闭UE,等待编辑器编译接口类,编译完成,在里面增加一个虚拟函数,用于获取角色等级,后面在MMC里面我们可以通过接口去获取等级。
virtual int32 GetPlayerLevel();
接口函数实现返回一个0
int32 ICombatInterface::GetPlayerLevel()
{
return 0;
}
然后在角色基类里面去继承接口,玩家角色类和敌人基类都继承了此接口。
class AURA_API ACharacterBase : public ACharacter, public IAbilitySystemInterface, public ICombatInterface
{
GENERATED_BODY()
接口实现了,接下来就是要在玩家角色类和敌人基类里面去覆盖接口类的虚拟函数
/* ICombatInterface战斗接口 */
virtual int32 GetPlayerLevel() override;
/* ICombatInterface战斗接口 结束 */
敌人基类里面直接将身上的等级返回
int32 AEnemyBase::GetPlayerLevel()
{
return Level;
}
而玩家的角色基类里面对函数的实现需要去PlayerState身上获取等级,因为玩家的等级是记录在PlayerState身上的。所以,我们在PlayerState身上实现一个等级获取函数。
这里前面加了FORCEINLINE ,可以提升效率,但是不能够使用循环,有一些限制,这里直接返回等级的值,没问题。
FORCEINLINE int32 GetPlayerLevel() const {return Level;}
然后在玩家角色类里面获取PlayerState,然后调用函数返回等级数值
int32 AHeroCharacter::GetPlayerLevel()
{
const APlayerStateBase* PlayerStateBase = GetPlayerState<APlayerStateBase>();
check(PlayerStateBase); //检测是否有效,无限会暂停游戏
return PlayerStateBase->GetPlayerLevel();
}
PS:编译的时候,突然之前设置的内容报错了
void CursorTrace(); //鼠标位置追踪拾取
TObjectPtr<IEnemyInterface> LastActor; //上一帧拾取到的接口指针
TObjectPtr<IEnemyInterface> ThisActor; //这一帧拾取到的接口指针
错误提示
改成下面即可
void CursorTrace(); //鼠标位置追踪拾取
IEnemyInterface* LastActor; //上一帧拾取到的接口指针
IEnemyInterface* ThisActor; //这一帧拾取到的接口指针
到这里,我们实现了预期的两步,那么,下一章节,将实现创建MMC类,并实现基于等级对最大血量和最大蓝量的修改。