Hi, everyone! =)
So since our teachers often encourage us to share our thoughts and progress I decided to do so but got a bit carried away hence this wall of text =)
After taking two courses on Unreal C++ and Blueprints and completing both in C++, I decided to take a challenge and create a small quest game. The idea was to prove myself that I’m capable of planning and writing code without looking into someone’s tutorials at all. This would also assure that I uderstand completely what the code does and how all parts work together. The challenge was only about coding and I didn’t put much effort into design (which is anyway not among my main interests), and all the assets were taken from free collections on UE Marketplace.
About the game
It’s a simple quest game with a short plot. It tells a story of two assasins (bound by not really warm but reliable friendship) who accidentally tricked themselves and got captured in an unfamiliar place and time and they need to return back. You control only one character, another assasin is following you and giving you some hints through the dialogs (but plotwise he’s mostly distracting your attention from a subject in a kinda joky way =)) ). I want to believe that the ending is somewhat unexpected although it doesn’t matter at all since this game is never gonna be played by other people - it’s all about me learning and practicing. The challenge took me a lot of time, like 50 days or so, but I didn’t count the hours actually spent on coding, don’t want to disappoint myself even more =)
What I’ve done:
Interactable Items, Places and Characters are interacted with via interface. The hovering widget shows which key to press (always E) and what you can do (collect, interact, talk or destroy). All places have their own visual representation of multi-step interaction but the underlying preceding logic (checking whether you have all prerequisites in your inventory and assosiated quest in log first of all) is the same. If you lack some items the widget will show you the list.
Bucket is an Item and can be simply collected, but the ivy pot is a Place and requires the knife.
Prerequisites appear with gentle fading in and out animation.
Inventory is as traditional as it can be. It has four sections - Food (increases health), Loot (needed for place and humans interaction), Tools (same as loot, but reusable) and Special (goals of the main quests). Each item has thorough description which isn’t really needed but adds some details to the story. You can use only food items from inventory (manually), loot disappears automatically on place interaction, and tools don’t disappear.
Description scrolls automatically when item is hovered.
AI is very specific for each character and I couldn’t find the way to unify it. Non-interactable humans are patrolling their paths to make some movement and lively look in the game. Four of the animals have the behavior which basically replaces the cinematics and is tied visually to their specific quests and locations. Another animal is attacking you but you can’t attack back (because I love animals and can’t hurt them even in games). And finally your associate is following you everywhere, boat travels and disembarkment included (no, boat is moving along the spline, you can’t control it; but it stops half-way once you decide to jump out into deadly cold water). Most of the characters are controlled by their specific AI controllers (which run their own behavior trees with custom C++ BTTasks, have different delegates, variables etc.), other two use simple AI.
Quest System was something I for some reason didn’t initially plan because I imagined my game more like an EscapeRoom (based on guessing) than a quest game (based on instructions). The sudden change of plans ruined the logic a bit but in the end it works. But if I’m to make a quest system again I will never use my approach. Currently it’s actor-based and I find it stupid, next time I’d rather make struct-based system (no mistakes - no learning, I guess). The system consists of QuestManager actor (kind of a library), QuestComponent for quest holding characters, QuestUpdater for items, locations and interactable characters, QuestLog array (in main character) which provides information for the QuestLogWidget and QuestTracker (small widget in the corner showing the progress). The QuestComponent is the most questionable decision; it has two responsibilities: a) to make all initializing logic once in the BeginPlay instead of running “finding” cycles when you urgently need a result and b) to add quests to your log according to the plot progress. As for positive experience - I like the overall minimalistic look of widgets and color indications (which I didn’t bother about for inventory) and I also took my time to write somewhat creative descriptions to tell the story.
When quest is completed it changes color and after some time fades away from the tracker but remains in log.
Dialog System was created without even having Unreal built-in Dialog System in mind, and it doesn’t involve voice records, solely the text. Dialog System is what actually moves the plot and is strongly controlled by GameMode. Probably not the greatest idea but thus I make sure that the story is linear which is important. Unlike the quests, the dialogs are struct-based and stored in a DataTable and created when they’re triggered. The dialogs are of different types but the underlying display logic is the same for all of them. So there are plot-moving dialogs which afterwards trigger quest adding to the log, their importance is highlighted with camera blend. There are reaction dialogs - they’re called on interact or as the second asassin’s reaction to events or to you reaching the quest location. And there are optional dialogs which add details to the plot but not neccessary for completing the game, you just choose the topic from the list.
Dialog lines of your interlocutors are colored differently.
Once you’ve completed the quest the optional dialogs about it are no longer available both for interactable humans and your companion; optional dialogs with him about your story remain even when you’ve discussed the topic. Color indication is used to mark topic as already used one.
- IK with Control Rig is done for biped and quardaped characters. It’s fine for humans, but for animals I made it poorly since I still don’t have an intuitive understanding of how the bones affect each other rotations. I managed to fix wings and tails trembling, but the feet still look a bit weird.
- Appearance via DataAsset - done in one short and clean-looking (I really believe so =) ) function on Construct and allows to create various appearances from modular clothing assets while having only one female and one male characters. Not sure what DataAssets are supposed to be used for but I really like what I implemented, it’s an easy and quick way to make a variety of looks.
What I haven’t done:
- Animations are extremely minimalistic since I used Paragon characters and their animations are meant for combat and basically nothing else (at least in Sparrow’s case). So the state machines are basic and boring and it doesn’t bother me now because there is just enough functionality for a quest game. But they have a parent C++ class which provides movement related and some other data and I like the idea of transfering these calculations to C++.
- Main Menu - just decided not to make it because it would be rather generic and I’ve already done it in C++ for the MarbleRun.
- No cinematics - leaving it for another project with more diverse environment and animations.
- Save/Load System - because initially I thought that different systems should be independent from each other and I will worry about saving later. But later I realised that not everything can be saved and I should’ve planed everything taking the “saveability” into account. Well, that’s a painful lesson but it’s taken. Also I’m not yet fluent with weak pointers and other advanced stuff which I assume should be used here, so I decided to also postpone this system to another project and not force myself into drastic refactoring (or rewriting from scratch) on this one. I think it’s better to first get some theoretical base than to blindly try to make it work using only my approximate understanding of the subject.
- Shopkeeper - for several reasons. Firstly, this idea was more an assumption than a plan, and I didn’t consider it thoroughly: it involves main character to have some money which doesn’t go well with the plot. Secondly, the interaction would be done mostly via widgets and have much in common with inventory and that would contradict with my desire to try making many different things instead of polishing one thing. And thirdly, because of current situation in my country we only have electricity for about 6 hours per day so making the system which partly doubles another system felt like a waste of precious time (especially when the game - which has value for no one but you - is kind of making you feel devastated and you have not much enthusiasm left to finish it).
A lot of lessons taken, a lot of conclusions done, some depression undergone as well. The game is playable, the plot is finished. The main goal - to prove myself that I’m able to create code without looking on others’ code - is achieved. Of course this code isn’t great, but at least I’ve got rid of my fear - that’s important. And I tried hard to not make it really bad and at least avoid code repetition and tangled if/elseif code (except for paranoid nullptr checks). In my defense I can say that I’ve only been studying Unreal and C++ for 3 months in total before taking this challenge =)
I want to say huge thanks to the lecturers who made those incredible inspiring courses. If not for you my acquaintance with Unreal and C++ would be much more painful and discouraging. I thank you for giving me a reliable knowledge base and I’m really looking forward to seeing your new courses if such are planed (maybe about good coding practices, memory managing and advanced math? =) ).
Thanks for reading till the end! =)