Limiting movements of a draggable 2D object with collisions

Hello, I’m trying to recreate this Professor Layton puzzle in Unity: https://www.youtube.com/watch?v=JA2AbrI7Z0M

I’m using a Rigidbody2D with a Box Collider 2D to make the object. Here is the code I’m using to make the object drggable using the mouse:

using UnityEngine;

public class DragBlock : MonoBehaviour
{
    public Transform block;
    Rigidbody2D r;
    public float blockDistance;
    public Vector3 startingPos;
    public Vector3 pos;
    public Vector3 distanceBlock;
    public bool dragging = false;
    public int moveCount;
    
    void Start()
    {
        r = block.GetComponent<Rigidbody2D>();
        block.GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Kinematic;
    }
    
    void OnMouseDown()
    {
        startingPos = block.GetComponent<Transform>().position;
        Debug.Log(startingPos);
        dragging = true;
        moveCount ++; 
    }
    
    void OnMouseDrag()
    {
        Vector3  pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        block.GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Dynamic;
        Vector3 distanceBlock = pos - startingPos;
        if(distanceBlock.magnitude < blockDistance && dragging)
        {
            r.linearVelocity = (pos - block.position) * 10;
        }
        else
        {
            r.linearVelocity = Vector3.zero;
        }
        
    }

    void OnMouseUp()
    {
        startingPos = block.GetComponent<Transform>().position;
        dragging = false;
        block.GetComponent<Rigidbody2D>().bodyType = RigidbodyType2D.Kinematic;
        r.linearVelocity = Vector3.zero;
    }
}

I want to restrict the possible distance while dragging to mimic the “sliding” effect of the original puzzle which only lets you cross one block at a time, so I use an If statement to check if the distance travelled is inferior to the distance limit I want to set.
I then use linearVelocity on the Ridigbody2D, I’m using this method instead of directly changing the object’s position to allow the object to collide and be stopped by other objects, like the other blocks or the play area. I also change the bodyType from Kinematic to Dynamic while dragging an object in order for it to remain in place when the player is letting go of the mouse, while still being stopped by other objects.

I’m having multiple problems with this code:

  • -When clicking, the object moves in order to have the mouse on its center. This wastes some of the distance limit I’ve set and reduces the player’s control. I guess this is caused by my Vector3 pos.

  • The distance limit is fine when moving up or down, but I would need a different distance for going left or right ! Since this limit is calculated from the center and the blue blocks are thinner than they are large, going from the center to one of the side faces is about double the distance from the center and the top or bottom faces.

I don’t really know if this code is the most optimal way to recreate something like this, I am very new to game development in general and programming in particular. I used this because it seemed like the only way to have working colliders, but it’s far from how the game is played on DS. I’m willing to start from scratch if somebody has an idea that could either fix my mess or be closer to the original gameplay.

Thank you so much for reading all that ! Have a great new year :slight_smile:

Hi @azyquentin ,

Nice challenge you give yourself :wink:

A few ideas to help you (hopefully). I would not use the rigidbody and the Colliders. Because you don’t need dynamic movement where objects with mass have a velicity and move with inertia.

  1. Consider working with a 2d array/grid/…, using the smallest dimension
  2. Some blocks are 1x1 other blocks are 2x1 on this grid
  3. A seperate function checks whether a block could be moved to a certain grid
  4. When dragging, snap to the closest grid position and do some animation on previewing whether it’s allowed to drag it
  5. On releasing, a block moves to the last ‘allowed preview position’
  6. Update your grid
  7. Update the position of all your blocks
  8. Reward when the main object is at the target x,y location

Hope it helps?!

I can almost guarantee that Professor Layton does not use actual Rigidbody physics.

It’s just using something like what you would use with Tetris to allow different-shaped / different-sized objects to move in a grid.

Tile-based / grid-based 2D games: match3, tetris, chips challenge, rogue, etc:

For any tile-based game such as Match3 or Tetris or a grid-based Roguelike, do all the logical comparisons in your own data storage mechanism for the tiles, such as a 2D array of tiles.

Otherwise you needlessly bind your game logic into Unity objects and the Unity API, making it about 10x more complicated than it needs to be.

If you have no idea how to work with 2D arrays, hurry to some basic C# tutorials for the language portions of it, then look at any good tutorial that uses a 2D array

Here is my Match3 demo using this technique of storing data in a grid. Full source linked in game comments.

It stores all of its data in a 2D array:

PieceController[,] Board;

This allows for easy simple checking in code, not relying on anything like physics.

You should strive to use that pattern for all logic, then only use Unity to present what is happening in the game logic.

If you absolutely insist on trying it with Rigidbody2D physics, MelvMay has a great 2D physics repo you can refer to:

2 Likes