It’ll come as no surprise to anyone that I like writing QML games, my gitorious account is littered with the prototypes I’ve thrown up in a few hours for fun. I’ve done another one which has proved to be extra-large: about a full working day’s effort scattered across a couple of nights.https://github.com/416365416c/checkers has a QML checkers prototype, with Human-on-Human and VS AI modes. It was fun and educational, which is good because checkers isn’t a very interesting game otherwise 😉 .
So what did I learn from the educational experience? Mostly, I tried doing some of the things that I keep recommending to QML developers but don’t apply to developing QML. So I built this using my system Qt Creator and Qt instead of in the magical Qt 5 land, because Qt 5 isn’t released yet so I don’t recommend it to the average developer (yet!). The next point is that I’ve always said to put serious chunks of logic into C++, not JS. My game logic may be frequently in JS, but that’s because it’s so trivially simple (you try writing checkers!). For comparison, the AI that uses classic minimax with α/β pruning is really going to be grinding the CPU for all it can. So the AI goes into C++, and still integrates easily with the QML. This at least serves as an example, albeit an pretty obvious one, of where the line can be drawn for C++ logic versus JS logic.
I also placed the AI inside a separate thread. I’ve been telling people to put long operations in a separate thread for such a long time that it deserves a separate thread 😛 . It’s the easiest way to get a responsive UI, and spinning the event loop manually tends to lead to trouble (especially with QML). I always thought it was easy, but this really was as easy as I thought. Placing the AI safely into a thread means hooking up signals and slots (necessary for making it asynchronous, not actually about multi-threading) and making a trivial QThread subclass. Throw in a QMutex and some QMutexLockers for flavor, and it’s done. With my theory vindicated, I will be even more eager to proselytize multi-threaded data-processing from now on.
So what made checkers interesting enough for me to give it a try? One reason is the simplicity of the UI; I haven’t tried a classical board game yet. This applies to both chess and checkers really, but chess is more complicated due to having multiple piece types. The rest of my games tend to send mouse clicks to a backend logic function for processing, in this UI I could actually leave the MouseAreas on GamePieces and control it all in the style of delegates. This allowed for an easy to implement drag and drop that was quite natural for the board game metaphor. The other aspect dictated by the board-game UI metaphor was the fluid motion of all the pieces, again quite easy with QML. While the user drops their own pieces, I just placed behaviors on the GamePiece for the AI moves. This led to the interesting need to artificially slow the AI, so that it didn’t over take the animation. In the case where multiple captures in a row are possible, I currently have the game wait for the animation duration before asking the AI for the next move. The other interesting thing is that I stopped using the positioners for the stacks of captured pieces because they feel so far behind in QtQuick 1. It’s the weird opacity/visible behavior and I was trying to use the positioner animations, both of those aspects have been improved in QtQuick 2 (and since QtQuick 2 is recent, that’s the behavior I tried to use).
There is one UI embellishment that I had in mind from the beginning. I always thought that the most valuable addition a computer version of checkers could give would be to highlight your available moves. Got a forced capture? It won’t be hard to find when there’s only one tile blinking right in front of you. This lead to a funny little logic design that worked out well. Instead of evaluating each move for legality and effect when the user attempts to move somewhere, all valid moves are computed and stored every turn. This makes the actual move part just picking an entry from a list and acting it out. The differing performance characteristics aren’t a problem here, I just found it interesting the odd data storage I had. Instead of the conventional board array, there’s just lists of pieces and valid moves – I almost felt like I was using LISP again! The programmer time characteristics were also a bit unusual, a bit longer to implement the basic game and then the ‘advanced’ feature of highlighting was a snap. Definitely a parable of the benefits (and costs) of designing the system before you start writing 😉 .
So there’s another random game implemented in a snap with QML. Now once Digia gets QtQuick 2 running on all mobile platforms, I’ll grind out a game a week and flood the markets :D!
An interesting coincidence, and the explanation for the poor title, Dinosaur Comics also did checkers this week. I almost restyled my checkers application to have a lava aesthetic, animated sprites of people who jump and kick, and a cheesy bubbling lava sound track (most of the assets I’d need are on opengameart.org). What stopped me? I thought of just how easy it would be to do all that in Qt 5 – and remembered I had written this one in Qt 4 🙁 . Remind me when Qt 5 is released and perhaps I’ll port it with the new theme. Until then I just don’t think it’s worth doing it without the fireball particles spewing randomly across the screen. In the meantime, the new maroon demo in Qt 5 already demonstrates how to use animated sprites, sound effects, and particle systems in an (underwater themed) game.
Edit May 2015: Gitorious has shut down, and I have updated the link and location above. The original location was https://gitorious.org/aalperts-microgames/checkers, for completness.