func is_within_bounds(cell_coordinates: Vector2) -> bool: # This method and the following one allow us to ensure the cursor or units can never go past the # map's limit. # Returns true if the `cell_coordinates` are within the grid. func calculate_grid_coordinates(map_position: Vector2) -> Vector2: We'll use this function to find # the grid coordinates they're placed on, and call `calculate_map_position()` to snap them to the # cell's center. # When designing a level, you'll place units visually in the editor. # This is the complementary of `calculate_map_position()` above. # Returns the coordinates of the cell on the grid given a position on the map. Return grid_position * cell_size + _half_cell_size func calculate_map_position(grid_position: Vector2) -> Vector2: # We'll place units and have them move through cells using this function. var _half_cell_size = cell_size / 2 # Returns the position of a cell's center in pixels. # That's how we can place units in the center of a cell. # We will use this to calculate the center of a grid cell in pixels, on the screen. export var cell_size : = Vector2( 80, 80) # It's meant to be shared between game objects that need access to those values.Įxtends Resource # The grid's size in rows and columns. # Represents a grid with its size, the size of each cell in pixels, and some helper functions to # calculate and convert coordinates. Name it Grid.gd and make it extend Resource. Right-click on the FileSystem tab and create a new script where we’ll define our Grid class. Import the start-project/ directory in Godot.
Changing the grid’s size in one place will update the data for all other nodes. I mentioned that with a resource, every reference to the grid will point to the same instance in memory. This will allow us to save it as a file in the FileSystem tab and to attach it directly to the nodes that need it in the Inspector. Our Grid will extend the built-in Resource type. They end up having to rewrite code late in a project’s lifecycle painfully. Or they mutate the singleton’s state directly from various places, making their code bug-prone.Īnd not only beginners do that: I’ve seen professionals use singletons carelessly many times over. People access them in illogical places, adding unnecessary coupling, because it’s the easiest thing to do at a given point in time. I keep seeing developers use singletons in fragile ways. However, when you create a singleton, as anything can access it, any bug that involves it can be anywhere in your codebase. If you’re really careful and intentional about how you use singletons and prefer their convenience, you may use them instead. Using a “less convenient” option removes the temptation of using the singleton in unintended ways and coupling it to more parts of the game than needed. This forces you to be deliberate about giving nodes access to the grid: you must define a dedicated variable in their script.
What’s the point, if you can use a singleton instead? Unlike singletons, only the nodes that explicitly hold a reference to a resource can access it. Like singletons, many nodes can share access to a given resource.
There is an excellent alternative to using a singleton in Godot: using a resource. But in a complete game project, this number won’t change. In our demo, which focuses on movement, it happens to be all the scenes we’ll code. However, you don’t need everything in the game to access the Grid: only five classes need it. That way, every script in the codebase could access it. In Godot, an option to give many objects access to another is to make the Grid an Autoload (singleton).
In our demo, five classes will need access to the grid’s data and helper functions, as we saw in the introduction. Units have to move to the middle of cells, the player’s cursor has to move along the grid, and so on. In a game like a turn-based tactical RPG, many actions take place on a grid.