Hi all,
When animating a sprite, I initially used this code:
c.Animate(1, 1);
c.LockView (3);
c.Animate(3, 1);
c.Animate(0, 1);
... but I found that the game was paused during the animation (due to eblock being enabled by default). I figured this would become an issue when I want to have multiple animations running simultaneously, so I updated the code like this:
c.Animate(1, 1, eOnce, eNoBlock);
c.LockView (3);
c.Animate(3, 1, eOnce, eNoBlock);
c.Animate(0, 1, eOnce, eNoBlock);
... which brings about a new issue: the animations now seem to be skipping through all of the frames instantly, as it jumps right from the starting frame to the ending frame with nothing perceptible in between. I slowed this down by inserting [Display ("blah"];] after each animation line, and that showed that each animation is indeed queing up, and then playing instantly without actually appearing to animate from the user's perspective.
Am I doing something wrong?
To clarify, do you animate one character or multiple characters? I'm asking this because you used same "c.Animate" everywhere.
Normally, if all animations run equal amount of time, then you may simply play last animation blocking.
If they have different length, then make a waiting loop:
while (c.Animating)
{
Wait(1); // let engine update and redraw
}
where you check the longest animation. Or you may check every character:
while (c.Animating || c2.Animating || c3.Animating)
^ this means that either of the characters still animating.
Quote from: Custerly on Mon 15/01/2024 17:25:20... which brings about a new issue: the animations now seem to be skipping through all of the frames instantly, as it jumps right from the starting frame to the ending frame with nothing perceptible in between.
Where did you put your code? To me it seems you may have put it in repeatedly execute (without any condition), which makes the animations start over again and again (normally 40 times per second), so they don't actually have a chance to play.
Quote from: Custerly on Mon 15/01/2024 17:25:20that showed that each animation is indeed queing up
They don't queue, each command runs once it gets to their time. If there's nothing blocking, they will run one after the other, in the same frame, so only the last one should play - or maybe not even that assuming you are properly locking and unlocking the view you want to animate.
If you want to queue animation in a way that is non-blocking, you will have to write your own system for it - I don't know if there is a module for this, if there's someone could suggest.
Thanks
@Crimson Wizard,
@Matti, &
@eri0o: Thanks very much for your input.
@Matti: The code I shared was from a function in the global script (not RepEx).
@Crimson Wizard: To clarify, it is one character animating (1 animation in 1 view, then switches to a 2nd view and runs 2 animations). It needs to be done sequentially obviously, so I guess the fact is that all animations need to run with eblocking enabled and I have to accept that the game will be paused during the animations, correct?
That being the case, if I want multiple animations running simultaneously, the way to achieve that is by having the first animation appearing in the script set to enoblock and the final animation in the script set to eblock, as you said. I understand that now.
I have a bonus issue that I'm wondering if you could weigh on on. When I revert to the original code with eblock enabled, and I run said animation chain via scripting triggered during a conversation (with my custom dialogue GUI open) it stutters noticably. When I run the same animation chain outside of dialogue via clicking on a hotspot, it runs butter smooth. After poking around a bit, I noticed that if I set all of the dialogue GUI buttons to invisible during the animations, it runs smoother (though still not as smooth as when the animations are triggered outside of the dialogue GUI). The game res is 4k, the sprite animating is 1024x1024, and those buttons all have needlessly large transparent PNGs as their background images (so I can have invisible buttons), but I can see no reason why those factors should be burdening the system so much as to produce noticable stuttering in a simple 2d animation. I am running this in a 3-year old low end Dell laptop that I'd think should be more than capable of handling it. Any ideas of things I may be doing wrong?
You should turn the buttons invisible instead of assigning transparent sprites to them.
As for the animation, you should be able to put all these frames into a single loop, that way you can have a single non-blocking animation.
It's also possible to run non-blocking commands in sequence but it requires a bunch of additional code (basically implementing a custom queue).
(Also, your game resolution is 4k? Seems excessive.)
4k resolution?... AGS currently runs 1920x1080 games more or less fine without too many simultaneous objects on screen, but they still get issues sometimes (we spent last year improving things there). But 4k sounds like an overkill. Frankly, AGS engine was simply never meant for this, it has a lot of old methods in it in way of it handles resources and graphics, that may not be suitable for this resolution.
There's also a question of how would this run for players whose monitors don't support a resolution that big, as pixel 2D games that AGS produces cannot reliably downscale the game without massive quality loss.
Quote from: Custerly on Mon 15/01/2024 19:27:32I run said animation chain via scripting triggered during a conversation (with my custom dialogue GUI open) it stutters noticably. When I run the same animation chain outside of dialogue via clicking on a hotspot, it runs butter smooth. After poking around a bit, I noticed that if I set all of the dialogue GUI buttons to invisible during the animations, it runs smoother (though still not as smooth as when the animations are triggered outside of the dialogue GUI)
When you say "custom dialogue GUI", do you mean that you script custom dialog options rendering, or something else? Are these actual buttons, or something that you draw using a DrawingSurface?
@Khris:
- To clarify, I do make the buttons invisible during animations (as I observed that reduces the stuttering while not toally eliminating it), but the reason for the transparent background is so that I can have dialogue options displayed to the player without a button graphic being seen. So obviously, turning the buttons invisible is not an option during conversations.
- Whether one loop or 100, the animations are not being displayed during runtime unless there is blocking. They just run through instantly and are inperceptible to the user unless there is blocking.
- Perhaps 4k is excessive, I just didn't previously see a reason not to go for the max resolution.
@Crimson Wizard:
- I see. I will then port everything over to 1080p as suggested. When doing so, is there a way that AGS can automate this transition, or do I need to manually resize all game assets, re-load them into the game, and re-place all sprites and redraw all hotspots?
PS. I have been developing this on a 1080p monitor with no perceptible degredation in quality from AGS downsizingh everything from 4k.
- It is a cutom dialogue GUI made from the ground up. You've helped me in the past with several roadblocks I've run into while building it. It has buttons for up to 9 dialogue options, it has a label displaying dialogue, it has labels showing numbers for each dialogue option visible, it has labels for each speaker in sequence, it has labels floating over buttons to get text wrapping on the buttons.
Here's what it looks like in action (please ignore the nonsense dialogue, it is purely for testing):
https://imgur.com/a/sLqtcYp
Bonus question: Can I have AGS automatically re-size a sprite during runtime? I want to have character portraits shrink to half size when they are not the current speaker during a conversation.
- EDIT: After some Googling, it looks like I want DynamicSprite.Resize right? Will I still be able to animate the new dynamic sprite using the same views as the original?
Resetting a button to displaying text should work by setting its .NormalGraphic to 0, but I haven't tested this.
That is weird, people use non-blocking animations all the time without issue. You do have a frame delay of 1 in your code, but that should still not run the entire animation in a single frame.
One reason would be your game graphics, which I don't think require 4k at all. You have a fairly lowres background, a portrait with visible pixels and even for the font, Full HD should easily suffice.
There is no reason to go for the max resolution unless you actually require that level of detail, especially on a "legacy" engine like AGS.
Quote from: Khris on Tue 16/01/2024 17:53:09Resetting a button to displaying text should work by setting its .NormalGraphic to 0, but I haven't tested this.
That is weird, people use non-blocking animations all the time without issue. You do have a frame delay of 1 in your code, but that should still not run the entire animation in a single frame.
One reason would be your game graphics, which I don't think require 4k at all. You have a fairly lowres background, a portrait with visible pixels and even for the font, Full HD should easily suffice.
There is no reason to go for the max resolution unless you actually require that level of detail, especially on a "legacy" engine like AGS.
@Khris: Above, eri0o said "If there's nothing blocking, they will run one after the other, in the same frame, so only the last one should play - or maybe not even that assuming you are properly locking and unlocking the view you want to animate." and that has been my experience when using animations without blocking. Are you sure it can work without blocking?
Quote from: Custerly on Tue 16/01/2024 17:31:30- Perhaps 4k is excessive, I just didn't previously see a reason not to go for the max resolution.
The logic of selecting game resolution for 2D games should be opposite: choose the lowest resolution that suits your requirements, as AGS itself can easily scale the game up to any monitor, and that should not cause any performance issues.
The reason to NOT go to max game and asset resolution is simple: the higher resolution, the
- more disk space the assets require
- the longer assets load and prepare for display.
- more RAM and GPU memory they require while being displayed on screen; and if there's not enough of that, they will keep getting reloaded all the time, slowing game down.
So you may be making your game unnecessarily big on disk, and introduce potential performance problems for the players who do not have powerful computers, while not gaining any actual benefit.
This is also why I don't suggest to go to 1080p, but rather recommend finding a resolution which matches the asset looks. That is a resolution where your sprites, fonts, etc can be displayed 1:1 without quality loss. Even if that's 720p or less.
Quote from: Custerly on Tue 16/01/2024 17:31:30I have been developing this on a 1080p monitor with no perceptible degredation in quality from AGS downsizingh everything from 4k.
I suppose that may be because 4k is evenly divided on 1080p.
@Crimson Wizard:
I will take your advice (and that of others here) and lower the res. Will I need to manually resize all assets, replace the current assets with the smaller ones, redraw all hotspots, re-place all sprites, etc. or is there a way to automate this within AGS?
Also, I want to have character portraits shrink to half size when they are not the current speaker during a conversation, but I want these miniature sprites to retain the ability to animate. Do I need to make new, smaller sprites and also make new views for animations, or is it possible to have AGS shrink down the existing sprites on the fly and thereby utilize the same graphics and animation views directly? I see that I can use DynamicSprite.Resize, but I assume that I won't be able to animate that.
Quote from: Custerly on Tue 16/01/2024 20:30:15I will take your advice (and that of others here) and lower the res. Will I need to manually resize all assets, replace the current assets with the smaller ones, redraw all hotspots, re-place all sprites, etc. or is there a way to automate this within AGS?
There's no way to automate this in AGS, but there must be a way to automate this using some graphics tool. Unfortunately, I do not have much knowledge of this, but maybe someone else can give an advice on which to use.
Quote from: Custerly on Tue 16/01/2024 20:30:15Also, I want to have character portraits shrink to half size when they are not the current speaker during a conversation, but I want these miniature sprites to retain the ability to animate.
Using Overlays will let you display sprites on screen with any scaling, but you will have to script animation yourself, changing overlay's graphic in periods of time. But I think you would have to do that yourself anyway, as AGS does not have a feature for displaying second portrait on screen.
https://adventuregamestudio.github.io/ags-manual/Overlay.html
EDIT: Alternatively, you could try using a dummy Character for that, which can be animated using a Animate function, and manually scaled too.
But keep in mind that characters are drawn on a "room layer" behind all GUIs, so that might be tricky (depends on situation).
@Custerly Well, properly unlocking the view means after the animation is done, so if you do it right after running a non-blocking animate command then it will indeed produce what you described (i.e. nix the animation).
Animating characters non-blocking during a dialog should work fine in principle; locking the view means that AGS will no longer switch to the appropriate view itself based on what's happening with the character (walking, talking, etc), so as long as it's the same view for all character frames, it's probably safe to call UnlockView() only after the dialog ends.
Quote from: Khris on Tue 16/01/2024 22:28:52@Custerly Well, properly unlocking the view means after the animation is done, so if you do it right after running a non-blocking animate command then it will indeed produce what you described (i.e. nix the animation).
Animating characters non-blocking during a dialog should work fine in principle; locking the view means that AGS will no longer switch to the appropriate view itself based on what's happening with the character (walking, talking, etc), so as long as it's the same view for all character frames, it's probably safe to call UnlockView() only after the dialog ends.
@Khris:
Here's the code I used:
c.Animate(1, 1, eOnce, eNoBlock);
c.LockView (3);
c.Animate(3, 1, eOnce, eNoBlock);
c.Animate(0, 1, eOnce, eNoBlock);
I originally did not unlock the view after (this is for a dummy character serving as a charatcer portrait during dialogues, so I don't need AGS to automatically animate this 'character' ever). Using the above code, I see no animation, just the switch from the former view over to view 3 (via [c.LockView (3);]).
If I add in [c.UnlockView (3);] afterwards, the issue remains.
If I enable eblocking, the animations play out, but the game pauses, which I'd hoped to avoid.
@Crimson Wizard:
I actually have already been using a dummy character for these portraits. I wasn't aware that AGS has a built-in system for character portraits as you implied. I will continue with this method, I just thought I'd check and see if there was a more automated route I could take.
Thanks once again for sharing your knowledge and insights.
@Crimson Wizard:
I updated everything to 1080p from 4k, and the animation stutter is persisting. Again, it is only choppy if my dialogue GUI is open while the animation is playing (if I set the GUI to invisible, the animation plays butter smooth).
I was wondering if this is the fault of my dialogue GUI specifically, or if this is an issue AGS has running animations while a GUI is open, so I tested this by making a new and very small GUI consisting of just one button to trigger the animation (the lightest GUI possible). The animation stuttered. Therefor, this is an inherent issue AGS has running animations while any GUI is open.
This seems beyond me to troubleshoot, but I did think of a potential workaround: Is it possible to copy the appearancy of my GUI (buttons, labels, and all), write that onto the background, make the actual GUI invisible, run the animation, then revert the background to its former appearance and make the GUI visible again?
Quote from: Custerly on Wed 17/01/2024 04:26:13I was wondering if this is the fault of my dialogue GUI specifically, or if this is an issue AGS has running animations while a GUI is open, so I tested this by making a new and very small GUI consisting of just one button to trigger the animation (the lightest GUI possible). The animation stuttered. Therefor, this is an inherent issue AGS has running animations while any GUI is open.
May we see this GUI? It seems likely that you're doing something non-optimal.
Quote from: Custerly on Wed 17/01/2024 04:26:13I was wondering if this is the fault of my dialogue GUI specifically, or if this is an issue AGS has running animations while a GUI is open, so I tested this by making a new and very small GUI consisting of just one button to trigger the animation (the lightest GUI possible). The animation stuttered. Therefor, this is an inherent issue AGS has running animations while any GUI is open.
This is unexpected, a lot of AGS games have some GUI on screen at all times. In the past there have been issues with GUIs slowing down high-res games because they redraw unnecessarily on some actions, but that has been fixed.
I'd need to investigate, and know some details about GUI, and how animation is run, how is it started in script, etc.
Do only animations stutter, or the rest of the game too? For example, does cursor slow down or jitter at the sametime?
Please also tell which version of AGS are you using, and which graphics driver do you use to run the game, because that may matter.
EDIT: in Default Setup pane there's "Display FPS on screen" option that enables a FPS counter useful for noticing performance problems. If you could turn that on and test this case, you would see if the whole game slows down or not. If not, then there's something incorrect in how animation is run.
Also, if you can reproduce this slow down in a small one room game, that you could share with us, this would be super helpful for debugging too.
@Snarky,
@Crimson Wizard,
@eri0o:
I will amass the requested info and files and post here at my first opportunity. Thanks for looking into this.
@Snarky,
@Crimson Wizard,
@eri0o:
Well, it seems there were several factors in the animation choppiness you good folks were helping me troubleshoot. I ran through the advice given, and had some interesting findings.
Firstly, I just moved around the mouse to see how the game overall was running. I noticed chopiness there, and after some investigation, I realized that AGS uses a default frame rate limiter of 40 FPS. I find this pretty strange, as most displays (mine included) are 60 hz. I changed the frame limitier to 60 FPS, and noticed a marked improvement in mouse motion clarity and the smoothness of the quick animations used in my dialogue portraits.
The issue was not totally resolved though, as I noticed a very slight stutter on the aforementioned portrait animations. Unexpectedly, this corresponded with a 5ish FPS drop only on the first time the animation ran per game. If I ran the animation again before restarting, it would not effect the frame rate. I tested this by pre-running all game animations on startup, and the frame rate remained clean when running the animations later in the typical gameplay context. This was all in the 4k (overkill, I know) build of the game. I tested again in the 1080p build, and noticed only a 1-2 frame drop during the first animation per game.
Adding to the confusion, for a period of time I noticed that, in the 4k build of the game, running animations for the first time per run would tank the frame rate down to the mid 40s. Testing the same build on subsequent days, I saw the frame rate in the same scenario drop to mid 50s. So I assume that there was some load on my computer that I wasnt aware of at some point, and that probably is what made me notice the frame rate hitches in the first place.
TLDR:
Ultimately, the solution was a combo of increasing frame rate limiter to 60 FPS, dropping the game resolution to 1080p, and my computer seamingly no longer having an unexplained secondary load on it.
A few questions for you guys, if you still have some patience for meL
- Why does AGS default to a 40 FPS limit?
- Why is there a greater frametime impact on the first run of these animations, and is there a way (other than the aforementioned pre-running of animations) to mitigate this?
- Is a 512 mb cache sufficent for 1080p graphics?
- I have been saving the graphics, after editing, via Photoshop 'export as PNG'. Is there a more size/performace optimal way of doing this?
Right, AGS has this old problem that it loads sprites only on first demand when they are displayed. Which means that if an animation plays for the first time, then it will be loaded simultaneously. Then the loaded sprites are put into the "cache". If the animation plays again, then the ready sprites are taken from the cache, and displayed momentarily. Actually, since 3.6.1 there's now a second "texture cache" that improves this even further, by spending more video memory.
The cache is limited by settings, and system RAM. Low-res games rarely reach this limit, but games with huge amount of assets, as well as high-res games, will eventually will. When this happens, the engine discards the sprites that were not used longest (it keeps record of that). If the sprite is requested again after, then it will have to be loaded again.
Quote from: Custerly on Tue 30/01/2024 16:58:36- Why does AGS default to a 40 FPS limit?
Purely historical reasons, I think this is how classic adventure games were run.
I must also note that unfortunately at this point in AGS FPS is both rate of render and rate of logic update. This should not cause much issues with 60 FPS, but may if you like to have higher (or very low) speed of game update for some reason.
Quote from: Custerly on Tue 30/01/2024 16:58:36- Why is there a greater frametime impact on the first run of these animations, and is there a way (other than the aforementioned pre-running of animations) to mitigate this?
Historically people used various hacks to trigger sprite preload during some "loading" screen. E.g. something like sequentially displaying these sprites on an object behind a opaque gui, or drawing small parts of them into an arbitrary DrawingSurface (the latter hack does not require to
display anything).
Since 3.6.1 there's are explicit functions called Game.PrecacheSprite and Game.PrecacheView.
Quote from: Custerly on Tue 30/01/2024 16:58:36- Is a 512 mb cache sufficent for 1080p graphics?
I guess it may be. But in practice this depends on how many different sprites you display at the same time (or in short term).
There will always be limits, if not for you then on some player's computer. It's better to not push as much as possible in the game, but try to optimize things, especially if it uses high-resolution assets. Keep the resolution to a minimal suitable level. If there are too many sprites then try to reuse them for multiple purposes. Do not make fullscreen animations if only a portion of the screen needs to change, animate only parts that need to change instead, etc. Well, this is a big topic on its own.
Quote from: Custerly on Tue 30/01/2024 16:58:36- I have been saving the graphics, after editing, via Photoshop 'export as PNG'. Is there a more size/performace optimal way of doing this?
AGS converts imported sprites to its own format, which is basically raw RGBA. But it also compresses them according to the options in General Settings -> Compiler section. In 3.6.0 the choice is "No compression", "RLE" (good for simplistic low-res gfx) and "LZW". Since 3.6.1 there's also "Deflate" which is same compression that PNG uses.
For long fullscreen animations I'd suggest try video instead.
@Crimson Wizard Thanks for all of your insights! Very informative, as always.