UE5中武器未跟随角色

系列文章目录

文章目录

前言

正常运行:

一、错误原因?

SpawnActor 生成出来的武器对象

没有正确传给后面的 Is Valid

结果后面的 Attach Actor To Component 没有基于正确的武器实例去执行

所以武器虽然生成了,但没有真正挂接到角色组件上

最终表现就是"武器生成了,但不跟随角色移动"

SpawnActor 返回的武器实例没有被正确传递到后续校验和挂接链路里,所以武器没有成功 Attach 到角色组件上。

二、解决步骤

1.角色骨骼挂载插槽并配置武器资产,调整武器位置

2.配置技能数据表

3.技能蓝图

蓝图逻辑:

Event ActivateAbility

-> SpawnActor

-> Is Valid(SpawnActor.ReturnValue)

-> Attach Actor To Component

注意:武器没跟随角色移动的问题

武器运行却不跟随原因是:如图,SpawnActor节点的输出值ReturnValue,没有连接到Is Valid节点的输入端Input Object

更准确地说,不是单纯"武器未跟随是因为没接到 Is Valid",而是:

因为 SpawnActor 返回的武器实例没有被正确传递到后续校验和挂接链路里,所以武器没有成功 Attach 到角色组件上。

Is Valid 这根线是这个问题暴露出来的关键。

4.c++部分

(1)角色能力基类:头文件

cpp 复制代码
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "AbilitySystem/WarriorAbilitySystemComponent.h"
#include "AbilitySystem/WarriorAttributeSet.h"
#include "AbilitySystemInterface.h"
#include "DataAssets/StartUpData/DataAsset_StartUpDataBase.h"
#include "WarriorCharacterBase.generated.h"

class UWarriorAbilitysystemComponent;
class UWarriorAttributeSet;
class UDataAsset_StartUpDataBase;



UCLASS()
class GODOFWAR_DEMO_API AWarriorCharacterBase : public ACharacter, public IAbilitySystemInterface
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	AWarriorCharacterBase();

	//获取GAS组件
	virtual UAbilitySystemComponent* GetAbilitySystemComponent() const ;

protected:
	//角色控制相关
	virtual void PossessedBy(AController* NewController) override;
	//技能系统组件
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AbilitySystem")
    UWarriorAbilitySystemComponent* WarriorAbilitySystemComponent;

	//属性集
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "AbilitySystem")
	UWarriorAttributeSet* WarriorAttributeSet;

	//角色初始化数据资产
	//TSoftObjectPtr存储资源的路径  使用软引用,避免资源丢失
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "CharacterData")
	TSoftObjectPtr<UDataAsset_StartUpDataBase> CharacterStartUpData;

public:
	//获取技能系统组件
	FORCEINLINE UWarriorAbilitySystemComponent* GetWarriorAbilitySystemComponent() const
	{
		return WarriorAbilitySystemComponent;
	}

	//获取属性集
	FORCEINLINE UWarriorAttributeSet* GetWarriorAttributeSet() const
	{
		return WarriorAttributeSet;
	}



};

角色能力基类:实现部分

cpp 复制代码
// Fill out your copyright notice in the Description page of Project Settings.


#include "Characters/WarriorCharacterBase.h"

// Sets default values
AWarriorCharacterBase::AWarriorCharacterBase()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = false;
	//表示在游戏开始时,这个actor的tick不会被自动启用,即使bCanEverTick=true,只要我们这个bStartWithTickEnabled=false,那么这个actor也不会在游戏开始时自动开始tick
	PrimaryActorTick.bStartWithTickEnabled = false;
	//mesh不会被贴花影响
	GetMesh()->bReceivesDecals = false;
	
	//创建并初始化自定义技能系统组件
	WarriorAbilitySystemComponent = CreateDefaultSubobject<UWarriorAbilitySystemComponent>(TEXT("WarriorAbilitySystemComponent"));
    //设置技能系统组件为可复制
	WarriorAbilitySystemComponent->SetIsReplicated(true);
    //创建并初始化自定义属性集
    WarriorAttributeSet = CreateDefaultSubobject<UWarriorAttributeSet>(TEXT("WarriorAttributeSet"));


}

// 返回技能系统组件
UAbilitySystemComponent* AWarriorCharacterBase::GetAbilitySystemComponent() const
{
	return GetWarriorAbilitySystemComponent();;
}

//当控制器占据此角色时调用
void AWarriorCharacterBase::PossessedBy(AController* NewController)
{
	//父类方法
	Super::PossessedBy(NewController);
	//检查技能系统组件是否为空
	if (WarriorAbilitySystemComponent)
    {
        //设置技能系统组件角色信息,this-1拥有角色的Actor(一般是控制器),this-2技能系统组件在游戏中
        WarriorAbilitySystemComponent->InitAbilityActorInfo(this, this);
		//验证角色初始化数据是否设置
		//如果没有设置,就报错,并且提示原因
		ensureMsgf(!CharacterStartUpData.IsNull(), TEXT("Forgot to assign start up data to %s"), *GetName());

    }
	
}

(2)数据资产:头文件

cpp 复制代码
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "DataAsset_StartUpDataBase.generated.h"

class UWarriorGameplayAbility;
class UWarriorAbilitySystemComponent;

UCLASS()
class GODOFWAR_DEMO_API UDataAsset_StartUpDataBase : public UDataAsset
{
	GENERATED_BODY()

public:
    //将此数据资产中的能力赋予给能力系统组件,InASCToGive目标能力系统组件,    ApplyLevel应用的能力等级
    virtual void GiveToAbilitySystemComponent(UWarriorAbilitySystemComponent* InASCToGive, int32 ApplyLevel = 1);

protected:
    //配置,获取时立即激活的被动能力
    UPROPERTY(EditDefaultsOnly, Category = "StartUpData")
    TArray<TSubclassOf<UWarriorGameplayAbility>> ActivateOnGivenAbilities;

    //配置,需要手动触发的主动能力
    UPROPERTY(EditDefaultsOnly, Category = "StartUpData")
    TArray<TSubclassOf<UWarriorGameplayAbility>> ReactiveAbilities;

    //将一组能力赋予给能力系统组件
    void GrantAbilities(const TArray<TSubclassOf<UWarriorGameplayAbility>>& InAbilitiesToGive, UWarriorAbilitySystemComponent* InASCToGive, int32 ApplyLevel = 1);
};

数据资产:实现

cpp 复制代码
// Fill out your copyright notice in the Description page of Project Settings.


#include "DataAssets/StartUpData/DataAsset_StartUpDataBase.h"
#include "AbilitySystem/WarriorAbilitySystemComponent.h"
#include "AbilitySystem/Abilities/WarriorGameplayAbility.h"

//将启动数据赋予能力系统组件
void UDataAsset_StartUpDataBase::GiveToAbilitySystemComponent(UWarriorAbilitySystemComponent* InASCToGive, int32 ApplyLevel)
{
    //确保目标能力系统组件有效
    check(InASCToGive);

    //赋予被动能力
    GrantAbilities(ActivateOnGivenAbilities, InASCToGive, ApplyLevel);
    
    //赋予主动能力
    GrantAbilities(ReactiveAbilities, InASCToGive, ApplyLevel);
}

//将一组能力赋予能力系统组件
void UDataAsset_StartUpDataBase::GrantAbilities(const TArray<TSubclassOf<UWarriorGameplayAbility>>& InAbilitiesToGive, UWarriorAbilitySystemComponent* InASCToGive, int32 ApplyLevel)
{   
    //如果能力数组为空,则直接返回
    if (InAbilitiesToGive.IsEmpty())
    {
        return;
    }
    //遍历能力数组
    for (const TSubclassOf<UWarriorGameplayAbility>& Ability : InAbilitiesToGive)
    {
        if (!Ability) continue;
        //基于能力类创建规格
        FGameplayAbilitySpec AbilitySpec(Ability);
        //设置能力来源对象
        AbilitySpec.SourceObject = InASCToGive->GetAvatarActor();
        //设置能力等级
        AbilitySpec.Level = ApplyLevel;
        //技能注册
        InASCToGive->GiveAbility(AbilitySpec);
    }
}

(3)角色移动和能力:头文件

