news 2026/9/18 16:35:16

打开 Godot 官方演示仓库,如何 10 分钟跑通自己的第一款游戏?零基础五步走到寻路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
打开 Godot 官方演示仓库,如何 10 分钟跑通自己的第一款游戏?零基础五步走到寻路

打开 Godot 官方演示仓库,如何 10 分钟跑通自己的第一款游戏?零基础五步走到寻路

【免费下载链接】godot-demo-projectsDemonstration and Template Projects项目地址: https://gitcode.com/GitHub_Trending/go/godot-demo-projects

刚把 godot-demo-projects 拉下来,打开就愣住了:2d、3d、mono 一堆文件夹,四十多个演示项目,到底从哪个下手?这是 Godot 引擎官方的演示项目集,每个文件夹都是一个能直接跑起来的小游戏,也是学 Godot 游戏开发最省时间的材料。这篇文章给你一条从零基础到寻路 AI 的具体练习路线。

先看难度再挑演示项目:一条 10 分钟的上手路线

仓库里 demo 从十几个文件到完整工程都有,挑的时候别挑"最酷"的,挑"能完整读懂"的。三个有代表性的候选:

演示项目你能学到什么需要先会什么跑通耗时难度
Pong场景结构、信号驱动碰撞知道怎么运行工程10 分钟
躲避小怪(Dodge the Creeps)Timer、场景实例化、计分 HUD认识节点是什么半小时★★
有限状态机(FSM)用状态机组织角色动作2D 移动能读懂一小时★★★

一句话概括这条梯度:Pong 用来"跑通",Dodge 用来理解"一个游戏怎么拼起来",FSM 用来看"角色代码该怎么组织"。3D 的 demo 都依赖模型资源,先放到 3D 那一节再说。

跑通 Pong 只需要四步:

  1. 本地没有代码就克隆一份:
git clone https://gitcode.com/GitHub_Trending/go/godot-demo-projects
  1. 打开 Godot 编辑器,选"导入",指向2d/pong里的 project.godot。
  2. 按 F5 运行。球开始弹、挡板跟鼠标走——恭喜,第一个演示项目已经跑通。
  3. 打开 pong.tscn 场景,你会看到 Ball、Left、Right、Wall 四个节点,每个都挂着脚本;再把 ball.gd 通读一遍,整个游戏一共就 12 行核心逻辑。

到这里你已经会三件事:导入工程、运行、读场景树。后面所有 demo 都是这个套路。

用 Pong 串讲 2D 游戏的三个核心:移动、碰撞、计分

Pong 小到离谱,但 2D 游戏开发里最关键的三件事——移动、碰撞、计分——它全占了。下面按四步拆开看。

第 1 步:整个游戏就是 4 个节点

场景里没有"主控制器",没有全局管理器,Ball、Left、Right、Wall 各自挂一个脚本,靠信号互相喊话。这个"去中心化"的结构是 Godot 的基本哲学:节点自治,事件通信。

第 2 步:球只干两件事

ball.gd 的_process里真正起作用的就两行,下面是球每帧做的事:

func _process(delta: float) -> void: _speed += delta * 2 position += _speed * delta * direction

关键细节在第一行:_speed += delta * 2让球每回合越打越快,reset()里才把它拉回默认值——这就是为什么拉锯几拍之后球会变得很难接。

第 3 步:挡板教会球往哪飞

挡板脚本 paddle.gd 干两件事:按输入上下移动自己,被撞到时改写球的方向:

func _process(delta: float) -> void: var input := Input.get_action_strength(_down) - Input.get_action_strength(_up) position.y = clamp(position.y + input * MOVE_SPEED * delta, 16, _screen_size_y - 16) func _on_area_entered(area: Area2D) -> void: if area.name == "Ball": area.direction = Vector2(_ball_dir, randf() * 2 - 1).normalized()

注意这里没有射线检测,碰撞靠 Area2D 的area_entered信号;而球的 y 方向分量每次命中都随机化——手感就是从这种小随机里来的。

