BLOG 2 - AI, UI, all the I's
June 9th, 2025
I will note, that I spent the first few days since my last update has been spent redesigning and reworking this entire website. I'm not the biggest fan of web development but I managed to get something I feel happy enough with here. The website prior had so many dead links and was a mess to navigate. I don't think it's perfect, but it's good enough for what I want to invest my time into for the web side of Doopwee Games. In making the website though I noted that I needed some images for the links to SotS and didn't even have a title image, so I went to paint.net and got to work on this... then I ran into the common challenge of what font to choose. I wanted something that felt robotic, cartoony and also piratey... Those are quite a few drastically different themes. Cartoony can be baked into robots or pirates, but robots AND pirates are a hard theme to merge as easily into a font. I ended up finding Righteous which clicked with me: https://fonts.google.com/specimen/Righteous. It felt like a nice middle ground. Armed with this I wanted to capture both the sea and scraps of the title. So I searched for some example images of rivets that were creative commons and took a screencap of the waves I implemented last week from the shader on sand. I masked these on the font. You can see the results above in the header of this page.
Now on to the development side of things. High level I was able to accomplish the following since I last blogged:
- Added the basic battle attack processing and have units die.
- Added little sphere indicator and damage text above characters heads
- Started the basic implementation of some of the AI logic
- Turn Action menus for the battle.
- Character Info Popup.
ATTACK PROCESSING:
Before I could dive into the attack processing I needed to rework my stats class systems. To begin with all I has was a simple stats instance inside my character class. I needed to manage that eventually there will be temporary stat modification from attacks, hp needs to reset to max at end of battle. That the equipment or configuration of bots needs to change the stats... so with all this said, I extracted the common stats that all of those things might change from the stats that don't change like XP, lvl, battery charge etc. Some are specific outside of battle and some are specific to battle so those are separate. When I enter the battle, I take the base stats, add the equipment stats to a new battle stats instance and then also keep a second copy of the original battle stats to know what I need to go back to. This isn't a final solution as I'll need to eventually handle turn timed battle stats modifications like temporary defense boosts and what not. For now it will do.
After I had this sorted, I was able to move on to the attack processing. For this, I need to consider multiple things in the algorithm, luck for miss / crit chance, attack for base damage, defense and hp of opponent etc. This is why all those stats were important to have setup properly.
Presently I'm not modifying the XP from this, I'll ideally process this later. My plan is that XP gets added throughout the battle and then levelling up is only done at the end of battle in the post battle screens.
So I implemented the logic and functionality and.... I couldn't quite tell if it was working. I was going to put in some debug logs to test but instead just put a breakpoint and verified that there was damage being done, but because of the general base stats I had set it was so low it took 12+ attacks to kill a unit doing 1-2 damage each attack and HP generally starting at 25-30... I knew I would need to rescale the base stats a bit because 12 hits is not fun or exciting.
INDICATOR & FEEDBACK TEXT:
I initially didn't plan on doing UI work until after the AI was implemented but I decided at this point I needed a bit more feedback to the player. I wanted a simple inidicator of who the active unit was, I just added a small sphere over the characters head and enable/disable it on turn start/end. Pretty quick implementation, I may change out the sphere in the future with another simple model.
I then decided the next feedback I needed was to visually see the text for the damage I was doing to units, this is something that will already make my quality of life as a developer easier but also something needed for the player in the end. So I figured might as well bite this bullet now. I started out looking at how I might get the text in 3D space and fumbled around with it slightly but realized quickly that this may not be the best approach. 3D world text would have a few drawbacks, It may be partially hidden behind objects, it may scale in distance and be harder to read. So Instead I searched for a solution to make the text appear above the players heads as a screen overlay but at the screen pixel location of where the characters were located. I stumbled upon this thread which had a great solution to this problem: Read the thread Essentially I take the screen position and in an update ensure the position is being set to the characters location. This component is within each character. The update for the text position is important as the camera can pan / zoom while this is going on which changes the location on screen.
After implementing this solution and tweaking it to fit the right spot, I eventually got it to look like this:
to visualize and debug how it would look In just assigned MISS! to all the texts. In game this would not be the case and you'd rarely see more than one text up at a time. I then cleared the text again and now only use it to display the damage invoked. I then tied a coroutine to show the text for 1 second and then clear the text value. I created this as a generalized coroutine function in case I need to use this for other feedback texts in the future. I can see having to have multiple instance of feedback text on a player.... Now that I write this, I'm wondering if perhaps it would be ideal to just have that as an added component when the attack is needed and then destroy the component when done... memory wise this may not be as ideal though but in the future if I have quick attacks that attack multiple smaller attacks it could display multiple at the same time. I'll add this as a TODO.
THE START OF AI:
I've played quite a few Tactical RPG's and I will always remember Final Fantasy Tactics because I loved how challenging it was... I also remember not loving Triangle Strategy (Don't come for me) because A) I wasn't being challenged and B) there was just too much story for me. These are personal takes I know and a side tangent but back to FFT. The levels I remember and loved the most were the ones I failed and especially the ones I failed repeatedly. I loved this... which sounds masochistic in writing this. The difficulty in some of these levels also scaled outside of AI decisions. Difficulty can be scaled by being outnumbered or outclasses as well but foundationally the AI will need to be an important factor moving forward in the future.
I've honestly never really done a lot of AI programming in my career and it was an interesting start. The first thing I needed to do was decide where the AI needed to be... my instinct was that the characters themselves would each have some AI logic to them... but then I thought about it and questioned should the AI be the characters or should the AI be the opponent controlling the characters. The problem I had thought of would be mostly in the architectural design of the codebase. If the AI was implemented per unit, the units would need to have knowledge or access to the other units which at this point I do not allow. It would mean that the AI made more internal decisions and I wanted the AI to eventually be making mostly strategic team based decisions like "Where are my healing units", "Where are my own units structured for defense" etc.. so I wanted to approach the AI from the sense of emulating the decisions another player would make and less about thinking of it like each enemy unit was making decisions. I'm sure both could have worked.
What I started with was in my battle manager created a separate state handling system for AI vs Human controlled. If the unit is AI, it goes through the AI processing loop, if the active character is human controlled it goes through the user input loop.
The next step was to jump into the logic of how the AI would then make it's decisions... and I'll be honest I had to think about the way I was breaking down my considerations moving units in the battlefield but also simplifying it down. This seemed pretty simple initially logically and quickly escalated when I started thinking about all the considerations you make on your turn like the following:
- How close am I to the enemy units?
- Is there one I can reach AND attack?
- Is that units health higher than mine?
- Are they weaker defensively?
- Are they the type that could counter attack?
- If I go in, am I in bad positioning / Will I be outnumbered for them to all jump on me?
- Who's turn will follow up after?
- What's my units health level at?
- Does any of my other units need healing?
- Is there loot or something on the battlefield to collect?
- and more...
There was so many questions that go through your head quickly as a player that I need to consider for the AI opponents considerations as well. I decided to extract what is the bare requirements and start mentality of battle and will add in the complexities later on. I also want the AI to scale so it starts off a little easier and scales up quickly or can scale from battle to battle.
So I identified that the first thing I needed to do was write functionality to find units with a range and a navigatable path range at that. I hijacked the code to be reused for outline move option paths. I used this multiple times already in my foundations AI.
The basic logic as it stands on the AI's turn is:
- Is there an opponent next to me? do I have energy to attack? if so attack them
- If there isn't an opponent next to me, Is there one within movement range leaving me with enough energy to attack?
- if so move towards them
- if there isn't any enemies within walk distance, just find the next nearest opponent and move as close as you can to them.
There were some hiccups with this and crashes but I worked through them. One of the ones that made me laugh was when the AI just didn't respect the movement range and decided the games rules didn't apply to them and walked all the way to their target no matter what. You can see that in the picture above on the left here where the red unit is just walking his was past the movement zone and then eventually landing in front of the target unit.
TURN ACTION MENU:
I started working on the UI a bit for the battle at this point. I started by getting a really ugly initial layout for any HUD elements I can think of to begin with to determine white spacing, alignments etc.
I then started focus on the most important for the gameplay Items as priority starting with the Action menu for the player and then planning to move onto the character cards next (displays icon hp stats etc)
I've started with some staple functionality the game will have for options:
- Move
- Attack (may consider switching this to each arms options in the future)
- Rotate (change face direction)
- Functions (programmable skills later)
- Recharge (end turn and wait)
At this point I had the buttons hooked up but needed to think about the UI and how I would decouple it from the battle system. In general when I start to implement a new class into a game I need to ask myself a few architectural questions. How do I make this modular and decoupled so that it could be removed from the game and replaced without having to change lots of other intertwined files and classes. I need to figure out what needs to have access to this class and what other classes does this class need to have access to? In general, some safe ways I combat this are through event based designs, subscribers etc. In this case I did go with the BattleManager sending out events for state changes. These events send data out to any subscribers. It doesn't care about who subscribes it just says if you are subscribed, here's what happened. This is also reducing the bloat on my battle manager as it will just send out these events to whoever needs them. Additionally, in the future I'll be able to hook in a soundManager to process sounds for each of the events and my network manager could maybe use it for event updates. Overall so far I'm happy with the system.
I hooked up the button click events to go through the input manager. I'm not sure yet if I like this as a decision but I wanted processing of input events even through button clicks through one location. At this point I realized I hadn't accounted for controller input handling and how the game would work with controller and Steam Deck so I needed to add tasks for this and ensure my designs moving forward had this accounted for. Click input vs controller input are quite different and not something that should be an afterthought.
The menu is very much the basic menu items and look at this point as I wanted to focus on functionality first, however for some reason I decided I wanted to do a test with UI anims to rejog my memory on the process and spent a bit of time implementing some ease in and out animations to the new Turn menu. I'm happy with how it turned out with some scaling, translating and fading in and reverse out. I needed to create an animation controller for this and create a few animation clips for the states and animations. I started out using triggers to change states and ran into some flakiness of it changing states after when I didn't call the trigger. I'm not sure the true intent of the triggers here but will need to investigate them after. I switched to two bools instead of AnimIn and AnimOut to handle the states for animation instead.
One gripe I have to vent about Unity's animator interface is that objects hierarchy doesn't appear to be respected and instead it lists all subobjects in alphabetical order which means my animation control looks messier and less organized on the timeline. If you're reading this and know why they chose to order alphabetically or if there's a way around this and what the triggers are properly for in animations conditions drop a comment below.
Character Info Popup:
I next decided to start tackling the info popup. This is vital information about your active character to know how much health they have as well as their battery power and oil. Oil will essentially be sort of like magic in other games to a certain extent giving access to stronger abilities. Battery is how much energy you have to use on your turn
I mocked up some drawings and through about how I wanted the information displayed and what info was required for immediate turn decisions. It's not done but it's a start where it animates in and out when the user has to make a turn choice like to move or attack. I make it disappear when choosing where to move or attack to reduce the amount of screen space occupied but will need something similar on screen to see the opponents health when selecting to attack and in the future friendly units when healing/repairing. For now the info only shows on turn choice.
There was one problem I encountered which set me back a bit on the animator for this one. For some reason it worked no problem on the turn choice popup but with this one it kept stuttering and looping back on the animation animating in and out. It turns out it was because of the transition settings. My animations start and end where the next one should be so I needed to have the exit time set to 1 and the transition duration set to 0. By default they were 0.75 and 1.0. To solve this i searched through forums, watched Youtube videos and nothing noted the fix. I then turned to chatGPT and explained my issue and it immediately had the solution. I've been trying to get into the habit of using AI as a resource like this but my old school instincts to google are still my default go to.
For functionality again, I went with events and subscriptions here where the battle manager notifies when a character accesses the turn options menu.
NEXT STEPS:
- Have confirmations of battle actions like move location and attack.
- Add Enemy info on selection to attack.
- Allow users to specify facing direction at end of turn and rotate bot towards that.
- Show a battle complete popup when there are no more opponents.
- Experience calculations, levelling up units
- Add terrain diversity by setting dirt and grass tiles as well
That's it for now... Steam Page coming July to wishlist.
If you are interested in the games development, please hop in the discord located here for the latest updates: