How are walk-behinds ordered in the engine? (C++ Plugin question)

Started by Walluce, Fri 06/01/2023 23:34:45

Previous topic - Next topic

Walluce

Hi,

I am writing a plugin and as part  of what my plugin needs to do, I basically need to redraw everything on the screen. I am just a little stuck when it comes to walk-behinds.

Basically, my Draw() loop is something like:
Code: ags
DrawObjects()
SortCharactersByY()
DrawCharacters()

I want to also draw walk-behinds.

This does not work (players are basically always behind):
Code: ags
DrawObjects()
DrawWalkBehinds()
SortCharactersByY()
DrawCharacters()

and this means that the players are basically always in front:
Code: ags
DrawObjects()
SortCharactersByY()
DrawCharacters()
DrawWalkBehinds()

I know this is because I am not taking into account the baseline of the walk-behinds.

What I think the Engine is doing is that it's basically creating the WBs as sprites and then simply sorting the sprites (chars, objects and WBs) in order of Y, BUT there is a mode in there called "DrawOverCharSprite". I think that my plugin could do that, but I am still not 100% how that works?

Any insights would be appreciated.


eri0o

Dualnames made a FakeScreen plugin, this is done like this

https://github.com/ericoporto/agsfakescreen/blob/3ebb2236379f6268a5549850cf12fba64520d8e0/AGSFakeScreen/AGSFakeScreen.cpp#L333-L342

The way to do is to organize everything that needs to be draw and then sort everything by ZOrder/baseline. In his plugin each item is given the name ScreenItem.

(He didn't do the actual drawing in the plugin though and paired a script module for some reason)

I think the mode you mention is only supported in software renderer, and not in the hardware accelerated renderers (Direct3D and OpenGL).

As new characteristics are added to the graphical items (rotations, blend modes, ...) it may require updating the plugin. If there is some specific use, it may be easier to leverage the existing renderers somehow or add new plugin interface features.

Crimson Wizard

Quote from: Walluce on Fri 06/01/2023 23:34:45I am writing a plugin and as part  of what my plugin needs to do, I basically need to redraw everything on the screen.

Could you please tell, for which purpose do you need to do that? I am asking, because sometimes there may be more convenient ways to achieve what you want. Maybe they already exist, if not, maybe something could be added to the plugin API, or script, to let that happen.
Like eri0o said above, depending on how much "in sync" would you like to keep your drawing, keeping your plugin up-to-date with the engine may become a struggle.

Quote from: Walluce on Fri 06/01/2023 23:34:45Basically, my Draw() loop is something like:
Code: ags
DrawObjects()
SortCharactersByY()
DrawCharacters()

This is wrong: the objects, and characters, and walk-behinds are all sorted among each other. So you need to gather ALL types of objects together and sort, then draw ALL of them sorted. For example, in AGS this is done by gathering structs of [sprite / order] in one std::vector from all relevant objects, then running std::sort: then you have a list of sprites to draw in a proper order.

Quote from: Walluce on Fri 06/01/2023 23:34:45What I think the Engine is doing is that it's basically creating the WBs as sprites and then simply sorting the sprites (chars, objects and WBs) in order of Y

It's not always Y.
Characters and Objects use Y by default. But they also have Baseline property: if it's set to 0 (by default), then they use their own Y instead. If Baseline is set to anything besides 0, then they use Baseline explicitly.
Walk-behinds always use Baseline as-is.
GUI and Overlays use ZOrder property for the similar purpose.
Since 3.6.0 Overlays may be drawn "inside" the room if they are created as "room overlays", with a special flag. In which case they use their ZOrder property to sort among characters and objects.

Summed up:
Room Objects: Y; but if Baseline !=0 then use Baseline;
Characters: Y; but if Baseline !=0 then use Baseline;
Walk-behinds: Baseline;
Overlays: ZOrder;
GUI: ZOrder;

Quote from: Walluce on Fri 06/01/2023 23:34:45BUT there is a mode in there called "DrawOverCharSprite". I think that my plugin could do that, but I am still not 100% how that works?

DrawOverCharSprite mode is a performance optimization done by the Software renderer. Because with the software rendering it may be slow to redraw large images on screen every time, instead every character and room object has an additional intermediate bitmap, consisting of its own sprite, and any parts of walk-behinds it is standing beyond painted over.
In other words: take a separate image size of character's sprite, paint character sprite, then paint over a piece of walk-behind which intersects with it.

Whether that is necessary for your plugin or not, - I cannot tell.

Walluce

@eri0o - Thanks. I don't quite understand the purpose of fakescreen, since there doesn't seem to be any documentation, but I can see that it basically does what I need to do, I.T.O. creating an image per overlay and sorting them.

@Crimson Wizard - I am not quite ready to talk about my plugin in detail yet, as would like to use it in a commercial game. I was hoping I could get away with a simpler algorithm, but I that see that I won't. Thank you for explaining that it's not always Y and the proper values to use.

Crimson Wizard

Quote from: Walluce on Sat 07/01/2023 21:44:57@Crimson Wizard - I am not quite ready to talk about my plugin in detail yet, as would like to use it in a commercial game.

Well, there's no need to go in full detail, but perhaps only mention the general purpose would be possible? If we do not see your code, we cannot "steal" anything anyway.

Of course it's up to you; but as I participate in AGS "tech support" for more than a decade already, I find that quite often people may use overcomplicated methods. So double checking that other methods don't exist may be useful.
For another reason, this helps to learn if AGS itself is missing some features, that could be added in the future for user convenience.

Walluce


Walluce

I understand, but if I say "My plugin is doing X" then I will get replies saying:
  • AGS Already Does X - Yes, but not the way I want it.
  • Why do you want to do X? - I want to do something other games don't
  • Why don't you just use plugin Y? - Because I think I can do it better and because I want to tailor it to my game

Or someone simply steals my idea before I can finish my game (I still have a long way to go).

eri0o

@Walluce I just shared that plugin code in case it help you since there aren't many references for C++ plugins + AGS, it was not with intent to force or tell you to use it, it was more in to explain a possible way to get all things together and then sorting.

There's a plugin function IAGSEngine.GetRenderStageDesc, that was added recently, which let you get the matrixes for use with 3D renderers, I mention this in case you do not need to create a entirely new alternative renderer, but can leverage what already exists and just apply what you intend. Also, if this is for your game as you mention, about the walkbehinds, you can skip them altogether and not use them. I never use those and use objects instead, as this gives me a better control of the alpha, when using walkbehinds things are either transparent or not, and this is usually fairly limiting.

Crimson Wizard

Quote from: Walluce on Sat 07/01/2023 22:14:46Is there a "GetNumberOfWalkBehindsInTheRoom()" method?

Not really, when in the editor users are presented with max areas always, regardless if they use them all or not.
The supported number of areas is hardcoded and may depend on the engine version (may be found in the manual hmm, manual is missing these), but absolute maximum is always 256, because that's a 8-bit mask.

It may be possible to scan the area mask on room load and check which pixel colors are present; afaik AGS does that for walk-behinds when cutting them into separate sprites.

Walluce

@eri0o I'm sorry if I didn't express myself well. I meant to thank you. The code you shared helped immensely. With the code you shared and with @Crimson Wizard's explanation, I got my plugin to work exactly as intended. Thanks again to you both.

SMF spam blocked by CleanTalk