ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

零基础游戏开发入门:用免费Godot引擎制作你的第一个2D小游戏

零基础游戏开发入门:用免费Godot引擎制作你的第一个2D小游戏 做游戏这件事我大概是这么“绕弯路”绕明白的最早我也以为没学过编程就没资格碰游戏引擎于是老老实实啃了不少理论结果几个月过去我发现自己连一个能跑的Demo都没有。后来换了个思路先选一款简单的引擎照着文档把一个十几分钟能做出来的小玩法完整跑通反而一下就找到了感觉。这条路才是零基础最合适的一条路。这篇博文是系列第一篇目标很明确——帮你把“游戏开发”这扇门推开一条缝。我会带你从零开始选一款完全免费、没有账号门槛的引擎搭建开发环境然后亲手做一个能玩的2D接球小游戏。整个过程不需要你有任何编程基础只要你愿意跟着点鼠标、敲几行像伪代码一样简单的东西。学完这篇你会得到第一个可运行的作品也会真正理解“游戏到底是怎么做出来的”这件事。1. 做游戏之前先把“游戏”拆成你能做的事情1.1 游戏开发的本质输入、反馈、迭代三件事很多零基础的朋友一想到“游戏开发”脑子里全是高大上画面3D建模、复杂AI、服务器联机、几十万行代码……确实有这些东西但那是大团队做大型项目时考虑的事。一个人零基础入门你要牢记一个事实只要你做的是小游戏开发本质上就是三件事来回循环。第一件事接收输入。按键盘、点鼠标、划屏幕让游戏知道“玩家做了某个动作”。第二件事处理逻辑。游戏判断这个动作有没有效比如子弹有没有打中敌人、角色有没有吃到金币、分数该不该加。第三件事给出反馈。画面里的角色移动了得分弹出来了怪物倒下了游戏结束了。反馈让玩家觉得“我的操作有意义”。你看这三件事其实不神秘。把它类比成你楼下便利店的自动门人走近是输入感应器判断“这是人要开门”是逻辑电机推动门打开是反馈。游戏就是一个更大范围、反馈更丰富的“感应器”。你制作游戏绝大部分时间就是在写这些“判断规则”和“反馈表现”。我见过太多新手一上来就研究渲染管线、光照模型结果被劝退。我自己的经验是第一个游戏只要是“能操作”“有反馈”“能分出胜负”就算成功。你不需要在第一个项目里证明自己美术多好、架构多牛你只需要完整走通从创意到可玩作品这条链路。一旦走通过一次第二个项目、第三个项目自然会有野心去打磨细节。所以请把你的期待放到“跑通”这个级别而不是“惊艳”。1.2 零基础最容易踩的三个坑和对应解法这三条是我看到的新手通病我自己也全部踩过。第一个坑觉得必须先学完编程再开始做游戏。引擎已经帮我们封装好了绝大部分底层逻辑比如物理碰撞、渲染、窗口事件处理你根本不需要从零去写。你只需要学会“在这个位置放一个规则”就能驱动一个游戏角色。就像你学做菜不需要先考厨师证你只需要学会开火、放油、下菜。真需要深入编程基础的时候往往是做到后面你自己想实现复杂功能时那是再回头补也不迟的。第二个坑一上来就想做“3A大作”。想做《原神》和《塞尔达》没问题但那是几十亿预算、几百人团队做了好几年的结果。零基础第一个项目题目应该小到“一个半小时能跑通”比如接球、贪吃蛇、打砖块、走迷宫。规模越小你能完整走完制作流程的可能性就越高而这个“完整走完”的经验比做什么题材都重要。第三个坑收藏了几十个教程一个都没动手做。“看懂了”和“做得出”是两码事。我甚至见过有人把引擎下载完打开一看界面很复杂就吓得关掉了。破解办法只有一条跟着一个教程哪怕完全照抄也要亲手做完它。照抄的过程不是丢人的事那是你第一次接触“真实流程”抄过一次你就有手感了。说到底看一百张游泳教学图不如憋一口气下水扑腾十分钟。2. 引擎选型为什么我让你先上手 Godot2.1 主流引擎对比看看哪个适合你选引擎这件事很多新手纠结到失眠。实话讲对零基础来说可选的其实就那几个Unity、Unreal、Godot、GameMaker以及RPG Maker这种垂直工具。我直接列个比较实用的对比表。引擎脚本语言上手难度主要方向收费模式适合人群UnityC#中等2D/3D通吃对小型项目有免费额度想走职业路线、学习资源多的朋友UnrealC、蓝图偏高3D大作有营收分成门槛注重画面的3D项目团队GodotGDScript低2D极佳、3D可用完全免费开源零基础、喜欢轻量、热爱开源GameMakerGML脚本低2D需付费订阅想做2D且不介意付费RPG Maker事件系统极低RPG专精付费买断只想做JRPG玩法我的建议很直接零基础第一款引擎从Godot开始。原因不只是免费而是它把“第一次打开”的门槛降到了极低。Godot本体只有几十兆解压就能用不用注册账号不用担心许可证弹窗。它的节点和场景系统会让你用“搭积木”的方式理解游戏结构而不是一上来就面对一堆空荡荡的工程文件。它的GDScript语言很像Python如果你的目标只是做小游戏这语法一眼就能看懂。当然如果你想走职业开发路线Unity的招聘需求量和中文资料量都更大未来从Godot迁移到Unity也不亏因为“场景、组件、物理碰撞、生命周期回调、信号”这些核心概念所有主流引擎都是相通的。你现在学的思路到哪个引擎都能复用。选Godot不是选错了路只是选了一条更顺畅的上路方式。2.2 下载安装与项目创建半小时进入主界面我以Windows环境为例其他系统流程类似。第一步去Godot官网下载。官网首页会自动识别你的操作系统直接下载标准版就好。如果你电脑配置真的很低下载32位版本也行。文件是zip压缩包不是安装程序解压出来就能用。第二步创建文件夹并解压。建议把解压目录放在一个方便找的地方比如D盘下的Godot文件夹路径里不要出现中文和空格。这一步不是玄学是避免后续某些脚本资源加载因为路径问题报错省得自己坑自己。第三步双击打开程序会看到项目管理器界面。点击右上角“新建”填一个项目名字比如我习惯叫“MyFirstGame”。项目路径选择你之前建好的空文件夹渲染引擎保持Forward Plus默认就行如果电脑太老可以选Compatibility。然后创建并进入。第四步进入项目前先把显示分辨率设置好。点击顶部菜单“项目”-“项目设置”打开“显示”选项卡把“视口宽度”设成1280“视口高度”设成720。这样后面写代码时的坐标范围就有依据了。如果你懒得设后面调试时发现画面比例不对再回来改也行。我会建议你顺手把界面切成中文菜单栏“编辑器”-“编辑器设置”在“界面”里找“语言”改成简体中文重启即可。对新手来说熟悉的语言能省很多查菜单的时间。2.3 认识Godot主界面场景关卡节点零件打开Godot第一眼会觉得眼花顶部是菜单和工具栏中央大块区域是视口也就是游戏世界的预览画面左侧上方是场景树列出当前场景里有哪些对象左侧下方是文件系统显示项目里的所有资源文件右边有一长串属性面板叫检查器。这里最重要的概念是“节点”和“场景”。节点是游戏世界里的最小零件比如一个2D图片、一段声音、一段逻辑、一个计时器都是节点。场景是由节点组成的集合可以理解成一个“关卡模板”或“预制房间”。你可以把一个人物角色做成一整个场景然后在主关卡场景里反复放多个。打个比方节点像乐高积木里的一块拼件场景就是用这几块积木拼成的模型。好处是你可以随时把某个模型单独拆出来修改改完其他用到它的地方跟着更新。这是Godot的核心思想本节先有个印象后面实战会反复用到。3. 第一个游戏做一款能玩的“接球游戏”3.1 先从一句话玩法拆出五个功能模块我选的这个例子你肯定很熟屏幕下方有一个挡板小球从屏幕上方不断落下你控制挡板左右移动去接住小球每接住一个加一分只要漏掉一个游戏就结束。这个玩法虽然简单但包含了一个完整游戏的几乎所有基本要素玩家控制、对象生成、碰撞判定、计分、结束条件。我之前说过做游戏就是把大问题拆成小零件。把这个玩法拆开我们会得到下面几个独立任务一个挡板响应键盘左右键在屏幕下方移动不能飞出屏幕。一个小球每隔一小段时间从屏幕顶部生成随机水平位置匀速下落。一个碰撞检测小球接触到挡板时判定“接住了”加分并让小球消失。一个计分系统用文字实时显示当前分数。一个失败条件小球落到底部出口判定“漏了”停止游戏并显示最终分数。你发现没有一旦拆成这五件小事“做个游戏”这个词就从虚无缥缈变成了可执行的清单。你接下来做的工作就是一项一项把它们搞定。我觉得这种“拆解”的能力比会写代码本身更重要。以后你做任何玩法都要养成先拆模块的习惯。3.2 创建玩家让挡板能动起来我们直接上手。先创建玩家场景点击顶部“场景”-“新建场景”创建后在“场景”面板空白处选择“其他节点”搜索CharacterBody2D创建它作为根节点改名为Player。CharacterBody2D是一个专门用于“玩家控制角色”的物理节点它的特点是可以自己移动并检测与其他物理体的碰撞。如果你以后做平台跳跃、顶视角走路游戏都会用到它。现在选它是让你一步到位接触最常用的玩家节点。接着给Player加两个子节点一个ColorRect用于显示绿色方块一个CollisionShape2D用于物理碰撞。怎么加右键Player选择“添加子节点”分别搜索创建即可。ColorRect颜色矩形在可视化没做美术素材前用它占位最方便。把“颜色”属性调成绿色把“大小”调成宽160、高20。CollisionShape2D碰撞形状会跟ColorRect一起移动。在它的“形状”属性里点击空值新建一个RectangleShape2D并把尺寸也调成宽160、高20。这里有个细节ColorRect是UI控件直接挂在世界节点下也能显示但碰撞判断真正依赖的是CollisionShape2D。你可以没有可视图形但不能没有碰撞形状否则物理计算根本不会把你当对象。下面写玩家的控制脚本。右键Player选择“附加脚本”文件名默认可以删掉默认内容输入以下代码extends CharacterBody2D const SPEED 600.0 func _physics_process(delta): # 读取左右方向键的输入得到 -1、0 或 1 var direction Input.get_axis(ui_left, ui_right) velocity.x direction * SPEED move_and_slide() # 把挡板限制在屏幕范围内 position.x clamp(position.x, 80, 1200)这段代码做了三件事。第一它用Input.get_axis读取系统默认绑定好的左右方向键。Godot默认把键盘方向键和A/D键都分别绑到了“ui_left”和“ui_right”这两个动作上所以你不用自己配置按键映射了。第二它把读取到的方向值-1是左、0是不动、1是右乘以速度得到水平速度。第三move_and_slide()让物理体根据速度发生移动完成真正的位移。最后一行clamp把挡板固定在屏幕80到1200这个范围防止飞出边界。在你运行之前你会发现还看不到挡板因为我们还没把它放进主场景。先别急小球也没做先继续等组装时一起看效果。3.3 创建小球动态生成、碰撞与计分做小球之前先理解一个选择为什么用Area2D而不是RigidBody2DRigidBody2D是受物理引擎驱动的刚体会自己计算重力、碰撞反弹看起来更“真实”但更难控制。对于接球这种需要稳定规则的游戏我自己更推荐Area2D。Area2D本身不做受力计算它更像一个“感应区”用来检测有没有其他物体进入自身范围。这让它的行为可预测对新手调试也更友好。创建一个新场景根节点选Area2D改名Ball。给它添加ColorRect子节点颜色调成红色大小设为宽40、高40再添加CollisionShape2D子节点形状选择CircleShape2D半径20让它正好盖住红色方块。这样视觉方块和碰撞范围基本重合。接下来给Ball附加脚本命名为ball.gd内容如下extends Area2D signal scored signal missed var speed : 200.0 func _process(delta): position.y speed * delta if position.y 800: missed.emit() queue_free() func _on_body_entered(body): if body.name Player: scored.emit() queue_free()解释一下。_process函数会在每一帧执行position.y speed * delta让小球每帧向下移动。为什么要乘以delta因为每帧耗时不一样不乘的话在高刷新率电脑上小球会飞得特别快游戏难度就和设备绑定了。delta是这一帧所用的秒数乘上它之后无论帧率多高小球每秒钟都是固定移动speed像素这是游戏开发的基本功。signal scored和signal missed是两个自定义信号也就是对外广播消息。小球接住时会发出“scored”掉出屏幕时发出“missed”由主场景监听并处理分数。用信号的好处是小球不需要知道主场景怎么存储分数它只管“广播事件”实现了模块解耦。这个概念你以后写复杂游戏会经常用到。最后_on_body_entered函数是碰撞信号处理器。它需要你在Ball场景的检查器里连接信号具体操作是选中场景树里的Ball节点点击右边的“节点”页签在信号列表里找到body_entered(body)双击它弹出连接窗口目标保持Ball节点点击“连接”。连接完成后Godot会自动在ball.gd脚本里生成一个空函数你把上面这段函数体复制进去就行。这步漏掉是新手最常见的问题做完以后可以看检查器侧的信号列表是否已勾选。3.4 组装主场景把零件拼成游戏现在做最后一个场景也是真正的“关卡”。新建场景根节点选择Node2D改名Main并保存。这是一个纯逻辑节点专门用来串起整个游戏。先把玩家放进去。刚才你做的Player场景需要保存为player.tscn然后在Main场景里你只需要从左侧“文件系统”面板里找到player.tscn按住拖到Main场景的视口里它就会作为一个完整实例出现。你可以用移动工具把它拖到底部中间位置。这种“一个场景实例被嵌进另一个场景”的用法就是Godot场景复用的核心魅力。现在Ball里面已经有了一个初始球但我们要动态生成多个球所以不需要把Ball场景直接拖进主场景。接下来给Main场景添加几个子节点。在Main上右键添加子节点CanvasLayer改名为ScoreLabel再给ScoreLabel加一个Label子节点这个就是分数文字。添加后把Label的“文本”属性改成“当前得分0”字号设成40左右字体颜色白色位置稍作调整拖到屏幕左上角。CanvasLayer的作用是让UI永远显示在画面最上层不会因为场景里物体的遮挡而看不见这个设计很实用。再加一个Timer子节点改名叫BallTimer。在检查器里把“等待时间”设为1秒把“自动开始”勾上。Timer是Godot内置的定时器它会每隔设定时间发一次timeout信号可以用来周期性地生成小球。如果你想让游戏更刺激以后可以把等待时间调短比如0.6秒。最后给Main挂一个脚本main.gd内容如下extends Node2D var ball_scene preload(res://ball.tscn) var score : 0 var running : true onready var score_label $ScoreLabel/Label onready var ball_timer $BallTimer func _ready(): ball_timer.timeout.connect(_spawn_ball) func _spawn_ball(): if not running: return var ball ball_scene.instantiate() ball.position Vector2(randf_range(80, 1200), -40) ball.speed 200.0 score * 10 ball.scored.connect(_on_ball_scored) ball.missed.connect(_on_ball_missed) add_child(ball) func _on_ball_scored(): score 1 score_label.text 当前得分%d % score func _on_ball_missed(): running false ball_timer.stop() score_label.text 游戏结束最后得分%d % score这段代码是游戏的“总导演”。preload在游戏开始前就把球场景加载进内存避免运行时临时读取卡顿。_ready里把Timer的timeout信号连接到_spawn_ball这样每隔预设时间就会生成一个新球。_spawn_ball里的关键步骤是instantiate()它等于复制出一个球的新实例然后设置它的初始位置在屏幕顶部的随机横向坐标并动态调整下落速度让分数越高球速越快制造一点难度增长。信号的连接在这里是重点。我让主脚本监听球的scored和missed信号所以当任何一个球被接住或漏掉时主脚本都能收到通知。收到scored就加分并更新文字收到missed就把running设为false、停止生成新球、显示游戏结束。这样整个游戏循环就闭环了。这里要特别提醒一个细节main.gd中的连接方式是通过代码动态连接而不是在编辑器里手动连。因为ball场景是代码实例化出来的用代码连接信号才是正解。你在检查器里手动连的只能解决“已经在场景里固定存在”的对象。3.5 跑起来调一调手感才是核心现在你可以点右上角的“运行当前场景”按钮运行了按方向键或A/D移动挡板开始接球试试。我第一次运行这个过程时问题一堆挡板移出画面、球速快到接不住、球生成几秒不出现、CPU风扇狂转。这些都是正常现象调参的过程本身就是游戏开发最有趣的环节。做游戏五成时间在“调节手感”不是夸张。先看最基础的几个参数。挡板速度在player.gd里是SPEED 600如果你觉得太快或太慢改数值再运行立刻体会差异。球速在main.gd里是200 score * 10如果觉得刚开局太无聊可以改成260 score * 12。Timer等待时间1秒想让节奏更紧凑就改成0.7秒。这些改动都只在一行代码里按下运行就能拿到反馈所以你完全可以自己玩几轮调出你觉得舒服的难度。有一个新手一定会遇到的现象球在某个瞬间直接穿过挡板掉到底部看起来像是没碰过。这通常是碰撞检测的“穿透”问题。但更常见的原因是球下落速度太快上一帧还在挡板上方下一帧已经在挡板下方物理引擎来不及检测到中间的交集。遇到这个最直接的办法是降低球的speed或者增加物理演算频率。物理演算帧率和渲染帧率不同可以在项目设置的“物理”里调整“物理帧”默认60如果项目物理表现很怪可以提到120代价是稍微多占一点CPU。我通常先降速度速度快还出问题再调物理帧。跑通之后你可以顺手做两个小优化。第一在Player脚本里加一行小小的“墙角限制”你已经有了我代码里已经写了clamp。第二游戏结束之后加上一行“按R键重新开始”要涉及到重载场景在第一个版本里可以先不做但值得知道方向。4. 新手必看的报错排查与进阶路线4.1 高频报错速查表我把新手做这个项目时最容易踩的坑整理成一张表你遇到问题直接来对照。现象可能原因解决办法玩家按下方向键没反应输入映射没有使用默认ui_left/ui_right或脚本没挂在Player上检查脚本文件是否挂载到Player节点检查输入动作名拼写小球不移动speed变量没有被赋值或脚本没有挂在Ball节点上确认ball.gd挂在Ball节点赋值位置在_spawn_ball里执行无误小球穿过挡板掉出去碰撞形状没配置或球速过快检查CollisionShape2D的形状是否设置尝试把speed从300降到200小球碰到挡板不计分Area2D的body_entered信号没连接检查ball.tscn里是否手动连接了body_entered信号到_on_body_entered生成的小球没有出现生成坐标在屏幕外但又超出可视区域很多检查position.x是不是超出了视口范围边界值取80到1200报错“Parse Error”GDScript语法错误比如少了冒号或end查看输出面板行号和代码对照教程检查缩进与符号报错“Cannot infer the type of...”变量类型推断有歧义给变量加显式类型比如var speed : 200.0改成var speed: float 200.0报错“res://ball.tscn”无法加载文件名或路径不一致检查main.gd里preload的路径是否与文件系统中的实际文件名一致这里我特意把“信号没连接”列为高频项。新手在编辑器里做信号连接时经常连完就忘或者连到别的节点上。你以后做任何项目看到一个节点有行为但另一个节点收不到通知第一时间检查信号连接。4.2 从第一个原型到完整游戏的六个进阶点第一个版本跑通之后恭喜你你已经拥有了真正的“游戏作品”。但这只是一个原型接下来如果你想继续把这个游戏做完或者开启下一个项目我建议沿着下面这几个方向一点点升级。第一增加游戏开始画面和结束画面。现在游戏一运行就出球漏球就结束缺一点仪式感。你可以在Main场景里加一个“开始”界面用一个按钮控制BallTimer是否启动游戏结束后再弹出一个重开按钮。这会让你的作品看起来更完整。第二增加游戏状态管理。现在程序里用了一个running布尔变量来判断是否结束这个模式在复杂游戏里会不够用。你可以引入一个简单的状态枚举比如“准备中”“游戏中”“已结束”根据状态执行不同逻辑。虽然现在看着繁琐但很快你会发现它能帮你少写很多判断。第三增加更多变量丰富玩法。比如小球不同大小、不同分值挡板越接越短生成加速道具甚至加入连击加分。每加一个变量游戏乐趣都可能成倍增长。但注意一次不要加太多先做一个变量看看好玩不好玩再说。第四替换占位美术。现在用的是ColorRect色块说实话很丑。你可以找一些免费2D素材或者用程序慢慢画出简单的圆角图形。用Sprite2D替换ColorRect体验“换皮”的过程这会让你对游戏表现层有直观理解。第五发布成可执行文件。Godot里点击“项目”-“导出”配置一个Windows导出模板很快就能拿到一个exe文件。把自己的游戏发给朋友玩那种成就感是写代码不能比的。这是零基础最容易拿到的那颗糖。第六尝试新的小玩法。把接球改成接金币、打飞碟、躲避障碍本质上都是在原玩法上做一层“换主题”。建议你在第二个项目里保持同样的结构只改一部分逻辑这样可以把注意力集中在新概念上而不用重新学一遍引擎。4.3 时间管理如何避免半途而废最后聊点跟代码无关、但同样重要的事。我观察过很多新手引擎装好了第一节课也学完了最后却倒在“中途弃坑”。弃坑原因通常不是难而是看不到进度或者总觉得做得不够好。我自己的应对办法是“每15分钟定义一个成果”。比如打开引擎创建项目算一个成果场景里放一个会动的红色方块算一个成果能生成小球算一个成果能计分算一个成果。你不要想“我要做一个完整游戏”而是想“我这15分钟只做一件事”。所有任务都拆成这种足够小、一定能完成的动作你就很难被挫败感压垮。另外新手一定要“先完成再完美”。先用色块占位、用最朴素的功能实现把流程跑通再回头做优化。我见过太多人卡在“想先把美术做好再做玩法”结果美术一直没做完玩法永远没开始。这种顺序对个人开发者来说是灾难。游戏好不好玩核心是玩法规则和反馈节奏美术和音效是锦上添花不能等它们到位才开始。我个人在做这一系列内容时的心态就是每个项目只求“能玩”再在这个基础上做一点点的优化。这样我永远处在“有作品”的状态里灵感反而越来越多。你做完接球这个原型之后也可以踩在其他玩法上继续试不用怕做出来的东西简陋你只是在积累做下一个作品的经验。
返回列表