UI를 캐릭터에 부착하는 기능을 구현
부착 할 수 있도록 UWidgetCompnent 라는 클래스를 제공함

class KGAME_API AABCharacter : public ACharacter
{
//
    UPROPERTY(VisibleAnywhere, Category = UI)
        class UWidgetComponent* HPBarWidget;
}

 

하지만 컴파일을 하면 확인 할 수 없는 외부 참조 에러메시지가 나온다
이유는 현재 프로젝트 설정에 UI 관련 엔진 모듈을 지정 안했기 때문이다
엔진 모듈은 KGame.Build.cs 에 추가할 수 있다.

//KGame.Build.cs
using UnrealBuildTool;

public class KGame : ModuleRules
{
    public KGame(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

        PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UMG" });

        PrivateDependencyModuleNames.AddRange(new string[] {  });

        // Uncomment if you are using Slate UI
        // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });

        // Uncomment if you are using online features
        // PrivateDependencyModuleNames.Add("OnlineSubsystem");

        // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
    }
}

UMG 모듈의 Public/Components폴더에는 현재 사용 중인 WidComponent.h 파일을 구현부 헤더 파일에 추가해 컴포넌트를 생성하는 코드를 생성한다.

//cpp
#include "ABCharacter.h"
#include "ABAnimInstance.h"
#include "ABWeapon.h"
#include "ABCharacterStatComponent.h"
#include "DrawDebugHelpers.h"
#include "WidgetComponent.h"

AABCharacter::AABCharacter()
{
    HPBarWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("HPBARWIDGET"));

    HPBarWidget->SetupAttachment(GetMesh());


    HPBarWidget->SetRelativeLocation(FVector(0.0f, 0.0f, 180.0f));
    HPBarWidget->SetWidgetSpace(EWidgetSpace::Screen);
    static ConstructorHelpers::FClassFinder<UUserWidget> UI_HUD(TEXT("/Game/UI/UI_Bar.UI_Bar"));
  ///Game/Book/UI/UI_Bar.UI_Bar_C
    if (UI_HUD.Succeeded())
    {
        HPBarWidget->SetWidgetClass(UI_HUD.Class);
        HPBarWidget->SetDrawSize(FVector2D(150.0f, 50.0f));
    }

UI는 끝에 _C 를 붙여 주어야 한다?

엑셀에 저장돼 있는 캐릭터의 스탯데이터 테이블을 언리얼 엔진에 불러들이는 기능을 구현한다
사용할라면 .xml -> .csv로 저장
파일을 불러들이기 위해 테이블 데이터의 각 열의 이름과 유형이 동일한 구조체를 선언해야 한다.
언리언 엔진에서 제공하는 FTableRowBase 구조체를 상속받은 FABCharacterData라는 이름의 구조체를 만든다

#include "KGame.h"
#include "Engine/DataTable.h"
#include "Engine/GameInstance.h"
#include "ABGameInstance.generated.h"

USTRUCT(BlueprintType)
struct FABCharacterData : public FTableRowBase
{
    GENERATED_BODY()
public:
    FABCharacterData() : Level(1),MaxHp(100), Attack(10.0f),DropExp(10),NextExp(30) {}

UPROPERTY(EditAnyWhere, BlueprintReadWrite, Category = "Data")
        int32 Level;
UPROPERTY(EditAnyWhere, BlueprintReadWrite, Category = "Data")
        float MaxHp;
UPROPERTY(EditAnyWhere, BlueprintReadWrite, Category = "Data")
        float Attack;
UPROPERTY(EditAnyWhere, BlueprintReadWrite, Category = "Data")
        int32 DropExp;
UPROPERTY(EditAnyWhere, BlueprintReadWrite, Category = "Data")
        int32 NextExp;
};


UCLASS()
class KGAME_API UABGameInstance : public UGameInstance
{
    GENERATED_BODY()

public:
    UABGameInstance();
    virtual void Init() override;
};

추가하고 임포트 후 데이터 테이블에 ABCharacterData를 설정할수 있다.

이제 DataTable을 게임 인스턴스의 멤버 변수로 선언하고, 데이터 에셋의 레퍼런스를 복사한 후 데이터를 불러들이는 기능을 구현한다.

//.h
class KGAME_API UABGameInstance : public UGameInstance
{
    GENERATED_BODY()

public:
    UABGameInstance();

    virtual void Init() override;
    FABCharacterData* GetABCharacterData(int32 Level);

private:
    UPROPERTY()
        class UDataTable* ABCharacterTable;
};

//.cpp
UABGameInstance::UABGameInstance()
{
    FString CharacterDataPath = TEXT("/Game/Book/GameData/ABCharacterData.ABCharacterData");
    static ConstructorHelpers::FObjectFinder<UDataTable> DT_ABCHARACTER(*CharacterDataPath);
    ABCharacterTable = DT_ABCHARACTER.Object;
}


FABCharacterData* UABGameInstance::GetABCharacterData(int32 Level)
{
    return ABCharacterTable->FindRow<FABCharacterData>(*FString::FromInt(Level), TEXT(""));
}

게임인스턴스를 사용하기 위해서 반드시 프로젝트 설정을 해주어야 한다. [7시간 뻘짓함]

게임인스턴스를 사용하기 위해서 반드시 프로젝트 설정을 해주어야 한다. [7시간 뻘짓함]

액터컴포넌트의 클래스를 생성한다
캐릭터에 부착해 캐릭터 스탯에 대한 관리를 액터 컴포넌트가 일임하도록 기능을 구현

//.h
class KGAME_API AABCharacter : public ACharacter
{
///
UPROPERTY(VisibleAnyWhere, Category = Stat)
        class UABCharacterStatComponent* CharacterStat;
};

//.cpp
#include "ABCharacter.h"
#include "ABAnimInstance.h"
#include "ABWeapon.h"
#include "ABCharacterStatComponent.h"
#include "DrawDebugHelpers.h"

AABCharacter::AABCharacter()
{
...
    SpringArm = CreateDefaultSubobject<USpringArmComponent>(TEXT("SPRINGARM"));
    Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("CAMERA"));
    CharacterStat = CreateDefaultSubobject<UABCharacterStatComponent>(TEXT("CHARACTERSTAT"));

스탯에 변경이 일어날 떄만 작업을 할예정이라 tick 작업은 할 필요가 없다
그래서 액터 컴포넌트의 설정을 변경해야 한다.
액터의 PostInitializeComponents에 대응하는 컴포넌트의 함수는 InitializeComponent 함수다. 이 함수는 액터의 PostInitalizeComponents 함수가 호출되기 전에 바로 호출된다. 
이 함수를 사용해 컴포넌트의 초기화 로직을 구현해주는데, 호출이 되려면 생성자에서 bWantsInitializeCOmpent 값을 true로 설정행 줘야 한다.

/.h
class KGAME_API UABCharacterStatComponent : public UActorComponent
{
protected:
    // Called when the game starts
    virtual void BeginPlay() override;
    virtual void InitializeComponent() override;
};

/.cpp
UABCharacterStatComponent::UABCharacterStatComponent()
{
    // off to improve performance if you don't need them.
    PrimaryComponentTick.bCanEverTick = false;
    bWantsInitializeComponent = true;
}

void UABCharacterStatComponent::InitializeComponent()
{
    Super::InitializeComponent();
}

데이터를 관리하는 변수들은 private으로 한정해 선언하고 레벨은 SetNewLevel 함수를 통해서만 변경 할 수 잇도록 설정한다.
언리얼 오브젝트에는 직렬화 기능이 있어서 오브젝트의 UPROPERTY 속성을 저장하고 로딩할 수 있다.
컴포넌트의 스탯 중 CurrentHp 값은 게임을 시작할 때마다 변경되므로 이 값을 보관하는 것은 의미가 없고 오히려 오브젝트를 저장할 떄 필요 없는 디크슼 공간만 차지한다. 
이러한 속성에는 Transient 키워드를 추가해 해당 속성을 직렬화에서 제외시키는 것이 좋다

//.h
class KGAME_API UABCharacterStatComponent : public UActorComponent
{
...
public: 
    void SetNewLevel(int32 NewLevel);

private:
    struct FABCharacterData* CurrentStatData = nullptr;

    UPROPERTY(EditInstanceOnly, Category = Stat, Meta = (AllowPrivateAccess = true))
        int32 Level;
    UPROPERTY(Transient, VisibleInstanceOnly, Category = Stat, Meta = (AllowPrivateAccess = true))
        float CurrentHP;
};

//.cpp

#include "ABCharacterStatComponent.h"
#include "ABGameInstance.h"

UABCharacterStatComponent::UABCharacterStatComponent()
{
...
    Level = 1;
}


void UABCharacterStatComponent::InitializeComponent()
{
    Super::InitializeComponent();
    SetNewLevel(Level);
}

void UABCharacterStatComponent::SetNewLevel(int32 NewLevel)
{
    auto ABGameInstance = Cast<UABGameInstance>(UGameplayStatics::GetGameInstance(GetWorld()));
    CurrentStatData = ABGameInstance->GetABCharacterData(NewLevel);

    if (nullptr != CurrentStatData)
    {
        Level = NewLevel;
        CurrentHP = CurrentStatData->MaxHp;
    }
    else
    {
        //레벨존재 X
    }
}//.h

릭터가 대미지를 받으면 받은 대미지만큼 CurrentHp에서 차감하고 그결과로 0이 보다 같거나 작으면 캐릭터가 죽도록 기능을 추가한다
액터 컴포넌트가 캐릭터에 의존성을 가지지 않도록, 액터 컴포넌트에 델리게이트를 선언하고 캐릭터에서 이를 바인딩을 시킨다

/.h
DECLARE_MULTICAST_DELEGATE(FOnHPIsZeroDelegete);

class KGAME_API UABCharacterStatComponent : public UActorComponent
{
public: 
    void SetDamage(float NewDamage);
    float GetAttack();

    FOnHPIsZeroDelegete OnHpIsZero;
};
//.cpp
void UABCharacterStatComponent::SetDamage(float NewDamage)
{
    CurrentHP = FMath::Clamp<float>(CurrentHP - NewDamage, 0.0f, CurrentStatData->MaxHp);
    if (CurrentHP <= 0.0f)
    {
        OnHpIsZero.Broadcast();
    }
}

float UABCharacterStatComponent::GetAttack()
{
    return CurrentStatData->Attack;
}

////////////////////////////////////////////////////

//Character
void AABCharacter::PostInitializeComponents()
{
...
    CharacterStat->OnHpIsZero.AddLambda([this]()->
        void {ABAnim->SetDeadAnim();
    SetActorEnableCollision(false); 
    });
}

void AABCharacter::AttackCheck()
{
    if (bResult)
    {
        if (HitResult.Actor.IsValid())
        {
                FDamageEvent DamageEvnet;
            HitResult.Actor->TakeDamage(CharacterStat->GetAttack(), DamageEvnet,GetController(),this);
        }
    }
}



float AABCharacter::TakeDamage(float DamageAmout, struct FDamageEvent const& DamageEvent,
    class AController* EventInstigator, AActor* DamageCauser)
{
    CharacterStat->SetDamage(FinalDamage);
    return FinalDamage;
}

 

'Unreal > Game 1 (C++)' 카테고리의 다른 글

18.UI와 데이터의 연동  (1) 2019.05.08
17.모듈과 빌드 설정 (UI작업중)  (0) 2019.05.08
15.아이템 습득 + 흭득이펙트  (0) 2019.05.02
14.아이템 상자의 제작  (0) 2019.05.02
13.소켓 + 무기액터  (0) 2019.05.02
  • 아이템을 통과하면 빈속의 아이템을 쥐어주는 기능을 구현
  • 아이템 상자에 클래스를 정보를 저정할 속성을 추가
  • 아이템 영역에 들어왔을 떄 아이템을 생성하도록 구현
  • 특정클래스와 상속받은 클래스들로 목록을 한정하도록 TSubclassof라는 키워드를 제공
  • 사용하면 목록에서 아이템 상자와 이를 선언한 클래스 목록만 볼수 있다. UCLASS일 경우 모든 오브젝트의 선언이 보이게됨
//.h
class KGAME_API AABItemBox : public AActor
{
...
    UPROPERTY(EditInstanceOnly, Category = Box)
        TSubclassOf<class AABWeapon > WeaponItemClass;
}
//.cpp
AABItemBox::AABItemBox()
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = false;

    Trigger = CreateDefaultSubobject<UBoxComponent>(TEXT("TRIGGER"));
    Box = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("BOX"));

    RootComponent = Trigger;
    Box->SetupAttachment(RootComponent);

    Trigger->SetBoxExtent(FVector(40.0f, 42.0f, 30.0f));
    static ConstructorHelpers::FObjectFinder<UStaticMesh> SM_BOX(TEXT(
        "/Game/InfinityBladeGrassLands/Environments/Breakables/StaticMesh/Box/SM_Env_Breakables_Box1.SM_Env_Breakables_Box1"));

    if (SM_BOX.Succeeded())
    {
        Box->SetStaticMesh(SM_BOX.Object);
    }
    Box->SetRelativeLocation(FVector(0.0f, -3.5f, -30.0f));


    Trigger->SetCollisionProfileName(TEXT("ItemBox"));
    Box->SetCollisionProfileName(TEXT("NoCollision"));


    WeaponItemClass = AABWeapon::StaticClass();
}
AABItemBox::AABItemBox()
{
...
    WeaponItemClass = AABWeapon::StaticClass();
}

 


 

/.h
class KGAME_API AABCharacter : public ACharacter
{
...
public:
    UPROPERTY(VisibleAnyWhere, Category = Weapon)
        class AABWeapon* CurrentWeapon;

    bool CanSetWeapon();
    void SetWeapon(class AABWeapon* NewWeapon);

 //.cpp

  bool AABCharacter::CanSetWeapon()
{
    return (nullptr == CurrentWeapon);
}

void AABCharacter::SetWeapon(class AABWeapon* NewWeapon)
{
    FName WeaponSocket(TEXT("hand_rSocket"));
    if (nullptr != NewWeapon)
    {
        NewWeapon->AttachToComponent(GetMesh(),
            FAttachmentTransformRules::SnapToTargetNotIncludingScale, WeaponSocket);
        NewWeapon->SetOwner(this);
        CurrentWeapon = NewWeapon;
    }
}

 

Overlap 이벤트가 발생할떄 아이템 상자에 설정된 클래스 정보로 부터 무기를 생성하고 이를 캐릭터에게 장착시키는 기능을 구현

void AABItemBox::OnCharacterOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, 
    bool bFromSweep, const FHitResult& SweepResult)
{
    auto ABCharacter = Cast<AABCharacter>(OtherActor);

    if (nullptr != ABCharacter && nullptr != WeaponItemClass)
    {
        if (ABCharacter->CanSetWeapon())
        {
            auto NewWeapon = GetWorld()->SpawnActor<AABWeapon>(WeaponItemClass, FVector::ZeroVector,FRotator::ZeroRotator);
            ABCharacter->SetWeapon(NewWeapon);

        }
        else
        {
//장비 할수 없다
        }
    }
}

 

이펙트 재생 및 아이템상자 제거
멤버 함수를 하나 추가하고 파티클 컴포넌트 시스템에서 제공하는 OnSystemFinished 델리게이트에 이를 연결해 이펙트 재생이 종료되면 아이템 상자가 제거되도록 로직을 구성한다.
이때 OnSystemFinished델리게이트는 다이나믹 델리게이트 형식이다.
다이나믹 델리게이트에는 UFUNCTION 함수를 사용해야 하므로 아쉽게도 C++ 람다식으로 표현한 함수는 바인딩 할 수 없다

 

//.h
class KGAME_API AABItemBox : public AActor
{
    UPROPERTY(VisibleAnywhere, Category = Box)
        UParticleSystemComponent* Effect;

    UFUNCTION()
        void OnEffectFinished(class UParticleSystemComponent* PSystem);
};

