ThirdPersonMPProjectile.h
// 版权所有 1998-2019 Epic Games, Inc。保留所有权利。
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "ThirdPersonMPProjectile.generated.h"
class UParticleSystem;
class UStaticMeshComponent;
class USphereComponent;
class UProjectileMovementComponent;
class UDamageType;
UCLASS()
class THIRDPERSONMP_API AThirdPersonMPProjectile : public AActor
{
GENERATED_BODY()
public:
// 为此Actor的属性设置默认值
AThirdPersonMPProjectile();
// 此投射物的基本组件
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components")
USphereComponent* SphereComponent;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components")
UStaticMeshComponent* StaticMesh;
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components")
UProjectileMovementComponent* ProjectileMovementComponent;
UPROPERTY(EditAnywhere, Category = "Effects")
UParticleSystem* ExplosionEffect;
//此投射物将造成的伤害类型和伤害。
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Damage")
TSubclassOf<UDamageType> DamageType;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Damage")
float Damage;
protected:
// 当游戏开始或生成时调用
virtual void BeginPlay() override;
UFUNCTION()
void OnProjectileImpact(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit);
void Destroyed() override;
public:
// 每一帧调用
virtual void Tick(float DeltaTime) override;
};
ThirdPersonMPProjectile.cpp
// 版权所有 1998-2019 Epic Games, Inc。保留所有权利。
#include "ThirdPersonMPProjectile.h"
#include "Components/SphereComponent.h"
#include "Components/StaticMeshComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "GameFramework/DamageType.h"
#include "Particles/ParticleSystem.h"
#include "Kismet/GameplayStatics.h"
#include "UObject/ConstructorHelpers.h"
#include "IDamageInterface.h"
// 设置默认值
AThirdPersonMPProjectile::AThirdPersonMPProjectile()
{
// 将此Actor设置为每一帧调用Tick()。如果不需要,可以关闭此选项来提高性能。
PrimaryActorTick.bCanEverTick = true;
bReplicates = true;
DamageType = UDamageType::StaticClass();
Damage = 10.0f;
static ConstructorHelpers::FObjectFinder<UParticleSystem> DefaultExplosionEffect(TEXT("/Game/StarterContent/Particles/P_Explosion.P_Explosion"));
if (DefaultExplosionEffect.Succeeded())
{
ExplosionEffect = DefaultExplosionEffect.Object;
}
//定义将作为投射物及其碰撞的根组件的SphereComponent。
SphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("RootComponent"));
SphereComponent->InitSphereRadius(12.5f);
SphereComponent->SetCollisionProfileName(TEXT("BlockAllDynamic"));
RootComponent = SphereComponent;
//在击中事件上注册此投射物撞击函数。
if (GetLocalRole() == ROLE_Authority)
{
SphereComponent->OnComponentHit.AddDynamic(this, &AThirdPersonMPProjectile::OnProjectileImpact);
}
//定义将作为视觉呈现的网格体。
static ConstructorHelpers::FObjectFinder<UStaticMesh> DefaultMesh(TEXT("/Game/StarterContent/Shapes/Shape_Sphere.Shape_Sphere"));
StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
StaticMesh->SetupAttachment(RootComponent);
if (DefaultMesh.Succeeded())
{
StaticMesh->SetStaticMesh(DefaultMesh.Object);
StaticMesh->RelativeLocation = FVector(0.0f, 0.0f, -12.5f);
StaticMesh->RelativeScale3D = FVector(0.25f, 0.25f, 0.25f);
}
//定义投射物移动组件。
ProjectileMovementComponent = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovement"));
ProjectileMovementComponent->SetUpdatedComponent(SphereComponent);
ProjectileMovementComponent->InitialSpeed = 1500.0f;
ProjectileMovementComponent->MaxSpeed = 1500.0f;
ProjectileMovementComponent->bRotationFollowsVelocity = true;
ProjectileMovementComponent->ProjectileGravityScale = 0.0f;
}
// 当游戏开始或生成时调用
void AThirdPersonMPProjectile::BeginPlay()
{
Super::BeginPlay();
}
void AThirdPersonMPProjectile::OnProjectileImpact(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit)
{
if ( OtherActor )
{
UGameplayStatics::ApplyPointDamage(OtherActor, Damage, NormalImpulse, Hit, Instigator->Controller, this, DamageType);
}
Destroy();
}
void AThirdPersonMPProjectile::Destroyed()
{
FVector spawnLocation = GetActorLocation();
UGameplayStatics::SpawnEmitterAtLocation(this, ExplosionEffect, spawnLocation, FRotator::ZeroRotator, true, EPSCPoolMethod::AutoRelease);
}
// 每一帧调用
void AThirdPersonMPProjectile::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
ThirdPersonMPCharacter.h
// 版权所有 1998-2019 Epic Games, Inc。保留所有权利。
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "IDamageInterface.h"
#include "ThirdPersonMPCharacter.generated.h"
UCLASS(config=Game)
class AThirdPersonMPCharacter : public ACharacter
{
GENERATED_BODY()
/** 摄像机吊杆将摄像机置于角色身后 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class USpringArmComponent* CameraBoom;
/** 跟随摄像机 */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera, meta = (AllowPrivateAccess = "true"))
class UCameraComponent* FollowCamera;
public:
/** 构造函数 */
AThirdPersonMPCharacter();
/** 属性复制 */
void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
/** 基础旋转速度,单位为度/秒。其他缩放比例可能会影响最终旋转速度。*/
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
float BaseTurnRate;
/** 基础向上/下看速度,单位为度/秒。其他缩放比例可能会影响最终速度。*/
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=Camera)
float BaseLookUpRate;
protected:
/** 玩家的最大生命值。这是玩家的最高生命值,也是出生时的生命值。*/
UPROPERTY(EditDefaultsOnly, Category = "Health")
float MaxHealth;
/** 玩家的当前生命值。降到0就表示死亡。*/
UPROPERTY(ReplicatedUsing=OnRep_CurrentHealth)
float CurrentHealth;
/** RepNotify,用于同步对当前生命值所做的更改。*/
UFUNCTION()
void OnRep_CurrentHealth();
/** 响应要更新的生命值。修改后,立即在服务器上调用,并在客户端上调用以响应RepNotify*/
void OnHealthUpdate();
public:
/** 最大生命值的取值函数。*/
UFUNCTION(BlueprintPure, Category="Health")
FORCEINLINE float GetMaxHealth() const { return MaxHealth; }
/** 当前生命值的取值函数。*/
UFUNCTION(BlueprintPure, Category="Health")
FORCEINLINE float GetCurrentHealth() const { return CurrentHealth; }
/** 当前生命值的存值函数。将此值的范围限定在0到MaxHealth之间,并调用OnHealthUpdate。仅在服务器上调用。*/
UFUNCTION(BlueprintCallable, Category="Health")
void SetCurrentHealth(float healthValue);
/** 承受伤害的事件。从APawn覆盖。*/
UFUNCTION(BlueprintCallable, Category = "Health")
float TakeDamage( float DamageTaken, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser ) override;
protected:
/** 在VR中重置HMD方向。*/
void OnResetVR();
/** 调用用于向前/向后输入 */
void MoveForward(float Value);
/** 调用用于侧向输入 */
void MoveRight(float Value);
/**
* 通过输入调用,以给定速度旋转。
* @param Rate 这是标准化速度,即1.0表示100%的所需旋转速度
*/
void TurnAtRate(float Rate);
/**
* 通过输入调用,以给定速度向上/下看。
* @param Rate 这是标准化速度,即1.0表示100%的所需旋转速度
*/
void LookUpAtRate(float Rate);
/** 触控输入开始时使用的处理程序。*/
void TouchStarted(ETouchIndex::Type FingerIndex, FVector Location);
/** 触控输入停止时使用的处理程序。*/
void TouchStopped(ETouchIndex::Type FingerIndex, FVector Location);
protected:
// APawn界面
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
// APawn界面结束
/** 角色要发射的发射物类型。*/
UPROPERTY(EditDefaultsOnly, Category="Gameplay|Projectile")
TSubclassOf<class AThirdPersonMPProjectile> ProjectileClass;
/** 射击之间的延迟,单位为秒。用于控制测试发射物的射击速度,还可防止服务器函数的溢出导致将SpawnProjectile直接绑定至输入。*/
UPROPERTY(EditDefaultsOnly, Category="Gameplay")
float FireRate;
/** 若为true,此武器正在发射过程中。*/
bool bIsFiringWeapon;
/** 用于启动武器发射的函数。应仅可由本地玩家触发。*/
UFUNCTION(BlueprintCallable, Category="Gameplay")
void StartFire();
/** 用于结束武器射击的函数。一旦调用这段代码,玩家可再次使用StartFire。*/
UFUNCTION(BlueprintCallable, Category = "Gameplay")
void StopFire();
/** 用于生成投射物的服务器函数。*/
UFUNCTION(Server, Reliable)
void HandleFire();
/** 定时器句柄,用于提供生成间隔时间内的射速延迟。*/
FTimerHandle FiringTimer;
public:
/** 返回CameraBoom子对象 **/
FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
/** 返回FollowCamera子对象 **/
FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
};
ThirdPersonMPCharacter.cpp
// 版权所有 1998-2019 Epic Games, Inc。保留所有权利。
#include "ThirdPersonMPCharacter.h"
#include "HeadMountedDisplayFunctionLibrary.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/Controller.h"
#include "GameFramework/SpringArmComponent.h"
#include "Net/UnrealNetwork.h"
#include "Engine/Engine.h"
#include "ThirdPersonMPProjectile.h"
#include "TimerManager.h"
//
// AThirdPersonMPCharacter
AThirdPersonMPCharacter::AThirdPersonMPCharacter()
{
// 设置碰撞胶囊体的大小
GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
// 设置输入的旋转速度
BaseTurnRate = 45.f;
BaseLookUpRate = 45.f;
// 控制器旋转时不旋转。只影响摄像机。
bUseControllerRotationPitch = false;
bUseControllerRotationYaw = false;
bUseControllerRotationRoll = false;
// 配置角色移动
GetCharacterMovement()->bOrientRotationToMovement = true; // 角色朝输入的方向移动...
GetCharacterMovement()->RotationRate = FRotator(0.0f, 540.0f, 0.0f); // ...采用此旋转速度
GetCharacterMovement()->JumpZVelocity = 600.f;
GetCharacterMovement()->AirControl = 0.2f;
// 创建摄像机吊杆(发生碰撞时向玩家拉近)
CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
CameraBoom->SetupAttachment(RootComponent);
CameraBoom->TargetArmLength = 300.0f; // 摄像机以这个距离跟在角色身后
CameraBoom->bUsePawnControlRotation = true; // 基于控制器旋转吊臂
// 创建跟随摄像头
FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName); // 将摄像机连接到吊杆末端,调节吊杆以匹配控制器方向
FollowCamera->bUsePawnControlRotation = false; // 摄像机不相对于吊臂旋转
// 注意:骨架网格体和网格体组件上的动画蓝图引用(继承自角色)
// 都在名为MyCharacter的派生蓝图资产中设置(以避免C++环境下的直接内容引用)
//初始化玩家生命值
MaxHealth = 100.0f;
CurrentHealth = MaxHealth;
//初始化投射物类
ProjectileClass = AThirdPersonMPProjectile::StaticClass();
//初始化射速
FireRate = 0.25f;
bIsFiringWeapon = false;
}
//
// 输入
void AThirdPersonMPCharacter::SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
{
// 设置游戏进程键绑定
check(PlayerInputComponent);
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
PlayerInputComponent->BindAction("Jump", IE_Released, this, &ACharacter::StopJumping);
PlayerInputComponent->BindAxis("MoveForward", this, &AThirdPersonMPCharacter::MoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &AThirdPersonMPCharacter::MoveRight);
// 我们有两个旋转绑定版本,可以用不同的方式处理不同类型的设备
// "turn"处理提供绝对增量的设备。
// "turnrate"用于选择视为变化速度的设备,例如模拟操纵杆
PlayerInputComponent->BindAxis("Turn", this, &APawn::AddControllerYawInput);
PlayerInputComponent->BindAxis("TurnRate", this, &AThirdPersonMPCharacter::TurnAtRate);
PlayerInputComponent->BindAxis("LookUp", this, &APawn::AddControllerPitchInput);
PlayerInputComponent->BindAxis("LookUpRate", this, &AThirdPersonMPCharacter::LookUpAtRate);
// 处理触控设备
PlayerInputComponent->BindTouch(IE_Pressed, this, &AThirdPersonMPCharacter::TouchStarted);
PlayerInputComponent->BindTouch(IE_Released, this, &AThirdPersonMPCharacter::TouchStopped);
// VR头戴设备功能
PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this, &AThirdPersonMPCharacter::OnResetVR);
// 处理发射投射物
PlayerInputComponent->BindAction( "Fire", IE_Pressed, this, &AThirdPersonMPCharacter::StartFire);
//PlayerInputComponent->BindAction("Fire", IE_Released, this, &AThirdPersonMPCharacter::StopFire);
}
//
// 复制的属性
void AThirdPersonMPCharacter::GetLifetimeReplicatedProps(TArray <FLifetimeProperty> & OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(AThirdPersonMPCharacter, CurrentHealth);
}
void AThirdPersonMPCharacter::StartFire()
{
if (!bIsFiringWeapon)
{
bIsFiringWeapon = true;
UWorld* World = GetWorld();
World->GetTimerManager().SetTimer(FiringTimer, this, &AThirdPersonMPCharacter::StopFire, FireRate, false);
SpawnProjectile();
}
}
void AThirdPersonMPCharacter::StopFire()
{
bIsFiringWeapon = false;
}
void AThirdPersonMPCharacter::SpawnProjectile_Implementation()
{
FVector spawnLocation = GetActorLocation() + ( GetActorRotation().Vector() * 100.0f ) + (GetActorUpVector() * 50.0f);
FRotator spawnRotation = GetActorRotation();
FActorSpawnParameters spawnParameters;
spawnParameters.Instigator = Instigator;
spawnParameters.Owner = this;
AThirdPersonMPProjectile* spawnedProjectile = GetWorld()->SpawnActor<AThirdPersonMPProjectile>(ProjectileClass, spawnLocation, spawnRotation, spawnParameters);
}
void AThirdPersonMPCharacter::OnHealthUpdate()
{
//客户端特定的功能
if (IsLocallyControlled())
{
FString healthMessage = FString::Printf(TEXT("You now have %f health remaining."), CurrentHealth);
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, healthMessage);
if (CurrentHealth <= 0)
{
FString deathMessage = FString::Printf(TEXT("You have been killed."));
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, deathMessage);
}
}
//服务器特定的功能
if (GetLocalRole() == ROLE_Authority)
{
FString healthMessage = FString::Printf(TEXT("%s now has %f health remaining."), *GetFName().ToString(), CurrentHealth);
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Blue, healthMessage);
}
//在所有机器上都执行的函数。
/*
因任何因伤害或死亡而产生的特殊功能都应放在这里。
*/
}
void AThirdPersonMPCharacter::OnRep_CurrentHealth()
{
OnHealthUpdate();
}
void AThirdPersonMPCharacter::SetCurrentHealth(float healthValue)
{
if (GetLocalRole() == ROLE_Authority)
{
CurrentHealth = FMath::Clamp(healthValue, 0.f, MaxHealth);
OnHealthUpdate();
}
}
float AThirdPersonMPCharacter::TakeDamage(float DamageTaken, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
float damageApplied = CurrentHealth - DamageTaken;
SetCurrentHealth(damageApplied);
return damageApplied;
}
void AThirdPersonMPCharacter::OnResetVR()
{
UHeadMountedDisplayFunctionLibrary::ResetOrientationAndPosition();
}
void AThirdPersonMPCharacter::TouchStarted(ETouchIndex::Type FingerIndex, FVector Location)
{
Jump();
}
void AThirdPersonMPCharacter::TouchStopped(ETouchIndex::Type FingerIndex, FVector Location)
{
StopJumping();
}
void AThirdPersonMPCharacter::TurnAtRate(float Rate)
{
// 根据速度信息计算此帧的增量
AddControllerYawInput(Rate * BaseTurnRate * GetWorld()->GetDeltaSeconds());
}
void AThirdPersonMPCharacter::LookUpAtRate(float Rate)
{
// 根据速度信息计算此帧的增量
AddControllerPitchInput(Rate * BaseLookUpRate * GetWorld()->GetDeltaSeconds());
}
void AThirdPersonMPCharacter::MoveForward(float Value)
{
if ((Controller != NULL) && (Value != 0.0f))
{
// find out which way is forward
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// 获取前向矢量
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
AddMovementInput(Direction, Value);
}
}
void AThirdPersonMPCharacter::MoveRight(float Value)
{
if ( (Controller != NULL) && (Value != 0.0f) )
{
// 找出正确的道路
const FRotator Rotation = Controller->GetControlRotation();
const FRotator YawRotation(0, Rotation.Yaw, 0);
// 获取正确的矢量
const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
// add movement in that direction
AddMovementInput(Direction, Value);
}
}