전략패턴이란?
게임을 제작할 때 , 무기에 따라서 캐릭터의 모션이나 사운드, 방식을 동시에 변경하는 게임을 만든다고 가정했을 때
전략패턴을 모를 경우에는 , 각 경우에 따라서 Switch 문을 이용해서 바꾸어 주는 방식으로 대부분 구현을 하는데
이런 방식은 작은 프로젝트에서는 사용하기 좋은 방식이나 스케일이 큰 프로젝트 안에서는 코드가 늘어나면서 실수로 빠지거나 작성하지 않아서 문제가 생기는 경우가 생길 수 있다.
출처 :
https://www.youtube.com/watch?v=63miHKtooo4&ab_channel=%EC%98%A4%EB%8A%98%EC%BD%94%EB%94%A9
(예시) [스크립트] 공통으로 사용하는 부모 클래스 GunWeapon
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//무기 클래스가 가지고 있는 공통 변수
public struct Data
{
public float delayTime;
public int maxBullet;
public string info;
public string soundEffect;
public GameObject bullet;
}
// 각 무기마다 스크립트를 만들어줄 것임
//상속해주기 위해서 abstract로 추상 클래스로 만들어줌
public abstract class GunWeapon : MonoBehaviour
{
public Data data;
bool shootAble = true;
float restTime = 0;
public abstract void InitSetting(UnityEngine.UI.Text text);
//abstract 에서는 구현이 불가능 하다. 그래서 virtual로 형식 변경해주어야함
public abstract void Using();
//총을 쏘는 방식은 구현방법이 동일 하기 때문에 부모에서 공통스크립트를 구현해준다.
public virtual void Using_virtural(Transform tip, TextMesh sound , Transform player)
{
if(Input.GetKey(KeyCode.Space) && shootAble)
{
var bull = Instantiate(data.bullet);
bull.transform.position = tip.position;
var effect = Instantiate(sound);
effect.transform.position = tip.position + new Vector3(0, 1f, 0);
effect.text = data.soundEffect;
shootAble = false;
data.maxBullet--;
}
if(shootAble == false)
{
restTime += Time.deltaTime;
if(restTime >= data.delayTime)
{
shootAble = true;
restTime = 0f;
}
}
}
}
(예시) [스크립트] MachineGun : GunWeapon
public class MachineGun : GunWeapon
{
public override void InitSetting(Text text)
{
data.delayTime = 0.1f;
data.maxBullet = 40;
data.info = "현재 무기 : 기관총";
data.soundEffect = "두두두두!";
data.bullet = Resources.Load<GameObject>("MachineGunBullet"); //돌격 소총 총알 이미지
}
public override void Using()
{
throw new System.NotImplementedException();
}
public override void Using_virtural(Transform tip, TextMesh sound, Transform player)
{
base.Using_virtural(tip, sound, player);
player.position += new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0);
}
}
(예시) [스크립트] RifleGun: GunWeapon
//라이플건 관련 스크립트
public class RifleGun : GunWeapon
{
//Defalt Gun 과 동일하게 구현
public override void InitSetting(UnityEngine.UI.Text text)
{
data.delayTime =0.5f;
data.maxBullet = 20;
data.info = "현재 무기 : 돌격 소총";
data.soundEffect = "탕!";
data.bullet = Resources.Load<GameObject>("RifleBullet"); //돌격 소총 총알 이미지
}
public override void Using()
{
throw new System.NotImplementedException();
}
public override void Using_virtural(Transform tip, TextMesh sound, Transform player)
{
base.Using_virtural(tip, sound, player);
//기본 권총 일 때의 이동방식
player.transform.position += new Vector3(Input.GetAxis("Vertical"), 0, 0) * 10 * Time.deltaTime;
}
}
(예시) [스크립트] DeafaltGun: GunWeapon
//플레이어에 넣어두고 WeaponController에서 사용함
public class DeafaltGun : GunWeapon
{
//GunWeapon에서 만든 함수 클래스
public override void InitSetting(UnityEngine.UI.Text text)
{
data.delayTime = 1;
data.maxBullet =-1;
data.info = "현재 무기 : 기본 권총";
data.soundEffect ="빵야!";
data.bullet = Resources.Load<GameObject>("DefaultBullet");
}
public override void Using()
{
}
//총쏘기 구현
public override void Using_virtural(Transform tip, TextMesh sound , Transform player)
{
base.Using_virtural(tip, sound, player);
//기본 권총 일 때의 이동방식
player.transform.position += new Vector3(Input.GetAxis("Horiaontal"), 0, 0) * 10 * Time.deltaTime;
}
}
2. 영문버전, 한글버전 선택시, 텍스트으로 해당 텍스트로 변경하기
전략패턴을 이용해서 영문버전, 한글버전 으로 선택했을 때 텍스트가 변경되는 방식을 만들어 보려고한다.
전략패턴을 사용하지 않았을 때는 Switch문을 이용해서
버튼KR 일 경우는 한국어를
ENG일 경우에는 영문으로 나오도록 만들었다.
[스크립트] ChangeLanguage
public class ChangeLanguage : MonoBehaviour
{
public Text level_text;
public Text name_text;
public Text playTime_text;
public void OnClick_Switch_Language(string lg)
{
switch (lg)
{
case "KR":
level_text.text = "레벨 : ";
name_text.text = "이름 : ";
playTime_text.text = "플레이타임 : ";
break;
case "ENG":
level_text.text = "Level : ";
name_text.text = "Name : ";
playTime_text.text = "PlayTime : ";
break;
}
}
}
지금은 레벨, 이름, 플레이타임 뿐이지만 여기서 추가되는 내용들이 많이 생기면 작업하는데 보기 불편할 뿐 아니라 어떤부분은 수정했는데 어떤 부분은 수정되지 않은 부분이 생길 수 있다.
이제 이 스크립트를 전략패턴 형식으로 만들어보려고 한다.
공통으로 사용되는 레벨, 유저이름, 플레이 타임을 담은 클래스를 스크립트로 하나 만들어준다.
public struct DefaltUI_Data
{
public string level_text;
public string userName_text;
public string playeTime_text;
}
public abstract class ChangeText : MonoBehaviour
{
public DefaltUI_Data defalt_data;
public abstract void InitSetting();
public virtual void Using(Text level, Text name, Text pTime)
{
level.text = defalt_data.level_text;
name.text = defalt_data.userName_text;
pTime.text = defalt_data.playeTime_text;
}
}
그리고 ChangeText를 상속받은 Korean, English 스크립트를 따로 만들어주었다.
public class Korean : ChangeText
{
public override void InitSetting()
{
defalt_data.level_text = "레벨 : ";
defalt_data.userName_text= "이름 : ";
defalt_data.playeTime_text = "플레이타임 : ";
}
}
public class English : ChangeText
{
public override void InitSetting()
{
defalt_data.level_text = "Level : ";
defalt_data.userName_text = "Name : ";
defalt_data.playeTime_text = "PlayTime : ";
}
}
그리고 버튼을 눌렀을 때, 텍스트가 변경 될 수 있도록 할 수 있는 스크립트를 만들어 주었다.
버튼안의 string이 KR일 경우, 한국어를 ENG 일 경우, 영문으로 나오도록 하였다.
public ChangeText changeT;
public Text level_text;
public Text name_text;
public Text playTime_text;
public Text info;
private void Start()
{
changeT.InitSetting();
}
public void OnClick_Switch_Language(string lg)
{
if (lg.Equals("KR")) {
changeT = GetComponent<Korean>();
changeT.InitSetting();
changeT.Using(level_text, name_text, playTime_text);
}
else if (lg.Equals("ENG"))
{
changeT = GetComponent<English>();
changeT.InitSetting();
changeT.Using(level_text, name_text, playTime_text);
}
}
버튼을 눌렸을 경우, 캔버스 안에 들어가있는 ChangeT 속성이 변경되면서 UI가 변경되는 형식
위와 방식은 다르나 일단 동일하게 구현은 되는 것으로 확인됬다.
유튜브를 보면서 공부할때는 나름 이해되서 나만의 방식으로 새로 코드를 짜보았는데
어느정도 클래스를 다룰수 있는 상황이 아니면 생각을 많이하면서 코드를 짜야겠다는 생각을 했다..
잘못 짤 경우, 스크립트가 더 더러워 질 수도 있겠다는 생각을 했다.
위 코드도 간단하게 짜여서 이해가 되는거 같으나 많은 코드들이 들어가면 헷갈릴거 같음..
연습이 좀 더 필요하다고 생각했다.