지우너

[Unity 3D Run] 03.05-03.09 계획, 캐릭터 이동 구현 본문

Project

[Unity 3D Run] 03.05-03.09 계획, 캐릭터 이동 구현

지옹 2023. 3. 9. 23:16

03.05

제작 목표 기간: 1달

03.05 계획 수립

03.06-03.12 스테이지를 만들 때 사용할 베이스 씬 만들기

03.13-03.19 스테이지 선택 씬, 스테이지 5개 정도만 만들어 보기

03.20-03.26 메인화면, 로그인 구현

03.27-03.31 테스트

04.03~ 버그는 없는지, 불편한 부분 고치기, 코드 수정

 

로그인 화면 - 로그인 구현은 어떻게 할 것인지 고민하기

메인화면 - 상점, 게임 시작으로 갈 수 있는 선택지

게임 시작 선택 시 → 단계 설정 화면(이전 단계를 클리어하지 않으면 다음 단계가 열리지 않도록)

행성 같은 느낌으로 해도 괜찮을 것 같다.

게임 스테이지

  • 캐릭터 - 좌우, 점프, 슬라이드 모션
  • 카메라 - 앞에서 뒤로 움직이는 것처럼 배경 움직이기
  • 장애물 - 기차와 같이 올라갈 수 있는 장애물, 허들이나 구멍처럼 뛰어 넘어야 하는 장애물
  • 점수 - 뛰어다니면서 많이 먹으면 좋은 점수를 주는 것
  • 아이템 - 시간이 되면 천천히 넣어도 되는데, 슈퍼 마리오나 커비처럼 먹으면 특별한 능력(모습이 변하면서 한 번 장애물에 닿아도 살 수 있거나, 특이한 능력을 쓸 수 있는?)을 사용할 수 있는 아이템

장애물을 랜덤으로 배치할 수 있으면 좋을 것 같은데, 살짝 애매할 것 같다.

스테이지별 난이도 조정은??

 

 

03.08

앞에 이틀은 감기에 걸려서 공부를 거의 안 했다… 아직 기침도 좀 나고 머리도 살짝 아프긴 하지만, 그래도 더 늦어지면 손 놓을 수도 있겠다 싶어서 오늘은 반드시 시작하려고 한다.

백지 상태에서 시작하는 게 제일 어렵다. Plane으로 땅을 만들고 Capsule로 플레이어 캐릭터를 만들어야 한다는 건 알겠는데 xyz가 조금 헷갈렸다.

plane을 일단 생성하고, Transfrom 우클릭 Reset을 시켜준 다음, Scale의 Z를 10으로 변경해주었다.

그리고 capsule을 생성하여, 마찬가지로 Transform > Reset, 이렇게 하면 plane과 동일하게 0, 0, 0으로 설정되기 때문에 캡슐의 윗부분은 plane위로 나와 있고 그 밑은 plane 아래로 나와 지면에 박혀 있는 것처럼 보임

이동 도구를 이용하여 plane의 끝으로 이동시켜줬다.

플레이 할 때 캐릭터와 앞의 길을 뒤에서 보는 것처럼 카메라를 이동시켜줬다.

Assets 아래에 Material 폴더를 만들어 주고, Plane과 Capsule을 구분하기 위해 Material을 만들어 각각에 드래그 해서 Add Component 해주려고 했는데 왜 안 되지

    Hierarchy에 드래그앤 드롭하면 안 되고, Inspector나 Scene에 있는 오브젝트에 드래그 앤 드랍 해주면 적용이 된다.

그 다음 capsule에 중력의 영향을 받도록 rigdbody를 넣어줬다.

https://github.com/SebLague/Create-a-Game-Source/tree/master/Episode 25

    전에 총게임 클론코딩할 때 사용했던 코드

    Player 스크립트는 모든 입력이 들어오는 스크립트이고, Player 스크립트는 이 입력을 PlayerController 스크립트로 보낸다.

    PlayerController 스크립트는 실제 플레이어 컨트롤을 담당.

 

