유니티6_Adapter 패턴과 Singleton 패턴

avatar
2025.04.21
·
6 min read

  1. 인터페이스의 응용과 어댑터 구현하기

    • 탱크의 상호작용에 반응하는 게임 오브젝트를 구성한다.

    • 탱크는 포탄을 쏴서 부딪힌 게임오브젝트가 데미지를 받을 수 있는 있다면 데미지를 가한다.

    • 몬스터 : 데미지를 받을 수 있으며 피격시 포탄의 공격력만큼 체력을 차감한다.

    • 스위치 : 데미지를 받지 않지만 포탄을 맞으면 문을 여닫을 수 있도록 행동을 수행한다.. 단, 스위치는 인터페이스를 포함하지 않으며, 스위치의 코드에서는 여닫기에 대한 기능만을 수행한다. 이를 어댑터를 통해서 포탄의 피격에 여닫기 기능을 수행하도록 구성한다.

public interface IDamagable
{
    public void TakeDamaged(int damage);
}

먼저 데미지를 받는 인터페이스를 만들자.

public class Monster : MonoBehaviour, IDamagable

public void TakeDamaged(int damage)
{
    hp -= damage;
}

저번에 만든 몬스터에 인터페이스를 상속시키고 TakeDamaged를 구현한다.

IDamagable damagable = collision.gameObject.GetComponent<IDamagable>();
if (damagable != null)
{
    damagable.TakeDamaged(bulletDamage);
}

bullet의 OnCollisionEnter에 위와 같은 코드를 추가해서 충돌 대상이 IDamagable를 상속한다면 TakeDamaged를 호출하도록 구현한다.

5415

이제 어뎁터 패턴을 활용해 문이 동작하도록 만들어보자.

어댑터 패턴은 이벤트를 이용해 구현해보고자 한다.

5416
public void InteractDoor()
{
    if(gameObject.activeSelf)
    {
        SetDoorOpen();
    }
    else
    {
        SetDoorClose();
    }
}
void SetDoorOpen()
{
    gameObject.SetActive(false);
}
void SetDoorClose()
{
    gameObject.SetActive(true);
}

InteractDoor를 호출할 때 활성화 상태라면 문이 닫혀있는 것이기에 SetDoorOpen을 호출하여 비활성화 시켜 열고 비활성화 상태에서 호출하게 되면 SetDoorClose를 호출해 활성화시켜 닫게된다.

public class SwitchAdapter : MonoBehaviour, IDamagable
{
    [SerializeField] UnityEvent unityEvent;

    public void TakeDamaged(int damage)
    {
        unityEvent.Invoke();
    }
}

스위치에 적용할 어댑터이다.

유니티 이벤트를 사용하고 TakeDamaged가 할당된 이벤트를 Invoke하게 만들었다.

5417

도어와 스위치에 각각 Door와 SwitchAdapter 스크립트를 넣어주고 SwitchAdapter의 이벤트에는 Door의 InteractDoor를 넣어준다.

5418

아주 잘 동작한다.

  1. 싱글톤 구현하기

    • 게임에는 GameManager 싱글톤 게임오브젝트가 있다

    • 싱글톤 게임오브젝트는 최대점수를 가지고 있으며, 게임 시작시 0점 부터 시작한다.

    • 몬스터가 탱크 방향으로 쫓아오며 탱크는 몬스터를 잡는 경우 스코어를 1점 올린다.

    • 탱크가 몬스터에 닿는 경우 게임오버가 되며 'R' 키로 재시작 할 수 있다.

    • 재시작시에도 최대점수는 손실되지 않으며 게임 중 가장 높은 점수를 보관하고 있는다.

public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }

    [SerializeField] public int score;

    void Awake()
    {
        if(Instance == null)
        {
            Instance = this;
        }
        else
        {
            Destroy(gameObject);
        }
    }

    void Update()
    {
        if(Input.GetKeyDown(KeyCode.R))
        {
            ReStartGame();
        }
    }

    void ReStartGame()
    {
        SceneManager.LoadScene(0);
    }
}

먼저 GameManager에 대한 코드를 작성하자.

싱글톤으로 만들것이기에 Awake에서 GameManager 인스턴스를 생성하고 만약 이미 하나의 인스턴스가 있다면 나머지는 파괴되도록 만들었다.

그리고 R키를 누르면 GameManager에서 재시작도 하도록 만들었다.

if (hp <= 0)
{
    Destroy(gameObject);
    GameManager.Instance.score += 1;
}

그리고 몬스터가 사망시 GameManager.Instance의 score를 +1 하도록 수정했다.

5419

아주 잘 적용되었다.

이제 재시작을 해보자.

5420

?

점수가 날아가버렸다.

뭐가 문제인가 하고 찾아보니 재시작을 씬을 다시 로드하는 것으로 했는데 GameManager의 인스턴스도 함께 사라지고 다시 생성되어 날아간 것이다.

if(Instance == null)
{
    Instance = this;
    DontDestroyOnLoad(gameObject);
}

생성 후 씬을 로드해도 파괴가 안되도록 코드를 수정하자.

5421

계속 재시작을 해도 유지되는 걸 볼 수 있다.