Creating a Nested Array "Comprehension"

Godot Version

4.3

Question

I first learned programming by creating a game in Python 2.7. In Python 2.7, you could create something called a Nested List Comprehension. This was very useful for storing a large number of tiles for a grid-based game and then accessing that specific tile quickly.

I know that Godot was built on Python / was inspired by Python, and was wondering if Godot 4.3 supports something like this. And if it does, how to go about building one?

I ask because I’ve created something “similar” to this with a pair of dictionaries, but I don’t think it is very efficient at accessing a specific tile.

ie …
The_Tile = Tiles[desired_grid_position.x][desired_grid_position.y]

(to get the tile, instead of my current Tiles[desired_grid_position.x][desired_grid_position.y])

I need a system like this as I have a variety of movement effects, ie Push/Pull, that requires quick access to a tile, and so a simple array to store all the tiles isn’t optimal at all.

This seems like the same Tiles[desired_grid_position.x][desired_grid_position.y] process in both examples?

What friction are you experiencing with your nested dictionaries? Could you use nested Arrays instead?

As I understand list comprehensions are for creating new lists from simple operations over each existing element. Not for getting specific elements or nested elements. How would you use list comprehensions in your own code? Could you paste psuedo code and explain what you want it to do?

List comprehension are just a shorthand way of writing for loops, so Godot can absolutely replicate the data structure your aiming for, but maybe not as a succinct one liner.

However, why not just use a Vector2 as your dictionary key and not have nesting at all for your grid e.g.:

var boundsx := 256
var boundsy := 256
var Tiles := {}
var counter := 0

for x:int in range(0,boundsx):
    for y:int in range 0,boundsy):
        Tiles[Vector2(x,y)] = {
            'someID':counter,
            'someOtherAttribute':'blah'
        }
        counter+=1

print("Tile 0,0 ID:" + str(Tiles[Vector2(0,0)]['someID']))

If you’re regularly iterating the entire grid and the grid is huge, than nested arrays may make more sense from a performance perspective (would need to test it, suspect it would be the same tbh). I’d tend to go the dict approach for simplicity regardless as until performance becomes a problem, it’s not a problem.

If your real finnicky about iteration performance, something like this may be better, I just find it more cognitive load to remember you have dictionaries nested in arrays instead of just a dictionary with a Vector2 key personally:

for x:int in range(0, boundsx):
	var row := []
	for y:int in range(0, boundsy):
		row.append({
			'someID': counter,
			'someOtherAttribute': 'blah'
		})
		counter += 1
	Tiles.append(row)

print("Tile 0,0 ID: " + str(Tiles[0][0]['someID']))

Side note, Godot is written largely in C++, GDScript is inspired by Python.

1 Like

but why dicts then? arrays are more efficient than dicts, not the opposite. you can initialize one like this:

var tiles: Array
func _ready() -> void:
	tiles = []
	tiles.resize(h*w)
	tiles.fill(EMPTY_TILE)

and access like this:

func get_tile_at(x, y):
	return tiles[y*w+x]
func set_tile_at(x, y, value):
	tiles[y*w+x] = value

(where h is hieth and w is width)

1 Like

Thanks for the replies!

@gertkeno My overall grid is several thousand tiles large, so I’ve noticed slower map generation and slower FOV generation with the conversion to Dictionaries, from an Array. The reason I posted this was because I am looking for Nested Arrays, I just couldn’t figure out how to write the code.

@Beau_Seymour I had considered a Vector as a key, but can’t remember why I didn’t use it. However, my grid is smaller than your example, and it already runs significantly worse than how the tiles were stored in an Array (x is max 140 and y is max 60), but it does allow me to access a Tile a lot faster than hunting for a tile in Tiles and comparing grid_positions.

Ahh didn’t know that it is just GDScript inspired by Python.

@plsnotascii I know Arrays are more efficient. That is why I was asking how to create something similar to my nested Dictionaries, but as an Array instead.

I didn’t know that you could get a specific index easily with X*W+Y.

It looks like your approach might produce the desired results I’m looking for, so I’ll try creating my Tiles as an Array, and access the indices with your methods.

1 Like

wait! i made a mistake: it’s actually y*w+x (or x*h+y if you prefer columns)

(i edited my post above)

Yeah, I was about to post that your method doesn’t work, as it kept trying to modify an index greater than what the array had for indices. But the change you provided does fix the code changes I made based on your advice.

1 Like

@plsnotascii Appears your method works best for what I’m going for. I had to adapt the code provided, as my tiles require an X, Y when they are initialized, but otherwise your methodology works better than my Nested Dictionaries. Noticed a slightly faster map generation time and FOV generation.

Thank you to everyone who replied!

1 Like

happy to help then!

if you still need faster you can also try packed arrays: PackedByteArray — Godot Engine (stable) documentation in English and also see classess below in the sidebar

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.