Shaders, Color Palettes, and Unit Animations

Flintlock

Emperor
Joined
Sep 25, 2004
Messages
1,041
Recently I've been playing around with shaders. I implemented a little palettized renderer, i.e. a shader that takes the palette and color indices and looks up the appropriate color for each pixel in the fragment stage. Right now it's being used to draw units_32.pcx as a test. I also enabled it to recolor the sprites according to a civColor parameter, right now the recoloring is pretty primitive since I don't know exactly how the original game does it (does anyone?) but it looks okay. Take a look on the ExperimentsWithMapView2 branch.

I found shaders in Godot easy to use, except for one problem. As far as I can tell, integer texture samplers are unusable. Even though Godot will let you declare (u/i)sampler2D variables in the shaders and attach textures to them, there's no way to create a compatible texture. Sampling from an incompatible texture is undefined behavior and in practice, at least on my system, returned a garbage result. References:
So instead of reading color indices as integers 0 - 255, the shader reads the indices as floats 0.0 - 1.0 and converts them. I'd rather not have to do this but it works alright.

Some miscellaneous thoughts:
  • Looking up colors through a palette means it's necessary to turn off texture filtering. When rendering at 1.0 scale this isn't a problem, in fact it's an advantage since it prevents filtering from blurring the sprites as they're drawn, but at other scales it looks worse. It's probably feasible to implement filtering manually in the shader but probably not worth the effort compared to de-palettizing the textures as they're loaded.
  • I mentioned wanting to draw the terrain with a shader attached to a mesh. I was thinking the mesh would be a large square grid holding all tiles in view, since then I've found about MultiMeshInstance2D, which I expect would be a better tool for the job. Instead of having one large mesh it does instanced rendering of many small meshes. Though I wonder if it would produce seams.
  • One advantage of doing palettized rendering might be reduced load times since it means PCX files would require less processing and there's less data to move around overall.

I'm tempted to charge ahead and start working on unit animation but I figured I'd create this thread first to see if you guys have any thoughts on this stuff. We never did decide exactly how animations would be implemented. Puppeteer's initial work builds off of Godot's built-in features, but I'd rather do it manually, i.e. put all the frames in a large texture and have the drawing functions pick an appropriate slice. I want to do this since it gives us fine-grained control over the animations, means MapView can remain independent of the game state, and I'm just the kind of person who likes doing everything myself. Animations need their state stored somewhere, of course, and the place for that is the engine. That makes sense since changes to the game state will often need to wait for animations to play out, for example imagine a unit attacking into a city, winning, and capturing it. The engine might know the outcome of the battle as soon as it begins but it can't process the city capture until the battle animations are finished.

So next I'll either start work on unit animations or rework terrain rendering to use MultiMeshInstance2D. Once the map is drawn with instanced rendering, it should be fast enough that we can redraw everything from scratch every frame (like a typical modern video game) and then would no longer need to call mapView.onVisibleAreaChanged whenever anything in the game world changes. Calling that function is not a problem right now but it will become one as we start to implement real gameplay.
 
Right now it's being used to draw units_32.pcx as a test. I also enabled it to recolor the sprites according to a civColor parameter, right now the recoloring is pretty primitive since I don't know exactly how the original game does it (does anyone?) but it looks okay.

I actually do know how the original game does it, thanks to @Vuldacon having figured it out before me and sharing the knowledge. A few references:

- The issue I had for my editor adding support for this is here: https://todo.sr.ht/~adj/civ3_cross_platform_editor/140 On August 26, 2018, I wrote a comment that is relevant, describing the basic process, how it connects with the ntp##.pcx files, and linking to documentation that Vuldacon attached to the forum and which I learned the process from (I am sure there is a thread somewhere related to that as well, but I just linked to the .zip he had containing descriptions and sample files).
- Direct link to Vuldacon's documentation, copied from the above: https://forums.civfanatics.com/attachments/civ_specific_units_32_pcx_files-zip.54769/
- Link to the Java code I wrote implementing this feature: https://hg.sr.ht/~adj/civ3_cross_pl...tor/imageSupport/Units32Supplier.java?rev=tip
getUnit32Image(int iconIndex, int colorIndex) is the entry point, and setCivSpecificColors does the civ-color-applying.

I am not sure if it uses the same general procedure for FLCs, although it might.

