Veera Raita
POKT23SP

Constellations

Project description

Team:

  • Nea Pulkkinen - Lead, Creative Director
  • Veera Raita - Programmer
  • Pentti Avolahti - Writer, Consultant
  • Samuli Suomi - Audio Lead
  • Contributors:

  • Kai Koro - Art Assets
  • Marius Larilahti - SFX
  • Constellations is a story-driven 2D-platformer, focusing on a cat trying to find closure after passing away. The GitHub repository can be found here. In short, the player has 9 "lives", which function as a marker of progress; you lose a life every time you get through a level, which represents the player character coming to terms with a specific stage of grief. Your goal is to "accept your death", as the player character was already dead by the time the game started

    I've focused heavily on programming on this project, all the way from the first iteration of the player controller script to having finished dialogue-, data persistence-, and state machine functionality (with adjustments coming to the state machine to change it into a hierarchical system). I've also added enemy NPC functionality, with player tracking and somewhat intelligent climbing and jumping behaviors.

    The movement system is also the most complex I've written so far, with a lot of adjustments to make it as smooth of an experience as possible for the player. It's implemented with the "new" Unity Input System, which should result in an easier time with possibly building the game for multiple platforms down the line. The project also makes use of Unity's Cinemachine package, enabling complex camera functionality with relatively low effort.

    Programmer Devlog #2

    Image of the title screen of Constellations

    There have been a LOT of changes since the first devlog, but we'll start off from the missing player states; the player now has attack states, a scream state, and a few other ones too that were missing previously. All enemies have also been refactored to be based on the StateMachineCore base-class.

    Our artist also did a lot of work on the UI side of things, and she added the first few levels to the game, which then needed some logic to be added. I first added logic to our "star room", which keeps track of the player's progress. As you complete levels and "lose lives", that progress is then reflected on the sky of the "star room", where your lost lives turn into stars in the constellation. The logic was fairly simple to add in practice, but required some fine tuning with the SceneTransition script, which I started work on next.

    I figured I'd tackle scene transitioning by fading in a black UI cover, and fading it out after a scene has finished loading. But that approach felt a bit "too obvious" to me, so I decided to ask for some advice from our teachers, who told me to check out Unity's async operation system. After some investigating, I implemented asynchronous scene loading, which made the transitions smoother, and allowed some more precise management of the scene loading. I also created a SceneTransition prefab to aid with any future scene transitioning needs, as you can simply drag a ScriptableObject into the prefabs's script, and the prefab will handle loading your desired scene, along with setting the background music, reducing a star from the player if the past scene was cleared for the first time and everything else that needs to be done.

    Our first level didn't need too much logic, as it was more of a tutorial level. But a tutorial level does need tutorials, so I whipped up a prefab, which fades out after touching its trigger collider. The collider's position and the text are easily customizable, and I set an editable string on the script, which saves a value to indicate if the tutorial has been faded out once or not. I also added a button to the main menu to reset all the tutorials, which would ease future demonstrations of the game.

    Our lead also thought it'd be good to have some actual gameplay elements in our demo builds, and I got tasked with implementing a gate and a key-system for said gates. I decided to go for a very simple route with regard to the keys, simply adding a string to an "inventory" list in the PlayerController-script when the player colliders with a key's triggerbox. After the key with the string corresponding to a gate's key-string has been picked up, the player can open the gate by interacting with it. There'd be many other simple additions later, but those are still in the planning-phase.


    I also refactored, optimized, and fixed a TON of problems, like:

  • NPC names now pop up correctly

  • fixed player duplication bug

  • health bar now lerps to new value

  • player now stays crouched in areas too narrow to stand in
  • player interaction rewritten and optimized
  • enemies now follow player's true center instead of player's bottom center
  • attack hitboxes adjusted to make combat more fair
  • attack timings adjusted to make combat more responsive
  • adjusted level bounds
  • fixed player input breaking at various points
  • For now that's about everything I've done with regards to programming, but to finish off this devlog, I'll talk a bit about our Bit1 venture!

    Bit1 is a competition, where students pitch their game projects, and the winner gets a pretty hefty prize. Our team lead decided to enter our project, and that kind of lit a fire under the development progress. In a very short amount of time we got 2 levels to a playable state, got testing done by two entire classes of XAMK Game Technologies students (thank you to Oiva Kahri for making that happen!), and added most of the things described in this devlog. The event was still a fairly stressful prospect, as neither of us had ever really pitched anything before. Either way, we got our pitch ready, our demo ready, and a video of our game ready, and went in expecting the worst (or at least I did). There were 4 other teams in our side of the event, as there were so many attendees that the event was split into two separate ones. We went first, and our pitch went about as well as it could have! But, the event also had other incredibly talented people, and so our Bit1 journey was cut short. We still learned a lot, got to talk to the obvious winners and received a load of good tips, and got a decent score overall, even when our project was still in fairly early stages of development. We'll probably go back next year and give it another shot!

    Image containing a slide from the presentation, which describes
                        the content of the presentation similarly to this site.

    Programmer Devlog #1

    The most important component of any platformer is the movement system, so that's where development began. I first implemented a simple system, which could be expanded later, and after being prompted by the Unity engine to use the new Input System, I decided to give it a try. After a short period of banging my head against a wall, I managed to get it implemented in a way where I could easily use it for all player movement and actions. Using the Input System also means that player inputs are easily swappable from gameplay controls to UI controls, leading to much less effort on disabling certain inputs at appropriate times.

    The first iteration of the movement system was fairly rudimentary, using simple tech to set a new player position using the attached rigidbody2d. The idea behind this was to give the player a feeling of responsiveness, but it resulted in the animations seeming much too choppy and abrupt. This iteration still had all the frills though, including jumping, climbing, dashing, running and crouching.

    Around this point I implemented a smoother camera system, using Unity's Cinemachine as its soft zone feature was exactly what was requested of me. The camera system was heavily inspired by the game Hollow Knight's system, though it isn't nearly as polished at this point in time. I also implemented a simple multi-purpose prefab to give the level-builder an easier way to add camera panning or swapping to the level (originally this was meant to simply be a down-pan around ledges, but I figured we might have similar needs later). I also added a small lerp on the camera while the player is falling, to give the falls slightly more juice.

    Then came time to redo the entire movement system using AddForce-based solutions. We wanted to keep the feeling of responsiveness while still adding flair, so I added high acceleration to the player, while capping the speed at a reasonable number. This resulted in a system with high responsiveness without the choppiness that came with setting the speed to max right away. The whole movement system revamp was relatively quick to add, so I quickly moved on to the next seemingly daunting task of creating a dialogue system.

    While I was scratching my head trying to figure out how to add a dialogue system in an efficient manner while still making it easy for the writer I came across ink - a scripting language quite literally built to make adding dialogue as easy as possible. Implementation was a breeze thanks to a Unity package built for that very purpose, and I quickly had JSON files to parse dialogue from, with choices to boot. Getting all this on screen took a short while, but eventually I had a fully working dialogue system, and our writer wouldn't have to deal with Unity, or even a single JSON file. While adding the dialogue system, I cobbled together a universal interaction system to go alongside it, which enabled running scripts on virtually any object with the proper tag (this will probably be changed to use an interface later).

    And now was time to add enemies to the game. For now there'd be two: a ghost that can pass through walls, and a skeleton with the ability to climb and jump. It took a while to add proper player tracking that wouldn't have to run on every update for optimization reasons, but I eventually got everything working. The rules for the skeleton's climbing were probably the biggest timesink, and I figured I'd have to build up a state machine system that could be used by all characters or this would keep getting worse.

    Back to the present, there now exists a functional state machine, though it's only used by the player for now. It encompasses all current and future player states, but it's not exactly usable by NPCs for now as it lacks hierarchy, and the majority of the logic is still baked into the PlayerController script. So for now the agenda is to make the state machine system hierarchical, and usable by every character, player or not.



    Programs and other tools I've used:

  • Unity (2022.3.22f1)
  • Visual Studio Code
  • ink
  • Also thanks to Sasquatch B Studios and AdamCYounis on YouTube for their excellent guides, they were a great help when developing many of the systems mentioned above for the first time.