You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Godot 3D网格对齐拖拽卡牌,避免重叠与超出游戏板边界

网格吸附+边界校验实现方案

核心实现逻辑为:将刚体游戏板的表面划分为与卡牌尺寸匹配的固定网格,每次射线检测到放置位置后,先将坐标对齐到最近的网格格子中心,再依次校验坐标是否在游戏板边界内、对应网格是否已被占用,全部符合条件再生成卡牌,即可解决重叠和超出边界的问题。

第一步:定义网格与边界全局参数

在脚本顶部新增以下变量,数值根据你项目中卡牌、游戏板的实际尺寸调整即可:

# 网格格子尺寸,对应单张卡牌的长宽(3D场景下游戏板平面一般为XZ轴)
var grid_cell_width = 5.0
var grid_cell_height = 3.0
# 游戏板坐标边界,可在编辑器中选中游戏板节点查看其尺寸范围后填写
var board_min_x = -20.0
var board_max_x = 20.0
var board_min_z = -10.0
var board_max_z = 10.0
# 存储已被占用的网格,key为网格坐标拼接的字符串,value为对应卡牌实例
var occupied_grid = {}

第二步:新增网格对齐工具函数

# 将射线检测到的世界坐标转换为最近的网格中心坐标
func snap_to_grid(world_pos: Vector3) -> Vector3:
    var grid_x = round(world_pos.x / grid_cell_width)
    var grid_z = round(world_pos.z / grid_cell_height)
    var snapped_x = grid_x * grid_cell_width
    var snapped_z = grid_z * grid_cell_height
    return Vector3(snapped_x, world_pos.y, snapped_z)

第三步:修改place()函数,新增校验逻辑

func place():
    var rayOrigin = Vector3()
    var rayEnd = Vector3()
    var cardbase = preload("res://Cards/CardBase.tscn")
    var card = preload("res://Cards/3dcard.tscn")
    var cardscript = preload("res://Cards/3dcard.gd")
    var camera = get_tree().get_root().find_node("Camera", true, false)
    if Input.is_action_just_released("leftclick") && cardinhand == 1 :
        var space_state = get_world().direct_space_state
        var mouse_position = get_viewport().get_mouse_position()
        rayOrigin = camera.project_ray_origin(mouse_position)
        rayEnd = rayOrigin + camera.project_ray_normal(mouse_position) * 2000000000
        var intersection = space_state.intersect_ray(rayOrigin, rayEnd)
        if not intersection.empty(): 
            # 校验点击对象是否为游戏板,避免点到其他物体也生成卡牌
            if intersection.collider.name != "替换为你游戏板节点的实际名称":
                return
            # 坐标对齐网格
            var target_pos = snap_to_grid(intersection.position)
            # 校验坐标是否在游戏板边界内
            if target_pos.x < board_min_x or target_pos.x > board_max_x or target_pos.z < board_min_z or target_pos.z > board_max_z:
                return
            # 校验当前网格是否已被占用
            var grid_key = str(round(target_pos.x/grid_cell_width)) + "," + str(round(target_pos.z/grid_cell_height))
            if grid_key in occupied_grid:
                return
            # 所有校验通过后生成卡牌
            board = 1
            var addcard = card.instance()
            addcard.Cardname = cardnm
            add_child(addcard)
            # 轻微抬升Y轴避免卡牌与游戏板穿模
            addcard.translation = Vector3(target_pos.x, intersection.position.y + 0.01, target_pos.z)
            # 标记当前网格为已占用
            occupied_grid[grid_key] = addcard
        cardinhand = 0

补充说明

如果后续需要实现卡牌移除功能,只需删除occupied_grid中对应的key即可释放对应网格,不影响后续卡牌放置。

内容的提问来源于stack exchange,提问作者TheBlackHorse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 07:48:01