//.cpp
AABItemBox::AABItemBox()
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = false;

    Trigger = CreateDefaultSubobject<UBoxComponent>(TEXT("TRIGGER"));
    Box = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("BOX"));
    Effect = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("EFFECT"));

    RootComponent = Trigger;
    Box->SetupAttachment(RootComponent);
    Effect->SetupAttachment(RootComponent);

    Trigger->SetBoxExtent(FVector(40.0f, 42.0f, 30.0f));
    static ConstructorHelpers::FObjectFinder<UStaticMesh> SM_BOX(TEXT(
        "/Game/InfinityBladeGrassLands/Environments/Breakables/StaticMesh/Box/SM_Env_Breakables_Box1.SM_Env_Breakables_Box1"));

    if (SM_BOX.Succeeded())
    {
        Box->SetStaticMesh(SM_BOX.Object);
    }
    Box->SetRelativeLocation(FVector(0.0f, -3.5f, -30.0f));


    static ConstructorHelpers::FObjectFinder<UParticleSystem> P_CHESTOPEN(TEXT(
        "/Game/InfinityBladeGrassLands/Effects/FX_Treasure/Chest/P_TreasureChest_Open_Mesh.P_TreasureChest_Open_Mesh"));
    if (P_CHESTOPEN.Succeeded())
    {
        Effect->SetTemplate(P_CHESTOPEN.Object);
        Effect->bAutoActivate = false;
    }

    Trigger->SetCollisionProfileName(TEXT("ItemBox"));
    Box->SetCollisionProfileName(TEXT("NoCollision"));

    WeaponItemClass = AABWeapon::StaticClass();
}


void AABItemBox::OnCharacterOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, 
    bool bFromSweep, const FHitResult& SweepResult)
{
    auto ABCharacter = Cast<AABCharacter>(OtherActor);

    if (nullptr != ABCharacter && nullptr != WeaponItemClass)
    {
        if (ABCharacter->CanSetWeapon())
        {
            auto NewWeapon = GetWorld()->SpawnActor<AABWeapon>(WeaponItemClass, FVector::ZeroVector,FRotator::ZeroRotator);
            ABCharacter->SetWeapon(NewWeapon);
            Effect->Activate(true);
            SetActorEnableCollision(false);
            Effect->OnSystemFinished.AddDynamic(this, &AABItemBox::OnEffectFinished);
        }
        else
        {

        }
    }
}


void AABItemBox::OnEffectFinished(class UParticleSystemComponent* PSystem)
{
    Destroy();

}
  • 플레이어에게 무기를 공급할 아이템 상자를 제작한다.
  • 트리거를 부모로함
  • SetBoxExtent()-> 박스 프레임 생성
//h
#include "KGame.h"
#include "GameFramework/Actor.h"
#include "ABItemBox.generated.h"

UCLASS()
class KGAME_API AABItemBox : public AActor
{
    GENERATED_BODY()

public: 
    // Sets default values for this actor's properties
    AABItemBox();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public: 
    // Called every frame
    virtual void Tick(float DeltaTime) override;
public:
    UPROPERTY(VisibleAnywhere, Category = Box)
        UBoxComponent* Trigger;

    UPROPERTY(VisibleAnywhere, Category = Box)
        UStaticMeshComponent* Box;
};



//cpp
AABItemBox::AABItemBox()
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = false;

    Trigger = CreateDefaultSubobject<UBoxComponent>(TEXT("TRIGGER"));
    Box = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("BOX"));

    RootComponent = Trigger;
    Box->SetupAttachment(RootComponent);

    Trigger->SetBoxExtent(FVector(40.0f, 42.0f, 30.0f));                //박스 틀 생성
    static ConstructorHelpers::FObjectFinder<UStaticMesh> SM_BOX(TEXT(
        "/Game/InfinityBladeGrassLands/Environments/Breakables/StaticMesh/Box/SM_Env_Breakables_Box1.SM_Env_Breakables_Box1"));

    if (SM_BOX.Succeeded())
    {
        Box->SetStaticMesh(SM_BOX.Object);
    }
    Box->SetRelativeLocation(FVector(0.0f, -3.5f, -30.0f));
}

 

오브젝트 채널 추가 후 프리셋 추가, 캐릭터도 박스랑 겹칩으로 설정

 

겹칩으로 설정을 하면 Overlap 이벤트를 처리 할수 있게 된다 OnComponentBeginIverlap 라는 델리게이트가 선언되 있다

//.h
virtual void PostInitializeComponents() override;
private:
    UFUNCTION()
        void OnCharacterOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
};

//cpp
AABItemBox::AABItemBox()
{
...
    Trigger->SetCollisionProfileName(TEXT("ItemBox"));
    Box->SetCollisionProfileName(TEXT("NoCollision"));
}

void AABItemBox::PostInitializeComponents()
{
    Super::PostInitializeComponents();
    Trigger->OnComponentBeginOverlap.AddDynamic(this, AABItemBox::OnCharacterOverlap);
}

void AABItemBox::OnCharacterOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, 
    bool bFromSweep, const FHitResult& SweepResult)
{

}

 

  • 무기는 스켈레톤 메쉬로 되어 있음으로 코드추가하고
  • 스켈레톤 메쉬에 경로를 찾아서 적용해준다
  • 마지막에 소켓을 부모로 해서 연결해준다.
//.h
    UPROPERTY(VisibleAnyWhere, Category = Weapon)
        USkeletalMeshComponent* Weapon;

//.cpp

    FName WeaponSocket(TEXT("hand_rSocket"));
    if (GetMesh()->DoesSocketExist(WeaponSocket))
    {
        Weapon = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("WEAPON"));
        static ConstructorHelpers::FObjectFinder<USkeletalMesh> SK_WEAPON(TEXT(
            "/Game/InfinityBladeWeapons/Weapons/Blade/Swords/Blade_BlackKnight/SK_Blade_BlackKnight.SK_Blade_BlackKnight"));

        if (SK_WEAPON.Succeeded())
        {
            Weapon->SetSkeletalMesh(SK_WEAPON.Object);
        }

        Weapon->SetupAttachment(GetMesh(), WeaponSocket);
    }

무기 액터 클래스 제작
위 코드와 흡사하게 제작

/.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once
#include "KGame.h"
#include "GameFramework/Actor.h"
#include "ABWeapon.generated.h"

UCLASS()
class KGAME_API AABWeapon : public AActor
{
    GENERATED_BODY()

public: 
    // Sets default values for this actor's properties
    AABWeapon();

protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

public: 
    // Called every frame
    virtual void Tick(float DeltaTime) override;
public:
    UPROPERTY(VisibleAnyWhere, Category = Weapon)
        USkeletalMeshComponent* Weapon;
};


//cpp
AABWeapon::AABWeapon()
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = false;

    Weapon = CreateDefaultSubobject<USkeletalMeshComponent>(TEXT("WEAPON"));
    RootComponent = Weapon;

    static ConstructorHelpers::FObjectFinder<USkeletalMesh> SK_WEAPON(TEXT(
        "/Game/InfinityBladeWeapons/Weapons/Blade/Swords/Blade_BlackKnight/SK_Blade_BlackKnight.SK_Blade_BlackKnight"));

    if (SK_WEAPON.Succeeded())
    {
        Weapon->SetSkeletalMesh(SK_WEAPON.Object);
    }

    Weapon->SetCollisionProfileName(TEXT("NoCOllision"));  //충돌 프리셋 설정
}

 

기존 캐릭터 클래스에 무기 헤더 추가해서 적용
기존 캐릭터에 있는 무기 관련 코드는 제거
SpawnActor<>() = 액터를 생성하는 명령

void AABCharacter::BeginPlay()
{
    Super::BeginPlay();
    FName WeaponSocket(TEXT("hand_rSocket"));
    auto CurWeapon = GetWorld()->SpawnActor<AABWeapon>(FVector::ZeroVector, FRotator::ZeroRotator);

    if (nullptr != CurWeapon)
    {
        CurWeapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::SnapToTargetNotIncludingScale, WeaponSocket);
    }
}

+ Recent posts