[SKKU DT] 47일차 -유니티 공공API 데이터 활용으로 UI 만들기 - CURD, 대리자(Delegate), LinkedList, WinMerge

2024. 1. 8. 18:53SKKU DT

728x90
반응형

CRUD 대부분의 컴퓨터 소프트웨어가 가지는 기본적인 데이터 처리 기능인 Create(생성), Read(읽기), Update(갱신), Delete(삭제)를 묶어서 일컫는 말이다.

 

 


 

 

Create 패널을 하나 만들어서 InputField로 사용자가 값을 입력할 수 있도록 한다. 저장 버튼과 닫기 버튼도 구현한다.

메인 UI에는 "+" 버튼으로 데이터를 추가할 수 있도록 한다. 추가된 데이터는 가장 아래로 들어간다.

 

 

DetailPanel 복사해서 CreatePanel 만들기

 

 

CreatePanel에 InputField, 저장 버튼 배치하기

 

버튼 설정은 아래와 같다. Vertical Layout Group으로 간격을 맞추고, 아래에 피봇을 설정해서 해상도 변경에도 대응한다.

 

 


 

 

이제 CreatePanelController 스크립트를 만들어서 추가되는 패널을 동작하게 할 것이다.

입력 받은 값을 외부 개체로 전달하는 기능이 필요하다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;

public class CreatePanelController : MonoBehaviour
{
    [SerializeField] TMP_InputField legacyNameInputField;
    [SerializeField] TMP_InputField typeInputField;
    [SerializeField] TMP_InputField designatedDateInputField;
    [SerializeField] TMP_InputField standardDateInputField;
    [SerializeField] TMP_InputField numberTextInputField;

    private void Awake() //나중에 Update 패널에서 동작하려면 Awake가 낫다.
    {
        //값 초기화
        legacyNameInputField.text = "";
        typeInputField.text = "";
        designatedDateInputField.text = "";
        standardDateInputField.text = "";
        numberTextInputField.text = "";
    }
    public void OnClickSaveButton()
    {

    }

    public void OnClickCloseButton()
    {
        Destroy(gameObject);
    }
}

 

 

스크립트를 완성하기 전에, 먼저 Scroll View 상단을 조금 줄이고 TitleBar 패널 추가, 데이터 추가 버튼도 배치한다.

 

 

+ 버튼을 누르면 CreatePanel이 생성되도록, LegacyDataManager 스크립트의 맨 마지막에 버튼에 매핑할 함수를 추가한다.

[SerializeField] private CreatePanelController createPanelPrefab; //추가 패널 받아오기
public void OnClickAddCell()
{
    CreatePanelController createPanelController = Instantiate(createPanelPrefab, canvas);
}

 

LegacyDataManager의 전체 코드는 아래와 같다.

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Networking;

public class LegacyDataManager : MonoBehaviour, ICellDelegate //나는 ICellDelegate를 따르겠다.
{
    [SerializeField] private string serviceKey; //서비스 키는 유출되지 않게 하기 위해서 Scene에서 관리할 예정이다.
    [SerializeField] private LegacyCellController legacyCellPrefab;
    [SerializeField] private MoreButtonCellController moreButtonPrefab;
    [SerializeField] private DetailPanelController detailPanelPrefab; //디테일 패널 받아오기
    [SerializeField] private CreatePanelController createPanelPrefab; //추가 패널 받아오기
    [SerializeField] private Transform content;
    [SerializeField] private Transform canvas; //디테일 패널 위치 받아올 캔버스

    //불러온 모든 Legacy 리스트 10개씩 계속 데이터를 불러오면 결국 50~60개의 데이터가 쌓이고 전체를 저장해야 한다.
    private List<Legacy> legacyList = new List<Legacy>();

    private void Start()
    {
        StartCoroutine(LoadData(1));
    }
    IEnumerator LoadData(int page, GameObject previousMoreButton = null) //몇 번째 page를 로드할 것인지.
    {
        //서버 URL 설정
        string url = string.Format("{0}?page={1}&perPage={2}&serviceKey={3}", Constants.url, page, Constants.perPage, serviceKey);//여러 개의 문자열을 조합할 수 있다.

        UnityWebRequest request = new UnityWebRequest();
        using (request = UnityWebRequest.Get(url))
        {
            yield return request.SendWebRequest();

            if(request.result != UnityWebRequest.Result.Success)
            {
                Debug.Log(request.error);
            }
            else
            {
                string result = request.downloadHandler.text; //텍스트 형식으로 받아오기. 많이 불러왔었던 빼곡한 글들로 불러온다.

                LegacyData legacyData = JsonUtility.FromJson<LegacyData>(result); //파싱에서 가져온 이름 LegacyData
                Legacy[] legacyArray = legacyData.data;

                for(int i = 0; i < legacyArray.Length; i++)
                {
                    legacyList.Add(legacyArray[i]);

                    LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
                    legacyCellController.SetData(legacyArray[i].명칭, legacyArray[i].종목, legacyArray[i].지정일, legacyList.Count - 1);
                    
                    legacyCellController.cellDelegate = this; //너의 일을 대신해줄 사람은 나다!
                }

                if(previousMoreButton != null) //더보기 버튼 제거
                {
                    Destroy(previousMoreButton.gameObject);
                }

                //더보기 버튼 추가
                int currentTotalCount = legacyData.perPage * (legacyData.page - 1) + legacyData.currentCount; //전체 불러온 데이터 카운트
                if(currentTotalCount < legacyData.totalCount) //아직 불러올 데이터가 있다면
                {
                    MoreButtonCellController moreButtonCellController = Instantiate(moreButtonPrefab, content); //더보기 버튼 생성
                    moreButtonCellController.loadMoreData = () => //변수에게 함수를 전달하기. 람다 형식으로
                    {
                        StartCoroutine(LoadData(legacyData.page + 1, moreButtonCellController.gameObject));
                    };
                }
            }
        }
    }
    public void OnClickCell(int index) //위에 ICellDelegate 인터페이스 따른다고 하여 자동 생성
    {
        DetailPanelController detailPanelController = Instantiate(detailPanelPrefab, canvas);
        detailPanelController.SetData(legacyList[index]);
    }

    public void OnClickAddCell()
    {
    CreatePanelController createPanelController = Instantiate(createPanelPrefab, canvas);
    }
}

 

프리팹을 연결하고, 버튼에 함수도 연결한다.

 

 

CreatPanel 프리팹의 저장/닫기 버튼에도 함수를 넣는다.

 

 

연번의 InputField는 [Content Type]을 [Integer Number]로 바꾼다. 숫자만 받을 수 있도록 하기 위해서.

 

 

CreatePanelController 스크립트에서, 이제 Save를 눌렀을 때 입력된 값을 저장할 수 있게 만들 것이다. 저장해야하는 값은 List에 있는 값이다. LegacyDataManager에 List<Legacy> legacyList = new List<Legacy>();가 있다.

