What is the best way to store a ds_map inside of a ds_grid

XD005

Member
Hello all, I am working on a Mario Party/Board game concept.
So far, my boardgame uses a ds_grid to store the gameboard layout.
Basically the x and y coordinates have a value which determines what type of square should be drawn.

I want each square to contain meta data about what should be there.
I was thinking of adding some type of ds map system. That way, each grid value can have a key.
Each square could have a challenge completed flag, what type of challenge, a text string, values to trigger story events, etc, etc.
I'm just stuck on how to implement this. I thought about using some type of string system and parsing the information manually but I feel as though
that will get really messy fast. Any suggestions?
 

Nidoking

Member
Sounds like the kind of thing you could do with structs in 2.3. Personally, I've used objects to hold that kind of information, but you could use maps as well - put the index of a map in each grid square and populate the map with the information about it.
 

XD005

Member
Sounds like the kind of thing you could do with structs in 2.3. Personally, I've used objects to hold that kind of information, but you could use maps as well - put the index of a map in each grid square and populate the map with the information about it.
Mmm I see, how slowly do you think that would run? I suppose I can just code this map solution and when 2.3 goes out of beta, code a struct version.
I know old versions of Game Maker also had a limit on how many entries you could have in a ds map.
I suppose you'd need an insanely large game board to even meet that limit though, right?
 

Nidoking

Member
It's not the size of the board, it's how much information you're putting in it. It's a map per grid square, remember. How many entries do you need to represent the state of one board position? If it's more than a couple dozen, then you probably need to design a simpler game.
 

XD005

Member
It's not the size of the board, it's how much information you're putting in it. It's a map per grid square, remember. How many entries do you need to represent the state of one board position? If it's more than a couple dozen, then you probably need to design a simpler game.
Gotcha, should work fine then. I don't think I'll be needing any more than 12 entries per square.
Thanks!
 

Neptune

Member
You can store anything in any cell of a ds_grid.
GML:
my_grid = ds_grid_create(3,10); //3 cols, 10 rows
my_list = ds_list_create();

ds_list_add(my_list,"some data");
ds_list_add(my_list,3);
ds_list_add(my_list,67.5);

