본문 바로가기
Studies/게임 디자인 패턴

[게임 디자인 패턴] 방문자 패턴

by NCTP 2024. 9. 26.

// 유니티로 배우는 게임 디자인 패턴 제 2판 (데이비드 바론, 구진수) 에 대한 공부 노트입니다.

 

 

개요

  방문자 패턴은 객체 구조에 새로운 기능을 추가할 때, 객체 구조를 변경하기 않도록 기능을 확장할 수 있도록 해주는 디자인 패턴이다. 이 패턴은 기능들을 객체의 클래스들로부터 분리하여 별도의 방문자 객체에 캡슐화함으로써, 객체 구조를 유지하면서 다양한 기능들을 추가할 수 있도록 한다.

  

 

구성 요소

  • Element : Element는 방문자가 방문할 요소를 의미한다. Element는 주로 Accept라는 매서드를 통해 Visitor의 방문을 허락하고, 이 때 Visitor의 Visit 메서드가 호출된다. 
  • Visitor : Element를 방문하는 방문자 클래스를 의미한다. Element의 허가가 떨어지면 Element를 방문하여 변수값을 수정하거나, 다양한 기능을 수행할 수 있다. Element에 새로운 기능을 추가하고 싶을 때 Visitor에 새로운 기능들을 작성하고, Element에 방문하여 해당 기능들을 불러와 동작하면 된다.

 

장단점

장점

  • 새로운 기능의 추가가 용이함 : 새로운 기능이 필요할 때마다 요소 클래스를 수정할 필요 없이 새로운 방문자를 추가하거나, 방문자의 기능을 수정하면 된다.
  • 개방 / 폐쇄 : 객체지향 프로그래밍의 원칙인 개방/폐쇄 원칙을 따른다.

단점

  • 복잡성 증가 : 다른 디자인 패턴들보다 구조적으로 복잡하며, 객체 구조가 간단할 경우 오히려 복잡성을 증가시킬 수 있다. 
  • 객체 구조에 의존 : 새로운 Element 추가될 때, 모든 Visitor를 수정해야 한다.

 

구현하기

  게임 속의 캐릭터 "킬러" 를 강화하는 기능들을 방문자 패턴을 통해 구현해보자.

  Killer는 KillerController, KillerWeapon, KillerMovement 총 세 가지 스크립트로 구성되어 있다. 방문자 패턴을 통해 킬러의 공격력, 이동속도를 강화하는 파워업 기능을 추가해보자.

 IVisitor.cs, IElement.cs

  먼저, 방문자와 객체의 인터페이스를 작성한다.

// Visitor Interface
public interface IVisitor
{
    void Visit(KillerWeapon killerWeapon);
    void Visit(KillerMovement killerMovement);
}

// Element Interface
public interface IElement
{
    void Accept(IVisitor visitor);
}

 

 

Killer관련 스크립트

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class KillerController : MonoBehaviour, IElement
{
    private List<IElement> _killerElements = new List<IElement>();
    public void Accept(IVisitor visitor)
    {
        foreach (IElement element in _killerElements)
        {
            element.Accept(visitor);
        }
    }
    void Start()
    {
        _killerElements.Add(gameObject.AddComponent<KillerWeapon>());
        _killerElements.Add(gameObject.AddComponent<KillerMovement>());
    }

}

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class KillerWeapon : MonoBehaviour, IElement
{
    public float attackPower = 0;
    public float attackSpeed = 0;
    public void Accept(IVisitor visitor)
    {
        visitor.Visit(this);
    }

    void OnGUI()
    {
        GUI.color = Color.red;
        GUI.Label(new Rect(125, 40, 200, 20), "Weapon Power : " + attackPower);
        GUI.Label(new Rect(125, 60, 200, 20), "Weapon Speed: " + attackSpeed);
    }
}

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class KillerMovement : MonoBehaviour, IElement
{
    public float movementSpeed = 0;
    public void Accept(IVisitor visitor)
    {
        visitor.Visit(this);
    }
    void OnGUI()
    {
        GUI.color = Color.green;
        GUI.Label(new Rect(125, 20, 200, 20), "Movement Speed : " + movementSpeed);
    }
}
using System.Collections;
using System.Collections.Generic;
using System.IO.Enumeration;
using UnityEngine;


// 여러가지 파워 업 에셋을 만들기 위해 ScriptableObject를 사용한다.
[CreateAssetMenu(fileName = "PowerUp", menuName = "PowerUp")]
public class KillerPowerUp : ScriptableObject, IVisitor
{
    public string powerUpName;
    public GameObject powerUpPrefab;
    public string powerUpDescriptionl;

    [Range(0.0f, 100.0f)]
    [Tooltip("AttackPower 0 ~ 100")]
    public float attackPower;

    [Range(0.0f, 100.0f)]
    [Tooltip("AttackSpeed 0% ~ 100%")]
    public float attackSpeed;

    [Range(0.0f, 1.0f)]
    [Tooltip("Movement Speed 0 ~ 1")]
    public float movementSpeed;

    public void Visit(KillerWeapon killerWeapon)
    {
        killerWeapon.attackPower += attackPower;
        killerWeapon.attackSpeed += attackSpeed;
    }

    public void Visit(KillerMovement killerMovement)
    {
        killerMovement.movementSpeed += movementSpeed;
    }

}

 

 

구현 결과

결과

댓글