대리자 CreateData 생성.

public delegate void CreateData(Legacy legacy);
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using Unity.VisualScripting;

public delegate void CreateData(Legacy legacy); //대리자 생성

public class CreatePanelController : MonoBehaviour
{
    [SerializeField] TMP_InputField legacyNameInputField;
    [SerializeField] TMP_InputField typeInputField;
    [SerializeField] TMP_InputField designatedDateInputField;
    [SerializeField] TMP_InputField standardDateInputField;
    [SerializeField] TMP_InputField numberTextInputField;

    public CreateData createData; //대리자 변수 선언. CreatePanel 외부로 부터 전달받는 통로.

    private void Awake() //나중에 Update 패널에서 동작하려면 Awake가 낫다.
    {
        //값 초기화
        legacyNameInputField.text = "";
        typeInputField.text = "";
        designatedDateInputField.text = "";
        standardDateInputField.text = "";
        numberTextInputField.text = "";
    }
    public void OnClickSaveButton()
    {
        //대리자
        if(this.createData != null)
        {
            Legacy legacy = new Legacy();
            legacy.명칭 = legacyNameInputField.text;
            legacy.종목 = typeInputField.text;
            legacy.지정일 = designatedDateInputField.text;
            legacy.기준일 = standardDateInputField.text;
            legacy.연번 = int.Parse(numberTextInputField.text); //정수형 변환
            this.createData(legacy);
        }
    }

    public void OnClickCloseButton()
    {
        Destroy(gameObject);
    }
}

 

 

기존 Legacy 스크립트에서 생성자를 만들 수 있다. 여러 개의 생성자는 메서드의 오버로딩이다.

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

[Serializable]
public class Legacy //배열의 이름과 맞춘다. Legacy[] data
{
    public string 기준일;
    public string 명칭;
    //public string 소재지(구); //괄호가 있으면 불러와지지 않는다.
    //public string 소재지(상세); //괄호가 있으면 불러와지지 않는다.
    public int 연번;
    public string 종목;
    public string 지정일;

    public Legacy() : this("", "", "", "", 0) //생성자. CreatePanelController의 Legacy legacy = new Legacy(); 오류 방지
    {
        /*
        this.명칭 = "";
        this.종목 = "";
        this.지정일 = "";
        this.기준일 = "";
        this.연번 = 0;
        */
        //this 이후를 쓰면서 내부는 필요없어졌다.
    }

    public Legacy(string name, string type, string designatedDate, string standardDate, int number) //생성자
    {
        this.명칭 = name;
        this.종목 = type;
        this.지정일 = designatedDate;
        this.기준일 = standardDate;
        this.연번 = number;
    }

    public Legacy(string name) : this("", name, "", "", 0) //이미 생성된 생성자를 활용해서 또다른 생성자를 만들 수도 있다.
    {

    }
}

public class LegacyData //JSON 전체의 큰 틀을 가지는 형식.
{
    public int currentCount;
    public Legacy[] data; //배열이다
    public int matchCount;
    public int page;
    public int perPage;
    public int totalCount;
}

 

 

생성자를 씀으로써 CreatePanelController 스크립트에서 Legacy legacy = new Legacy() 괄호 안에 매개변수를 생성할 수 있게 되었다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using Unity.VisualScripting;

public delegate void CreateData(Legacy legacy); //대리자 생성

public class CreatePanelController : MonoBehaviour
{
    [SerializeField] TMP_InputField legacyNameInputField;
    [SerializeField] TMP_InputField typeInputField;
    [SerializeField] TMP_InputField designatedDateInputField;
    [SerializeField] TMP_InputField standardDateInputField;
    [SerializeField] TMP_InputField numberTextInputField;

    public CreateData createData; //대리자 변수 선언. CreatePanel 외부로 부터 전달받는 통로.

    private void Awake() //나중에 Update 패널에서 동작하려면 Awake가 낫다.
    {
        //값 초기화
        legacyNameInputField.text = "";
        typeInputField.text = "";
        designatedDateInputField.text = "";
        standardDateInputField.text = "";
        numberTextInputField.text = "";
    }
    public void OnClickSaveButton()
    {
        //대리자
        if(this.createData != null)
        {
            Legacy legacy = new Legacy(legacyNameInputField.text, typeInputField.text, designatedDateInputField.text, standardDateInputField.text, int.Parse(numberTextInputField.text));
            //legacy.명칭 = legacyNameInputField.text;
            //legacy.종목 = typeInputField.text;
            //legacy.지정일 = designatedDateInputField.text;
            //legacy.기준일 = standardDateInputField.text;
            //legacy.연번 = int.Parse(numberTextInputField.text); //정수형 변환
            this.createData(legacy);
        }
    }

    public void OnClickCloseButton()
    {
        Destroy(gameObject);
    }
}

 

 

LegacyDataManager 스크립트에서는, 대리자를 이용해서 SaveData 메서드로 저장을 가능하게 만든다.

public void OnClickAddCell()
{
    CreatePanelController createPanelController = Instantiate(createPanelPrefab, canvas);
    createPanelController.createData = new CreateData(SaveData);
}

public void SaveData(Legacy legacy)
{
    //Create Panel Controller에서 전달된 Legacy 객체를 legacyList에 추가
    legacyList.Add(legacy);
}

전체 코드

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Networking;

public class LegacyDataManager : MonoBehaviour, ICellDelegate //나는 ICellDelegate를 따르겠다.
{
    [SerializeField] private string serviceKey; //서비스 키는 유출되지 않게 하기 위해서 Scene에서 관리할 예정이다.
    [SerializeField] private LegacyCellController legacyCellPrefab;
    [SerializeField] private MoreButtonCellController moreButtonPrefab;
    [SerializeField] private DetailPanelController detailPanelPrefab; //디테일 패널 받아오기
    [SerializeField] private CreatePanelController createPanelPrefab; //추가 패널 받아오기
    [SerializeField] private Transform content;
    [SerializeField] private Transform canvas; //디테일 패널 위치 받아올 캔버스

    //불러온 모든 Legacy 리스트 10개씩 계속 데이터를 불러오면 결국 50~60개의 데이터가 쌓이고 전체를 저장해야 한다.
    private List<Legacy> legacyList = new List<Legacy>();

    private void Start()
    {
        StartCoroutine(LoadData(1));
    }
    IEnumerator LoadData(int page, GameObject previousMoreButton = null) //몇 번째 page를 로드할 것인지.
    {
        //서버 URL 설정
        string url = string.Format("{0}?page={1}&perPage={2}&serviceKey={3}", Constants.url, page, Constants.perPage, serviceKey);//여러 개의 문자열을 조합할 수 있다.

        UnityWebRequest request = new UnityWebRequest();
        using (request = UnityWebRequest.Get(url))
        {
            yield return request.SendWebRequest();

            if(request.result != UnityWebRequest.Result.Success)
            {
                Debug.Log(request.error);
            }
            else
            {
                string result = request.downloadHandler.text; //텍스트 형식으로 받아오기. 많이 불러왔었던 빼곡한 글들로 불러온다.

                LegacyData legacyData = JsonUtility.FromJson<LegacyData>(result); //파싱에서 가져온 이름 LegacyData
                Legacy[] legacyArray = legacyData.data;

                for(int i = 0; i < legacyArray.Length; i++)
                {
                    legacyList.Add(legacyArray[i]);

                    LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
                    legacyCellController.SetData(legacyArray[i].명칭, legacyArray[i].종목, legacyArray[i].지정일, legacyList.Count - 1);
                    
                    legacyCellController.cellDelegate = this; //너의 일을 대신해줄 사람은 나다!
                }

                if(previousMoreButton != null) //더보기 버튼 제거
                {
                    Destroy(previousMoreButton.gameObject);
                }

                //더보기 버튼 추가
                int currentTotalCount = legacyData.perPage * (legacyData.page - 1) + legacyData.currentCount; //전체 불러온 데이터 카운트
                if(currentTotalCount < legacyData.totalCount) //아직 불러올 데이터가 있다면
                {
                    MoreButtonCellController moreButtonCellController = Instantiate(moreButtonPrefab, content); //더보기 버튼 생성
                    moreButtonCellController.loadMoreData = () => //변수에게 함수를 전달하기. 람다 형식으로
                    {
                        StartCoroutine(LoadData(legacyData.page + 1, moreButtonCellController.gameObject));
                    };
                }
            }
        }
    }
    public void OnClickCell(int index) //위에 ICellDelegate 인터페이스 따른다고 하여 자동 생성
    {
        DetailPanelController detailPanelController = Instantiate(detailPanelPrefab, canvas);
        detailPanelController.SetData(legacyList[index]);
    }

    public void OnClickAddCell()
    {
        CreatePanelController createPanelController = Instantiate(createPanelPrefab, canvas);
        createPanelController.createData = new CreateData(SaveData);
    }

    public void SaveData(Legacy legacy)
    {
        //Create Panel Controller에서 전달된 Legacy 객체를 legacyList에 추가
        legacyList.Add(legacy);

        //리스트 갱신
        reloadData();
    }

    //데이터가 추가되었을 때 화면 갱신하기
    void reloadData()
    {
        //모든 cell 지우기
        foreach (Transform transform in content.GetComponentInChildren<Transform>())
        {
            Destroy(transform.gameObject);
        }

        //다시 List의 값을 cell로 생성
        for (int i = 0; i < legacyList.Count; i++)
        {
            LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
            legacyCellController.SetData(legacyList[i].명칭, legacyList[i].종목, legacyList[i].지정일, legacyList.Count - 1);

            legacyCellController.cellDelegate = this; //너의 일을 대신해줄 사람은 나다!
        }
    }
}

 

 

데이터를 추가하면, 리스트 마지막에 잘 뜨는 것을 볼 수 있다.

 

 


 

 

LegacyDataManager의 스크립트 중 일부 함수는 람다 형식으로 줄일 수 있다.

public void OnClickAddCell()
{
    CreatePanelController createPanelController = Instantiate(createPanelPrefab, canvas);
    //createPanelController.createData = new CreateData(SaveData);
    createPanelController.createData = (legacy) => //람다 형태는 따로 함수를 만들지 않고 즉시 사용할 수 있다. 아래 SaveData 함수를 사용하지 않아도 된다.
    {
        legacyList.Add(legacy);
        reloadData();
    };
}

/*
public void SaveData(Legacy legacy)
{
    //Create Panel Controller에서 전달된 Legacy 객체를 legacyList에 추가
    legacyList.Add(legacy);

    //리스트 갱신
    reloadData();
}
*/

 

 

reloadData 함수까지 끌어들어들이면 아래와 같이 함수를 더 줄일 수도 있다.

public void OnClickAddCell()
{
    CreatePanelController createPanelController = Instantiate(createPanelPrefab, canvas);
    //createPanelController.createData = new CreateData(SaveData);
    createPanelController.createData = (legacy) => //람다 형태는 따로 함수를 만들지 않고 즉시 사용할 수 있다. 아래 SaveData 함수를 사용하지 않아도 된다.
    {
        legacyList.Add(legacy);
        
        LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
        legacyCellController.SetData(legacy.명칭, legacy.종목, legacy.지정일, legacyList.Count - 1);

        legacyCellController.cellDelegate = this;
        
        legacyCellController.transform.SetSiblingIndex(legacyList.Count - 1); //SetSiblingIndex는 셀의 위치를 바꾸는 것이다. 없었으면 더보기 버튼 다음에 만들어졌을 것이다.
    };
}
/*
public void SaveData(Legacy legacy)
{
    //Create Panel Controller에서 전달된 Legacy 객체를 legacyList에 추가
    legacyList.Add(legacy);

    //리스트 갱신
    reloadData();
}
*/
/*
//데이터가 추가되었을 때 화면 갱신하기
void reloadData()
{
    //모든 cell 지우기
    foreach (Transform transform in content.GetComponentInChildren<Transform>())
    {
        Destroy(transform.gameObject);
    }

    //다시 List의 값을 cell로 생성
    for (int i = 0; i < legacyList.Count; i++)
    {
        LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
        legacyCellController.SetData(legacyList[i].명칭, legacyList[i].종목, legacyList[i].지정일, legacyList.Count - 1);

        legacyCellController.cellDelegate = this; //너의 일을 대신해줄 사람은 나다!
    }
}
*/

이렇게 함수를 줄여서 사용해도 충분히 잘 돌아간다.

 

 


 

Action을 활용한 방식

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using System;

public delegate void CreateData(Legacy legacy); //대리자 생성

public class CreatePanelController : MonoBehaviour
{
    [SerializeField] TMP_InputField legacyNameInputField;
    [SerializeField] TMP_InputField typeInputField;
    [SerializeField] TMP_InputField designatedDateInputField;
    [SerializeField] TMP_InputField standardDateInputField;
    [SerializeField] TMP_InputField numberTextInputField;

    //대리자 변수 선언. CreatePanel 외부로 부터 전달받는 통로.
    public CreateData createData;

    //액션 생성. 액션은 매개변수 타입만 있으면 된다. 무조건 void 타입만 할당 가능. 대리자보다 간결하게 선언부가 없다.
    public Action<Legacy> createDataAction;

