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
- ShooterGame-weapon
- Weapon
- Weapon
- shootergame-pickup
- ShooterGame常见问题解答
- ue4 weapon
- hdu 4617 : Weapon
- hdu4617 Weapon(立体几何题)
- hdu 4617 Weapon
- hdu 4617 Weapon (几何)
- 【hdoj 4617】Weapon
- HDU 4617 Weapon
- HDU.4617 Weapon
- 淌水 UE4的shootergame 案例 准备
- ue4 shooterGame 第一步 搭建git linux服务器
- Total Commander, what a weapon!
- HDU 4617 Weapon 解题报告
- hdu 4617 Weapon(几何)
- AlDl跨进程服务之电话拦截
- Builder构造器模式在struts2中示例学习
- TCP协议的PSH标识详解
- /etc/bashrc和/etc/profile傻傻分不清楚?
- Can't use 'defined(@array)' (Maybe you should just omit the defined()?)
- ShooterGame-weapon
- PHP连接数据库
- HTC vive开发:关于手柄按键对接控制
- 【经典算法】:图中的最小生成树算法之Prim算法和Kruskal算法
- Android官方开发文档Training系列课程中文版:电池续航时间优化之检查与监测坞的状态与类型
- 课程设计之贪吃蛇小游戏制造
- windows环境meidawiki部署
- RxJava操作符
- PHP源码阅读之源码目录结构