【UE5.3 C++】ARPG游戏 06-拾取武器

目录

效果

步骤

一、角色接触武器后直接拾取武器

二、角色接触武器后按E键拾取武器

三、站立姿态切换到持剑站立姿态

四、跑步姿态切换到持剑跑步姿态

代码


效果

步骤

一、角色接触武器后直接拾取武器

在蓝图中我们可以通过如下节点让角色接触"BP_Weapon"时将其附加到角色到手上,但是我们准备用C++实现。

如下是蓝图的C++版本,可以实现同样效果

此时角色接触武器后,武器就会附加到角色手上

二、角色接触武器后按E键拾取武器

新建一个输入操作,这里命名为"IA_Equip"

打开输入映射上下文,添加一个映射

在"SlashCharacter.h"中首先声明一个输入动作对象的指针"InteractAction",用来关联输入映射"中定义的 IA_Equip。然后定义输入响应函数"Interact",当玩家触发 InteractAction 对应的输入时,虚幻引擎会自动调用这个函数。

在"SlashCharacter.cpp"中先不实现函数"Interact"具体逻辑

通过增强输入组件 执行输入动作绑定,把输入动作"InteractAction"和响应函数"Interact"关联起来

在"BP_SlashCharacter"中设置"Interact Action"为"IA_Equip"

此时我们按下E键就会执行函数"Interact",因此接下来需要实现函数"Interact"逻辑。这里逻辑是希望只有角色和Item(包括武器)发生重叠时,才能被拾取。

首先在"SlashCharacter.h"中定义一个变量"OverlappingItem",用于存储角色拾取的物品

再定义一个内联函数用于设置变量"OverlappingItem"的值

在"Item.cpp"中设置物品与角色重叠后,角色可以获取到Item,结束重叠后角色无法获取到Item

在"Weapon"中定义函数"Equip",作用是将武器的网格组件附着到目标父组件的指定插槽上

回到"SlashCharacter.cpp",具体实现"Interact"函数如下,如果拾取的物品可以转换为武器类,就把武器附加到角色的手上

此时运行游戏可以看到角色在未接触Item时,"OverlappingItem"为空

接触到Item后,"OverlappingItem"就有值了。当角色再次离开Item范围,"OverlappingItem"会再次为空。

当角色接触到Item条件下,并且Item是一个武器,此时按下E键就可以将武器拿在手中。

三、站立姿态切换到持剑站立姿态

如果要实现姿态的切换,就需要在动画蓝图中获取到此时角色是否处于持剑状态。因此我们需要在"SlashCharacter"中添加一个这种状态。首先新建一个头文件

这里命名为"CharacterTypes.h"

在"CharacterTypes.h"中定义一个一个枚举类 ECharacterState,如下图

ASlashCharacter类中定义一个"ECharacterState"类型的变量"CharacterState",用于在角色中记录它当前的装备状态,默认为未装备武器状态

再定义一个内联函数来获取这个"CharacterState"的值

当角色按E拾取武器后,修改变量"CharacterState"的值

在"SlashAnimInstance.h"中也定义一个"CharacterState"

逐帧获取角色类中的状态变量来更新动画实例中的状态变量

打开动画蓝图"ABP_SlashCharacter",根据变量"CharacterState"来切换播放不同的动画序列。如下图,"Blend Poses"节点可以根据枚举的不同来输出不同的动作姿态,其中"Blend Time"表示姿势切换时过渡所用的时间

添加元素引脚

如下图,此时就可以根据"CharacterState"来决定使用哪一种站立姿势。

四、跑步姿态切换到持剑跑步姿态

在Mixamo中下载持剑跑步的动画资源

下载后导入UE

打开IK重定向器"RTG_XBot"

将持剑跑步动画重定向到角色Echo上

在动画蓝图中,根据变量"Character State"来输出不同的持剑跑步姿势

注意设置新动画循环播放

此时效果如文章开头所示。

代码

cpp 复制代码
// Weapon.h
#pragma once

#include "CoreMinimal.h"
#include "Items/Item.h"
#include "Weapon.generated.h"

UCLASS()
class SLASH_API AWeapon : public AItem
{
	GENERATED_BODY()
public:
	void Equip(USceneComponent* InParent, FName InSocketName);

protected:
	virtual void OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) override;
	virtual void OnSphereEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex) override;

};
cpp 复制代码
// Weapon.cpp


#include "Items/Weapons/Weapon.h"
#include "Characters/SlashCharacter.h"