    private void Awake() //나중에 Update 패널에서 동작하려면 Awake가 낫다.
    {
        //값 초기화
        legacyNameInputField.text = "";
        typeInputField.text = "";
        designatedDateInputField.text = "";
        standardDateInputField.text = "";
        numberTextInputField.text = "";
    }
    public void OnClickSaveButton()
    {
        Legacy legacy = new Legacy(legacyNameInputField.text, typeInputField.text, designatedDateInputField.text, standardDateInputField.text, int.Parse(numberTextInputField.text));
        /*대리자
        if (this.createData != null)
        {
            //legacy.명칭 = legacyNameInputField.text;
            //legacy.종목 = typeInputField.text;
            //legacy.지정일 = designatedDateInputField.text;
            //legacy.기준일 = standardDateInputField.text;
            //legacy.연번 = int.Parse(numberTextInputField.text); //정수형 변환
            this.createData(legacy);
        }
        */
        //액션
        if(this.createDataAction != null)
        {
            this.createDataAction(legacy);
        }
    }

    public void OnClickCloseButton()
    {
        Destroy(gameObject);
    }
}

 

 

LegacyDataManager 스크립트에서는, 대리자를 쓴 것과 동일하게 적용할 수 있다. 주석처리 된 부분은 대리자이다. 아래 부분이 액션 활용 부분.

public void OnClickAddCell()
{
    CreatePanelController createPanelController = Instantiate(createPanelPrefab, canvas);
    ////createPanelController.createData = new CreateData(SaveData);
    //createPanelController.createData = (legacy) => //람다 형태는 따로 함수를 만들지 않고 즉시 사용할 수 있다. 아래 SaveData 함수를 사용하지 않아도 된다.
    //{
    //    legacyList.Add(legacy);
        
    //    LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
    //    legacyCellController.SetData(legacy.명칭, legacy.종목, legacy.지정일, legacyList.Count - 1);

    //    legacyCellController.cellDelegate = this;
        
    //    legacyCellController.transform.SetSiblingIndex(legacyList.Count - 1); //SetSiblingIndex는 셀의 위치를 바꾸는 것이다. 없었으면 더보기 버튼 다음에 만들어졌을 것이다.
    //};
    //액션을 사용한 방식
    createPanelController.createDataAction = (legacy) => //람다 형태는 따로 함수를 만들지 않고 즉시 사용할 수 있다. 아래 SaveData 함수를 사용하지 않아도 된다.
    {
        legacyList.Add(legacy);

        LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
        legacyCellController.SetData(legacy.명칭, legacy.종목, legacy.지정일, legacyList.Count - 1);

        legacyCellController.cellDelegate = this;

        legacyCellController.transform.SetSiblingIndex(legacyList.Count - 1); //SetSiblingIndex는 셀의 위치를 바꾸는 것이다. 없었으면 더보기 버튼 다음에 만들어졌을 것이다.
    };
}

아래는 LegacyDataManager의 전체 코드이다.

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Networking;

public class LegacyDataManager : MonoBehaviour, ICellDelegate //나는 ICellDelegate를 따르겠다.
{
    [SerializeField] private string serviceKey; //서비스 키는 유출되지 않게 하기 위해서 Scene에서 관리할 예정이다.
    [SerializeField] private LegacyCellController legacyCellPrefab;
    [SerializeField] private MoreButtonCellController moreButtonPrefab;
    [SerializeField] private DetailPanelController detailPanelPrefab; //디테일 패널 받아오기
    [SerializeField] private CreatePanelController createPanelPrefab; //추가 패널 받아오기
    [SerializeField] private Transform content;
    [SerializeField] private Transform canvas; //디테일 패널 위치 받아올 캔버스

    //불러온 모든 Legacy 리스트 10개씩 계속 데이터를 불러오면 결국 50~60개의 데이터가 쌓이고 전체를 저장해야 한다.
    private List<Legacy> legacyList = new List<Legacy>();

    private void Start()
    {
        StartCoroutine(LoadData(1));
    }
    IEnumerator LoadData(int page, GameObject previousMoreButton = null) //몇 번째 page를 로드할 것인지.
    {
        //서버 URL 설정
        string url = string.Format("{0}?page={1}&perPage={2}&serviceKey={3}", Constants.url, page, Constants.perPage, serviceKey);//여러 개의 문자열을 조합할 수 있다.

        UnityWebRequest request = new UnityWebRequest();
        using (request = UnityWebRequest.Get(url))
        {
            yield return request.SendWebRequest();

            if(request.result != UnityWebRequest.Result.Success)
            {
                Debug.Log(request.error);
            }
            else
            {
                string result = request.downloadHandler.text; //텍스트 형식으로 받아오기. 많이 불러왔었던 빼곡한 글들로 불러온다.

                LegacyData legacyData = JsonUtility.FromJson<LegacyData>(result); //파싱에서 가져온 이름 LegacyData
                Legacy[] legacyArray = legacyData.data;

                for(int i = 0; i < legacyArray.Length; i++)
                {
                    legacyList.Add(legacyArray[i]);

                    LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
                    legacyCellController.SetData(legacyArray[i].명칭, legacyArray[i].종목, legacyArray[i].지정일, legacyList.Count - 1);
                    
                    legacyCellController.cellDelegate = this; //너의 일을 대신해줄 사람은 나다!
                }

                if(previousMoreButton != null) //더보기 버튼 제거
                {
                    Destroy(previousMoreButton.gameObject);
                }

                //더보기 버튼 추가
                int currentTotalCount = legacyData.perPage * (legacyData.page - 1) + legacyData.currentCount; //전체 불러온 데이터 카운트
                if(currentTotalCount < legacyData.totalCount) //아직 불러올 데이터가 있다면
                {
                    MoreButtonCellController moreButtonCellController = Instantiate(moreButtonPrefab, content); //더보기 버튼 생성
                    moreButtonCellController.loadMoreData = () => //변수에게 함수를 전달하기. 람다 형식으로
                    {
                        StartCoroutine(LoadData(legacyData.page + 1, moreButtonCellController.gameObject));
                    };
                }
            }
        }
    }
    public void OnClickCell(int index) //위에 ICellDelegate 인터페이스 따른다고 하여 자동 생성
    {
        DetailPanelController detailPanelController = Instantiate(detailPanelPrefab, canvas);
        detailPanelController.SetData(legacyList[index]);
    }

    public void OnClickAddCell()
    {
        CreatePanelController createPanelController = Instantiate(createPanelPrefab, canvas);
        ////createPanelController.createData = new CreateData(SaveData);
        //createPanelController.createData = (legacy) => //람다 형태는 따로 함수를 만들지 않고 즉시 사용할 수 있다. 아래 SaveData 함수를 사용하지 않아도 된다.
        //{
        //    legacyList.Add(legacy);
            
        //    LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
        //    legacyCellController.SetData(legacy.명칭, legacy.종목, legacy.지정일, legacyList.Count - 1);

        //    legacyCellController.cellDelegate = this;
            
        //    legacyCellController.transform.SetSiblingIndex(legacyList.Count - 1); //SetSiblingIndex는 셀의 위치를 바꾸는 것이다. 없었으면 더보기 버튼 다음에 만들어졌을 것이다.
        //};
        //액션을 사용한 방식
        createPanelController.createDataAction = (legacy) => //람다 형태는 따로 함수를 만들지 않고 즉시 사용할 수 있다. 아래 SaveData 함수를 사용하지 않아도 된다.
        {
            legacyList.Add(legacy);

            LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
            legacyCellController.SetData(legacy.명칭, legacy.종목, legacy.지정일, legacyList.Count - 1);

            legacyCellController.cellDelegate = this;

            legacyCellController.transform.SetSiblingIndex(legacyList.Count - 1); //SetSiblingIndex는 셀의 위치를 바꾸는 것이다. 없었으면 더보기 버튼 다음에 만들어졌을 것이다.
        };
    }

    public void SaveData(Legacy legacy)
    {
        //Create Panel Controller에서 전달된 Legacy 객체를 legacyList에 추가
        legacyList.Add(legacy);

        //리스트 갱신
        reloadData();
    }

    //데이터가 추가되었을 때 화면 갱신하기
    void reloadData()
    {
        //모든 cell 지우기
        foreach (Transform transform in content.GetComponentInChildren<Transform>())
        {
            Destroy(transform.gameObject);
        }

        //다시 List의 값을 cell로 생성
        for (int i = 0; i < legacyList.Count; i++)
        {
            LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
            legacyCellController.SetData(legacyList[i].명칭, legacyList[i].종목, legacyList[i].지정일, legacyList.Count - 1);

            legacyCellController.cellDelegate = this; //너의 일을 대신해줄 사람은 나다!
        }
    }
}

 

 


 

 

WinMerge

 

WinMerge - 차이를 알게 될 것입니다…

WinMerge가 무엇인가요? WinMerge는 Windows용 오픈 소스 차이점 분석 및 병합 도구입니다. WinMerge는 폴더와 파일을 모두 비교하여 시각적 텍스트 형식의 차이를 쉽게 이해하고 처리할 수 있습니다. 스

winmerge.org

코드를 비교하기 위해서 위의 사이트에서 프로그램을 다운로드 받을 수 있다.

 

 


 

 

 

데이터 Update 하기

Create와 비슷한 기능을 사용하여 기존의 값을 수정한다.

DetailPanel에서, "닫기" 버튼 옆에 "수정" 버튼을 만들어서 InputField로 수정하도록.

 

 

CreatePanel을 복사해서 UpdatePanel을 하나 만든다.

 

 

DetailPanel에 "수정" 버튼을 만들어준 후 해당 버튼을 누르면 CreatePanel처럼 InputField가 생성되게 해야한다. 대신, 기존에 DetailPanel에 있던 정보들은 InputField에 남았도록.

 

 

CreatePanelController 스크립트를 복사해서 UpdatePanelController 스크립트 생성.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using System;
using Unity.VisualScripting;

public delegate void UpdateData(Legacy legacy); //대리자 생성

public class UpdatePanelController : MonoBehaviour
{
    [SerializeField] TMP_InputField legacyNameInputField;
    [SerializeField] TMP_InputField typeInputField;
    [SerializeField] TMP_InputField designatedDateInputField;
    [SerializeField] TMP_InputField standardDateInputField;
    [SerializeField] TMP_InputField numberTextInputField;

    //대리자 변수 선언. CreatePanel 외부로 부터 전달받는 통로.
    public UpdateData updateData;

    private void Awake() //나중에 Update 패널에서 동작하려면 Awake가 낫다.
    {
        //값 초기화
        legacyNameInputField.text = "";
        typeInputField.text = "";
        designatedDateInputField.text = "";
        standardDateInputField.text = "";
        numberTextInputField.text = "";
    }

    //DetailPanelController의 함수를 그대로 가져다 쓴다. 데이터 불러오는 메서드.
    public void SetData(Legacy legacy)//Legacy 타입으로 받고 있다. 수업에선 Office.
    {
        legacyNameInputField.text = legacy.명칭;
        typeInputField.text = legacy.종목;
        designatedDateInputField.text = legacy.지정일;
        standardDateInputField.text = legacy.기준일;
        numberTextInputField.text = legacy.연번.ToString();
    }

    public void OnClickUpdateButton()
    {
        Legacy legacy = new Legacy(legacyNameInputField.text, typeInputField.text, designatedDateInputField.text, standardDateInputField.text, int.Parse(numberTextInputField.text));
        //대리자
        if(this.updateData != null)
        {
            this.updateData(legacy);
        }
    }

    public void OnClickCloseButton()
    {
        Destroy(gameObject);
    }
}

 

 

UpdatePanel에 빈 오브젝트들을 끌어다 놓는다.

 

 

Update 버튼과 Close 버튼에 각각 함수 매핑

 

 


 

 

LegacyDataManager 스크립트 수정

수정부분

[SerializeField] private UpdatePanelController updatePanelPrefab; //업데이트 패널 받아오기
public void OnClickCell(int index) //위에 ICellDelegate 인터페이스 따른다고 하여 자동 생성
{
    //DetailPanelController detailPanelController = Instantiate(detailPanelPrefab, canvas);
    //detailPanelController.SetData(legacyList[index]);

    UpdatePanelController updatePanelController = Instantiate(updatePanelPrefab, content);
    updatePanelController.SetData(legacyList[index]);
}

 

전체 코드

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Networking;

public class LegacyDataManager : MonoBehaviour, ICellDelegate //나는 ICellDelegate를 따르겠다.
{
    [SerializeField] private string serviceKey; //서비스 키는 유출되지 않게 하기 위해서 Scene에서 관리할 예정이다.
    [SerializeField] private LegacyCellController legacyCellPrefab;
    [SerializeField] private MoreButtonCellController moreButtonPrefab;
    [SerializeField] private DetailPanelController detailPanelPrefab; //디테일 패널 받아오기
    [SerializeField] private CreatePanelController createPanelPrefab; //추가 패널 받아오기
    [SerializeField] private UpdatePanelController updatePanelPrefab; //업데이트 패널 받아오기
    [SerializeField] private Transform content;
    [SerializeField] private Transform canvas; //디테일 패널 위치 받아올 캔버스

    //불러온 모든 Legacy 리스트 10개씩 계속 데이터를 불러오면 결국 50~60개의 데이터가 쌓이고 전체를 저장해야 한다.
    private List<Legacy> legacyList = new List<Legacy>();

    private void Start()
    {
        StartCoroutine(LoadData(1));
    }
    IEnumerator LoadData(int page, GameObject previousMoreButton = null) //몇 번째 page를 로드할 것인지.
    {
        //서버 URL 설정
        string url = string.Format("{0}?page={1}&perPage={2}&serviceKey={3}", Constants.url, page, Constants.perPage, serviceKey);//여러 개의 문자열을 조합할 수 있다.

        UnityWebRequest request = new UnityWebRequest();
        using (request = UnityWebRequest.Get(url))
        {
            yield return request.SendWebRequest();

            if(request.result != UnityWebRequest.Result.Success)
            {
                Debug.Log(request.error);
            }
            else
            {
                string result = request.downloadHandler.text; //텍스트 형식으로 받아오기. 많이 불러왔었던 빼곡한 글들로 불러온다.

                LegacyData legacyData = JsonUtility.FromJson<LegacyData>(result); //파싱에서 가져온 이름 LegacyData
                Legacy[] legacyArray = legacyData.data;

                for(int i = 0; i < legacyArray.Length; i++)
                {
                    legacyList.Add(legacyArray[i]);

                    LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
                    legacyCellController.SetData(legacyArray[i].명칭, legacyArray[i].종목, legacyArray[i].지정일, legacyList.Count - 1);
                    
                    legacyCellController.cellDelegate = this; //너의 일을 대신해줄 사람은 나다!
                }

                if(previousMoreButton != null) //더보기 버튼 제거
                {
                    Destroy(previousMoreButton.gameObject);
                }

                //더보기 버튼 추가
                int currentTotalCount = legacyData.perPage * (legacyData.page - 1) + legacyData.currentCount; //전체 불러온 데이터 카운트
                if(currentTotalCount < legacyData.totalCount) //아직 불러올 데이터가 있다면
                {
                    MoreButtonCellController moreButtonCellController = Instantiate(moreButtonPrefab, content); //더보기 버튼 생성
                    moreButtonCellController.loadMoreData = () => //변수에게 함수를 전달하기. 람다 형식으로
                    {
                        StartCoroutine(LoadData(legacyData.page + 1, moreButtonCellController.gameObject));
                    };
                }
            }
        }
    }
    /// <summary>
    /// Cell을 클릭했을 때 동작하는 메서드
    /// </summary>
    /// <param name="index">선택한 Cell의 Index</param>
    public void OnClickCell(int index) //위에 ICellDelegate 인터페이스 따른다고 하여 자동 생성
    {
        //DetailPanelController detailPanelController = Instantiate(detailPanelPrefab, canvas);
        //detailPanelController.SetData(legacyList[index]);

        UpdatePanelController updatePanelController = Instantiate(updatePanelPrefab, content);
        updatePanelController.SetData(legacyList[index]);
    }

    public void OnClickAddCell()
    {
        CreatePanelController createPanelController = Instantiate(createPanelPrefab, canvas);
        ////createPanelController.createData = new CreateData(SaveData);
        //createPanelController.createData = (legacy) => //람다 형태는 따로 함수를 만들지 않고 즉시 사용할 수 있다. 아래 SaveData 함수를 사용하지 않아도 된다.
        //{
        //    legacyList.Add(legacy);
            
        //    LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
        //    legacyCellController.SetData(legacy.명칭, legacy.종목, legacy.지정일, legacyList.Count - 1);

        //    legacyCellController.cellDelegate = this;
            
        //    legacyCellController.transform.SetSiblingIndex(legacyList.Count - 1); //SetSiblingIndex는 셀의 위치를 바꾸는 것이다. 없었으면 더보기 버튼 다음에 만들어졌을 것이다.
        //};
        //액션을 사용한 방식
        createPanelController.createDataAction = (legacy) => //람다 형태는 따로 함수를 만들지 않고 즉시 사용할 수 있다. 아래 SaveData 함수를 사용하지 않아도 된다.
        {
            legacyList.Add(legacy);

            LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
            legacyCellController.SetData(legacy.명칭, legacy.종목, legacy.지정일, legacyList.Count - 1);

            legacyCellController.cellDelegate = this;

            legacyCellController.transform.SetSiblingIndex(legacyList.Count - 1); //SetSiblingIndex는 셀의 위치를 바꾸는 것이다. 없었으면 더보기 버튼 다음에 만들어졌을 것이다.
        };
    }

    public void SaveData(Legacy legacy)
    {
        //Create Panel Controller에서 전달된 Legacy 객체를 legacyList에 추가
        legacyList.Add(legacy);

        //리스트 갱신
        reloadData();
    }

    //데이터가 추가되었을 때 화면 갱신하기
    void reloadData()
    {
        //모든 cell 지우기
        foreach (Transform transform in content.GetComponentInChildren<Transform>())
        {
            Destroy(transform.gameObject);
        }

        //다시 List의 값을 cell로 생성
        for (int i = 0; i < legacyList.Count; i++)
        {
            LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
            legacyCellController.SetData(legacyList[i].명칭, legacyList[i].종목, legacyList[i].지정일, legacyList.Count - 1);

            legacyCellController.cellDelegate = this; //너의 일을 대신해줄 사람은 나다!
        }
    }
}

 

 

 

다시, UpdatePanelController 스크립트 수정. 셀의 Index 추가

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using System;
using Unity.VisualScripting;

public delegate void UpdateData(Legacy legacy, int index); //대리자 생성

public class UpdatePanelController : MonoBehaviour
{
    [SerializeField] TMP_InputField legacyNameInputField;
    [SerializeField] TMP_InputField typeInputField;
    [SerializeField] TMP_InputField designatedDateInputField;
    [SerializeField] TMP_InputField standardDateInputField;
    [SerializeField] TMP_InputField numberTextInputField;

    //대리자 변수 선언. CreatePanel 외부로 부터 전달받는 통로.
    public UpdateData updateData;

    private int dataIndex;

    private void Awake() //나중에 Update 패널에서 동작하려면 Awake가 낫다.
    {
        //값 초기화
        legacyNameInputField.text = "";
        typeInputField.text = "";
        designatedDateInputField.text = "";
        standardDateInputField.text = "";
        numberTextInputField.text = "";
    }

    //DetailPanelController의 함수를 그대로 가져다 쓴다. 데이터 불러오는 메서드.
    public void SetData(Legacy legacy, int index)//Legacy 타입으로 받고 있다. 수업에선 Office.
    {
        dataIndex = index;
        legacyNameInputField.text = legacy.명칭;
        typeInputField.text = legacy.종목;
        designatedDateInputField.text = legacy.지정일;
        standardDateInputField.text = legacy.기준일;
        numberTextInputField.text = legacy.연번.ToString();
    }

    public void OnClickUpdateButton()
    {
        Legacy legacy = new Legacy(legacyNameInputField.text, typeInputField.text, designatedDateInputField.text, standardDateInputField.text, int.Parse(numberTextInputField.text));
        //대리자
        if(this.updateData != null)
        {
            this.updateData(legacy, dataIndex); //인덱스도 같이 넣어야 한다.
        }
    }

    public void OnClickCloseButton()
    {
        Destroy(gameObject);
    }
}

 

 

LegacyDataManager 스크립트에서도 index 추가, 람다식 추가

public void OnClickCell(int index) //위에 ICellDelegate 인터페이스 따른다고 하여 자동 생성
{
    //DetailPanelController detailPanelController = Instantiate(detailPanelPrefab, canvas);
    //detailPanelController.SetData(legacyList[index]);

    UpdatePanelController updatePanelController = Instantiate(updatePanelPrefab, canvas);
    updatePanelController.SetData(legacyList[index], index); //index 추가
    updatePanelController.updateData = (legacy, updateIndex) => //람다식 추가
    {
        legacyList[updateIndex] = legacy;
        reloadData();
    };
}

 

전체 코드

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Networking;

public class LegacyDataManager : MonoBehaviour, ICellDelegate //나는 ICellDelegate를 따르겠다.
{
    [SerializeField] private string serviceKey; //서비스 키는 유출되지 않게 하기 위해서 Scene에서 관리할 예정이다.
    [SerializeField] private LegacyCellController legacyCellPrefab;
    [SerializeField] private MoreButtonCellController moreButtonPrefab;
    [SerializeField] private DetailPanelController detailPanelPrefab; //디테일 패널 받아오기
    [SerializeField] private CreatePanelController createPanelPrefab; //추가 패널 받아오기
    [SerializeField] private UpdatePanelController updatePanelPrefab; //업데이트 패널 받아오기
    [SerializeField] private Transform content;
    [SerializeField] private Transform canvas; //디테일 패널 위치 받아올 캔버스

    //불러온 모든 Legacy 리스트 10개씩 계속 데이터를 불러오면 결국 50~60개의 데이터가 쌓이고 전체를 저장해야 한다.
    private List<Legacy> legacyList = new List<Legacy>();

    private void Start()
    {
        StartCoroutine(LoadData(1));
    }
    IEnumerator LoadData(int page, GameObject previousMoreButton = null) //몇 번째 page를 로드할 것인지.
    {
        //서버 URL 설정
        string url = string.Format("{0}?page={1}&perPage={2}&serviceKey={3}", Constants.url, page, Constants.perPage, serviceKey);//여러 개의 문자열을 조합할 수 있다.

        UnityWebRequest request = new UnityWebRequest();
        using (request = UnityWebRequest.Get(url))
        {
            yield return request.SendWebRequest();

            if(request.result != UnityWebRequest.Result.Success)
            {
                Debug.Log(request.error);
            }
            else
            {
                string result = request.downloadHandler.text; //텍스트 형식으로 받아오기. 많이 불러왔었던 빼곡한 글들로 불러온다.

                LegacyData legacyData = JsonUtility.FromJson<LegacyData>(result); //파싱에서 가져온 이름 LegacyData
                Legacy[] legacyArray = legacyData.data;

                for(int i = 0; i < legacyArray.Length; i++)
                {
                    legacyList.Add(legacyArray[i]);

                    LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
                    legacyCellController.SetData(legacyArray[i].명칭, legacyArray[i].종목, legacyArray[i].지정일, legacyList.Count - 1);
                    
                    legacyCellController.cellDelegate = this; //너의 일을 대신해줄 사람은 나다!
                }

                if(previousMoreButton != null) //더보기 버튼 제거
                {
                    Destroy(previousMoreButton.gameObject);
                }

                //더보기 버튼 추가
                int currentTotalCount = legacyData.perPage * (legacyData.page - 1) + legacyData.currentCount; //전체 불러온 데이터 카운트
                if(currentTotalCount < legacyData.totalCount) //아직 불러올 데이터가 있다면
                {
                    MoreButtonCellController moreButtonCellController = Instantiate(moreButtonPrefab, content); //더보기 버튼 생성
                    moreButtonCellController.loadMoreData = () => //변수에게 함수를 전달하기. 람다 형식으로
                    {
                        StartCoroutine(LoadData(legacyData.page + 1, moreButtonCellController.gameObject));
                    };
                }
            }
        }
    }
    /// <summary>
    /// Cell을 클릭했을 때 동작하는 메서드
    /// </summary>
    /// <param name="index">선택한 Cell의 Index</param>
    public void OnClickCell(int index) //위에 ICellDelegate 인터페이스 따른다고 하여 자동 생성
    {
        //DetailPanelController detailPanelController = Instantiate(detailPanelPrefab, canvas);
        //detailPanelController.SetData(legacyList[index]);

        UpdatePanelController updatePanelController = Instantiate(updatePanelPrefab, canvas);
        updatePanelController.SetData(legacyList[index], index); //index 추가
        updatePanelController.updateData = (legacy, updateIndex) => //람다식 추가
        {
            legacyList[updateIndex] = legacy;
            reloadData();
        };
    }

    public void OnClickAddCell()
    {
        CreatePanelController createPanelController = Instantiate(createPanelPrefab, canvas);
        ////createPanelController.createData = new CreateData(SaveData);
        //createPanelController.createData = (legacy) => //람다 형태는 따로 함수를 만들지 않고 즉시 사용할 수 있다. 아래 SaveData 함수를 사용하지 않아도 된다.
        //{
        //    legacyList.Add(legacy);
            
        //    LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
        //    legacyCellController.SetData(legacy.명칭, legacy.종목, legacy.지정일, legacyList.Count - 1);

        //    legacyCellController.cellDelegate = this;
            
        //    legacyCellController.transform.SetSiblingIndex(legacyList.Count - 1); //SetSiblingIndex는 셀의 위치를 바꾸는 것이다. 없었으면 더보기 버튼 다음에 만들어졌을 것이다.
        //};
        //액션을 사용한 방식
        createPanelController.createDataAction = (legacy) => //람다 형태는 따로 함수를 만들지 않고 즉시 사용할 수 있다. 아래 SaveData 함수를 사용하지 않아도 된다.
        {
            legacyList.Add(legacy);

            LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
            legacyCellController.SetData(legacy.명칭, legacy.종목, legacy.지정일, legacyList.Count - 1);

            legacyCellController.cellDelegate = this;

            legacyCellController.transform.SetSiblingIndex(legacyList.Count - 1); //SetSiblingIndex는 셀의 위치를 바꾸는 것이다. 없었으면 더보기 버튼 다음에 만들어졌을 것이다.
        };
    }

    public void SaveData(Legacy legacy)
    {
        //Create Panel Controller에서 전달된 Legacy 객체를 legacyList에 추가
        legacyList.Add(legacy);

        //리스트 갱신
        reloadData();
    }

    //데이터가 추가되었을 때 화면 갱신하기
    void reloadData()
    {
        //모든 cell 지우기
        foreach (Transform transform in content.GetComponentInChildren<Transform>())
        {
            Destroy(transform.gameObject);
        }

        //다시 List의 값을 cell로 생성
        for (int i = 0; i < legacyList.Count; i++)
        {
            LegacyCellController legacyCellController = Instantiate(legacyCellPrefab, content);
            legacyCellController.SetData(legacyList[i].명칭, legacyList[i].종목, legacyList[i].지정일, i); //마지막 매개변수 i로 수정

            legacyCellController.cellDelegate = this; //너의 일을 대신해줄 사람은 나다!
        }
    }
}

 

 

UpdatePanel을 DataManager에 넣는다.

 

 

업데이트가 잘 되는 것을 볼 수 있다.

 

 


 

 

LinkedList

LinkedList는 인덱스나 키를 가지고 있지 않고 First와 Last를 갖는다. 여러 개 정보 중에서 가장 첫 번째와 마지막 정보를 가진다. 두 값을 기준으로 특정한 이후/이전 값을 알 수 있다. Random 엑세스는 불가능하다. 중간에 특정한 값이 삽입되는 것이 용이하다.

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

public class LinkedListSample : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        LinkedList<string> list = new LinkedList<string>();

        list.AddFirst("홍길동");
        list.AddLast("김길동");
        list.AddLast("고길동");
        list.AddFirst("박길동");
        list.AddLast("최길동");
        list.AddLast("함길동");

        LinkedListNode<string> node = list.First; //첫 번째 값을 가져온다.

        Debug.Log(list);
    }
}

 

 


 

ObjectPool 이용해서 셀 관리하기

30개의 셀을 가져와야 한다고 생각하면, 스크롤 뷰에 일부만 보여지게 하는 방법이다. 이미 스크롤 뷰를 벗어난 셀은 Queue를 이용해서 빼내고, 아래에 새로운 셀을 Instantiate 하는 것이 아니라 Reuse Queue에서 쓸 수 있는 것이 있는지 체크하고 쓸 수 있으면 쓰고 쓸 수 없으면 생성한다.

이런 방법은 적은 개수의 셀 보다 몇 천개의 셀을 한꺼번에 관리해야할 때 FPS를 효과적으로 끌어올릴 수 있다.

 

 


 

 

Self Study로 연구해보기

같이 예제를 만들기 전에 혼자 잠깐 연구를 해보면, 전에 오브젝트 풀로 공 던지는 프로젝트를 했었다.

 

[SKKU DT] 43일차 -유니티 오브젝트 풀(Object Pool)

3D -공간 디자인, 라이팅, 3D 오브젝트 UI -Autolayout, TableView 서버 -JSON 배포 -Window-인스톨러/Web-최적화/Mobile-최적화,배포 데이터처리 C# -자료구조, 알고리즘 협업 -소스코드 관리, 작업 관리 오브젝트

lightbakery.tistory.com

 

 

이 프로젝트를 참고해서 스크롤 뷰의 오브젝트 풀을 만들면 될 것 같다.

이전 프로젝트에서는 BulletPool과 Bullet에 각각 BulletPool 스크립트와 Bullet 스크립트를 사용하였다.

일단 Queue를 사용했으므로 스크롤 뷰에서도 Queue를 사용한다.

 

 

BulletPool -> CellPool 스크립트

Queue<Bullet> bulletsQueue = new Queue<Bullet>(); //큐 선언
Queue<Cell> cellsQueue = new Queue<Cell>(); //큐 선언

Bullet을 Cell로 바꾸었고 Cell을 반환하는 함수를 만들어야한다.

 

 

Bullet을 Cell로 바꾸었다.

//BulletPool에서 사용할 수 있는 총알을 반환하는 함수
public Bullet GetBullet() //Bullet을 반환해야 하기 때문에 반환값이 Bullet
{
    if(bulletsQueue.Count > 0) //리스트에 총알이 남아있다면
    {
        Bullet bullet = bulletsQueue.Dequeue();
        bullet.gameObject.SetActive(true);

        return bullet; //총알을 반환한다.
    }
    return null;
}
//더 이상 사용되지 않는 총알을 Bullet Pool에 추가하는 함수
public void AddBullet(Bullet bullet)
{
    bulletsQueue.Enqueue(bullet); //Queue에 값 추가. Add와 같은 역할
}
//CellPool에서 사용할 수 있는 셀을 반환하는 함수
public Cell GetCell() //Cell을 반환해야 하기 때문에 반환값이 Cell
{
    if(cellsQueue.Count > 0) //리스트에 셀이 남아있다면
    {
        Cell cell = cellsQueue.Dequeue();
        cell.gameObject.SetActive(true);

        return cell; //셀을 반환한다.
    }
    return null;
}
//더 이상 사용되지 않는 셀을 CellPool에 추가하는 함수
public void AddCell(Cell cell)
{
    cellsQueue.Enqueue(cell); //Queue에 값 추가. Add와 같은 역할
}

 

 

Bullet -> Cell 스크립트

이전 프로젝트에서는 3D 오브젝트가 한 방향으로 발사되었지만, 이번에는 셀이 3D 상에서 움직일 필요는 없다.

어쨌든 LegacyCell 프리팹에 Cell.cs 스크립트는 넣어야할 것 같다. 아래는 기존 프로젝트에서 썼던 Bullet 스크립트를 내 나름대로 수정해보았다.

public class Cell : MonoBehaviour
{
    public CellPool cellPool;

    //시간 부여 -이전에는 일정 시간이 지나면 없어져야 하지만 이번에는 일정 높이(?)까지 올라가면 없어져야한다.
    //OnBecameInvisible일 때 없어지는 방법도 있다.
    //float killTime = 3f; //시간이 남아있어야 되는지는 의문. 일단 Keep.
    float time = 0;

    private void Awake()
    {
        //이전에는 Awake로 Rigidbody를 가져왔다.
    }

    private void Update()
    {
        /*
        time += Time.deltaTime;
        if(time > killTime)
        {
            this.gameObject.SetActive(false);
        }
        */ 일단 시간초 지나서 없어지는 코드는 Keep.
    }
    /*
    public void Shoot() //GameManager에서 호출할 수 있게 하려면 public이어야 한다.
    {
         rb.AddForce(transform.forward * force); //forward 방향인 z축 방향으로 힘을 준다.
    }
    */ 일단 Shoot() 함수는 필요없어 보임.

    //OnBecameInvisible이면,
    this.gameObject.SetActive(false); //오브젝트 비활성화

    private void OnEnable() //SetActive(true)이면 호출
    {
        //Enable 되는 순간 셀의 transform 초기화
        this.transform.position = Vector3.zero;
    }

    private void OnDisable() //SetActive(false)이면 호출
    {
        this.time = 0;
        bulletPool.AddBullet(this); //BullPool에 담는다
    }
}
728x90
반응형