my_grid[# 2,9] = ds_list_write(my_list); //Grid cell now contains a read-only string out of the list data

var access_list = ds_list_create();
ds_list_read(access_list,my_grid[# 2,9]);

var the_number_3 = access_list[| 1];

//alternatively

my_grid[# 2,9] = my_list; //Grid cell now contains a POINTER to my_list (the list must stay available in memory to access its data) like so:

var the_number_3 = my_grid[# 2,9][| 1];
//or
var pointer = my_grid[# 2,9];
var the_number_3 = pointer[| 1];


//store 100,003 pieces of data in one cell
for(var i = 0; i < 100000; i++)
{
     ds_list_add(my_list,i);
}
my_grid[# 0,0] = ds_list_write(my_list);


//store a map
my_map = ds_map_create();
my_map[? "name"] = "Neptune";
my_map[? "tile_size"] = 150;
my_map[? "tile_type"] = true;

my_grid[# 1,7] = ds_map_write(my_map);
There is not a single thing in 2D game development that cannot be done with the current state of GMS2.
You don't need 2.3 for anything. It may have some fancier approaches and make life a hell of a lot easier, but it is not needed.

@Nidoking I would not give advice like "you should design a simpler game" or recommend multiple objects for anything. It is just absolutely WRONG.
 
Last edited:

Neptune

Member
Hope this helps, understanding basic data storage and retrieval is key to doing anything complex, and doing it well.
Best of luck!
 

Nidoking

Member
I would not give advice like "you should design a simpler game" or recommend multiple objects for anything. It is just absolutely WRONG.
If you want to write an entire game in only one object, that's your business, but it loses a lot of what OOP is all about, and most of what makes Game Maker viable for beginning developers who don't understand things like complex data structures-of-structures. You might as well just be writing in C at that point. Likewise, show me a game that requires so many variables that it exceeds the number of valid indices for a GML map, and I'll show you a game that could stand to be a lot simpler.
 

XD005

Member
Hope this helps, understanding basic data storage and retrieval is key to doing anything complex, and doing it well.
Best of luck!
Thanks this is a good approach. I need the variables to be read/write. Couldn't I also create a map for each cell but store the ds_map IDs in the grid cell information instead? With the variable_instance functions, I can make unique identifiers for each cell. When the information is no longer needed, we can look through the who grid ds and clear the maps afterward. Only thing that'll be sort of complicated is saving the game but we can look through the grid the same way and copy the map information into a .ini.
 

Neptune

Member
Exactly, you can store read-only strings in the grid cells, or you can store pointers to existing maps / lists / grids etc in the cells.

When you need to save, having each cell containing read-only would make it super easy (because you'd just write the whole grid to a string using ds_grid_write, and thus containing all of the data in one string).
Either way, read-only or existing pointers, saving/loading wont be too bad.
 

Neptune

Member
@Nidoking GML is procedural and not supposed to be object oriented (even tho we call them objects).
I'm not saying you couldnt spawn in instances for each tile that have their own set of variables and what not (in fact you probably will at some point), but it is not a good or ideal way to handle core data, and will complicate things if the game grows.

The information I shared, is the kind of information I WISH someone would have slapped into my face early on when I was learning, so I didnt waste 2 years doing everything with objects and 100s of instance variables.
 

Nidoking

Member
I'm not saying you couldnt spawn in instances for each tile that have their own set of variables and what not (in fact you probably will at some point), but it is not a good or ideal way to handle core data, and will complicate things if the game grows.
It sounds to me like you never learned how to do object-oriented programming properly. Not the end of the world, but you don't get to capital letter WRONG someone for your own shortcomings.
 

Neptune

Member
Again GML is not object oriented or OOP (it is procedural). But ok, agree to disagree.

And I'm passionate about this because advice to use more objects in place of data structures is just setting people up failure if they try to make anything beyond pong and tic-tac-toe.
And it is the same advice I got back when I started.
 
Last edited:

Nidoking

Member
Again GML is not object oriented or OOP (it is procedural). But ok, agree to disagree.
If you want to make a game with no objects at all, again, that's your business. Whatever may go on internally in the Game Maker runtime is irrelevant - you're creating objects/instances that manage their own functionality and state. They implement encapsulation, abstraction, inheritance, and polymorphism. If OOP means something else to you, then again, you never learned OOP properly, and again, I really recommend that you stop saying wrong things with authority. It's not a good look for you.

And I'm passionate about this because advice to use more objects in place of data structures is just setting people up failure if they try to make anything beyond pong and tic-tac-toe.
And it is the same advice I got back when I started.
Again, you got bad advice and you're inflicting it on other people. Data structures are great at what they do. Objects are also great at what they do. Sometimes, they can both do the same thing in different, equally valid ways. At other times, one or the other is the blatantly correct choice, and if you're going to sit there and, once again, capital letter WRONG people who make good decisions based on all of the available options, you can sit in your cloud of smug while I move you into a cloud of Mute and recommend that people who want to learn to make good decisions do the same.

I mean, sure, if you were using hundreds of instance variables as you said, then you were probably doing something wrong. But blaming the existence of objects for your own design decisions is bad form, and trying to spread that mentality to other impressionable people is actively harming the community.
 

Neptune

Member
I don't have anymore time for this senseless rambling, or ad hominem based argument. Do what you want.
 
Last edited:

Neptune

Member
Sweet :)
You can use a map, list, grid -- They can all store read-only or pointers!
When you create a data structure the "pointer" is just an integer (whatever value assigned to the structure when created), and a "read-only" is a just a string; makes it easier to think of it like that.
 

XD005

Member
Sweet :)
You can use a map, list, grid -- They can all store read-only or pointers!
When you create a data structure the "pointer" is just an integer (whatever value assigned to the structure when created), and a "read-only" is a just a string; makes it easier to think of it like that.
Thanks again for this. So this is what I came up with.

GML:
galaxy = ds_grid_create(5,4);
galaxy_status = ds_map_create();

//Populate the status map
for (var _mapx = 0; _mapx < ds_grid_width(galaxy) + 1; _mapx++;){
    for (var _mapy = 0; _mapy < ds_grid_height(galaxy); _mapy++;) {
       
        var _mapkey = "[" + string(_mapx) + " " + string(_mapy) + "]";
        var mymap = ds_map_create();
       
        ds_grid_set(galaxy,_mapx,_mapy,mymap);
        ds_map_add(galaxy_status,_mapkey,1);
        ds_map_add_map(galaxy_status,_mapkey,mymap);
       
        ds_map_add(galaxy_status[? _mapkey],"block_type",1);
       

    }
}
I'd like to store a nested DS Map due to this being easier to debug in the future.
This code isn't working though. I can use the pointer method but I feel like that will get very messy, quickly.

EDIT: Nevermind, got it. Didn't realize I was trying to add a key that already existed
 

Neptune

Member
Couple things that might help:

GML:
galaxy = ds_grid_create(5,4); //5 columns, 4 rows

var data_map = ds_map_create();

for (var yy = 0; yy < ds_grid_height(galaxy); yy++;)
{
    for (var xx = 0; xx < ds_grid_width(galaxy); xx++;)
    {
        ds_map_clear(data_map); //clear it for next cell's data

        data_map[? "block_type"] = 1;
        data_map[? "block_size"] = 25;
        data_map[? "block_color"] = c_green;

        galaxy[# xx,yy] = ds_map_write(data_map); //store the read-only data for the cell.
    }
}
ds_map_destroy(data_map); //must clean up data structures to not make a memory leak
I don't use ds_maps too often, but it will work.
Also, looping horizontally in your inner loop may help with confusion -- since it will then be looping left to right, top to bottom as most things are.

At this point, to access the data of a cell to change or add to it:

GML:
//access cell 1,3's data
var data_map = ds_map_create();
ds_map_read(data_map,galaxy[# 1,3]); //retrieve the map data stored in cell 1,3 of the grid

    var block_type = data_map[? "block_type"];
    block_type += 1;
    data_map[? "block_type"] = block_type;
    //or
    data_map[? "block_type"] += 1; // you don't need the intermediate step of setting to a variable
    data_map[? "block_size"] += 75;
    data_map[? "block_color"] = c_red;

    galaxy[# 1,3] = ds_map_write(data_map); //re-store the read only data

ds_map_destroy(data_map); //clean up
You could do a similar 2D loop across to make changes/additions to every cell also... Read / changes / write.

For notation you should never need the set/get functions for grids, maps, lists.

GML:
map[? "key"] = 0; //set
var get = map[? "key"]; //get

list[| x] = 0; //set
var get = list[| x]; //get

grid[# x,y] = 0; //set
var get = grid[# x,y]; //get
Use whatever you feel comfortable with, if ds_map_get and ds_map_set is easier to think about for now.
 
Last edited:

Neptune

Member
Ultimately it is your choice whether to keep each map in each cell active (meaning you used ds_map_create for each cell, thus storing a "pointer" to an access-ready map) - In which case you don't need to write & read whenever data is needed to be accessed, but you gotta be careful to not create memory leaks.

For example, if you need to destroy all the maps, you would loop across and destroy each map using the pointer that is stored in each cell.
You cannot just destroy the grid, and call it good (otherwise all those active maps will still be sitting lost in memory). Vise versa, you can just destroy the grid with the read-only approach, because the "maps" are just strings.

With more practice you'll be able to decide what should be stowed away as read-only and what needs to actively sit in memory.
Either way will work if done properly, it is just a matter of convenience.
 
Last edited:
Top