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\$
    – Evorlor
    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\$
    – Nikaas
    Commented Nov 22, 2023 at 8:54

0

You must log in to answer this question.

Browse other questions tagged .