기본 콘텐츠로 건너뛰기

250407 C++ 애니메이션

블루프린트 사용하는 이유

유지보수가 쉬움


Animation Blueprint

C++ 클래스 생성에서 All Classes를 클릭하고 AnimInstance를 하나 만든다.


블루프린트에서 Animation Blueprint 생성을 누른다.

Skeleton을 본인이 사용할 것을 골라주고, Parent Class를 생성한 AnimInstance의 이름을 골라 생성해준다.


제대로 생성되었다면, Class Settings - Class Options - Parent Class에 해당 AnimInstance가 들어간 것을 확인할 수 있다.


//메쉬의 애니메이션 설정에 애니메이션 인스턴스 삽입
static ConstructorHelpers::FClassFinder<UAnimInstance> AnimInstance(TEXT("/Game/Blueprints/ABP_Player.ABP_Player_C"));
if (AnimInstance.Class)
{
GetMesh()->SetAnimInstanceClass(AnimInstance.Class);
}

경로를 지정해줄때, Class를 가져올 것이므로 경로 끝에_C를 추가해주자.
아래의 경우에도 동작은 한다.


PlayerAnimInstance.h

PlayerAnimInstance.cpp
NativeInitiallizeAnimation
Owner에 GetOwningActor를 하여 가져올 때 Cast를 통해서 캐릭터형으로 받고 있다.
찾은 Owner에 CharacterMovement를 Owner->GetCharcterMovement()를 통해 Movement에 넣고 있다.
Super::NativeInitializeAnimation();

//Cast ACharacter
Owner = Cast<ACharacter>(GetOwningActor());

//Movement
if (Owner)
{
Movement = Owner->GetCharacterMovement();
}

NativeUpdateAnimation
앞서 찾아온 Movement와 Owner를 이용해서, Velocity, MoveSpeed, isFalling, Angle의 값을 할당해주고 있다.
Super::NativeUpdateAnimation(DeltaSeconds);

//Movement를 가져와서 값 할당
if (Movement)
{
Velocity = Movement->Velocity;
MoveSpeed = Velocity.Size2D();
isFalling = Movement->IsFalling();
Angle = UAnimInstance::CalculateDirection(Velocity, Owner->GetActorRotation());
}




AnimationBlueprint - My Blueprint의 톱니바퀴 버튼을 눌러, Show Inherited Variables 를 눌러준다.



생성한 BlendSpace를 Entry와 연결한다.
플레이어의 MoveSpeed를 BlendSpace의 이동 애니메이션에 사용할 것이라서 연결해준다.

각도값 또한 추가해주기 위해 변수 Angle을 만들어서 넣어준다.
UAnimInstance::CalculateDirection



캐릭터에 무기를 추가하기 위해서, 캐릭터의 스켈레톤 메쉬를 클릭하여 열고, Add Socket을 통해 Weapon Socket을 추가한다. 
다음으로 Add Preview Asset을 통해서, 추가할 Weapon의 위치를 조정해준다.

하지만, 이러한 방식을 사용하면, 상위의 Bone이나 그러한 것들 때문에, Weapon_Socket_R이 영향을 받아서 제대로 된 애니메이션이 나오지 않을 것이다.
무기를 더해줄 때는, 어느 부분까지 영향을 미칠 수 있는 가를 생각해서, Socket을 만들어 넣어야한다.


제대로 된 애니메이션을 위해서 hand_r의 밑에 Socket을 만들고 Preview를 통해서 적절한 각도, 위치로 설정한다.

가능하면 Skeleton에서 해당 작업을 하자. SkeletalMesh에서는 모델링에 영향을 끼칠 수 있다.

스태틱 메쉬를 사용할것이라 include 해준다.

Weapon에 스태틱메쉬를 만들어 WeaponMesh로 이름을 정한다.
SetupAttachment를 통해, 플레이어의 메쉬에 Weapon을 하위로 붙입니다.
ConstructorHelpers::FObjectFinder를 통해, Weapon의 스태틱 메쉬의 위치를 찾습니다.

Succeeded()를 통해 오류를 방지하고, 제대로 가져왔는지 확인합니다.
제대로 가져왔다면, SetStaticMesh로 SM_Weapon에 넣어줍니다.

AttachToComponent를 통해, 플레이어 캐릭터 메쉬의 hand_rSocket에 상대좌표로 붙입니다.

AttachToComponent(붙일 Mesh 이름, 붙일 방식, TEXT("붙일 본/소켓의 이름"));

//무기 추가
SM_Weapon = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("WeaponMesh"));
SM_Weapon->SetupAttachment(GetMesh());
static ConstructorHelpers::FObjectFinder<UStaticMesh> WeaponMesh(TEXT("/Script/Engine.StaticMesh'/Game/Asset/Weapons/FPS_Weapon_Bundle/Weapons/Meshes/AR4/SM_AR4.SM_AR4'"));

//SM_Weapon에 Ar4 추가
if (WeaponMesh.Succeeded())
{
SM_Weapon->SetStaticMesh(WeaponMesh.Object);
}

// hand_rSocket에 SM_Weapon을 추가한다.
SM_Weapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::KeepRelativeTransform, TEXT("hand_rSocket"));


조준 모드 / 총알 발사
InputAction Aim과 Fire를 통해 구현한다.

아래에서 액션을 가져온다.

버튼을 누르는 동안 조준과 조준을 푸는 것을 위해서 다음과 같이 만든다.

ETriggerEvent::Triggered : 매 틱마다
ETriggerEvent::Started : 맨 처음 한 번
ETriggerEvent::Completed : 맨 마지막에 한 번

AnimInstance를 사용하므로, cpp의 상위에 선언해주자.


Montage를 이용한 애니메이션
Montage로 생성하고 싶은 애니메이션 시퀀스를 선택하고,
오른쪽 마우스 버튼 - Create - Create AnimMontage로 생성한다.

C++ 상에서 사용하기 위해서 정의해준다.
여기서 BlueprintReadWrite를 통해, 우리가 원하는 Montage를 할당할 수 있게 된다.
대신에, 이러한 방식의 경우 Montage가 없을 경우,

문제가 발생하므로, 코드 작성시에 Montage가 존재 하는가에 대한 확인이 필요하다.
따라서 코드 작성시에
if (RifleAimMontage)
{
    AnimInstance->MontagePlay(RifleAimMontage);
}



애니메이션 블루 프린트의 Locomotion에 Slot을 하나 만들어서 연결한다.

해당 몽타주는 우리가 만든 Default 슬롯을 통해서 플레이 된다.
몽타주를 재생할 슬롯은 몽타주에서 설정 가능하다.

Montage를 할당한 후에 AnimInstance->Montage_Play(몽타주); 를 통해 플레이 하면, 애니메이션이 재생되기는 하지만, 중간에 끊기거나 제대로 플레이 되지 않을 것이다.
왜냐하면, Montage_Play에 기본 설정시에 1.0f시간 만큼만 재생되기 때문이다.

따라서, Montage의 EndTime의 값을 확인해서
AnimInstance->Montage_Play(재생할 몽타주 , End Time);
이번의 경우는 0.3f로 설정해주었다.

잘못된 애니메이션 시퀀스의 경우는 약간의 노력으로 수정될 수도 있다.
Additive Settings의 Additive Anim Type
Base Pose Animation의 애니메이션 시퀀스 선택
Root Motion Force Root Lock 여부 변경
등을 통해서 잘못되는 경우를 막을 수 있다.



참고🐋🐋🐋🐋🐋

ConstructorHelpers
https://dev.epicgames.com/documentation/ko-kr/unreal-engine/gameplay-classes-in-unreal-engine?application_version=5.5#%EC%83%9D%EC%84%B1%EC%9E%90%EC%8A%A4%ED%83%9C%ED%8B%B1%EB%B0%8F%ED%97%AC%ED%8D%BC

NativeInitializeAnimation
https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Engine/Animation/UAnimInstance/NativeInitializeAnimation?application_version=5.5

NativeUpdateAnimation
https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Engine/Animation/UAnimInstance/NativeUpdateAnimation?application_version=5.5

GetAnimInstance
https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Engine/Components/USkeletalMeshComponent/GetAnimInstance

AttachToComponent
https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Engine/GameFramework/AActor/AttachToComponent?application_version=4.27

ETrigger
https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Plugins/EnhancedInput/ETriggerEvent?application_version=5.5

Montage_play
https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Runtime/Engine/Animation/UAnimInstance/Montage_Play

이 블로그의 인기 게시물

250523 PCG 2

PCG Building Volume Sampler를 사용할 경우 Voxel Size를 맞추어주자. 2m길이를 Transform Points로 Offset을 이동시켜 Difference를 통해 메쉬를 생성한다. 사용시에 Pivot의 위치가 중앙이 아니면 제대로 표현되지 않을 것이다. PCG Landscape Landscape 생성후 PCG Volume 생성. Get Landscape Data - Surface Sampler 안보인다면 Generate Surface Sampler 일반 그리드 패턴으로 서피스 데이터에 포인트를 샘플링합니다. 이 노드에는 다음과 같은 옵션이 있습니다. 포인트 규모(Point Extents) : 서피스의 기본 그리드 셀 크기를 정의합니다. 여유(Looseness) : 변형 가능한 셀 크기를 정의합니다. 실제로 셀 크기는 포인트 규모 * (1 + 여유)입니다. 제곱미터당 포인트(Points Per Square Meter) : 유지되는 셀의 비율을 계산합니다. 이 프로퍼티는 그리드가 클 때 과잉을 제한합니다. Looseness : 기본 1. 0일경우 Extent의 길이 만큼, 1일경우 2배가 됨.  Attribute Filter를 생성하고, Material에 해당하는 Layer를 찾아서 이름을 넣어준다. Type을 Float로 하고 Float Value를 설정하면, 해당 머티리얼이 색칠된 정도 0~1에서 Operator에 의해 >(초과) 0.6초과 일 경우만 생성되게 할 수 있다. 값을 1로 하고 Equal을 이용해서 1인 경우만 생성되게 하는 것도 가능하다. plugin Water body lake를 넣으면, 이또한 Spline이라는 것을 알 수 있다. 따라서 Get Spline Data-Spline Sampler를 통해 사용할 수 있다. GetActorData와 Difference를 통해서 Lake내부를 제거해주기 PCG Mesh Mesh Sampler를 쓰기위해 Get Actor Property를 이용하면, 해당 액터가 ...

250604 저장불러오기

SaveGame 블루프린트 생성 Game Instance 생성, Init 오버라이드 AC_Inventory Save Load

250609 온라인

 Online Subsystem외에도 어느 서버를 사용할 것인지에 따라 플러그인을 추가해주어야 한다. 공통으로 OnlineSubsystem으로 시작한다. 헤더파일 CPP파일 cmd에서 Ipconfig를 통해 Ipv4를 넣어주면 된다. DefaultEngine.ini 수정 https://dev.epicgames.com/documentation/ko-kr/unreal-engine/online-subsystem-steam-interface-in-unreal-engine#%EC%99%84%EC%84%B1%EC%84%B8%ED%8C%85 [/Script/Engine.GameEngine] +NetDriverDefinitions=(DefName="GameNetDriver",DriverClassName="OnlineSubsystemSteam.SteamNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver") [OnlineSubsystem] DefaultPlatformService=Steam [OnlineSubsystemSteam] bEnabled=true SteamDevAppId=480 bInitServerOnclient=true [/Script/OnlineSubsystemSteam.SteamNetDriver] NetConnectionClassName="OnlineSubsystemSteam.SteamNetConnection" 전용 서버 없이 클라이언트가 서버를 만들 수 있게 함. 호스팅 Binaries와 Saved파일을 한번 제거해주고, Generate해주기 헤더파일 헤더파일 CPP 파일 CreateSessionComplete, FindSessionsComplete, JoinSessionComplete 생성자 CreateGameSession 세션을 생성하고 존재하면 제거한다. OnCreateSessionComplete ServerTrav...