Take a look on the ExperimentsWithMapView2 branch.

I found shaders in Godot easy to use, except for one problem. As far as I can tell, integer texture samplers are unusable. Even though Godot will let you declare (u/i)sampler2D variables in the shaders and attach textures to them, there's no way to create a compatible texture. Sampling from an incompatible texture is undefined behavior and in practice, at least on my system, returned a garbage result. References:
So instead of reading color indices as integers 0 - 255, the shader reads the indices as floats 0.0 - 1.0 and converts them. I'd rather not have to do this but it works alright.

Some miscellaneous thoughts:
  • Looking up colors through a palette means it's necessary to turn off texture filtering. When rendering at 1.0 scale this isn't a problem, in fact it's an advantage since it prevents filtering from blurring the sprites as they're drawn, but at other scales it looks worse. It's probably feasible to implement filtering manually in the shader but probably not worth the effort compared to de-palettizing the textures as they're loaded.
  • I mentioned wanting to draw the terrain with a shader attached to a mesh. I was thinking the mesh would be a large square grid holding all tiles in view, since then I've found about MultiMeshInstance2D, which I expect would be a better tool for the job. Instead of having one large mesh it does instanced rendering of many small meshes. Though I wonder if it would produce seams.
  • One advantage of doing palettized rendering might be reduced load times since it means PCX files would require less processing and there's less data to move around overall.

I'm tempted to charge ahead and start working on unit animation but I figured I'd create this thread first to see if you guys have any thoughts on this stuff. We never did decide exactly how animations would be implemented. Puppeteer's initial work builds off of Godot's built-in features, but I'd rather do it manually, i.e. put all the frames in a large texture and have the drawing functions pick an appropriate slice. I want to do this since it gives us fine-grained control over the animations, means MapView can remain independent of the game state, and I'm just the kind of person who likes doing everything myself. Animations need their state stored somewhere, of course, and the place for that is the engine. That makes sense since changes to the game state will often need to wait for animations to play out, for example imagine a unit attacking into a city, winning, and capturing it. The engine might know the outcome of the battle as soon as it begins but it can't process the city capture until the battle animations are finished.

So next I'll either start work on unit animations or rework terrain rendering to use MultiMeshInstance2D. Once the map is drawn with instanced rendering, it should be fast enough that we can redraw everything from scratch every frame (like a typical modern video game) and then would no longer need to call mapView.onVisibleAreaChanged whenever anything in the game world changes. Calling that function is not a problem right now but it will become one as we start to implement real gameplay.

By and large, this is getting too deep into proper graphics terminology and techniques for me to be able to follow effectively, much less make suggestions. E.g. I have been looking up what a "shader" is in a 2D context for the past 20 minutes, since my knowledge of them is pretty much limited to "more of them on a graphics card = faster". Is my civ color code a CPU-powered shader? Maybe, maybe not. Is the image-blurring filter I wrote in CUDA for my computer architecture class in college a shader? Sounds like it could be, but I'm not sure. We didn't have a computer graphics course when I was there, and all the graphics work I have done since then (mostly the map tab in my editor) has been without any knowledge of how things are supposed to be done. Well, maybe a tiny bit from a Java game programming book written around 2005, but nothing that would count by today's standards.

So... sounds good? Maybe @Puppeteer or @WildWeazel are more qualified to provide feedback. If not, this might be an area where knowledge sharing may be helpful. The code looks decently well commented, the problem is lack of background knowledge with the fundamental graphical terminology.
 
It's been at least a few months since I did the prototype unit animation. I remember wanting to use shaders for civ color in particular but never quite got there, but I was using Godot's AnimatedSprite and ImageTexture. You're right that the animation control would be a challenge...I figured we'd have to catch it by time passed to perform the exact number of animations we want.

Side note: Each civ actually has two colors, and the built-in units are colored to take advantage of that...it's pretty cool when you see it. There are several shades of each color in the palette. But for some reason they seemed to go with monocolor civs.

My unit draw-er takes an int which references the ntp files, so the code should be doing it right as far as civ color.

I like the idea of using the shader to draw the image. Having gone with AnimatedSprite that didn't seem to be an option for me, but it sounds like an intriguing angle if you're rolling your own. It would make the civ color issue trivial to deal with without duplicating image data unnecessarily.

