Adding Gravity to a game object to make a black hole sucking effect.
Hello, I was trying to make a black hole effect with this script I found. The only problem is that the rigid bodies it sucks in orbit around the object for a little bit but I want them to just directly go towards it. The game objects getting sucked in are moving in the scene so I know that that is one of the problems.
using UnityEngine;
namespace _Scripts
{
/// <summary>
/// Gravity behavioura added to object
/// </summary>
public class WormHoleMaker : MonoBehaviour
{
public float PullRadius; // Radius to pull
public float GravitationalPull; // Pull force
public float MinRadius; // Minimum distance to pull from
public float DistanceMultiplier; // Factor by which the distance affects force
public LayerMask LayersToPull;
// Function that runs on every physics frame
void FixedUpdate()
{
Collider[] colliders = Physics.OverlapSphere(transform.position, PullRadius, LayersToPull);
foreach (var collider in colliders)
{
Rigidbody rb = collider.GetComponent<Rigidbody>();
if (rb == null) continue; // Can only pull objects with Rigidbody
Vector3 direction = transform.position - collider.transform.position;
if (direction.magnitude < MinRadius) continue;
float distance = direction.sqrMagnitude * DistanceMultiplier + 1; // The distance formula
// Object mass also affects the gravitational pull
rb.AddForce(direction.normalized * (GravitationalPull / distance) * rb.mass * Time.fixedDeltaTime);
}
}
}
}
Answer by DenisGLabrecque · Oct 17, 2018 at 01:58 AM
I think you may want to remove the part with direction.sqrMagnitude
, as the script is getting the root of the distance to apply the force over. This is an exponential curve instead of a linear acceleration (which seems to be what you want).
I also have a script you may try. To use it, create an empty game object that represents where the black hole is, and add a sphere collider to that empty game object to represent the area of attraction of that black hole. Be sure to check IsTrigger
on the sphere collider so the sphere collider acts as an area rather than a giant wall. Then add this script to the black hole game object:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(SphereCollider))]
public class Gravity : MonoBehaviour {
[SerializeField] public float GRAVITY_PULL = .78f;
public static float m_GravityRadius = 1f;
void Awake()
{
m_GravityRadius = GetComponent<SphereCollider>().radius;
}
/// <summary>
/// Attract objects towards an area when they come within the bounds of a collider.
/// This function is on the physics timer so it won't necessarily run every frame.
/// </summary>
/// <param name="other">Any object within reach of gravity's collider</param>
void OnTriggerStay(Collider other)
{
if(other.attachedRigidbody)
{
float gravityIntensity = Vector3.Distance(transform.position, other.transform.position) / m_GravityRadius;
other.attachedRigidbody.AddForce((transform.position - other.transform.position) * gravityIntensity * other.attachedRigidbody.mass * GRAVITY_PULL * Time.smoothDeltaTime);
Debug.DrawRay(other.transform.position, transform.position - other.transform.position);
}
}
}
For a stronger attraction, increase the GRAVITY_PULL
constant.
This is terrific, thanks @DenisGLabrecque. Very nearly what I was looking for, except I am working in 2D. In case anyone else stumbles upon this, and want something analogous but for 2d, this may save you a little time:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(CircleCollider2D))]
public class Gravity : $$anonymous$$onoBehaviour
{
[SerializeField] public float GRAVITY_PULL = .78f;
public static float m_GravityRadius = 1f;
void Awake()
{
m_GravityRadius = GetComponent<CircleCollider2D>().radius;
}
/// <summary>
/// Attract objects towards an area when they come within the bounds of a collider.
/// This function is on the physics timer so it won't necessarily run every frame.
/// </summary>
/// <param name="other">Any object within reach of gravity's collider</param>
void OnTriggerStay2D(Collider2D other)
{
if (other.attachedRigidbody)
{
float gravityIntensity = Vector3.Distance(transform.position, other.transform.position) / m_GravityRadius;
other.attachedRigidbody.AddForce((transform.position - other.transform.position) * gravityIntensity * other.attachedRigidbody.mass * GRAVITY_PULL * Time.smoothDeltaTime);
Debug.DrawRay(other.transform.position, transform.position - other.transform.position);
}
}
}
@tonyrobots Great to see it used! There is a problem with it in that using more than one collider results in multiplying gravity by the number of colliders (1 collider = 1x gravity, 2 colliders = 2x gravity). If that works for you, then great!
Otherwise, the force must be added to a rigidbody individually. I do have an updated setup that uses the principle, but it's more inter-related with other scripts: https://github.com/DenisLabrecque/Warglobe/blob/master/Assets/Scripts/Gravity.cs
Your answer
Follow this Question
Related Questions
Why my gravity direction is rotating with transform.rotation? 1 Answer
Rigid body robot animation 0 Answers
GravityBody hides Rigidbody 1 Answer