Chapter 1. 컴포넌트 패턴(Component Pattern)
카테고리: Design Pattern
인프런에 있는 이재환님의 강의 게임 디자인 패턴 with Unity 를 듣고 정리한 필기입니다. 😀
Chapter 1. Component Pattern
🔔 상속 Vs. 컴포넌트
상속 : A is B
- 장점
- 부모 클래스로부터 물려받은 부분은 다시 쓸 필요가 없기 때문에 재사용 면에 있어서 효율적이다.
- 단점
- 부모, 조상 클래스가 될 공통된 최소 필수 집합을 개발자가 미리 예상하기가 힘들다.
- 즉, 완벽하게 순수한 Base Class를 만드는 것은 힘듬
- 자식 클래스가 필요로 하지 않는 기능도 물려줄 수 있는 등등 +, - 해야할게 너무 많다.
- 컴포넌트들끼리 커플링이 심하다.
- A 를 삭제하면 A 를 상속 받고 있던 B, C 에 치명적인 문제가 생긴다. A 의 부분을 상당수 재사용하고 있었는데 A 가 없어졌으니까!
- 부모, 조상 클래스가 될 공통된 최소 필수 집합을 개발자가 미리 예상하기가 힘들다.
- 오크는 몬스터다. Orc is a monster.
- Is-A 관계이므로 오크는 몬스터를 상속 받아 만들어야 함.
컴포넌트 : A has B
빈 컨테이너에 필요할 때마다 원하는 기능을 갖다 붙여 포함시키는 방식. 부품
- 각자의 기능을 가지고 있는 스스로 동작하는 독립적인 부품
- 애니메이션만 관련있는 컴포넌트, 물리 기능만 관련있는 컴포넌트, 이동 시키는 컴포넌트 등등 각자 기능을 담당하는 컴포넌트들이 독립적으로 구현이 되어 있다.
- 독립적인 클래스로 만들어 두고, 만들어 둔 것을 부품 붙이듯이 갖다 붙이며 됨.
- 컴포넌트를 뗀다고 해서 오브젝트의 다른 컴포넌트에 영향이 가는 것은 없음. 가장 큰 장점! 서로 영향 가는게 없다.
- 컴포넌트들끼리 독립적이며 커플링이 없다.
- 부품을 갈아끼면 그만.
- +, - 가 쉽다.
- 애니메이션만 관련있는 컴포넌트, 물리 기능만 관련있는 컴포넌트, 이동 시키는 컴포넌트 등등 각자 기능을 담당하는 컴포넌트들이 독립적으로 구현이 되어 있다.
- 코드의 의존성을 줄이고 재활용성을 높인다.
- 플레이어는 인벤토리 기능을 가진다. Player has a inventory.
- 인벤토리 기능은 컴포넌트로 구현하는 것이 좋다.
🔔 컴포넌트란
로직을 기능별로 컴포넌트화 하는 것. 기능들을 나누어 각각 독립적인 클래스로 분리.
- 한 개체가 여러 분야를 서로 커플링 없이 다룰 수 있게 해준다.
- 컴포넌트만 수정하면 되서 요구사항에 대한 대처가 빨라 유지보수시 편하다.
- 유니티는 컴포넌트 구조를 사용하고 있다.
- 캡슐화를 더 살려 준다.
🔔 코드
컴포넌트 패턴을 사용하지 않았을 때
- 한 코드 내에서
- 오브젝트를 90도 회전시키고
- 오브젝트를 이동시키고
- 0.5초 yield 기다리는
- 위와 같은 세가지 일련의 과정을 가진 코루틴 함수가 있다고 해보자.
- 한 코드 내에서 회전도 하고 이동도 하고 있지만
- 만약에 회전을 다하고나면 0.3초 기다리고 이동은 0.5초 기다리도록 수정해야 한다면 코드 상당수를 수정해야 한다.
📜SpaghetticAct.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
enum MOVE
{
MOVE_RIGHT,
MOVE_LEFT
}
public class SpaghettiAct : MonoBehaviour {
GameObject player1;
GameObject player2;
MOVE move = MOVE.MOVE_RIGHT;
void Start () {
player1 = GameObject.Find("Player1") as GameObject;
player2 = GameObject.Find("Player2") as GameObject;
StartCoroutine("MixedAct");
}
// 0.5초마다 객체들을 회전하면서 (글로벌 방향으로) 이동시키기
IEnumerator MixedAct()
{
while (true)
{
player1.transform.Rotate(90.0f * Vector3.up);
player2.transform.Rotate(90.0f * Vector3.up);
if (player1.transform.position.x < -4)
{
move = MOVE.MOVE_RIGHT;
}
else if (player1.transform.position.x > 4)
{
move = MOVE.MOVE_LEFT;
}
if (move == MOVE.MOVE_RIGHT)
{
player1.transform.Translate(1.0f * Vector3.right, Space.World);
player2.transform.Translate(-1.0f * Vector3.right, Space.World);
}
else
{
player1.transform.Translate(-1.0f * Vector3.right, Space.World);
player2.transform.Translate(1.0f * Vector3.right, Space.World);
}
yield return new WaitForSeconds(0.5f);
}
}
}
컴포넌트 패턴을 사용했을 때
기능 별로 나누어 클래스로 묶어 독립적으로 분리해놓고 필요할 대 가져다 쓴다.
- 이동과 회전 기능을 클래스로 따로 묶어 분리하였다.
- 📜Movs.cs : 이동에 관한 것만 묶어 작성. 0.3초 기다리는 코루틴 함수 有.
- 📜Rotate.cs : 회전에 관한 것만 묶어 작성. 0.5초 기다리는 코루틴 함수 有.
- 이제 유지보수시 한 컴포넌트에 대한 부분만 수정하면 되서 편해진다.
- 두 컴포넌트(C#스크립트)의 커플링이 없어졌기 때문이다.
📜Move.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
enum MOVE2
{
MOVE_RIGHT,
MOVE_LEFT
}
public class MoveAct : MonoBehaviour {
MOVE2 move = MOVE2.MOVE_RIGHT;
void Start () {
StartCoroutine("Move");
}
// 0.5초마다 (글로벌 방향으로) 이동시키기
IEnumerator Move()
{
while (true)
{
if (transform.position.x < -4)
{
move = MOVE2.MOVE_RIGHT;
}
else if (transform.position.x > 4)
{
move = MOVE2.MOVE_LEFT;
}
if (move == MOVE2.MOVE_RIGHT)
{
transform.Translate(1.0f * Vector3.right, Space.World);
}
else
{
transform.Translate(-1.0f * Vector3.right, Space.World);
}
yield return new WaitForSeconds(0.5f);
}
}
}
📜Rotate.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RotateAct : MonoBehaviour {
void Start () {
StartCoroutine("Rotate");
}
// 0.5초마다 객체 회전시키기
IEnumerator Rotate()
{
while (true)
{
transform.Rotate(90.0f * Vector3.up);
yield return new WaitForSeconds(0.3f);
}
}
}
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기