Godot 城市模拟 – 003 根据不规则底面和高度,动态创建节点

下面我将详细分解这段代码的每个步骤,展示如何在 Godot 引擎中从零开始创建一个不规则棱柱体并实现旋转动画:

步骤1:设置场景基础结构

gdscript 复制代码
extends Node3D  # 继承自3D场景节点

var prism_mesh_instance: MeshInstance3D  # 声明棱柱体网格实例变量
  • 脚本继承自 Node3D,这是所有3D场景的基类
  • 声明一个 MeshInstance3D 类型的变量,用于存储创建的棱柱体

步骤2:初始化场景 (_ready 函数)

gdscript 复制代码
func _ready() -> void:
    # 定义底面多边形的5个顶点坐标
    var base_points = [
        Vector3(-3, 0, -2),  # 点1 (X, Y, Z)
        Vector3(2, 0, -3),   # 点2
        Vector3(4, 0, 1),    # 点3
        Vector3(1, 0, 4),    # 点4
        Vector3(-2, 0, 3)    # 点5
    ]
    
    # 设置棱柱体的高度
    var height = 2.5
    
    # 调用创建棱柱体的核心函数
    create_prism_from_base_and_height(base_points, height)
  • 在场景加载时自动执行
  • 定义了一个不规则五边形的底面顶点坐标(所有Y值为0,位于XZ平面)
  • 设置棱柱体的高度为2.5个单位
  • 调用核心函数创建棱柱体

步骤3:创建棱柱体核心函数

3.1 验证输入和准备数据

gdscript 复制代码
func create_prism_from_base_and_height(base_points: Array, height: float) -> void:
    # 验证输入是否有效(至少3个点才能构成多边形)
    if base_points.size() < 3:
        print("错误:至少需要3个点来构成多边形")
        return
    
    var point_count = base_points.size()
    var prism_points = []  # 存储所有顶点(底面+顶面)
  • 检查底面点数是否足够(至少3个点)
  • 获取底面点数
  • 初始化顶点数组

3.2 计算顶点位置

gdscript 复制代码
    # 添加底面顶点
    for i in range(point_count):
        prism_points.append(base_points[i])
    
    # 添加顶面顶点(底面顶点+Y方向偏移)
    for i in range(point_count):
        var top_point = base_points[i] + Vector3(0, height, 0)
        prism_points.append(top_point)
  • 首先添加所有底面顶点
  • 然后添加顶面顶点(每个底面顶点沿Y轴向上偏移高度值)

3.3 使用 SurfaceTool 创建网格

gdscript 复制代码
    # 初始化SurfaceTool
    var surface_tool = SurfaceTool.new()
    surface_tool.begin(Mesh.PRIMITIVE_TRIANGLES)
  • 创建 SurfaceTool 实例,用于动态生成网格
  • 指定网格使用三角形图元

3.4 创建底面

gdscript 复制代码
    # 计算底面中心点(用于三角剖分)
    var bottom_center = Vector3.ZERO
    for point in base_points:
        bottom_center += point
    bottom_center /= point_count
    bottom_center.y = 0  # 确保在底面平面上
    
    # 创建底面三角形(扇形三角剖分)
    for i in range(point_count):
        var next_i = (i + 1) % point_count
        surface_tool.add_vertex(bottom_center)        # 中心点
        surface_tool.add_vertex(prism_points[i])      # 当前点
        surface_tool.add_vertex(prism_points[next_i]) # 下一个点
  • 计算底面多边形的中心点(所有顶点的平均值)
  • 使用扇形三角剖分创建底面:
    • 从中心点出发
    • 连接相邻顶点形成三角形

3.5 创建顶面

gdscript 复制代码
    # 计算顶面中心点
    var top_center = bottom_center + Vector3(0, height, 0)
    var top_start = point_count  # 顶面顶点起始索引
    
    # 创建顶面三角形(类似底面)
    for i in range(point_count):
        var next_i = (i + 1) % point_count
        surface_tool.add_vertex(top_center)                  # 顶部中心点
        surface_tool.add_vertex(prism_points[top_start + i]) # 当前顶部点
        surface_tool.add_vertex(prism_points[top_start + next_i]) # 下一个顶部点
  • 顶面中心点是底面中心点沿Y轴偏移高度值
  • 使用相同的扇形三角剖分方法创建顶面

