Solitaire Redux: Lots of Changes
So it’s been quite awhile since I worked on the Solitaire game, and honestly I had kind of written it off as a forgotten project. It’s a normal development trend and I fall into it often. A project seems fun and interesting when you start off but then you start to develop a codebase that you have to live in, you start to live with some of that technical debt, and you start dealing with the more insidious problems and you kind of lose interest. Not only that but by the time you get to the established codebase you’ve been working on this codebase for quite awhile, so the concept itself becomes less interesting due to a lack of novelty.
This is very much where the Solitaire project was for me and I wasn’t paying much attention to it. However, about a week ago my mind drifted to game dev and I wanted to write some code. I work professionally as a software engineer, however I find that the op-tempo for professional work comes as a kind of sine wave. We code like crazy leading up to a release, but after we snap the chalk line on a release version and get to testing we basically stop coding and start doing either in depth testing or documentation. All very important tasks for sure, but it leaves you for a couple of months with no coding work. I find that if I go too long without having some code problems in front of me my mind wanders and I get frustrated, so in those times I often come back to coding personal projects. This time around I considered starting a new game project, maybe learning something with ECS, but after a little bit of deliberation and prototyping I decided to buckle down and finish the Solitaire project. And, for about a week now, I’ve been hard at work and have covered quite a few features.
I have about six features that I actually made this week, but I capped the week by merging my dev to master and officially snapping the chalk line on version 0.0.4, something that I hadn’t done for awhile. Since I hadn’t done this in a bit, a lot of my old bug fixes and half features also got merged to master. That’s not super significant, since it’s just me working on this and looking at the code base, but I say that just because it feels rewarding and it feels like I’m breathing new life into a stale project.
Features that I did complete this week include:
- Added the ability for mobile players to set left vs right hand game orientation.
- Added a ScheduledMovement system that allowed me to make card animations that take place over time (instead of all at once).
- Refactored win-on-board and deal game actions to use the new scheduled movement system.
- Added ability for “click-to-move”. Players can now click or tap on cards and they will auto-move to the first detected available movement.
- Added a particle effect that will trigger when any card is moved onto a Heap structure.
- Added a “winner!” animation that will happen whenever the player wins a game.
Quite a few features, lets talk about them in detail.
So outside of adding features, one of the first things I did this week was read my old posts/notes and try to sus out what features I was thinking about a year ago when I first started this project. One that came to mind was “left vs right handed” layout. This is a lot more obvious in person when playing mobile then it is when seeing it in gif form, but with the original mobile layout where the deck/stack is on the left hand of side of the screen I found that I was always covering the screen with my right hand whenever I interacted with the deck at all. It seemed like a good idea to move the deck to the other side of the screen for right handed players (like myself). This ended up being a pretty simple feature, just a couple of methods in the GameMaster class to reposition the layout based on the AppPreferences, and a hook in the AppPreferences setter method to move the top row of the game around if the player changed the orientation mid game.
The ScheduledMovement system is a little bit harder of a concept. The way this game (and really all games) work is that there is a core game ‘loop’. This loop happens many times a second, and in that loop you spend part of your time updating the games ‘model’, and the rest of the time rendering the game world out to the screen. If you have a method that triggers under certain circumstances, it all happens on one game loop. So, for example, my “deal a game” method all happened in the update portion of one game loop. Dealing a game involved creating and assigning movement to cards so that they would move to their new positions, but I didn’t have a way to slow this movement down and have it happen over time. I would just assign all the movements all at once, and every card would go where it was supposed to. This looked neat a year ago, but what I wanted now was a scheduled system where I could decide that card x needed to go to position y, but instead of having that happen right now I would plan these movements in the future and make the whole, collective animation look more realistic.
This feature is actually kind of interesting, cause I did a major refactor to it halfway through the week. The core feature is pretty simple, I implemented a ‘ScheduledMovement’ class that could hold all of the data that I may want for any kind of scheduled movement, and this class had an “updateAndExecute” method that would take in a time value (designed to be delta time), which it would subtract from it’s starting delay time. This would return back a boolean. If the delta time subtraction caused the delay time to go below 0 seconds, it would perform the action and return true. If the delta time did not cause the delay time to go below 0 seconds, it would subtract the delta time from the delay time, would not execute the action, and would return false. GameMaster would then keep a data structure of these objects, and would call this updateAndExecute method on them, dereferencing them if they came back true.
The data structure that GameMaster uses was the cause of the refactor here. On first implementation, this was a Queue. The thought here was to make movement scheduling easier. On each update cycle GameMaster would peek the first element in the queue and would perform the “updateAndExcecute” method. If true was returned, we’d dereference the movement and move on. The key thing here is that the delay ascribed to each movement was NOT in absolute time, but rather time relative to whatever action was scheduled before it. This actually worked great for the problem that I had in mind at the time that I was building this (doing the game deal/win-on-board animations). Really great. It fell apart however when I tried to apply this system to other, newer problems. The key shortfall was that it wasn’t easy or determinate to have multiple actions that weren’t obvious sequences of each other happening concurrently. Namely, when I got to the particle animation feature that we’ll talk about next, I needed a way to say “perform this action when this card reaches it’s destination, but do NOT delay moving the next card until that happens”. So basically if I needed to perform actions a, b, and c in order, the system worked great. But when I got to the situation of “perform a, b, and c, but c should not be dependent on b at all”, it got a lot harder.
I settled on making the data structure a List instead of a Queue. Now, on every update cycle, GameMaster examines all ScheduledMovement objects in it’s list, and performs the “updateAndExecute” method on all of them. In this, all schedule timing is absolute, not relative. To be honest this works just as good for the small case of easily ordered actions, once you work out the scheduling in the calling method, but it also allows for actions to not be dependent on each other. Big win.
Before Animation Refactor
After Animation Refactor
Click to move functionality was another old desired feature. Up until this point, all card movement was done using the “click-and-drag” system (which is honestly great, not knocking it here). Most other Solitaire game I’ve played though have the ability to be a little more “high speed” then click and drag, allowing the user to simply ‘touch’ a card and the card moves to a spot. My first thought with this was that, obviously, there could be more then one valid spot for the card to move. So it should probably move to the spot the player most likely wants it to move. Given that, I developed some search criteria:
- We first check all available Heaps for a valid move. If any move is found in any Heap, we assume that that is the most desirable move and do that.
- If no valid Heap moves were found, we check all column ends.
- If the user is “click-to-moving” a CardGroup (aka a group of cards off of a Column) then we forego the Heap check, as those will always be invalid, and only check Column ends for valid moves.
- Currently we do not apply the “click-to-move” functionatlity to any card currently on a Heap. May be added in the future.
This turned out pretty great, and hard to find any qualms with.
The particle effect was very interesting. It seemed apparent to me even when starting this project a year ago that animation and “celebration” was necessary to make the game interesting. Part of this would be being flashy when the player made a good move, such as adding a card to a Heap (as that’s an obvious move towards a win). For this I knew I wanted to use the libGDX Particle Editor, however it was my first time using it so I spent an evening learning how to use the software. From there I put together an animation that used a bunch of the generic ‘particle.png’ objects and created a firework effect (I don’t have video of this, sorry). This looked “OK”. And I mean okay. It got the job done, and it looked cool, but it was also kinda meh. The next day I sat and brainstormed what a better look would be, and I realized that it might be cool if it was subtle, but used assets that were used elsewhere in the game already. I landed on an effect that only used one particle, but the particle was the Heap object sprite (which, by chance, is also the sprite of all of my card backs).
This looked MUCH better and I was very happy with it. I played a little bit with how to get this to work in a Scene2D project, and I got the animation working in a reliable state in game and in a state where it could happen multiple times simultaneously (very important in my mind).
Finally the last feature, the ‘winner’ animation. This was a doozy. The concept itself isn’t that hard to understand, nor is it’s implementation now that the prerequisites are there. Basically, I wanted a cool card effect (easily repogrammable) to play when the player won a game. This would rely heavily on the ScheduledMovement system implemented earlier this week. Implementation wasn’t crazy at first, I made a convencience “DeckAnimation” class that calls animations, and it has everything it needs to schedule movements on the ScheduledMovement list. The ‘doozy’ part was a bug in the win-on-board determination. It boiled down to something simple, part of the win-on-board movements was moving cards between card structures. What I forgot was that moving cards between cards will also, sometimes, trigger looking for win conditions. This will in turn trigger looking for win-on-board conditions, which will trigger performing win-on-board movements. So the method was calling itself in a non-obvious way. Once I discovered this I simply set a flag when I entered and left the method to prevent recalling things, and that fixed the bug, but I’m serious when I say this took me around a day and a half to find and I was thinking I probably wasn’t going to make this post just because of that. It presented itself in a non-obvious way, the last card in the last heap would fail to make it’s final ‘win-on-board’ movement animation whenever I had the “winner” animation turned on (because before it got a chance to set itself, we’d make the “winner” animations that would happen at the same time as it), and the whole grouping would look wrong.
Anyway, after a day and a half of trobleshooting I found the problem, fixed it, and now have this snazzy winner animation. (NOTE: This doesn’t look quite as great when capturing the gif, but it looks very smooth in real life.)
And that’s a solid week of development. Honestly I feel great having worked that whole week and accomplished stuff. Now to pick new features off the list, implement, and work towards release. For posterity, my hope is to have released this game, with some form of ad integration (somehow) onto the Google Play Store by the end of November of this year. Fingers crossed!