ShooterGame-weapon

来源:互联网 发布:杭州淘宝摄影公司 编辑:程序博客网 时间:2024/06/05 12:45

ShooterDamageType.cpp

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.#include "ShooterGame.h"#include "Weapons/ShooterDamageType.h"UShooterDamageType::UShooterDamageType(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer){}

ShooterProjectile.cpp

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.#include "ShooterGame.h"#include "Weapons/ShooterProjectile.h"#include "Particles/ParticleSystemComponent.h"#include "Effects/ShooterExplosionEffect.h"AShooterProjectile::AShooterProjectile(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer){    CollisionComp = ObjectInitializer.CreateDefaultSubobject<USphereComponent>(this, TEXT("SphereComp"));    CollisionComp->InitSphereRadius(5.0f);    CollisionComp->AlwaysLoadOnClient = true;    CollisionComp->AlwaysLoadOnServer = true;    CollisionComp->bTraceComplexOnMove = true;    CollisionComp->SetCollisionEnabled(ECollisionEnabled::QueryOnly);    CollisionComp->SetCollisionObjectType(COLLISION_PROJECTILE);    CollisionComp->SetCollisionResponseToAllChannels(ECR_Ignore);    CollisionComp->SetCollisionResponseToChannel(ECC_WorldStatic, ECR_Block);    CollisionComp->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Block);    CollisionComp->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block);    RootComponent = CollisionComp;    ParticleComp = ObjectInitializer.CreateDefaultSubobject<UParticleSystemComponent>(this, TEXT("ParticleComp"));    ParticleComp->bAutoActivate = false;    ParticleComp->bAutoDestroy = false;    ParticleComp->SetupAttachment(RootComponent);    MovementComp = ObjectInitializer.CreateDefaultSubobject<UProjectileMovementComponent>(this, TEXT("ProjectileComp"));    MovementComp->UpdatedComponent = CollisionComp;    MovementComp->InitialSpeed = 2000.0f;    MovementComp->MaxSpeed = 2000.0f;    MovementComp->bRotationFollowsVelocity = true;    MovementComp->ProjectileGravityScale = 0.f;    PrimaryActorTick.bCanEverTick = true;    PrimaryActorTick.TickGroup = TG_PrePhysics;    SetRemoteRoleForBackwardsCompat(ROLE_SimulatedProxy);    bReplicates = true;    bReplicateMovement = true;}void AShooterProjectile::PostInitializeComponents(){    Super::PostInitializeComponents();    MovementComp->OnProjectileStop.AddDynamic(this, &AShooterProjectile::OnImpact);    CollisionComp->MoveIgnoreActors.Add(Instigator);    AShooterWeapon_Projectile* OwnerWeapon = Cast<AShooterWeapon_Projectile>(GetOwner());    if (OwnerWeapon)    {        OwnerWeapon->ApplyWeaponConfig(WeaponConfig);    }    SetLifeSpan( WeaponConfig.ProjectileLife );    MyController = GetInstigatorController();}void AShooterProjectile::InitVelocity(FVector& ShootDirection){    if (MovementComp)    {        MovementComp->Velocity = ShootDirection * MovementComp->InitialSpeed;    }}void AShooterProjectile::OnImpact(const FHitResult& HitResult){    if (Role == ROLE_Authority && !bExploded)    {        Explode(HitResult);        DisableAndDestroy();    }}void AShooterProjectile::Explode(const FHitResult& Impact){    if (ParticleComp)    {        ParticleComp->Deactivate();    }    // effects and damage origin shouldn't be placed inside mesh at impact point    const FVector NudgedImpactLocation = Impact.ImpactPoint + Impact.ImpactNormal * 10.0f;    if (WeaponConfig.ExplosionDamage > 0 && WeaponConfig.ExplosionRadius > 0 && WeaponConfig.DamageType)    {        UGameplayStatics::ApplyRadialDamage(this, WeaponConfig.ExplosionDamage, NudgedImpactLocation, WeaponConfig.ExplosionRadius, WeaponConfig.DamageType, TArray<AActor*>(), this, MyController.Get());    }    if (ExplosionTemplate)    {        FTransform const SpawnTransform(Impact.ImpactNormal.Rotation(), NudgedImpactLocation);        AShooterExplosionEffect* const EffectActor = GetWorld()->SpawnActorDeferred<AShooterExplosionEffect>(ExplosionTemplate, SpawnTransform);        if (EffectActor)        {            EffectActor->SurfaceHit = Impact;            UGameplayStatics::FinishSpawningActor(EffectActor, SpawnTransform);        }    }    bExploded = true;}void AShooterProjectile::DisableAndDestroy(){    UAudioComponent* ProjAudioComp = FindComponentByClass<UAudioComponent>();    if (ProjAudioComp && ProjAudioComp->IsPlaying())    {        ProjAudioComp->FadeOut(0.1f, 0.f);    }    MovementComp->StopMovementImmediately();    // give clients some time to show explosion    SetLifeSpan( 2.0f );}///CODE_SNIPPET_START: AActor::GetActorLocation AActor::GetActorRotationvoid AShooterProjectile::OnRep_Exploded(){    FVector ProjDirection = GetActorForwardVector();    const FVector StartTrace = GetActorLocation() - ProjDirection * 200;    const FVector EndTrace = GetActorLocation() + ProjDirection * 150;    FHitResult Impact;    if (!GetWorld()->LineTraceSingleByChannel(Impact, StartTrace, EndTrace, COLLISION_PROJECTILE, FCollisionQueryParams(TEXT("ProjClient"), true, Instigator)))    {        // failsafe        Impact.ImpactPoint = GetActorLocation();        Impact.ImpactNormal = -ProjDirection;    }    Explode(Impact);}///CODE_SNIPPET_ENDvoid AShooterProjectile::PostNetReceiveVelocity(const FVector& NewVelocity){    if (MovementComp)    {        MovementComp->Velocity = NewVelocity;    }}void AShooterProjectile::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const{    Super::GetLifetimeReplicatedProps( OutLifetimeProps );    DOREPLIFETIME( AShooterProjectile, bExploded );}

