1. 程式人生 > >UE4 多人網路對戰遊戲筆記

UE4 多人網路對戰遊戲筆記

1.給物體施加一個徑向力

定義一個徑向力:

URadialForceComponent* RadialForceComp;

在建構函式裡賦預設值:

RadialForceComp = CreateDefaultSubobject<URadialForceComponent>(TEXT("RadialForceComp"));
RadialForceComp->SetupAttachment(MeshComp);
RadialForceComp->Radius = 250.0f; //力影響的半徑範圍
RadialForceComp->bImpulseVelChange = true;//作用力速率變化為真
RadialForceComp->bAutoActivate = false;//prevent component from ticking, and only use fireImpulse() instead把自動啟用關閉,用fireimpulse的方法來代替
RadialForceComp->bIgnoreOwningActor = true;//是否忽略自身

觸發:

RadialForceComp->FireImpulse();

 

 

2.建立一個ActorComponent處理主角掉血事件

新建一個類 繼承自UActorComponent:

class COOPGAME_API USHealthComponent : public UActorComponent

定義一個OnHealthChanged的事件:

UPROPERTY(BlueprintAssignable,Category = "Events")
FOnHealthChangedSignature OnHealthChanged;

在UCLASS的上面宣告這個事件:

DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(FOnHealthChangedSignature, USHealthComponent*, HealthComp, float, Health, float, HealthDelta, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser);//定義一個動態、多播、委託、六個引數的事件

定義一個HandleTakeAnyDamage的事件來處理受到的傷害:

UFUNCTION()
void HandleTakeAnyDamage(AActor* DamagedActor ,float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);

將主角受到的傷害動態繫結到HandleTakeAnyDamage上:

AActor* MyOwner = GetOwner();

if (MyOwner)
{
MyOwner->OnTakeAnyDamage.AddDynamic(this, &USHealthComponent::HandleTakeAnyDamage);
}

實現HandleTakeAnyDamage方法:

void USHealthComponent::HandleTakeAnyDamage(AActor * DamagedActor, float Damage, const UDamageType * DamageType, AController * InstigatedBy, AActor * DamageCauser)
{
if (Damage <= 0.0f)
{
return;
}

//update helth clamped
Health = FMath::Clamp(Health - Damage, 0.0f, DefaultHealth);

UE_LOG(LogTemp, Warning, TEXT("Health Changed: %s"),*FString::SanitizeFloat(Health));

OnHealthChanged.Broadcast(this, Health, Damage, DamageType, InstigatedBy, DamageCauser);//將引數廣播給OnHealthChanged
}

 

 

3.若要讓伺服器和客戶端同時表現一樣的效果,需要讓伺服器通知客戶端該做什麼,但程式碼依然在伺服器端執行

將是否爆破的UPROPERTY加上ReplicatedUsing = OnRep_Exploded:

UPROPERTY(ReplicatedUsing = OnRep_Exploded)
bool bExploded;

定義一個事件OnRep_Exploded將上述實現

UFUNCTION()
void OnRep_Exploded();

在血量為0時,同時執行 OnRep_Exploded() 函式,目的是為了讓客戶端表現和伺服器端一樣的狀態:

void ASExplosiveBarrel::OnHealthChanged(USHealthComponent * OwningHealthComp, float Health, float HealthDelta, const UDamageType * DamageType, AController * InstigatedBy, AActor * DamageCauser)
{
if (bExploded)
{
return;
}
if (Health <= 0.0f)
{
bExploded = true;
OnRep_Exploded();
FVector BoostIntensity = FVector::UpVector * ExplosionImpulse;
MeshComp->AddImpulse(BoostIntensity, NAME_None, true);

UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ExplosionEffect, GetActorLocation());
MeshComp->SetMaterial(0, ExplodedMaterial);
RadialForceComp->FireImpulse();
}
}

void ASExplosiveBarrel::OnRep_Exploded()
{
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ExplosionEffect, GetActorLocation());
MeshComp->SetMaterial(0, ExplodedMaterial);
}

接下來記得在生命週期中複製bExploded給客戶端,在cpp中引入標頭檔案#include"Net/UnrealNetwork.h"後即可直接新增GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const 方法,後面的引數可保持不變:

void ASExplosiveBarrel::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);

DOREPLIFETIME(ASExplosiveBarrel,bExploded);
}

 

4.客戶端伺服器端同步

在建構函式中設定可重複為true:

SetReplicates(true);

定義一個伺服器開火的事件,設定UFUNCTION裡面的內容:

UFUNCTION(Server, Reliable, WithValidation)
void ServerFire();

接著在cpp裡新增伺服器開火函式的_Implementation和_Validate,直接在ServerFire後新增(Validate有時候不需要):

void ASWeapon::ServerFire_Implementation()
{
Fire();
}

bool ASWeapon::ServerFire_Validate()
{
return true;
}

判斷Role是否在伺服器上:

if (Role < ROLE_Authority) //判斷Role是否在伺服器上
{
ServerFire();                       //Role < ROLE_Authority,說明不在伺服器上,讓Server處理這個開火
}

else{}          //在伺服器上,自己處理開火

 

 

 

 

 

 

原始碼貼:

 1 // Fill out your copyright notice in the Description page of Project Settings.
 2 
 3 #pragma once
 4 
 5 #include "CoreMinimal.h"
 6 #include "GameFramework/Character.h"
 7 #include"Public/SWeapon.h"
 8 #include "SCharacter.generated.h"
 9 
10 class UCameraComponent;
11 class USpringArmComponent;
12 class ASWeapon;
13 class USHealthComponent;
14 
15 UCLASS()
16 class COOPGAME_API ASCharacter : public ACharacter
17 {
18     GENERATED_BODY()
19 
20 public:
21     // Sets default values for this character's properties
22     ASCharacter();
23 
24 protected:
25     // Called when the game starts or when spawned
26     virtual void BeginPlay() override;
27 
28     void MoveForward(float value);
29 
30     void MoveRight(float value);
31 
32     void BeginCrouch();
33 
34     void EndCrouch();
35     UFUNCTION(BlueprintImplementableEvent)
36     void JumpFromAction();
37 
38     UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category = "Components")
39     UCameraComponent* CameraComp;
40 
41     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
42     USpringArmComponent* SpringArmComp;
43 
44     UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
45     USHealthComponent* HealthComp;
46 
47     bool bWantsToZoom;
48 
49 
50     UPROPERTY(EditDefaultsOnly, Category = "Player")
51     float ZoomedFOV;
52 
53     UPROPERTY(EditDefaultsOnly, Category = "Player",meta = (ClampMin = 0.1,ClampMax = 100))
54     float ZoomInterpSpeed;
55 
56     /*default FOV set during begin play*/
57     float DefaultFOV;
58 
59     void BeginZoom();
60 
61     void EndZoom();
62 
63     UPROPERTY(Replicated)
64     ASWeapon* CurrentWeapon;
65 
66     UPROPERTY(EditDefaultsOnly,Category = "Player")
67     TSubclassOf<ASWeapon> StarterWeaponClass;
68 
69     UPROPERTY(VisibleDefaultsOnly,Category = "Player")
70     FName WeaponAttachSocketName;
71     void StartFire();
72 
73     void StopFire();
74 
75     UFUNCTION()
76     void OnHealthChanged(USHealthComponent* OwingHealthComp, float Health, float HealthDelta, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
77 
78     UPROPERTY(Replicated,BlueprintReadOnly,Category = "Player")
79     bool bDied ;
80 public:    
81     // Called every frame
82     virtual void Tick(float DeltaTime) override;
83 
84     // Called to bind functionality to input
85     virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
86 
87     virtual FVector GetPawnViewLocation() const;
88     
89 };
SCharacter.h
  1 // Fill out your copyright notice in the Description page of Project Settings.
  2 
  3 #include "Public/SCharacter.h"
  4 #include"Camera/CameraComponent.h"
  5 #include"GameFramework/SpringArmComponent.h"
  6 #include"Components/CapsuleComponent.h"
  7 #include"CoopGame/CoopGame.h"
  8 #include"SHealthComponent.h"
  9 #include"GameFramework/PawnMovementComponent.h"
 10 #include"Net/UnrealNetwork.h"
 11 
 12 
 13 // Sets default values
 14 ASCharacter::ASCharacter()
 15 {
 16      // Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
 17     PrimaryActorTick.bCanEverTick = true;
 18 
 19     SpringArmComp = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpringArmComp"));
 20     SpringArmComp->bUsePawnControlRotation = true;
 21     SpringArmComp->SetupAttachment(RootComponent);
 22 
 23     GetMovementComponent()->GetNavAgentPropertiesRef().bCanCrouch = true;
 24 
 25     GetCapsuleComponent()->SetCollisionResponseToChannel(COLLISION_WEAPON, ECR_Ignore);
 26 
 27     HealthComp = CreateDefaultSubobject<USHealthComponent>(TEXT("HealthComp"));
 28 
 29     CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComp"));
 30     CameraComp->bUsePawnControlRotation = true;
 31     CameraComp->SetupAttachment(SpringArmComp);
 32 
 33     ZoomedFOV = 65.0f;
 34     ZoomInterpSpeed = 20.0f;
 35 
 36     WeaponAttachSocketName = "WeaponSocket";
 37 }
 38 
 39 // Called when the game starts or when spawned
 40 void ASCharacter::BeginPlay()
 41 {
 42     Super::BeginPlay();
 43     
 44     DefaultFOV = CameraComp->FieldOfView;
 45 
 46     HealthComp->OnHealthChanged.AddDynamic(this, &ASCharacter::OnHealthChanged);
 47 
 48     if (Role == ROLE_Authority) 
 49     {
 50 
 51     FActorSpawnParameters SpawnParams;
 52     SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
 53 
 54     CurrentWeapon = GetWorld()->SpawnActor<ASWeapon>(StarterWeaponClass, FVector::ZeroVector, FRotator::ZeroRotator, SpawnParams);
 55     if (CurrentWeapon) 
 56     {
 57         CurrentWeapon->SetOwner(this);
 58         CurrentWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale,WeaponAttachSocketName);
 59     }
 60 
 61     }
 62 }
 63 
 64 void ASCharacter::MoveForward(float value)
 65 {
 66     AddMovementInput(GetActorForwardVector()*value);
 67 }
 68 
 69 void ASCharacter::MoveRight(float value)
 70 {
 71     AddMovementInput(GetActorRightVector()*value);
 72 }
 73 
 74 void ASCharacter::BeginCrouch()
 75 {
 76     Crouch();
 77 }
 78 
 79 void ASCharacter::EndCrouch()
 80 {
 81     UnCrouch();
 82 }
 83 
 84 
 85 void ASCharacter::BeginZoom()
 86 {
 87     bWantsToZoom = true;
 88 }
 89 
 90 void ASCharacter::EndZoom()
 91 {
 92     bWantsToZoom = false;
 93 }
 94 
 95 void ASCharacter::StartFire()
 96 {
 97     if (CurrentWeapon) 
 98     {
 99         CurrentWeapon->StartFire();
100     }
101 }
102 
103 void ASCharacter::StopFire()
104 {
105     if (CurrentWeapon) 
106     {
107         CurrentWeapon->StopFire();
108     }
109 }
110 
111 void ASCharacter::OnHealthChanged(USHealthComponent* HealthComp, float Health, float HealthDelta, 
112     const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser)
113 {
114     if (Health <= 0.0f && !bDied) 
115     {
116         bDied = true;
117         GetMovementComponent()->StopMovementImmediately();
118         GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
119 
120         DetachFromControllerPendingDestroy();
121 
122         SetLifeSpan(10.0f);
123     }
124 }
125 
126 // Called every frame
127 void ASCharacter::Tick(float DeltaTime)
128 {
129     Super::Tick(DeltaTime);
130 
131     float TargetFOV = bWantsToZoom ? ZoomedFOV : DefaultFOV;
132 
133     float NewFOV = FMath::FInterpTo(CameraComp->FieldOfView, TargetFOV, DeltaTime, ZoomInterpSpeed);
134     CameraComp->SetFieldOfView(NewFOV);
135 
136 }
137 
138 // Called to bind functionality to input
139 void ASCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
140 {
141     Super::SetupPlayerInputComponent(PlayerInputComponent);
142 
143     PlayerInputComponent->BindAxis("MoveForward", this, &ASCharacter::MoveForward);
144     PlayerInputComponent->BindAxis("MoveRight", this, &ASCharacter::MoveRight);
145 
146     PlayerInputComponent->BindAxis("LookUp", this, &ASCharacter::AddControllerPitchInput);
147     PlayerInputComponent->BindAxis("Turn", this, &ASCharacter::AddControllerYawInput);
148 
149     PlayerInputComponent->BindAction("Crouch", IE_Pressed,this, &ASCharacter::BeginCrouch);
150     PlayerInputComponent->BindAction("Crouch", IE_Released,this, &ASCharacter::EndCrouch);
151 
152     PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ASCharacter::JumpFromAction);
153 
154     PlayerInputComponent->BindAction("Zoom", IE_Pressed, this, &ASCharacter::BeginZoom);
155     PlayerInputComponent->BindAction("Zoom", IE_Released, this, &ASCharacter::EndZoom);
156 
157     PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &ASCharacter::StartFire);
158     PlayerInputComponent->BindAction("Fire", IE_Released, this, &ASCharacter::StopFire);
159 }
160 
161 FVector ASCharacter::GetPawnViewLocation() const
162 {
163     if (CameraComp) 
164     {
165         return CameraComp->GetComponentLocation();
166     }
167 
168     return FVector();
169 }
170 void ASCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
171 {
172     Super::GetLifetimeReplicatedProps(OutLifetimeProps);
173 
174     DOREPLIFETIME(ASCharacter, CurrentWeapon);
175     DOREPLIFETIME(ASCharacter, bDied);
176 }
SCharacter.cpp
 1 // Fill out your copyright notice in the Description page of Project Settings.
 2 
 3 #pragma once
 4 
 5 #include "CoreMinimal.h"
 6 #include "GameFramework/Actor.h"
 7 #include "SExplosiveBarrel.generated.h"
 8 
 9 class USHealthComponent;
10 class URadialForceComponent;
11 class UParticleSystem;
12 UCLASS()
13 class COOPGAME_API ASExplosiveBarrel : public AActor
14 {
15     GENERATED_BODY()
16     
17 public:    
18     // Sets default values for this actor's properties
19     ASExplosiveBarrel();
20 
21 protected:
22     // Called when the game starts or when spawned
23     virtual void BeginPlay() override;
24 
25     UPROPERTY(VisibleAnywhere,Category = "Components")
26     USHealthComponent* HealthComp;
27 
28     UPROPERTY(VisibleAnywhere, Category = "Components")
29     UStaticMeshComponent* MeshComp;
30 
31     UPROPERTY(VisibleAnywhere, Category = "Components")
32     URadialForceComponent* RadialForceComp;
33 
34     UFUNCTION()
35         void OnHealthChanged(USHealthComponent* OwningHealthComp, float Health, float HealthDelta, 
36             const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
37 
38     UPROPERTY(ReplicatedUsing = OnRep_Exploded)
39         bool bExploded;
40 
41     UFUNCTION()
42         void OnRep_Exploded();
43 
44     UPROPERTY(EditDefaultsOnly, Category = "FX")
45         float ExplosionImpulse;
46     UPROPERTY(EditDefaultsOnly, Category = "FX")
47         UParticleSystem* ExplosionEffect;
48     UPROPERTY(EditDefaultsOnly, Category = "FX")
49         UMaterialInterface* ExplodedMaterial;
50 
51 
52 public:    
53 
54 
55     
56     
57 };
SExplosiveBarrel.h
 1 // Fill out your copyright notice in the Description page of Project Settings.
 2 
 3 #include "SExplosiveBarrel.h"
 4 #include"SHealthComponent.h"
 5 #include"Kismet/GameplayStatics.h"
 6 #include"PhysicsEngine/RadialForceComponent.h"
 7 #include"Components/StaticMeshComponent.h"
 8 #include"Net/UnrealNetwork.h"
 9 
10 
11 // Sets default values
12 ASExplosiveBarrel::ASExplosiveBarrel()
13 {
14      // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
15     HealthComp = CreateDefaultSubobject<USHealthComponent>(TEXT("HealthComp"));
16     HealthComp->OnHealthChanged.AddDynamic(this, &ASExplosiveBarrel::OnHealthChanged);
17 
18     MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComp"));
19     RootComponent = MeshComp;
20 
21     RadialForceComp = CreateDefaultSubobject<URadialForceComponent>(TEXT("RadialForceComp"));
22     RadialForceComp->SetupAttachment(MeshComp);
23     RadialForceComp->Radius = 250.0f;
24     RadialForceComp->bImpulseVelChange = true;
25     RadialForceComp->bAutoActivate = false;//prevent component from ticking, and only use fireImpulse() instead
26     RadialForceComp->bIgnoreOwningActor = true;//ignore self
27 
28     ExplosionImpulse = 400;
29 
30     SetReplicates(true);
31     SetReplicateMovement(true);
32 
33 }
34 
35 // Called when the game starts or when spawned
36 void ASExplosiveBarrel::BeginPlay()
37 {
38     Super::BeginPlay();
39     
40 }
41 
42 void ASExplosiveBarrel::OnHealthChanged(USHealthComponent * OwningHealthComp, float Health, float HealthDelta, const UDamageType * DamageType, AController * InstigatedBy, AActor * DamageCauser)
43 {
44     if (bExploded) 
45     {
46         return;
47     }
48     if (Health <= 0.0f) 
49     {
50         bExploded = true;
51         OnRep_Exploded();
52         FVector BoostIntensity = FVector::UpVector * ExplosionImpulse;
53         MeshComp->AddImpulse(BoostIntensity, NAME_None, true);
54 
55         UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ExplosionEffect, GetActorLocation());
56         MeshComp->SetMaterial(0, ExplodedMaterial);
57         RadialForceComp->FireImpulse();
58     }
59 }
60 
61 void ASExplosiveBarrel::OnRep_Exploded()
62 {
63     UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ExplosionEffect, GetActorLocation());
64     MeshComp->SetMaterial(0, ExplodedMaterial);
65 }
66 
67 void ASExplosiveBarrel::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
68 {
69     Super::GetLifetimeReplicatedProps(OutLifetimeProps);
70 
71     DOREPLIFETIME(ASExplosiveBarrel,bExploded);
72 }
SExplosiveBarrel.cpp
 1 // Fill out your copyright notice in the Description page of Project Settings.
 2 
 3 #pragma once
 4 
 5 #include "CoreMinimal.h"
 6 #include "Components/ActorComponent.h"
 7 #include "SHealthComponent.generated.h"
 8 
 9 //OnHealthChanged event
10 DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(FOnHealthChangedSignature, USHealthComponent*, HealthComp, float, Health, float, HealthDelta, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser);
11 UCLASS( ClassGroup=(COOP), meta=(BlueprintSpawnableComponent))
12 class COOPGAME_API USHealthComponent : public UActorComponent
13 {
14     GENERATED_BODY()
15 
16 public:    
17     // Sets default values for this component's properties
18     USHealthComponent();
19 
20 protected:
21     // Called when the game starts
22     virtual void BeginPlay() override;
23 
24     UPROPERTY(Replicated,BlueprintReadOnly,Category = "HealthComponent")
25     float Health;
26 
27     UPROPERTY(EditAnywhere,BlueprintReadWrite, Category = "HealthComponent")
28     float DefaultHealth;
29 
30     UFUNCTION()
31     void HandleTakeAnyDamage(AActor* DamagedActor ,float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
32 
33 
34 public:    
35 
36     UPROPERTY(BlueprintAssignable,Category = "Events")
37     FOnHealthChangedSignature    OnHealthChanged;
38     
39 };
SHealthComponent.h
 1 // Fill out your copyright notice in the Description page of Project Settings.
 2 
 3 #include "SHealthComponent.h"
 4 #include"Net/UnrealNetwork.h"
 5 
 6 
 7 // Sets default values for this component's properties
 8 USHealthComponent::USHealthComponent()
 9 {
10     DefaultHealth = 100;
11 
12     SetIsReplicated(true);
13 }
14 
15 
16 // Called when the game starts
17 void USHealthComponent::BeginPlay()
18 {
19     Super::BeginPlay();
20 
21     // ...
22     // Only hook if we are server
23     if (GetOwnerRole() == ROLE_Authority) 
24     {
25 
26         AActor* MyOwner = GetOwner();
27 
28         if (MyOwner)
29         {
30             MyOwner->OnTakeAnyDamage.AddDynamic(this, &USHealthComponent::HandleTakeAnyDamage);
31         }
32     }
33 
34 
35     Health = DefaultHealth;
36 }
37 
38 void USHealthComponent::HandleTakeAnyDamage(AActor * DamagedActor, float Damage, const UDamageType * DamageType, AController * InstigatedBy, AActor * DamageCauser)
39 {
40     if (Damage <= 0.0f) 
41     {
42         return;
43     }
44 
45     //update helth clamped
46     Health = FMath::Clamp(Health - Damage, 0.0f, DefaultHealth);
47 
48     UE_LOG(LogTemp, Warning, TEXT("Health Changed: %s"),*FString::SanitizeFloat(Health));
49 
50     OnHealthChanged.Broadcast(this, Health, Damage, DamageType, InstigatedBy, DamageCauser);
51 }
52 void USHealthComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
53 {
54     Super::GetLifetimeReplicatedProps(OutLifetimeProps);
55 
56     DOREPLIFETIME(USHealthComponent, Health);
57 }
SHealthComponent.cpp
  1 // Fill out your copyright notice in the Description page of Project Settings.
  2 
  3 #pragma once
  4 
  5 #include "CoreMinimal.h"
  6 #include "GameFramework/Actor.h"
  7 #include "SWeapon.generated.h"
  8 
  9 class UDamageType;
 10 class UParticleSystem;
 11 
 12 //
 13 USTRUCT()
 14 struct FHitScanTrace
 15 {
 16     GENERATED_BODY()
 17 
 18 public:
 19     UPROPERTY()
 20     TEnumAsByte<EPhysicalSurface> SurfaceType;
 21     UPROPERTY()
 22     FVector_NetQuantize TraceTo;
 23 };
 24 
 25 UCLASS()
 26 class COOPGAME_API ASWeapon : public AActor
 27 {
 28     GENERATED_BODY()
 29     
 30 public:    
 31     // Sets default values for this actor's properties
 32     ASWeapon();
 33 
 34 protected:
 35     // Called when the game starts or when spawned
 36     virtual void BeginPlay() override;
 37 
 38     UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category = "Components")
 39     USkeletalMeshComponent* MeshComp;
 40 
 41 
 42     void PlayFireEffects(FVector TraceEnd);
 43 
 44     void PlayImpatEffects(EPhysicalSurface SurfaceType, FVector ImpactPoint);
 45 
 46     UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
 47     TSubclassOf<UDamageType> DamageType;
 48 
 49     UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
 50     FName MuzzleSocketName;
 51     UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
 52     FName TracerTargetName;
 53 
 54     UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
 55     UParticleSystem* MuzzleEffect;
 56 
 57     UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
 58     UParticleSystem*DefaultImpactEffect;
 59 
 60     UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
 61     UParticleSystem*FleshImpactEffect;
 62 
 63     UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "Weapon")
 64     UParticleSystem*TraceEffect;
 65 
 66     UPROPERTY(EditDefaultsOnly, Category = "Weapon")
 67     TSubclassOf<UCameraShake> FireCameraShake;
 68 
 69     UPROPERTY(EditDefaultsOnly, Category = "Weapon")
 70     float BaseDamage;
 71 
 72     UFUNCTION(Server, Reliable, WithValidation)
 73     void ServerFire();
 74 
 75     FTimerHandle TimerHandle_TimeBetweenShots;
 76 
 77     float LastFireTime;
 78 
 79     /*RPM- Bullets per minute fired by weapon*/
 80     UPROPERTY(EditDefaultsOnly, Category = "Weapon")
 81     float RateOfFire;
 82 
 83     float TimeBetweenShots;
 84 
 85     UPROPERTY(ReplicatedUsing = OnRep_HitScanTrace)
 86     FHitScanTrace HitScanTrace;
 87 
 88     UFUNCTION()
 89     void OnRep_HitScanTrace();
 90 public:    
 91     // Called every frame
 92     virtual void Tick(float DeltaTime) override;
 93 
 94     UFUNCTION(BlueprintCallable, Category = "Weapon")
 95     virtual void Fire();
 96 
 97 
 98     void StartFire();
 99 
100     void StopFire();
101 
102     
103 };
SWeapon.h
  1 // Fill out your copyright notice in the Description page of Project Settings.
  2 
  3 #include "Public/SWeapon.h"
  4 #include"Components/SkeletalMeshComponent.h"
  5 #include"DrawDebugHelpers.h"
  6 #include"Kismet/GameplayStatics.h"
  7 #include"Particles/ParticleSystem.h"
  8 #include"PhysicalMaterials/PhysicalMaterial.h"
  9 #include"Particles/ParticleSystemComponent.h"
 10 #include"CoopGame.h"
 11 #include"TimerManager.h"
 12 #include"Net/UnrealNetwork.h"
 13 
 14 
 15 static int32 DebugWeaponDrawing = 0;
 16 FAutoConsoleVariableRef CVARDebugWeaponDrawing(
 17     TEXT("COOP.DebugWeapons"),
 18     DebugWeaponDrawing, 
 19     TEXT("Draw Debug Lines for Weapons"), 
 20     ECVF_Cheat);
 21 // Sets default values
 22 ASWeapon::ASWeapon()
 23 {
 24      // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
 25     PrimaryActorTick.bCanEverTick = true;
 26 
 27     MeshComp = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("MeshComp"));
 28     RootComponent = MeshComp;
 29 
 30     MuzzleSocketName = "MuzzleSocket";    
 31     TracerTargetName = "Target";
 32 
 33     BaseDamage = 20.0f;
 34 
 35     RateOfFire = 600;
 36 
 37     SetReplicates(true);
 38 
 39     NetUpdateFrequency = 66.0f;
 40     MinNetUpdateFrequency = 33.0f;
 41 }
 42 
 43 // Called when the game starts or when spawned
 44 void ASWeapon::BeginPlay()
 45 {
 46     Super::BeginPlay();
 47     
 48     TimeBetweenShots = 60 / RateOfFire;
 49 }
 50 
 51 void ASWeapon::Fire()
 52 {
 53     //Trace the World ,form pawn eyes to cross hair location
 54 
 55     if (Role < ROLE_Authority) 
 56     {
 57         ServerFire();
 58     }
 59 
 60     AActor* MyOwner = GetOwner();
 61     if (MyOwner) 
 62     {
 63         FVector EyeLocation;
 64         FRotator EyeRotation;
 65         MyOwner->GetActorEyesViewPoint(EyeLocation, EyeRotation);
 66 
 67         FVector ShotDirection = EyeRotation.Vector();
 68         FVector TraceEnd = EyeLocation + (ShotDirection * 10000);
 69 
 70         FCollisionQueryParams QueryParams;
 71         QueryParams.AddIgnoredActor(MyOwner);
 72         QueryParams.AddIgnoredActor(this);
 73         QueryParams.bTraceComplex = true;
 74         QueryParams.bReturnPhysicalMaterial = true;
 75 
 76         //Particle"Target"parameter
 77         FVector TracerEndPoint = TraceEnd;
 78 
 79         EPhysicalSurface SurfaceType = SurfaceType_Default;
 80 
 81         FHitResult Hit;
 82         if (GetWorld()->LineTraceSingleByChannel(Hit, EyeLocation, TraceEnd, COLLISION_WEAPON,QueryParams))
 83         {
 84             AActor* HitActor = Hit.GetActor();
 85 
 86             EPhysicalSurface SurfaceType = UPhysicalMaterial::DetermineSurfaceType(Hit.PhysMaterial.Get());
 87 
 88             float ActualDamage = BaseDamage;
 89             if (SurfaceType == SURFACE_FLESHVULNERABLE) 
 90             {  
 91                 //UE_LOG(LogTemp, Warning, TEXT(".........................."));
 92                  ActualDamage *= 4.0f;
 93             }
 94 
 95             UGameplayStatics::ApplyPointDamage(HitActor, ActualDamage, ShotDirection, Hit, MyOwner->GetInstigatorController(), this, DamageType);
 96 
 97             PlayImpatEffects(SurfaceType, Hit.ImpactPoint);
 98 
 99             TracerEndPoint = Hit.ImpactPoint;
100 
101             HitScanTrace.SurfaceType = SurfaceType;
102         }
103         if (DebugWeaponDrawing > 0)
104         {
105             DrawDebugLine(GetWorld(), EyeLocation, TraceEnd, FColor::White, false, 1.0f, 0, 1.0f);
106         }
107         PlayFireEffects(TracerEndPoint);
108 
109         if (Role == ROLE_Authority) 
110         {
111             HitScanTrace.TraceTo = TracerEndPoint;
112 
113         }
114     
115         LastFireTime = GetWorld()->TimeSeconds;
116     }
117 }
118 
119 void ASWeapon::StartFire()
120 {
121     float FirstDelay =FMath::Max(LastFireTime + TimeBetweenShots - GetWorld()->TimeSeconds,0.0f);
122 
123     GetWorldTimerManager().SetTimer(TimerHandle_TimeBetweenShots,this,&ASWeapon::Fire, TimeBetweenShots, true, FirstDelay);
124 
125 }
126 
127 void ASWeapon::StopFire()
128 {
129     GetWorldTimerManager().ClearTimer(TimerHandle_TimeBetweenShots);
130 }
131 
132 void ASWeapon::PlayFireEffects(FVector TraceEnd)
133 {
134     if (MuzzleEffect)
135     {
136         UGameplayStatics::SpawnEmitterAttached(MuzzleEffect, MeshComp, MuzzleSocketName);
137     }
138 
139     if (TraceEffect)
140     {
141         FVector MuzzleLocation = MeshComp->GetSocketLocation(MuzzleSocketName);
142 
143         UParticleSystemComponent* TracerComp = UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), TraceEffect, MuzzleLocation);
144 
145         if (TracerComp)
146         {
147             TracerComp->SetVectorParameter("Target", TraceEnd);
148         }
149     }
150 
151     APawn* MyOwner = Cast<APawn>(GetOwner());
152 
153     if (MyOwner) 
154     {
155         APlayerController* PC = Cast<APlayerController>(MyOwner->GetController());
156         if (PC) 
157         {
158             PC->ClientPlayCameraShake(FireCameraShake);
159         }
160 
161     }
162 }
163 
164 void ASWeapon::PlayImpatEffects(EPhysicalSurface SurfaceType,FVector ImpactPoint)
165 {
166     UParticleSystem* SelectedEffect = nullptr;
167     switch (SurfaceType)
168     {
169     case  SURFACE_FLESHDEFAULT:
170     case  SURFACE_FLESHVULNERABLE:
171         SelectedEffect = FleshImpactEffect;
172         break;
173     default:
174         SelectedEffect = DefaultImpactEffect;
175         break;
176     }
177     if (SelectedEffect)
178     {
179         FVector MuzzleLocation = MeshComp->GetSocketLocation(MuzzleSocketName);
180         FVector ShotDirection = ImpactPoint - MuzzleLocation;
181         ShotDirection.Normalize();
182         UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), SelectedEffect, ImpactPoint, ShotDirection.Rotation());
183 
184     }
185 
186 }
187 
188 void ASWeapon::ServerFire_Implementation()
189 {
190     Fire();
191 }
192 
193 bool ASWeapon::ServerFire_Validate()
194 {
195     return true;
196 }
197 
198 void ASWeapon::OnRep_HitScanTrace()
199 {
200     //play cosmetic FX
201     PlayFireEffects(HitScanTrace.TraceTo);
202     PlayImpatEffects(HitScanTrace.SurfaceType, HitScanTrace.TraceTo);
203 }
204 
205 // Called every frame
206 void ASWeapon::Tick(float DeltaTime)
207 {
208     Super::Tick(DeltaTime);
209 
210 }
211 
212 void ASWeapon::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
213 {
214     Super::GetLifetimeReplicatedProps(OutLifetimeProps);
215 
216     DOREPLIFETIME_CONDITION(ASWeapon, HitScanTrace,COND_SkipOwner);
217 }
SWeapon.cpp
 1 // Fill out your copyright notice in the Description page of Project Settings.
 2 
 3 #pragma once
 4 
 5 #include "CoreMinimal.h"
 6 
 7 #define SURFACE_FLESHDEFAULT        SurfaceType1
 8 #define SURFACE_FLESHVULNERABLE        SurfaceType2
 9 
10 #define COLLISION_WEAPON            ECC_GameTraceChannel1
CoopGame.h
1 // Fill out your copyright notice in the Description page of Project Settings.
2 
3 #include "CoopGame.h"
4 #include "Modules/ModuleManager.h"
5 
6 IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, CoopGame, "CoopGame" );
CoopGame.cpp
 1 // Fill out your copyright notice in the Description page of Project Settings.
 2 
 3 #pragma once
 4 
 5 #include "CoreMinimal.h"
 6 #include "SWeapon.h"
 7 #include "SProjectileWeapon.generated.h"
 8 
 9 /**
10  * 
11  */
12 UCLASS()
13 class COOPGAME_API ASProjectileWeapon : public ASWeapon