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