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