게임엔진/개인 플젝

[Unity] Flow Free 게임 카피캣 만들기

ShovelingLife 2023. 12. 5. 11:34

 

 

UI 관한 로직 - UIManager.cs

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;

public class UI_Manager : SingletonLocal<UI_Manager>
{
    public GameUI GameUI_Obj;
    public ResultUI ResultUI_Obj;
    int mLayer;
    bool isCreatedResUI = false;

    // 파이프 UI 용도
    public Canvas pipeUI_CanvasPrefab = null;
    Canvas pipeUI_Canvas;

    // 서브 UI 용도 (결과창)
    public Canvas subUI_CanvasPrefab = null;
    Canvas subUI_Canvas;

    // Start is called before the first frame update
    void Start()
    {
        Init();
    }

    private void Update()
    {
    }

    public void Init()
    {
        mLayer = LayerMask.NameToLayer("UI");

        // 서브캔버스 결과 (UI 전용) 세팅 및 카메라 세팅
        pipeUI_Canvas = GameObject.Instantiate(pipeUI_CanvasPrefab);
        pipeUI_Canvas.worldCamera = Global.GetUI_Camera();
        subUI_Canvas = GameObject.Instantiate(subUI_CanvasPrefab);
        subUI_Canvas.worldCamera = Global.GetUI_Camera();
        GameObject.Instantiate(GameUI_Obj, transform);
    }

    public void ResetLevel()
    {
        // Init();
        // GetComponent<Canvas>().worldCamera = Global.GetUI_Camera();
    }


    public Slot CheckForHoveredItem()
    {
        return CheckForHoveredItem(GetEventSystemRaycastResults());
    }

    public Slot CheckForHoveredItem(List<RaycastResult> listRaycastResult)
    {
        Slot tmpSlot = null;

        for (int index = 0; index < listRaycastResult.Count; index++)
        {
            RaycastResult res = listRaycastResult[index];

            if (res.gameObject.layer == mLayer)
                tmpSlot = res.gameObject.GetComponentInChildren<Slot>();
        }
        return tmpSlot;
    }

    List<RaycastResult> GetEventSystemRaycastResults()
    {
        PointerEventData eventData = new PointerEventData(EventSystem.current);
        eventData.position = Input.mousePosition;
        List<RaycastResult> listRes = new List<RaycastResult>();
        EventSystem.current.RaycastAll(eventData, listRes);
        return listRes;
    }

    public void ClearedGame()
    {
        if (isCreatedResUI ||
            subUI_Canvas == null)
            return;

        GameObject.Instantiate(ResultUI_Obj, subUI_Canvas.transform);
        isCreatedResUI = true;
    }
}

각 슬롯들 즉 드래그 종료할 한 시점에서 다시 그리는 용도

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
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() { return img.sprite == null; }

    public bool IsEndPoint() { return rect.sizeDelta == new Vector2(150f, 150f); }

    // 
    public bool IsLineEndPoint() { return rect.sizeDelta == new Vector2(100f, 100f); }

    public bool IsEndPoint(Color color) { return img.color == color && img.sprite.name == "Knob"; }

    public Sprite GetSprite() { return (img) ? img.sprite : null; }

    public Vector3 GetSize() { return rect.sizeDelta; }
    public PipeInfo GetPipeInfo() { return new PipeInfo(color, pipePos, idx); }
}

맵 생성 - GameManager.cs

char형 2차원 배열에 색상 동그라미와 설정함. 텍스트 파일 또는 서버로부터 갖고 올 수 있음. 

플레이어의 입력을 제어함.

아래는 Global.cs에 있을 색상에 대한 정

public static Color[] arrColor = new Color[]
    {
        red,
        blue,
        green,
        yellow,
        purple,
        cyan,
        orange,
        pink,
        brown
    };
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using Color = UnityEngine.Color;

public class GameManager : SingletonLocal<GameManager>, IPointerUpHandler, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    public Dictionary<Color, bool> dicCompleted = new Dictionary<Color, bool>();
    Slot mCurSlot = null;

    char[,] mLvl1 = new char[,]
    {
        { '\0', '\0', '\0','\0','\0','6' },
        { '\0', '\0', '\0','\0','\0','\0' },
        { '\0', '3', '0','\0','\0','\0' },
        { '\0', '\0', '\0','2','\0','3' },
        { '\0', '2', '\0','6','0','1' },
        { '\0', '\0', '\0','1','\0','\0' }
    };

    public int curPipes = 0, totalPipes = 0, movements = 0, best = 0, percent = 0;
    public int size = 0;

    // Start is called before the first frame update
    void Start()
    {
        GridManager.instance.SetLevel(mLvl1);
        size = mLvl1.Length;
    }

    // Update is called once per frame
    void Update()
    {
        CheckIfGameEnded();
    }

    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;
        }
    }

    // 선 업데이트
    public void OnDrag(PointerEventData eventData)
    {
        CheckIfDragging(eventData, UI_Manager.instance.CheckForHoveredItem());
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        ResetPipe();
    }

    public void OnEndDrag(PointerEventData eventData)
    {
        ResetPipe();
    }

    bool IsMouseOverUI()
    {
        return EventSystem.current.IsPointerOverGameObject();
    }

    void CheckIfGameEnded()
    {
        int tmpCurPipes = 0;

        foreach (var item in dicCompleted)
        {
            if (item.Value)
                tmpCurPipes++;
        }
        curPipes = tmpCurPipes;

        if (curPipes == totalPipes)
        {
            UI_Manager.instance.ClearedGame();
            return;
        }
    }

    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;
    }

    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;

                    movements++;

                    // 다른 선일 때 이동 가능
                    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;
            }
        }
    }

    public void ResetLevel()
    {
        SceneManager.LoadScene("PLAY");
        // curPipes = totalPipes = movements = best = percent = 0;
        GridManager.instance.ResetLevel(mLvl1);
        UI_Manager.instance.ResetLevel();
    }

    void ResetPipe()
    {
        if (mCurSlot)
        {
            dicCompleted[mCurSlot.color] = false;
            mCurSlot = null;
            PipeManager.instance.curPipePos = Vector3.zero;
            PipeManager.instance.canMove = true;
        }
    }
}

그리드 (타일) 생성 - GridManager.cs

ContextMenu 어트리뷰트를 응용해서 해당되는 레벨 그리드를 생성함.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GridManager : SingletonLocal<GridManager>
{
    public GameObject gridObj = null;
    public GameObject backObj = null;

    // 전체 리스트 담당
    public List<Slot> listSlot = new List<Slot>();

    // 시작과 끝 지점을 저장
    public Dictionary<Vector3, Slot> dicEntryPointSlot = new Dictionary<Vector3, Slot>();
    public Sprite knobSprite;

    // ------- 크기 종류 -------
    Vector2 mPos, mSlotSize, mMainGridPos;
    float mCellSize = 0f;
    int mLeftPadding = 0;

    public int emptyCount = 0;

    public GridManager()
    {

    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
    }

    void InitGrid(int size)
    {
        gridObj = Instantiate(new GameObject(), transform);
        var layoutGroup = gridObj.AddComponent<GridLayoutGroup>();
        gridObj.name = $" Grid {size}x{size} ";
        var rect = gridObj.transform as RectTransform;
        rect.localPosition = mPos;
        rect.sizeDelta = mSlotSize;

        layoutGroup.padding.left = mLeftPadding;
        layoutGroup.spacing = new Vector2(15f, 15f);
        layoutGroup.cellSize = new Vector2(mCellSize,mCellSize);
        Vector2 pipePos = new Vector2(5.32f, 2.675f);
        int idx = 1;

        for (int i = 0; i < size; i++)
        {
            for (int j = 0; j < size; j++)
            {
                // 배경 슬롯 설정
                var tmpObj = Instantiate(backObj, gridObj.transform);
                tmpObj.name = $" Background Grid {idx} ";

                // 메인 슬롯 설정
                var childObj = tmpObj.transform.GetChild(0);
                childObj.name = $" Main Grid {idx} ";

                // 메인 슬롯 위치 설정
                var childRect = childObj.transform as RectTransform;
                childRect.localPosition = mMainGridPos;
                childRect.sizeDelta = new Vector2(mCellSize, mCellSize);

                // 선 위치 설정
                var slot = childObj.GetComponent<Slot>();
                slot.idx = idx - 1;
                slot.pipePos = pipePos;
                pipePos.x += 0.86f;
                listSlot.Add(slot);
                idx++;
            }
            pipePos.y -= 0.86f;
            pipePos.x = 5.32f;
        }
    }

    public void SetLevel(char[,] map)
    {
        if (listSlot.Count == 0)
            return;

        int pipeIdx = 0;

        for (int i = 0; i < map.GetLength(0); i++)
        {
            for (int j = 0; j < map.GetLength(1); j++)
            {
                var letter = map[i, j];
                var slot = listSlot[i + j + pipeIdx];

                if (letter == '\0')
                {
                    emptyCount++;
                    RectTransform rect = slot.GetComponent<RectTransform>();
                    float size = 100.0f;
                    rect.sizeDelta = new Vector2(size, size);
                    continue;
                }
                var color = Global.arrColor[letter - '0'];

                GameManager.instance.dicCompleted.TryAdd(color, false);
                dicEntryPointSlot.TryAdd(slot.pipePos, slot);
                slot.img.sprite = knobSprite;
                slot.img.color = color;
                slot.color = slot.img.color;
                GameManager.instance.totalPipes++;
            }
            pipeIdx += 5;
        }
        GameManager.instance.totalPipes /= 2;
    }

    [ContextMenu("5x5 생성")]
    public void Create5x5()
    {
        mPos = new Vector2(237f, 100f);
        mSlotSize = new Vector2(1080f, 975f);
        mMainGridPos = new Vector2(92.5f, -92.5f);
        mLeftPadding = 50;
        mCellSize = 185f;
        InitGrid(5);
    }

    [ContextMenu("6x6 생성")]
    public void Create6x6()
    {
        mPos = new Vector2(237f, 100f);
        mSlotSize = new Vector2(1080f, 975f);
        mMainGridPos = new Vector2(75f, -75f);
        mLeftPadding = 50;
        mCellSize = 150f;
        InitGrid(6);
    }

    [ContextMenu("7x7 생성")]
    public void Create7x7()
    {
        mPos = new Vector2(237f, 100f);
        mSlotSize = new Vector2(1080f, 1000f);
        mMainGridPos = new Vector2(65f, -65f);
        mLeftPadding = 40;
        mCellSize = 130f;
        InitGrid(7);
    }

    [ContextMenu("8x8 생성")]
    public void Create8x8()
    {
        mPos = new Vector2(237f, 87f);
        mSlotSize = new Vector2(1080f, 1000f);
        mMainGridPos = new Vector2(57.5f, -57.5f);
        mLeftPadding = 30;
        mCellSize = 115f;
        InitGrid(8);
    }

    [ContextMenu("9x9 생성")]
    public void Create9x9()
    {
        mPos = new Vector2(237f, 76f);
        mSlotSize = new Vector2(1080f, 1020f);
        mMainGridPos = new Vector2(50f, -50f);
        mLeftPadding = 30;
        mCellSize = 100f;
        InitGrid(9);
    }

    [ContextMenu("삭제")]
    public void DeleteGrid()
    {
#if UNITY_EDITOR
        // 삭제 후 클리어
        DestroyImmediate(gridObj);
        listSlot.Clear();
#endif
    }

    public void ResetLevel(char[,] map)
    {
        // 초기화
        PipeManager.instance.Reset();
        dicEntryPointSlot.Clear();
        DeleteGrid();

        // 크기에 따른 게임판 초기화
        switch (map.GetLength(0))
        {
            case 5: Create5x5(); break;
            case 6: Create6x6(); break;
            case 7: Create7x7(); break;
            case 8: Create8x8(); break;
            case 9: Create9x9(); break;
        }
        SetLevel(map);
    }
}

2) 연결 파이프 (판정 및 점수 획득) - PipeManager.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Net;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UIElements;
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>
{
    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;

    // 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.instance.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);

        Pipe pipe = null;
        dicLine.TryGetValue(info.color, out 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.instance.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.instance.dicEntryPointSlot;
            Slot startSlot, endSlot;
            pointSlot.TryGetValue(startPos, out startSlot);
            pointSlot.TryGetValue(endPos, out 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();
    }
}