ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 2D.MetalSlug 02. Player 이동 구현
    연습 프로젝트/2D.Metal Slug

    안녕하세요!!

    오랜만에 2D.Metal Slug 카테고리에 글을 올리게 됐습니다.

     

    지난번에는 Intro영상을 만들어 봤었는데요

     

    Intro 영상이 끝난 후 InGame에서 활약을 할 Player의 이동 구현을 만들어 보겠습니다.

     

    Player로 활약할 주인공 입니다!

     

    이번 글에선 현재 주인공인 Player의 이동 구현을 제작하는 과정을 담아 봤습니다.


    그럼 바로 시작하겠습니다!

     

    우선 Player의 이미지들을 받아 왔습니다.

     

    상체 / 하체를 나눠서 애니메이션을 나눠서

     

    동작을 서로 나눠서 그에 맞게 진행하게 하려고 했습니다.

     

     

    한 가지 예를 보자면

     

    플레이어가 앞으로 가는 Walk상태 여도

    하체는 Walk상태이지만

    상체는 대기 / 발사 앞/위/아래 등 다양한 동작을 취하게 됩니다.

     

    하체가 점프 및 대기 상태여도 하체와는 상관없이 상체가 따로 동작 구현되니

     

    다양한 연출이 가능해집니다.

     

     

    처음으로 한 점은

     

    플레이어의 상체 하체를 구분해

     

    각 동작에 맞게 애니메이션을 제작한 것입니다.

     

    쉽게 애니메이션 구현을 보자면

    Ctrl : 플레이어 상체 애니메이션 컨트롤러

    Ctrl_foot : 플레이어 하체 애니메이션 컨트롤러

     

    Idle : 플레이어 상체 대기 애니메이션

    Idle_foot : 플레이어 하체 대기 애니메이션

     

    Attack_D : 플레이어 상체 아래 공격 모션

    Attack_F : 플레이어 상체 앞 공격 모션

    Attack_U : 플레이어 상체 위 공격 모션

    Attack_Knife : 플레이어 상체 칼 공격 모션

     

    Jump_foot : 플레이어 하체 점프 모션 

    Move_foot : 플레이어 하체 이동 모션

     

    으로 각 상체 / 하체를 나누어 애니메이션을 만들었습니다.


    그럼 우선 만드는 과정을 보러 가 보시죠!

     

    이미지를 유니티로 가져와 주고

     

    맨 처음 변경한 부분이 Sprite Mode입니다.

     

    Sprite Mode에는 3가지 타입이 존재합니다.

     

    우선 처음 이미지 파일을 보면 Sprite Mode가 single로 되어있는데.

     

    Single Mode는 하나의 스프라이트로 이루어져 있기에 현재 슬라이스가 안되어 있습니다.

     

    이 Sprite Mode를 Multiple Mode로 바꿔주어 여러 개의 스프라이트를 가지게 해 줬습니다.

     

    그리고 마지막으로 Polygon 타입이 있는데 4 각형 원 삼각형 오각형 등 다양한 모양을 스프라이트로 지원합니다.


    Sprite Mode를 Multiple Mode로 바꿔주고 Apply를 눌러 저장을 해 준 뒤

     

    Sprite editor로 들어가 Slice를 해 줍니다.

     

    여러 개의 Sprite 가 생긴 걸 볼 수 있습니다.

     

    이렇게 나눠진 Sprite 들을 각 필요 동작에 맞춰 애니메이션을 제작했습니다.

     

    상체 대기 Sprite

     

    여기서 좀 애먹었던 부분은

     

    이렇게 sprite를 클릭해 보시면.

     

    중심축이 sprite의 가운데로 잡혀 있는 걸 볼 수 있는데요.

     

    상체 대기 Sprite들을 하체 대기 Sprite들과 씬 뷰에서 맞춰 놓아도

     

    상체 공격 Sprite들로 전환하여 하체 대기 Sprite들과 맞춰 보면

     

    중심축이 다 재각각이기에, 일일이 맞춰서 중심축을 변경해 줘야 한다는 번거로움이 있었습니다.

     

    어려운 건 아니지만,, 하다 보니 조금이라도 안 맞는 게 너무 신경 쓰여서..

    또한 좌표값이 따로 정해진 것이기 아니기에, 최대한 면밀히 맞춰보려 노력했습니다...

     

    그래서!! 현재 필요한 동작 구현을 할 Sprite 들은 맨 왼쪽 허리춤으로 중심축을 맞춰 놓았습니다.

     

     

    00 : 05초

    Animator안에 있는 ENTRY(처음 시작 시 진행될 애니메이션)에서 

    상체 Idle / 하체 Idle로 지정해 준 영상입니다.


    주인공인 Player에게 생명을 입혀 줬으니 ㅎㅎ

     

    이제 이동 구현을 하며 그에 맞는 동작 구현을 하는 코드로 들어가 보겠습니다.

     

    새로 생성한 PlayerController에 선언 변수들입니다.

     

    우선 플레이어의 상태 값을 받기 위해 enum으로 열거형 상수를 나열했습니다.

     

    Idle(대기) / Move(이동) / Jump(점프) 상태입니다.

     

    또한 이 상태 값을 제어하기 위해 State state로 변수를 선언했습니다.

     

    rb2D : 플레이어에게 붙은 리지드 바디 2D입니다. (3D와 헷갈리시면 안 됩니다)

     

    aniUp / aniDown : 각 상체 / 하체 애니메이터를 받아올 변수입니다.

     

    moveSpeed : 플레이어가 이동 시 스피드입니다.

    JumpPower : 플레이어가 점프 시 적용될 파워입니다.

     

    둘 다 public으로 선언하여 인스펙터 창에서 조절이 가능하게 하였습니다. (테스트 용이함)

     

    isJump : 플레이어가 점프를 했는지 체크할 불 변수입니다.

    (점프는 1번만 구현할 예정입니다. 즉, 2단 점프 이상을 막기 위한 변수입니다.)

     


    그럼 가장 먼저 Start 함수를 보겠습니다.

     

    (Start 함수는 Awake와 같이 실행 시 딱 1회만 실행되는 함수였죠?)

    Rigidbody2D컴포넌트를 rb2D에 GetComponent로 가져와 주고

     

    GetChild를 이용해 현재 transform의 자식의 0번과 1번을 가져와 주었습니다.

    이 스크립트는 Player 빈 오브젝트에 추가될 것이며

    그 자식으로 Player(상체) / Player_Foot(하체)가 있습니다.

     

    각 상체 / 하체를 GetChild(0) / GetChild(1)로 할당받았습니다.

     

    그리고 초기 시작하면 상태 값은 Idle로 시작합니다.

    state = State.Idle

     


    우선 Update 함수에서 움직임 값과 점프를 실행하는 코드를 보겠습니다.

     

    좌 우 이동의 반환 값은 Horizontal과 Vertical로 받았습니다.

    각 유니티에 저장된 Input Manager의 Axes 들이고

     

    Horizontal : a, d / ←, →

    Vertical : w, s / ↑, ↓

    GetAxis를 입력 시 -1.0f ~ 0 ~ 1.0f의 값을 반환해 줍니다.

     

    하지만 GetAxisRaw를 이용해서 -1, 0, 1 세 가지 값 중 하나가 반환되도록 했습니다.

    (즉, 즉시 반응이 일어나게 했습니다.)

     

    이동은 Rigidbody2D에 있는 velocity 값으로 조절하였습니다.

     

    velocity는 해당 오브젝트의 RigidBody 속력 벡터를 나타내는데요

     

    RigidBody에

     

    h * moveSpeed, rb2D.velocity.y로

     

    x 축은 방향 * 속도, y축은 rgidbody2D의 현재 값 그대로를 받았습니다.

     


    점프를 실행하는 코드를 보겠습니다.

     

    if (! isJump)로 / isJump == false 일시 실행되도록 하였고

     

    if (Input.GetButtonDown("Jump")) 

    이 Jump키를 입력해 점프 키를 space로 받게 하였습니다.

     

    또한 GetButtonDown으로 누를 때 1번 작동하도록 하였습니다.

     

    GetButtonUp : 떼어졌을 시 / GetButton : 누르는 동안 계속 이 있습니다.

     

     

     

    이 점프 키를 눌렀을 시 판단은, 간단히 Trigger이벤트 함수에서 실행하였습니다.

     

    player에게 입힌 collider2D가 tag가 "Ground"인 오브젝트와 닿고 있는 상태라면 (Stay)

     

    isJump는 false로 바꿔주어 점프 키의 입력이 가능하게 만들어 줍니다 (if (! isJump))

     

    OnTriggerStay2 D 머무를 시 

    OnTriggerExit2 D 떼어졌을 시

    OnTriggerEnter2 D 닿을 때

     

    가 있으니 기본적으로 아시겠지만 한번 더 알고 가면 좋을 것 같습니다.

     


    Player의 상태 값에 따른 하체 애니메이션 적용 부분부터 보겠습니다.

     

    첫 번째로 Idle 상태 라면 실행될 case입니다.

    h!= 0 

    즉 x축의 반환 값이 0이 아니라면 이동 중인 것으로 간주하고

    상태를 State.Move로 바꿔줍니다.

     

    두 번째로 Move 상태 라면 실행될 case입니다.

    이 if문은 하나씩 조건을 추가하면서 맞췄는데요

     

    코드를 보자면

    if (h!= 0 && isJump == false && rb2D.velocity.y <= 0.01f)

    1. h!= 0 : 이동 중인 것으로 간주 

    2. isJump == false : 점프 상태가 아닌 것으로 간주

    3. rb2D.velocity.y <= 0.01f : 

    player의 velocity y축이 0.01f보다 낮은 것으로 간주입니다.

     

    aniDown.StopPlayback()으로 실행 중인 애니메이션이 있다면 스톱을 해주고

    aniDown.Play("Player_Move_foot")

    Play함수를 이용해 "Player_Move_foot"를 실행시켜 줍니다.

     


    반대로 else if (h == 0 && isJump == false)

    h의 반환 값이 0이라면 / isJump의 상태가 false라면 의 조건이 붙습니다.

     

    isJump를 추가시킨 이유는 상태가 true라면 점프를 하고 있다고 간주하기 때문입니다.

    OnTriggerStay2D가 실행되기 전까지.

     

    그렇다면 이동이 없다는 상태로 간주하니깐

    aniDown.SetTrigger("MoveToIdle")로 Move에서 Idle로 돌아가는 트랜지션의 조건값을 맞춰주고

     

    state값은 Idle로 바꿔줍니다.

     

    player의 하체 애니메이터입니다.

     


    그렇다면 이제 상체를 가보겠습니다.

     

    흠 이 상체는 코드를 몇 번 수정을 했습니다.

     

    Animator의 파라미터 값을 float로 바꿀지 trigger로 바꿀지 

     

    섞어서 써도 될지 기타 등등..

     

    하체를 담당하는 Switch문 과도 맞춰보면서 했기에 약간 시간이 걸렸습니다 (야 악간..)

     

    Animator의 파라미터 값을 고민했던 가장 큰 이유가

     

    float를 쓰게 되면 스크립트가 길어지지만

     

    Animator의 창은 보다 간결해지고 (트랜지션들이 많이 없기에)

     

    trigger을 쓰게 되면 스크립트는 보다 간결해지지만 Animator 창이 다소 복잡해지는 경향이 있었습니다.

     

    추 후 수정과 추가되는 부분까지 생각하면..

     

    개인적으로 Animator창을 간결히 해서 float값으로 조절을 해놓으면

     

    다시 봤을 시 기억하기가 매우 힘들 것 같았습니다.

     

     

    그렇기에 파라미터의 값을 trigger로 통일시켰고 스크립트는 보다 간결해지며

     

    Animator는 다소 복잡하지만 직관적인 형태를 이룰 수 있었다고는 현재 생각하지만

     

    변동 사항이나 추가 사항 등 이 나오면 수정 후 알려드리겠습니다 ㅎㅎ...

     


    우선 코드부터 보시죠!

     

    보기 편하지 않나요??

    GetKey를 이용해 (누르는 동안 true)를 발생하고 KeyCode는 K 를받고 / v가 0이라면

    기본 발사모 션("IdleToFire")을 합니다.

     

    v가 0보다 크다면 위를 보는 발사 모션인 "FireUp"을 실행하고

     

    v가 0보다 작다면 아래를 보는 발사모션인 "FireDown"을 실행합니다.

     

     

     v는 (Vertical : w, s / ↑, ↓)을 Input.GetAxisRaw로 받아왔었습니다.

     

    GetKey를 사용해 KeyCode가 j 이면 

    SetTrigger를 이용해 칼 모션인 IdleToKnife를 실행합니다.

     

    // 한 가지 추가사항으로 //

    칼 모션에서 Idle로 돌아올 시나 다른 조건으로 변환 시

    칼 모션을 하던 도중 바뀌게 되어서 

    어색함이 연출되었었습니다.

    이를 HasExitTime을 체크해 두어

    애니메이션이 끝난 뒤 전환되게 조건을 걸어줬습니다.

    // Knife 모션 //

     

    마지막으로 모든 조건이 아닌 else문에서는

    aniUp.SetTrigger("Idle");

     

    SetTrigger를 이용해 어떠한 애니메이션이 동작 중 이어도

    Idle로 향하게끔 트랜지션을 연결해 주었습니다.

     

    Player의 상체 애니메이터입니다.

     

    이게 처음 보면 복잡해 보이실 수 있는데 생각보다 깔끔하고 직관적인 코드인데..

    아직 뭐가 좋은지 모르니 말을 아끼겠습니다... T.T

    마지막으로 현재 제작된 동작 구현 부분을 영상으로 보시겠습니다.

     

    00 : 30초

    발사 시 생성되는 Bullet Sprite와 적들 그리고 맵 등등

     

    해야 할 부분이 많이 남은 것 같습니다.

     

     

    Player를 제작하면서 도중 애니메이션이 실행 안되거나 잘되다가 안 되는 오류들이 많았는데

     

    집중에서 하나씩 테스트해보고 또 해 보고 반복을 해 보니

     

    어색하지 않은 원하는 동작 연출이 결국 완성돼서 기쁩니다ㅎㅎ

     

    금방 돌아오겠습니다!

     

    감사합니다!

    '연습 프로젝트 > 2D.Metal Slug' 카테고리의 다른 글

    2D.MetalSlug 06. Manager  (0) 2022.05.07
    2D.MetalSlug 05. Map 02 / 02  (0) 2022.05.01
    2D.MetalSlug 04. Map 01 / 02  (0) 2022.04.27
    2D.MetalSlug 03. Bullet Fire(Animation Event)  (0) 2022.04.25
    2D.MetalSlug 01. Intro 영상  (0) 2022.04.11

    댓글

김효겸 / Tel. 010-7735-0580 / E-mail. dollzzang2@hanmail.net