Last active
May 31, 2020 13:57
-
-
Save niuage/170c5a0d8bf69a3980cf7eec358c3962 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Some small parts of the code are specific to my setup, as I'm sometimes getting game objects by name (for the wheel parts), | |
// but most of it could be easily adapted to your project. | |
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Security.Cryptography; | |
using UnityEditor.SceneManagement; | |
using UnityEngine; | |
using UnityEngine.Rendering.Universal.Internal; | |
using UnityEngine.UIElements; | |
public class Hoverjet : MonoBehaviour | |
{ | |
[SerializeField] | |
private Wheel[] wheels; | |
private Wheel[] rightWheels; | |
private Wheel[] leftWheels; | |
private int wheelsInContactCount = 0; | |
public float height; | |
public float thrust; | |
public float forwardSpeed; | |
public float rotationSpeed; | |
public Color thrusterColor; | |
public float minForceMultiplier; | |
public float flightRotationDelay; | |
private float startedFlighingAt; | |
[HideInInspector] | |
public Rigidbody rb; | |
[HideInInspector] | |
public float mass; | |
[HideInInspector] | |
public float verticalAxis, horizontalAxis; | |
private Vector3 lastPosition; | |
[SerializeField] | |
private float _speed = 0; | |
public float Speed { | |
get { return _speed; } | |
set { _speed = value; } | |
} | |
private float zRotationSpeed; | |
public float zRotationAngle; | |
public float zRotationSmoothTime; | |
private void Start() | |
{ | |
rb = GetComponent<Rigidbody>(); | |
mass = rb.mass; | |
rb.centerOfMass += transform.up * -3 + transform.forward * -0.3f; | |
for (int i = 0; i < wheels.Length; i++) wheels[i].Init(this); | |
rightWheels = new Wheel[2] { wheels[0], wheels[2] }; | |
leftWheels = new Wheel[2] { wheels[1], wheels[3] }; | |
lastPosition = transform.position; | |
} | |
private void Update() | |
{ | |
verticalAxis = Input.GetAxis("Vertical"); | |
horizontalAxis = Input.GetAxis("Horizontal"); | |
} | |
void FixedUpdate() | |
{ | |
ComputeSpeed(); | |
for (int i = 0; i < wheels.Length; i++) wheels[i].IsBoosted = false; | |
bool anyWheelsWereInContact = wheelsInContactCount > 0; | |
wheelsInContactCount = 0; | |
if (Mathf.Abs(verticalAxis) > 0.1f) BoostWheels(wheels); | |
if (horizontalAxis > 0.1f) BoostWheels(leftWheels); | |
if (horizontalAxis < -0.1f) BoostWheels(rightWheels); | |
for (int i = 0; i < wheels.Length; i++) { | |
Wheel wheel = wheels[i]; | |
if (Physics.Raycast(wheel.thrusterTransform.position + transform.up, Vector3.down, out RaycastHit hit, height)) | |
wheel.Hit = hit; | |
else | |
wheel.isTouchingGround = false; | |
wheel.Thrust(); | |
wheel.UpdateState(); | |
if (wheel.isTouchingGround) wheelsInContactCount += 1; | |
} | |
// Apply force and torque | |
if (wheelsInContactCount > 1) { | |
rb.AddForce(transform.forward * forwardSpeed * mass * verticalAxis, ForceMode.Force); | |
rb.AddTorque(transform.up * rotationSpeed * mass * horizontalAxis, ForceMode.Force); | |
Vector3 newRotation = transform.eulerAngles; | |
newRotation.z = Mathf.SmoothDampAngle(newRotation.z, horizontalAxis * -zRotationAngle, ref zRotationSpeed, zRotationSmoothTime); | |
transform.eulerAngles = newRotation; | |
} | |
// Allow driver to move while airborne after a certain amount of time | |
if (wheelsInContactCount <= 1 && startedFlighingAt + flightRotationDelay < Time.time) { | |
transform.Rotate(verticalAxis, 0, 0); | |
transform.Rotate(0, 0, -horizontalAxis * 2); | |
} | |
// Records the time at which the car starts flying | |
if (anyWheelsWereInContact && wheelsInContactCount == 0) startedFlighingAt = Time.time; | |
// Allow the car to conserve its momentum when airborne | |
rb.drag = wheelsInContactCount * 0.4f; | |
} | |
private void BoostWheels(Wheel[] _wheels) | |
{ | |
for (int i = 0; i < _wheels.Length; i++) { _wheels[i].IsBoosted = true; } | |
} | |
private void ComputeSpeed() | |
{ | |
Speed = (transform.position - lastPosition).sqrMagnitude / Time.fixedDeltaTime; | |
lastPosition = transform.position; | |
} | |
private void OnDrawGizmos() | |
{ | |
for (int i = 0; i < wheels.Length; i++) { | |
Wheel wheel = wheels[i]; | |
if (wheel.isTouchingGround) { | |
if (wheel.IsBoosted) { | |
Gizmos.color = Color.red; | |
} else { | |
Gizmos.color = Color.grey; | |
} | |
Gizmos.DrawSphere(wheel.Hit.point, 0.2f); | |
} | |
} | |
} | |
} | |
[Serializable] | |
class Wheel | |
{ | |
public GameObject visual; | |
private Transform visualTransform; | |
public float emissionRateMultiplier; | |
private Transform rim; | |
[HideInInspector] | |
public Transform thrusterTransform; | |
private ParticleSystem dustParticles; | |
private Hoverjet jet; | |
[HideInInspector] | |
public bool isTouchingGround; | |
private bool _isBoosted; | |
public bool IsBoosted { get { return _isBoosted; } set { wasBoosted = _isBoosted; _isBoosted = value; } } | |
private bool wasBoosted; | |
private MaterialPropertyBlock block; | |
Renderer boosterRenderer; | |
private RaycastHit _hit; | |
public RaycastHit Hit { | |
get { return _hit; } | |
set { _hit = value; isTouchingGround = true; } | |
} | |
public float thrustMultiplier; | |
private int groundLayer; | |
public void Init(Hoverjet _jet) | |
{ | |
jet = _jet; | |
visualTransform = visual.transform; | |
rim = visualTransform.Find("Rim"); | |
thrusterTransform = visualTransform.Find("thruster"); | |
Transform dust = visualTransform.Find("dust"); | |
if (dust != null) { | |
dustParticles = dust.gameObject.GetComponent<ParticleSystem>(); | |
} | |
block = new MaterialPropertyBlock(); | |
boosterRenderer = thrusterTransform.Find("Plane").GetComponent<Renderer>(); | |
groundLayer = LayerMask.NameToLayer("Plane"); | |
} | |
public void Thrust() | |
{ | |
if (!isTouchingGround) return; | |
jet.rb.AddForceAtPosition( | |
Vector3.up * jet.thrust * (IsBoosted ? thrustMultiplier : 1) * Mathf.InverseLerp(jet.height, jet.minForceMultiplier, Hit.distance), // force | |
thrusterTransform.position - Vector3.up // at position | |
); | |
EmitSandParticles(); | |
} | |
public void UpdateState() | |
{ | |
RotateRimToTerrain(); | |
RotateBoosterToMovingDirection(); | |
UpdateColor(); | |
} | |
/// ///////////////////////////////////////////////////////////////////////////////////////////////// | |
private void EmitSandParticles() | |
{ | |
if (dustParticles == null) return; | |
var emission = dustParticles.emission; | |
if (isTouchingGround && Hit.collider.gameObject.layer == groundLayer) | |
emission.rateOverTime = emissionRateMultiplier * Mathf.InverseLerp(2, 5, jet.Speed); | |
else | |
emission.rateOverTime = 0f; | |
} | |
private void UpdateColor() | |
{ | |
if (IsBoosted) { | |
SetThrusterColor(jet.thrusterColor * Mathf.Lerp(1, 3, jet.verticalAxis + jet.horizontalAxis)); | |
} else if (wasBoosted) { | |
SetThrusterColor(jet.thrusterColor); | |
} | |
} | |
private void SetThrusterColor(Color color) | |
{ | |
block.SetColor("_BaseColor", color); | |
boosterRenderer.SetPropertyBlock(block); | |
} | |
private void RotateRimToTerrain() | |
{ | |
Vector3 direction = isTouchingGround ? Hit.normal : thrusterTransform.up; | |
rim.rotation = Quaternion.RotateTowards( | |
rim.rotation, | |
Quaternion.FromToRotation(rim.up, direction) * rim.rotation, | |
90 * Time.deltaTime | |
); | |
// Restrict the rotation on X axis. | |
// there's probably a more efficient way to do this than doing the full rotation then cancelling out the other axis. | |
Quaternion q = rim.localRotation; | |
q.eulerAngles = new Vector3(q.eulerAngles.x, 0, 0); | |
rim.localRotation = q; | |
} | |
private void RotateBoosterToMovingDirection() | |
{ | |
thrusterTransform.rotation = Quaternion.RotateTowards( | |
thrusterTransform.rotation, | |
Quaternion.FromToRotation( | |
thrusterTransform.up, | |
Vector3.up + (jet.verticalAxis * jet.transform.forward + jet.horizontalAxis * jet.transform.right).Map(comp => comp * 0.6f) | |
) * thrusterTransform.rotation, | |
360 * Time.deltaTime | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment