*Przedstawiam analizę tylko tych części projektu nad którymi pracowałem.
Spis Treści
1. Burza mózgów i proof of concept
1.1 Burza mózgów
Pierwszym etapem po odkryciu motywu (Antiquated future) game jamu’u była burza mózgów w naszym zespole. Wspólnymi siłami wywnioskowaliśmy, że najlepszym sposobem na wykorzystanie motywu jest zmiana czasu jako główna mechanika w grze. Gracz może zmienić za pomocą przycisku „czas” teleportując się w to samo miejsce, zmieniając jednak otoczenie. Każdy z nas brał czynny udział przy designie gry i tego jak ma wyglądać rozgrywka.
1.2 Proof of concept
Mieliśmy 2 różne pomysły na utworzenie tej samej mechaniki. Chciałem wykorzystać technologię „Data Layers” Zaprezentowaną na pokazie Unreal Engine 5, zaś drugi programista chciał wykonać mechanikę w taki sposób, by na jednej mapie utworzyć scenę oraz duplikat sceny przesunięty o pewną wartość w osi Y. Gracz miałby po „teleportacji w czasie” zostać przeniesiony na drugi duplikat sceny czyli w osiach X, Z w których aktualnie się znajduje, a w osi Y, Y+((+/-)Wartość przesunięcia sceny).
1.3 Konkluzje z konceptów mechaniki
„Data Layers” to system używany do organizowania aktorów w osobne warstwy. Warstwy te można ładować i rozładowywać, aby uporządkować swój świat. Dzięki warstwom danych można dynamicznie ładować i usuwać aktorów w Edytorze poziomów, co ułatwia zarządzanie złożonymi poziomami. Brzmi to jak idealne rozwiązanie dla tej mechaniki, w rzeczywistości jednak pojawiły się pewne problemy, a sam system okazał się zdatny, lecz do zupełnie innych celów.
Po pierwsze Kluczowym była blokada teleportacji gracza w momencie, gdy w drugim „czasie” była w tym samym miejscu przeszkoda lub gracz nie powinien teleportować się bez uprzedniej interakcji z danymi zagadkami środowiskowymi. „Data Layers” nie umożliwiają nic oprócz załadowania lub rozładowywania poszczególnych obiektów na scenie. Nie możliwym jest więc sprawdzenie czy gracz może w danym miejscu się teleportować czy nie, nie można także dodawać lub usuwać obiektów z warstw, jeśli AI wystrzeli pocisk to pocisk jest również rozładowywany jako dziecko obiektu. Szereg różnych problemów zmusił nas do skorzystania z druegiero rozwiązania i nie miałem problemu żeby przyznać się do tego, że moje rozwiązanie było gorszym wyjściem.
1.4 Dodatkowe koncepty zagadek środowiskowych
Wykonałem także dodatkowe koncepty/prototypy łamigłówek środowiskowych które współgrały z mechaniką zmiany czasu, i które po części zostały wykorzystane w projekcie.
2. Podział prac
W naszym zespole skupiliśmy się na efektywnej współpracy nad projektem, angażując różne umiejętności i specjalizacje. W skład naszego zespołu wchodził: VFX Designer, Level Designer, Sound Designer oraz dwóch programistów. Aby jak najlepiej wykorzystać nasze umiejętności, podzieliliśmy się zadaniami, zaczynając asynchronicznie prace nad różnymi aspektami projektu.
Jako, że było 2 programistów, podjęliśmy decyzję, że jeden z nas skupi się na tworzeniu sztucznej inteligencji (AI), podczas gdy drugi (czyli ja) zajął się rozwijaniem logiki i animacji głównej postaci. Naszym celem był spójny i zintegrowany proces tworzenia, tak, by pracować bez kolizji lub czekania na wynik prac drugiej osoby.
Nasza współpraca opierała się na regularnych spotkaniach, podczas których dzieliliśmy się postępami i pomysłami. Dzięki temu podejściu mieliśmy pewność, że wszystkie elementy projektu idealnie ze sobą współgrają.
2.5 Przygotowanie Repozytorium Github oraz inicjalizacja Git LFS
Byłem odpowiedzialny za Source control w naszym zespole. Ze względu na małą ilość osób w zespole oraz rozmiar projektu zdecydowałem się na technologię Git.

Podczas 286-ciu commitów, konflikt wydarzył się tylko raz, gdy jedna osoba przypadkowo nadpisała klasę z której nie korzystała i spushowała do main’a.

Plik gitignore
# Visual Studio 2015 user specific files
.vs/
# Rider files (just ignore all for now)
.idea/
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
*.ipa
# These project files can be generated by the engine
*.xcodeproj
*.xcworkspace
*.sln
*.suo
*.opensdf
*.sdf
*.VC.db
*.VC.opendb
# Precompiled Assets
SourceArt/**/*.png
SourceArt/**/*.tga
# Binary Files
Binaries/*
Plugins/*/Binaries/*
# Builds
Build/*
# Whitelist PakBlacklist-<BuildConfiguration>.txt files
!Build/*/
Build/*/**
Builds/
!Build/*/PakBlacklist*.txt
# Don't ignore icon files in Build
!Build/**/*.ico
# Built data for maps
*_BuiltData.uasset
# Configuration files generated by the Editor
Saved/*
# Compiled source files for the engine to use
Intermediate/*
Plugins/*/Intermediate/*
# Cache files for the editor to use
DerivedDataCache/*
plik gitattributes
# UE file types
*.uasset filter=lfs diff=lfs merge=lfs -text
*.umap filter=lfs diff=lfs merge=lfs -text
# Raw Content types
*.fbx filter=lfs diff=lfs merge=lfs -text
*.3ds filter=lfs diff=lfs merge=lfs -text
*.psd filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
*.xcf filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
3. Logika głównej postaci
Po moich namowach zespół zdecydował się na implementację frameworku AGR PRO. Od pewnego czasu używam go we wszystkich swoich projektach, ułatwia on życie dostarczając gotowe funkcje, klasy nadrzędne i zmienne dzięki którym część z mozolnych czynności przy tworzeniu projektu jest już wykonana. Framework jest darmowy, polecam każdemu 🙂
Fragment kodu BP_PlayerCharacter – Event tick
Fragment kodu ABP_PlayerCharacter – Anim Graph – Aim Offset Layer
3.1 Udogodnienia AGR
Po dodaniu Frameworku AGR do projektu, do klasy głównej postaci dodany został komponent AGR Animation master który:
– Pobiera wartości z komponentu character movement i zapisuje je w zmiennych, zmiennych które można wykorzystać w Animation Blueprint z klasą nadrzędną AGR Anim Core.
– Umożliwia dodawanie dynamicznych tagów co umożliwia w łatwy sposób na Layered Animation Design
– Dostarcza predefiniowane metody rotacji gracza tj. Rotate to velocity, Desired Rotation, Absolute rotation itd.
– Replikuje (swoje) niezbędne wartości, zmienne i klasy
a to tylko kilka zalet jednego z 6 komponentów tego frameworku 🙂
3.2 Ustawienia animation master component w klasach BP i ABP postaci

W klasie głównej postaci w komponencie Anim master zostały wykorzystane tylko niektóre z funkcji. Użyto Rotate to velocity jako metody rotacji postaci dzięki czemu postać obraca się tam gdzie aktualnie podąża.

Dostępne zmienne w ABP postaci. Dzięki frameworkowi AGR możemy w łatwy sposób odczytywać wartości i stany logiczne postaci korzystając z dziedziczonych zmiennych i na tej podstawie tworzyć stany animacji itd.
3.3 Interakcja z innymi aktorami
Zdecydowałem się na prosty system interakcji w którym po naciśnięciu odpowiedniego przycisku sprawdzany jest aktor który aktualnie koliduje z graczem oraz posiada interfejs „BPI_Interaction”, jeśli tak to wysyłana jest wiadomość o wywołaniu funkcji Interact


4. Animacje
W projekcie utworzyłem wszystkie oprócz 2 animacji głównej postaci: Idle oraz Run. Tworzyłem je wewnątrz Unreal Engine najpierw tworząc Control Rig dla danego Skeletal mesh’a, a następnie używajac sequencera.
5. Boss
Boss to element z którego jestem najbardziej zadowolony w tym projekcie, w całości pracowałem nad jego logiką i animacjami, Efekty wizualne zostały utworzone i dodane przez naszego VFX designera w niagarze. Chciałem uzyskać taki efekt jak ma to miejsce w grach typu souls-like jednakże zachowując względnie łatwy poziom trudności. Boss wciąż jest w stanie zadać duże obrażenia jednak łatwo jest ominąć jego ataki.
Z perspektywy czasu przyspieszyłbym czasy animacji „Homing missile” oraz „Laser Spin” przez co walka byłaby bardziej dynamiczna. Pierwotnym założeniem było przygotowanie gracza na pojawiający się atak, aczkolwiek trwa to zbyt długo i prowadzi do sytuacji w których gracz może przez pewien czas stać w miejscu i atakować bez żadnych zagrożeń, nie pomiędzy atakami lecz w trakcie ładowania ww. animacji.
5.1 Logika bossa
Na etapie tworzenia tego projektu nie znałem jeszcze frameworku Gameplay Ability System (GAS), dziś wykonałbym logikę bossa w oparciu o ten system. Wykorzystałem listę enumeracyjną w której zawarłem nazwy dostępnych ataków. Utworzyłem tablicę typu enum tej konkretnej listy i zawarłem w niej dostępne ataki, dodałem atak podstawowy 5 razy.

Atak podstawowy został dodany więcej razy żeby zwiększyć jego wagę. Z dostępnej listy wartość jest wybierana losowo, a nie chciałem uzyskać efektu w którym boss używa cały czas najgroźniejszych ataków.

Po widocznym na zdjęciu wywołaniu kodu następuje „Play anim montage” z odpowiednią animacją. W anim montages znajdują się notifiery które uruchamiają Event „Attack”.


5.1.1 Homing missile
Rakieta namierzająca gracza; Po wywołaniu eventu Attack spawnowany jest aktor typu projectile klasy BP_HomingMissile.

Link do kodu BP_HomingMissile
5.1.2 Single Shot
Front bossa jest standardowo zawsze zwrócony do aktualnej pozycji gracza więc wystarczyło zespawnować aktora typu projectile odpowiedniej klasy.
5.1.3 Laser Spin
Po wywołaniu eventu Attack uruchamiany jest kolejny event „LaserAttack” w którym sprawdzany jest boolean „Laser Turn On” modyfikowany przy pomocy anim notifier’a.

Jeśli bool == true to boss przestaje obracać się w kierunku gracza, wywoływana jest w zapętleniu funkcja „LaserTrace” generująca Line Trace By Channel z miejsca każdego slota. Jeśli Line Trace dotknie gracza to aplikowany jest Damage.

Logika w tej funkcji jest powielana, z perspektywy czasu użyłbym tu raczej „For each loop”, dodając sloty z których ma wydobywać się laser do tablicy przez co kod byłby bardziej skalowalny i czysty/przejrzysty.
6. Dodatkowe elementy projektu
6.1 Wieże zasilające oraz ich przyciski
By przejść do końcowej fazy gry i uruchomić maszynę PrototypeH która kończy rozgrywkę trzeba najpierw zlokalizować przyciski i uruchomić 3 wieże zasilające, przyciski do uruchomienia tych wież są rozsiane po mapie i należy użyć narzędzia do zmiany czasu, by się do nich dostać.
6.2 Stoisko odblokowujące mechanikę zmiany czasu
Mechanika zmiany czasu jest możliwa do odblokowania dopiero po pokonaniu głównego lobby mapy w którym znajdują się wieże zasilające, gdy gracz po raz pierwszy znajdzie się w pobliżu stoiska ma możliwość zebrania gadżetu któy odblokowuje tę umięjętność.
6.3 PrototypeH, końcowe urządzenie
Urządzenie PrototypeH może zostać uruchomione tylko jeśli włączone zostały wszystkie wieże zasilające, po uruchomieniu zostaje uruchomiona sekwencja zakończenia gry.

Za każdym razem, gdy włączona zostaje wieża zasilająca, wywoływana jest funkcja Update Power z poziomu klasy wieży, znajdująca się w klasie BP_PrototypeH

W BP_PrototypeH w BeginPlay, pobierana jest ilość wież za pomocą 'Get all actors of class’ i zapisywana do zmiennej 'NumberOfTowers’

Po wywołaniu eventu „UpdatePower” sprawdzana jest ilość aktywnych wież, dla każdej aktywnej wieży zmienna „TowersActive” typu int jest zwiększana o 1, jeśli ilość aktywnych wież odpowiada ilości wszystkich dostepnych wież wtedy wywołany jest event „TurnOn”, jeśli zaś ilość wież aktywnych nie jest równa ilości wszystkich wież wtedy resetowana jest zmienna „TowersActive”

Event TurnOn to zmiana oświetlenia/koloru modelu z czerwony na zielony oraz zmiana booleana Active na true. Boolean jest sprawdzany podczas interakcji gracza z aktorem.
6.4 Narzędzia dla level designera
Podczas trwania projektu wykonałem także generator napisów oraz platformę przemieszczającą się z punktu A do punktu B po wyznaczonej trasie dla naszego level designera.
6.4.1 Generator napisów
Dostałem wytyczne co do tego jak powinno wyglądać narzędzie do tworzenia napisów w projekcie. Przede wszystkim wymagana była możliwość wielokrotnego użytku oraz elastyczność. Generator napisów to nic innego jak aktor posiadający zmienne które są Exposed on spawn oraz Instance editable.
Do wyboru są dwa tryby:
Default – czyli po tym jak gracz przejdzie przez niewidzialny collision box, uruchamiany jest widget napisów na określony czas po którym napis znika
Overlap – Napis pojawia się, gdy gracz zacznie kolidować z collision boxem i zniknie dopiero, gdy gracz wyjdzie z jego obszaru
BoxSize to zmienna w której ustalane są parametry wymiarów collision boxa
Text to zmienna w której ustalany jest tekst napisów
Duration to zmienna w której ustalona jest długość trwania tekstu w trybie default
SubtitleType to zmienna typu enum w której wybiera się tryb napisów wyjaśniony powyżej
6.4.2 Platforma
Zdecydowaliśmy się również wykorzystać jeden z moich konceptów czyli platformę lewitującą z punktu A do punktu B. Zmieniłem logikę platformy tak, by level designer mógł z łatwością ustawiać wszystkie niezbędne zmienne.
6.5 Menu oraz napisy końcowe
Menu zostało utworzone według wytycznych oraz dostarczonych materiałów.
6.6 Testowanie projektu
W końcowych fazach projektu wraz z drugim programistą testowaliśmy nasz projekt w poszukiwaniu błędów, najczęściej zdarzały się one przy używaniu mechaniki zmiany czasu teleportując gracza poza mapę, ale także były to błędy związane z rozmieszczeniem wrogich AI oraz zadawanymi przez nie obrażeniami, balans rozgrywki powodujący, że niektóre miejsca były zbyt trudne, a inne zbyt łatwe. Niestety przez brak czasu poprawione zostały tylko najważniejsze błędy uniemożliwiające dokończenie rozgrywki.
7. Podsumowanie
Udział w tym projekcie wzbogacił mnie o cenną wiedzę, pierwszy raz pracowałem w zespole przy tworzeniu projektu, sprawdziłem swoje umiejętności komunikacji oraz współpracy. Dałem również radę pracować pod presją czasu i prawidłowo rozdysponowałem czas na poszczególne taski. W kwestii kodu z perspektywy czasu wiele bym zmienił, poprawił, jestem teraz na wyższym etapie i widzę błędy, niedociągnięcia, czy po prostu faux pas których wcześniej bym nie zauważył.