I'm not sure if this helps as it's been a while since I touched shader code and I know it's a different beast entirely, but Godot's Color does have an int constructor. It's a single int, but I only just realized in the past few days that it stuffs the RGBA bytes into one 32-bit int, so red<<24 | green<<16 | blue<<8 | alpha should have Godot make the float color for you, but I don't recall if the shader is able to use that directly.

The downside of the shader is that because it's float you can't just say if value is 180 then use this other color; you have to do a greater-than/less-than comparison, if I recall correctly. On the other hand, 3.4 has some new shader features I haven't touched or looked into.

Sounds like fun, go for it.

Here is where I was playing with the shader in 2018 when I was messing around with GDScript and PopHeads. I talk about the shader challenges, and there is some code. One Turn Deserves Another III

In that same thread, this post and the one after have some videos of some tests, and there is a link to a repo I made based on that tinkering, and here's the shader code https://github.com/midnightfreddie/civ-viewer/blob/main/civcolor.shader . It's a little different, but almost certainly not inspired.
 
I actually do know how the original game does it, thanks to @Vuldacon having figured it out before me and sharing the knowledge. A few references:
Great, I was hoping someone else had already figured this out. Piping another palette into the shader is pretty easy. Ultimately I'd like to make do without the civ color palettes so that we can create civs with any arbitrary RGB color value instead of being limited to 32 fixed colors. After all we want to do away with the 32 civ limit. I'm not sure yet what's the best way to do this, after eyeballing the NTP files it looks like we could approximate all the colored ones by sampling from the white one (00) then multiplying by the civ color.
Side note: Each civ actually has two colors, and the built-in units are colored to take advantage of that...it's pretty cool when you see it. There are several shades of each color in the palette. But for some reason they seemed to go with monocolor civs.
Interesting about the two colors, where is the second one stored? Is that what the last row in the NTP palettes is for? There's another reason to move beyond those NTP files, we could give the original civs secondary colors.



By the way, do any of you know what the OTP files are for?
 
Flintlock... What are you referring to with "OTP" files?
I generally do not use abbreviations unless they are extremely obvious to everyone... that may be just me trying to be specific to communicate and avoid confusions :)
 
Flintlock... What are you referring to with "OTP" files?

Not an abbreviation...by him, at least.

Spoiler ntp and otp filenames :

ntp00.pcx
ntp01.pcx
ntp02.pcx
ntp03.pcx
ntp04.pcx
ntp05.pcx
ntp06.pcx
ntp07.pcx
ntp08.pcx
ntp09.pcx
ntp10.pcx
ntp11.pcx
ntp12.pcx
ntp13.pcx
ntp14.pcx
ntp15.pcx
ntp16.pcx
ntp17.pcx
ntp18.pcx
ntp19.pcx
ntp20.pcx
ntp21.pcx
ntp22.pcx
ntp23.pcx
ntp24.pcx
ntp25.pcx
ntp26.pcx
ntp27.pcx
ntp28.pcx
ntp29.pcx
ntp30.pcx
ntp31.pcx
otp00.pcx
otp01.pcx
otp02.pcx
otp03.pcx
otp04.pcx
otp05.pcx
otp06.pcx
otp07.pcx
otp08.pcx
otp09.pcx
otp10.pcx
otp11.pcx
otp12.pcx
otp13.pcx
otp14.pcx
otp15.pcx
otp16.pcx


Uh, I'm not sure what otp are, and come to think of it I don't know exactly how the second color is implemented; just that it exists. It's probably also a pointer to another NTP file.

Or maybe I misunderstood something way back when and someone was playing with the NTP palette to make two colors, but my brain currently holds to the belief there are two colors you can specify per civ. And I just checked, and there are two color settings in the Conquests editor, and actually some of the build-in units do have differing alternate but closely-hued colors.

I'm not sure yet what's the best way to do this

My vague thought was to have a single base color (per civ color) and then make a gradient on shade, brightness, multiplier or something...maybe mod-configurable. And of course my pie-in-the-sky civ color idea is to expose shader functionality to the mods. Rainbow civ color or fire pattern civ color, shimmering colors...whatever they can come up with. But that's a very high level idea, and I haven't begun to mentally construct an implementation of such.

