feat: 网络同步角色移动
This commit is contained in:
@@ -1,63 +0,0 @@
|
||||
// Copyright Majowaveon Games. All Rights Reserved.
|
||||
|
||||
|
||||
#include "SuriverPlayer.h"
|
||||
#include "HealthComponent.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
|
||||
/**
|
||||
* @brief ASuriverPlayer的构造函数。
|
||||
*/
|
||||
ASuriverPlayer::ASuriverPlayer() {
|
||||
// 允许该角色每帧调用Tick()
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
// 创建并附加生命组件
|
||||
HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("HealthComponent"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 游戏开始或角色生成时调用。
|
||||
*/
|
||||
void ASuriverPlayer::BeginPlay() {
|
||||
Super::BeginPlay();
|
||||
if (HealthComponent) {
|
||||
HealthComponent->OnDeath.AddDynamic(this, &ASuriverPlayer::OnPlayerDied);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 每帧调用。
|
||||
* @param DeltaTime 帧间隔时间。
|
||||
*/
|
||||
void ASuriverPlayer::Tick(float DeltaTime) {
|
||||
Super::Tick(DeltaTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绑定输入功能。
|
||||
* @param PlayerInputComponent 用于绑定输入的组件。
|
||||
*/
|
||||
void ASuriverPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) {
|
||||
Super::SetupPlayerInputComponent(PlayerInputComponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 接收伤害的实现。
|
||||
* @param DamageAmount 伤害量。
|
||||
* @param InstigatorActor 造成伤害的Actor。
|
||||
*/
|
||||
void ASuriverPlayer::ReceiveDamage(float DamageAmount, AActor* InstigatorActor) {
|
||||
if (HealthComponent) {
|
||||
// 通过生命组件处理伤害
|
||||
HealthComponent->HandleDamage(DamageAmount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 当玩家死亡时调用,打印日志。
|
||||
* @todo 通知 GameMode 玩家已死亡,处理结算逻辑
|
||||
*/
|
||||
void ASuriverPlayer::OnPlayerDied() {
|
||||
UE_LOG(LogTemp, Warning, TEXT("你死了"));
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright Majowaveon Games. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "DamageableInterface.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "SuriverPlayer.generated.h"
|
||||
|
||||
class UHealthComponent;
|
||||
|
||||
/**
|
||||
* @class ASuriverPlayer
|
||||
* @brief 玩家角色基类,实现了可受伤害接口。
|
||||
* @ingroup Battle
|
||||
*/
|
||||
UCLASS()
|
||||
class FIRSTPERSONDEMO_API ASuriverPlayer : public ACharacter, public IDamageableInterface {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数,设置角色的默认属性。
|
||||
*/
|
||||
ASuriverPlayer();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 管理角色生命值的组件。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
|
||||
UHealthComponent* HealthComponent;
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
/**
|
||||
* @brief 当玩家死亡时调用。
|
||||
* @todo 添加死亡处理逻辑,如UI弹窗等
|
||||
*/
|
||||
UFUNCTION()
|
||||
void OnPlayerDied();
|
||||
|
||||
public:
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
|
||||
|
||||
/**
|
||||
* @brief 接收伤害的接口实现。
|
||||
* @param DamageAmount 伤害量。
|
||||
* @param InstigatorActor 造成伤害的Actor。
|
||||
*/
|
||||
virtual void ReceiveDamage(float DamageAmount, AActor* InstigatorActor) override;
|
||||
};
|
||||
169
Source/FirstPersonDemo/Surviver_FPS/Battle/SurviverPlayer.cpp
Normal file
169
Source/FirstPersonDemo/Surviver_FPS/Battle/SurviverPlayer.cpp
Normal file
@@ -0,0 +1,169 @@
|
||||
// Copyright Majowaveon Games. All Rights Reserved.
|
||||
|
||||
|
||||
#include "SurviverPlayer.h"
|
||||
#include "HealthComponent.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
|
||||
#include "Animation/AnimInstance.h"
|
||||
#include "Camera/CameraComponent.h"
|
||||
#include "Components/CapsuleComponent.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "EnhancedInputComponent.h"
|
||||
#include "InputActionValue.h"
|
||||
#include "GameFramework/CharacterMovementComponent.h"
|
||||
#include "FirstPersonDemo.h"
|
||||
|
||||
/**
|
||||
* @brief ASuriverPlayer的构造函数。
|
||||
*/
|
||||
ASurviverPlayer::ASurviverPlayer() {
|
||||
// 网络同步设置
|
||||
bReplicates = true;
|
||||
GetCharacterMovement()->SetIsReplicated(true);
|
||||
|
||||
// 允许该角色每帧调用Tick()
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
// 创建并附加生命组件
|
||||
HealthComponent = CreateDefaultSubobject<UHealthComponent>(TEXT("HealthComponent"));
|
||||
|
||||
// Set size for collision capsule
|
||||
GetCapsuleComponent()->InitCapsuleSize(55.f, 96.0f);
|
||||
|
||||
// Create the first person mesh that will be viewed only by this character's owner
|
||||
CharacterMesh = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("角色 Mesh"));
|
||||
CharacterMesh->SetupAttachment(GetMesh());
|
||||
// CharacterMesh->SetOnlyOwnerSee(true);
|
||||
// CharacterMesh->FirstPersonPrimitiveType = EFirstPersonPrimitiveType::FirstPerson;
|
||||
CharacterMesh->SetCollisionProfileName(FName("NoCollision"));
|
||||
|
||||
// Create the Camera Component
|
||||
CharacterCameraComponent = CreateDefaultSubobject<UCameraComponent>(TEXT("First Person Camera"));
|
||||
CharacterCameraComponent->SetupAttachment(CharacterMesh, FName("head"));
|
||||
CharacterCameraComponent->
|
||||
SetRelativeLocationAndRotation(FVector(-2.8f, 5.89f, 0.0f), FRotator(0.0f, 90.0f, -90.0f));
|
||||
CharacterCameraComponent->bUsePawnControlRotation = true;
|
||||
CharacterCameraComponent->bEnableFirstPersonFieldOfView = true;
|
||||
CharacterCameraComponent->bEnableFirstPersonScale = true;
|
||||
CharacterCameraComponent->FirstPersonFieldOfView = 70.0f;
|
||||
CharacterCameraComponent->FirstPersonScale = 0.6f;
|
||||
|
||||
// configure the character comps
|
||||
GetMesh()->SetOwnerNoSee(true);
|
||||
GetMesh()->FirstPersonPrimitiveType = EFirstPersonPrimitiveType::WorldSpaceRepresentation;
|
||||
|
||||
GetCapsuleComponent()->SetCapsuleSize(34.0f, 96.0f);
|
||||
|
||||
// Configure character movement
|
||||
GetCharacterMovement()->BrakingDecelerationFalling = 1500.0f;
|
||||
GetCharacterMovement()->AirControl = 0.5f;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 游戏开始或角色生成时调用。
|
||||
*/
|
||||
void ASurviverPlayer::BeginPlay() {
|
||||
Super::BeginPlay();
|
||||
if (HealthComponent) {
|
||||
HealthComponent->OnDeath.AddDynamic(this, &ASurviverPlayer::OnPlayerDied);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 每帧调用。
|
||||
* @param DeltaTime 帧间隔时间。
|
||||
*/
|
||||
void ASurviverPlayer::Tick(float DeltaTime) {
|
||||
Super::Tick(DeltaTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 绑定输入功能。
|
||||
* @param PlayerInputComponent 用于绑定输入的组件。
|
||||
*/
|
||||
void ASurviverPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) {
|
||||
// Set up action bindings
|
||||
if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent)) {
|
||||
// Jumping
|
||||
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &ASurviverPlayer::DoJumpStart);
|
||||
EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ASurviverPlayer::DoJumpEnd);
|
||||
|
||||
// Moving
|
||||
EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ASurviverPlayer::MoveInput);
|
||||
|
||||
// Looking/Aiming
|
||||
EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ASurviverPlayer::LookInput);
|
||||
EnhancedInputComponent->BindAction(MouseLookAction, ETriggerEvent::Triggered, this,
|
||||
&ASurviverPlayer::LookInput);
|
||||
}
|
||||
else {
|
||||
UE_LOG(LogFirstPersonDemo, Error,
|
||||
TEXT(
|
||||
"'%s' Failed to find an Enhanced Input Component! This template is built to use the Enhanced Input system. If you intend to use the legacy system, then you will need to update this C++ file."
|
||||
), *GetNameSafe(this));
|
||||
}
|
||||
}
|
||||
|
||||
void ASurviverPlayer::MoveInput(const FInputActionValue& Value) {
|
||||
// get the Vector2D move axis
|
||||
FVector2D MovementVector = Value.Get<FVector2D>();
|
||||
|
||||
// pass the axis values to the move input
|
||||
DoMove(MovementVector.X, MovementVector.Y);
|
||||
}
|
||||
|
||||
void ASurviverPlayer::LookInput(const FInputActionValue& Value) {
|
||||
// get the Vector2D look axis
|
||||
FVector2D LookAxisVector = Value.Get<FVector2D>();
|
||||
|
||||
// pass the axis values to the aim input
|
||||
DoAim(LookAxisVector.X, LookAxisVector.Y);
|
||||
}
|
||||
|
||||
void ASurviverPlayer::DoAim(float Yaw, float Pitch) {
|
||||
if (GetController()) {
|
||||
// pass the rotation inputs
|
||||
AddControllerYawInput(Yaw);
|
||||
AddControllerPitchInput(Pitch);
|
||||
}
|
||||
}
|
||||
|
||||
void ASurviverPlayer::DoMove(float Right, float Forward) {
|
||||
if (GetController()) {
|
||||
// pass the move inputs
|
||||
AddMovementInput(GetActorRightVector(), Right);
|
||||
AddMovementInput(GetActorForwardVector(), Forward);
|
||||
}
|
||||
}
|
||||
|
||||
void ASurviverPlayer::DoJumpStart() {
|
||||
// pass Jump to the character
|
||||
Jump();
|
||||
}
|
||||
|
||||
void ASurviverPlayer::DoJumpEnd() {
|
||||
// pass StopJumping to the character
|
||||
StopJumping();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 接收伤害的实现。
|
||||
* @param DamageAmount 伤害量。
|
||||
* @param InstigatorActor 造成伤害的Actor。
|
||||
*/
|
||||
void ASurviverPlayer::ReceiveDamage(float DamageAmount, AActor* InstigatorActor) {
|
||||
if (HealthComponent) {
|
||||
// 通过生命组件处理伤害
|
||||
HealthComponent->HandleDamage(DamageAmount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 当玩家死亡时调用,打印日志。
|
||||
* @todo 通知 GameMode 玩家已死亡,处理结算逻辑
|
||||
*/
|
||||
void ASurviverPlayer::OnPlayerDied() {
|
||||
UE_LOG(LogTemp, Warning, TEXT("你死了"));
|
||||
}
|
||||
105
Source/FirstPersonDemo/Surviver_FPS/Battle/SurviverPlayer.h
Normal file
105
Source/FirstPersonDemo/Surviver_FPS/Battle/SurviverPlayer.h
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright Majowaveon Games. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "DamageableInterface.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "SurviverPlayer.generated.h"
|
||||
|
||||
struct FInputActionValue;
|
||||
class UInputAction;
|
||||
class UCameraComponent;
|
||||
class UHealthComponent;
|
||||
|
||||
/**
|
||||
* @class ASurviverPlayer
|
||||
* @brief 玩家角色基类,具有网络同步的移动组件,实现了可受伤害接口。
|
||||
* @ingroup Battle
|
||||
*/
|
||||
UCLASS()
|
||||
class FIRSTPERSONDEMO_API ASurviverPlayer : public ACharacter, public IDamageableInterface {
|
||||
GENERATED_BODY()
|
||||
|
||||
/** Pawn mesh: first person view */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
|
||||
USkeletalMeshComponent* CharacterMesh;
|
||||
|
||||
/** First person camera */
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
|
||||
UCameraComponent* CharacterCameraComponent;
|
||||
|
||||
protected:
|
||||
/** Jump Input Action */
|
||||
UPROPERTY(EditAnywhere, Category ="Input")
|
||||
UInputAction* JumpAction;
|
||||
|
||||
/** Move Input Action */
|
||||
UPROPERTY(EditAnywhere, Category ="Input")
|
||||
UInputAction* MoveAction;
|
||||
|
||||
/** Look Input Action */
|
||||
UPROPERTY(EditAnywhere, Category ="Input")
|
||||
UInputAction* LookAction;
|
||||
|
||||
/** Mouse Look Input Action */
|
||||
UPROPERTY(EditAnywhere, Category ="Input")
|
||||
UInputAction* MouseLookAction;
|
||||
|
||||
/** Called from Input Actions for movement input */
|
||||
void MoveInput(const FInputActionValue& Value);
|
||||
|
||||
/** Called from Input Actions for looking input */
|
||||
void LookInput(const FInputActionValue& Value);
|
||||
|
||||
/** Handles aim inputs from either controls or UI interfaces */
|
||||
UFUNCTION(BlueprintCallable, Category="Input")
|
||||
virtual void DoAim(float Yaw, float Pitch);
|
||||
|
||||
/** Handles move inputs from either controls or UI interfaces */
|
||||
UFUNCTION(BlueprintCallable, Category="Input")
|
||||
virtual void DoMove(float Right, float Forward);
|
||||
|
||||
/** Handles jump start inputs from either controls or UI interfaces */
|
||||
UFUNCTION(BlueprintCallable, Category="Input")
|
||||
virtual void DoJumpStart();
|
||||
|
||||
/** Handles jump end inputs from either controls or UI interfaces */
|
||||
UFUNCTION(BlueprintCallable, Category="Input")
|
||||
virtual void DoJumpEnd();
|
||||
|
||||
/** Set up input action bindings */
|
||||
virtual void SetupPlayerInputComponent(UInputComponent* InputComponent) override;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数,设置角色的默认属性。
|
||||
*/
|
||||
ASurviverPlayer();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 管理角色生命值的组件。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Components")
|
||||
UHealthComponent* HealthComponent;
|
||||
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
/**
|
||||
* @brief 当玩家死亡时调用。
|
||||
* @todo 添加死亡处理逻辑,如UI弹窗等
|
||||
*/
|
||||
UFUNCTION()
|
||||
void OnPlayerDied();
|
||||
|
||||
public:
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
/**
|
||||
* @brief 接收伤害的接口实现。
|
||||
* @param DamageAmount 伤害量。
|
||||
* @param InstigatorActor 造成伤害的Actor。
|
||||
*/
|
||||
virtual void ReceiveDamage(float DamageAmount, AActor* InstigatorActor) override;
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright Majowaveon Games. All Rights Reserved.
|
||||
|
||||
|
||||
#include "SurviverPlayerController.h"
|
||||
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "EnhancedInputSubsystems.h"
|
||||
#include "Engine/LocalPlayer.h"
|
||||
#include "InputMappingContext.h"
|
||||
#include "FirstPersonDemoCameraManager.h"
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "FirstPersonDemo.h"
|
||||
#include "Widgets/Input/SVirtualJoystick.h"
|
||||
|
||||
ASurviverPlayerController::ASurviverPlayerController() {
|
||||
// set the player camera manager class
|
||||
PlayerCameraManagerClass = AFirstPersonDemoCameraManager::StaticClass();
|
||||
}
|
||||
|
||||
void ASurviverPlayerController::BeginPlay() {
|
||||
Super::BeginPlay();
|
||||
|
||||
// only spawn touch controls on local player controllers
|
||||
if (ShouldUseTouchControls() && IsLocalPlayerController()) {
|
||||
// spawn the mobile controls widget
|
||||
MobileControlsWidget = CreateWidget<UUserWidget>(this, MobileControlsWidgetClass);
|
||||
|
||||
if (MobileControlsWidget) {
|
||||
// add the controls to the player screen
|
||||
MobileControlsWidget->AddToPlayerScreen(0);
|
||||
}
|
||||
else {
|
||||
UE_LOG(LogFirstPersonDemo, Error, TEXT("Could not spawn mobile controls widget."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ASurviverPlayerController::SetupInputComponent() {
|
||||
Super::SetupInputComponent();
|
||||
// only add IMCs for local player controllers
|
||||
if (IsLocalPlayerController()) {
|
||||
// Add Input Mapping Context
|
||||
if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<
|
||||
UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer())) {
|
||||
for (UInputMappingContext* CurrentContext : DefaultMappingContexts) {
|
||||
Subsystem->AddMappingContext(CurrentContext, 0);
|
||||
}
|
||||
|
||||
// only add these IMCs if we're not using mobile touch input
|
||||
if (!ShouldUseTouchControls()) {
|
||||
for (UInputMappingContext* CurrentContext : MobileExcludedMappingContexts) {
|
||||
Subsystem->AddMappingContext(CurrentContext, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ASurviverPlayerController::ShouldUseTouchControls() const {
|
||||
// are we on a mobile platform? Should we force touch?
|
||||
return SVirtualJoystick::ShouldDisplayTouchInterface() || bForceTouchControls;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright Majowaveon Games. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "SurviverPlayerController.generated.h"
|
||||
|
||||
class UInputMappingContext;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class FIRSTPERSONDEMO_API ASurviverPlayerController : public APlayerController {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/** Constructor */
|
||||
ASurviverPlayerController();
|
||||
|
||||
protected:
|
||||
|
||||
/** Input Mapping Contexts */
|
||||
UPROPERTY(EditAnywhere, Category="Input|Input Mappings")
|
||||
TArray<UInputMappingContext*> DefaultMappingContexts;
|
||||
|
||||
/** Input Mapping Contexts */
|
||||
UPROPERTY(EditAnywhere, Category="Input|Input Mappings")
|
||||
TArray<UInputMappingContext*> MobileExcludedMappingContexts;
|
||||
|
||||
/** Mobile controls widget to spawn */
|
||||
UPROPERTY(EditAnywhere, Category="Input|Touch Controls")
|
||||
TSubclassOf<UUserWidget> MobileControlsWidgetClass;
|
||||
|
||||
/** Pointer to the mobile controls widget */
|
||||
UPROPERTY()
|
||||
TObjectPtr<UUserWidget> MobileControlsWidget;
|
||||
|
||||
/** If true, the player will use UMG touch controls even if not playing on mobile platforms */
|
||||
UPROPERTY(EditAnywhere, Config, Category = "Input|Touch Controls")
|
||||
bool bForceTouchControls = false;
|
||||
|
||||
/** Gameplay initialization */
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
/** Input mapping context setup */
|
||||
virtual void SetupInputComponent() override;
|
||||
|
||||
/** Returns true if the player should use UMG touch controls */
|
||||
bool ShouldUseTouchControls() const;
|
||||
};
|
||||
@@ -3,3 +3,8 @@
|
||||
|
||||
#include "Surviver_FPS/SurviverGameMode.h"
|
||||
|
||||
#include "Battle/SurviverPlayer.h"
|
||||
|
||||
ASurviverGameMode::ASurviverGameMode() {
|
||||
DefaultPawnClass = ASurviverPlayer::StaticClass();
|
||||
}
|
||||
|
||||
@@ -18,4 +18,9 @@ class FIRSTPERSONDEMO_API ASurviverGameMode : public AGameModeBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数,设置默认加入游戏时的玩家为 @ref ASurviverPlayer
|
||||
*/
|
||||
ASurviverGameMode();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user