3.6 创建侧面

gdscript 复制代码
    # 创建侧面(四边形分解为两个三角形)
    for i in range(point_count):
        var next_i = (i + 1) % point_count
        
        # 第一个三角形
        surface_tool.add_vertex(prism_points[i])                   # 底部当前点
        surface_tool.add_vertex(prism_points[top_start + i])        # 顶部当前点
        surface_tool.add_vertex(prism_points[next_i])               # 底部下一个点
        
        # 第二个三角形
        surface_tool.add_vertex(prism_points[next_i])               # 底部下一个点
        surface_tool.add_vertex(prism_points[top_start + i])        # 顶部当前点
        surface_tool.add_vertex(prism_points[top_start + next_i])   # 顶部下一个点
  • 每个侧面由四边形组成
  • 每个四边形被分解为两个三角形:
    1. 底部点A → 顶部点A → 底部点B
    2. 底部点B → 顶部点A → 顶部点B
  • 确保所有三角形顶点顺序为逆时针(保证法线方向正确)

3.7 生成网格和材质

gdscript 复制代码
    # 生成法线(用于光照计算)
    surface_tool.generate_normals()
    
    # 提交网格数据
    var array_mesh = surface_tool.commit()
    
    # 创建网格实例
    prism_mesh_instance = MeshInstance3D.new()
    prism_mesh_instance.mesh = array_mesh
    
    # 创建材质
    var material = StandardMaterial3D.new()
    material.albedo_color = Color(0.2, 0.6, 0.8)  # 蓝色
    material.cull_mode = BaseMaterial3D.CULL_DISABLED  # 禁用背面剔除
    prism_mesh_instance.material_override = material
    
    # 添加到场景
    add_child(prism_mesh_instance)
  • 自动生成顶点法线(使光照效果正确)
  • 提交网格数据生成 ArrayMesh
  • 创建 MeshInstance3D 节点并设置网格
  • 创建蓝色材质并禁用背面剔除(保证从内部也能看到)
  • 将棱柱体添加到场景中

3.8 设置摄像机视角

gdscript 复制代码
    # 定位摄像机
    $Camera3D.position = Vector3(0, 10, 10)  # 摄像机位置(斜上方)
    $Camera3D.look_at(prism_mesh_instance.position)  # 看向棱柱体中心
  • 将摄像机放置在 (0, 10, 10) 位置
  • 让摄像机看向棱柱体中心点 (0, 0, 0)

步骤4:实现旋转动画 (_process 函数)

gdscript 复制代码
func _process(delta: float) -> void:
    if prism_mesh_instance:
        # 每秒绕Y轴旋转30度
        prism_mesh_instance.rotate_y(deg_to_rad(30) * delta)
  • 每帧执行一次
  • 检查棱柱体是否已创建
  • 使用 rotate_y() 方法绕Y轴旋转
  • deg_to_rad(30) 将角度转换为弧度
  • 乘以 delta 保证旋转速度与帧率无关

最终效果

当运行此代码时:

  1. 场景初始化时创建一个不规则五边形底面的棱柱体
  2. 棱柱体呈现蓝色,禁用背面剔除
  3. 摄像机从斜上方 (0,10,10) 位置看向棱柱体中心
  4. 棱柱体以每秒30度的速度绕Y轴匀速旋转

完整代码

ini 复制代码
extends Node3D

var prism_mesh_instance: MeshInstance3D

func _ready() -> void:
# Called when the node enters the scene tree for the first time.
	var base_points = [
		Vector3(-3, 0, -2),  # 点1
		Vector3(2, 0, -3),   # 点2
		Vector3(4, 0, 1),    # 点3
		Vector3(1, 0, 4),    # 点4
		Vector3(-2, 0, 3)    # 点5
	]
	
	# 棱柱体高度
	var height = 2.5
	
	# 调用函数创建棱柱体
	create_prism_from_base_and_height(base_points, height)

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	if prism_mesh_instance:
		# 绕y轴旋转,每秒旋转30度
		prism_mesh_instance.rotate_y(deg_to_rad(30) * delta)


