게임 매니저가 맨 후순위로 와야한다
슬롯 생성
맵에 존재하는 모든 그리드들이 Slot.cs 기반이 될거다, 실제로는 임의의 위치에 동그라미 생성을 해야하기 때문에 2개의 이미지를 겹쳐놨다, 추가로 Slot.cs는 선 색상, 이어서 할 수 있는 동그라미 정보 등 Grid 관련 데이터를 들고 있을거다.
아래는 옵션
Slot.cs
using UnityEngine;
using UnityEngine.UI;
public class Slot : MonoBehaviour
{
RectTransform rect;
public Image img;
// 선 관련 정보
public Color color;
public Vector3 pipePos;
public int idx = 0;
private void Awake()
{
img = GetComponent<Image>();
rect = GetComponent<RectTransform>();
}
public void SetColor(Color color)
{
this.color = color;
img.color = color;
}
public void DeleteLineEndPoint()
{
if (IsLineEndPoint())
{
img.sprite = null;
img.color = Color.white;
}
}
public bool IsEmpty() => img.sprite == null;
public bool IsEndPoint() => rect.sizeDelta == new Vector2(150f, 150f);
//
public bool IsLineEndPoint() => rect.sizeDelta == new Vector2(100f, 100f);
public bool IsEndPoint(Color color) => img.color == color && img.sprite.name == "Knob";
public Sprite GetSprite() => img?.sprite;
public Vector3 GetSize() => rect.sizeDelta;
public PipeInfo GetPipeInfo() => new PipeInfo(color, pipePos, idx);
}
파이프 생성
파이프 매니저에서 모든 선에 대한 로직을 담당한다, 하나의 선이 이어져 있는걸 판별하기 위해선 예) 빨강색 원끼리 선으로 이어져 있다. Dictionary<Color,bool>을 만들어서 선이 동일한 색상의 원에 도달했을 때 이에 대해 true로만 바꿔주면 된다.
using System;
using System.Collections.Generic;
using UnityEngine;
using Color = UnityEngine.Color;
public struct PipeInfo : IEquatable<PipeInfo>
{
public Color color;
public Vector3 pos;
public int idx;
public PipeInfo(Color color, Vector3 pos, int idx)
{
this.color = color;
this.pos = pos;
this.idx = idx;
}
public PipeInfo(Slot slot) : this(slot.color, slot.pipePos, slot.idx)
{
}
public static bool operator ==(PipeInfo obj1, PipeInfo obj2)
{
if (ReferenceEquals(obj1, obj2))
return true;
if (ReferenceEquals(obj1, null) ||
ReferenceEquals(obj2, null))
return false;
return obj1.Equals(obj2);
}
public static bool operator !=(PipeInfo obj1, PipeInfo obj2) => !(obj1 == obj2);
public bool Equals(PipeInfo other)
{
if (ReferenceEquals(other, null))
return false;
if (ReferenceEquals(this, other))
return true;
return color.Equals(other.color) &&
pos.Equals(other.pos) &&
idx.Equals(other.idx);
}
}
public class PipeManager : SingletonLocal<PipeManager>
{
GridManager gridManager;
public Dictionary<Color, Pipe> dicLine = new Dictionary<Color, Pipe>();
public Dictionary<Vector3, Color> dicLinePos = new Dictionary<Vector3, Color>();
// 시작지점 위치랑 비교용
public Dictionary<Color, Vector3> dicStartPos = new Dictionary<Color, Vector3>();
public Pipe pipe = null;
public Vector3 curPipePos;
public bool canMove = false;
private void Awake()
{
gridManager = GridManager.instance;
}
// Update is called once per frame
void Update()
{
CheckIfCompleted();
//if (Global.IsNullOrEmpty(mArrPoints))
// return;
//for (int i = 0; i < mArrPoints.Length; i++)
// mLineRenderer.SetPosition(i, mArrPoints[i].position);
}
void UpdatePipeLocations()
{
// 위치 갱신
dicLinePos.Clear();
foreach (var line in dicLine)
{
var lineRenderer = line.Value.lineRenderer;
for (int i = 0; i < lineRenderer.positionCount; i++)
{
if (!dicLinePos.ContainsKey(lineRenderer.GetPosition(i)))
dicLinePos.Add(lineRenderer.GetPosition(i), line.Key);
}
}
}
public void InitPipe(PipeInfo info)
{
// 비어있을 시 초기화 해줄 필요 없음
if (info == new PipeInfo())
return;
curPipePos = info.pos;
// 파이프가 존재함
if (dicLine.ContainsKey(info.color))
DestroyPipe(info.color);
CheckForOtherPipe(info.color);
Pipe tmpPipe = Instantiate(pipe, transform);
var lineRenderer = tmpPipe.lineRenderer;
lineRenderer.startColor = info.color;
lineRenderer.endColor = info.color;
dicLine.Add(info.color, tmpPipe);
dicStartPos.Add(info.color, info.pos);
lineRenderer.positionCount++;
lineRenderer.SetPosition(tmpPipe.idx, info.pos);
}
public void DestroyPipe(Color color)
{
// 지점 초기화
var dicCompleted = GameManager.instance.dicCompleted;
if (dicCompleted.ContainsKey(color))
dicCompleted[color] = false;
// 색상 라인 오브젝트 제거
gridManager.listSlot[dicLine[color].slotIdx].DeleteLineEndPoint();
DestroyImmediate(dicLine[color].gameObject);
dicLine.Remove(color);
dicStartPos.Remove(color);
UpdatePipeLocations();
}
public void AddPos(PipeInfo info)
{
if (!canMove)
return;
// 파이프가 존재함
CheckIfDiagonalMove(info);
dicLine.TryGetValue(info.color, out Pipe pipe);
if (!pipe)
return;
// 직선 확인
curPipePos = info.pos;
CheckForOtherPipe(info.color);
// 다른 선이 존재하는지 확인 (예 ) 시작지점 클릭 후 다른 시작 지점에서 클릭)
var lineRenderer = pipe.lineRenderer;
var lineCount = lineRenderer.positionCount;
if (lineRenderer.GetPosition((lineCount > 0) ? lineCount - 1 : 0) == info.pos)
return;
if (!dicLinePos.ContainsKey(info.pos))
dicLinePos.Add(info.pos, info.color);
lineRenderer.positionCount++;
lineRenderer.SetPosition(++dicLine[info.color].idx, info.pos);
pipe.slotIdx = info.idx;
canMove = true;
}
// 다른 색상의 선이 존재하는지 확인
void CheckForOtherPipe(Color color)
{
if (dicLinePos.ContainsKey(curPipePos))
{
var colorToCheck = dicLinePos[curPipePos];
if (dicLine.ContainsKey(colorToCheck) &&
colorToCheck != color)
DestroyPipe(colorToCheck);
}
}
// 대각선에 다른 선이 있는지 확인
void CheckForOtherPipe(Vector3 linePos)
{
if (dicLinePos.ContainsKey(linePos))
DestroyPipe(dicLinePos[linePos]);
}
// 대각선마다 체크
public void CheckIfDiagonalMove(PipeInfo info)
{
var slotList = gridManager.listSlot;
int idx = info.idx;
var size = GameManager.instance.size;
float y = curPipePos.y, x = curPipePos.x, val = 0.86f;
Vector3 upLeft = new Vector3(x - val, y + val);
Vector3 upRight = new Vector3(x + val, y + val);
Vector3 downLeft = new Vector3(x - val, y - val);
Vector3 downRight = new Vector3(x + val, y - val);
Pipe pipe = null;
dicLine.TryGetValue(info.color, out pipe);
if (!pipe)
return;
var lineRenderer = pipe.lineRenderer;
// 좌상 또는 좌하
if (info.pos == upLeft ||
info.pos == downLeft)
{
if (idx + 1 < size)
{
var nextSlot = slotList[idx + 1];
var nextPipePos = nextSlot.pipePos;
CheckForOtherPipe(nextPipePos);
lineRenderer.positionCount++;
lineRenderer.SetPosition(++dicLine[info.color].idx, nextPipePos);
}
}
// 우상 또는 우하
else if (info.pos == upRight ||
info.pos == downRight)
{
if (idx - 1 >= 0)
{
var nextSlot = slotList[idx - 1];
var nextPipePos = nextSlot.pipePos;
CheckForOtherPipe(nextPipePos);
lineRenderer.positionCount++;
lineRenderer.SetPosition(++dicLine[info.color].idx, nextPipePos);
}
}
}
// 두 점이 이어졌는지 확인
void CheckIfCompleted()
{
foreach (var item in dicLine)
{
var lineRenderer = item.Value.lineRenderer;
var startPos = lineRenderer.GetPosition(0);
var endPos = lineRenderer.GetPosition((lineRenderer.positionCount > 0) ? lineRenderer.positionCount - 1 : 9);
if (startPos == endPos)
continue;
var pointSlot = gridManager.dicEntryPointSlot;
pointSlot.TryGetValue(startPos, out Slot startSlot);
pointSlot.TryGetValue(endPos, out Slot endSlot);
if (startSlot &&
endSlot)
GameManager.instance.dicCompleted[item.Key] = startSlot.IsEndPoint(item.Key) && endSlot.IsEndPoint(item.Key);
}
}
// 시작 또는 도착 지점인지 확인
bool IsEntryPoint(PipeInfo info)
{
return false;
}
// 도착 지점이 현재 색상하고 맞는지 체크
public bool OtherPointExists(Slot curSlot, Slot nextSlot)
{
return false;
}
public bool CanDeletePoint(Color color, Vector3 pos)
{
if (dicLine.ContainsKey(color))
{
var lineRenderer = dicLine[color].lineRenderer;
var lineCount = lineRenderer.positionCount;
Vector3 lastPos = lineRenderer.GetPosition(lineCount - 1);
for (int i = 0; i < lineCount; i++)
{
if (pos == lineRenderer.GetPosition(i))
{
lineRenderer.positionCount = i + 1;
dicLine[color].idx = i;
UpdatePipeLocations();
return true;
}
}
}
return false;
}
public bool IsStartPoint(Color color, Vector3 size)
{
if (dicLine.ContainsKey(color))
{
var lineRenderer = dicLine[color].lineRenderer;
if (lineRenderer.positionCount == 2 &&
size == new Vector3(150f, 150f))
{
DestroyPipe(color);
return true;
}
}
return false;
}
// 비활성화 함수
public void Reset()
{
foreach (var line in dicLine)
DestroyImmediate(line.Value.gameObject);
//line.Value.gameObject.SetActive(false);
dicLine.Clear();
dicLinePos.Clear();
dicStartPos.Clear();
}
}
마우스로 드래그 할 때 선을 생성 해줘야하므로 게임매니저가 이를 관리한다
드래그 시작할 땐 원이 있는 슬롯인지를 레이캐스트로 판별한 후 파이프를 초기화 시킨다.
public void OnBeginDrag(PointerEventData eventData)
{
mCurSlot = TryToGetSlot(eventData);
Slot hoveredSlot = UI_Manager.instance.CheckForHoveredItem();
// 시작 지점일때만 초기화
if (mCurSlot &&
!mCurSlot.IsLineEndPoint())
{
var inst = PipeManager.instance;
inst.InitPipe(new PipeInfo(mCurSlot.color, mCurSlot.pipePos, mCurSlot.idx));
inst.canMove = true;
}
}
Slot TryToGetSlot(PointerEventData eventData)
{
List<RaycastResult> listRayRes = new List<RaycastResult>();
if (eventData.button == PointerEventData.InputButton.Left)
{
EventSystem.current.RaycastAll(eventData, listRayRes);
foreach (var item in listRayRes)
{
var slot = item.gameObject.GetComponent<Slot>();
if (slot &&
slot.IsEndPoint(slot.color))
return slot;
}
}
return null;
}
public void OnDrag(PointerEventData eventData) => CheckIfDragging(eventData, UI_Manager.instance.CheckForHoveredItem());
public void OnPointerUp(PointerEventData eventData) => ResetPipe();
public void OnEndDrag(PointerEventData eventData) => ResetPipe();
드래그 중일 땐 파이프를 연장할 지 없앨 지를 판별한다
void CheckIfDragging(PointerEventData eventData, Slot nextSlot)
{
var pipeInst = PipeManager.instance;
if (nextSlot &&
mCurSlot)
{
if(nextSlot != mCurSlot)
{
var curColor = mCurSlot.color;
// 이동 가능한 칸이면 즉시 이동
if (!nextSlot.GetSprite())
{
if (!pipeInst.canMove)
return;
UI_Manager.instance.gameUI.UpdateMoveTxt();
// 다른 선일 때 이동 가능
if (nextSlot.IsEndPoint(curColor) &&
nextSlot.IsLineEndPoint())
pipeInst.canMove = true;
// 유효하면 끝부분에 작은 동그라미 생성
if (pipeInst.canMove &&
!dicCompleted[curColor])
{
if (!pipeInst.CanDeletePoint(mCurSlot.color, nextSlot.pipePos))
pipeInst.AddPos(new PipeInfo(curColor, nextSlot.pipePos, nextSlot.idx));
if (!pipeInst.canMove)
return;
mCurSlot.DeleteLineEndPoint();
nextSlot.SetColor(curColor);
nextSlot.img.sprite = GridManager.instance.knobSprite;
mCurSlot = nextSlot;
}
else
{
}
}
// 다른 색상의 도착 지점인지 확인
else
{
if (!nextSlot.IsEndPoint(curColor))
{
pipeInst.canMove = false;
if (pipeInst.dicLine.ContainsKey(nextSlot.color))
{
pipeInst.DestroyPipe(nextSlot.color);
pipeInst.canMove = true;
}
}
else
{
// 원위치 시키면 제거
if (pipeInst.dicStartPos[curColor] == nextSlot.pipePos)
pipeInst.DestroyPipe(curColor);
// 시작지점이 아닐 때만 끝부분 설정
if (!dicCompleted[curColor] &&
!pipeInst.IsStartPoint(curColor, nextSlot.GetSize()))
{
pipeInst.AddPos(new PipeInfo(mCurSlot.color, nextSlot.pipePos, nextSlot.idx));
mCurSlot.DeleteLineEndPoint();
mCurSlot = nextSlot;
}
else
pipeInst.canMove = false;
}
}
}
else
{
pipeInst.canMove = true;
}
}
}
파이프를 초기화 시키는 함
void ResetPipe()
{
if (mCurSlot)
{
dicCompleted[mCurSlot.color] = false;
mCurSlot = null;
PipeManager.instance.curPipePos = Vector3.zero;
PipeManager.instance.canMove = true;
}
}
'게임엔진 > 개인 플젝' 카테고리의 다른 글
[Outdated] 농장 시뮬레이터 개인작 (0) | 2024.04.30 |
---|---|
플레이어 방향에 따른 발사 위치 변경 (0) | 2024.04.23 |
Flow Free 3 - UI (0) | 2024.03.27 |
Flow Free 1 - 그리드 (맵) 생성 (0) | 2024.03.27 |
[Unity] Flow Free 게임 카피캣 만들기 (0) | 2023.12.05 |