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;
public Transform pointOfBuyancy;
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);
GetComponent<Rigidbody>().AddForceAtPosition(floatForce, pointOfBuyancy.position);
}
}
}
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()
{
// Receive player input
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
// https://forum.unity.com/threads/rigidbody-lookat-torque.146625/
// rigidbody.AddTorque(Vector3.Cross(transform.forward, rigidbody.velocity), ForceMode.FORCE);
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.