cpp 复制代码
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Characters/WarriorCharacterBase.h"
#include "InputActionValue.h"
#include "WarriorCharacterHero.generated.h"

class USpringArmComponent;
class UCameraComponent;
class UWarriorDataAsset_InputConfig;

struct FInputActionValue;

UCLASS()
class GODOFWAR_DEMO_API AWarriorCharacterHero : public AWarriorCharacterBase
{
	GENERATED_BODY()

public:
	AWarriorCharacterHero();

protected:
	virtual void PossessedBy(AController* NewController) override;

	virtual void BeginPlay() override;
	//调用以将功能绑定到输入
    virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

private:

//组件
#pragma region Components

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera", meta = (AllowPrivateAccess = "true"))
	USpringArmComponent* CameraBoom;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera", meta = (AllowPrivateAccess = "true"))
	UCameraComponent* FollowCamera;

#pragma endregion

//输入
#pragma  region Inputs
	//声明一个输入配置数据资产
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "CharacterData", meta = (AllowPrivateAccess = "true"))
	UWarriorDataAsset_InputConfig* InputConfigDataAsset;
	////声明移动输入函数
    void Input_Move(const FInputActionValue& InputActionValue);
	void Input_Look(const FInputActionValue& InputActionValue);

#pragma  endregion Inputs

};

(3)角色移动和能力:实现部分

cpp 复制代码
// Fill out your copyright notice in the Description page of Project Settings.


#include "Characters/WarriorCharacterHero.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "EnhancedInputSubsystems.h"
#include "DataAssets/Input/WarriorDataAsset_InputConfig.h"
#include "Components/Input/WarriorEnhancedInputComponent.h"
#include "WarriorGameplayTags.h"
#include "AbilitySystem/WarriorAbilitySystemComponent.h"
#include "DataAssets/StartUpData/DataAsset_HeroStartUpData.h"
#include "GameFramework/PlayerController.h"

#include "Characters/WarriorCharacterBase.h"
#include "WarriorDebugHelper.h"



AWarriorCharacterHero::AWarriorCharacterHero()
{
	GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);

	bUseControllerRotationPitch = false;
	bUseControllerRotationYaw = false;
    bUseControllerRotationRoll = false;

	CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));
	CameraBoom->SetupAttachment(GetRootComponent());
    CameraBoom->TargetArmLength = 200.0f;
	//弹簧臂偏移值
	CameraBoom->SocketOffset = FVector(0.0f, 55.f, 65.f);
    CameraBoom->bUsePawnControlRotation = true;

	FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));
	FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);
	FollowCamera->bUsePawnControlRotation = false;

	GetCharacterMovement()->bOrientRotationToMovement = true;
	//角色转身速度
	GetCharacterMovement()->RotationRate = FRotator(0.f, 500.f, 0.f);
	GetCharacterMovement()->MaxWalkSpeed = 400.f;
    GetCharacterMovement()->BrakingDecelerationWalking = 2000.f;

	//获取输入数据资产引用
	static ConstructorHelpers::FObjectFinder<UWarriorDataAsset_InputConfig> InputConfigAssetRef(
		TEXT("/Game/PlayerCharacter/DA_InputConfig.DA_InputConfig")
	);

	if (InputConfigAssetRef.Succeeded())
	{
		InputConfigDataAsset = InputConfigAssetRef.Object;
	}
	else
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to load DA_InputConfig from /Game/PlayCharacter/DA_InputConfig.DA_InputConfig"));
	}

}


void AWarriorCharacterHero::PossessedBy(AController* NewController)
{
    //先调用父类方法,处理基础的角色控制逻辑和GAS初始化
	Super::PossessedBy(NewController);
	//判断技能数据资产是否有效
	if (!CharacterStartUpData.IsNull())
	{
	    //将武器同步加载
	    if (UDataAsset_StartUpDataBase* LoadedData = CharacterStartUpData.LoadSynchronous())
	    {
		    LoadedData->GiveToAbilitySystemComponent(WarriorAbilitySystemComponent);
	    }	   
	}
}

void AWarriorCharacterHero::BeginPlay()
{
	Super::BeginPlay();
}