[캐릭터를 만들 때 생각해야 할 것]

  • 키보드 입력을 받으면 움직임 (상하입력을 받으면 점프, 슬라이딩을 함)
  • 캐릭터는 정면을 향해 일정한 속도 혹은 점점 속도가 빨라지도록 이동해야 한다.
  • 장애물에 부딪히면 게임 오버
  • 아이템을 먹으면 점수를 얻거나 잃는다.

비주얼 스튜디오 자동완성이 안 돼서 구글에 검색했는데 Edit > Preference에 가라는 말이 대부분이었다.

Edit에 그런 선택지가 없는 것을 보고 맥 유니티 자동완성으로 검색을 했고, Preference는 못 찾았지만 Setting에서 External Tools가 있었던 덕분에 잘 해결할 수 있었다.

 

[Unity] 캐릭터 이동 - MayQing Study

 

03.09

[Unity 3D] 캐릭터 움직임 구현 - 인용세상

[Unity] Start 와 Awake의 차이

이제 캐릭터가 움직일 수 있도록 코드를 짜야한다.

캐릭터 이동에 대한 블로그 글을 보고 코드를 그래로 사용해보았다.

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

[RequireComponent (typeof (PlayerController))]
public class Player : MonoBehaviour
{
    public float Speed = 10.0f;

    PlayerController controller;
    float h, v; // 가로, 세로축

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    // 이동 관련 함수를 짤 때는 Update보다 FixedUpdate가 더 효율이 좋다고 한다.
    void FixedUpdate()
    {
        // Input.GetAxis(string name)
        // -1.0f 부터 1.0f 까지의 범위의 값을 반환합니다. 즉, 부드러운 이동이 필요한 경우에 사용
        h = Input.GetAxis("Horizontal");        // 가로축
        v = Input.GetAxis("Vertical");          // 세로축

        transform.position += new Vector3(h, 0, v) * Speed * Time.deltaTime;
    }

}

모바일 런게임처럼 왼쪽, 가운데, 오른쪽 구역에 슉슉 이동하는 것이 아니라 부드럽게 실수의 좌표로 이동함.

→원래 생각했던 것과는 살짝 달라지지만, 일단 이렇게 pc용으로 만들고, 살짝 수정해서 원래 생각했던 모바일 버전 게임도 만들어봐야겠다.

 

Rigidbody가 있어서 좌우로 움직이면 넘어짐

    Player의 Inspector 창에서 RigidBody > Constraints에서 Freeze Rotation x, y, z 옵션 체크

 

+++ 움직이게 만드는 방법들에 대하여: 성능 +++

[유니티]Rigidbody. position과 MovePosition()의 차이와 성능. 그리고 결론?

MovePosition - 백지부터 시작하는 이세계 코딩 생활

https://github.com/SebLague/Create-a-Game-Source/tree/master/Episode 25

전에 클론코딩하던 강좌의 코드를 보니 FixedUpdate()에 이동 함수로 myRigidbody.MovePosition이라는 것을 사용했다. MovePosition이라는 함수를 이 사람이 만든 건가?라는 생각을 잠시 했다. myRigidbody는 Rigidbody의 객체?(객체라고 해야하나??)이기 때문에 Rigidbody 안에 있는 MovePosition을 사용했다고 생각했다. 그래서 구글에 검색하여 위의 블로그 글을 발견했다.

https://blog.naver.com/sabotduke/220802330662

50만번 테스트한 결과 transform보다 rigidbody.position이 10배 이상 빠르다는 것을 알 수 있다.

  • Rigidbody.position

Rigidbody.position allows you to get and set the position of a Rigidbody using the physics engine. If you change the position of a Rigibody using Rigidbody.position, the transform will be updated after the next physics simulation step. This is faster than updating the position using Transform.position, as the latter will cause all attached Colliders to recalculate their positions relative to the Rigidbody.

  • Rigidbody.MovePosition(Vector3 position)

