본문 바로가기
유니티3D

유니티3D 플레이어 조작 #2.점프와 행글라이더

행글라이더를 타본 적이 있으신가요? 크레이는 타본 적이 없습니다.
위험해서 아무나 할건 못되고 체력도 받쳐주어야 가능한 일이지 않습니까?
도전해서 안될건 없지만 크레이는 그쪽은 적성이 맞지 않는 것 같습니다 :)
솔직히 무섭습니다 ㅎㅎ

하지만 가상세계에서는 위험부담 없이 체력부담 없이 마음껏 탈수 있지요.
이번 시간에는 지난 시간의 플레이어 조작#1에 이어 플레이어의 점프행글라이더 조작을 다루어 보겠습니다.

itadventure.tistory.com/395

 

유니티3D(Unity3D) 플레이어 조작 #1

지난 시간의 중력#2에 이어 이번에는 플레이어 조작에 대해 다루어 보도록 하겠습니다. itadventure.tistory.com/394 유니티3D(Unity3D) - 중력#2 지난 챕터에서는 물체의 중력에 대해 알아보았습니다 itadvent

itadventure.tistory.com


우선 한가지 옥(?)의 티를 잡고 가도록 하겠습니다.
지난번 우리의 멋진 큐브 플레이어가 땅에 착지한 후에 유심히 살펴보신 분은 큐브가
살포시 공중 부양해 있는 것을 보실 수 있습니다.

이 것은 플레이어 캐릭터 컨트롤러의 키 높이 설정 때문인데요.
플레이어는 보이는 물체가 아닌 자체적인 키 높이와 반경을 가지고 충돌을 감지하기 때문입니다

그렇기 때문에 플레이어 큐브를 선택한 후에 Inspector 창을 맨 아래로 스크롤시켜,
Character Controller ( 캐릭터 컨트롤러 ) 항목에서 Height ( 키 ) 를 0.9로 변경해 주세요.

그리고 플레이해보시면 바닥에 닿았을 때 큐브 플레이어가 바닥에 붙어 있는 것을 보실 수 있습니다.

지난번 만든 무대에 걸쳐 기능을 이어 가도록 하겠습니다.
플레이어가 점프를 하려면 무언가 액션을 통해 점프를 발동하도록 하면 됩니다.

여기서는 스페이스키를 누르는 것으로 그 액션을 실행하도록 하겠습니다.

스페이스키를 누르면 점프를 하게 하는 코드는 다음과 같이 단 한줄을 추가하면 됩니다.
5.0f는 점프량인데 적당히 조절해주면 됩니다.

if (Input.GetButton("Jump")) MoveDir.y = 5.0f;

하지만 좀 더 코드를 세련되게, 점프량 수치 또한 별도의 멤버 변수로 분리해 주도록 합시다.

멤버변수 JumpPow 를 추가해주고,

public float JumpPow;

이 변수의 초기화는 Start() 메소드에서 수행하도록 합니다.

void Start()
{
          :
    JumpPow = 5.0f;
 }

그리고 점프하는 코드는 Update() 메소드 안에 넣는 것이지요.

funtion Update(){
           :
    if (Input.GetButton("Jump")) MoveDir.y = JumpPow;
           :
}

위 내용만 가지고는 어디에 넣는지 구체적으로 알수 없으니 최종 코드를 싣도록 하겠습니다..

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

public class PlayerController : MonoBehaviour
{
    public CharacterController SelectPlayer; // 제어할 캐릭터 컨트롤러
    public float Speed;  // 이동속도
    public float JumpPow;

    private float Gravity; // 중력   
    private Vector3 MoveDir; // 캐릭터의 움직이는 방향.

    // Start is called before the first frame update
    void Start()
    {
        // 기본값
        Speed = 5.0f;
        Gravity = 10.0f;
        MoveDir = Vector3.zero;
        JumpPow = 5.0f;
    }

    // Update is called once per frame
    void Update()
    {
        if (SelectPlayer == null) return;
        // 캐릭터가 바닥에 붙어 있는 경우만 작동합니다.
        // 캐릭터가 바닥에 붙어 있지 않다면 바닥으로 추락하고 있는 중이므로
        // 바닥 추락 도중에는 방향 전환을 할 수 없기 때문입니다.
        if (SelectPlayer.isGrounded)
        {
            // 키보드에 따른 X, Z 축 이동방향을 새로 결정합니다.
            MoveDir = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
            // 오브젝트가 바라보는 앞방향으로 이동방향을 돌려서 조정합니다.
            MoveDir = SelectPlayer.transform.TransformDirection(MoveDir);
            // 속도를 곱해서 적용합니다.
            MoveDir *= Speed;
            
            // 스페이스 버튼에 따른 점프
            if (Input.GetButton("Jump")) MoveDir.y = JumpPow;
        }
        // 캐릭터가 바닥에 붙어 있지 않다면
        else
        {
            // 중력의 영향을 받아 아래쪽으로 하강합니다.           
            MoveDir.y -= Gravity * Time.deltaTime;
        }
        // 앞 단계까지는 캐릭터가 이동할 방향만 결정하였으며,
        // 실제 캐릭터의 이동은 여기서 담당합니다.
        SelectPlayer.Move(MoveDir * Time.deltaTime);
    }
}

