捋一下开火整体流程
客户端按键开火
角色代码中,在开火按键绑定的函数里,检查战斗组件
if(Combat)
{
Combat->FireButtonPressed(true);
}
在战斗组件的FireButtonPressed
函数这:
void UCombatComponent::FireButtonPressed(bool bPressed)
{
bFireButtonPressed = bPressed;
if (bFireButtonPressed)
{
Fire();
}
}
然后调用了Fire()
函数,主要得经过CanFire()
函数检查确保能够射击:
void UCombatComponent::Fire()
{
if (CanFire())
{
bCanFire = false;
ServerFire(HitTarget);
if (EquippedWeapon)
{
// 本地更新准星导致准星扩大
CrosshairShootingFactor = .75f;
}
StartFireTimer();
}
}
而对于ServerFire
函数只是调用了MulticastFire
,这是一个多播,将服务器的射击结果传回客户端:
void UCombatComponent::ServerFire_Implementation(const FVector_NetQuantize& TraceHitTarget)
{
MulticastFire(TraceHitTarget);
}
显然对于作为多播RPC的MulticastFire
,服务端执行,然后才是客户端。这个函数主要就是播放人物动画、设置人物状态和调用EquippedWeapon->Fire(TraceHitTarget);
有些东西完全可以本地来做
比如说Fire()
函数,其中枪械射击动画和生成弹壳完全可以本地立即播放而不用等待多播RPC复制回来再进行完整的射击流程。
当然,射击的弹药数目及其更新还是得经手服务器的,这是关键的数据。
即除非在服务器上,否则开火不修改弹药。
具体到射弹武器,射弹也应该只在服务器生成,造成伤害也应该只在服务器进行
这里特别要注意的是射击的散布服务器和客户端可能不一样。
所以结论就是可以有本地的LocalFire()
在ServerFire()
函数调用的同时也被调用,但是内容是普通的ServerFire()
的精简版,只有一些“表面工作”,也就是播放一些效果什么的。本地可以做权威服务器做的任何事情,但是不可以修改任何数据。
所以LocalFire()
会执行MulticastFire()
函数做的一切(逻辑上的开火工作)
这些东西可以在有延迟的客户端按下开火键立即处理,显得游戏响应的非常快。但是实际造成伤害并反馈到客户端的时间仍有延迟,且和网络有关系。
void UCombatComponent::ServerFire_Implementation(const FVector_NetQuantize& TraceHitTarget)
{
MulticastFire(TraceHitTarget);
}
void UCombatComponent::MulticastFire_Implementation(const FVector_NetQuantize& TraceHitTarget)
{
if (Character && Character->IsLocallyControlled() && !Character->HasAuthority()) return;
LocalFire(TraceHitTarget);
}
然后这样下来的流程就是按下开火按键后一系列的调用来到UCombatComponent::Fire()
被调用:
void UCombatComponent::Fire()
{
if (CanFire())
{
// 开火时不能再开火,得等这个开火完才行
bCanFire = false;
ServerFire(HitTarget);
LocalFire(HitTarget);
if (EquippedWeapon)
{
// 本地更新准星导致准星扩大
CrosshairShootingFactor = .75f;
}
// 非自动武器需要有计时器
StartFireTimer();
}
}
直接请求服务器射击后,就先本地播放射击动画LocalFire(HitTarget)
,这个播放的数据取自本地(有可能在本地看起来是命中了),并且这对于开枪的客户端来说已经播放一次了(不需要在播放第二次)。
这里需要指明的是造成伤害和播放动画与否以及播放的动画击中与否是无关的。
所以因为ServerFire()
,它会计算出结果后调用多播,我们需要确保Multicast
不会为进行那个射击的客户端再播放一遍动画,所以也就是在其他客户端上,会调用LocalFire()
其实就是把整个射击过程能提前的单独拿出来做为了一个函数,本地的就让它提前播放,不必等到多播传回来再进行。多播的就照常等多播过去再播放