97 lines
3.9 KiB
C#
97 lines
3.9 KiB
C#
using UnityEngine;
|
|
|
|
public class StableHovercarController : MonoBehaviour
|
|
{
|
|
public float hoverHeight = 3.0f;
|
|
public float positionAdjustmentSpeed = 10.0f;
|
|
public float raycastDistance = 10.0f;
|
|
public LayerMask terrainLayer;
|
|
|
|
public float movementSpeed = 10.0f; // Max speed
|
|
public float acceleration = 5.0f; // How quickly to accelerate
|
|
public float deceleration = 7.0f; // How quickly to decelerate
|
|
public float rotationSpeed = 100.0f;
|
|
|
|
private float currentSpeed = 0.0f; // Speed that interpolates over time
|
|
private Rigidbody rb;
|
|
|
|
void Start()
|
|
{
|
|
rb = GetComponent<Rigidbody>();
|
|
rb.useGravity = false; // Disable gravity for stability
|
|
}
|
|
|
|
void FixedUpdate()
|
|
{
|
|
HandleHovering();
|
|
HandleMovement();
|
|
}
|
|
|
|
void HandleHovering()
|
|
{
|
|
RaycastHit hit;
|
|
Vector3 rayOrigin = transform.position;
|
|
|
|
// Cast a ray downward to detect the terrain
|
|
if (Physics.Raycast(rayOrigin, -transform.up, out hit, raycastDistance, terrainLayer))
|
|
{
|
|
Mesh mesh = hit.collider.GetComponent<MeshFilter>().mesh;
|
|
int triangleIndex = hit.triangleIndex;
|
|
int vertex1Index = mesh.triangles[triangleIndex * 3 + 0];
|
|
int vertex2Index = mesh.triangles[triangleIndex * 3 + 1];
|
|
int vertex3Index = mesh.triangles[triangleIndex * 3 + 2];
|
|
|
|
Vector3 worldVertex1 = hit.collider.transform.TransformPoint(mesh.vertices[vertex1Index]);
|
|
Vector3 worldVertex2 = hit.collider.transform.TransformPoint(mesh.vertices[vertex2Index]);
|
|
Vector3 worldVertex3 = hit.collider.transform.TransformPoint(mesh.vertices[vertex3Index]);
|
|
|
|
Vector3 interpolatedPoint = worldVertex1 * hit.barycentricCoordinate.x +
|
|
worldVertex2 * hit.barycentricCoordinate.y +
|
|
worldVertex3 * hit.barycentricCoordinate.z;
|
|
|
|
Vector3 localNormal1 = mesh.normals[vertex1Index];
|
|
Vector3 localNormal2 = mesh.normals[vertex2Index];
|
|
Vector3 localNormal3 = mesh.normals[vertex3Index];
|
|
Vector3 worldNormal1 = hit.collider.transform.TransformDirection(localNormal1);
|
|
Vector3 worldNormal2 = hit.collider.transform.TransformDirection(localNormal2);
|
|
Vector3 worldNormal3 = hit.collider.transform.TransformDirection(localNormal3);
|
|
Vector3 interpolatedNormal = worldNormal1 * hit.barycentricCoordinate.x +
|
|
worldNormal2 * hit.barycentricCoordinate.y +
|
|
worldNormal3 * hit.barycentricCoordinate.z;
|
|
interpolatedNormal.Normalize();
|
|
|
|
Vector3 targetPosition = interpolatedPoint + interpolatedNormal * hoverHeight;
|
|
|
|
transform.position = Vector3.Lerp(transform.position, targetPosition, Time.fixedDeltaTime * positionAdjustmentSpeed);
|
|
|
|
Quaternion targetRotation = Quaternion.FromToRotation(transform.up, interpolatedNormal) * transform.rotation;
|
|
rb.rotation = Quaternion.Slerp(rb.rotation, targetRotation, Time.fixedDeltaTime * 5.0f);
|
|
}
|
|
}
|
|
|
|
void HandleMovement()
|
|
{
|
|
// Get player input
|
|
float input = Input.GetAxis("Vertical");
|
|
|
|
// Determine target speed based on input
|
|
float targetSpeed = input * movementSpeed;
|
|
|
|
if (input != 0)
|
|
{
|
|
// Smooth acceleration
|
|
currentSpeed = Mathf.Lerp(currentSpeed, targetSpeed, Time.fixedDeltaTime * acceleration);
|
|
}
|
|
else
|
|
{
|
|
// Smooth deceleration when no input is present
|
|
currentSpeed = Mathf.Lerp(currentSpeed, 0, Time.fixedDeltaTime * deceleration);
|
|
}
|
|
|
|
// Apply movement and rotation
|
|
rb.linearVelocity = transform.forward * currentSpeed;
|
|
float turn = Input.GetAxis("Horizontal") * rotationSpeed;
|
|
rb.angularVelocity = transform.up * turn * Mathf.Deg2Rad;
|
|
}
|
|
}
|