Search Unity

2D Planetary Gravity?

Discussion in 'Scripting' started by VirusXProgramming, Jan 17, 2015.

  1. VirusXProgramming

    VirusXProgramming

    Joined:
    Nov 15, 2014
    Posts:
    67
    I have a Gravity script in my 2D game but 1: It isn't very efficient. I need to implement the equation of m/d squared where m is mass and d is distance. I can't figure out a way to implement it. This is how far i've gotten
    using UnityEngine;
    using System.Collections;

    public class Gravity : MonoBehaviour {

    public float maxGravDist = 4.0f;
    public float maxGravity = 35.0f;

    GameObject[] planets;

    void Start () {
    planets = GameObject.FindGameObjectsWithTag("Planet");
    }

    void FixedUpdate () {
    foreach(GameObject planet in planets) {
    float dist = Vector3.Distance(planet.transform.position, transform.position);
    if (dist <= maxGravDist) {
    Vector3 v = planet.transform.position - transform.position;
    rigidbody2D.AddForce(v.normalized * (1.0f - dist / maxGravDist) * maxGravity);
    //This is an alternative
    //rigidbody2D.AddForce(v.normalized*maxGravity/(dist*dist+1.0f));
    }
    }
    }
    }
    So basically I want it to calculate the mass of the planet(tagged planet) and use the rigidbody to atract itself. One problem with this script is it's the same gravity for all objects and gravity still affects it if it is out of range of the gravity.
    Thanks in advance!
     
  2. VirusXProgramming

    VirusXProgramming

    Joined:
    Nov 15, 2014
    Posts:
    67
  3. VirusXProgramming

    VirusXProgramming

    Joined:
    Nov 15, 2014
    Posts:
    67
    I know im not supposed to bump more than every twelve hours but this is getting pathetic - My first question and no answers? Cme on!
     
  4. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    Learn how to use script tags correctly, please. Code is no fun to look at if you just paste it into the dialog.

    I made something similar to this one day, for S***s and giggles. Now it isn't physically accurate, but it makes for reasonably good game code. It allows you to set the maximum distance from a body that it is effected by gravity, and the the gravitational pull deceases by it's square until it reaches the maximum distance and becomes zero.

    Relevant code: (Using script tags, note)
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3. using System.Collections;
    4.  
    5. [RequireComponent(typeof(Rigidbody2D))]
    6. public class GravitationalBody : MonoBehaviour {
    7.  
    8.     public float maxDistance;
    9.     public float startingMass;
    10.     public Vector2 initialVelocity;
    11.  
    12.     //I use a static list of bodies so that we don't need to Find them every frame
    13.     static List<Rigidbody2D> attractableBodies = new List<Rigidbody2D>();
    14.  
    15.     void Start() {
    16.  
    17.         SetupRigidbody2D();
    18.         //Add this gravitational body to the list, so that all other gravitational bodies can be effected by it
    19.         attractableBodies.Add (rigidbody2D);
    20.  
    21.     }
    22.  
    23.     void SetupRigidbody2D() {
    24.  
    25.         rigidbody2D.gravityScale = 0f;
    26.         rigidbody2D.drag = 0f;
    27.         rigidbody2D.angularDrag = 0f;
    28.         rigidbody2D.mass = startingMass;
    29.         rigidbody2D.velocity = initialVelocity;
    30.  
    31.     }
    32.  
    33.     void FixedUpdate() {
    34.  
    35.         foreach (Rigidbody2D otherBody in attractableBodies) {
    36.  
    37.             if (otherBody == null)
    38.                 continue;
    39.  
    40.             //We arn't going to add a gravitational pull to our own body
    41.             if (otherBody == rigidbody2D)
    42.                 continue;
    43.  
    44.             otherBody.AddForce(DetermineGravitationalForce(otherBody));
    45.  
    46.         }
    47.  
    48.     }
    49.  
    50.     Vector2 DetermineGravitationalForce(Rigidbody2D otherBody) {
    51.  
    52.         Vector2 relativePosition = rigidbody2D.position - otherBody.position;
    53.    
    54.         float distance = Mathf.Clamp (relativePosition.magnitude, 0, maxDistance);
    55.  
    56.         //the force of gravity will reduce by the distance squared
    57.         float gravityFactor = 1f - (Mathf.Sqrt(distance) / Mathf.Sqrt(maxDistance));
    58.  
    59.         //creates a vector that will force the otherbody toward this body, using the gravity factor times the mass of this body as the magnitude
    60.         Vector2 gravitationalForce = relativePosition.normalized * (gravityFactor * rigidbody2D.mass);
    61.  
    62.         return gravitationalForce;
    63.        
    64.     }
    65.    
    66. }
    67.  
    Also included is a working scene. The camera tracks all the bodies in play, and you can see how they orbit and clump together.
    And working scene attached:
     

    Attached Files:

    MD_Reptile and VirusXProgramming like this.
  5. VirusXProgramming

    VirusXProgramming

    Joined:
    Nov 15, 2014
    Posts:
    67

    Wow really nice thank you!

    I'm new to the forums so ill use script tags next time!!
     
    BenZed likes this.
  6. VirusXProgramming

    VirusXProgramming

    Joined:
    Nov 15, 2014
    Posts:
    67

    Just one thing.
    Whenever I start the game the console seems to get spammed with these messages.
    Rigidbody2D.AddForce(force) assign attempt for 'New Sprite' is not valid. Input force is { NaN, NaN }.
    UnityEngine.Rigidbody2D:AddForce(Vector2)
    Gravity:FixedUpdate() (at Assets/Scripts/Gravity.cs:44)
     
  7. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    You probably haven't set "Max Distance" on the gravitational body in the editor.
     
    VirusXProgramming likes this.
  8. VirusXProgramming

    VirusXProgramming

    Joined:
    Nov 15, 2014
    Posts:
    67
    Ok thanks!
     
  9. VirusXProgramming

    VirusXProgramming

    Joined:
    Nov 15, 2014
    Posts:
    67
    Just one last question here. The gravity has been going all good but as soon as I start messing around with player movement it starts freaking out.
    Could I somehow make it comppatible?
    This is my code:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4.  
    5. [RequireComponent(typeof(AudioSource))]
    6. public class PlayerMovement : MonoBehaviour {
    7.  
    8.     public float maxSpeed = 13f;
    9.     public float rotSpeed = 180f;
    10.     public AudioClip engine;
    11.  
    12.     void Start () {
    13.  
    14.     }
    15.  
    16.     void Update () {
    17.  
    18.         Quaternion rot = transform.rotation;
    19.  
    20.         float z = rot.eulerAngles.z;
    21.  
    22.         z -= Input.GetAxis("Horizontal") * rotSpeed * Time.deltaTime;
    23.  
    24.         rot = Quaternion.Euler (0,0,z);
    25.  
    26.         transform.rotation = rot;
    27.  
    28.         Vector3 pos = transform.position;
    29.  
    30.         Vector3 velocity = new Vector3(0, Input.GetAxis("Vertical") * maxSpeed * Time.deltaTime, 0);
    31.  
    32.         pos += rot * velocity;
    33.  
    34.         transform.position = pos;
    35.  
    36.         if(Input.GetAxis ("Horizontal") > 0 || Input.GetAxis("Vertical") > 0){
    37.  
    38.             audio.PlayOneShot (engine, 0.7f);
    39.  
    40.         }
    41.  
    42.                 }
    43. }
    (Notice the code tags)
     
  10. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    Beautiful code tags my friend, well done.

    Okay, NOW the problem is that you're using the kinematic system to move something that should be moved using physics.

    What this means is, instead of altering the transform rotation and position directly, you should be applying force to the rigidbody2D:

    Code (CSharp):
    1. //Untested example
    2.  
    3. [SerializedField] float thrustPower = 5f;
    4.  
    5. void FixedUpdate() { //<- We're making this update in FixedUpdate, notice. Because we're using physics.
    6.  
    7.     float hor = Input.GetAxis("Horizontal");
    8.     float vert = Input.GetAxis("Vertical");
    9.  
    10.     Vector2 force = transform.up * thrustPower * vert; // Apply the force in the direction we're facing. (Assuming your ship sprite has the front of the ship facing upwards)
    11.     float torque = hor * thrustPower;
    12.  
    13.     rigidbody2D.AddForce(force);// No need to multply by deltaTime because addForce will do this automatically with the default force mode
    14.     rigidbody2D.AddTorque(torque);
    15.  
    16. }
     
  11. VirusXProgramming

    VirusXProgramming

    Joined:
    Nov 15, 2014
    Posts:
    67
    I had an error with the serialized field.
     
  12. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    Whoops.

    [SerializeField]

    Anyway, don't just copy and paste the code. Read it, learn how it works and implement your own version.
     
  13. VirusXProgramming

    VirusXProgramming

    Joined:
    Nov 15, 2014
    Posts:
    67
    Thanks!
    The rotation - I tried everything but rigidbody2D.addTorque doesn't seem to be doing anything
    I also modified your gravity script a little but is there a way to make the object move slower (like a star) but not by just increasing the mass?
     
    Last edited: Jan 31, 2015
  14. VirusXProgramming

    VirusXProgramming

    Joined:
    Nov 15, 2014
    Posts:
    67
  15. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    It probably wont seem to do much if you're rotating something that is round, like a planet. Compare rigidbody2D.angularVelocity after you add torque to see if it's changing something. Beyond that do a little bit of investigation. Maybe you've locked the rotation. Maybe something else is determining the rotation of your object. Did you disable your Kinematic movement before you added Physical Movement?

    To make it move slower, try adding drag. Or capping it's velocity via script:
    Code (CSharp):
    1. if (rigidbody2D.velocity.magnitude > maxVelocity)
    2.      rigibody2D.velocity = rigidbody2D.velocity.normalized * maxVelocity;
    There's lots of ways to solve simple problems like this and half the fun is figuring it out!
     
  16. VirusXProgramming

    VirusXProgramming

    Joined:
    Nov 15, 2014
    Posts:
    67
    Oh sorry I found out why it wasnt working - The rigid body was set to Fixed Angle :(