void AWarriorCharacterHero::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	//确保父类输入设置执行
	Super::SetupPlayerInputComponent(PlayerInputComponent);


	/*if (!InputConfigDataAsset)
	{
		UE_LOG(LogTemp, Error, TEXT("InputConfigDataAsset is null on %s"), *GetName());
		return;
	}*/

	//强制验证数据资产有效性
	checkf(InputConfigDataAsset, TEXT("Forgot to assign a valid data asset as input config"));
	//获取本地玩家控制器引用
	ULocalPlayer* LocalPlayer = GetController<APlayerController>()->GetLocalPlayer();
	//获取输入子系统引用
	UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(LocalPlayer);
	//确保输入子系统有效
	checkf(Subsystem, TEXT("EnhancedInputLocalPlayerSubsystem is null"));
	//将输入映射上下文添加到增强输入子系统
    Subsystem->AddMappingContext(InputConfigDataAsset->DefaultMappingContext, 0);
	//输入组件转换,使用自定义的WarriorInputComponent替换基础输入组件
	UWarriorEnhancedInputComponent* WarriorInputComponent = CastChecked<UWarriorEnhancedInputComponent>(PlayerInputComponent);
	//绑定输入映射
	WarriorInputComponent->BindNativeInputAction(InputConfigDataAsset, WarriorGameplayTags::InputTag_Move, ETriggerEvent::Triggered, this, &ThisClass::Input_Move);
	//绑定输入视角移动
	WarriorInputComponent->BindNativeInputAction(InputConfigDataAsset, WarriorGameplayTags::InputTag_Look, ETriggerEvent::Triggered, this, &ThisClass::Input_Look);
}




//输入移动
void AWarriorCharacterHero::Input_Move(const FInputActionValue& InputActionValue)
{
	const FVector2D MovementVector = InputActionValue.Get<FVector2D>();
	const FRotator MovementRotation(0.f, Controller->GetControlRotation().Yaw, 0.f);

	if (MovementVector.Y != 0.f)
	{
		//获取向前方向Forward Direction
		const FVector ForwardDirection = MovementRotation.RotateVector(FVector::ForwardVector);
        //添加角色向前的移动输入
		AddMovementInput(ForwardDirection, MovementVector.Y);
	}

	if (MovementVector.X != 0.f)
	{
		//获取向右方向Right Direction
		const FVector RightDirection = MovementRotation.RotateVector(FVector::RightVector);
        //添加角色向右的移动输入
		AddMovementInput(RightDirection, MovementVector.X);
	}
}


//输入视角移动
void AWarriorCharacterHero::Input_Look(const FInputActionValue& InputActionValue)
{
	const FVector2D LookAxisVector = InputActionValue.Get<FVector2D>();
	if (LookAxisVector.X != 0.f)
	{
		AddControllerYawInput(LookAxisVector.X);
	}
	if (LookAxisVector.Y != 0.f)
	{
		AddControllerPitchInput(LookAxisVector.Y);
	}
}
相关推荐
平行云7 小时前
虚拟直播混合式2D/3D应用程序实时云渲染推流解决方案
linux·unity·云原生·ue5·图形渲染·实时云渲染·像素流送
曼巴UE513 小时前
UE 客户端 需要的网络同步概念总结
网络·c++·ue5
chudonghao1 天前
[UE学习笔记][基于源码] 控制器、Pawn、相机的控制关系
笔记·学习·ue5
吴梓穆2 天前
UE5 移动鼠标玩家不动,相机围绕玩家移动
数码相机·ue5
邪修king4 天前
UE5 零基础入门第三弹: 碰撞与触发交互,解锁场景机关与蓝图封装(高娱乐性学习)
学习·ue5·交互
成都渲染101云渲染66669 天前
UE5 云渲染实战:渲染 101 与川翔云电脑怎么选、怎么用
ue5·电脑
邪修king11 天前
UE5 零基础入门第二弹:让你的几何体 “活” 起来 ——Actor 基础与蓝图交互入门
c++·ue5·交互
Дерек的学习记录11 天前
Unreal Eangie 5:蓝图编程
开发语言·学习·ue5
吴梓穆11 天前
UE5 c++ 常用方法
java·c++·ue5