오늘은 팀 프로젝트 3일차. 팀원들과 다시 한번 Merge, 인터페이스 특강이 도중에 있었다.
https://velog.io/@amberjack/C으로-풀기-콜라츠-추측
오늘의 교훈은 문제에서 기본으로 적혀있는 데이터 타입도 의심해보자 이다..
https://velog.io/@amberjack/인터페이스-활용
인터페이스의 활용에 대한 특강을 들었다. 코드의 중복을 줄이고 의존성을 줄일 수 있다고 한다.
예를 들어 Player와 Monster가 있다고 가정을 해보자. 이 때, Player와 Monster 모두 서로를 공격하거나 피격될 수 있는 게임을 만든다고 할 때, Player와 Monster 2개의 클래스에 Attack와 TakeDamage에 대한 함수를 만들어줘야 할 것이다.
이를 인터페이스를 활용하여 Character에 Attack과 TakeDamage를 만들어주고, 이 Character를 Player와 Monster가 상속 받도록 하면 코드의 중복을 피할 수 있다.
또한, NPC라는 클래스를 만들어 NPC도 공격, 피격 처리가 필요할 때, 단순히 Character를 상속받도록 하면 해결된다.
이와 같이 확장성도 높여줄 수 있기 때문에 알아놓을 필요가 있다.
오늘은 우리 팀 프로젝트의 스테이지에서 마우스 우클릭을 누른 채로 카메라를 움직이는 것을 구현했다. 좌우로 긴 스테이지를 가진 프로젝트이기 때문에 플레이어의 원활한 플레이를 위해 필요하다고 생각하여 추가를 하게 되었다.
public class DragCamera : MonoBehaviour
{
Vector2 clickPoint;
float dragSpeed = 30f;
// 카메라 X 좌표의 최대값, 최소값.
float limitMinX = -28f, limitMaxX = 24f;
// 카메라의 가로 길이의 반
float cameraHalfWidth;
// Start is called before the first frame update
void Start()
{
// 카메라의 가로 길이의 반
cameraHalfWidth = Camera.main.aspect * Camera.main.orthographicSize;
}
// 카메라의 처리는 보통 LateUpdate()에서 이루어진다.
void LateUpdate()
{
// Input.GetMouseButtonDown(1) : 마우스 우클릭을 누르는 순간
// 마우스 우클릭을 누르는 순간의 마우스 좌표값을 clickPoint 에 저장.
if (Input.GetMouseButtonDown(1)) clickPoint = Input.mousePosition;
// Input.GetMouseButton(1) : 마우스 우클릭을 누르는 동안
if (Input.GetMouseButton(1))
{
// LateUpdate가 갱신되기 전까지 마우스가 움직인 위치
Vector3 position = Camera.main.ScreenToViewportPoint((Vector2)Input.mousePosition - clickPoint);
// 카메라의 이동 속도
Vector3 move = position * (Time.deltaTime * dragSpeed);
float y = transform.position.y;
// Translate()를 통해 GameObject를 원하는 방향으로 움직일 수 있다.
// 이 경우, GameObject는 카메라가 된다.
transform.Translate(move);
// 카메라의 위치 변경하기.
// Mathf.Clamp()를 통해 카메라의 X좌표의 최대값, 최소값을 지정함.
transform.transform.position = new Vector3(
Mathf.Clamp(transform.position.x, limitMinX + cameraHalfWidth, limitMaxX - cameraHalfWidth),
y, transform.position.z);
}
}
}
GameObject를 DontDestroyOnLoad 처리를 할 경우, 씬을 로드해도 사라지지 않는다. 때문에 새 씬이 로드되어도 Start()가 실행되지 않는다. 따라서 Start()와 같이 씬이 로드될 때 처리를 해야할 경우, OnSceneLoaded를 사용하면 비슷한 효과를 얻을 수 있다.
private void OnEnable()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
void OnDisable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if (PlayerPrefs.HasKey("StageCleared"))
{
stageCleared = PlayerPrefs.GetInt("StageCleared");
}
else stageCleared = 1;
}
위의 코드의 경우, PlayerPrefs에 저장되어 있는 StageCleared를 가져오기 위해 사용한 코드로, 씬이 로드될 때 OnSceneLoaded가 호출되어 stageCleared에 값을 할당해줄 수 있다.
GameObject는 SetActive를 통해 활성화, 비활성화를 시켜줄 수 있다. 하지만 이미 활성화 혹은 비활성화 되어 있는 데 다시 한번 같은 작업을 진행할 필요가 없기 때문에, GameObject의 활성화 상태를 확인해줄 필요가 있다. 이 때 GameObject.activeSelf를 통해 확인할 수 있다.
public void OnClickUpBtn()
{
if (stage >= 5)
return;
if (downButton.activeSelf == false) downButton.SetActive(true);
stage++;
stageTxt.text = stage.ToString();
if (stage >= 5)
upButton.SetActive(false);
}
위의 코드는 엘리베이터의 Up 버튼의 입력 처리를 위한 함수이다. 스테이지는 1 ~ 5층으로 나뉘어져 있으며, 1층에서는 Down 버튼이, 5층에서는 Up 버튼이 비활성화 시켜야 한다고 가정해보자.
이 때, 1층에서 2층으로 엘리베이터가 올라오면 비활성화되어 있던 Down 버튼이 다시 활성화가 되어야 한다. 이를 위한 처리로 Up 버튼이 클릭되었을 경우, downbutton.activeSelf == false
라면 1층에서 2층으로 올라온 것으로 간주, downButton을 활성화 시켜준다.
이와 같이 activeSelf를 통해 현재 GameObject의 활성화 여부를 확인할 수 있다.
오늘은 다행히도 무난하게 흘러간 것 같다. 다시 한번 팀원들끼리 Merge를 했는데, 별 다른 문제 없이 Merge가 되었고, 내가 시험삼아 씬들을 병합하여 동작 여부를 확인해보았는데, 현재 구현한 기능들은 모두 동작하는 것 같아 정말 다행이다. 이제 BGM 추가와 Player Unit들의 엘리베이터 공격, 스테이지 승리 및 패배 구현, 인 게임 내 UI에 Unit 소환 재화 및 제한 시간을 구현하는 것만 하면 우리가 목표로 한 것은 모두 완성할 수 있을 것 같다.