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