4programmers.net
2018-07-03 20:02Sponsorowane

Pamiętacie Artura? Młody przedsiębiorca - freelancer IT, który wyszedł z problemów finansowych dzięki inviPay.com. W kwietniu otrzymał propozycję pracy nad projektem mechanizmu podzielonej płatności w jednym z banków działających w Polsce. Artur zgodził się na kontrakt, w którym miał zapewnione wysokie wynagrodzenie, ale dopiero 60 dni po ukończeniu i przetestowaniu prac. Artur stwierdził "Maćkowi i Konradowi zapłacę własnymi środkami, a po ukończeniu prac otrzymam swoje pieniądze". Wszyscy byli zachwyceni możliwością pracy nad wdrożeniem innowacyjnego rozwiązania jakim jest split payment.

Niestety to, co zawodowo okazało się interesującym zadaniem, biznesowo przyniosło masę problemów. Artur po oddaniu prac, wystawieniu faktury i odczekaniu 60 dni był zaskoczony, że przelew do niego nie dotarł. Zdziwiony zadzwonił do banku i załamany usłyszał: "Panie Arturze, od 1 lipca płatności wykonujemy split paymentem, a Pan posiada tylko rachunek osobisty". Artur w związku z intensywnością prac całkowicie zapomniał o zmianie swojego rachunku osobistego na firmowy. Co prawda, będąc przedsiębiorcą prowadzącym jednoosobową działalność gospodarczą, nie miał obowiązku posiadania rachunku firmowego, ale niestety to płatnik decydował o formie rozliczenia. Bank chcąc działać według nowych regulacji zrealizował płatność nowym, podzielonym mechanizmem i przelew został odrzucony.

Załamany Artur ponownie stracił płynność finansową. Na domiar złego przekroczył termin zapłaty kwartalnych podatków i fiskus zaczął się o nie upominać.

Tym razem Artur wiedział jak szybko zareagować. Udał się do banku, założył rachunek firmowy i w porozumieniu z bankiem wystawił nową fakturę. Niestety z nowym terminem płatności 60 dni. Dzień po wystawieniu Artur zadzwonił do Pani Magdaleny z inviPay.com, żeby pomogła mu odzyskać kontrolę nad swoimi finansami. Pani Magdalena wyjaśniła ze szczegółami wszystkie elementy mechanizmu podzielonej płatności. Artur następnego dnia otrzymał środki na konto za pomocą split payment, dzięki czemu mógł od razu przelać zaległe podatki do urzędu skarbowego i odzyskał płynność finansową.

A czy Ty wiesz co to split payment? Zadzwoń pod 22 295 15 05 - odpowiemy na wszystkie Twoje pytania.

Modliszka

@endrius: to może odezwij się do nich żeby Cię usunęli z bazy i po kłopocie? Przy obecnym prawie większość firm jest o wiele bardziej restrykcyjna i ostrożna

endrius

@Modliszka: kliknąłem magiczny odnośnik "Nie subskrybuj", żeby wypisać się z czegoś do czego się nigdy nie zapisywałem :). To handlowcy takim spamowaniem i nękaniem limity chcą wyrobić

MichalRemote
2018-08-14 16:42

Jeśli chcesz otrzymywać cotygodniową porcję ofert pracy zdalnej to zapraszam tutaj ;)

Frontend/Javascript
Front-end Developer (Resin.io) - oferta
React Native Developer (Adeo) PLN 6k - 12k - oferta
Frontend developer (Telemedi.co) PLN 4,5k - 9k - oferta
Front End Javascript Engineer (Clevertech) - oferta
Frontend Tech Lead (Netguru) PLN 14,5k - 18k - oferta
Javascript/Typescript Software Developer (Status) - oferta
Front End Engineer - Ember.js (Customer.io) USD 7k - 10k - oferta

Backend
Golang Engineer (Resin.io) - oferta
Backend Engineer (Resin.io) - oferta
Python Developer (Accelerit) PLN 8,5k - 15k - oferta
.NET Developer (TechScoped) PLN 7k - 10k - oferta
.NET Developer (VentureDevs) PLN 8k - 16k - oferta
Java Full-Stack Developer ( UWS Software Service) EUR 3k - 4,5k - oferta
Software Engineer (Clojure) (Status) - oferta

Data
Bioinformatics Software Engineer (7N) PLN 12k - 14,4k - oferta

Mobile
Android developer (Telemedi.co) PLN 7k - 10k - oferta
Mobile React Developer (ALTEN Polska) - oferta
Senior Android Developer (TallyGo) - oferta

Devops
Senior DevOps Engineer (Xapo) - oferta
DevOps (Espeo) PLN 12k - 16k - oferta
DevOps Engineer (Tooploox) PLN 9k - 12k - oferta

Testing
Testery Manualny (7N) PLN 9,6k - 11,2k - oferta
Test Lead (7N) PLN 11,8k - 14k - oferta
QA Engineer (IP Partner) PLN 8,4k - 10k - oferta

Design
UX Designer (SMARTASSISTANT) PLN 8k - 12k - oferta
UI Designer (Mental) PLN 4k - 6k - oferta

Business/Product
Head of Marketing (Semaphore) - oferta

#pracait #it #startup #pracazdalna #praca #zdalna #zdalnie #zdalnieio

kmph
2018-08-13 20:04

Walki z projektem ciąg dalszy. Chyba nie zdążę przed końcem wakacji zrobić wszystkiego, co planuję :(

Mam dość poważne problemy z duplikacją kodu. Niestety, choć staram się to ograniczać (o ile tylko owo ograniczanie nie zatrzyma mnie zbyt mocno), copypasty jest tam nadal zdecydowanie za dużo. Nie wszędzie mam rozsądne pomysły, jak się tego pozbyć.

Weźmy na przykład public class Block : IAction. Mój oryginalny pomysł był taki, że utrzymywanie bloka (a) kosztuje niewielką ilość Staminy na turę, jeśli Staminy nie ma to blok jest zdejmowany; (b) natychmiast, gdy przeciwnik ukończy swój ruch, blok jest zdejmowany. Interfejs IAction wymaga metody execute - świetnie zatem, niech Block implementuje execute w ten sposób, że zabiera nieco Staminy, sprawdza, czy przeciwnik jest wolny i jeśli tak, zdejmuje się.

Działa. Ale! Oprócz Block mam jeszcze i Dodge, które działa bardzo podobnie, tyle że zamiast blokowania ataku przeciwnika (pewne zmniejszenie obrażeń o % wynikły ze stosunku Power walczących) usiłuje wykonać unik (przyjęcie albo 0% albo 100% obrażeń w zależności od RNG, szansa zależy od stosunku Speed obu walczących). No i obie klasy duplikują kod odpowiedzialny za zdejmowanie się wskutek braku Staminy albo wskutek faktu, że przeciwnik zakończył już swój ruch.

Co gorsza: Za chwilę okazuje się, że jednak Block oraz sposób jego nakładania i zdejmowania to są dwie różne rzeczy. No bo teraz np. chcę zrobić taki ruch jak Ram. Działa on tak: user podbiega do przeciwnika, blokując podczas podbiegania, następnie atakuje i zdejmuje bloka. No i mam problem. Jak to zrobić bez duplikacji kodu? Duplikowanie całej klasy Block oprócz kodu odpowiedzialnego za zdejmowanie to już przesada, nawet dla mnie.

Chcę wielodziedziczenia! Wtedy, myślę, dałbym sobie klasę Block, klasę Dodge, klasę RequireStaminaPerTurn, klasę UntilEnemyFinishesMove, klasę UntilEndOfChannel. I teraz niech NormalBlock dziedziczy po Block, RequreStaminaPerTurn, UntilEnemyFinishesMove. I niech NormalDodge dziedziczy po Dodge, RequireStaminaPerTurn, UntilEnemyFinishesMove. I wreszcie niech ChannelBlock dziedziczy po Block, RequireStaminaPerTurn, UntilEndOfChanel. Wszystko. Teraz nawet można (teoretycznie, nie zrobiłem tego) dać postać, która będzie miała taki passive, że będzie mieć bez przerwy założonego bloka bez żadnych kosztów Staminy.

Niestety! Wielodziedziczenie jest be i nie ma go w C#. Coś jednak muszę wymyślić! Z jednej strony nie chcę duplikować kodu ponad miarę, a z drugiej strony, nauczony tym doświadczeniem, chcę wprowadzić i utrzymać zasadę, że żaden efekt mający wpływ na grę nie odpowiada za swoje własne zdejmowanie i nakładanie.

W końcu wymyśliłem. Block nie implementuje już IAction. Zamiast tego pojawiły się takie klasy, jak: public class PlaceEffect: IAction, public class RemoveEffect : IAction, taki interfejs: public interface RequireSetup : IEffect { List<IAction> onPlacement { get; } List<IAction> onRemoval { get; } List<IAction> onFailure { get; } } itepe, itede.

Biorąc pod uwagę fakt, że dodatkowo jeszcze, celem umożliwienia, by tury wykonywały się jednocześnie (w sensie, obaj gracze dają polecenia i wtedy wykonuje się tura, nie ma znaczenia który gracz wykonuje swą turę jako pierwszy) wprowadziłem wymóg, by każda IAction implementowała property o nazwie priority - no zrobiło mi się spaghetti. Te wszystkie priority, getPriority, PlaceEffect, onBlahblah => { new List<IAction> { new PlaceEffect(_priority: yaddayadda, _effect: new JakiśtamEfekt(_onFailure : new List<IAction> {new RemoveEffect(plepleple.... nie chce mi się kończyć otwartych nawiasów i tym podobne... Wydaje mi się że brzydkie, obawiam się że błędogenne, i co gorsza podobne ruchy powtarzają ten niemiły kod czyli i tak mamy copypasty.

No ale idziemy dalej. Teraz implementuję sobie damage over time. Na pierwszy ogień idzie Hurricane i Conflagration. Te efekty odnoszą się do całego Field, toteż atakują obu graczy. No i tu już mamy problem, gdyż to ma działać jako efekt "stackowalny", tj: wykonanie ruchu Hurricane nakłada stack efektu Hurricane który to stack zostanie zdjęty po czasie zależnym od statystyk gracza nakładającego ten stack. Tak samo Conflagration. Co więcej, Hurricane i Conflagration razem tworzą kombo FieryTornado. I wreszcie, Hurricane, oprócz damage over time, nakłada jeszcze na obu graczy taki debuff, że każdy ich ruch kosztuje nieco więcej Staminy.

Zaimplementowane. Mam klasę Weather : IExecutable<Field>, IOnTire. Implementowanie IExecutable<Field> jest po to, by mogła nakładać obrażenia każdej tury. Implementowanie IOnTire jest po to, by mogła zwiększać zużycie Staminy. W niej klasy: PlaceWeatherStack : PlaceEffect, RemoveWeatherStack : RemoveEffect (ich zadanie: nakładanie / zdejmowanie instancji klasy Weather do zbioru efektów na Field kiedy tylko zostanie nałożony jakiś stack Hurricane lub Conflagration / kiedy ostatni stack zostanie zdjęty); no i oczywiście klasy HurricaneStack, ConflagrationStack. Klasa Weather, wywołana, sumuje sobie wszystkie stacki i na tej podstawie zadaje obrażenia / zwiększa użycie Staminy. Powinno działać. (Jeszcze nie przetestowałem).

Niestety. Klasa Weather, ze wszystkimi klasami zagnieżdżonymi, to jest sporo kodu. Więcej, niżbym przewidywał. 200 linii z hakiem.

A teraz nadchodzą kolejne dwa efekty: ToxicSpores i BurningSpores. Także zadają damage over time, ale już tylko na przeciwników. Nie mają także dodatkowych efektów takich jak IOnTire. Mimo to... duplikacja kodu pomiędzy tymi klasami a Weather jest straszna. Nieakceptowalna nawet dla mnie.

Ale przecież nie chcę! zrobić klasy bazowej, choćby abstrakcyjnej, która by zawierała w sobie zduplikowany kod! Przecież nie ma wielodziedziczenia, a pojedyncza klasa bazowa to przepis na powrót problemów, na jakie natknąłem się przy Block. W końcu (chyba) udało mi się wydzielić część zarządzania stackami:

public class PlaceStackedEffect : PlaceEffect
    {
        public interface IStackedEffect : IEffect
        {
            List<IStackManager> managers { get; }
 
            void prepare(Species affected);
 
            IStackedEffect accumulate(IStackedEffect eff, IStackedEffect acc);
        }
 
        public interface IStackManager : IEffect
        {
            void update(IEffectStorage storage);
        }
 
        public IStackedEffect eff;
 
        public PlaceStackedEffect(IStackedEffect _eff, List<IEffect> _others, List<int> _priority = null, IEffectStorage _target = null) : base(_others.Prepend(_eff), _priority, _target)
        {
            eff = _eff;
        }
 
        public override void execute(Species affected)
        {
            foreach (IStackManager required in eff.managers)
                if (!target.ownEffects.OfType<IStackManager>().Any(eff => eff.GetType().IsAssignableFrom(required.GetType())))
                    target.ownEffects.Add(required);
 
            eff.prepare(affected);
            base.execute(affected);
 
            foreach (IStackManager manager in target.ownEffects.OfType<IStackManager>().Where(sm => eff.managers.Any(required => sm.GetType().IsAssignableFrom(required.GetType()))))
                manager.update(affected);
        }
 
        public class RemoveStackedEffect : RemoveEffect
        {
            IStackedEffect eff;
 
            public RemoveStackedEffect(IStackedEffect _eff, List<IAction> _onRemove = null, List<int> _priority = null, IEffectStorage _target = null) : base(_eff, _onRemove, _priority, _target)
            {
                eff = _eff;
            }
 
            public override void execute(IEffectStorage affected)
            {
                base.execute(affected);
 
                foreach (IStackManager required in eff.managers)
                    foreach (IStackManager manager in target.ownEffects.Where(eff => eff.GetType().IsAssignableFrom(required.GetType())))
                        manager.update(affected);
            }
        }
    }

Wciąż nie usuwa to znacznej części duplikacji... np. sumowanie damage jest analogiczne we wszystkich 3 przypadkach, ale tego nie chcę wydzielać do IStackedEffect, bo łatwo wyobrazić sobie taki efekt, który by się zupełnie inaczej agregował. Tak samo analogiczny jest kod odpowiadający za dispelowanie stacków, ale znowu: boję się powrotu problemów spod znaku Block (tj. duplikacja kodu wymuszona brakiem wielodziedziczenia). No bo co, jeśli jakiś inny efekt stackowalny będzie inaczej dispelował stacki i co więcej, tak jak w przypadku Block / Dodge, jego inne dispelowanie też winno być dziedziczone po iluśtam innych efektach?

Przyznaję, że ten projekcik jest dla mnie solidną lekcją pokory.

kmph

@Afish: Dzięki za porady. Spróbuję przerobić projekt zgodnie z Twoimi sugestiami. Jednak zajmę się tym raczej później: postawiłem sobie (nie wiem, czy słusznie), że wielkich zmian architektonicznych nie będę robił, zanim nie będę miał działającej (choćby zabugowanej, ale działającej) wersji alfa. A to dlatego, że chciałbym mieć tę alfę jak najszybciej, najchętniej przed końcem wakacji (choć to nierealne raczej :( ), a poza tym boję się paralysis by analysis: ciągłego przepisywania i poprawiania kodu tak, że nic nie zrobię. Z ruchów do zaprototypowania pozostały mi jeszcze 2, potem zajmę się innymi partiami projektu, i wtedy rzeczywiście mam w planach ogarnięcie całości i przepisanie jej. Twe sugestie z pewnością okażą się tu bardzo pomocne. Chyba, że radziłbyś mi raczej przepisać to natychmiast...?

Afish

Jak się zastanawiasz, czy przepisywać teraz, to nie przepisuj, dzięki temu kiedyś dojdziesz do momentu, w którym będziesz wiedział, kiedy to potencjalnie należało przepisać i czy ten moment przegapiłeś, a to też jest cenna wiedza.

lion137
2018-08-12 22:17
LukeJL

"(...), cancer cells, heart cells and neurons. When exposed to blue light, these cell types died"
Czyli nie takie głupie to niebieskie światło.

lion137

@LukeJL: Czyli tak jakby radioterapia.

Shizzer
2018-08-12 15:50

Dziś skończyłem pisać ostatni post na moim blogu na temat interpretera Brainfucka pisanego w C. Program ten skończyłem pisać już trochę czasu temu i już nawet zająłem się nowym projektem, ale z racji, że ciężko mi się oderwać od kodu to po prostu odkładałem pisanie tych artykułów. :) Od czasu mojego ostatniego wpisu tutaj napisałem jeszcze bodajże trzy posty. Jeśli jesteście zainteresowani jak poradziłem sobie z napisaniem tego interpretera od początku do końca to zapraszam - https://shizz3r.blogspot.com/

Dodam, że teraz tworzę usługę serwera FTP + klienta w oparciu o programowanie asynchroniczne w Pythonie. Postaram się opisać również i ten projekt także jeśli jesteście ciekawi to śledźcie bloga. :)

Oczywiście zaznaczam, że nie jestem ekspertem dlatego wszelka konstruktywna krytyka jest mile widziana. :)

msm

Tak prywatnie, to straszny overengineering w tym projekcie ;). Cały interpreter brainfucka można lajtowo zamknąc w 50-100 linijkach (zaleznie od zwięzłości stylu) a Ty już do 300 dochodzisz. Schludność kodu OK, ale jakość często biedna (np. te definy na górze - O_o).

Mikroblog to nie miejsce na takie wywody, ale jak napiszesz do działu Własne Projekty (albo na PM w ostateczności) to mogę rozwinąć co IMO jest słabo napisane.

kodokleta

@msm: czysty kod... tak odruchowo bym to tłumaczył... jest tam?... a nie... pliki powinny mieć mniej niż 100 linijek

furious programming
2018-08-12 01:43

Projekt platformera nie umarł! Czas na kolejne ciekawostki – tym razem będzie śmiesznie… ;)


Przedwczoraj zająłem się spinaniem silnika gry z klasą zawierającą dane na temat fabuły. Jak już wspominałem, fabuła przewiduje kilka światów (plus ekrany tytułowe), każdy składający się z kilku poziomów. Dodatkowo, pomiędzy poziomami i podczas rozgrywki, przewidziałem możliwość wyświetlania cutscenek. Cutscenki to proste animacje, mogą wyświetlać tło w kolorze lub jako szum telewizyjny oraz posiadać dowolną liczbę elementów (etykiet i obrazków). Mogą pojawiać się z czerni, być widoczne w pełnym świetle oraz zanikać (do dyspozycji są łącznie trzy fazy, różniące się oświetleniem).


Animowanie poziomu

Kończyłem pisanie ostatniego mechanizmu obsługi scen, czyli właściwej sceny rozgrywki. A tu zonk – pojawiła się konieczność animowania poziomu, a konkretniej dwóch faz – wyłaniania się z czerni i zanikania – na potrzeby startu danego poziomu i jego zakończenia. Problem drugi – aby kod tej sceny był zgodny stylem z pozostałymi, powinien odwoływać się do danego obiektu sceny i jedynie wołać wysoce abstrakcyjne, dużo robiące metody. Tyle że animowanie czegokolwiek wymaga liczenia klatek, a takich obliczeń w obiektach scen nie wykonuję. Trzeba było wybrać – albo niepasujące do innych scen babranie się z licznikami, albo skorzystanie z gotowych klas do animacji, wyposażonych w licznik i przydatne metody. Wybrałem więc opcję drugą, ale problem w tym, że te klasy nie obsługują poziomu jako tła…


Cutscenki i ich wyjątkowość

Zostawiłem więc sceny, a skupiłem się na rozwinięciu kodu animacji. Cutscenki dotyczące jednego poziomu (rozpoczęcia, zakończenia i wewnętrzne) pierwotnie osadzane były w plikach z poziomami. Podczas ładowania poziomu do pamięci, wczytywane były wszystkie potrzebne dane – grafiki warstw, macierze hitboxów, dane dotyczące bramek itd. oraz właśnie te cutscenki. Skoro cutscenki dało się wczytywać z pliku, a inne animacje nie (intro, outro, ekran staffu itd.), to trzeba było coś z tym zrobić.

Klasa renderera animacji korzysta z renderera tekstu do malowania napisów, tyle że współrzędne etykiet np. dla intra miałem wrzucone do stałych, a całe intro było generowane z poziomu kodu, a nie ładowane z pliku. Problem numer trzy – nie było możliwości (na potrzeby cutscenek) ładowania z pliku czegokolwiek innego niż tekst i długość trwania animacji. Brak wsparcia obrazków, brak obsługi szumu jako tła, brak współrzędnych. Trzeba było rozwinąć klasy obsługujące animacje.


Animacje a pliki

Zacząłem więc dłubać przy animacjach, tak aby można było je ładować z plików. Wszystkie – intro, outro, staff i trzy typy cutscenek dla poziomów. Problem numer cztery – nie mam takiego generatora. Zostawiłem więc klasy animacji i skupiłem się na generatorze.


Nowy generator

Wymyśliłem strukturę zawartości konfigów, tak aby przewidywały wszystkie wymagane dane i stworzyłem nowy projekt dla nowego generatora. Współrzędne elementów animacji pierwotnie miałem wrzucone do stałych. Jeśli element miał być widoczny w lewym górnym rogu, to współrzędne były dwiema gołymi liczbami. Jednak jeśli coś miało być wyświetlone np. w prawym dolnym rogu, to wartości koordynatów obliczane były za pomocą innych stałych (tu: z rozmiarem ekranu).

Aby animacje z plików binarnych miały sens, powinny uwzględniać rozmiar ekranu, czyli oprócz offsetów, powinny również posiadać dane dotyczące wyrównania tekstu. Skoro animacja ma być umieszczona w binarce, to nie będzie dostępu do stałych z rozmiarem ekranu. Potrzebna jest więc możliwość wyrównywania tekstu do któregoś rogu lub centrowania go na ekranie. Problem numer pięć – renderer tekstu nie obsługuje wyrównania…


Brakujące wyrównywanie tekstu

Porzuciłem więc generator i wróciłem do głównego projektu. Trzeba było dorobić do metod renderera tekstu możliwość podania wyrównania. Póki co wspierał możliwość malowania napisów jednym fontem lub dwoma, łamania linii, pozwalał też malować tekst według konkretnych współrzędnych lub centrowania wewnątrz zadanego obszaru. Nijak się to miało do nowych wymagań. Przepisałem więc kod tak, aby metody obsługiwały dodatkowy parametr (align). Problem numer… sześć – renderer nie potrafi namalować poprawnie tekstu dwoma fontami, jeśli mają różne rozmiary. Nie było to potrzebne – wszystkie fonty były takie same, różniły się jedynie kolorem. Ale skoro mam rozwinąć kod, to przyda się też dorobić kolejną funkcję.


Bottom-up dobiegł końca

Totalnie przeorałem klasę renderera tekstu, tworząc go w pełni uniwersalnego. Teraz pozwala malować tekst jednym lub dwoma fontami, o takim samym rozmiarze lub o różnym, ze wsparciem prostego kerningu. Daje też możliwość malowania treści na podstawie współrzędnych lub wewnątrz obszaru, a także wyrównywania tekstu (do konkretnego rogu lub do środka obszaru). Obsługuje znaczniki łamania linii oraz znacznik swapu fontów. Wyszło nieźle.

Klasa malująca tekst gotowa, więc mogłem wrócić do generatora binarek z animacjami. Okazało się, że generowanie binarek z tak prostymi filmikami jest bardziej skomplikowane niż tworzenie plików z poziomami. Kodu generatora animacji wyszło jedynie 1200 linijek. Narzędzie to finalnie potrafi wypluwać pliki binarne z dowolną liczbą animacji (z dowolną liczbą faz, od jednej do trzech), a każda z nich może zawierać dane na temat tła i dowolnej liczby elementów.

Generator animacji skończyłem, przetestowałem – śmiga. Wygenerowałem sobie przy okazji binarki z dwiema częściami intra, które przydadzą się do testów, kiedy wrócę do rozwijania kodu klas obsługujących cutscenki. Przy okazji jeszcze odwiedziłem pozostałe cztery narzędzia, dodając do nich kilka przydatnych rzeczy, o których wcześniej nie pomyślałem (np. wyświetlanie krótkiej instrukcji obsługi, jeśli uruchomi się program bez parametrów). Ujednoliciłem też ich kod, tak aby wszystkie narzędzia były w tym samym stylu (same drobne poprawki). Teraz wyglądają i działają profesjonalnie.


Podsumowanie

I tak zleciał mi wczorajszy dzień – sporo poprawek, kupa kodu dla nowego narzędzia, reorganizacja struktury plików projektu, dorabianie binarek, ikon i plików readme.txt, oczyszczanie bieżącego kodu. Teraz, skoro już binarki z filmikami są gotowe, można wrócić do klas obsługujących animacje i dodać długo wyczekiwaną metodę – LoadFromFile. Następnie stworzyć klasę animacji poziomu, dodać dwie scenki dla jego pokazywania i zanikania, a na koniec dokończyć główną scenę rozgrywki. I to będzie finał – koniec pisania kodu, zostanie tylko stworzyć ostateczne binarki z poziomami i demówka będzie gotowa do publikacji.

Przy okazji rzuciłem okiem na zawartość katalogu z projektem tej platformówki – zawiera 142 podfoldery, 663 pliki, łącznie o wadze 40MB. A miał to być tylko prosty test przełączania warstw… taki skrumniutki… ;)

#free-pascal #lazarus #platformer #retro