이 상태로 플레이해보시면 플레이어가 바닥에 있을 때 스페이스바를 누르면 캐릭터가 통통 뜁니다.

하지만 공중에서 방향전환을 할수는 없기 때문에 오른쪽으로 빠르게 달려가며 점프라도 하는 순간!
플레이어는 운명을 달리 하게 됩니다..


이제 한가지 제약조건을 추가하도록 합시다.
기획자가 아래와 같은 요구를 했다고 칩시다.

플레이어가 너무 기운이 넘칩니다.
스페이스바를 누르고 있는 것만으로 계속 점프가 되고 있는데요.
스페이스바를 한번 누를 때만 점프하고
스페이스키를 떼지 않고 계속 누르고 있는다면 점프를 하지 않는 것이지요.
다시 점프를 하려면 반드시 스페이스키를 놓은 상태에서 눌러야 합니다.
점프 도중에 미리 놓는 것은 상관없습니다.

이를 위해서는 최종 스페이스바 눌림 상태가 어떠한지를 보관할 별도의 변수가 필요합니다.
그래서 최종 눌림 상태에 따라 누르고 놓지 않고 있었다면 점프를 하지 않고
놓았다가 누른 경우에만 점프를 하도록 하는 것입니다.

이를 위해서 아래와 같이 최종 점프 버튼 눌림상태 변수를 선언하고,

private bool JumpButtonPressed;

Start() 에서 이를 초기화해준 다음에,

void Start()
{
       :
    JumpButtonPressed = false;
}

Update()에서 점프하는 코드를 아래와 같이 변경해 줍니다.
이 코드는 최종점프버튼이 눌려지지 않은 상태에서 버튼을 누를 경우만 점프하도록 동작하는 동시에,
최종점프 버튼을 누른 것으로 상태를 바꾸어 줍니다.

// 스페이스 버튼에 따른 점프
if (JumpButtonPressed == false && Input.GetButton("Jump"))
{
    JumpButtonPressed = true;
    MoveDir.y = JumpPow;
}

그리고 Update() 함수의 마무리 부분에 스페이스키가 만일 눌려지지 않았다면
최종점프버튼 상태를 '눌리지 않음'으로 해제하는 부분이 필요합니다.

if (!Input.GetButton("Jump"))
{
    JumpButtonPressed = false;
}

최종 코드는 아래와 같습니다.

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

public class PlayerController : MonoBehaviour
{
    public CharacterController SelectPlayer; // 제어할 캐릭터 컨트롤러
    public float Speed;  // 이동속도
    public float JumpPow;

    private float Gravity; // 중력   
    private Vector3 MoveDir; // 캐릭터의 움직이는 방향.
    private bool JumpButtonPressed;  //  최종 점프 버튼 눌림 상태
 
    // Start is called before the first frame update
    void Start()
    {
        // 기본값
        Speed = 5.0f;
        Gravity = 10.0f;
        MoveDir = Vector3.zero;
        JumpPow = 5.0f;
        JumpButtonPressed = false;
    }

    // Update is called once per frame
    void Update()
    {
        if (SelectPlayer == null) return;
        // 캐릭터가 바닥에 붙어 있는 경우만 작동합니다.
        // 캐릭터가 바닥에 붙어 있지 않다면 바닥으로 추락하고 있는 중이므로
        // 바닥 추락 도중에는 방향 전환을 할 수 없기 때문입니다.
        if (SelectPlayer.isGrounded)
        {
            // 키보드에 따른 X, Z 축 이동방향을 새로 결정합니다.
            MoveDir = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
            // 오브젝트가 바라보는 앞방향으로 이동방향을 돌려서 조정합니다.
            MoveDir = SelectPlayer.transform.TransformDirection(MoveDir);
            // 속도를 곱해서 적용합니다.
            MoveDir *= Speed;

            // 스페이스 버튼에 따른 점프 : 최종 점프버튼이 눌려있지 않았던 경우만 작동
            if (JumpButtonPressed == false && Input.GetButton("Jump"))
            {
                JumpButtonPressed = true;
                MoveDir.y = JumpPow;
            }
        }
        // 캐릭터가 바닥에 붙어 있지 않다면
        else
        {
            // 중력의 영향을 받아 아래쪽으로 하강합니다.           
            MoveDir.y -= Gravity * Time.deltaTime;
        }

        // 점프버튼이 눌려지지 않은 경우
        if (!Input.GetButton("Jump"))
        {
            JumpButtonPressed = false;  // 최종점프 버튼 눌림 상태 해제
        }
        // 앞 단계까지는 캐릭터가 이동할 방향만 결정하였으며,
        // 실제 캐릭터의 이동은 여기서 담당합니다.
        SelectPlayer.Move(MoveDir * Time.deltaTime);
    }
}

이제 플레이를 시작해보시면 스페이스바를 한번 누른 경우 점프가 시작되지만,
놓지 않는다고 계속 점프를 하지는 않습니다.

이제 마지막으로 행글라이더 모드를 살펴보겠습니다.
기획자가 다음과 같은 요구를 해왔다고 칩시다.

플레이어가 점프중 또는 하강 도중 또는 절벽에서 떨어지던 중
스페이스바를 누르면 하강 속도가 감속하여
행글라이더를 타듯이 서서히 내려오도록 해주세요.

하강 도중 스페이스바를 놓은 경우 다시 빠르게 하강하나,
다시 스페이스바를 누른 경우 느리게 하강하도록 합니다

그리고 이 때는 키보드의 상하좌우 키를 이용하여 캐릭터의 이동 컨트롤이 가능하게 합니다.

단, 스페이스바를 눌러 점프를 시작한 경우,
플레이어 하강이 시작될 때까지 스페이스바를 계속 누르고 있었다면 작동하지 않고
이 때에도 스페이스바를 놓았다가 다시 눌러야 합니다.때

약간 까다로운 조건이네요.
위 처리를 위해서는 행글라이더 상태 변수가 추가되어야 합니다.

버튼을 눌렀다 떼었다 동작에 따라 행글라이더 상태 변수를 활성화, 비활성화하도록 해야 하니까요.

행글라이더 모드 변수를 아래와 같이 추가하고,

private bool FlyingMode;  // 행글라이더 모드여부

Start() 에서 비활성화로 초기화합니다.

FlyingMode = false;

Update() 에서는 하강도중 스페이스바를 떼었다가 놓은 경우에만
행글라이더 모드가 발동하도록 하고

// 하강중에 스페이스 버튼을 누르면 슬로우 낙하모드 발동!
if (MoveDir.y < 0 && JumpButtonPressed == false && Input.GetButton("Jump"))
{
    FlyingMode = true;
}

중력을 처리하는 한줄의 코드 부분을 아래와 같이 바꾸어 줍니다.
이는 행글라이더 모드에서 중력의 영향을 급속히 감소하는 부분과,
행글라이더 모드에서 플레이어의 공중 컨트롤을 가능하게 하는 부분을 포함합니다.

if (FlyingMode)
{
    JumpButtonPressed = true;
                
    // 중력 수치를 감속합니다.
    MoveDir.y *= 0.95f;

    // 하지만 하늘에서 정지해 있는 일은 벌어지지 않게 하기 위해
    // 최소 초당 -1의 하강 속도는 유지합니다.
    if (MoveDir.y > -1) MoveDir.y = -1;

    // 또한 이 때는 방향전환이 가능합니다.
    MoveDir.x = Input.GetAxis("Horizontal");
    MoveDir.z = Input.GetAxis("Vertical");
}
else
    // 중력의 영향을 받아 아래쪽으로 하강합니다.           
    MoveDir.y -= Gravity * Time.deltaTime;

전체 코드는 아래와 같습니다.

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

public class PlayerController : MonoBehaviour
{
    public CharacterController SelectPlayer; // 제어할 캐릭터 컨트롤러
    public float Speed;  // 이동속도
    public float JumpPow;

    private float Gravity; // 중력   
    private Vector3 MoveDir; // 캐릭터의 움직이는 방향.
    private bool JumpButtonPressed;  //  최종 점프 버튼 눌림 상태
    private bool FlyingMode;  // 행글라이더 모드여부

    // Start is called before the first frame update
    void Start()
    {
        // 기본값
        Speed = 5.0f;
        Gravity = 10.0f;
        MoveDir = Vector3.zero;
        JumpPow = 5.0f;
        JumpButtonPressed = false;
        FlyingMode = false;
    }

