UE4 ShooterGame Demo的開火的代碼


之前一直沒搞懂按下鼠標左鍵開火之后,代碼的邏輯是怎么走的,今天看懂了之前沒看懂的部分,進了一步

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();
    }
}

下面標紅的FireWeapon這個函數,實際上在AShooterWeapon中是一個虛函數,並且沒有實現,實現是放在了子類中。它有兩個子類,分別是AShooterWeapon_Instant(彈道類)和AShooterWeapon_Projectile(投擲類)

我覺得肯定真正使用到的類就是這兩個子類,所以包括之前的這個流程,實際上都是子類中的,只不過是從父類中繼承而已,當然我此時此刻沒有去深究那些方法的可見性修飾符,是否會被繼承這些細節問題,我先暫時這么認為吧

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();
}

 

void AShooterWeapon_Instant::FireWeapon()
{
    const int32 RandomSeed = FMath::Rand();
    FRandomStream WeaponRandomStream(RandomSeed);
    const float CurrentSpread = GetCurrentSpread();
    const float ConeHalfAngle = FMath::DegreesToRadians(CurrentSpread * 0.5f);

   //獲取AShooterPlayerController的rotation的單位向量
const FVector AimDir = GetAdjustedAim(); const FVector StartTrace = GetCameraDamageStartLocation(AimDir); const FVector ShootDir = WeaponRandomStream.VRandCone(AimDir, ConeHalfAngle, ConeHalfAngle); const FVector EndTrace = StartTrace + ShootDir * InstantConfig.WeaponRange; const FHitResult Impact = WeaponTrace(StartTrace, EndTrace); ProcessInstantHit(Impact, StartTrace, ShootDir, RandomSeed, CurrentSpread); CurrentFiringSpread = FMath::Min(InstantConfig.FiringSpreadMax, CurrentFiringSpread + InstantConfig.FiringSpreadIncrement); }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM