[UE] Hand IK, AnimInstance, Fire
Unreal Engine 4의 Hand IK(Inverse Kinematics)는 캐릭터의 가상 손이 사실적인 방식으로 환경 및 개체와 상호 작용할 수 있도록 하는 기능이다. 대상 개체의 위치와 방향을 기반으로 캐릭터 손의 위치와 방향을 계산하여 작동한다. 언리얼 엔진 4에서 Hand IK를 사용하려면 스켈레탈 메시가 있는 캐릭터와 손 애니메이션이 포함된 애니메이션 세트가 있어야 한다. Hand IK 시스템은 애니메이션 데이터를 사용하여 캐릭터 손의 위치와 방향을 실시간으로 계산한다.
목차
Weapon
Unreal Engine 4의 Hand IK(Inverse Kinematics)는 캐릭터의 가상 손이 사실적인 방식으로 환경 및 개체와 상호 작용할 수 있도록 하는 기능이다. 대상 개체의 위치와 방향을 기반으로 캐릭터 손의 위치와 방향을 계산하여 작동한다.
언리얼 엔진 4에서 Hand IK를 사용하려면 스켈레탈 메시가 있는 캐릭터와 손 애니메이션이 포함된 애니메이션 세트가 있어야 한다. Hand IK 시스템은 애니메이션 데이터를 사용하여 캐릭터 손의 위치와 방향을 실시간으로 계산한다.
Hand IK를 설정하려면 애니메이션 블루프린트에 Hand IK 노드를 생성해야 합니다. 이 노드는 대상 오브젝트의 위치와 회전을 입력으로 받아 캐릭터 손의 위치와 회전을 출력합니다. 그런 다음 이 출력을 사용하여 게임에서 캐릭터 손의 위치와 회전을 구동할 수 있다.
Hand IK는 물체 집기 및 조작, 등반, 몸짓과 같은 다양한 상호 작용에 사용할 수 있습니다. 또한 포인팅, 손 흔들기 및 잡기와 같은 애니메이션 중에 사실적인 손 움직임을 시뮬레이션하는 데 사용할 수 있다.
전반적으로 Unreal Engine 4의 Hand IK는 플레이어가 가상 손을 사용하여 게임 세계에 완전히 참여할 수 있도록 하는 몰입형 인터랙티브 가상 환경을 만들기 위한 강력한 도구이다.
Characters | |
CAnimInstance.h .cpp CPlayer.h .cpp |
|
Utilities | |
CHelpers.h CLog.h .cpp |
|
Weapons | |
CWeapon.h .cpp CWeapon_AR4.h .cpp CWeaponComponent.h .cpp |
|
CGameMode.h .cpp Global.h CAnimNotifyState_Equip.h .cpp |
Animation, AimOffset, BlendSpace, Montage 작업
Rifle_Idle_AO에서 Rifle_Idle_Center, Up, Down 생성
모든 동작이 존재하는 Rifle_Idle_AO를 복사해서 파일 3개를 만든다.
Center, Up, Down에 해당하는 프레임 하나를 남기고 다 지운다. 이렇게 만든 Rifle_Idle_Center, Up, Down은 AO_Idle를 만들 때 쓰인다.
Additive Anim Type
- Local Space: 수정 불가.
- Mesh Space: 언리얼엔진 옵션으로 수정.
- Component Space: 프로그램 영역에서 수정.
Base Pose Type
- Skeleton Referenc Pose: A포즈
- Selected animation scaled: 애니메이션 커졌다 작아졌다 할 때 주로 쓰인다.
- Selected animation frame: 매 프레임 단위로 섞어준다. (주로 사용되는 옵션이다).
Base Pose Animation
- 기준이 되는 애니메이션. 여기서는 Rifle_Idle로 설정하였다.
AO_Rifle_Idle 생성
애니메이션 - 에임 오프셋 1D - AO_Rifle_Idle 생성
Rifle_Idle_Center, Up, Down 할당.
Rifle_Aim_AO에서 Rifle_Aim_Center, Up, Down 생성
위와 같은 방법으로 Rifle_Aim_Center, Up, Down 생성
모든 동작이 존재하는 Rifle_Aim_AO를 복사해서 파일 3개를 만든다.
Center, Up, Down에 해당하는 프레임 하나를 남기고 다 지운다. 이렇게 만든 Rifle_Idle_Center, Up, Down은 AO_Rifle_Aim을 만들 때 쓰인다.
Idle에서 사용한 세팅값과 같이 넣어준다.
AO_Rifle_Aim 생성
애니메이션 - 에임 오프셋 1D - AO_Rifle_Aim 생성
Aim_Idle_Center, Up, Down 할당.
BS_Rifle_Aim 생성
Blend Space를 만든다.
BS_Rifle를 복사하여 BS_Rifle_Aim 생성
Rifle_Aim_Fixed 생성
Rifle_Aim_Idle 애니메이션을 복사하여 Rifle_Aim_Fixed 생성
0 프레임을 제외한 나머지 프레임 삭제하여 정지된 프레임으로 만들어준다.
Rifle_Equip_Montage - 커브 추가
커브 - 커브 추가 - 커브 생성 - LeftHand 생성
- 값 추가 - (0, 1), (1.421167, 1), (1.5, 0)
CAnimInstance
CAnimInstance.h
#pragma once
#include "CoreMinimal.h"
#include "Animation/AnimInstance.h"
#include "Weapons/CWeaponComponent.h"
#include "CAnimInstance.generated.h"
UCLASS()
class U2212_04_API UCAnimInstance : public UAnimInstance
{
GENERATED_BODY()
protected:
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Animation")
float Speed;
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Animation")
float Pitch;
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Animation")
float Direction;
protected:
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Weapons")
EWeaponType WeaponType = EWeaponType::Max; //기본값 Max
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Weapons")
bool bInAim;
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Weapons")
bool bUseIK;
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Weapons")
FVector LeftHandLocation;
public:
void NativeBeginPlay() override;
void NativeUpdateAnimation(float DeltaSeconds) override;
private:
UFUNCTION()
void OnWeaponTypeChanged(EWeaponType InPrevType, EWeaponType InNewType);//?
private:
class ACPlayer* OwnerCharacter;
class UCWeaponComponent* Weapon;
private:
FRotator PrevRotation; //이전 값 저장
};
변수 생성
- Animation 카테고리: bool Pitch, float Direction
- Weapons 카테고리: bool bUseIK, FVector LeftHandLocation
- FRotator PrevRotation; //이전 값 저장
CAnimInstance.cpp
#include "CAnimInstance.h"
#include "Global.h"
#include "CPlayer.h"
void UCAnimInstance::NativeBeginPlay()
{
Super::NativeBeginPlay();
OwnerCharacter = Cast<ACPlayer>(TryGetPawnOwner());
CheckNull(OwnerCharacter);
Weapon = CHelpers::GetComponent<UCWeaponComponent>(OwnerCharacter);
CheckNull(Weapon);
Weapon->OnWeaponTypeChanged.AddDynamic(this, &UCAnimInstance::OnWeaponTypeChanged);
}
void UCAnimInstance::NativeUpdateAnimation(float DeltaSeconds)
{
Super::NativeUpdateAnimation(DeltaSeconds);
CheckNull(OwnerCharacter);
Speed = OwnerCharacter->GetVelocity().Size2D();
FRotator rotator = OwnerCharacter->GetVelocity().ToOrientationRotator();
FRotator rotator2 = OwnerCharacter->GetControlRotation();
FRotator delta = UKismetMathLibrary::NormalizedDeltaRotator(rotator, rotator2);
PrevRotation = UKismetMathLibrary::RInterpTo(PrevRotation, delta, DeltaSeconds, 25); //선형보간 사용.
Pitch = UKismetMathLibrary::FInterpTo(Pitch, OwnerCharacter->GetBaseAimRotation().Pitch, DeltaSeconds, 25);
Direction = PrevRotation.Yaw;
bInAim = Weapon->IsInAim();
bUseIK = Weapon->IsUnarmedMode() == false;
LeftHandLocation = Weapon->GetLeftHandLocation();
}
void UCAnimInstance::OnWeaponTypeChanged(EWeaponType InPrevType, EWeaponType InNewType)
{
WeaponType = InNewType;
}
NativeUpdateAnimation(float DeltaSeconds)에서 헤더에서 생성한 변수에 값을 넣어준다.
- rotator, rotator2를 구하여 delta로 두 값을 Normalize 한다.
- FRotator delta = UKismetMathLibrary::NormalizedDeltaRotator(rotator, rotator2);
- Direction
- Direction = PrevRotation.Yaw (방향을 이전 Rotation의 Yaw값 사용)
CPlayer
CPlayer.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "CPlayer.generated.h"
UCLASS()
class U2212_04_API ACPlayer : public ACharacter
{
GENERATED_BODY()
private:
UPROPERTY(EditDefaultsOnly, Category = "Camera")
FVector2D PitchRange = FVector2D(-40, +40);
private:
UPROPERTY(VisibleAnywhere)
class USpringArmComponent* SpringArm;
UPROPERTY(VisibleAnywhere)
class UCameraComponent* Camera;
private:
UPROPERTY(VisibleAnywhere)
class UCWeaponComponent* Weapon;
public:
ACPlayer();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
private:
void OnMoveForward(float InAxisValue);
void OnMoveRight(float InAxisValue);
void OnHorizontalLook(float InAxisValue);
void OnVerticalLook(float InAxisValue);
private:
void OnRun();
void OffRun();
};
플레이어의 시야각을 제한하기 위해 PitchRange 변수를 생성하고 Default값을 설정한다.
- FVector 2D PitchRange = FVector2D(-40, +40)
CPlayer.cpp
#include "CPlayer.h"
#include "Global.h"
#include "CAnimInstance.h"
#include "Weapons/CWeaponComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Camera/CameraComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/InputComponent.h"
#include "Materials/MaterialInstanceDynamic.h"
ACPlayer::ACPlayer()
{
PrimaryActorTick.bCanEverTick = true;
CHelpers::CreateComponent<USpringArmComponent>(this, &SpringArm, "SpringArm", GetCapsuleComponent());
CHelpers::CreateComponent<UCameraComponent>(this, &Camera, "Camera", SpringArm);
CHelpers::CreateActorComponent<UCWeaponComponent>(this, &Weapon, "Weapon");
USkeletalMesh* mesh;
CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Character/Mesh/SK_Mannequin.SK_Mannequin'");
GetMesh()->SetSkeletalMesh(mesh);
GetMesh()->SetRelativeLocation(FVector(0, 0, -90));
GetMesh()->SetRelativeRotation(FRotator(0, -90, 0));
TSubclassOf<UCAnimInstance> animInstance;
CHelpers::GetClass<UCAnimInstance>(&animInstance, "AnimBlueprint'/Game/Player/ABP_Player.ABP_Player_C'");
GetMesh()->SetAnimClass(animInstance);
GetCharacterMovement()->MaxWalkSpeed = 400;
SpringArm->SetRelativeLocation(FVector(0, 0, 60));
SpringArm->TargetArmLength = 200;
SpringArm->bUsePawnControlRotation = true;
SpringArm->bEnableCameraLag = true;
}
void ACPlayer::BeginPlay()
{
Super::BeginPlay(); //Super가 BP의 BeginPlay를 콜한다.
GetController<APlayerController>()->PlayerCameraManager->ViewPitchMin = PitchRange.X; //각 제한 걸기
GetController<APlayerController>()->PlayerCameraManager->ViewPitchMax = PitchRange.Y; //각 제한 걸기
}
void ACPlayer::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ACPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis("MoveForward", this, &ACPlayer::OnMoveForward);
PlayerInputComponent->BindAxis("MoveRight", this, &ACPlayer::OnMoveRight);
PlayerInputComponent->BindAxis("HorizontalLook", this, &ACPlayer::OnHorizontalLook);
PlayerInputComponent->BindAxis("VerticalLook", this, &ACPlayer::OnVerticalLook);
PlayerInputComponent->BindAction("Run", EInputEvent::IE_Pressed, this, &ACPlayer::OnRun);
PlayerInputComponent->BindAction("Run", EInputEvent::IE_Released, this, &ACPlayer::OffRun);
PlayerInputComponent->BindAction("AR4", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::SetAR4Mode);//Object를 Weapon으로 받는다.
PlayerInputComponent->BindAction("Aim", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::Begin_Aim);
PlayerInputComponent->BindAction("Aim", EInputEvent::IE_Released, Weapon, &UCWeaponComponent::End_Aim);
PlayerInputComponent->BindAction("Fire", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::Begin_Fire);
PlayerInputComponent->BindAction("Fire", EInputEvent::IE_Released, Weapon, &UCWeaponComponent::End_Fire);
}
void ACPlayer::OnMoveForward(float InAxisValue)
{
FRotator rotator = FRotator(0, GetControlRotation().Yaw, 0);
FVector direction = FQuat(rotator).GetForwardVector();
AddMovementInput(direction, InAxisValue);
}
void ACPlayer::OnMoveRight(float InAxisValue)
{
FRotator rotator = FRotator(0, GetControlRotation().Yaw, 0);
FVector direction = FQuat(rotator).GetRightVector();
AddMovementInput(direction, InAxisValue);
}
void ACPlayer::OnHorizontalLook(float InAxisValue)
{
AddControllerYawInput(InAxisValue);
}
void ACPlayer::OnVerticalLook(float InAxisValue)
{
AddControllerPitchInput(InAxisValue);
}
void ACPlayer::OnRun()
{
GetCharacterMovement()->MaxWalkSpeed = 600;
}
void ACPlayer::OffRun()
{
GetCharacterMovement()->MaxWalkSpeed = 400;
}
BeginPlay()
- GetController<APlayerController>()->PlayerCameraManager->ViewPitchMin = PitchRange.X; //각 제한 걸기
- GetController<APlayerController>()->PlayerCameraManager->ViewPitchMax = PitchRange.Y;
SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
- PlayerInputComponent-> BindAction("Fire", EInputEvent::IE_Pressed, Weapon, &UCWeaponComponent::Begin_Fire);
- PlayerInputComponent-> BindAction("Fire", EInputEvent::IE_Released, Weapon, &UCWeaponComponent::End_Fire);
- Fire 입력키값 지정.
IK - Left Hand
IK (Inverse Kinetic) 3가지 종류
Two Bone IK
CCD(Cyclic Coordinate Descent) IK
FABR(Forward and Backward Reaching) IK
- 상대간격을 구한 후 사용한다.
ABP_Player
Rifle Layer
Zoom In
- Root Bone: 어디까지 Bone이 움직일 것인가.
- Precision: 정밀도
- Max Iterations: 정밀도를 높이기위해 몇번이나 반복해서 계산할 것인가. 높을수록 정밀도가 높지만 연산량이 많아진다.
IK Layer
AnimGraph
CWeapon
CWeapon.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CWeapon.generated.h"
//직렬화 해야하는 struct는 클래스 사이에 선언해야 한다.
USTRUCT()
struct FWeaponAimData
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
float TargetArmLength;
UPROPERTY(EditAnywhere)
FVector SocketOffset;
UPROPERTY(EditAnywhere)
float FieldOfView;
public:
void SetData(class ACharacter* InOwner);
void SetDataByNoneCurve(class ACharacter* InOwner);
};
//abstract를 쓰면 Unreal Editor에서 추상 베이스 클래스로 취급하겠다는 의미. 추상이되면 배치시킬 수 없다.
UCLASS(abstract)
class U2212_04_API ACWeapon : public AActor
{
GENERATED_BODY()
protected:
UPROPERTY(EditDefaultsOnly, Category = "Equip")
FName HolsterSocketName;
UPROPERTY(EditDefaultsOnly, Category = "Equip")
class UAnimMontage* EquipMontage;
UPROPERTY(EditDefaultsOnly, Category = "Equip")
float EquipMontage_PlayRate;
UPROPERTY(EditDefaultsOnly, Category = "Equip")
FName RightHandSocketName;
UPROPERTY(EditDefaultsOnly, Category = "Equip")
FVector LeftHandLocation;
protected:
UPROPERTY(EditDefaultsOnly, Category = "Aim")
FWeaponAimData BaseData;
UPROPERTY(EditDefaultsOnly, Category = "Aim")
FWeaponAimData AimData;
UPROPERTY(EditDefaultsOnly, Category = "Aim")
class UCurveFloat* AimCurve;
UPROPERTY(EditDefaultsOnly, Category = "Aim")
float AimingSpeed = 200;
protected:
UPROPERTY(EditDefaultsOnly, Category = "Hit")
float HitDistance = 3000;
private:
UPROPERTY(VisibleAnywhere)
class USceneComponent* Root;
protected:
UPROPERTY(VisibleAnywhere)
class USkeletalMeshComponent* Mesh;
private:
UPROPERTY(VisibleAnywhere)
class UTimelineComponent* Timeline;
public:
FORCEINLINE bool IsInAim() { return bInAim; }
FORCEINLINE FVector GetLeftHandLocation() { return LeftHandLocation; }
public:
ACWeapon();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
public:
bool CanEquip();
void Equip();
void Begin_Equip();
void End_Equip();
bool CanUnequip();
void Unequip();
public:
bool CanAim();
void Begin_Aim();
void End_Aim();
private:
UFUNCTION()
void OnAiming(float Output); //조준
public:
bool CanFire();
void Begin_Fire();
void End_Fire();
private:
UFUNCTION()
void OnFiring(); //연사
private:
bool bEquipping;
bool bInAim;
bool bFiring;
bool bReload;
bool bAutoFire = true;
private:
class ACPlayer* Owner;
};
변수 생성
- FName RightHandSocketName;
- FVector LeftHandLocation;
- CWeapon_AR4에서 LeftHandLocation 값을 넣어준다.
Weapon.cpp
#include "Weapons/CWeapon.h"
#include "Global.h"
#include "Character/CPlayer.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/TimelineComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "Camera/CameraComponent.h"
void FWeaponAimData::SetData(ACharacter* InOwner)
{
USpringArmComponent* springArm = CHelpers::GetComponent<USpringArmComponent>(InOwner);
springArm->TargetArmLength = TargetArmLength;
springArm->SocketOffset = SocketOffset;
}
void FWeaponAimData::SetDataByNoneCurve(ACharacter* InOwner)
{
USpringArmComponent* springArm = CHelpers::GetComponent<USpringArmComponent>(InOwner);
springArm->TargetArmLength = TargetArmLength;
springArm->SocketOffset = SocketOffset;
UCameraComponent* camera = CHelpers::GetComponent<UCameraComponent>(InOwner);
camera->FieldOfView = FieldOfView; //curve를 이용하여 FieldOfView를 만들어준다.
}
///////////////////////////////////////////////////////////////////////////////
ACWeapon::ACWeapon()
{
PrimaryActorTick.bCanEverTick = true;
CHelpers::CreateComponent<USceneComponent>(this, &Root, "Root");
CHelpers::CreateComponent<USkeletalMeshComponent>(this, &Mesh, "Mesh", Root);
CHelpers::CreateActorComponent<UTimelineComponent>(this, &Timeline, "Timeline");
//CurveFloat
CHelpers::GetAsset<UCurveFloat>(&AimCurve, "CurveFloat'/Game/Character/Weapons/Curve_Aim.Curve_Aim'");
}
void ACWeapon::BeginPlay()
{
Super::BeginPlay();
Owner = Cast<ACPlayer>(GetOwner());
if (HolsterSocketName.IsValid())
CHelpers::AttachTo(this, Owner->GetMesh(), HolsterSocketName);
BaseData.SetDataByNoneCurve(Owner);
if(!!AimCurve)
{
FOnTimelineFloat timeline;
timeline.BindUFunction(this, "OnAiming");
Timeline->AddInterpFloat(AimCurve, timeline); //이벤트 객체를 다룸.
Timeline->SetLooping(false);
Timeline->SetPlayRate(AimingSpeed);
}
}
void ACWeapon::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
bool ACWeapon::CanEquip()
{
bool b = false;
//3개 다 false일 때 b = false
//3개 중 하나라도 true면 b = true
b |= bEquipping;
b |= bReload;
b |= bFiring;
return !b; //b = false일 때 True를 리턴
}
void ACWeapon::Equip()
{
bEquipping = true;
if (!!EquipMontage)
Owner->PlayAnimMontage(EquipMontage, EquipMontage_PlayRate);
}
void ACWeapon::Begin_Equip()
{
if (RightHandSocketName.IsValid())
CHelpers::AttachTo(this, Owner->GetMesh(), RightHandSocketName);
}
void ACWeapon::End_Equip()
{
bEquipping = false;
}
bool ACWeapon::CanUnequip()
{
bool b = false;
b |= bEquipping;
b |= bReload;
b |= bFiring;
return !b;
}
void ACWeapon::Unequip()
{
if (HolsterSocketName.IsValid())
CHelpers::AttachTo(this, Owner->GetMesh(), HolsterSocketName);
}
bool ACWeapon::CanAim()
{
bool b = false;
b |= bEquipping;
b |= bReload;
b |= bInAim;
return !b;
}
void ACWeapon::Begin_Aim()
{
bInAim = true;
//우리는 생성자 ACWeapon()에서 AimCurve를 가져왔다.
if (!!AimCurve) //AimCurve가 있으면 true, 없으면 nullptr이기 때문에 false
{
Timeline->PlayFromStart();
AimData.SetData(Owner);
return;
}
AimData.SetDataByNoneCurve(Owner);
}
void ACWeapon::End_Aim()
{
CheckFalse(bInAim);
bInAim = false;
if (!!AimCurve)
{
Timeline->ReverseFromEnd();
BaseData.SetData(Owner);
return;
}
BaseData.SetDataByNoneCurve(Owner);
}
void ACWeapon::OnAiming(float Output)
{
UCameraComponent* camera = CHelpers::GetComponent<UCameraComponent>(Owner);
camera->FieldOfView = FMath::Lerp(AimData.FieldOfView, BaseData.FieldOfView, Output);
}
bool ACWeapon::CanFire()
{
bool b = false;
b |= bEquipping;
b |= bReload;
b |= bFiring; //발사하고 있는지
return !b;
}
void ACWeapon::Begin_Fire()
{
bFiring = true;
OnFiring();
}
void ACWeapon::End_Fire()
{
CheckFalse(bFiring);
bFiring = false;
}
void ACWeapon::OnFiring()
{
UCameraComponent* camera = CHelpers::GetComponent<UCameraComponent>(Owner);
FVector direction = camera->GetForwardVector();
FTransform transform = camera->GetComponentToWorld();
FVector start = transform.GetLocation() + direction; //눈알 안보다 앞쪽으로 설정하기 때문에 쏘는 방향쪽으로 조금 더해준다.
FVector end = transform.GetLocation() + direction * HitDistance;
DrawDebugLine(GetWorld(), start, end, FColor::Red, true, 5);
}
CWeapon_AR4
CWeapon_AR4.h
#pragma once
#include "CoreMinimal.h"
#include "Weapons/CWeapon.h"
#include "CWeapon_AR4.generated.h"
UCLASS()
class U2212_04_API ACWeapon_AR4 : public ACWeapon
{
GENERATED_BODY()
public:
ACWeapon_AR4();
};
변경사항 없음
CWeapon_AR4.cpp
#include "Weapons/CWeapon_AR4.h"
#include "Global.h"
#include "Components/SkeletalMeshComponent.h"
#include "Animation/AnimMontage.h"
ACWeapon_AR4::ACWeapon_AR4()
{
USkeletalMesh* mesh;
CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/FPS_Weapon_Bundle/Weapons/Meshes/AR4/SK_AR4.SK_AR4'");
Mesh->SetSkeletalMesh(mesh);
//Equip
{
HolsterSocketName = "Rifle_AR4_Holster";
CHelpers::GetAsset<UAnimMontage>(&EquipMontage, "AnimMontage'/Game/Character/Animations/Rifle_Equip_Montage.Rifle_Equip_Montage'");
EquipMontage_PlayRate = 2.0f;
RightHandSocketName = "Rifle_AR4_RightHand";
LeftHandLocation = FVector(-32.5f, 15.5f, 7.0f);
}
//Aim
{
BaseData.TargetArmLength = 200;
BaseData.SocketOffset = FVector(0, 50, 15);
BaseData.FieldOfView = 90;
AimData.TargetArmLength = 80;
AimData.SocketOffset = FVector(0, 55, 10);
AimData.FieldOfView = 65;
}
}
LeftHandLocation에 Default값을 넣어준다.
- LeftHandLocation = FVector(-32.5f, 15.5f, 7.0f);
참고) 에디터 개인설정 - 키보드 단축키 - 월드 플레이 일시정지
실행화면
Hand IK가 적용되었다.
ABP: Animation Blueprint
AN: Blueprint Class AnimNotify
ANS: Blueprint Class - AnimNotifyState
AO: Aim Offset
BP: Blueprint Class
BS: Blend Space
BF: Blueprint Function Library
CS: Matinee Camera Shake
E: Enum 열거형
DT: Data Table
F: 구조체
I: Blueprint Interface
WB: Widget Bluepprint
UE2212_04
Level - LEVEL | |||
Character | Animation | Rifle | |
Unarmed | |||
Rifle_Equip_Montage | |||
BlendSpaces | AO_Rifle_Aim AO_Rifle_Idle BS_Rifle BS_Rifle_Aim BS_Unarme |
||
Materials |
MaterialLayers |
||
M_UE4Man_Body M_UE4Man_ChestLogo |
|||
Mesh | SK_Mannequin SK_MAnnequin_PhysicsAsset Skel_Mannequin |
||
Montages |
|||
Textures | UE4_LOGO_CARD UE4_Mannequin__normals UE4_Mannequin_MAT_MASKA UE4Man_Logo_N |
||
Weapons | Elven Bow | ||
Greate Hammer | |||
Sword |
|||
Environment |
Materials | Textures | |
Meshes | |||
Obstacle | |||
FPS_Weapon_Bundle |
Weapons |
Material | |
Meshes | |||
Textures | |||
Materials | M_Mesh M_White_Inst M_Red_Inst M_Green_Inst M_Blue_Inst M_UE4Man_Body_Inst M_UE4Man_ChesLogo_Inst |
||
Meshes | Cone_2 Cube_2 Cylinder_2 Sphere_2 |
||
Player | ABP_Player BP_Player |
||
Textures | Crosshair | ||
Weapons |
BP_CWeapon_AR4 | ||
'⭐ Unreal Engine > UE FPS TPS' 카테고리의 다른 글
[UE] 총알 연사, HUD & CrossHair (0) | 2023.03.23 |
---|---|
[UE] 총알 발사 구현하기 (0) | 2023.03.22 |
[UE] TPS Weapon, AnimNotify, Aim (0) | 2023.03.17 |
[UE] TPS Weapon - Weapon Framework 짜기 (0) | 2023.03.16 |
[UE] Line Trace, Multi Trace, TPS 기본 세팅 (0) | 2023.03.15 |
댓글
이 글 공유하기
다른 글
-
[UE] 총알 연사, HUD & CrossHair
[UE] 총알 연사, HUD & CrossHair
2023.03.23 -
[UE] 총알 발사 구현하기
[UE] 총알 발사 구현하기
2023.03.22 -
[UE] TPS Weapon, AnimNotify, Aim
[UE] TPS Weapon, AnimNotify, Aim
2023.03.17 -
[UE] TPS Weapon - Weapon Framework 짜기
[UE] TPS Weapon - Weapon Framework 짜기
2023.03.16