c# 在推送之前检查堆栈中的最后一项是否等于 X?

问题描述

所以我希望我的问题有意义,但我正在 Unity 中开发一个基本的撤消/重做系统,并且我试图防止添加“空”迭代(基本上如果对象没有改变位置) .

我正在使用用于撤消/重做系统的典型堆栈工作流,这是我向撤消堆栈添加方法方法

public static void AddAction(UndoableChange action)
{
    redoStack.Clear();
    undoStack.Push(action);
}

当鼠标按下时在框架上“选择”一个对象时,我正在添加可撤销的更改,然后在鼠标抬起时再次添加。但在我这样做之前,我想确保它不会记录我只是随机点击屏幕(这导致我必须在调用实际撤消之前多次撤消)。
检查堆栈中最后/最近的项目是否不等于我尝试推送的 UndoableChange 的语法是什么?

我已经试过了,但它仍然记录“空”点击:

public static void AddAction(UndoableChange action)
{
    if (undoStack.Count >0 && undoStack.Peek().Equals(action))
        return;

    redoStack.Clear();
    undoStack.Push(action);
}

这个好像应该比较简单。任何帮助将不胜感激!


完整脚本如下

    public struct ObjectState
{
    // The transform this data belongs to
    private Transform transform;

    private Vector3 localPosition;
    private Quaternion localRotation;
    private Vector3 localScale;

    private bool active;

    public ObjectState(GameObject obj)
    {
        transform = obj.transform;
        localPosition = transform.localPosition;
        localRotation = transform.localRotation;
        localScale = transform.localScale;

        active = obj.activeSelf;
    }

    public void Apply()
    {
        transform.localPosition = localPosition;
        transform.localRotation = localRotation;
        transform.localScale = localScale;

        transform.gameObject.SetActive(active);
    }
}
public struct UndoableChange
{
    private ObjectState _before;
    private ObjectState _after;

    public UndoableChange(ObjectState before,ObjectState after)
    {
        _before = before;
        _after = after;
    }

    public void Undo()
    {
        _before.Apply();
    }

    public void Redo()
    {
        _after.Apply();
    }
}

public static class UndoRedoControls
{
    private static Stack<UndoableChange> undoStack = new Stack<UndoableChange>();
    private static Stack<UndoableChange> redoStack = new Stack<UndoableChange>();

    public static void Undo()
    {
        if (undoStack.Count == 0) return;

        var lastAction = undoStack.Pop();

        lastAction.Undo();

        redoStack.Push(lastAction);
        Debug.Log("UNDOING");
    }

    public static void Redo()
    {
        if (redoStack.Count == 0) return;

        var lastAction = redoStack.Pop();

        lastAction.Redo();

        undoStack.Push(lastAction);
        Debug.Log("REDOING");
    }

    public static void AddAction(UndoableChange action)
    {
        if (undoStack.Count >0 && undoStack.Peek().Equals(action))
            return;

        redoStack.Clear();
        undoStack.Push(action);
    }
}

解决方法

正如默认情况下所说,Equals 检查 引用相等 的类,这不是您想要做的,并且 struct 的 afaik 默认情况下失败,因为它们是值类型因此永远不会是同一个实例。

您更希望查看 beforeafter 值是否不同。

您当然可以实现任何检查相等性的方法,但最佳实践是实现 IEquatable<T>

因此你可以使用类似的东西

public struct ObjectState : IEquatable<ObjectState>
{
    // The transform this data belongs to
    public readonly Transform transform;

    private readonly Vector3 localPosition;
    private readonly Quaternion localRotation;
    private readonly Vector3 localScale;

    private readonly bool active;

    public ObjectState(GameObject obj)
    {
        transform = obj.transform;
        localPosition = transform.localPosition;
        localRotation = transform.localRotation;
        localScale = transform.localScale;

        active = obj.activeSelf;
    }

    public void Apply()
    {
        transform.localPosition = localPosition;
        transform.localRotation = localRotation;
        transform.localScale = localScale;

        transform.gameObject.SetActive(active);
    }

    public override bool Equals(object obj)
    {
        if (!(obj is ObjectState objState)) return false;
        return Equals(objState);
    }

    public bool Equals(ObjectState other)
    { 
        if(other.transform != transform) return false;

        // For now using the approximations Unity uses by default
        // with precision of 0.00001
        return other.localPosition == localPosition && other.localRotation == localRotation && other.localScale == localScale;
    }

    public static bool operator ==(ObjectState a,ObjectState b)
    {  
        return a.Equals(b);
    }

    public static bool operator !=(ObjectState a,ObjectState b)
    {
        return !a.Equals(b); 
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = transform.GetHashCode();
            hash = 31 * hash + localPosition.GetHashCode();
            hash = 31 * hash + localRotation.GetHashCode();
            return 31 * hash + localScale.GetHashCode(); 
        }
    }
}

现在您可以实际检查两个 ObjectState 实例是否相等。

现在你可以使用这个

public struct UndoableChange : IEquatable<UndoableChange>
{
    private ObjectState _before;
    private ObjectState _after;

    // Check if this is a valid action
    // It refers to the same object
    // and actually something was changed
    public bool IsValid()
    {
        return _before.transform == _after.transform && _before != _after;
    }

    public UndoableChange(ObjectState before,ObjectState after)
    {
        _before = before;
        _after = after;
    }

    public void Undo()
    {
        _before.Apply();
    }

    public void Redo()
    {
        _after.Apply();
    }

    public override bool Equals (object obj)
    {
        if(!(obj is UndoableChange other)) return false;
        return Equals (other);
    }

    public bool Equals (UndoableChange other)
    {
        return other._before == _before && other._after == _after;
    }

    public static bool operator ==(UndoableChange a,UndoableChange b)
    {
        return a.Equals(b);
    }

    public static bool operator !=(UndoableChange a,UndoableChange b)
    {
        return !a.Equals(b);
    }

    public override int GetHashCode ()
    {
        unchecked
        {
            return _before.GetHashCode() * 31 + _after.GetHashCode();
        }
    }
}

现在你可以检查两件事

if(action.IsValid())

if(undoStack.Peek() != action)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...