#Macros & Layer ID & config struct

1 messages Β· Page 1 of 1 (latest)

marsh jasper
#

So, I met with that "Don't rely on execution order" thing far too early to my taste, but that's ok, however it shows another nasty problem and I'd like advice about how any of you handle it. Here is the settings :

  • my player character has two "modes" that would change it's parameters. In one mode it can walk on water tiles instead of sinking.

I use two macros to get the ground and the water tilemap id of the current room.

#macro LAYER_GROUND GetTilemapID("ground")
#macro LAYER_WATER GetTilemapID("water")

And then another couple of macros as arrays that I would use in my collision code, one for each modes.

#macro COL_SOLID_A [oSolid, LAYER_GROUND]
#macro COL_SOLID_B [oSolid, LAYER_GROUND, LAYER_WATER]

I have a an array that store two config struct for each mode in the PC object and I switch between those in-game.
In those config struct, I have a variable member that get the proper COL_SOLID macro for each mode.

// in the struct for mode A
col_solid = COL_SOLID_A
// in the struct for mode B
col_solid = COL_SOLID_B

Then my generic collision code has stats[mode].col_solid as he collision to check in place_meeting (and others)

So I think you guessed what would be the problem:
col_solid reference the very first call of those macros and if I change room, it won't be updated to reflect the new room's ground and water's layer id, until I regenerate the whole config structs or call a method in the PC that would update that variable at room change, which is really dumb imho XD.

How would you resolve that ?

frosty tinsel
#

the way I handle this is to use global variables instead of macros, and then I update the global in room start

//in some controller object room start event
global.collision = [layer_tilemap_get_id("collision"), obj_wall, //whatever else]``` and then I use the global directly instead of storing it in a variable
#

so every object uses the same global array

tropic glade
#

^ I do something like that but as a function call instead

#

The array is stored as a static, and I just update the array manually within the function lol

#

Then I return the array

frosty tinsel
#

for dynamically using different ones on the player, I think you want some approach that doesn't pre-store the array forever

#

that or just update it in room start

tropic glade
#

Yeah, if it's one array that's always changing based on context, then that's more situation

#

But for simpler arrays where the values aren't really changing

#

I tend to just roll with function/s

#

And having the tilemap id fetched

marsh jasper
frosty tinsel
#

actually thinking about it more

#

if you just update the array on room start instead of re-declaring it

#

your array in the struct could automatically update due to being a reference

#

so that's probably the way to go

marsh jasper
#

Wait I'm lost and I think I have overlooked something in my explanation post.
My PC has a something like this (well I use constructors in fact) :

modes = [
//A
    { col_solid : global.COL_SOLID_A},
//B
    {col_solid : global.COL_SOLID_B}
]

Then I start with Mode A when the player is spawned
param = modes[0]
When I switch mode I update param.
And whatever the mode, when I check for collision I just use :
if place_meeting(x,y, param.col_solid, false, true){// code}

So I'm not sure the col_solid variables would be updated to the new globals value, right ?

frosty tinsel
#

it should, since it'd be references to the same array the whole way

#

as long as you never re-declare the array

marsh jasper
#

I don't think I am re-declaring the array : I update the global in the room start of my controller object like you suggest in your first post.
But well, col_solid keep the value of the first declaration of those global (which is noone)

frosty tinsel
#

my first example is re-declaring it

#

need to do something like

//game start
global.collision = [layer_tilemap_get_id("collision"), obj_wall, //whatever else]
//room start
global.collision[0] = layer_tilemap_get_id("collision");```
marsh jasper
#

what a dumber I am XD

tropic glade
#

This is why I do it with a function haha

function get_collision_data() {
  static _data = [col_solid, undefined];
  _data[1] = layer_tilemap_get_id("collision");

  return _data;
}
#

I get none of the array re-reference issues

marsh jasper
#

yep, that's what I was thinking of doing

tropic glade
#

It's just, automatic

#

And I also don't need to account for the odd cases where I forget to update the array really (since calling it anywhere will just update it)

marsh jasper
#

but is putting the _data array inside a macro good enough ?

tropic glade
#

It's a static variable

#

I mean, you can do the call itself yeah

#
#macro COL_ARRAY (get_collision_data())
#

Obviously having it update once between rooms is probably better (for performance probably), and there are perhaps better ways of doing it (room id static caching maybe?)

#

But, it's worked fine enough for me that I don't really bother about it

#

Since how often would you need to call COL_ARRAY?

#

(For the record, I call that function in the create event of any instance/s that need to access X collision data, assigned to a variable)

marsh jasper
tropic glade
#

How do you even switch modes?

#

Is it like an if/else

#

Or do you just swap the array used?

marsh jasper
#

#1435032721326997566 message

tropic glade
#

Righty then

#
function get_collision_data() {
  static _data = [
    { col_solid: [oSolid, undefined] },
    { col_solid: [oSolid, undefined, undefined] },
  ];

  static _roomId = -1;

  if (room != _roomId) {
    _data[0].col_solid[1] = layer_tilemap_get_id("ground"); 
    _data[1].col_solid[1] = layer_tilemap_get_id("ground"); 
    _data[1].col_solid[2] = layer_tilemap_get_id("water");
    _roomId = room;
  }

  return _data;
}
#

That is basically what you'd have really lol

#

This would be the most optimial way with the least amount of manual updates to be made

#

I mentioned a room id static cache for performance gains because you can sort of cache the room id

#

Might not be needed here

#

But it's something I do in cases where the data isn't likely to change as often, and the calls aren't cheap

#

And then in macro form

#

#macro COL_SOLID_A (get_collision_data()[0].col_solid)

marsh jasper
#

And then in the config struct of each mode I would put for their col_solid member :

//Mode A
col_solid : COL_SOLID_A
//Mode B
col_solid : COL_SOLID B //#macro COL_SOLID_B (get_collision_data()[1].col_solid)

Right ?

tropic glade
#

Well, erm, no

marsh jasper
#

lol

tropic glade
#

Is because the function already has the data

marsh jasper
#

I have a hard time to get around statics XD

tropic glade
#

Calling get_collision_data() by itself would give you an array of structs, that hold onto an array each

#

And it's the same array and structs for every single call made

#

It never changes

#

Only what inside, does

#

So, with that in mind

#

We no longer really need COL_SOLID_A or COL_SOLID_B

#

Unless, you need to reference them outside of the initial data

#

#macro COL_SOLID_A (get_collision_data()[0].col_solid)

#

That's what this macro is for

#

It calls the function, fetches our data, then the col_solid data, and returns that

#

So, same structure, same kind of data you'd expect

marsh jasper
#

(seen your modifications in the code above)

tropic glade
#

Just pointed in a different location that never changes

#

(I didn't really changed anything, I just realized I forgot to cover ground in mode b... :P)

#

Statics themselves exist throughout the entire games runtime

marsh jasper
#

This I think I get it.
What I don't is how I'd swap that based on the mode I am in.

tropic glade
#

So whatever is stored in a static, stays there. And it never goes away. Until the game is closed, or you change the static value it holds onto

tropic glade
#

Or rather, how do you even determine when you are in a different mode?

#

State change?

#

A variable flip?

#

Because fundamentally speaking, you would still run into the same issue.

#

With or without that function call

#

It really does depend on how you determine the mode change

marsh jasper
#

Ok.
So I have states, right.
When I press the button, I simply (well I do other stuf as well) update param

//pseudo code
if button_pressed {
  mode++
  param = modes[mode]
}
tropic glade
#

Is param meant to hold onto a struct, or an array?

marsh jasper
#

then param.col_solid handle the collision data

tropic glade
#

Right, okay

#
// Create Event
modes = get_collision_data();

// Step Event or whatever
//pseudo code
if button_pressed {
  mode++
  param = modes[mode]
}
marsh jasper
#

modes has a lot more than just the collision data, actually.

tropic glade
#

Obviously, modulo mode is included (or resetting it back to 0

#

Right, but you never really stated this

tropic glade
#

At least not entirely clearly

#

I'm just pointing out a good way of going about it

#

Without having to add in extra checks for updating data between other events

marsh jasper
#

let me take a screenshot of the whole actual thing then.

tropic glade
#

So, you can just add other stuff into the data

#

Nothing stops you from doing that πŸ˜›

marsh jasper
tropic glade
#

I'm failing to see where modes has more going on πŸ€”

#

I just see col_solid and col_water

#

Being the closest two here

marsh jasper
#

yeah, so modes are "size"

#

Then in the player create event :

#

specs is the param stuff

tropic glade
#

Are these constructor instances created every time, or only once?

marsh jasper
#

only once

#

stored in global.HeroAttributs

tropic glade
#

Well if you have multiple of arrays, then you would need to update multiple of them as well

#

Which, still doesn't actually change really anything here

#

The same solution I made would work technically

#

All I've done at the end of it is just made it so it updates nicely in one place

#

Which... You would have to do anyway if you want to update those tilemaps in those arrays

#

Whether it's a global variable, or a static variable

#

So again, it doesn't really change too much

#

But, knowing now what this is all being used as

#
function get_collision_data() {
  static _data = [
    [oSolid, undefined], // COL_SOLID
    [oSolid, undefined, undefined], // COL_WATER
  ];

  static _roomId = -1;

  if (room != _roomId) {
    _roomId = room;

    // COL_SOLID
    _data[0][1] = layer_tilemap_get_id("ground"); 

    // COL_WATER
    _data[1][1] = layer_tilemap_get_id("ground"); 
    _data[1][2] = layer_tilemap_get_id("water"); 
  }

  return _data;
}
#
#macro COL_SOLID (get_collision_data()[0])
#macro COL_WATER (get_collision_data()[1])
#

If you have COL_SOLID_SMALL and COL_WATER_SMALL, you can just add additional arrays to _data and update those as well

#

And then every single reference to those macros would connect all of the arrays together so they are all apart of the same array, for every single instance & constructor and wherever else you need them

marsh jasper
#

actually your COL_WATER is COL_SOLID_SMALL πŸ˜„

tropic glade
#

Erm, no?

#

Or I guess it is

marsh jasper
#

haha

tropic glade
#

I was just going by your original code

marsh jasper
#

yeah

tropic glade
#
#macro COL_SOLID_A [oSolid, LAYER_GROUND]
#macro COL_SOLID_B [oSolid, LAYER_GROUND, LAYER_WATER]
marsh jasper
#

don't worry, I get it

tropic glade
#

This is why it's always important to share the full picture

#

If you don't wanna have to follow up with extra info and correct things later πŸ˜‰

marsh jasper
#

yep XD sorry

tropic glade
#

It's all connected to the same array

#

If you wanna update any of them

#

You just need to call get_collision_data()

#

Once, anywhere

#

So if you are moving between rooms, then the call itself would update the tilemap refs, and cache the result, and then all of your existing collision arrays would also be updated because they point to the same array

marsh jasper
tropic glade
#

Yes

#

Not the one I pointed here ofc

#
#macro COL_SOLID (get_collision_data()[0])
#macro COL_WATER (get_collision_data()[1])
#

But those, yes

#

Has to be specifically this

#

as long as your macros do that

#

It's fine

marsh jasper
#

That's super interesting and super hard to get around XD
So super thank you

tropic glade
#

Just at the end of the day, it's a function call that is being made, that function call holds onto statics

#

(Another way to think about it, imagine every single gamemaker function has its own lil tiny struct, called statics)

#

(Actually, that's not even something you need to imagine... static_get(function_name) will get the static struct)

#

Those statics hold onto data, and it always returns the static value, which keeps the exact same array refs

#

Because it is not temporary, it is forever until the game closes, or the static itself happens to get updated

#

(Updated as in, like how _roomId, another static, gets updated)

marsh jasper
#

I think I get it. may be. But I will play with your solution to get all the ins and outs πŸ˜„

tropic glade
#
function count() {
  static _num = 0;
  return _num++;
}

repeat(3) {
  show_debug_message(count()); 
}
0
1
2
#

If you want a more barebones example πŸ˜‰

marsh jasper
#

That was really helpful.

tropic glade
#

🫑

#

No worries

marsh jasper
tropic glade
#

Hehehehe well, you are then aware the basics of statics at least πŸ˜‰

marsh jasper
#

haha

tropic glade
#

Which is all that matters really!

marsh jasper
#

MMMMMMMMH

#

Something not right.
I'm overlooking something.

#

col_solid member isn't updated when I change size.
I'd say that's expected, right ?

tropic glade
#

How are you changing size, exactly?

marsh jasper
#

With that function (stripped out of all my french comment and log stuff)
I use Input and Shaun's SnowState.

function InputChangeSize()
{
    if InputPressed(INPUT_VERB.GROW){
        playerSizeNext = min(++playerSizeNext, playerSizeMax);
    };
    if InputPressed(INPUT_VERB.SHRINK){
        playerSizeNext = max(--playerSizeNext, playerSizeMin);
    };
    
    var _prev_size_name = specs.size_name;
    
    var _state    = GetSizeState(playerSizeNext);
    var _size_data    = GetSizeData(playerSizeNext);
    
        playerSize = playerSizeNext;

    fsm.change(_state, undefined, function(_data){
                //get the former enter event of the next state.
                var _next_state_enter = fsm.event_get_current_function();
                // update the variable that store the size parameters
                stats.size_specs = _data._size_data;
                specs = _data._size_data;
                //execute the former enter event.
                _next_state_enter();
        
       }, {_size_data, _prev_size_name});
    
    return true;
    
}
#

so I just grab the value in the global.HeroAttributs array that match the size.

#

and as I said above, that global is declared once

#

and that was the issue since it is declared before being in any room that would fill the col_solid member with a proper tilemeap id.

#

So I need to update it at room start (which was Kormex approch to swap macros to global.variable

#

in my case with statics, what I would need to do is to call specs.col_solid = COL_SOLID in the room start, but that won't take in consideration the size

#

(well I would create a method to update each col_solid in their respective size, I guess)

marsh jasper
#

I have merged your approach and Kormx's XD

#
global.collision = [
    [oSolid, undefined], //COL_SOLID [oSolid, LAYER_MAIN]
    [oSolid, undefined, undefined], //COL_SOLID_SMALL[oSolid, LAYER_MAIN, LAYER_WATER]
    [undefined] //COL_WATER [LAYER_WATER]    
];

#macro LAYER_MAIN GetTilemapID("main")
#macro LAYER_WATER GetTilemapID("water")

#macro COL_SOLID (GetCollisionData()[0]) //[oSolid, LAYER_MAIN]
#macro COL_SOLID_SMALL (GetCollisionData()[1]) //[oSolid, LAYER_MAIN,LAYER_WATER]
#macro COL_WATER (GetCollisionData()[2]) //[LAYER_WATER]
#

And the GetCollisionData :

function GetCollisionData(){
    static _roomID = -1;
    
    if (room != _roomID) {
        _roomID = room;
        
        //COL_SOLID
        global.collision[0][1] = LAYER_MAIN;
        
        //COL_SOLID_SMALL
        global.collision[1][1] = LAYER_MAIN;
        global.collision[1][2] = LAYER_WATER;
        
        //COL_WATER
        global.collision[2][0] = LAYER_WATER;
    }
    
    return global.collision;
}
#

When I build HeroAttributs :

// for Normal size
col_solid = _config[$ "col_solid"] ?? global.collision[0];
//for Small size
col_solid = _config[$ "col_solid"] ?? global.collision[1];

// for water collision in both size
col_water = _config[$ "col_solid"] ?? global.collision[2];
#

I just have to define the proper collision data in my custom MoveAndCollide function when I am in water while small, because the default parameter is specs.col_solid and LAYER_WATER is then included in the check, so the character is stuck XD

tropic glade
#

But hey, if it works, it works!

marsh jasper
tropic glade
#

Whiiich, you could easily solve just by calling it at game start lol

#

(The function GetColisionData(), not the constructors)

marsh jasper
#

mmmmh

tropic glade
#

If you call the function while you're not in a room, the data it fetches would be trying to fetch from a non-existent room

#

And you can't get info from a non-existent room

#

But if you were to call GetCollisionDate() in a game start event for example... It'll properly update it

marsh jasper
#

I call the fonction in the room_start of my oLevelManager

#

But since I use the macros to set col_solid when I build the HeroAttributs, and at that moment, you are neither in a room nor anything is created yet, yeah, I get an empty Tilemap.

tropic glade
#

Yeah, but since the arrays are the same between them (as they are in the statics, or now, global variables)

#

Calling it at game start or room start, should all you need to do

#

I don't think there's anything else that should be happening here

marsh jasper
#

then I enter the room, and oLevelManager is created and with it its room start event, right ?

tropic glade
#

Yeah

#

And it should just update the arrays

marsh jasper
#

could it be that col_solid dosen't get a ref of those array ?

tropic glade
#

Well no

#

Unless you somehow replaced the entire array (which my code didn't even do)

#

Or I guess if you made _data not static?

marsh jasper
#

it is static

tropic glade
#

Then no idea

#

Theoretically nothing should've broken, or have changed

marsh jasper
#

So I'm probably doing something wrong

#

(well the version with global.collision works so that's ok, but it bother me that your approach doesn't XD)

tropic glade
#

Well if the solution works, it works!

#

That's all that matters

marsh jasper
#

ok. the issue is ... room caching.

#

Since I don't have any other room than the one I have right now

#

That's what happen

#

I have wrapped my Data_Hero script asset into a function in order to initialise it when I want because I faced that issue when GM execute stuff in the order it wants (without telling)

tropic glade
#

Honestly given your circumstances

#

You could just remove the room caching

#

It's not going to really affect much lol

marsh jasper
#

So it declares my Macro_Game stuff, then Data_Hero and try to get the collision data, but since I have one room only room is 0, while not being created yet. so GetCollisionData try to get the layers find nothing because no room created and return -1.
Once the room is created, oLevelManager call GetCollisionData again but since room is already 0 ....

marsh jasper
tropic glade
#

🫑

marsh jasper
#

The funny thing is that with theglobal.collision version, room caching doesn't get in the way XD

#

or may be it got ! since I used directly the global array to declare col_solid

#

ANYWAY