void AWeapon::Equip(USceneComponent* InParent, FName InSocketName)
{
	FAttachmentTransformRules TransformRules(EAttachmentRule::SnapToTarget, true);
	ItemMesh->AttachToComponent(InParent, TransformRules, InSocketName);
}

void AWeapon::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	Super::OnSphereOverlap(OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex, bFromSweep, SweepResult);

}

void AWeapon::OnSphereEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	Super::OnSphereEndOverlap(OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex);
}
cpp 复制代码
// SlashCharacter.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "InputActionValue.h"
#include "CharacterTypes.h"
#include "SlashCharacter.generated.h"

UCLASS()
class SLASH_API ASlashCharacter : public ACharacter
{
	GENERATED_BODY()

public:
	ASlashCharacter();

    virtual void Tick(float DeltaTime) override;
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

private:
    ECharacterState CharacterState = ECharacterState::ECS_Unequipped;

    UPROPERTY(VisibleAnywhere)
    class USpringArmComponent* SpringArm;

    UPROPERTY(VisibleAnywhere)
    class UCameraComponent* ViewCamera;

    UPROPERTY(VisibleAnywhere)
    class UGroomComponent* Hair;

    UPROPERTY(VisibleAnywhere)
    class UGroomComponent* Eyebrows;

    UPROPERTY(VisibleInstanceOnly)
    class AItem* OverlappingItem;

protected:
	virtual void BeginPlay() override;

    // 映射上下文
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
    class UInputMappingContext* SlashContext;

    // 声明4个独立的动作变量
    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
    class UInputAction* MoveForwardAction; // 对应 IA_MoveForward

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
    class UInputAction* MoveRightAction;   // 对应 IA_MoveRight

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
    class UInputAction* TurnAction;        // 对应 IA_Turn (鼠标X)

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
    class UInputAction* LookUpAction;      // 对应 IA_LookUp (鼠标Y)

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
    class UInputAction* JumpAction; // 对应 IA_Jump

    UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
    class UInputAction* InteractAction; // 对应 IA_Equip

    // 声明4个对应的函数
    void MoveForward(const FInputActionValue& Value);
    void MoveRight(const FInputActionValue& Value);
    void Turn(const FInputActionValue& Value);
    void LookUp(const FInputActionValue& Value);
    void Interact(const FInputActionValue& Value);

public:
    FORCEINLINE void SetOverlappingItem(AItem* Item) { OverlappingItem = Item; }
    FORCEINLINE ECharacterState GetCharacterState() const { return CharacterState; }
};
cpp 复制代码
// SlashCharacter.cpp


#include "Characters/SlashCharacter.h"
#include "Components/InputComponent.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "GameFramework/Controller.h" 
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "GroomComponent.h"
#include "Items/Item.h"
#include "Items/Weapons/Weapon.h"

ASlashCharacter::ASlashCharacter()
{
	PrimaryActorTick.bCanEverTick = true;

    // 让角色不要跟着控制器旋转(只让摄像机转,人脸朝移动方向)
    bUseControllerRotationPitch = false;
    bUseControllerRotationYaw = false;
    bUseControllerRotationRoll = false;

    // 添加组件
    SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SpingArm"));
    SpringArm->SetupAttachment(RootComponent);
    SpringArm->TargetArmLength = 300.f;

    ViewCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("ViewCamera"));
    ViewCamera->SetupAttachment(SpringArm);

    Hair = CreateDefaultSubobject<UGroomComponent>(TEXT("Hair"));
    Hair->SetupAttachment(GetMesh());

    Eyebrows = CreateDefaultSubobject<UGroomComponent>(TEXT("Eyebrows"));
    Eyebrows->SetupAttachment(GetMesh());
}