Edit: Quintillus asked a while back about shaders and 2d. I'm still a huge shader noob, but if I recall Godot has two types of shaders: one is the vector/polygon shader for rendering textures like you recall, and the other is a pixel shader that just looks at the pixel and can make alterations based on that. So yeah using it to turn a palette and index into a 32-bit image seems like a thing it should be able to do, but the shader is more of an OpenGL thing and not a Godot-native thing from what I understand, so it's pretty much yet another language and environment you have to figure out, outside of Godot's thing.
 
Flintlock... What are you referring to with "OTP" files?
Like Puppeteer said, I was referring to the "otp##.pcx" files in Art/Units/Paletttes. I don't know what "OTP" stands for either. If no one knows what these files are for, I could have a look at the decompiled code, hopefully it's something obvious. As an aside, I spent a couple of days looking into how the engine handles .amb files. Unfortunately it's far from obvious how those work and the fact that most of the sound functionality is in a separate DLL only makes things worse.
Edit: Quintillus asked a while back about shaders and 2d.
Oh yeah, he said he was reading up on it and I just assumed he got his answer. Shaders are basically just small programs that run on the GPU to do something related to graphics. You can run shaders on the CPU but routines written for the CPU generally aren't called shaders. And GPU programs unrelated to graphics generally aren't called shaders but instead "kernels". The two kinds of shaders, vertex and fragment, aren't specific to Godot but are common to all graphics APIs (in DirectX fragment shaders are called pixel shaders). There are other shader stages besides those two like geometry and tessellation shaders, I doubt any of those would be useful for C7. Since shaders are these awkward little programs-in-a-program they're both easy and hard to make moddable. They're easy since we could have mods submit a string to be dropped into some shader's source code, so that's easy for us, but writing shaders can be tricky and there's no way we could make it easier for modders b/c the source is a kind of black box.
 
I am not yet properly informed about the OTP files (colors?) either, only NTP (see below for some clarifications).

Thanks for the shader info, Puppeteer and Flintlock! So my understanding it we would have pixel/fragment shaders, since we're working with 2D pixels, and they can apply a bunch of effects to pixels, and run on the GPU. I was too tired to really solidify an understanding when I posted the other night, and it still seems a little high level compared to what I typically am used to... but my understanding of Godot seemed high level until I started writing Godot code, too.

Great, I was hoping someone else had already figured this out. Piping another palette into the shader is pretty easy. Ultimately I'd like to make do without the civ color palettes so that we can create civs with any arbitrary RGB color value instead of being limited to 32 fixed colors. After all we want to do away with the 32 civ limit. I'm not sure yet what's the best way to do this, after eyeballing the NTP files it looks like we could approximate all the colored ones by sampling from the white one (00) then multiplying by the civ color.

Interesting about the two colors, where is the second one stored? Is that what the last row in the NTP palettes is for? There's another reason to move beyond those NTP files, we could give the original civs secondary colors.

Or maybe I misunderstood something way back when and someone was playing with the NTP palette to make two colors, but my brain currently holds to the belief there are two colors you can specify per civ. And I just checked, and there are two color settings in the Conquests editor, and actually some of the build-in units do have differing alternate but closely-hued colors.

My vague thought was to have a single base color (per civ color) and then make a gradient on shade, brightness, multiplier or something...maybe mod-configurable. And of course my pie-in-the-sky civ color idea is to expose shader functionality to the mods. Rainbow civ color or fire pattern civ color, shimmering colors...whatever they can come up with. But that's a very high level idea, and I haven't begun to mentally construct an implementation of such.

Not quite correct in either case. There are a few areas that can cause confusion, particularly over the "two colors you can specify for civ." I think many modders would agree that there are two such colors, and they would be referring to the "unique color" and "default color" that you can assign to a civilization. These are in fact references to ntp##.pcx files, and both the Firaxis and Quintillus editors use a dropdown of colors to refer to them.

However, the ntp##.pcx files actually contain seventy (70) colors. Rhye pioneered a technique to modify the ntp##.pcx files to have two-tone colors for one ntp##.pcx file - and thus for one civilization, in-game. See this post for a dark blue/orange combo color, based on one ntp##.pcx file, and scroll up and down in that thread for some other screenshots he posted of various combinations he created by modifying ntp##.pcx files. So this is another case where you can have two colors for a civilization - but they're all specified within a single ntp##.pcx file.

To fully support such mods, we'd have to have full NTP palette support in our "legacy/compatibility" mode.

In practice, with the default Firaxis set, you probably could approximate the 70 colors in each file by simply changing the hue (in an HSB color model) of the first ntp file. But with modded palettes, such as Rhye's, that isn't a safe assumption. In my editor, I tried to strike a middle ground by allowing modders to adjust the hue/saturation/balance for all colors within an ntp##.pcx file at once, but still allowing them to modify individual colors if they want e.g. a significantly different color for the Histograph than for the other civ colors specified in that file.

It's also worth noting that while I've read up on the palettes and tried to make it easier to take advantage of their power, I am not a unit creator. Rhye showed that Firaxis units can benefit from a complex NTP palette, but I do not know how many community units use all/most of the available shades of the civ color within the palette, and do not know the nitty-gritty details of how to design a unit to take maximal advantage of the colors in an NTP palette.

-----

Edit: Moar Links. Basically a summary of who's who in the history of palette modding, along with links (some dead, some living) to prior art in this area. The living links are likely worth trying if you want to see the results of the community's creativity along these lines in action.

Spoiler Comprehensive summary of ntp##.pcx modding over the years :

Link to the post with Rhye's colors: https://forums.civfanatics.com/threads/new-team-colours.85016/#post-1755358

Goldflash created a Light Red custom civ color which you can download at https://forums.civfanatics.com/threads/new-team-colours.85016/page-8#post-1920174 (the link doesn't work in Chrome/Chromium based browsers, but does in Firefox and Internet Explorer).

Sloth_MD created a utility for modding NTP palettes and posted download links at http://www.geocities.com/diplosloth/classic_blue.html . Unfortunately, GeoCities went down in 2009, and the Internet Archive didn't archive that page. I haven't found another link for it yet. Pounder posted a screenshot of it (which no longer works), and thus must have downloaded it, but somehow it's already been three years since Pounder last posted at CFC.

CyberTyrant (formerly known as low) created some custom palettes, and appears to have posted them, but the link rotted. Rhye and the CFC user Reagan posted replies indicating they downloaded CyberTyrant's colors. CyberTyrant was working on an updated version using Sloth_MD's program to make improvements when a hard disk crash caused it all to be lost.

MaisseArsouye made a custom color set at https://forums.civfanatics.com/threads/new-civ-colours.97681/, and the links still work (download only works if you don't use Chrome). Some civs use one color, some two, and France even has a tricolor pattern.

Optional posted a custom color set at https://forums.civfanatics.com/threads/civ-colours-alternative.303007/, but the links are dead. However, at least two active users, namely @Ozymandias and Virote_Considon, posted indicating they downloaded it, and if I am reading the replies correctly, Blue Monkey appears to have download it as well (though possibly just viewed the screenshots). I have rescued the screenshots (but not the download) from the darkest depths of Photobucket, and have attached the archive to this post (unzip, view "Civ Colours Alternative.html" in your favorite browser).

Wolfshade reported in this post being less than thrilled with the results of two-tone palettes with custom units. He also unsuccessfully asked if anyone had a copy of Sloth_MD's program shortly before that.

MeteorPunch (who was active in C&C in early 2021) posted custom colors in this post. The post now says the link is removed with a new version coming soon, as of February 19, 2005. A follow-up thread was posted on March 2, 2005, seeking testers, and promising a release in a few days. But although MeteorPunch was prolific in the spring and summer of 2005, no thread with a release of the custom colors was made prior to the release of Civ4 (I have not checked all the threads MeterorPunch created after that time), unless I missed it in my check.
 

Attachments

  • Civ Colors Alternative.zip
    316.2 KB · Views: 32
Last edited:
Comprehensive summary of ntp##.pcx modding over the years
Thanks for the info and for digging all this up. The lazy corner-cutter inside me says we could approximate custom palettes by doing the inverse of tinting the white palette with a civ color, i.e., divide the colored pixels by the white palette equivalents then average the results to get a civ color. That should work as long as the custom palettes only have one civ color. Are the two-color palettes widely used enough to be worth bothering with? In that first link Rhye himself points out that some units don't look good with a two-color palette. Not that it would be all that tough to pass all the NTP palettes into the shader along with an instruction to use one of two methods for tinting the units, either multiplying by the white palette or sampling one of the others.



