This page has been translated automatically.
Основы UNIGINE
1. Введение
2. Виртуальные миры и работа с ними
3. Подготовка 3D моделей
4. Материалы
5. Камеры и освещение
6. Реализация логики приложения
7. Создание кат-сцен и запись видео
8. Подготовка проекта к релизу
9. Физика
10. Основы оптимизации
11. ПРОЕКТ2: Шутер от первого лица
13. ПРОЕКТ4: VR приложение с простым взаимодействием

Реализация физики движения автомобиля

Now let's move on to bringing the main character to life. Create a new C# component named Car to control the physical model of the car.Перейдем к оживлению главного героя. Создадим новый C# компонент Car, который будет управлять физической моделью автомобиля.

The component will provide a set of vehicle parameters and functions according to which the wheels will rotate and the motors embedded in the wheel joints will be activated.Компонент будет предоставлять набор параметров и функций автомобиля, в соответствии с которыми будут вращаться колеса и включаться моторы, встроенные в колесные сочленения.

Исходный код (C#)
using System;
using System.Collections;
using System.Collections.Generic;
using Unigine;

[Component(PropertyGuid = "AUTOGENERATED_GUID")] // <-- идентификатор генерируется автоматически для нового компонента
public class Car : Component
{
	// определяем два режима движения: вперед и назад
	protected enum MoveDirection
	{
		Forward,
		Reverse,
	}
	
	// параметры автомобиля: ускорение, максимальная скорость и поворот руля, крутящий момент
	public float acceleration = 50.0f;
	public float max_velocity = 90.0f;
	private float max_turn_angle = 30.0f;
	public float default_torque = 5.0f;
	// длина и ширина кузова
	public float car_base = 3.0f;
	public float car_width = 2.0f;
	// быстрота газа, тормоза и поворота
	public float throttle_speed = 2.0f;
	public float brake_speed = 1.2f;
	public float wheel_speed = 2.0f;
	// сила рабочего и стояночного тормоза
	public float brake_damping = 8.0f;
	public float hand_brake_damping = 30.0f;

	// ссылки на ноды колес
	public Node wheel_fl = null;
	public Node wheel_fr = null;
	public Node wheel_rl = null;
	public Node wheel_rr = null;

	// ссылки на ноды светотехники: стоп-сигнал и фонарь заднего хода
	public Node brake_light = null;
	public Node reverse_light = null;

	// колесные сочленения
	private JointWheel joint_wheel_fl = null;
	private JointWheel joint_wheel_fr = null;
	private JointWheel joint_wheel_rl = null;
	private JointWheel joint_wheel_rr = null;

	// определим желаемые и текущие значения для газа, тормоза, руля и стояночного тормоза
	private float target_throttle = 0.0f;
	private float target_brake = 0.0f;
	private float target_wheel = 0.0f;
	private float target_hand_brake = 0.0f;

	private float current_throttle = 0.0f;
	private float current_brake = 0.0f;
	private float current_wheel = 0.0f;
	private float current_hand_brake = 0.0f;

	// по умолчанию автомобиль в режиме движения вперёд
	private MoveDirection current_move_direction = MoveDirection.Forward;

	// переменные для текущей скорости вращения, крутящего момента и угла поворота
	private float current_velocity = 0.0f;
	private float current_torque = 0.0f;
	private float current_turn_angle = 0.0f;

	// физическое тело кузова
	private BodyRigid CarBodyRigid = null;

	private void Init()
	{
		// при инициализации получаем колесные сочленения и физическое тело кузова
		if (wheel_rl)
			joint_wheel_rl = wheel_rl.ObjectBody.GetJoint(0) as JointWheel;

		if (wheel_rr)
			joint_wheel_rr = wheel_rr.ObjectBody.GetJoint(0) as JointWheel;

		if (wheel_fl)
			joint_wheel_fl = wheel_fl.ObjectBody.GetJoint(0) as JointWheel;

		if (wheel_fr)
			joint_wheel_fr = wheel_fr.ObjectBody.GetJoint(0) as JointWheel;

		CarBodyRigid = node.ObjectBodyRigid;
	}

	protected virtual void Update()
	{
		// используем время рендера предыдущего кадра, чтобы не зависеть от FPS
		float deltaTime = Game.IFps;

		// плавно изменяем текущие газ, тормоз и положение руля в сторону требуемых
		current_throttle = MathLib.MoveTowards(current_throttle, target_throttle, throttle_speed * deltaTime);
		current_brake = MathLib.MoveTowards(current_brake, target_brake, brake_speed * deltaTime);
		current_wheel = MathLib.MoveTowards(current_wheel, target_wheel, wheel_speed * deltaTime);
		current_hand_brake = MathLib.MoveTowards(current_hand_brake, target_hand_brake, brake_speed * deltaTime);

		// включаем ноду стоп-сигнала, если тормоз активен (значение больше ~нуля)
		if (brake_light != null)
			brake_light.Enabled = target_brake > MathLib.EPSILON;
		// текущее значение крутящего момента вычисляется как произведение положения газа и стандартного множителя
		current_torque = default_torque * current_throttle;

		// при нажатии на газ
		if (current_throttle > MathLib.EPSILON)
		{
			// текущая угловая скорость колес изменяется согласно ускорению и направлению движения
			current_velocity += deltaTime * MathLib.Lerp(0.0f, acceleration, current_throttle) * (current_move_direction == MoveDirection.Forward ? 1.0f : -1.0f);
		}
		else
		{
			// в противном случае снижаем скорость экспоненциально
			current_velocity *= MathLib.Exp(-deltaTime);
		}

		// вычисляем силу тормозов в зависимости от их текущей интенсивности
		float damping = MathLib.Lerp(0.0f, brake_damping, current_brake);
		float rdamping = MathLib.Lerp(0.0f, hand_brake_damping, current_hand_brake);
		// применяем торможение для всех колес, для задних колес применяется также и стояночный тормоз
		joint_wheel_fl.AngularDamping = damping;
		joint_wheel_fr.AngularDamping = damping;
		joint_wheel_rl.AngularDamping = MathLib.Max(damping, rdamping);
		joint_wheel_rr.AngularDamping = MathLib.Max(damping, rdamping);

		// вычисляем текущие угловую скорость и угол поворота, ограничив крайние значения
		current_velocity = MathLib.Clamp(current_velocity, -max_velocity, max_velocity);
		current_turn_angle = MathLib.Lerp(-max_turn_angle, max_turn_angle, MathLib.Clamp(0.5f + current_wheel * 0.5f, 0.0f,1.0f));

		// симуляция дифференциала для передней оси: колеса должны повернуться на различный угол
		float angle_0 = current_turn_angle;
		float angle_1 = current_turn_angle;
		if (MathLib.Abs(current_turn_angle) > MathLib.EPSILON)
		{
			float radius = car_base / MathLib.Tan(current_turn_angle * MathLib.DEG2RAD);
			float radius_0 = radius - car_width * 0.5f;
			float radius_1 = radius + car_width * 0.5f;

			angle_0 = MathLib.Atan(car_base / radius_0) * MathLib.RAD2DEG;
			angle_1 = MathLib.Atan(car_base / radius_1) * MathLib.RAD2DEG;
		}
		// применяем поворот для обоих передних колес при помощи матрицы поворота вдоль оси Z
		joint_wheel_fr.Axis10 = MathLib.RotateZ(angle_1).GetColumn3(0);
		joint_wheel_fl.Axis10 = MathLib.RotateZ(angle_0).GetColumn3(0);
	}

	// параметры физических объектов важно изменять в методе UpdatePhysics
	private void UpdatePhysics()
	{
		// применяем расчетные значения угловой скорости и крутящего момента колес
		// все 4 колеса имеют 'двигатель', т.е. автомобиль полноприводный.
		joint_wheel_fl.AngularVelocity = current_velocity;
		joint_wheel_fr.AngularVelocity = current_velocity;

		joint_wheel_fl.AngularTorque = current_torque;
		joint_wheel_fr.AngularTorque = current_torque;
		
		joint_wheel_rl.AngularVelocity = current_velocity;
		joint_wheel_rr.AngularVelocity = current_velocity;

		joint_wheel_rl.AngularTorque = current_torque;
		joint_wheel_rr.AngularTorque = current_torque;
	}

	// добавим методы для управления автомобилем: газ, тормоз, поворот руля и стояночный тормоз
	protected void SetThrottle(float value)
	{
		target_throttle = MathLib.Clamp(value, 0.0f, 1.0f);
	}

	protected void SetBrake(float value)
	{
		target_brake = MathLib.Clamp(value, 0.0f, 1.0f);
	}

	protected void SetWheelPosition(float value)
	{
		target_wheel = MathLib.Clamp(value, -1.0f, 1.0f);
	}

	protected void SetHandBrake(float value)
	{
		target_hand_brake = MathLib.Clamp(value, -1.0f, 1.0f);
	}

	// метод смены режима движения, здесь также происходит управление фонарем заднего хода
	protected void SetMoveDirection(MoveDirection value)
	{
		if (current_move_direction == value)
			return;
		current_velocity = 0.0f;
		current_move_direction = value;
		if (reverse_light != null)
			reverse_light.Enabled = current_move_direction == MoveDirection.Reverse;
	}

	protected MoveDirection CurrentMoveDirection { get { return current_move_direction; } }

	// метод для мгновенного перемещения автомобиля, будет использован для возвращения авто на начальную позицию
	public void Reset(mat4 transform)
	{
		node.WorldTransform = transform;
		node.ObjectBodyRigid.LinearVelocity = vec3.ZERO;
		node.ObjectBodyRigid.AngularVelocity = vec3.ZERO;
		current_velocity = 0.0f;
	}

	// получение скорости сразу в км/ч
	public float Speed { get { return CarBodyRigid.LinearVelocity.Length * 3.6f; } }
}
Последнее обновление: 25.03.2024
Build: ()