ue4-c++定时器和时间轴简易模板

发布时间 2024-01-11 08:20:33作者: XTG111

定时器Delay

在头文件中需要声明TimerHandle和功能函数,功能函数是计时结束后执行的功能
在源文件中利用GetWorldTimerManager()实现定时器的开启(绑定功能函数)和清除。

//.h
//自动开火的定时器
FTimerHandle FireTime;
//定时器的开始绑定函数
void StartFireTimer();
//功能函数 Delay之后的功能
void FireTimeFinished();

//.cpp
void StartFireTimer()
{
  GetWorldTimerManager().SetTimer(
    FireTime,  //FTimerHandle
    this,      //使用对象
    FireTimeFinied, //功能函数
    DelayTime  //延迟时间
  );
}

使用完定时器记得清除,防止其他一些情况导致定时器的开启

GetTimerManager().ClearTimer(FireTime);

时间轴

之前有一篇已经对时间轴有了较为详细的介绍
首先在.h中需要通过UCurveFloat创建一个Curve 用来接受我们在UE中创建好的曲线,这个曲线将作为时间轴中的结果曲线,第二点需要创建一个时间轴组件UTimelineComponent来实现时间轴的播放。第三点是创建一个轨道FOnTimelineFloat用于在时间轴中存放曲线(轨道的类型和曲线类型要相互对应)。第四点是功能函数Func1即利用曲线的返回值我们要对哪些参数进行修改。第五个是时间轴触发函数Func2,主要是被其他函数调用用于实现值的变化然后传给Func1。
除了第四点,其他都相当于蓝图中TimeLine模块,第四点相当于TimeLine模块之后连接的部分。

//.h
//时间轴组件
UPROPERTY(VisibleAnywhere)
	UTimelineComponent* DissolveTimeline;
//时间轴曲线
UPROPERTY(EditAnywhere)
	UCurveFloat* DissolveCurve;
//相当于蓝图中的轨道
  FOnTimelineFloat DissolveTrack;

//获取曲线上的值,用于更新我们要操作的值,相当于蓝图中的输出
UFUNCTION()
	void UpdateDissolveMaterial(float Dissolve);
//开始溶解,时间轴绑定上方那个获取值函数的地方,可以被外界调用
	void StartDissolve();

//.cpp

//时间轴初始化操作
void AXCharacter::StartDissolve()
{
  //绑定功能函数
	DissolveTrack.BindDynamic(this, &AXCharacter::UpdateDissolveMaterial);
	if (DissolveCurve && DissolveTimeline)
	{
    //为时间轴添加轨道和输出曲线
		DissolveTimeline->AddInterpFloat(DissolveCurve, DissolveTrack);
    //时间轴的正向播放,如果有其他的条件可以调用反向播放
		DissolveTimeline->Play();
	}
}

//功能函数UpdateDissolveMaterial,一般会有形参接受,该形参通过BindDynamic将时间轴上Curve值获得
void AXCharacter::UpdateDissolveMaterial(float Dissolve)
{
	if (DynamicDissolveMaterialInstance)
	{
		DynamicDissolveMaterialInstance->SetScalarParameterValue(TEXT("Dissolve"), Dissolve);
	}
}

之后就直接在外部调用StartDissolve这个函数即可。

UE4 网络同步 角色权威性

UE4提供了两种网络同步的方式,属性复制和RPC。
属性复制只能从服务器到客户端,可以通过设置DOREPLIFETIME的方式,体现出是只对本地客户端响应还是对所有客户端响应--(感觉有点类似ClientRPC 和 MulticastRPC)
其一般针对的是一个变量需要设置其变量属性UPROPERTY(ReplicatedUsing = OnRep_Func)
OnRep_Func会在变量改变时自动调用,其中的功能一般都是直接复制了服务器上Func的函数实现,并且OnRep_Func一般不携带参数,如果要携带也只能携带一个,且为该变量。
RPC在定义函数时需要在函数名之后加上_Implementation
RPC多用于客户端需要向服务器传递信息(ServerRPC),然后服务器处理完成之后再广播到客户端。比如本地客户端按下左键开火,这个状态就需要利用ServerRPC传递给服务器,服务器然后调用MulticastRPC传递给所有客户端。
具体操作就是,当我们角色开火时,调用ServerFire,如果是服务器那么就会执行ServerFire函数里的操作。如果ServerFire里面是一个Multicast那么就会服务器就会执行这个函数,将开火的这个客户端状态广播到所有客户端,具体是Multicast内部的实现,比如内部有一个Character实例,这个实例就是开火客户端上的这个actor。
如果要使用ClientRPC,一般也是通过ServerRPC调用执行的,因为ClientRPC只能在服务器上执行,而ServerRPC就是调用客户端上的函数在服务器上执行。所以如果想要获取服务器上的时间就需要在ServerRPC上实现GetTime,然后再ClientRPC上的GetTime就是客户端上的时间。

角色权威性可以用来区分是否是服务器,这样我们就可以将一些重要的数据操作放在服务器上执行。

if(HasAuthority())
{
  ApplyDamage();
}

角色模拟,除了本地客户端其他客户端上actor都表现为模拟,模拟就意味着没有PlayerController。比如开火函数,在应用伤害时需要添加一个控制器来确定当前开火的actor。如果我们将判断这个控制器是否有效放在了外面,将会导致其他功能在其他客户端上的失效

if(..&&Controller)
{
  if(HasAuthority())
  {
    ApplyDamage();
  }
  Func1();
  Func2();
  //func1 func2 不会在其他客户端实现
}

因为我们通过Multicast将这个函数传给了所有客户端执行,而其他客户端没有这个actor的Controller。
所以正确做法是

if(..)
{
  if(HasAuthority()&&Controller)
  {
    ApplyDamage();
  }
  Func1();
  Func2();
  //func1 func2 不会在其他客户端实现
}