오브젝트가 많을 때 오브젝트 간의 충돌을 검사 한다고 하면
모든 오브젝트를 검사하는데 비용이 매우 많이 들어감
그래서 전체 영역을 n개로 쪼개서
쪼개진 부분과 동일한 곳에 있는 오브젝트끼리만 검사를 하는 패턴
using UnityEngine;
public class KMS_SpacePartition : MonoBehaviour
{
public static KMS_Grid gridMap = new KMS_Grid();
GameObject obj = null;
private void Start()
{
obj = Instantiate(Resources.Load("Cube") as GameObject);
Camera.main.gameObject.transform.parent = obj.transform;
}
private void Update()
{
// 충돌 체크
if (Input.GetKeyDown(KeyCode.Space))
{
KMS_Unit unit = obj.GetComponent<KMS_Unit>();
gridMap.HandleMelee(unit.x, unit.y);
}
if( Input.GetKeyUp(KeyCode.Space))
{
gridMap.ResetColor();
}
// 방향키로 메인 Object 이동
if (Input.GetKey(KeyCode.LeftArrow))
{
var unit = obj.GetComponent<KMS_Unit>();
unit.Move(-(Time.deltaTime * 10.0f), 0);
}
else if (Input.GetKey(KeyCode.RightArrow))
{
var unit = obj.GetComponent<KMS_Unit>();
unit.Move((Time.deltaTime * 10.0f), 0);
}
if (Input.GetKey(KeyCode.DownArrow))
{
var unit = obj.GetComponent<KMS_Unit>();
unit.Move(0, -(Time.deltaTime * 10.0f));
}
else if (Input.GetKey(KeyCode.UpArrow))
{
var unit = obj.GetComponent<KMS_Unit>();
unit.Move(0, (Time.deltaTime * 10.0f));
}
}
}
물론 영역은 다르지만, 충돌 거리안으로 들어 오는 경우도 있을 듯, 해당 부분도 추가로 검사
크게 유닛과, 그리드로 나뉨
Unit.cs
using UnityEngine;
public class KMS_Unit : MonoBehaviour
{
private void Start()
{
x = transform.localPosition.x;
y = transform.localPosition.y;
prev = null;
next = null;
// 생성이 완료 되면 gridMap에 넣는게 가장 중요
KMS_SpacePartition.gridMap.Add(this);
UpdateColor();
}
public float x, y;
public KMS_Unit prev;
public KMS_Unit next;
public Color findColor = new Color(255, 255, 255);
Renderer cubeRenderer = null;
Color myColor;
Color crashColor = new Color(0, 0, 255);
// 키 입력을 받으면 이동
public void Move(float _x, float _y)
{
if ((x + _x) >= KMS_Grid.NUM_CELL * KMS_Grid.CELL_SIZE)
return;
if ((x + _x) < 0)
return;
if ((y + _y) >= KMS_Grid.NUM_CELL * KMS_Grid.CELL_SIZE)
return;
if ((y + _y) < 0)
return;
KMS_SpacePartition.gridMap.Move(this, x + _x, y + _y);
transform.localPosition = new Vector3(x, y, transform.localPosition.z);
UpdateColor();
}
// 위치에 따른 컬러 표기
public void UpdateColor()
{
if (cubeRenderer == null)
cubeRenderer = gameObject.GetComponentInChildren<Renderer>();
if (cubeRenderer != null)
{
int cellX = (int)(x / KMS_Grid.CELL_SIZE);
int cellY = (int)(y / KMS_Grid.CELL_SIZE);
myColor = new Color(cellX / (float)KMS_Grid.CELL_SIZE,
cellY / (float)KMS_Grid.CELL_SIZE, 0);
cubeRenderer.material.SetColor("_Color", myColor);
}
}
// 검사 대상자에 따른 컬러 표기
public void UpdateFindColor()
{
if (cubeRenderer == null)
cubeRenderer = gameObject.GetComponentInChildren<Renderer>();
if (cubeRenderer != null)
{
cubeRenderer.material.SetColor("_Color", findColor);
}
}
}
Grid.cs
using UnityEngine;
public class KMS_Grid
{
public const int NUM_CELL = 4;
public const int CELL_SIZE = 2;
public const float ATTACK_DISTANCE = 1.5f;
// 모든 Unit은 Add를 통해 등록이 되어야 함
private KMS_Unit[,] cells = new KMS_Unit[NUM_CELL, NUM_CELL];
public void Add(KMS_Unit unit)
{
int cellX = (int)(unit.x / CELL_SIZE);
int cellY = (int)(unit.y / CELL_SIZE);
unit.prev = null;
unit.next = cells[cellX, cellY];
cells[cellX, cellY] = unit;
if (unit.next != null)
{
unit.next.prev = unit;
}
}
public void HandleMelee(float unitX, float unitY)
{
int cellX = (int)(unitX / CELL_SIZE);
int cellY = (int)(unitY / CELL_SIZE);
// 내 유닛이 있는 곳만 검색
HandleCell(cellX, cellY);
}
void HandleCell(int x, int y)
{
KMS_Unit unit = cells[x, y];
while (unit != null)
{
HandleUnit(unit, unit.next);
if (x > 0) HandleUnit(unit, cells[x - 1, y]);
if (y > 0) HandleUnit(unit, cells[x, y - 1]);
if (x > 0 && y > 0) HandleUnit(unit, cells[x - 1, y - 1]);
if (x > 0 && y < NUM_CELL - 1) HandleUnit(unit, cells[x - 1, y + 1]);
if (x < NUM_CELL - 1) HandleUnit(unit, cells[x + 1, y]);
if (y < NUM_CELL - 1) HandleUnit(unit, cells[x, y + 1]);
if (x < NUM_CELL - 1 && y < NUM_CELL - 1) HandleUnit(unit, cells[x + 1, y + 1]);
if (x < NUM_CELL - 1 && y > 0) HandleUnit(unit, cells[x + 1, y - 1]);
unit = unit.next;
}
}
void HandleAttack(KMS_Unit attackUnit, KMS_Unit defenceUnit)
{
if (Vector3.Distance(attackUnit.transform.localPosition, defenceUnit.transform.localPosition) <= ATTACK_DISTANCE)
{
// 충돌 성공 시 로그 출력
Debug.Log(defenceUnit.gameObject.name + "&" + attackUnit.gameObject.name + " Hit");
}
}
public void Move(KMS_Unit unit, float x, float y)
{
// 위치 정보를 업데이트
int oldCellX = (int)(unit.x / CELL_SIZE);
int oldCellY = (int)(unit.y / CELL_SIZE);
int cellX = (int)(x / CELL_SIZE);
int cellY = (int)(y / CELL_SIZE);
unit.x = x;
unit.y = y;
// 여기까지가 위치 정보 수정이고
// 여기서 부터는 검색 cell 위치 수정
if (oldCellX == cellX && oldCellY == cellY)
{
return;
}
if (unit.prev != null)
{
unit.prev.next = unit.next;
}
if (unit.next != null)
{
unit.next.prev = unit.prev;
}
if (cells[oldCellX, oldCellY] == unit)
{
cells[oldCellX, oldCellY] = unit.next;
}
Add(unit);
Debug.Log(string.Format("Current Cell Pos : {0}, {1}", cellX, cellY));
}
public void HandleUnit(KMS_Unit unit, KMS_Unit other)
{
while (other != null)
{
if (Distance(unit, other) < ATTACK_DISTANCE)
{
HandleAttack(unit, other);
}
other = other.next;
}
}
float Distance(KMS_Unit unit, KMS_Unit other)
{
// 거리 계산, 대상 오브젝트들은 컬러를 변경해서 눈에 잘 보이게
unit.UpdateFindColor();
other.UpdateFindColor();
return Vector3.Distance(unit.transform.localPosition, other.transform.localPosition);
}
public void ResetColor()
{
for (int x = 0; x < NUM_CELL; ++x)
{
for (int y = 0; y < NUM_CELL; ++y)
{
KMS_Unit unit = cells[x, y];
while (unit != null )
{
unit.UpdateColor();
unit = unit.next;
}
}
}
}
}
'프로그래밍 > 디자인패턴' 카테고리의 다른 글
Singleton Pattern_싱글톤_유니티(Unity)에서 사용가능한 5가지 싱글톤_유니티(C#)로 사용해 보는 디자인 패턴 (0) | 2020.06.06 |
---|---|
유니티(C#)로 사용해 보는 디자인 패턴_ProtoType Pattern_프로토타입 (0) | 2020.05.30 |
유니티(C#)로 사용해 보는 디자인 패턴_Observer Pattern_관찰자 패턴 (2) | 2020.05.24 |
유니티(C#)로 사용해 보는 디자인 패턴_Flyweight Pattern_경량 패턴 (0) | 2020.05.24 |
유니티(C#)로 사용해 보는 디자인 패턴_Command Pattern_커맨드 패턴 (0) | 2020.02.23 |