继续开发。

in STEEM CN/中文5 days ago

{
"template_name": "Software_Development_Prompt_V1",
"request_id": "DEV-2025-01-001",
"developer_role": "高级 godot游戏开发 工程师",
"project_context": {
"project_name": "3D水上交通事故模拟",
"technology_stack": [
"godot",
"GDscript",
"sqlite",
"python"
],
"goals": "开发一个使用godot游戏引擎的3d水上交通事故模拟应用"
},
"primary_task": "请实现一个3D的模拟内河船舶航行的实时渲染动画应用",
"technical_requirements": [
"可以使用用户指定的不同的船舶模型",
"具有真实感的水面shader",
"船舶模型按照给定的AIS经纬度序列所描述的轨迹在水面上航行",
"AIS经纬度序列从sqlite数据库文件中读取"
],

"expected_output_format": {
"format_type": "markdown",
"content_structure": [
"1. 简要的实现思路说明(Markdown 文本)",
"2. 详细的godot操作步骤((用户是初学者,不要遗漏或跳过任何步骤)",
"2. 完整的 TypeScript 代码块(文件名:authMiddleware.ts)"
],
"explanation_required": true
},
"constraints": {
"max_length_lines": 200,
"language": "中文"
}
}

3D水上交通事故模拟应用开发指南

作为一名高级Godot游戏开发工程师,我将为您详细阐述如何利用Godot引擎实现一个3D内河船舶航行的实时渲染动画应用。本指南将涵盖从核心思路、数据准备到Godot引擎内详细操作的每一个环节,旨在帮助初学者也能顺利完成项目。

  1. 简要实现思路说明

项目的核心是将真实的船舶自动识别系统(AIS)轨迹数据,在Godot 3D场景中进行可视化重现。

基本思路分为以下三步:

数据预处理: Godot本身直接读取SQLite数据库操作较为复杂,且不适合在游戏循环中频繁调用。因此,我们采用一个独立的Python脚本预先从SQLite数据库文件中提取AIS经纬度序列,并将其转换为Godot易于读取的JSON格式。同时,为了在3D世界中展示,需要将地球上的经纬度坐标(Longitude, Latitude)映射为Godot场景中的Vector3(x, y, z)坐标。为简化问题,我们将经度映射到X轴,纬度映射到Z轴,并进行适当缩放。

场景搭建: 在Godot中创建一个3D场景。该场景将包含一个广阔的平面作为水面,并为其应用一个带有动态效果的着色器(Shader)以模拟真实感的水波。接着,导入用户指定的船舶模型,并为场景添加基础的环境光和定向光(模拟太阳光)以及摄像机。

动态航行实现: 为船舶模型节点挂载一个GDScript脚本。该脚本在启动时(_ready函数)加载并解析上一步生成的JSON轨迹文件。然后在每一帧的更新中(_process函数),脚本会读取下一个轨迹点作为目标,控制船舶朝目标点移动和转向。通过线性插值(lerp)等方法,可以实现船舶平滑地沿着轨迹航行,直至所有AIS点都遍历完成。

  1. 详细的Godot操作步骤

第一步:数据预处理 (使用Python)

在开始Godot项目之前,我们先将SQLite中的数据提取出来。

创建Python脚本: 在你的项目文件夹(Godot项目文件夹之外)创建一个名为 process_ais.py 的文件。

编写脚本: 复制以下代码到文件中。假设你的SQLite数据库文件名为 ais_data.db,表名为 ship_track,经纬度字段分别为 longitude 和 latitude。

code
Python
download
content_copy
expand_less
import sqlite3
import json

--- 配置参数 ---

DB_FILE = 'ais_data.db' # 你的SQLite文件名
TABLE_NAME = 'ship_track' # 表名
LON_COLUMN = 'longitude' # 经度字段名
LAT_COLUMN = 'latitude' # 纬度字段名
OUTPUT_FILE = 'trajectory.json' # 输出的JSON文件名

--- 连接并查询数据库 ---

try:
conn = sqlite3.connect(DB_FILE)
cursor = conn.cursor()

query = f"SELECT {LON_COLUMN}, {LAT_COLUMN} FROM {TABLE_NAME} ORDER BY timestamp ASC;" # 假设有时间戳字段用于排序
cursor.execute(query)

rows = cursor.fetchall()

# --- 转换为字典列表 ---
trajectory = []
for row in rows:
    trajectory.append({'lon': row[0], 'lat': row[1]})
    
# --- 写入JSON文件 ---
with open(OUTPUT_FILE, 'w') as f:
    json.dump(trajectory, f, indent=4)
    
print(f"成功从 {DB_FILE} 提取 {len(rows)} 个轨迹点到 {OUTPUT_FILE}")

except sqlite3.Error as e:
print(f"数据库错误: {e}")
finally:
if conn:
conn.close()

运行脚本: 在终端中运行 python process_ais.py。成功后,你会在同目录下找到一个 trajectory.json 文件,这就是我们要在Godot中使用的轨迹数据。

第二步:搭建Godot 3D场景

创建项目: 打开Godot引擎,新建一个项目,例如命名为 WaterSimulation。

创建主场景:

在“场景”面板中,点击“3D场景”创建一个以 Node3D 为根节点的场景。

立即保存场景,命名为 Main.tscn。

添加环境和光照:

在“场景”面板中选中 Node3D,点击加号按钮,添加一个 WorldEnvironment 节点。

在右侧“检查器”中,点击 Environment 属性旁的 [空],选择“新建 Environment”。

再次点击新创建的 Environment 资源,在 Background 部分,将 Mode 设置为 Sky。

点击 Sky 属性旁的 [空],选择“新建 Sky”。

再次点击 Sky 资源,在 Sky Material 属性旁,选择“新建 PanoramaSkyMaterial”(或ProceduralSkyMaterial)。这样场景就有了天空背景。

回到场景树,再次选中 Node3D,添加一个 DirectionalLight3D 节点作为太阳光。你可以通过旋转该节点来改变光照方向。

创建水面:

选中 Node3D,添加一个 MeshInstance3D 节点,并将其重命名为 Water。[1]

在“检查器”中,点击 Mesh 属性旁的 [空],选择“新建 PlaneMesh”。[1]

点击展开 PlaneMesh,设置 Size 的 x 和 z 值为一个较大的数字,例如 2000,以创建广阔的水域。

在 Material 属性旁,点击 [空],选择“新建 ShaderMaterial”。[2][3]

点击新创建的 ShaderMaterial 资源,在其 Shader 属性旁,点击 [空],选择“新建 Shader”。

点击新创建的 Shader 资源,此时底部会弹出着色器编辑器。在这里你可以编写或粘贴水面着色器代码。你可以从Godot资源商店或在线社区(如Godot Shaders网站)找到许多优秀的开源水面着色器代码。[2][4][5]

导入船舶模型:

Godot推荐使用glTF 2.0 (.gltf 或 .glb) 格式的模型。[6][7]

将你的船舶模型文件(例如 boat.glb)和 trajectory.json 文件一起拖拽到Godot编辑器左下角的“文件系统”面板中。[8][9] Godot会自动处理导入过程。

将文件系统中的 boat.glb 文件拖拽到3D视窗中,或场景树中的 Node3D 节点下。你将看到船舶模型出现在场景中。将其节点重命名为 Ship。

调整摄像机:

在场景树中添加一个 Camera3D 节点。

通过移动和旋转 Camera3D 节点,调整好观察船舶的初始视角。在视窗右上角点击“预览”可以查看摄像机画面。

  1. 完整的 GDScript 代码块

注意: 您在需求中提到的 TypeScript 和文件名 authMiddleware.ts 适用于Web后端开发,与Godot引擎的GDScript开发环境不符。以下将提供项目所需的正确GDScript代码。

文件名: ShipController.gd

创建并附加脚本: 在“场景”面板中选中 Ship 节点,然后在“检查器”面板底部点击“附加脚本”按钮。将脚本命名为 ShipController.gd 并创建。

编写代码: 将以下代码完整地复制到脚本编辑器中。

code
Gdscript
download
content_copy
expand_less
extends Node3D

导出变量,可以在Godot编辑器中调整

@export var speed: float = 10.0 # 船舶航行速度 (米/秒)
@export var rotation_speed: float = 1.0 # 船舶转向速度
@export var map_scale: float = 1000.0 # 经纬度到Godot坐标的缩放系数

内部变量

var trajectory_points: Array = []
var current_target_index: int = 0
var is_moving: bool = false

func _ready():
# 加载并解析AIS轨迹数据
load_trajectory("res://trajectory.json")

# 如果有轨迹点,则设置初始位置和朝向
if !trajectory_points.is_empty():
    var initial_position = _convert_lon_lat_to_vector3(trajectory_points[0])
    self.global_transform.origin = initial_position
    
    if trajectory_points.size() > 1:
        var next_position = _convert_lon_lat_to_vector3(trajectory_points[1])
        look_at(next_position, Vector3.UP)
        
    current_target_index = 1
    is_moving = true

func _process(delta: float):
if !is_moving or current_target_index >= trajectory_points.size():
return # 如果不在移动或已到达终点,则不执行任何操作

# 获取当前目标点
var target_global_position = _convert_lon_lat_to_vector3(trajectory_points[current_target_index])

# 计算到目标点的距离和方向
var current_position = self.global_transform.origin
var direction_to_target = current_position.direction_to(target_global_position)
var distance_to_target = current_position.distance_to(target_global_position)

# --- 转向 ---
# 计算目标方向和当前方向的夹角,并平滑转向
var target_basis = self.transform.basis.looking_at(direction_to_target, Vector3.UP)
self.transform.basis = self.transform.basis.slerp(target_basis, rotation_speed * delta)

# --- 移动 ---
# 如果距离目标点足够近,则切换到下一个目标点
if distance_to_target < 1.0: # 1米的阈值
    current_target_index += 1
    if current_target_index >= trajectory_points.size():
        is_moving = false # 到达最后一个点,停止移动
        print("航行结束!")
        return

# 向前移动
var velocity = -self.global_transform.basis.z * speed
self.global_position += velocity * delta

从JSON文件加载轨迹数据

func load_trajectory(file_path: String):
var file = FileAccess.open(file_path, FileAccess.READ)
if file == null:
printerr("无法打开轨迹文件: ", file_path)
return

var content = file.get_as_text()
var json = JSON.new()
var error = json.parse(content)
if error != OK:
    printerr("JSON解析错误: ", json.get_error_message(), " in ", content, " at line ", json.get_error_line())
    return

trajectory_points = json.get_data()
print("成功加载 ", trajectory_points.size(), " 个轨迹点。")

将经纬度坐标转换为Godot的3D世界坐标

func _convert_lon_lat_to_vector3(point_dict: Dictionary) -> Vector3:
# 这是一个简化的线性映射,适用于小范围区域的模拟
# lat -> -Z, lon -> X, Y是高度保持为0
if point_dict.has("lon") and point_dict.has("lat"):
var lon = float(point_dict["lon"])
var lat = float(point_dict["lat"])
# 注意:通常纬度对应Z轴,经度对应X轴。
# 这里我们将lat乘以-1,是为了符合Godot中Z轴负方向为“向前”的惯例。
return Vector3(lon * map_scale, 0, -lat * map_scale)
return Vector3.ZERO

最后一步:运行模拟

保存所有文件和场景,然后点击Godot编辑器右上角的“运行项目”按钮(或按F5)。如果一切配置正确,你将看到船舶在水面上,沿着trajectory.json文件中定义的轨迹平滑航行。你可以在编辑器中调整ShipController脚本导出的speed和map_scale等参数,以获得最佳的模拟效果。

Sources
help
youtube.com
youtube.com
youtube.com
godotassetlibrary.com
godotshaders.com
godotengine.org
coohom.com
youtube.com
youtube.com
Google Search Suggestions
Display of Search Suggestions is required when using Grounding with Google Search. Learn more
godot 3d realistic water shader
Godot 导入3D模型
Godot GDScript 读取 SQLite 数据库
Godot GDScript解析AIS数据
Godot 根据经纬度序列移动3D模型
Godot 3D 船舶航行模拟教程

Sort:  

Upvoted! Thank you for supporting witness @jswit.