오늘부터는 무기를 만들어볼 것이다 

일단 아이템클래스를 상속받는 C++ 클래스를 만들어주고 이를 바탕으로하는 블루프린트 클래스를 만들어주자 

 

이 Weapon 클래스는 아이템클래스를 상속받기 때문에 아이템이 가지고있는 기능을 쓸 수 있다.

 

아이템 클래스에서 오버랩에 빠져나갈 때와 들어갔을 때의 이벤트를 virtual로 만들어서 Weapon 클래스에서 override

할 수 있게 만들어주자

Item.h

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);

 

Weapon.h

	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;

Weapon.cpp

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


#include "Items/Weapons/Weapon.h"

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

}

void AWeapon::OnSphereEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{

}

 

이렇게 하면 override된 함수에서는 아무런 기능을 구현해두지 않았기 때문에 아무런 효과가 나타나지 않는다.

 

만약 여기서 Super를 통해 부모의 함수를 가져와 사용하게 된다면 다시 글이 나타나게 된다.

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


#include "Items/Weapons/Weapon.h"

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::OnActorEndOverlap(OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex);
}

 

이제 애니메이션을 추가해보자 

 

캐릭터 애니메이션은 보통 Mixamo에서 무료 애니메이션을 찾아서 다운받을 수 있다

https://www.mixamo.com/#/

 

Mixamo

 

www.mixamo.com

 

나는 여기중에서  xbot을 다운해서 사용해 볼 것이다.

 

다운받을 때 설정은 기본값으로 유지한채로 다운받아주자

 

그리고 받은 에셋을 임폴트 해주자 마찬가지로 설정값은 디폴트상태로 유지한채로 임폴트해주

 

이제 애니메이션을 다운받아주자 애니메이션은 axe 애니메이션중에 Standing Idle와 Standing Melee Attack  을 다운받아주자 다운로드  설정값은 Skin부분만 Without Skin으로 맞춰주고 다운로드해주자 

 

이제 이 애니메이션을 임폴트해주면 되는데 이때 매시를 다운받은 Xbot 매시로 지정해주면 된다

 

이제 이 애니메이션을 기존에 만들어둔 캐릭터에서도 사용할 수 있도록 IK리타겟팅을 해주어야한다. 이를 위해 SKM_Xbot을 기반으로 IK Rigs를 만들어주고 이를 통해 리타겟팅 체인을 만들어주자

 

 

이제 이것을 캐릭터에도 동일하게 해주면 된다.

 

이제 애니메이션/IK 리타켓터를 만들어둔  Xbot의 IK Rig를 바탕으로 만들어서 캐릭터로 리타겟팅해보자 

리타켓팅할때는 기본 포즈가 Xbot과 같도록 팔의 각도를 바꿔주면서 자세 애니메이션이 자연스러운지 확인해보면서 

만들면된다.

 

그리고 나서는 Export하고자하는 애니메이션을 클릭한뒤 Export 해주면된다.

 

이렇게하게되면 애니메이션을 추출해서 사용할 수 있다.

 

이제 칼을 실제로 들고 있을 수 있도록 해보자 

일단 블루프린트로 먼저 구현하고 C++로 구현해보자

블루프린트에서는 Collision이 시작될 때 캐릭터의 RightHandSocket에 Attach되도록 만들어주자

이렇게 하게되면 충돌시 무기가 Socket자리에 들어가게 된다.

이 과정을 코드로 구현해보자

 FAttachmentTransformRules을 선언해주고 AttachToComponent로 붙여주면 된다.

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

	ASlashCharacter* SlashCharacter = Cast<ASlashCharacter>(OtherActor);
	if (SlashCharacter)
	{
		FAttachmentTransformRules TransformRules(EAttachmentRule::SnapToTarget,true);
		ItemMesh->AttachToComponent(SlashCharacter->GetMesh(), TransformRules, FName("RightHandSocket"));
	}
}

 

결과

 

 

이제 E키를 통해 무기를 주울 수 있게 구현해보자 

일단 만약 아이템근처에 가게되면 캐릭터에서 그 아이템을 식별할 수 있도록 AItem 포인터 변수를 선언해주고 Set함수를 통해 아이템근처에 가면 그 변수를 초기화해주고 근처에서 벗어나면 nullptr로 바뀌도록 해주자

 

SlashCharacter.h

private:
	UPROPERTY(VisibleInstanceOnly)
	AItem* OverlappingItem;
public:
	FORCEINLINE void SetOverlappingItem(AItem* Item) { OverlappingItem = Item; }

 

Item.cpp

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);
	}
}

 

그리고 만약 E키를 누르면 무기가 소켓안으로  Attach될수있도록 E키 액션바인딩함수와 무기클래스에서 Attach함수를 만들어 작동할 수 있도록 구현하자

 

Weapon.h

