일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- ability task
- animation
- CTF
- gas
- unity
- 보안
- UI
- gameplay ability system
- Multiplay
- Aegis
- 게임 개발
- attribute
- map design
- photon fusion2
- nanite
- MAC
- 언리얼 엔진
- listen server
- Unreal Engine
- os
- local prediction
- gameplay tag
- 게임개발
- 언리얼엔진
- stride
- widget
- gameplay effect
- rpc
- Replication
- 유니티
- Today
- Total
Replicated
[Unity6 Tutorial] #2 애니메이션 & 액션 - 달리기, 점프, Grounded 체크 본문
[Unity6 Tutorial] #2 애니메이션 & 액션 - 달리기, 점프, Grounded 체크
라구넹 2025. 4. 25. 13:16달리기 애니메이션
일단 달리기부터 만들어보자
액션은 저번 시간에 만들었다! a랑 d를 누르면 움직인다
그런데 이제 애니메이션을 만들어보자!
일단 테스트용으로 깔아놨던 Terrain의 x 스케일을 30정도로 해서 움직여도 될만한 바닥을 만들자
그리고 Player에 Add Component해서 Animator 추가
Animator는 자신보다 하위 계층에 있는 게임 오브젝트가 컨트롤 가능하니, 최대한 상위인 Player 오브젝트에다 컴포넌트를 붙여주자
그리고 윈도우 → 애니메이터, 애니메이션 둘 다 열어주자
Assets에 애니메이션 폴더를 만들어주고
폴더 들어가서 우클릭 → 크리에이트 → 애니메이션 → 애니메이터 컨트롤러
이름 : PlayerAnimController
만든 컨트롤러를 Player에다 붙였던 Animator의 Controller에 마우스로 잡아 끌고 가서 붙인다
혹은 옆에 동그라미 누르면 컨트롤러 검색 가능
이제 Animation으로 가면 애니메이션 만들기 가능
Create 선택
이제 프레임 별로 애니메이션을 설정 가능한 창이 나온다
이 경로 따라가면
이렇게 있는데 이걸 쓸 거다
Add Property 누르면 Animator보다 하위에 있는 오브젝트들을 건드릴 수 있는데
그 중에서 PlayerSprite 선택
그럼 PlayerSprite 밑에 있는 컴포넌트인 SpriteRender에 접근 가능
반드시 PlayerSprite에서 작업해야 함
(Player에서 작업 시 스프라이트 여러 개 됨)
맨 밑에 Sprite 선택
애셋 폴더에서 스프라이트 잡아다 끌면 붙는다
순서대로 쭉 이어 붙인다
맨 뒤에 있던 건 삭제 (맨 끝이 애니메이션의 끝이 되는데 필요 없어서 삭제)
씬 우클릭해서 하나 추가하고
같이 볼 수 있게 적당한 곳에 창을 위치시키고 애니메이션 플레이 버튼 누르면 애니메이션 어떻게 실행되는지 알 수 있음
이대로 실행해보면??
아마 가만히 있는데도 뛰고 있을 거다
왜냐하면 가만히 있는 애니메이션을 안만들었기 때문
Idle 애니메이션
하나 새로 만들어보자
Idle도 다 있다
똑같이 해주자
그리고 애니메이터 들어가서
Idle 애니메이션 우클릭해서 디폴트 선택
근데 실행해보면 너무 빠르다
Idle 애니메이션에서 마우스 잡아 끌어서 전체 선택하고
30프레임 정도까지 잡아 끌자
이제 좀 자연스러울 거다
코드랑 애니메이션 연결하기
일단 애니메이터는 이 상태일 거다
우클릭하면 MakeTransition이 생길 텐데 해주자
양쪽으로
옆에 Layer랑 Parameter도 있는데 파라미터 가서
Bool 고르고
IsRun 파라미터 만들기
트랜지션 누르면 자세한 설정 가능한데
Condition이 일치 시 트랜잭션이 넘어감
각각 설정해주자
이제 문제는 IsRun을 어디서 설정해 주느냐 이다
이제 코드를 짜야 한다
저번 시간에 만들었던 PlayerController에 들어가자
public class PlayerController : MonoBehaviour
{
private Rigidbody2D rb;
private PlayerControlInput inputActions;
private Animator animator;
private Vector2 inputVector;
private float moveSpeed = 5.0f;
void Awake()
{
rb = GetComponentInChildren<Rigidbody2D>();
animator = GetComponent<Animator>();
inputActions = new PlayerControlInput();
inputActions.Player.Move.performed += ctx => inputVector = ctx.ReadValue<Vector2>();
inputActions.Player.Move.canceled += ctx => inputVector = Vector2.zero;
}
private void Update()
{
// sqrMagnitude은 벡터의 길이 제곱값. 연산이 magnitude보다 가벼움
animator.SetBool("IsRun", inputVector.sqrMagnitude > 0.01f);
}
애니메이터를 Awake에서 가져오고, Update에서 애니메이션 설정을 해준다
이대로 실행하면??
아마 애니메이션 전환이 제대로 안될 거다
이유는..
트랜지션에서 Settings를 열어보면 Interruption Source라는게 있다
저게 None이면 현재 애니메이션 실행 중에는 다른 애니메이션으로 넘어가지지 않는다
Current State로 바꿔주자
그리고 HasExitTime도 꺼줘야 한다
이것도 체크되어 있으면 저게 끝나기 전까지 다른 걸로 못넘어간다
이제 좌우 방향 전환만 되면 된다
void Awake()
{
rb = GetComponentInChildren<Rigidbody2D>();
animator = GetComponent<Animator>();
inputActions = new PlayerControlInput();
inputActions.Player.Move.performed += ctx =>
{
inputVector = ctx.ReadValue<Vector2>();
if (inputVector.x > 0)
{
// transform.localScale.x = Mathf.Abs(transform.localScale.x); 이건 안됨
transform.localScale = new Vector3(Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
}
else
{
transform.localScale = new Vector3(Mathf.Abs(transform.localScale.x) * -1.0f, transform.localScale.y, transform.localScale.z);
}
};
inputActions.Player.Move.canceled += ctx => inputVector = Vector2.zero;
}
람다로 키 입력 발생했던 거에 같이 방향 전환을 넣어주면 된다
transform(소문자 시작)은 해당 컴포넌트가 붙어있는 게임 오브젝트의 Transform(위치, 회전, 스케일..)을 가져오는데, 거기서 localScale을 -1을 곱하면 방향이 반전됨
⇒ 이동 인풋 발생 시 스케일을 이벤트 발생으로 조정하기!
이제 방향 전환까지 된다
점프 액션 + Grounded 체크, 애니메이션
일단 액션부터 만들자
저번에 만들었던 인풋 열어서
Add Binding → 스페이스에 연결
Add Binding하면 그냥 함수랑 연결 가능
Save하고 닫으면 알아서 반영됨
private float jumpPower = 150.0f;
void Awake()
{
...
inputActions.Player.Move.canceled += ctx => inputVector = Vector2.zero;
inputActions.Player.Jump.performed += ctx => rb.AddForceY(jumpPower);
}
이렇게 해주면 이제 스페이스바를 누르면 점프가 된다!
그런데, 스페이스바를 여러번 눌러도 계속 점프가 된다
이러면 이제 맞닿은 땅이 있는지 체크해야 한다
1. 어디서 체크해야 하는가
groundCheck 선언하고
** [SerializeField] 선언 시 praivate 이어도 에디터에 보임
안에 빈 오브젝트 만들고
위치 적당히 설정한 다음
** 좌측 상단 디버깅용 아이콘 설정 가능
PlayerController에다 설정하기
저 위치를 기준으로 Grounded 체크를 할 것이다
2. 무엇을 체크해야 하는가
레이어 마스크라는 걸 추가
저걸로 부딫힐 오브젝트를 선택할 수 있음
아무대서나 Layer누르고 아래 AddLayer 선택
레이어에 그라운드 추가
만들었던 바닥의 Layer를 Ground로 설정
PlayerController에서 GroundLayer로 Ground 레이어 마스크 설정
적용
체크 범위도 만들어준 다음에
private void Update()
{
// sqrMagnitude은 벡터의 길이 제곱값. 연산이 magnitude보다 가벼움
animator.SetBool("IsRun", inputVector.sqrMagnitude > 0.01f);
isGrounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, groundLayer);
}
Update에서 Physics2D.OverlapCircle을 이용해
groundCheck.position을 기준으로, groundCheckRadius 범위 내에 groundLayer인 오브젝트가 있는지 찾아낸다
void Awake()
{
rb = GetComponentInChildren<Rigidbody2D>();
animator = GetComponent<Animator>();
inputActions = new PlayerControlInput();
inputActions.Player.Move.performed += ctx =>
{
inputVector = ctx.ReadValue<Vector2>();
if (inputVector.x > 0)
{
// transform.localScale.x = Mathf.Abs(transform.localScale.x); 이건 안됨
transform.localScale = new Vector3(Mathf.Abs(transform.localScale.x), transform.localScale.y, transform.localScale.z);
}
else
{
transform.localScale = new Vector3(Mathf.Abs(transform.localScale.x) * -1.0f, transform.localScale.y, transform.localScale.z);
}
};
inputActions.Player.Move.canceled += ctx => inputVector = Vector2.zero;
inputActions.Player.Jump.performed += ctx =>
{
if (!isGrounded) return;
rb.AddForceY(jumpPower);
};
}
그리고 점프 부분에서 isGrounded 체크를 해주면 된다
이제 점프가 한 번만 가능하다!
애니메이션 연결
한가지 주의할 사항이 있는데, 애니메이션 만들 때 Animation 폴더 선택하고 Create해야 한다.
왜냐하면 현재 선택된 폴더 기준으로 파일을 생성창이 열려서 이상한 곳에 파일을 만들 수도 있다
만들어주고
Loop Time 꺼준다
저거 켜져 있으면 애니메이션 계속 반복한다
Idle이나 Jump는 반복하는게 맞는데 점프같은 단발성 액션은 꺼 주자
점프는 10프레임 정도만 하나의 이미지로 해주자
(애초에 애셋에 이미지가 하나)
** 알트를 누르면 Animator 창에서 이동이 가능
일단 트랜지션은 이렇게 만들어주자
Any State는 말 그대로 어떤 스테이트도 가능하다는 뜻
이번엔 트리거로 점프를 만들어주고
AnyState → Jump는 HasExitTime 없이, 인터럽트 소스는 Current, 조건은 Jump이다
Can Transition To Self는 Any State→ 에서만 있는 설정인데, 스스로에서 스스로로 전이가 가능하냐는 설정이다. 해제해주자.
Jump → Exit는 알아서 잘 되있기에 안해도 된다(HasExitTime 등)
Exit 이후는 알아서 Entry로 돌아간다
void Awake()
{
...
inputActions.Player.Jump.performed += ctx =>
{
if (!isGrounded) return;
animator.SetTrigger("Jump");
rb.AddForceY(jumpPower);
};
}
그리고 트리거 Jump 설정만 해주면 이제 점프 애니메이션까지 잘 연동된다
2주차 부록 - 애니메이션
트랜지션 타임라인
이렇게 생긴 건 좌표나 회전 같은 값이 있을 때,
알아서 보간 처리해주는 구간 설정이라고 보면 된다.
근데 우리는 스프라이트를 바꿔 끼는 방식으로 애니메이션을 처리했기에 별 의미는 없다
스켈레톤 애니메이션?
각각의 파츠를 분리(팔 관절마다, 다리 관절마다, 몸통 등등등..)하고,
하나하나 포지션이랑 로테이션을 조절해서 끊기지 않는 애니메이션을 만들 수 있음
그 밖에도 Animator로 굉장히 많은 값들을 변경 가능함
'유니티 엔진 > Unity6 Tutorial' 카테고리의 다른 글
[Unity6 Tutorial] #3 적 만들기1: 트리거 처리와 Scriptable Object, Coroutine을 통한 Spawner 만들기 (0) | 2025.04.25 |
---|---|
[Unity6 Tutorial] #1 프로젝트 생성, 애셋 스토어, 유니티 물리, 유니티 생명 주기, 뉴 인풋 (0) | 2025.03.29 |