Efekt ruchu celu po ścieżce
Efekt ruchu celu po ścieżce jest jednym z prostszych zachowań automatów utworzonych w scenie świata gry 3D. Ten krótki film ilustruje treść tego tematu.
Ścieżkę tworzą kolejne punkty współrzędnych 3D, które są kolejno odwiedzane przez poruszający się obiekt. Ścieżka w edytorze może być reprezentowana przez same punkty lub punkty połączone linią. Patrz poniższy rysunek.
Linia pomiędzy punktami jest widoczna tylko w edytorze sceny. Ten temat pokaże jak zrobić ruch automatu oraz taką ścieżkę.
Przygotowujemy siatkę poruszającego się obiektu
Przyjmujemy, że ruch obiektu będzie oparty na modelu ruchu pojazdu czterokołowego. W celach poglądowych zrobimy napęd na wszystkie cztery koła ora każde z kół będzie skrętne. Uzyskamy podwójny układ kierowniczy, co przy dość długiej ramie sprawi, że pojazd będzie bardziej zwrotny.
Poniżej proponowana siatka przygotowana w Blenderze
Siatkę eksportujemy z Blendera do pliku *fbx i importujemy w Unity. W unity tworzymy prefabrykat obiektu pojazdu czterokołowego.
Siatka modelu 3d użyta w przykładzie dostepna jest pod tym linkiem- pobierz
Ogólne zasady budowy pojazdu w Unity
Fizyka pojazdu w Unity wymaga przestrzegania kilku zasad.
- collider nadwozia nie może mieć punktów wspólnych z colliderami kół
- pojazd musi posiadać ciało fizyczne (Rigidbody)
- obroty siatek kół odczytuje się z obrotów colliderów kół
- siatka koła nie może być zaczepiona w colliderze koła, i na odwrót
- napęd na koła podawany jest przez moment obrotowy wyrażony w Nm
W prefabrykacie budowanego pojazdu dodajemy dwa puste obiekty (Create Empty), którym przypisujemy odpowiednio nazwy Kola, ZderzaczeKol. Do głównego nadrzędnego obiektu budowanego pojazdu dodajemy Box Collidera i Rigidbody. Box Collider?a edytujemy tak aby był na ramie i nie wchodził w koła (przygotuj margines odstępu na skręt kół). Masę pojazdu próbnie ustawiamy na 1000 jednostek. Patrz poniższy rysunek
Do obiektu Kola (Create Empty) przeciągamy siatki kół.
Do obiektu ZderzaczeKol (Create Empty) dodajemy cztery puste obiekty (Create Empty) tak aby ich współrzędne pokrywały się z siatkami kół. Zmieniamy im nazwę na (ja nadałem ZderzaczKolo1, ZderzaczKolo2,?). Do każdego dodajemy Wheele Collider. Patrz poniższa ilustracja.
Piszemy skrypt dla pojazdu
Utworzymy skrypt o nazwie TarczaStrzelnicza. W skrypcie dodajemy trzy publiczne pola na tablice siatek kół, tablice WheelCollider?ów oraz na obiekt siatki, względem której będzie się poruszać pojazd. Początkowy stan skryptu przedstawia poniższy kod
Wskazówka:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TarczaStrzelnicza : MonoBehaviour
{
public Transform[] Kola;
public WheelCollider[] KolaKolider;
public GameObject sciezka;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
Skrypt podpinamy do prefabrykatu tworzonego pojazdu. Tablice siatek kół oraz ich colliderów uzupełniamy odpowiednimi obiektami budowanego modelu 3D. Patrz poniższa ilustracja.
Na tę chwilę można uruchomić scenę. Pojazd ni powinien zapadać się w grunt. Może się staczać z nierówności. Jeżeli będzie ci podskakiwać, to oznacza ,że jego masa jest zbyt mała w stosunku do ustawionej wartości sił sprężyn amortyzatorów (właściwość dostępna w WheelCollider)
Ścieżka ruchu
Ścieżka ruchu zbudowana jest z obiektów typu Create Empty, jeden jest główny a pozostała są podrzędne. Do głównego obiektu podpięty jest skrypt, którego zadaniem jest rysowanie linii w edytorze ora przechowywanie podrzędnych obiektów, z których pobierane są współrzędne podczas ruchu.
Do tworzonego świata dodaj pusty obiekt Create Empty. Zmień mu nazwę na sciezka. Dodaj kolejny mu podrzędny. Zmień mu nazwę na p. Przypisz ikonę (na rysunku krok 3). Utworzony obiekt kilkukrotnie zduplikuj zmieniając im współrzędne (p 1,p 2,p 3?). Patrz poniższy rysunek.
Utwór skrypt o nazwie Sciezka
Wskazówka:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class Sciezka : MonoBehaviour
{
public Transform[] Punkt;
}
//tylko dla widoczności ścieżki w edytorze
[CustomEditor(typeof(Sciezka))]
public class ExampleEditor : Editor
{
public void OnSceneGUI()
{
var t = target as Sciezka;
Handles.color = Color.yellow;
for (int i = 0; i < t.Punkt.Length-1; ++i)
{
Handles.DrawLine(t.Punkt[i].position, t.Punkt[i + 1].position, 3);
}
}
}
Skrypt podepnij do obiektu sciezka. W odpowiednie pola tablicy dodaj osadzone punkty p, p 1, p 2 itd.
Jeżeli wszystko prawidłowo zostało zrobione, to po wskazaniu myszką głównego obiektu sciezka w edytorze zostaną narysowane linie łączące kolejne punkty. Pierwszy i ostatni punktu w tablicy to te same punkty. Ścieżka zostanie zamknięta, a pojazd będzie się w niej poruszać cyklicznie.
Modyfikacja skryptu TarczaStrzelnicza
Wcześniej utworzony skrypt o nazwie TarczaStrzelnicza modyfikujemy tak aby była możliwa jazda. Dodajemy zmienne na maksymalny kąt skrętu kół, maksymalny moment obrotowy, maksymalną prędkość i indeks aktualnego punktu celu pobranego ze ścieżki. Jazda pojazdu to kolejno wykonywane funkcje ObracajKola(), UaktualnijSkret(), Jazda(), ZmianaPunktu(). Poniżej pełny kod skryptu
Wskazówka:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TarczaStrzelnicza : MonoBehaviour
{
public Transform[] Kola;
public WheelCollider[] KolaKolider;
public GameObject sciezka;
private float maxKatSkretu = 55.0f;
[SerializeField] float maxMomentObrotowy = 1000;
[SerializeField] float maxPredkosc = 25;
int idAktualny = 0;
void ObracajKola()
{
for (int i = 0; i < Kola.Length; i++)
{
var pos = Vector3.zero;
var rot = Quaternion.identity;
KolaKolider[i].GetWorldPose(out pos, out rot);
Kola[i].position = pos;
Kola[i].rotation = rot*Quaternion.Euler(0,-90,90);
}
}
// Start is called before the first frame update
void Start()
{
}
void UaktualnijSkret()
{
Vector3 dir = transform.InverseTransformPoint(
new Vector3(
sciezka.GetComponent<Sciezka>().Punkt[idAktualny].position.x,
transform.position.y,
sciezka.GetComponent<Sciezka>().Punkt[idAktualny].position.z
)
);
float skret=dir.x/dir.magnitude*maxKatSkretu;
//przednia para
KolaKolider[0].steerAngle = skret;
KolaKolider[2].steerAngle = skret;
//tylna para
KolaKolider[1].steerAngle = -skret;
KolaKolider[3].steerAngle = -skret;
}
void Jazda()
{
float predkosc = 2 * Mathf.PI * KolaKolider[0].radius * KolaKolider[0].rpm * 60 / 1000;
if (predkosc < maxPredkosc)
{
for (int i = 0; i < KolaKolider.Length; i++)
KolaKolider[i].motorTorque = maxMomentObrotowy;
}
else
{
for (int i = 0; i < KolaKolider.Length; i++)
KolaKolider[i].motorTorque = 0;
}
}
void ZmianaPunktu()
{
Vector3 cel = new Vector3(sciezka.GetComponent<Sciezka>().Punkt[idAktualny].position.x,
transform.position.y,
sciezka.GetComponent<Sciezka>().Punkt[idAktualny].position.z);
//zbliżaj sie do odległości do 5 metrów
if (Vector3.Distance(transform.position, cel) < 5.0f)
{
if (idAktualny == sciezka.GetComponent<Sciezka>().Punkt.Length - 1)
idAktualny = 0;
else
idAktualny++;
}
}
// Update is called once per frame
void Update()
{
ObracajKola();
UaktualnijSkret();
Jazda();
ZmianaPunktu();
}
}
Uruchom scenę i sprawdź zachowanie się kół. Może się okazać, że siatki kół będą się źle obracać
Przyczyną tego jest inna orientacja osi siatek kół. Poprawkę zrobisz taką linijką kodu w funkcji ObracajKola()
Wskazówka:
Kola[i].rotation = rot*Quaternion.Euler(0,-90,90);
Gdzie współrzędne dla kwaternionu dobierz z danych rotacji siatki koła
W prawidłowo działającym skrypcie skręcają się wszystkie cztery koła
Dokładność osiągnięcia celu jest określana z bieżącej pozycji poruszającego się obiektu i bieżącego punktu celu. Osiągniecie celu mierzone jest z dokładnością pięciu jednostek
Wskazówka:
if (Vector3.Distance(transform.position, cel) < 5.0f)
Przyjęta dokładność wynika z wielkości ramy poruszającego się obiektu. Zauważ, że przy obliczeniach wektorowych nie jest brana współrzędna y kolejnych punktów scieżki. Związane jest to z różną wysokością terenu.