10. Điều Khiển GridMap Bằng Code (Using GridMap In Code)

Ngoài việc vẽ các màn chơi tĩnh bằng tay trong Editor, bạn hoàn toàn có thể kiểm soát toàn bộ hệ thống GridMap thông qua mã nguồn GDScript hoặc C# lúc trò chơi đang chạy.

Điều này cực kỳ hữu ích cho các thể loại game: - Sinh hầm ngục, mê cung ngẫu nhiên (Procedural Generation / Roguelike). - Game sinh tồn xây dựng căn cứ dạng khối hộp (như Minecraft, Terraria 3D). - Đào xới, phá hủy địa hình, đặt bẫy hoặc xây tường thành trong các tựa game chiến thuật (RTS / Tower Defense).


🛠️ Bảng tra cứu các phương thức cốt lõi của class GridMap

Dưới đây là các hàm quan trọng nhất bạn sẽ thường xuyên sử dụng:

Phương thức (GDScript) Phương thức (C#) Ý nghĩa & Ứng dụng
set_cell_item(pos, item, orientation) SetCellItem(pos, item, orientation) Đặt một ô gạch có ID item vào tọa độ lưới Vector3i(x, y, z). Hướng xoay orientation mặc định là 0.
get_cell_item(pos) GetCellItem(pos) Đọc ID của khối tại ô tọa độ chỉ định. Nếu ô trống sẽ trả về hằng số GridMap.INVALID_CELL_ITEM (-1).
set_cell_item(pos, GridMap.INVALID_CELL_ITEM) SetCellItem(pos, GridMap.InvalidCellItem) Xóa bỏ khối gạch tại ô tọa độ chỉ định.
local_to_map(local_pos) LocalToMap(local_pos) Chuyển đổi tọa độ không gian 3D cục bộ (mét) sang tọa độ ô lưới nguyên Vector3i.
map_to_local(map_pos) MapToLocal(map_pos) Chuyển đổi tọa độ ô lưới nguyên Vector3i sang tọa độ không gian 3D tâm ô (mét).
get_used_cells() GetUsedCells() Trả về một danh sách mảng chứa tọa độ của toàn bộ các ô hiện đang có đặt khối mesh.
clear() Clear() Xóa sạch toàn bộ bản đồ GridMap chỉ trong một lệnh duy nhất.

💻 Mã nguồn mẫu hoàn chỉnh (GDScript & C#)

Đoạn mã dưới đây minh họa một kịch bản hoàn chỉnh gắn trực tiếp vào node GridMap để: 1. Tự động sinh phòng: Khi game khởi động (_ready), tự động tạo một căn phòng có sàn và 4 bức tường bao quanh bằng vòng lặp. 2. Đào & Đặt khối bằng chuột: Bắn tia RayCast từ vị trí trỏ chuột của người chơi, chuột trái đặt thêm tường và chuột phải đập bỏ khối gạch.

extends GridMap

# ID tương ứng của từng ô trong MeshLibrary.tres
# (0: sàn nhà, 1: tường)
const FLOOR_ITEM_ID: int = 0
const WALL_ITEM_ID: int = 1

func _ready() -> void:
    generate_room(12, 12)

# 1. Hàm tự động sinh phòng hình chữ nhật bằng code
func generate_room(width: int, depth: int) -> void:
    clear() # Xóa sạch các ô cũ nếu có

    for x in range(width):
        for z in range(depth):
            # Tạo sàn nhà tại cao độ y = 0
            set_cell_item(Vector3i(x, 0, z), FLOOR_ITEM_ID)

            # Xây tường bao quanh 4 cạnh viền
            if x == 0 or x == width - 1 or z == 0 or z == depth - 1:
                set_cell_item(Vector3i(x, 1, z), WALL_ITEM_ID)

# 2. Xử lý tương tác chuột thời gian thực (đặt / xóa khối)
func _unhandled_input(event: InputEvent) -> void:
    if event is InputEventMouseButton and event.pressed:
        var camera = get_viewport().get_camera_3d()
        if not camera:
            return

        var mouse_pos = event.position
        var ray_origin = camera.project_ray_origin(mouse_pos)
        var ray_normal = camera.project_ray_normal(mouse_pos)
        var ray_distance = 100.0

        var space_state = get_world_3d().direct_space_state
        var query = PhysicsRayQueryParameters3D.create(ray_origin, ray_origin + ray_normal * ray_distance)
        var result = space_state.intersect_ray(query)

        if result:
            var hit_pos = result.position
            var hit_normal = result.normal

            if event.button_index == MOUSE_BUTTON_LEFT:
                # Nhấp chuột trái: Đặt thêm khối vào mặt ngoài tiếp xúc
                var target_pos = hit_pos + hit_normal * (cell_size.y * 0.5)
                var cell_coord = local_to_map(to_local(target_pos))
                set_cell_item(cell_coord, WALL_ITEM_ID)

            elif event.button_index == MOUSE_BUTTON_RIGHT:
                # Nhấp chuột phải: Đập khối đang bị bấm trúng
                var target_pos = hit_pos - hit_normal * (cell_size.y * 0.5)
                var cell_coord = local_to_map(to_local(target_pos))
                set_cell_item(cell_coord, GridMap.INVALID_CELL_ITEM)
using Godot;

public partial class LevelGridMap : GridMap
{
    // ID tương ứng của từng ô trong MeshLibrary.tres
    private const int FloorItemId = 0;
    private const int WallItemId = 1;

    public override void _Ready()
    {
        GenerateRoom(12, 12);
    }

    // 1. Hàm tự động sinh phòng hình chữ nhật bằng code
    public void GenerateRoom(int width, int depth)
    {
        Clear(); // Xóa sạch các ô cũ nếu có

        for (int x = 0; x < width; x++)
        {
            for (int z = 0; z < depth; z++)
            {
                // Tạo sàn nhà tại tầng y = 0
                SetCellItem(new Vector3I(x, 0, z), FloorItemId);

                // Xây tường bao quanh 4 cạnh viền
                if (x == 0 || x == width - 1 || z == 0 || z == depth - 1)
                {
                    SetCellItem(new Vector3I(x, 1, z), WallItemId);
                }
            }
        }
    }

    // 2. Xử lý tương tác chuột thời gian thực (đặt / xóa khối)
    public override void _UnhandledInput(InputEvent @event)
    {
        if (@event is InputEventMouseButton mouseBtn && mouseBtn.Pressed)
        {
            var camera = GetViewport().GetCamera3D();
            if (camera == null) return;

            Vector2 mousePos = mouseBtn.Position;
            Vector3 rayOrigin = camera.ProjectRayOrigin(mousePos);
            Vector3 rayNormal = camera.ProjectRayNormal(mousePos);
            float rayDistance = 100.0f;

            var spaceState = GetWorld3D().DirectSpaceState;
            var query = PhysicsRayQueryParameters3D.Create(rayOrigin, rayOrigin + rayNormal * rayDistance);
            var result = spaceState.IntersectRay(query);

            if (result.Count > 0)
            {
                Vector3 hitPos = (Vector3)result["position"];
                Vector3 hitNormal = (Vector3)result["normal"];

                if (mouseBtn.ButtonIndex == MouseButton.Left)
                {
                    // Chuột trái: Đặt thêm khối vào mặt phẳng tiếp xúc
                    Vector3 targetPos = hitPos + hitNormal * (CellSize.Y * 0.5f);
                    Vector3I cellCoord = LocalToMap(ToLocal(targetPos));
                    SetCellItem(cellCoord, WallItemId);
                }
                else if (mouseBtn.ButtonIndex == MouseButton.Right)
                {
                    // Chuột phải: Xóa khối được nhấp trúng
                    Vector3 targetPos = hitPos - hitNormal * (CellSize.Y * 0.5f);
                    Vector3I cellCoord = LocalToMap(ToLocal(targetPos));
                    SetCellItem(cellCoord, GridMap.InvalidCellItem);
                }
            }
        }
    }
}

🎯 Tổng kết toàn bộ khóa học

Xin chúc mừng! Bạn đã hoàn thành toàn bộ chuyên đề Sử dụng GridMap trong Godot 3D. Giờ đây bạn đã nắm vững: - Cách tổ chức Scene nguồn cho MeshLibrary chuẩn cấu trúc Node3D > MeshInstance3D. - Gán tự động va chạm vật lý StaticBody3D và Bake lưới tìm đường AI NavigationMesh. - Bake ánh sáng tĩnh Lightmaps và các lưu ý về rãnh vật liệu. - Xuất thư viện lưới sang file tài nguyên .tres. - Thành thạo tất cả các thao tác vẽ, xoay, nhân bản trên thanh công cụ GridMap Panel. - Làm chủ bộ API set_cell_item, get_cell_item, local_to_map để lập trình sinh màn chơi động theo ý muốn!