Lamer update this week as the jam is over and I don't feel as much pressure to grind content and feel more inclined to do polish.
lerp
and Quaternion
slerp
respectivelyLast week1 I talked about how I'd begin working on a fall demo for the Autumn Next Fest, in retrospect this isn't a good current baring because:
My ultimate goal and bet is that I can release a game by my next birthday in February, doing the next fest in that month may mean I don't hit that date exactly, but as I understand it, you've got to give Steam some lead time before you can release and there's a "lock-in" date after which you can't back out. I am content to flex the rules a TEENY BIT if it's in the best interests of a successful release, but if this turns into a "maybe in March", "Perhaps after summer", "maybe next year" kind of delay, I WILL be paying out double the amount as recompense.
Commitments aside, I think the feedback I'm getting from the jam is a little more important to address directly before I move forward toward a demo, essentially, I want to create a "best version" of the jam game and do a quick hip-check on if I'm healing an injured bird eager to fly or trying to give CPR to a dead fish. The key feedback I want to address is:
Thankfully multiple people echoed they liked the aesthetic so far and wanted to see more done with the system, which gives me some outside assurance that I am heading in a good direction artistically and mechanically. In fact, by word count, this is probably the most feedback I've gotten about a game ever! A good sign that I'm not locking lips with a dead carp.
Along with those items, There's a few things that I think need to be fixed as easy 'next steps' people might've not noticed because of more glaring flaws or held back because "jam game I understand":
I believe most of this is doable by Lent's end and I want to push myself to see if I can turn up the heat a little now that I'm doing consistent work.
It is to both my great pleasure and mild amusement that I'm now using Quad Meshes(effectively planes)2 instead of Sprite3D3, Sprite3D is dead, long live Quad Mesh.
Switching implementations for the enemies for the third time feels a little funky, but Sprite3D is a little limiting in that they're not as easy to play with when it comes to shaders. For example, I can't punch a hole in a Sprite3D, modify the albedo(only add to it), or warp it around like silly string. Having an actual mesh opens up all those things I wanted to do, while casting shadows and being shaded, the only disadvantage is now I have to manage an enemy shader…
instance uniform int texture_index;
uniform sampler2D texture_weeper : source_color,filter_linear_mipmap,repeat_enable;
uniform sampler2D texture_shade : source_color,filter_linear_mipmap,repeat_enable;
uniform sampler2D texture_spike : source_color,filter_linear_mipmap,repeat_enable;
//....
switch(texture_index) {
case 0:
albedo_tex = texture(texture_weeper,base_uv);
break;
case 1:
albedo_tex = texture(texture_shade,base_uv);
break;
case 2:
albedo_tex = texture(texture_spike,base_uv);
break;
}
That's right, I have a shader with every enemy texture in it, which chooses what enemy to display based on a per-instance index. It feels janky, however if my understanding of uniform
is correct, this isn't duplicating the enemy texture for each shader, so there's no performance cost, it's just slightly less ergonomic in code that I can probably generate with Sed in 10 minutes? Nice trade-off. There's also multi-frame enemies I might want to add, like a slot machine, which can probably be achieved by just adding another "repr" element that'll handle it or maybe swtich to using texture2DArray
4 like some sort of sprite sheet.
The other ergonomic bit I have to do is create a mesh resource5 that I duplicate per instance so that I can scale the model to the aspect ratio of the enemy texture in question and not have that reflect across every instance. Godot's idea of scene uniqueness is saving me in other areas I'm sure…
All this buys me hole punching in enemies, and Lord, I love punching holes.
One of Godot's "killer apps" is the Animation Player, since it lets you quickly create animations to interpolate properties like movement, color modulation, rotation, and scale as well as call methods at pin-point times. I also have a bone to pick with it because every project I have at least one episode where some animation freaks out and I realize that the RESET
track has a funky value that puts the enemy way off the screen by default.6
See, Godot animations can be composed onto each other so while one animation is throwing something across a screen, another could be interpolating a shader uniform to cause it to glow hot like fire. The issue becomes managing that behavior, that state…
Nah, state machines are for losers, we're gonna use a behavior tree instead:
Godot's real killer app is actually the AnimationTree
which is sort of a behavior tree that makes it very easy to manage what animations are played based on parameters you set in code7. Here you can see that for my enemy reprs, they have the OneShot
called Arrive
, which when trigger will play the arrival
animation and then go back to the rest of the tree. Following things downward, there's the death animation, the addition of the targeting animation to the idle one, the frozen animation, and so on.
Up till this point I've kept most things in my own node that I've called Animation Player
(for maximum confusion) which just uses tweens to move enemies around and manage things. With this implementation, I can safely start writing functions like freeze
and unfreeze
on the enemy reprs that the animation player can call to manage the state of enemies as their status changes or manage more complex damage flourishes like putting holes into your least favorite shade and then he explodes. Part of me wants to keep things rigidly in code with the animation player, so I may hold off on going COMPLETELY tree mode, although it's definitely a useful tool in my arsenal.
One thing I remember games like SMT 4 and Persona doing very well is "pace" in battles. The music bangs, the SFX is punchy, the effects are vivid, and everything just feels fast and flows well. I see the path to this end and it's fraught with much work of making the perfect UI with the best animations, so I'm gonna go off to art school and see you all lat-
Oh hey, the camera zooms in on enemies and show a stat line when you target them for a spell! That title looks not centered and the things a little large though.
I think the key to "getting to Persona"8 isn't agonizing over making things 100% perfect from the get go, but implementing something, seeing where it royally fails, and continuously iterating on the thing until it meets expectations. I might come back to this 'consider enemy' stat window a hundred times over the course of development and I think that's okay, even ideal if it becomes the anchor for the whole game. And you may notice something else…
See, the game is totally different now because I moved Sigils from one corner to the other! Until you realized that, in the past implementation, the player had to DRAG THE SIGILS ACROSS THE LONGEST EXTENT OF THE SCREEN. This is why the best UI/UX requires iteration, sometimes you'll be soaking in a tub and say: "Dang, seems like the player's gotta do a lot of work to cast spells in my spell casting game…".
This week was spent putting to bed a lot of headaches I was having with enemy reprs, shaders, animation, and a bunch of other things that aren't core game play. Maybe it's speak to my deeper psychological need for clarifying systems or the desires to have my artistic tools in place so I can grind those things out while developing the more technical card-gamey side OR maybe I've just mis-prioritized and should immediately start busting out content OR maybe I'm psychologically avoiding jam feedback?
It's all a game of personal humming and hawing I think. There's more work I've been doing in the background I wont speak of at the moment, but once I'm over jam feedback, It'll probably accelerate me in the next few months.
See you next week!
1 https://soulreviews.net/blog/Monastic-Inversion-DevLog-2
2 https://docs.godotengine.org/en/stable/classes/class_quadmesh.html
3 https://docs.godotengine.org/en/stable/classes/class_sprite3d.html
4 https://docs.godotengine.org/en/stable/classes/class_texture2darray.html
5 https://docs.godotengine.org/en/stable/classes/class_mesh.html
6 If something funky ever happens with the AnimationPlayer, check the RESET
track.
7 Because parameters sort of peculate upward here to create an output I am to much of a coward to call it a 'real' behavior tree, but nerds and their patterns are meant to be bullied for rigid insistence.
8 Play on Francis Fukuyama' phrase "Getting to Denmark"