Use Rigidbody.MovePosition to move a Rigidbody, complying with the Rigidbody's interpolation setting.If Rigidbody interpolation is enabled on the Rigidbody, calling Rigidbody.MovePosition results in a smooth transition between the two positions in any intermediate frames rendered. This should be used if you want to continuously move a rigidbody in each FixedUpdate.Set Rigidbody.position instead, if you want to teleport a rigidbody from one position to another, with no intermediate positions being rendered.

 

간단히 A지점에서 B지점으로 움직인다고 할 때, position은 순간이동MovePosition()은 이동이라고 생각하면 된다.

그래서 클론코딩 코드를 대충 복붙하여 코드를 수정해줬다.

//PlayerController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class PlayerController : MonoBehaviour
{
    Vector3 velocity;
    Rigidbody myRigidbody;

    // Start is called before the first frame update
    void Start()
    {
        myRigidbody = GetComponent<Rigidbody>();
    }

    public void Move(Vector3 _velocity)
    {
        velocity = _velocity;
    }

    void FixedUpdate()
    {
        myRigidbody.MovePosition(myRigidbody.position + velocity * Time.fixedDeltaTime);

    }
}
//Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// GetComponent<PlayerController>()는 PlayerController와 Player 스크립트가 같은 오브젝트에 붙어 있는걸 전제로 한다.
// [RequireComponent(typeof(PlayerController))] 코드를 적어줘서 이 부분이 에러를 내지 않게 됨.
[RequireComponent (typeof (PlayerController))]
public class Player : MonoBehaviour
{
    public float Speed = 10.0f;
    // moveVelocity를 다른 스크립트인 PlayerController로 전달해서 물리적인 부분들을 처리할 수 있도록 할 것
    // 그래서 PlayerController에 대한 레퍼런스를 가져와야합니다.
    PlayerController controller;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    void Awake()
    {
        controller = GetComponent<PlayerController>();
    }

        // Update is called once per frame
        void Update()
    {
        // Movement input
        // 플레이어의 움직임을 입력 받는 것
        // 수평(Horizontal) 과 수직(Vertical) 방향에 대한 입력을 받고 싶으니
        // Input. 을 적고 GetAxis()를 호출해서 "Horizontal"을 넣습니다.
        // y값은 내버려 둬도 되며(0), 이어서 Input.GetAxis("Vertical")을 호출
        Vector3 moveInput = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
        // 입력 받은 값을 방향으로 변환하고 움직임 속도를 곱할 겁니다.
        // 입력의 방향을 얻기 위해 moveInput를 정규화(nomalized)하여 가져옵니다 (nomarlized는 방향을 가리키는 단위벡터로 만드는 연산)
        // 거기에 moveSpeed를 곱해줍니다
        Vector3 moveVelocity = moveInput.normalized * Speed;
        controller.Move(moveVelocity);
    }

}

NullReferenceException 에러가 났다.

 

왜 에러가 난 건지 보면서 주석만 조금 달았는데, 갑자기 에러 없이 캐릭터가 움직인다. 캐릭터에 Player tag를 설정해줘서 그런 건가?

그런데 태그를 불러오는 코드도 딱히 없었는데 player 태그를 붙여주는 게 그렇게 큰 의미가 있는 건가?

나도 모르게 코드의 어딘가가 잘 작동하게 만든 건가...? 왜 잘 되는 건지 모르겠다...