void ASlashCharacter::BeginPlay()
{
	Super::BeginPlay();

    if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
    {
        if (UEnhancedInputLocalPlayerSubsystem* Subsystem = 
            ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
        {
            // 注意:SlashContext 必须在蓝图里被赋值,否则这里也会失败
            if (SlashContext)
            {
                Subsystem->AddMappingContext(SlashContext, 0);
            }
        }
    }
}

void ASlashCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

// 1. 实现 MoveForward (只负责前后)
void ASlashCharacter::MoveForward(const FInputActionValue& Value)
{
    const float MovementValue = Value.Get<float>(); // 获取浮点数
    // UE_LOG(LogTemp, Warning, TEXT("MovementValue: %f"), MovementValue);              

    if (MovementValue != 0.f && Controller != nullptr)
    {
        // 找方向:基于摄像机的 Yaw
        const FRotator Rotation = Controller->GetControlRotation();
        const FRotator YawRotation(0, Rotation.Yaw, 0);

        // 获取前方向量 (X轴)
        const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
        AddMovementInput(Direction, MovementValue);
    }
}

// 2. 实现 MoveRight (只负责左右)
void ASlashCharacter::MoveRight(const FInputActionValue& Value)
{
    const float MovementValue = Value.Get<float>();

    if (MovementValue != 0.f && Controller != nullptr)
    {
        const FRotator Rotation = Controller->GetControlRotation();
        const FRotator YawRotation(0, Rotation.Yaw, 0);

        // 获取右方向量 (Y轴)
        const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
        AddMovementInput(Direction, MovementValue);
    }
}

// 3. 实现 Turn (鼠标左右动 -> 角色左右转)
void ASlashCharacter::Turn(const FInputActionValue& Value)
{
    const float TurnValue = Value.Get<float>();
    AddControllerYawInput(TurnValue);
}

// 4. 实现 LookUp (鼠标上下动 -> 摄像机上下看)
void ASlashCharacter::LookUp(const FInputActionValue& Value)
{
    const float LookUpValue = Value.Get<float>();
    AddControllerPitchInput(LookUpValue);
}

// 5. 实现拾取功能
void ASlashCharacter::Interact(const FInputActionValue& Value)
{
    AWeapon* OverlappingWeapon = Cast<AWeapon>(OverlappingItem);
    if (OverlappingWeapon)
    {
        OverlappingWeapon->Equip(GetMesh(), FName("RightHandSocket"));
        CharacterState = ECharacterState::ECS_EquippedOneHandWeapon;
    }
}

void ASlashCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);

    if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
    {
        // 分别绑定4个动作
        if (MoveForwardAction)
            EnhancedInputComponent->BindAction(MoveForwardAction, ETriggerEvent::Triggered, this, &ASlashCharacter::MoveForward);

        if (MoveRightAction)
            EnhancedInputComponent->BindAction(MoveRightAction, ETriggerEvent::Triggered, this, &ASlashCharacter::MoveRight);

        if (TurnAction)
            EnhancedInputComponent->BindAction(TurnAction, ETriggerEvent::Triggered, this, &ASlashCharacter::Turn);

        if (LookUpAction)
            EnhancedInputComponent->BindAction(LookUpAction, ETriggerEvent::Triggered, this, &ASlashCharacter::LookUp);

        if (JumpAction)
        {
            EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACharacter::Jump);
        }

        if (InteractAction)
        {
            EnhancedInputComponent->BindAction(InteractAction, ETriggerEvent::Triggered, this, &ASlashCharacter::Interact);
        }
    }
}
cpp 复制代码
// Item.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Item.generated.h"

UCLASS()
class SLASH_API AItem : public AActor
{
	GENERATED_BODY()
	
public:	
	AItem();
	virtual void Tick(float DeltaTime) override;

protected:
	virtual void BeginPlay() override;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Sine Parameters")
	float Amplitude = 30.0f;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sine Parameters")
	float RunningTime;

	UPROPERTY(EditAnywhere, BlueprintReadOnly)
	FVector InitialLocation;

	UFUNCTION(BlueprintPure)
	float TransformedSin();

	UFUNCTION()
	virtual void OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

	UFUNCTION()
	virtual void OnSphereEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
	UStaticMeshComponent* ItemMesh;

private:
	UPROPERTY(VisibleAnywhere)
	class USphereComponent* Sphere;
};
cpp 复制代码
// Item.cpp


#include "Items/Item.h"
#include "Slash/DebugMacros.h"
#include "Components/SphereComponent.h"
#include "Characters/SlashCharacter.h"


AItem::AItem()
{
	PrimaryActorTick.bCanEverTick = true;

	ItemMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ItemMeshComponent"));
	RootComponent = ItemMesh;

	Sphere = CreateDefaultSubobject<USphereComponent>(TEXT("Sphere"));
	Sphere->SetupAttachment(RootComponent);
}

void AItem::BeginPlay()
{
	Super::BeginPlay();
	InitialLocation = GetActorLocation();

	Sphere->OnComponentBeginOverlap.AddDynamic(this, &AItem::OnSphereOverlap);
	Sphere->OnComponentEndOverlap.AddDynamic(this, &AItem::OnSphereEndOverlap);
}

