With this video, I'm happy to say that unit sprites, shadows, movement, path finding, rounds, turns and very basic AI are now all functioning within the engine. Some tweaking is needed, but it's basically all there.
While I had showcased a basic version of some of these features in a previous video what you're seeing here is light years ahead in terms of code and of course, three.js powered, so let me go through all the core points of what's on display here and tell you a bit about them.
Currently I'm using a Geomancer from Final Fantasy Tactics as my test sprite. I created a sprite sheet for the Geomancer's walking animation, which is basically 8 frames in total and made animation methods to handle the offsetting of this sheet along at specific intervals to create the walking animations you see. This also includes the 'back facing' frames, and a nifty bit of code that flips the sprite based on its vector in comparison to the camera position.
The sprite sheets are handled in a scalable manner, all the data is set within the unit's data object so swapping this sheet out for a custom made sprite is incredibly simple. The only requirement currently is that the sprite be isometric and have forward and back facing frames. Some tweaking may be required in terms of height, but that's quite trivial.
Another aspect to these sprites is the use of an amazing library for upscaling images - js-hqx which basically allows me to upscale the sprites at runtime if needed. This is a powerful feature for supporting a wide variety of displays.
I haven't bothered to add jumping frames to the sprite sheet, because this is all placeholder stuff for now, but again adding such things is easy and would just require the writing of a new animation method to slot the sprite sheet offsets in at the correct frames in the animation sequence.
Okay, I'm not going to lie, this was hard. The truth is that the spot shadows (as well as the sprites) are illusions trying to pull off tricks inside a 3D world. Because of that, you're always going to run into issues here and there where the two worlds collide. Luckily three.js has a sprite geometry and material type that makes handling them fairly easy, the shadows however are something different altogether.
I'm currently achieving the effect by using a circle geometry that is flattened to the surface normal of the tile the unit is currently on. There's a bit of trickery going on when jumping or falling to fake the fact that the shadow should stay on the ground but all in all I think the result is pretty good. I'm not entirely sure how other games have handled this feature, but if you want to support a wide variety of vertex possibilities for your tiles, it's not easy to get this looking right. Using the method I am currently sort of prohibits me from supporting tiles that have non planar top faces. It works, but some ugly clipping happens at times. I can fix this by bumping the shadow up the y axis a bit though, so that's a possibility for now.
In my opinion, the true solution to this problem would be to have a directional light pointed from above, covering the entire world and directed at a vector of y:-1. Each unit would have an invisible circle geometry attached to it that is always placed at the unit's waist. This geometry would cast shadows, and would therefore yield actual real world shadows that are responding to light correctly. However, I can't implement this solution just yet because firstly, three.js doesn't seem to support multiple directional lights in one scene and secondly I cannot select which light the geometries would cast shadows from, so they would cast shadows from every light source in the scene. Isometric worlds need directional light to give them dimension, so I can't really take the current one out, but if I decide to bake shadows in Blender then this solution is viable. I'm not far along enough in the pipeline to make that call yet though, so I'm leaving it for now. I'll come back to it when some actual level design or art assets have gone into production.
Movement & pathfinding
This is the fun part, making the little guys move! There are four basic movement types so far, walk, jump, fall and hop. Each step of a unit's path sets a movement type is defined by a set of parameters and then performed. I'm currently using Pathfinding.js for my A* algorithm, however I am considering dropping it as it currently does not support cost natively, which is a fairly fundamental feature of any A* algorithm. Each unit has a stride and leap score which determines how far they can move both horizontally and vertically.
Rounds & turns
Programming in a turn based system was really fun and exciting. It made this demo feel like a real game. Let me explain a bit about how rounds and turns work in the GoldScript rule set. Each unit has a speed score, and at the beginning of a round, all units are sorted according to this score. The round steps through each unit, initialising their turn which only ends after they choose the wait action. I plan to implement action points (AP) in my rule set, so each tile move will cost 1 AP, that’ll be the next feature added however, so it’s not shown in this video. All in all the whole process works quite smoothly now.
I'll admit it, AI scares me a bit. Especially for an RPG where the tactical combat needs to be challenging yet beatable. There are so many possibilities to think of, each unit will have a variety of skills to choose from, and defining which skills which unit will use and at what time is going to be a big chunk of work. Possibly I can funnel this through a path finding algorithm, but I need a process first. What you're seeing in this video is a very primitive implementation of that process. When the units are loaded into the level, I randomise their stride and leap scores, so each time I run the demo, each unit has a random score for each of these stats within a range. The movement choice and picking methods are then run each turn, passing a random choice from the results which the path finder deems as valid. Directional facing on when the wait action is selected to end a turn is also implemented, although it’s also randomised so they don’t always do it in this demo. Switching between player and AI controlled units is done automatically, if a unit has AI disabled then control passes to the player, simple as that. You can see this in the video when one of the sprites is controlled by me, the UI for the actions menu is 100% placeholder at the moment and is just something I did for testing. I’m still not completely sure on how I’m going to handle it. I have keyboard shortcuts already implemented (‘m’ for move, ‘w’ for wait etc.,) but I’m considering something like contextual radial menus for the GUI. Since the engine is being developed with PCs in mind first, I need something which is intuitive for this audience. FFT's UI would fail here, but I also would like to avoid having to use a HUD so it's a tricky one. I'll probably have to prototype a bunch of options before choosing one. I may even put an online demo up and ask people to help me decide.
Phew, that was a lot of work. It took me all of November pretty much to implement these features in my spare time. Special thanks go to @MarbleBucket and @Okonomiyaki3000 for their help with sprite directions, shadows and animations respectively!
Next up will be an implementation of an action points stat and factoring this in to movement choices.