第 4 步:加一个计分,四行搞定

原 demo 没做计分,我们补上。wall.gd 里原本只有area.reset(),按下面思路加个计数器:

var score := {"left": 0, "right": 0} func _on_wall_area_entered(area: Area2D) -> void: score["right"] += 1 # 球撞了左墙 → 右边得分 area.reset() # 这里省略了更新 HUD 的文本赋值,核心就这一句

存好再运行,球出界时角落的分数就该 +1 了。能改、能跑、能验证,Pong 就算彻底消化:移动是"向量乘 delta",碰撞是"信号",计分是"一个变量"。

从 2D 跨到 3D:你会撞上 3 个问题

先看目标:3d/squash_the_creeps里你可以前后左右移动、跳跃,从怪头顶踩过去得分,相机跟随,角色落地还有压扁的挤压动画。

倒推它为什么能跑:玩家脚本挂在 CharacterBody3D(带碰撞的角色刚体)上,_physics_process里每帧做三件事——把输入变成速度、加跳跃、扣重力,最后交给move_and_slide()处理碰撞:

if is_on_floor() and Input.is_action_just_pressed(&"jump"): velocity.y += jump_impulse velocity.y -= fall_acceleration * delta move_and_slide()

最后那行velocity.y -= fall_acceleration * delta前面源码里特意写了注释:重力必须每帧都加,哪怕角色正站在地面上。跳一下就记住这条 2D 到 3D 的转换表,能少走很多弯路:

你 2D 里写的3D 里变成为什么变
Vector2Vector3多一个轴,"上下"成了坐标的一部分
move_and_slide(velocity)先设 velocity 属性再调 move_and_slide()3D 版一次调用可能分多次子步滑开碰撞
进信号判断碰撞循环查 get_slide_collision_count()子步碰撞不会逐个发信号,得自己遍历

Vector2 不够用了,朝向得换套写法

移动输入从两键变四键,direction 变成 Vector3。2D 里翻转贴图就行,3D 里要真正转节点朝向,demo 用的是basis = Basis.looking_at(direction)——把角色的正前方对准移动方向。

🤔 角色为什么会陷进地面或飘在半空

两个高频症状,同一个病根:重力没每帧加,或者只加了"空中"的重力。跳起来的那一帧靠velocity.y += jump_impulse给一个冲量而不是固定高度,落地判断靠is_on_floor(),而它只有在持续碰撞的帧才可靠——所以重力那行绝对不能写进if not on_floor里。

为什么踩怪判定要套一层 for 循环

move_and_slide()在一帧内可能分多次子步推开重叠,每次子步都是一次碰撞记录,但不会发信号。所以踩怪逻辑要循环get_slide_collision_count(),再检查碰撞法线和向上方向的点乘大于 0.1(确认"从头顶踩下"而不是蹭到侧面),命中就压扁怪物、给个反弹速度,然后break防止一只怪算两次分。

物理发顿、边缘发糊:两个调优时真实的坑

角色走起来一"顿"一顿,问题出在哪

做 3D 第一人称时会遇到一个怪现象:代码逻辑完全正确,但角色在低帧率机器上看起来一顿一顿,像定格。原因不在你的移动代码,而在物理和渲染的节奏错位——物理按固定步长跑(默认 60Hz),渲染按显示器刷新率跑(可能是 144Hz),位置只在物理步更新,中间的画面是"冻"的。

3d 文件夹下的 physics_interpolation 演示就是专门讲这个的,它的修复方案是一行工程设置:开启物理插值(项目设置里common/physics_interpolation=true)。引擎会在相邻两个物理步之间自动插值渲染位置,你的代码一行不用改。反过来也看看 2d 里的 physics_platformer:它的玩家干脆是 RigidBody2D,在_integrate_forces回调里手控加速度和摩擦,文件顶部 WALK_ACCEL、WALK_MAX_VELOCITY 那些常量就是"手感"旋钮——把 1000 改成 500 再跑一次,角色立刻变肉。

边缘锯齿和拖影:三个抗锯齿旋钮

画面另一个高频抱怨是锯齿。3d/antialiasing演示把三个选项做成了一个可切换面板,对应三行核心代码:

# MSAA:质量最好,开销高,对透明贴图边缘没效果 get_viewport().msaa_3d = index as Viewport.MSAA # TAA:连高光锯齿都能抹平,但运动物体会有拖影 get_viewport().use_taa = index == 1 # 屏幕空间抗锯齿:快,代价是整幅画面轻微发糊 get_viewport().screen_space_aa = int(index) as Viewport.ScreenSpaceAA

实际选择路径:低端机选屏幕空间抗锯齿,高配机要画质选 MSAA,中间档选 TAA——如果 TAA 拖影明显,把渲染缩放降到 75% 再开 FSR 锐化,是 demo 里给出的组合拳。

逆向官方寻路演示:角色到底怎么在网格上找到路

先看效果:在2d/navigation_astar里点一下地面,小飞船绕开所有障碍飞过去,身后留下一条折线路径。

它的实现有个反直觉的地方:寻路逻辑挂在地图上,不在角色身上。pathfind_astar.gd 继承的是 TileMapLayer,里面维护一个 AStarGrid2D——导航网格,简单说就是"角色能走哪些格子"的地图。角色只是照着路径点走。

_obstacle 集合就是"你画了什么"。_ready()里先把地图逻辑变成网格并标记障碍:

_astar.cell_size = CELL_SIZE _astar.update() for pos in get_used_cells(): _astar.set_point_solid(pos) _path = _astar.get_point_path(_start_point, _end_point)

前几行的含义:把已画出的格子全部标为实心(不可走),起点终点标成可走之后,一次get_point_path就拿到完整路径,画线交给_draw()

角色别瞬移过去,要"开"过去

character.gd 的精髓是把角色当辆车:每帧算"理想速度"(指向下一路点的方向乘最大速度),用"理想速度减当前速度"得到转向力,再除以质量 MASS 累加进速度——质量就是惯性,车不会急停。走到距路点小于 ARRIVE_DISTANCE 就算到达,弹出这个点,取下一个。整条路径就是被一辆有惯性的车"开"过去的,所以转弯有弧度,不呆板。

下一步做什么

  1. 今天就做:按第一节的四步跑通 Pong,把球速常量DEFAULT_SPEED从 100 改成 60,看拉锯手感差多少。
  2. 本周内:给 Dodge the Creeps 加一个倒计时 HUD,重点读懂 Timer 信号和mob_scene.instantiate()的实例化流程。
  3. 练手 3D:把你自己写的 2D 角色移动照搬进踩怪演示,故意注释掉"每帧加重力"那行,亲眼看看角色怎么飘起来。
  4. 理解寻路:在导航演示里抹掉一格障碍重跑,确认路径会绕开;再试着把 MASS 改大,观察车感变化。
  5. 用 C# 的话:翻到mono/目录,同样的演示有等价实现,对着 GDScript 版看语法差异,比啃教程快。

【免费下载链接】godot-demo-projectsDemonstration and Template Projects项目地址: https://gitcode.com/GitHub_Trending/go/godot-demo-projects

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 16:34:03

复数与欧拉公式:从二维几何到工程应用的完整理解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 16:32:17

【SAP FI】5.财务报表_1.S4 HANA中实现资产负债表和利润表

目录 一、要点 二、【OB58】定义财务报表 1.最佳实践定义 2.我自己的定义 二、【F.01】查看财务报表 一、要点 1.B/S:资产负债表科目最后看余额。累计折旧(成本折旧)。 2.P/L:损益表科目在每个月的月末都要结转到【本年利润…

作者头像 李华
网站建设 2026/9/18 16:28:10

AI标准与评估规范落地指南:从国际标准到可执行报告

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华