본문 바로가기
게임 프로그래밍/게임개발 중급

게임개발 중급(12) - 2D 플랫포머 게임 만들기(7)

by jyppro 2023. 5. 1.

2D 플랫포머 게임 만들기(7)

 

지난 시간에는 만들어진 프리팹 Coin을 사용하여 정해진 바운더리 안에서 3초마다 랜덤하게 생성되도록 하는 제너레이터 스크립트를 작성해 보았습니다. 이번에는 좀 더 플랫포머 게임다운 방식으로 게임에 변화를 줄 것입니다. 코인이 랜덤위치에 생성되는 것이 아닌 정해진 위치에서 생성되고, 일정속도로 왼쪽방향으로 움직이도록 만들고, 플레이어가 맵을 이탈하지 않도록 경계를 설정해 줄 것입니다.

 

CoinSpawner 변경

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

public class CoinSpawner : MonoBehaviour
{
    public GameObject coinPrefab;
    
    void Start()
    {
        InvokeRepeating("SpawnCoin", 0f, 1f); // 1초마다 SpawnCoin 함수 실행
    }
    
    void SpawnCoin()
    {
        Vector2 spawnPosition = new Vector2(10f, 0f);
        
        Instantiate(coinPrefab, spawnPosition, Quaternion.identity);
    }
}

 

이전에 작성한 CoinSpawner를 바꾸어 줍니다. 3초마다 생성되는 것을 1초로 바꾸어주고, 랜덤위치를 오른쪽 끝인 Vector2(10f,0f)에 맞춰주었습니다.

 

그리고 새로운 스크립트 CoinMovement를 작성하겠습니다.

 

CoinMovement 스크립트 작성

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

public class CoinMovement : MonoBehaviour
{
    public float speed = 3f;
    private Rigidbody2D rb;

    void Start()
    {
        rb = GetComponent<Rigidbody2D>();
        rb.velocity = new Vector2(-speed, 0);
    }
}

 

이것은 코인을 일정한 속도로 왼쪽방향으로 움직일 수 있게 해주는 스크립트 입니다. 여기서는 velocity를 사용하기 때문에, Rigidbody2D를 코인 프리팹에 추가해줘야 합니다. 그리고 해당 스크립트도 같이 넣어줘야 합니다.

 

하지만, 이렇게만 하면 코인은 Rigidbody2D의 중력의 영향을 받기 때문에 코인이 밑으로 떨어지게 됩니다. 이 경우 Rigidbody2D에 있는 옵션 중 Body Type을 보면 Dynamic으로 되어있을 겁니다. 이것을 Kinematic으로 바꾸어 줍니다.

 

키네마틱
바디타입을 키네마틱으로 변경

 

이렇게 하면, 중력의 영향을 받지 않아, 생성된 코인들은 마치 공장레일처럼 일정한 속도로 왼쪽방향으로 이동하게 됩니다.

 

코인이-생성되고-있음
코인이 일정하게 생성

 

이제 플레이어가 바깥으로 사라지지 않도록 경계를 생성해 보겠습니다.

 

경계 생성

using UnityEngine;
using UnityEngine.UI;

public class PlayerController : MonoBehaviour
{
    public float moveSpeed = 5f;
    public float jumpForce = 15f;
    public int scoreValue = 10;  // 코인 하나의 점수
    public int score = 0;  // 현재 점수
    public float boundaryX = 10f; // x축 경계
    public float boundaryY = 5f; // y축 경계
    private Rigidbody2D rb;
    private bool isGrounded = false;
    GameObject ST;

    void Start()
    {
        this.ST = GameObject.Find("ScoreText"); 
        rb = GetComponent<Rigidbody2D>();
    }

    void FixedUpdate()
    {
        float moveDirection = Input.GetAxis("Horizontal");
        rb.velocity = new Vector2(moveDirection * moveSpeed, rb.velocity.y);

        if (moveDirection > 0)
        {
            transform.localScale = new Vector3(1f, 1f, 1f);
        }
        else if (moveDirection < 0)
        {
            transform.localScale = new Vector3(-1f, 1f, 1f);
        }
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space) && isGrounded)
        {
            rb.AddForce(new Vector2(0f, jumpForce), ForceMode2D.Impulse);
            isGrounded = false;
        }

        // 현재 위치
        Vector3 currentPosition = transform.position;

        // 경계를 벗어난 경우
        if (Mathf.Abs(currentPosition.x) > boundaryX || Mathf.Abs(currentPosition.y) > boundaryY)
        {
            // 원점으로 돌아가기
            transform.position = Vector3.zero;
        }
    }

    void OnCollisionEnter2D(Collision2D other)
    {
        if (other.gameObject.CompareTag("Ground"))
        {
            isGrounded = true;
        }  
    }

    void OnTriggerEnter2D(Collider2D other)
    {
        score += scoreValue;  // 점수 증가
        if(score >= 0){
        this.ST.GetComponent<Text>().text = "Score: " + score.ToString();
        }  // UI Text에 점수 반영
        Destroy(other.gameObject);  // 코인 아이템 제거
    }
}

 

코드를 보시면, 기존의 PlayerController에 BoundaryX 와 Y를 설정해 경계범위를 지정해 줍니다. 그리고 Update에서 현재 위치를 계속 받아오고, 절대값 계산을 통해 음수든 양수든 현 위치가 10이나 5보다 커지면 원점으로 되돌려주는 방식입니다. 여기서 Zero는 원점(0,0,0)을 의미합니다.

 

이렇게 해주면, 플레이어가 맵을 이탈해도 다시 원점으로 돌아오게 됩니다.

 

<Next>

이번에는 코인의 고정위치 생성 및 이동과 플레이어의 맵 이탈 방지를 위한 경계설정을 하였습니다. 다음에는 프리팹으로 생성하는 코인이 계속해서 생성되지만, 플레이어가 코인을 먹지 않으면 게임 상의 코인이 계속 남아있게 됩니다. 그래서 해당 오브젝트도 플레이어와 마찬가지로 일정범위를 넘어가면 없어지도록 하는 방법을 알아보겠습니다. 감사합니다.