//PlayerController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class PlayerController : MonoBehaviour
{
    Vector3 velocity; // velocity=speed+direction
    Rigidbody myRigidbody;

    // Start is called before the first frame update
    void Start()
    {
        myRigidbody = GetComponent<Rigidbody>();
    }
    // Player에서 들어온 입력으로 계산한 velocity를
    // PlayerController의 velocity변수에 저장
    public void Move(Vector3 _velocity)
    {
        velocity = _velocity;
    }

    void FixedUpdate()
    {
        myRigidbody.MovePosition(myRigidbody.position + velocity * Time.fixedDeltaTime);
    }
}
//player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// GetComponent<PlayerController>()는 PlayerController와 Player 스크립트가 같은 오브젝트에 붙어 있는걸 전제로 한다.
// [RequireComponent(typeof(PlayerController))] 코드를 적어줘서 이 부분이 에러를 내지 않게 됨.
[RequireComponent (typeof (PlayerController))]
public class Player : MonoBehaviour
{
    public float moveSpeed = 5.0f;
    // moveVelocity를 다른 스크립트인 PlayerController로 전달해서 물리적인 부분들을 처리할 수 있도록 할 것
    // 그래서 PlayerController에 대한 레퍼런스를 가져와야합니다.
    PlayerController controller;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    void Awake()
    {
        controller = GetComponent<PlayerController>();
    }

        // Update is called once per frame
        void Update()
    {
        // Movement input 플레이어의 움직임을 입력 받는 것
        // 수평(Horizontal) 과 수직(Vertical) 방향에 대한 입력을 받고 싶으니
        // Input. 을 적고 GetAxis()를 호출해서 "Horizontal"을 넣습니다.
        // y값은 내버려 둬도 되며(0), 이어서 Input.GetAxis("Vertical")을 호출
        Vector3 moveInput = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));
        // 입력 받은 값을 방향으로 변환하고 움직임 속도를 곱할 겁니다.
        // 입력의 방향을 얻기 위해 moveInput를 정규화(nomalized)하여 가져옵니다 (nomarlized는 방향을 가리키는 단위벡터로 만드는 연산)
        // 거기에 moveSpeed를 곱해줍니다
        Vector3 moveVelocity = moveInput.normalized * moveSpeed;
        // controller에게 velocity값을 넘겨줌
        controller.Move(moveVelocity);
    }

}
Vector3 moveInput = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical"));

고쳐야 할 점은 음 캐릭터가 앞뒤로 움직일 수 있게 할 것인가?? 움직일 수 없게 하는 게 역시 맞을 것 같다.

  • 앞뒤로 움직일 수 있게 할 경우: 카메라에 안 나오는 부분에 닿으면 죽는 투명한 오브젝트를 만들어서 카메라와 함께 앞으로 계속 이동하도록 만들어야함(유니티 2D 게임 제작 책에서 비슷한 걸 배웠다)
  • 움직일 수 없게 할 경우: 원래는 이렇게 만들려고 했다. 앞으로 가는 값은 일정한 속도로 앞으로 이동하도록 만들기

움직일 수 없게 하려면 moveInput에 Input.GetAxisRaw("Vertical")을 moveSpeed로 바꾸면 일정한 속도로 앞으로 갈 것 같은데??

Vector3 moveInput = new Vector3(Input.GetAxisRaw("Horizontal"), 0, moveSpeed);

Vector3 moveInput = new Vector3(Input.GetAxisRaw("Horizontal"), 0, 5.0f);

두 코드는 같게 되는데, Lerp 글을 읽으니까 이렇게 실수로 입력을 넣는 것이 괜찮은지 의문이 든다.

일단 잘 작동될지부터 의문이니까 코드를 넣어서 실행시켜봤다.

 

Vector3 moveInput = new Vector3(Input.GetAxisRaw("Horizontal"), 0, moveSpeed);

앞을 향해 일정한 속도로 이동하는데, 좌우 이동 속도가 조금 느려진 느낌이다.

 

<내일 할 것>

정면으로 이동하는 속도를 조금 줄이고, 좌우 이동 속도가 지금보다는 살짝 빨라지도록 수정해야 한다.

카메라도 캐릭터가 앞으로 이동하는 속도에 맞게 이동하도록 만들어야 한다.