Command pattern (Undo/Redo) system began. (Read desc)

I spent about 6 hours trying to fix this one specific bug involving the move undo. Turns out all I had to do was calm down and think logically instead of typing random bullshit for a few hours until it worked. I'm tired and I thank this for ruining my sleep schedule.
This commit is contained in:
Braedon
2022-01-22 05:44:19 -05:00
parent dea5860e95
commit 1ed2291844
26 changed files with 2206 additions and 77 deletions

View File

@ -0,0 +1,75 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using RhythmHeavenMania.Editor.Commands;
namespace RhythmHeavenMania.Editor
{
public class CommandManager : MonoBehaviour
{
private Stack<IAction> historyStack = new Stack<IAction>();
private Stack<IAction> redoHistoryStack = new Stack<IAction>();
int maxItems = 128;
public bool canUndo()
{
return historyStack.Count > 0;
}
public bool canRedo()
{
return redoHistoryStack.Count > 0;
}
public static CommandManager instance { get; private set; }
private void Awake()
{
instance = this;
}
public void Execute(IAction action)
{
action.Execute();
historyStack.Push(action);
redoHistoryStack.Clear();
}
public void Undo()
{
if (!canUndo()) return;
if (historyStack.Count > 0)
{
redoHistoryStack.Push(historyStack.Peek());
historyStack.Pop().Undo();
}
}
public void Redo()
{
if (!canRedo()) return;
if (redoHistoryStack.Count > 0)
{
historyStack.Push(redoHistoryStack.Peek());
redoHistoryStack.Pop().Redo();
}
}
// this is here as to not hog up memory, "max undos" basically
private void EnsureCapacity()
{
if (maxItems > 0)
{
}
}
private void Clear()
{
historyStack.Clear();
redoHistoryStack.Clear();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6187911411a100640b5f4f3f2f84b912
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,13 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace RhythmHeavenMania.Editor.Commands
{
public interface IAction
{
void Execute();
void Undo();
void Redo();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 64b35e3b4d623144a82ed956ee52a136
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,129 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace RhythmHeavenMania.Editor.Commands
{
public class Selection : IAction
{
public void Execute()
{
throw new System.NotImplementedException();
}
public void Redo()
{
throw new System.NotImplementedException();
}
public void Undo()
{
throw new System.NotImplementedException();
}
}
// I spent 7 hours trying to fix this instead of sleeping, which would've probably worked better.
// I'll go fuck myself later I'm just glad it works
// I give massive props to people who code undo/redo systems
// -- Starpelly
public class Move : IAction
{
public List<Pos> pos = new List<Pos>();
public class Pos
{
public TimelineEventObj eventObj;
public Vector2 lastPos_;
public Vector3 previousPos;
}
public Move(List<TimelineEventObj> eventObjs)
{
pos.Clear();
for (int i = 0; i < eventObjs.Count; i++)
{
Pos p = new Pos();
p.eventObj = eventObjs[i];
p.lastPos_ = eventObjs[i].lastPos_;
p.previousPos = eventObjs[i].transform.localPosition;
this.pos.Add(p);
}
}
public void Execute()
{
}
public void Redo()
{
for (int i = 0; i < pos.Count; i++)
{
EnsureEventObj(i);
pos[i].eventObj.transform.localPosition = pos[i].previousPos;
}
}
public void Undo()
{
for (int i = 0; i < pos.Count; i++)
{
EnsureEventObj(i);
pos[i].eventObj.transform.localPosition = pos[i].lastPos_;
}
}
private void EnsureEventObj(int id)
{
if (pos[id].eventObj == null)
{
pos[id].eventObj = GameManager.instance.Beatmap.entities.Find(c => c.eventObj.eventObjID == pos[id].eventObj.eventObjID).eventObj;
}
}
}
public class Deletion : IAction
{
List<TimelineEventObj> eventObjs;
List<TimelineEventObj> deletedObjs;
public Deletion(List<TimelineEventObj> eventObjs)
{
this.eventObjs = eventObjs;
}
public void Execute()
{
deletedObjs = eventObjs;
for (int i = 0; i < eventObjs.Count; i++)
{
Selections.instance.Deselect(eventObjs[i]);
Timeline.instance.DestroyEventObject(eventObjs[i].entity);
}
}
public void Redo()
{
deletedObjs = eventObjs;
for (int i = 0; i < eventObjs.Count; i++)
{
Selections.instance.Deselect(eventObjs[i]);
Timeline.instance.DestroyEventObject(eventObjs[i].entity);
}
}
public void Undo()
{
for (int i = 0; i < deletedObjs.Count; i++)
{
Beatmap.Entity e = deletedObjs[i].entity;
eventObjs[i] = Timeline.instance.AddEventObject(e.datamodel, false, new Vector3(e.beat, -e.track * Timeline.instance.LayerHeight()), e, true, e.eventObj.eventObjID);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8e13e41a59182b74ba7f0be1e3b58ff9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,34 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using RhythmHeavenMania.Editor.Commands;
public class TestCommand : IAction
{
private GameObject prefab;
private Vector3 pos;
private GameObject spawnedgameObj;
public TestCommand(GameObject prefab, Vector3 pos)
{
this.prefab = prefab;
this.pos = pos;
}
public void Execute()
{
spawnedgameObj = GameObject.Instantiate(prefab, pos, Quaternion.identity);
}
public void Redo()
{
throw new System.NotImplementedException();
}
public void Undo()
{
GameObject.Destroy(spawnedgameObj);
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1e32a4a20b85d944aa030268410b0101
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: