1. 에러 해결
이번에는 재화를 만들어서 오브젝트를 만드는데 돈을 소비하도록 할 것이다.
그 전에 캐릭터가 시간이 지나면 멈추는 문제를 수정해야 할 것 같다. A* 큐에 너무 많은 패스요청이 쌓이는게 문제 같아서 디버그로 큐의 갯수를 한번 찍어 보았다.
아무래도 문제의 원인은 패스를 담는 큐를 하나로 공유하기 때문에 요청이 너무 많이 쌓여서 이런 문제가 발생하는 것 같았다. 좀비를 50마리로 줄여서도 실험해봤는데 50마리 일때는 큐의 갯수가 정상적으로 유지 되었다.
그러다 좀비를 클릭했는데 큐의 갯수가 갑자기 증가하였다. 아무래도 처리 못하는 큐 작업을 만날경우 Dequeue가 작동하지 않아서 뒤의 작업이 밀리는 것 같았다. 좀비를 1마리로 해서 실험해봤다.
이제 처리하지 못하는 큐 작업을 실행하지 못하도록 만들거나
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PathRequestManager : MonoBehaviour
{
Queue<PathRequest> pathRequestQueue = new Queue<PathRequest>();
PathRequest currentPathRequest;
PathFinding pathfinder;
bool isProcessingPath;
static PathRequestManager instance;
void Awake()
{
instance = this;
pathfinder = GetComponent<PathFinding>();
}
struct PathRequest
{
public Vector3 pathStart;
public Vector3 pathEnd;
public Action<Vector3[], bool> callback;
public PathRequest(Vector3 _pathStart, Vector3 _pathEnd, Action<Vector3[], bool> _callback)
{
pathStart = _pathStart;
pathEnd = _pathEnd;
callback = _callback;
}
}
public static void RequestPath(Vector3 pathStart, Vector3 pathEnd, Action<Vector3[], bool> callback)
{
PathRequest newRequest = new PathRequest(pathStart, pathEnd, callback);
instance.pathRequestQueue.Enqueue(newRequest);
Debug.Log(instance.pathRequestQueue.Count);
instance.TryProcessNext();
}
void TryProcessNext()
{
if (!isProcessingPath && pathRequestQueue.Count > 0)
{
currentPathRequest = pathRequestQueue.Dequeue();
isProcessingPath = true;
pathfinder.StartFindPath(currentPathRequest.pathStart, currentPathRequest.pathEnd);
}
}
public void FinishedProcessingPath(Vector3[] path, bool success)
{
currentPathRequest.callback(path, success);
isProcessingPath = false;
TryProcessNext();
}
}
코드를 확인해보니 Dequeue까지는 작동할 것 같고 isProcessingPath 변수가 false로 초기화되지 않아서 발생하는 문제 같아서 Debug를 찍어 보았다.
요컨데 문제는 PathFinding스크립트의 RetracePath에서 currentNode와 endNode가 같지 않아야 코드가 실행되는데 처음부터 같게 나오면 path가 null로 반환되는게 문제였다.
그래서 코드에 다음 조건을 추가해주었다.
if(currentNode == startNode)
{
path.Add(currentNode);
}
렉은 다소 나아졌으나 근본적으로 좀비가 많을때 큐에 작업이 쌓여서 캐릭터의 움직임이 늦어지는 문제는 해결되지 않았다. 100마리까진 어느정도 괜찮으나 200마리 부턴 렉이 심해진다.. 시간 상의 문제로 최적화는 나중에 하고 일단 게임 시스템을 마저 완성해야 겠다.
2. 게임 시스템
일단 간단하게 UI를 만들어 봤다.
이제 게임매니저에 필드에 골드 변수를 추가해서 건축물을 짓는데 골드를 소비하도록 할 것이다. 골드 값의 선언은 게임매니저의 Init()에 넣어주었다 나중에 난이도를 만든다면 난이도에 따라서 초기골드를 다르게 할 것이다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using static UnityEditor.PlayerSettings;
using static UnityEngine.GraphicsBuffer;
public class GameManager : MonoBehaviour
{
public enum ePlaceObject
{
Barricade, Turret, RepairRobot
}
[SerializeField]
private GameObject character;
[SerializeField]
private CameraCtrl cam;
[SerializeField]
private GameObject[] placeObject;
[SerializeField]
private Drag[] drag;
[SerializeField]
private AGrid grid;
[SerializeField]
private GameObject tile1;
[SerializeField]
private GameObject tile2;
[SerializeField]
private TMP_Text goldText;
[SerializeField]
private Transform[] spawnPoints;
public bool[,] tileMap = new bool[128, 128];
private Player player;
private List<Enemy> enemies = new List<Enemy>();
private Coroutine routine;
private int gold = 0;
private int zombieHp = 30;
private int zombieDamage = 5;
void Start()
{
Init();
drag[0].onBuild = () =>
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 50f))
{
Debug.Log(hit.point);
Vector3 buildPos = new Vector3(Mathf.Floor(hit.point.x) + 0.5f, hit.point.y, Mathf.Round(hit.point.z) - 0.3f);
int tile1PosX = (int)(buildPos.x + 0.5f);
int tile2PosX = (int)(buildPos.x - 0.5f);
int tilePosZ = (int)Mathf.Round(hit.point.z);
if (player != null)
{
if (tileMap[tile1PosX, tilePosZ] == true || tileMap[tile2PosX, tilePosZ] == true)
{
}
else
{
if(this.gold >= 5)
{
this.gold -= 5;
Instantiate(placeObject[0], buildPos, placeObject[0].transform.rotation);
SetFulled(tile1PosX, tilePosZ);
SetFulled(tile2PosX, tilePosZ);
}
else
{
}
}
}
}
tile1.SetActive(false);
tile2.SetActive(false);
};
drag[0].onBuildable = () =>
{
tile1.SetActive(true);
tile2.SetActive(true);
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 50f))
{
Vector3 buildPos = new Vector3(Mathf.Floor(hit.point.x) + 0.5f, hit.point.y, Mathf.Round(hit.point.z) - 0.3f);
tile1.transform.position = new Vector3((buildPos.x + 0.5f), -0.49f, Mathf.Round(hit.point.z));
tile2.transform.position = new Vector3((buildPos.x - 0.5f), -0.49f, Mathf.Round(hit.point.z));
if (tileMap[(int)tile1.transform.position.x, (int)tile1.transform.position.z] == false)
{
this.tile1.GetComponent<Renderer>().materials[0].color = Color.green;
}
else
{
this.tile1.GetComponent<Renderer>().materials[0].color = Color.red;
}
if (tileMap[(int)tile2.transform.position.x, (int)tile2.transform.position.z] == false)
{
this.tile2.GetComponent<Renderer>().materials[0].color = Color.green;
}
else
{
this.tile2.GetComponent<Renderer>().materials[0].color = Color.red;
}
}
};
drag[1].onBuild = () =>
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 50f))
{
Vector3 buildPos = new Vector3(Mathf.Floor(hit.point.x), hit.point.y, Mathf.Round(hit.point.z));
int tile1PosX = (int)(buildPos.x);
int tilePosZ = (int)Mathf.Round(hit.point.z);
if (player != null)
{
if (tileMap[tile1PosX, tilePosZ] == true)
{
}
else
{
if(this.gold >= 15)
{
this.gold -= 15;
Instantiate(placeObject[1], buildPos, placeObject[1].transform.rotation);
SetFulled(tile1PosX, tilePosZ);
}
else
{
}
}
}
}
tile1.SetActive(false);
};
drag[1].onBuildable = () =>
{
tile1.SetActive(true);
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 50f))
{
Vector3 buildPos = new Vector3(Mathf.Floor(hit.point.x), hit.point.y, Mathf.Round(hit.point.z));
tile1.transform.position = new Vector3((buildPos.x), -0.49f, Mathf.Round(hit.point.z));
if (tileMap[(int)tile1.transform.position.x, (int)tile1.transform.position.z] == false)
{
this.tile1.GetComponent<Renderer>().materials[0].color = Color.green;
}
else
{
this.tile1.GetComponent<Renderer>().materials[0].color = Color.red;
}
}
};
drag[2].onBuild = () =>
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 50f))
{
Debug.Log(hit.point);
Vector3 buildPos = new Vector3(Mathf.Floor(hit.point.x), hit.point.y, Mathf.Round(hit.point.z));
int tile1PosX = (int)(buildPos.x);
int tilePosZ = (int)Mathf.Round(hit.point.z);
if (player != null)
{
if (tileMap[tile1PosX, tilePosZ] == true)
{
}
else
{
if(gold >= 20)
{
this.gold -= 20;
Instantiate(placeObject[2], buildPos, placeObject[2].transform.rotation);
SetFulled(tile1PosX, tilePosZ);
}
else
{
}
}
}
}
tile1.SetActive(false);
};
drag[2].onBuildable = () =>
{
tile1.SetActive(true);
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 50f))
{
Vector3 buildPos = new Vector3(Mathf.Floor(hit.point.x), hit.point.y, Mathf.Round(hit.point.z));
tile1.transform.position = new Vector3((buildPos.x), -0.49f, Mathf.Round(hit.point.z));
if (tileMap[(int)tile1.transform.position.x, (int)tile1.transform.position.z] == false)
{
this.tile1.GetComponent<Renderer>().materials[0].color = Color.green;
}
else
{
this.tile1.GetComponent<Renderer>().materials[0].color = Color.red;
}
}
};
}
private void Update()
{
if (Input.GetMouseButtonDown(1))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if(Physics.Raycast(ray, out hit, 50f))
{
Vector3 buildPos = new Vector3(Mathf.Floor(hit.point.x) + 0.5f, hit.point.y, Mathf.Round(hit.point.z) - 0.3f);
if (player != null)
{
PathRequestManager.RequestPath(player.transform.position, hit.point, player.OnPathFound);
}
}
}
this.goldText.text = gold.ToString();
}
private void SetEmpty(int x, int y)
{
if (x >= 0 && x < 128 && y >= 0 && y < 128)
{
tileMap[x, y] = false;
}
}
private void SetFulled(int x, int y)
{
if (x >= 0 && x < 128 && y >= 0 && y < 128)
{
tileMap[x, y] = true;
}
}
private void Init()
{
tile1.SetActive(false);
tile2.SetActive(false);
for (int i = 0; i < 128; i++)
{
for (int j = 0; j < 128; j++)
{
if (i >= 0 && i < 128 && j >= 0 && j < 128)
{
tileMap[i, j] = true;
}
}
}
for (int i = 2; i < 126; i++)
{
for (int j = 2; j < 126; j++)
{
if (i >= 0 && i < 128 && j >= 0 && j < 128)
{
tileMap[i, j] = false;
}
}
}
GameObject ch = Instantiate(character);
ch.transform.position = new Vector3(32, 0, 32);
cam.Init(ch);
this.player = ch.GetComponent<Player>();
var loading = GameObject.FindObjectOfType<Loading>();
for(int i = 0; i < loading.GetZombies().Count; i++)
{
var zombie = loading.GetZombies()[i].GetComponent<Enemy>();
zombie.onDie = () =>
{
zombie.gameObject.SetActive(false);
};
enemies.Add(zombie);
}
StartCoroutine(CoCreateMonster());
this.gold = 100;
}
IEnumerator CoCreateMonster()
{
yield return new WaitForSeconds(2f);
while(true)
{
for (int i = 0; i < enemies.Count; i++)
{
int random = Random.Range(0, spawnPoints.Length);
if (enemies[i].gameObject.activeSelf == false)
{
enemies[i].gameObject.SetActive(true);
enemies[i].Init(zombieHp, zombieDamage);
enemies[i].gameObject.transform.position = new Vector3(spawnPoints[random].position.x, 0, spawnPoints[random].position.z);
break;
}
}
yield return new WaitForSeconds(0.2f);
}
}
}
그리고 좀비를 잡을 경우 골드를 획득한다.
그리고 카메라의 무브를 기존과 다르게 변경해주었다.
원래 캐릭터를 따라 다니던 카메라를 방향키나 마우스로 이동할 수 있게 만들고 마우스휠로 줌인 줌아웃이 가능하게 만들 것이다.
'3D 콘텐츠 제작' 카테고리의 다른 글
좀짓막(좀비 집짓고 막기) [#9] - 세부 조정 및 정리 (1) | 2023.10.11 |
---|---|
좀짓막(좀비 집짓고 막기) [#7] - 오브젝트 풀링 (0) | 2023.10.06 |
좀짓막(좀비 집짓고 막기) [#6] - 공격/이동/설치기능 완성 (0) | 2023.10.05 |
좀짓막(좀비 집짓고 막기) [#5] - 목표물 타게팅/설치 기능 만들기 (2) | 2023.10.04 |
좀짓막(좀비 집짓고 막기) [#4] - 길찾기 알고리즘 이동에 적용 (0) | 2023.09.27 |