Unity 2D - 3. 플레이어 이동 구현하기

2023. 7. 21. 22:43C# & Unity 공부

오늘은 플레이어가 이동하는 것을 구현해 볼 것이다.

 

물리력에 의한 이동

우선 배우기 앞서, 플레이어가 이동해야 하므로,

밑의 사진처럼 움직일 수 있는 땅을 만들어주고, 카메라도 뒤로 땡겨주자.

그리고 스크립트를 관리할 파일 또한 만들어주자.

그리고 PlayerMove라는 스크립트 파일을 만들어서 다음과 같이 코드를 짜준다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMove : MonoBehaviour
{
    Rigidbody2D rigid;

    void Awake()        //플레이어 오브젝트가 만들어졌을 때
    {
        rigid = GetComponent<Rigidbody2D>();
    }

    void FixedUpdate()      //플레이어가 움직이는 물리연산이므로
    {
        float h = Input.GetAxisRaw("Horizontal");

        rigid.AddForce(Vector2.right*h,ForceMode2D.Impulse);
    }
}

그리고 실행시키면 문제점을 발견할 수 있다.

FixedUpdate는 1초에 50번 정도 사이클을 가지고

AddForce로 주었기 때문에 힘을 1초에 50번을 주었다고 생각하면 된다.

한 마디로 엄청 빠르다는 것이다.

 

그래서 가속도를 무한으로 받지 않기 위해 최대 속력을 정해줄 것이다.

주석을 보면서 코드를 다음과 같이 짜보자.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerMove : MonoBehaviour
{
    public float maxSpeed;      //최대속도 변수(public으로 설정하여 Inspector 창에 뜸
    Rigidbody2D rigid;

    void Awake()        //플레이어 오브젝트가 만들어졌을 때
    {
        rigid = GetComponent<Rigidbody2D>();
    }

    void FixedUpdate()      //플레이어가 움직이는 물리연산이므로
    {
        //Move by Control
        float h = Input.GetAxisRaw("Horizontal");
        rigid.AddForce(Vector2.right*h,ForceMode2D.Impulse);

        //Right Max Speed
        //현재 물체의 속도가 최대 속도를 넘었다면
        //속도를 최대속도로 한정
        //y축 속도를 0으로 해버리면 공중에 멈춤으로 0으로 설정 X
        if (rigid.velocity.x > maxSpeed)
            rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y);

        //Left Max Speed
        else if (rigid.velocity.x < maxSpeed*(-1))
            rigid.velocity = new Vector2(maxSpeed * (-1), rigid.velocity.y);
    }
}

실행시켜보면 Max Speed가 생겨서 직접 관리할 수 있으면서,

최대 속도가 제한된 것을 확인할 수 있다.

 

다음으로는 다음 시간에 오르막길을 만들어 볼 것인데,

그에 앞서서 지형이 마찰력을 한 번 뺴볼 것이다.

지형의 Material에 기본적으로 주어지는 마찰력이 있다.

이 마찰력을 0로 만들어서 빙판처럼 만들어 볼 것이다.

그래야 플레이어가 오르막길을 잘 올라갈 수 있다.

 

재질을 만들어 볼 것이다.

Project 우클릭 > Create > 2D > Physics Material 2D

그리고 Physical2D 파일의 이름을 PlayerMove라고 해주자.

PlayerMove의 Inspector 창에 나와 있는 Friction (마찰력)을 0으로 하고,

모든 땅 오브젝트에다가 집어넣자.

 

 

그리고 실행시켜보면 마치 빙판길처럼 조그만 힘을 가해도

미끄러지는 것을 확인할 수 있다.

 

저항 설정

그러면 땅의 마찰력을 잡아줬으니, 우리는 다른 힘으로 이 문제를 해결해야한다.

공기 저항을 이용할 것이다.

 

플레이어를 선택하고 Inspector 창을 보면 두 가지의 Drag가 있다.

그 중, 우리가 사용할 것은 Linear Drag이다.

 

Linear Drag : 공기 저항, 이동 시 속도를 느리게 해준다. 단 너무 높게 잡지 않는다.

                       점프하고 내려올 때도 공기 저항을 받기 때문이다.

 

Angular Drag에서 Angular은 회전을 의미한다. 현재 쓸 필요가 없으니 나중에 알아보도록 하자.

 

우선 Linear Drag를 1이나 2로 바꾸면 속도가 점점 줄어드는 것을 확인할 수 있다.

그러나, 우리가 원하는 것은 키보드에서 손을 땠을 때 속도가 급격하게 줄어들어서

현재와 같이 빙판에서 미끄러지는 효과를 없애는 것이다.

 

다음과 같이 Update함수를 구현해주자.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

public class PlayerMove : MonoBehaviour
{
    public float maxSpeed;      //최대속도 변수(public으로 설정하여 Inspector 창에 뜸
    Rigidbody2D rigid;

    void Awake()        //플레이어 오브젝트가 만들어졌을 때
    {
        rigid = GetComponent<Rigidbody2D>();
    }

    //지속적인 키 입력은 FixedUpdate에서 하는 게 좋지만,
    //단발적인 키 입력은 Update에서 하는게 훨씬 더 좋다.
    //FixedUpdate는 1초에 약 50번 돌고, Update는 약 60번식 돈다
    //그래서 단발적인 키 입력은 FixedUpdate에서 하면 키가 씹힐 수도 있다.
    //이를 방지하기 위해 Update에다가 해주는 게 좋다.
    void Update()           
    {
        if(Input.GetButtonUp("Horizontal"))     //Up은 떼는 것
        {
            //Stop speed
            //rigid.velocity = new Vector2(0.5, rigid.velocity.y);
            //오른쪽으로 가는지 왼쪽으로 가는 지 모름
            //normalized : 벡터 크기를 1로 만든 상태 (단위 벡터)
            //rigid.velocity.x는 현재 가고 있는 수평방향의 속력과 방향을 모두 알수 있음
            //rigid.velocity.normalized => 현재 속도를 1로 만듦
            rigid.velocity = new Vector2(rigid.velocity.normalized.x * 0.5f, rigid.velocity.y);
        }
    }

    void FixedUpdate()      //플레이어가 움직이는 물리연산이므로
    {
        //Move by Control
        float h = Input.GetAxisRaw("Horizontal");
        rigid.AddForce(Vector2.right*h,ForceMode2D.Impulse);

        //Right Max Speed
        //현재 물체의 속도가 최대 속도를 넘었다면
        //속도를 최대속도로 한정
        //y축 속도를 0으로 해버리면 공중에 멈춤으로 0으로 설정 X
        if (rigid.velocity.x > maxSpeed)
            rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y);

        //Left Max Speed
        else if (rigid.velocity.x < maxSpeed*(-1))
            rigid.velocity = new Vector2(maxSpeed * (-1), rigid.velocity.y);
    }
}

 

위와 같이 코드를 짜고 실행시켜 주면, 좀 더 부드럽게 멈추는 것을 확인할 수 있다.

 

그리고 다음으로 넘어가기 전에

Freeze Rotation을 체크하고 넘어가자.

Freeze Rotation : 오브젝트 회전을 얼리는 옵션

 

이를 체크하면 오브젝트가 넘어지는 것을 방지할 수 있다.

 

애니메이션 순환

이제 애니메이션을 건드릴 차례이다.

왼쪽 방향키를 누르면 문워크를 하고 있으며, 저번 시간에는 애니메이션을 두 개 넣었는데,

걸을 때 걷는 모션이 나오지 않는다.

 

이번에는 애니메이션을 컨트롤을 해볼 것이다.

 

우선 왼쪽을 볼 때, 왼쪽으로 뒤집어지는 것부터 구현해보겠다.

생각보다 간단하다.

캐릭터의 Sprite Renderer에서 Flip을 눌러보면 뒤집혀지는 것을 확인 할 수 있다.

 

 

이것을 C# 스크립트로 구현을 하면 된다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

public class PlayerMove : MonoBehaviour
{
    public float maxSpeed;      //최대속도 변수(public으로 설정하여 Inspector 창에 뜸
    Rigidbody2D rigid;
    SpriteRenderer spriteRenderer;

    void Awake()        //플레이어 오브젝트가 만들어졌을 때
    {
        rigid = GetComponent<Rigidbody2D>();
        spriteRenderer = GetComponent<SpriteRenderer>();
    }

    //지속적인 키 입력은 FixedUpdate에서 하는 게 좋지만,
    //단발적인 키 입력은 Update에서 하는게 훨씬 더 좋다.
    //FixedUpdate는 1초에 약 50번 돌고, Update는 약 60번식 돈다
    //그래서 단발적인 키 입력은 FixedUpdate에서 하면 키가 씹힐 수도 있다.
    //이를 방지하기 위해 Update에다가 해주는 게 좋다.
    void Update()           
    {
        if(Input.GetButtonUp("Horizontal"))     //Up은 떼는 것
        {
            //Stop speed
            //rigid.velocity = new Vector2(0.5, rigid.velocity.y);
            //오른쪽으로 가는지 왼쪽으로 가는 지 모름
            //normalized : 벡터 크기를 1로 만든 상태 (단위 벡터)
            //rigid.velocity.x는 현재 가고 있는 수평방향의 속력과 방향을 모두 알수 있음
            //rigid.velocity.normalized => 현재 속도를 1로 만듦
            rigid.velocity = new Vector2(rigid.velocity.normalized.x * 0.5f, rigid.velocity.y);
        }

        //Direction Sprite (방향 전환)
        //flipX는 Bool값이다. Input을 통해 들어오는 값이 -1이면 뒤집는다.
        if (Input.GetButton("Horizontal"))
        {
            spriteRenderer.flipX = Input.GetAxisRaw("Horizontal") == -1;
        }
    }

    void FixedUpdate()      //플레이어가 움직이는 물리연산이므로
    {
        //Move by Control
        float h = Input.GetAxisRaw("Horizontal");
        rigid.AddForce(Vector2.right*h,ForceMode2D.Impulse);

        //Right Max Speed
        //현재 물체의 속도가 최대 속도를 넘었다면
        //속도를 최대속도로 한정
        //y축 속도를 0으로 해버리면 공중에 멈춤으로 0으로 설정 X
        if (rigid.velocity.x > maxSpeed)
            rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y);

        //Left Max Speed
        else if (rigid.velocity.x < maxSpeed*(-1))
            rigid.velocity = new Vector2(maxSpeed * (-1), rigid.velocity.y);
    }
}

 

저장하고 실행하면

 

 

이렇게 뒤집어지는 모습을 볼 수 있다.

그 다음은 애니메이션을 건드릴 차례이다.

키를 눌렀을 떄 걷는 애니메이션이 나오게 해보자.

 

우선 Animator에 들어가 대기 상태를 우클릭 해서 Make Transition을 통해 화살표를 이어주자

 

Transition. : 애니메이션 상태를 옮겨가는 통로

 

우클릭 후 Make Transition을 눌러 화살표를 이어주자.

 

그리고 좌측을 보면 파라미터가 있다.

+를 누르면 float, int, bool, trigger가 있다.

이는 애니메이터 매개변수이다.

 

애니메이터 매개변수 : 상태를 바꿀 때 필요한 변수

 

+를 눌러서 bool을 선택하고 isWalking을 하나 만들어주자.

 

그리고 화살표를 클릭한 뒤 Conditions에서 +를 눌러준다.

아래 사진은 isWalking이라는 조건을 만족하면 Walk 애니메이션을 실행한다는 뜻이다

 

 

그리고 타임라인은 애니메이션이 바뀔 때 부드럽게 바꾸어주는 그런 것이다.

2D 애니메이션이기 때문에 즉각즉각 바뀌면 되므로 겹치는 건 없애주면 된다.

 

수정 후

그리고 Has Exit Time을 해제해준다.

 

Has Exit Time : 애니메이션이 끝날 때까지 상태를 유지

 

저것을 해제하지 않으면 이동을 멈췄을 때도 애니메이션이 다 끝날 때까지

뛰는 모션이 나오게 된다.

 

반대쪽도 똑같이 Condition에 유의하며 만들어준다.

 

 

준비는 완료되었으나, isWalking을 바꿔주는 건 스크립트로 구현해야 한다.

Animator 컴포넌트를 받아서 속도가 0일 때, 저 상태를 끌 수 있도록 만들어주자.

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

public class PlayerMove : MonoBehaviour
{
    public float maxSpeed;      //최대속도 변수(public으로 설정하여 Inspector 창에 뜸
    Rigidbody2D rigid;
    SpriteRenderer spriteRenderer;
    Animator anim;

    void Awake()        //플레이어 오브젝트가 만들어졌을 때
    {
        rigid = GetComponent<Rigidbody2D>();
        spriteRenderer = GetComponent<SpriteRenderer>();
        anim = GetComponent<Animator>();
    }

    //지속적인 키 입력은 FixedUpdate에서 하는 게 좋지만,
    //단발적인 키 입력은 Update에서 하는게 훨씬 더 좋다.
    //FixedUpdate는 1초에 약 50번 돌고, Update는 약 60번식 돈다
    //그래서 단발적인 키 입력은 FixedUpdate에서 하면 키가 씹힐 수도 있다.
    //이를 방지하기 위해 Update에다가 해주는 게 좋다.
    void Update()           
    {
        if(Input.GetButtonUp("Horizontal"))     //Up은 떼는 것
        {
            //Stop speed
            //rigid.velocity = new Vector2(0.5, rigid.velocity.y);
            //오른쪽으로 가는지 왼쪽으로 가는 지 모름
            //normalized : 벡터 크기를 1로 만든 상태 (단위 벡터)
            //rigid.velocity.x는 현재 가고 있는 수평방향의 속력과 방향을 모두 알수 있음
            //rigid.velocity.normalized => 현재 속도를 1로 만듦
            rigid.velocity = new Vector2(rigid.velocity.normalized.x * 0.5f, rigid.velocity.y);
        }

        //Direction Sprite (방향 전환)
        //flipX는 Bool값이다. Input을 통해 들어오는 값이 -1이면 뒤집는다.
        if (Input.GetButton("Horizontal"))
        {
            spriteRenderer.flipX = Input.GetAxisRaw("Horizontal") == -1;
        }

        //Animation
        if (Mathf.Abs(rigid.velocity.x) < 0.3)
            anim.SetBool("isWalking", false);

        else
            anim.SetBool("isWalking", true);

        //Mathf : 수학 관련 함수를 제공하는 클래스 (Unity에서 제공)
        //Abs() : 절댓값을 나타냄
    }

    void FixedUpdate()      //플레이어가 움직이는 물리연산이므로
    {
        //Move by Control
        float h = Input.GetAxisRaw("Horizontal");
        rigid.AddForce(Vector2.right*h,ForceMode2D.Impulse);

        //Right Max Speed
        //현재 물체의 속도가 최대 속도를 넘었다면
        //속도를 최대속도로 한정
        //y축 속도를 0으로 해버리면 공중에 멈춤으로 0으로 설정 X
        if (rigid.velocity.x > maxSpeed)
            rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y);

        //Left Max Speed
        else if (rigid.velocity.x < maxSpeed*(-1))
            rigid.velocity = new Vector2(maxSpeed * (-1), rigid.velocity.y);
    }
}

 

 Mathf 클래스의  Abs() (절댓값) 함수를 사용하여 0.3 (오른쪽)이거나 -0.3(왼쪽)보다

속도가 줄어들면 캐릭터가 멈추게 만들었다.