Simulate gravity in a top down 2D rpg?

I’m attempting to simulate gravity in my game. It’s a game similar to this:

I want to be able to blow things up and have the debris follow a realistic arc in my game world and then land on the game world floor.

Currently I’m using 2D physics and my world is in the XY-plane, the camera is above in Z looking down. I guess that’s fairly standard for Unity 2D.

The problem is obviously that my world is actually completely flat in the XY-plane, but it’s simulating sort of a 45 degree angle (like in the video). My objects will have to arc along the Y-axis, but I have to fake this arc and I have to fake a world floor for my items to land on.

My initial idea was to to fake a starting height for the debris, a max height of the arc, and then simulate gravity along the Y-axis. When my debris goes below -1 * starting height it would “hit the ground” and I would stop simulating gravity. All the way through the arc I would draw a drop shadow at -1 * starting height for the debris. That way the shadow would get closer and closer to the debris as it reached the ground. But I’m struggling. :stuck_out_tongue: The arcs look so artificial. Nothing like real gravity.

And I sort of wanted to fake bouncing and rolling as well, but unless I can get this working I’m obviously not getting to that. Unless I can get this working I’m not going to continue with this project I guess because I just love blowing things up. And I want to blow up 2d pixel art in this game. :stuck_out_tongue:

Edit: I also quickly tried 3D physics in an attempt to use gravity along the z-axis and some trickery to make this work, but that looked even worse. So I guess I have to stick with 2D physics, but I guess I have to fake most of this no matter what because of how unrealistic such a 2D top down game world is?

Slept on it and thought of a simple solution which I wrote down when I woke up. Tried it out now and it seems to work pretty well. :stuck_out_tongue:

You could even multiply the localscale of the sprite based on the height value to simulate perspective to make it even more realistic. But I’m using pixel art so that looked a bit wonky for me. I’m keeping the heights within reasonable levels to avoid the lack of perspective to be really noticeable, but it already looks much better than any physics I’ve seen in a top down 2D game.

In the future I want to try and add bouncing, variable friction and maybe even pitch/roll for the debris. That would require draw it from multiple angles and showing the correct sprite depending on the angle, but I think it could look really cool

The script is attached.

I’ve attached this script to my debris gameobject. It has a rigidbody2D and a shadow sprite and as a child of that there’s the sprite for the debris itself. The gameobject is instantiated with a velocity for the rigidbody2D, but then in flight I only manipulate the sprite, not the gameobject. That way the shadow follows a straight line, but the sprite looks like it follows an arc which makes it look 3D.

I’m not sure about the double Time.deltaTime, but that was the only way I was able to make the script framerate independent. In my earlier attempt there was very noticable differences in how far the debris flew depending on the framerate.

Edit: One thing I’ve written down as a todo for myself which I forgot to mention here is that I plan to multiply the localscale of the shadow sprite based on the height of the sprite. I’m also thinking about setting the opacity of it. The shadow fading out and fading in depending on the height of the arc will really sell this effect I think.

using UnityEngine;
using System.Collections;

public class Debris : MonoBehaviour {

    // The fake gravity pulling this object down along the y-axis.
    float gravity = -500f;

    // The velocity we're moving at. Only for the y-axis.
    float velocity;

    // Our initial velocity up along the y-axis.
    float initialVelocity;

    // Our sprite which is a child of this gameobject. This is the the thing
    // the player sees so this is the object we manipulate with our fake gravity.
    Transform sprite;

    // A flag we set to true when our sprite has landed on our fake ground plane.
    bool sleep = false;

    // A place for us to store the angularVelocity for the rigidbody2D. We don't want
    // to rotate the entire rigidbody because that would offset the shadow sprite which
    // follows the ground plane so we rotate the sprite by this velocity instead.
    float angularVelocity;

    // The amount we place the sprite above the game object to simulate it flying over
    // the ground. The shadow follows the gameobject in a straight line so when we launch
    // the sprite in an arc it looks like it's flying even though everything is still
    // entirely flat.
    float height;

    void Start () {
        // Find the sprite so we can position it in Update().
        sprite = transform.FindChild("Sprite");

        // Set the initial velocity.
        initialVelocity = Random.Range(50f, 400f);
        velocity = initialVelocity;

        // Get the angluar velocity and zero it for the rigidbody2D.
        angularVelocity = rigidbody2D.angularVelocity;
        rigidbody2D.angularVelocity = 0f;
    }

    void Update() {
        if (sleep) {
            return;
        }

        // Calculate the new height.
        velocity += gravity * Time.deltaTime;
        height += velocity * Time.deltaTime;

        // Set the local position of the sprite to be the height.
        // Because of the initial upwards velocity it will start going up
        // and the gravity will pull it down again so it follows an arc.
        // The gameobject the sprite is attached to will move in a straight line however
        // and so will the attached shadow sprite so we'll get the illusion of 3D physics.
        sprite.localPosition = new Vector3(0, height, 0);

        // Rotate the sprite.
        sprite.Rotate(0, 0, angularVelocity * Time.deltaTime);

        // If the height is 0 we've landed and we stop moving.
        // The rigidbody2D's velocity is what moves us in a straight line across the ground,
        // we're only faking the vertical part.
        if (height <= 0) {
            rigidbody2D.velocity = Vector2.zero;
            sleep = true;
        }
    }
}

Thanks for this, not only did it provide a great script but also a logical way for the shadows which I have been looking everywhere for.

This helps my project as well !! Thank you !!