public:
	void Equip(USceneComponent* Inparent,FName InSocketName);

Weapon.cpp

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

 

SlashCharacter.h

protected:
UPROPERTY(EditAnywhere, Category = Input)
UInputAction* EKeyAction;
    
void EKeyPressed();

 

SlashCharacter.cpp

void ASlashCharacter::EKeyPressed()
{
	AWeapon* OverlappingWeapon = Cast<AWeapon>(OverlappingItem);
	if (OverlappingItem)
	{
		OverlappingWeapon->Equip(GetMesh(), FName("RightHandSocket"))
	}
}

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

	if (UEnhancedInputComponent* EnhancedInputComponent = CastChecked<UEnhancedInputComponent>(PlayerInputComponent))
	{
		EnhancedInputComponent->BindAction(MovementAction, ETriggerEvent::Triggered, this, &ASlashCharacter::Move);
		EnhancedInputComponent->BindAction(LookAction, ETriggerEvent::Triggered, this, &ASlashCharacter::Look);
		EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Triggered, this, &ACharacter::Jump);
		EnhancedInputComponent->BindAction(EKeyAction, ETriggerEvent::Triggered, this, &ASlashCharacter::EKeyPressed);
	}
}

 

이렇게하고 

키액션을 바인딩해주면 E키를 누르면 무기가 주워지는 모습을 볼 수 있다.

 

결과화면

 

무기를 가지고있을 때의 애니메이션을 적용시켜주기위해 Enum Class CharacterState를 만들어주고

무기를 착용하면 캐릭터의 State가 바뀌도록 만들고 이를 블루프린트에서 볼 수 있도록 적용해주자

CharacterTypes.h

#pragma once


UENUM(BlueprintType)
enum class ECharacterState : uint8
{
	ECS_Unequipped UMETA(DisplayName = "Unequipped"),
	ECS_EquippedOneHandedWeapon UMETA(DisplayName = "Equipped One-Handed Weapon"),
	ECS_EquippedTwoHandedWeapon UMETA(DisplayName = "Equipped Two-Handed Weapon")
};

SlashCharacter.h

	FORCEINLINE ECharacterState GetCharacterState() const { return CharacterState; }

 

SlashAnimInstance.h

	UPROPERTY(BlueprintReadOnly,Category="Movement | Character State")
	ECharacterState CharacterState;

SlashAnimInstance.cpp

void ASlashCharacter::EKeyPressed()
{
	AWeapon* OverlappingWeapon = Cast<AWeapon>(OverlappingItem);
	if (OverlappingItem)
	{
		OverlappingWeapon->Equip(GetMesh(), FName("RightHandSocket"));
		CharacterState = ECharacterState::ECS_EquippedOneHandedWeapon;
	}
}

 

그리고 Blend pose by EcharacterState를 사용하여 각각의 State에 따라 다른 애니메이션이 출력될 수 있도록 하자

 

이렇게 해주면 이제 무기를 쥐고있을 때 다른 애니메이션이 출력된다.

 

이제 무기를 쥐고 달리는 애니메이션을 추가해보자 

Mixamo에서 axe의 Standing Run Forward 애니메이션에서 In Place가 체크된 애니메이션을 다운받아서 임폴트 해주자

전에 했던것과 같이 리타켓팅을 통해 애니메이션을 Export 해주자

 

그리고 위에 했던 것과 같이 Blend를 통해 애니메이션이 출력되도록 해주자

 

그런데 이렇게 하게되면 애니메이션이 추가될때마다 블루프린트가 복잡해지게 된다.

다양한 애니메이션을 Mutiple Animation BluePrint를 통해 관리해줄 수 있다.

 

일단 캐릭터의 주요한 애니메이션을 담을 ABP_Echo_MainStates 애니메이션 블루프린트를 새로 만들어주자 

그리고  Main state와 Ground Locomotion을 복사 붙여넣기 해주자

이상태로 컴파일해주게 되면 오류가 발생하는데 변수까지 복사를 해주어서 발생하는 문제점이다. 일단 오류가 발생하는 변수들을 모두 생성해주자 

이 변수들은 블루프린트에서 만들어진 변수로 캐릭터의 상태가 바뀌어도 변수의 값이 변하지 않기때문에 주의해야한다. 

그리고 Linked Anim Graph를 통해  ABP_Echo에 사용한다. 이때 변수들은 ABP_Echo의 변수가 바인딩될 수 있도록

디테일 창에서 바인딩 시켜준다.

 

이제 IK부분을 추출하여 애니메이션 블루프린트를 만들어주자 

이때 변수들은 바인딩시켜주고 캐시포즈같은경우에는 Input Pose를 통해 처리해주면 된다.

이렇게 하면 이전과 동일한 애니메이션이 출력되는 것을 볼 수 있다.

 

 

+ Recent posts