Meanwhile I've been quietly working on animating units with the shader I wrote before. I chose to work on units instead of the terrain since the only things wrong with the terrain drawing are the seams between the base layer tiles and the less than optimal performance, neither of which are urgent issues. I have unit animations working except for two things: (1) The FLC file names aren't read from the unit INIs, right now it just looks for the standard names. For units with nonstandard names (settler and worker), I just carved out some ugly little special cases. (2) The only animated actions are fortify and run. I've been putting off these matters since they're related to the interface between the engine and UI, and the separation of responsibilities between the two, which I'm still unsure about.

The way it works is that the UI layer asks the engine every frame for the animation state of every unit it intends to draw. The engine returns one of these:
Code:
public struct ActiveAnimation {
    public string name;
    public TileDirection direction;
    public float progress; // Varies 0 to 1
    public float offsetX, offsetY; // Offset is in grid cells from the unit's location
}
For now "name" is the name of the FLC file containing the animation. That should change since the engine shouldn't have to worry about FLC files. The easy thing to do would be to make it an enum but that might make it difficult for modders to add unit actions.

This means the engine is in total control of the animation process. Like I mentioned in a previous post, this makes sense since in some circumstances the engine will need to wait for animations to complete before continuing. The downside is that it means the engine needs to know about real-world time. Eventually we'll want to have the engine running on its own thread, that way it can have its own timer and waiting for an animation to complete is as simple as sleeping the thread. For now it runs on the UI thread and needs to be told the time periodically. moveUnit and fortifyUnit both need to take in time stamps so they can store when the actions started, and then the function to retrieve the animation state also takes a time stamp so it can determine the progress of the animation. One of the problems with this temporary setup is that there's no way for the engine to wait for an animation unless it's receiving constant ticks from the UI.

Also I replaced the yellow circle highlighting the selected unit with the animated cursor from the original game. Unfortunately it can't be drawn with the normal unit shader since it has its own special rules for drawing that I still haven't figured out entirely, though I've made some progress and the cursor looks OK as long as you don't look too closely. Does anyone know the exact rules for drawing this thing?
 
the only things wrong with the terrain drawing are the seams between the base layer tiles

I still think we're going to stumble on a filter flag setting somewhere sooner or later that will fix this (been there, done that), but yeah it just hasn't been worth chasing down in the present.

This means the engine is in total control of the animation process. Like I mentioned in a previous post, this makes sense since in some circumstances the engine will need to wait for animations to complete before continuing.

I'm not fond of that pattern...sounds like a use case for a closure, async wait/promise, or observable pattern, but I have yet to do any of these in C#. (Edit: For single player games. At some point we'll have to ask ourselves about how multiplayer games will work with timing issues like this. I don't like the idea of a server engine waiting on client animation, but I really haven't given any thought to how the game flow works in multiplayer as I'm not a 4x multiplayer person.)

Everything else looks great, though! If I start messing with animated terrain I'll make sure not to step on your code. I'll either use a different branch or have an entirely separate test repo to compare animation strategies against each other. It's all just shoveling bits into Godot one way or another.
 