ShooterWeapon.cpp

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.#include "ShooterGame.h"#include "Weapons/ShooterWeapon.h"#include "Particles/ParticleSystemComponent.h"#include "Bots/ShooterAIController.h"#include "Online/ShooterPlayerState.h"#include "UI/ShooterHUD.h"AShooterWeapon::AShooterWeapon(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer){    Mesh1P = ObjectInitializer.CreateDefaultSubobject<USkeletalMeshComponent>(this, TEXT("WeaponMesh1P"));    Mesh1P->MeshComponentUpdateFlag = EMeshComponentUpdateFlag::OnlyTickPoseWhenRendered;    Mesh1P->bReceivesDecals = false;    Mesh1P->CastShadow = false;    Mesh1P->SetCollisionObjectType(ECC_WorldDynamic);    Mesh1P->SetCollisionEnabled(ECollisionEnabled::NoCollision);    Mesh1P->SetCollisionResponseToAllChannels(ECR_Ignore);    RootComponent = Mesh1P;    Mesh3P = ObjectInitializer.CreateDefaultSubobject<USkeletalMeshComponent>(this, TEXT("WeaponMesh3P"));    Mesh3P->MeshComponentUpdateFlag = EMeshComponentUpdateFlag::OnlyTickPoseWhenRendered;    Mesh3P->bReceivesDecals = false;    Mesh3P->CastShadow = true;    Mesh3P->SetCollisionObjectType(ECC_WorldDynamic);    Mesh3P->SetCollisionEnabled(ECollisionEnabled::NoCollision);    Mesh3P->SetCollisionResponseToAllChannels(ECR_Ignore);    Mesh3P->SetCollisionResponseToChannel(COLLISION_WEAPON, ECR_Block);    Mesh3P->SetCollisionResponseToChannel(ECC_Visibility, ECR_Block);    Mesh3P->SetCollisionResponseToChannel(COLLISION_PROJECTILE, ECR_Block);    Mesh3P->SetupAttachment(Mesh1P);    bLoopedMuzzleFX = false;    bLoopedFireAnim = false;    bPlayingFireAnim = false;    bIsEquipped = false;    bWantsToFire = false;    bPendingReload = false;    bPendingEquip = false;    CurrentState = EWeaponState::Idle;    CurrentAmmo = 0;    CurrentAmmoInClip = 0;    BurstCounter = 0;    LastFireTime = 0.0f;    PrimaryActorTick.bCanEverTick = true;    PrimaryActorTick.TickGroup = TG_PrePhysics;    SetRemoteRoleForBackwardsCompat(ROLE_SimulatedProxy);    bReplicates = true;    bNetUseOwnerRelevancy = true;}void AShooterWeapon::PostInitializeComponents(){    Super::PostInitializeComponents();    if (WeaponConfig.InitialClips > 0)    {        CurrentAmmoInClip = WeaponConfig.AmmoPerClip;        CurrentAmmo = WeaponConfig.AmmoPerClip * WeaponConfig.InitialClips;    }    DetachMeshFromPawn();}void AShooterWeapon::Destroyed(){    Super::Destroyed();    StopSimulatingWeaponFire();}//////////////////////////////////////////////////////////////////////////// Inventoryvoid AShooterWeapon::OnEquip(const AShooterWeapon* LastWeapon){    AttachMeshToPawn();    bPendingEquip = true;    DetermineWeaponState();    // Only play animation if last weapon is valid    if (LastWeapon)    {        float Duration = PlayWeaponAnimation(EquipAnim);        if (Duration <= 0.0f)        {            // failsafe            Duration = 0.5f;        }        EquipStartedTime = GetWorld()->GetTimeSeconds();        EquipDuration = Duration;        GetWorldTimerManager().SetTimer(TimerHandle_OnEquipFinished, this, &AShooterWeapon::OnEquipFinished, Duration, false);    }    else    {        OnEquipFinished();    }    if (MyPawn && MyPawn->IsLocallyControlled())    {        PlayWeaponSound(EquipSound);    }}void AShooterWeapon::OnEquipFinished(){    AttachMeshToPawn();    bIsEquipped = true;    bPendingEquip = false;    // Determine the state so that the can reload checks will work    DetermineWeaponState();     if (MyPawn)    {        // try to reload empty clip        if (MyPawn->IsLocallyControlled() &&            CurrentAmmoInClip <= 0 &&            CanReload())        {            StartReload();        }    }}void AShooterWeapon::OnUnEquip(){    DetachMeshFromPawn();    bIsEquipped = false;    StopFire();    if (bPendingReload)    {        StopWeaponAnimation(ReloadAnim);        bPendingReload = false;        GetWorldTimerManager().ClearTimer(TimerHandle_StopReload);        GetWorldTimerManager().ClearTimer(TimerHandle_ReloadWeapon);    }    if (bPendingEquip)    {        StopWeaponAnimation(EquipAnim);        bPendingEquip = false;        GetWorldTimerManager().ClearTimer(TimerHandle_OnEquipFinished);    }    DetermineWeaponState();}void AShooterWeapon::OnEnterInventory(AShooterCharacter* NewOwner){    SetOwningPawn(NewOwner);}void AShooterWeapon::OnLeaveInventory(){    if (Role == ROLE_Authority)    {        SetOwningPawn(NULL);    }    if (IsAttachedToPawn())    {        OnUnEquip();    }}void AShooterWeapon::AttachMeshToPawn(){    if (MyPawn)    {        // Remove and hide both first and third person meshes        DetachMeshFromPawn();        // For locally controller players we attach both weapons and let the bOnlyOwnerSee, bOwnerNoSee flags deal with visibility.        FName AttachPoint = MyPawn->GetWeaponAttachPoint();        if( MyPawn->IsLocallyControlled() == true )        {            USkeletalMeshComponent* PawnMesh1p = MyPawn->GetSpecifcPawnMesh(true);            USkeletalMeshComponent* PawnMesh3p = MyPawn->GetSpecifcPawnMesh(false);            Mesh1P->SetHiddenInGame( false );            Mesh3P->SetHiddenInGame( false );            Mesh1P->AttachToComponent(PawnMesh1p, FAttachmentTransformRules::KeepRelativeTransform, AttachPoint);            Mesh3P->AttachToComponent(PawnMesh3p, FAttachmentTransformRules::KeepRelativeTransform, AttachPoint);        }        else        {            USkeletalMeshComponent* UseWeaponMesh = GetWeaponMesh();            USkeletalMeshComponent* UsePawnMesh = MyPawn->GetPawnMesh();            UseWeaponMesh->AttachToComponent(UsePawnMesh, FAttachmentTransformRules::KeepRelativeTransform, AttachPoint);            UseWeaponMesh->SetHiddenInGame( false );        }    }}void AShooterWeapon::DetachMeshFromPawn(){    Mesh1P->DetachFromComponent(FDetachmentTransformRules::KeepRelativeTransform);    Mesh1P->SetHiddenInGame(true);    Mesh3P->DetachFromComponent(FDetachmentTransformRules::KeepRelativeTransform);    Mesh3P->SetHiddenInGame(true);}//////////////////////////////////////////////////////////////////////////// Inputvoid AShooterWeapon::StartFire(){    if (Role < ROLE_Authority)    {        ServerStartFire();    }    if (!bWantsToFire)    {        bWantsToFire = true;        DetermineWeaponState();    }}void AShooterWeapon::StopFire(){    if (Role < ROLE_Authority)    {        ServerStopFire();    }    if (bWantsToFire)    {        bWantsToFire = false;        DetermineWeaponState();    }}void AShooterWeapon::StartReload(bool bFromReplication){    if (!bFromReplication && Role < ROLE_Authority)    {        ServerStartReload();    }    if (bFromReplication || CanReload())    {        bPendingReload = true;        DetermineWeaponState();        float AnimDuration = PlayWeaponAnimation(ReloadAnim);               if (AnimDuration <= 0.0f)        {            AnimDuration = WeaponConfig.NoAnimReloadDuration;        }        GetWorldTimerManager().SetTimer(TimerHandle_StopReload, this, &AShooterWeapon::StopReload, AnimDuration, false);        if (Role == ROLE_Authority)        {            GetWorldTimerManager().SetTimer(TimerHandle_ReloadWeapon, this, &AShooterWeapon::ReloadWeapon, FMath::Max(0.1f, AnimDuration - 0.1f), false);        }        if (MyPawn && MyPawn->IsLocallyControlled())        {            PlayWeaponSound(ReloadSound);        }    }}void AShooterWeapon::StopReload(){    if (CurrentState == EWeaponState::Reloading)    {        bPendingReload = false;        DetermineWeaponState();        StopWeaponAnimation(ReloadAnim);    }}bool AShooterWeapon::ServerStartFire_Validate(){    return true;}void AShooterWeapon::ServerStartFire_Implementation(){    StartFire();}bool AShooterWeapon::ServerStopFire_Validate(){    return true;}void AShooterWeapon::ServerStopFire_Implementation(){    StopFire();}bool AShooterWeapon::ServerStartReload_Validate(){    return true;}void AShooterWeapon::ServerStartReload_Implementation(){    StartReload();}bool AShooterWeapon::ServerStopReload_Validate(){    return true;}void AShooterWeapon::ServerStopReload_Implementation(){    StopReload();}void AShooterWeapon::ClientStartReload_Implementation(){    StartReload();}//////////////////////////////////////////////////////////////////////////// Controlbool AShooterWeapon::CanFire() const{    bool bCanFire = MyPawn && MyPawn->CanFire();    bool bStateOKToFire = ( ( CurrentState ==  EWeaponState::Idle ) || ( CurrentState == EWeaponState::Firing) );       return (( bCanFire == true ) && ( bStateOKToFire == true ) && ( bPendingReload == false ));}bool AShooterWeapon::CanReload() const{    bool bCanReload = (!MyPawn || MyPawn->CanReload());    bool bGotAmmo = ( CurrentAmmoInClip < WeaponConfig.AmmoPerClip) && (CurrentAmmo - CurrentAmmoInClip > 0 || HasInfiniteClip());    bool bStateOKToReload = ( ( CurrentState ==  EWeaponState::Idle ) || ( CurrentState == EWeaponState::Firing) );    return ( ( bCanReload == true ) && ( bGotAmmo == true ) && ( bStateOKToReload == true) );   }//////////////////////////////////////////////////////////////////////////// Weapon usagevoid AShooterWeapon::GiveAmmo(int AddAmount){    const int32 MissingAmmo = FMath::Max(0, WeaponConfig.MaxAmmo - CurrentAmmo);    AddAmount = FMath::Min(AddAmount, MissingAmmo);    CurrentAmmo += AddAmount;    AShooterAIController* BotAI = MyPawn ? Cast<AShooterAIController>(MyPawn->GetController()) : NULL;    if (BotAI)    {        BotAI->CheckAmmo(this);    }    // start reload if clip was empty    if (GetCurrentAmmoInClip() <= 0 &&        CanReload() &&        MyPawn->GetWeapon() == this)    {        ClientStartReload();    }}void AShooterWeapon::UseAmmo(){    if (!HasInfiniteAmmo())    {        CurrentAmmoInClip--;    }    if (!HasInfiniteAmmo() && !HasInfiniteClip())    {        CurrentAmmo--;    }    AShooterAIController* BotAI = MyPawn ? Cast<AShooterAIController>(MyPawn->GetController()) : NULL;      AShooterPlayerController* PlayerController = MyPawn ? Cast<AShooterPlayerController>(MyPawn->GetController()) : NULL;    if (BotAI)    {        BotAI->CheckAmmo(this);    }    else if(PlayerController)    {        AShooterPlayerState* PlayerState = Cast<AShooterPlayerState>(PlayerController->PlayerState);        switch (GetAmmoType())        {            case EAmmoType::ERocket:                PlayerState->AddRocketsFired(1);                break;            case EAmmoType::EBullet:            default:                PlayerState->AddBulletsFired(1);                break;                  }    }}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();}bool AShooterWeapon::ServerHandleFiring_Validate(){    return true;}void AShooterWeapon::ServerHandleFiring_Implementation(){    const bool bShouldUpdateAmmo = (CurrentAmmoInClip > 0 && CanFire());    HandleFiring();    if (bShouldUpdateAmmo)    {        // update ammo        UseAmmo();        // update firing FX on remote clients        BurstCounter++;    }}void AShooterWeapon::ReloadWeapon(){    int32 ClipDelta = FMath::Min(WeaponConfig.AmmoPerClip - CurrentAmmoInClip, CurrentAmmo - CurrentAmmoInClip);    if (HasInfiniteClip())    {        ClipDelta = WeaponConfig.AmmoPerClip - CurrentAmmoInClip;    }    if (ClipDelta > 0)    {        CurrentAmmoInClip += ClipDelta;    }    if (HasInfiniteClip())    {        CurrentAmmo = FMath::Max(CurrentAmmoInClip, CurrentAmmo);    }}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::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::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::OnBurstFinished(){    // stop firing FX on remote clients    BurstCounter = 0;    // stop firing FX locally, unless it's a dedicated server    if (GetNetMode() != NM_DedicatedServer)    {        StopSimulatingWeaponFire();    }    GetWorldTimerManager().ClearTimer(TimerHandle_HandleFiring);    bRefiring = false;}//////////////////////////////////////////////////////////////////////////// Weapon usage helpersUAudioComponent* AShooterWeapon::PlayWeaponSound(USoundCue* Sound){    UAudioComponent* AC = NULL;    if (Sound && MyPawn)    {        AC = UGameplayStatics::SpawnSoundAttached(Sound, MyPawn->GetRootComponent());    }    return AC;}float AShooterWeapon::PlayWeaponAnimation(const FWeaponAnim& Animation){    float Duration = 0.0f;    if (MyPawn)    {        UAnimMontage* UseAnim = MyPawn->IsFirstPerson() ? Animation.Pawn1P : Animation.Pawn3P;        if (UseAnim)        {            Duration = MyPawn->PlayAnimMontage(UseAnim);        }    }    return Duration;}void AShooterWeapon::StopWeaponAnimation(const FWeaponAnim& Animation){    if (MyPawn)    {        UAnimMontage* UseAnim = MyPawn->IsFirstPerson() ? Animation.Pawn1P : Animation.Pawn3P;        if (UseAnim)        {            MyPawn->StopAnimMontage(UseAnim);        }    }}FVector AShooterWeapon::GetCameraAim() const{    AShooterPlayerController* const PlayerController = Instigator ? Cast<AShooterPlayerController>(Instigator->Controller) : NULL;    FVector FinalAim = FVector::ZeroVector;    if (PlayerController)    {        FVector CamLoc;        FRotator CamRot;        PlayerController->GetPlayerViewPoint(CamLoc, CamRot);        FinalAim = CamRot.Vector();    }    else if (Instigator)    {        FinalAim = Instigator->GetBaseAimRotation().Vector();           }    return FinalAim;}FVector AShooterWeapon::GetAdjustedAim() const{    AShooterPlayerController* const PlayerController = Instigator ? Cast<AShooterPlayerController>(Instigator->Controller) : NULL;    FVector FinalAim = FVector::ZeroVector;    // If we have a player controller use it for the aim    if (PlayerController)    {        FVector CamLoc;        FRotator CamRot;        PlayerController->GetPlayerViewPoint(CamLoc, CamRot);        FinalAim = CamRot.Vector();    }    else if (Instigator)    {        // Now see if we have an AI controller - we will want to get the aim from there if we do        AShooterAIController* AIController = MyPawn ? Cast<AShooterAIController>(MyPawn->Controller) : NULL;        if(AIController != NULL )        {            FinalAim = AIController->GetControlRotation().Vector();        }        else        {                       FinalAim = Instigator->GetBaseAimRotation().Vector();        }    }    return FinalAim;}FVector AShooterWeapon::GetCameraDamageStartLocation(const FVector& AimDir) const{    AShooterPlayerController* PC = MyPawn ? Cast<AShooterPlayerController>(MyPawn->Controller) : NULL;    AShooterAIController* AIPC = MyPawn ? Cast<AShooterAIController>(MyPawn->Controller) : NULL;    FVector OutStartTrace = FVector::ZeroVector;    if (PC)    {        // use player's camera        FRotator UnusedRot;        PC->GetPlayerViewPoint(OutStartTrace, UnusedRot);        // Adjust trace so there is nothing blocking the ray between the camera and the pawn, and calculate distance from adjusted start        OutStartTrace = OutStartTrace + AimDir * ((Instigator->GetActorLocation() - OutStartTrace) | AimDir);    }    else if (AIPC)    {        OutStartTrace = GetMuzzleLocation();    }    return OutStartTrace;}FVector AShooterWeapon::GetMuzzleLocation() const{    USkeletalMeshComponent* UseMesh = GetWeaponMesh();    return UseMesh->GetSocketLocation(MuzzleAttachPoint);}FVector AShooterWeapon::GetMuzzleDirection() const{    USkeletalMeshComponent* UseMesh = GetWeaponMesh();    return UseMesh->GetSocketRotation(MuzzleAttachPoint).Vector();}FHitResult AShooterWeapon::WeaponTrace(const FVector& StartTrace, const FVector& EndTrace) const{    static FName WeaponFireTag = FName(TEXT("WeaponTrace"));    // Perform trace to retrieve hit info    FCollisionQueryParams TraceParams(WeaponFireTag, true, Instigator);    TraceParams.bTraceAsyncScene = true;    TraceParams.bReturnPhysicalMaterial = true;    FHitResult Hit(ForceInit);    GetWorld()->LineTraceSingleByChannel(Hit, StartTrace, EndTrace, COLLISION_WEAPON, TraceParams);    return Hit;}void AShooterWeapon::SetOwningPawn(AShooterCharacter* NewOwner){    if (MyPawn != NewOwner)    {        Instigator = NewOwner;        MyPawn = NewOwner;        // net owner for RPC calls        SetOwner(NewOwner);    }   }//////////////////////////////////////////////////////////////////////////// Replication & effectsvoid AShooterWeapon::OnRep_MyPawn(){    if (MyPawn)    {        OnEnterInventory(MyPawn);    }    else    {        OnLeaveInventory();    }}void AShooterWeapon::OnRep_BurstCounter(){    if (BurstCounter > 0)    {        SimulateWeaponFire();    }    else    {        StopSimulatingWeaponFire();    }}void AShooterWeapon::OnRep_Reload(){    if (bPendingReload)    {        StartReload(true);    }    else    {        StopReload();    }}void AShooterWeapon::SimulateWeaponFire(){    if (Role == ROLE_Authority && CurrentState != EWeaponState::Firing)    {        return;    }    if (MuzzleFX)    {        USkeletalMeshComponent* UseWeaponMesh = GetWeaponMesh();        if (!bLoopedMuzzleFX || MuzzlePSC == NULL)        {            // Split screen requires we create 2 effects. One that we see and one that the other player sees.            if( (MyPawn != NULL ) && ( MyPawn->IsLocallyControlled() == true ) )            {                AController* PlayerCon = MyPawn->GetController();                               if( PlayerCon != NULL )                {                    Mesh1P->GetSocketLocation(MuzzleAttachPoint);                    MuzzlePSC = UGameplayStatics::SpawnEmitterAttached(MuzzleFX, Mesh1P, MuzzleAttachPoint);                    MuzzlePSC->bOwnerNoSee = false;                    MuzzlePSC->bOnlyOwnerSee = true;                    Mesh3P->GetSocketLocation(MuzzleAttachPoint);                    MuzzlePSCSecondary = UGameplayStatics::SpawnEmitterAttached(MuzzleFX, Mesh3P, MuzzleAttachPoint);                    MuzzlePSCSecondary->bOwnerNoSee = true;                    MuzzlePSCSecondary->bOnlyOwnerSee = false;                              }                           }            else            {                MuzzlePSC = UGameplayStatics::SpawnEmitterAttached(MuzzleFX, UseWeaponMesh, MuzzleAttachPoint);            }        }    }    if (!bLoopedFireAnim || !bPlayingFireAnim)    {        PlayWeaponAnimation(FireAnim);        bPlayingFireAnim = true;    }    if (bLoopedFireSound)    {        if (FireAC == NULL)        {            FireAC = PlayWeaponSound(FireLoopSound);        }    }    else    {        PlayWeaponSound(FireSound);    }    AShooterPlayerController* PC = (MyPawn != NULL) ? Cast<AShooterPlayerController>(MyPawn->Controller) : NULL;    if (PC != NULL && PC->IsLocalController())    {        if (FireCameraShake != NULL)        {            PC->ClientPlayCameraShake(FireCameraShake, 1);        }        if (FireForceFeedback != NULL)        {            PC->ClientPlayForceFeedback(FireForceFeedback, false, "Weapon");        }    }}void AShooterWeapon::StopSimulatingWeaponFire(){    if (bLoopedMuzzleFX )    {        if( MuzzlePSC != NULL )        {            MuzzlePSC->DeactivateSystem();            MuzzlePSC = NULL;        }        if( MuzzlePSCSecondary != NULL )        {            MuzzlePSCSecondary->DeactivateSystem();            MuzzlePSCSecondary = NULL;        }    }    if (bLoopedFireAnim && bPlayingFireAnim)    {        StopWeaponAnimation(FireAnim);        bPlayingFireAnim = false;    }    if (FireAC)    {        FireAC->FadeOut(0.1f, 0.0f);        FireAC = NULL;        PlayWeaponSound(FireFinishSound);    }}void AShooterWeapon::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const{    Super::GetLifetimeReplicatedProps( OutLifetimeProps );    DOREPLIFETIME( AShooterWeapon, MyPawn );    DOREPLIFETIME_CONDITION( AShooterWeapon, CurrentAmmo,       COND_OwnerOnly );    DOREPLIFETIME_CONDITION( AShooterWeapon, CurrentAmmoInClip, COND_OwnerOnly );    DOREPLIFETIME_CONDITION( AShooterWeapon, BurstCounter,      COND_SkipOwner );    DOREPLIFETIME_CONDITION( AShooterWeapon, bPendingReload,    COND_SkipOwner );}USkeletalMeshComponent* AShooterWeapon::GetWeaponMesh() const{    return (MyPawn != NULL && MyPawn->IsFirstPerson()) ? Mesh1P : Mesh3P;}class AShooterCharacter* AShooterWeapon::GetPawnOwner() const{    return MyPawn;}bool AShooterWeapon::IsEquipped() const{    return bIsEquipped;}bool AShooterWeapon::IsAttachedToPawn() const{    return bIsEquipped || bPendingEquip;}EWeaponState::Type AShooterWeapon::GetCurrentState() const{    return CurrentState;}int32 AShooterWeapon::GetCurrentAmmo() const{    return CurrentAmmo;}int32 AShooterWeapon::GetCurrentAmmoInClip() const{    return CurrentAmmoInClip;}int32 AShooterWeapon::GetAmmoPerClip() const{    return WeaponConfig.AmmoPerClip;}int32 AShooterWeapon::GetMaxAmmo() const{    return WeaponConfig.MaxAmmo;}bool AShooterWeapon::HasInfiniteAmmo() const{    const AShooterPlayerController* MyPC = (MyPawn != NULL) ? Cast<const AShooterPlayerController>(MyPawn->Controller) : NULL;    return WeaponConfig.bInfiniteAmmo || (MyPC && MyPC->HasInfiniteAmmo());}bool AShooterWeapon::HasInfiniteClip() const{    const AShooterPlayerController* MyPC = (MyPawn != NULL) ? Cast<const AShooterPlayerController>(MyPawn->Controller) : NULL;    return WeaponConfig.bInfiniteClip || (MyPC && MyPC->HasInfiniteClip());}float AShooterWeapon::GetEquipStartedTime() const{    return EquipStartedTime;}float AShooterWeapon::GetEquipDuration() const{    return EquipDuration;}

ShooterWeapon_Instant.cpp

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.#include "ShooterGame.h"#include "Weapons/ShooterWeapon_Instant.h"#include "Particles/ParticleSystemComponent.h"#include "Effects/ShooterImpactEffect.h"AShooterWeapon_Instant::AShooterWeapon_Instant(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer){    CurrentFiringSpread = 0.0f;}//////////////////////////////////////////////////////////////////////////// Weapon usagevoid AShooterWeapon_Instant::FireWeapon(){    const int32 RandomSeed = FMath::Rand();    FRandomStream WeaponRandomStream(RandomSeed);    const float CurrentSpread = GetCurrentSpread();    const float ConeHalfAngle = FMath::DegreesToRadians(CurrentSpread * 0.5f);    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);}bool AShooterWeapon_Instant::ServerNotifyHit_Validate(const FHitResult& Impact, FVector_NetQuantizeNormal ShootDir, int32 RandomSeed, float ReticleSpread){    return true;}void AShooterWeapon_Instant::ServerNotifyHit_Implementation(const FHitResult& Impact, FVector_NetQuantizeNormal ShootDir, int32 RandomSeed, float ReticleSpread){    const float WeaponAngleDot = FMath::Abs(FMath::Sin(ReticleSpread * PI / 180.f));    // if we have an instigator, calculate dot between the view and the shot    if (Instigator && (Impact.GetActor() || Impact.bBlockingHit))    {        const FVector Origin = GetMuzzleLocation();        const FVector ViewDir = (Impact.Location - Origin).GetSafeNormal();        // is the angle between the hit and the view within allowed limits (limit + weapon max angle)        const float ViewDotHitDir = FVector::DotProduct(Instigator->GetViewRotation().Vector(), ViewDir);        if (ViewDotHitDir > InstantConfig.AllowedViewDotHitDir - WeaponAngleDot)        {            if (CurrentState != EWeaponState::Idle)            {                if (Impact.GetActor() == NULL)                {                    if (Impact.bBlockingHit)                    {                        ProcessInstantHit_Confirmed(Impact, Origin, ShootDir, RandomSeed, ReticleSpread);                    }                }                // assume it told the truth about static things because the don't move and the hit                 // usually doesn't have significant gameplay implications                else if (Impact.GetActor()->IsRootComponentStatic() || Impact.GetActor()->IsRootComponentStationary())                {                    ProcessInstantHit_Confirmed(Impact, Origin, ShootDir, RandomSeed, ReticleSpread);                }                else                {                    // Get the component bounding box                    const FBox HitBox = Impact.GetActor()->GetComponentsBoundingBox();                    // calculate the box extent, and increase by a leeway                    FVector BoxExtent = 0.5 * (HitBox.Max - HitBox.Min);                    BoxExtent *= InstantConfig.ClientSideHitLeeway;                    // avoid precision errors with really thin objects                    BoxExtent.X = FMath::Max(20.0f, BoxExtent.X);                    BoxExtent.Y = FMath::Max(20.0f, BoxExtent.Y);                    BoxExtent.Z = FMath::Max(20.0f, BoxExtent.Z);                    // Get the box center                    const FVector BoxCenter = (HitBox.Min + HitBox.Max) * 0.5;                    // if we are within client tolerance                    if (FMath::Abs(Impact.Location.Z - BoxCenter.Z) < BoxExtent.Z &&                        FMath::Abs(Impact.Location.X - BoxCenter.X) < BoxExtent.X &&                        FMath::Abs(Impact.Location.Y - BoxCenter.Y) < BoxExtent.Y)                    {                        ProcessInstantHit_Confirmed(Impact, Origin, ShootDir, RandomSeed, ReticleSpread);                    }                    else                    {                        UE_LOG(LogShooterWeapon, Log, TEXT("%s Rejected client side hit of %s (outside bounding box tolerance)"), *GetNameSafe(this), *GetNameSafe(Impact.GetActor()));                    }                }            }        }        else if (ViewDotHitDir <= InstantConfig.AllowedViewDotHitDir)        {            UE_LOG(LogShooterWeapon, Log, TEXT("%s Rejected client side hit of %s (facing too far from the hit direction)"), *GetNameSafe(this), *GetNameSafe(Impact.GetActor()));        }        else        {            UE_LOG(LogShooterWeapon, Log, TEXT("%s Rejected client side hit of %s"), *GetNameSafe(this), *GetNameSafe(Impact.GetActor()));        }    }}bool AShooterWeapon_Instant::ServerNotifyMiss_Validate(FVector_NetQuantizeNormal ShootDir, int32 RandomSeed, float ReticleSpread){    return true;}void AShooterWeapon_Instant::ServerNotifyMiss_Implementation(FVector_NetQuantizeNormal ShootDir, int32 RandomSeed, float ReticleSpread){    const FVector Origin = GetMuzzleLocation();    // play FX on remote clients    HitNotify.Origin = Origin;    HitNotify.RandomSeed = RandomSeed;    HitNotify.ReticleSpread = ReticleSpread;    // play FX locally    if (GetNetMode() != NM_DedicatedServer)    {        const FVector EndTrace = Origin + ShootDir * InstantConfig.WeaponRange;        SpawnTrailEffect(EndTrace);    }}void AShooterWeapon_Instant::ProcessInstantHit(const FHitResult& Impact, const FVector& Origin, const FVector& ShootDir, int32 RandomSeed, float ReticleSpread){    if (MyPawn && MyPawn->IsLocallyControlled() && GetNetMode() == NM_Client)    {        // if we're a client and we've hit something that is being controlled by the server        if (Impact.GetActor() && Impact.GetActor()->GetRemoteRole() == ROLE_Authority)        {            // notify the server of the hit            ServerNotifyHit(Impact, ShootDir, RandomSeed, ReticleSpread);        }        else if (Impact.GetActor() == NULL)        {            if (Impact.bBlockingHit)            {                // notify the server of the hit                ServerNotifyHit(Impact, ShootDir, RandomSeed, ReticleSpread);            }            else            {                // notify server of the miss                ServerNotifyMiss(ShootDir, RandomSeed, ReticleSpread);            }        }    }    // process a confirmed hit    ProcessInstantHit_Confirmed(Impact, Origin, ShootDir, RandomSeed, ReticleSpread);}void AShooterWeapon_Instant::ProcessInstantHit_Confirmed(const FHitResult& Impact, const FVector& Origin, const FVector& ShootDir, int32 RandomSeed, float ReticleSpread){    // handle damage    if (ShouldDealDamage(Impact.GetActor()))    {        DealDamage(Impact, ShootDir);    }    // play FX on remote clients    if (Role == ROLE_Authority)    {        HitNotify.Origin = Origin;        HitNotify.RandomSeed = RandomSeed;        HitNotify.ReticleSpread = ReticleSpread;    }    // play FX locally    if (GetNetMode() != NM_DedicatedServer)    {        const FVector EndTrace = Origin + ShootDir * InstantConfig.WeaponRange;        const FVector EndPoint = Impact.GetActor() ? Impact.ImpactPoint : EndTrace;        SpawnTrailEffect(EndPoint);        SpawnImpactEffects(Impact);    }}bool AShooterWeapon_Instant::ShouldDealDamage(AActor* TestActor) const{    // if we're an actor on the server, or the actor's role is authoritative, we should register damage    if (TestActor)    {        if (GetNetMode() != NM_Client ||            TestActor->Role == ROLE_Authority ||            TestActor->bTearOff)        {            return true;        }    }    return false;}void AShooterWeapon_Instant::DealDamage(const FHitResult& Impact, const FVector& ShootDir){    FPointDamageEvent PointDmg;    PointDmg.DamageTypeClass = InstantConfig.DamageType;    PointDmg.HitInfo = Impact;    PointDmg.ShotDirection = ShootDir;    PointDmg.Damage = InstantConfig.HitDamage;    Impact.GetActor()->TakeDamage(PointDmg.Damage, PointDmg, MyPawn->Controller, this);}void AShooterWeapon_Instant::OnBurstFinished(){    Super::OnBurstFinished();    CurrentFiringSpread = 0.0f;}//////////////////////////////////////////////////////////////////////////// Weapon usage helpersfloat AShooterWeapon_Instant::GetCurrentSpread() const{    float FinalSpread = InstantConfig.WeaponSpread + CurrentFiringSpread;    if (MyPawn && MyPawn->IsTargeting())    {        FinalSpread *= InstantConfig.TargetingSpreadMod;    }    return FinalSpread;}//////////////////////////////////////////////////////////////////////////// Replication & effectsvoid AShooterWeapon_Instant::OnRep_HitNotify(){    SimulateInstantHit(HitNotify.Origin, HitNotify.RandomSeed, HitNotify.ReticleSpread);}void AShooterWeapon_Instant::SimulateInstantHit(const FVector& ShotOrigin, int32 RandomSeed, float ReticleSpread){    FRandomStream WeaponRandomStream(RandomSeed);    const float ConeHalfAngle = FMath::DegreesToRadians(ReticleSpread * 0.5f);    const FVector StartTrace = ShotOrigin;    const FVector AimDir = GetAdjustedAim();    const FVector ShootDir = WeaponRandomStream.VRandCone(AimDir, ConeHalfAngle, ConeHalfAngle);    const FVector EndTrace = StartTrace + ShootDir * InstantConfig.WeaponRange;    FHitResult Impact = WeaponTrace(StartTrace, EndTrace);    if (Impact.bBlockingHit)    {        SpawnImpactEffects(Impact);        SpawnTrailEffect(Impact.ImpactPoint);    }    else    {        SpawnTrailEffect(EndTrace);    }}void AShooterWeapon_Instant::SpawnImpactEffects(const FHitResult& Impact){    if (ImpactTemplate && Impact.bBlockingHit)    {        FHitResult UseImpact = Impact;        // trace again to find component lost during replication        if (!Impact.Component.IsValid())        {            const FVector StartTrace = Impact.ImpactPoint + Impact.ImpactNormal * 10.0f;            const FVector EndTrace = Impact.ImpactPoint - Impact.ImpactNormal * 10.0f;            FHitResult Hit = WeaponTrace(StartTrace, EndTrace);            UseImpact = Hit;        }        FTransform const SpawnTransform(Impact.ImpactNormal.Rotation(), Impact.ImpactPoint);        AShooterImpactEffect* EffectActor = GetWorld()->SpawnActorDeferred<AShooterImpactEffect>(ImpactTemplate, SpawnTransform);        if (EffectActor)        {            EffectActor->SurfaceHit = UseImpact;            UGameplayStatics::FinishSpawningActor(EffectActor, SpawnTransform);        }    }}void AShooterWeapon_Instant::SpawnTrailEffect(const FVector& EndPoint){    if (TrailFX)    {        const FVector Origin = GetMuzzleLocation();        UParticleSystemComponent* TrailPSC = UGameplayStatics::SpawnEmitterAtLocation(this, TrailFX, Origin);        if (TrailPSC)        {            TrailPSC->SetVectorParameter(TrailTargetParam, EndPoint);        }    }}void AShooterWeapon_Instant::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const{    Super::GetLifetimeReplicatedProps( OutLifetimeProps );    DOREPLIFETIME_CONDITION( AShooterWeapon_Instant, HitNotify, COND_SkipOwner );}

ShooterWeapon_Projectile.cpp

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.#include "ShooterGame.h"#include "Weapons/ShooterWeapon_Projectile.h"#include "Weapons/ShooterProjectile.h"AShooterWeapon_Projectile::AShooterWeapon_Projectile(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer){}//////////////////////////////////////////////////////////////////////////// Weapon usagevoid AShooterWeapon_Projectile::FireWeapon(){    FVector ShootDir = GetAdjustedAim();    FVector Origin = GetMuzzleLocation();    // trace from camera to check what's under crosshair    const float ProjectileAdjustRange = 10000.0f;    const FVector StartTrace = GetCameraDamageStartLocation(ShootDir);    const FVector EndTrace = StartTrace + ShootDir * ProjectileAdjustRange;    FHitResult Impact = WeaponTrace(StartTrace, EndTrace);    // and adjust directions to hit that actor    if (Impact.bBlockingHit)    {        const FVector AdjustedDir = (Impact.ImpactPoint - Origin).GetSafeNormal();        bool bWeaponPenetration = false;        const float DirectionDot = FVector::DotProduct(AdjustedDir, ShootDir);        if (DirectionDot < 0.0f)        {            // shooting backwards = weapon is penetrating            bWeaponPenetration = true;        }        else if (DirectionDot < 0.5f)        {            // check for weapon penetration if angle difference is big enough            // raycast along weapon mesh to check if there's blocking hit            FVector MuzzleStartTrace = Origin - GetMuzzleDirection() * 150.0f;            FVector MuzzleEndTrace = Origin;            FHitResult MuzzleImpact = WeaponTrace(MuzzleStartTrace, MuzzleEndTrace);            if (MuzzleImpact.bBlockingHit)            {                bWeaponPenetration = true;            }        }        if (bWeaponPenetration)        {            // spawn at crosshair position            Origin = Impact.ImpactPoint - ShootDir * 10.0f;        }        else        {            // adjust direction to hit            ShootDir = AdjustedDir;        }    }    ServerFireProjectile(Origin, ShootDir);}bool AShooterWeapon_Projectile::ServerFireProjectile_Validate(FVector Origin, FVector_NetQuantizeNormal ShootDir){    return true;}void AShooterWeapon_Projectile::ServerFireProjectile_Implementation(FVector Origin, FVector_NetQuantizeNormal ShootDir){    FTransform SpawnTM(ShootDir.Rotation(), Origin);    AShooterProjectile* Projectile = Cast<AShooterProjectile>(UGameplayStatics::BeginDeferredActorSpawnFromClass(this, ProjectileConfig.ProjectileClass, SpawnTM));    if (Projectile)    {        Projectile->Instigator = Instigator;        Projectile->SetOwner(this);        Projectile->InitVelocity(ShootDir);        UGameplayStatics::FinishSpawningActor(Projectile, SpawnTM);    }}void AShooterWeapon_Projectile::ApplyWeaponConfig(FProjectileWeaponData& Data){    Data = ProjectileConfig;}
0 0
原创粉丝点击