void AItem::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	RunningTime += DeltaTime;
}

float AItem::TransformedSin()
{
	return InitialLocation.Z + Amplitude * FMath::Sin(RunningTime * 5.f);
}

void AItem::OnSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	ASlashCharacter* SlashCharacter = Cast<ASlashCharacter>(OtherActor);
	if (SlashCharacter)
	{
		SlashCharacter->SetOverlappingItem(this);
	}

}

void AItem::OnSphereEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	ASlashCharacter* SlashCharacter = Cast<ASlashCharacter>(OtherActor);
	if (SlashCharacter)
	{
		SlashCharacter->SetOverlappingItem(nullptr);
	}
}
cpp 复制代码
// CharacterTypes.h

#pragma once

UENUM(BlueprintType)
enum class ECharacterState : uint8
{
    ECS_Unequipped UMETA(DisplayName = "Unequipped"),
    ECS_EquippedOneHandWeapon UMETA(DisplayName = "EquippedOneHandWeapon"),
    ECS_EquippedTwoHandWeapon UMETA(DisplayName = "EquippedTwoHandWeapon")
};
cpp 复制代码
// SlashAnimInstance.h

#pragma once

#include "CoreMinimal.h"
#include "Animation/AnimInstance.h"
#include "CharacterTypes.h"
#include "SlashAnimInstance.generated.h"

/**
 * 
 */
UCLASS()
class SLASH_API USlashAnimInstance : public UAnimInstance
{
	GENERATED_BODY()
public:
	virtual void NativeInitializeAnimation() override;
	virtual void NativeUpdateAnimation(float DeltaTime) override;

	UPROPERTY(BlueprintReadOnly)
	class ASlashCharacter* SlashCharacter;

	UPROPERTY(BlueprintReadOnly, Category = Movement)
	class UCharacterMovementComponent* SlashCharacterMovement;

	UPROPERTY(BlueprintReadOnly, Category = Movement)
	float GroundSpeed;

	UPROPERTY(BlueprintReadOnly, Category = Movement)
	bool IsFalling;

	UPROPERTY(BlueprintReadOnly, Category = "Movement | Character State")
	ECharacterState CharacterState;
};
cpp 复制代码
// SlashAnimInstance.cpp


#include "Characters/SlashAnimInstance.h"
#include "Characters/SlashCharacter.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Kismet/KismetMathLibrary.h"

void USlashAnimInstance::NativeInitializeAnimation()
{
	Super::NativeInitializeAnimation();  //调用父类的NativeInitializeAnimation函数

	SlashCharacter = Cast<ASlashCharacter>(TryGetPawnOwner());
	if (SlashCharacter)
	{
		SlashCharacterMovement = SlashCharacter->GetCharacterMovement();
	}
}

void USlashAnimInstance::NativeUpdateAnimation(float DeltaTime)
{
	Super::NativeUpdateAnimation(DeltaTime);

	if (SlashCharacterMovement)
	{
		GroundSpeed = UKismetMathLibrary::VSizeXY(SlashCharacterMovement->Velocity);
		IsFalling = SlashCharacterMovement->IsFalling();
		CharacterState = SlashCharacter->GetCharacterState();
	}
}
相关推荐
咕噜企业分发小米2 小时前
如何利用云服务器搭建游戏服务器并实现跨平台游戏?
运维·服务器·游戏
九影网络5 小时前
游戏开发耗时有标准吗?核心影响因素与周期范围汇总
游戏
yunteng5215 小时前
游戏逻辑_垂直拆分_增量更新
游戏·aoi·地图优化
2501_948122636 小时前
React Native for OpenHarmony 实战:Steam 资讯 App 个人中心页面
javascript·react native·react.js·游戏·ecmascript·harmonyos
Younglina6 小时前
想提升专注力?我做了一个web端的训练工具
前端·vue.js·游戏
avi91117 小时前
UnityProfiler游戏优化-举一个简单的Editor调试
游戏·unity·游戏引擎·aigc·vibe coding·editor扩展
二DUAN帝7 小时前
像素流与UE通信
前端·javascript·css·ue5·html·ue4·html5
SmartRadio7 小时前
滚球老鼠标DIY改造成游戏光枪完整方案2
网络·游戏·计算机外设
科技块儿9 小时前
【游戏防外挂】同一IP多账号登录?IP地址查询定位快速识别工作室
网络协议·tcp/ip·游戏