I’m making a videogame about submarines. I need the player to be able to move around the submarine. I’ve implemented this with a player using a rigidbody and a controller script and a submarine gameobject using a rigidbody and a controller script. The rigidbody has a script which is called buoyancy.
Depending on the volume of water displaced and the weight of the rigidbody, it will sink up to a certain height. This works fine.
The problem is since there is drag on each rigidbody, when the player moves, it will act this force upon the submarine and move it. This also happens when the player is pushing on a wall connect to the interior of the submarine.
I want the player not to be able to move the submarine from walking inside it but if it is light enough, to be able to push it from the outside.
[Here's a link to a video showing this][1]
Buoyancy Script
using System;
using UnityEngine;
public class Buoyancy : MonoBehaviour
{
private const float surfaceHeight = 0f;
private const float waterDensity = 1f;
[SerializeField]
private float area = 0f;
[SerializeField]
private Rigidbody objectRigidbody = null;
private Vector3 buoyantForce = Vector3.zero;
public Vector3 Force => buoyantForce;
private void FixedUpdate()
{
if (transform.position.y >= surfaceHeight)
{
buoyantForce = Vector3.zero;
return;
}
buoyantForce = GetBuoyantForce();
objectRigidbody.AddForce(buoyantForce, ForceMode.Force);
}
private Vector3 GetBuoyantForce()
{
Vector3 u = Vector3.up;
float magnitude = waterDensity * -Physics.gravity.y * (surfaceHeight - transform.position.y) * area;
return magnitude * u;
}
}
Player Movment Script
using System;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
[SerializeField]
private Rigidbody playerRigidbody = default;
[SerializeField]
private float jumpHeight = 1f;
[SerializeField]
private float airPenalty = 0.2f;
[SerializeField]
private float topSpeed = 2f;
[Header("Ground")]
[SerializeField]
private Transform groundCheck = default;
[SerializeField]
private float groundRadius = 1f;
[SerializeField]
private LayerMask groundMask = default;
private Input input = default;
private Vector2 move = default;
private bool grounded = false;
private bool jump = false;
private void Awake()
{
input = new Input();
input.Player.Move.performed += ctx => move = ctx.ReadValue<Vector2>();
input.Player.Move.canceled += _ => move = Vector2.zero;
input.Player.Jump.performed += _ => jump = true;
input.Player.Jump.canceled += _ => jump = false;
}
private void FixedUpdate()
{
CheckGrounded();
Vector3 force = Vector3.ClampMagnitude(transform.right * move.x + transform.forward * move.y, 1f) * topSpeed - playerRigidbody.velocity;
force.y = 0f;
if (grounded)
{
if (jump)
{
playerRigidbody.AddForce(Vector3.up * Mathf.Sqrt(-2f * Physics.gravity.y * jumpHeight), ForceMode.VelocityChange);
}
}
else
{
force *= airPenalty;
}
if (force != Vector3.zero)
{
playerRigidbody.AddForce(force, ForceMode.VelocityChange);
}
}
private void CheckGrounded()
{
grounded = Physics.CheckSphere(groundCheck.position, groundRadius, groundMask);
}
private void OnEnable()
{
input.Enable();
}
private void OnDisable()
{
input.Disable();
}
private void OnDrawGizmos()
{
if (groundCheck != null)
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(groundCheck.position, groundRadius);
}
}
}