Okay, I am planning on adding bodies of water into a video game I'm making. I want to have a buoyancy system, where if an rigidbody (including that of the player character) is in the water (which I have as trigger colliders), it compares the water's density to the object's, to determine if it floats or sinks. Objects denser than the water (density is stored as a float) will sink, and objects less dense than the water will float. How would I go about doing that?
Assuming you want to apply the Archimedes principle here, which basically states that the buoyant force is equal to the weight of the fluid displaced by the submerged object, you'd need to calculate how deep in the water each shape is every frame. From there, you calculate the submerged volume, multiply that by gravity and the water density and apply as the force acting directly up. That will target exactly counteracting the weight of the bodies in water at some penetration depth (if possible). Calculating submerged volumes is easier done with primitive shapes.
As above but if you're asking about 2D physics then exactly what you ask for is provided by the BuoyancyEffector2D.
Okay, I've been able to make a rudimentary buoyancy system where objects less dense than water will float to the surface if submerged, and objects denser than water will sink to the bottom. I've added those physics to the grabbable objects the player can pick up, carry, and throw, and the player character has a rigidbody and a head check that detects if the player submerges her head below water. Here's the script I'm using to run the events for if the objects are in the water or not: Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; public class Water : MonoBehaviour { //holds the rigidbody of whatever comes into contact with it [SerializeField] private Rigidbody rb; //holds the density of the water public float waterDensity; //gets the player script [SerializeField] private Aquatic aquatic; //gets the grabbable script [SerializeField] private Grabbable grabbable; //gets the rigidbody of objects put in water private void Awake() { /*rb = gameObject.GetComponent<Rigidbody>(); player = gameObject.GetComponent<Player>(); grabbable = Object.FindObjectOfType<Grabbable>();*/ } //applies force of buoyancy when the object enters the water private void OnTriggerEnter(Collider other) { Grabbable submergeCheck = other.GetComponent<Grabbable>(); submergeCheck.isSubmerged = true; other.transform.gameObject.SendMessage("FloatInWater", waterDensity); Aquatic playerHead = other.GetComponent<Aquatic>(); playerHead.isSubmerged = true; other.transform.gameObject.SendMessage("FloatInWater", waterDensity); } //removes force of buoyancy when the object exits the water private void OnTriggerExit(Collider other) { Grabbable submergeCheck = other.GetComponent<Grabbable>(); submergeCheck.isSubmerged = false; Aquatic playerHead = other.GetComponent<Aquatic>(); playerHead.isSubmerged = false; } } While the buoyancy works for grabbable objects, it doesn't work for the player. Why is that? And how could I fix it? SOLVED: I've been able to get my rudimentary buoyancy to work. Here's the code I use for the water: Code (CSharp): using System.Collections; using System.Collections.Generic; using UnityEngine; public class Water : MonoBehaviour { //holds the rigidbody of whatever comes into contact with it [SerializeField] private Rigidbody rb; //holds the density of the water public float waterDensity; //gets the player script [SerializeField] private Aquatic aquatic; //gets the grabbable script [SerializeField] private Grabbable grabbable; //gets the rigidbody of objects put in water private void Awake() { /*rb = gameObject.GetComponent<Rigidbody>(); player = gameObject.GetComponent<Player>(); grabbable = Object.FindObjectOfType<Grabbable>();*/ aquatic = Object.FindObjectOfType<Aquatic>(); } //applies force of buoyancy when the object enters the water private void OnTriggerEnter(Collider other) { if(other.TryGetComponent<Grabbable>(out Grabbable grabbable)) { //Grabbable submergeCheck = other.GetComponent<Grabbable>(); grabbable.isSubmerged = true; other.transform.gameObject.SendMessage("FloatInWater", waterDensity); } else if(other.TryGetComponent<Aquatic>(out Aquatic aquatic)) { //Aquatic playerHead = other.GetComponent<Aquatic>(); aquatic.isSubmerged = true; other.transform.gameObject.SendMessage("FloatInWater", waterDensity); } } //removes force of buoyancy when the object exits the water private void OnTriggerExit(Collider other) { if(other.TryGetComponent<Grabbable>(out Grabbable grabbable)) { //Grabbable submergeCheck = other.GetComponent<Grabbable>(); grabbable.isSubmerged = false; } else if(other.TryGetComponent<Aquatic>(out Aquatic aquatic)) { //Aquatic playerHead = other.GetComponent<Aquatic>(); aquatic.isSubmerged = false; } } }