在c# /XNA Game中创建撤销功能

本文关键字:创建 功能 Game XNA | 更新日期: 2023-09-27 18:04:06

所以我想要一个撤消功能的MapEditor我正在工作在我的游戏。我让它部分工作。当我点击地图时,它会将新旧贴图存储在不同的列表中。当我按Ctrl + Z时,它会撤销上一个动作,以此类推。

我的问题是当你撤消一些操作,然后添加一个新的操作列表。接下来会发生什么呢?我是应该在列表的末尾添加新操作,还是应该删除列表中当前位置直到最后的所有内容,然后再将新操作添加到列表中?

我的问题是我无法理解这个。我尝试了很多方法,但是当这种情况发生时,它们都有点坏了。

所以,我需要知道如何继续添加新的动作到撤消列表。

添加到撤消列表时的当前代码:

private void UpdateCorrectedTiles(Dictionary<TileSide, Tile> correctedTiles, bool saveEditedTiles)
{
    List<Tile> tmpUndoTilesList = new List<Tile>();
    List<Tile> tmpRedoTilesList = new List<Tile>();
    foreach (Tile tile in tiles)
    {
        foreach (KeyValuePair<TileSide, Tile> correctedTile in correctedTiles)
        {
            if (tile.GetTilePosition() == correctedTile.Value.GetTilePosition())
            {
                if (correctedTile.Key == TileSide.Clicked && saveEditedTiles
                    && Tile.IsTileChanged(previousClickedTile, correctedTile.Value))
                {
                    Tile undoTile = Tile.CreateCloneTile(previousClickedTile);
                    Tile redoTile = correctedTile.Value;
                    tmpUndoTilesList.Add(undoTile);
                    tmpRedoTilesList.Add(redoTile);
                }
                TileInfo info = correctedTile.Value.GetTileInfo();
                Vector2 frames = correctedTile.Value.GetCurrentFrame();
                tile.SetTileInfo(info);
                tile.SetCurrentFrame(frames);
            }
        }
    }
    if (saveEditedTiles && tmpUndoTilesList.Count > 0 && tmpRedoTilesList.Count > 0)
    {
        undoTilesList.Add(tmpUndoTilesList);
        redoTilesList.Add(tmpRedoTilesList);
        currentUndoRedoIndex = undoTilesList.Count - 1;
    }
}

这段代码所做的是在Foreach中,它将循环遍历所有已更正的tile。如果被更正的贴图是被点击的贴图,并且它实际上已经改变了,它将把它添加到撤消和重做列表中。你现在应该忽略重做列表,因为重做功能还没有实现。我想让撤消功能先工作。

所以在函数的最后一部分,我将新的动作添加到列表中,但是,再一次,我认为我需要做一些其他的事情,然后添加当你在列表的某个地方,而不是在最后。

希望你能理解我想要什么。
谢谢!

在c# /XNA Game中创建撤销功能

撤销的标准预期行为是你可以撤销一个动作,然后重做回到你使用撤销之前…除非你执行一个新的动作!新操作删除所有重做能力。

帮助概念化(parent显示当前状态):

Act1 -> Act2 -> (Act3)

撤销两次给出…

(Act1) -> Act2 -> Act3

现在,如果没有采取新的操作,您可以在此时重做。现在如果用户执行NewAction我们得到:

Act1 -> (NewAct2)

…就是这样!第三幕现在只是被遗忘了,像昨天的垃圾一样被丢弃了。替代方案太难实现,而且使用起来非常不直观!比如,如果你创建了一个平铺,改变了颜色,然后在平铺创建之前撤消并在其他地方创建了一个平铺。如果你重做,新贴图会改变颜色吗?旧的瓷砖会以新的颜色重新出现吗?如果您再次撤消,重做会返回第一组编辑而不是最后一组吗?讨厌的东西!

这就是像Photoshop这样复杂的程序是如何工作的,所以期望一个地图编辑器以其他方式工作是不合理的。许多程序甚至不支持重做,所以它取决于你,如果你甚至想支持它!这只是一个人把事情弄得比实际困难的时候。

基本上,你看起来可以这样走了!

一般来说,您应该有一个IMapChange堆栈,其中IMapChange看起来像这样:

public interface IMapChange
{
    //Performs the change on a TileMap
    Boolean PerformChange(TileMap map);
    //Reverts the change on a TileMap
    Boolean RevertChange(TileMap map);
}

你应该有一个方法被调用来进行更改:

public void SetTile(Vector2 position, int tileId)
{
    Tile oldTile = Map.GetTile(position);
    Tile newTile = new Tile(position, tileId);
    MapChange change = new MapChange(oldTile, newTile);
    //Only push to cahngestacks if successfull    
    if (change.PerformChange(Map))
    {
        ChangeStack.Push(change);
        //We don't want you to be able to "redo" anymore if you do something new.
        RedoStack.Clear();
    }
}
public void RemoveTile(Vector2 position)
{
    Tile oldTile = Map.GetTile(position);
    Tile newTile = null;
    MapChange change = new MapChange(oldTile, newTile);
    //Only push to changestacks if successfull    
    if (change.PerformChange(Map))
    {
        ChangeStack.Push(change);
        //We don't want you to be able to "redo" anymore if you do something new.
        RedoStack.Clear();
    }
}

撤销可以像这样:

public void Undo()
{
    var lastChange = ChangeStack.Pop();
    //Try to revert. If revert fails, put the change back in the stack
    if (!lastChange.RevertChange(Map))
        ChangeStack.Push(lastChange);
    else
        RedoStack.Push(lastChange);
}

和撤消:

public void Redo()
{
    var lastChange = RedoStack.Pop();
    //Try to perform. If successfull, put back in ChangeStack
    if (lastChange.PerformChange(Map))
        ChangeStack.Push(lastChange);
    else
        RedoStack.Push(lastChange);
}

PerformChange和RevertChange基本上:

执行:
-如果oldTile不为空,尝试从map中删除它。
-插入newTile到地图。

回复:
-如果newTile不为空,就从地图中删除它
-插入oldTile到映射。