Hello, I’m currently trying to implement realistic boat physics. So far I got the buoyancy right, but I’m a bit stuck with drag.

I would like to have a “wall” of ray casts constantly “ahead” of the collider (i.e in the direction it moves) so that I can cast those ray casts into the collider, in the opposite direction of the velocity, and determine, foreach ray cast, a drag force to be applied on the hit triangle of the collider.

My problem is that, I do have the direction of the ray casts, which is the opposite of the velocity, but I don’t have the ray casts starting points (i.e the points belonging to the “wall” plane) and don’t know how to compute them. Below is an image to give you a better idea of what I mean

The ball is my boat (which isn’t necessarily moving forward). The red arrow is its velocity. The green lines are (some of) the ray casts start from the plane and (maybe) hitting the collider. What I would like to compute is the starting points of these ray casts.

Could someone please put me into the direction ? Thank you very much.

EDIT : I thought about creating a new Transform object. Position that transform based on the opposite velocity direction and on a position far enough “ahead” of the collider so that all ray casts start outside of the collider. And then determine ray cast starting points in local coordinates of that transform and then convert them to world space using that transform. Could this work ? Thanks.

Ok I found a solution ! The idea is to create a new transform using a dummy empty game object. Then set the position of that transform by adding a large enough distance to the boat’s position, than make that transform look at the boat. From there it is possible to compute the ray casts starting points locally to that transform, and then transform them into real world coordinates.

The code below only shows how to compute the ray casts starting points, not how to compute the drag forces (not done yet).

```
private void ProcessDragForces()
{
rayCastStartPoints = new List<Vector3>(rayCastCountX * rayCastCountY);
rayCastHitPoints = new List<Vector3>(rayCastStartPoints.Count);
// The wall must be at least as big as the collider projection on that wall can be,
// regardless of its rotation. Using the diagonal of the bounds ensure that.
float wallSize = boatMeshCollider.bounds.size.magnitude;
float halfWallSize = wallSize / 2f;
// Divding the raycast wall into multiple cells in order to determine the starting
// point for each ray cast locally to the ray cast wall's transform.
float cellSizeX = wallSize / rayCastCountX;
float cellSizeY = wallSize / rayCastCountY;
Vector3 cellOffset = new Vector3(-halfWallSize + cellSizeX / 2f, -halfWallSize + cellSizeY / 2f, 0f);
// Cast in the opposite direction of the magnitude.
Vector3 raycastDirection = -boatRigidbody.velocity;
raycastWallTransform.position = gameObject.transform.position + boatRigidbody.velocity.normalized * boatMeshCollider.bounds.size.magnitude * 2f;
raycastWallTransform.rotation = gameObject.transform.rotation;
raycastWallTransform.LookAt(gameObject.transform);
for (int x = 0; x < rayCastCountX; ++x)
{
for (int y = 0; y < rayCastCountY; ++y)
{
Vector3 localRayCastStartPoint = cellOffset + new Vector3(x * cellSizeX, y + cellSizeY, 0f);
Vector3 rayCastStartPoint = raycastWallTransform.TransformPoint(localRayCastStartPoint);
rayCastStartPoints.Add(rayCastStartPoint);
RaycastHit raycastHit;
if (Physics.Raycast(rayCastStartPoint, raycastDirection, out raycastHit) && raycastHit.collider == boatMeshCollider)
{
rayCastHitPoints.Add(raycastHit.point);
boatRigidbody.AddForceAtPosition(new Vector3(10f, 10f, 10f), raycastHit.point);
}
}
}
}
```