Hello,
I’m still quite new to Unity. But thanks to the forums and various YouTube tutorials, I have made good progress.
Currently, I am building a space game. Since it is set in space, I am using the physics engine but without gravity - otherwise, everything would fall down.
My current plan (I hope I won’t run into a dead end) is to base movements (and rotations) on forces from thrusters (i.e., main engines and side thrusters for roll, pitch, and yaw).
It’s already working quite well for the player. But application of this for a enemy or rockets is currently impossible.
For the ship control, I use the following:
Here is just the essential excerpt:
public float maxThrustInkN = 10f; // Maximum thrust of the ship in kN
public float currentThrottle = 0f; // Current value
public float afterburnerThrustInkN = 300f; // Maximum thrust of the ship's afterburner in kN
public float massInTons = 1f; // Mass of the ship in metric tons
public float drag = 0.1f; // Air resistance of the ship
public float maxThrustGier = 5f; // Maximum yaw rate of the ship
public float maxThrustNick = 5f; // Maximum pitch rate of the ship
public float maxThrustRoll = 5f; // Maximum roll rate of the ship
public float initialGierThrustPercent = 0.1f; // Initial percentage of maximum yaw thrust
public float gierThrustIncrement = 0.1f; // Increase in yaw thrust per second
public float nickThrustIncrement = 0.1f; // Increase in pitch thrust per second
public float rollThrustIncrement = 0.1f; // Increase in roll thrust per second
public TextMeshProUGUI currentSpeedText; // Reference to the TextMeshPro text element to display the speed
public float crossSectionalArea = 10f; // Cross-sectional area of the ship in square meters
public float airDensity = 1.225f; // Air density in kg/m^3
public float angularDrag = 1f; // Angular drag of the ship
void Awake()
{
rb = GetComponent<Rigidbody>();
rb.mass = massInTons * 1000f; // Converts tons to kilograms
rb.drag = drag; // Sets the air resistance of the rigidbody
rb.angularDrag = angularDrag; // Sets the angular drag of the rigidbody
}
void Start()
{
// Connect the controls to the ship
....
InvokeRepeating(nameof(CheckStatus), 0f, 0.1f); // Calls the CheckStatus method every 0.1 seconds
InvokeRepeating(nameof(IncreaseGierThrust), 0f, 0.1f); // Calls the IncreaseGierThrust method every 0.1 seconds
InvokeRepeating(nameof(IncreaseNickThrust), 0f, 0.1f); // Calls the IncreaseNickThrust method every 0.1 seconds
InvokeRepeating(nameof(IncreaseRollThrust), 0f, 0.1f); // Calls the IncreaseRollThrust method every 0.1 seconds
}
void Update()
{
// Read controls and apply
HandleThrustInput();
HandleGierInput();
HandleNickInput();
HandleRollInput();
}
void FixedUpdate()
{
// Apply forces
ApplyThrust();
ApplyGier();
ApplyNick();
ApplyRoll();
}
void HandleThrustInput()
{
if (Input.GetKeyDown(KeyCode.KeypadMultiply)) // Full thrust
{
currentThrust = maxThrustInkN;
}
if (Input.GetKeyDown(KeyCode.KeypadDivide)) // Stop thrust
{
Debug.Log("Stop");
currentThrust = 0;
}
}
// Just as example
void HandleGierInput()
{
if (Input.GetKey(KeyCode.A)) // A key for turning left
{
Debug.Log("Left");
isGierLeft = true;
isGierRight = false;
}
else if (Input.GetKey(KeyCode.D)) // D key for turning right
{
Debug.Log("Right");
isGierLeft = false;
isGierRight = true;
}
else
{
isGierLeft = false;
isGierRight = false;
}
}
// Application of thrust force
void ApplyThrust()
{
thrustForce = transform.right * currentThrust * 1000f; // Converts kN to N and directs the force in the ship's X direction
rb.AddForce(thrustForce);
// Adjusts the velocity vector to the ship's orientation
if (currentThrust > 0f)
{
rb.velocity = transform.right * rb.velocity.magnitude;
}
}
void ApplyGier()
{
Vector3 gierTorque = transform.up * currentGierThrust * 1000f; // Converts kN to N and directs the torque in the ship's Y direction
rb.AddTorque(gierTorque);
}
void ApplyNick()
{
Vector3 nickTorque = transform.forward * currentNickThrust * 1000f; // Converts kN to N and directs the torque in the ship's Z direction
rb.AddTorque(nickTorque);
}
void ApplyRoll()
{
Vector3 rollTorque = transform.right * currentRollThrust * 1000f; // Converts kN to N and directs the torque in the ship's X direction
rb.AddTorque(rollTorque);
}
t’s working quite well.
The issue, however, is that the rotation starts slowly and lingers a bit. For a player, this is not a problem, but for the AI, or initially for something like a rocket flying towards a target, it’s a big problem.
Attached is the rocket script.
To try it out:
Create 3 bodies (rocket, launcher, target) and 1 camera.
The rocket needs a Rigidbody. The script goes on the rocket.
When the rocket starts, it moves along the Y-direction of the launcher. Then it should fly towards the target - for this, it needs to use the forces to rotate.
I started a script to calculate the direction.
Flight axis rocket: Y+ Launch direction launcher: Y+
using UnityEngine;
using UnityEngine;
public class RocketController : MonoBehaviour
{
public float throttleInKilonewtons = 100f; // Throttle of the rocket in kN
public float steeringThrust = 10f; // Steering thrust in kN for yaw and pitch
public float rocketMass = 1f; // Mass of the rocket in tons
public float drag = 0.1f; // Air resistance of the rocket
public float angularDrag = 0.1f; // Roll resistance / inertia
public float startVelocity = 80f; // Start speed in m/s
public float currentSpeed = 0f;
private Rigidbody rb;
public Transform target; // The target as a parameter
public Transform launchPoint; // The launch point as a parameter
public float proportionalGain = 1.0f;
public float derivativeGain = 1.0f;
private float previousError = 0.0f;
private float maxControlSignal = 30;
private void Start()
{
// Setup Rigidbody for rocket
rb = GetComponent<Rigidbody>();
rb.mass = rocketMass * 1000f; // Set physics for the weight of the rocket
rb.drag = drag; // Sets the air resistance of the rigidbody
rb.angularDrag = angularDrag; // Sets the angular drag of the rigidbody
rb.useGravity = false; // Ensure gravity is off - else rockets do not work!
if (launchPoint != null)
{
// Set the initial position and direction of the rocket to point from the launch point
transform.position = launchPoint.position;
Vector3 launchDirection = (launchPoint.forward).normalized;
transform.rotation = Quaternion.LookRotation(launchDirection, Vector3.forward); // Correctly aligned!
// Set start speed in the launch direction of the launcher's Y+ axis
rb.velocity = transform.up * startVelocity;
}
else
{
// Default start speed if no launch point is provided
rb.velocity = transform.up * startVelocity;
}
}
private void FixedUpdate()
{
// Calculate acceleration through throttle / thrust
Vector3 forwardForce = transform.up * throttleInKilonewtons * 1000f; // Convert kN to N - Y direction
rb.AddForce(forwardForce);
if (target != null)
{
// Logic for steering ...see below
Debug.DrawLine(transform.position, target.position, Color.red);
}
}
}
Variant 1: Berechung
// Direction to the target in world coordinates
Vector3 targetDirection = target.position - transform.position;
Debug.Log("Targetdirection:" + targetDirection.ToString());
// Transform the direction into the local space of the rocket
Vector3 localTargetDirection = transform.InverseTransformDirection(targetDirection);
Debug.Log("local Targetdirection:" + localTargetDirection.ToString());
// Determine the local basis vectors of the rocket
Vector3 localForward = transform.up;
Vector3 localUp = transform.forward;
Vector3 localRight = transform.right;
// Calculate the torque around the local Y+ axis to rotate the rocket towards the target
Vector3 torque = Vector3.Cross(localTargetDirection.normalized, localUp) * steeringThrust * 1000f;
Debug.Log("Torque:" + torque.ToString());
// Apply the torques
rb.AddTorque(torque);
[/code}
Variant 2: P-D Regler
[code]
// Calculate the error (deviation from the target)
Vector3 targetDirection = target.position - transform.position;
float error = Vector3.Angle(transform.forward, targetDirection);
// Calculate the rate of change of the error
float errorDerivative = (error - previousError) / Time.fixedDeltaTime;
// Calculate the control signal with the PD controller
float controlSignal = proportionalGain * error + derivativeGain * errorDerivative;
// Limit the control signal to avoid excessive reactions
controlSignal = Mathf.Clamp(controlSignal, -maxControlSignal, maxControlSignal);
// Apply the control signal to the rocket
rb.AddTorque(transform.up * controlSignal);
// Save the current error for the next calculation
previousError = error;
Both do strange things. The rocket does start correctly from the launcher, but the rotation is very wild—it almost looks like tumbling. If the target and the launcher are aligned, it’s minimal, but if the rocket has to fly in an arc, it goes completely out of control. Somehow, I have a mental block here. If any of you have an idea, suggestions would be very welcome.
Otherwise … have fun with your creative work in Unity!