Chapter 3. 전략 패턴(Strategy Pattern)

Date:     Updated:

카테고리:

태그:

인프런에 있는 이재환님의 강의 게임 디자인 패턴 with Unity 를 듣고 정리한 필기입니다. 😀

Chapter 3. Strategy Pattern

🔔 Strategy Pattern이란?

인터페이스 & 다형성과 관련있다.

  • 여러 알고리즘을 하나의 추상적인 접근점(인터페이스)을 만들어 접근점에서 알고리즘이 서로 교환 가능하도록 하는 패턴
    • 동일 목적 알고리즘의 선택 적용


인터페이스

  • 기능에 대한 선언
    • 구현과의 분리
      • 기능들을 구현하진 않고 프로토타입만 명시한다.
  • 여러가지 기능을 사용하기 위한 단일 통로
  • 예시
    • 워드 문서에서 프린터, 폰트 사용
      • ‘프린트’ 버튼만 누르면 어떤 종류의 프린터든간에 알아서 각자에 맞게 프린트함
      • 똑같은 문장을 써도 폰트에 따라 모양은 다르게 나타남
    • 정수 배열에 대해 사용하는 정렬 알고리즘
    • 게임 캐릭터의 무기 사용

인터페이스에 무기에 대한 추상적인 기능 이름들만 명시해주면 각 무기들은 이 인터페이스를 상속받아 각자 무기 특성에 맞게 알아서 기능들을 구현하면 된다.

또한 부모인 인터페이스 이름 하나로 한방에 그 아래 자식들을 관리할 수 있다. 다형성

인터페이스 클래스 구현 in C#

public interface 뫄뫄 {
    void func();
}
  • 클래스 이름 앞엔 대문자 I를 붙여주어야 한다.
  • 멤버 변수를 가질 수 없다.
  • 멤버 함수들은 전부 프로토타입만 명시해주며 구현하지 않는다.
    • 자식 클래스들은 인터페이스에 있는 모든 함수들은 무조건 다 오버라이딩 해야 한다.


🔔 예제 1 : 텍스트로 무기 교체

무기를 교체할때마다 콘솔창에 출력

구조

  • 📜IWeapon.cs 인터페이스
    • Shoot()
    • 📜Arrow.cs 상속
      • Shoot() 오버라이딩
    • 📜Bullet.cs 상속
      • Shoot() 오버라이딩
    • 📜Missile.cs 상속
      • Shoot() 오버라이딩
  • 📜MyWeapon.cs
    • private IWeapon weapon
    • SetWeapon 무기를 교체하고
    • weapon.Shoot(); 발사
      • 다형성
        IWeapon weapon = Arrow arrow;
        IWeapon weapon = Bullet bullet;
        IWeapon weapon = Missile missile;
        
        • 👉 📜MyWeapon 입장에선 여러 무기들에 대한 정보를 알지 못하지만 IWeapon타입으로 받은 무기는 각자 무기 타입에 따라 각자 오버라이딩 한대로 다른 내용의 Shoot()을 실행한다.
        • 👉 📜MyWeapon 입장에선 그저 Shoot()만 실행해도 각자 무기에 따라 알아서 다른 내용으로 실행되는 셈.
  • 📜WeaponManager.cs
    • 무기 교체


📜IWeapon.cs

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

public interface IWeapon{
	void Shoot(GameObject obj); 
}


📜Arrow.cs

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

public class Arrow : IWeapon {

	public void Shoot () {
		Debug.Log ("화살 공격");
	} 
}


📜Bullet.cs

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

public class Bullet : IWeapon {

	public void Shoot () {
		Debug.Log ("총알 공격");
	} 

}


📜Missile.cs

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

public class Missile : IWeapon {

	public void Shoot () {
		Debug.Log ("미사일 공격");
	} 

}


📜MyWeapon.cs

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

public class MyWeapon {

	// 접근점
	private IWeapon weapon;

	// 무기 교환 가능하게...
	public void setWeapon (IWeapon weapon) {
		this.weapon = weapon;
	}

	public void Shoot() {
		// Function Delegage
		weapon.Shoot();
	}
}


📜WeaponManager.cs

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

public class WeaponManager : MonoBehaviour {

	MyWeapon myweapon;

	void Start(){

		myweapon = new MyWeapon ();

		myweapon.setWeapon(new Bullet());
	}

	public void ChangeBullet() {
		myweapon.setWeapon(new Bullet());
	}

	public void ChangeMissile() {
		myweapon.setWeapon(new Missile());
	}

	public void ChangeArrow() {
		myweapon.setWeapon(new Arrow());
	}

	public void Fire() {
		myweapon.Shoot();
	}
}


🔔 예제 2 : 직접 무기 교체

image

image

  • 가운데 있는 오브젝트는 총알 or 화살 or 미사일을 쏘는 오브젝트인 BattleShip오브젝트다.
  • 총알 or 화살 or 미사일 버튼을 누르고 Fire 버튼을 누르면 발사한다.
    • 총알 or 화살 or 미사일 버튼을 누를때마다 BattleShip오브젝트에 붙어있는 C# 스크립트 컴포넌트가 바뀐다.
  • 총알 or 화살 or 미사일은 실시간으로 게임 도중 프리팹을 복사아여 오브젝트로 생성된다.
    • Instantiate
      • 오브젝트의 사본을 실시간으로 게임 도움에 생성한다.

구조

  • 📜IWeapon.cs 인터페이스
    • Shoot(GameObject obj)
  • Arrow 프리팹
    • 📜Arrow.cs 상속
      • Shoot(GameObject obj) 오버라이딩
    • 📜BulletMove.cs
      • 총알 이동
      • update 함수
  • Bullet 프리팹
    • 📜Bullet.cs 상속
      • Shoot(GameObject obj) 오버라이딩
    • 📜BulletMove.cs
      • 총알 이동
      • update 함수
  • Missile 프리팹
    • 📜Missile.cs 상속
      • Shoot(GameObject obj) 오버라이딩
    • 📜BulletMove.cs
      • 총알 이동
      • update 함수
  • BattleShip 오브젝트
    • 📜WeaponManager.cs
      • 무기 교체
      • 슬롯에 각각 Arrow 프리팹, Bullet 프리팹, Missile 프리팹 연결
    • 게임이 실행되면
      • 버튼에 따라 📜Arrow.cs, 📜Bullet.cs, 📜Missile.cs 중 하나씩 붙는다.
      • 게임 시작시엔 📜Bullet.cs가 기본적으로 붙어있다.

📜IWeapon.cs

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

public interface IWeapon{
	void Shoot(GameObject obj); 
}


📜Arrow.cs

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

public class Arrow : MonoBehaviour, IWeapon {

	void Start () {
	}
	
	public void Shoot(GameObject obj)
    {
		Vector3 initialPosition = new Vector3 (transform.position.x, transform.position.y, 0);
        GameObject bullet = Instantiate(obj) as GameObject;
        bullet.transform.position = initialPosition;
	} 
}
  • Arrow 프리팹에 붙는다.
  • 게임 실행 중 Arrow 버튼을 누르면 BattleShip 오브젝트에 붙는다.
  • public void Shoot(GameObject obj)
    • 인터페이스 오버라이딩
    • 인수로 들어온 프리팹 obj를 Instantiate 하여 GameObject 타입으로 생성한다.
      • obj는 날아갈 화살이 된다.
    • obj의 초기 위치를 BattleShip 오브젝트의 위치로 설정


📜Bullet.cs

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

public class Bullet : MonoBehaviour, IWeapon {

	void Start () {
	}
	
	public void Shoot(GameObject obj)
    {
		Vector3 initialPosition = new Vector3 (transform.position.x, transform.position.y, 0);
        GameObject bullet = Instantiate(obj) as GameObject;
        bullet.transform.position = initialPosition;
	} 

}

  • Bullet 프리팹에 붙는다.
  • 게임 실행 중 Bullet 버튼을 누르면 BattleShip 오브젝트에 붙는다.
  • public void Shoot(GameObject obj)
    • 인터페이스 오버라이딩
    • 인수로 들어온 프리팹 obj를 Instantiate 하여 GameObject 타입으로 생성한다.
      • obj는 날아갈 총알이 된다.
    • obj의 초기 위치를 BattleShip 오브젝트의 위치로 설정


📜Missile.cs

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

public class Missile : MonoBehaviour, IWeapon {

	void Start () {
	}
	
	public void Shoot(GameObject obj) {
		Vector3 initialPosition = new Vector3 (transform.position.x, transform.position.y, 0);
        GameObject bullet = Instantiate(obj) as GameObject;
        bullet.transform.position = initialPosition;
	} 

}
  • Missile 프리팹에 붙는다.
  • 게임 실행 중 Missile 버튼을 누르면 BattleShip 오브젝트에 붙는다.
  • public void Shoot(GameObject obj)
    • 인터페이스 오버라이딩
    • 인수로 들어온 프리팹 obj를 Instantiate 하여 GameObject 타입으로 생성한다.
      • obj는 날아갈 미사일이 된다.
    • obj의 초기 위치를 BattleShip 오브젝트의 위치로 설정


📜BulletMove.cs

using UnityEngine;
using System.Collections;

public class BulletMove : MonoBehaviour 
{
	float Speed = 10.0f;

	void Update () 
	{
		this.transform.Translate(Vector3.up * Speed * Time.deltaTime);
		
		if (this.transform.position.y > 10.0f)
		{
			Destroy(this.gameObject);
		}
	}
}
  • Arrow, Bullet, Missile에 붙는다.
  • update 함수
    • 시간, 속도에 따라 총알을 계속해서 이동시킴
    • y 방향으로 위로 이동시킴 Vector3.up * Speed * Time.deltaTime
    • y방향으로 10을 벗어나면 스스로를 파괴


📜WeaponManager.cs

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

public enum WeaponType {
	Bullet,
	Missile,
	Arrow
}

public class WeaponManager : MonoBehaviour {

    public GameObject _arrow;
    public GameObject _bullet;
    public GameObject _missile;

    private GameObject myWeapon;

    // 접근점
    private IWeapon weapon;

	private void setWeaponType (WeaponType weaponType) {

		Component c = gameObject.GetComponent<IWeapon>() as Component;  // 현재 게임 오브젝트의 IWeapon 타입의 컴포넌트를 가져온다.

		if (c != null) {   
			Destroy(c);
		}

        switch (weaponType)
        {
            case WeaponType.Bullet:
                weapon = gameObject.AddComponent<Bullet>();
                myWeapon = _bullet;
                break;

            case WeaponType.Missile:
                weapon = gameObject.AddComponent<Missile>();
                myWeapon = _missile;
                break;

            case WeaponType.Arrow:
                weapon = gameObject.AddComponent<Arrow>();
                myWeapon = _arrow;
                break;

            default:
                weapon = gameObject.AddComponent<Bullet>();
                myWeapon = _bullet;
                break;
        }
    }

    void Start(){
		setWeaponType(WeaponType.Bullet);
	}

	public void ChangeBullet () {
		setWeaponType(WeaponType.Bullet);			
	}

	public void ChangeMissile () {
		setWeaponType(WeaponType.Missile);			
	}

	public void ChangeArrow () {
		setWeaponType(WeaponType.Arrow);			
	}

	public void Fire() {
		weapon.Shoot(myWeapon);
	}
}
  • BattleShip 오브젝트에 붙는다.
    • _arrow, _missile, bullet 에 각 프리팹을 드래그 앤 드롭하여 넣어준다.
  • private void setWeaponType (WeaponType weaponType)
    • Component c = gameObject.GetComponent<IWeapon>() as Component;
      • BattleShip 오브젝트에 현재 붙어있는 IWeapon 타입의 컴포넌트를 컴포넌트 타입으로 형변환하여 리턴한다. (최상위 부모타입인 Component로)
      • 현재 미사일 모드였었다면 미사일(📜Missile)이 리턴될 것이고 이를 컴포넌트로 추상화하여 형변환해 리턴
    • if (c != null) Destroy(c)
      • 게임을 처음 싲작한 경우엔 c 가 null로 나올테니 이땐 파괴 안해도 되지만
      • c가 null이 아니라는건 이미 BattleShip 오브젝트에 붙어있었던 무기가 있었다는거다. 원래 무기를 파괴해주기.
        • 미사일모드였었는데 화살 버튼을 눌렀다면 기존 BattleShip 오브젝트에 붙어있었던 미사일 컴포넌트를 실시간으로 삭제함
  • switch문
    • 인수로 총알, 화살, 미사일 enum을 받았을때 각각 처리
      1. weapon = gameObject.AddComponent<Arrow>();
      • BattleShip 오브젝트에 📜Arrow.cs 를 붙여준다.
      • 인터페이스이자 부모타입인 weapon으로 이를 참조하게 되었다. 2. myWeapon = _arrow;
      • 화살 프리팹을 myWeapon이 참조하게끔
  • 각 Change 무기 교체 함수들은 위 setWeaponType 함수를 호출한다.
  • Fire()
    • weapon.Shoot(myWeapon);
      • bullet의 종류가 화살이던 미사일이던 총알이던 간에 알 필요없이 그냥 weapon 하나로 퉁쳐서 무기에 따라 다른 내용으로 오버라이딩 된 Shoot 멤버 함수를 호출할 수 있다. 다형성



🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

Design Pattern 카테고리 내 다른 글 보러가기

댓글 남기기