메멘토는 라틴어로서 기억이라는 뜻을 가진다.

이전 데이터를 기억하고 때로는 복원하는 기능을 메멘토 패턴이라고 한다.


메멘토 패턴을 쓰는 경우는 많이 있지만, 몇가지 예시를 들겠다.

1. 캐릭터 장비를 새로 세팅했다가 [취소]버튼을 누르는 경우, 예전 세팅으로 돌아가기

2. 액션게임에서 캐릭터가 죽으면 이전 세이브 시점의 장소와 레벨로 돌아가기


이 외에도 많은 경우에 이 패턴을 쓸 수 있다. 요점은 데이터를 저장하고 필요시 그 데이터를 복원하는 것이다.




예제에서는 캐릭터가 죽으면 이전 세이브 시점의 데이터를 복원하는 것을 해보겠다.



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

//플레이어의 레벨과 자동저장된 곳의 이름을 담을 수 있는 데이터 클래스.
public class AutoSaveData
{
    public int level = -1;
    public string location = "시작의 마을";

    public AutoSaveData()
    {

    }

    public AutoSaveData(int level, string location)
    {
        this.level = level;
        this.location = location;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//오토 세이브 데이터를 기억하고 복원해주는 관리 클래스
public class AutoSaveManager
{
    //AutoSaveManager는 자주 쓰이고 
    //게임 내에 하나만 존재할 것이므로 싱글톤으로 구현.
    private static AutoSaveManager instance = null;

    public static AutoSaveManager Instance
    {
        get
        {
            if(instance == null)
            {
                instance = new AutoSaveManager();
            }
            return instance;
        }
    }

    private List autoSaveDataList = new List();


    //오토 세이브 데이터를 리스트에 넣어 보존.
    public void PushAutoSaveData(AutoSaveData data)
    {
        if(autoSaveDataList == null)
        {
            autoSaveDataList = new List();
        }
        autoSaveDataList.Add(data);
    }

    //오토 세이브 데이터를 리스트에서 빼오고 리스트에서 삭제. 
    public AutoSaveData PopLastAutoSaveData()
    {
        AutoSaveData data = GetLastAutoSaveData();

        //최근 데이터가 존재한다면 list에서 삭제
        if (data != null)
        { 
            autoSaveDataList.Remove(data);
        }

        return data;
    }

    //가장 최근 오토 세이브 데이터 불러오기.
    public AutoSaveData GetLastAutoSaveData()
    {
        //최소한의 예외처리
        if(!CheckListUsable())
        {
            Debug.Log("데이터 불러오기 실패 : GetLastAutoSaveData");
            return null;
        }

        //프로그래밍 언어는 숫자를 셀 때 0부터 시작하므로, 마지막 세이브 데이터의 순번은 1을 빼준다.
        return autoSaveDataList[autoSaveDataList.Count - 1];
    }

    //특정위치의 오토 세이브 데이터 불러오기
    public AutoSaveData GetAutoSaveDataAt(int index)
    {
        //최소한의 예외처리
        if (!CheckListUsable())
        {
            Debug.Log("데이터 불러오기 실패 : GetAutoSaveDataAt.");
            return null;
        }
        else if (autoSaveDataList.Count <= index)
        {
            Debug.Log("요청한 인덱스가 리스트의 크기를 넘어섬.");
            return null;
        }

        return autoSaveDataList[index];
    }

    //autoSaveDataList가 정의되어있는지, 혹은 비어있는지 체크
    private bool CheckListUsable()
    {
        if (autoSaveDataList == null)
        {
            Debug.Log("오토 세이브 데이터 리스트가 정의되지 않음.");
        }
        else if (autoSaveDataList.Count == 0)
        {
            Debug.Log("오토 세이트 데이터 리스트에 저장된 데이터가 없음.");
        }
        else
        {
            return true;
        }

        return false;
    }

    //데이터 클리어
    public void ClearAutoSaveDataList()
    {
        autoSaveDataList.Clear();
    }  
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//실사용 예. 실제로는 자동저장 시점에 데이터를 저장해주고, 플레이어가 죽으면 복원하면 될 것이다.
public class SceneGame : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        AutoSaveData data = new AutoSaveData(1, "마을 입구");
        AutoSaveManager.Instance.PushAutoSaveData(data);

        AutoSaveData lastData = AutoSaveManager.Instance.GetLastAutoSaveData();
        Debug.Log(lastData.level + ", " + lastData.location);
    }
}




참고 - Pop과 Get

이전 데이터를 복원할 때 2가지 방법이 있다.

1. 이전 데이터를 복원하고 그 데이터를 더이상 기억하지 않는다(1회성 기억). = pop기능

2. 이전 데이터를 복원하고 그 데이터를 계속 남겨둔다. 한번 기억한 데이터는 원할 때 몇번이고 복원한다(지속성 기억). = get기능


pop과 get은 관용인 표현이며, 어느 때는 그 경계가 모호하다. 

보통 자료구조에서 stack이나 queue를 공부할 때 push와 pop이라는 이름을 사용하며, push는 데이터를 배열(또는 벡터)에 넣는 기능, pop은 데이터를 배열에서 빼내오는(그리고 배열에서 삭제함) 기능을 뜻한다.

get set은 유니티 프로그래머라면 property를 공부할 때 보게 될텐데, get은 property 들어있는 값을 가져오는 기능, set은 데이터를 property에 값을 넣어주는 기능이다.


push와 set이 비슷하고,

pop과 get이 비슷한 개념인 셈이다.


그래서 나는 pop을 사용하면 데이터를 빼오면서 배열에서 삭제해주고, get은 데이터를 배열에서 삭제하지 않는 식으로 함수의 이름을 지어준다. 이건 동료 프로그래머와의 소통을 위해 주석을 달아주어야 하는 문제라고 생각한다. 


자신의 게임에 어느 방식이 좋은지는 스스로 선택하도록 한다. 또는 pop과 get 2가지 함수를 모두 만들어줘도 상관 없을것이다.


+ Recent posts