5주차 숙제 게임개발
Run & Jump - 프로토타입 설명
1.마우스를 누르면 캐릭터 머리 위에 파워 게이지가 표시됨
2.마우스를 놓으면 놓은 방향으로 캐릭터가 빙글빙글 돌아가며 발판들을 넘어감
3.이미 한번 착지한 발판은 점수 x , 발판을 처음 밟을때만 점수 증가
오늘 구현 한것
1. 플레이어 캐릭터가 카메라 경계선으로 이동시 카메라 Zoom In/Out 기능 추가
2. 블럭 스폰 위치 규격화 하기
2-1. 4방향에서 랜덤하게 나오게 구현
구현 목표
1.블럭 사이에 있을때 점프로 가시 통과할 수 있게 구현
1-2. 블럭 끼리 겹치지 않게 구현 - BlockSpanwer에서 생성 로직 변경 필요
2.블럭에 빠지는 함정 만들기(함정 특징: 땅바닥인척 하지만 갑자기 내려감)
3.LV 수치에 따른 스테이지 변화 다르게 하기
4.옆으로 흐르는 백그라운드 만들기
5.Title Scene 만들기(마스크 트랜지션 사용해서 화면 옮겨보기)
1.카메라 Zoom 기능 추가
이유 : 게임을 플레이 해보는 도중 경계선에 있으면 캐릭터가 화면 밖에 있을때 조작이 잘 되고있는지 못하는 불편함을 느껴 추가하기로 하였다.
하지만 카메라가 오브젝트를 추적 할 수있는 메서드를 몰라 구글링 및 ChatGpt의 도움을 받아 추천 받았다.
카메라 안에 있는 특정 오브젝트를 찾는 방법은 2가지를 알았다.
1-1.첫번째는 Camera.WorldToViewportPoint' 메서드를 사용
해당 메서드는 월드 좌표를 뷰 포트 좌표로 변환해주는 역할을 한다.
ViewPort란 좌측하단(0,0) 에서 우측상단(1,1)을 가르키는 좌표계이다. 최소(-1,-1) 에서 최대 (2,2)까지 값을 가진다.
즉,카메라가 보는 영역인 ViewPort 좌표계에 Player 월드좌표를 변환 시켜서 사용 하는것이다.
public float originSize = 6.87f;
public float targetSize = 15f; // 목표로 하는 Orthographic Size
public float zoomSpeed = 5f; // 줌 인/아웃 속도
void ObjINCamera() //해당 메서드를 Update()에 포함 시킴
{
// 오브젝트의 월드 좌표를 뷰포트 좌표로 변환
Vector3 viewportPos = Camera.main.WorldToViewportPoint(transform.position);
// 뷰포트 좌표가 카메라의 시야에 있는지 확인
if (!(viewportPos.x >= 0 && viewportPos.x <= 1 && viewportPos.y >= 0 && viewportPos.y <= 1 && viewportPos.z > 0))
{
Debug.Log("오브젝트는 카메라의 뷰 밖에 있습니다.");
Camera.main.orthographicSize = Mathf.Lerp(Camera.main.orthographicSize, targetSize, Time.deltaTime * zoomSpeed);
}
else
{
Debug.Log("오브젝트는 카메라의 뷰 안에 있습니다.");
Camera.main.orthographicSize = Mathf.Lerp(Camera.main.orthographicSize, originSize, Time.deltaTime * zoomSpeed);
}
}
이런식으로 하면 Lerp 함수를 통하여 자연스럽게 화면이 Zoom기능이 구현된다.
1-2.GeometryUtility.TestPlanesAABB 메서드를 사용한 방법
GeometryUtility.CalculateFrustumPlanes 와 GeometryUtility.TestPlanesAABB 두 메서드를 사용하는 방법이다.
카메라의 View frustum(절두체)에 오브젝트의 바운딩 박스가 포함되는지 확인 하는 방법이다.
절두체는 카메라가 실질적으로 비추는 3D 공간이라 생각하면된다.
즉, 절두체에 오브젝트가 존재하는지 체크하여 Zoom 기능이 동작하는 코드이다.
using UnityEngine;
public class ObjectInCameraView : MonoBehaviour
{
public Camera mainCamera; // 사용할 카메라
void Update()
{
// 카메라의 뷰 프러스텀을 계산
Plane[] planes = GeometryUtility.CalculateFrustumPlanes(mainCamera);
// 오브젝트의 바운딩 박스를 얻음
Bounds objectBounds = GetComponent<Renderer>().bounds;
// 오브젝트의 바운딩 박스가 카메라의 뷰 프러스텀 안에 있는지 확인
if (GeometryUtility.TestPlanesAABB(planes, objectBounds))
{
Debug.Log("오브젝트는 카메라의 뷰 안에 있습니다.");
}
else
{
Debug.Log("오브젝트는 카메라의 뷰 밖에 있습니다.");
}
}
}
절두체는 컬링기법(그래픽 렌더링을 줄여 최적화하는 기법중 하나)을 사용할떄 주로 나타나는 이론이다.
잘 모르면 해당 글을 참고하자
우선, 나는 첫번째 방법으로 구현을 하였으나 문제가 있었다.
플레이어가 뷰포트 경계에 서있는경우 카메라 Zoom 기능이 계속 동작하여 불쾌함을 주었다.
해당 코드를 조건문을 View 포트 안/밖 여부만 확인하고 바로 동작하게 되는 로직을 짜서 해당 현상이 일어났던 것이다.
비동기로 Zoom 기능이 동작을 해야만 자연스럽게 움직일것같아 코드를 수정하였다.
public float originSize = 6.87f;
public float targetSize = 15f; // 목표로 하는 Orthographic Size
public float zoomSpeed = 5f; // 줌 인/아웃 속도
bool isZooming = false;
void ObjINCamera() //Update()함수안에 포함
{
// 오브젝트의 월드 좌표를 뷰포트 좌표로 변환
Vector3 viewportPos = Camera.main.WorldToViewportPoint(transform.position);
// 뷰포트 좌표가 카메라의 시야에 있는지 확인
if (!(viewportPos.x >= 0 && viewportPos.x <= 1 && viewportPos.y >= 0 && viewportPos.y <= 1 && viewportPos.z > 0))
{
//Camera.main.orthographicSize = Mathf.Lerp(Camera.main.orthographicSize, targetSize, Time.deltaTime * zoomSpeed);
if (!isZooming)
{
StartCoroutine(ZoomCamera(targetSize));
}
}
else
{
//Camera.main.orthographicSize = Mathf.Lerp(Camera.main.orthographicSize, originSize, Time.deltaTime * zoomSpeed);
if (!isZooming)
{
StartCoroutine(ZoomCamera(originSize));
}
}
}
// 코루틴을 사용하여 카메라의 줌을 부드럽게 변경
IEnumerator ZoomCamera(float targetZoom)
{
isZooming = true;
while (Mathf.Abs(Camera.main.orthographicSize - targetZoom) > 0.01f)
{
Camera.main.orthographicSize = Mathf.Lerp(Camera.main.orthographicSize, targetZoom, Time.deltaTime * zoomSpeed);
yield return null;
}
Camera.main.orthographicSize = targetZoom;
isZooming = false;
}
isZooming 이라는 bool 함수를 넣어서 Zoom 기능 사용중인지 체크 여부를 했다.
코루틴에서 isZooming을 true로 바꿔 ObjINCamera()의 if문이 참이여도 동작을 안하게 막아놓았다.
코루틴은 비동기로 해당 Camera Size 만큼 Zoom 동작을 하고 코루틴을 탈출하게 설계를 하였다.
해당 코드로 수정하니 카메라 Zoom 기능이 잘 동작 하였다.
2. 블럭 스폰 위치 규격화 하기
블럭 위치를 4방향에서 랜덤하게 나오게 구현하였다.
BlockSpanwer.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum defineNum
{
Blokc_End_posX = -17, //해당 x좌표에 닿으면 pivot이 닿으면 사라지는 좌표
}
public class BlockSpawner : MonoBehaviour
{
[SerializeField] List<Block> block_list;
[SerializeField] float blockSummonCoolTime = 2.0f;
[SerializeField] float startPosX = 17.0f; //생성 시작되는 위치 x좌표
[SerializeField] float startPosY_Min = -5.0f;
[SerializeField] float startPosY_Max = 2.0f;
Coroutine coroutineBlockSpanw;
[SerializeField] List<float> blockSpawnPosY;
int isSpawnBlockIndex = -1;
public void BlockSpawnerON()
{
if (coroutineBlockSpanw == null)
{
coroutineBlockSpanw = StartCoroutine(BlockSpanw());
}
else
{
StopCoroutine(coroutineBlockSpanw);
coroutineBlockSpanw = StartCoroutine(BlockSpanw());
}
}
public void BlockSpawnerOFF()
{
if (coroutineBlockSpanw != null)
{
StopCoroutine(coroutineBlockSpanw);
}
}
IEnumerator BlockSpanw()
{
while(true)
{
MakeBlock();
yield return new WaitForSeconds(blockSummonCoolTime);
}
}
private void MakeBlock()
{
int type = Random.Range(0, block_list.Count);
Block block = Instantiate(block_list[type]);
block.gameObject.SetActive(false);
float x = startPosX;
//y 범위 : -4.5 ,-1.5,1.5, 4.5
//한번 생겼던 위치에 다시 생기지 않는 함수
int y;
do
{
y = Random.Range(0, blockSpawnPosY.Count);
}
while (isSpawnBlockIndex == y);
isSpawnBlockIndex = y;
block.block_pos = new Vector3(x, blockSpawnPosY[y], 0);
block.gameObject.SetActive(true);
}
}
블럭 생성하는것을 코루틴으로 작성하여 비동기로 동작하게 하였으며 스테이지 LV 변화에 따라 생성속도를 빠르게 하기위해 blockSummonCoolTime 변수로 조절할 수 있게 했다.
또한 한번 생성한 자리에는 다시 생성하지 않게 로직을 만들어 변칙성을 주었다.
SerializeField(직렬화)를 통하여 Inspector에서 제어 할 수 있게 했다.
'내일배움캠프_Unity_6기 > TIL(Today I Learend)' 카테고리의 다른 글
TIL : 2024-08-29(목) (0) | 2024.08.29 |
---|---|
TIL : 2024-08-28(수) (0) | 2024.08.28 |
TIL : 2024-08-26(월) (0) | 2024.08.26 |
TIL : 2024-08-22(금) (0) | 2024.08.23 |
TIL :: 2024-08-22(목) (0) | 2024.08.22 |