Autor Wątek: Płynny ruch AI  (Przeczytany 2210 razy)

Offline MJay

  • Użytkownik

# Lipiec 01, 2013, 13:12:38
Hey.
Mam prośbę, ponieważ potrzebuję wykminić jak zrobić płynny ruch postaci, która lata sobie między jednym waypointem a innym.
Chcę aby to wyglądało płynnie dlatego używam sił do poruszania obiektem a nie zwykłych obliczeń matematycznych.

Chcę osiągnąć coś takiego ( zdjęcie ). Te narysowane to są wektory prędkości. Jak teraz dobrać siłę aby coś takiego osiągnąć?
Próbując ciągle otrzymuję rezultat, że obraca się obiekt wokół punktu jak asteroida wokół Słońca. Bo siła jest wyliczana z każdego kolejnego punktu tak, aby leciała prosto do punktu.

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline ZdzislawJarzabek

  • Użytkownik

# Lipiec 01, 2013, 13:28:22
Siłę zmieniasz w punktach, w których liczysz. W update() wykonujesz przesunięcie z prędkością uwzględniającą siłę i deltaTime. W pracy nie mam Unity, jak do wieczora nie wykombinujesz jak to zrobić, to jak nie zapomnę to wrzucę prosty przykład.

Offline MJay

  • Użytkownik

# Lipiec 01, 2013, 13:34:14
Tzn tak. Mój kod wygląda tak:
var _points     : GameObject[];
var _dst        : int;
var _maxSpeed   : float;
var _go         : boolean;
var _direction : Vector3;

function Start () {
        _points = GameObject.FindGameObjectsWithTag( "Point" );
        _dst = 0;
        _direction = moveDirection( gameObject, _points[ _dst ].transform.position );
        _maxSpeed = 5;
}

function FixedUpdate () {
                MovePiranha( gameObject, _points[ _dst ].transform.position );
}

function MovePiranha( thisObject : GameObject, destination : Vector3 ) {
        if( Mathf.Abs( thisObject.rigidbody.velocity.magnitude - _maxSpeed ) > 0.01 || _go ) {
                thisObject.rigidbody.AddForce( _direction * _maxSpeed, ForceMode.Acceleration );
                _go = false;
        } else {
        thisObject.rigidbody.velocity = moveDirection( thisObject, destination ) * _maxSpeed;
        }
       
        //Sprawdzenie czy obiekt dotarł do celu
        if( Mathf.Abs( ( destination - thisObject.transform.position ).magnitude ) < ( Time.deltaTime * _maxSpeed ) ) {
                _direction = -_direction + moveDirection( gameObject, _points[ ++_dst ].transform.position );
                _go = true;             
        }
       
        Debug.Log( thisObject.rigidbody.velocity.magnitude );
}

function moveDirection( thisObject : GameObject, destination : Vector3 ) {
//Wyliczenie kierunku lotu obiektu
        var direction = destination - thisObject.transform.position;
        direction.Normalize( );
       
        return direction;
}

Działa tak jak chcę, żeby działało, ale nie trafia w punkt. Potrzebuję uwzględnić korektę lotu.

//Edit + CodeEdit
Dodałem korekcję lotu i działa niby, ale płynnie to wygląda tylko dla pierwszego punktu..
« Ostatnia zmiana: Lipiec 01, 2013, 14:03:13 wysłana przez MJay »

Offline ZdzislawJarzabek

  • Użytkownik

  • +1
# Lipiec 01, 2013, 14:15:50
W domu popatrzę na kod, teraz tylko rzuciło mi się w oczy: w FixedUpdate updatuj tylko fizykę, poruszanie obiektu w Update().
« Ostatnia zmiana: Lipiec 01, 2013, 14:17:22 wysłana przez ZdzislawJarzabek »

Offline Xirdus

  • Redaktor

# Lipiec 01, 2013, 15:17:57
Zamiast bawić się z siłą, ja bym poszedł w stronę krzywych NURBS. Chociaż one też nie przechodzą dokładnie przez punkty kontrolne.

Offline Adam27

  • Użytkownik

# Lipiec 01, 2013, 17:31:40
Można również pomyśleć nad interpolacją sześcienną - np. http://en.wikipedia.org/wiki/Cubic_Hermite_spline.

Offline Kurak

  • Użytkownik

# Lipiec 01, 2013, 18:34:55
Jeśli ślizganie ci niestraszne i właściwie to postać nie ma nóg to wystarczy prosty steering.

http://www.red3d.com/cwr/steer/PathFollow.html

Offline MJay

  • Użytkownik

# Lipiec 01, 2013, 20:59:44
Chcę uzyskać efekt latania obiektu w lewo i prawo na planszy 2D, ale nie chcę żeby się poruszało tylko po jednej linii, a żeby zmieniało wysokość.
Wygląda na to, że na mój obiekt ciągle oddziałują siły, nawet jeśli w else'ie ustawiana jest prędkość na sztywno, to siła jest zapamiętana i w kolejnym punkcie gdy dochodzi do zmiany kierunku, dodawane są nowe siły, które najwidoczniej nie redukują całkowicie tamtych wcześniejszych..

Istnieje jakiś sposób na zresetowanie sił które oddziałują na obiekt? Gógl nic nie wie na ten temat.
Tu widzę gościa który miał ten sam problem 3 lata temu http://forum.unity3d.com/threads/39998-reset-rigidbody

Offline sir_xeno

  • Użytkownik

# Lipiec 01, 2013, 21:23:22
Najprościej będzie ustawić mu isKinematic i sterować nim samemu jak tylko chcesz.

Offline MJay

  • Użytkownik

# Lipiec 01, 2013, 21:45:33
W sensie poprzez równania matematyczne? Tak miałem wcześniej, ale nie mogłem osiągnąć tej płynności co przez użycie siły. Mogę ewentualnie jeszcze zrobić tak, że wygaszam prędkość w którą leci obiekt zastępując ją rosnącą prędkościa w kierunku celu.
Ok, ale to wyjście ewakuacyjne ( bo to dokładnie to co powinna robić siła ) jeśli da się za pomocą siły, to proszę o pomoc ;-)

Offline Xirdus

  • Redaktor

# Lipiec 02, 2013, 00:51:04
Jakiś specjalny powód, dlaczego chcesz używać sił, a nie matematycznych krzywych? (Bo jak najbardziej da się opisać płynny ruch za pomocą równań matematycznych - matma to nie tylko proste.)

Offline MJay

  • Użytkownik

# Lipiec 02, 2013, 01:04:24
Optymalizacja, piszę grę na zlecenie na tablety, chcę aby kod był jak najbardziej zoptymalizowany, aby nie powstały żadne dławienia. Gra sama w sobie wykorzystuje już wiele obiektów. Jeszcze się nagłowię do czwartku, ewentualnie napiszę swoje funkcje fizyczne.
Ok. Dzięki wszystkim za pomoc. Dobrej nocy.

Offline Xirdus

  • Redaktor

  • +1
# Lipiec 02, 2013, 01:40:48
Jeśli ma być optymalnie na tablety, to właśnie krzywe matematyczne wymagają mniej obliczeń niż fizyka. Chyba że z fizyki już korzystasz w inny sposób.

Offline .:NOXY:.

  • Użytkownik
    • Profil

  • +1
# Lipiec 02, 2013, 12:00:44
A nie mozesz tego zrobic po najmniejszej lini oporu i poprostu miec pare punktow, robic lookAt na niego i potem robic position += forward; z interpolacja zeby to bylo plynne ?