2021. 6. 16. 13:56ㆍ기술/C#
Undo 기능을 구현할 때, 기존 자료구조를 복사해서 들고 있어야 한다.
기존 자료구조는 Dictionary로 구현이 돼있는데 이를 Deep Copy하는 방법을 알아보자.
Dictionay<uint, Node> listNode = new Dictionary<uint, Node>();
스택 자료구조로 구현해서 위에서 빼도 되는데 최대 N개만 들고 있기를 구현하려고 List 자료구조 사용
List<Dictionary<uint, Node>> stackNode = new List<Dictionary<uint, Node>>();
생성자를 사용해서 listNode를 복사하려고 했으나 이렇게 하면 참조값이 복사가 된다.
그러므로 기존 값이 변경됨에 따라 새로 생성한 자료들도 값이 변경되게 된다.
m_listUndo.Add( new Dictionary<uint, Node>(listNode) );
그래서 ICloneable을 사용하는데 값 복사가 필요한 Node 클래스에 인터페이스를 상속하고 구현한다.
class Node : ICloneable
{
public object Clone()
{
// 값을 하나씩 대입해주어도 되지만 변수가 많을 경우를 대비해
Node node = MemberwiseClone() as Node;
return node;
}
}
유저가 인풋 값이 있을 때 특정 부분에서 RecodeUndo() 함수를 실행한다.
void RecodeUndo()
{
var cloneNode = new Dictionary<uint, Node>();
cloneNode = m_mapNode.ToDictionary( node => node.Key, node => (Node)node.Value.Clone());
m_listUndo.Add(cloneNode);
}
자 그런데 이 Node 클래스가 Tile 클래스를 참조하고 있다.
그럼 Tile 클래스도 함께 Clone()해주어야 한다.
class Tile : IConeable
{
public object Clone()
{
return MemberwiseClone() as Tile;
}
}
class Node : ICloneable
{
public object Clone()
{
// 값을 하나씩 대입해주어도 되지만 변수가 많을 경우를 대비해
Node node = MemberwiseClone() as Node;
if (m_Tile != null)
node.tile = m_tile.Clone() as Tile;
return node;
}
}
Undo() 기능을 실행할 때는 List의 뒤에서 부터 빼고 특정 N개가 넘어가면 앞에 자료들도 빼주어야 한다.
그렇게 들고 있는 자료들을 특정 N개로 맞춘다.
// RecodeUndo() 함수를 수정 : 유저가 불필요한 행동을 했을 때는 undo에 기록하지 않게 하기 위해서
void UserAction()
{
// 행동 후에 값이 변경되니 미리 만들어 둔다
bool undo = false;
var cloneNode = new Dictionary<uint, Node>();
cloneNode = m_Node.ToDictionary(node => node.Key, node => (Node)node.Value.Clone());
// 유저 행동
// ...
// undo해야할 행동을 하였는가?
if (undo)
{
RecodeUndo(cloneNode); // 기록한다
if (undoable) // UI 표시 갱신
panelUI.CheckUndoUI();
}
}
void RecodeUndo(Dictionary<uint, Node> cloneNode)
{
// 연속으로 최대 N번까지 되돌리기 가능
if (m_listUndo.Count >= N)
m_listUndo.RemoveAt(0); // 리스트의 앞을 빼준다.
m_listUndo.Add(cloneMapNode);
}
'기술 > C#' 카테고리의 다른 글
박싱과 언박싱 struct와 class의 차이점에 대해서 (0) | 2021.08.06 |
---|