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

Przesuwanie o stałą wartość nie jest dokładne
T1
  • Rejestracja:około 3 lata
  • Ostatnio:około rok
  • 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);
            }
        }
    }
}
edytowany 3x, ostatnio: flowCRANE
Spine
  • Rejestracja:prawie 22 lata
  • Ostatnio:20 minut
  • Postów:6632
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.

🕹️⌨️🖥️🖱️🎮
edytowany 3x, ostatnio: Spine
T1
  • Rejestracja:około 3 lata
  • Ostatnio:około rok
  • 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:prawie 22 lata
  • Ostatnio:20 minut
  • Postów:6632
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.


🕹️⌨️🖥️🖱️🎮
edytowany 2x, ostatnio: Spine
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 3 godziny
  • Lokalizacja:Tuchów
  • Postów:12166
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. ;)


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 3x, ostatnio: flowCRANE
T1
  • Rejestracja:około 3 lata
  • Ostatnio:około rok
  • 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
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 3 godziny
  • Lokalizacja:Tuchów
  • Postów:12166
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.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
T1
  • Rejestracja:około 3 lata
  • Ostatnio:około rok
  • 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:prawie 22 lata
  • Ostatnio:20 minut
  • Postów:6632
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ę.


🕹️⌨️🖥️🖱️🎮
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)