Those unit animations look pretty cool! It's definitely fun sending my Warrior out walking across water. The only imperfection I've spotted so far (aside from the cursor, which, alas, I don't know anything specific about) is that if I send the Chariot off one way, and the Warrior off another, then once they get far enough apart that they aren't in the same part of the map (while zoomed in at Civ3 levels), after I move the Warrior, it immediately focuses on the Chariot, rather than waiting for the animation to end, as in Civ.

I'll have to look at the code a bit more closely, but from what is written here, I'm also not a fan of the engine keeping track of the animation progress, and the UI asking it how far along it is every frame. The engine will need to know if an animation is finished (e.g. when showing AI moves to the human), but I wonder if it would make more sense to use an event based system where the engine says, "play the move animation for this unit going here", and then pauses the AI turn, and the front-end then sends an "okay, animation finished, continue along!" event when the animation finishes? That would decrease the checkpoints to the beginning and the end.

Although maybe you're already going in this direction, since as you said eventually the engine should be on its own thread, and can simply sleep itself for the duration of the animation, which it should know/be able to calculate in advance (maybe? will it have enough FLC information for that?).

Not yet accounting for any multiplayer latency. Which is its own can of worms. Its been a few years since I played Civ3 at LAN parties with friends, but IIRC different people can have different settings for whether units are animated, and Civ3 respects those settings? That may be an area where it would behoove us to not blindly follow Civ3's lead, and where studying how it works in various 4X games might lead to some better options. IIRC Civ4 was a bit more sophisticated in this regard, but it's been even longer since the summer of 2007 when I played a lot of Civ4 Vanilla multiplayer online. Ah, the days of seeing the sun rise after playing 150 too many turns, and voting for another human in the UN Diplomatic Victory vote, because they'd been a good ally and you were too tired to try to contest any other victory condition yourself.

Overall I'm inclined to go for the incremental iron-out-progress route rather than try to account for all the variants including MP. And I'm excited to see the units moving around with real animation - it's definitely a step forward in the real-game-feel area.
 
Eventually we'll need some way to pass signals between the UI and engine so the engine can trigger things like sound effects and map messages (pollution strikes, city starts rioting, etc), and the UI can pass player actions to the engine. Once that mechanism is in place we can use it for animations as well.
I still think we're going to stumble on a filter flag setting somewhere sooner or later that will fix this
I've looked over the Godot docs and didn't see anything, though it's possible I just missed it. I was surprised how few knobs and switches Godot offers for manipulating texture filtering.
Although maybe you're already going in this direction, since as you said eventually the engine should be on its own thread, and can simply sleep itself for the duration of the animation, which it should know/be able to calculate in advance (maybe? will it have enough FLC information for that?).
I'm guessing the animation durations depend on the INI and FLC files, and ideally the engine wouldn't have to know about those, but I haven't yet looked into how the durations are determined. For now I've set a hard coded value of 0.5 seconds for the unit animations. I thought the "Timing" values in the animation INI files determined the durations, but in Cursor.INI they're set to 0.5 and the cursor animation clearly does not have a duration of 0.5 seconds. Instead it's something like 2.5 seconds, which I guessed by eye and hard coded in. I also don't know how the "Speed" values factor in, they're 225 for the units (or at least the warrior) and 175 for the cursor. The missing variable here is probably the number of frames in the animation.
 
I'm going to submit a PR for my work so far on unit animations since it's reached a sort of local maximum. I addressed the exceptions I mentioned last week, so now all animation types can be played and the FLC file names are loaded automatically from the unit's INI file. What's still left to do is figure out how animations are going to fit in with everything else. For now I pulled the animation state information out of the MapUnit objects and put it in its own module called AnimationTracker. Code that triggers an animation can wait for it to complete using a callback. So for example the code to found a city that used to look like this:
Code:
else if (buttonName.Equals("buildCity"))
{
    PopupOverlay popupOverlay = GetNode<PopupOverlay>("CanvasLayer/PopupOverlay");
    popupOverlay.ShowPopup("buildCity", PopupOverlay.PopupCategory.Advisor);
}
Now looks like this:
Code:
else if (buttonName.Equals("buildCity"))
{
    EngineStorage.animTracker.startAnimation(
        OS.GetTicksMsec(),
        CurrentlySelectedUnit.guid,
        MapUnit.AnimatedAction.BUILD,
        (unitGUID, action) => {
            PopupOverlay popupOverlay = GetNode<PopupOverlay>("CanvasLayer/PopupOverlay");
            popupOverlay.ShowPopup("buildCity", PopupOverlay.PopupCategory.Advisor);
            return false;
        });
}
It's the same code but put in a lambda that gets run after the animation is complete. This is simple and straight forward but I doubt it will scale well. E.g. the callbacks get called from inside the AnimationTracker's update function which gets called from Game._Process, so if we want the engine to run on a separate thread (as I think we should) any callbacks it stores would get called from the UI thread. We could work around that but still it makes things messy. Similarly chaining multiple animations together would become a mess.

I think a better approach is to use async & await. In fact we could use async/await for all situations where the engine has to wait for something to happen elsewhere, like for the player to make a decision or (in multiplayer) for the server to OK an action. The task of processing a build city command from the local player could look something like this:
Code:
if game rules do not allow settler to found here:
    return
if in multiplayer game:
    await server's OK
    if server did not accept this action:
        return
await playing build animation on settler
if in multiplayer game:
    get next city name from list
else:
    await asking the player for a city name
    if player cancelled out of the popup:
        return
actually found the city: create it, despawn the settler, etc
Awaiting an animation fits in neatly with everything else.

I don't have a clear idea yet of how this would all work under the hood, the next step is to start tinkering with awaiting animations. Async/await are still new to me, I've heard good things about them but never had an opportunity to actually use them until now. I've been holding off on this tinkering since it requires reworking the interface between the engine and UI. For one thing the unit interactions (moveUnit, fortifyUnit, ...) will have to become async methods, or at least there will need to be async versions of them. Also the UI would need to present an interface to the engine that allows it to trigger popups and animations through async methods.

What do you guys think about using async/await? It potentially touches many different aspects of the code and IMO we should either use it everywhere or not at all in order to keep the codebase as conceptually simple as possible. I know @WildWeazel is working on an event system, could async/await work on top of that or would they conflict? Or is the event system unrelated, intended to solve a different problem?
 
Good to see the animation has reached a local maximum! That's definitely a key milestone.

Major caveat: I don't know what the good multi-threaded options are in C#. I tend to associate async/await with JavaScript, where I'm not a fan of it compared to promises. Promises have a nice syntax of invokeFunction(someParams).then(() => doSomeThings()). In JavaScript, I often see async/await bubbling most of the way up the call stack, since methods that call await must themselves be async methods. async/await is syntactic sugar in JavaScript, though; perhaps I just don't have a sweet tooth in JavaScript.

But I'm not sure if async/await has the same tradeoffs in C# as in JavaScript. If it's more like how you can .wait() on a thread in Java and do something once the thread's run method has finished, or like a promise in JavaScript, it would probably be a good option.

Those who are more experienced in C# should probably chime in on the options and relative merits of each before we decided how to proceed.

(and yes, my threading in Java is often low-level. I tried modernizing it in my editor, but the slowdown was noticeable - and it's fun to create lock-free multithreaded algorithms. Though I do use ParallelStream in places where that works well)
 
In JavaScript, I often see async/await bubbling most of the way up the call stack, since methods that call await must themselves be async methods.
It works the same way in C#. That is potentially a problem since we'll often want to avoid animations, like when the animating unit is under FoW or animations are turned off, but any method that potentially starts an animation would have to be made async. There is an overhead associated with calling an async method but how severe it is I don't know. The fact that the overhead is mentioned prominently in the docs makes me think it's pretty severe, but maybe I'm just being pessimistic.



Meanwhile I've been thinking some more about threading and am not so sure that it's worth it to split the engine off into its own thread. The only thing that's likely to slow down the engine thread is AI logic and that can easily be run in the background if we structure the AI accordingly. The AI logic could begin with an analysis phase that reads the game state but doesn't modify it, so could run in parallel with the UI, and then an action phase that carries out the AI player's planned actions. I doubt processing player actions, whether from the AI or another human player in MP, would be noticeable drag on performance, and if necessary it could easily be interleaved with UI work.
 
As just mentioned in the other thread I've steered away from thinking about events in the UI layer (such as animations and timing) because as I envision it, those will be Godot signals that trigger function calls down to the game layer. (That reminds me, I have an architectural diagram I need to digitize and share.) So "my" events will probably bubble up to Godot to make the UI respond, but not vice versa. So as far as async/await goes, I think they're unrelated systems.
 
Ehhhhh... if it's like JavaScript I'm definitely going to need to be convinced, probably via examples. I was just talking with a friend earlier this evening about how fun it was to be working in C# instead of JavaScript. And while there are definitely other benefits (static types, not having the tradeoffs of being designed in a week, etc.), async/await would make my top 5 of least favorite JavaScript features.

But at a higher level, I'm also thinking, do we want asynchronous behavior? And if so, do we want it everywhere? If CitySizeIncreased fires, we can safely and synchronously invoke any handlers registered to that event, and have simpler code. BuildingSold is another good example where I'm pretty sure it could be handler synchronously, even if a mod registered an additional handler on it.

Admittedly this is probably an area where I'm overly sensitive based on past pain points relating to async/await in JavaScript, and JavaScript in general. await also seems to make a lot more sense for cases where there's player interaction, e.g. Flintlock's example of awaiting the player naming a city.

Breaking my other thoughts into the Architecture thread since that seems more appropriate.
 
In the game layer, there are very specific things like calculating trade connections between turns that could be done with something like a thread pool for performance. Otherwise the game lends itself to linear execution. UI naturally relies more on async operations, but I don't yet know enough about Godot to say what that should look like beyond "signals".
 
Top Bottom