之前一直没搞懂按下鼠标左键开火之后,代码的逻辑是怎么走的,今天终于看懂了。但是我只是知道了代码怎么调用的,但是还没有深入看最后的开火函数的内容,我要继续研究一下ShooterWeapon.cpp里面的void AShooterWeapon::HandleFiring()方法
ShooterCharacter.cpp
void AShooterCharacter::OnStartFire() { AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(Controller); if (MyPC && MyPC->IsGameInputAllowed()) { if (IsRunning()) { SetRunning(false, false); } StartWeaponFire(); } }
void AShooterCharacter::StartWeaponFire() { if (!bWantsToFire) { bWantsToFire = true; if (CurrentWeapon) { CurrentWeapon->StartFire(); } } }
ShooterWeapon.cpp,其中Role==ROLE_Authority表示该程序运行在服务器。如果是客户端,则将调用ServerStartFire来调用服务端的StartFire方法。这是多人游戏中的机制
void AShooterWeapon::StartFire() { if (Role < ROLE_Authority) { ServerStartFire(); } if (!bWantsToFire) { bWantsToFire = true; DetermineWeaponState(); } }
void AShooterWeapon::DetermineWeaponState() { EWeaponState::Type NewState = EWeaponState::Idle; if (bIsEquipped) { if( bPendingReload ) { if( CanReload() == false ) { NewState = CurrentState; } else { NewState = EWeaponState::Reloading; } } else if ( (bPendingReload == false ) && ( bWantsToFire == true ) && ( CanFire() == true )) { NewState = EWeaponState::Firing; } } else if (bPendingEquip) { NewState = EWeaponState::Equipping; } SetWeaponState(NewState); }
void AShooterWeapon::SetWeaponState(EWeaponState::Type NewState) { const EWeaponState::Type PrevState = CurrentState; if (PrevState == EWeaponState::Firing && NewState != EWeaponState::Firing) { OnBurstFinished(); } CurrentState = NewState; if (PrevState != EWeaponState::Firing && NewState == EWeaponState::Firing) { OnBurstStarted(); } }
void AShooterWeapon::OnBurstStarted() { // start firing, can be delayed to satisfy TimeBetweenShots const float GameTime = GetWorld()->GetTimeSeconds(); if (LastFireTime > 0 && WeaponConfig.TimeBetweenShots > 0.0f && LastFireTime + WeaponConfig.TimeBetweenShots > GameTime) { GetWorldTimerManager().SetTimer(TimerHandle_HandleFiring, this, &AShooterWeapon::HandleFiring, LastFireTime + WeaponConfig.TimeBetweenShots - GameTime, false); } else { HandleFiring(); } }
void AShooterWeapon::HandleFiring() { if ((CurrentAmmoInClip > 0 || HasInfiniteClip() || HasInfiniteAmmo()) && CanFire()) { if (GetNetMode() != NM_DedicatedServer) { SimulateWeaponFire(); } if (MyPawn && MyPawn->IsLocallyControlled()) { FireWeapon(); UseAmmo(); // update firing FX on remote clients if function was called on server BurstCounter++; } } else if (CanReload()) { StartReload(); } else if (MyPawn && MyPawn->IsLocallyControlled()) { if (GetCurrentAmmo() == 0 && !bRefiring) { PlayWeaponSound(OutOfAmmoSound); AShooterPlayerController* MyPC = Cast<AShooterPlayerController>(MyPawn->Controller); AShooterHUD* MyHUD = MyPC ? Cast<AShooterHUD>(MyPC->GetHUD()) : NULL; if (MyHUD) { MyHUD->NotifyOutOfAmmo(); } } // stop weapon fire FX, but stay in Firing state if (BurstCounter > 0) { OnBurstFinished(); } } if (MyPawn && MyPawn->IsLocallyControlled()) { // local client will notify server if (Role < ROLE_Authority) { ServerHandleFiring(); } // reload after firing last round if (CurrentAmmoInClip <= 0 && CanReload()) { StartReload(); } // setup refire timer bRefiring = (CurrentState == EWeaponState::Firing && WeaponConfig.TimeBetweenShots > 0.0f); if (bRefiring) { GetWorldTimerManager().SetTimer(TimerHandle_HandleFiring, this, &AShooterWeapon::HandleFiring, WeaponConfig.TimeBetweenShots, false); } } LastFireTime = GetWorld()->GetTimeSeconds(); }