diff --git a/Binaries/Win64/UnrealEditor-FirstPersonDemo.dll b/Binaries/Win64/UnrealEditor-FirstPersonDemo.dll index 728dcd5..a673050 100644 --- a/Binaries/Win64/UnrealEditor-FirstPersonDemo.dll +++ b/Binaries/Win64/UnrealEditor-FirstPersonDemo.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:33a48f294e712f45baaa582184875ac3b8e08ebcf03034e7ed98e63d68fa6cae -size 647680 +oid sha256:ae229afe9e1b892a1826f3f17c74573be1818162b0173feee70516ee8859e956 +size 675840 diff --git a/Binaries/Win64/UnrealEditor-FirstPersonDemo.patch_0.exe b/Binaries/Win64/UnrealEditor-FirstPersonDemo.patch_0.exe index dd50ec9..d99eca9 100644 --- a/Binaries/Win64/UnrealEditor-FirstPersonDemo.patch_0.exe +++ b/Binaries/Win64/UnrealEditor-FirstPersonDemo.patch_0.exe @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c49ee102c8824239d6247a8af26cdd6efb6db3762b0269b803f38ccff2f343e3 -size 433664 +oid sha256:0282face21b88277a97bd8cafc2462b4cc0947a1f7328416c4a388ca00127f01 +size 457216 diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index 237d1d6..8bd26df 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -114,3 +114,6 @@ ConnectionType=USBOnly bUseManualIPAddress=False ManualIPAddress= + +[CoreRedirects] ++ClassRedirects=(OldName="/Script/FirstPersonDemo.SuriverPlayer",NewName="/Script/FirstPersonDemo.SurviverPlayer") \ No newline at end of file diff --git a/Content/Surviver.umap b/Content/Surviver.umap new file mode 100644 index 0000000..c0f5462 --- /dev/null +++ b/Content/Surviver.umap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2cd2952d255c4fda10e5782ba012dc28597714fa98cc3c6eaea7579f9ce00330 +size 48554 diff --git a/Content/Surviver/Blueprints/BP_SurviverGameMode.uasset b/Content/Surviver/Blueprints/BP_SurviverGameMode.uasset new file mode 100644 index 0000000..88d527c --- /dev/null +++ b/Content/Surviver/Blueprints/BP_SurviverGameMode.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:284a33c419f638ef07561dcd7a5b48d69745754f99ab12070c797ef5e84f8e51 +size 21503 diff --git a/Content/Surviver/Blueprints/BP_SurviverPlayerCharacter.uasset b/Content/Surviver/Blueprints/BP_SurviverPlayerCharacter.uasset new file mode 100644 index 0000000..35dfc94 --- /dev/null +++ b/Content/Surviver/Blueprints/BP_SurviverPlayerCharacter.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ef4bb7eb9d889c2cbbf1b8063dc8548e423a19dd361e8923207b6aee84575f8 +size 37655 diff --git a/Content/Surviver/Blueprints/BP_SurviverPlayerController.uasset b/Content/Surviver/Blueprints/BP_SurviverPlayerController.uasset new file mode 100644 index 0000000..cfa0c44 --- /dev/null +++ b/Content/Surviver/Blueprints/BP_SurviverPlayerController.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2350372ac9a3b583e180dff456a26c089938ed4ca6429560a7b641816f0b1776 +size 21078 diff --git a/Source/FirstPersonDemo/Surviver_FPS/Battle/SuriverPlayer.cpp b/Source/FirstPersonDemo/Surviver_FPS/Battle/SuriverPlayer.cpp deleted file mode 100644 index 6119796..0000000 --- a/Source/FirstPersonDemo/Surviver_FPS/Battle/SuriverPlayer.cpp +++ /dev/null @@ -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(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("你死了")); -} diff --git a/Source/FirstPersonDemo/Surviver_FPS/Battle/SuriverPlayer.h b/Source/FirstPersonDemo/Surviver_FPS/Battle/SuriverPlayer.h deleted file mode 100644 index 28be90f..0000000 --- a/Source/FirstPersonDemo/Surviver_FPS/Battle/SuriverPlayer.h +++ /dev/null @@ -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; -}; diff --git a/Source/FirstPersonDemo/Surviver_FPS/Battle/SurviverPlayer.cpp b/Source/FirstPersonDemo/Surviver_FPS/Battle/SurviverPlayer.cpp new file mode 100644 index 0000000..112a39c --- /dev/null +++ b/Source/FirstPersonDemo/Surviver_FPS/Battle/SurviverPlayer.cpp @@ -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(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(TEXT("角色 Mesh")); + CharacterMesh->SetupAttachment(GetMesh()); + // CharacterMesh->SetOnlyOwnerSee(true); + // CharacterMesh->FirstPersonPrimitiveType = EFirstPersonPrimitiveType::FirstPerson; + CharacterMesh->SetCollisionProfileName(FName("NoCollision")); + + // Create the Camera Component + CharacterCameraComponent = CreateDefaultSubobject(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(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(); + + // 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(); + + // 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("你死了")); +} diff --git a/Source/FirstPersonDemo/Surviver_FPS/Battle/SurviverPlayer.h b/Source/FirstPersonDemo/Surviver_FPS/Battle/SurviverPlayer.h new file mode 100644 index 0000000..4160093 --- /dev/null +++ b/Source/FirstPersonDemo/Surviver_FPS/Battle/SurviverPlayer.h @@ -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; +}; diff --git a/Source/FirstPersonDemo/Surviver_FPS/Battle/SurviverPlayerController.cpp b/Source/FirstPersonDemo/Surviver_FPS/Battle/SurviverPlayerController.cpp new file mode 100644 index 0000000..c4c3197 --- /dev/null +++ b/Source/FirstPersonDemo/Surviver_FPS/Battle/SurviverPlayerController.cpp @@ -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(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; +} diff --git a/Source/FirstPersonDemo/Surviver_FPS/Battle/SurviverPlayerController.h b/Source/FirstPersonDemo/Surviver_FPS/Battle/SurviverPlayerController.h new file mode 100644 index 0000000..7fa7913 --- /dev/null +++ b/Source/FirstPersonDemo/Surviver_FPS/Battle/SurviverPlayerController.h @@ -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 DefaultMappingContexts; + + /** Input Mapping Contexts */ + UPROPERTY(EditAnywhere, Category="Input|Input Mappings") + TArray MobileExcludedMappingContexts; + + /** Mobile controls widget to spawn */ + UPROPERTY(EditAnywhere, Category="Input|Touch Controls") + TSubclassOf MobileControlsWidgetClass; + + /** Pointer to the mobile controls widget */ + UPROPERTY() + TObjectPtr 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; +}; diff --git a/Source/FirstPersonDemo/Surviver_FPS/SurviverGameMode.cpp b/Source/FirstPersonDemo/Surviver_FPS/SurviverGameMode.cpp index aaa3f80..d451e4d 100644 --- a/Source/FirstPersonDemo/Surviver_FPS/SurviverGameMode.cpp +++ b/Source/FirstPersonDemo/Surviver_FPS/SurviverGameMode.cpp @@ -3,3 +3,8 @@ #include "Surviver_FPS/SurviverGameMode.h" +#include "Battle/SurviverPlayer.h" + +ASurviverGameMode::ASurviverGameMode() { + DefaultPawnClass = ASurviverPlayer::StaticClass(); +} diff --git a/Source/FirstPersonDemo/Surviver_FPS/SurviverGameMode.h b/Source/FirstPersonDemo/Surviver_FPS/SurviverGameMode.h index 46cb520..8bd046f 100644 --- a/Source/FirstPersonDemo/Surviver_FPS/SurviverGameMode.h +++ b/Source/FirstPersonDemo/Surviver_FPS/SurviverGameMode.h @@ -18,4 +18,9 @@ class FIRSTPERSONDEMO_API ASurviverGameMode : public AGameModeBase { GENERATED_BODY() +public: + /** + * @brief 构造函数,设置默认加入游戏时的玩家为 @ref ASurviverPlayer + */ + ASurviverGameMode(); };