Przesuwanie o stałą wartość nie jest dokładne

Przesuwanie o stałą wartość nie jest dokładne
T1
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 37
0

Witam.
Otóż mam pewien problem z 4 Sprite-ami, które mam jako Background w grze.
Przesuwam je w FixedUpdate() i nieważne czy wpiszę że maja się przesuwać o Time.fixedDeltaTime czy o 0.02 to po kilku przesunięciach zmienia się ta wartość.

Dla sprostowania, wartość przesuwana ciągle podawana jest jako 0.02, jednak wartość ta jest co kilka aktualizacji powiększana o 0.000001 przez co połączone Sprite-y co jakiś czas rozjeżdżają mi się. Na jednym zdjęciu położenie GameObject ze Sprite-em, a na kolejnym jak przerwa wygląda w powiększeniu (bardzo duże powiększenie).
Oraz kod jak przesuwam tło.

Dodam że w gameObjectBackground[] znajdują sie tylko 4 obiekty. Podczas debugowania podaje prawidłowe położenie obiektów jakie powinno być (czyli zmieniać się co 0.02), natomiast w nity ta wartość jest inna.

Kod FixedUpdate():

Kopiuj
private void FixedUpdate()
{
    Debug.Log("Position X: " + gameObjectBackground[2].transform.position.x.ToString());
    float time = Time.fixedDeltaTime;
    float moveSpeed = sceneVariable.MoveSpeed;
    if (sceneVariable.Move)
    {
        foreach (GameObject item in gameObjectBackground)
        {
            item.transform.position += Vector3.left * time;
            //Sprawdza czy jedno z teł doszło do ganicy
            if (item.transform.localPosition.x <= -1*moveBackground)
            {
                //Przesuwa z lewej na prawą stronę tło, aby zapętlić
                item.transform.localPosition = new Vector3(((gameObjectBackground.Length-1)*moveBackground),
                    item.transform.localPosition.y, item.transform.localPosition.z);
            }
        }
    }
}
Spine
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 6969
4
  1. Typ float jest niedokładny, więc przesuwanie grupy obiektów o pozornie taką samą wartość nie przesunie ich dokładnie o taką samą wartość. Poczytaj sobie o wewnętrznej reprezentacji liczb zmiennoprzecinkowych: https://en.wikipedia.org/wiki/IEEE_754
  2. Jeśli chcesz przesuwać grupę obiektów, to najlepiej wszystkie obiekty podłączyć pod jeden GameObject/Transform, czyli wspólny parent i tylko ten parent przesuwaj, a nie każdy obiekt z osobna.
  3. FixedUpdate nie jest dobry do przesuwania wyświetlanej grafiki. Będzie szarpało, bo tło nie jest Rigidbody z włączoną interpolacją. Rób przesuwanie w zwykłym Update() i używaj Time.deltaTime.
  4. Spróbuj zwiększyć ilość cyfr po przecinku w liczbie podawanej do Debug.Log(...) => https://answers.unity.com/questions/173094/show-vector3-full-float-value.html
  5. Jeśli sprite'y idealnie do siebie przylegają to i tak mogą powstawać szczeliny podczas renderingu. Najlepiej jeśli sprite'y na siebie lekko nachodzą i mają delikatnie rozmyte brzegi w miejscach łączenia (z kanałem alpha).
  6. Jeśli tło ma się powtarzać, to możesz zrobić jeden obrazek, ustawić mu repeat i przesuwać jako teksturę po renderowanym czworokącie, zamiast przesuwać obiekty. Wtedy na pewno nie będzie żadnych szczelin.
T1
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 37
0

Trochę ciężko mi uwierzyć o tym że tym float nie jest dokładny, ponieważ zawsze było dobrze.
0.02 to było zawsze 0.0200000 i nie ważne ile zer po przecinku, więc zmiana nagle z 0.02 na 0.02000001 jest dla mnie trochę mało wiarygodna,
zwłaszcza że w Visual Studio podczas podglądu wartości zawsze były dobre.
Z tego co pamiętam to jak podają w tutorialach i kursach to aby ni szarpało dać właśnie w FixedUpdate ,zamiast w Update, ponieważ zawsze FixedUpdate ma stałą czasową i odświeża się 50 razy na sekundę (chyba że ustawimy inaczej). I wszędzie zalecali stosowaie FixedUpdate też do działań na fizyce.

Co do metody z Repeat, to ją wykorzystam, ponieważ będzie mniej z nią problemów.

Zastanawiałem się też nad użyciem zamiast Float do Double....
I dziwne że tylko w tym przypadku mam z tym problemy.

Spine
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 6969
3
tdx110 napisał(a):

Trochę ciężko mi uwierzyć o tym że tym float nie jest dokładny, ponieważ zawsze było dobrze.
0.02 to było zawsze 0.0200000 i nie ważne ile zer po przecinku, więc zmiana nagle z 0.02 na 0.02000001 jest dla mnie trochę mało wiarygodna,

0.02 może i jest stałe i dobrze reprezentowane w pamięci.
Ale kiedy przez 0.02 mnożysz Vector3.left i wynik dodajesz do item.transform.position to masz już kilka operacji, które naruszają precyzję.

Poza tym jak widać, 0.02 nie jest dobrze reprezentowane w pamięci :] ( https://www.h-schmidt.net/FloatConverter/IEEE754.html )

screenshot-20230212210634.png

Z tego co pamiętam to jak podają w tutorialach i kursach to aby ni szarpało dać właśnie w FixedUpdate ,zamiast w Update, ponieważ zawsze FixedUpdate ma stałą czasową i odświeża się 50 razy na sekundę (chyba że ustawimy inaczej). I wszędzie zalecali stosowaie FixedUpdate też do działań na fizyce.

Twórcy tutoriali mają różny poziom wtajemniczenia... Albo mieli złe doświadczenia z Update(), albo źle się nauczyli od innych "specjalistów".

Wyświetlanie grafiki w grze jest powiązane z Update().
Update się wykonuje przed każdym wyświetleniem klatki, więc gdy zaimplementujesz ruch w Update(), to niezależnie, czy wyświetlasz 30 klatek na sekundę, czy 120 klatek na sekundę, to obiekt wykona ruch o odpowiednią wartość między każdymi dwiema klatkami.
W FixedUpdate() jak masz te 50 razy na sekundę, to przy 120 klatkach na sekundę, obiekt między dwiema klatkami czasem poruszy się 2 razy, a czasem 3 razy. Co spowoduje szarpanie. Raz szybciej, raz wolniej.

W fizyce (rigidbody) zaznaczysz sobie interpolację, i dzięki temu obiekt nie skacze, bo jego grafika jest odpowiednio przesuwana w Update(), chociaż symulacja fizyczna ma stały skok czasu. Czytaj: https://docs.unity3d.com/ScriptReference/Rigidbody2D-interpolation.html

Zastanawiałem się też nad użyciem zamiast Float do Double....

W Unity to i tak nic nie da, bo wszystkie klasy operują na float.

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12270
1
Spine napisał(a):

W FixedUpdate() jak masz te 50 razy na sekundę, to przy 120 klatkach na sekundę, obiekt między dwiema klatkami czasem poruszy się 2 razy, a czasem 3 razy. Co spowoduje szarpanie. Raz szybciej, raz wolniej.

Machnąłeś się. Jeśli logika aktualizowana jest 50 razy na sekundę, a klatki renderowane 120 razy na sekundę, to bez interpolacji, dany stan świata widoczny będzie czasem przez 2 klatki, a czasem przez 3 klatki. ;)

T1
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 37
0

Ok, rozumiem.
Człowiek uczy się przez całe życie.

Spine napisał(a):

W FixedUpdate() jak masz te 50 razy na sekundę, to przy 120 klatkach na sekundę, obiekt między dwiema klatkami czasem poruszy się 2 razy, a czasem 3 razy. Co spowoduje szarpanie. Raz szybciej, raz wolniej.

Co do tego to przecież również ulega zmianie Time.fixedDeltaTime
Oraz z tego co wiem z wykresu Unity Manual
To FixedUpdate jest wywoływany dużo wcześniej.
Dodatkowo podają nawet w Manualu Unity że zaleca się stosowanie rzeczy o stałym ruchu w FixedUpdate, ponieważ tak jak Update przez obciążenia może mieć raz 100kl/s a raz 10kl/s to fizyka czy ruch musi się zawsze odbywać ze stałym czasem.

FixedUpdate: FixedUpdate is often called more frequently than Update. It can be called multiple times per frame, if the frame rate is low and it may not be called between frames at all if the frame rate is high. All physics calculations and updates occur immediately after FixedUpdate. When applying movement calculations inside FixedUpdate, you do not need to multiply your values by Time.deltaTime. This is because FixedUpdate is called on a reliable timer, independent of the frame rate.

flowCRANE
  • Rejestracja: dni
  • Ostatnio: dni
  • Lokalizacja: Tuchów
  • Postów: 12270
1
tdx110 napisał(a):

Dodatkowo podają nawet w Manualu Unity że zaleca się stosowanie rzeczy o stałym ruchu w FixedUpdate, ponieważ tak jak Update przez obciążenia może mieć raz 100kl/s a raz 10kl/s to fizyka czy ruch musi się zawsze odbywać ze stałym czasem.

Po to właśnie jest FixedUpdate, aby w nim aktualizować fizykę. Dzięki temu będzie ona odporna na lagi i deterministyczna.

T1
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 37
0

I jeszcze w takim razie mam jedno pytanie do bardziej doświadczonych kolegów. :)
Jako iż robię grę 2D i nie jest ona specjalnie mocno obciążająca lepiej jest dodać grafikę i całą resztę w UI, czy normalnie w GameObject-y nie należące do UI?
Obecnie mam dość spory problem z uniwersalnym skryptem skalowania widoku z kamery do różnych rozdzielczości.
Wadą UI z tego co czytałem jest to że jest on dość wolny.

Spine
  • Rejestracja: dni
  • Ostatnio: dni
  • Postów: 6969
0
tdx110 napisał(a):

Dodatkowo podają nawet w Manualu Unity że zaleca się stosowanie rzeczy o stałym ruchu w FixedUpdate, ponieważ tak jak Update przez obciążenia może mieć raz 100kl/s a raz 10kl/s to fizyka czy ruch musi się zawsze odbywać ze stałym czasem.

Fizyka owszem, ale w przypadku ruchu tła, które nie ma ciał fizycznych, to nie robi różnicy, czy tło porusza się 10 klatek na sekundę, czy 100 klatek na sekundę...
Ważne jest, żeby w momencie renderowania tło na ekranie miało właściwą pozycję.
Przesuwanie tła w Update() nie zaburzy działania gry, niezależnie od ilości klatek na sekundę.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.