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 };
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 }
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 };
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 }
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 };
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 }
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 };
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 }
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
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" );
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 14 { 15 GENERATED_BODY() 16 17 18 protected: 19 20 virtual void Fire() override; 21 UPROPERTY(EditDefaultsOnly,Category = "ProjectileWeapon") 22 TSubclassOf<AActor> ProjectileClass; 23 };
1 // Fill out your copyright notice in the Description page of Project Settings. 2 3 #include "Public/SProjectileWeapon.h" 4 5 void ASProjectileWeapon::Fire() 6 { 7 AActor* MyOwner = GetOwner(); 8 if (MyOwner && ProjectileClass) 9 { 10 FVector EyeLocation; 11 FRotator EyeRotation; 12 MyOwner->GetActorEyesViewPoint(EyeLocation, EyeRotation); 13 14 FVector MuzzleLocation = MeshComp->GetSocketLocation(MuzzleSocketName); 15 16 FActorSpawnParameters SpawnParams; 17 SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; 18 19 GetWorld()->SpawnActor<AActor>(ProjectileClass,MuzzleLocation,EyeRotation, SpawnParams); 20 } 21 }