1
\$\begingroup\$

I've got a problem with my undo system that ive made, the idea is I want to make a sort puzzle game like this => https://www.youtube.com/watch?v=VdXi9UQhOng

WHAT I UNDERSTAND

  • There is some movement when click the holder to another holder, the movement is also based on contigues of the object, for example A.{a,b,c,c} and B.{c,c,c} the c,c from A can move to another holder if in another holder have a same of object in this case is c but the next of holder cant be full, if there is full the remainig is stay on current holder for example A.{a,b,c,c} and B.{c,c,c,c} so the A and B only can move c once, either A to B vice verca.
  • Undo System that I think almost same like movement but when click the button instead of click the holder.

Thats for the mechanics from the game that i understand so far.

WHAT IM DOING SO FAR

So I implement all of that inside script down below

  • Holder.cs

Move Up: This when the holder i click then its select the contigues objects and store it to frontObjects List

 public void MoveObjectsUp(float distance)
 {
     frontObjects.Clear();

     for (int i = objects.Count - 1; i >= 0; i--)
     {
         var obj = objects[i];

         if (obj != null && (frontObjects.Count == 0 || obj.ObjectGroupId == frontObjects[0].ObjectGroupId))
         {
             frontObjects.Insert(0, obj);  // Insert at the beginning of the list
         }
         else
         {
             break; // Stop when a different ObjectGroupId is encountered
         }
     }

     foreach (var obj in frontObjects)
     {
         if (obj != null)
         {
             obj.ActivateHighlight();

             Move(obj, distance);
         }
     }

     IsPending = true;
 }

Move Down: This when the holder i click again after Move Up it's like a toggle, also clearing the frontObjects from list

 public void MoveBack(float distance)
 {
     foreach (var obj in frontObjects)
     {
         if (obj != null)
         {
             obj.DeadActivateHighlight();

             Move(obj, -distance);
         }
     }

     frontObjects.Clear();

     IsPending = false;
 }

MoveToHolder: move the current object to next holder, there is objects list to store objects into a list and backObject list to store the undo objects into a list, and unmovedObjects list to store remaining objects that doesnt move

 public void MoveToHolder(List<Object> objs)
 {
     if (IsPending)
     {
         IsPending = false;
     }

     int objectsToAdd = Mathf.Min(objs.Count, maxObject - objects.Count);
     int totalObject = objects.Count + objs.Count;

     Debug.Log($"Object to add:{objectsToAdd}, current list count: {objects.Count}, total object: {totalObject}");

     for (int i = 0; i < objs.Count; i++)
     {
         if (objects.Count < maxObject)
         {
             objects.Add(objs[i]);

             if (!backObjects.Contains(objs[i]))
             {
                 backObjects.Add(objs[i]);
             }

             objs[i].Move(GetObjectPosition(objects.Count - 1));
         }
         else
         {
             unmovedObjects.Add(objs[i]);
         }

     }
 }

UnMoveListThenRemove: this is for clear the current unmoved list after move to next holder

 public void UnmoveListThenRemove(Holder fromHolder, Holder toHolder)
 {
     if (fromHolder.unmovedObjects.Count > 0)
     {
         foreach (var obj in fromHolder.unmovedObjects)
         {
             toHolder.objects.Add(obj);
         }

         fromHolder.unmovedObjects.Clear();

         // Move the Object again
         for (var i = 0; i < toHolder.objects.Count; i++)
         {
             toHolder.objects[i].transform.SetParent(toHolder.contentParent.transform);

             toHolder.objects[i].transform.position = GetObjectPosition(i);
         }
     }
 }

RemoveFromList: this is for clear the objects list after MoveToHolder

 public void RemoveFromList()
 {
     objects.RemoveAll(obj => frontObjects.Contains(obj));

     // Clear Frontobject
     foreach (var obj in frontObjects)
     {
         if (obj != null)
         {
             obj.DeadActivateHighlight();
         }
     }

     frontObjects.Clear();

     IsPending = false;
 }

Remove Undo: this is for clear the backObjects list, usually used after undo

 public void RemoveUndo()
 {
     objects.RemoveAll(obj => backObjects.Contains(obj));

     // Clear BackObjects
     foreach (var obj in backObjects)
     {
         if (obj != null)
         {
             obj.DeadActivateHighlight();
         }
     }

     backObjects.Clear();

     IsPending = false;
 }
  • LevelManager.cs

In this script is how that I called from holder script

public struct MoveData
{
   public Holder FromHolder { get; set; }
    public Holder ToHolder { get; set; }
    public List<Object> ObjectsData { get; set; }
}
public class LevelManager : MonoBehaviour
{
  private List<Holder> _holders = new List<Holder>();
  public Stack<MoveData> _undoStack = new Stack<MoveData>();
    

 public void OnClickUndo()
 {
     // Problem here
     if (CurrentState != State.Playing || _undoStack.Count <= 0)
         return;

     var moveData = _undoStack.Pop();
     MoveObjectUndo(moveData.ToHolder, moveData.FromHolder);
 }

 private void Update()
 {
     if (CurrentState != State.Playing)
         return;

     // For Click on Holder
     if (Input.GetMouseButtonDown(0))
     {
         var collider = Physics2D.OverlapPoint(_camera.ScreenToWorldPoint(Input.mousePosition));
         if (collider != null)
         {
             var holder = collider.GetComponent<Holder>();

             if (holder != null)
                 OnClickHolder(holder);
         }
     }
 }
private void OnClickHolder(Holder holder)
{
    //Debug.Log("Clicked " + holder.gameObject.name);

    var pendingHolder = _holders.FirstOrDefault(h => h.IsPending);

    if (pendingHolder != null && pendingHolder != holder)
    {
        if (holder.objects.Count == 0)
        {
            MoveObjectsFromOneToAnother(pendingHolder, holder);
        }
        else
        {


            for (int i = 0; i < pendingHolder.frontObjects.Count; i++)
            {
                List<Object> movedObjects = new List<Object>(pendingHolder.backObjects);

                if (pendingHolder.frontObjects.All(frontObj =>
                holder.objects.Any(holderObj =>
                frontObj.ObjectGroupId == holderObj.ObjectGroupId &&
                frontObj.ObjectGroupId == holder.objects.Last().ObjectGroupId)) && !holder.IsFull)
                {
                    // Problem here
                    _undoStack.Push(new MoveData
                    {
                        FromHolder = pendingHolder,
                        ToHolder = holder,
                        ObjectsData = movedObjects
                    });

                    MoveObjectsFromOneToAnother(pendingHolder, holder);

                }
                else
                {
                    holder.IsPending = true;
                    pendingHolder.IsPending = false;
                }
            }
        }

        // Like Switch
        if (pendingHolder != null && !pendingHolder.frontObjects.All(frontObj =>
                holder.objects.Any(holderObj =>
                frontObj.ObjectGroupId == holderObj.ObjectGroupId &&
                frontObj.ObjectGroupId == holder.objects.Last().ObjectGroupId)))
        {
            pendingHolder.MoveBack(.2f);

            holder.MoveObjectsUp(.2f);
        }
    }
    else
    {
        if (holder.objects.Any())
        {
            holder.IsPending = !holder.IsPending;
        }

        if (holder.IsPending)
        {
            holder.MoveObjectsUp(.2f);
        }
        else
        {
            holder.MoveBack(.2f);
        }
    }
}

private void MoveObjectsFromOneToAnother(Holder fromHolder, Holder toHolder)
{
    previousHolder = fromHolder;

    // Move the object
    toHolder.MoveToHolder(fromHolder.frontObjects);

    //Move To its Parent
    toHolder.MoveGameObjectToItsParent();

    //Remove the list
    fromHolder.RemoveFromList();

    fromHolder.UnmoveListThenRemove(toHolder, fromHolder);
}

private void MoveObjectUndo(Holder fromHolder, Holder toHolder)
{
    // Move the object
    toHolder.MoveToHolderUndo(fromHolder.backObjects);

    //Move To its Parent
    toHolder.MoveGameObjectToItsParent();

    ////Remove the list
    fromHolder.RemoveUndo();

    //toHolder.ClearPreviousObject();

    //fromHolder.UnmoveListThenRemove(toHolder, fromHolder);
}
}

ISSUE

So i think all working but it has bug in the undo system for now.

the issue is when I click the undo its working only on the 1 object or 2 contigues objects, when object more that that inflict bug like the all of objects move to previous holder, sometimes when I move the object to the empty holder I cant undo the object so object stay on that holder, also some times move the other index

  • Working one

  • When move to empty Holder (bug)

  • Other Index that move (bug)

 

  • More of 2 contigues objects move all of them to previous holder (bug)

Am i missing something here? I have no idea how could this happen?

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Related: en.wikipedia.org/wiki/Memoization \$\endgroup\$ Commented Nov 19, 2023 at 0:59
  • \$\begingroup\$ @Evorlor much appreciated, I'll try to implement that, but for now how could the issue coming? \$\endgroup\$ Commented Nov 19, 2023 at 10:21
  • \$\begingroup\$ You can search the internet for "command pattern". Often the examples explaining it are about implementing undo/redo functionality. \$\endgroup\$ Commented Nov 22, 2023 at 8:54

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.