2018. 6. 18. 23:15 IT/Unity 게임개발
[게임 개발] 도시경영 전략 게임 로직 만들기 - 3 (건물 설치)
세번째, 메뉴를 통해서 건물 설치하는 부분까지.
아래 같은 것을 만드는 겁니다.
원래는 메뉴 / 건물 설치로 하려고 했는데, 메뉴는 별 내용이 없어서... 건물 설치 파트와 이전 버전에서 변경된 지점만 진행을 하겠습니다.
우선 이전의 InputController와 State 클래스입니다.
이전 포스팅의 InputController를 일반 / 설치 / 메뉴 상태로 나누어 변경된 지점입니다.
public class InputController : MonoBehaviour { Vector3 lastMousePos; TileController tileController; FacilityController facilityController; Dictionary<statetype,state> stateMap = new Dictionary<statetype, state>(); State state; // Use this for initialization void Start () { tileController = GameObject.Find("TileController").GetComponent<tilecontroller>(); facilityController = GameObject.Find("FacilityController").GetComponent<facilitycontroller>(); CreateState(); SetState(StateType.Normal); } void CreateState() { stateMap.Add(StateType.Normal, new NormalState(tileController)); stateMap.Add(StateType.Install, new InstallState(tileController,facilityController, this)); stateMap.Add(StateType.Menu, new MenuState()); } // Update is called once per frame void Update () { if (Input.GetMouseButton(1)) { state.UpdateDrag(lastMousePos); } lastMousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition); lastMousePos.z = 0; state.UpdateMove(lastMousePos); if (Input.GetMouseButtonDown(0)) { state.UpdateClick(); } } public void SetState(StateType _type) { if (state != null) { state.ExitState(); } state = stateMap[_type]; state.EnterState(); } }
State는 각각 미리 객체를 생성해두어 Dictionary로 객체 풀 관리를 합니다. (CreateState)
호출시에는 SetState를 통해 state 변경시 바꾸기 직전, 직후 해야할 일을 진행하도록 하였읍니다.
그리고 Update에 있던 기존의 코드는 모두 NormalState로 옮겨 state에 위임하도록 변경하였습니다. 솔직히 Drag,Click,Move 다 하나로 만들어도 될거 같긴한데.... 역할별로 작성하는게 더 용이하게 느껴지더군요.
(InputController는 가능하면 앞으로 건들지 않았으면 좋겠네요.)
다음은 State와 그 하위클래스들.
아래 하위 클래스 외에 Null처리를 해줄 NullState와 메뉴상태의 MenuState가 있는데 별도로 언급할 내용이 없습니다.
public enum StateType { Normal,Install,Menu } public abstract class State { public abstract void EnterState(); public abstract void ExitState(); public abstract void UpdateMove(Vector3 _pos); public abstract void UpdateClick(); public abstract void UpdateDrag(Vector3 _pos); protected Vector3 getTileCenterPosFromMouse(Vector3 _mousePos) { _mousePos.y = _mousePos.y * 2; int mPosX = Mathf.FloorToInt(_mousePos.x); int mPosY = Mathf.FloorToInt(_mousePos.y); // 홀짝 구분 / 노출되는 타일들의 중앙점은 합이 짝수다. int checkEven = (mPosX + mPosY) & 1; // 홀수라면 remainX에 곱하여 양수로 바꿔준다. int tempEven = (checkEven * -2) + 1; // 홀수라면 짝수로 기준센터 이동. mPosX += checkEven; float remainX = _mousePos.x - mPosX; float remainY = _mousePos.y - mPosY; // 소수점 이하의 수를 더하여, 1보다 크면 이동. float remainSum = (tempEven * remainX) + remainY; // 더한 값을 내림하여, 1.0 이상이면1로 만들어 최종 계산식에 사용. int floorSum = Mathf.FloorToInt(remainSum); // 더한 값이 1.0 이상이고, checkEven이 짝수라면 x+1,y+1 (floorSum = 1 , tempEven = 1) // 더한 값이 1.0 이상이고, checkEven이 홀수라면 x-1,y+1 (floorSum = 1 , tempEven = -1) // 더한 값이 1.0 이하라면, x,y (floorSum = 0) Vector3 result = new Vector3(mPosX + (floorSum * tempEven), (mPosY + floorSum) * 0.5f, -1); //Debug.Log("_mousePos = " +_mousePos + " mPosX = " + mPosX + " mPosY = " + mPosY + " checkEven = " + checkEven + " tempEven = " + tempEven + " remainX = " + remainX + " remainY = " + remainY + " remainSum = " + remainSum +" FloorSum = " + floorSum + " result = " + result); return result; } }
추상클래스인 State는
State변경 직후 필요한 부분을 작성하는 EnterState
State변경 직전 필요한 부분을 작성하는 ExitState
마우스 움직임에 대응하는 UpdateMove
마우스 왼쪽 클릭에 대응하는 UpdateClick
마우스 오른쪽 드래그에 대응하는 UpdateDrag
그리고 이전에 작성한 그대로인 getTileCenterPosFromMouse로 이루어져있습니다.
public class NormalState : State { TileController tileController; GameObject mousePointer; public NormalState(TileController _tile) { tileController = _tile; mousePointer = new GameObject("MousePointer"); mousePointer.AddComponent<SpriteRenderer>().sprite = SpriteManager.Instance.getFacilitySprite(FacilityType.Empty); Color facilColor = mousePointer.GetComponent<SpriteRenderer>().color; facilColor.a = 1.0f; mousePointer.GetComponent<SpriteRenderer>().color = facilColor; } public override void UpdateClick() { Vector3 _pos = mousePointer.transform.position; int _posX = Mathf.FloorToInt(_pos.x); int _posY = Mathf.FloorToInt(_pos.y*2); int _x = (_posY + _posX) >> 1; int _y = (_posY - _posX) >> 1; tileController.getTile(_x, _y).GetInformation(); } public override void UpdateDrag(Vector3 _pos) { Vector3 currentPos = currentPos = Camera.main.ScreenToWorldPoint(Input.mousePosition); currentPos.z = 0; Camera.main.transform.Translate(_pos - currentPos); } public override void UpdateMove(Vector3 _pos) { mousePointer.transform.position = getTileCenterPosFromMouse(_pos); } public override void EnterState() { mousePointer.SetActive(true); } public override void ExitState() { mousePointer.SetActive(false); } }
public class InstallState : State { Facility selectFacility; TileController tileController; FacilityController facilityController; InputController input; GameObject mousePointer; public InstallState(TileController _tile, FacilityController _facilityController, InputController _inputController) { tileController = _tile; selectFacility = new NullFacility(); facilityController = _facilityController; input = _inputController; mousePointer = new GameObject("buildPointer"); mousePointer.AddComponent<SpriteRenderer>().sprite = selectFacility.facilitySprite; Color facilColor = mousePointer.GetComponent<SpriteRenderer>().color; facilColor.a = 0.5f; mousePointer.GetComponent<SpriteRenderer>().color = facilColor; } public void SetSelectFacility(Facility _selectFacility) { selectFacility = _selectFacility; mousePointer.GetComponent<SpriteRenderer>().sprite = _selectFacility.facilitySprite; } public override void UpdateClick() { Vector3 _pos = mousePointer.transform.position; int _posX = Mathf.FloorToInt(_pos.x); int _posY = Mathf.FloorToInt(_pos.y * 2); int _x = (_posY + _posX) >> 1; int _y = (_posY - _posX) >> 1; bool isInstall = tileController.getTile(_x, _y).ChangeFacility(selectFacility); if (isInstall) { input.SetState(StateType.Normal); } } public override void UpdateDrag(Vector3 _pos) { Vector3 currentPos = currentPos = Camera.main.ScreenToWorldPoint(Input.mousePosition); currentPos.z = 0; Camera.main.transform.Translate(_pos - currentPos); } public override void UpdateMove(Vector3 _pos) { Vector3 movePos = getTileCenterPosFromMouse(_pos); movePos.y += 0.5f; mousePointer.transform.position = movePos; } public override void EnterState() { SetSelectFacility(facilityController.getSelectFacility()); mousePointer.SetActive(true); } public override void ExitState() { mousePointer.SetActive(false); } }
위에가 Normal상태, 아래가 Install 상태로,
각각 자신이 사용할 포인터( 건물모양, 하이라이트)를 생성하고, EnterState에서 활성화를 해주고, ExitState에서 비활성화를 해주고 있습니다.
Normal은 이전 포스팅에서 작성한걸 역할별로 나누어 작성되어있을 뿐입니다.
Install은 EnterState에서 FacilityController로부터 선택된 Facility를 받아오고, 마우스 포인터를 해당 건물의 모양으로 변경해주고 있습니다. (FacilityController는 설치할 건물 선택시 selectFacility로 선택된 건물을 저장하고 있습니다.)
그리고 빈 타일에 클릭시 타일에 선택된 건물을 생성해주고, Normal상태로 변경합니다.
(옮기고나니 중복 코드 구린내가 좀 보이는 것 같네요....흠..)
다음은 Facility와 FacilityController.
public abstract class Facility { public FacilityType type; public Sprite facilitySprite; GameObject facilityObj; public void CreateFacility(GameObject tileObj) { GameObject obj = new GameObject("facility"); obj.transform.SetParent(tileObj.transform); obj.AddComponent<SpriteRenderer>().sprite = facilitySprite; obj.transform.localPosition = new Vector3(0, 0.5f, -1); InteractWithFacility(); } public abstract bool isNull(); public abstract void InteractWithFacility(); public abstract void InteractWithCharacter(); } public class FacilityController : MonoBehaviour { Facility selectFacility; InputController inputController; void Start() { inputController = GameObject.Find("InputController").GetComponent<InputController>(); } public Facility getSelectFacility() { return selectFacility; } public void BuildButton(int _typeIndex) { //Test용 // 향후 DB 인덱스로 변경하여 DB와 연동하여 건물 데이터 얻어오도록 변경. (Type까지 DB로 관리) FacilityType _type = (FacilityType)_typeIndex; Facility build; if (_type == FacilityType.Room1) { build = new EtcFacility(_type); } else if (_type == FacilityType.Seller) { build = new SellFacility(_type); } else if (_type == FacilityType.Flower) { build = new DecoFacility(_type); } else if (_type == FacilityType.Playground) { build = new GymFacility(_type); } else { build = new NullFacility(); } selectFacility = build; inputController.SetState(StateType.Install); } }
Facility의 하위클래스들은 다음에 건물 기능과 동작 추가를 하면서 같이 하고, 이번에는 뺏습니다. 설명도 다음 포스팅에 같이 하겠습니다. 이번에는 설치까지만 됐으니까요.
CreateFacility는 타일 오브젝트 하위에 Facility 객체를 생성하는 공통 함수로, InstallState에서 타일의 건물 교체를 호출하면 타일을 통해 호출되는 함수입니다.
FacilityController는 Facility 생성과 관리에 관여하는 클래스로, Test를 위해서 BuildButton을 만드는데 일단은 FacilityController에 옮겨놨습니다. 실제 메뉴에서 설치할 건물을 클릭하면 BuildButton이 호출됩니다. 향후에 DB가 연동된다면,(기능 구현 뒤에 예정) 변경할 예정입니다.
다음엔 건물 기능/ 동작 추가 하고, 캐릭터 프로토타입까지일듯 하네요.
'IT > Unity 게임개발' 카테고리의 다른 글
[게임 개발] 도시경영 전략 게임 로직 만들기 - 5 (캐릭터 구현) (0) | 2018.06.28 |
---|---|
[게임 개발] 도시경영 전략 게임 로직 만들기 - 4 (건물 기능 / 캐릭터 프로토타입) (0) | 2018.06.23 |
[게임 개발] 도시경영 전략 게임 로직 만들기 - 2 (Isometric(마름모 타일) 구현) (9) | 2018.06.17 |
[게임 개발]도시경영 전략 게임 로직 만들기 - 1 (0) | 2018.06.15 |
[unity] 구글 플레이 연동 및 파이어 베이스 사용기 (환경 및 오류 위주) (1) | 2018.05.01 |