내일배움캠프 Node.js 사전캠프 16일차
1. ZEP에서 이루어진 팀단위 게임 개발 스터디
(1) 스터디 계획
이제 횡스크롤 슈팅 게임을 만들기 위한 준비를 모두 끝마쳤다. 더 다양한 기능을 추가하여 그럴 듯한 모양의 게임을 만들어 보자. 우리에게 필요한 기능은 다음과 같다.
1) 총을 어떻게 발사할 수 있을까?
2) 바닥이 사라지지 않고 계속 나올 순 없을까?
3) 장애물이 랜덤하게 생성되었으면 좋겠는데…!
1번과 2번은 팀장님의 가르침을 받아 해결할 수 있었고, 3번은 각자 해결 방법을 고민해보기로 했다.
(2) 스터디 진행 과정
1) 총을 어떻게 발사할 수 있을까?
public float currentAttackTime;
public float attackInterval = 0.1f;
public void InputMouse()
{
// 화면상에서 마우스가 입력된 곳의 좌표를 구한다.
var mousePosition = cam.ScreenToWorldPoint(Input.mousePosition);
mousePosition.z = 0;
mousePointer.position = mousePosition;
bool isAttackReady = currentAttackTime < Time.time;
if(Input.GetMouseButton(0) && isAttackReady)
{
currentAttackTime = Time.time + attackInterval;
TGameManager.InstanceTGameManager.BulletSpawn(transform.position, mousePosition);
}
}
public void BulletSpawn(Vector2 start, Vector2 end)
{
var direction = (end - start).normalized;
// 단위 벡터 구하기
// (마우스 포인터 위치-플레이어의 위치)
var angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
// 2D 벡터의 방향을 각도로 변환하는 과정
// Mathf.Atan2(y, x) * Mathf.Rad2Deg : 아크탄젠트를 이용해서 좌표를 각도로 변환한다.
var rotation = Quaternion.Euler(0, 0, angle);
Action<GameObject> action = (go) =>
{
go.GetComponent<Rigidbody2D>().velocity = direction * 20;
}; // 게임오브젝트를 인자로 가질 수 있는 action 함수
// 게임오브젝트의 속력을 단위 벡터에 20을 곱한 값으로 대체한다.
Spawn(bulletPrefab, start, rotation, action: action);
}
private void Spawn(
GameObject obj,
Vector2 pos = default,
Quaternion rot = default,
Transform parent = null,
Action<GameObject> action = null)
{
var prefab = Instantiate(obj, pos, rot, parent)
action?.Invoke(prefab);
// Bullet prefab의 초깃값을 지정하고 생성한다.
}
2) 바닥이 사라지지 않고 계속 나올 순 없을까?
유니티는 씬 뷰와 게임 뷰로 구성되어 있다. 제작자는 씬 뷰를 볼 수 있지만 실제 게임에서 보이는 화면은 게임 뷰에 존재한다. 바닥을 무한이 이어주는 것보단 이미 게임 뷰 밖으로 사라진 바닥을 다시 활용하는 게 효율적일 터.
public class TCamSensor : MonoBehaviour
{
private void OnTriggerExit2D(Collider2D other)
// 바닥 오브젝트가 카메라 콜라이더를 벗어날 때 실행된다.
{
if(other.CompareTag("Ground") == false) return;
other.GetComponent<TGround>().TransPosition(transform.position);
}
}
public float InitialPositionX;
public float nextPositionX;
private void Start()
{
InitialPositionX = transform.position.x;
}
public void TransPosition(Vector3 camPosition)
{
if (camPosition.x < transform.position.x) return;
var position = new Vector2(transform.position.x + nextPositionX, transform.position.y);
// 바닥의 포지션이 카메라 콜라이더의 위치보다 좌측이어야 실행
// 바닥은 nextPositionX만큼 더한 값을 이동한다.
transform.position = position;
}
3) 장애물 생성하기!
랜덤한 시간마다 장애물을 생성해보자
public float currentObstacleTime = 0f;
// 장애물 스폰 시간 랜덤하게 정하기
private void ObstacleSpawn()
{
if (currentObstacleTime > Time.time) return;
// 현재 시간(Time.time)이 장애물 스폰 시간보다 작으면 리턴
// 장애물 생성
TGameManager.InstanceTGameManager.SpawnObstacle(transform.position);
// 4초~7초 랜덤 주기로 다음 스폰 시간 설정
currentObstacleTime = Time.time + Random.Range(2f, 5f);
Debug.Log($"Next obstacle spawn time: {currentObstacleTime}");
}
장애물은 Ground 위 랜덤한 곳에 생성되어야 한다. 스폰 시간의 코드를 참고해서 randomPosition의 x 값에 UnityEngine.Random.Range(5f, 10f)
를 더해 주었다. 그리고 땅 위에 생성되어야 하기 때문에,y축에는 -playerPosition.y
를 해주었고, ground와 obstacle의 position과 두께를 고려해서 값을 조정해주었다.
public void SpawnObstacle(Vector3 playerPosition)
{
Vector3 randomPosition = new Vector3(UnityEngine.Random.Range(5f, 10f), -playerPosition.y - 2.37f + 1f +1f, 0);
Spawn(ObstaclePrefab, playerPosition + randomPosition);
}
private void Spawn(
GameObject obj,
Vector2 pos = default,
Quaternion rot = default,
Transform parent = null,
Action<GameObject> action = null)
{
var prefab = Instantiate(obj, pos, rot, parent);
action?.Invoke(prefab);
}
실행은 TPlayer 스크립트에서 이루어진다.
장애물이 랜덤하게 생성되고 있음을 콘솔창을 통해 확인할 수 있다. (장애물 위에서도 점프를 뛰어야 하기 때문에 일단은 Ground 태그랑 스크립트 달아주었다.)
(3) 개선해야할 점
이유를 모르겠는데 IsGround의 값이 간헐적으로 false가 되어 점프가 불가능해진다. 유니티 자체의 버그인 건지, 아니면 내 코드에 문제가 있는 건지 아직 확인하지 못한 상태다.
false가 되는 조건이 무엇인지도 아직 모르겠다. 총을 많이 쏠 때? 바닥에 쏠 때? 바닥에서 오래 달릴 때?
→ 코드를 열심히 살펴봤는데 OnTrigger 설정에서 Stay가 빠져 있던 게 문제가 되었던 것 같다. 아래처럼 추가해주었더니 문제 없이 작동되었다.
private void OnTriggerEnter2D(Collider2D other) // enter 상태일 때만(이벤트 발생) 진행되는 함수
{
SetGround(other, true); // 발이 닿으면 tplayer.IsGround = true
}
private void OnTriggerStay2D(Collider2D other)
{
SetGround(other, true); // 발이 닿으면 tplayer.IsGround = true
}
private void OnTriggerExit2D(Collider2D other) // exit되면 진행되는 함수
{
SetGround(other, false); // 발이 안 닿으면 tplayer.IsGround = false
}
다음 목표 : 보스를 잡아보자! 총알을 100번 맞히면 보스가 사라지고 게임이 클리어 화면으로 넘어가게끔 코드를 짜보려고 한다.
'개발일지 > TIL(Today I Learned)' 카테고리의 다른 글
2024-10-28 (1) | 2024.10.28 |
---|---|
24-10-25 (2) | 2024.10.25 |
24-10-23 (1) | 2024.10.23 |
24-10-22 (1) | 2024.10.22 |
24-10-21 (1) | 2024.10.21 |
댓글