GameMaker Bit-masks and objects in a 2D Array

B

Blaize

Guest
I was wondering if it was possible to set a mask and store an object id in the same 2D array entry. I'm still relatively new to bitmasking in general, so any corrections are appreciated.

In a nutshell, I have a GameGrid[x,y] which starts empty. Then, I set specific tiles to hold certain features (heal, damage over time, etc). The goal is that an object that occupies a tile will react to that tile in a certain way.
Roughly (pseudo-codey), my project looks something like this:
Code:
for (i=room_height;i>-1;i--)
{
    for(j=room_width;j>-1;j--)
    {
        GameGrid[j,i] = 0;
    }
}
GameGrid[@0,0] = START; //These are macros I created as a bitmask
GameGrid[@2,3] = HEAL;
instance_create_depth(0,0,0,Player Object);
And the player:
Code:
Map.GameGrid[@x,y] |= id;
CurrentX=x;
CurrentY=y;
TargetX=0;
TargetY=0;
Code:
LeftRight = keyboard(Right)-keyboard(Left);
if (LeftRight != 0)
{
    TargetX = CurrentX + LeftRight;
    if (Map.GameGrid[@TargetX,CurrentY]==noone)
    {
        Map.GameGrid[@CurrentX,CurrentY] &=~id;
        x = TargetX;
        Map.GameGrid[@TargetX,CurrentY] |= id;
    }
}
Without the masks (if I'm using them properly that is), everything works fine. When the player moves, it's original position in the array is cleared and the array stores the id in its new position. However, when combined with the tile masks, if the player moves over a masked tile, then moves off, it also removes the mask.
Should I be approaching this differently?
 
D

drowned

Guest
you're using Map.GameGrid[@x,y] |= id to add the character to a cell. Then you're using Map.GameGrid[@CurrentX,CurrentY] &=~id to remove it, right?

If your character''s ID is 0001 and your tile's mask is 1111, then character | tile = 1111 right? Cell &= ~ID becomes Cell = 1111 & ~0001 = 1111 & 1110 = 1110. Not what you started with.

Anyway, why are you trying to do this? Why do you need to combine the character's ID with the value of the tile?
 

TheouAegis

Member
You need to | the player's id shifted left the most significant number of bits used by the tiles. So if you have 256 tiles, you need to shift the player's id left 8 bits first.
 
D

drowned

Guest
Or just literally add the values together. But I still want to know... why do you want or need to do this?
 
B

Blaize

Guest
Or just literally add the values together. But I still want to know... why do you want or need to do this?
Pure interest.

Edit: I'm just trying to learn something new. Like I said, I'm relatively new to working with bitmasks, and while I could solve this with objects, I wanted to see how I could go about this with fewer instances.
 
D

drowned

Guest
solve what though? If the character is on the tile, then you know it. Why do you have to store it in the tile's bitmask also?
 

Simon Gust

Member
Generally you want to avoid storing ids if they are the built-in ones.
Instance ids start at 1'000'000, that already takes 20 bits off the bitmask.
Tile ids (at least in GMS 1.4) also start at 1 mil.
 
B

Blaize

Guest
My logic was all over the place!
All I had to do was set a bit in the appropriate space:
Code:
//All pseudo code
if ((Map.GameGrid[@TargetX,CurrentY]!=TAKEN))
        {     
            Map.GameGrid[@CurrentX,CurrentY]&=~TAKEN;
            Move();
            Map.GameGrid[@TargetX,CurrentY]|=TAKEN;
        }
 
D

drowned

Guest
edit: I see what you changed... so you simply wanted a bit for taken/not taken, not to store the ID of the object in the bitmask?
 
B

Blaize

Guest
Yeah pretty much.
Now that I think about it, not sure why I needed to store an ID in the first place...
 

Hyomoto

Member
Nearly all numbers in GML are 64--bit, so yes, it is perfectly possible. However, the question becomes how far to shift. The math was done that it would take a few dozen hours to reach the 32-bit mark under constantly creating new ids, so it's pretty safe to reserve the first 32 and use the other 32 for whatever you want.

That said, and I've had some varying results in this category, is whether or not the remaining 32 are useful. I've been able to set and read all of them, but also not been able to set and read all of them. I'm not sure what the context is. But it might have to do with bit operations require casting these numbers into an integer internally, perform the operation, then return it to a double. It's possible this might lead to the inconsistent behavior. That said, I haven't seen it in patch notes but it may also be more reliable now. I did those tests early in GM2's life. Not sure if it's helpful, but something to look at.
 

TheouAegis

Member
Nearly all numbers in GML are 64--bit, so yes, it is perfectly possible. However, the question becomes how far to shift. The math was done that it would take a few dozen hours to reach the 32-bit mark under constantly creating new ids, so it's pretty safe to reserve the first 32 and use the other 32 for whatever you want.

That said, and I've had some varying results in this category, is whether or not the remaining 32 are useful. I've been able to set and read all of them, but also not been able to set and read all of them. I'm not sure what the context is. But it might have to do with bit operations require casting these numbers into an integer internally, perform the operation, then return it to a double. It's possible this might lead to the inconsistent behavior. That said, I haven't seen it in patch notes but it may also be more reliable now. I did those tests early in GM2's life. Not sure if it's helpful, but something to look at.
Did you try declaring the variable as int64 first?
 

Hyomoto

Member
@TheouAegis - No, but I don't think that function was available when I was trying this out. Like I said, it's been a while and I've had inconsistent results.

That said, are there any good reads on this? If there's a new magic sauce, I definitely don't mind reading what people think it's good for. Though what comes back if I:
Code:
var _x = int64( 9999999999) & 16;
An int64 or a double?
 

Simon Gust

Member
@TheouAegis - No, but I don't think that function was available when I was trying this out. Like I said, it's been a while and I've had inconsistent results.

That said, are there any good reads on this? If there's a new magic sauce, I definitely don't mind reading what people think it's good for. Though what comes back if I:
Code:
var _x = int64( 9999999999) & 16;
An int64 or a double?
_x will be an int64.
So will this
Code:
var _x = 9999999999 & 16;
be an int64.
 

Simon Gust

Member
I tested again and when I bitwise operate a number that gets over 31 bits in size, it does get cast.
Code:
var _x = 40 | 1073741824; // does not cast 

var _x = 40 | 2147483648; // does cast
 
D

drowned

Guest
was just about to test the same thing. I don't understand it though.

Edit: what I don't understand is: int64(9999999999) & 16 returns an int64 but 9999999999 & 16 does not. 9999999999 is necessarily an int64 without the cast right?
 

Hyomoto

Member
Well, it looks like we have some new tools but if your tests are all working as intended there's certainly some unexpected results going on. I was under the impression it was always cast back to a double for internal consistency.
 
D

drowned

Guest
I just checked is_int64(9999999999) and of course it returns true. So why would the explicit cast before the bitwise and change anything?
 
D

drowned

Guest
I'm using GMS2. This upsets me even more

Edit: this is returning false on 1.4:
Code:
var _x = 9999999999 & 16;

if (is_int64(_x)) 
    show_debug_message("is int64");
 
Last edited by a moderator:

Simon Gust

Member
I'm using GMS2. This upsets me even more

Edit: this is returning false on 1.4:
Code:
var _x = 9999999999 & 16;

if (is_int64(_x))
    show_debug_message("is int64");
yes that for me too because it's smaller than 31 bits in size.
It seems that it depends on the end result whether the number is int64 or not.
Because this
Code:
var _x = 72057594037927936;
is an int64 for me. I did not bitwise operate it.
2147483647 is a double?
2147483648 is an int64
the 32nd bit must be the bit to make a negative value so it kinda makes sense that it switches there?
 

TheouAegis

Member
I thought in GMS1 it cast to int, performed the operation, then returned to f32, which is why bitwise operators were so slow in GMS1 and older.
 
D

drowned

Guest
Right, it's not a uint. I still want an explanation of this:

int64(9999999999) & 16 -> int64
9999999999 & 16 -> not int64
 
D

drowned

Guest
so we're saying that even though 9999999999 is positive, the sign bit isn't set, unless you explicitly cast to int64, in which case it is ?
 
D

drowned

Guest
Not that this theory ever really made sense, but nope, not the sign bit:

upload_2018-4-13_18-14-22.png
 
Top