
인게임 내에서 플레이 시간과 물까지 남은 거리를 표시할 수 있도록 스크립트를 작성하고 이를 각각의 UI에 컴포넌트로 넣어 필드를 인스펙터로 연결했다.
public class HeightCalculate : MonoBehaviour
{
[SerializeField] private TextMeshProUGUI heightText;
[SerializeField] private Transform playerTrans;
[SerializeField] private Transform waterTrans;
private void Update()
{
float height = playerTrans.position.y - waterTrans.position.y;
heightText.text = $"Height : {height:00.0}m";
}
}
TextMeshPro를 사용할 경우 TextMeshProUGUI 클래스로 인스펙터에서 연결하는 것이 가능하다. Update()에서 할당한 변수 height.text로 매 프레임 변동되는 높이가 출력되도록 만들었다.
플레이어가 물에 닿아 게임오버가 될 경우 화면이 Fade-Out되면서 게임오버 UI가 출력되도록 구현했다.
public class FadeOutPanel : MonoBehaviour
{
[SerializeField] private Image fadeOutPanel;
[SerializeField] private TextMeshProUGUI gameOverText;
[SerializeField] private TextMeshProUGUI restartBtnText;
[SerializeField] private TextMeshProUGUI exitBtnText;
[SerializeField] private float fadeDuration = 1f;
private void Start()
{
gameObject.SetActive(false);
}
// 외부에서 호출이 가능하도록 public으로 설정
public void GameOver()
{
StartCoroutine(FadeIn());
}
// 화면이 암전되면서 GameOver UI가 출력되도록 코루틴으로 구현
private IEnumerator FadeIn()
{
Color fadeColor = fadeOutPanel.color;
fadeColor.a = 0;
fadeOutPanel.color = fadeColor;
Color textColor = gameOverText.color;
textColor.a = 0;
gameOverText.color = textColor;
Color btnColor = restartBtnText.color;
btnColor.a = 0;
restartBtnText.color = btnColor;
float timer = 0f;
// 배경이 비치도록 이미지의 알파값을 0.95로 설정
while (timer < fadeDuration * 0.95f)
{
timer += Time.deltaTime;
fadeColor.a = Mathf.Clamp01(timer / fadeDuration);
fadeOutPanel.color = fadeColor;
textColor.a = Mathf.Clamp01(timer * 0.95f / fadeDuration);
gameOverText.color = textColor;
btnColor.a = Mathf.Clamp01(timer * 0.95f / fadeDuration);
restartBtnText.color = btnColor;
exitBtnText.color = btnColor;
yield return null;
}
}
}
결승선을 통과하여 게임을 통과한 경우 SceneManager 을 통해 씬 전환을 할 수 있도록 하고, UI 요소를 ClearScene에 추가하였다.
public class SceneManager : MonoBehaviour
{
private static SceneManager instance;
public static SceneManager Inst
{
get
{
if (instance == null)
{
instance = FindObjectOfType<SceneManager>();
if (instance == null)
{
GameObject obj = new GameObject(nameof(SceneManager));
instance = obj.AddComponent<SceneManager>();
DontDestroyOnLoad(obj);
}
}
return instance;
}
}
private void Awake()
{
if (instance == null)
{
instance = this;
DontDestroyOnLoad(gameObject);
}
else if (instance != this)
{
Destroy(gameObject);
}
public void LoadScene(string sceneName)
{
UnityEngine.SceneManagement.SceneManager.LoadScene(sceneName);
}
public void LoadMainScene()
{
LoadScene("TestScene");
}
public void LoadIngameScene()
{
LoadScene("Ingame");
}
}
SceneManager는 싱글톤을 활용하여 구현했고, LoadScene 관련 메서드를 public으로 선언하여 전역에서 씬 전환이 가능하도록 했다.
위 아래 카메라 이동을 추가하니 너무 어지러워서 좌우 이동만 남긴 뒤, 카메라의 위치를 살짝 조정하여 플레이어 머리 뒤에 위치하도록 구현했다.
줌인, 줌아웃의 경우 z필드를 추가하여 마우스 휠로 조작될 수 있도록 소스코드 추가 예정.
public class CameraControllerLSW : MonoBehaviour
{
[SerializeField] Transform target; // 따라갈 플레이어
[SerializeField] Vector3 offset = new Vector3(0f, 2f, -5f); // 카메라 초기 위치
[SerializeField][Range(5, 20)] float rotateSpeed = 5f; // 마우스 회전 속도 초기값
[SerializeField][Range(5, 20)] float followSpeed = 10f; // 플레이어 따라가는 속도 초기값
private float curRotationX = 10f; // 현재 X축위치 초기화
private float curRotationY = 0f; // 현재 Y축위치 초기화
private void Update()
{
CameraMoveInput();
}
// 플레이어가 움직이고 나서 카메라로 확인해야 해서 LateUpdate에서 구현
private void LateUpdate()
{
CameraMove();
}
// 카메라 위치 입력
private void CameraMoveInput()
{
// 마우스 커서의 위치로 카메라 회전
float mouseMoveX = Input.GetAxis("Mouse X");
float mouseMoveY = Input.GetAxis("Mouse Y");
curRotationY += mouseMoveX * rotateSpeed;
curRotationX -= mouseMoveY * rotateSpeed;
}
// 카메라 움직임
private void CameraMove()
{
// 플레이어 위치 + 오프셋 (카메라 X, Y축)
// X축 이동 마우스 방향으로 카메라 쳐다보게
Quaternion rotation = Quaternion.Euler(0, curRotationY, 0);
Vector3 movePosition = target.position + rotation * offset;
// 부드럽게 이동 (Lerp 사용)
transform.position = Vector3.Lerp(transform.position, movePosition, followSpeed * Time.deltaTime);
// 항상 카메라 방향이 위에서 플레이어로
transform.LookAt(target.position + Vector3.up * 2f);
}
}