[{"data":1,"prerenderedAt":9419},["ShallowReactive",2],{"\u002Fdevlog\u002Fxggame-bird\u002F02-create-bird":3,"devlog-chapters-xggame-bird":1108},{"id":4,"title":5,"body":6,"cover":1097,"date":1098,"description":1099,"extension":1100,"game":1101,"github":1102,"icon":1097,"meta":1103,"navigation":174,"path":1104,"seo":1105,"stem":1106,"toc":174,"__hash__":1107},"devlog\u002Fdevlog\u002Fxggame-bird\u002F02-create-bird.md","创建主角：小鸟",{"type":7,"value":8,"toc":1086},"minimark",[9,14,23,26,29,54,64,70,74,83,89,92,102,108,111,117,120,125,131,137,143,437,441,454,462,511,536,542,545,572,585,730,733,775,854,865,903,931,971,1002,1008,1020,1043,1046,1052,1059,1079,1082],[10,11,13],"h2",{"id":12},"创建节点的方法","创建节点的方法：",[15,16,17,18],"p",{},"创建节点\u002F创建Node，点击加号「+」，然后输入需要的节点名称即可创建\n",[19,20],"img",{"alt":21,"src":22},"02-create-bird-创建节点的方法","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F02-create-bird-%E5%88%9B%E5%BB%BA%E8%8A%82%E7%82%B9%E7%9A%84%E6%96%B9%E6%B3%95.png",[10,24,5],{"id":25},"创建主角小鸟",[15,27,28],{},"我们需要用到 3 个节点来组成小鸟：",[30,31,32],"ul",{},[33,34,35,39,40],"li",{},[36,37,38],"code",{},"CharacterBody2D"," — 角色节点（控制移动）\n",[30,41,42,48],{},[33,43,44,47],{},[36,45,46],{},"Sprite2D"," — 图片节点（显示外观）",[33,49,50,53],{},[36,51,52],{},"CollisionShape2D"," — 碰撞体节点（检测碰撞）",[15,55,56,57,59,60,63],{},"先用 Godot 自带的图标当小鸟，把它拖到 ",[36,58,46],{}," 的 ",[36,61,62],{},"Texture"," 上即可：",[15,65,66],{},[19,67],{"alt":68,"src":69},"02-create-bird-创建小鸟","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F02-create-bird-%E5%88%9B%E5%BB%BA%E5%B0%8F%E9%B8%9F.png",[71,72,73],"h3",{"id":73},"保存场景",[15,75,76,79,80],{},[36,77,78],{},"Ctrl + S"," 保存场景，建一个文件夹专门放场景文件：",[36,81,82],{},"scenes\u002Fbird.tscn",[15,84,85],{},[19,86],{"alt":87,"src":88},"02-create-bird-保存场景","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F02-create-bird-%E4%BF%9D%E5%AD%98%E5%9C%BA%E6%99%AF.png",[71,90,91],{"id":91},"设置碰撞体",[15,93,94,95,97,98,101],{},"选中 ",[36,96,52],{},"，在右侧面板选一个形状(矩形：",[36,99,100],{},"RectangleShape2D",")，缩放到和图片差不多大：",[15,103,104],{},[19,105],{"alt":106,"src":107},"02-create-bird-碰撞体1","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F02-create-bird-%E7%A2%B0%E6%92%9E%E4%BD%931.png",[15,109,110],{},"这个蓝色矩形就是碰撞体，游戏里小鸟撞到东西靠的就是它：",[15,112,113],{},[19,114],{"alt":115,"src":116},"02-create-bird-碰撞体2","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F02-create-bird-%E7%A2%B0%E6%92%9E%E4%BD%932.png",[10,118,119],{"id":119},"编写脚本",[15,121,94,122,124],{},[36,123,38],{}," 节点，点击右上角的 📜 图标创建脚本：",[15,126,127],{},[19,128],{"alt":129,"src":130},"02-create-bird-创建脚本","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F02-create-bird-%E5%88%9B%E5%BB%BA%E8%84%9A%E6%9C%AC.png",[15,132,133,134],{},"同样建一个文件夹专门放脚本：",[36,135,136],{},"scripts\u002Fbird.gd",[15,138,139],{},[19,140],{"alt":141,"src":142},"02-create-bird-脚本","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F02-create-bird-%E8%84%9A%E6%9C%AC.png",[144,145,150],"pre",{"className":146,"code":147,"language":148,"meta":149,"style":149},"language-gdscript shiki shiki-themes vitesse-light vitesse-dark","extends CharacterBody2D # 创建时自带的\n\n@export var gravity_scale := 2.0       # 重力倍率（控制掉落快慢）\n@export var jump_force := -500.0       # 跳跃力量（负值 = 向上）\n@export var max_fall_speed := 1000.0   # 最大掉落速度\n\nfunc _physics_process(delta: float) -> void:\n    # 1. 施加重力：每帧让小鸟往下掉\n    if not is_on_floor():\n        velocity += get_gravity() * delta * gravity_scale\n\n    # 2. 限速：防止掉太快\n    if velocity.y > max_fall_speed:\n        velocity.y = max_fall_speed\n\n    # 3. 按键跳跃：按下就给一个向上的速度\n    if Input.is_action_just_pressed(\"fly\"):\n        velocity.y = jump_force\n\n    move_and_slide()\n","gdscript","",[36,151,152,169,176,202,223,241,246,280,286,303,328,333,339,360,375,380,386,409,423,428],{"__ignoreMap":149},[153,154,157,161,165],"span",{"class":155,"line":156},"line",1,[153,158,160],{"class":159},"sTPum","extends",[153,162,164],{"class":163},"s_NWU"," CharacterBody2D",[153,166,168],{"class":167},"snYqZ"," # 创建时自带的\n",[153,170,172],{"class":155,"line":171},2,[153,173,175],{"emptyLinePlaceholder":174},true,"\n",[153,177,179,183,187,191,195,199],{"class":155,"line":178},3,[153,180,182],{"class":181},"s_xSY","@export",[153,184,186],{"class":185},"s5TCs"," var",[153,188,190],{"class":189},"s9nN2"," gravity_scale",[153,192,194],{"class":193},"si6no"," :=",[153,196,198],{"class":197},"sqbOQ"," 2.0",[153,200,201],{"class":167},"       # 重力倍率（控制掉落快慢）\n",[153,203,205,207,209,212,214,217,220],{"class":155,"line":204},4,[153,206,182],{"class":181},[153,208,186],{"class":185},[153,210,211],{"class":189}," jump_force",[153,213,194],{"class":193},[153,215,216],{"class":185}," -",[153,218,219],{"class":197},"500.0",[153,221,222],{"class":167},"       # 跳跃力量（负值 = 向上）\n",[153,224,226,228,230,233,235,238],{"class":155,"line":225},5,[153,227,182],{"class":181},[153,229,186],{"class":185},[153,231,232],{"class":189}," max_fall_speed",[153,234,194],{"class":193},[153,236,237],{"class":197}," 1000.0",[153,239,240],{"class":167},"   # 最大掉落速度\n",[153,242,244],{"class":155,"line":243},6,[153,245,175],{"emptyLinePlaceholder":174},[153,247,249,252,255,258,262,265,268,271,274,277],{"class":155,"line":248},7,[153,250,251],{"class":185},"func",[153,253,254],{"class":181}," _physics_process",[153,256,257],{"class":193},"(",[153,259,261],{"class":260},"s8w-G","delta",[153,263,264],{"class":193},":",[153,266,267],{"class":163}," float",[153,269,270],{"class":193},")",[153,272,273],{"class":185}," ->",[153,275,276],{"class":163}," void",[153,278,279],{"class":193},":\n",[153,281,283],{"class":155,"line":282},8,[153,284,285],{"class":167},"    # 1. 施加重力：每帧让小鸟往下掉\n",[153,287,289,292,295,298,301],{"class":155,"line":288},9,[153,290,291],{"class":159},"    if",[153,293,294],{"class":185}," not",[153,296,297],{"class":181}," is_on_floor",[153,299,300],{"class":193},"()",[153,302,279],{"class":260},[153,304,306,309,312,315,317,320,323,325],{"class":155,"line":305},10,[153,307,308],{"class":189},"        velocity",[153,310,311],{"class":185}," +=",[153,313,314],{"class":181}," get_gravity",[153,316,300],{"class":193},[153,318,319],{"class":185}," *",[153,321,322],{"class":189}," delta",[153,324,319],{"class":185},[153,326,327],{"class":189}," gravity_scale\n",[153,329,331],{"class":155,"line":330},11,[153,332,175],{"emptyLinePlaceholder":174},[153,334,336],{"class":155,"line":335},12,[153,337,338],{"class":167},"    # 2. 限速：防止掉太快\n",[153,340,342,344,347,350,353,356,358],{"class":155,"line":341},13,[153,343,291],{"class":159},[153,345,346],{"class":189}," velocity",[153,348,349],{"class":193},".",[153,351,352],{"class":189},"y",[153,354,355],{"class":185}," >",[153,357,232],{"class":189},[153,359,279],{"class":260},[153,361,363,365,367,369,372],{"class":155,"line":362},14,[153,364,308],{"class":189},[153,366,349],{"class":193},[153,368,352],{"class":189},[153,370,371],{"class":193}," =",[153,373,374],{"class":189}," max_fall_speed\n",[153,376,378],{"class":155,"line":377},15,[153,379,175],{"emptyLinePlaceholder":174},[153,381,383],{"class":155,"line":382},16,[153,384,385],{"class":167},"    # 3. 按键跳跃：按下就给一个向上的速度\n",[153,387,389,391,394,396,399,401,405,407],{"class":155,"line":388},17,[153,390,291],{"class":159},[153,392,393],{"class":163}," Input",[153,395,349],{"class":260},[153,397,398],{"class":181},"is_action_just_pressed",[153,400,257],{"class":193},[153,402,404],{"class":403},"spP0B","\"fly\"",[153,406,270],{"class":193},[153,408,279],{"class":260},[153,410,412,414,416,418,420],{"class":155,"line":411},18,[153,413,308],{"class":189},[153,415,349],{"class":193},[153,417,352],{"class":189},[153,419,371],{"class":193},[153,421,422],{"class":189}," jump_force\n",[153,424,426],{"class":155,"line":425},19,[153,427,175],{"emptyLinePlaceholder":174},[153,429,431,434],{"class":155,"line":430},20,[153,432,433],{"class":181},"    move_and_slide",[153,435,436],{"class":193},"()\n",[71,438,440],{"id":439},"这段代码在干嘛","这段代码在干嘛？",[144,442,444],{"className":146,"code":443,"language":148,"meta":149,"style":149},"extends CharacterBody2D # 创建时自带的\n",[36,445,446],{"__ignoreMap":149},[153,447,448,450,452],{"class":155,"line":156},[153,449,160],{"class":159},[153,451,164],{"class":163},[153,453,168],{"class":167},[30,455,456],{},[33,457,458,459,461],{},"为 ",[36,460,38],{}," 节点创建脚本时会自带的代码。告诉 Godot：这个脚本是给「角色」用的（一般不用理会）。",[144,463,465],{"className":146,"code":464,"language":148,"meta":149,"style":149},"@export var gravity_scale := 2.0       # 重力倍率（控制掉落快慢）\n@export var jump_force := -500.0       # 跳跃力量（负值 = 向上）\n@export var max_fall_speed := 1000.0   # 最大掉落速度\n",[36,466,467,481,497],{"__ignoreMap":149},[153,468,469,471,473,475,477,479],{"class":155,"line":156},[153,470,182],{"class":181},[153,472,186],{"class":185},[153,474,190],{"class":189},[153,476,194],{"class":193},[153,478,198],{"class":197},[153,480,201],{"class":167},[153,482,483,485,487,489,491,493,495],{"class":155,"line":171},[153,484,182],{"class":181},[153,486,186],{"class":185},[153,488,211],{"class":189},[153,490,194],{"class":193},[153,492,216],{"class":185},[153,494,219],{"class":197},[153,496,222],{"class":167},[153,498,499,501,503,505,507,509],{"class":155,"line":178},[153,500,182],{"class":181},[153,502,186],{"class":185},[153,504,232],{"class":189},[153,506,194],{"class":193},[153,508,237],{"class":197},[153,510,240],{"class":167},[30,512,513,519,525,531],{},[33,514,515,518],{},[36,516,517],{},"var"," 定义变量",[33,520,521,524],{},[36,522,523],{},"jump_force"," 等是自定义的变量名，",[33,526,527,530],{},[36,528,529],{},"-500.0"," 是我们定义的数值！",[33,532,533,535],{},[36,534,182],{}," 加在前面是为了让 Godot 把变量放到面板上，方便我们后续调整。👇",[15,537,538],{},[19,539],{"alt":540,"src":541},"02-create-bird-右侧面板","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F02-create-bird-%E5%8F%B3%E4%BE%A7%E9%9D%A2%E6%9D%BF.png",[15,543,544],{},"它就是我们说的「数值」，例如一般游戏中用来定义角色的「攻击力」「血量」......",[144,546,548],{"className":146,"code":547,"language":148,"meta":149,"style":149},"func _physics_process(delta: float) -> void:\n",[36,549,550],{"__ignoreMap":149},[153,551,552,554,556,558,560,562,564,566,568,570],{"class":155,"line":156},[153,553,251],{"class":185},[153,555,254],{"class":181},[153,557,257],{"class":193},[153,559,261],{"class":260},[153,561,264],{"class":193},[153,563,267],{"class":163},[153,565,270],{"class":193},[153,567,273],{"class":185},[153,569,276],{"class":163},[153,571,279],{"class":193},[30,573,574,579],{},[33,575,576,578],{},[36,577,251],{},"： 用来定义方法",[33,580,581,584],{},[36,582,583],{},"_physics_process(delta)","：游戏每一帧都会自动执行这个函数（大约每秒 60 次）",[144,586,588],{"className":146,"code":587,"language":148,"meta":149,"style":149},"func _physics_process(delta: float) -> void:\n    # 1. 施加重力：每帧让小鸟往下掉\n    if not is_on_floor():\n        velocity += get_gravity() * delta * gravity_scale\n\n    # 2. 限速：防止掉太快\n    if velocity.y > max_fall_speed:\n        velocity.y = max_fall_speed\n\n    # 3. 按键跳跃：按下就给一个向上的速度\n    if Input.is_action_just_pressed(\"fly\"):\n        velocity.y = jump_force\n\n    move_and_slide()\n",[36,589,590,612,616,628,646,650,654,670,682,686,690,708,720,724],{"__ignoreMap":149},[153,591,592,594,596,598,600,602,604,606,608,610],{"class":155,"line":156},[153,593,251],{"class":185},[153,595,254],{"class":181},[153,597,257],{"class":193},[153,599,261],{"class":260},[153,601,264],{"class":193},[153,603,267],{"class":163},[153,605,270],{"class":193},[153,607,273],{"class":185},[153,609,276],{"class":163},[153,611,279],{"class":193},[153,613,614],{"class":155,"line":171},[153,615,285],{"class":167},[153,617,618,620,622,624,626],{"class":155,"line":178},[153,619,291],{"class":159},[153,621,294],{"class":185},[153,623,297],{"class":181},[153,625,300],{"class":193},[153,627,279],{"class":260},[153,629,630,632,634,636,638,640,642,644],{"class":155,"line":204},[153,631,308],{"class":189},[153,633,311],{"class":185},[153,635,314],{"class":181},[153,637,300],{"class":193},[153,639,319],{"class":185},[153,641,322],{"class":189},[153,643,319],{"class":185},[153,645,327],{"class":189},[153,647,648],{"class":155,"line":225},[153,649,175],{"emptyLinePlaceholder":174},[153,651,652],{"class":155,"line":243},[153,653,338],{"class":167},[153,655,656,658,660,662,664,666,668],{"class":155,"line":248},[153,657,291],{"class":159},[153,659,346],{"class":189},[153,661,349],{"class":193},[153,663,352],{"class":189},[153,665,355],{"class":185},[153,667,232],{"class":189},[153,669,279],{"class":260},[153,671,672,674,676,678,680],{"class":155,"line":282},[153,673,308],{"class":189},[153,675,349],{"class":193},[153,677,352],{"class":189},[153,679,371],{"class":193},[153,681,374],{"class":189},[153,683,684],{"class":155,"line":288},[153,685,175],{"emptyLinePlaceholder":174},[153,687,688],{"class":155,"line":305},[153,689,385],{"class":167},[153,691,692,694,696,698,700,702,704,706],{"class":155,"line":330},[153,693,291],{"class":159},[153,695,393],{"class":163},[153,697,349],{"class":260},[153,699,398],{"class":181},[153,701,257],{"class":193},[153,703,404],{"class":403},[153,705,270],{"class":193},[153,707,279],{"class":260},[153,709,710,712,714,716,718],{"class":155,"line":335},[153,711,308],{"class":189},[153,713,349],{"class":193},[153,715,352],{"class":189},[153,717,371],{"class":193},[153,719,422],{"class":189},[153,721,722],{"class":155,"line":341},[153,723,175],{"emptyLinePlaceholder":174},[153,725,726,728],{"class":155,"line":362},[153,727,433],{"class":181},[153,729,436],{"class":193},[15,731,732],{},"其中：",[144,734,736],{"className":146,"code":735,"language":148,"meta":149,"style":149},"# 1. 施加重力：每帧让小鸟往下掉\nif not is_on_floor():\n    velocity += get_gravity() * delta * gravity_scale\n",[36,737,738,743,756],{"__ignoreMap":149},[153,739,740],{"class":155,"line":156},[153,741,742],{"class":167},"# 1. 施加重力：每帧让小鸟往下掉\n",[153,744,745,748,750,752,754],{"class":155,"line":171},[153,746,747],{"class":159},"if",[153,749,294],{"class":185},[153,751,297],{"class":181},[153,753,300],{"class":193},[153,755,279],{"class":260},[153,757,758,761,763,765,767,769,771,773],{"class":155,"line":178},[153,759,760],{"class":189},"    velocity",[153,762,311],{"class":185},[153,764,314],{"class":181},[153,766,300],{"class":193},[153,768,319],{"class":185},[153,770,322],{"class":189},[153,772,319],{"class":185},[153,774,327],{"class":189},[30,776,777,805],{},[33,778,779,781,782],{},[36,780,747],{}," 是用来【判断】的\n",[30,783,784,790,799],{},[33,785,786,789],{},[36,787,788],{},"not"," 是否定句",[33,791,792,795,796,798],{},[36,793,794],{},"is_on_floor()"," 是 Godot引擎 为 ",[36,797,38],{},"节点赋予的属性",[33,800,801,804],{},[36,802,803],{},"if not is_on_floor():"," 的意思是“如果 ‘角色’ 没有 站在地面时”\n是的，如果角色没有在地面，那就让角色往下掉！但是要以什么速度往下掉呢？",[33,806,807,810],{},[36,808,809],{},"velocity += get_gravity() * delta * gravity_scale",[30,811,812,828,834,839,845],{},[33,813,814,817,818,820,821,824,825,827],{},[36,815,816],{},"velocity"," 是 Godot 给 ",[36,819,38],{}," 内置的「速度」属性。它是一个二维数值，有 ",[36,822,823],{},"x","（左右速度）和 ",[36,826,352],{},"（上下速度）两个分量。初始值是 0（小鸟不动）",[33,829,830,833],{},[36,831,832],{},"get_gravity()"," 是 Godot 内置方法，返回项目设置里的「重力」值（默认向下 980）",[33,835,836,838],{},[36,837,261],{}," 是「上一帧到这一帧的时间间隔」（秒）。乘上它能保证不同帧率（60fps \u002F 120fps）下游戏速度一致",[33,840,841,844],{},[36,842,843],{},"gravity_scale"," 是我们自己定义的「重力倍率」，越大掉得越快",[33,846,847,850,851],{},[36,848,849],{},"+="," 是「累加」的意思，等价于 ",[36,852,853],{},"velocity = velocity + ...",[15,855,856,857,864],{},"所以这一行的意思是：",[858,859,860,861,863],"strong",{},"每一帧给 ",[36,862,816],{}," 累加一点点重力","。速度越累越大 → 小鸟越掉越快，这就是物理课上学过的「自由落体」。",[144,866,868],{"className":146,"code":867,"language":148,"meta":149,"style":149},"# 2. 限速：防止掉太快\nif velocity.y > max_fall_speed:\n    velocity.y = max_fall_speed\n",[36,869,870,875,891],{"__ignoreMap":149},[153,871,872],{"class":155,"line":156},[153,873,874],{"class":167},"# 2. 限速：防止掉太快\n",[153,876,877,879,881,883,885,887,889],{"class":155,"line":171},[153,878,747],{"class":159},[153,880,346],{"class":189},[153,882,349],{"class":193},[153,884,352],{"class":189},[153,886,355],{"class":185},[153,888,232],{"class":189},[153,890,279],{"class":260},[153,892,893,895,897,899,901],{"class":155,"line":178},[153,894,760],{"class":189},[153,896,349],{"class":193},[153,898,352],{"class":189},[153,900,371],{"class":193},[153,902,374],{"class":189},[30,904,905,915,925],{},[33,906,907,910,911,914],{},[36,908,909],{},"velocity.y"," 就是上下方向的速度（",[858,912,913],{},"往下为正、往上为负","，这是 Godot 的坐标系约定）",[33,916,917,918,920,921,924],{},"如果当前下落速度 ",[36,919,909],{}," 超过了我们设的最大值 ",[36,922,923],{},"max_fall_speed","，就把它「卡」在最大值上",[33,926,927,930],{},[858,928,929],{},"为什么需要？"," 因为重力会一直累加，没有这个限制小鸟会越掉越快，最后快得看不见",[144,932,934],{"className":146,"code":933,"language":148,"meta":149,"style":149},"# 3. 按键跳跃：按下就给一个向上的速度\nif Input.is_action_just_pressed(\"fly\"):\n    velocity.y = jump_force\n",[36,935,936,941,959],{"__ignoreMap":149},[153,937,938],{"class":155,"line":156},[153,939,940],{"class":167},"# 3. 按键跳跃：按下就给一个向上的速度\n",[153,942,943,945,947,949,951,953,955,957],{"class":155,"line":171},[153,944,747],{"class":159},[153,946,393],{"class":163},[153,948,349],{"class":260},[153,950,398],{"class":181},[153,952,257],{"class":193},[153,954,404],{"class":403},[153,956,270],{"class":193},[153,958,279],{"class":260},[153,960,961,963,965,967,969],{"class":155,"line":178},[153,962,760],{"class":189},[153,964,349],{"class":193},[153,966,352],{"class":189},[153,968,371],{"class":193},[153,970,422],{"class":189},[30,972,973,983,995],{},[33,974,975,978,979,982],{},[36,976,977],{},"Input.is_action_just_pressed(\"fly\")"," 检测「fly」这个按键",[858,980,981],{},"刚刚被按下","（一次按下只触发一次，长按不会连续触发）",[33,984,985,986,988,989,991,992,994],{},"一旦按下，直接把 ",[36,987,909],{}," 设为 ",[36,990,523],{},"（前面定义的 ",[36,993,529],{},"，负数 = 向上）",[33,996,997,998,1001],{},"这就是「跳跃」：瞬间把速度变成向上，",[858,999,1000],{},"覆盖掉","之前累加的下落速度",[1003,1004,1005],"blockquote",{},[15,1006,1007],{},"「fly」是什么按键？我们还没告诉 Godot，下面就来配置快捷键。",[144,1009,1011],{"className":146,"code":1010,"language":148,"meta":149,"style":149},"move_and_slide()\n",[36,1012,1013],{"__ignoreMap":149},[153,1014,1015,1018],{"class":155,"line":156},[153,1016,1017],{"class":181},"move_and_slide",[153,1019,436],{"class":193},[30,1021,1022,1028,1037],{},[33,1023,1024,1025,1027],{},"Godot 给 ",[36,1026,38],{}," 内置的方法",[33,1029,1030,1036],{},[858,1031,1032,1033,1035],{},"根据 ",[36,1034,816],{}," 真正移动小鸟","，并自动处理碰撞（撞到东西就停下来）",[33,1038,1039,1040,1042],{},"没有这一行，前面算好的 ",[36,1041,816],{}," 都只是「空想」，小鸟不会真的动起来",[71,1044,1045],{"id":1045},"配置跳跃按键",[15,1047,1048,1049,1051],{},"代码里用了 ",[36,1050,977],{},"，所以我们需要在项目设置里添加这个按键映射：",[15,1053,1054,1055],{},"路径：项目 -> 项目设置 -> 输入映射 (Input Map)\n",[19,1056],{"alt":1057,"src":1058},"02-create-bird-配置跳跃按键","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F02-create-bird-%E9%85%8D%E7%BD%AE%E8%B7%B3%E8%B7%83%E6%8C%89%E9%94%AE.png",[1060,1061,1062,1069],"ol",{},[33,1063,1064,1065,1068],{},"在上方输入 ",[36,1066,1067],{},"fly","，点击「添加」",[33,1070,1071,1072,1074,1075,1078],{},"点击 ",[36,1073,1067],{}," 右边的 ",[36,1076,1077],{},"+","，按下你想要的按键（比如空格键）",[15,1080,1081],{},"这样按空格键小鸟就会往上跳了！不过目前这只小鸟还没有舞台，下一章，我们将为它搭建一个可以飞的场景。",[1083,1084,1085],"style",{},"html pre.shiki code .sTPum, html code.shiki .sTPum{--shiki-default:#1E754F;--shiki-dark:#4D9375}html pre.shiki code .s_NWU, html code.shiki .s_NWU{--shiki-default:#2E8F82;--shiki-dark:#5DA994}html pre.shiki code .snYqZ, html code.shiki .snYqZ{--shiki-default:#A0ADA0;--shiki-dark:#758575DD}html pre.shiki code .s_xSY, html code.shiki .s_xSY{--shiki-default:#59873A;--shiki-dark:#80A665}html pre.shiki code .s5TCs, html code.shiki .s5TCs{--shiki-default:#AB5959;--shiki-dark:#CB7676}html pre.shiki code .s9nN2, html code.shiki .s9nN2{--shiki-default:#B07D48;--shiki-dark:#BD976A}html pre.shiki code .si6no, html code.shiki .si6no{--shiki-default:#999999;--shiki-dark:#666666}html pre.shiki code .sqbOQ, html code.shiki .sqbOQ{--shiki-default:#2F798A;--shiki-dark:#4C9A91}html pre.shiki code .s8w-G, html code.shiki .s8w-G{--shiki-default:#393A34;--shiki-dark:#DBD7CAEE}html pre.shiki code .spP0B, html code.shiki .spP0B{--shiki-default:#B56959;--shiki-dark:#C98A7D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":149,"searchDepth":171,"depth":171,"links":1087},[1088,1089,1093],{"id":12,"depth":171,"text":13},{"id":25,"depth":171,"text":5,"children":1090},[1091,1092],{"id":73,"depth":178,"text":73},{"id":91,"depth":178,"text":91},{"id":119,"depth":171,"text":119,"children":1094},[1095,1096],{"id":439,"depth":178,"text":440},{"id":1045,"depth":178,"text":1045},null,"2026-05-12","XGGame-Bird Godot","md","XGGame-Bird","https:\u002F\u002Fgithub.com\u002FXXGGG\u002FXGGame-Bird",{},"\u002Fdevlog\u002Fxggame-bird\u002F02-create-bird",{"title":5,"description":1099},"devlog\u002Fxggame-bird\u002F02-create-bird","4ulFauc0NCvEFhdOh3vFco0Uc_Za6ZBezoBXrpjuq6s",[1109,1329,2175,2871,4416,5888,7166,7756,8589],{"id":1110,"title":1111,"body":1112,"cover":1097,"date":1098,"description":1099,"extension":1100,"game":1101,"github":1102,"icon":1097,"meta":1324,"navigation":174,"path":1325,"seo":1326,"stem":1327,"toc":174,"__hash__":1328},"devlog\u002Fdevlog\u002Fxggame-bird\u002F01-getting-started.md","安装 Godot 及项目设置",{"type":7,"value":1113,"toc":1316},[1114,1118,1129,1132,1138,1141,1148,1155,1161,1164,1170,1173,1176,1179,1185,1188,1194,1234,1239,1243,1249,1255,1296,1301,1310,1313],[10,1115,1117],{"id":1116},"下载与安装-godot","下载与安装 Godot",[1003,1119,1120],{},[15,1121,1122,1123],{},"官网：",[1124,1125,1126],"a",{"href":1126,"rel":1127},"https:\u002F\u002Fgodotengine.org\u002F",[1128],"nofollow",[15,1130,1131],{},"下载后解压即可使用，无需安装。",[15,1133,1134],{},[19,1135],{"alt":1136,"src":1137},"01-getting-started-Godot","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F01-getting-started-Godot.png",[10,1139,1140],{"id":1140},"新建项目",[15,1142,1143,1144,1147],{},"打开 Godot，新建一个项目，取名 ",[36,1145,1146],{},"Bird","。",[15,1149,1150,1151,1154],{},"渲染器选择 ",[858,1152,1153],{},"【兼容】","— 这样后续可以导出为网页版在浏览器里玩。",[15,1156,1157],{},[19,1158],{"alt":1159,"src":1160},"01-getting-started-Godot-Bird","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F01-getting-started-Godot-Bird.png",[15,1162,1163],{},"创建完成后进入编辑器：",[15,1165,1166],{},[19,1167],{"alt":1168,"src":1169},"01-getting-started-界面","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F01-getting-started-%E7%95%8C%E9%9D%A2.png",[10,1171,1172],{"id":1172},"项目设置",[15,1174,1175],{},"因为这是一个手机竖屏游戏，所以需要先调整窗口尺寸。",[15,1177,1178],{},"路径：项目 → 项目设置 → 显示 → 窗口",[15,1180,1181],{},[19,1182],{"alt":1183,"src":1184},"01-getting-started-项目设置位置","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F01-getting-started-%E9%A1%B9%E7%9B%AE%E8%AE%BE%E7%BD%AE%E4%BD%8D%E7%BD%AE.png",[71,1186,1187],{"id":1187},"窗口尺寸",[15,1189,1190],{},[19,1191],{"alt":1192,"src":1193},"01-getting-started-项目设置","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F01-getting-started-%E9%A1%B9%E7%9B%AE%E8%AE%BE%E7%BD%AE.png",[1195,1196,1197,1210],"table",{},[1198,1199,1200],"thead",{},[1201,1202,1203,1207],"tr",{},[1204,1205,1206],"th",{},"设置项",[1204,1208,1209],{},"值",[1211,1212,1213,1224],"tbody",{},[1201,1214,1215,1219],{},[1216,1217,1218],"td",{},"视口宽度 (Viewport Width)",[1216,1220,1221],{},[36,1222,1223],{},"720",[1201,1225,1226,1229],{},[1216,1227,1228],{},"视口高度 (Viewport Height)",[1216,1230,1231],{},[36,1232,1233],{},"1280",[1003,1235,1236],{},[15,1237,1238],{},"视口是游戏的实际分辨率。",[71,1240,1242],{"id":1241},"拉伸适配-与-强制竖屏","拉伸适配 与 强制竖屏",[15,1244,1245],{},[19,1246],{"alt":1247,"src":1248},"01-getting-started-项目设置2","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F01-getting-started-%E9%A1%B9%E7%9B%AE%E8%AE%BE%E7%BD%AE2.png",[15,1250,1251,1254],{},[858,1252,1253],{},"「拉伸」"," 部分：",[1195,1256,1257,1268],{},[1198,1258,1259],{},[1201,1260,1261,1263,1265],{},[1204,1262,1206],{},[1204,1264,1209],{},[1204,1266,1267],{},"作用",[1211,1269,1270,1283],{},[1201,1271,1272,1275,1280],{},[1216,1273,1274],{},"模式 (Mode)",[1216,1276,1277],{},[36,1278,1279],{},"canvas_items",[1216,1281,1282],{},"保持 2D 图形清晰",[1201,1284,1285,1288,1293],{},[1216,1286,1287],{},"比例 (Aspect)",[1216,1289,1290],{},[36,1291,1292],{},"keep",[1216,1294,1295],{},"保持宽高比不变形",[15,1297,1298,1254],{},[858,1299,1300],{},"「手持」",[30,1302,1303],{},[33,1304,1305,1306,1309],{},"方向 (Orientation)：选择 ",[36,1307,1308],{},"portrait","（竖屏）",[1311,1312],"hr",{},[15,1314,1315],{},"设置完成！接下来就可以开始创建「小鸟」了。",{"title":149,"searchDepth":171,"depth":171,"links":1317},[1318,1319,1320],{"id":1116,"depth":171,"text":1117},{"id":1140,"depth":171,"text":1140},{"id":1172,"depth":171,"text":1172,"children":1321},[1322,1323],{"id":1187,"depth":178,"text":1187},{"id":1241,"depth":178,"text":1242},{},"\u002Fdevlog\u002Fxggame-bird\u002F01-getting-started",{"title":1111,"description":1099},"devlog\u002Fxggame-bird\u002F01-getting-started","xfXIiVt-cjGYA7F0hi9cWLKLIpkdWG1VTKPqgfQQ7ts",{"id":4,"title":5,"body":1330,"cover":1097,"date":1098,"description":1099,"extension":1100,"game":1101,"github":1102,"icon":1097,"meta":2173,"navigation":174,"path":1104,"seo":2174,"stem":1106,"toc":174,"__hash__":1107},{"type":7,"value":1331,"toc":2162},[1332,1334,1338,1340,1342,1358,1364,1368,1370,1376,1380,1382,1388,1392,1394,1398,1400,1404,1408,1412,1416,1620,1622,1634,1640,1688,1706,1710,1712,1738,1748,1892,1894,1932,1988,1994,2030,2048,2086,2106,2110,2120,2136,2138,2142,2146,2158,2160],[10,1333,13],{"id":12},[15,1335,17,1336],{},[19,1337],{"alt":21,"src":22},[10,1339,5],{"id":25},[15,1341,28],{},[30,1343,1344],{},[33,1345,1346,39,1348],{},[36,1347,38],{},[30,1349,1350,1354],{},[33,1351,1352,47],{},[36,1353,46],{},[33,1355,1356,53],{},[36,1357,52],{},[15,1359,56,1360,59,1362,63],{},[36,1361,46],{},[36,1363,62],{},[15,1365,1366],{},[19,1367],{"alt":68,"src":69},[71,1369,73],{"id":73},[15,1371,1372,79,1374],{},[36,1373,78],{},[36,1375,82],{},[15,1377,1378],{},[19,1379],{"alt":87,"src":88},[71,1381,91],{"id":91},[15,1383,94,1384,97,1386,101],{},[36,1385,52],{},[36,1387,100],{},[15,1389,1390],{},[19,1391],{"alt":106,"src":107},[15,1393,110],{},[15,1395,1396],{},[19,1397],{"alt":115,"src":116},[10,1399,119],{"id":119},[15,1401,94,1402,124],{},[36,1403,38],{},[15,1405,1406],{},[19,1407],{"alt":129,"src":130},[15,1409,133,1410],{},[36,1411,136],{},[15,1413,1414],{},[19,1415],{"alt":141,"src":142},[144,1417,1418],{"className":146,"code":147,"language":148,"meta":149,"style":149},[36,1419,1420,1428,1432,1446,1462,1476,1480,1502,1506,1518,1536,1540,1544,1560,1572,1576,1580,1598,1610,1614],{"__ignoreMap":149},[153,1421,1422,1424,1426],{"class":155,"line":156},[153,1423,160],{"class":159},[153,1425,164],{"class":163},[153,1427,168],{"class":167},[153,1429,1430],{"class":155,"line":171},[153,1431,175],{"emptyLinePlaceholder":174},[153,1433,1434,1436,1438,1440,1442,1444],{"class":155,"line":178},[153,1435,182],{"class":181},[153,1437,186],{"class":185},[153,1439,190],{"class":189},[153,1441,194],{"class":193},[153,1443,198],{"class":197},[153,1445,201],{"class":167},[153,1447,1448,1450,1452,1454,1456,1458,1460],{"class":155,"line":204},[153,1449,182],{"class":181},[153,1451,186],{"class":185},[153,1453,211],{"class":189},[153,1455,194],{"class":193},[153,1457,216],{"class":185},[153,1459,219],{"class":197},[153,1461,222],{"class":167},[153,1463,1464,1466,1468,1470,1472,1474],{"class":155,"line":225},[153,1465,182],{"class":181},[153,1467,186],{"class":185},[153,1469,232],{"class":189},[153,1471,194],{"class":193},[153,1473,237],{"class":197},[153,1475,240],{"class":167},[153,1477,1478],{"class":155,"line":243},[153,1479,175],{"emptyLinePlaceholder":174},[153,1481,1482,1484,1486,1488,1490,1492,1494,1496,1498,1500],{"class":155,"line":248},[153,1483,251],{"class":185},[153,1485,254],{"class":181},[153,1487,257],{"class":193},[153,1489,261],{"class":260},[153,1491,264],{"class":193},[153,1493,267],{"class":163},[153,1495,270],{"class":193},[153,1497,273],{"class":185},[153,1499,276],{"class":163},[153,1501,279],{"class":193},[153,1503,1504],{"class":155,"line":282},[153,1505,285],{"class":167},[153,1507,1508,1510,1512,1514,1516],{"class":155,"line":288},[153,1509,291],{"class":159},[153,1511,294],{"class":185},[153,1513,297],{"class":181},[153,1515,300],{"class":193},[153,1517,279],{"class":260},[153,1519,1520,1522,1524,1526,1528,1530,1532,1534],{"class":155,"line":305},[153,1521,308],{"class":189},[153,1523,311],{"class":185},[153,1525,314],{"class":181},[153,1527,300],{"class":193},[153,1529,319],{"class":185},[153,1531,322],{"class":189},[153,1533,319],{"class":185},[153,1535,327],{"class":189},[153,1537,1538],{"class":155,"line":330},[153,1539,175],{"emptyLinePlaceholder":174},[153,1541,1542],{"class":155,"line":335},[153,1543,338],{"class":167},[153,1545,1546,1548,1550,1552,1554,1556,1558],{"class":155,"line":341},[153,1547,291],{"class":159},[153,1549,346],{"class":189},[153,1551,349],{"class":193},[153,1553,352],{"class":189},[153,1555,355],{"class":185},[153,1557,232],{"class":189},[153,1559,279],{"class":260},[153,1561,1562,1564,1566,1568,1570],{"class":155,"line":362},[153,1563,308],{"class":189},[153,1565,349],{"class":193},[153,1567,352],{"class":189},[153,1569,371],{"class":193},[153,1571,374],{"class":189},[153,1573,1574],{"class":155,"line":377},[153,1575,175],{"emptyLinePlaceholder":174},[153,1577,1578],{"class":155,"line":382},[153,1579,385],{"class":167},[153,1581,1582,1584,1586,1588,1590,1592,1594,1596],{"class":155,"line":388},[153,1583,291],{"class":159},[153,1585,393],{"class":163},[153,1587,349],{"class":260},[153,1589,398],{"class":181},[153,1591,257],{"class":193},[153,1593,404],{"class":403},[153,1595,270],{"class":193},[153,1597,279],{"class":260},[153,1599,1600,1602,1604,1606,1608],{"class":155,"line":411},[153,1601,308],{"class":189},[153,1603,349],{"class":193},[153,1605,352],{"class":189},[153,1607,371],{"class":193},[153,1609,422],{"class":189},[153,1611,1612],{"class":155,"line":425},[153,1613,175],{"emptyLinePlaceholder":174},[153,1615,1616,1618],{"class":155,"line":430},[153,1617,433],{"class":181},[153,1619,436],{"class":193},[71,1621,440],{"id":439},[144,1623,1624],{"className":146,"code":443,"language":148,"meta":149,"style":149},[36,1625,1626],{"__ignoreMap":149},[153,1627,1628,1630,1632],{"class":155,"line":156},[153,1629,160],{"class":159},[153,1631,164],{"class":163},[153,1633,168],{"class":167},[30,1635,1636],{},[33,1637,458,1638,461],{},[36,1639,38],{},[144,1641,1642],{"className":146,"code":464,"language":148,"meta":149,"style":149},[36,1643,1644,1658,1674],{"__ignoreMap":149},[153,1645,1646,1648,1650,1652,1654,1656],{"class":155,"line":156},[153,1647,182],{"class":181},[153,1649,186],{"class":185},[153,1651,190],{"class":189},[153,1653,194],{"class":193},[153,1655,198],{"class":197},[153,1657,201],{"class":167},[153,1659,1660,1662,1664,1666,1668,1670,1672],{"class":155,"line":171},[153,1661,182],{"class":181},[153,1663,186],{"class":185},[153,1665,211],{"class":189},[153,1667,194],{"class":193},[153,1669,216],{"class":185},[153,1671,219],{"class":197},[153,1673,222],{"class":167},[153,1675,1676,1678,1680,1682,1684,1686],{"class":155,"line":178},[153,1677,182],{"class":181},[153,1679,186],{"class":185},[153,1681,232],{"class":189},[153,1683,194],{"class":193},[153,1685,237],{"class":197},[153,1687,240],{"class":167},[30,1689,1690,1694,1698,1702],{},[33,1691,1692,518],{},[36,1693,517],{},[33,1695,1696,524],{},[36,1697,523],{},[33,1699,1700,530],{},[36,1701,529],{},[33,1703,1704,535],{},[36,1705,182],{},[15,1707,1708],{},[19,1709],{"alt":540,"src":541},[15,1711,544],{},[144,1713,1714],{"className":146,"code":547,"language":148,"meta":149,"style":149},[36,1715,1716],{"__ignoreMap":149},[153,1717,1718,1720,1722,1724,1726,1728,1730,1732,1734,1736],{"class":155,"line":156},[153,1719,251],{"class":185},[153,1721,254],{"class":181},[153,1723,257],{"class":193},[153,1725,261],{"class":260},[153,1727,264],{"class":193},[153,1729,267],{"class":163},[153,1731,270],{"class":193},[153,1733,273],{"class":185},[153,1735,276],{"class":163},[153,1737,279],{"class":193},[30,1739,1740,1744],{},[33,1741,1742,578],{},[36,1743,251],{},[33,1745,1746,584],{},[36,1747,583],{},[144,1749,1750],{"className":146,"code":587,"language":148,"meta":149,"style":149},[36,1751,1752,1774,1778,1790,1808,1812,1816,1832,1844,1848,1852,1870,1882,1886],{"__ignoreMap":149},[153,1753,1754,1756,1758,1760,1762,1764,1766,1768,1770,1772],{"class":155,"line":156},[153,1755,251],{"class":185},[153,1757,254],{"class":181},[153,1759,257],{"class":193},[153,1761,261],{"class":260},[153,1763,264],{"class":193},[153,1765,267],{"class":163},[153,1767,270],{"class":193},[153,1769,273],{"class":185},[153,1771,276],{"class":163},[153,1773,279],{"class":193},[153,1775,1776],{"class":155,"line":171},[153,1777,285],{"class":167},[153,1779,1780,1782,1784,1786,1788],{"class":155,"line":178},[153,1781,291],{"class":159},[153,1783,294],{"class":185},[153,1785,297],{"class":181},[153,1787,300],{"class":193},[153,1789,279],{"class":260},[153,1791,1792,1794,1796,1798,1800,1802,1804,1806],{"class":155,"line":204},[153,1793,308],{"class":189},[153,1795,311],{"class":185},[153,1797,314],{"class":181},[153,1799,300],{"class":193},[153,1801,319],{"class":185},[153,1803,322],{"class":189},[153,1805,319],{"class":185},[153,1807,327],{"class":189},[153,1809,1810],{"class":155,"line":225},[153,1811,175],{"emptyLinePlaceholder":174},[153,1813,1814],{"class":155,"line":243},[153,1815,338],{"class":167},[153,1817,1818,1820,1822,1824,1826,1828,1830],{"class":155,"line":248},[153,1819,291],{"class":159},[153,1821,346],{"class":189},[153,1823,349],{"class":193},[153,1825,352],{"class":189},[153,1827,355],{"class":185},[153,1829,232],{"class":189},[153,1831,279],{"class":260},[153,1833,1834,1836,1838,1840,1842],{"class":155,"line":282},[153,1835,308],{"class":189},[153,1837,349],{"class":193},[153,1839,352],{"class":189},[153,1841,371],{"class":193},[153,1843,374],{"class":189},[153,1845,1846],{"class":155,"line":288},[153,1847,175],{"emptyLinePlaceholder":174},[153,1849,1850],{"class":155,"line":305},[153,1851,385],{"class":167},[153,1853,1854,1856,1858,1860,1862,1864,1866,1868],{"class":155,"line":330},[153,1855,291],{"class":159},[153,1857,393],{"class":163},[153,1859,349],{"class":260},[153,1861,398],{"class":181},[153,1863,257],{"class":193},[153,1865,404],{"class":403},[153,1867,270],{"class":193},[153,1869,279],{"class":260},[153,1871,1872,1874,1876,1878,1880],{"class":155,"line":335},[153,1873,308],{"class":189},[153,1875,349],{"class":193},[153,1877,352],{"class":189},[153,1879,371],{"class":193},[153,1881,422],{"class":189},[153,1883,1884],{"class":155,"line":341},[153,1885,175],{"emptyLinePlaceholder":174},[153,1887,1888,1890],{"class":155,"line":362},[153,1889,433],{"class":181},[153,1891,436],{"class":193},[15,1893,732],{},[144,1895,1896],{"className":146,"code":735,"language":148,"meta":149,"style":149},[36,1897,1898,1902,1914],{"__ignoreMap":149},[153,1899,1900],{"class":155,"line":156},[153,1901,742],{"class":167},[153,1903,1904,1906,1908,1910,1912],{"class":155,"line":171},[153,1905,747],{"class":159},[153,1907,294],{"class":185},[153,1909,297],{"class":181},[153,1911,300],{"class":193},[153,1913,279],{"class":260},[153,1915,1916,1918,1920,1922,1924,1926,1928,1930],{"class":155,"line":178},[153,1917,760],{"class":189},[153,1919,311],{"class":185},[153,1921,314],{"class":181},[153,1923,300],{"class":193},[153,1925,319],{"class":185},[153,1927,322],{"class":189},[153,1929,319],{"class":185},[153,1931,327],{"class":189},[30,1933,1934,1954],{},[33,1935,1936,781,1938],{},[36,1937,747],{},[30,1939,1940,1944,1950],{},[33,1941,1942,789],{},[36,1943,788],{},[33,1945,1946,795,1948,798],{},[36,1947,794],{},[36,1949,38],{},[33,1951,1952,804],{},[36,1953,803],{},[33,1955,1956,1958],{},[36,1957,809],{},[30,1959,1960,1970,1974,1978,1982],{},[33,1961,1962,817,1964,820,1966,824,1968,827],{},[36,1963,816],{},[36,1965,38],{},[36,1967,823],{},[36,1969,352],{},[33,1971,1972,833],{},[36,1973,832],{},[33,1975,1976,838],{},[36,1977,261],{},[33,1979,1980,844],{},[36,1981,843],{},[33,1983,1984,850,1986],{},[36,1985,849],{},[36,1987,853],{},[15,1989,856,1990,864],{},[858,1991,860,1992,863],{},[36,1993,816],{},[144,1995,1996],{"className":146,"code":867,"language":148,"meta":149,"style":149},[36,1997,1998,2002,2018],{"__ignoreMap":149},[153,1999,2000],{"class":155,"line":156},[153,2001,874],{"class":167},[153,2003,2004,2006,2008,2010,2012,2014,2016],{"class":155,"line":171},[153,2005,747],{"class":159},[153,2007,346],{"class":189},[153,2009,349],{"class":193},[153,2011,352],{"class":189},[153,2013,355],{"class":185},[153,2015,232],{"class":189},[153,2017,279],{"class":260},[153,2019,2020,2022,2024,2026,2028],{"class":155,"line":178},[153,2021,760],{"class":189},[153,2023,349],{"class":193},[153,2025,352],{"class":189},[153,2027,371],{"class":193},[153,2029,374],{"class":189},[30,2031,2032,2038,2044],{},[33,2033,2034,910,2036,914],{},[36,2035,909],{},[858,2037,913],{},[33,2039,917,2040,920,2042,924],{},[36,2041,909],{},[36,2043,923],{},[33,2045,2046,930],{},[858,2047,929],{},[144,2049,2050],{"className":146,"code":933,"language":148,"meta":149,"style":149},[36,2051,2052,2056,2074],{"__ignoreMap":149},[153,2053,2054],{"class":155,"line":156},[153,2055,940],{"class":167},[153,2057,2058,2060,2062,2064,2066,2068,2070,2072],{"class":155,"line":171},[153,2059,747],{"class":159},[153,2061,393],{"class":163},[153,2063,349],{"class":260},[153,2065,398],{"class":181},[153,2067,257],{"class":193},[153,2069,404],{"class":403},[153,2071,270],{"class":193},[153,2073,279],{"class":260},[153,2075,2076,2078,2080,2082,2084],{"class":155,"line":178},[153,2077,760],{"class":189},[153,2079,349],{"class":193},[153,2081,352],{"class":189},[153,2083,371],{"class":193},[153,2085,422],{"class":189},[30,2087,2088,2094,2102],{},[33,2089,2090,978,2092,982],{},[36,2091,977],{},[858,2093,981],{},[33,2095,985,2096,988,2098,991,2100,994],{},[36,2097,909],{},[36,2099,523],{},[36,2101,529],{},[33,2103,997,2104,1001],{},[858,2105,1000],{},[1003,2107,2108],{},[15,2109,1007],{},[144,2111,2112],{"className":146,"code":1010,"language":148,"meta":149,"style":149},[36,2113,2114],{"__ignoreMap":149},[153,2115,2116,2118],{"class":155,"line":156},[153,2117,1017],{"class":181},[153,2119,436],{"class":193},[30,2121,2122,2126,2132],{},[33,2123,1024,2124,1027],{},[36,2125,38],{},[33,2127,2128,1036],{},[858,2129,1032,2130,1035],{},[36,2131,816],{},[33,2133,1039,2134,1042],{},[36,2135,816],{},[71,2137,1045],{"id":1045},[15,2139,1048,2140,1051],{},[36,2141,977],{},[15,2143,1054,2144],{},[19,2145],{"alt":1057,"src":1058},[1060,2147,2148,2152],{},[33,2149,1064,2150,1068],{},[36,2151,1067],{},[33,2153,1071,2154,1074,2156,1078],{},[36,2155,1067],{},[36,2157,1077],{},[15,2159,1081],{},[1083,2161,1085],{},{"title":149,"searchDepth":171,"depth":171,"links":2163},[2164,2165,2169],{"id":12,"depth":171,"text":13},{"id":25,"depth":171,"text":5,"children":2166},[2167,2168],{"id":73,"depth":178,"text":73},{"id":91,"depth":178,"text":91},{"id":119,"depth":171,"text":119,"children":2170},[2171,2172],{"id":439,"depth":178,"text":440},{"id":1045,"depth":178,"text":1045},{},{"title":5,"description":1099},{"id":2176,"title":2177,"body":2178,"cover":1097,"date":1098,"description":1099,"extension":1100,"game":1101,"github":1102,"icon":1097,"meta":2866,"navigation":174,"path":2867,"seo":2868,"stem":2869,"toc":174,"__hash__":2870},"devlog\u002Fdevlog\u002Fxggame-bird\u002F03-game-scene.md","游戏场景",{"type":7,"value":2179,"toc":2856},[2180,2183,2186,2189,2204,2207,2222,2225,2232,2236,2247,2251,2262,2265,2270,2276,2279,2282,2289,2291,2314,2320,2330,2336,2357,2360,2363,2374,2377,2380,2383,2386,2406,2413,2834,2846,2850,2853],[15,2181,2182],{},"这一章我们来完善游戏世界：搭建舞台、调整摄像机、加边界。",[10,2184,2185],{"id":2185},"创建游戏场景",[15,2187,2188],{},"小鸟做好了，但现在还没有一个「舞台」让它表演。我们需要创建一个游戏主场景，把小鸟放进去，再加一个摄像机来\"看\"它。",[15,2190,2191,2192,2195,2196,2199,2200],{},"新建场景，根节点选 ",[36,2193,2194],{},"Node2D","，保存为 ",[36,2197,2198],{},"scenes\u002Fgame.tscn","：\n",[19,2201],{"alt":2202,"src":2203},"03-game-scene-创建游戏场景","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F03-game-scene-%E5%88%9B%E5%BB%BA%E6%B8%B8%E6%88%8F%E5%9C%BA%E6%99%AF.png",[15,2205,2206],{},"使用到的节点：",[30,2208,2209],{},[33,2210,2211,2213,2214],{},[36,2212,2194],{}," — 游戏主场景\n",[30,2215,2216],{},[33,2217,2218,2221],{},[36,2219,2220],{},"Camera2D"," — 摄像机（决定屏幕看到哪里）",[10,2223,2224],{"id":2224},"调整摄像机位置",[15,2226,2227,2228],{},"调整摄像机（固定）位置：\n",[19,2229],{"alt":2230,"src":2231},"03-game-scene-调整摄像机位置","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F03-game-scene-%E8%B0%83%E6%95%B4%E6%91%84%E5%83%8F%E6%9C%BA%E4%BD%8D%E7%BD%AE.png",[10,2233,2235],{"id":2234},"导入小鸟角色","导入“小鸟”角色",[15,2237,2238,2239,2242,2243],{},"把之前做好的 ",[36,2240,2241],{},"bird.tscn"," 拖进场景中，小鸟就出现了：\n",[19,2244],{"alt":2245,"src":2246},"03-game-scene-导入bird","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F03-game-scene-%E5%AF%BC%E5%85%A5bird.png",[10,2248,2250],{"id":2249},"设置主场景然后测试","设置【主场景】然后测试",[15,2252,2253,2254,2257,2258],{},"然后把 ",[36,2255,2256],{},"game.tscn"," 设置为【主场景】，点击运行游戏：\n",[19,2259],{"alt":2260,"src":2261},"03-game-scene-设置主场景","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F03-game-scene-%E8%AE%BE%E7%BD%AE%E4%B8%BB%E5%9C%BA%E6%99%AF.png",[15,2263,2264],{},"这时候小鸟就在游戏场景里了，点击【鼠标左键】或【空格键】小鸟就会起飞！",[1003,2266,2267],{},[15,2268,2269],{},"确保你已经按上一章配置了【fly】按键映射！",[15,2271,2272],{},[19,2273],{"alt":2274,"src":2275},"03-game-scene-运行游戏","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F03-game-scene-%E8%BF%90%E8%A1%8C%E6%B8%B8%E6%88%8F.png",[15,2277,2278],{},"不过现在不按按键的话，小鸟就会一直往下掉，掉到屏幕外面去。别急，接下来我们会给游戏场景加上边界。",[10,2280,2281],{"id":2281},"添加边界",[15,2283,2284,2285,2288],{},"为了让小鸟不会掉出屏幕，我们给游戏世界加上",[858,2286,2287],{},"底部","边界。",[15,2290,2206],{},[30,2292,2293],{},[33,2294,2295,2298,2299],{},[36,2296,2297],{},"StaticBody2D"," — 静态物理体（不动，但能碰撞）\n",[30,2300,2301],{},[33,2302,2303,2305,2306],{},[36,2304,52],{}," — 碰撞形状\n",[30,2307,2308],{},[33,2309,2310,2313],{},[36,2311,2312],{},"WorldBoundaryShape2D"," — 选择这个形状（世界边界）",[15,2315,2316],{},[19,2317],{"alt":2318,"src":2319},"03-game-scene-边界","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F03-game-scene-%E8%BE%B9%E7%95%8C.png",[15,2321,2322,2323,2325,2326,2329],{},"然后选择 ",[36,2324,2297],{}," 节点往下拖到边缘底部，因为后续要在该节点里",[858,2327,2328],{},"加入","【死亡区域（Killzone）】，这样才能触发游戏结束的代码。",[15,2331,2332],{},[19,2333],{"alt":2334,"src":2335},"03-game-scene-边界2","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F03-game-scene-%E8%BE%B9%E7%95%8C2.png",[1060,2337,2338,2344,2354],{},[33,2339,2340,2341,2343],{},"先选择 ",[36,2342,2297],{}," 节点",[33,2345,2346,2347],{},"然后点击上方的【编辑所选节点】\n",[1060,2348,2349],{},[33,2350,2351,2352,2343],{},"避免拖拽时不小心选中 ",[36,2353,52],{},[33,2355,2356],{},"然后往下拖拽到摄像机和画面边缘。",[10,2358,2359],{"id":2359},"测试运行",[15,2361,2362],{},"会发现几个问题，不过都不是问题！",[1060,2364,2365,2368,2371],{},[33,2366,2367],{},"小鸟在原地高低移动，没有往前飞！但是这是正常的，就是要小鸟在原地高低移动。后续我们只要设置背景和障碍物往左移动就会让玩家以为【小鸟】在往右👉飞。",[33,2369,2370],{},"小鸟没有点击【fly】就会倒地，但是游戏没有结束，别着急，后面就会完善代码！",[33,2372,2373],{},"与原版的小鸟有所不同，原本的《Flappy Bird》小鸟在飞的时候会有种“头重脚轻”的感觉：就是在飞的时候仰头，不飞的时候头垂下。在玩家连点的时候有种“扑腾”的感觉。",[15,2375,2376],{},"所以接下来我们就先来优化小鸟的代码脚本，让它可以“扑腾”",[10,2378,2379],{"id":2379},"优化小鸟的旋转",[15,2381,2382],{},"在原版 Flappy Bird 里，小鸟不是一直直挺挺的 — 往上飞的时候头会抬起来，往下掉的时候头会低下去，看起来更生动。",[15,2384,2385],{},"我们来实现这个效果，思路很简单：",[30,2387,2388,2394,2400],{},[33,2389,2390,2393],{},[858,2391,2392],{},"往上飞"," → 头朝上（抬头 -30°）",[33,2395,2396,2399],{},[858,2397,2398],{},"往下掉"," → 头朝下（低头 90°）",[33,2401,2402,2405],{},[858,2403,2404],{},"旋转要平滑过渡","，不能一下子就转过去（那样看起来很僵硬）",[15,2407,2408,2409,2412],{},"在 ",[36,2410,2411],{},"bird.gd"," 里加一个旋转函数：",[144,2414,2416],{"className":146,"code":2415,"language":148,"meta":149,"style":149},"extends CharacterBody2D\n\n@export var gravity_scale := 2.0\n@export var jump_force := -400.0\n@export var max_fall_speed := 1000.0\n@export var rotate_speed := 4.0   # 旋转过渡速度（越大越快）\n\nfunc _physics_process(delta: float) -> void:\n    # 重力\n    if not is_on_floor():\n        velocity += get_gravity() * delta * gravity_scale\n\n    # 限速\n    if velocity.y > max_fall_speed:\n        velocity.y = max_fall_speed\n\n    # 跳跃\n    if Input.is_action_just_pressed(\"fly\"):\n        velocity.y = 0\n        velocity.y = jump_force\n\n    # 旋转\n    handle_rotation(delta)\n    move_and_slide()\n\nfunc handle_rotation(delta: float) -> void:\n    var target_rotation = 0.0\n\n    if velocity.y \u003C 0:\n        # 往上飞 → 抬头\n        target_rotation = deg_to_rad(-30)\n    else:\n        # 往下掉 → 低头\n        target_rotation = deg_to_rad(90)\n\n    # lerp_angle：让旋转平滑过渡，不会一下子\"弹\"过去\n    rotation = lerp_angle(rotation, target_rotation, rotate_speed * delta)\n",[36,2417,2418,2425,2429,2442,2457,2470,2487,2491,2513,2518,2530,2548,2552,2557,2573,2585,2589,2594,2612,2625,2637,2642,2648,2661,2668,2673,2697,2711,2716,2735,2741,2762,2770,2776,2792,2797,2803],{"__ignoreMap":149},[153,2419,2420,2422],{"class":155,"line":156},[153,2421,160],{"class":159},[153,2423,2424],{"class":163}," CharacterBody2D\n",[153,2426,2427],{"class":155,"line":171},[153,2428,175],{"emptyLinePlaceholder":174},[153,2430,2431,2433,2435,2437,2439],{"class":155,"line":178},[153,2432,182],{"class":181},[153,2434,186],{"class":185},[153,2436,190],{"class":189},[153,2438,194],{"class":193},[153,2440,2441],{"class":197}," 2.0\n",[153,2443,2444,2446,2448,2450,2452,2454],{"class":155,"line":204},[153,2445,182],{"class":181},[153,2447,186],{"class":185},[153,2449,211],{"class":189},[153,2451,194],{"class":193},[153,2453,216],{"class":185},[153,2455,2456],{"class":197},"400.0\n",[153,2458,2459,2461,2463,2465,2467],{"class":155,"line":225},[153,2460,182],{"class":181},[153,2462,186],{"class":185},[153,2464,232],{"class":189},[153,2466,194],{"class":193},[153,2468,2469],{"class":197}," 1000.0\n",[153,2471,2472,2474,2476,2479,2481,2484],{"class":155,"line":243},[153,2473,182],{"class":181},[153,2475,186],{"class":185},[153,2477,2478],{"class":189}," rotate_speed",[153,2480,194],{"class":193},[153,2482,2483],{"class":197}," 4.0",[153,2485,2486],{"class":167},"   # 旋转过渡速度（越大越快）\n",[153,2488,2489],{"class":155,"line":248},[153,2490,175],{"emptyLinePlaceholder":174},[153,2492,2493,2495,2497,2499,2501,2503,2505,2507,2509,2511],{"class":155,"line":282},[153,2494,251],{"class":185},[153,2496,254],{"class":181},[153,2498,257],{"class":193},[153,2500,261],{"class":260},[153,2502,264],{"class":193},[153,2504,267],{"class":163},[153,2506,270],{"class":193},[153,2508,273],{"class":185},[153,2510,276],{"class":163},[153,2512,279],{"class":193},[153,2514,2515],{"class":155,"line":288},[153,2516,2517],{"class":167},"    # 重力\n",[153,2519,2520,2522,2524,2526,2528],{"class":155,"line":305},[153,2521,291],{"class":159},[153,2523,294],{"class":185},[153,2525,297],{"class":181},[153,2527,300],{"class":193},[153,2529,279],{"class":260},[153,2531,2532,2534,2536,2538,2540,2542,2544,2546],{"class":155,"line":330},[153,2533,308],{"class":189},[153,2535,311],{"class":185},[153,2537,314],{"class":181},[153,2539,300],{"class":193},[153,2541,319],{"class":185},[153,2543,322],{"class":189},[153,2545,319],{"class":185},[153,2547,327],{"class":189},[153,2549,2550],{"class":155,"line":335},[153,2551,175],{"emptyLinePlaceholder":174},[153,2553,2554],{"class":155,"line":341},[153,2555,2556],{"class":167},"    # 限速\n",[153,2558,2559,2561,2563,2565,2567,2569,2571],{"class":155,"line":362},[153,2560,291],{"class":159},[153,2562,346],{"class":189},[153,2564,349],{"class":193},[153,2566,352],{"class":189},[153,2568,355],{"class":185},[153,2570,232],{"class":189},[153,2572,279],{"class":260},[153,2574,2575,2577,2579,2581,2583],{"class":155,"line":377},[153,2576,308],{"class":189},[153,2578,349],{"class":193},[153,2580,352],{"class":189},[153,2582,371],{"class":193},[153,2584,374],{"class":189},[153,2586,2587],{"class":155,"line":382},[153,2588,175],{"emptyLinePlaceholder":174},[153,2590,2591],{"class":155,"line":388},[153,2592,2593],{"class":167},"    # 跳跃\n",[153,2595,2596,2598,2600,2602,2604,2606,2608,2610],{"class":155,"line":411},[153,2597,291],{"class":159},[153,2599,393],{"class":163},[153,2601,349],{"class":260},[153,2603,398],{"class":181},[153,2605,257],{"class":193},[153,2607,404],{"class":403},[153,2609,270],{"class":193},[153,2611,279],{"class":260},[153,2613,2614,2616,2618,2620,2622],{"class":155,"line":425},[153,2615,308],{"class":189},[153,2617,349],{"class":193},[153,2619,352],{"class":189},[153,2621,371],{"class":193},[153,2623,2624],{"class":197}," 0\n",[153,2626,2627,2629,2631,2633,2635],{"class":155,"line":430},[153,2628,308],{"class":189},[153,2630,349],{"class":193},[153,2632,352],{"class":189},[153,2634,371],{"class":193},[153,2636,422],{"class":189},[153,2638,2640],{"class":155,"line":2639},21,[153,2641,175],{"emptyLinePlaceholder":174},[153,2643,2645],{"class":155,"line":2644},22,[153,2646,2647],{"class":167},"    # 旋转\n",[153,2649,2651,2654,2656,2658],{"class":155,"line":2650},23,[153,2652,2653],{"class":181},"    handle_rotation",[153,2655,257],{"class":193},[153,2657,261],{"class":189},[153,2659,2660],{"class":193},")\n",[153,2662,2664,2666],{"class":155,"line":2663},24,[153,2665,433],{"class":181},[153,2667,436],{"class":193},[153,2669,2671],{"class":155,"line":2670},25,[153,2672,175],{"emptyLinePlaceholder":174},[153,2674,2676,2678,2681,2683,2685,2687,2689,2691,2693,2695],{"class":155,"line":2675},26,[153,2677,251],{"class":185},[153,2679,2680],{"class":181}," handle_rotation",[153,2682,257],{"class":193},[153,2684,261],{"class":260},[153,2686,264],{"class":193},[153,2688,267],{"class":163},[153,2690,270],{"class":193},[153,2692,273],{"class":185},[153,2694,276],{"class":163},[153,2696,279],{"class":193},[153,2698,2700,2703,2706,2708],{"class":155,"line":2699},27,[153,2701,2702],{"class":185},"    var",[153,2704,2705],{"class":189}," target_rotation",[153,2707,371],{"class":193},[153,2709,2710],{"class":197}," 0.0\n",[153,2712,2714],{"class":155,"line":2713},28,[153,2715,175],{"emptyLinePlaceholder":174},[153,2717,2719,2721,2723,2725,2727,2730,2733],{"class":155,"line":2718},29,[153,2720,291],{"class":159},[153,2722,346],{"class":189},[153,2724,349],{"class":193},[153,2726,352],{"class":189},[153,2728,2729],{"class":185}," \u003C",[153,2731,2732],{"class":197}," 0",[153,2734,279],{"class":260},[153,2736,2738],{"class":155,"line":2737},30,[153,2739,2740],{"class":167},"        # 往上飞 → 抬头\n",[153,2742,2744,2747,2749,2752,2754,2757,2760],{"class":155,"line":2743},31,[153,2745,2746],{"class":189},"        target_rotation",[153,2748,371],{"class":193},[153,2750,2751],{"class":181}," deg_to_rad",[153,2753,257],{"class":193},[153,2755,2756],{"class":185},"-",[153,2758,2759],{"class":197},"30",[153,2761,2660],{"class":193},[153,2763,2765,2768],{"class":155,"line":2764},32,[153,2766,2767],{"class":159},"    else",[153,2769,279],{"class":260},[153,2771,2773],{"class":155,"line":2772},33,[153,2774,2775],{"class":167},"        # 往下掉 → 低头\n",[153,2777,2779,2781,2783,2785,2787,2790],{"class":155,"line":2778},34,[153,2780,2746],{"class":189},[153,2782,371],{"class":193},[153,2784,2751],{"class":181},[153,2786,257],{"class":193},[153,2788,2789],{"class":197},"90",[153,2791,2660],{"class":193},[153,2793,2795],{"class":155,"line":2794},35,[153,2796,175],{"emptyLinePlaceholder":174},[153,2798,2800],{"class":155,"line":2799},36,[153,2801,2802],{"class":167},"    # lerp_angle：让旋转平滑过渡，不会一下子\"弹\"过去\n",[153,2804,2806,2809,2811,2814,2816,2819,2822,2824,2826,2828,2830,2832],{"class":155,"line":2805},37,[153,2807,2808],{"class":189},"    rotation",[153,2810,371],{"class":193},[153,2812,2813],{"class":181}," lerp_angle",[153,2815,257],{"class":193},[153,2817,2818],{"class":189},"rotation",[153,2820,2821],{"class":193},",",[153,2823,2705],{"class":189},[153,2825,2821],{"class":193},[153,2827,2478],{"class":189},[153,2829,319],{"class":185},[153,2831,322],{"class":189},[153,2833,2660],{"class":193},[1003,2835,2836],{},[15,2837,2838,2841,2842,2845],{},[36,2839,2840],{},"lerp_angle"," 的作用就像是给旋转加了\"缓动\"，从当前角度慢慢转到目标角度，而不是瞬间跳过去。",[36,2843,2844],{},"rotate_speed"," 越大转得越快，可以自己调到手感合适为止。",[10,2847,2849],{"id":2848},"再次测试","再次测试！",[15,2851,2852],{},"再次运行游戏，看看小鸟是不是有了那种「扑腾扑腾」的灵动感？是不是和原版《Flappy Bird》一个味儿了 ✨",[1083,2854,2855],{},"html pre.shiki code .sTPum, html code.shiki .sTPum{--shiki-default:#1E754F;--shiki-dark:#4D9375}html pre.shiki code .s_NWU, html code.shiki .s_NWU{--shiki-default:#2E8F82;--shiki-dark:#5DA994}html pre.shiki code .s_xSY, html code.shiki .s_xSY{--shiki-default:#59873A;--shiki-dark:#80A665}html pre.shiki code .s5TCs, html code.shiki .s5TCs{--shiki-default:#AB5959;--shiki-dark:#CB7676}html pre.shiki code .s9nN2, html code.shiki .s9nN2{--shiki-default:#B07D48;--shiki-dark:#BD976A}html pre.shiki code .si6no, html code.shiki .si6no{--shiki-default:#999999;--shiki-dark:#666666}html pre.shiki code .sqbOQ, html code.shiki .sqbOQ{--shiki-default:#2F798A;--shiki-dark:#4C9A91}html pre.shiki code .snYqZ, html code.shiki .snYqZ{--shiki-default:#A0ADA0;--shiki-dark:#758575DD}html pre.shiki code .s8w-G, html code.shiki .s8w-G{--shiki-default:#393A34;--shiki-dark:#DBD7CAEE}html pre.shiki code .spP0B, html code.shiki .spP0B{--shiki-default:#B56959;--shiki-dark:#C98A7D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":149,"searchDepth":171,"depth":171,"links":2857},[2858,2859,2860,2861,2862,2863,2864,2865],{"id":2185,"depth":171,"text":2185},{"id":2224,"depth":171,"text":2224},{"id":2234,"depth":171,"text":2235},{"id":2249,"depth":171,"text":2250},{"id":2281,"depth":171,"text":2281},{"id":2359,"depth":171,"text":2359},{"id":2379,"depth":171,"text":2379},{"id":2848,"depth":171,"text":2849},{},"\u002Fdevlog\u002Fxggame-bird\u002F03-game-scene",{"title":2177,"description":1099},"devlog\u002Fxggame-bird\u002F03-game-scene","8FuNcn0nabe5yIBK8x_umOGAktOPpJxM53aPIKn6ApQ",{"id":2872,"title":2873,"body":2874,"cover":1097,"date":1098,"description":1099,"extension":1100,"game":1101,"github":1102,"icon":1097,"meta":4411,"navigation":174,"path":4412,"seo":4413,"stem":4414,"toc":174,"__hash__":4415},"devlog\u002Fdevlog\u002Fxggame-bird\u002F04-obstacles.md","障碍物",{"type":7,"value":2875,"toc":4394},[2876,2879,2899,2917,2921,2924,2946,2952,2961,2964,2971,2990,2997,3002,3069,3075,3087,3093,3096,3102,3109,3115,3119,3125,3138,3211,3239,3363,3367,3374,3380,3384,3387,3407,3413,3645,3651,3655,3700,3708,3713,3719,3724,3998,4000,4011,4013,4037,4043,4046,4052,4063,4069,4079,4085,4088,4289,4292,4345,4360,4362,4365,4379,4385,4391],[15,2877,2878],{},"Flappy Bird 最经典的玩法 — 上下成对的水管，小鸟要从中间缝隙穿过去。这一章我们做：",[1060,2880,2881,2887,2893],{},[33,2882,2883,2886],{},[858,2884,2885],{},"障碍物本体"," — 上下两根水管 + 一个得分区",[33,2888,2889,2892],{},[858,2890,2891],{},"死区 Killzone"," — 撞上就死",[33,2894,2895,2898],{},[858,2896,2897],{},"障碍物生成器"," — 定时刷新、自动移动、屏幕外销毁",[1003,2900,2901],{},[15,2902,2903,2904,2907,2908,2910,2911,2916],{},"这里用 ",[36,2905,2906],{},"Area2D","（死区）而不是 ",[36,2909,2297],{},"，因为 ",[858,2912,2913,2915],{},[36,2914,2906],{}," 不会真的\"卡住\"小鸟，只会触发信号","。这样我们就能在信号里写\"游戏结束\"的逻辑，而不是把小鸟物理地卡在水管里动弹不得。",[10,2918,2920],{"id":2919},"创建死区-killzone","创建死区 Killzone",[15,2922,2923],{},"先创建死区，使用到的节点：",[30,2925,2926],{},[33,2927,2928,2930,2931,2934,2935],{},[36,2929,2906],{}," — 死区根节点（重命名：",[36,2932,2933],{},"Killzone","）\n",[30,2936,2937],{},[33,2938,2939,2941,2942,2945],{},[36,2940,52],{}," — 碰撞形状（",[858,2943,2944],{},"先留空","，下面会解释为什么）",[15,2947,2948],{},[19,2949],{"alt":2950,"src":2951},"04-obstacles-死区","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F04-obstacles-%E6%AD%BB%E5%8C%BA.png",[15,2953,2954,2956,2957,2960],{},[36,2955,52],{}," 暂时",[858,2958,2959],{},"不要选形状","，因为后续这个死区既可以放到\"障碍物\"上，也可以放到\"地板\"上（小鸟没按飞掉地上也会 Game Over）。形状到时候根据位置再单独设置。",[10,2962,2963],{"id":2963},"障碍物的组成",[15,2965,2966,2967,2970],{},"每个障碍物由 ",[858,2968,2969],{},"3 个区域"," 组成：",[30,2972,2973,2979,2984],{},[33,2974,2975,2978],{},[858,2976,2977],{},"上水管","（Killzone — 死区）→ 撞到就死",[33,2980,2981,2978],{},[858,2982,2983],{},"下水管",[33,2985,2986,2989],{},[858,2987,2988],{},"中间的得分区","（Goal）→ 穿过加 1 分",[15,2991,2992,2993,2996],{},"这里我们同样先用 Godot 的默认素材（也就是默认的 ",[36,2994,2995],{},"icon.svg"," 来作为图片精灵图）",[1003,2998,2999],{},[15,3000,3001],{},"等后续我们再一起更新美术资源素材！",[30,3003,3004],{},[33,3005,3006,3008,3009,2934,3012],{},[36,3007,2194],{},"（重命名：",[36,3010,3011],{},"PillarPair",[30,3013,3014,3034,3055],{},[33,3015,3016,3018,3019],{},[36,3017,2933],{},"（我们刚刚创建的死区）\n",[30,3020,3021,3029],{},[33,3022,3023,3025,3026,3028],{},[36,3024,46],{}," — 放入 Godot 默认素材（",[36,3027,2995],{},"）",[33,3030,3031,3033],{},[36,3032,52],{}," — 碰撞形状（矩形，调整到一根水管的大小）",[33,3035,3036,3039,3040,3042,3043],{},[36,3037,3038],{},"Killzone2","（复制 ",[36,3041,2933],{}," 一份，改名即可）\n",[30,3044,3045,3051],{},[33,3046,3047,3025,3049,3028],{},[36,3048,46],{},[36,3050,2995],{},[33,3052,3053,3033],{},[36,3054,52],{},[33,3056,3057,3008,3059,2934,3062],{},[36,3058,2906],{},[36,3060,3061],{},"Goal",[30,3063,3064],{},[33,3065,3066,3068],{},[36,3067,52],{}," — 得分区域",[15,3070,3071],{},[19,3072],{"alt":3073,"src":3074},"04-obstacles-阻碍物","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F04-obstacles-%E9%98%BB%E7%A2%8D%E7%89%A9.png",[15,3076,3077,3078,3080,3081,3083,3084,3086],{},"这里可以调整碰撞体的颜色，用红色来区分。\n然后在设置好一个 ",[36,3079,2933],{}," 里的 ",[36,3082,46],{}," 和 ",[36,3085,52],{}," 后可以复制",[15,3088,3089],{},[19,3090],{"alt":3091,"src":3092},"04-obstacles-阻碍物2","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F04-obstacles-%E9%98%BB%E7%A2%8D%E7%89%A92.png",[15,3094,3095],{},"然后调整两个的位置，在中间空出一道间隙。",[15,3097,3098],{},[19,3099],{"alt":3100,"src":3101},"04-obstacles-得分区域","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F04-obstacles-%E5%BE%97%E5%88%86%E5%8C%BA%E5%9F%9F.png",[15,3103,3104,3105,3108],{},"然后把得分区域",[858,3106,3107],{},"放在中间偏后","的位置，这样只有完全通过的时候才算得分！",[15,3110,3111,3112],{},"障碍物设置好后，就可以保存该场景为：",[36,3113,3114],{},"scenes\u002Fpillar_pair.tscn",[71,3116,3118],{"id":3117},"给-killzone-挂脚本","给 Killzone 挂脚本",[15,3120,3121],{},[19,3122],{"alt":3123,"src":3124},"04-obstacles-死区脚本","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F04-obstacles-%E6%AD%BB%E5%8C%BA%E8%84%9A%E6%9C%AC.png",[15,3126,94,3127,3129,3130,3133,3134,3137],{},[36,3128,2906],{}," 节点（Killzone） → 右侧【节点】面板找到 ",[36,3131,3132],{},"body_entered"," 信号 → 双击 → 连接到自己。Godot 会自动生成 ",[36,3135,3136],{},"_on_body_entered"," 函数：",[144,3139,3141],{"className":146,"code":3140,"language":148,"meta":149,"style":149},"extends Area2D\n\nfunc _on_body_entered(body: Node2D) -> void:\n    if body.name == \"Bird\":\n        print(\"撞击死亡！\")\n",[36,3142,3143,3150,3154,3179,3199],{"__ignoreMap":149},[153,3144,3145,3147],{"class":155,"line":156},[153,3146,160],{"class":159},[153,3148,3149],{"class":163}," Area2D\n",[153,3151,3152],{"class":155,"line":171},[153,3153,175],{"emptyLinePlaceholder":174},[153,3155,3156,3158,3161,3163,3166,3168,3171,3173,3175,3177],{"class":155,"line":178},[153,3157,251],{"class":185},[153,3159,3160],{"class":181}," _on_body_entered",[153,3162,257],{"class":193},[153,3164,3165],{"class":260},"body",[153,3167,264],{"class":193},[153,3169,3170],{"class":163}," Node2D",[153,3172,270],{"class":193},[153,3174,273],{"class":185},[153,3176,276],{"class":163},[153,3178,279],{"class":193},[153,3180,3181,3183,3186,3188,3191,3194,3197],{"class":155,"line":204},[153,3182,291],{"class":159},[153,3184,3185],{"class":189}," body",[153,3187,349],{"class":193},[153,3189,3190],{"class":189},"name",[153,3192,3193],{"class":185}," ==",[153,3195,3196],{"class":403}," \"Bird\"",[153,3198,279],{"class":260},[153,3200,3201,3204,3206,3209],{"class":155,"line":225},[153,3202,3203],{"class":181},"        print",[153,3205,257],{"class":193},[153,3207,3208],{"class":403},"\"撞击死亡！\"",[153,3210,2660],{"class":193},[30,3212,3213,3222,3228],{},[33,3214,3215,3217,3218,3221],{},[36,3216,3132],{},"：",[858,3219,3220],{},"任何物理体进入这个区域","时触发的信号",[33,3223,3224,3227],{},[36,3225,3226],{},"if body.name == \"Bird\":"," — 判断进来的是不是小鸟（防止其他东西误触发）",[33,3229,3230,3231,3234,3235,3238],{},"暂时用 ",[36,3232,3233],{},"print"," 打印日志，下一章做完 ",[36,3236,3237],{},"GameManager"," 后再换成真正的\"游戏结束\"逻辑",[1003,3240,3241,3248,3350],{},[15,3242,3243,3244,3247],{},"💡 ",[858,3245,3246],{},"后续优化预览","（下一章 GameManager + 第 8 章 SoundManager 后会改成这样，现在不要这么写）：",[144,3249,3251],{"className":146,"code":3250,"language":148,"meta":149,"style":149},"extends Area2D\n\nfunc _on_body_entered(body: Node2D) -> void:\n    if body.name == \"Bird\" and GameManager.current_state == GameManager.GameState.PLAYING:\n        SoundManager.play_die()\n        GameManager.game_over()\n",[36,3252,3253,3259,3263,3285,3326,3338],{"__ignoreMap":149},[153,3254,3255,3257],{"class":155,"line":156},[153,3256,160],{"class":159},[153,3258,3149],{"class":163},[153,3260,3261],{"class":155,"line":171},[153,3262,175],{"emptyLinePlaceholder":174},[153,3264,3265,3267,3269,3271,3273,3275,3277,3279,3281,3283],{"class":155,"line":178},[153,3266,251],{"class":185},[153,3268,3160],{"class":181},[153,3270,257],{"class":193},[153,3272,3165],{"class":260},[153,3274,264],{"class":193},[153,3276,3170],{"class":163},[153,3278,270],{"class":193},[153,3280,273],{"class":185},[153,3282,276],{"class":163},[153,3284,279],{"class":193},[153,3286,3287,3289,3291,3293,3295,3297,3299,3302,3305,3307,3310,3312,3314,3316,3319,3321,3324],{"class":155,"line":204},[153,3288,291],{"class":159},[153,3290,3185],{"class":189},[153,3292,349],{"class":193},[153,3294,3190],{"class":189},[153,3296,3193],{"class":185},[153,3298,3196],{"class":403},[153,3300,3301],{"class":185}," and",[153,3303,3304],{"class":163}," GameManager",[153,3306,349],{"class":193},[153,3308,3309],{"class":189},"current_state",[153,3311,3193],{"class":185},[153,3313,3304],{"class":163},[153,3315,349],{"class":193},[153,3317,3318],{"class":189},"GameState",[153,3320,349],{"class":193},[153,3322,3323],{"class":159},"PLAYING",[153,3325,279],{"class":260},[153,3327,3328,3331,3333,3336],{"class":155,"line":225},[153,3329,3330],{"class":163},"        SoundManager",[153,3332,349],{"class":260},[153,3334,3335],{"class":181},"play_die",[153,3337,436],{"class":193},[153,3339,3340,3343,3345,3348],{"class":155,"line":243},[153,3341,3342],{"class":163},"        GameManager",[153,3344,349],{"class":260},[153,3346,3347],{"class":181},"game_over",[153,3349,436],{"class":193},[30,3351,3352,3357],{},[33,3353,3354,3356],{},[36,3355,3237],{}," 是整个游戏的整体管理",[33,3358,3359,3362],{},[36,3360,3361],{},"SoundManager"," 是整个游戏的音效管理",[71,3364,3366],{"id":3365},"地板也可以变成-killzone","地板也可以变成 Killzone",[15,3368,3369,3370,3373],{},"之前第 3 章做的底部边界，",[858,3371,3372],{},"也可以变成死区"," — 小鸟掉到地上就算游戏结束。",[15,3375,3376],{},[19,3377],{"alt":3378,"src":3379},"04-obstacles-地板死区","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F04-obstacles-%E5%9C%B0%E6%9D%BF%E6%AD%BB%E5%8C%BA.png",[10,3381,3383],{"id":3382},"障碍物移动-销毁-得分","障碍物移动 + 销毁 + 得分",[15,3385,3386],{},"障碍物根节点要做三件事：",[1060,3388,3389,3395,3401],{},[33,3390,3391,3394],{},[858,3392,3393],{},"往左移动"," — 营造小鸟在向右飞的错觉",[33,3396,3397,3400],{},[858,3398,3399],{},"离开屏幕后销毁"," — 不然会一直累积，性能爆炸",[33,3402,3403,3406],{},[858,3404,3405],{},"检测小鸟穿过"," — 加分",[15,3408,3409,3410,3412],{},"这些都写在障碍物根节点（",[36,3411,2194],{},"）的脚本里：",[144,3414,3416],{"className":146,"code":3415,"language":148,"meta":149,"style":149},"extends Node2D\n\n@export var speed := 200.0\n@onready var goal: Area2D = $Goal\n\nfunc _physics_process(delta: float) -> void:\n    # 统一向左移动\n    position.x -= speed * delta\n    # 离开屏幕后自动销毁\n    if position.x \u003C -500:\n        queue_free()\n\n# 当物体进入得分区域【得分】\nfunc _on_goal_body_entered(body: Node2D) -> void:\n    # 检查进入的是不是小鸟（防止其他东西误触发）\n    if body.name == \"Bird\":\n        print(\"得分！\")\n        # 重要：得分后立即禁用这个检测区域，防止重复得分\n        # 使用 set_deferred 是因为在碰撞回调中不能直接修改物理属性\n        goal.set_deferred(\"monitoring\", false)\n",[36,3417,3418,3425,3429,3443,3467,3471,3493,3498,3517,3522,3542,3549,3553,3558,3581,3586,3602,3613,3618,3623],{"__ignoreMap":149},[153,3419,3420,3422],{"class":155,"line":156},[153,3421,160],{"class":159},[153,3423,3424],{"class":163}," Node2D\n",[153,3426,3427],{"class":155,"line":171},[153,3428,175],{"emptyLinePlaceholder":174},[153,3430,3431,3433,3435,3438,3440],{"class":155,"line":178},[153,3432,182],{"class":181},[153,3434,186],{"class":185},[153,3436,3437],{"class":189}," speed",[153,3439,194],{"class":193},[153,3441,3442],{"class":197}," 200.0\n",[153,3444,3445,3448,3450,3453,3455,3458,3460,3463],{"class":155,"line":204},[153,3446,3447],{"class":181},"@onready",[153,3449,186],{"class":185},[153,3451,3452],{"class":189}," goal",[153,3454,264],{"class":193},[153,3456,3457],{"class":163}," Area2D",[153,3459,371],{"class":193},[153,3461,3462],{"class":159}," $",[153,3464,3466],{"class":3465},"sfsYZ","Goal\n",[153,3468,3469],{"class":155,"line":225},[153,3470,175],{"emptyLinePlaceholder":174},[153,3472,3473,3475,3477,3479,3481,3483,3485,3487,3489,3491],{"class":155,"line":243},[153,3474,251],{"class":185},[153,3476,254],{"class":181},[153,3478,257],{"class":193},[153,3480,261],{"class":260},[153,3482,264],{"class":193},[153,3484,267],{"class":163},[153,3486,270],{"class":193},[153,3488,273],{"class":185},[153,3490,276],{"class":163},[153,3492,279],{"class":193},[153,3494,3495],{"class":155,"line":248},[153,3496,3497],{"class":167},"    # 统一向左移动\n",[153,3499,3500,3503,3505,3507,3510,3512,3514],{"class":155,"line":282},[153,3501,3502],{"class":189},"    position",[153,3504,349],{"class":193},[153,3506,823],{"class":189},[153,3508,3509],{"class":185}," -=",[153,3511,3437],{"class":189},[153,3513,319],{"class":185},[153,3515,3516],{"class":189}," delta\n",[153,3518,3519],{"class":155,"line":288},[153,3520,3521],{"class":167},"    # 离开屏幕后自动销毁\n",[153,3523,3524,3526,3529,3531,3533,3535,3537,3540],{"class":155,"line":305},[153,3525,291],{"class":159},[153,3527,3528],{"class":189}," position",[153,3530,349],{"class":193},[153,3532,823],{"class":189},[153,3534,2729],{"class":185},[153,3536,216],{"class":185},[153,3538,3539],{"class":197},"500",[153,3541,279],{"class":260},[153,3543,3544,3547],{"class":155,"line":330},[153,3545,3546],{"class":181},"        queue_free",[153,3548,436],{"class":193},[153,3550,3551],{"class":155,"line":335},[153,3552,175],{"emptyLinePlaceholder":174},[153,3554,3555],{"class":155,"line":341},[153,3556,3557],{"class":167},"# 当物体进入得分区域【得分】\n",[153,3559,3560,3562,3565,3567,3569,3571,3573,3575,3577,3579],{"class":155,"line":362},[153,3561,251],{"class":185},[153,3563,3564],{"class":181}," _on_goal_body_entered",[153,3566,257],{"class":193},[153,3568,3165],{"class":260},[153,3570,264],{"class":193},[153,3572,3170],{"class":163},[153,3574,270],{"class":193},[153,3576,273],{"class":185},[153,3578,276],{"class":163},[153,3580,279],{"class":193},[153,3582,3583],{"class":155,"line":377},[153,3584,3585],{"class":167},"    # 检查进入的是不是小鸟（防止其他东西误触发）\n",[153,3587,3588,3590,3592,3594,3596,3598,3600],{"class":155,"line":382},[153,3589,291],{"class":159},[153,3591,3185],{"class":189},[153,3593,349],{"class":193},[153,3595,3190],{"class":189},[153,3597,3193],{"class":185},[153,3599,3196],{"class":403},[153,3601,279],{"class":260},[153,3603,3604,3606,3608,3611],{"class":155,"line":388},[153,3605,3203],{"class":181},[153,3607,257],{"class":193},[153,3609,3610],{"class":403},"\"得分！\"",[153,3612,2660],{"class":193},[153,3614,3615],{"class":155,"line":411},[153,3616,3617],{"class":167},"        # 重要：得分后立即禁用这个检测区域，防止重复得分\n",[153,3619,3620],{"class":155,"line":425},[153,3621,3622],{"class":167},"        # 使用 set_deferred 是因为在碰撞回调中不能直接修改物理属性\n",[153,3624,3625,3628,3630,3633,3635,3638,3640,3643],{"class":155,"line":430},[153,3626,3627],{"class":189},"        goal",[153,3629,349],{"class":260},[153,3631,3632],{"class":181},"set_deferred",[153,3634,257],{"class":193},[153,3636,3637],{"class":403},"\"monitoring\"",[153,3639,2821],{"class":193},[153,3641,3642],{"class":159}," false",[153,3644,2660],{"class":193},[15,3646,3647],{},[19,3648],{"alt":3649,"src":3650},"04-obstacles-障碍物脚本","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F04-obstacles-%E9%9A%9C%E7%A2%8D%E7%89%A9%E8%84%9A%E6%9C%AC.png",[71,3652,3654],{"id":3653},"这段代码做了啥","这段代码做了啥？",[30,3656,3657,3663,3672,3678,3688],{},[33,3658,3659,3662],{},[36,3660,3661],{},"@export var speed := 200.0"," — 把移动速度暴露到面板，方便实时调",[33,3664,3665,3668,3669,3671],{},[36,3666,3667],{},"@onready var goal: Area2D = $Goal"," — 拿到子节点 ",[36,3670,3061],{}," 的引用（用于得分后禁用它）",[33,3673,3674,3677],{},[36,3675,3676],{},"position.x -= speed * delta"," — 每帧整个障碍物往左移一点",[33,3679,3680,3683,3684,3687],{},[36,3681,3682],{},"if position.x \u003C -500: queue_free()"," — 飞出屏幕外就",[858,3685,3686],{},"销毁自己","，回收内存",[33,3689,3690,3693,3694,3696,3697,3028],{},[36,3691,3692],{},"_on_goal_body_entered"," — 小鸟进入 ",[36,3695,3061],{}," 时触发（同样要在编辑器里",[858,3698,3699],{},"连接信号",[71,3701,3703,3704,3707],{"id":3702},"为什么要-set_deferredmonitoring-false","为什么要 ",[36,3705,3706],{},"set_deferred(\"monitoring\", false)","？",[15,3709,3710,1147],{},[858,3711,3712],{},"为了防止重复得分",[15,3714,3715,3716,3718],{},"如果不禁用，小鸟身上的碰撞体可能在 Goal 区域里横跨多帧，每帧都触发一次 ",[36,3717,3132],{},"，你就从 1 分跳到 5 分了 😅",[15,3720,3721,3723],{},[36,3722,3632],{}," 是因为在物理回调里不能直接修改物理属性，要\"延迟到下一帧\"再改。",[1003,3725,3726,3731,3977,3980],{},[15,3727,3243,3728,3730],{},[858,3729,3246],{},"（下一章 GameManager + 第 8 章 SoundManager 后会改成这样）：",[144,3732,3734],{"className":146,"code":3733,"language":148,"meta":149,"style":149},"extends Node2D\n\n@export var speed := 200.0\n@onready var goal: Area2D = $Goal\n\nfunc _physics_process(delta: float) -> void:\n    if GameManager.current_state == GameManager.GameState.PLAYING:\n        position.x -= speed * delta\n        if position.x \u003C -500:\n            queue_free()\n\nfunc _on_goal_body_entered(body: Node2D) -> void:\n    if body.name == \"Bird\":\n        SoundManager.play_score()\n        if GameManager.has_method(\"add_score\"):\n            GameManager.add_score(1)\n        goal.set_deferred(\"monitoring\", false)\n",[36,3735,3736,3742,3746,3758,3776,3780,3802,3826,3843,3862,3869,3873,3895,3911,3922,3942,3959],{"__ignoreMap":149},[153,3737,3738,3740],{"class":155,"line":156},[153,3739,160],{"class":159},[153,3741,3424],{"class":163},[153,3743,3744],{"class":155,"line":171},[153,3745,175],{"emptyLinePlaceholder":174},[153,3747,3748,3750,3752,3754,3756],{"class":155,"line":178},[153,3749,182],{"class":181},[153,3751,186],{"class":185},[153,3753,3437],{"class":189},[153,3755,194],{"class":193},[153,3757,3442],{"class":197},[153,3759,3760,3762,3764,3766,3768,3770,3772,3774],{"class":155,"line":204},[153,3761,3447],{"class":181},[153,3763,186],{"class":185},[153,3765,3452],{"class":189},[153,3767,264],{"class":193},[153,3769,3457],{"class":163},[153,3771,371],{"class":193},[153,3773,3462],{"class":159},[153,3775,3466],{"class":3465},[153,3777,3778],{"class":155,"line":225},[153,3779,175],{"emptyLinePlaceholder":174},[153,3781,3782,3784,3786,3788,3790,3792,3794,3796,3798,3800],{"class":155,"line":243},[153,3783,251],{"class":185},[153,3785,254],{"class":181},[153,3787,257],{"class":193},[153,3789,261],{"class":260},[153,3791,264],{"class":193},[153,3793,267],{"class":163},[153,3795,270],{"class":193},[153,3797,273],{"class":185},[153,3799,276],{"class":163},[153,3801,279],{"class":193},[153,3803,3804,3806,3808,3810,3812,3814,3816,3818,3820,3822,3824],{"class":155,"line":248},[153,3805,291],{"class":159},[153,3807,3304],{"class":163},[153,3809,349],{"class":193},[153,3811,3309],{"class":189},[153,3813,3193],{"class":185},[153,3815,3304],{"class":163},[153,3817,349],{"class":193},[153,3819,3318],{"class":189},[153,3821,349],{"class":193},[153,3823,3323],{"class":159},[153,3825,279],{"class":260},[153,3827,3828,3831,3833,3835,3837,3839,3841],{"class":155,"line":282},[153,3829,3830],{"class":189},"        position",[153,3832,349],{"class":193},[153,3834,823],{"class":189},[153,3836,3509],{"class":185},[153,3838,3437],{"class":189},[153,3840,319],{"class":185},[153,3842,3516],{"class":189},[153,3844,3845,3848,3850,3852,3854,3856,3858,3860],{"class":155,"line":288},[153,3846,3847],{"class":159},"        if",[153,3849,3528],{"class":189},[153,3851,349],{"class":193},[153,3853,823],{"class":189},[153,3855,2729],{"class":185},[153,3857,216],{"class":185},[153,3859,3539],{"class":197},[153,3861,279],{"class":260},[153,3863,3864,3867],{"class":155,"line":305},[153,3865,3866],{"class":181},"            queue_free",[153,3868,436],{"class":193},[153,3870,3871],{"class":155,"line":330},[153,3872,175],{"emptyLinePlaceholder":174},[153,3874,3875,3877,3879,3881,3883,3885,3887,3889,3891,3893],{"class":155,"line":335},[153,3876,251],{"class":185},[153,3878,3564],{"class":181},[153,3880,257],{"class":193},[153,3882,3165],{"class":260},[153,3884,264],{"class":193},[153,3886,3170],{"class":163},[153,3888,270],{"class":193},[153,3890,273],{"class":185},[153,3892,276],{"class":163},[153,3894,279],{"class":193},[153,3896,3897,3899,3901,3903,3905,3907,3909],{"class":155,"line":341},[153,3898,291],{"class":159},[153,3900,3185],{"class":189},[153,3902,349],{"class":193},[153,3904,3190],{"class":189},[153,3906,3193],{"class":185},[153,3908,3196],{"class":403},[153,3910,279],{"class":260},[153,3912,3913,3915,3917,3920],{"class":155,"line":362},[153,3914,3330],{"class":163},[153,3916,349],{"class":260},[153,3918,3919],{"class":181},"play_score",[153,3921,436],{"class":193},[153,3923,3924,3926,3928,3930,3933,3935,3938,3940],{"class":155,"line":377},[153,3925,3847],{"class":159},[153,3927,3304],{"class":163},[153,3929,349],{"class":260},[153,3931,3932],{"class":181},"has_method",[153,3934,257],{"class":193},[153,3936,3937],{"class":403},"\"add_score\"",[153,3939,270],{"class":193},[153,3941,279],{"class":260},[153,3943,3944,3947,3949,3952,3954,3957],{"class":155,"line":382},[153,3945,3946],{"class":163},"            GameManager",[153,3948,349],{"class":260},[153,3950,3951],{"class":181},"add_score",[153,3953,257],{"class":193},[153,3955,3956],{"class":197},"1",[153,3958,2660],{"class":193},[153,3960,3961,3963,3965,3967,3969,3971,3973,3975],{"class":155,"line":388},[153,3962,3627],{"class":189},[153,3964,349],{"class":260},[153,3966,3632],{"class":181},[153,3968,257],{"class":193},[153,3970,3637],{"class":403},[153,3972,2821],{"class":193},[153,3974,3642],{"class":159},[153,3976,2660],{"class":193},[15,3978,3979],{},"升级点：",[30,3981,3982,3989],{},[33,3983,3984,3985,3988],{},"加了 ",[36,3986,3987],{},"if GameManager.current_state == PLAYING"," — 游戏暂停\u002F结束时不再移动",[33,3990,3991,3992,3083,3995],{},"得分时调用 ",[36,3993,3994],{},"GameManager.add_score(1)",[36,3996,3997],{},"SoundManager.play_score()",[10,3999,2897],{"id":2897},[15,4001,4002,4003,4006,4007,4010],{},"光有一根水管不够，需要",[858,4004,4005],{},"源源不断的水管","。用 ",[36,4008,4009],{},"Timer"," 节点定时生成：",[15,4012,2206],{},[30,4014,4015],{},[33,4016,4017,4019,4020,2934,4023],{},[36,4018,2194],{}," — 生成器根节点（重命名：",[36,4021,4022],{},"PillarSpawner",[30,4024,4025],{},[33,4026,4027,4029,4030,4033,4034,3028],{},[36,4028,4009],{}," — 定时器（",[36,4031,4032],{},"Wait Time = 2.5s","，勾选 ",[36,4035,4036],{},"Autostart",[15,4038,4039],{},[19,4040],{"alt":4041,"src":4042},"04-obstacles-生成器节点","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F04-obstacles-%E7%94%9F%E6%88%90%E5%99%A8%E8%8A%82%E7%82%B9.png",[15,4044,4045],{},"设置 Timer 节点的属性：",[15,4047,4048],{},[19,4049],{"alt":4050,"src":4051},"04-obstacles-生成器节点2","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F04-obstacles-%E7%94%9F%E6%88%90%E5%99%A8%E8%8A%82%E7%82%B92.png",[15,4053,4054,4055,4058,4059,4062],{},"把 ",[36,4056,4057],{},"pillar_pair.tscn"," 拖到 ",[36,4060,4061],{},"Pillar Scene"," 槽里：",[15,4064,4065],{},[19,4066],{"alt":4067,"src":4068},"04-obstacles-生成器节点3","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F04-obstacles-%E7%94%9F%E6%88%90%E5%99%A8%E8%8A%82%E7%82%B93.png",[15,4070,4071,4072,4074,4075,4078],{},"最后，把 ",[36,4073,4022],{}," 整个节点",[858,4076,4077],{},"拖到游戏窗口右边外面","（这样水管才会从屏幕右侧\"飞\"进来）：",[15,4080,4081],{},[19,4082],{"alt":4083,"src":4084},"04-obstacles-生成器节点4","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F04-obstacles-%E7%94%9F%E6%88%90%E5%99%A8%E8%8A%82%E7%82%B94.png",[71,4086,4087],{"id":4087},"生成器脚本",[144,4089,4091],{"className":146,"code":4090,"language":148,"meta":149,"style":149},"extends Node2D\n\n@export var pillar_scene: PackedScene\n@export var y_range := 250.0\n\nfunc _on_timer_timeout() -> void:\n    spawn_pillar()\n\nfunc spawn_pillar() -> void:\n    var new_pillar = pillar_scene.instantiate()\n\n    var spawn_pos = global_position\n    spawn_pos.y += randf_range(-y_range, y_range)\n\n    new_pillar.global_position = spawn_pos\n\n    # 重点：加到主场景或者专门的容器里，防止柱子跟着生成器动\n    get_tree().current_scene.add_child(new_pillar)\n",[36,4092,4093,4099,4103,4117,4131,4135,4150,4157,4161,4176,4194,4198,4210,4237,4241,4256,4260,4265],{"__ignoreMap":149},[153,4094,4095,4097],{"class":155,"line":156},[153,4096,160],{"class":159},[153,4098,3424],{"class":163},[153,4100,4101],{"class":155,"line":171},[153,4102,175],{"emptyLinePlaceholder":174},[153,4104,4105,4107,4109,4112,4114],{"class":155,"line":178},[153,4106,182],{"class":181},[153,4108,186],{"class":185},[153,4110,4111],{"class":189}," pillar_scene",[153,4113,264],{"class":193},[153,4115,4116],{"class":163}," PackedScene\n",[153,4118,4119,4121,4123,4126,4128],{"class":155,"line":204},[153,4120,182],{"class":181},[153,4122,186],{"class":185},[153,4124,4125],{"class":189}," y_range",[153,4127,194],{"class":193},[153,4129,4130],{"class":197}," 250.0\n",[153,4132,4133],{"class":155,"line":225},[153,4134,175],{"emptyLinePlaceholder":174},[153,4136,4137,4139,4142,4144,4146,4148],{"class":155,"line":243},[153,4138,251],{"class":185},[153,4140,4141],{"class":181}," _on_timer_timeout",[153,4143,300],{"class":193},[153,4145,273],{"class":185},[153,4147,276],{"class":163},[153,4149,279],{"class":193},[153,4151,4152,4155],{"class":155,"line":248},[153,4153,4154],{"class":181},"    spawn_pillar",[153,4156,436],{"class":193},[153,4158,4159],{"class":155,"line":282},[153,4160,175],{"emptyLinePlaceholder":174},[153,4162,4163,4165,4168,4170,4172,4174],{"class":155,"line":288},[153,4164,251],{"class":185},[153,4166,4167],{"class":181}," spawn_pillar",[153,4169,300],{"class":193},[153,4171,273],{"class":185},[153,4173,276],{"class":163},[153,4175,279],{"class":193},[153,4177,4178,4180,4183,4185,4187,4189,4192],{"class":155,"line":305},[153,4179,2702],{"class":185},[153,4181,4182],{"class":189}," new_pillar",[153,4184,371],{"class":193},[153,4186,4111],{"class":189},[153,4188,349],{"class":260},[153,4190,4191],{"class":181},"instantiate",[153,4193,436],{"class":193},[153,4195,4196],{"class":155,"line":330},[153,4197,175],{"emptyLinePlaceholder":174},[153,4199,4200,4202,4205,4207],{"class":155,"line":335},[153,4201,2702],{"class":185},[153,4203,4204],{"class":189}," spawn_pos",[153,4206,371],{"class":193},[153,4208,4209],{"class":189}," global_position\n",[153,4211,4212,4215,4217,4219,4221,4224,4226,4228,4231,4233,4235],{"class":155,"line":341},[153,4213,4214],{"class":189},"    spawn_pos",[153,4216,349],{"class":193},[153,4218,352],{"class":189},[153,4220,311],{"class":185},[153,4222,4223],{"class":181}," randf_range",[153,4225,257],{"class":193},[153,4227,2756],{"class":185},[153,4229,4230],{"class":189},"y_range",[153,4232,2821],{"class":193},[153,4234,4125],{"class":189},[153,4236,2660],{"class":193},[153,4238,4239],{"class":155,"line":362},[153,4240,175],{"emptyLinePlaceholder":174},[153,4242,4243,4246,4248,4251,4253],{"class":155,"line":377},[153,4244,4245],{"class":189},"    new_pillar",[153,4247,349],{"class":193},[153,4249,4250],{"class":189},"global_position",[153,4252,371],{"class":193},[153,4254,4255],{"class":189}," spawn_pos\n",[153,4257,4258],{"class":155,"line":382},[153,4259,175],{"emptyLinePlaceholder":174},[153,4261,4262],{"class":155,"line":388},[153,4263,4264],{"class":167},"    # 重点：加到主场景或者专门的容器里，防止柱子跟着生成器动\n",[153,4266,4267,4270,4272,4274,4277,4279,4282,4284,4287],{"class":155,"line":411},[153,4268,4269],{"class":181},"    get_tree",[153,4271,300],{"class":193},[153,4273,349],{"class":260},[153,4275,4276],{"class":189},"current_scene",[153,4278,349],{"class":260},[153,4280,4281],{"class":181},"add_child",[153,4283,257],{"class":193},[153,4285,4286],{"class":189},"new_pillar",[153,4288,2660],{"class":193},[71,4290,4291],{"id":4291},"关键点",[30,4293,4294,4307,4313,4321,4327,4333],{},[33,4295,4296,4299,4300],{},[36,4297,4298],{},"@export var pillar_scene: PackedScene"," — 把\"水管场景\"暴露到面板，",[858,4301,4302,4303,4306],{},"你需要在面板上把 ",[36,4304,4305],{},"pillar.tscn"," 拖进这个槽里",[33,4308,4309,4312],{},[36,4310,4311],{},"@export var y_range := 250.0"," — 水管在垂直方向上的随机偏移范围",[33,4314,4315,4318,4319,3028],{},[36,4316,4317],{},"_on_timer_timeout"," — 每次 Timer 触发就生成新的（同样要",[858,4320,3699],{},[33,4322,4323,4326],{},[36,4324,4325],{},"pillar_scene.instantiate()"," — 把场景\"实例化\"成一个真正的节点",[33,4328,4329,4332],{},[36,4330,4331],{},"randf_range(-y_range, y_range)"," — 随机一个垂直偏移",[33,4334,4335,4340,4341,4344],{},[858,4336,4337],{},[36,4338,4339],{},"get_tree().current_scene.add_child(new_pillar)"," ⚠️ 重点！把水管加到",[858,4342,4343],{},"主场景","而不是生成器自己。否则如果生成器以后会移动，水管会跟着一起动，整个画面就乱套了。",[1003,4346,4347],{},[15,4348,3243,4349,4351,4352,4355,4356,4359],{},[858,4350,3246],{},"（下一章 GameManager 之后）：在 ",[36,4353,4354],{},"spawn_pillar"," 最外层包一个 ",[36,4357,4358],{},"if GameManager.current_state == GameManager.GameState.PLAYING:","，这样游戏结束后就不会继续生成水管了。",[10,4361,2359],{"id":2359},[15,4363,4364],{},"应该能看到：",[30,4366,4367,4370,4373,4376],{},[33,4368,4369],{},"✅ 水管从右往左移动",[33,4371,4372],{},"✅ 每 2.5 秒生成一根，高度随机",[33,4374,4375],{},"✅ 小鸟穿过会在控制台打印「得分！」",[33,4377,4378],{},"✅ 小鸟撞到水管或地面会在控制台打印「撞击死亡！」",[15,4380,4381],{},[19,4382],{"alt":4383,"src":4384},"04-obstacles-尝试运行","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F04-obstacles-%E5%B0%9D%E8%AF%95%E8%BF%90%E8%A1%8C.png",[15,4386,4387,4388,4390],{},"下一章我们来做 ",[858,4389,3237],{},"，把\"撞击死亡\"真正变成\"游戏结束 + 显示分数 + 重新开始\"。",[1083,4392,4393],{},"html pre.shiki code .sTPum, html code.shiki .sTPum{--shiki-default:#1E754F;--shiki-dark:#4D9375}html pre.shiki code .s_NWU, html code.shiki .s_NWU{--shiki-default:#2E8F82;--shiki-dark:#5DA994}html pre.shiki code .s5TCs, html code.shiki .s5TCs{--shiki-default:#AB5959;--shiki-dark:#CB7676}html pre.shiki code .s_xSY, html code.shiki .s_xSY{--shiki-default:#59873A;--shiki-dark:#80A665}html pre.shiki code .si6no, html code.shiki .si6no{--shiki-default:#999999;--shiki-dark:#666666}html pre.shiki code .s8w-G, html code.shiki .s8w-G{--shiki-default:#393A34;--shiki-dark:#DBD7CAEE}html pre.shiki code .s9nN2, html code.shiki .s9nN2{--shiki-default:#B07D48;--shiki-dark:#BD976A}html pre.shiki code .spP0B, html code.shiki .spP0B{--shiki-default:#B56959;--shiki-dark:#C98A7D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sqbOQ, html code.shiki .sqbOQ{--shiki-default:#2F798A;--shiki-dark:#4C9A91}html pre.shiki code .sfsYZ, html code.shiki .sfsYZ{--shiki-default:#A65E2B;--shiki-dark:#C99076}html pre.shiki code .snYqZ, html code.shiki .snYqZ{--shiki-default:#A0ADA0;--shiki-dark:#758575DD}",{"title":149,"searchDepth":171,"depth":171,"links":4395},[4396,4397,4401,4406,4410],{"id":2919,"depth":171,"text":2920},{"id":2963,"depth":171,"text":2963,"children":4398},[4399,4400],{"id":3117,"depth":178,"text":3118},{"id":3365,"depth":178,"text":3366},{"id":3382,"depth":171,"text":3383,"children":4402},[4403,4404],{"id":3653,"depth":178,"text":3654},{"id":3702,"depth":178,"text":4405},"为什么要 set_deferred(\"monitoring\", false)？",{"id":2897,"depth":171,"text":2897,"children":4407},[4408,4409],{"id":4087,"depth":178,"text":4087},{"id":4291,"depth":178,"text":4291},{"id":2359,"depth":171,"text":2359},{},"\u002Fdevlog\u002Fxggame-bird\u002F04-obstacles",{"title":2873,"description":1099},"devlog\u002Fxggame-bird\u002F04-obstacles","3A4xyMDP63Q0nzX7ZiPr4mConIp3Z60qkzeE1gvT5wM",{"id":4417,"title":3237,"body":4418,"cover":1097,"date":1098,"description":1099,"extension":1100,"game":1101,"github":1102,"icon":1097,"meta":5883,"navigation":174,"path":5884,"seo":5885,"stem":5886,"toc":174,"__hash__":5887},"devlog\u002Fdevlog\u002Fxggame-bird\u002F05-game-manager.md",{"type":7,"value":4419,"toc":5858},[4420,4433,4437,4440,4459,4462,4469,4473,4479,4828,4832,4842,4857,4860,4866,4872,4898,4905,4933,4947,4949,4955,4980,5000,5047,5053,5082,5089,5105,5111,5120,5167,5185,5194,5208,5213,5217,5224,5231,5314,5321,5328,5560,5567,5763,5765,5771,5777,5846,5855],[15,4421,4422,4423,4426,4427,4430,4431,1147],{},"游戏到这一步已经能玩了，但缺少",[858,4424,4425],{},"游戏状态管理","和",[858,4428,4429],{},"计分","。我们需要一个统一的管理者来掌控全局 — 这就是 ",[858,4432,3237],{},[10,4434,4436],{"id":4435},"为什么需要-gamemanager","为什么需要 GameManager？",[15,4438,4439],{},"想象一下，游戏有 3 种状态：",[30,4441,4442,4448,4453],{},[33,4443,4444,4447],{},[858,4445,4446],{},"READY","（准备）— 显示\"点击开始\"",[33,4449,4450,4452],{},[858,4451,3323],{},"（进行中）— 小鸟能跳、水管在生成",[33,4454,4455,4458],{},[858,4456,4457],{},"GAME_OVER","（结束）— 显示分数、可以重开",[15,4460,4461],{},"如果让每个节点自己判断状态，比如小鸟自己看\"现在能不能跳\"、水管自己看\"现在能不能生成\"，代码会乱成一团 — 改一个地方要同步改很多处。",[15,4463,4464,4465,4468],{},"GameManager 就是",[858,4466,4467],{},"所有节点共同遵守的\"裁判\""," — 状态、分数都由它统一保管，其他节点只问它要、不自己存。",[10,4470,4472],{"id":4471},"创建-gamemanager-脚本","创建 GameManager 脚本",[15,4474,4475,4476,3217],{},"新建文件 ",[36,4477,4478],{},"scripts\u002Fautoload\u002Fgame_manager.gd",[144,4480,4482],{"className":146,"code":4481,"language":148,"meta":149,"style":149},"extends Node\n\n# 定义游戏状态\nenum GameState { READY, PLAYING, GAME_OVER }\nvar current_state = GameState.READY\n\nvar total_score: int = 0   # 总分\nvar high_score: int = 0    # 最高分\n\n# 信号：通知其他节点\nsignal score_changed(new_score)\nsignal state_changed(new_state)\n\n## 清空分数\nfunc clear_score():\n    total_score = 0\n    score_changed.emit(total_score)\n\n## 添加得分\nfunc add_score(amount: int = 1):\n    total_score += amount\n    score_changed.emit(total_score)\n\n## 设置当前游戏状态\nfunc set_state(new_state: GameState):\n    current_state = new_state\n    state_changed.emit(new_state)\n\n## 游戏结束\nfunc game_over():\n    set_state(GameState.GAME_OVER)\n    if high_score \u003C total_score:\n        high_score = total_score\n",[36,4483,4484,4491,4495,4500,4526,4542,4546,4565,4583,4587,4592,4607,4621,4625,4630,4640,4649,4666,4670,4675,4699,4708,4722,4726,4731,4748,4758,4773,4777,4782,4791,4806,4818],{"__ignoreMap":149},[153,4485,4486,4488],{"class":155,"line":156},[153,4487,160],{"class":159},[153,4489,4490],{"class":163}," Node\n",[153,4492,4493],{"class":155,"line":171},[153,4494,175],{"emptyLinePlaceholder":174},[153,4496,4497],{"class":155,"line":178},[153,4498,4499],{"class":167},"# 定义游戏状态\n",[153,4501,4502,4505,4508,4511,4514,4517,4519,4521,4523],{"class":155,"line":204},[153,4503,4504],{"class":159},"enum",[153,4506,4507],{"class":163}," GameState",[153,4509,4510],{"class":193}," {",[153,4512,4513],{"class":189}," READY",[153,4515,4516],{"class":260},", ",[153,4518,3323],{"class":189},[153,4520,4516],{"class":260},[153,4522,4457],{"class":189},[153,4524,4525],{"class":193}," }\n",[153,4527,4528,4530,4533,4535,4537,4539],{"class":155,"line":225},[153,4529,517],{"class":185},[153,4531,4532],{"class":189}," current_state",[153,4534,371],{"class":193},[153,4536,4507],{"class":163},[153,4538,349],{"class":193},[153,4540,4541],{"class":159},"READY\n",[153,4543,4544],{"class":155,"line":243},[153,4545,175],{"emptyLinePlaceholder":174},[153,4547,4548,4550,4553,4555,4558,4560,4562],{"class":155,"line":248},[153,4549,517],{"class":185},[153,4551,4552],{"class":189}," total_score",[153,4554,264],{"class":193},[153,4556,4557],{"class":163}," int",[153,4559,371],{"class":193},[153,4561,2732],{"class":197},[153,4563,4564],{"class":167},"   # 总分\n",[153,4566,4567,4569,4572,4574,4576,4578,4580],{"class":155,"line":282},[153,4568,517],{"class":185},[153,4570,4571],{"class":189}," high_score",[153,4573,264],{"class":193},[153,4575,4557],{"class":163},[153,4577,371],{"class":193},[153,4579,2732],{"class":197},[153,4581,4582],{"class":167},"    # 最高分\n",[153,4584,4585],{"class":155,"line":288},[153,4586,175],{"emptyLinePlaceholder":174},[153,4588,4589],{"class":155,"line":305},[153,4590,4591],{"class":167},"# 信号：通知其他节点\n",[153,4593,4594,4597,4600,4602,4605],{"class":155,"line":330},[153,4595,4596],{"class":185},"signal",[153,4598,4599],{"class":181}," score_changed",[153,4601,257],{"class":193},[153,4603,4604],{"class":260},"new_score",[153,4606,2660],{"class":193},[153,4608,4609,4611,4614,4616,4619],{"class":155,"line":335},[153,4610,4596],{"class":185},[153,4612,4613],{"class":181}," state_changed",[153,4615,257],{"class":193},[153,4617,4618],{"class":260},"new_state",[153,4620,2660],{"class":193},[153,4622,4623],{"class":155,"line":341},[153,4624,175],{"emptyLinePlaceholder":174},[153,4626,4627],{"class":155,"line":362},[153,4628,4629],{"class":167},"## 清空分数\n",[153,4631,4632,4634,4637],{"class":155,"line":377},[153,4633,251],{"class":185},[153,4635,4636],{"class":181}," clear_score",[153,4638,4639],{"class":193},"():\n",[153,4641,4642,4645,4647],{"class":155,"line":382},[153,4643,4644],{"class":189},"    total_score",[153,4646,371],{"class":193},[153,4648,2624],{"class":197},[153,4650,4651,4654,4656,4659,4661,4664],{"class":155,"line":388},[153,4652,4653],{"class":189},"    score_changed",[153,4655,349],{"class":260},[153,4657,4658],{"class":181},"emit",[153,4660,257],{"class":193},[153,4662,4663],{"class":189},"total_score",[153,4665,2660],{"class":193},[153,4667,4668],{"class":155,"line":411},[153,4669,175],{"emptyLinePlaceholder":174},[153,4671,4672],{"class":155,"line":425},[153,4673,4674],{"class":167},"## 添加得分\n",[153,4676,4677,4679,4682,4684,4687,4689,4691,4693,4696],{"class":155,"line":430},[153,4678,251],{"class":185},[153,4680,4681],{"class":181}," add_score",[153,4683,257],{"class":193},[153,4685,4686],{"class":260},"amount",[153,4688,264],{"class":193},[153,4690,4557],{"class":163},[153,4692,371],{"class":193},[153,4694,4695],{"class":197}," 1",[153,4697,4698],{"class":193},"):\n",[153,4700,4701,4703,4705],{"class":155,"line":2639},[153,4702,4644],{"class":189},[153,4704,311],{"class":185},[153,4706,4707],{"class":189}," amount\n",[153,4709,4710,4712,4714,4716,4718,4720],{"class":155,"line":2644},[153,4711,4653],{"class":189},[153,4713,349],{"class":260},[153,4715,4658],{"class":181},[153,4717,257],{"class":193},[153,4719,4663],{"class":189},[153,4721,2660],{"class":193},[153,4723,4724],{"class":155,"line":2650},[153,4725,175],{"emptyLinePlaceholder":174},[153,4727,4728],{"class":155,"line":2663},[153,4729,4730],{"class":167},"## 设置当前游戏状态\n",[153,4732,4733,4735,4738,4740,4742,4744,4746],{"class":155,"line":2670},[153,4734,251],{"class":185},[153,4736,4737],{"class":181}," set_state",[153,4739,257],{"class":193},[153,4741,4618],{"class":260},[153,4743,264],{"class":193},[153,4745,4507],{"class":163},[153,4747,4698],{"class":193},[153,4749,4750,4753,4755],{"class":155,"line":2675},[153,4751,4752],{"class":189},"    current_state",[153,4754,371],{"class":193},[153,4756,4757],{"class":189}," new_state\n",[153,4759,4760,4763,4765,4767,4769,4771],{"class":155,"line":2699},[153,4761,4762],{"class":189},"    state_changed",[153,4764,349],{"class":260},[153,4766,4658],{"class":181},[153,4768,257],{"class":193},[153,4770,4618],{"class":189},[153,4772,2660],{"class":193},[153,4774,4775],{"class":155,"line":2713},[153,4776,175],{"emptyLinePlaceholder":174},[153,4778,4779],{"class":155,"line":2718},[153,4780,4781],{"class":167},"## 游戏结束\n",[153,4783,4784,4786,4789],{"class":155,"line":2737},[153,4785,251],{"class":185},[153,4787,4788],{"class":181}," game_over",[153,4790,4639],{"class":193},[153,4792,4793,4796,4798,4800,4802,4804],{"class":155,"line":2743},[153,4794,4795],{"class":181},"    set_state",[153,4797,257],{"class":193},[153,4799,3318],{"class":163},[153,4801,349],{"class":193},[153,4803,4457],{"class":159},[153,4805,2660],{"class":193},[153,4807,4808,4810,4812,4814,4816],{"class":155,"line":2764},[153,4809,291],{"class":159},[153,4811,4571],{"class":189},[153,4813,2729],{"class":185},[153,4815,4552],{"class":189},[153,4817,279],{"class":260},[153,4819,4820,4823,4825],{"class":155,"line":2772},[153,4821,4822],{"class":189},"        high_score",[153,4824,371],{"class":193},[153,4826,4827],{"class":189}," total_score\n",[10,4829,4831],{"id":4830},"autoload-全局单例","Autoload — 全局单例",[15,4833,4834,4835,4838,4839,1147],{},"脚本写好了，但现在它只是个 ",[36,4836,4837],{},".gd"," 文件，",[858,4840,4841],{},"别的节点没办法访问到它",[15,4843,4844,4845,4848,4849,4852,4853,4856],{},"Godot 提供了 ",[858,4846,4847],{},"Autoload","（自动加载）机制 — 注册之后，这个脚本\u002F场景",[858,4850,4851],{},"全程只有一份","，任何节点都能直接通过名字访问，不需要 ",[36,4854,4855],{},"get_node()"," 找路径。",[71,4858,4859],{"id":4859},"注册步骤",[15,4861,4862,4863],{},"路径：",[858,4864,4865],{},"项目 → 项目设置 → 全局 → 自动加载",[15,4867,4868],{},[19,4869],{"alt":4870,"src":4871},"05-game-manager-全局单列GM","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F05-game-manager-%E5%85%A8%E5%B1%80%E5%8D%95%E5%88%97GM.png",[1060,4873,4874,4883,4892,4895],{},[33,4875,4876,4879,4880],{},[858,4877,4878],{},"路径","：选 ",[36,4881,4882],{},"res:\u002F\u002Fscripts\u002Fautoload\u002Fgame_manager.gd",[33,4884,4885,4888,4889,4891],{},[858,4886,4887],{},"节点名称","：自动填好 ",[36,4890,3237],{},"（也可以手动改）",[33,4893,4894],{},"点击「添加」",[33,4896,4897],{},"确认右侧「启用」是勾选状态",[15,4899,4900,4901,4904],{},"注册之后，任何节点的脚本里都能直接写 ",[36,4902,4903],{},"GameManager.xxx"," 来访问它，比如：",[144,4906,4908],{"className":146,"code":4907,"language":148,"meta":149,"style":149},"GameManager.add_score(1)\nGameManager.current_state\n",[36,4909,4910,4924],{"__ignoreMap":149},[153,4911,4912,4914,4916,4918,4920,4922],{"class":155,"line":156},[153,4913,3237],{"class":163},[153,4915,349],{"class":260},[153,4917,3951],{"class":181},[153,4919,257],{"class":193},[153,4921,3956],{"class":197},[153,4923,2660],{"class":193},[153,4925,4926,4928,4930],{"class":155,"line":171},[153,4927,3237],{"class":163},[153,4929,349],{"class":193},[153,4931,4932],{"class":189},"current_state\n",[15,4934,4935,4936,4939,4940,4939,4943,4946],{},"无需 ",[36,4937,4938],{},"preload"," \u002F ",[36,4941,4942],{},"load",[36,4944,4945],{},"get_node","，超方便。",[10,4948,3654],{"id":3653},[71,4950,4952],{"id":4951},"enum-gamestate",[36,4953,4954],{},"enum GameState",[144,4956,4958],{"className":146,"code":4957,"language":148,"meta":149,"style":149},"enum GameState { READY, PLAYING, GAME_OVER }\n",[36,4959,4960],{"__ignoreMap":149},[153,4961,4962,4964,4966,4968,4970,4972,4974,4976,4978],{"class":155,"line":156},[153,4963,4504],{"class":159},[153,4965,4507],{"class":163},[153,4967,4510],{"class":193},[153,4969,4513],{"class":189},[153,4971,4516],{"class":260},[153,4973,3323],{"class":189},[153,4975,4516],{"class":260},[153,4977,4457],{"class":189},[153,4979,4525],{"class":193},[15,4981,4982,4984,4985,4988,4989,4992,4993,4992,4996,4999],{},[36,4983,4504],{},"（枚举）就是给一组\"状态\"取个",[858,4986,4987],{},"好记的名字","。本质上 ",[36,4990,4991],{},"READY=0","、",[36,4994,4995],{},"PLAYING=1",[36,4997,4998],{},"GAME_OVER=2","，但写代码时用名字比写数字清楚得多：",[144,5001,5003],{"className":146,"code":5002,"language":148,"meta":149,"style":149},"# ❌ 不直观\nif current_state == 0:\n# ✅ 一眼明白\nif current_state == GameManager.GameState.READY:\n",[36,5004,5005,5010,5022,5027],{"__ignoreMap":149},[153,5006,5007],{"class":155,"line":156},[153,5008,5009],{"class":167},"# ❌ 不直观\n",[153,5011,5012,5014,5016,5018,5020],{"class":155,"line":171},[153,5013,747],{"class":159},[153,5015,4532],{"class":189},[153,5017,3193],{"class":185},[153,5019,2732],{"class":197},[153,5021,279],{"class":260},[153,5023,5024],{"class":155,"line":178},[153,5025,5026],{"class":167},"# ✅ 一眼明白\n",[153,5028,5029,5031,5033,5035,5037,5039,5041,5043,5045],{"class":155,"line":204},[153,5030,747],{"class":159},[153,5032,4532],{"class":189},[153,5034,3193],{"class":185},[153,5036,3304],{"class":163},[153,5038,349],{"class":193},[153,5040,3318],{"class":189},[153,5042,349],{"class":193},[153,5044,4446],{"class":159},[153,5046,279],{"class":260},[71,5048,5050,5052],{"id":5049},"signal信号",[36,5051,4596],{},"（信号）",[144,5054,5056],{"className":146,"code":5055,"language":148,"meta":149,"style":149},"signal score_changed(new_score)\nsignal state_changed(new_state)\n",[36,5057,5058,5070],{"__ignoreMap":149},[153,5059,5060,5062,5064,5066,5068],{"class":155,"line":156},[153,5061,4596],{"class":185},[153,5063,4599],{"class":181},[153,5065,257],{"class":193},[153,5067,4604],{"class":260},[153,5069,2660],{"class":193},[153,5071,5072,5074,5076,5078,5080],{"class":155,"line":171},[153,5073,4596],{"class":185},[153,5075,4613],{"class":181},[153,5077,257],{"class":193},[153,5079,4618],{"class":260},[153,5081,2660],{"class":193},[15,5083,5084,5085,5088],{},"信号是 Godot 的「事件通知机制」。GameManager 不需要知道",[858,5086,5087],{},"谁","在听 — 它只管\"广播消息\"：",[30,5090,5091,5098],{},[33,5092,5093,5094,5097],{},"分数变了 → 喊一声 ",[36,5095,5096],{},"score_changed","，UI 接到了就更新分数显示",[33,5099,5100,5101,5104],{},"状态变了 → 喊一声 ",[36,5102,5103],{},"state_changed","，所有关心状态的节点都能响应",[15,5106,5107,5110],{},[858,5108,5109],{},"这样 GameManager 不用知道 UI 长啥样，UI 也不用主动来问 GameManager","。两边解耦，互不干扰 — 信号是 Godot 里非常核心的设计思想。",[71,5112,5114,3083,5117],{"id":5113},"add_score-和-score_changedemit",[36,5115,5116],{},"add_score()",[36,5118,5119],{},"score_changed.emit()",[144,5121,5123],{"className":146,"code":5122,"language":148,"meta":149,"style":149},"func add_score(amount: int = 1):\n    total_score += amount\n    score_changed.emit(total_score)\n",[36,5124,5125,5145,5153],{"__ignoreMap":149},[153,5126,5127,5129,5131,5133,5135,5137,5139,5141,5143],{"class":155,"line":156},[153,5128,251],{"class":185},[153,5130,4681],{"class":181},[153,5132,257],{"class":193},[153,5134,4686],{"class":260},[153,5136,264],{"class":193},[153,5138,4557],{"class":163},[153,5140,371],{"class":193},[153,5142,4695],{"class":197},[153,5144,4698],{"class":193},[153,5146,5147,5149,5151],{"class":155,"line":171},[153,5148,4644],{"class":189},[153,5150,311],{"class":185},[153,5152,4707],{"class":189},[153,5154,5155,5157,5159,5161,5163,5165],{"class":155,"line":178},[153,5156,4653],{"class":189},[153,5158,349],{"class":260},[153,5160,4658],{"class":181},[153,5162,257],{"class":193},[153,5164,4663],{"class":189},[153,5166,2660],{"class":193},[30,5168,5169,5175],{},[33,5170,5171,5174],{},[36,5172,5173],{},"amount: int = 1"," — 参数默认值是 1，不传就 +1，传 2 就 +2",[33,5176,5177,5180,5181,5184],{},[36,5178,5179],{},"score_changed.emit(total_score)"," — ",[858,5182,5183],{},"发出信号","，把最新分数传出去",[71,5186,5188,3083,5191],{"id":5187},"set_state-和-game_over",[36,5189,5190],{},"set_state()",[36,5192,5193],{},"game_over()",[15,5195,5196,5199,5200,5203,5204,5207],{},[36,5197,5198],{},"set_state"," 是修改状态的",[858,5201,5202],{},"唯一入口","，每次修改都自动 emit 信号。这样",[858,5205,5206],{},"任何状态改变都会被广播","，不会漏。",[15,5209,5210,5212],{},[36,5211,3347],{}," 不仅切换状态，还顺便更新最高分。",[10,5214,5216],{"id":5215},"把-gamemanager-接进-killzone-和-障碍物","把 GameManager 接进 Killzone 和 障碍物",[15,5218,5219,5220,5223],{},"GameManager 创建好了，现在回到 ",[1124,5221,5222],{"href":4412},"上一章"," 里我们留的\"后续优化预览\"代码，把它们正式接入：",[71,5225,5227,5228,3028],{"id":5226},"死区脚本killzonegd","死区脚本（",[36,5229,5230],{},"killzone.gd",[144,5232,5234],{"className":146,"code":5233,"language":148,"meta":149,"style":149},"extends Area2D\n\nfunc _on_body_entered(body: Node2D) -> void:\n    if body.name == \"Bird\" and GameManager.current_state == GameManager.GameState.PLAYING:\n        GameManager.game_over()\n",[36,5235,5236,5242,5246,5268,5304],{"__ignoreMap":149},[153,5237,5238,5240],{"class":155,"line":156},[153,5239,160],{"class":159},[153,5241,3149],{"class":163},[153,5243,5244],{"class":155,"line":171},[153,5245,175],{"emptyLinePlaceholder":174},[153,5247,5248,5250,5252,5254,5256,5258,5260,5262,5264,5266],{"class":155,"line":178},[153,5249,251],{"class":185},[153,5251,3160],{"class":181},[153,5253,257],{"class":193},[153,5255,3165],{"class":260},[153,5257,264],{"class":193},[153,5259,3170],{"class":163},[153,5261,270],{"class":193},[153,5263,273],{"class":185},[153,5265,276],{"class":163},[153,5267,279],{"class":193},[153,5269,5270,5272,5274,5276,5278,5280,5282,5284,5286,5288,5290,5292,5294,5296,5298,5300,5302],{"class":155,"line":204},[153,5271,291],{"class":159},[153,5273,3185],{"class":189},[153,5275,349],{"class":193},[153,5277,3190],{"class":189},[153,5279,3193],{"class":185},[153,5281,3196],{"class":403},[153,5283,3301],{"class":185},[153,5285,3304],{"class":163},[153,5287,349],{"class":193},[153,5289,3309],{"class":189},[153,5291,3193],{"class":185},[153,5293,3304],{"class":163},[153,5295,349],{"class":193},[153,5297,3318],{"class":189},[153,5299,349],{"class":193},[153,5301,3323],{"class":159},[153,5303,279],{"class":260},[153,5305,5306,5308,5310,5312],{"class":155,"line":225},[153,5307,3342],{"class":163},[153,5309,349],{"class":260},[153,5311,3347],{"class":181},[153,5313,436],{"class":193},[15,5315,5316,5317,5320],{},"加上 ",[36,5318,5319],{},"current_state == PLAYING"," 判断 — 防止 READY 状态（还没开始）和 GAME_OVER 状态（已经结束）时误触发。",[71,5322,5324,5325,3028],{"id":5323},"障碍物脚本pillar_pairgd","障碍物脚本（",[36,5326,5327],{},"pillar_pair.gd",[144,5329,5331],{"className":146,"code":5330,"language":148,"meta":149,"style":149},"extends Node2D\n\n@export var speed := 200.0\n@onready var goal: Area2D = $Goal\n\nfunc _physics_process(delta: float) -> void:\n    # 只在游戏进行中才移动\n    if GameManager.current_state == GameManager.GameState.PLAYING:\n        position.x -= speed * delta\n        if position.x \u003C -500:\n            queue_free()\n\nfunc _on_goal_body_entered(body: Node2D) -> void:\n    if body.name == \"Bird\":\n        if GameManager.has_method(\"add_score\"):\n            GameManager.add_score(1)\n        goal.set_deferred(\"monitoring\", false)\n",[36,5332,5333,5339,5343,5355,5373,5377,5399,5404,5428,5444,5462,5468,5472,5494,5510,5528,5542],{"__ignoreMap":149},[153,5334,5335,5337],{"class":155,"line":156},[153,5336,160],{"class":159},[153,5338,3424],{"class":163},[153,5340,5341],{"class":155,"line":171},[153,5342,175],{"emptyLinePlaceholder":174},[153,5344,5345,5347,5349,5351,5353],{"class":155,"line":178},[153,5346,182],{"class":181},[153,5348,186],{"class":185},[153,5350,3437],{"class":189},[153,5352,194],{"class":193},[153,5354,3442],{"class":197},[153,5356,5357,5359,5361,5363,5365,5367,5369,5371],{"class":155,"line":204},[153,5358,3447],{"class":181},[153,5360,186],{"class":185},[153,5362,3452],{"class":189},[153,5364,264],{"class":193},[153,5366,3457],{"class":163},[153,5368,371],{"class":193},[153,5370,3462],{"class":159},[153,5372,3466],{"class":3465},[153,5374,5375],{"class":155,"line":225},[153,5376,175],{"emptyLinePlaceholder":174},[153,5378,5379,5381,5383,5385,5387,5389,5391,5393,5395,5397],{"class":155,"line":243},[153,5380,251],{"class":185},[153,5382,254],{"class":181},[153,5384,257],{"class":193},[153,5386,261],{"class":260},[153,5388,264],{"class":193},[153,5390,267],{"class":163},[153,5392,270],{"class":193},[153,5394,273],{"class":185},[153,5396,276],{"class":163},[153,5398,279],{"class":193},[153,5400,5401],{"class":155,"line":248},[153,5402,5403],{"class":167},"    # 只在游戏进行中才移动\n",[153,5405,5406,5408,5410,5412,5414,5416,5418,5420,5422,5424,5426],{"class":155,"line":282},[153,5407,291],{"class":159},[153,5409,3304],{"class":163},[153,5411,349],{"class":193},[153,5413,3309],{"class":189},[153,5415,3193],{"class":185},[153,5417,3304],{"class":163},[153,5419,349],{"class":193},[153,5421,3318],{"class":189},[153,5423,349],{"class":193},[153,5425,3323],{"class":159},[153,5427,279],{"class":260},[153,5429,5430,5432,5434,5436,5438,5440,5442],{"class":155,"line":288},[153,5431,3830],{"class":189},[153,5433,349],{"class":193},[153,5435,823],{"class":189},[153,5437,3509],{"class":185},[153,5439,3437],{"class":189},[153,5441,319],{"class":185},[153,5443,3516],{"class":189},[153,5445,5446,5448,5450,5452,5454,5456,5458,5460],{"class":155,"line":305},[153,5447,3847],{"class":159},[153,5449,3528],{"class":189},[153,5451,349],{"class":193},[153,5453,823],{"class":189},[153,5455,2729],{"class":185},[153,5457,216],{"class":185},[153,5459,3539],{"class":197},[153,5461,279],{"class":260},[153,5463,5464,5466],{"class":155,"line":330},[153,5465,3866],{"class":181},[153,5467,436],{"class":193},[153,5469,5470],{"class":155,"line":335},[153,5471,175],{"emptyLinePlaceholder":174},[153,5473,5474,5476,5478,5480,5482,5484,5486,5488,5490,5492],{"class":155,"line":341},[153,5475,251],{"class":185},[153,5477,3564],{"class":181},[153,5479,257],{"class":193},[153,5481,3165],{"class":260},[153,5483,264],{"class":193},[153,5485,3170],{"class":163},[153,5487,270],{"class":193},[153,5489,273],{"class":185},[153,5491,276],{"class":163},[153,5493,279],{"class":193},[153,5495,5496,5498,5500,5502,5504,5506,5508],{"class":155,"line":362},[153,5497,291],{"class":159},[153,5499,3185],{"class":189},[153,5501,349],{"class":193},[153,5503,3190],{"class":189},[153,5505,3193],{"class":185},[153,5507,3196],{"class":403},[153,5509,279],{"class":260},[153,5511,5512,5514,5516,5518,5520,5522,5524,5526],{"class":155,"line":377},[153,5513,3847],{"class":159},[153,5515,3304],{"class":163},[153,5517,349],{"class":260},[153,5519,3932],{"class":181},[153,5521,257],{"class":193},[153,5523,3937],{"class":403},[153,5525,270],{"class":193},[153,5527,279],{"class":260},[153,5529,5530,5532,5534,5536,5538,5540],{"class":155,"line":382},[153,5531,3946],{"class":163},[153,5533,349],{"class":260},[153,5535,3951],{"class":181},[153,5537,257],{"class":193},[153,5539,3956],{"class":197},[153,5541,2660],{"class":193},[153,5543,5544,5546,5548,5550,5552,5554,5556,5558],{"class":155,"line":388},[153,5545,3627],{"class":189},[153,5547,349],{"class":260},[153,5549,3632],{"class":181},[153,5551,257],{"class":193},[153,5553,3637],{"class":403},[153,5555,2821],{"class":193},[153,5557,3642],{"class":159},[153,5559,2660],{"class":193},[71,5561,5563,5564,3028],{"id":5562},"生成器脚本pillar_spawnergd","生成器脚本（",[36,5565,5566],{},"pillar_spawner.gd",[144,5568,5570],{"className":146,"code":5569,"language":148,"meta":149,"style":149},"extends Node2D\n\n@export var pillar_scene: PackedScene\n@export var y_range := 250.0\n\nfunc _on_timer_timeout() -> void:\n    spawn_pillar()\n\nfunc spawn_pillar() -> void:\n    # 只在游戏进行中才生成\n    if GameManager.current_state == GameManager.GameState.PLAYING:\n        var new_pillar = pillar_scene.instantiate()\n        var spawn_pos = global_position\n        spawn_pos.y += randf_range(-y_range, y_range)\n        new_pillar.global_position = spawn_pos\n        get_tree().current_scene.add_child(new_pillar)\n",[36,5571,5572,5578,5582,5594,5606,5610,5624,5630,5634,5648,5653,5677,5694,5704,5729,5742],{"__ignoreMap":149},[153,5573,5574,5576],{"class":155,"line":156},[153,5575,160],{"class":159},[153,5577,3424],{"class":163},[153,5579,5580],{"class":155,"line":171},[153,5581,175],{"emptyLinePlaceholder":174},[153,5583,5584,5586,5588,5590,5592],{"class":155,"line":178},[153,5585,182],{"class":181},[153,5587,186],{"class":185},[153,5589,4111],{"class":189},[153,5591,264],{"class":193},[153,5593,4116],{"class":163},[153,5595,5596,5598,5600,5602,5604],{"class":155,"line":204},[153,5597,182],{"class":181},[153,5599,186],{"class":185},[153,5601,4125],{"class":189},[153,5603,194],{"class":193},[153,5605,4130],{"class":197},[153,5607,5608],{"class":155,"line":225},[153,5609,175],{"emptyLinePlaceholder":174},[153,5611,5612,5614,5616,5618,5620,5622],{"class":155,"line":243},[153,5613,251],{"class":185},[153,5615,4141],{"class":181},[153,5617,300],{"class":193},[153,5619,273],{"class":185},[153,5621,276],{"class":163},[153,5623,279],{"class":193},[153,5625,5626,5628],{"class":155,"line":248},[153,5627,4154],{"class":181},[153,5629,436],{"class":193},[153,5631,5632],{"class":155,"line":282},[153,5633,175],{"emptyLinePlaceholder":174},[153,5635,5636,5638,5640,5642,5644,5646],{"class":155,"line":288},[153,5637,251],{"class":185},[153,5639,4167],{"class":181},[153,5641,300],{"class":193},[153,5643,273],{"class":185},[153,5645,276],{"class":163},[153,5647,279],{"class":193},[153,5649,5650],{"class":155,"line":305},[153,5651,5652],{"class":167},"    # 只在游戏进行中才生成\n",[153,5654,5655,5657,5659,5661,5663,5665,5667,5669,5671,5673,5675],{"class":155,"line":330},[153,5656,291],{"class":159},[153,5658,3304],{"class":163},[153,5660,349],{"class":193},[153,5662,3309],{"class":189},[153,5664,3193],{"class":185},[153,5666,3304],{"class":163},[153,5668,349],{"class":193},[153,5670,3318],{"class":189},[153,5672,349],{"class":193},[153,5674,3323],{"class":159},[153,5676,279],{"class":260},[153,5678,5679,5682,5684,5686,5688,5690,5692],{"class":155,"line":335},[153,5680,5681],{"class":185},"        var",[153,5683,4182],{"class":189},[153,5685,371],{"class":193},[153,5687,4111],{"class":189},[153,5689,349],{"class":260},[153,5691,4191],{"class":181},[153,5693,436],{"class":193},[153,5695,5696,5698,5700,5702],{"class":155,"line":341},[153,5697,5681],{"class":185},[153,5699,4204],{"class":189},[153,5701,371],{"class":193},[153,5703,4209],{"class":189},[153,5705,5706,5709,5711,5713,5715,5717,5719,5721,5723,5725,5727],{"class":155,"line":362},[153,5707,5708],{"class":189},"        spawn_pos",[153,5710,349],{"class":193},[153,5712,352],{"class":189},[153,5714,311],{"class":185},[153,5716,4223],{"class":181},[153,5718,257],{"class":193},[153,5720,2756],{"class":185},[153,5722,4230],{"class":189},[153,5724,2821],{"class":193},[153,5726,4125],{"class":189},[153,5728,2660],{"class":193},[153,5730,5731,5734,5736,5738,5740],{"class":155,"line":377},[153,5732,5733],{"class":189},"        new_pillar",[153,5735,349],{"class":193},[153,5737,4250],{"class":189},[153,5739,371],{"class":193},[153,5741,4255],{"class":189},[153,5743,5744,5747,5749,5751,5753,5755,5757,5759,5761],{"class":155,"line":382},[153,5745,5746],{"class":181},"        get_tree",[153,5748,300],{"class":193},[153,5750,349],{"class":260},[153,5752,4276],{"class":189},[153,5754,349],{"class":260},[153,5756,4281],{"class":181},[153,5758,257],{"class":193},[153,5760,4286],{"class":189},[153,5762,2660],{"class":193},[10,5764,2359],{"id":2359},[15,5766,5767,5768,1147],{},"现在还没有 UI，所以视觉上看不出区别 — ",[858,5769,5770],{},"但状态系统已经在跑了",[15,5772,5773,5774,5776],{},"你可以在 ",[36,5775,2411],{}," 里临时加个调试：",[144,5778,5780],{"className":146,"code":5779,"language":148,"meta":149,"style":149},"func _physics_process(delta: float) -> void:\n    if Input.is_action_just_pressed(\"fly\"):\n        print(\"当前状态:\", GameManager.current_state)\n        # ...\n",[36,5781,5782,5804,5822,5841],{"__ignoreMap":149},[153,5783,5784,5786,5788,5790,5792,5794,5796,5798,5800,5802],{"class":155,"line":156},[153,5785,251],{"class":185},[153,5787,254],{"class":181},[153,5789,257],{"class":193},[153,5791,261],{"class":260},[153,5793,264],{"class":193},[153,5795,267],{"class":163},[153,5797,270],{"class":193},[153,5799,273],{"class":185},[153,5801,276],{"class":163},[153,5803,279],{"class":193},[153,5805,5806,5808,5810,5812,5814,5816,5818,5820],{"class":155,"line":171},[153,5807,291],{"class":159},[153,5809,393],{"class":163},[153,5811,349],{"class":260},[153,5813,398],{"class":181},[153,5815,257],{"class":193},[153,5817,404],{"class":403},[153,5819,270],{"class":193},[153,5821,279],{"class":260},[153,5823,5824,5826,5828,5831,5833,5835,5837,5839],{"class":155,"line":178},[153,5825,3203],{"class":181},[153,5827,257],{"class":193},[153,5829,5830],{"class":403},"\"当前状态:\"",[153,5832,2821],{"class":193},[153,5834,3304],{"class":163},[153,5836,349],{"class":193},[153,5838,3309],{"class":189},[153,5840,2660],{"class":193},[153,5842,5843],{"class":155,"line":204},[153,5844,5845],{"class":167},"        # ...\n",[15,5847,5848,5849,5851,5852,5854],{},"不过这时候默认是 ",[36,5850,4446],{}," 状态，水管不会生成、撞死也不会触发 — 因为我们还没有\"开始游戏\"的入口。下一章做 UI 时会加上一个开始按钮，把状态切到 ",[36,5853,3323],{},"，整个流程才会真正跑起来。",[1083,5856,5857],{},"html pre.shiki code .sTPum, html code.shiki .sTPum{--shiki-default:#1E754F;--shiki-dark:#4D9375}html pre.shiki code .s_NWU, html code.shiki .s_NWU{--shiki-default:#2E8F82;--shiki-dark:#5DA994}html pre.shiki code .snYqZ, html code.shiki .snYqZ{--shiki-default:#A0ADA0;--shiki-dark:#758575DD}html pre.shiki code .si6no, html code.shiki .si6no{--shiki-default:#999999;--shiki-dark:#666666}html pre.shiki code .s9nN2, html code.shiki .s9nN2{--shiki-default:#B07D48;--shiki-dark:#BD976A}html pre.shiki code .s8w-G, html code.shiki .s8w-G{--shiki-default:#393A34;--shiki-dark:#DBD7CAEE}html pre.shiki code .s5TCs, html code.shiki .s5TCs{--shiki-default:#AB5959;--shiki-dark:#CB7676}html pre.shiki code .sqbOQ, html code.shiki .sqbOQ{--shiki-default:#2F798A;--shiki-dark:#4C9A91}html pre.shiki code .s_xSY, html code.shiki .s_xSY{--shiki-default:#59873A;--shiki-dark:#80A665}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spP0B, html code.shiki .spP0B{--shiki-default:#B56959;--shiki-dark:#C98A7D}html pre.shiki code .sfsYZ, html code.shiki .sfsYZ{--shiki-default:#A65E2B;--shiki-dark:#C99076}",{"title":149,"searchDepth":171,"depth":171,"links":5859},[5860,5861,5862,5865,5874,5882],{"id":4435,"depth":171,"text":4436},{"id":4471,"depth":171,"text":4472},{"id":4830,"depth":171,"text":4831,"children":5863},[5864],{"id":4859,"depth":178,"text":4859},{"id":3653,"depth":171,"text":3654,"children":5866},[5867,5868,5870,5872],{"id":4951,"depth":178,"text":4954},{"id":5049,"depth":178,"text":5869},"signal（信号）",{"id":5113,"depth":178,"text":5871},"add_score() 和 score_changed.emit()",{"id":5187,"depth":178,"text":5873},"set_state() 和 game_over()",{"id":5215,"depth":171,"text":5216,"children":5875},[5876,5878,5880],{"id":5226,"depth":178,"text":5877},"死区脚本（killzone.gd）",{"id":5323,"depth":178,"text":5879},"障碍物脚本（pillar_pair.gd）",{"id":5562,"depth":178,"text":5881},"生成器脚本（pillar_spawner.gd）",{"id":2359,"depth":171,"text":2359},{},"\u002Fdevlog\u002Fxggame-bird\u002F05-game-manager",{"title":3237,"description":1099},"devlog\u002Fxggame-bird\u002F05-game-manager","FNw8591Sth7D2Iaz9Dt3V0d-rXtMLyEjwyH5oxagnGQ",{"id":5889,"title":5890,"body":5891,"cover":1097,"date":1098,"description":1099,"extension":1100,"game":1101,"github":1102,"icon":1097,"meta":7161,"navigation":174,"path":7162,"seo":7163,"stem":7164,"toc":174,"__hash__":7165},"devlog\u002Fdevlog\u002Fxggame-bird\u002F06-ui.md","UI 界面 + 主场景",{"type":7,"value":5892,"toc":7141},[5893,5896,5929,5932,5937,5945,5951,5958,5960,5987,6002,6008,6011,6024,6072,6083,6089,6102,6105,6203,6209,6215,6586,6588,6663,6670,6722,6731,6734,6743,6748,6751,6759,6860,6874,6880,6883,6892,6898,7065,7083,7095,7098,7104,7107,7113,7115,7118,7135,7138],[15,5894,5895],{},"GameManager 把状态和分数管理好了，这一章让玩家\"看得见\"。我们要做：",[1060,5897,5898,5908,5917,5923],{},[33,5899,5900,5903,5904,5907],{},[858,5901,5902],{},"开始菜单","（",[36,5905,5906],{},"menu.tscn","）— 一个开始按钮",[33,5909,5910,5903,5913,5916],{},[858,5911,5912],{},"游戏主场景",[36,5914,5915],{},"main.tscn","）— 整合小鸟、水管、UI",[33,5918,5919,5922],{},[858,5920,5921],{},"分数 UI"," — 实时显示当前分数",[33,5924,5925,5928],{},[858,5926,5927],{},"结束画面"," — 显示最终分数 + 重新开始按钮",[10,5930,5931],{"id":5931},"整体流程",[1003,5933,5934],{},[15,5935,5936],{},"整理一下我们要搭的\"两个场景\"的关系：",[144,5938,5943],{"className":5939,"code":5941,"language":5942},[5940],"language-text","menu.tscn（开始菜单）\n   ↓ 点击「开始」按钮\nmain.tscn（游戏主场景）\n   ├─ READY 状态：等待玩家按下 fly\n   ├─ PLAYING 状态：正常游戏\n   └─ GAME_OVER 状态：显示分数 + 重新开始按钮\n","text",[36,5944,5941],{"__ignoreMap":149},[10,5946,5948,5949],{"id":5947},"开始菜单-menutscn","开始菜单 ",[36,5950,5906],{},[15,5952,2191,5953,2195,5955,1147],{},[36,5954,2194],{},[36,5956,5957],{},"scenes\u002Fmenu.tscn",[15,5959,2206],{},[30,5961,5962],{},[33,5963,5964,5966,5967],{},[36,5965,2194],{}," — 菜单根节点\n",[30,5968,5969],{},[33,5970,5971,5974,5975],{},[36,5972,5973],{},"CanvasLayer"," — UI 图层（不随摄像机移动）\n",[30,5976,5977],{},[33,5978,5979,5982,5983,5986],{},[36,5980,5981],{},"Button","（重命名 ",[36,5984,5985],{},"StartButton","）— 开始按钮",[1003,5988,5989],{},[15,5990,5991,5992,5994,5995,5997,5998,6001],{},"为什么 UI 要放在 ",[36,5993,5973],{}," 下？因为 ",[36,5996,5973],{}," 的内容",[858,5999,6000],{},"不会被摄像机变换影响","，永远固定在屏幕上。",[15,6003,6004],{},[19,6005],{"alt":6006,"src":6007},"06-ui-menu-场景","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F06-ui-menu-%E5%9C%BA%E6%99%AF.png",[71,6009,6010],{"id":6010},"菜单脚本",[15,6012,6013,6014,6017,6018,59,6020,6023],{},"选中根节点挂脚本 ",[36,6015,6016],{},"scripts\u002Fmenu.gd","，然后在【节点】面板把 ",[36,6019,5985],{},[36,6021,6022],{},"pressed"," 信号连接到根节点：",[144,6025,6027],{"className":146,"code":6026,"language":148,"meta":149,"style":149},"extends Node2D\n\nfunc _on_start_button_pressed() -> void:\n    get_tree().change_scene_to_file(\"res:\u002F\u002Fscenes\u002Fmain.tscn\")\n",[36,6028,6029,6035,6039,6054],{"__ignoreMap":149},[153,6030,6031,6033],{"class":155,"line":156},[153,6032,160],{"class":159},[153,6034,3424],{"class":163},[153,6036,6037],{"class":155,"line":171},[153,6038,175],{"emptyLinePlaceholder":174},[153,6040,6041,6043,6046,6048,6050,6052],{"class":155,"line":178},[153,6042,251],{"class":185},[153,6044,6045],{"class":181}," _on_start_button_pressed",[153,6047,300],{"class":193},[153,6049,273],{"class":185},[153,6051,276],{"class":163},[153,6053,279],{"class":193},[153,6055,6056,6058,6060,6062,6065,6067,6070],{"class":155,"line":204},[153,6057,4269],{"class":181},[153,6059,300],{"class":193},[153,6061,349],{"class":260},[153,6063,6064],{"class":181},"change_scene_to_file",[153,6066,257],{"class":193},[153,6068,6069],{"class":403},"\"res:\u002F\u002Fscenes\u002Fmain.tscn\"",[153,6071,2660],{"class":193},[30,6073,6074],{},[33,6075,6076,6079,6080,6082],{},[36,6077,6078],{},"change_scene_to_file()"," — 切换到指定场景，路径填你保存 ",[36,6081,5915],{}," 的位置",[10,6084,6086,6087],{"id":6085},"游戏主场景-maintscn","游戏主场景 ",[36,6088,5915],{},[15,6090,6091,6092,6095,6096,6098,6099,6101],{},"现在把之前章节做的所有东西",[858,6093,6094],{},"整合到一起","。如果之前你已经做了 ",[36,6097,2256],{},"，可以直接改名为 ",[36,6100,5915],{},"，或者新建一个。",[15,6103,6104],{},"使用到的节点（核心结构）：",[30,6106,6107],{},[33,6108,6109,6111,6112,2934,6115],{},[36,6110,2194],{}," — 主场景根节点（挂 ",[36,6113,6114],{},"main.gd",[30,6116,6117,6122,6135,6143,6150,6155],{},[33,6118,6119,6121],{},[36,6120,2220],{}," — 摄像机",[33,6123,6124,5982,6127,6130,6131,3028],{},[36,6125,6126],{},"Parallax2D",[36,6128,6129],{},"BG","）— 滚动背景（",[1124,6132,6134],{"href":6133},"\u002Fdevlog\u002Fxggame-bird\u002F07-art-assets","第 7 章会做",[33,6136,6137,5982,6139,6142],{},[36,6138,6126],{},[36,6140,6141],{},"Progress","）— 滚动地面",[33,6144,6145,6147,6148,3028],{},[36,6146,1146],{}," — 小鸟（之前做的 ",[36,6149,2241],{},[33,6151,6152,6154],{},[36,6153,4022],{}," — 障碍物生成器",[33,6156,6157,6159,6160],{},[36,6158,5973],{}," — UI 层\n",[30,6161,6162,6171],{},[33,6163,6164,5982,6167,6170],{},[36,6165,6166],{},"Label",[36,6168,6169],{},"ScoreLabel","）— 分数显示",[33,6172,6173,5982,6176,6179,6180],{},[36,6174,6175],{},"Panel",[36,6177,6178],{},"GameOverPanel","）— 结束面板\n",[30,6181,6182,6187,6195],{},[33,6183,6184,6186],{},[36,6185,6166],{}," — \"Game Over\"",[33,6188,6189,5903,6191,6194],{},[36,6190,6166],{},[36,6192,6193],{},"FinalScore","）— 最终分数",[33,6196,6197,5903,6199,6202],{},[36,6198,5981],{},[36,6200,6201],{},"RestartButton","）— 重新开始",[15,6204,6205],{},[19,6206],{"alt":6207,"src":6208},"06-ui-main-场景","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F06-ui-main-%E5%9C%BA%E6%99%AF.png",[10,6210,6212,6213],{"id":6211},"主场景脚本-maingd","主场景脚本 ",[36,6214,6114],{},[144,6216,6218],{"className":146,"code":6217,"language":148,"meta":149,"style":149},"extends Node2D\n\nfunc _ready() -> void:\n    # 刚加载场景，设置为准备状态\n    GameManager.set_state(GameManager.GameState.READY)\n    GameManager.state_changed.connect(_on_game_state)  # 监听信号\n    Engine.time_scale = 0  # 冻结游戏\n    GameManager.clear_score()  # 游戏开始前清空分数\n\nfunc _input(event: InputEvent) -> void:\n    # 准备状态下，按一下 fly 就开始游戏\n    if GameManager.current_state == GameManager.GameState.READY:\n        if event.is_action_pressed(\"fly\"):\n            start_game()\n\nfunc start_game():\n    GameManager.set_state(GameManager.GameState.PLAYING)\n    Engine.time_scale = 1  # 解冻游戏\n\n## 重新开始游戏\nfunc _on_restart_button_pressed() -> void:\n    get_tree().reload_current_scene()\n\nfunc _on_game_state(new_state) -> void:\n    # 游戏结束时，让背景和地面停止滚动\n    if new_state == GameManager.GameState.GAME_OVER:\n        $BG.autoscroll.x = 0\n        $Progress.autoscroll.x = 0\n",[36,6219,6220,6226,6230,6245,6250,6273,6296,6313,6327,6331,6356,6361,6385,6405,6412,6416,6425,6447,6462,6466,6471,6486,6499,6503,6522,6527,6548,6568],{"__ignoreMap":149},[153,6221,6222,6224],{"class":155,"line":156},[153,6223,160],{"class":159},[153,6225,3424],{"class":163},[153,6227,6228],{"class":155,"line":171},[153,6229,175],{"emptyLinePlaceholder":174},[153,6231,6232,6234,6237,6239,6241,6243],{"class":155,"line":178},[153,6233,251],{"class":185},[153,6235,6236],{"class":181}," _ready",[153,6238,300],{"class":193},[153,6240,273],{"class":185},[153,6242,276],{"class":163},[153,6244,279],{"class":193},[153,6246,6247],{"class":155,"line":204},[153,6248,6249],{"class":167},"    # 刚加载场景，设置为准备状态\n",[153,6251,6252,6255,6257,6259,6261,6263,6265,6267,6269,6271],{"class":155,"line":225},[153,6253,6254],{"class":163},"    GameManager",[153,6256,349],{"class":260},[153,6258,5198],{"class":181},[153,6260,257],{"class":193},[153,6262,3237],{"class":163},[153,6264,349],{"class":193},[153,6266,3318],{"class":189},[153,6268,349],{"class":193},[153,6270,4446],{"class":159},[153,6272,2660],{"class":193},[153,6274,6275,6277,6279,6281,6283,6286,6288,6291,6293],{"class":155,"line":243},[153,6276,6254],{"class":163},[153,6278,349],{"class":193},[153,6280,5103],{"class":189},[153,6282,349],{"class":260},[153,6284,6285],{"class":181},"connect",[153,6287,257],{"class":193},[153,6289,6290],{"class":189},"_on_game_state",[153,6292,270],{"class":193},[153,6294,6295],{"class":167},"  # 监听信号\n",[153,6297,6298,6301,6303,6306,6308,6310],{"class":155,"line":248},[153,6299,6300],{"class":163},"    Engine",[153,6302,349],{"class":193},[153,6304,6305],{"class":189},"time_scale",[153,6307,371],{"class":193},[153,6309,2732],{"class":197},[153,6311,6312],{"class":167},"  # 冻结游戏\n",[153,6314,6315,6317,6319,6322,6324],{"class":155,"line":282},[153,6316,6254],{"class":163},[153,6318,349],{"class":260},[153,6320,6321],{"class":181},"clear_score",[153,6323,300],{"class":193},[153,6325,6326],{"class":167},"  # 游戏开始前清空分数\n",[153,6328,6329],{"class":155,"line":288},[153,6330,175],{"emptyLinePlaceholder":174},[153,6332,6333,6335,6338,6340,6343,6345,6348,6350,6352,6354],{"class":155,"line":305},[153,6334,251],{"class":185},[153,6336,6337],{"class":181}," _input",[153,6339,257],{"class":193},[153,6341,6342],{"class":260},"event",[153,6344,264],{"class":193},[153,6346,6347],{"class":163}," InputEvent",[153,6349,270],{"class":193},[153,6351,273],{"class":185},[153,6353,276],{"class":163},[153,6355,279],{"class":193},[153,6357,6358],{"class":155,"line":330},[153,6359,6360],{"class":167},"    # 准备状态下，按一下 fly 就开始游戏\n",[153,6362,6363,6365,6367,6369,6371,6373,6375,6377,6379,6381,6383],{"class":155,"line":335},[153,6364,291],{"class":159},[153,6366,3304],{"class":163},[153,6368,349],{"class":193},[153,6370,3309],{"class":189},[153,6372,3193],{"class":185},[153,6374,3304],{"class":163},[153,6376,349],{"class":193},[153,6378,3318],{"class":189},[153,6380,349],{"class":193},[153,6382,4446],{"class":159},[153,6384,279],{"class":260},[153,6386,6387,6389,6392,6394,6397,6399,6401,6403],{"class":155,"line":341},[153,6388,3847],{"class":159},[153,6390,6391],{"class":189}," event",[153,6393,349],{"class":260},[153,6395,6396],{"class":181},"is_action_pressed",[153,6398,257],{"class":193},[153,6400,404],{"class":403},[153,6402,270],{"class":193},[153,6404,279],{"class":260},[153,6406,6407,6410],{"class":155,"line":362},[153,6408,6409],{"class":181},"            start_game",[153,6411,436],{"class":193},[153,6413,6414],{"class":155,"line":377},[153,6415,175],{"emptyLinePlaceholder":174},[153,6417,6418,6420,6423],{"class":155,"line":382},[153,6419,251],{"class":185},[153,6421,6422],{"class":181}," start_game",[153,6424,4639],{"class":193},[153,6426,6427,6429,6431,6433,6435,6437,6439,6441,6443,6445],{"class":155,"line":388},[153,6428,6254],{"class":163},[153,6430,349],{"class":260},[153,6432,5198],{"class":181},[153,6434,257],{"class":193},[153,6436,3237],{"class":163},[153,6438,349],{"class":193},[153,6440,3318],{"class":189},[153,6442,349],{"class":193},[153,6444,3323],{"class":159},[153,6446,2660],{"class":193},[153,6448,6449,6451,6453,6455,6457,6459],{"class":155,"line":411},[153,6450,6300],{"class":163},[153,6452,349],{"class":193},[153,6454,6305],{"class":189},[153,6456,371],{"class":193},[153,6458,4695],{"class":197},[153,6460,6461],{"class":167},"  # 解冻游戏\n",[153,6463,6464],{"class":155,"line":425},[153,6465,175],{"emptyLinePlaceholder":174},[153,6467,6468],{"class":155,"line":430},[153,6469,6470],{"class":167},"## 重新开始游戏\n",[153,6472,6473,6475,6478,6480,6482,6484],{"class":155,"line":2639},[153,6474,251],{"class":185},[153,6476,6477],{"class":181}," _on_restart_button_pressed",[153,6479,300],{"class":193},[153,6481,273],{"class":185},[153,6483,276],{"class":163},[153,6485,279],{"class":193},[153,6487,6488,6490,6492,6494,6497],{"class":155,"line":2644},[153,6489,4269],{"class":181},[153,6491,300],{"class":193},[153,6493,349],{"class":260},[153,6495,6496],{"class":181},"reload_current_scene",[153,6498,436],{"class":193},[153,6500,6501],{"class":155,"line":2650},[153,6502,175],{"emptyLinePlaceholder":174},[153,6504,6505,6507,6510,6512,6514,6516,6518,6520],{"class":155,"line":2663},[153,6506,251],{"class":185},[153,6508,6509],{"class":181}," _on_game_state",[153,6511,257],{"class":193},[153,6513,4618],{"class":260},[153,6515,270],{"class":193},[153,6517,273],{"class":185},[153,6519,276],{"class":163},[153,6521,279],{"class":193},[153,6523,6524],{"class":155,"line":2670},[153,6525,6526],{"class":167},"    # 游戏结束时，让背景和地面停止滚动\n",[153,6528,6529,6531,6534,6536,6538,6540,6542,6544,6546],{"class":155,"line":2675},[153,6530,291],{"class":159},[153,6532,6533],{"class":189}," new_state",[153,6535,3193],{"class":185},[153,6537,3304],{"class":163},[153,6539,349],{"class":193},[153,6541,3318],{"class":189},[153,6543,349],{"class":193},[153,6545,4457],{"class":159},[153,6547,279],{"class":260},[153,6549,6550,6553,6555,6557,6560,6562,6564,6566],{"class":155,"line":2699},[153,6551,6552],{"class":159},"        $",[153,6554,6129],{"class":3465},[153,6556,349],{"class":193},[153,6558,6559],{"class":189},"autoscroll",[153,6561,349],{"class":193},[153,6563,823],{"class":189},[153,6565,371],{"class":193},[153,6567,2624],{"class":197},[153,6569,6570,6572,6574,6576,6578,6580,6582,6584],{"class":155,"line":2713},[153,6571,6552],{"class":159},[153,6573,6141],{"class":3465},[153,6575,349],{"class":193},[153,6577,6559],{"class":189},[153,6579,349],{"class":193},[153,6581,823],{"class":189},[153,6583,371],{"class":193},[153,6585,2624],{"class":197},[71,6587,3654],{"id":3653},[1195,6589,6590,6599],{},[1198,6591,6592],{},[1201,6593,6594,6597],{},[1204,6595,6596],{},"函数",[1204,6598,1267],{},[1211,6600,6601,6615,6628,6640,6650],{},[1201,6602,6603,6608],{},[1216,6604,6605],{},[36,6606,6607],{},"_ready()",[1216,6609,6610,6611,6614],{},"场景一加载就执行：把状态设为 READY、监听状态变化、",[858,6612,6613],{},"冻结整个游戏","、清空分数",[1201,6616,6617,6622],{},[1216,6618,6619],{},[36,6620,6621],{},"_input()",[1216,6623,6624,6625],{},"监听全局输入：在 READY 状态下，按下 fly 就调用 ",[36,6626,6627],{},"start_game()",[1201,6629,6630,6634],{},[1216,6631,6632],{},[36,6633,6627],{},[1216,6635,6636,6637],{},"把状态切到 PLAYING，",[858,6638,6639],{},"解冻游戏",[1201,6641,6642,6647],{},[1216,6643,6644],{},[36,6645,6646],{},"_on_restart_button_pressed()",[1216,6648,6649],{},"重启按钮：直接重新加载当前场景",[1201,6651,6652,6657],{},[1216,6653,6654],{},[36,6655,6656],{},"_on_game_state()",[1216,6658,6659,6660,6662],{},"监听 GameManager 的 ",[36,6661,5103],{}," 信号，GAME_OVER 时停止背景滚动",[71,6664,6666,6669],{"id":6665},"enginetime_scale-全局时间缩放",[36,6667,6668],{},"Engine.time_scale"," — 全局时间缩放",[144,6671,6673],{"className":146,"code":6672,"language":148,"meta":149,"style":149},"Engine.time_scale = 0  # 完全冻结\nEngine.time_scale = 1  # 正常速度\nEngine.time_scale = 0.5  # 慢动作\n",[36,6674,6675,6691,6706],{"__ignoreMap":149},[153,6676,6677,6680,6682,6684,6686,6688],{"class":155,"line":156},[153,6678,6679],{"class":163},"Engine",[153,6681,349],{"class":193},[153,6683,6305],{"class":189},[153,6685,371],{"class":193},[153,6687,2732],{"class":197},[153,6689,6690],{"class":167},"  # 完全冻结\n",[153,6692,6693,6695,6697,6699,6701,6703],{"class":155,"line":171},[153,6694,6679],{"class":163},[153,6696,349],{"class":193},[153,6698,6305],{"class":189},[153,6700,371],{"class":193},[153,6702,4695],{"class":197},[153,6704,6705],{"class":167},"  # 正常速度\n",[153,6707,6708,6710,6712,6714,6716,6719],{"class":155,"line":178},[153,6709,6679],{"class":163},[153,6711,349],{"class":193},[153,6713,6305],{"class":189},[153,6715,371],{"class":193},[153,6717,6718],{"class":197}," 0.5",[153,6720,6721],{"class":167},"  # 慢动作\n",[15,6723,6724,6725,6727,6728,1147],{},"这是 Godot 的\"全局慢放\u002F暂停\"开关，影响所有用 ",[36,6726,261],{}," 计算的逻辑（包括小鸟下落、水管移动、Timer 等）。",[858,6729,6730],{},"比一个一个写\"如果暂停就别动\"省心得多",[71,6732,6733],{"id":6733},"信号的连接",[15,6735,6736,6739,6740,1147],{},[36,6737,6738],{},"GameManager.state_changed.connect(_on_game_state)"," — 这就是",[858,6741,6742],{},"订阅信号",[1003,6744,6745],{},[15,6746,6747],{},"你也可以在编辑器的【节点】面板里手动连接信号，效果一样。代码连接的好处是更明确、便于版本管理。",[10,6749,5921],{"id":6750},"分数-ui",[15,6752,6753,6755,6756,3217],{},[36,6754,6169],{}," 挂个脚本 ",[36,6757,6758],{},"score_label.gd",[144,6760,6762],{"className":146,"code":6761,"language":148,"meta":149,"style":149},"extends Label\n\nfunc _ready() -> void:\n    text = \"0\"\n    GameManager.score_changed.connect(_on_score_changed)\n\nfunc _on_score_changed(new_score: int) -> void:\n    text = str(new_score)\n",[36,6763,6764,6771,6775,6789,6799,6818,6822,6845],{"__ignoreMap":149},[153,6765,6766,6768],{"class":155,"line":156},[153,6767,160],{"class":159},[153,6769,6770],{"class":163}," Label\n",[153,6772,6773],{"class":155,"line":171},[153,6774,175],{"emptyLinePlaceholder":174},[153,6776,6777,6779,6781,6783,6785,6787],{"class":155,"line":178},[153,6778,251],{"class":185},[153,6780,6236],{"class":181},[153,6782,300],{"class":193},[153,6784,273],{"class":185},[153,6786,276],{"class":163},[153,6788,279],{"class":193},[153,6790,6791,6794,6796],{"class":155,"line":204},[153,6792,6793],{"class":189},"    text",[153,6795,371],{"class":193},[153,6797,6798],{"class":403}," \"0\"\n",[153,6800,6801,6803,6805,6807,6809,6811,6813,6816],{"class":155,"line":225},[153,6802,6254],{"class":163},[153,6804,349],{"class":193},[153,6806,5096],{"class":189},[153,6808,349],{"class":260},[153,6810,6285],{"class":181},[153,6812,257],{"class":193},[153,6814,6815],{"class":189},"_on_score_changed",[153,6817,2660],{"class":193},[153,6819,6820],{"class":155,"line":243},[153,6821,175],{"emptyLinePlaceholder":174},[153,6823,6824,6826,6829,6831,6833,6835,6837,6839,6841,6843],{"class":155,"line":248},[153,6825,251],{"class":185},[153,6827,6828],{"class":181}," _on_score_changed",[153,6830,257],{"class":193},[153,6832,4604],{"class":260},[153,6834,264],{"class":193},[153,6836,4557],{"class":163},[153,6838,270],{"class":193},[153,6840,273],{"class":185},[153,6842,276],{"class":163},[153,6844,279],{"class":193},[153,6846,6847,6849,6851,6854,6856,6858],{"class":155,"line":282},[153,6848,6793],{"class":189},[153,6850,371],{"class":193},[153,6852,6853],{"class":181}," str",[153,6855,257],{"class":193},[153,6857,4604],{"class":189},[153,6859,2660],{"class":193},[30,6861,6862,6871],{},[33,6863,6864,6867,6868,6870],{},[36,6865,6866],{},"_ready"," 里订阅 ",[36,6869,5096],{}," 信号",[33,6872,6873],{},"每次分数变化，自动更新 Label 文本",[15,6875,6876,6879],{},[858,6877,6878],{},"这就是信号解耦的好处"," — Label 不需要主动去问 GameManager 当前几分，GameManager 也不知道有这么个 Label 存在，但分数照样能正确显示。",[10,6881,6882],{"id":6882},"结束面板",[15,6884,6885,6887,6888,6891],{},[36,6886,6178],{}," 默认隐藏（在编辑器把 ",[36,6889,6890],{},"visible"," 取消勾选），监听到 GAME_OVER 状态时才显示。",[15,6893,6894,6895,3217],{},"挂脚本 ",[36,6896,6897],{},"game_over_panel.gd",[144,6899,6901],{"className":146,"code":6900,"language":148,"meta":149,"style":149},"extends Panel\n\n@onready var final_score: Label = $FinalScore\n\nfunc _ready() -> void:\n    visible = false\n    GameManager.state_changed.connect(_on_state_changed)\n\nfunc _on_state_changed(new_state) -> void:\n    if new_state == GameManager.GameState.GAME_OVER:\n        final_score.text = \"得分：%d\" % GameManager.total_score\n        visible = true\n",[36,6902,6903,6910,6914,6935,6939,6953,6963,6982,6986,7005,7025,7055],{"__ignoreMap":149},[153,6904,6905,6907],{"class":155,"line":156},[153,6906,160],{"class":159},[153,6908,6909],{"class":163}," Panel\n",[153,6911,6912],{"class":155,"line":171},[153,6913,175],{"emptyLinePlaceholder":174},[153,6915,6916,6918,6920,6923,6925,6928,6930,6932],{"class":155,"line":178},[153,6917,3447],{"class":181},[153,6919,186],{"class":185},[153,6921,6922],{"class":189}," final_score",[153,6924,264],{"class":193},[153,6926,6927],{"class":163}," Label",[153,6929,371],{"class":193},[153,6931,3462],{"class":159},[153,6933,6934],{"class":3465},"FinalScore\n",[153,6936,6937],{"class":155,"line":204},[153,6938,175],{"emptyLinePlaceholder":174},[153,6940,6941,6943,6945,6947,6949,6951],{"class":155,"line":225},[153,6942,251],{"class":185},[153,6944,6236],{"class":181},[153,6946,300],{"class":193},[153,6948,273],{"class":185},[153,6950,276],{"class":163},[153,6952,279],{"class":193},[153,6954,6955,6958,6960],{"class":155,"line":243},[153,6956,6957],{"class":189},"    visible",[153,6959,371],{"class":193},[153,6961,6962],{"class":159}," false\n",[153,6964,6965,6967,6969,6971,6973,6975,6977,6980],{"class":155,"line":248},[153,6966,6254],{"class":163},[153,6968,349],{"class":193},[153,6970,5103],{"class":189},[153,6972,349],{"class":260},[153,6974,6285],{"class":181},[153,6976,257],{"class":193},[153,6978,6979],{"class":189},"_on_state_changed",[153,6981,2660],{"class":193},[153,6983,6984],{"class":155,"line":282},[153,6985,175],{"emptyLinePlaceholder":174},[153,6987,6988,6990,6993,6995,6997,6999,7001,7003],{"class":155,"line":288},[153,6989,251],{"class":185},[153,6991,6992],{"class":181}," _on_state_changed",[153,6994,257],{"class":193},[153,6996,4618],{"class":260},[153,6998,270],{"class":193},[153,7000,273],{"class":185},[153,7002,276],{"class":163},[153,7004,279],{"class":193},[153,7006,7007,7009,7011,7013,7015,7017,7019,7021,7023],{"class":155,"line":305},[153,7008,291],{"class":159},[153,7010,6533],{"class":189},[153,7012,3193],{"class":185},[153,7014,3304],{"class":163},[153,7016,349],{"class":193},[153,7018,3318],{"class":189},[153,7020,349],{"class":193},[153,7022,4457],{"class":159},[153,7024,279],{"class":260},[153,7026,7027,7030,7032,7034,7036,7039,7042,7045,7048,7050,7052],{"class":155,"line":330},[153,7028,7029],{"class":189},"        final_score",[153,7031,349],{"class":193},[153,7033,5942],{"class":189},[153,7035,371],{"class":193},[153,7037,7038],{"class":403}," \"得分：",[153,7040,7041],{"class":3465},"%d",[153,7043,7044],{"class":403},"\"",[153,7046,7047],{"class":185}," %",[153,7049,3304],{"class":163},[153,7051,349],{"class":193},[153,7053,7054],{"class":189},"total_score\n",[153,7056,7057,7060,7062],{"class":155,"line":335},[153,7058,7059],{"class":189},"        visible",[153,7061,371],{"class":193},[153,7063,7064],{"class":159}," true\n",[30,7066,7067,7072,7075],{},[33,7068,7069,7070],{},"监听 ",[36,7071,5103],{},[33,7073,7074],{},"GAME_OVER 时显示面板 + 填上最终分数",[33,7076,7077,7079,7080],{},[36,7078,7041],{}," 是字符串占位符，等同于 ",[36,7081,7082],{},"str(GameManager.total_score)",[1003,7084,7085],{},[15,7086,7087,7088,59,7090,7092,7093,1147],{},"⚠️ 别忘了把 ",[36,7089,6201],{},[36,7091,6022],{}," 信号连接到 main 场景根节点的 ",[36,7094,6646],{},[10,7096,7097],{"id":7097},"设置主场景",[15,7099,7100,7101,7103],{},"最后把【主场景】改成 ",[36,7102,5906],{},"（这样游戏启动直接到菜单）：",[15,7105,7106],{},"路径：项目 → 项目设置 → 常规 → 运行 → 主场景",[15,7108,7109],{},[19,7110],{"alt":7111,"src":7112},"06-ui-主场景设置","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F06-ui-%E4%B8%BB%E5%9C%BA%E6%99%AF%E8%AE%BE%E7%BD%AE.png",[10,7114,2359],{"id":2359},[15,7116,7117],{},"按下运行键，整个流程应该是：",[1060,7119,7120,7123,7126,7129,7132],{},[33,7121,7122],{},"✅ 启动 → 显示菜单 + 开始按钮",[33,7124,7125],{},"✅ 点击开始 → 切换到游戏场景，画面冻结，等待按键",[33,7127,7128],{},"✅ 按下 fly → 游戏开始，水管生成，小鸟可控",[33,7130,7131],{},"✅ 撞死 → 显示结束面板 + 最终分数",[33,7133,7134],{},"✅ 点重新开始 → 回到游戏初始状态",[15,7136,7137],{},"下一章我们替换美术资源，把这些灰色矩形变成真正的小鸟和水管 🎨",[1083,7139,7140],{},"html pre.shiki code .sTPum, html code.shiki .sTPum{--shiki-default:#1E754F;--shiki-dark:#4D9375}html pre.shiki code .s_NWU, html code.shiki .s_NWU{--shiki-default:#2E8F82;--shiki-dark:#5DA994}html pre.shiki code .s5TCs, html code.shiki .s5TCs{--shiki-default:#AB5959;--shiki-dark:#CB7676}html pre.shiki code .s_xSY, html code.shiki .s_xSY{--shiki-default:#59873A;--shiki-dark:#80A665}html pre.shiki code .si6no, html code.shiki .si6no{--shiki-default:#999999;--shiki-dark:#666666}html pre.shiki code .s8w-G, html code.shiki .s8w-G{--shiki-default:#393A34;--shiki-dark:#DBD7CAEE}html pre.shiki code .spP0B, html code.shiki .spP0B{--shiki-default:#B56959;--shiki-dark:#C98A7D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .snYqZ, html code.shiki .snYqZ{--shiki-default:#A0ADA0;--shiki-dark:#758575DD}html pre.shiki code .s9nN2, html code.shiki .s9nN2{--shiki-default:#B07D48;--shiki-dark:#BD976A}html pre.shiki code .sqbOQ, html code.shiki .sqbOQ{--shiki-default:#2F798A;--shiki-dark:#4C9A91}html pre.shiki code .sfsYZ, html code.shiki .sfsYZ{--shiki-default:#A65E2B;--shiki-dark:#C99076}",{"title":149,"searchDepth":171,"depth":171,"links":7142},[7143,7144,7148,7150,7157,7158,7159,7160],{"id":5931,"depth":171,"text":5931},{"id":5947,"depth":171,"text":7145,"children":7146},"开始菜单 menu.tscn",[7147],{"id":6010,"depth":178,"text":6010},{"id":6085,"depth":171,"text":7149},"游戏主场景 main.tscn",{"id":6211,"depth":171,"text":7151,"children":7152},"主场景脚本 main.gd",[7153,7154,7156],{"id":3653,"depth":178,"text":3654},{"id":6665,"depth":178,"text":7155},"Engine.time_scale — 全局时间缩放",{"id":6733,"depth":178,"text":6733},{"id":6750,"depth":171,"text":5921},{"id":6882,"depth":171,"text":6882},{"id":7097,"depth":171,"text":7097},{"id":2359,"depth":171,"text":2359},{},"\u002Fdevlog\u002Fxggame-bird\u002F06-ui",{"title":5890,"description":1099},"devlog\u002Fxggame-bird\u002F06-ui","MaW5eGhaEWyzREhq8HxUBpIIw5GmRnx7tB25QX3pkfw",{"id":7167,"title":7168,"body":7169,"cover":1097,"date":1098,"description":1099,"extension":1100,"game":1101,"github":1102,"icon":1097,"meta":7752,"navigation":174,"path":6133,"seo":7753,"stem":7754,"toc":174,"__hash__":7755},"devlog\u002Fdevlog\u002Fxggame-bird\u002F07-art-assets.md","替换美术资源",{"type":7,"value":7170,"toc":7738},[7171,7174,7177,7192,7195,7230,7233,7259,7265,7269,7279,7290,7307,7313,7320,7323,7337,7343,7352,7361,7365,7381,7384,7397,7403,7414,7417,7421,7428,7441,7444,7449,7467,7473,7478,7538,7543,7546,7554,7572,7586,7592,7603,7606,7614,7693,7700,7702,7705,7719,7725,7728,7735],[15,7172,7173],{},"到目前为止小鸟还是 Godot 的\"机器人头\"，水管也是个矩形 — 这一章给游戏换上真正的美术资源，从「白板原型」一秒变成「真游戏」。✨",[15,7175,7176],{},"我们要做：",[1060,7178,7179,7182,7185],{},[33,7180,7181],{},"准备 + 导入美术素材",[33,7183,7184],{},"替换小鸟、水管的精灵图",[33,7186,7187,7188,7191],{},"加上",[858,7189,7190],{},"滚动","的地面和背景（这是 Flappy Bird \"横向飞行错觉\"的灵魂）",[10,7193,7194],{"id":7194},"准备美术资源",[1003,7196,7197,7203,7206],{},[15,7198,7199,7200],{},"我用的素材来源：",[153,7201,7202],{},"TODO 素材链接",[15,7204,7205],{},"你也可以自己画、或者从这些免费站找：",[30,7207,7208,7215,7223],{},[33,7209,7210],{},[1124,7211,7214],{"href":7212,"rel":7213},"https:\u002F\u002Fitch.io\u002Fgame-assets\u002Ffree",[1128],"itch.io 免费素材",[33,7216,7217,7222],{},[1124,7218,7221],{"href":7219,"rel":7220},"https:\u002F\u002Fkenney.nl\u002F",[1128],"Kenney.nl","（CC0 免版权）",[33,7224,7225],{},[1124,7226,7229],{"href":7227,"rel":7228},"https:\u002F\u002Fopengameart.org\u002F",[1128],"OpenGameArt",[15,7231,7232],{},"需要这几样：",[30,7234,7235,7241,7247,7253],{},[33,7236,7237,7240],{},[36,7238,7239],{},"bird.png"," — 小鸟（推荐 3 帧动画，扑翅膀更生动）",[33,7242,7243,7246],{},[36,7244,7245],{},"pipe.png"," — 水管",[33,7248,7249,7252],{},[36,7250,7251],{},"ground.png"," — 地面",[33,7254,7255,7258],{},[36,7256,7257],{},"background.png"," — 背景（最好做成左右无缝拼接的）",[15,7260,7261],{},[19,7262],{"alt":7263,"src":7264},"07-art-assets-素材","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F07-art-assets-%E7%B4%A0%E6%9D%90.png",[10,7266,7268],{"id":7267},"导入设置像素美术别变模糊","导入设置：像素美术别变模糊",[15,7270,4054,7271,7274,7275,7278],{},[36,7272,7273],{},".png"," 文件拖进 Godot 项目，默认导入会用",[858,7276,7277],{},"双线性插值","把像素图变得模糊糊的，这不是我们要的复古感觉。",[1003,7280,7281],{},[15,7282,7283,7284,1147],{},"⚠️ 像素美术必须把 ",[858,7285,7286,7287],{},"Filter 设为 ",[36,7288,7289],{},"Nearest",[15,7291,7292,7293,7296,7297,988,7300,7302,7303,7306],{},"选中图片资源 → 上方【导入】标签页 → ",[36,7294,7295],{},"Compress \u002F Mode = Lossless"," + ",[36,7298,7299],{},"Filter",[36,7301,7289],{}," → 点击「",[858,7304,7305],{},"重新导入","」。",[15,7308,7309],{},[19,7310],{"alt":7311,"src":7312},"07-art-assets-导入设置","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F07-art-assets-%E5%AF%BC%E5%85%A5%E8%AE%BE%E7%BD%AE.png",[15,7314,7315,7316,7319],{},"或者一劳永逸：项目设置 → 渲染 → 纹理 → ",[36,7317,7318],{},"Canvas Textures > Default Texture Filter = Nearest","，整个项目所有图片都默认 Nearest。",[10,7321,7322],{"id":7322},"替换小鸟",[15,7324,7325,7326,7328,7329,7331,7332,7334,7335,1147],{},"打开 ",[36,7327,2241],{},"，选中 ",[36,7330,46],{}," 节点，把它的 ",[36,7333,62],{}," 换成 ",[36,7336,7239],{},[15,7338,7339],{},[19,7340],{"alt":7341,"src":7342},"07-art-assets-小鸟替换","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F07-art-assets-%E5%B0%8F%E9%B8%9F%E6%9B%BF%E6%8D%A2.png",[15,7344,7345,7348,7349,7351],{},[858,7346,7347],{},"碰撞体也要对应调整","：选中 ",[36,7350,52],{},"，把矩形缩放到正好包住新的小鸟图。",[1003,7353,7354],{},[15,7355,7356,7357,7360],{},"💡 小贴士：碰撞体可以",[858,7358,7359],{},"略小于图片","（特别是小鸟）。这样玩家会觉得\"我明明擦边过去了\"的判定更宽容，手感更好。这是 Flappy Bird 类游戏的隐藏秘诀之一。",[71,7362,7364],{"id":7363},"可选小鸟扑翅膀动画","（可选）小鸟扑翅膀动画",[15,7366,7367,7368,7370,7371,7334,7373,7376,7377,7380],{},"如果你的 ",[36,7369,7239],{}," 是 3 帧的 sprite sheet，可以把 ",[36,7372,46],{},[36,7374,7375],{},"AnimatedSprite2D","，然后用 ",[36,7378,7379],{},"SpriteFrames"," 资源做帧动画。这部分稍微进阶一点，你可以先跳过 — 不影响后续。",[10,7382,7383],{"id":7383},"替换水管",[15,7385,7325,7386,7388,7389,7391,7392,7394,7395,1147],{},[36,7387,4057],{},"，选中两个 ",[36,7390,46],{},"（上下水管），把它们的 ",[36,7393,62],{}," 都换成 ",[36,7396,7245],{},[15,7398,7399],{},[19,7400],{"alt":7401,"src":7402},"07-art-assets-水管替换","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F07-art-assets-%E6%B0%B4%E7%AE%A1%E6%9B%BF%E6%8D%A2.png",[15,7404,7405,7406,7409,7410,7413],{},"上水管要",[858,7407,7408],{},"垂直翻转","（在 Inspector 里勾选 ",[36,7411,7412],{},"Flip V","）。",[15,7415,7416],{},"碰撞体同样调整到包住水管的大小。",[10,7418,7420],{"id":7419},"滚动地面-背景重头戏","滚动地面 + 背景（重头戏 🎬）",[15,7422,7423,7424,7427],{},"Flappy Bird 的精髓 — 小鸟其实在原地扑腾，",[858,7425,7426],{},"背景和地面在向左移动","，制造出\"鸟在向右飞\"的错觉。",[15,7429,7430,7431,7433,7434,7436,7437,7440],{},"Godot 4.3+ 有专门的 ",[36,7432,6126],{}," 节点，自带 ",[36,7435,6559],{},"（自动滚动）和 ",[36,7438,7439],{},"repeat","（无缝循环），非常好用。",[71,7442,7443],{"id":7443},"添加滚动背景",[15,7445,2408,7446,7448],{},[36,7447,5915],{}," 的根节点下添加：",[30,7450,7451],{},[33,7452,7453,5982,7455,2934,7457],{},[36,7454,6126],{},[36,7456,6129],{},[30,7458,7459],{},[33,7460,7461,5180,7463,988,7465],{},[36,7462,46],{},[36,7464,62],{},[36,7466,7257],{},[15,7468,7469],{},[19,7470],{"alt":7471,"src":7472},"07-art-assets-背景节点","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F07-art-assets-%E8%83%8C%E6%99%AF%E8%8A%82%E7%82%B9.png",[15,7474,94,7475,7477],{},[36,7476,6129],{},"，在 Inspector 里设置：",[1195,7479,7480,7491],{},[1198,7481,7482],{},[1201,7483,7484,7487,7489],{},[1204,7485,7486],{},"属性",[1204,7488,1209],{},[1204,7490,1267],{},[1211,7492,7493,7509,7522],{},[1201,7494,7495,7500,7506],{},[1216,7496,7497],{},[36,7498,7499],{},"Autoscroll > x",[1216,7501,7502,7505],{},[36,7503,7504],{},"-50","（或你喜欢的速度）",[1216,7507,7508],{},"每秒向左移动多少像素",[1201,7510,7511,7516,7519],{},[1216,7512,7513],{},[36,7514,7515],{},"Repeat Size > x",[1216,7517,7518],{},"背景图宽度",[1216,7520,7521],{},"让它无缝循环",[1201,7523,7524,7529,7535],{},[1216,7525,7526],{},[36,7527,7528],{},"Repeat Times",[1216,7530,7531,7534],{},[36,7532,7533],{},"3"," 或更大",[1216,7536,7537],{},"重复多少次（保证屏幕外有备用）",[1003,7539,7540],{},[15,7541,7542],{},"负值表示向左滚动；正值表示向右。",[71,7544,7545],{"id":7545},"添加滚动地面",[15,7547,7548,7549,5982,7551,7553],{},"同样的方式，加一个 ",[36,7550,6126],{},[36,7552,6141],{},"）：",[30,7555,7556],{},[33,7557,7558,5903,7560,2934,7562],{},[36,7559,6126],{},[36,7561,6141],{},[30,7563,7564],{},[33,7565,7566,5180,7568,988,7570],{},[36,7567,46],{},[36,7569,62],{},[36,7571,7251],{},[15,7573,7574,7577,7578,7581,7582,7585],{},[36,7575,7576],{},"Autoscroll.x"," 可以",[858,7579,7580],{},"比背景快","（比如 ",[36,7583,7584],{},"-150","），制造\"近景快、远景慢\"的视差感（这就是 Parallax 这个词的来源）。",[15,7587,7588],{},[19,7589],{"alt":7590,"src":7591},"07-art-assets-地面节点","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F07-art-assets-%E5%9C%B0%E9%9D%A2%E8%8A%82%E7%82%B9.png",[1003,7593,7594],{},[15,7595,7596,7597,7599,7600,7602],{},"💡 这两个节点的 z-index 注意一下：背景 ",[36,7598,6129],{}," 要在最底层（z 最小），地面 ",[36,7601,6141],{}," 在小鸟之上（z 最大，遮住可能的视觉穿帮）。",[10,7604,7605],{"id":7605},"让背景在游戏结束时停下来",[15,7607,7608,7609,7613],{},"还记得 ",[1124,7610,7612],{"href":7611},"\u002Fdevlog\u002Fxggame-bird\u002F06-ui#%E4%B8%BB%E5%9C%BA%E6%99%AF%E8%84%9A%E6%9C%AC-maingd","上一章 main.gd"," 里这两行吗？",[144,7615,7617],{"className":146,"code":7616,"language":148,"meta":149,"style":149},"func _on_game_state(new_state) -> void:\n    if new_state == GameManager.GameState.GAME_OVER:\n        $BG.autoscroll.x = 0\n        $Progress.autoscroll.x = 0\n",[36,7618,7619,7637,7657,7675],{"__ignoreMap":149},[153,7620,7621,7623,7625,7627,7629,7631,7633,7635],{"class":155,"line":156},[153,7622,251],{"class":185},[153,7624,6509],{"class":181},[153,7626,257],{"class":193},[153,7628,4618],{"class":260},[153,7630,270],{"class":193},[153,7632,273],{"class":185},[153,7634,276],{"class":163},[153,7636,279],{"class":193},[153,7638,7639,7641,7643,7645,7647,7649,7651,7653,7655],{"class":155,"line":171},[153,7640,291],{"class":159},[153,7642,6533],{"class":189},[153,7644,3193],{"class":185},[153,7646,3304],{"class":163},[153,7648,349],{"class":193},[153,7650,3318],{"class":189},[153,7652,349],{"class":193},[153,7654,4457],{"class":159},[153,7656,279],{"class":260},[153,7658,7659,7661,7663,7665,7667,7669,7671,7673],{"class":155,"line":178},[153,7660,6552],{"class":159},[153,7662,6129],{"class":3465},[153,7664,349],{"class":193},[153,7666,6559],{"class":189},[153,7668,349],{"class":193},[153,7670,823],{"class":189},[153,7672,371],{"class":193},[153,7674,2624],{"class":197},[153,7676,7677,7679,7681,7683,7685,7687,7689,7691],{"class":155,"line":204},[153,7678,6552],{"class":159},[153,7680,6141],{"class":3465},[153,7682,349],{"class":193},[153,7684,6559],{"class":189},[153,7686,349],{"class":193},[153,7688,823],{"class":189},[153,7690,371],{"class":193},[153,7692,2624],{"class":197},[15,7694,7695,7696,7699],{},"它做的就是：",[858,7697,7698],{},"游戏结束时把背景\u002F地面的滚动速度设为 0","。这样画面整体定住，更有\"撞死\"的仪式感。",[10,7701,2359],{"id":2359},[15,7703,7704],{},"现在游戏应该长这样：",[30,7706,7707,7710,7713,7716],{},[33,7708,7709],{},"✅ 真正的小鸟在画面里扑腾",[33,7711,7712],{},"✅ 水管是绿色（或你的素材颜色）的管子",[33,7714,7715],{},"✅ 背景缓缓向左滚动 + 地面快速向左滚动 → 视差错觉",[33,7717,7718],{},"✅ 撞死瞬间，背景立刻定住",[15,7720,7721],{},[19,7722],{"alt":7723,"src":7724},"07-art-assets-效果","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F07-art-assets-%E6%95%88%E6%9E%9C.png",[15,7726,7727],{},"是不是一瞬间像个游戏了？🎮",[15,7729,7730,7731,7734],{},"下一章给它加上",[858,7732,7733],{},"音效"," — 翅膀扇动、得分、碰撞，最后一点\"灵魂\"。",[1083,7736,7737],{},"html pre.shiki code .s5TCs, html code.shiki .s5TCs{--shiki-default:#AB5959;--shiki-dark:#CB7676}html pre.shiki code .s_xSY, html code.shiki .s_xSY{--shiki-default:#59873A;--shiki-dark:#80A665}html pre.shiki code .si6no, html code.shiki .si6no{--shiki-default:#999999;--shiki-dark:#666666}html pre.shiki code .s8w-G, html code.shiki .s8w-G{--shiki-default:#393A34;--shiki-dark:#DBD7CAEE}html pre.shiki code .s_NWU, html code.shiki .s_NWU{--shiki-default:#2E8F82;--shiki-dark:#5DA994}html pre.shiki code .sTPum, html code.shiki .sTPum{--shiki-default:#1E754F;--shiki-dark:#4D9375}html pre.shiki code .s9nN2, html code.shiki .s9nN2{--shiki-default:#B07D48;--shiki-dark:#BD976A}html pre.shiki code .sfsYZ, html code.shiki .sfsYZ{--shiki-default:#A65E2B;--shiki-dark:#C99076}html pre.shiki code .sqbOQ, html code.shiki .sqbOQ{--shiki-default:#2F798A;--shiki-dark:#4C9A91}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":149,"searchDepth":171,"depth":171,"links":7739},[7740,7741,7742,7745,7746,7750,7751],{"id":7194,"depth":171,"text":7194},{"id":7267,"depth":171,"text":7268},{"id":7322,"depth":171,"text":7322,"children":7743},[7744],{"id":7363,"depth":178,"text":7364},{"id":7383,"depth":171,"text":7383},{"id":7419,"depth":171,"text":7420,"children":7747},[7748,7749],{"id":7443,"depth":178,"text":7443},{"id":7545,"depth":178,"text":7545},{"id":7605,"depth":171,"text":7605},{"id":2359,"depth":171,"text":2359},{},{"title":7168,"description":1099},"devlog\u002Fxggame-bird\u002F07-art-assets","ELNEWo2f5dW9OT0EJ3dApq6f3iJy7cbJJ_itwQbN3ls",{"id":7757,"title":7758,"body":7759,"cover":1097,"date":1098,"description":1099,"extension":1100,"game":1101,"github":1102,"icon":1097,"meta":8584,"navigation":174,"path":8585,"seo":8586,"stem":8587,"toc":174,"__hash__":8588},"devlog\u002Fdevlog\u002Fxggame-bird\u002F08-sound.md","音效 SoundManager",{"type":7,"value":7760,"toc":8564},[7761,7764,7787,7790,7825,7832,7852,7858,7871,7879,7886,7908,7912,7918,7920,7963,7969,7973,7976,7995,8001,8005,8011,8165,8180,8184,8189,8212,8218,8227,8230,8233,8239,8289,8295,8400,8406,8491,8493,8496,8507,8520,8524,8534,8555,8558,8561],[15,7762,7763],{},"游戏没声音就像炒菜没盐 — 没那个味儿。这一章加上：",[30,7765,7766,7773,7780],{},[33,7767,7768,7769,7772],{},"🪶 ",[858,7770,7771],{},"翅膀扇动声","（小鸟跳跃时）",[33,7774,7775,7776,7779],{},"⭐ ",[858,7777,7778],{},"得分声","（穿过水管时）",[33,7781,7782,7783,7786],{},"💥 ",[858,7784,7785],{},"碰撞声","（撞死时）",[10,7788,7789],{"id":7789},"准备音效文件",[1003,7791,7792,7797,7800],{},[15,7793,7794,7795],{},"我用的音效来源：",[153,7796,7202],{},[15,7798,7799],{},"免费音效推荐：",[30,7801,7802,7810,7817],{},[33,7803,7804,7809],{},[1124,7805,7808],{"href":7806,"rel":7807},"https:\u002F\u002Ffreesound.org\u002F",[1128],"freesound.org","（CC 许可）",[33,7811,7812],{},[1124,7813,7816],{"href":7814,"rel":7815},"https:\u002F\u002Fwww.zapsplat.com\u002F",[1128],"zapsplat.com",[33,7818,7819,7824],{},[1124,7820,7823],{"href":7821,"rel":7822},"https:\u002F\u002Fsfxr.me\u002F",[1128],"sfxr"," — 在线生成复古 8-bit 音效，超适合 Flappy Bird",[15,7826,7827,7828,7831],{},"需要 3 个文件，放到 ",[36,7829,7830],{},"audio\u002F"," 目录下：",[30,7833,7834,7840,7846],{},[33,7835,7836,7839],{},[36,7837,7838],{},"wing.wav"," — 扑翅膀",[33,7841,7842,7845],{},[36,7843,7844],{},"score.wav"," — 得分",[33,7847,7848,7851],{},[36,7849,7850],{},"strike.wav"," — 撞击",[15,7853,7854],{},[19,7855],{"alt":7856,"src":7857},"08-sound-音效文件","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F08-sound-%E9%9F%B3%E6%95%88%E6%96%87%E4%BB%B6.png",[1003,7859,7860],{},[15,7861,7862,7863,7866,7867,7870],{},"💡 推荐用 ",[36,7864,7865],{},".wav"," 而不是 ",[36,7868,7869],{},".mp3","：体积大一点，但延迟低、循环不卡顿。短音效用 wav 是标配。",[10,7872,7874,7875,7878],{"id":7873},"soundmanager-是个场景不是脚本","SoundManager 是个",[858,7876,7877],{},"场景","，不是脚本",[15,7880,7881,7882,7885],{},"和 GameManager 不一样，SoundManager 我们用 ",[858,7883,7884],{},"场景（.tscn）"," 来做，而不是单纯的脚本。",[15,7887,7888,7891,7892,7895,7896,7899,7900,7903,7904,7907],{},[858,7889,7890],{},"为什么？"," 因为每个音效都需要一个 ",[36,7893,7894],{},"AudioStreamPlayer"," 节点 — 与其在脚本里 ",[36,7897,7898],{},"new AudioStreamPlayer()","，不如直接在场景里把所有 player 节点摆好，",[858,7901,7902],{},"音效文件也提前在 Inspector 里预设好","，调用时直接 ",[36,7905,7906],{},".play()"," 就行。",[10,7909,7911],{"id":7910},"创建-soundmanager-场景","创建 SoundManager 场景",[15,7913,7914,7915,1147],{},"新建场景，保存为 ",[36,7916,7917],{},"scenes\u002Fautoload\u002Fsound_manager.tscn",[15,7919,2206],{},[30,7921,7922],{},[33,7923,7924,5982,7927,7929,7930],{},[36,7925,7926],{},"Node",[36,7928,3361],{},"）— 根节点\n",[30,7931,7932],{},[33,7933,7934,5982,7936,7939,7940],{},[36,7935,7926],{},[36,7937,7938],{},"SFX","）— 音效组\n",[30,7941,7942,7949,7956],{},[33,7943,7944,5982,7946,3028],{},[36,7945,7894],{},[36,7947,7948],{},"wing_sfx",[33,7950,7951,5982,7953,3028],{},[36,7952,7894],{},[36,7954,7955],{},"score_sfx",[33,7957,7958,5982,7960,3028],{},[36,7959,7894],{},[36,7961,7962],{},"strike_sfx",[15,7964,7965],{},[19,7966],{"alt":7967,"src":7968},"08-sound-scene-结构","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F08-sound-scene-%E7%BB%93%E6%9E%84.png",[71,7970,7972],{"id":7971},"每个-audiostreamplayer-的设置","每个 AudioStreamPlayer 的设置",[15,7974,7975],{},"选中每个 player，在右侧 Inspector 里：",[30,7977,7978,7989],{},[33,7979,7980,7983,7984,4058,7986,7988],{},[858,7981,7982],{},"Stream","：拖入对应的音效文件（如 ",[36,7985,7838],{},[36,7987,7948],{}," 的 Stream 槽）",[33,7990,7991,7994],{},[858,7992,7993],{},"Volume dB","：根据音量调整（-6 dB 是温和的衰减，-12 dB 是明显的降低）",[15,7996,7997],{},[19,7998],{"alt":7999,"src":8000},"08-sound-audiostreamplayer","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F08-sound-audiostreamplayer.png",[10,8002,8004],{"id":8003},"soundmanager-脚本","SoundManager 脚本",[15,8006,8007,8008,3217],{},"给根节点挂脚本 ",[36,8009,8010],{},"sound_manager.gd",[144,8012,8014],{"className":146,"code":8013,"language":148,"meta":149,"style":149},"extends Node\n\n# 在检查器里预载好你的音效文件\n@onready var score_sfx: AudioStreamPlayer = $SFX\u002Fscore_sfx\n@onready var strike_sfx: AudioStreamPlayer = $SFX\u002Fstrike_sfx\n@onready var wing_sfx: AudioStreamPlayer = $SFX\u002Fwing_sfx\n\nfunc play_score():\n    score_sfx.play()\n\nfunc play_die():\n    strike_sfx.play()\n\nfunc play_wing():\n    wing_sfx.play()\n",[36,8015,8016,8022,8026,8031,8052,8072,8092,8096,8105,8117,8121,8130,8141,8145,8154],{"__ignoreMap":149},[153,8017,8018,8020],{"class":155,"line":156},[153,8019,160],{"class":159},[153,8021,4490],{"class":163},[153,8023,8024],{"class":155,"line":171},[153,8025,175],{"emptyLinePlaceholder":174},[153,8027,8028],{"class":155,"line":178},[153,8029,8030],{"class":167},"# 在检查器里预载好你的音效文件\n",[153,8032,8033,8035,8037,8040,8042,8045,8047,8049],{"class":155,"line":204},[153,8034,3447],{"class":181},[153,8036,186],{"class":185},[153,8038,8039],{"class":189}," score_sfx",[153,8041,264],{"class":193},[153,8043,8044],{"class":163}," AudioStreamPlayer",[153,8046,371],{"class":193},[153,8048,3462],{"class":159},[153,8050,8051],{"class":3465},"SFX\u002Fscore_sfx\n",[153,8053,8054,8056,8058,8061,8063,8065,8067,8069],{"class":155,"line":225},[153,8055,3447],{"class":181},[153,8057,186],{"class":185},[153,8059,8060],{"class":189}," strike_sfx",[153,8062,264],{"class":193},[153,8064,8044],{"class":163},[153,8066,371],{"class":193},[153,8068,3462],{"class":159},[153,8070,8071],{"class":3465},"SFX\u002Fstrike_sfx\n",[153,8073,8074,8076,8078,8081,8083,8085,8087,8089],{"class":155,"line":243},[153,8075,3447],{"class":181},[153,8077,186],{"class":185},[153,8079,8080],{"class":189}," wing_sfx",[153,8082,264],{"class":193},[153,8084,8044],{"class":163},[153,8086,371],{"class":193},[153,8088,3462],{"class":159},[153,8090,8091],{"class":3465},"SFX\u002Fwing_sfx\n",[153,8093,8094],{"class":155,"line":248},[153,8095,175],{"emptyLinePlaceholder":174},[153,8097,8098,8100,8103],{"class":155,"line":282},[153,8099,251],{"class":185},[153,8101,8102],{"class":181}," play_score",[153,8104,4639],{"class":193},[153,8106,8107,8110,8112,8115],{"class":155,"line":288},[153,8108,8109],{"class":189},"    score_sfx",[153,8111,349],{"class":260},[153,8113,8114],{"class":181},"play",[153,8116,436],{"class":193},[153,8118,8119],{"class":155,"line":305},[153,8120,175],{"emptyLinePlaceholder":174},[153,8122,8123,8125,8128],{"class":155,"line":330},[153,8124,251],{"class":185},[153,8126,8127],{"class":181}," play_die",[153,8129,4639],{"class":193},[153,8131,8132,8135,8137,8139],{"class":155,"line":335},[153,8133,8134],{"class":189},"    strike_sfx",[153,8136,349],{"class":260},[153,8138,8114],{"class":181},[153,8140,436],{"class":193},[153,8142,8143],{"class":155,"line":341},[153,8144,175],{"emptyLinePlaceholder":174},[153,8146,8147,8149,8152],{"class":155,"line":362},[153,8148,251],{"class":185},[153,8150,8151],{"class":181}," play_wing",[153,8153,4639],{"class":193},[153,8155,8156,8159,8161,8163],{"class":155,"line":377},[153,8157,8158],{"class":189},"    wing_sfx",[153,8160,349],{"class":260},[153,8162,8114],{"class":181},[153,8164,436],{"class":193},[30,8166,8167,8173],{},[33,8168,8169,8172],{},[36,8170,8171],{},"@onready var xxx = $SFX\u002Fxxx"," — 拿到子节点的引用",[33,8174,8175,8176,8179],{},"方法本身就一行：调用对应 player 的 ",[36,8177,8178],{},"play()"," 即可",[10,8181,8183],{"id":8182},"注册为-autoload","注册为 Autoload",[15,8185,8186,8187,3217],{},"和 GameManager 一样，",[858,8188,4865],{},[1195,8190,8191,8199],{},[1198,8192,8193],{},[1201,8194,8195,8197],{},[1204,8196,4878],{},[1204,8198,4887],{},[1211,8200,8201],{},[1201,8202,8203,8208],{},[1216,8204,8205],{},[36,8206,8207],{},"res:\u002F\u002Fscenes\u002Fautoload\u002Fsound_manager.tscn",[1216,8209,8210],{},[36,8211,3361],{},[15,8213,8214],{},[19,8215],{"alt":8216,"src":8217},"08-sound-autoload","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F08-sound-autoload.png",[1003,8219,8220],{},[15,8221,8222,8223,8226],{},"⚠️ 注意这里加载的是 ",[858,8224,8225],{},"场景文件 (.tscn)","，不是脚本 (.gd)。Godot 的 Autoload 同时支持两者。",[10,8228,8229],{"id":8229},"在游戏里调用",[15,8231,8232],{},"现在任何节点都能直接喊 SoundManager 一声 — 在合适的位置加上音效调用：",[71,8234,8236,8237,3028],{"id":8235},"小鸟跳跃时birdgd","小鸟跳跃时（",[36,8238,2411],{},[144,8240,8242],{"className":146,"code":8241,"language":148,"meta":149,"style":149},"if Input.is_action_just_pressed(\"fly\"):\n    velocity.y = jump_force\n    SoundManager.play_wing()  # 👈 加这一行\n",[36,8243,8244,8262,8274],{"__ignoreMap":149},[153,8245,8246,8248,8250,8252,8254,8256,8258,8260],{"class":155,"line":156},[153,8247,747],{"class":159},[153,8249,393],{"class":163},[153,8251,349],{"class":260},[153,8253,398],{"class":181},[153,8255,257],{"class":193},[153,8257,404],{"class":403},[153,8259,270],{"class":193},[153,8261,279],{"class":260},[153,8263,8264,8266,8268,8270,8272],{"class":155,"line":171},[153,8265,760],{"class":189},[153,8267,349],{"class":193},[153,8269,352],{"class":189},[153,8271,371],{"class":193},[153,8273,422],{"class":189},[153,8275,8276,8279,8281,8284,8286],{"class":155,"line":178},[153,8277,8278],{"class":163},"    SoundManager",[153,8280,349],{"class":260},[153,8282,8283],{"class":181},"play_wing",[153,8285,300],{"class":193},[153,8287,8288],{"class":167},"  # 👈 加这一行\n",[71,8290,8292,8293,3028],{"id":8291},"穿过水管得分时pillar_pairgd","穿过水管得分时（",[36,8294,5327],{},[144,8296,8298],{"className":146,"code":8297,"language":148,"meta":149,"style":149},"func _on_goal_body_entered(body: Node2D) -> void:\n    if body.name == \"Bird\":\n        SoundManager.play_score()  # 👈 加这一行\n        if GameManager.has_method(\"add_score\"):\n            GameManager.add_score(1)\n        goal.set_deferred(\"monitoring\", false)\n",[36,8299,8300,8322,8338,8350,8368,8382],{"__ignoreMap":149},[153,8301,8302,8304,8306,8308,8310,8312,8314,8316,8318,8320],{"class":155,"line":156},[153,8303,251],{"class":185},[153,8305,3564],{"class":181},[153,8307,257],{"class":193},[153,8309,3165],{"class":260},[153,8311,264],{"class":193},[153,8313,3170],{"class":163},[153,8315,270],{"class":193},[153,8317,273],{"class":185},[153,8319,276],{"class":163},[153,8321,279],{"class":193},[153,8323,8324,8326,8328,8330,8332,8334,8336],{"class":155,"line":171},[153,8325,291],{"class":159},[153,8327,3185],{"class":189},[153,8329,349],{"class":193},[153,8331,3190],{"class":189},[153,8333,3193],{"class":185},[153,8335,3196],{"class":403},[153,8337,279],{"class":260},[153,8339,8340,8342,8344,8346,8348],{"class":155,"line":178},[153,8341,3330],{"class":163},[153,8343,349],{"class":260},[153,8345,3919],{"class":181},[153,8347,300],{"class":193},[153,8349,8288],{"class":167},[153,8351,8352,8354,8356,8358,8360,8362,8364,8366],{"class":155,"line":204},[153,8353,3847],{"class":159},[153,8355,3304],{"class":163},[153,8357,349],{"class":260},[153,8359,3932],{"class":181},[153,8361,257],{"class":193},[153,8363,3937],{"class":403},[153,8365,270],{"class":193},[153,8367,279],{"class":260},[153,8369,8370,8372,8374,8376,8378,8380],{"class":155,"line":225},[153,8371,3946],{"class":163},[153,8373,349],{"class":260},[153,8375,3951],{"class":181},[153,8377,257],{"class":193},[153,8379,3956],{"class":197},[153,8381,2660],{"class":193},[153,8383,8384,8386,8388,8390,8392,8394,8396,8398],{"class":155,"line":243},[153,8385,3627],{"class":189},[153,8387,349],{"class":260},[153,8389,3632],{"class":181},[153,8391,257],{"class":193},[153,8393,3637],{"class":403},[153,8395,2821],{"class":193},[153,8397,3642],{"class":159},[153,8399,2660],{"class":193},[71,8401,8403,8404,3028],{"id":8402},"撞死时killzonegd","撞死时（",[36,8405,5230],{},[144,8407,8409],{"className":146,"code":8408,"language":148,"meta":149,"style":149},"func _on_body_entered(body: Node2D) -> void:\n    if body.name == \"Bird\" and GameManager.current_state == GameManager.GameState.PLAYING:\n        SoundManager.play_die()  # 👈 加这一行\n        GameManager.game_over()\n",[36,8410,8411,8433,8469,8481],{"__ignoreMap":149},[153,8412,8413,8415,8417,8419,8421,8423,8425,8427,8429,8431],{"class":155,"line":156},[153,8414,251],{"class":185},[153,8416,3160],{"class":181},[153,8418,257],{"class":193},[153,8420,3165],{"class":260},[153,8422,264],{"class":193},[153,8424,3170],{"class":163},[153,8426,270],{"class":193},[153,8428,273],{"class":185},[153,8430,276],{"class":163},[153,8432,279],{"class":193},[153,8434,8435,8437,8439,8441,8443,8445,8447,8449,8451,8453,8455,8457,8459,8461,8463,8465,8467],{"class":155,"line":171},[153,8436,291],{"class":159},[153,8438,3185],{"class":189},[153,8440,349],{"class":193},[153,8442,3190],{"class":189},[153,8444,3193],{"class":185},[153,8446,3196],{"class":403},[153,8448,3301],{"class":185},[153,8450,3304],{"class":163},[153,8452,349],{"class":193},[153,8454,3309],{"class":189},[153,8456,3193],{"class":185},[153,8458,3304],{"class":163},[153,8460,349],{"class":193},[153,8462,3318],{"class":189},[153,8464,349],{"class":193},[153,8466,3323],{"class":159},[153,8468,279],{"class":260},[153,8470,8471,8473,8475,8477,8479],{"class":155,"line":178},[153,8472,3330],{"class":163},[153,8474,349],{"class":260},[153,8476,3335],{"class":181},[153,8478,300],{"class":193},[153,8480,8288],{"class":167},[153,8482,8483,8485,8487,8489],{"class":155,"line":204},[153,8484,3342],{"class":163},[153,8486,349],{"class":260},[153,8488,3347],{"class":181},[153,8490,436],{"class":193},[10,8492,2359],{"id":2359},[15,8494,8495],{},"戴上耳机，应该能听到：",[30,8497,8498,8501,8504],{},[33,8499,8500],{},"✅ 按 fly → 翅膀扇动声",[33,8502,8503],{},"✅ 穿过水管 → 清脆的得分音",[33,8505,8506],{},"✅ 撞死 → 一声闷响",[15,8508,8509,8510,8513,8514,8516,8517,1147],{},"如果声音太大\u002F太小，去 ",[36,8511,8512],{},"sound_manager.tscn"," 里调 ",[36,8515,7993],{},"，",[858,8518,8519],{},"不用改代码",[10,8521,8523],{"id":8522},"可选背景音乐-bgm","（可选）背景音乐 BGM",[15,8525,8526,8527,8529,8530,8533],{},"你可以再加一个 ",[36,8528,7894],{},"（命名 ",[36,8531,8532],{},"BGM","），在 Inspector 里：",[30,8535,8536,8541,8547],{},[33,8537,8538,8540],{},[858,8539,7982],{},"：拖入背景音乐",[33,8542,8543,8546],{},[858,8544,8545],{},"Autoplay","：✅ 勾选（场景一加载就播放）",[33,8548,8549,8552,8553],{},[858,8550,8551],{},"Loop","：在 Stream 的导入设置里勾选 ",[36,8554,8551],{},[15,8556,8557],{},"这样游戏一开始就有背景音乐。",[15,8559,8560],{},"下一章我们打包导出游戏，发布到 itch.io！🚀",[1083,8562,8563],{},"html pre.shiki code .sTPum, html code.shiki .sTPum{--shiki-default:#1E754F;--shiki-dark:#4D9375}html pre.shiki code .s_NWU, html code.shiki .s_NWU{--shiki-default:#2E8F82;--shiki-dark:#5DA994}html pre.shiki code .snYqZ, html code.shiki .snYqZ{--shiki-default:#A0ADA0;--shiki-dark:#758575DD}html pre.shiki code .s_xSY, html code.shiki .s_xSY{--shiki-default:#59873A;--shiki-dark:#80A665}html pre.shiki code .s5TCs, html code.shiki .s5TCs{--shiki-default:#AB5959;--shiki-dark:#CB7676}html pre.shiki code .s9nN2, html code.shiki .s9nN2{--shiki-default:#B07D48;--shiki-dark:#BD976A}html pre.shiki code .si6no, html code.shiki .si6no{--shiki-default:#999999;--shiki-dark:#666666}html pre.shiki code .sfsYZ, html code.shiki .sfsYZ{--shiki-default:#A65E2B;--shiki-dark:#C99076}html pre.shiki code .s8w-G, html code.shiki .s8w-G{--shiki-default:#393A34;--shiki-dark:#DBD7CAEE}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spP0B, html code.shiki .spP0B{--shiki-default:#B56959;--shiki-dark:#C98A7D}html pre.shiki code .sqbOQ, html code.shiki .sqbOQ{--shiki-default:#2F798A;--shiki-dark:#4C9A91}",{"title":149,"searchDepth":171,"depth":171,"links":8565},[8566,8567,8569,8572,8573,8574,8582,8583],{"id":7789,"depth":171,"text":7789},{"id":7873,"depth":171,"text":8568},"SoundManager 是个场景，不是脚本",{"id":7910,"depth":171,"text":7911,"children":8570},[8571],{"id":7971,"depth":178,"text":7972},{"id":8003,"depth":171,"text":8004},{"id":8182,"depth":171,"text":8183},{"id":8229,"depth":171,"text":8229,"children":8575},[8576,8578,8580],{"id":8235,"depth":178,"text":8577},"小鸟跳跃时（bird.gd）",{"id":8291,"depth":178,"text":8579},"穿过水管得分时（pillar_pair.gd）",{"id":8402,"depth":178,"text":8581},"撞死时（killzone.gd）",{"id":2359,"depth":171,"text":2359},{"id":8522,"depth":171,"text":8523},{},"\u002Fdevlog\u002Fxggame-bird\u002F08-sound",{"title":7758,"description":1099},"devlog\u002Fxggame-bird\u002F08-sound","-xino61vnvQDm2Uq8TfxIUqUrf99NjngMPZDav9wZr8",{"id":8590,"title":8591,"body":8592,"cover":1097,"date":1098,"description":1099,"extension":1100,"game":1101,"github":1102,"icon":1097,"meta":9414,"navigation":174,"path":9415,"seo":9416,"stem":9417,"toc":174,"__hash__":9418},"devlog\u002Fdevlog\u002Fxggame-bird\u002F09-export.md","导出 + 发布到 itch.io",{"type":7,"value":8593,"toc":9395},[8594,8600,8603,8622,8635,8639,8642,8649,8652,8658,8662,8665,8676,8682,8685,8768,8771,8803,8812,8815,8821,8824,8830,8884,8891,8897,8901,8906,8914,8917,8923,8929,8932,9021,9025,9031,9070,9081,9095,9101,9107,9111,9117,9160,9166,9169,9175,9201,9208,9212,9215,9278,9282,9289,9309,9316,9320,9323,9347,9350,9352,9356,9359,9380,9389,9392],[15,8595,8596,8597,1147],{},"游戏做完了！最激动人心的一步 — ",[858,8598,8599],{},"让别人玩到你的游戏",[15,8601,8602],{},"我们要做两件事：",[1060,8604,8605,8611],{},[33,8606,8607,8610],{},[858,8608,8609],{},"导出为 Web 版（HTML5）"," — 在浏览器里就能玩",[33,8612,8613,8621],{},[858,8614,8615,8616],{},"发布到 ",[1124,8617,8620],{"href":8618,"rel":8619},"https:\u002F\u002Fitch.io",[1128],"itch.io"," — 全世界都能搜到、能玩",[1003,8623,8624],{},[15,8625,8626,8627,8630,8631,8634],{},"还记得我们在 ",[1124,8628,8629],{"href":1325},"第 1 章","选了「兼容」渲染器吗？就是为了",[858,8632,8633],{},"这一步","！🎉",[10,8636,8638],{"id":8637},"安装-web-导出模板","安装 Web 导出模板",[15,8640,8641],{},"第一次导出 Web 版需要下载导出模板：",[15,8643,8644,8645,8648],{},"路径：编辑器 → ",[858,8646,8647],{},"管理导出模板"," (Manage Export Templates)",[15,8650,8651],{},"如果是首次安装，点击「下载并安装」，等几分钟下载完成即可。",[15,8653,8654],{},[19,8655],{"alt":8656,"src":8657},"09-export-导出模板","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F09-export-%E5%AF%BC%E5%87%BA%E6%A8%A1%E6%9D%BF.png",[10,8659,8661],{"id":8660},"配置-web-导出","配置 Web 导出",[15,8663,8664],{},"路径：项目 → 导出 (Export)",[15,8666,8667,8668,8671,8672,8675],{},"点击「",[858,8669,8670],{},"添加","」→ 选择 ",[858,8673,8674],{},"Web"," 平台。",[15,8677,8678],{},[19,8679],{"alt":8680,"src":8681},"09-export-配置","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F09-export-%E9%85%8D%E7%BD%AE.png",[71,8683,8684],{"id":8684},"关键设置",[1195,8686,8687,8700],{},[1198,8688,8689],{},[1201,8690,8691,8694,8697],{},[1204,8692,8693],{},"项",[1204,8695,8696],{},"推荐值",[1204,8698,8699],{},"说明",[1211,8701,8702,8715,8727,8742,8755],{},[1201,8703,8704,8709,8712],{},[1216,8705,8706],{},[36,8707,8708],{},"Custom HTML Shell",[1216,8710,8711],{},"留空",[1216,8713,8714],{},"用 Godot 默认模板即可",[1201,8716,8717,8722,8724],{},[1216,8718,8719],{},[36,8720,8721],{},"Head Include",[1216,8723,8711],{},[1216,8725,8726],{},"如果想自定义页面 meta，可以加",[1201,8728,8729,8734,8739],{},[1216,8730,8731],{},[36,8732,8733],{},"Canvas Resize Policy",[1216,8735,8736],{},[36,8737,8738],{},"Adaptive",[1216,8740,8741],{},"自适应窗口大小",[1201,8743,8744,8749,8752],{},[1216,8745,8746],{},[36,8747,8748],{},"Focus Canvas on Start",[1216,8750,8751],{},"✅ 启用",[1216,8753,8754],{},"启动就聚焦，方便按键响应",[1201,8756,8757,8762,8765],{},[1216,8758,8759],{},[36,8760,8761],{},"Experimental Virtual Keyboard",[1216,8763,8764],{},"看需求",[1216,8766,8767],{},"移动端用得上",[10,8769,8770],{"id":8770},"导出游戏",[1060,8772,8773,8779,8786,8796],{},[33,8774,8775,8776,8778],{},"在【导出】面板选中 ",[858,8777,8674],{}," 配置",[33,8780,8781,8782,8785],{},"点击右下角「",[858,8783,8784],{},"导出项目","」",[33,8787,8788,8789,8792,8793,3028],{},"选择一个",[858,8790,8791],{},"空文件夹","保存（比如 ",[36,8794,8795],{},"build\u002Fweb\u002F",[33,8797,8798,8799,8802],{},"文件名填 ",[36,8800,8801],{},"index.html","（itch.io 上传约定）",[1003,8804,8805],{},[15,8806,8807,8808,8811],{},"⚠️ ",[858,8809,8810],{},"必须导出到空文件夹"," — 否则会和已有文件混在一起。",[15,8813,8814],{},"导出后这个目录会出现一堆文件：",[144,8816,8819],{"className":8817,"code":8818,"language":5942},[5940],"build\u002Fweb\u002F\n├── index.html\n├── index.js\n├── index.pck       ← 游戏资源\n├── index.wasm      ← 游戏引擎（WebAssembly）\n├── index.audio.worklet.js\n└── index.icon.png  ← 图标（可选）\n",[36,8820,8818],{"__ignoreMap":149},[10,8822,8823],{"id":8823},"本地测试",[15,8825,8826,8829],{},[858,8827,8828],{},"Web 导出无法直接双击 index.html 打开","（浏览器安全策略会阻止 wasm 加载）。需要起一个本地 server：",[144,8831,8835],{"className":8832,"code":8833,"language":8834,"meta":149,"style":149},"language-bash shiki shiki-themes vitesse-light vitesse-dark","# Python 自带 server（最方便）\ncd build\u002Fweb\npython -m http.server 8000\n\n# 或者 Node.js\nnpx serve build\u002Fweb\n","bash",[36,8836,8837,8842,8851,8865,8869,8874],{"__ignoreMap":149},[153,8838,8839],{"class":155,"line":156},[153,8840,8841],{"class":167},"# Python 自带 server（最方便）\n",[153,8843,8844,8848],{"class":155,"line":171},[153,8845,8847],{"class":8846},"sHLBJ","cd",[153,8849,8850],{"class":403}," build\u002Fweb\n",[153,8852,8853,8856,8859,8862],{"class":155,"line":178},[153,8854,8855],{"class":181},"python",[153,8857,8858],{"class":3465}," -m",[153,8860,8861],{"class":403}," http.server",[153,8863,8864],{"class":197}," 8000\n",[153,8866,8867],{"class":155,"line":204},[153,8868,175],{"emptyLinePlaceholder":174},[153,8870,8871],{"class":155,"line":225},[153,8872,8873],{"class":167},"# 或者 Node.js\n",[153,8875,8876,8879,8882],{"class":155,"line":243},[153,8877,8878],{"class":181},"npx",[153,8880,8881],{"class":403}," serve",[153,8883,8850],{"class":403},[15,8885,8886,8887,8890],{},"打开浏览器访问 ",[36,8888,8889],{},"http:\u002F\u002Flocalhost:8000","，能玩就说明导出成功。",[15,8892,8893],{},[19,8894],{"alt":8895,"src":8896},"09-export-本地测试","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F09-export-%E6%9C%AC%E5%9C%B0%E6%B5%8B%E8%AF%95.png",[10,8898,8900],{"id":8899},"注册-itchio","注册 itch.io",[1003,8902,8903],{},[15,8904,8905],{},"已有账号可以跳过这一步。",[15,8907,8908,8909,8913],{},"去 ",[1124,8910,8620],{"href":8911,"rel":8912},"https:\u002F\u002Fitch.io\u002Fregister",[1128]," 注册账号，确认邮箱即可。",[10,8915,8916],{"id":8916},"上传游戏",[15,8918,8919,8920,1147],{},"登录后，右上角头像 → ",[858,8921,8922],{},"Upload new project",[15,8924,8925],{},[19,8926],{"alt":8927,"src":8928},"09-export-上传页面","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F09-export-%E4%B8%8A%E4%BC%A0%E9%A1%B5%E9%9D%A2.png",[71,8930,8931],{"id":8931},"填写项目信息",[1195,8933,8934,8944],{},[1198,8935,8936],{},[1201,8937,8938,8941],{},[1204,8939,8940],{},"字段",[1204,8942,8943],{},"填写建议",[1211,8945,8946,8956,8964,8972,8983,8996,9010],{},[1201,8947,8948,8951],{},[1216,8949,8950],{},"Title",[1216,8952,8953,8954,3028],{},"游戏名（如 ",[36,8955,1101],{},[1201,8957,8958,8961],{},[1216,8959,8960],{},"Project URL",[1216,8962,8963],{},"URL slug（自动生成，可改）",[1201,8965,8966,8969],{},[1216,8967,8968],{},"Short description",[1216,8970,8971],{},"一句话简介（搜索结果会显示）",[1201,8973,8974,8977],{},[1216,8975,8976],{},"Classification",[1216,8978,8979,8980],{},"选 ",[36,8981,8982],{},"Games",[1201,8984,8985,8988],{},[1216,8986,8987],{},"Kind of project",[1216,8989,8990,8995],{},[858,8991,8979,8992],{},[36,8993,8994],{},"HTML"," ← 关键！",[1201,8997,8998,9001],{},[1216,8999,9000],{},"Release status",[1216,9002,9003,9006,9007],{},[36,9004,9005],{},"Released"," 或 ",[36,9008,9009],{},"Prototype",[1201,9011,9012,9015],{},[1216,9013,9014],{},"Pricing",[1216,9016,9017,9020],{},[36,9018,9019],{},"$0 or donate"," （免费 \u002F 自愿打赏）",[10,9022,9024],{"id":9023},"上传文件-打包成-zip","上传文件 — 打包成 zip",[15,9026,9027,9028,3217],{},"itch.io 要求 Web 游戏",[858,9029,9030],{},"打包成 zip 上传",[144,9032,9034],{"className":8832,"code":9033,"language":8834,"meta":149,"style":149},"# 进入 build\u002Fweb 目录\ncd build\u002Fweb\n\n# 打包成 zip（不要有外层文件夹，要 index.html 在 zip 根目录）\nzip -r ..\u002Fxggame-bird-web.zip .\n",[36,9035,9036,9041,9047,9051,9056],{"__ignoreMap":149},[153,9037,9038],{"class":155,"line":156},[153,9039,9040],{"class":167},"# 进入 build\u002Fweb 目录\n",[153,9042,9043,9045],{"class":155,"line":171},[153,9044,8847],{"class":8846},[153,9046,8850],{"class":403},[153,9048,9049],{"class":155,"line":178},[153,9050,175],{"emptyLinePlaceholder":174},[153,9052,9053],{"class":155,"line":204},[153,9054,9055],{"class":167},"# 打包成 zip（不要有外层文件夹，要 index.html 在 zip 根目录）\n",[153,9057,9058,9061,9064,9067],{"class":155,"line":225},[153,9059,9060],{"class":181},"zip",[153,9062,9063],{"class":3465}," -r",[153,9065,9066],{"class":403}," ..\u002Fxggame-bird-web.zip",[153,9068,9069],{"class":403}," .\n",[15,9071,9072,9073,9076,9077,9080],{},"或者直接在文件管理器里：选中 ",[36,9074,9075],{},"build\u002Fweb"," 里的",[858,9078,9079],{},"所有文件","（注意是文件，不是文件夹）→ 右键 → 压缩为 zip。",[1003,9082,9083],{},[15,9084,8807,9085,9088,9089,8516,9091,9094],{},[858,9086,9087],{},"常见坑","：zip 解压后必须直接看到 ",[36,9090,8801],{},[858,9092,9093],{},"不能有外层文件夹","。否则 itch.io 加载不出来。",[15,9096,9097,9098,1147],{},"上传 zip 后，勾选 ",[858,9099,9100],{},"「This file will be played in the browser」",[15,9102,9103],{},[19,9104],{"alt":9105,"src":9106},"09-export-上传文件","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F09-export-%E4%B8%8A%E4%BC%A0%E6%96%87%E4%BB%B6.png",[10,9108,9110],{"id":9109},"配置嵌入设置embed-options","配置嵌入设置（Embed Options）",[15,9112,9113,9114,3217],{},"下拉找到 ",[858,9115,9116],{},"Embed options",[1195,9118,9119,9127],{},[1198,9120,9121],{},[1201,9122,9123,9125],{},[1204,9124,8693],{},[1204,9126,1209],{},[1211,9128,9129,9140,9152],{},[1201,9130,9131,9134],{},[1216,9132,9133],{},"Viewport dimensions",[1216,9135,9136,9137,3028],{},"你游戏的窗口大小（如 ",[36,9138,9139],{},"608 × 1080",[1201,9141,9142,9145],{},[1216,9143,9144],{},"Frame options",[1216,9146,9147,9148,9151],{},"一般勾 ",[36,9149,9150],{},"Fullscreen button"," 让玩家可全屏",[1201,9153,9154,9157],{},[1216,9155,9156],{},"Mobile friendly",[1216,9158,9159],{},"如果适配了移动端，勾上",[15,9161,9162],{},[19,9163],{"alt":9164,"src":9165},"09-export-嵌入设置","\u002Fimg\u002Fdevlog\u002Fxggame-bird\u002F09-export-%E5%B5%8C%E5%85%A5%E8%AE%BE%E7%BD%AE.png",[10,9167,9168],{"id":9168},"上传封面图和截图",[15,9170,9171,9172,3217],{},"往下翻到 ",[858,9173,9174],{},"Cover image",[30,9176,9177,9189,9195],{},[33,9178,9179,9181,9182,9006,9185,9188],{},[858,9180,9174],{}," — 主图，建议 ",[858,9183,9184],{},"630 × 500",[858,9186,9187],{},"315 × 250","，会出现在 itch.io 列表里",[33,9190,9191,9194],{},[858,9192,9193],{},"Screenshots"," — 游戏截图，2-4 张就够了",[33,9196,9197,9200],{},[858,9198,9199],{},"Banner"," — 横幅图（可选）",[15,9202,9203,9204,9207],{},"封面图建议用游戏的",[858,9205,9206],{},"最高光时刻","做截图 + 加点文字，吸引点击。",[10,9209,9211],{"id":9210},"设置标签-分类","设置标签 + 分类",[15,9213,9214],{},"帮助玩家发现你的游戏：",[30,9216,9217,9226,9249,9257,9272],{},[33,9218,9219,9222,9223],{},[858,9220,9221],{},"Genre",": ",[36,9224,9225],{},"Action",[33,9227,9228,9222,9231,4516,9234,4516,9237,4516,9240,4516,9243,4516,9246],{},[858,9229,9230],{},"Tags",[36,9232,9233],{},"flappy",[36,9235,9236],{},"casual",[36,9238,9239],{},"pixel-art",[36,9241,9242],{},"arcade",[36,9244,9245],{},"endless",[36,9247,9248],{},"godot",[33,9250,9251,9222,9254],{},[858,9252,9253],{},"Average session",[36,9255,9256],{},"A few minutes",[33,9258,9259,9222,9262,4516,9265,4516,9268,9271],{},[858,9260,9261],{},"Inputs",[36,9263,9264],{},"Mouse",[36,9266,9267],{},"Keyboard",[36,9269,9270],{},"Touchscreen","（如果支持）",[33,9273,9274,9277],{},[858,9275,9276],{},"Accessibility",": 看实际情况",[10,9279,9281],{"id":9280},"发布","发布！",[15,9283,9284,9285,9288],{},"设置好后，把 ",[858,9286,9287],{},"Visibility"," 改成：",[30,9290,9291,9297,9303],{},[33,9292,9293,9296],{},[858,9294,9295],{},"Public"," — 完全公开，任何人都能搜到",[33,9298,9299,9302],{},[858,9300,9301],{},"Restricted"," — 只有有链接的人能访问（适合先给朋友测试）",[33,9304,9305,9308],{},[858,9306,9307],{},"Draft"," — 草稿，只有自己能看",[15,9310,9311,9312,9315],{},"点击右下角 ",[858,9313,9314],{},"Save & view page","，欣赏一下自己的成果 🎉",[10,9317,9319],{"id":9318},"分享出去","分享出去！",[15,9321,9322],{},"把 itch.io 的链接发到：",[30,9324,9325,9328,9336,9344],{},[33,9326,9327],{},"朋友圈、Twitter、小红书、抖音视频简介",[33,9329,9330,9335],{},[1124,9331,9334],{"href":9332,"rel":9333},"https:\u002F\u002Freddit.com\u002Fr\u002Fgodot",[1128],"r\u002Fgodot"," — Godot 社区",[33,9337,9338,9343],{},[1124,9339,9342],{"href":9340,"rel":9341},"https:\u002F\u002Fitch.io\u002Fboard\u002F4\u002Frelease-announcements",[1128],"Itch.io 论坛"," — Release Announcements",[33,9345,9346],{},"Discord 游戏开发群",[15,9348,9349],{},"记得在我的博客评论区也贴一下你的 itch.io 链接，互相玩一下！🎮",[1311,9351],{},[10,9353,9355],{"id":9354},"整个系列到这就完结了","整个系列到这就完结了 🎉",[15,9357,9358],{},"回顾一下我们完成的事：",[30,9360,9361,9364,9367,9370,9373],{},[33,9362,9363],{},"✅ 从零搭建一个 Flappy Bird 类游戏",[33,9365,9366],{},"✅ 学会了 Godot 的核心概念：节点、场景、信号、Autoload、Parallax",[33,9368,9369],{},"✅ 实现了完整的游戏循环：菜单 → 游戏 → 结束 → 重开",[33,9371,9372],{},"✅ 加上美术 + 音效，让游戏有了\"灵魂\"",[33,9374,9375,9376,9379],{},"✅ ",[858,9377,9378],{},"公开发布","，全世界都能玩",[15,9381,9382,9383,9388],{},"如果你跟着做完了整个流程，请一定告诉我！可以发我作品链接 → ",[1124,9384,9387],{"href":9385,"rel":9386},"https:\u002F\u002Ftwitter.com\u002FX_XXGGG",[1128],"Twitter @X_XXGGG"," \u002F 或博客评论。",[15,9390,9391],{},"下一个游戏，我们再战 💪",[1083,9393,9394],{},"html pre.shiki code .snYqZ, html code.shiki .snYqZ{--shiki-default:#A0ADA0;--shiki-dark:#758575DD}html pre.shiki code .sHLBJ, html code.shiki .sHLBJ{--shiki-default:#998418;--shiki-dark:#B8A965}html pre.shiki code .spP0B, html code.shiki .spP0B{--shiki-default:#B56959;--shiki-dark:#C98A7D}html pre.shiki code .s_xSY, html code.shiki .s_xSY{--shiki-default:#59873A;--shiki-dark:#80A665}html pre.shiki code .sfsYZ, html code.shiki .sfsYZ{--shiki-default:#A65E2B;--shiki-dark:#C99076}html pre.shiki code .sqbOQ, html code.shiki .sqbOQ{--shiki-default:#2F798A;--shiki-dark:#4C9A91}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":149,"searchDepth":171,"depth":171,"links":9396},[9397,9398,9401,9402,9403,9404,9407,9408,9409,9410,9411,9412,9413],{"id":8637,"depth":171,"text":8638},{"id":8660,"depth":171,"text":8661,"children":9399},[9400],{"id":8684,"depth":178,"text":8684},{"id":8770,"depth":171,"text":8770},{"id":8823,"depth":171,"text":8823},{"id":8899,"depth":171,"text":8900},{"id":8916,"depth":171,"text":8916,"children":9405},[9406],{"id":8931,"depth":178,"text":8931},{"id":9023,"depth":171,"text":9024},{"id":9109,"depth":171,"text":9110},{"id":9168,"depth":171,"text":9168},{"id":9210,"depth":171,"text":9211},{"id":9280,"depth":171,"text":9281},{"id":9318,"depth":171,"text":9319},{"id":9354,"depth":171,"text":9355},{},"\u002Fdevlog\u002Fxggame-bird\u002F09-export",{"title":8591,"description":1099},"devlog\u002Fxggame-bird\u002F09-export","ljlAqloJGlO7Zma_Ci8OgRQJmwaOGyMsS93o62Z1Htk",1779499825836]