# Floating Boat 3D Physics Rotation from Rudder (no water resistance)

I have been able to simulate a floating boat very simply by attaching a script called Boat.cs to a ship model with a Rigidbody; the Drag and Angular Drag are set to 2, and the mass to 15, and the Rigidbody uses gravity.

  using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
public class Boat : MonoBehaviour {
public static float waterLevel = 16.6f;
public float floatThreshhold = 2.0f;
public float waterDensity = 0.125f;
public float downForce = 0.5f;

float forceFactor;
Vector3 floatForce;

void FixedUpdate () {

forceFactor = 1.0f - ((transform.position.y - waterLevel) / floatThreshhold);
if(forceFactor > 0.0f)
{
floatForce = -Physics.gravity * GetComponent<Rigidbody>().mass * (forceFactor - GetComponent<Rigidbody>().velocity.y * waterDensity);
floatForce += new Vector3(0.0f, -downForce * GetComponent<Rigidbody>().mass, 0.0f);
}
}
}


the Transform named pointOfBuyancy is an empty placed above the ship model. This is where the floating force is applied, acting as a pendulum to stabilize the ship. This script works very well, and doesn't require changes.

The control script is called SimpleBoatController.cs, and is as follows:

  // Denis Labrecque, copy of #02 Boat Movement - How to make a Boat Controller - Unity 3D on YouTube

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(Boat))]
public class SimpleBoatController : MonoBehaviour {

public float movementThreshold = 0.2f;
public float turnThreshold = 0.2f;
public float maxSpeed = 30.0f;

public Transform pointOfEngine;

Rigidbody rigidbody;
Transform centerOfMassTransform;
float waterDynamicFactor;
float verticalInput;
float movementFactor;
float horizontalInput;
float steerFactor;

public float CurrentSpeed { get; private set; }

// Use this for initialization
void Start () {
rigidbody = GetComponent<Rigidbody>();
}

// Update is called once per frame
void Update () {
// Assign properties
CurrentSpeed = rigidbody.velocity.x;

Move();
}

void Move()
{
verticalInput = Input.GetAxis("Vertical");
horizontalInput = Input.GetAxis("Horizontal");

// Calculate factors
movementFactor = Mathf.Lerp(movementFactor, verticalInput, Time.deltaTime * movementThreshold);
steerFactor = Mathf.Lerp(steerFactor, horizontalInput, Time.deltaTime * turnThreshold);

// Move forwards
rigidbody.AddForceAtPosition(new Vector3(movementFactor * rigidbody.mass * maxSpeed, 0, 0), pointOfEngine.position);

// Turn
rigidbody.AddTorque(0, transform.position.y * CurrentSpeed * 25f * horizontalInput, 0); // Rotate
rigidbody.AddTorque(Vector3.Cross(rigidbody.velocity, transform.forward), ForceMode.Force); // Align relative to direction
Debug.Log("Turning of " + horizontalInput);
}
}


This script requires an additional empty at the location of the center of mass of the ship, called pointOfEngine in the code, where the ship is pushed forwards. This works very well.

My difficulty is in the move method, when it comes to applying rotation.

Because the flotation script does not apply any angular drag, the boat rotates without turning in the direction of rotation. It makes a flat spin and wobbles like over jellow -- which looks good, but just doesn't allow controlling the boat.

How can I make the boat travel in an arc, and not just back and forth while making a flat spin?

I have searched various physics answers about rotation and wind resistance, but these don't apply to my problem.

I use the following for the boat control:

public Vector3 COM;
[Space (15)]
public float speed = 1.0f;
public float steerSpeed = 1.0f;
public float movementThresold = 10.0f;

Transform m_COM;
float verticalInput;
float movementFactor;
float horizontalInput;
float steerFactor;

public GameObject Rudder;

// Update is called once per frame
void Update () {
Balance ();
Movement ();
Steer ();
}

void Balance () {
if (!m_COM) {
m_COM = new GameObject ("COM").transform;
m_COM.SetParent (transform);
}

m_COM.position = COM;
GetComponent<Rigidbody> ().centerOfMass = m_COM.position;
}

void Movement () {
verticalInput = Input.GetAxis ("Vertical");
movementFactor = Mathf.Lerp (movementFactor, verticalInput, Time.deltaTime / movementThresold);
transform.Translate (0.0f, 0.0f, movementFactor * speed);
}

void Steer () {
horizontalInput = Input.GetAxis ("Horizontal");
steerFactor = Mathf.Lerp (steerFactor, horizontalInput * verticalInput, Time.deltaTime / movementThresold);
transform.Rotate (0.0f, steerFactor * steerSpeed, 0.0f);

// To move the physical rudder wheel on the deck
Rudder.transform.Rotate(Vector3.down, steerFactor * steerSpeed * 5f);
}

• This answer would be even better if it included a description of why this solves OP's problem, to help a reader develop an intuition about the strategy you're using rather than just copying your code. Nov 28, 2018 at 18:10
• I'm using physics, so I'll have to check whether transform.Rotate and transform.Translate are really the way to go. Thank you for your feedback; I'll look into it once I have time. Nov 29, 2018 at 0:19