--- name: godot-building-system description: Godot 4 建造系统实现,包含GridMap方块管理、第一人称视角、射线交互。 --- # Godot 建造系统实现 ## 概述 基于 Godot 4 引擎的 3D 建造类游戏,核心玩法是玩家收集资源并使用方块建造桥梁到达目标地点。 ## 1. GridMap 方块管理系统 ### 1.1 方块操作核心 ```gdscript extends GridMap signal put_success signal get_success func destroy_block(world_pos): var map_coords = local_to_map(world_pos) if get_cell_item(map_coords) != INVALID_CELL_ITEM: set_cell_item(map_coords, INVALID_CELL_ITEM) get_success.emit() audio_stream_player_3d.play() func place_block(world_pos, block_id): var map_coords = local_to_map(world_pos) set_cell_item(map_coords, block_id) put_success.emit() audio_stream_player_3d.play() ``` ### 1.2 GridMap 关键 API | 方法 | 作用 | |------|------| | `local_to_map(world_pos)` | 将世界坐标转换为网格坐标 | | `map_to_local(map_coords)` | 将网格坐标转换为世界坐标 | | `set_cell_item(map_coords, item)` | 在指定网格位置放置方块 | | `get_cell_item(map_coords)` | 获取指定位置的方块 ID | | `INVALID_CELL_ITEM` | 表示该位置无方块 | ## 2. 玩家控制器 ### 2.1 玩家场景结构 ``` Player (CharacterBody3D) ├── CollisionShape3D ├── MeshInstance3D ├── Camera3D │ ├── GetRayCast (RayCast3D) - 射线检测(用于获取/销毁方块) │ └── PutRayCast (RayCast3D) - 射线检测(用于放置方块) └── Runing (AudioStreamPlayer3D) ``` ### 2.2 玩家控制核心 ```gdscript class_name Player extends CharacterBody3D @onready var camera_3d: Camera3D = $Camera3D @onready var get_ray_cast: RayCast3D = $Camera3D/GetRayCast @onready var put_ray_cast: RayCast3D = $Camera3D/PutRayCast const SPEED = 16.0 const JUMP_VELOCITY = 12.0 const gravity = Vector3(0, -24, 0) func _unhandled_input(event: InputEvent) -> void: if event is InputEventMouseMotion: rotation.y -= event.relative.x * sensitivity camera_3d.rotation.x -= event.relative.y * sensitivity camera_3d.rotation.x = clamp(camera_3d.rotation.x, deg_to_rad(-70), deg_to_rad(85)) func _physics_process(delta: float) -> void: if not is_on_floor(): velocity += gravity * delta if Input.is_action_just_pressed("jump") and is_on_floor(): velocity.y = JUMP_VELOCITY var input_dir := Input.get_vector("left", "right", "forward", "back") var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized() if direction: velocity.x = direction.x * SPEED velocity.z = direction.z * SPEED else: velocity.x = move_toward(velocity.x, 0, SPEED) velocity.z = move_toward(velocity.z, 0, SPEED) # 左键 - 放置方块 if Input.is_action_just_pressed("left_click"): if put_ray_cast.is_colliding(): put_block.emit(put_ray_cast.get_collision_point() + put_ray_cast.get_collision_normal()) # 右键 - 销毁方块 if Input.is_action_just_pressed("right_click"): if get_ray_cast.is_colliding(): get_block.emit(get_ray_cast.get_collision_point() - get_ray_cast.get_collision_normal()) move_and_slide() ``` ### 2.3 射线检测配置 ```gdscript # GetRayCast - 检测 GridMap 层 [GetRayCast] target_position = Vector3(0, 0, -10) collision_mask = 2 # PutRayCast - 检测地面层和玩家层 [PutRayCast] target_position = Vector3(0, 0, -10) collision_mask = 3 ``` ## 3. 游戏世界管理器 ### 3.1 游戏逻辑核心 ```gdscript extends Node3D @onready var grid_map_2: GridMap = $GridMap2 var remaining_block = 5: set(val): remaining_block = val remaining_block_count.text = "剩余方块数量:" + str(val) func handle_put_block(pos): if remaining_block <= 0: return grid_map_2.place_block(pos, 16) func handle_get_block(pos): grid_map_2.destroy_block(pos) func handle_put_seccess(): remaining_block -= 1 func handle_get_seccess(): remaining_block += 1 ``` ## 4. 坐标系统理解 ```gdscript # 世界坐标 -> 网格坐标 var map_pos = grid_map.local_to_map(world_pos) # 网格坐标 -> 世界坐标(方块中心点) var center_pos = grid_map.map_to_local(map_pos) ``` ## 5. 放置/销毁逻辑 ```gdscript # 放置:在碰撞点 + 法线方向(让方块紧贴表面) place_pos = collision_point + collision_normal # 销毁:在碰撞点 - 法线方向(移除被点击的方块) destroy_pos = collision_point - collision_normal ``` ## 6. GridMap 双层设计 - `GridMap` (collision_layer=5) - 地形方块,射线检测层 2 - `GridMap2` (collision_layer=6) - 玩家放置的方块,射线检测层 3 这种设计确保玩家只能放置/销毁自己放置的方块,不能破坏地形。