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.