Godot 2D游戏开发笔记
本篇笔记是对[想在2025年做游戏?用Godot做出你的第一个2D游戏吧:安装Godot_哔哩哔哩_bilibili]的总结
Part0 系统
设置语言:gamemanager界面右上角Settings
创建新项目:左上角“+创建”
(进入项目之后)
左下角:文件系统 可将外部文件直接拖入以导入 导入的文件可双击预览
左上角:节点管理(场景树)
右侧:检查器
右上角:运行项目
顶部:切换2D 3D 脚本(Script)
顶部下方:切换对节点的选择、移动、旋转、锁定等操作
鼠标中键:拖动视角
鼠标滚轮:缩放视角
中间蓝色框:默认渲染区域
文件管理
游戏场景(.tscn)放在Scenes文件夹
代码文件(.gd)放在Scripts文件夹
设置窗口缩放
项目->项目设置->显示->窗口->拉伸->模式,可选择让场景随窗口放大缩小
viewport 像素吸附
canvas_items 不限像素
管理导出模板
编辑器->管理导出模板->下载或导入
导出项目
项目->导出->添加,选择操作系统->启用内嵌PCK
Part1 搭建场景
添加一个”Node2D“作为根节点
在该Node2D根节点下
添加”Sprite2D“节点作为场景
在右侧的检查器中,将所需的场景拖入”Texture“中
像素风格设置:左上角项目->项目设置->渲染->纹理->将默认纹理过滤设置为Nearest
设置主场景:第一次运行项目时会弹出窗口,将背景场景设置为主场景即可
添加摄像机:Camera2D(粉色方框)
在右侧检查器中,改变”Zoom“的数值以改变方框大小
Part2 创建玩家并添加动画
点击顶部下方场景栏的加号”+“,新建一个场景
添加节点“CharacterBody2D”作为玩家的根节点
在该节点下添加”AnimatedSprite2D“节点
左侧会出现一个黄三角警告,需要在右侧检查器中,设置"SpriteFrames"图片帧
点击Animation->SpriteFrames右边的<空>,新建SpriteFrames,再点击创建好的SpriteFrames即可打开之
在下方的SpriteFrames栏中,上侧的网格图标可分割并添加数个图片,组成一个动画(如”idle“待机动画)
动画帧率可调
可设置游戏开始时就自动播放某一动画
将玩家场景添加到主场景:回到主场景(背景场景)左侧节点栏上方,用链接图标即可将某一场景添加到主场景
Part3 添加代码
为玩家添加代码:选中玩家场景的根节点,点击右上侧一个有绿色加号的图标
使用Node:Default模板
可通过”创建“新代码或”加载“已有代码的方式将代码挂载到某一节点
Part4 操控玩家
为玩家设置速度(附2之5,6)
以键盘操控玩家移动
接收输入信号:项目->项目设置->输入映射->添加新动作并分配键位
实时检测键盘的输入(附2之8)
将玩家的速度赋值为输入与速度的乘积,形如:
velocity=Input.get_vector(”left“,"right","up","down")*move_speed
Part5 播放动画
在角色的脚本中,声明一个类型为AnimatedSprite2D的变量animator,并使其暴露在检测器中(附2之10)
在检测器中,给animator的“Animator”赋值(可拖动或选择)为Part2中创建的AnimatedSprite2D节点(在场景树中)
在特定情况下播放动画,如在角色速度为0时播放待机(idle)动画,否则播放跑步动画,形如:
if velocity == Vector2.ZERO:
animator.play("idle")
else:
animator.play("run")
Part6 添加碰撞体-场景边界空气墙
在主场景中,添加一个"StaticBody2D"节点
在其之下添加"CollisionShape2D"节点
在右侧的检查器中为其添加“Shape”,其中的“WorldBoundaryShape2D”适合用来做空气墙,检查器中的“Transform”->"Position"可用于精确调整位置
可将所有空气墙整合到一个Node2D节点下,并锁定,以使场景整洁
Part7 添加碰撞体-角色
在角色场景的根节点下,添加一个“CollisionShape2D”节点
在右侧的检查器中添加“Shape”,用合适的图形概括角色外形。
Part8 创建敌人
新建场景,以“Area2D”作为敌人的根节点
Area2D是一个物理节点,可检测是否有别的物理节点碰到了它
用“AnimatedSprite2D”为敌人添加动画,方法同前
用“CollisionShape2D”为敌人添加碰撞体,方法同前
回到主场景,注意不要选中任何节点,将敌人的场景链接到主场景中
Area2D下没有velocity参数,但可以通过改变position来控制敌人的移动,形如:
position += Vector2(-100,0) * delta
(若不加“* delta”则会导致按帧改变位置,使移速过快)
解决玩家与怪的叠放次序问题:
按照物体在y轴上的坐标来渲染:选中根节点->在右侧检查器中选择"Ordering"->勾选 Y Sort Enabled
Part9 碰撞检测
来到敌人场景->右侧节点面板下找到信号栏->选择body_entered(body: Node2D)->选择敌人节点并连接
自动创建了如下函数
func _on_body_entered(body: Node2D) -> void:
每次body entered信号被触发,该函数下语句便会被执行
需判断敌人碰到的是玩家而非他物:
if body is CharacterBody2D: #body指碰到的物体
Part10 游戏结束并刷新场景
实现”碰到敌人就输掉游戏“
来到玩家代码,添加函数:
func game_over():
get_tree().reload_current_scene() #重新加载当前场景
回到敌人代码
if body is CharacterBody2D: #body指碰到的物体
body.game_over() #body即代指玩家,可直接调用写好的game_over函数
设置游戏结束时玩家的动画,在game_over函数中:
await get_tree().create_timer(3).timeout #等待3秒倒计时
可使用bool型变量记录游戏是否结束
添加失败动画,方法同前
在game_over函数中,播放该动画:
animator.play("game_over")
总结如下:
func game_over():
animator.play("game_over")
await get_tree().create_timer(3).timeout
get_tree().reload_current_scene()
Part11 不断发射子弹
新建场景,Area2D作根节点,Sprite2D作子弹图片,CollisionShape2D作碰撞体
添加脚本:子弹移动同前
每隔一段时间就发射子弹
回到玩家节点,添加“Timer”节点,在右侧的检查器中,不勾选“One Shot”以使循环执行,勾选“Autostart”以使在游戏开始时自动执行
在Timer的节点->信号栏中,双击“timeout()“,将其连接到玩家的代码上,可将其命名成”_on_fire“
可在Timer节点的检查器中对这个倒计时循环进行精细的控制
声明一个类型为”PackedScene“的变量来储存子弹的场景,命名为”bullet_scene“
在_on_fire函数下,新声明一个局部变量bullet_node来存子弹场景,并让其生成在玩家边合适的位置:
func _on_fire() -> void
var bullet_node = bullet_scene.instantiate()
bullet_node.position = position #前一个position是子弹的,后一个是玩家的
get_tree().current_scene.add_child(bullet_node)
回到玩家场景的根节点,将右侧检查器中的“Bullet Scene”快速加载为子弹的场景
可在_on_fire函数中自由设计开火条件(如只能站定才能开火,游戏结束后无法开火等)
Part12 性能优化
在子弹的_ready()函数加添加一个倒计时(附2之16)
倒计时结束后摧毁生成的子弹(附2之20)
Part13 消灭敌人-分组功能
给敌人添加被消灭的动画,方法同前,注意死亡动画不需要重复播放
检测敌人被子弹打到
回到敌人场景根节点,添加area_entered(area: Area2D)信号,并连接到敌人的代码
区分敌人和子弹,避免敌人碰到自己人而触发被消灭
回到子弹场景,右侧节点->分组->添加分组并命名为“bullet”
回到敌人代码,判断撞到的是否为子弹:
func _on_area_entered(area: Area2D) ->void
if area.is_in_group("bullet"):#被子弹撞到了
#让敌人停止运动
$CollisionShape2D.queue_free() #删除敌人碰撞体积,防止尸体吞箭
#摧毁打死敌人的子弹
#播放死亡动画
#摧毁死亡的敌人
Part14 不断生成敌人
在主场景的根节点上(重点)新建代码“GameManager”
回到主场景,添加一个Timer节点,勾选Autostart
在该Timer节点的信号面板中,双击timeout(),选中根节点的GameManager以连接,可命名为_spawn_slime()
希望敌人随机生成在一定范围中
范围赋值为随机值(附2之23)
生成敌人和生成子弹代码类似:
@export var slime_scene : PackedScene
func _spawn_slime() -> void:
var slime_node = slime_scene.instantiate()
slime_node.position = Vector2(x,randf_range(a,b))
get_tree().current_scene.add_child(slime_node)
选中场景根节点,在右侧的检查器中看到Slime Scene变量,将其设置成敌人的场景
使敌人生成得越来越快
声明一个类型为Timer的变量spawn_timer,通过控制它来控制生成间隔,记得在检查器中将其设置到连接了生成敌人代码的Timer上。
func _process(delta: float) -> void:
spawn_timer.wait_time -= 0.2 * delta
spawn_timer.wait_time = clamp(spawn_timer.wait_time,1,3)
敌人到达左侧边界,则摧毁
if position.x < value:
quene_free()
Part15 记分
在GameManager代码中,声明一个记分的变量score:
@export var score : int = 0
在敌人被消灭的代码中,获取主场景的根节点(因为GameManager连接在那)
get_tree().current_scene.score += 1
Part16 UI
回到主场景,添加"CanvasLayer"节点,出现的蓝色大框为其渲染区
在CanvasLayer之下,添加“Label”节点,在右侧检查器的Text中编辑文字,可自由设置文字参数
如:Theme Overrides中可设置字体、颜色、大小等
在其下Fonts可快速加载某一字体文件(.ttf)
显示分数
来到GameManager代码,声明一个类型为Label的变量
@export var score_label : Label
将score节点拖动到GameManager检查器中的score_label中
在process函数中实时显示分数:
score_lable.text = "Score: " + str(score)
显示Game Over
创建好节点后,先点右侧眼睛按钮让它隐藏
在玩家死亡代码中,告诉GameManager播放gameover
get_tree().current_scene.show_game_over()
在GameManager中设置show_game_over函数,显示gameover
@export var game_over_label: Label
func show_game_over()
game_over_label.visible = true
在检查器中将game over label设置成gameover节点
Part17 音乐音效
需要循环播放的,要在预览界面勾选“循环”并重新导入
音效
来到玩家场景,添加节点“AudioStreamPlayer”
将音效文件拖入节点右侧检查器中,可调节其参数
可使用拖动的方式,将音效节点拖到对应的代码函数中,使其按需播放
可用.play()或.stop()来控制其播放与停止
.playing 表示它正在播放,是一种状态
背景音乐
在主场景添加节点“AudioStreamPlayer”,将BGM文件拖入检查器,勾选Autoplay
令失败重启不影响BGM播放
让BGM节点存在于主场景节点之外,使用godot自动加载功能,与主场景同时存在
将BGM保存为一个场景,而非节点
左上角项目->项目设置->全局->自动加载->BGM场景->+添加
附1 快捷键
2D界面中按F 使选中的节点居中
ctrl+A 新建节点
ctrl+Z 撤回
ctrl+D 复制并粘贴
ctrl+S 保存
ctrl+X 剪切整行代码
旋转中按住ctrl 磁吸
F5 运行项目
F8 退出运行
附2 系统函数与参数
func _ready() -> void:
该函数下的语句会在游戏开始运行时或被加入场景时被执行
print()
输出括号中的内容
func _process(delta: float) -> void:
该函数下的语句会在游戏的每一帧被执行(基于电脑可能有所不同)
若改成 _physics_process则会以固定60帧/秒运行
extends CharaceterBody2D
出现在代码顶端,表示该代码是由CharaceterBody2D类型延申而来,可用其下的参数
velocity = v
CharacterBody2D特有的参数,给节点速度赋值v
Vector2(x,y)
二维向量,能表示在x轴,y轴上的数值
move_and_slide()
CharacterBody2D特有的功能,可让该节点以设置好的velocity来移动
Input.get_vector(negative_x,positive_x,negative_y,positive_y)
获取左,右,上,下四个方向上的输入
var a : int = 1
声明a是一个值为1的整型变量
@export
将某个变量暴露在左侧检查器中
Vector2.ZERO
向量值为0,可用于判断速度是否为零,形如velocity == Vector2.ZERO
animator.play()
animator为自定义的AnimatedSprite2D类型变量,该函数功能为播放括号中指定的动画
position
节点的世界坐标
func _on_body_entered(body: Node2D) -> void:
Area2D的函数,每次body entered信号被触发(碰到物体),该函数下语句便会被执行
get_tree().reload_current_scene()
重新加载当前场景
get_tree().create_timer(t).timeout
创建倒计时,括号中的t为具体的时间
bullet_scene.instantiate()
bullet_scene为自定义的PackedScene类型变量,该函数可生成一个bullet_scene节点
get_tree().current_scene.add_childe(node)
将第二个括号中的node节点变成当前场景的子节点
return
提前结束函数
queue_free()
摧毁当前节点
area.is_in_group("group1")
这个area场景属于分组“group1”
area.queue_free()
摧毁area节点
randf_range(a,b)
a~b之间随机生成一个数(含a不含b)
clamp(value,min,max)
将value的值限制在min和max之间
附3 报错
1.indented
与缩进有关的报错
Godot 2D游戏开发笔记的更多相关文章
- Phaser是一款专门用于桌面及移动HTML5 2D游戏开发的开源免费框架
Phaser是一款专门用于桌面及移动HTML5 2D游戏开发的开源免费框架,提供JavaScript和TypeScript双重支持,内置游戏对象的物理属性,采用Pixi.js引擎以加快Canvas和W ...
- 关于《Unity3D/2D游戏开发从0到1》书籍再版说明
关于<Unity3D/2D游戏开发从0到1>第一版本在2015年7月1日全国发行,累计得到不少国内高校教师.培训机构的好评.但是由于Unity官方对于技术不断的升级与版本的快速迭代,基于U ...
- 《Unity3D/2D游戏开发从0到1(第二版本)》 书稿完结总结
前几天,个人著作<Unity3D/2D游戏开发从0到1(第二版)>经过七八个月的技术准备以及近3个月的日夜编写,在十一长假后终于完稿.今天抽出一点时间来,给广大热心小伙伴们汇报一下书籍概况 ...
- 《Unity3D/2D游戏开发从0到1》正式出版发行
<Unity3D/2D游戏开发从0到1>正式出版发行 去年个人编写的Unity书籍正式在2015年7月正式发行,现在补充介绍一下个人著作.书籍信息: 书籍的名称: <Uni ...
- Unity 2D游戏开发教程之精灵的死亡和重生
Unity 2D游戏开发教程之精灵的死亡和重生 精灵的死亡和重生 目前为止,游戏项目里的精灵只有Idle和Walking这两种状态.也就是说,无论精灵在游戏里做什么,它都不会进入其它的状态,如死亡.于 ...
- Unity 2D游戏开发教程之摄像头追踪功能
Unity 2D游戏开发教程之摄像头追踪功能 上一章,我们创建了一个简单的2D游戏.此游戏中的精灵有3个状态:idle.left和right.这看起来确实很酷!但是仅有的3个状态却限制了精灵的能力,以 ...
- Unity 2D游戏开发教程之2D游戏的运行效果
Unity 2D游戏开发教程之2D游戏的运行效果 2D游戏的运行效果 本章前前后后使用了很多节的篇幅,到底实现了怎样的一个游戏运行效果呢?或者说,游戏中的精灵会不会如我们所想的那样运行呢?关于这些疑问 ...
- Unity 2D游戏开发教程之使用脚本实现游戏逻辑
Unity 2D游戏开发教程之使用脚本实现游戏逻辑 使用脚本实现游戏逻辑 通过上一节的操作,我们不仅创建了精灵的动画,还设置了动画的过渡条件,最终使得精灵得以按照我们的意愿,进入我们所指定的动画状态. ...
- Unity 2D游戏开发教程之游戏精灵的开火状态
Unity 2D游戏开发教程之游戏精灵的开火状态 精灵的开火状态 “开火”就是发射子弹的意思,在战争类型的电影或者电视剧中,主角们就爱这么说!本节打算为精灵添加发射子弹的能力.因为本游戏在后面会引入敌 ...
- Unity 2D游戏开发教程之游戏中精灵的跳跃状态
Unity 2D游戏开发教程之游戏中精灵的跳跃状态 精灵的跳跃状态 为了让游戏中的精灵有更大的活动范围,上一节为游戏场景添加了多个地面,于是精灵可以从高的地面移动到低的地面处,如图2-14所示.但是却 ...
随机推荐
- git记住多个账号
前言 git每次推送都需要输入密码,或者两个不同账号间互顶. 如何处理这些问题呢? 两种途径:记住ssh协议公钥和记住多个密码. 前者我一直不推荐,所以不提. git配置文件 git全局的用户名.账号 ...
- SJGC 笔试 反思
1.考察text-align的属性值(不属于) 应该选择top 和 justify 好伤心 只选择了top 2. 考察 instanceof 和 null 和 NaN 但null返回obje ...
- jlink + jz2440 + win10 操作方式
### 重新烧写 jlink uboot.bin `http://www.jz2440.com/started/j-flash/nor-flash烧写-以uboot为例` 1.第一阶段 通过 jli ...
- leetcode 69 x 的平方根 牛顿迭代法
简介 简单的题, 直接上代码. 其实还挺复杂的. 参考链接 https://leetcode-cn.com/problems/sqrtx/solution/x-de-ping-fang-gen-by- ...
- Golang基础笔记十一之日期与时间处理
本文首发于公众号:Hunter后端 原文链接:Golang基础笔记十一之日期与时间处理 本篇笔记介绍 Golang 里日期与时间的处理,以下是本篇笔记目录: 当前日期与时间的获取 字符串与时间格式的互 ...
- Rust中的代码组织:package/crate/mod
刚接触Rust遇到一堆新概念,特别是package, crate, mod 这些,特别迷糊,记录一下 一.pakcage与crate 当我们用cargo 创建一个新项目时,默认就创建了一个packag ...
- Git--进阶2 分支的理解--九五小庞
分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN. 如果两个平行宇宙互不干扰,那对现在的你也没啥影响.不过,在某个时间点,两个平行宇宙合并 ...
- Win10系统如何清理Hosts文件的问题
近期有电脑基地用户在电脑的文件过程中发现有一个叫Hosts文件,Hosts文件是做什么的呢?Hosts文件一般用于填补或替代网络里DNS功能的,但是由于Hosts文件中的信息过多会影响到电脑网上,所以 ...
- ansible-playbook批量安装tomcat8版本
ansible-playbook 进行批量安装tomcat8 ansible-playbook 进行安装tomcat操作 说明: 先下载好对应版本的tomcat放到本地目录,然后通过playbook ...
- Claude Code:AI编程的深度体验与实践
前言:从代码补全到智能协作的进化 在AI技术日新月异的今天,开发者们正经历着一场前所未有的效率革命.面对日益复杂的开发需求和快速迭代的技术栈,借助AI工具提升开发效率已不再是选择题,而是必选项. 我的 ...