# 根据任意多边形底面和高度创建棱柱体
func create_prism_from_base_and_height(base_points: Array, height: float) -> void:
	# 验证输入
	if base_points.size() < 3:
		print("错误:至少需要3个点来构成多边形")
		return
	
	var point_count = base_points.size()
	
	# 计算棱柱体的所有顶点
	var prism_points = []
	
	# 底部点(底面多边形)
	for i in range(point_count):
		prism_points.append(base_points[i])
	
	# 顶部点(底面多边形加上高度)
	for i in range(point_count):
		var top_point = base_points[i] + Vector3(0, height, 0)
		prism_points.append(top_point)
	
	# 使用SurfaceTool创建棱柱体
	var surface_tool = SurfaceTool.new()
	surface_tool.begin(Mesh.PRIMITIVE_TRIANGLES)
	
	# 创建底面(多边形三角剖分 - 使用扇形三角剖分)
	var bottom_center = Vector3.ZERO
	for point in base_points:
		bottom_center += point
	bottom_center /= point_count
	bottom_center.y = 0  # 确保在底面平面上
	
	for i in range(point_count):
		var next_i = (i + 1) % point_count
		surface_tool.add_vertex(bottom_center)          # 中心点
		surface_tool.add_vertex(prism_points[i])        # 当前点
		surface_tool.add_vertex(prism_points[next_i])   # 下一个点
	
	# 创建顶面(多边形三角剖分 - 使用扇形三角剖分)
	var top_start = point_count
	var top_center = bottom_center + Vector3(0, height, 0)
	
	for i in range(point_count):
		var next_i = (i + 1) % point_count
		surface_tool.add_vertex(top_center)                         # 顶部中心点
		surface_tool.add_vertex(prism_points[top_start + i])        # 当前顶部点
		surface_tool.add_vertex(prism_points[top_start + next_i])   # 下一个顶部点
	
	# 创建侧面(连接底部和顶部的四边形面)
	for i in range(point_count):
		var next_i = (i + 1) % point_count  # 下一个点的索引(循环)
		
		# 侧面四边形分解为两个三角形
		# 第一个三角形(逆时针顺序)
		surface_tool.add_vertex(prism_points[i])                    # 底部当前点
		surface_tool.add_vertex(prism_points[top_start + i])       # 顶部当前点
		surface_tool.add_vertex(prism_points[next_i])              # 底部下一个点
		
		# 第二个三角形(逆时针顺序)
		surface_tool.add_vertex(prism_points[next_i])              # 底部下一个点
		surface_tool.add_vertex(prism_points[top_start + i])       # 顶部当前点
		surface_tool.add_vertex(prism_points[top_start + next_i])  # 顶部下一个点
	
	surface_tool.generate_normals()
	# 生成网格
	var array_mesh = surface_tool.commit()
	
	# 创建节点和材质
	prism_mesh_instance = MeshInstance3D.new()
	prism_mesh_instance.mesh = array_mesh
	
	var material = StandardMaterial3D.new()
	material.albedo_color = Color(0.2, 0.6, 0.8)  # 蓝色
	material.cull_mode = BaseMaterial3D.CULL_DISABLED  # 禁用背面剔除
	prism_mesh_instance.material_override = material
	
	add_child(prism_mesh_instance)
	print("基于不规则多边形底面创建棱柱体完成!点数:", point_count)
	
	$Camera3D.position = Vector3(0,10,10)
	
	$Camera3D.look_at(prism_mesh_instance.position)
相关推荐
Brianna Home2 天前
Godot4.3开发2D游戏全记录
游戏·游戏引擎·godot·游戏程序·动画
小武的开发空间3 天前
Godot 城市模拟 - 002 根据地面四个点的坐标和高度,动态创建立方体节点
godot
陈尕六14 天前
从零开始的 Godot 之旅 — EP5:控制角色移动
godot·游戏开发
陈尕六18 天前
从零开始的 Godot 之旅 — EP4:工作区基础操作与检查器初识
godot·游戏开发
新石器程序员18 天前
借鉴bevy实现适用于Godot-rust的状态管理
rust·游戏引擎·godot·bevy
陈尕六20 天前
从零开始的 Godot 之旅 — EP3:Hello World
godot
相与还23 天前
【2D横版游戏开发】godot实现tileMap地图
android·游戏引擎·godot
相与还1 个月前
godot+c#使用godot-sqlite连接数据库
数据库·c#·godot
相与还1 个月前
godot+c#操作sqlite并加解密
sqlite·c#·godot·sqlcipher