Asked by Coder
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ShipController : MonoBehaviour
{
private Rigidbody shipRB;
public float shipMaxSpeed = 5; // The ship's MAX speed.
public float shipAcceleration = 0.55f; // The amount of which the ship accelerates when moving.
public float shipDeceleration = 0.15f; // The amount of which the ship decelerates when NOT moving.
public float shipTurnSpeed = 2.5f; //. The ship's turn speed.
private float moveZ;
private float turnY;
private float zValue;
private bool isMoving = false;
// Start is called before the first frame update
void Start()
{
shipRB = GetComponent<Rigidbody>();
StartCoroutine(LogVariables());
}
// Update is called once per frame
void Update()
{
moveZ = Input.GetAxisRaw("Vertical");
turnY = Input.GetAxisRaw("Horizontal");
CheckMovement();
if (isMoving)
{
zValue += Time.deltaTime * shipAcceleration;
}
else
{
zValue -= Time.deltaTime * shipDeceleration;
}
zValue = Mathf.Clamp(zValue, 0, shipMaxSpeed);
}
void FixedUpdate()
{
shipRB.velocity = new Vector3(0, 0, zValue);
}
IEnumerator LogVariables()
{
Debug.Log("Ship speed: " + shipRB.velocity.magnitude);
yield return new WaitForSeconds(2.0f);
StartCoroutine(LogVariables());
}
void CheckMovement()
{
if (moveZ > 0.1f)
{
isMoving = true;
}
else
{
isMoving = false;
}
}
}
Add realistic ship physics.
All Answers 1
Answered by
GPT-5 mini
AI
Below is a practical, physics-driven rewrite of your ShipController that replaces direct assignment of velocity with forces and torques. It models thrust, braking, quadratic water drag, lateral damping (to reduce unrealistic strafing), speed-limited rudder turning, and a visual roll (bank) on turns. The controller works with a Rigidbody and uses a child GameObject for the visible mesh so the visual roll does not fight the physics rotation.
Key ideas used:
- Use AddForce for propulsion rather than directly setting velocity.
- Quadratic drag (proportional to speed^2) for water resistance.
- Lateral damping to reduce side-slipping (ships resist sideways motion).
- Rudder torque scaled by forward speed: ships turn more effectively when moving.
- A separate model transform to handle roll/bank visuals smoothly.
Script (replace your current script with this):
using System.Collections;
using UnityEngine;
public class ShipController : MonoBehaviour
{
Rigidbody shipRB;
[Header("Speed & Thrust")]
public float maxForwardSpeed = 12f; // m/s
public float maxReverseSpeed = 5f; // m/s (in reverse)
public float enginePower = 4000f; // N (peak forward thrust)
public float reversePower = 2000f; // N (reverse thrust)
public float brakePower = 6000f; // N (braking / rapid slowdown)
[Header("Drag & Damping")]
public float waterDragCoefficient = 0.5f; // coefficient for quadratic drag
public float lateralDamping = 4f; // reduces sideways sliding (N per m/s lateral)
public float angularDamping = 2f; // set initial angularDrag in RB
[Header("Steering / Rudder")]
public float rudderTorque = 1200f; // torque applied around up axis at full steer
public float minRudderEffectiveness = 0.05f; // turning at low speeds still possible a bit
[Header("Visuals")]
public Transform modelTransform; // child object to apply roll/bank visuals
public float maxRollAngle = 12f; // degrees bank on hard turn
public float rollLerpSpeed = 2.5f; // how quickly the model banks/unbanks
// inputs
float throttleInput;
float steerInput;
void Start()
{
shipRB = GetComponent<Rigidbody>();
if (shipRB == null) Debug.LogError("ShipController requires a Rigidbody.");
shipRB.angularDrag = angularDamping;
if (modelTransform == null)
{
Debug.LogWarning("Model transform not assigned. Visual roll will be applied to the same transform.");
modelTransform = transform;
}
StartCoroutine(LogVariables());
}
void Update()
{
// Read raw inputs in Update
throttleInput = Input.GetAxis("Vertical"); // -1..1
steerInput = Input.GetAxis("Horizontal"); // -1..1
}
void FixedUpdate()
{
// Local forward speed (positive = moving forward along transform.forward)
float forwardSpeed = Vector3.Dot(shipRB.velocity, transform.forward);
// Desired target speed based on throttle
float targetSpeed = throttleInput >= 0f ? throttleInput * maxForwardSpeed : throttleInput * maxReverseSpeed;
// Compute thrust/brake force along forward vector
float speedDiff = targetSpeed - forwardSpeed;
// Choose which power to use: forward engine or reverse or braking
float appliedForce = 0f;
if (Mathf.Approximately(throttleInput, 0f))
{
// No throttle -> we don't add thrust, ship slows via water drag and optional braking if needed
appliedForce = 0f;
}
else if (throttleInput > 0f)
{
// Forward throttle
appliedForce = Mathf.Clamp(speedDiff * shipRB.mass * 5f, -brakePower, enginePower);
}
else // throttleInput < 0
{
// Reverse throttle (we allow slower reverse than forward)
appliedForce = Mathf.Clamp(speedDiff * shipRB.mass * 5f, -reversePower, reversePower);
}
// Add forward/reverse thrust
shipRB.AddForce(transform.forward * appliedForce);
// Quadratic water drag opposing velocity (F = -k * v * |v|)
Vector3 v = shipRB.velocity;
if (v.sqrMagnitude > 0.0001f)
{
Vector3 drag = -v.normalized * (waterDragCoefficient * v.sqrMagnitude);
shipRB.AddForce(drag);
}
// Lateral damping: resist sideways motion so ship doesn't slide unrealistically
float lateralSpeed = Vector3.Dot(shipRB.velocity, transform.right); // sideways velocity
Vector3 lateralDampingForce = -transform.right * lateralSpeed * lateralDamping * shipRB.mass;
shipRB.AddForce(lateralDampingForce * Time.fixedDeltaTime); // scale a bit by fixedDeltaTime
// Rudder / turning torque: scale effectiveness with forward speed
float speedFactor = Mathf.Clamp01(Mathf.Abs(forwardSpeed) / maxForwardSpeed);
speedFactor = Mathf.Max(speedFactor, minRudderEffectiveness); // allow some turning at low speeds
float torque = steerInput * rudderTorque * speedFactor;
shipRB.AddTorque(Vector3.up * torque);
// Optional: apply small stabilizing torque to reduce yaw oscillation (you can tune or remove)
// shipRB.AddTorque(-shipRB.angularVelocity * 0.5f);
// Visual banking / roll on turns: affect only the model transform to avoid fighting physics
ApplyVisualRoll(forwardSpeed);
}
void ApplyVisualRoll(float forwardSpeed)
{
// Bank magnitude scales with steering and speed
float bankTarget = -steerInput * maxRollAngle * Mathf.Clamp01(Mathf.Abs(forwardSpeed) / Mathf.Max(1f, maxForwardSpeed));
Quaternion localTarget = Quaternion.Euler(0f, 0f, bankTarget);
// modelTransform is a child; we interpolate localRotation for smoothness
modelTransform.localRotation = Quaternion.Slerp(modelTransform.localRotation, localTarget, rollLerpSpeed * Time.fixedDeltaTime);
}
IEnumerator LogVariables()
{
while (true)
{
if (shipRB != null)
Debug.Log($"Speed: {shipRB.velocity.magnitude:F2} m/s Forward component: {Vector3.Dot(shipRB.velocity, transform.forward):F2}");
yield return new WaitForSeconds(2f);
}
}
}
Notes, tuning & recommended Rigidbody setup:
- Use a realistic mass for the ship (e.g. 5000-20000 kg depending on scale). The enginePower/reversePower/brakePower values above expect a larger mass; tune them to get the acceleration you want.
- Keep angular drag moderate (e.g. 1–3) so turning stabilizes. Linear drag is handled by our water drag function; set Rigidbody.drag low (0–0.2).
- To avoid physics fights, put the visible mesh as a child of the Rigidbody GameObject and assign it to modelTransform. The script will rotate the child to show banking while leaving physics rotation to the RB.
- If you need buoyancy (floating on waves), add a buoyancy system (applying upward force at points) — that's a separate subsystem.
- If you want tighter turning while stationary (e.g., using thrusters), add a separate lateral thruster system or allow some lateral force when brakes or thrusters are used.
This should give a much more realistic, momentum-driven feel and stop the sudden velocity snapping you had when setting velocity directly. Tweak mass, enginePower, waterDragCoefficient, rudderTorque and lateralDamping until the handling matches your desired ship behavior.
Key ideas used:
- Use AddForce for propulsion rather than directly setting velocity.
- Quadratic drag (proportional to speed^2) for water resistance.
- Lateral damping to reduce side-slipping (ships resist sideways motion).
- Rudder torque scaled by forward speed: ships turn more effectively when moving.
- A separate model transform to handle roll/bank visuals smoothly.
Script (replace your current script with this):
using System.Collections;
using UnityEngine;
public class ShipController : MonoBehaviour
{
Rigidbody shipRB;
[Header("Speed & Thrust")]
public float maxForwardSpeed = 12f; // m/s
public float maxReverseSpeed = 5f; // m/s (in reverse)
public float enginePower = 4000f; // N (peak forward thrust)
public float reversePower = 2000f; // N (reverse thrust)
public float brakePower = 6000f; // N (braking / rapid slowdown)
[Header("Drag & Damping")]
public float waterDragCoefficient = 0.5f; // coefficient for quadratic drag
public float lateralDamping = 4f; // reduces sideways sliding (N per m/s lateral)
public float angularDamping = 2f; // set initial angularDrag in RB
[Header("Steering / Rudder")]
public float rudderTorque = 1200f; // torque applied around up axis at full steer
public float minRudderEffectiveness = 0.05f; // turning at low speeds still possible a bit
[Header("Visuals")]
public Transform modelTransform; // child object to apply roll/bank visuals
public float maxRollAngle = 12f; // degrees bank on hard turn
public float rollLerpSpeed = 2.5f; // how quickly the model banks/unbanks
// inputs
float throttleInput;
float steerInput;
void Start()
{
shipRB = GetComponent<Rigidbody>();
if (shipRB == null) Debug.LogError("ShipController requires a Rigidbody.");
shipRB.angularDrag = angularDamping;
if (modelTransform == null)
{
Debug.LogWarning("Model transform not assigned. Visual roll will be applied to the same transform.");
modelTransform = transform;
}
StartCoroutine(LogVariables());
}
void Update()
{
// Read raw inputs in Update
throttleInput = Input.GetAxis("Vertical"); // -1..1
steerInput = Input.GetAxis("Horizontal"); // -1..1
}
void FixedUpdate()
{
// Local forward speed (positive = moving forward along transform.forward)
float forwardSpeed = Vector3.Dot(shipRB.velocity, transform.forward);
// Desired target speed based on throttle
float targetSpeed = throttleInput >= 0f ? throttleInput * maxForwardSpeed : throttleInput * maxReverseSpeed;
// Compute thrust/brake force along forward vector
float speedDiff = targetSpeed - forwardSpeed;
// Choose which power to use: forward engine or reverse or braking
float appliedForce = 0f;
if (Mathf.Approximately(throttleInput, 0f))
{
// No throttle -> we don't add thrust, ship slows via water drag and optional braking if needed
appliedForce = 0f;
}
else if (throttleInput > 0f)
{
// Forward throttle
appliedForce = Mathf.Clamp(speedDiff * shipRB.mass * 5f, -brakePower, enginePower);
}
else // throttleInput < 0
{
// Reverse throttle (we allow slower reverse than forward)
appliedForce = Mathf.Clamp(speedDiff * shipRB.mass * 5f, -reversePower, reversePower);
}
// Add forward/reverse thrust
shipRB.AddForce(transform.forward * appliedForce);
// Quadratic water drag opposing velocity (F = -k * v * |v|)
Vector3 v = shipRB.velocity;
if (v.sqrMagnitude > 0.0001f)
{
Vector3 drag = -v.normalized * (waterDragCoefficient * v.sqrMagnitude);
shipRB.AddForce(drag);
}
// Lateral damping: resist sideways motion so ship doesn't slide unrealistically
float lateralSpeed = Vector3.Dot(shipRB.velocity, transform.right); // sideways velocity
Vector3 lateralDampingForce = -transform.right * lateralSpeed * lateralDamping * shipRB.mass;
shipRB.AddForce(lateralDampingForce * Time.fixedDeltaTime); // scale a bit by fixedDeltaTime
// Rudder / turning torque: scale effectiveness with forward speed
float speedFactor = Mathf.Clamp01(Mathf.Abs(forwardSpeed) / maxForwardSpeed);
speedFactor = Mathf.Max(speedFactor, minRudderEffectiveness); // allow some turning at low speeds
float torque = steerInput * rudderTorque * speedFactor;
shipRB.AddTorque(Vector3.up * torque);
// Optional: apply small stabilizing torque to reduce yaw oscillation (you can tune or remove)
// shipRB.AddTorque(-shipRB.angularVelocity * 0.5f);
// Visual banking / roll on turns: affect only the model transform to avoid fighting physics
ApplyVisualRoll(forwardSpeed);
}
void ApplyVisualRoll(float forwardSpeed)
{
// Bank magnitude scales with steering and speed
float bankTarget = -steerInput * maxRollAngle * Mathf.Clamp01(Mathf.Abs(forwardSpeed) / Mathf.Max(1f, maxForwardSpeed));
Quaternion localTarget = Quaternion.Euler(0f, 0f, bankTarget);
// modelTransform is a child; we interpolate localRotation for smoothness
modelTransform.localRotation = Quaternion.Slerp(modelTransform.localRotation, localTarget, rollLerpSpeed * Time.fixedDeltaTime);
}
IEnumerator LogVariables()
{
while (true)
{
if (shipRB != null)
Debug.Log($"Speed: {shipRB.velocity.magnitude:F2} m/s Forward component: {Vector3.Dot(shipRB.velocity, transform.forward):F2}");
yield return new WaitForSeconds(2f);
}
}
}
Notes, tuning & recommended Rigidbody setup:
- Use a realistic mass for the ship (e.g. 5000-20000 kg depending on scale). The enginePower/reversePower/brakePower values above expect a larger mass; tune them to get the acceleration you want.
- Keep angular drag moderate (e.g. 1–3) so turning stabilizes. Linear drag is handled by our water drag function; set Rigidbody.drag low (0–0.2).
- To avoid physics fights, put the visible mesh as a child of the Rigidbody GameObject and assign it to modelTransform. The script will rotate the child to show banking while leaving physics rotation to the RB.
- If you need buoyancy (floating on waves), add a buoyancy system (applying upward force at points) — that's a separate subsystem.
- If you want tighter turning while stationary (e.g., using thrusters), add a separate lateral thruster system or allow some lateral force when brakes or thrusters are used.
This should give a much more realistic, momentum-driven feel and stop the sudden velocity snapping you had when setting velocity directly. Tweak mass, enginePower, waterDragCoefficient, rudderTorque and lateralDamping until the handling matches your desired ship behavior.
Submit Your Answer
We prioritize human answers over AI answers.
If you are human, and you can answer this question, please submit your answer.