zoukankan      html  css  js  c++  java
  • 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); }
  • 相关阅读:
    Redis进阶
    redis常用指令
    MarkDown基本语法
    JAVA多线程面试
    使用POI操作Excel
    IDEA+GIT的使用
    获取地址栏的参数
    mybatis逆向工程
    遍历map集合
    springboot批量删除
  • 原文地址:https://www.cnblogs.com/heben/p/8046726.html
Copyright © 2011-2022 走看看