.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "GameCharacter.generated.h"
class ASWeapon;
class HSHealthComponent;
UCLASS()
class NOSTOPP_API AGameCharacter : public ACharacter
{
GENERATED_BODY()
public:
// Sets default values for this character's properties
AGameCharacter();
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Camera)
class UCameraComponent*CameraComp;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = CameraBoom)
class USpringArmComponent*Cameraboom;
UPROPERTY(EditDefaultsOnly, Category = "Player")
TSubclassOf<ASWeapon> StarterWeaponClass;
UPROPERTY(VisibleDefaultsOnly, Category = "Player")
FName WeaponAttachSocketName;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
virtual FVector GetPawnViewLocation() const override;
protected:
void MoveForward(float value);
void MoveRight(float value);
void BeginCrouch();
void EndCrouch();
void StartFire();
void EndFire();
void StopRun();
void Run();
void BeginZoom();
void EndZoom();
bool bWantToBoom;
USHealthComponent*HealthComp;
UPROPERTY(Replicated)
ASWeapon* CurrentWeapon;
UPROPERTY(EditDefaultsOnly, Category = "Player")
float ZoomFov;
UPROPERTY(EditDefaultsOnly, Category = "Player", meta = (ClampMin = 0.0, ClampMax = 100))//meta是指把flaot限制在0.1到100之间
float ZoomInterpSpeed;
float DefaultFov;
UPROPERTY(Replicated,BlueprintReadOnly,Category="Player")
bool bDied;
UFUNCTION()
void OnHealthChanged(USHealthComponent* OwningHealthComponent, float Health, float HealthDelta, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "GameCharacter.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/PawnMovementComponent.h"
#include "Public/SWeapon.h"
#include "Components/CapsuleComponent.h"
#include "NoStopP.h"
#include "Components/SHealthComponent.h"
#include "Net/UnrealNetwork.h"
#include "GameFramework/CharacterMovementComponent.h"
// Sets default values
AGameCharacter::AGameCharacter()
{
// Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
Cameraboom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
Cameraboom->bUsePawnControlRotation = true;
Cameraboom->SetupAttachment(RootComponent);
GetMovementComponent()->GetNavAgentPropertiesRef().bCanCrouch = true;
GetCapsuleComponent()->SetCollisionResponseToChannel(COLLISION_WEAPON, ECR_Ignore);
HealthComp = CreateDefaultSubobject<USHealthComponent>(TEXT("HealthComp"));
CameraComp = CreateDefaultSubobject<UCameraComponent>(TEXT("CameraComp"));
CameraComp->SetupAttachment(Cameraboom);
ZoomFov = 65.0f;
ZoomInterpSpeed = 20;
WeaponAttachSocketName = "WeaponSocket";
}
// Called when the game starts or when spawned
void AGameCharacter::BeginPlay()
{
Super::BeginPlay();
DefaultFov = CameraComp->FieldOfView;
HealthComp->OnHealthChanged.AddDynamic(this, &AGameCharacter::OnHealthChanged);
if (Role==ROLE_Authority)
{
FActorSpawnParameters SpawnParams;
SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
CurrentWeapon = GetWorld()->SpawnActor<ASWeapon>(StarterWeaponClass, FVector::ZeroVector, FRotator::ZeroRotator, SpawnParams);
if (CurrentWeapon)
{
CurrentWeapon->SetOwner(this);
CurrentWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, WeaponAttachSocketName);
}
}
//生成默认的武器
}
// Called every frame
void AGameCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
float TargetFov = bWantToBoom ? ZoomFov : DefaultFov;
float NewFov = FMath::FInterpTo(CameraComp->FieldOfView, TargetFov, DeltaTime, ZoomInterpSpeed);
CameraComp->SetFieldOfView(NewFov);
}
FVector AGameCharacter::GetPawnViewLocation() const
{
if (CameraComp)
{
return CameraComp->GetComponentLocation();
}
return Super::GetPawnViewLocation();
}
void AGameCharacter::OnHealthChanged( USHealthComponent* HealthComp, float Health, float HealthDelta, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser)
{
if (Health<=0.0f&& !bDied)
{
//死亡
bDied = true;
GetMovementComponent()->StopMovementImmediately();
GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
DetachFromControllerPendingDestroy();
SetLifeSpan(1.0f);
}
}
void AGameCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AGameCharacter,CurrentWeapon);
DOREPLIFETIME(AGameCharacter, bDied);
}
// Called to bind functionality to input
void AGameCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAction("jump", IE_Pressed, this, &AGameCharacter::Jump);
PlayerInputComponent->BindAction("Crouch", IE_Pressed, this, &AGameCharacter::BeginCrouch);
PlayerInputComponent->BindAction("Crouch", IE_Released, this, &AGameCharacter::EndCrouch);
PlayerInputComponent->BindAction("Zoom", IE_Pressed, this, &AGameCharacter::BeginZoom);
PlayerInputComponent->BindAction("Zoom", IE_Released, this, &AGameCharacter::EndZoom);
PlayerInputComponent->BindAction("Fire", IE_Pressed, this, &AGameCharacter::StartFire);
PlayerInputComponent->BindAction("Fire", IE_Released, this, &AGameCharacter::EndFire);
PlayerInputComponent->BindAction("Run", IE_Pressed, this, &AGameCharacter::Run);
PlayerInputComponent->BindAction("Run", IE_Released, this, &AGameCharacter::StopRun);
PlayerInputComponent->BindAxis("MoveForward", this, &AGameCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AGameCharacter::MoveRight);
PlayerInputComponent->BindAxis("CameraX", this, &AGameCharacter::AddControllerPitchInput);
PlayerInputComponent->BindAxis("CameraY", this, &AGameCharacter::AddControllerYawInput);
}
void AGameCharacter::MoveForward(float value)
{
AddMovementInput(GetActorForwardVector()*value);
}
void AGameCharacter::MoveRight(float value)
{
AddMovementInput(GetActorRightVector()*value);
}
void AGameCharacter::BeginCrouch()
{
Crouch();
}
void AGameCharacter::EndCrouch()
{
UnCrouch();
}
void AGameCharacter::BeginZoom()
{
bWantToBoom = true;
}
void AGameCharacter::EndZoom()
{
bWantToBoom = false;
}
void AGameCharacter::StartFire()
{
if (CurrentWeapon)
{
CurrentWeapon->StartFire();
}
}
void AGameCharacter::EndFire()
{
if (CurrentWeapon)
{
CurrentWeapon->EndFire();
}
}
void AGameCharacter::StopRun()
{
GetCharacterMovement()->MaxWalkSpeed = 300.f;
}
void AGameCharacter::Run()
{
GetCharacterMovement()->MaxWalkSpeed =1200.f;
}
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SWeapon.generated.h"
USTRUCT()
struct FHitScanTrace
{
GENERATED_BODY()
public:
UPROPERTY()
TEnumAsByte<EPhysicalSurface> SurfaceType;
UPROPERTY()
FVector_NetQuantize TraceTo;
};
UCLASS()
class NOSTOPP_API ASWeapon : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ASWeapon();
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "weapon")
class USkeletalMeshComponent*MeshComponent;
public:
void StartFire();
void EndFire();
protected:
virtual void BeginPlay() override;
void Fire();
void PlayFireEffect(FVector TraceEnd);
void PlayImpactEffects(EPhysicalSurface SurfaceType, FVector ImpactPoint);
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "DAMAGE")
class TSubclassOf<UDamageType> DamageType;
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "weapon")
FName MuzzleSocketName;
UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "weapon")
FName TraceTargetName;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "weapon")
class UParticleSystem*MuzzleEffect;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "weapon")
class UParticleSystem*DefaultImpactEffect;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "weapon")
class UParticleSystem*FleshImpactEffect;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "weapon")
class UParticleSystem*TraceEffect;
UPROPERTY(EditDefaultsOnly, Category = "weapon")
TSubclassOf<UCameraShake> FireCamShake;
UPROPERTY(EditDefaultsOnly, Category = "weapon")
float BaseDamage;
UFUNCTION(Server, Reliable, WithValidation)
void ServerFire();
UPROPERTY(ReplicatedUsing = OnRep_HitScanTrace)
FHitScanTrace HitScanTrace;
UFUNCTION()
void OnRep_HitScanTrace();
float LastFireTime;
//Second Fire Damo
UPROPERTY(EditDefaultsOnly, Category = "weapon")
float RateOfFire;
//Derived From RateOfRate
float TimerBetweenShots;
FTimerHandle TimerHandle_TimeBetweenShot;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "SWeapon.h"
#include "Components/SkeletalMeshComponent.h"
#include "DrawDebugHelpers.h"
#include "Kismet/GameplayStatics.h"
#include "Particles/ParticleSystem.h"
#include "Particles/ParticleSystemComponent.h"
#include "NoStopP.h"
#include "PhysicalMaterials/PhysicalMaterial.h"
#include "TimerManager.h"
#include "Net/UnrealNetwork.h"
static int32 DebugWeaponDrawing = 0;
FAutoConsoleVariable CVARDebugWeaponDrawing(TEXT("COOP.DebugWeapons"), DebugWeaponDrawing, TEXT("Draw Debug Lines for Weapons"),
ECVF_Cheat);
// Sets default values
ASWeapon::ASWeapon()
{
MeshComponent = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("MeshComp"));
RootComponent = MeshComponent;
MuzzleSocketName = "MuzzleSocket";
TraceTargetName = "Target";
BaseDamage = 20.0f;
RateOfFire = 600;
SetReplicates(true);
NetUpdateFrequency = 66.0f;
MinNetUpdateFrequency = 33.0f;
}
void ASWeapon::BeginPlay()
{
Super::BeginPlay();
TimerBetweenShots = 60 / RateOfFire;
}
void ASWeapon::StartFire()
{
float FirstDelay = FMath::Max(LastFireTime + TimerBetweenShots - GetWorld()->TimeSeconds, 0.0f);
GetWorldTimerManager().SetTimer(TimerHandle_TimeBetweenShot, this, &ASWeapon::Fire, TimerBetweenShots, true, 0.0f);
Fire();
}
void ASWeapon::EndFire()
{
GetWorldTimerManager().ClearTimer(TimerHandle_TimeBetweenShot);
}
void ASWeapon::Fire()
{
if (Role < ROLE_Authority)
{
ServerFire();
}
AActor*MyOwner = GetOwner();//蓝图spawn函数里面有个owner持有者用主角self就可以使用
if (MyOwner)
{
FVector EyeLocation;//发射位置
FRotator EyeRotation;//发射角度
MyOwner->GetActorEyesViewPoint(EyeLocation, EyeRotation);//(这函数是获取actor双眼视角)获取发射位置和角度
FVector ShotDiretion = EyeRotation.Vector();
FVector traceEnd = EyeLocation + (ShotDiretion * 10000);//最多发射到的位置
FCollisionQueryParams QueryParams;//碰撞通道
QueryParams.AddIgnoredActor(MyOwner);
QueryParams.AddIgnoredActor(this);
QueryParams.bTraceComplex = true;
QueryParams.bReturnPhysicalMaterial = true;
//粒子“目的”参数
FVector TraceEndPoint = traceEnd;
EPhysicalSurface SurfaceType = SurfaceType_Default;
FHitResult Hit;//碰撞hit返回结果
if (GetWorld()->LineTraceSingleByChannel(Hit, EyeLocation, traceEnd, COLLISION_WEAPON, QueryParams))
{
//阻挡hit。点击伤害
AActor*HitActor = Hit.GetActor();
EPhysicalSurface SurfaceType = UPhysicalMaterial::DetermineSurfaceType(Hit.PhysMaterial.Get());
float ActualDamage = BaseDamage;
if (SurfaceType == SURFACE_FLESHVULNERABLE)
{
ActualDamage *= 4.0f;
}
UGameplayStatics::ApplyPointDamage(HitActor, ActualDamage, ShotDiretion, Hit, MyOwner->GetInstigatorController(), this, DamageType);
PlayImpactEffects(SurfaceType, Hit.ImpactPoint);
TraceEndPoint = Hit.ImpactPoint;
}
if (DebugWeaponDrawing > 0)
{
DrawDebugLine(GetWorld(), EyeLocation, traceEnd, FColor::White, false, 1.0f, 0, 1.0f);
}
/* DrawDebugLine(GetWorld(), EyeLocation, traceEnd, FColor::White, false, 1.0f, 0, 1.0f);*/
PlayFireEffect(TraceEndPoint);
if (Role == ROLE_Authority)
{
HitScanTrace.TraceTo = TraceEndPoint;
}
LastFireTime = GetWorld()->TimeSeconds;
}
}
void ASWeapon::PlayFireEffect(FVector TraceEnd)
{
if (MuzzleEffect)
{
UGameplayStatics::SpawnEmitterAttached(MuzzleEffect, MeshComponent, MuzzleSocketName);
}
if (TraceEffect)
{
FVector MuzzleLocation = MeshComponent->GetSocketLocation(MuzzleSocketName);
UParticleSystemComponent*TraceComp = UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), TraceEffect, MuzzleLocation);
if (TraceComp)
{
TraceComp->SetVectorParameter(TraceTargetName, TraceEnd);
}
}
APawn*MyOwner = Cast<APawn>(GetOwner());
if (MyOwner)
{
APlayerController*PC = Cast<APlayerController>(MyOwner->GetController());
if (PC)
{
PC->ClientPlayCameraShake(FireCamShake);
}
}
}
//多人播放冲击FX粒子
void ASWeapon::PlayImpactEffects(EPhysicalSurface SurfaceType, FVector ImpactPoint)
{
UParticleSystem*SelectedEffect = nullptr;
switch (SurfaceType)
{
case SURFACE_FLESHDEFAULT:
case SURFACE_FLESHVULNERABLE:
break;
SelectedEffect = FleshImpactEffect;
default:
SelectedEffect = DefaultImpactEffect;
break;
}
if (SelectedEffect)
{
FVector MuzzleLocation = MeshComponent->GetSocketLocation(MuzzleSocketName);
FVector ShotDirection = ImpactPoint - MuzzleLocation;
ShotDirection.Normalize();
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), SelectedEffect, ImpactPoint, ShotDirection.Rotation());
}
}
void ASWeapon::OnRep_HitScanTrace()
{//播发外部特效FX
PlayFireEffect(HitScanTrace.TraceTo);
PlayImpactEffects(HitScanTrace.SurfaceType, HitScanTrace.TraceTo);
}
//实现多人射击
void ASWeapon::ServerFire_Implementation()
{
Fire();
}
bool ASWeapon::ServerFire_Validate()
{
return true;
}
void ASWeapon::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME_CONDITION(ASWeapon, HitScanTrace, COND_SkipOwner);
}
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "SHealthComponent.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_SixParams(FOnHealthChangedSignature, USHealthComponent*, HealthComp, float, Health, float, HealthDelta, const class UDamageType*, DamageType, class AController*, InstigatedBy, AActor*, DamageCauser);
UCLASS( ClassGroup=(COOP), meta=(BlueprintSpawnableComponent) )
class NOSTOPP_API USHealthComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
USHealthComponent();
protected:
// Called when the game starts
virtual void BeginPlay() override;
UPROPERTY(Replicated,EditAnywhere, BlueprintReadOnly, Category = "HealthComponent")
float Health;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "HealthComponent")
float DefaultHealth;
UFUNCTION()
void HandleTakeAnyDamage( AActor* DamagedActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser);
public:
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnHealthChangedSignature OnHealthChanged;
};
#include "SHealthComponent.h"
#include "Net/UnrealNetwork.h"
// Sets default values for this component's properties
USHealthComponent::USHealthComponent()
{
DefaultHealth = 100;
// ...
SetIsReplicated(true);
}
// Called when the game starts
void USHealthComponent::BeginPlay()
{
Super::BeginPlay();
//仅当我们为服务器时
if (GetOwnerRole()==ROLE_Authority)
{
AActor*MyOwner = GetOwner();
if (MyOwner)
{
MyOwner->OnTakeAnyDamage.AddDynamic(this, &USHealthComponent::HandleTakeAnyDamage);
}
Health = DefaultHealth;
}
// ...
}
void USHealthComponent::HandleTakeAnyDamage(AActor* DamagedActor, float Damage, const class UDamageType* DamageType, class AController* InstigatedBy, AActor* DamageCauser)
{
if (Damage <= 0.0f)
{
return;
}
Health = FMath::Clamp(Health - Damage, 0.0f, DefaultHealth);
UE_LOG(LogTemp, Log, TEXT("Health Changed:%s"), *FString::SanitizeFloat(Health));
OnHealthChanged.Broadcast(this, Health, Damage, DamageType, InstigatedBy, DamageCauser);
}
// Called every frame
void USHealthComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// ...
}
void USHealthComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(USHealthComponent, Health);
}
#pragma once
#include "CoreMinimal.h"
#define SURFACE_FLESHDEFAULT SurfaceType1
#define SURFACE_FLESHVULNERABLE SurfaceType2
#define COLLISION_WEAPON ECC_GameTraceChannel1
效果图,