문제 요약
- 프로젝트 종류 : 2D 전략 게임
- 개발내용 : 전투 중 각 Soldier의 공격 방향에 맞게 스프라이트 모양 및 방향을 바꿔주는 것
- 문제 상황 : 스프라이트 모양, 방향을 바꾸는데 각각 IsFront / IsRight로 함수를 나누어서 코드를 짬.
이와 같이 코드를 짜니 디버깅하기 힘든 문제가 있었음.- IsFront() / IsRight() 결과를 둘다 확인해야 함
해결 방법
스프라이트 모양및방향 조합은 앞면오른쪽/앞면왼쪽/뒷면오른쪽/뒷면왼쪽으로 있다.
모양+방향이 원하는 결과이므로 enum을 사용하여 리팩토링했다.
IsFront(), IsRight() : bool => GetSpriteDirection : enum으로 변경
문제가 발생한 이유
객체 지향 프로그래밍은 프로그램 구현에 필요한 객체를 파악하고 각각의 객체들의 역할을 정의하며 서로의 관계를 맺는 프로그래밍이라고 할 수 있다. 이 때 객체를 파악하기 위해 객체를 아주 작은 단위로 쪼갤 수 있는데 나는 어디까지 나눠야할지 고민이었다. 나눌 수록 좋다고 생각해서 막 나눴더니 합쳐도 되는 부분까지 함수로 나눠서 구조가 복잡해지고 위와 같이 디버깅하기 힘든 상황이 발생했다. 때문에 이에 관해서 대표님과 이야기해보니 테스트 단위를 생각해서 코드를 짜면 도움이 된다고 했다.
대표님 같은 시니어 개발자들은 더 나누면 코드가 어지러워지겠다는 것을 직감적으로 알 수 있는데 나같은 신입은 의도적으로 테스트 단위를 생각하면서 코드를 짜야 코드가 어지워지는 것을 막을 수 있다고 했다. 즉, 테스트의 내용이 우리가 함수를 나눠야할 기준이라고 볼 수 있다.
(필자는 TDD의 개념을 참고자료 블로그만을 참고하여 자세한 정의는 적지 않겠다. 추후 책을 통해 TDD에 대한 이해도가 높아지면 해당 글을 수정하거나 관련 글을 추가하는 방식으로 내용을 보충할 예정이다.)
TDD 개념 정리
TDD의 개념은 애자일 개발 방식에 대한 이해를 기반으로 소프트웨어 엔트로피 (소프트웨어 내의 '무질서'를 뜻함. 요구사항의 변화에 따라 증가함)를 관리하기 위한 방식이다. TDD는 테스트를 '진화를 위한 발판'으로 여기며 이는 폭포수 개발 방식이 테스트를 '계약에 대한 검증'으로 여기는 것과 큰 차이가 있다.
TDD 핵심 요약
(와디즈 기술 블로그에서 로버트 C 마틴의 트윗 내용을 번역한 것이다.)
- 실패하는 테스트 없이 코드를 넣지 않기
- 실패가 생기면 테스트 작성을 멈추기
- 실패한 테스트를 넘기면 즉시 코딩 멈추기
- 리팩토링 이후 반복
- 한 사이클은 10~60초
테스트를 먼저 작성 후 테스트 오류에 맞게 코드를 적는 방식인데 이를 10~60초 안에 끝내야 한다.
이게 어떻게 되나 싶지만... 되니까 사람들이 써먹는거겠지..?
TDD 형식으로 개발하는 사람의 화면을 한번 보고 싶다. 그래야 좀 감이 잡힐거 같다.
참고자료
와디즈 기술 블로그 (TDD에 대한 자세한 설명이 있음)
Unity 테스트 프레임워크
빠른 사이클을 돌리기 위해서는 빠르게 테스트를 할 수 있는 도구가 필요하다.
Unity에는 테스트 도구로 테스트 프레임워크라는게 있다.
사전 지식
- 어셈블리 : Unity에서는 테스트를 테스트 어셈블리로 그룹화하고, 테스트가 실행되어야 하는 플랫폼을 참조하고 테스트하려는 프로젝트 어셈블리를 선택적으로 참조할 수 있도록 어셈블리 정의를 설정함
- NUnit : NUnit의 커스텀 버전을 사용하고 있기 때문에 사용하다 메소드 참조는 NUnit API를 찾아봐야 함
- TDD 및 유닛테스트 : AAA 패턴
- Arrange : 테스트를 위해 객체 초기화 / 데이터의 값을 set 함
- Act : Arrange 단계에서 준비된 파라미터를 가지고 실제 메소드 실행
- Assert : 메소드의 실행 결과가 유효한지 판단
- 편집 모드 테스트 : 게임 코드, 에디터 코드 모두 엑세스 가능
- 플레이 모드 테스트 : 타겟 플렛폼에 대해 테스트
환경설정
공식 문서의 시작하기 부분을 그대로 따라하면 아래와 같이 Test Runner에서 성공적으로 테스트한 모습을 볼 수 있다.
+ 사전 지식 : 어셈블리
테스트를 할 때 어셈블리가 필요하다는 점을 사전 지식에서 언급했다.
이는 시작하기를 따라하면 가장 먼저 생성되는 Test.asmdef와 관련된 내용으로 아래와 같이 처음에 생성된다.