    // Update is called once per frame
    void Update()
    {
        if (SelectPlayer == null) return;
        // 캐릭터가 바닥에 붙어 있는 경우만 작동합니다.
        // 캐릭터가 바닥에 붙어 있지 않다면 바닥으로 추락하고 있는 중이므로
        // 바닥 추락 도중에는 방향 전환을 할 수 없기 때문입니다.
        if (SelectPlayer.isGrounded)
        {
            // 키보드에 따른 X, Z 축 이동방향을 새로 결정합니다.
            MoveDir = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
            // 오브젝트가 바라보는 앞방향으로 이동방향을 돌려서 조정합니다.
            MoveDir = SelectPlayer.transform.TransformDirection(MoveDir);
            // 속도를 곱해서 적용합니다.
            MoveDir *= Speed;

            // 스페이스 버튼에 따른 점프 : 최종 점프버튼이 눌려있지 않았던 경우만 작동
            if (JumpButtonPressed == false && Input.GetButton("Jump"))
            {
                JumpButtonPressed = true;
                MoveDir.y = JumpPow;
            }
        }
        // 캐릭터가 바닥에 붙어 있지 않다면
        else
        {
            // 하강중에 스페이스 버튼을 누르면 슬로우 낙하모드 발동!
            if (MoveDir.y < 0 && JumpButtonPressed == false && Input.GetButton("Jump"))
            {
                FlyingMode = true;
            }

            if (FlyingMode)
            {
                JumpButtonPressed = true;
                
                // 중력 수치를 감속합니다.
                MoveDir.y *= 0.95f;

                // 하지만 하늘에서 정지해 있는 일은 벌어지지 않게 하기 위해
                // 최소 초당 -1의 하강 속도는 유지합니다.
                if (MoveDir.y > -1) MoveDir.y = -1;

                // 또한 이 때는 방향전환이 가능합니다.
                MoveDir.x = Input.GetAxis("Horizontal");
                MoveDir.z = Input.GetAxis("Vertical");
            }
            else
                // 중력의 영향을 받아 아래쪽으로 하강합니다.           
                MoveDir.y -= Gravity * Time.deltaTime;
        }

        // 점프버튼이 눌려지지 않은 경우
        if (!Input.GetButton("Jump"))
        {
            JumpButtonPressed = false;  // 최종점프 버튼 눌림 상태 해제
            FlyingMode = false;         // 행글라이더 모드 해제
        }
        // 앞 단계까지는 캐릭터가 이동할 방향만 결정하였으며,
        // 실제 캐릭터의 이동은 여기서 담당합니다.
        SelectPlayer.Move(MoveDir * Time.deltaTime);
    }
}

이제 플레이를 해보도록 합시다.
컨트롤 방법은 아래와 같습니다.

플레이어가 점프를 할 때, 스페이스바를 놓았다가 다시 누르거나,
또는 절벽에서 떨어지던 도중 스페이스바를 누르면 플레이어가 천천히 내려옵니다.
이 때 상하좌우 키로 플레이어를 조정하여 낙하 지점을 조정할 수 있습니다.
이제 절벽으로 떨어질 일은 없겠지요 ㅎ..

갑자기 난이도가 어려워졌다고 느껴지실 수도 있는데요.
소스가 점점 길어지고 새로운 기법들이 등장하면 처음에 익숙하지 않기 때문입니다.
코드를 몇번이고 반복해서 살펴 보다 보면 어느 순간 눈에 익어 이해가 되기 마련이니
도전하시는 분들께서는 여러차례 소스를 살펴보시고 궁금하신 점은 문의 주세요 :)

아무쪼록 필요하신 분께 도움이 되셨길 바랍니다.
오늘도 여기까지 읽어주셔서 감사합니다.


성경말씀을 믿는 자는 감사할 수밖에 없습니다. 이는 예수 그리스도께서 우리를 '구속'하셨기 때문입니다.
참고로 이 떄의 구속(救贖:구원하고 죄를 씻음)은 자유를 제한하는 구속(拘束:잡고 묶음)과는 다른 의미입니다 :)
만일 마음에 감동이 되신다면 성경을 찾아 읽어보시길 권유 드립니다.

그리스도 예수 안에 있는 구속으로 말미암아 
하나님의 은혜로 값없이 의롭다 하심을 얻은 자 되었느니라

- 로마서 3장 24절 말씀 -


다음 챕터. 카메라 팔로우 미!

itadventure.tistory.com/397

 

 

유니티3D 플레이어 조작 #3. 카메라 팔로우 미!

드라마나 영화를 보면 카메라가 주연이나 조연을 향해 카메라를 클로즈업할 때가 있습니다. 사실 그 이유는..  :  : '시청률'을 올리기 위해서지요 :) 아무래도 필요한 순간에 필요한 인물에게

itadventure.tistory.com