유니티3D - 2
이번 목표
라이플을 들었을 때 다른 애님트리로 넘어가 작동하기 , 라이플 총알 구현
애니메이션
Idle 상태에서 Rifle로 넘어가는 조건을 Enum으로 하려했는데 유니티에는 enum 타입이 없으니 Int파라미터로 만들어 하거나 StringToHash 둘 중 하나를 선택해야 할 거 같다.
간단하게 Int 타입으로 변환하여 진행할 생각이다.
일단 State 스크립트를 만든 후 똑같이 Player Input에서 PlayerController에 입력을 전하여 State의 함수를 실행 시키는 구조다.
하지만 여기서 AnimController 스크립트에서 계속 State 값을 받는 것 보단 언리얼에서 사용했던 것 처럼 델리게이트구조로 하는게 좋다고 생각이 들었다.
그래서 찾아보고 event함수를 이용하여 값이 바뀔때마다 함수를 호출하는 구조로했다.
- 이벤트 함수 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;
using UnityEngine;
public enum ECharacterState
{
Idle = 0,
Rifle = 1,
}
public class State : MonoBehaviour
{
ECharacterState characterState;
public event Action<ECharacterState> characterStateChange;
public ECharacterState GetState() { return characterState; }
public void SetCharacterState(int InState)
{
if (characterState == (ECharacterState)InState)
{
characterState = ECharacterState.Idle;
}
else
{
characterState = (ECharacterState)InState;
}
characterStateChange.Invoke(characterState);
}
}
- 애님컨트롤러에서 바인딩한 함수 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void OnEnable()
{
state.characterStateChange += SetAnimState;
}
void OnDisable()
{
state.characterStateChange -= SetAnimState;
}
void SetAnimState(ECharacterState state)
{
animator.SetInteger("State", (int)state);
}
여기서 OnEnable/OnDisable 한 쌍으로 구독/해제를 관리해 메모리 누수를 방지한다. 바인딩 연산자는 += / -= 를 사용하며, 동일한 위치에서 한 쌍으로 유지하는 습관이 중요하다.
라이플 변경
Gun클래스 생성
라이플 클래스를 구현하기전에 무기를 추가하는 구조도 생각하여 짜야한다. 일단 GunWeapon 스크립트를 만들어 기본적인 총 데이터들을 만들고 이 클래스를 abstract 사용하여 추상 클래스로 만들고 Init 설정을 추상메서드를 이용해 무조건 알맞은 데이터가 들어가게끔 설계했다. 그리고 총을 쏘는 Fire()함수는 가상함수로 이용하여 기본적인 발사부분을 구현하고 더 기능이필요하면 오버라이딩하게 끔 설계를 했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using UnityEngine;
[System.Serializable]
public struct GunData
{
public float delayTime;
public int maxBullet;
public bool auto;
public GameObject bullet;
public Transform muzzlePoint;
}
public abstract class GunWeapon : MonoBehaviour
{
public GunData data;
public abstract void InitSetting();
public virtual void fire()
{
Instantiate(data.bullet, data.muzzlePoint);
}
}
웨폰컴포넌트를 만들어 현재 무기와 보조무기, 근접무기 등을 관리한다. 일단은 주 무기를 생성해 오른쪽 팔에 부착해보았다. 여기서 언리얼과 다른점은 스켈레톤트리라는 창이 일단 없는거같다. (내가 못찾은거 일수도 있지만) 소켓을 내가 직접 Player에 캐릭터스켈레톤 프리펩에 있는 트랜스폼으로만 되어있는 트리구조에서 직접 빈오브젝트를 만들어 소켓을 구현하였다. 그 후 사용할 총 프리펩을 자식으로 넣어 위치와 회전을 설정해 주었다.
여기서 느낀점은 언리얼에 비해 이런점이 확실히 불편했던거 같고 언리얼이 필요한기능들을 정말 편리하게 구현이 되어있는거같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using UnityEngine;
using UnityEngine.UIElements;
public class WeaponComponent : MonoBehaviour
{
public GunWeapon CurrentWeapon;
public GunWeapon MainGunPrefab;
public GunWeapon pistolPrefab;
public GameObject KnifePrefab;
public Transform Hand_R;
void Start()
{
CurrentWeapon = Instantiate(MainGunPrefab, Hand_R);
}
void Update()
{
}
public void L_Attack()
{
CurrentWeapon.fire();
}
}
총알 구현은 일단 간단한 코드로 진행을 하였고 머티리얼을 빛나는 노란색으로 하고싶었는데
언리얼 처럼 이미시브 컬러에서 알파값을 높이면 발광효과같이 나오는 것을 똑같이 구현하려했는데
유니티의 머티리얼을 잘 몰라서 찾아보고 진행해봤는데 shader에서 standard로 바꾸면 핑크색으로 바뀌어 컬러가 바뀌지 않는다. 총이나 다른 에셋들이 핑크색으로 보였던 이유도 동일했다. 렌더 파이프라인/Shader 설정을 정리하기 전까지는, 임시로 표준 Shader가 정상인 환경에서 머티리얼을 재설정하거나 프로젝트 SRP 설정을 우선 통일해 시간을 절약하고, 발사 로직은 우선 프로젝타일 스크립트로 진행했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using UnityEngine;
public class Projectile : MonoBehaviour
{
public float speed = 20f;
public float lifetime = 3f;
private float timer = 0f;
void Start()
{
}
void Update()
{
transform.position += transform.forward * speed * Time.deltaTime;
timer += Time.deltaTime;
if (timer >= lifetime)
{
Destroy(gameObject);
}
}
}
총을 들고 계속 달려보다가 캐릭터의 메시와 현재 콜리전이 일치하지 않는것을 확인했다. 메시 프리펩을 확인해보니 회전값과 포지션값이 바뀌어 있었다.
상식적으로 저 두값은 0,0,0 에서 바뀌면안된다.
그래서 원인이 무엇인지 계속 찾아보았다 원인은 아마 애니메이션이라고 추측을하고 애니메이션에 루트 트랜스폼 회전, 포지션의 자세로 베이크 변수가 있어 True로 활성화하여 해보니 잘되었다.
Bake Into Pose 변수 기능 ✅ Bake Into Pose | 애니메이션의 루트 트랜스폼의 이동/회전 값을 “메쉬 자체”의 포즈에만 반영하고, 실제 Transform에는 적용하지 않음 ❌ 비활성화 | 루트 트랜스폼의 이동/회전이 GameObject 자체에 적용됨 (즉, 캐릭터가 움직이거나 회전함) 이것과 근데 루트모션이 밀접하게 연관돼 있다. 내가 발생한 문제를 해결할 때 사실 루트모션만 꺼도 일어나지않을 문제이다. 루트모션이란 애니메이션에의해 게임오브젝트에 회전과 포지션이 변경된다는 점이다.
여기서 루트모션을 끄고 이용하면 만약 내가 대쉬공격하는 애니메이션을 가져와도 그 애니메이션의 이동이나 점프등이 반영되지 않아 좋지않다.
루트모션을 켜고 애니메이션마다 자세로 베이크(Bake Into Pose)를 적절히 설정하는 방식이 현실적이었다. 코드로 이동을 제어하는 접근도 가능하지만, 애니메이션 데이터와 이동 제어를 혼용하면 품질 튜닝 난도가 올라간다.
총쏘기
입력과 총알 생성은 정상이나, 총알 방향은 히트스캔 기준점과 시각 메쉬 축의 차이로 인해 어긋날 수 있다. 히트스캔을 기준으로 시각용 총알은 보조 역할로 유지한다. 루트모션에 대해서 언리얼에서 나는 모든 애니메이션에 적용을했다. 그래도 포지션과 회전에 관련해서 문제가 생기지않아 몰랐는데 생각보다 까다로운 녀석인거 같기도하다.
아니면 언리얼이 정말 간편하게 잘 최적화되 있는 것인가?? 아니면 내 기억이 이상하게 된것인가 이 프로젝트를 마친 후 언리얼도 하나 할 거인데 그 때 다시 비교해봐야겠다.
TODO
프로젝타일 방향 정합(히트스캔 기준 회전) , 캐릭터 감속 튜닝