https://docs.unity3d.com/Manual/cus-tests.html
위 문서를 참조해서 attribute 설정을 통해 테스트 설정을 할 수 있는거 같다.
테스트 코드 작성
현재 이동 방향에 따라 이동 방향 enum 값을 정하고자 한다.
[이동 방향 종류 : 이동 방향 부호]
- UPRIGHT : x > 0, y > 0
- UPLEFT : x < 0, y > 0
- DOWNRIGHT : x > 0, y < 0
- DOWNLEFT : x < 0, y < 0
- 이동방향을 결정한 클래스 생성
- 이동방향 부호에 따른 이동방향 enum 값 테스트 코드 작성
- 이동방향 enum 을 결정하는 코드 작성
- 테스트 코드 Run
코드
테스트 코드
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
public class DirectionScript
{
[Test]
public void SprietDirectionTest()
{
//Arrange
GameObject gameObject = new GameObject();
SpriteTest spriteTest = gameObject.AddComponent<SpriteTest>();
// 0 case
//Act
spriteTest.MoveSpeed = new Vector2(0, 1);
//Assert
Assert.AreEqual(spriteTest.SpriteDirection, Enums.SpriteDirection.NONE);
//Act
spriteTest.MoveSpeed = new Vector2(1, 0);
//Assert
Assert.AreEqual(spriteTest.SpriteDirection, Enums.SpriteDirection.NONE);
//Act
spriteTest.MoveSpeed = new Vector2(0, 0);
//Assert
Assert.AreEqual(spriteTest.SpriteDirection, Enums.SpriteDirection.NONE);
/* ------------------------------------------------------------------- */
//Act
spriteTest.MoveSpeed = new Vector2(1, 1);
//Assert
Assert.AreEqual(spriteTest.SpriteDirection, Enums.SpriteDirection.UPRIGHT);
//Act
spriteTest.MoveSpeed = new Vector2(-1, 1);
//Assert
Assert.AreEqual(spriteTest.SpriteDirection, Enums.SpriteDirection.UPLEFT);
//Act
spriteTest.MoveSpeed = new Vector2(1, -1);
//Assert
Assert.AreEqual(spriteTest.SpriteDirection, Enums.SpriteDirection.DOWNRIGHT);
//Act
spriteTest.MoveSpeed = new Vector2(-1, -1);
//Assert
Assert.AreEqual(spriteTest.SpriteDirection, Enums.SpriteDirection.DOWNLEFT);
}
}
SpriteTest.cs (테스트 대상)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpriteTest : MonoBehaviour
{
...
private void SetSpriteDirection(Vector2 moveSpeed)
{
if (moveSpeed.x > 0 && moveSpeed.y > 0)
{
spriteDirection = Enums.SpriteDirection.UPRIGHT;
}
else if (moveSpeed.x < 0 && moveSpeed.y > 0)
{
spriteDirection = Enums.SpriteDirection.UPLEFT;
}
else if (moveSpeed.x > 0 && moveSpeed.y < 0)
{
spriteDirection = Enums.SpriteDirection.DOWNRIGHT;
}
else if (moveSpeed.x < 0 && moveSpeed.y < 0)
{
spriteDirection = Enums.SpriteDirection.DOWNLEFT;
}
else
{
spriteDirection = Enums.SpriteDirection.NONE;
}
text.text = spriteDirection.ToString();
}
...
}
시행 착오
1. 테스트 코드 인식이 안 됨
테스트 코드 상단에 [Test] 혹은 [UnityTest]를 붙여야 함
단, UnityTest인 경우 Update당 호출되어야 하기 때문에 반환형이 IEnumerator이어야 한다.
public class DirectionScript
{
[Test]
public void SprietDirectionTest()
...
}
2. 클래스 작성해도 생성한 클래스 인식 안 함
이 부분에서 어셈블리 관련한 지식이 필요한거 같은데 간단한 해결방법으로는 작성한 클래스가 있는 폴더에
마우스 우클릭 > Create > Assembly Definition 생성
3. Assembly Definition 생성 후 작성한 클래스에 TMPro 인식 안 됨
테스트 클래스가 아닌 클래스에서 TMPro 자체를 인식을 못 해서
위에서 생성한 Assembly Definition에 References를 추가했다.
이실직고 하자면.. TDD 방식으로 안 하고 먼저 개발하고 테스트 코드 작성하는 방식으로 했다.
처음에는 테스트 코드에 클래스 적고 새 클래스 만들면서 했는데
코드 적다가 방향 결정하는 클래스를 다 적어버렸다 ㅎㅎ..
리팩토링 하는 과정도 있어야 하는데 하나도 안 지켰다..ㅎㅎ..
글 적을 때 의식해야 하는데.. 다음에는 의식하면서 코드 작성해야 겠다.
[위 클래스 결과물]
위에 테스트 작성 후에 플레이 모드 테스트로 Runtime에서도 잘 돌아가는지 테스트 했어야 했는데.. ㅎㅎ
플레이모드 테스트 하면
- IEnumerator로 변경
- Runtime에서 MoveSpeed 가져오고 Enum 비교?
이렇게 해야하지 않을까 싶다.
앞으로 적용 방향
Unity에서 Unit Test를 위한 Unity Test Framework라는 것이 있다. 사용환경만 설정해 보고 실무에서 사용해 본 적은 없다. 현재 Unity를 사용하며 업무를 하고 있으니 TDD를 제대로 하고 싶으면 위 프레임워크를 사용하는게 맞을거 같다. 하지만 현재 Unity로만 작업하는 프로젝트는 마감이 얼마 안 남아서 TDD를 적용하기에는 무리가 있다. 이후 다른 프로젝트는 순수 Unity로만 작업하는 것이 아니라서 위 프레임워크를 적용하기에는 어려움이 있다.
따라서 프레임워크를 사용하기에는 무리가 있어 대표님이 추천해주신 방법인 '주석'을 활용해보고자 한다. (와디즈 테크 블로그 : 어떻게 시작할까요?에 관련 내용 있음) 주석을 통해 기능별 내용을 적고 주석 내용에 맞는 코드를 적는 형태로 하면 내가 무슨 코드를 짜고 있었는지 까먹는 현상을 덜 할 거 같다.
하지만 주석 적으면서 내용 까먹으면 Fail.. 개발할 기능에 대해 구체적으로 적으려면 실생활 물체를 코드화하는 연습이 필요할거 같다. 게임은 실제 객체를 게임 세계로 가져오는 것이기에 실제 물체에 대한 상식이 반드시 있어야 코드가 복잡해지지 않기 때문이다. (연습 아장장)
'개발 > 게임' 카테고리의 다른 글
Unity:: 안드로이드 빌드 오류 해결방안 (0) | 2024.12.08 |
---|---|
UI:: World to Screen Position 주의할 점 (UI Scaling) (0) | 2024.10.17 |
Unity:: ScrollRect의 개념 & 동적 정렬 (0) | 2023.08.06 |
Unity:: Layout Group & Layout Element (0) | 2023.07.23 |
Unity:: UI 해상도 대응 (0) | 2023.07.16 |