2D 콘텐츠 제작

Geometry Dash [#2] - 씬전환 / 캐릭터

송현호 2023. 9. 14. 10:36

1. 씬전환

 

저번 포스트에서 배경 스크롤링과 컬러 시프트를 했었는데 이번 포스트에선 씬전환을 관리하는 App씬을 만들고 메인화면에서 게임시작 버튼을 누르면 인게임으로 이동할 수 있도록 만들어보겠다.

 

예전에 관련 프로젝트를 진행한 적이 있어서 비동기 방식을 이용해 씬 전환을 만들어 줬다.

 

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

public class App : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        DontDestroyOnLoad(this.gameObject);

        AsyncOperation oper = SceneManager.LoadSceneAsync("Main");
        oper.completed += OnLoadCompleteTitleScene;
    }

    private void OnLoadCompleteTitleScene(AsyncOperation obj)
    {
        Debug.Log("Main씬 로드 완료");
        Main main = GameObject.FindObjectOfType<Main>();
        main.onChangeScene = (() =>
        {
            this.LoadGameScene();
        });
    }

    private void LoadGameScene()
    {
        //비동기 로드 
        AsyncOperation oper = SceneManager.LoadSceneAsync("Game");
        oper.completed += (ao) => {

        };
    }


}

 

게임이 끝났을때 메인화면으로 돌아가는 기능도 추가해야겠지만 일단 클리어를 설계하고나서 하는 걸로 하자.

 

2. 캐릭터

씬전환까지 마쳤으므로 작업을 게임씬으로 옮겼다 일단 임시로 다음과 같이 캐릭터와 배경을 구성해주었다 배경은 메인씬에서 뜯어 왔다. 그 다음 캐릭터를 움직여 보기 위해 화살표키를 입력해서 리지드바디의 무브포지션으로 움직이도록 코드를 작성하였다.

 

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

public class CharacterCtrl : MonoBehaviour
{
    private Rigidbody2D rb;
    private float speed = 10;
    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody2D>();   
    }

    // Update is called once per frame
    void Update()
    {
        if(Input.GetKey(KeyCode.LeftArrow))
        {
            this.rb.MovePosition(this.transform.position + Time.deltaTime * speed * -this.transform.right);
        }
        if (Input.GetKey(KeyCode.RightArrow))
        {
            this.rb.MovePosition(this.transform.position + Time.deltaTime * speed * this.transform.right);
        }
    }
}

그 다음엔 지면에 붙어있을때만 점프가 가능하도록 onCollisionStay에 점프기능을 추가해주었다.

 

그리고 자연스러운 점프를 구현하기 위해 계속 수치를 조정하다가 점프를 할때 살짝 튕기는 현상이 있는걸 깨달았다. 그래서 chat gpt님에게 해결방법을 물어봤다.

 

음.. Physics Materials 2D를 사용하면 된다고 한다. 뭔지 알아보기 위해 유니티 공식문서에 검색해보았다.

 

머티리얼을 만들어서 해봤는데 안된다 그래서 다른 방법을 찾아보았다.

 

Collsion Detection을 continuous로 바꿔주었더니 충돌 시 살짝 들어가는 반응이 사라졌다

그리고 캐릭터의 크기를 좀 줄여주고 점프값과 이동값을 바꿔가면서 적당히 조절해주었다.

이제 캐릭터의 점프 시 회전을 만들어야 한다 일단 내가 아는 방법으로 리지드바디에 rotate함수를 이용해서 회전을 시켜봤다.

void Update()
    {
        Vector2 vel = rb.velocity;
        vel.x = 10;
        rb.velocity = vel;
        Debug.Log(isGround);

        if (Input.GetKey(KeyCode.Space) && isGround == true)
        {
            isGround = false;
            this.rb.AddForce(Vector3.up * jumpForce);
            
        }
        if(this.isGround == false)
        {
            this.transform.Rotate(Vector3.back * 4.8f);
        }


    }

되긴 되는데 뭔가 좀 부자연스럽다 회전도 잘 안되고 스페이스를 누르고 있거나 연타하면 점프하는 간격이 너무 빠르다

그래서 땅에 떨어지기 직전에 값을 고정시켜서 rotation의 z축이 90의 배수가 되기 전까진 점프를 못하도록 해봤다

 

void Update()
    {
        Vector2 vel = rb.velocity;
        vel.x = 10;
        rb.velocity = vel;
        Debug.Log(isGround);

        

        if (isGround == true)
        {
            Quaternion rot = this.transform.rotation;
            rot.z = Mathf.Round(rot.z/90)*90;
            this.transform.rotation = rot;
            Debug.Log(rot.z % 90);
            if (Input.GetKey(KeyCode.Space)&& rot.z % 90 == 0)
            {
                isGround = false;
                this.rb.AddForce(Vector3.up * jumpForce);
            }
            
            
        }
        else
        {
            this.transform.Rotate(Vector3.back * 5f);
        }
        

    }

아무래도 지면과 접촉한 시점에서 쿼터니언의 z값이 이미 0이 되기 때문에 z축의 90으로 나눈 나머지로 제한하는 건 크게 의미가 없는 듯 하다.

 

왜 이런 현상이 나타나는지 곰곰히 생각해본 결과 rotation의 값을 고정시켜도 회전힘을 가했기 때문에 남은 힘때문에 이렇게 된다고 생각했다. 그래서 이번엔 angularvelocity를 써서 물체의 회전 속도도 같이 0으로 만들어 주기로 했다. 

 

실험해본 결과 캐릭터가 핑글핑글 도는 현상은 사라졌지만 스페이스를 연타했을때 가끔씩 점프가 2번 되는 현상은 아직 남아 있다. 그리고 isGround로 점프 여부를 결정하고 있기 때문에 만약 땅에서 떨어지더라도 isGround가 false가 되지 않기 때문에 지면콜라이더와의 접촉이 떨어질때 isGround를 false로 만들어 주었다.

 

그리고 임시로 장애물을 만들고 원하는 만큼의 점프 거리와 높이 그리고 회전값이 나올 수 있도록 세부적인 수치를 계속 조정해주었다.

 

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

public class CharacterCtrl : MonoBehaviour
{
    private Rigidbody2D rb;
    private float speed = 10f;
    private float jumpForce = 750f;

    private bool isGround = true;
    // Start is called before the first frame update
    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        
    }

    // Update is called once per frame
    void Update()
    {
        Vector2 vel = rb.velocity;
        vel.x = 10;
        rb.velocity = vel;
        Debug.Log(isGround);

        

        if (isGround == true)
        {
            Quaternion rot = this.transform.rotation;
            rot.z = Mathf.Round(rot.z/90)*90;
            this.transform.rotation = rot;
            Debug.Log(rot.z % 90);
            if (Input.GetKey(KeyCode.Space)&& rot.z % 90 == 0)
            {
                isGround = false;
                this.rb.AddForce(Vector3.up * jumpForce);
            }
            
            
        }
        else
        {
            this.transform.Rotate(Vector3.back * 7f);
        }
        

    }

    

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if(collision.collider.tag == "ground")
        {
            isGround = true;
            this.rb.velocity = Vector2.zero;
            this.rb.angularVelocity = 0f;
        }
    }

    private void OnCollisionExit2D(Collision2D collision)
    {
        isGround = false;
    }
}