atarionline.pl Mad Pascal - przypisanie tablic liczb - Forum Atarum

Jeśli chcesz wziąć udział w dyskusjach na forum - zaloguj się. Jeżeli nie masz loginu - poproś o członkostwo.

  • :
  • :

Vanilla 1.1.4 jest produktem Lussumo. Więcej informacji: Dokumentacja, Forum.

    • 1:
       
      CommentAuthorMq
    • CommentTime4 Jan 2021
     
    Na poniższej natury problem natknąłem się już kilkakrotnie i za każdym razem kombinuję od nowa jak to zrobić.

    Jaki jest najskuteczniejszy (najwydajniejszy i oszczędny) sposób przypisania jakiemuś zbiorowi liczb innych liczb?
    Nie wiem czy dobrze to opisuję, chodzi mi o takie coś, że mam kilka liczb pojawiających się w jednej zmiennej i potrzebuję dla każdej z nich przypisać inne liczby drugiej zmiennej w zależności od tej pierwszej, ale zależność nie jest wynikiem działania matematycznego, liczby mogą być różne, zupełnie dowolne, czyli np. coś takiego:

    if a=5 then b:=8;
    if a=12 then b:=3;
    if a=255 then b:=78;
    if a=100 then b:=152;

    Rozchodzi mi się o to, czy da się to zrobić jakoś lepiej niż pisząc taki szereg if-ów, bo np. mam takich liczb 50. Czy taki kod z if-ami będzie wydajny, czy coś da się ulepszyć?

    Zależy mi na zwięzłości kodu i jeśli chodzi o wykorzystanie pamięci, ale też na maksymalnej szybkości działania takiego algorytmu, który to potrafi zrobić.

    Myślałem o jakichś tablicach, ale wtedy musiał bym je za każdym razem przeszukiwać dla każdej liczby, więc wydajność słaba...

    Rozwiązania jakie mnie interesują, to Mad Pascal, lub może być ze wstawkami w assemblerze jeśli ma to sens.
    • 2: CommentAuthormono
    • CommentTime4 Jan 2021 zmieniony
     
    A mapa? Coś a'la:
    b := tablica[a];

    W tablica pod indeksem:
    - 5 masz wartość 8
    - 12 masz 3
    - 100 masz 152
    - 255 masz 78
    itd.

    Edit: Jeśli zbiór a da się jakoś przeliczyć na mniejszą liczbę indeksów (np. a jest zawsze parzyste), no to tablica ma 128 elementów zamiast 256, a wywołanie to wtedy:
    b := tablica[a shr 1]
    • 3: CommentAuthorAdam
    • CommentTime4 Jan 2021
     
    Dwie tablice A i B z odpowiadającymi sobie parami wartości <a, b>, na pierwszej posortowanej robisz wyszukiwanie binarne i w maksymalnie kilku krokach znajdujesz odpowiednik b dla a?
    • 4:
       
      CommentAuthorMq
    • CommentTime4 Jan 2021
     
    @mono, tak jak piszesz, to mam zrobione tablice odwróconych wszystkich bajtów (dla odwracania postaci w grze w prawo i w lewo na pmg) i tablicę odwróconych wszystkich bajtów w znakach trybu Antic 4 (dla robienia mirrorów obrazków w tym trybie). Pomysł z shr też jest dobry i też już na niego wcześniej wpadłem robiąc tablicę tekstów wyświetlanych w grze przy obiektach mijanych (mam tak, że pomiędzy tekstami jest zawsze pusty string, więc zamiast umieszczać co drugi string pusty w tablicy, to zrobiłem właśnie myk z shr i na parzyste mam teksty z tablicy, a na nieparzyste pusty string).

    Kłopot w tym, że takie tablice zajmują dużo miejsca i wymagają jednak tego, że nie mamy dowolności (np. parzyste/nieparzyste itp.).

    Adam, Twój pomysł bardzo ciekawy, spróbuję sobie coś takiego zaprogramować, powinno się opłacić w określonych przypadkach

    Wszystko zależy ile jest liczb. Podam może przykłady do czego ja takich rzeczy potrzebuję:

    Kilkadziesiąt elementów, które zwracają adresy na ekranie. Chodzi o to, że np. mam przejście do innego świata i jest ono konkretnym znakiem. Ten konkretny znak występuje na mapie w kilku miejscach i po kliknięciu fire na takim znaku chcę się przenieść do innego świata. Ja to robię tak, że konkretny adres w pamięci ekranu ma przypisany konkretny numer świata, do którego przenosi. Po kliknięciu fire program sprawdza, czy fire kliknięto stojąc na znaku o określonym kodzie. Jeżeli tak, to znaczy że jest przejście, ale żeby było wiadomo dokąd, to trzeba właśnie sprawdzić adres i wtedy dla tego adresu znaleźć który świat ma się pokazać.

    Na podobnej zasadzie dialogi w grze. Klikamy fire i program sprawdza na jakiej postaci stoimy, żeby wiedzieć z kim rozmawiać i wywołać określony dialog. Postać rozpoznawana jest też po adresie, a konkretniej po podpisie tekstu z nazwą postaci, który to tekst ma swój numerek.

    I znowu podobnie: wywołanie dialogu w grze wyłącza aktualny świat i pokazuje ekran rozmowy: po rozmowie z konkretną osobą trzeba wywołać konkretny świat z powrotem i ustawić go w odpowiednie miejsce.

    Dalej, zbieranie wielu jednakowych przedmiotów, ale z zachowaniem informacji który jest skąd.

    I teraz te wszystkie przykłady da się ogarnąć kilkoma if-ami, ale wolał bym mieć to jakoś potablicowane, bo jak wprowadzam zmiany to muszę przerabiać wszystkie if-y, a łatwiej jest mieć gotowe funkcje działające w oparciu o tablice i tylko w tych tablicach dopisywać/zmieniać sobie liczby w jednym miejscu. Oczywiście chodzi też o miejsce, pamięć, cykle itd. Ale jak napiszę sobie jedną dobrą i szybką funkcję wyszukiwania binarnego jak poradził Adam, to będę mógł ją użyć w bardzo wielu miejscach i to spowoduje znaczne oszczędności.

    Dzięki za szybkie odpowiedzi:-)
    • 5:
       
      CommentAuthorpirx
    • CommentTime4 Jan 2021
     
    najszybsza jest oczywiście tablica z mapowaniem. jesli tablica jest bardzo dziurawa, to oczywiście szkoda miejsca. raz mi się udało tak pokombinować, że w jednej takiej tablicy miałem kilka różnych rzeczy - no tak się jakoś dało podopasowywać wartości, że w sumie z 256 bajtów >200 było użytych w trzech różnych prockach.

    oprócz przeszukiwania binarnego zadziałać też może hash, nawet miałem gdzieś w głowie taki pomysł na hash, który płynnie zmniejsza bitowość, np. jeśli masz <32 wartości 8-bit to by się to dało z...
    o, dobra, już mi się przypomniało - można to zhuffmanować i mieć wszystkie wartości po kolei.

    pomysł z innych maszyn i innych czasów - de fuckto potrzebujesz funkcji f(x) -> y, masz iksy i ygreki, możesz to ekstrapolować do wielomianu, gotowe :]
    • 6: CommentAuthorilmenit
    • CommentTime4 Jan 2021 zmieniony
     
    Ja zwykle robię kombinację tablicy i IFów.

    Załóżmy że masz jakiś labirynt i w nim ściany nie do przejścia, przedmioty do zebrania i postacie z którymi można porozmawiać. Aby nie trzymać innej reprezentacji mapy i bezpośrednio sprawdzać z pamięci ekranu wartość konkretnego znaku:
    1. Zadbaj o to, aby Twój zestaw fontów miał dane pogrupowane w odpowiedniej kolejności np.:
    puste miejsce x1, ściany x5, przedmioty x20, postacie x3.
    2. Zrób enum'a, któremu przypiszesz odpowiednie wartości. Składni Pascala dobrze nie znam, ale patrząć na dokumentację to chyba tak:
    type map_type = (
    empty,

    wall_brick,
    wall_tree,
    wall_rock,
    wall_water,
    wall_fire,

    item_first,
    item_abcd,
    item_efgh,
    item_last,
    ...
    person_first,
    ...
    person_last,
    );


    teraz (w pseudokodzie):

    char = GetMapChar(x,y)

    if (char>=item_first and char<=item_last)
    process_item(char - item_first); // 0 for the first one

    if (char>=person_first and char<=person_last)
    process_person(char - person_first); // 0 for the first person


    Dzięki temu można mieć niezależną tablicę (lub tablicę struktur lub strukturę tablic) np.

    person[3] lub person_name[3]

    i potem :

    process_person(byte index)
    begin
    print(person_name[index]);
    end
    • 7: CommentAuthortebe
    • CommentTime4 Jan 2021
     
    dla takiej sytuacji
    if a=5 then b:=8;
    if a=12 then b:=3;
    if a=255 then b:=78;
    if a=100 then b:=152;


    CASE będzie wydajniejsze
    case a of
    5: b:=8;
    12: b:=3;
    255: b:=78;
    100: b:=152;
    end;
    • 8:
       
      CommentAuthorMq
    • CommentTime4 Jan 2021 zmieniony
     
    @pirx: zbyt abstrakcyjna dla mnie Twoja odpowiedź, ale bardzo ładna:-)

    @ilmenit: tak to robię akurat, mam grupy znaków i korzystam z ich zakresów. Chodzi o to, że konkretny jeden znak jest np. przejściem do innego świata i jedyna zmienna jaką on posiada, to jest jego adres na ekranie. Nie trzymam nigdzie informacji stablicowanych o tym gdzie się co znajduje w światach, na wszystko mam procedury działające po kodach znaków, tak że światy mogę dowolnie rysować i nie muszę nigdzie zapisywać co się w nich gdzie znajduje, a program sam obsługuje wszystko to co narysuję na mapie. W ten sposób np. przeszkadzajki mogę przenosić w dowolne miejsca, a one się same wyliczają później, żeby się poruszać. Z tym że w razie spotkania się z takim znakiem bohatera, muszę określić z którym konkretnie się spotkałem, a tutaj tylko adres rozróżnia znaki między sobą. Te adresy właśnie tablicuję na końcu, jak już mam narysowany świat i do tego potrzebuję przyporządkowania.

    @tebe: faktycznie, case wydaje się najprostszy, zapomniałem zupełnie, że jest w pascalu. Również przetestuję.

    Dziękuję wszystkim za te nowe podpowiedzi.

    Edit: oj, z case'em mi się nie opłaca. Zrobiłem test i tak:

    if a=9 then b:=2;	
    if a=8 then b:=1;
    if a=185 then b:=4;
    if a=33 then b:=3;
    if a=212 then b:=6;
    if a=211 then b:=5;

    kod zajmuje 69 bajtów

    case a of
    9: b:=2;
    8: b:=1;
    185: b:=4;
    33: b:=3;
    212: b:=6;
    211: b:=5;
    end;

    kod zajmuje 91 bajtów

    Nie zaglądałem do assemblera co on tam wygenerował ten kompilator.
  1.  
    Jeśli rzeczywiście ciśniesz na rozmiar to się nie opłaca. Wydajnościowo jednak opłacać się może, ale to by trzeba sprawdzić.
    • 10: CommentAuthortebe
    • CommentTime4 Jan 2021
     
    obecna wersja dla IF - 72 bajty, CASE 75 - bajty
    • 11:
       
      CommentAuthorMq
    • CommentTime4 Jan 2021
     
    @tebe, a jaka jest obecna wersja? I kiedy przesiadać się na nowszą, bo wiem, że się pojawiają, ale oficjalnie wisi publikacja 1.6.4 i takiej używam. Tutaj w releases: ->link<-
    • 12:
       
      CommentAuthorbocianu
    • CommentTime4 Jan 2021
     
    polecam używać najnowszej wersji z master brancha :)
    Zazwyczaj działa lepiej od każdej poprzedniej, choć jak to w życiu bywa - zdarzają się wyjątki. Ale te są jedynie chwilowe.
    • 13:
       
      CommentAuthorMq
    • CommentTime4 Jan 2021
     
    A które pliki trzeba podmienić? Wszystkie? A Madsa też trzeba zmieniać podnosząc wersję mp, czy to nie ma nic do rzeczy, czy jak?
    • 14:
       
      CommentAuthorbocianu
    • CommentTime4 Jan 2021
     
    Pliki nadpisz wszystkie.
    Madsa tez najnowszego najlepiej mieć
    • 15:
       
      CommentAuthorMq
    • CommentTime4 Jan 2021
     
    Podniosłem na próbę wersję MP do najnowszej jak piszesz, ale nie jest to taka pojedyncza sprawa, ani też nie do końca dobra niestety dla mnie.
    Na około 20kB wynikowego pliku gry, zwiększył się on o 150 bajtów (o tyle więcej zajmuje skompilowany kod), ale to nie jest najistotniejsze - natomiast gorzej, że przestały mi się niektóre rzeczy w ramkach mieścić, a także zaczęły mi się wykrzaczać niektóre procedury, w których jest dużo obliczeń i inne wyniki zapewne zaczęły w nich wychodzić, bo się np. przy animacjach nie te znaki co trzeba pokazują...

    Wróciłem do 1.6.4. Gra jest już na tyle rozbudowana, że chyba będę ją kończył na sprawdzonej wersji kompilatora. Ja wiem, że tam były różne istotne poprawki, ale pisząc kod zaglądałem do assemblera i wiedziałem co tam się z tego generowało, bardzo dużo optymalizowałem w ten sposób, i teraz jak mi się to kompletnie inaczej kompiluje, to nie wyobrażam sobie tego wszystkiego od nowa analizować. Po prostu jest tego za dużo. Przejdę na nowszy MP przy następnej grze:-)
  2.  
    Najlepiej przejdź na asm :)

    Oczywiście nie mam nic przeciwko MP, jednak do projektów wymagających daleko idącej, ręcznej optymalizacji to asm sprawdzi się najlepiej.
    • 17:
       
      CommentAuthorMq
    • CommentTime4 Jan 2021
     
    Ja sporo rzeczy już i tak robię w asm, sporo też robię w mp, ale patrzę często co się generuje w asm z tego i często zmieniam tak, żeby się optymalizowało. Lubię mimo wszystko mp, bo mam bardziej przejrzysty cały projekt, łatwiej mi się w kodzie połapać. Takie programowanie hybrydowe:-) Ale przez to właśnie teraz jest taki efekt podniesienia wersji jaki uzyskałem, zobaczymy, może w kolejnym projekcie pójdę bardziej w stronę asm jeszcze.
    • 18: CommentAuthorilmenit
    • CommentTime5 Jan 2021
     
    Chodzi o to, że konkretny jeden znak jest np. przejściem do innego świata i jedyna zmienna jaką on posiada, to jest jego adres na ekranie. Nie trzymam nigdzie informacji stablicowanych o tym gdzie się co znajduje w światach, na wszystko mam procedury działające po kodach znaków, tak że światy mogę dowolnie rysować i nie muszę nigdzie zapisywać co się w nich gdzie znajduje, a program sam obsługuje wszystko to co narysuję na mapie. W ten sposób np. przeszkadzajki mogę przenosić w dowolne miejsca, a one się same wyliczają później, żeby się poruszać. Z tym że w razie spotkania się z takim znakiem bohatera, muszę określić z którym konkretnie się spotkałem, a tutaj tylko adres rozróżnia znaki między sobą. Te adresy właśnie tablicuję na końcu, jak już mam narysowany świat i do tego potrzebuję przyporządkowania.


    taka reprezentacja danych ma dla mnie sens gdy:
    - na jednym polu może być więcej niż jeden obiekt
    - świat ma małe zagęszczenie obiektów do interakcji (czyli głównie puste pola lub jest generowany proceduralnie)
    W innym przypadku myślę, że wygodniej i krócej w kodzie do obsługi by było trzymać mapę 2D z indeksami obiektów oraz odpowiadające im krótkie tablice jak w moim przykładzie powyżej.
    • 19: CommentAuthortebe
    • CommentTime5 Jan 2021
     
    jeśli ktoś zauważył że MP generuje inny/błędny kod niż dotychczas to ja poproszę taki fragment do analizy, często wymaga to dodania jakiegoś warunku granicznego, którego nie przewidziałem
    • 20:
       
      CommentAuthorMq
    • CommentTime5 Jan 2021
     
    @tebe: ja bardzo chętnie dam Ci znać jak będę w stanie każdą taką rzecz ogarnąć i zauważyć. Na razie mój ograniczony czas wykorzystuję możliwie do tworzenia kolejnych rzeczy, a nie analizowania czegoś co już wcześniej zrobiłem i zoptymalizowałem na tyle, na ile się dało.

    @ilmenit: zajrzyj do sąsiedniego mojego wątku o postępach z Dude Story. Zwróć uwagę na gifa, na którym latają motyle i osa. Motyle latają sobie w losowych kierunkach i nie mają ograniczeń, mogą latać po całym świecie gry, a jedynym warunkiem jaki mają są określone znaki, po których mogą się poruszać. Osa ma taki sam warunek, różnica polega na tym, że zamiast losowych kierunków osa porusza się w górę i w dół. Aha, jeszcze pająk też się tak samo porusza, tylko w lewo i prawo i inny pająk góra dół, ale z zatrzymaniami. Losowość kierunku ruchu niektórych obiektów oraz poruszanie się ich po tych samych przestrzeniach powoduje, że obiekty się spotykają, przeszkadzają sobie nawzajem, zakłócają wzajemnie trajektorie. Dlatego nie mają z góry założonej trasy poruszania się, wymusza to tylko grafika tła i przy każdym ruchu każdy obiekt sprawdza dokąd może się poruszyć lub nie i wtedy zmienia swój kierunek. Na planszy może być różna ilość różnych obiektów ruchomych - tablicowane jest jedynie położenie obiektów na początku w taki sposób, że po wejściu na daną planszę jest ona przeszukana pod kątem tych obiektów. Między planszami przechodzi się swobodnie, więc zawsze po przejściu do innej planszy od nowa szukane są wszystkie obiekty. Tablica na obiekty ma określoną wielkość, ale obiekty mogą być różne i w różnych ilościach. Przykładowo przy tablicy na 20 obiektów tyle maksymalnie może znaleźć się na mapie, a tablica adresów zajmuje 40 bajtów. Przy takim podejściu nie ma różnicy czy plansz jest 100 czy 1000, a tablica położenia wszystkich obiektów zawsze zajmuje tylko tych 40 bajtów. Gdybym miał określać z góry położenie wszystkich obiektów dla danej mapy, to zajmowały by te dane ilość obiektów razy ilość map.
    Jeśli chodzi o animowane elementy tła, to nie ma znaczenia który jest który, jeśli chodzi o przeszkadzajki, też nie ma to znaczenia, bo kontakt z którąkolwiek powoduje taki sam efekt straty energii czy tam życia. Natomiast istotne są elementy, które zbieramy i tutaj przedmioty jeśli są powtarzalne, to też nie ma znaczenia który jest który jak z przeszkadzajkami.
    Tak czy siak, rozpisałem się trochę - ale wszystkie te przykładowe powyższe rzeczy można zrobić sposobami o jakich napisałeś, ja to robię w gruncie rzeczy podobnie jak Ty w tym powyższym przykładzie, który podałeś.

    Moje pytanie dotyczyło sytuacji trochę odmiennej. W moim przypadku jest to np. punkt przejścia do innej mapy. Takich punktów jest bardzo dużo, a każdy z nich stanowi ten sam znak o określonym kodzie. I tu jest mi potrzebne owo przyporządkowanie które przejście prowadzi od którego innego przejścia (innej mapy). Mam stablicowane adresy wszystkich przejść i teraz jak postać stoi na znaku przejścia, to po wciśnięciu fire dostaję adres tego znaku jeśli stoi na znaku będącym przejściem. Z tego adresu muszę ifami sprawdzić np. czy adres jest 123 i jeżeli tak, to idziemy do świata nr 7. Oczywiście idealnie by było jak by adres był 7 i idziemy do świata 7, ale tak nie jest.

    Tak czy siak: ważniejsze jest tu ile zajmie miejsca taki kod i na razie zwykłe if-y zajmują mi najmniej. Po wciśnięciu fire muszę w trakcie gry wykonać tylko jeden if sprawdzający czy znak, na którym aktualnie jest postać, jest znakiem przejścia. Jeśli nie, to koniec warunku i gra nie czuje, że w ogóle cokolwiek robiła w kodzie. Jeśli tak, to dalsze spowolnienie nie ma dla gry znaczenia, bo gra i tak się zatrzymuje ładując inną planszę i robiąc owe przejście, więc mogę sobie tu pozwolić na to, żeby sprawdzić if-ami kolejne wszystkie adresy wszystkich przejść w grze. Najbardziej trafia do mnie propozycja Adama z wyszukiwaniem binarnym w tablicach, bo taka funkcja może mi skrócić zarówno kod jak i szybkość (w tym wypadku mniej istotne) - a jeśli funkcję dla dwóch tablic napiszę uniwersalnie, to będzie ją można wykorzystać jeszcze do innych zupełnie celów w tej samej grze, gdzie z kolei na kodzie zyska się 100% miejsca, bo funkcja i tak już będzie, a na szybkości sporo dzięki wyszukiwaniu binarnemu.

    Ogólnie wszystkie podpowiedzi bardzo fajne, będę sobie testował i wykorzystywał.
    Te opisy długawe robię po to, żeby było widać na jakie przykłady i jakie myślenie się natykam, może się ten sposób rozumowania przyda też innym, którzy czytając to w przyszłości wpadną na jakieś własne pomysły, lub rozwiążą własne problemy.
    Dzięki:-)
    • 21: CommentAuthorilmenit
    • CommentTime5 Jan 2021 zmieniony
     
    @Mq: ok, dzięki temu GIFowi rozumiem co chcesz osiągnąć - to standardowy problem "hit-boxów 2D", ale:
    1. może być wiele obiektów o takiej samej znakowej reprezentacji (e.g. kilka motylków)
    2. obiekty wykrywają kolizję z innymi obiektami wg. znaków, ale potrzebujemy "mapowanie wsteczne" aby wykryć z którym obiektem nastąpiła kolizja.
    3. jeden obiekt może się składać z kilku znaków (e.g. mrówki)

    Jeszcze pytanie odnośnie punktu 3 - czy to mapowanie adresów i wykrywanie kolizji robisz dla każdego znaku obiektu, czy tylko dla jednego znaku? (jeżeli dla jednego, to prościej i dla grywalności lepiej gdy hitbox jest mniejszy niż wielkość obiektu).

    Zaproponowane wyszukiwanie binarne wymaga posortowanej tablicy, więc jeżeli zmieniasz pozycję obiektów, to musiałbyś dokonywać po ruchu sortowania tablicy, więc cały zysk przeszukiwania binarnego "bierze w łeb", szczególnie gdy wiele obiektów na planszy zmienia pozycję. Jeżeli obiektów masz tylko 20, to liniowe wyszukiwanie odpowiedniego to do 40 porównań, co przy wykonaniu tylko przy kolizji z odpowiednim znakiem powinno być szybkie.
    Jeżeli byś rozbił adresy na 2 tablice dla młodszego i starszego bajtu zamiast porównywać zawsze liczby 16bitowe to jeszcze mniej.
    Zamiast trzymać adresy można też trzymać pozycje obiektów, czyli:
    byte obj_x[max_objects];
    byte obj_y[max_objects];
    i przy szukaniu obiektu najpierw porównywać pozycję x, a potem tylko gdy jest równa też pozycję y.

    Tak bym też trzymał np.
    byte obj_type[max_objects];
    i przy iteracji obiektów wołał odpowiedni handler obsługi obiektu lub nawet trzymał bezpośrednio adres handlera dla każdego obiektu.
    word obj_handler[max_objects];
    • 22: CommentAuthorAdam
    • CommentTime5 Jan 2021
     

    ilmenit:

    Zaproponowane wyszukiwanie binarne wymaga posortowanej tablicy, więc jeżeli zmieniasz pozycję obiektów, to musiałbyś dokonywać po ruchu sortowania tablicy, więc cały zysk przeszukiwania binarnego "bierze w łeb", szczególnie gdy wiele obiektów na planszy zmienia pozycję. Jeżeli obiektów masz tylko 20 (...)


    versus:

    Mq:

    Moje pytanie dotyczyło sytuacji trochę odmiennej. W moim przypadku jest to np. punkt przejścia do innej mapy. Takich punktów jest bardzo dużo, a każdy z nich stanowi ten sam znak o określonym kodzie. I tu jest mi potrzebne owo przyporządkowanie które przejście prowadzi od którego innego przejścia (innej mapy). Mam stablicowane adresy wszystkich przejść i teraz jak postać stoi na znaku przejścia, to po wciśnięciu fire dostaję adres tego znaku jeśli stoi na znaku będącym przejściem. Z tego adresu muszę ifami sprawdzić np. czy adres jest 123 i jeżeli tak, to idziemy do świata nr 7. (...)


    Ja zrozumiałem Mq tak, że to jest ten case, który go bardziej interesuje niż obsługa 20 przeszkadzajek, Mq jak rozumiem ma bardzo dużo takich punktów ("przejść"), o wiele więcej niż 20. Przypuszczam, że one się jednak nie poruszają, więc spokojnie można mieć je raz posortowane :)
    • 23:
       
      CommentAuthorMq
    • CommentTime6 Jan 2021 zmieniony
     
    Dokładnie jak pisze Adam.
    W zasadzie ogarnięte to ja mam wszystko już, ale poszukuję optymalizacji do tych rzeczy, które mi się w kodzie nie podobają, a ten problem z przypisywaniem różnych zmiennych za pomocą szeregu if-ów powrócił do mnie już kilkakrotnie w różnych sytuacjach. Czasem udało się jakąś iterację zastosować w pętli dla zysku na ilości kodu, czasem udało się coś stablicować i posortować w innej kolejności, czasem liczby które wydawały się nie być po kolei okazywało się, że można jakoś jednak wyliczyć żeby były po kolei. Po prostu są różne rozwiązania, ale chodziło mi o to, żeby dowiedzieć się od Was jak to można zrobić kiedy faktycznie mamy tablicę przypisań zupełnie przypadkowych liczb do siebie.

    Wracając do tego co mam jak ogarnięte, a też odpowiadając na pytania ilmenita. Przeszkadzajki mam w kilku rodzajach, które określiłem sobie wielkością w znakach np. 2x2, 2x3, 3x2. Dla każdego typu mam zdefiniowaną procedurę algorytmu poruszania się takiego obiektu. Obiekty tablicują się przy starcie mapy jako adresy pierwszego znaku obiektu, a poruszanie ich następuje poprzez przesunięcie znaków w określonym kierunku, przy czym jest sprawdzane czy przesunięcie może nastąpić po tym jaki znak jest w miejscu w które się chcemy przesunąć. Jak nie ma tam miejsca, to zmieniamy kierunek. Kierunki wszystkich przeszkadzajek są też stablicowane, żeby np. dwa pająki mogły iść w przeciwnych kierunkach, w innych momentach zawracać i ogólnie chodzić niezależnie. Dla płynności ruchu część przesunięcia odbywa się jako animacja w obrębie znaku, a część to dopiero przesunięcie znaków. Trochę wyzwaniem było tu, żeby tak zanimować znaki, żeby te same znaki w tych samych klatkach animacji mogły służyć dla pająka idącego i w prawo i w lewo, bo jak jeden idzie w jedną a drugi w drugą stronę, to trzeba było to wszystko jakoś posynchronizować. Nie dotyczy to os i motyli, one się przesuwają o cały znak zawsze, bo miało to wyglądać bardziej tak jak latają owady, czyli trochę skokowo, trochę tak nerwowo. Za to osy mają mechanizm odwracania się w lewo i w prawo, co zrealizowałem odwracając bajty tablicą wszystkich bajtów odwróconych "graficznie" dla trybu Antic 4 (po prostu narysowane symetrycznie bajty w tym trybie). Ogólnie to wszystko dość skomplikowane, bo trzeba pomieścić w ramkach animacje wewnątrz znakowe, przesunięcia, kolizje i wtedy zmiany kierunków, odwracanie tych bajtów. Dodatkowo np. zadbałem o to, żeby wszystkie animowane elementy miały różne liczniki ramek i animowały się niesynchronicznie, co inną ilość ramek, z różnymi prędkościami. Dotyczy to też postaci, elementów tła takich jak woda, czy oczy chmurek i kilku innych rzeczy. Pilnowałem też, żeby animacje nie synchronizowały się z rytmem muzyki. Ogólnie żeby to wszystko wyglądało naturalnie.

    Znowu się rozpisałem. Wracając bardziej do tematu: przejścia między mapami nie poruszają się, są statyczne. Wprowadziłem sobie zasadę, że na mapie nie ma nigdy dwóch przejść jedno nad drugim, więc muszę mieć zapisane tylko przesunięcie w poziomie, a więc część młodszą adresu. Dodatkowo rysuję mapy tak, żeby nigdy się przejścia nie powtórzyło na różnych mapach pod tym samym adresem, więc liczby są unikalne. Mogą też być posortowane. No i tu właśnie zadałem pytanie na forum w tym wątku, bo przy pierwszych trzech przejściach zrobiłem po prostu if a=10 then b=0, if a=50 then b=1, if a=72 then b=2. Ale jestem na etapie rysowania kolejnych map i kolejnych przejść i pomimo że działa to wszystko dobrze, sprawnie i poprawnie, to nie podoba mi się, że w jednym miejscu kodu mam już taką listę if-ów jeden pod drugim, a każdy kolejny zwiększa mi rozmiary kodu coraz bardziej. Takich przejść nie wiem ile będzie, ale na pewno co najmniej z 50. Myślę też przyszłościowo o tym, że jak bym zrobił uniwersalną funkcję na odczyt takiego przypisania z jednej tablicy na podstawie drugiej, to mając już gotową funkcję można ją wykorzystać w innych miejscach, nawet jeśli te inne miejsca będą zawierały tylko kilka przypisań czegoś do czegoś. Takie serie if-ów zdarzają się często w różnych przypadkach, a mając funkcję i tak gotową bardzo można zmniejszyć potem kod stosując zamiast np. 5 if-ów dwie tablice po 5 bajtów i tylko wywołać funkcję.

    Edit: aha, jeszcze ilmenit pytał o kolizje. Kolizje robię tak jak mówisz, tzn. mniej-więcej tak jak mówisz. Mam zawsze na bieżąco uzupełnioną tablicę znaków na których znajduje się postać bohatera. Ta tablica jest zgrubna, tzn. jak bohater skacze i jest szerszy, to tam wystająca na bok ręka nie jest "zaewidencjonowana" do kolidowania. Podobnie z przeszkadzajkami, koliduje tylko część znaków. Dobrałem to doświadczalnie tak, że jest po prostu dobrze, ani zbyt dokładnie, żeby nie było, że tam jednym pikselem się coś dotknie i już ginie, ani też nie jest na tyle niedokładnie, żeby nie ginąć w pustej przestrzeni obok przeszkadzajki, lub nie móc wleźć całą postacią na przeszkadzajkę i nie zginąć. Wszystko jest mniej-więcej, ale właśnie tak, żeby było grywalnie. Pod tym kątem są też dobrane rozmiary przeszkadzajek, ich prędkości w stosunku do gracza, prędkość poruszania gracza, wysokość skoku itd. No wiecie, tak żeby było dobrze:-)

    Edit: ostatni edit, bo trochę odbiegliśmy od tematu, ja teraz żyję tą grą, więc lubię o niej opowiadać, ale lepiej się będzie gadać jak zobaczycie i pogracie, więc jeszcze chwila cierpliwości, myślę, że w ciągu miesiąca uda mi się w końcu opublikować.
    • 24: CommentAuthorilmenit
    • CommentTime6 Jan 2021
     
    przy tym jak masz to rozplanowane to rzeczywiście najszybsze będzie wyszukiwanie binarne. Polecam wariant iteracyjny np. z
    ->link<-
    Wyszukaj "Iterative implementation of Binary Search" i masz przykład dla kilku języków. Bardzo krótki i czytelny kod.
    • 25:
       
      CommentAuthorMq
    • CommentTime7 Jan 2021
     
    Dzięki ilmenit. Zajrzę, sprawdzę, wykorzystam:-)
    • 26:
       
      CommentAuthorjhusak
    • CommentTime7 Jan 2021 zmieniony
     
    A ja bym to zrobił na prostej tablicy asocjacyjnej. Parę bajtów kodu, szybkość spoko, a to 255 elementów proste jak konstrukcja cepa.

    find: ldx #tablen
    nxt: cmp tabin,x
    beq found
    dex
    bne nxt
    found: lda tabout,x
    rts


    W tym przypadku jeśli nie znajdzie, zwraca element zerowy
    Oczywiście, w pascalu będzie to kod rzędu 200 bajtów + tablice, dlatego tę wstawkę bym w asmie wkleił.

    Przy <128 elementach można zrobić przeplatanie, aby uniknąć błędów w dopasowaniu wyjścia do wejścia.
    • 27:
       
      CommentAuthorMq
    • CommentTime7 Jan 2021
     
    Kuba jak zwykle nieoceniony:-) Kurde, ja się chyba jednak w końcu przesiądę całkowicie na asm:-) Mam już kilka takich wstawek własnie, które zajmują kilka linijek i zasuwają z prędkością światła. Szczerze mówiąc myślałem już też o tym czy by tak tego mniej-więcej nie zrobić, ale pisałem w pascalu uparcie. Dopiero jak teraz widzę ten kod na ekranie, to do mnie trafia.

    Kuba, a na początku jeszcze:
    lda szukana_wartosc_w_tabin
    tak?
    • 28: CommentAuthortebe
    • CommentTime7 Jan 2021
     
    "Oczywiście, w pascalu będzie to kod rzędu 200 bajtów + tablice, dlatego tę wstawkę bym w asmie wkleił."

    skąd taki wniosek? z wyobraźni? ;)
    • 29:
       
      CommentAuthorjhusak
    • CommentTime7 Jan 2021 zmieniony
     
    @tebe z wyobraźni :) strzelałem :) Może 100? Pewnie powinienem dodać wyraz "prawdopodobnie" :)
    Dawaj, sprawdzimy, o ile pamiętam pascala :)

    function find (byte a): byte
    var i: byte;
    begin
    i=MAXLENGTH-1; { dlugosc tablicy asocjacyjnej}
    while (i) do
    begin
    if (tabin[i]=a) then break;
    dec(i);
    end;
    result=tabout[i];
    return;

    Teraz widać, że będzie to tak koło 50 bajtów.

    @Mq
    Oczywiście, wywołanie lda dana, jsr find, w akumulatorze jest mapowana wartość.

    Nie ma większego sensu przesiadać się całkowicie na asm. Najlepsza jest hybryda - logika w języku wysokiego poziomu, a niektóre procedurki w asm.

    W sumie w madsie można taki "wyższy poziom" osiągnąć, ze wskazaniem, że w cudzysłowie :)
    • 30:
       
      CommentAuthorMq
    • CommentTime8 Jan 2021
     
    Kuba, ja dlatego wszedłem w Mad Pascala w ogóle. Z góry zakładałem, że nie chce mi się robić wszystkich pętli for, while itp. w assemblerze, bo mam potem problem z poszukaniem czegokolwiek w takim dłuuuugim kodzie, a jak zajrzę za pół roku, to już w ogóle nie pamiętam co autor (czyli ja) miał na myśli:-)

    U mnie ścieżka jest taka, że za dzieciaka był Basic na Atari, potem próby assemblera na Atari, ale dla dzieciaka był za trudny. W szkole średniej assembler na pecetach pod dosem, robiłem jakieś tam proste gierki i masę różnych efektów, jakieś ognie, plazmy, fraktale itp. A potem już całe dorosłe życie, to zawodowo chyba wszystkie współczesne języki jakie by nie były pod windę, weba, bazy danych, na mikrokontrolerach, jest mi wszystko jedno w czym programuję, patrzę na cel, a język to narzędzie, które dobieram w zależności od potrzeb. Tak że jak postanowiłem wrócić teraz do Atari i zrealizować marzenie sprzed lat, żeby zrobić "prawdziwą" grę na Atari, to wybrałem Mad Pascala jako mocno wspieranego i z góry zakładałem, że będę wstawiał co trzeba w assemblerze. Z pecetowego assemblera coś tam pamiętam po łebkach, więc algorytmiczny sposób myślenia i odruchowe stosowanie operacji logicznych zostało mi jeszcze. Musiałem tylko szybko chwycić sposoby adresowania, sztuczki-kruczki, stronę zerową i jadę z koksem:-) Mistrzem świata nie jestem i używam pewnie z 10% rozkazów, ale po co komu więcej, jak i tak w większości jedzie się na lda i sta:-)

    Całkowite sięprzesiąście na asm, to miałem na myśli żeby szkielet tylko w Pascalu został. Już się przyzwyczaiłem, mam środowisko wygodne poustawiane wg porad Bocianu, to nie zmieniał bym całości.
    • 31:
       
      CommentAuthorKaz
    • CommentTime9 Jan 2021
     
    jhusak - Kuba, polecam znaczniki [ code ] i [ /code ], żeby wszelkie listingi były czytelniejsze. Wstawiłem w Twoich dwóch postach.