- Home /
Buoyancy Script Timing Inverted
I am trying to make a buoyancy script that will interact with waves. I have two scripts: one to change the mesh of a plane and create waves, and the other to deal with the buoyancy. The buoyancy script originally just took the vertices from the mesh, found the closest vertex, and based on the position of the object, calculated the buoyancy. This worked fine. My plan for the future is to add a ship building system using blocks, so now the buoyancy script sits on an empty gameobject and grabs all the colliders from its children. Now, the boat rises when there is no wave and falls when there is a wave. In other words, the timing is inverted, and I don't know how to fix it.
Here is the wave script:
using UnityEngine;
using System.Collections;
public class PlaneWater : MonoBehaviour
{
public float scale = 0.1f;
public float speed = 1.0f;
public float noiseStrength = 1f;
public float noiseWalk = 1f;
private Vector3[] baseHeight;
public Vector3[] vertices;
void Update () {
Mesh mesh = GetComponent<MeshFilter>().mesh;
if (baseHeight == null)
baseHeight = mesh.vertices;
vertices = new Vector3[baseHeight.Length];
for (int i=0;i<vertices.Length;i++)
{
Vector3 vertex = baseHeight[i];
vertex.y += Mathf.Sin(Time.time * speed+ baseHeight[i].x + baseHeight[i].y + baseHeight[i].z) * scale;
vertex.y += Mathf.PerlinNoise(baseHeight[i].x + noiseWalk, baseHeight[i].y + Mathf.Sin(Time.time * 0.1f) ) * noiseStrength;
vertices[i] = vertex;
}
mesh.vertices = vertices;
mesh.RecalculateNormals();
}
}
And here is the buoyancy script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Buoyancy : MonoBehaviour {
public GameObject currentWaterTile;
public Vector3[] vertices;
public float buoyancyMultiplier;
public List<Collider> publicCols;
public Material woodMat;
public Material underwaterMat;
private Rigidbody rigidbody;
// Use this for initialization
void Start () {
rigidbody = GetComponent<Rigidbody>();
publicCols = CollectColliders();
}
// Update is called once per frame
void Update () {
CalculateBuoyancy();
}
float FindCurrentVertexY(Vector2 myPos)
{
//float startTime = Time.time;
vertices = new Vector3[currentWaterTile.GetComponent<PlaneWater>().vertices.Length];
vertices = currentWaterTile.GetComponent<PlaneWater>().vertices;
Vector2 closestVertex = Vector2.zero;
int closestVertexIndex = 0;
for(int i = 0; i < vertices.Length; i++)
{
if(Vector2.Distance(myPos, new Vector2(vertices[i].x, vertices[i].z)) < Vector2.Distance(myPos, closestVertex))
{
closestVertex = new Vector2(vertices[i].x, vertices[i].z);
closestVertexIndex = i;
}
}
return vertices[closestVertexIndex].y;
}
void CalculateBuoyancy()
{
int totalUnderwaterParts = 0;
foreach(Collider col in publicCols)
{
float distanceUnderWater = FindCurrentVertexY(new Vector2(col.transform.position.x, col.transform.position.z)) - col.transform.position.y;
if(distanceUnderWater > 0)
{
Vector3 movementVector = new Vector3(0, 1, 0) - Physics.gravity * buoyancyMultiplier * distanceUnderWater;
rigidbody.AddForceAtPosition(movementVector, col.transform.position);
col.GetComponent<MeshRenderer>().material = underwaterMat;
totalUnderwaterParts++;
}
else
{
col.GetComponent<MeshRenderer>().material = woodMat;
}
}
if((float)totalUnderwaterParts >= (float)publicCols.Count / 3f)
{
ChangeRigidbodyValues(true);
}
else
{
ChangeRigidbodyValues(false);
}
}
void ChangeRigidbodyValues(bool underwater)
{
if(underwater)
{
rigidbody.angularDrag = 3;
rigidbody.drag = 5;
}
if(!underwater)
{
rigidbody.angularDrag = 0.05f;
rigidbody.drag = 0;
}
}
List<Collider> CollectColliders()
{
List<Collider> cols = new List<Collider>();
for(int i = 0; i < GetComponents<Collider>().Length; i++)
{
cols.Add(GetComponents<Collider>()[i]);
}
foreach(Transform child in transform)
{
for(int k = 0; k < child.GetComponents<Collider>().Length; k++)
{
cols.Add(child.GetComponents<Collider>()[k]);
}
}
return cols;
}
}
Here's a video of the problem in action.
Answer by FortisVenaliter · Aug 03, 2017 at 09:38 PM
I'm pretty sure your issue is with this line here:
Vector3 movementVector = new Vector3(0, 1, 0) - Physics.gravity * buoyancyMultiplier * distanceUnderWater;
Remember that with the order of operations, this will evaluate to:
Vector3 movementVector = new Vector3(0, 1, 0) - (Physics.gravity * buoyancyMultiplier * distanceUnderWater);
Another problem could be that if your Water Plane has a non-one scale, non-zero position, or non-identity rotation, then FindCurrentVertexY() will not return the correct value, because it's using object space.
Your answer
Follow this Question
Related Questions
Get an object to float to the top of water. 2 Answers
Bouyancy timing issue 0 Answers
How to Script Buoyancy with a Water Shader 2 Answers
Damping forces to stop boat oscillation 0 Answers
2D 360 degress platformer example needed 0 Answers