← 所有文章

像素藝術的房間:家具網格、擺放上限與評分

在《寶可夢 綠寶石》(Pokémon Emerald)裡,一間布置好的房間,就是十六件東西擺在一張由 16 像素的格子組成的網格上。反編譯原始碼把玩家的基地上限定為 16 件擺放中的裝飾,臥室定為 12 件,而存檔最多保留 20 座基地,包括您自己的與其他玩家的。12 型錄共有 120 件裝飾,分成八類:35 個玩偶、23 件擺飾、18 張墊子、10 張海報、10 個坐墊、9 張桌子、9 張椅子與 6 盆植物。其中 71 件只佔一格,平均佔 2.43 格,另有 18 件是可以承托小件裝飾的檯面。23 一座基地有 44 到 81 個可行走的格子,平均 60.2 格,所以上限大約是地板的四分之一。能不能擺放,由每一個被覆蓋的格子各自回答;擺放函式裡沒有任何一處檢查通道是否仍然暢通。24 本文之後的每一款遊戲,都以不同的預算沿用這套模型:依 Nookipedia 的說法,Animal Crossing: New Horizons 每個房間 150 件,外加一封每個星期天替房間評分的信;Final Fantasy XIV 自 Patch 7.5 起,室內依住宅類型為 150 到 600 件;依一個粉絲來源,Club Penguin 的冰屋是 99 件。5678 我的 App Kiradex 有一些房間,由它的美術工坊從 23 件家具的詞彙中布置,玩家一件也不能移動。本文實測這些經典作品,把規則連同數字寫出來,對應到 SwiftData 與 SwiftUI,最後附上一份 Kiradex 尚未打造的家具布置系統規格書:每層樓上限 16 件、一條《綠寶石》擺放函式所沒有的通道規則、一份每一分都列出來源的每週評分,以及一份只靠遊玩解鎖的型錄。910

重點摘要

  • 十六是構圖的預算,不是收納的上限。 《綠寶石》在基地裡擺 16 件裝飾、在臥室擺 12 件,卻在八個分類欄位中容納 150 件持有的裝飾,所以玩家擁有的會比擺得下的多。每件擺放的裝飾佔兩個位元組:一個 ID,加上一個以兩個半位元組(nibble)分別存放 x 與 y 的位置,於是一整座基地的布局只需 32 個位元組。111122
  • 規則寫在地板裡。 《綠寶石》有五種擺放權限(地板、可踩過的地板、後方、牆面,以及擺在檯面上)。桌子頂端的格子帶有一種行為,允許小件裝飾立在上面,所以桌子一擺好,玩偶就能放上去。有 18 件裝飾能承托小件,14 件能承托大件。臥室只接受玩偶與坐墊。1324
  • 上限隨平台而上升,其中一個同時也是影格預算。 依 Nookipedia 的說法,Animal Crossing 每個房間的物品上限在 Wild World 是 24 件、City Folk 是 64 件、New Leaf 是 48 件、New Horizons 是 150 件。Final Fantasy XIV 在 Patch 7.5 把室內上限提高一半,成為 150 到 600 件,更新說明還補充:畫面上的家具超過 400 件時,位於其他樓層的家具會停止繪製。57
  • 評分要有列出的來源、固定的日子,以及隨房屋擴建而提高的門檻。 Nookipedia 的住家評鑑協會(Happy Home Academy)表格顯示,一個房間的物品達到 6、10、15 與 20 件時各給 1,000 分,另有系列、套組、類別、顏色與風水加分,而 S 級門檻隨房屋擴建從 15,000 提高到 90,000。該頁沒有為這些 New Horizons 的數字註明任何來源。6
  • 型錄的成長靠購買、靠遊玩,或兩者兼有。 在《綠寶石》裡,120 件裝飾中有 90 件用錢買得到,24 件從不販售(獎品、對戰點數與贈品),另有兩件要用一步一步收集來的 6,000 與 8,000 份火山灰換取。Stardew 每天販售隨機的商品;第一次擴建房屋之後,Robin 還會以 200,000g 販售家具型錄(Furniture Catalogue),之後型錄會以 0g、不限數量販售它所列出的家具,不過仍有一些家具只能從博物館、節慶與其他來源取得。Habbo 販售家具,其中有些刻意做成稀有或隨機的。14151617
  • 訪客只看不碰。 Animal Crossing 的訪客可以使用另一位玩家家中的家具,但不能「modify the interior in any way」(以任何方式改動室內)。Habbo 的訪客沒有主人的權限,就不能放下或移動家具。依一份粉絲轉載的說明頁,Club Penguin 每個設計每天只能按一次讚,而它的總讚數(Grand Total)「will never decrease」(永遠不會減少)。51819
  • 這帶給 Kiradex 什麼。 是一份規格書,不是已上線的功能:工坊 23 件家具中的 20 件可依格子擺放;一條從門口通往樓梯、主人位置與每組展示品至少一個前方格子的通道規則,以實際走一遍來檢查;每層樓 16 件,起始房間出貨時為 8 件;由工坊繪製的六種壁紙與六種地板;一封列出每一項得分來源的星期天的信(最高 23,600 分);一份只靠遊玩解鎖的型錄,來源是現有的等級與徽章,取自同步的收藏、App 記錄在裝置上的任務印章,以及三個新的同步計數器,分別記錄步數、拜訪過的房間與信中的房屋等級;伺服器能儲存布局之後才開放的唯讀拜訪;一個沒有唯一性約束的 SwiftData 模型,因為 CloudKit 同步無法強制執行唯一性;以及不必觸控、用 VoiceOver 也能完成的拖曳擺放。9202122

1. 《綠寶石》:網格上的十六件東西,從原始碼量出

這類玩法最小而完整的版本,是《寶可夢 紅寶石/藍寶石/綠寶石》(Pokémon Ruby, Sapphire and Emerald)的裝飾系統,而它幾乎全是資料。我讀的是 pret 對《綠寶石》的反編譯,commit 731ad5b(2026年10月1日提交),用兩支解析原始碼、從不執行遊戲的腳本:一支處理型錄、上限、承托檯面與房間尺寸,另一支處理每件裝飾的來源。它們的輸出就存放在腳本旁邊。214 本節的每一個數字,都來自這兩份輸出,或來自註腳所指名的原始碼檔案。

上限

三個常數訂出界限。DECOR_MAX_SECRET_BASE 是 16,DECOR_MAX_PLAYERS_HOUSE 是 12,SECRET_BASES_COUNT 是 20,也就是存檔保留的基地數量:您自己的,加上從其他玩家那裡收到的。12 擺放的上限與持有的上限是分開的。存檔在各分類的欄位中保存 150 件持有的裝飾:10 張桌子、10 張椅子、10 盆植物、30 件擺飾、30 張墊子、10 張海報、40 個玩偶與 10 個坐墊。112 因此玩家擁有的可以比一個房間放得下的更多(新遊戲開始時每個欄位都是空的),而挑選要放進去的東西,正是遊戲本身。23

一件擺放的裝飾佔兩個位元組。一座基地存放 decorations[16],每件一個 ID,以及 decorationPositions[16],每件一個位元組,x 在高半位元組、y 在低半位元組,所以基地是以最多 16 乘 16 格的網格來定址,一整座基地的布局只需 32 個位元組。1112 我的解讀是,正因為這麼小,存檔才容得下二十座基地,包括您自己的與其他玩家的。

型錄

gDecorations[] 有 121 個條目。第 0 項 DECOR_NONE 是一個空欄位,沿用小桌子的資料,因此剩下 ID 為 1 到 120 的 120 件裝飾。32 依類別分,是 35 個玩偶、23 件擺飾、18 張墊子、10 張海報、10 個坐墊、9 張桌子、9 張椅子與 6 盆植物。2 依形狀(以格計),是 71 件 1 乘 1、22 件 1 乘 2、13 件 3 乘 3、5 件 2 乘 1、4 件 2 乘 2、3 件 3 乘 2,以及 2 乘 4 與 4 乘 2 各一件。標頭檔還宣告了 3 乘 1 與 1 乘 3,但沒有任何裝飾使用。一件裝飾覆蓋 1 到 9 格,平均 2.43 格。213 如果像我先前為建築那篇文章做的筆記那樣,把空欄位也算成一件裝飾,就會得到 72 件單格裝飾與 19 件實心地板裝飾;不算它,則是 71 件與 18 件。2

一張《綠寶石》120 件裝飾依類別排列的水平堆疊長條圖,每條長條再依裝飾覆蓋多少個 16 像素的格子切分:玩偶 35(單格 25、兩格 10);擺飾 23(單格 9、兩格 9、四到六格 1、八或九格 4);墊子 18(單格 11、八或九格 7);海報 10(各 5);坐墊 10,全為單格;桌子 9(單格 2、四到六格 3、八或九格 4);椅子 9,全為單格;植物 6(兩格 3、四到六格 3)。

《綠寶石》的型錄大多是單格;大件的是桌子、3 乘 3 的墊子,以及少數幾件擺飾。243

表中的價格從最便宜的墊子 500,到最貴的擺飾與玩偶 10,000。有三件價格為 0,是兩面盾牌與一件玻璃擺飾,三件都是獎品。23

五種權限,以及身為資料的檯面

每件裝飾帶有五種權限之一。標頭檔把它們命名為「collision and placement permissions, in that order」(依序為碰撞與擺放權限)。13 依數量:

權限 意義 裝飾
實心地板 阻擋行走,立在地板上 18:全部 9 張桌子、9 件擺飾
可通行地板 可以踩過,立在地板上 37:全部 18 張墊子、全部 9 張椅子、10 件擺飾
後方地板 阻擋行走,最上排可以貼在後牆上 10:全部 6 盆植物、4 件擺飾
牆面 掛在後牆上 10:全部 10 張海報
Sprite 立在檯面上的人偶 45:全部 35 個玩偶、全部 10 個坐墊

數量來自 measure_emerald_decor.py 對 src/data/decoration/header.h 的計算。2

最巧妙的地方在於,檯面是格子的屬性,而不是一段程式碼。每件裝飾由 metatile 繪成,而每個 metatile 都帶有一個行為位元組。有十八件裝飾的格子帶有 MB_HOLDS_SMALL_DECORATION 行為:9 張桌子中的 7 張、3 塊磚、一個輪胎與 7 張墊子。有十四件帶有 MB_HOLDS_LARGE_DECORATION:同樣那 7 張桌子與 7 張墊子。2 1 乘 2 的玩偶底下需要一個「大」格;其他玩偶或坐墊則放在「小」格或「大」格都可以。4 所以一旦擺好一張桌子,地板上就多出一些會對玩偶回答「可以」的格子,而處理兩者的是同一個擺放函式。這就是 16 件東西能讓房間看起來滿滿的原因:120 件中有 45 件,也就是那些 sprite,立在 18 件檯面上。2

有些裝飾還會動作。八張音符墊被踩到時會發出聲音;跳躍墊、旋轉墊與閃亮墊就如其名;三個氣球與一顆泥球會破掉;另有一扇可破壞的門、一座溜滑梯與一件沙子擺飾。每一種都是裝飾 metatile 上的一個行為(MB_SECRET_BASE_SOUND_MAT、_JUMP_MAT、_SPIN_MAT、_GLITTER_MAT、_BALLOON、_BREAKABLE_DOOR、MB_SLIDE_SOUTH、_SAND_ORNAMENT),讀自秘密基地圖塊組的屬性檔。225

擺放是每個格子各自回答的問題

src/decoration.c 裡的 CanPlaceDecoration 會逐一走過手上那件裝飾將覆蓋的格子,向每一格提出一個問題。地板與可通行地板類的裝飾,每一格底下都需要普通地板(MB_NORMAL);只有實心木板可以蓋住洞。格子上不能站著任何物件。玩家打開選單時所站的那一格,裝飾只能以一般圖層類型的圖塊覆蓋;若裝飾在該格的圖塊是其他圖層類型,函式就會拒絕。後方地板類的裝飾,除了最上排之外都需要地板,最上排則可以貼在北牆上。牆面裝飾的每一格都必須是北牆。Sprite 底下需要一個承托格。4 在臥室裡,選單會拒絕玩偶與坐墊以外的所有類別:當 isPlayerRoom 被設定時,DecorationItemsMenuAction_AttemptPlace 會顯示 gText_CantPlaceInRoom。4

CanPlaceDecoration 裡沒有任何一處檢查通道是否仍然暢通。這個函式對行走唯一的保護,就是上面那條關於您所站格子的條件規則。依我對這個函式的解讀,玩家可以用桌子把基地的電腦整個圍起來,而遊戲會照准。4 我會在規格書裡回到這一點,因為一個有人來拜訪的房間,需要這個函式沒有做的那項檢查。

上限所對照的房間

24 種基地布局是六種風格、每種四個尺寸。從每個 map.bin 連牆一起量,寬 7 到 17 格、深 7 到 17 格,面積從 99 格(11 乘 9)到 196 格(14 乘 14),細長的則有 7 乘 16、10 乘 17 與 17 乘 8。226 從每個布局的碰撞位元讀出的可行走格子,從 44 到 81,平均 60.2。2 上限 16 是一座平均基地可行走地板的 0.27。2 臥室是兩棟玩家住家的二樓,9 乘 8,有 54 個可行走格子,上限是 12。21

只看地板會高估擁擠程度,因為那 16 件裡有許多是可以踩過的墊子,或是立在桌上的玩偶。上限預算的是構圖,不是地板。

格子與角色的對照

玩家的行走圖是 144 乘 32 像素,九張 16 乘 32 的畫格,所以在 16 像素的 metatile 網格上,角色的畫格是一格寬、兩格高。布置時的姿勢是單獨一張 16 乘 32 的畫格。27 網格的一格就是畫格的寬度,而每件裝飾都以這個單位計算尺寸。

型錄如何成長

來源腳本在商店、獎品與贈品腳本中搜尋每一個裝飾常數。120 件中有 90 件在五家商店用錢購買。三件是獎品兌換櫃台的兌換品,十五件用對戰點數兌換,十件由腳本贈送:又是那三件獎品兌換品、完成一間解謎屋可得的兩頂帳篷、博物館樓上的一件擺飾、對戰設施主人給的兩面盾牌,以及居民給的兩個玩偶。有二十四件只能贏得,從不以金錢販售。14

另有兩件是用走路換來的。一間玻璃工坊用 6,000 份火山灰換一張椅子、8,000 份換一張桌子,而火山灰要在玩家攜帶袋子時穿過覆滿火山灰的草叢,每走一步收集一份,最多 9,999(VAR_ASH_GATHER_COUNT)。15 我把這一對視為經典作品中「靠遊玩解鎖」最純粹的版本:沒有任何金錢易手,只有步數。袋子不顯示數量。它在野外的用途是「無法使用的道具」所用的函式,說明文字則是一段固定的文字,講的是收集並裝著火山灰。28 除了計步本身之外,只有工坊的腳本會讀取這個數量:玩家不夠時,它會把價格減去已收集的火山灰,並把差額換算成還要走的步數告訴玩家。15

有四件裝飾,三個傳說玩偶與另一個玩偶,沒有被搜尋範圍內的任何商店、獎品或贈品腳本引用。它們可能是透過交換或連線功能取得;我沒有追查,所以「90 件販售」與「24 件只能贏得」這兩個數字,建立在對商店、獎品與贈品腳本的搜尋上,那四件則未追查。14

《綠寶石》教了我們什麼

一套裝飾系統就是三張表加一個函式:每件一列的型錄(權限、形狀、類別、價格、metatile)、一份每個格子都標明自身是什麼的布局(地板、北牆、洞)、一份比擺放上限更大的持有清單,以及一個向每個被覆蓋格子提出一個問題的擺放檢查。檯面就是會對較小件回答「可以」的格子。上限只計算擺放的件數,別無其他。缺口在於行走:擺放函式檢查的是格子,從不檢查通道。

2. Animal Crossing:150 件物品,與一封星期天的信

本節依據的是粉絲 wiki Nookipedia,於2026年10月5日閱讀並存檔;我沒有去找任天堂官方的數字。wiki 沒有註明出處的地方,我會直接說明。56

上限與房間

系列中的每一款遊戲都為房間裡的物品設了上限,而這個上限一路增加。Nookipedia 列出 Wild World 每個房間 24 件「furniture and clothing items」(家具與服裝物品),City Folk 64 件,New Leaf 48 件。至於 New Horizons:「A maximum of 150 items can be placed in each room of the house, including wall and ceiling-mounted furniture.」(房屋的每個房間最多可放 150 件物品,包括掛在牆上與天花板上的家具。)5

依同一頁的擴建表,New Horizons 的房間是:帳篷 4 乘 4;房屋 6 乘 6;主房間擴建後成為 8 乘 8;後方、左方與右方房間各 6 乘 6;二樓與地下室 10 乘 6。「Unlike previous games, only the main room can be expanded in size, as all other rooms have a fixed size.」(與先前的作品不同,只有主房間能擴大,其他房間的尺寸都是固定的。)5 在 6 乘 6 的房間放 150 件,等於每塊地板圖塊 4.2 件,之所以可行,只因為牆面、天花板與其他家具的頂部都能放物品而不佔地板(這是我從這兩個數字算出來的)。529

家具有「a size in tiles that it takes up when placed, ranging from 1.0×0.5」(擺放時所佔的圖塊尺寸,最小為 1.0×0.5),最大到 3 乘 3,而在 New Horizons 裡,它「can also now be pushed in half-tile increments」(現在也能以半個圖塊為單位推動)。29 New Horizons 把家具分成家居用品、可以放在地板或檯面上的雜貨、掛牆家具,以及在 2.0 版加入的天花板裝飾。29 在本文讀過擺放網格的遊戲中(《綠寶石》的格子、Stardew 的圖塊與 New Horizons 的圖塊),只有 New Horizons 能以不到一整個圖塊的距離移動家具。我沒有讀 FFXIV 的擺放網格,而 Habbo 被引用的更細步進,是來自第三方用戶端的堆疊高度(見第 3 節)。

型錄

截至 3.0.2 版,Nookipedia 統計 New Horizons 有 2,076 件家具,其中 1,074 件是在更新中加入的。一個系列是「around 10 furniture items, as well as a matching wallpaper and flooring」(約 10 件家具,再加上搭配的壁紙與地板)。29 截至 1.9.0 版,wiki 統計有 262 種壁紙與 215 種地板。3031 壁紙與地板可以每個房間各自更換,而 2.0 版加入了主題牆:「a second wall covering can be applied to single wall in the room」(房間中的單一面牆可以再貼上第二種牆面)。5

住家評鑑協會

評分這一半,正是 Kiradex 借用的部分。Nookipedia 寫道,該協會「sends an evaluation by mail most Sunday mornings, which cannot be disabled」(大多數星期天早上會寄來一份評鑑,而且無法關閉)。等級分為 B、A 與 S,S 級門檻隨每次擴建而提高:第一棟房屋 15,000,主房間擴建後 23,000,接著是 35,000、47,000、60,000、75,000,加上地下室之後為 90,000。6

wiki 所整理的 New Horizons 加分項目如下:

條件 分數
一個房間有 6、10、15 與 20 件家具 每個門檻 1,000
幸運物品 每件 777
掛牆家具 每件 400,最多三件
同一系列 4 件以上 每件 1,000
一整套家具 每件 800
同一類別 3 件以上 每件 500
房間物品有 70% 以上為同一顏色 每件 200
90% 以上為同一顏色 每件 600
紅、綠或黃色風水 500
蟑螂 每隻扣 2,500
地上的非家具物品 每件扣 1
地上的垃圾 每件扣 500
面向牆壁的有方向性物品 每件扣 300

Nookipedia,「Happy Home Academy」,New Horizons 的表格,存檔於2026年10月5日;頁面上未註明出處。6

這些數字是本節中最不確定的部分。加分與扣分表都沒有註明出處。表格下方的獎勵表寫著它「Includes data sourced from the Data Spreadsheet for Animal Crossing New Horizons」(包含取自 Animal Crossing New Horizons 資料試算表的資料),由具名的貢獻者彙編;而 New Horizons 的每件物品分數那一節寫著「This section is a stub」(本節尚待補充)。6 所以這套算術是粉絲的重建。文末的規格書借用的是它的形狀(列出的來源、數量門檻、套組與顏色加分、固定的日子),數字一個也不借。

其中有兩個選擇,對任何想做每週房間評分的 App 都很重要。門檻隨房屋擴建而提高,所以房間變大並不會讓最高等級變得更容易。而信是在固定的日子寄來,大多數星期天早上,玩家也無法關閉。6 在那裡,不在家並不是沒有代價:房屋那一頁說,玩家若「for at least one week」(至少一週)疏於照顧房屋,就會發現家裡出現蟑螂,而上表每隻蟑螂扣 2,500 分。56 規格書保留固定的日子,拿掉懲罰。

拜訪

依 wiki 的說法,在系列的每一款遊戲裡,「a player may enter any other player’s house freely」(玩家可以自由進入任何其他玩家的房屋)。他們可以使用家具,但不能放下或撿起物品,「or modify the interior in any way」(也不能以任何方式改動室內)。5 到另一座島的夢境拜訪則以另一種方式達到同樣的結果。在 wiki 介紹主持夢境的角色的那一頁,New Leaf 一節說,在夢中「any changes will not be saved and items cannot be brought back to the real world」(任何改動都不會被儲存,物品也無法帶回現實世界),New Horizons 一節則說她的服務「work the same as they do in New Leaf」(與 New Leaf 中的運作方式相同)。上傳夢境可以讓主人獲得一張價值 5,000 鈴錢的夢境鈴錢兌換券(Dream Bell Exchange Ticket),每次更新再得一張。32 依我的解讀,對兒童 App 而言,這是本文中最安全的一種拜訪:訪客走過的是一份複本,無法改動任何會被保留的東西,而主人則因分享而得到獎勵。

3. Habbo、Stardew、Final Fantasy XIV、Club Penguin,以及這套模型從何而來

Habbo:房間就是商品,而商品是拿來賣的

Habbo 自己的說明中心直截了當地寫出持有與訪客規則。「You can own 200 rooms.」(您可以擁有 200 個房間。)33 地板與牆面一旦貼上就是永久的:「Wallpaper and flooring are stuck down - so once you put them down, they can’t be picked back up. You can put new wallpaper or floor down if you wish to change the look of your room.」(壁紙與地板是黏死的,一旦鋪上就無法再撿回來;想改變房間的樣子,可以鋪上新的壁紙或地板。)33 訪客不能碰:「You can’t drop or manipulate furni in other Habbo’s rooms unless you’re a member of a group, and the owner of that group has enabled you to participate in the building of that room.」(除非您是某個群組的成員,且群組擁有者允許您參與那個房間的建造,否則您不能在其他 Habbo 的房間裡放下或操作家具。)18

家具經濟以購買為主。「To buy furni you need credits, diamonds or duckets.」(購買家具需要點數、鑽石或 duckets。)18 Builders Club 會出借家具,額度上限每多一個月的會員資格就增加 250,並包含在平面圖編輯器中儲存自訂房間布局的功能,還保證「Furni limits never go down.」(家具額度永遠不會減少。)34 家具定義頁描述了被扣留「for 4-12 months in order to leave an ample window for traders to build their businesses around them」(4 到 12 個月,好讓交易者有充裕的時間圍繞它們經營生意)的活動家具、「will never be re-released」(永遠不會再推出)的稀有家具、買下後敲開「to reveal another, random rare furni」(會露出另一件隨機稀有家具)的可敲開稀有家具,以及 LTD,也就是「a special type of rare, which comes in limited quantities」(一種限量供應的特殊稀有家具)。17 交易需要交易通行證,而且「There is no guide to what items of furni are worth」(沒有任何指南告訴您家具值多少)。35 Habbo 甚至限制了房間裡的機率元素:「Placing more than three chance elements will disable all randomiser functions in the room.」(放置超過三個機率元素,會停用房間內所有的隨機功能。)36

Habbo 自己的頁面沒有給出網格、堆疊高度或每個房間的家具上限;在說明中心搜尋「furni」、「stack」與「floor plan」,沒有任何一篇文章提到。37 大家引用的數字來自 Nitro,一個開源的第三方用戶端。它的平面圖編輯器常數允許每軸最多 64 個圖塊與一套 27 級的高度方案,本系列的建築那篇文章也從同一個檔案引用過;我先前的筆記還從這個用戶端補充了一項從 0 到 40、以 0.01 為步進的堆疊高度工具,這一點我在本文中沒有重讀。3839 這三個數字都請視為該用戶端的數字,而不是 Sulake 的。

依我的解讀,Habbo 示範了當型錄本身就是生意時,家具布置的循環會變成什麼樣子。它的訪客規則值得照抄;它的經濟(刻意的稀缺、隨機的內容、交易價值)則應該拒絕。

Stardew Valley:一份買一次的型錄,之後家具免費

Stardew Valley Wiki 的 Furniture 頁面,修訂版 193159,在 26 個區塊中列出 718 列物品,共 615 個不同的名稱。這些是我對該頁各列的計數;Movie Posters 區塊使用另一種標記,所以跳過,而「不同」是把列在兩個區塊中的同一件物品合併為一。4016 Wallpaper 頁面,修訂版 193801,顯示 137 種壁紙(型錄內 123 種、型錄外 12 種、無法取得 2 種),Flooring 頁面,修訂版 190285,顯示 97 種地板,兩者都是從頁面的圖示數出來的,而不是從遊戲的資料檔。404142

型錄同時以兩種方式成長。Robin 與旅行貨車各自「offer a random selection of furniture each day they are open」(每個營業日提供隨機挑選的家具)。「After the first Farmhouse upgrade, Robin also offers the Furniture Catalogue for sale」(第一次農舍擴建之後,Robin 也會販售家具型錄),在該頁的型錄表中定價 200,000g,而一旦擺好,它就會以 0g「in unlimited quantity」(不限數量)販售它所列出的家具。Furniture 頁面中有 278 列把家具型錄列為來源。1640 該頁的例外仍是例外:「Some furniture can be obtained only by donating items to the Museum」(有些家具只能藉由捐贈物品給博物館取得),或在節慶、賭場與其他來源取得。16 所以,一個遊玩的里程碑加上一筆以遊戲自有貨幣支付的大額購買,就終結了型錄所列家具的稀缺,而且只限於那些家具。

擺放的回饋是逐圖塊的:「Furniture will display a green square on tiles where it can be placed. The tile will turn red if the furniture cannot be placed.」(家具會在可以擺放的圖塊上顯示綠色方塊;不能擺放時,圖塊會變成紅色。)16 壁紙是一個動作對應一個表面:壁紙「cover the whole room they’re placed in」(會覆蓋所在的整個房間),要貼上時「the character must be facing up by its wall」(角色必須在牆邊面朝上方);它們是「single-use and do not stack」(一次性的,而且不能堆疊)。41 農舍在擴建前內部是 10 乘 7,使用 16 像素的圖塊,這是建築那篇文章從 wiki 引用的數字。39

Final Fantasy XIV:一個同時也是影格預算的上限

Square Enix 在 Lodestone 上的 Patch 7.5 更新說明提高了每一項住宅上限。室內家具方面,公寓與私人房間從 100 提高到 150,小屋從 200 到 300,房屋從 300 到 450,豪宅從 400 到 600;室外家具則從 20 到 40、30 到 60、40 到 80。7 同一份說明用它自己的話給出理由:「Please note that up to 400 furnishings can be displayed on-screen at once. If more than 400 furnishings are on-screen simultaneously, those on a different floor to your character will stop being displayed, with small, more distant furnishings being hidden first.」(請注意,畫面上一次最多可顯示 400 件家具;同時超過 400 件時,與角色不在同一樓層的家具會停止顯示,並從較小、較遠的家具開始隱藏。)7 上限同時是設計預算,也是算繪預算。

這份說明還加入了「Furnishings from the FFXIV Furnishing Design Contest」(來自 FFXIV 家具設計大賽的家具):一場社群比賽,獲勝的設計在這次更新中成為家具。7 這是比賽的安全形式:玩家創作,工作室評審並推出。

本文不談地塊尺寸。Square Enix 的住宅指南在2026年10月5日回傳 HTTP 404,而我沒有再試其他官方頁面,所以 FFXIV 只貢獻了 Lodestone 說明中的物品上限,別無其他。7

Club Penguin:一天一個讚,與一個永不減少的總數

這裡關於 Club Penguin 的每一項事實都只有單一來源。Club Penguin Wiki 引用的那些封存官方部落格文章,在2026年10月5日兩度從 Wayback Machine 回傳 HTTP 429,所以我無法查證。43

That Penguin Game 轉載了它所稱的遊戲說明頁;我沒有查證這個出處。它的「Your Igloo」頁面寫著:「You can Like any igloo design once every day!」(任何冰屋設計您每天都能按一次讚!)每個設計「earns its own individual Likes」(各自累積自己的讚),這些讚加總成總讚數(Grand Total),而且「Likes are permanent, so your Grand Total Likes will never decrease, even if you delete an igloo design!」(讚是永久的,所以就算您刪除某個冰屋設計,總讚數也永遠不會減少!)熱門分頁依總讚數為冰屋排名,而冰屋可以開放給「Everyone」(所有人)或只開放給「Friends」(朋友)。19

Club Penguin Wiki 說「igloos have a limit preventing more than 99 units of furniture being placed at once」(冰屋有一個上限,一次最多只能擺放 99 件家具),大多數家具每種最多持有 99 件,而擁有者可以布置自己的冰屋,「but not other player’s igloos」(但不能布置其他玩家的冰屋)。8 布置是會員福利:從2005年11月1日起,非會員玩家可以擁有冰屋,但不能擺放家具;從2012年7月26日起,他們獲得 6 件免費家具:一件地板物品、四件一般物品與一件牆面物品。438

Wiki 記載的冰屋比賽歷史:第一場在遊戲內報紙第 9 期公布,出刊於2005年12月15日,一位優勝者獲得 5,000 枚金幣。之後的比賽在徵件後一到四週公布優勝者與入圍者,獎品一定有金幣,有時還有家具。從 2008 年的萬聖節比賽起,由遊戲角色評選優勝者,他們的評語會和冰屋一起刊出;從 2009 年起,冰屋上的一個按鈕就能提交作品;最後一場比賽在 2012 年 12 月舉行。舉一個例子,2008 年的 Ye Olde Igloo Contest 給十位優勝者每人 25,000 枚金幣,二十位入圍者每人 15,000 枚。44

The Sims:人物原本是來為房子評分的

網格加型錄這套模型有一個起源故事,而那是一個關於評分的故事。我的來源只有一篇訪談:Will Wright 接受 Tristan Donovan 訪問,由 Game Developer 於2011年5月23日刊出。我沒有找到 Wright 在 1999 或 2000 年談建造模式的任何錄音或錄影演講。45

關於靈感:「one of the original things that was a really inspiration for The Sims was this book, A Pattern Language, by Christopher Alexander.」(The Sims 最初真正的靈感之一,是 Christopher Alexander 的《建築模式語言》這本書。)關於評分:「In some sense I wanted The Sims originally to be an architecture game where it was analyzing these patterns. So the people in The Sims originally were just there to score the architecture.」(某種意義上,我原本想讓 The Sims 成為一款分析這些模式的建築遊戲,所以 The Sims 裡的人物原本只是用來為建築評分的。)關於名稱:「my original name for it was Doll House. We did some test marketing and found out that the name didn’t go very well with males.」(我原本給它取的名字是 Doll House;我們做了一些市場測試,發現這個名字不太受男性歡迎。)關於物件:「Everything we put in the world is advertising」(我們放進世界裡的一切都是廣告),他把這個模型追溯到 SimAnt 的費洛蒙軌跡。45

The Sims Wiki 是一個粉絲 wiki,也是單一來源,它描述了實際推出的畫面。購買模式讓玩家「purchase items from an object catalog and place them on the current lot」(從物件型錄購買物品,並放在目前的地塊上);The Sims 與 The Sims 2 依物件功能為型錄分類;手上物件的佔地圖塊在可以放的地方顯示綠色,不能放的地方顯示紅色。wiki 把這句綠與紅的描述標註在 The Sims 2 與 The Sims 3,所以我不把它算在 2000 年的初代上。46 評分以「動機」的形式留在模擬市民身上,初代叫 Room,第二代叫 Environment,它「analyzes the design and content of the room that the Sim is currently standing in」(分析模擬市民目前所在房間的設計與內容):房間太小、光線不足,以及髒污或損壞的東西都會讓它下降。47

依我的解讀,本文中的每一款遊戲都是那一個畫面,只是預算不同:一份型錄、一張以人的一步為單位的網格、一塊會變綠或變紅的佔地,以及一位評審。

各家上限並排比較

一張標題為「擺放物品上限」的水平長條圖,每條長條都標明其上限計算的對象,並依來源著色:Final Fantasy XIV 依住宅類型的室內擺放上限,來自 Lodestone 的 Patch 7.5 說明(豪宅 600,原為 400;房屋 450,原為 300;小屋 300,原為 200;公寓或私人房間 150,原為 100);Animal Crossing 每個房間,來自 Nookipedia(New Horizons 150、City Folk 64、New Leaf 48、Wild World 24);Club Penguin 每座冰屋 99,來自一個粉絲 wiki;《綠寶石》來自反編譯原始碼(一座基地 16,臥室 12);以及 Kiradex 提議的每層樓 16。

這些上限從《綠寶石》的臥室到 FFXIV 的豪宅相差五十倍,但它們計算的不是同一種東西:FFXIV 是依住宅類型列出的室內擺放上限,Animal Crossing 是每個房間,Club Penguin 是每座冰屋,《綠寶石》是每座基地或臥室,Kiradex 則是每層樓。只有 FFXIV 與《綠寶石》的數字來自發行商自己的文字或程式碼。247581

4. 這門手藝,寫成帶數字的規則

以下是這些量測所支持的規則,每一條都附上它所依據的範圍。數字若是我的算術或我的選擇,該列會註明。「格」指 16 像素的行走格,也就是 Kiradex 的圖塊。227

元素 規則 來源
擺放上限 為房間所展示的東西設上限,以房間為單位,掛牆物品也計入。 《綠寶石》每座基地 16、臥室 12;New Horizons 每個房間 150,「including wall and ceiling-mounted furniture」(包括掛牆與天花板家具);FFXIV 室內依住宅大小 150 到 600;Club Penguin 99(單一粉絲來源)
持有上限 讓玩家擁有的比放得下的多,於是擺放就是選擇。 《綠寶石》八個欄位共持有 150 件,對照擺放 16 件;Club Penguin 每種可持有 99 件(粉絲來源)
上限對照地板 在沒有牆面或天花板圖層的 2D 房間裡,維持在每三個地板格不到一件(這是我的門檻,選擇它是為了讓《綠寶石》恰好落在其下);檯面與可踩過的物件能把它撐大。 《綠寶石》16 件對照平均 60.2 個可行走格子,0.27;New Horizons 在 6 乘 6 的房間放 150 件,每塊圖塊 4.2 件,只有靠牆面、天花板與檯面擺放才可能(我的算術)
檯面 把「能承托小件」做成格子的屬性,讓一張桌子為玩偶增加地板。 《綠寶石》:18 件裝飾能承托小件、14 件能承托大件,而 45 件 sprite 裝飾需要其中之一
網格的格 依角色畫格或格子的寬度來決定網格大小;只有在物件能微調、美術也允許時,才採用更細的網格。 《綠寶石》玩家畫格為 1 乘 2 個 16 像素的格;Stardew 16 像素圖塊;New Horizons 半個圖塊
擺放檢查 向每個被覆蓋的格子提出一個問題(地板、牆面、檯面;是否空著;不在抵達格或主人格上),並逐格顯示答案。 《綠寶石》的 CanPlaceDecoration 負責逐格提問,不過它只在圖塊為非一般圖層類型時才拒絕玩家起始所站的格子;抵達格與主人格是 Kiradex 的規則(7.1);Stardew 每個圖塊顯示綠色或紅色方塊;The Sims 2 與 3 的綠色或紅色佔地(粉絲 wiki)
通道 每次擺放後都檢查行走路線是否仍然暢通(規格書的補充)。 《綠寶石》的 CanPlaceDecoration 不檢查通道,且只防止非一般圖層類型的圖塊佔用玩家所站的格子;本文讀過的 Stardew 與 Sims 頁面描述的是逐圖塊回饋,對通道的事無論正反都沒有提及;規格書加上了行走檢查
壁紙與地板 每個房間一種,一個動作就能更換,可以還原。 Stardew 從牆邊覆蓋「the whole room」(整個房間);New Horizons 每個房間各自設定,外加一面主題牆;Habbo 的是永久的,只能替換
型錄規模 大到讓風格成為一種選擇。 Stardew 137 種壁紙與 97 種地板(我對 wiki 圖示的計數);New Horizons 262 種與 215 種;截至 3.0.2 版 New Horizons 有 2,076 件家具
評分 列出來源、固定日子、隨房屋擴建提高門檻、絕不懲罰缺席。 住家評鑑協會:大多數星期天;B、A、S;S 從 15,000 到 90,000;6、10、15 與 20 件時各 1,000;系列、套組、類別、顏色(粉絲表格,未註明出處);wiki 的房屋頁面沒有指明是哪一款遊戲,說房屋至少一週疏於照顧就會出現蟑螂,而 New Horizons 的 HHA 表每隻蟑螂扣 2,500,這條規則拒絕這種懲罰
型錄成長 對分級 9+ 的 App 來說,靠遊玩成長:一個靠贏得而來的架子,加上一份在里程碑開放的型錄。絕不靠隨機或稀缺的販售。 《綠寶石》120 件中 90 件販售、24 件從不販售,另有兩件以 6,000 與 8,000 份火山灰換取;Stardew 每日隨機商品,第一次擴建後有一份 200,000g 的型錄,其所列家具售價 0g,型錄之外另有博物館、節慶與其他來源的獨有家具;Habbo 的稀有家具、可敲開家具與 LTD 是反例
拜訪 訪客只看不碰;每位訪客每天一個標記;主人的總數永不下降。 Animal Crossing 訪客不能「modify the interior in any way」(以任何方式改動室內);Habbo 訪客沒有權限就不能放下或移動家具;Club Penguin 每個設計每天一個讚,總讚數永不減少(單一粉絲來源)
儲存 一件擺放的物品只需幾個位元組:種類、格子、朝向。 《綠寶石》:一個 ID 位元組加一個位置位元組,一座基地 32 個位元組

來源:第 1 到第 3 節及其註腳。256781916414033181746141511

其中有三條值得各多說一句。

上限是給眼睛的預算,有時也是給 GPU 的。 《綠寶石》的 16 大約是一座基地地板的四分之一。FFXIV 則明說,畫面上超過 400 件家具時,它會停止繪製其中一部分。27 對手機 App 來說,第二個理由確實存在,但依我的解讀,決定數字的是第一個:上限是把一個架子變成一件構圖的東西,這也是 10 月 3 日那篇世界文章選擇十六的理由。10

通道規則是規格書的補充。 《綠寶石》的 CanPlaceDecoration 最多只守護一個格子,也就是您打開選單時所站的那一格,從不問門口是否仍然通得到電腦。4 我讀過的 Stardew 與 Sims 頁面描述的是逐圖塊著色的佔地;它們沒有說這兩款遊戲是否也檢查通道,我也沒有再深入查找,所以本文對它們無論正反都不做任何主張。1646 一個有人來拜訪的房間需要回答這個問題,因為訪客如果走不到展示品前,就沒有東西可看。

對兒童 App 而言,靠遊玩解鎖是型錄問題中站得住腳的那一端。 這是我的解讀,不是法律意見:一個靠購買、隨機或稀缺驅動的裝飾循環,會招來戰利品箱與施壓的問題,而一款收藏 App 不需要這些。《綠寶石》靠贏得而來的架子,示範了一個完全不需要那些東西的房間循環;而 Stardew 的型錄,在一個遊玩里程碑之後以遊戲自有貨幣購買一次,則示範了如何終結它所列出的一切的稀缺。141617

5. Apple 的做法:會同步的模型、會吸附的拖曳,以及 VoiceOver 能用的網格

iOS 上的家具布置畫面需要三樣東西:每件擺放物件一筆透過 iCloud 同步的紀錄、一種把物件拖到格子上的方法,以及一種不靠視覺或觸控也能做同一件事的方法。以下內容全部來自2026年10月5日以 JSON 存檔的 Apple 文件頁面,標題、宣告、摘要與平台資訊由腳本擷取。48 本文沒有為此建置或執行任何東西,下面提到的成本也沒有一項經過量測。

紀錄:SwiftData,依 CloudKit 的需求塑形

@Model「Converts a Swift class into a stored model that’s managed by SwiftData」(把 Swift 類別轉換為由 SwiftData 管理的儲存模型),自 iOS 17.0 起可用。49 SwiftData 可以用 #Unique 強制唯一性,它「Specifies the key-paths that SwiftData uses to enforce the uniqueness of model instances」(指定 SwiftData 用來強制模型實例唯一性的 key-path),自 iOS 18.0 起可用。50 擺放的物件看起來正是它的自然用途:每格一件。

CloudKit 同步排除了這個做法。Apple 關於同步 SwiftData 的指南說,這個框架「does include a small number of features that CloudKit doesn’t support natively, such as unique constraints and nonoptional relationships」(確實包含少數 CloudKit 原生不支援的功能,例如唯一性約束與非選擇性關聯),而且「CloudKit requires all relationships to be optional.」(CloudKit 要求所有關聯都是選擇性的。)22 Kiradex 的模型已經是這個形狀。CardCopy 的註解寫著「every property has a default and nothing is unique, for CloudKit」(為了 CloudKit,每個屬性都有預設值,沒有任何東西是唯一的),而 PriceHistory 的註解說它「Shaped for CloudKit like CollectionEntry: defaults everywhere, nothing unique」(像 CollectionEntry 一樣為 CloudKit 塑形:處處有預設值,沒有任何東西是唯一的),並有一段合併邏輯,在兩台裝置建立同一份歷史時,於每台裝置上挑出同一個勝出者。5152

所以真正重要的唯一性,也就是每格一件,必須在程式碼中強制執行:一個純函式,在每次讀取時執行,把兩台裝置放在同一格上的物件在每台裝置上都解析成同一個勝出者,並把落敗的那件送回收納匣。兩台裝置一起弄壞的不只是單一格子。兩個在各自手機上都通過每一條規則的編輯,合在一起時可能失敗,所以這個函式必須讓整個房間重新通過完整的擺放檢查,而不只是重疊測試。第 7 節的規格書會詳細規定它。

拖曳:draggable、放置目的地,以及手指下的那一格

draggable(_:)「Activates this view as the source of a drag and drop operation」(把這個 view 啟用為拖放操作的來源),用於 Transferable 酬載,自 iOS 16.0 起可用。53 dropDestination(for:action:isTargeted:) 接收被放下的項目與一個 CGPoint,它的頁面把這個點描述為「the drop location in this view’s coordinate space」(此 view 座標空間中的放置位置),同樣自 iOS 16.0 起可用。54 把這個點對應到格子只是算術:減去地圖的原點,再除以圖塊以點為單位的大小。

有一項發現是我先前的筆記所沒有的。dropDestination(for:action:isTargeted:) 的存檔頁面把 iOS 27.2 列為將它標示為棄用的版本,訊息是「Use dropDestination(for:isEnabled:action:) with an action that takes a DropSession parameter instead.」(請改用 dropDestination(for:isEnabled:action:),並搭配接收 DropSession 參數的 action。)54 替代版本的 action 接收的是一個 DropSession,也就是「A description of a drop that is in progress」(對一個進行中的放置的描述),而不是一個點。54 我沒有存下 DropSession 的頁面,所以它是否帶有 view 座標空間中的位置仍未讀到,規格書因此兩種形式都列出。

要移動已經在房間裡的物件,DragGesture 是「A dragging motion that invokes an action as the drag-event sequence changes」(隨拖曳事件序列變化而觸發動作的拖曳動作,iOS 13.0),SpatialTapGesture 則是「A gesture that recognizes one or more taps and reports their location」(辨識一次或多次點按並回報其位置的手勢,iOS 16.0):點一下選取物件,拖曳讓它一格一格移動。5556

在 Kiradex 的鏡頭下,放置點能否乾淨地對應到圖塊格子,還沒有測試過。房間由 RealityKit 以每個 texel 整數個裝置像素繪製,而接收放置的 SwiftUI 覆蓋層疊在它上面;這段算術和鏡頭的適配已經在做的是同一套,但目前還沒有人在上面放下過任何東西。10

不靠觸控的網格:VoiceOver

家具網格不適合逐一滑過每個元素,所以這份設計給 VoiceOver 一組專屬的動詞:

  • accessibilityElement(children:)「Creates a new accessibility element, or modifies the AccessibilityChildBehavior of the existing accessibility element」(建立新的輔助使用元素,或修改既有輔助使用元素的 AccessibilityChildBehavior,iOS 13.0),讓一件佔多格的物件成為一個元素,而不是六個。57
  • accessibilityAction(named:_:) 加入一個具名動作;「Actions allow assistive technologies, such as the VoiceOver, to interact with the view by invoking the action」(動作讓 VoiceOver 等輔助技術能藉由觸發動作與 view 互動,iOS 16.0)。向左移、向右移、向上移、向下移、轉向與收起,各是一個動作。58
  • accessibilityAdjustableAction(_:) 以 AccessibilityAdjustmentDirection 處理上下滑動(iOS 13.0),適合用來切換壁紙或地板。59
  • accessibilityRotor(_:entries:) 建立「an Accessibility Rotor with the specified user-visible label」(一個帶有指定使用者可見標籤的輔助使用轉子,iOS 16.0),讓一個「家具」轉子可以在房間的物件之間跳轉。60
  • AccessibilityNotification.Announcement 是「A notification that an app posts when it needs to convey an announcement to an assistive app」(App 需要向輔助 App 傳達公告時發出的通知,iOS 17.0):每一次被拒絕的移動都會連同理由被念出來。61

接著,UI 測試可以用 XCUIApplication.performAccessibilityAudit(for:_:) 稽核這個畫面,它列為 iOS 17.0 可用;存檔的頁面沒有摘要,並為這個模組路徑列出 Xcode 16.3。62 它能做的就只是稽核。在隨 Xcode 27.0 安裝的 XCUIAutomation 標頭檔中,這項稽核「Runs an accessibility audit on the current view」(對目前的 view 執行輔助使用稽核)。同一批標頭檔加入了自 iOS 27.0 起可用的 XCUIVoiceOverService,它「Provides programmatic control of VoiceOver for UI testing」(為 UI 測試提供以程式控制 VoiceOver 的能力):能開啟與關閉 VoiceOver,把焦點向前、向後、移入與移出容器,並回傳 VoiceOver 為目前焦點元素念出的內容。搜尋這個框架的每一個標頭檔,找不到任何能執行具名自訂動作、轉動轉子或讀取公告的呼叫,所以 UI 測試無法像 VoiceOver 使用者那樣按下「向左移」或「收起」。63 規格書因此直接測試這些動作的處理函式,用 VoiceOver 服務檢查念出的標籤,並把動作、轉子與公告留給開著 VoiceOver 的真人檢查。

成本

未量測。一棟布置好的房屋,兩層樓每層最多 16 筆小紀錄。如果擺放的物件併入引擎已經為工坊家具建立的那一個物件網格,任何變動就意味著重建那個網格;這些都還沒有在手機上做過效能分析。39

6. 案例研究:Kiradex 今天的室內

Kiradex 是一款 iPhone 上的卡片收藏 App,裡面有一座小小的像素城鎮 Kiradex World,每位收藏者都有一棟兩層樓的房屋,城鎮裡還有一座展示大廳。本節的一切都是在2026年10月5日從 App 的程式庫讀出來的,從未執行或修改:室內由腳本解析,文件與模型則當作文字閱讀。964

建築那篇文章之後改變了什麼

10 月 3 日的建築文章說,interiors.py 繪製的房間、樓上與大廳「each 14 by 11, with a two-row wall band」(各為 14 乘 11,帶兩排牆帶)。39 寫的時候這是對的。同一天,TestFlight build 26 帶來了室內套件:「Rooms sixteen wide with a three-row wall band (cornice, face, wainscot)」(房間寬十六格,帶三排牆帶:簷口、牆面、護牆板),家具採 32 像素的節奏,靠牆的物件畫成地面,獨立的物件則畫成收藏者可以走到後方的物件。65 如今工坊把 room 與 room-up 畫成 16 乘 12 格,大廳畫成 16 乘 13 格,每一個都帶三排牆帶:簷口、牆面與護牆板。964 Build 26 介於建築文章所描述的 build 23 與它晚間補充所描述的 build 35 之間,所以兩份文字對各自的 build 而言都是對的。3965

工坊的家具

scripts/forge/interiors.py 在它的 PIECES 表中列出 23 件家具,每件都有以格為單位的寬與高,以及一個標示是否獨立擺放的旗標。9

類型 家具與佔地(格,寬乘高)
靠牆,畫進地面(9) 書架 5 乘 2;衣櫃 2 乘 2;流理台、水槽與爐子各 1 乘 1;壁爐 2 乘 2;上樓梯與下樓梯各 2 乘 3;柱子 1 乘 3
獨立,匯出為收藏者會與之排序遮擋的物件(14) 床與玫瑰床各 2 乘 3;床頭櫃 1 乘 1;桌子 2 乘 2;四個朝向的椅子,各 1 乘 1;沙發 3 乘 2;長椅 2 乘 1;植物 1 乘 2;箱子、板條箱與木桶各 1 乘 1

由 measure_kiradex_interiors.py 從 PIECES 解析;該檔案從未被匯入或執行。9

一件家具覆蓋 1 到 10 格,平均 3.04 格,對照《綠寶石》的 1 到 9 格與 2.43 格。92 不同的圖比名稱少:四張椅子是同一張圖的四個朝向,兩張床是兩個變體,兩道樓梯是兩個方向,而流理台、水槽與爐子是同一座流理台的三種款式。9

這個檔案的 docstring 與它的表格彼此矛盾。docstring 訂下的慣例是「furniture on the 16-pixel grid in a 32-pixel rhythm, every piece at least two tiles on one axis」(家具在 16 像素的網格上採 32 像素的節奏,每件至少有一軸是兩個圖塊)。64 而在表格中,23 件裡有 11 件是 1 乘 1:四張椅子、流理台、水槽、爐子、床頭櫃、箱子、板條箱與木桶。9 這個節奏對大件成立,對小件不成立。這是同一個檔案 scripts/forge/interiors.py 中註解與程式碼之間的差異,而規格書保留小件,因為一個只有兩圖塊家具的房間就不會有椅子。

牆面有一套自己的詞彙,以字母表示:畫 p、時鐘 k、窗戶 N、掛窗簾的窗戶 K、旗幟 n、壁燈 l,以及掛牆板的位置 W。地板有木板 o、磁磚 t、大理石 s,以及一塊依鄰格以九宮格方式自行繪製的地毯 r。牆面有兩種風格:房屋的灰泥加木構,以及大廳的修整石材,每個室內透過 FACES 選擇。64

房間的原始配置

地圖 尺寸 地板格 家具(可移動) 家具下的地板 空地板 App 布置的位置
room 16 乘 12 125 14(13) 25 100 書架 5 格、展示櫃 2、書桌 2;抵達點、樓梯、主人
room-up 16 乘 12 125 12(11) 27 98 牆板 3 格、書桌 2;抵達點在樓梯旁
hall 16 乘 13 144 6(4) 8 放置 8 個展台後為 128 8 個展台

可移動不包括樓梯與柱子。數據來自 measure_kiradex_interiors.py。9

host 位置在檔案中的註解是「where the owner stands when you visit」(您來拜訪時主人站的位置)。64 工坊的 check() 已經會拒絕:落在錯誤字母上的書架、展示櫃、書桌或展台位置,不在門或樓梯上的傳送點,不可行走的抵達、樓梯或主人格,以及兩個匯出物件落在同一格。它較窄的邊界對規格書很重要。它沒有為牆板的位置做字母檢查,而它的重疊迴圈只讀取匯出的物件,也就是獨立擺放的家具,所以畫進地面的靠牆家具從來不在其中。它也不檢查門口能否通到每一個位置。64

通道規則,套用在今天的房間上

我寫了一支腳本,像 export() 那樣重建匯出的字母,並從每層樓的抵達點出發,在工坊的可行走字母 otsrUD 上往四個方向走。20 在今天的布局上,這趟行走能到達樓下的樓梯與主人位置,以及樓上的樓梯。展示位置正前方的格子則是另一回事:

  • 樓下,書架的五個前方格:兩個在木桶與板條箱底下,三個走得到。
  • 展示櫃的兩個前方格都走得到。
  • 書桌的兩個前方格:一個在拉到桌前的椅子底下,一個走得到。
  • 樓上,書桌也一樣,兩個中一個。
  • 樓上,牆板的三個前方格在床與床頭櫃底下。一個都走不到。20

所以,若規定每個位置自己的前方格都必須走得到,就會讓工坊在兩層樓上布置的四組失敗(樓下的書架與書桌、樓上的書桌與牆板),共七個被擋住的前方格;若規定每組布置至少有一個走得到的前方格,則只有一組失敗:樓上的牆板。規格書採用以組為單位的形式,並在起始房間的重新配置中修好牆板。

這支腳本也試著在 room 裡加入額外的家具。加入一件額外的獨立家具,有 733 種擺法通過格子規則;其中 44 種會把某個目標切斷,而且每一種都是直接蓋住目標格本身。加入兩件額外家具,各自避開所有目標、彼此不重疊,共有 229,891 種組合,其中 713 種在沒有蓋住目標的情況下把它切斷:例如 (9,3) 的一張床與 (6,4) 的一個床頭櫃,就封住了玻璃展示櫃前方的那塊空間。20 這 713 種,正是逐格檢查會漏掉、而行走檢查能抓到的。它們也正是兩支手機能合力造出來的:每件家具單獨擺放時,所有目標都仍然走得到,所以各自在自己的裝置上通過行走檢查,而這一對要等兩者都同步之後才會失敗(7.7)。66 我沒有把搜尋擴展到兩件以上的額外家具;到了十六件時,有多少布局會被逐格檢查放行、卻被行走檢查拒絕,尚未量測。

收藏者與網格的對照

收藏者的 sprite 格是 32 乘 40 像素,人物上方留 7 像素、下方留 2 像素,所以人物約 31 像素高,而這一格是 2 個圖塊寬、2.5 個圖塊高,對照《綠寶石》1 乘 2 的畫格。WORLD.md 把它描述為「a 30-pixel figure in a 32 × 40 cell」(一個 32 × 40 格中的 30 像素人物)。2767 人物本身的寬度沒有量測。我的解讀是,正因為這一格的關係,工坊才在 16 像素的網格上以 32 像素的節奏繪製家具:收藏者的格子是兩個圖塊寬。

這些房間做不到的事

建築那篇文章誠實列出的清單,對房屋來說仍然成立:「Rooms have no wallpaper or floor choice and no placeable furniture; the house is dressed from the collection and nothing else.」(房間沒有壁紙或地板可選,也沒有可擺放的家具;房屋只以收藏來布置,別無其他。)39 App 用收藏來布置那些具名的位置(書架上的卡冊、展示櫃裡的卡片、牆板上的緞帶與徽章、大廳展台上的卡片),而每一件家具都由工坊擺放。6439

伺服器目前還無法保存房間。它的 rooms 模組寫著:「Everything here is in memory; a room empties when its last collector leaves and nothing of them is kept.」(這裡的一切都在記憶體中;最後一位收藏者離開後房間就會清空,他們的任何東西都不會保留。)68 所以今天拜訪另一位收藏者的房屋,拜訪的只是一張地圖,而要以唯讀方式拜訪真實收藏者的擺設,需要伺服器能儲存布局。

房間規則寫在哪裡

10 月 3 日的世界文章公布了房間規則:「a furnishing cap of sixteen」(擺設上限十六件);「visits are read-only, a like is once a day, and the room’s total never decreases」(拜訪是唯讀的,一天按讚一次,房間的總數永不減少);以及「A report arrives every Sunday grading the room, with its point sources listed」(每個星期天會送來一份為房間評分的報告,並列出得分來源)。10 這些規則不在 App 的設計文件裡。docs/WORLD.md 第 4 節列出金幣商店(「clothes, hats, backpacks, card-display frames and emotes」,服裝、帽子、背包、卡片展示框與表情動作)以及「that cannot be bought」(無法購買)的等級鎖定外觀,卻對家具、上限、按讚或星期天的報告隻字未提。67 這些規則寫在研究筆記 docs/research/pixel-art/06-collecting-and-hubs.md 的 5.3 節,其中還要求那封信是「a pixel object that appears on the doormat」(一個出現在門口地墊上的像素物件),要有「a stamp on their passport per room visited, with ranks at 30 / 100 / 500」(每拜訪一個房間就在護照上蓋一個章,等級門檻為 30 / 100 / 500),並在 5.7 節要求贊助者物品「never a bigger furnishing cap」(絕不包括更大的擺設上限)。69 本文把這些規則視為已定案,因為世界文章已經公布了它們,而規格書的第一項整理工作就是把它們抄進 WORLD.md。世界文章的同一段還說「Patron flair stays on the name-plate and the room skin」(贊助者的標記只留在名牌與房間外觀上)。10 規格書不把房間外觀視為已定案:它把這一點重新列為 Blake 的決定(7.5)。

「週」早已存在

每週任務在 Quest.current 中依日曆的週輪替,大廳的主題就是該週任務的標題,而原型筆記也詢問測試者對「Weekly reset on your calendar’s week」(依您日曆的週重置每週內容)的看法。7071 星期天的信不需要新的時鐘,不過它要用自己的鍵,也就是那個星期天的日期,理由見 7.4。

7. 規格書:Kiradex 要做什麼,以及必須通過的檢查

這是一份規格書,不是已上線工作的報告:下面的東西目前都還不存在於 App 中。它是以第 6 節量到的現況為基準所寫的差異:工坊的 23 件家具、兩層 16 乘 12 的房屋樓層與一座 16 乘 13 的大廳、已布置的位置、一台什麼都不保留的伺服器、一個透過 CloudKit 同步的 SwiftData 資料庫、每週任務的時鐘,以及世界文章已經公布的房間規則。96810 每一個元素都保留上面量到的某項機制,並以 Kiradex 自己的工坊、調色盤與用語繪製一切。沒有任何元素提及生物、任何遊戲中的地點或任何系列的功能,房間裡也沒有任何東西會被投擲、捕捉、召喚或騎乘,所以沒有任何東西類似世界文章所描述的那些遊戲方法專利中所主張的步驟;這是我的解讀,不是法律意見。10 座標採用工坊的座標,x 向右、y 從左上角的格子向下。本節的數字(起始的八件、評分表、房屋等級門檻、解鎖時程)是我的提案,不是事實。

7.1 依格子擺放,使用工坊自己的詞彙

在房屋中加入一個布置模式。可擺放的家具是工坊 23 件中的 20 件:除了 stairs、stairs_down 與 pillar 之外的全部,這三件仍是布局的一部分。四張椅子合併為一種帶朝向的家具,所以這 20 件是 17 種地板家具。牆面上的畫、時鐘、旗幟與壁燈成為四種可擺放的牆面物品,於是從今天的工坊得到 21 種;窗戶與樓梯開口仍屬建築結構。7.5 節會再加入三件新家具(一張展示桌、一座屏風與一件壓花牆面物品),使總數成為 24 種。72 一件擺放的家具是 (kind, x, y, facing),以它在該樓層字母地圖上的左上角格子為準,與 ROOM_FURNITURE 已經採用的形狀相同。964

規則,逐格列出。前五條是《綠寶石》的逐格提問,以 Kiradex 的字母重新表述;第六條是規格書的補充,一項《綠寶石》擺放函式不做的檢查。4

  1. 獨立家具覆蓋的每一格都必須是地板(o、t、s 或 r),且上面沒有其他家具。
  2. 靠牆家具(書架、衣櫃、流理台、水槽、爐子、壁爐)的最上排必須在牆帶上,也就是工坊現在擺放它們的地方:壁爐在 y = 1,橫跨牆面與護牆板,其餘在 y = 2,位於護牆板上。64
  3. 牆面物品需要第 1 排中的一個牆面格(A),且不能是窗戶或樓梯開口。
  4. 抵達格、兩個門格(工坊把門畫在底牆上,寬兩格)、樓梯的可行走格與主人格上都不能有家具。
  5. 書架會帶著它的五個書架位置一起移動,因為這些位置是相對於家具而定的,所以移動書架就會移動卡冊。玻璃展示櫃 c 與書桌 d 在本規格書中維持為固定字母。
  6. 通道規則。 擺放之後,從抵達點出發、在可行走格子上往四個方向的行走,仍必須到達樓梯格、主人格,以及每組布置(書架、展示櫃、書桌、牆板)正前方至少一個格子。破壞行走路線的家具會被拒絕,而拒絕訊息會指出它會切斷什麼。

一張 Kiradex 一樓今天由工坊原始配置的 16 乘 12 網格,只畫格子、沒有美術:橫跨上方的三排牆帶,靠牆的書架與玻璃展示櫃,右上角的樓梯格,下方的門;抵達點走得到的地板為白色;工坊的獨立家具為灰色;抵達點、樓梯與主人分別標為 A、S 與 H;書架、展示櫃與書桌的前方格標示為走得到,若有木桶、板條箱或椅子擋在上面則打叉;上面再以橘色畫出 (9,3) 的一張床與 (6,4) 的一個床頭櫃,並框出這一對所切斷的展示櫃前方六個格子。

兩件橘色家具各自都通過格子規則;合在一起就把展示櫃圍住了,只有規則 6 的行走檢查能抓到。2420

為什麼採用以組為單位的形式。在今天的布局上,位置前方的格子常常就是一件家具:木桶與板條箱壓在書架五個前方格中的兩個上面,每張書桌前各有一張椅子,而床與床頭櫃壓住了牆板全部三個前方格。20 工坊已經會說這種語言的一部分:check() 會拒絕獨立物件之間的重疊,以及落在錯誤字母上的書架、展示櫃、書桌與展台位置。64 玩家得到的是這些規則的延伸:擴及每一塊佔地(包括靠牆家具)與牆板的格子,再加上行走檢查。

驗收條件。 - 一項單元測試對 room.json 執行這六條規則:24 種家具(今天工坊的 21 種加上 7.5 新增的三種)中的每一種,都在某一格被接受,並針對每一條適用於它的規則,在某一格被拒絕。 - 通道規則在兩層出貨的起始樓層(7.2)上都通過,並有一項測試確定樓上的牆板有一個走得到的前方格。 - 一項性質測試在 room 與 room-up 中隨機放置最多 16 件的合法序列,並在每一步之後斷言從抵達點出發的行走能到達樓梯、主人,以及每組布置的一個前方格。 - 一項回歸測試在 room 中放置 (9,3) 的床與 (6,4) 的床頭櫃,並斷言第二件被拒絕,且訊息指出展示櫃。20 - 在 iPhone 18 Pro Max 模擬器上擷取布置模式的畫面,顯示手上家具的佔地逐格著色,一格合法、一格不合法。 - 工坊的 check() 在 App 能產生的每一種布局上都通過:測試把擺好的布局匯出為 ROOM_FURNITURE,再以唯讀方式對它執行工坊的檢查。由於這項檢查只讀取匯出的物件,又沒有牆板的字母檢查,同一項測試還會以規格書自己的驗證器斷言:沒有任何兩塊擺放的佔地共用同一格(包括靠牆家具),而牆板的三個位置都落在原始地圖中牆板的 W 格上。64

7.2 上限:每層樓十六件,起始房間八件

內容。 每張樓層地圖 16 件擺放的家具,room 與 room-up 各自計算,牆面物品也計入,就像 New Horizons 把牆面與天花板物品算進它的 150 件一樣。5 收藏所布置的東西(書架上的卡冊、展示櫃裡的卡片、牆板上的緞帶與徽章)永遠不計入:上限預算的是家具,而收藏才是重點。持有數量不受它限制;收藏者可以擁有比放得下的更多,就像《綠寶石》允許持有 150 件、擺放 16 件那樣。2

數字。 16 件對照 125 個地板格,是每格 0.13,為《綠寶石》0.27 的一半,這適合平均佔 3.04 格(對照《綠寶石》的 2.43 格)的家具,以及格子寬兩個圖塊的收藏者(我的算術)。9227 工坊的起始 room 已經有 13 件可移動家具,room-up 有 11 件,所以一位新收藏者一到就是 16 件中的 13 件,只剩三件可選。9 提案是:每層起始樓層出貨時放 8 件可移動家具,在工坊中挑選。樓下保留書架,因為它帶著書架位置;樓上保留一張床,而重新配置至少要清出牆板前方的一個格子,今天一格都沒有。被移除的家具會進入收藏者的收納匣,而不是消失,所以每層樓有 8 個名額從第一天起就屬於收藏者。20 起始家具以固定的 ID 建立,讓兩台冷啟動的裝置建立出相同的紀錄,而不是兩套(7.7)。

不看贊助等級。 研究筆記的那一句維持不變:贊助者物品「never a bigger furnishing cap」(絕不包括更大的擺設上限)。69

驗收條件。 一項單元測試放置 16 件,並斷言第 17 件被拒絕,且訊息指出上限;一項測試確認已布置位置上的卡冊、卡片、緞帶與徽章不改變計數;一項測試確認每一個贊助等級看到的上限都是 16;一項測試確認兩層起始樓層載入時都有 8 件可移動家具,並通過通道規則。

7.3 壁紙與地板

內容。 每張樓層地圖一種牆面風格與一種地板風格,各自在布置模式中點一下就能更換,免費且可還原:採用 Stardew 一個動作對應一個表面,以及 New Horizons 每個房間各自選擇的做法,而不是 Habbo 的永久性。41533 風格不計入那 16 件。

現有的詞彙。 兩種牆面(plaster 與 blocks)、三種地板(木板 o、磁磚 t、大理石 s)以及地毯。64 工坊依規則從具名的調色盤色階繪製,所以一種風格就是一個繪製器加一道色階。第一批是 2 種牆面各 3 道色階、3 種地板各 2 道色階:6 種牆面風格與 6 種地板風格,每一種都由工坊繪製,沒有任何一種是手繪的。更多風格靠遊玩取得(7.5)。

如何出貨。 工坊把每種風格匯出為與房間本身並列的圖集格,App 再替換牆面格與地板格的地面索引;地毯保留它的九宮格。

驗收條件。 擷取 room 在每種牆面風格與每種地板風格下的畫面,共 12 張;一項調色盤檢查,確認每種風格的每個像素都是工坊的調色盤索引;一項測試確認更換風格不會動到擺好的家具與已布置的位置。

7.4 星期天的信:列出每一項來源的每週評分

內容。 每週一次,在當地時間星期天午夜當下或之後第一次開啟 App 時,門口地墊上會出現一封信(一個位於抵達格的物件),依房屋當下的樣子為它評分。每個星期天一封,以當地時間那個星期天的日期為鍵。任務板的週不能當鍵:Quest.current 採用日曆的 weekOfYear 區間,它從日曆設定的每週第一天開始,所以週的鍵與星期天規則可能不一致。70 錯過一週沒有任何代價。信會印出每一行來源與它的分數,接著是總分與房屋等級。名稱用 Kiradex 自己的,暫定為策展人的短箋,絕不使用它借用節奏的那款遊戲的名稱。

來源。 形狀取自 Nookipedia 所整理的住家評鑑協會(數量門檻、套組加分、顏色加分);數字是我的,而且收藏也在其中。6 上限以每張樓層地圖計,和那 16 件一樣。

來源 分數 上限 借用的是什麼
每件擺放的家具 100 每層 16 件 數量門檻
布置好的樓層:6、10 與 16 件 每個門檻 500 每層 1,500 6、10、15 與 20 件時各 1,000
套組:同一層有同一工坊套組的家具 4 件以上 每件 200 每層 16 件 同一系列 4 件以上
單一色階:該層家具與牆面、地板兩個表面中有 70% 屬於同一色階家族 該層每件擺放家具 100 每層 16 70% 同一顏色
單一色階達 90%(取代 70% 那一行) 該層每件擺放家具 300 每層 16 90% 同一顏色
書架:5 個卡冊位置全部以收藏填滿 500 一次 《綠寶石》能承托玩偶的桌子
展示櫃:2 個位置各放一張卡 每個 300 600
牆板:3 個格子各放一枚徽章、印章或緞帶 每個 100 300
扣分 無 通道規則本來就會拒絕被堵住的房間

提案。每一行都能從本機的房間與收藏計算出來。

這張表能給出的最高分是 23,600,是以兩層樓都放滿 16 件、每一項加分都達成,逐行計算出來的;套組與色階兩行假設有一個大到足以容納全部十六件的套組與色階家族,所以這是上限,而不是任何人畫過的布局。一層 8 件的起始樓層,在沒有套組、色階或位置加分之前,得 1,300 分。21

一張星期天的信各項得分來源在兩層樓都達到最大值時的水平長條圖:每件擺放的家具 3,200;6、10 與 16 件的布置樓層 3,000;套組 6,400;單一色階 90% 9,600;書架填滿 500;展示櫃 600;牆板 300;合計 23,600。

在分數的頂端,構圖(套組與色階)比數量更值錢,這正是設上限的意義。2421

這封信以三個房屋等級為房屋評分,每個門檻都是這張表本身給某棟參考房屋的分數:兩層樓各 10 件且書架填滿為 4,500;兩層樓各 16 件且書架、展示櫃與牆板都填滿為 7,600;同一棟房屋每層再加上 8 件的套組與 70% 單一色階為 14,000。起始房屋兩層各 8 件,得 2,600 分,低於第一個等級。21 這三個數字是我的提案;信上線之前,由 Blake 訂出最終數字。和住家評鑑協會不同,這些門檻不會提高,因為房屋的形狀永遠不變(見下方的「不在本規格書範圍內」)。6 房屋等級不是緞帶。App 已經保存的緞帶來自城鎮裡的收藏者,存放在 plaza.ribbons;那些才是牆板上展示的緞帶(第 6 節)。73 房屋等級印在信上,並以一個數字保存(7.5),而且永遠不會放上牆板。

評分永遠不讀取的東西。 贊助等級、金幣、購買紀錄、讚數、任何關於其他玩家的資訊,或贊助者的房間外觀。每一行都能從本機的房間與收藏計算出來,所以收藏者可以清楚看出自己為什麼得到這個分數。如果贊助者的房間外觀上線,它會畫在該層一般的牆面風格之上,本身並不是一種牆面風格:該層在評分時保留那個一般風格,所以不論外觀開或關,色階兩行計算的都是同樣的家具與表面(7.5)。

工坊的變更。 PIECES 加上一個 set 標籤,以相同的材質繪製;第一批套組就是把現有家具依繪製時所用的材質分組。

驗收條件。 一個純函式 Score.week(room:collection:),每一行都有單元測試:一層 5、6、10 與 16 件;3 件與 4 件的套組;69%、70%、89% 與 90% 單一色階;書架放 4 本與 5 本卡冊。一項測試確認最高分為 23,600。一項測試確認三棟參考房屋分別得 4,500、7,600 與 14,000 分,並取得第一、第二與第三房屋等級,而起始房屋以 2,600 分一個等級都拿不到。一項測試確認達到房屋等級不會為 plaza.ribbons 增加任何東西,也不會為牆板增加任何東西。一項測試確認每個星期天的信只產生一次(同一個星期天的日期出現兩次只產生一封信,下一個星期天再產生另一封),並確認每週從星期一開始的日曆會產生相同的鍵。一項測試確認只有贊助等級不同的兩份個人資料得到完全相同的分數。一張在星期天門口地墊上的信的 UI 擷取畫面,以啟動參數設定日期。

7.5 靠遊玩解鎖,絕不靠購買

內容。 收藏者擁有的型錄由遊玩推導而來。六個輸入中有三個今天已經存在:等級與徽章,由同步的收藏推導;以及任務印章,只記錄在裝置上。其餘三個需要新的計數器,各自具名並同步:

  • 等級。 WORLD.md 的曲線是 level = floor(sqrt(XP / 40)) + 1。67 從等級 2 到 20 每一級解鎖一種家具或風格,之後每兩級一種,就像 WORLD.md 已經用等級鎖定那些「that cannot be bought」(無法購買)的外觀一樣。67 今天就能用,由同步的收藏推導:App 從收藏算出經驗值與等級,「so every device agrees without anything being stored」(所以每台裝置都會得到一致的結果,而不必儲存任何東西)。74
  • 徽章。 每枚徽章解鎖一件指定的家具:Full Set 解鎖一張展示桌,Vintage 解鎖一座時鐘,Across the Sea 解鎖一座屏風。第一件與最後一件是新的工坊家具。67 今天就能用,以同樣的計算由同步的收藏推導。74
  • 每週任務。 每完成一項每週任務,就增加一件基本家具:一張椅子、一盆植物、一個板條箱。70 今天已有紀錄,但只在裝置上:印章存在 @AppStorage("quests.stamps") 中,也就是 UserDefaults,而不是同步的資料庫,所以在任何家具依賴它們之前,它們要先搬進 PlayRecord(見下文)。7375
  • 步數。 一趟長途行走,一件需要大量步數的家具,就像《綠寶石》的玻璃椅子要用一步一步收集的 6,000 份火山灰換取。《綠寶石》的袋子不顯示數量,工坊也只在玩家回去時才說明還差多少;這裡的提案是一個在家具的收納匣卡片上一直看得到的計數器,顯示已走步數對照所需步數。2815 窗台植物的壓花是另一項提案,是一件牆面物品,其解鎖條件訂在致敬那篇文章的規格書中:植物每 256 步長大一個階段,開花維持 1,024 步,然後掉落一朵花,而不是依這一項的計數器解鎖。新增:App 今天沒有保存任何步數,所以這需要 stepsWalked。73
  • 拜訪。 護照在拜訪 30、100 與 500 個房間時的等級,各解鎖一種風格。69 新增:今天無論在裝置上或伺服器上,都沒有任何東西記錄拜訪,所以這需要 roomsVisited,也就是各主人的 ID。7368
  • 信。 收藏者第一次達到每個房屋等級時,解鎖一件家具。新增:房屋等級只存在於這份規格書中(7.4),所以這需要 letterTier,也就是達到過的最高房屋等級。

模型差異。 任務印章與三個新計數器放在同一個同步模型 PlayRecord 中,每台裝置一筆紀錄,與 App 的其他模型一樣為 CloudKit 塑形(7.7)。型錄讀取每台裝置的紀錄:步數相加,印章與拜訪過的主人取聯集,房屋等級取最高者。裝置把自己那筆紀錄的 uid 保存在一個 @AppStorage 鍵中,與 plaza.id(裝置在廣場上的 ID)位於同一個 UserDefaults 命名空間。7375 每台裝置只寫入自己的紀錄,所以兩台裝置永遠不會編輯同一筆,也就不需要任何合併規則。這只在指標一直留在同一台裝置上時才成立,而還原到第二支手機上的備份會把指標一起帶過去(這是我的解讀;沒有測試過還原)。所以紀錄帶有一個裝置標記 writer,規則是:當裝置的指標所指的紀錄是由另一台裝置寫入時,被還原的裝置要建立一筆新紀錄。舊紀錄保留下來,型錄仍然會讀取它。哪一個值能做成備份不會帶到新手機上的標記,本規格書沒有定案;測試會把這條規則固定下來。變更後第一次啟動時,裝置會把它的 quests.stamps 清單複製進自己的紀錄一次。

拒絕的東西。 不把家具或風格做成 App 內購買,家具不標金幣價格,不做任何形式的隨機家具,不做限量家具,也不開放家具交易:Habbo 的可敲開家具、稀有家具與交易價值就是反例。1735 我的解讀:一款分級 9+ 的 App,若裝飾循環靠購買或隨機驅動,就會招來戰利品箱與施壓的問題,而這款收藏 App 不需要這些,靠遊玩解鎖則能避開它們。研究筆記允許的唯一付費例外,是給贊助者的「one room skin」(一款房間外觀),而世界文章也點名了它:「Patron flair stays on the name-plate and the room skin」(贊助者的標記只留在名牌與房間外觀上)。6910 同一句話也劃下了界線:贊助者的標記「never touches a pedestal slot or a judging bonus」(永遠不碰展台名額或評審加分)。10 本規格書把外觀重新列為 Blake 的決定,而不是視為已定案。如果他保留它,外觀就是一層視覺覆蓋,而不是一種牆面風格。該層保留收藏者選擇的一般牆面風格,外觀畫在它上面,而評分只讀取一般風格,所以不論外觀開或關,7.4 的色階兩行都以同樣的分母計算同樣的家具與表面。若改成把牆面從色階計算中拿掉,就不再中立,因為這個比例同時計算表面與家具。一層 16 件家具,其中 15 件與地板風格屬於同一色階家族、牆面不屬於,是 18 中的 16,88.9%,落在 70% 那一行,得 1,600 分,16 件擺放家具各 100 分,那件不在色階中的也算在內;拿掉牆面,就成了 17 中的 16,94.1%,落在 90% 那一行,得 4,800 分,16 件各 300 分,等於為付費外觀多給了 3,200 分的評審加分。76 下面的測試會以名稱把外觀明確排除。

驗收條件。 一項單元測試確認擁有的型錄是經驗值、徽章、任務印章、已走步數、拜訪過的房間與房屋等級的純函式,不隨其他任何東西改變;一項測試確認兩台裝置的 PlayRecord 不論以哪種順序讀取,都得到相同的型錄(步數相加、印章與主人取聯集、房屋等級取最高);一項測試確認裝置既有的 quests.stamps 清單被複製進它的紀錄一次,且絕不會兩次;一項測試確認當裝置的指標指向一筆帶有另一台裝置 writer 的紀錄時,它會建立一筆新紀錄、不更動舊紀錄,並且仍然兩筆都計算;一項測試確認沒有任何 StoreKit 產品識別碼或金幣商店物品對應到家具種類或風格,只有一個具名的例外:如果 Blake 保留贊助者的房間外觀,它的產品恰好對應到一層覆蓋,不對應任何牆面風格,套用它也不會改變該層用於評分的牆面風格;一項測試確認一層樓在外觀開與關時逐行得分相同、取得相同的房屋等級,並在每個門檻邊界上執行:5、6、10 與 16 件,69%、70%、89% 與 90% 單一色階,每種情況都分別測試一般牆面在色階家族之內與之外,以及比每個房屋等級門檻少一分與恰好等於門檻的房屋;以及一項快照測試,拍下等級 1(只有起始套組)與等級 20 時的型錄收納匣。

7.6 拜訪:唯讀、一天一個讚、一個永不下降的總數

內容。 訪客從房屋的門走進主人的家,使用城鎮為自己收藏者的房屋所設的房間傳送點,39 並看到主人擺好的家具、風格與已布置的位置。訪客可以走動、說預設語句,但不能移動、拿走或加入任何東西:這是 Habbo 的規則,也是 New Horizons 的夢境藉由不保留訪客的任何改動所達到的結果。1832 門上的讚,每位訪客對每位主人每個當地日只算一次,而主人的總數永不減少,即使重新布置也一樣:這是 Club Penguin 的規則,出自它那唯一的粉絲來源。19 拜訪為訪客賺得一枚護照印章,由訪客的裝置把它加進自己 PlayRecord 中的 roomsVisited(7.5)。69

伺服器差異。 今天的伺服器什麼都不保留。68 拜訪需要每位玩家兩筆小紀錄。第一筆是房屋布局:兩層樓合計最多 32 件擺放的家具,每件是種類、x、y 與朝向,再加上每層兩個風格 ID。以帶具名鍵的精簡 JSON 表示,每個欄位都填入最長的提案種類名稱、風格 ID 為 16 個字元,是 1,974 位元組,不到 2 KB;若以純數字四元組表示則是 385 位元組(我的計算;《綠寶石》一整座基地是 32 位元組)。7211 第二筆是讚的總數,加上當天的訪客集合。兩者都不含玩家代稱以外的任何個人資料。伺服器在儲存布局之前,會以同樣的六條規則驗證,所以被修改過的用戶端無法儲存不合法的房間。7.5 的遊玩計數器(任務印章、已走步數、拜訪過的房間、房屋等級)不是伺服器紀錄:它們透過收藏者自己的 iCloud 在 PlayRecord 中同步,伺服器從不讀取。

驗收條件。 伺服器測試:有重疊、通道被堵住或某層出現第 17 件的布局會被拒絕;訪客在主人房間中的移動或擺放訊息會被拒絕;同一位訪客同一天的兩個讚只算一次,隔天的讚則會計入;主人清空房間後總數仍然保留。一場雙模擬器的實地走查:裝置 A 布置,裝置 B 拜訪,並在相同的格子上看到相同的家具,雙方各擷取一張畫面。

7.7 iOS 26 上的模型與畫面

SwiftData。 兩個模型,與 App 的其他模型一樣為 CloudKit 塑形:處處有預設值,沒有任何東西是唯一的,並在 init 中把 uid 設為一個 UUID,就像 Profile、PriceHistory 與 CardCopy 各自帶有一個那樣。51775222

@Model final class PlacedPiece {
    var uid: String = ""             // set once, at creation; orders the repair
    var roomMap: String = "room"     // "room" or "room-up"
    var kind: String = ""            // a forge PIECES name
    var x: Int = 0
    var y: Int = 0
    var facing: String = "down"
    init() { uid = UUID().uuidString }
}

@Model final class PlayRecord {      // one per device (7.5)
    var uid: String = ""             // the device's pointer to it is an @AppStorage key
    var writer: String = ""          // device marker: the one device that writes it
    var questStamps: [String] = []   // moved from @AppStorage("quests.stamps")
    var stepsWalked: Int = 0
    var roomsVisited: [String] = []  // hosts' ids
    var letterTier: Int = 0          // highest house tier reached, 0 to 3
    init() { uid = UUID().uuidString }
}

牆面與地板風格以字串形式存放在 Profile 上,與 lookData 並列。77 沒有 #Unique,也沒有必要的關聯,因為 CloudKit 兩者都不支援。22 格子的獨佔、上限與行走,都由一個函式在程式碼中強制執行。RoomLayout.repair(_:) 在每次讀取時執行。它把一層樓的同步家具依 uid 遞增排序,從該層固定的建築結構(字母地圖、樓梯與柱子)重建這層樓,依序加入每件家具,前提是 7.1 的完整擺放檢查針對已被接受的家具接受它:格子規則(因此沒有重疊)、16 件的上限,以及通道規則。檢查拒絕的家具回到收納匣。每台裝置都以同樣的方式排序同樣的紀錄,所以每台裝置都重建出同樣的房間,而當兩件家具衝突時,uid 較小的那件勝出,這正是 PriceBook 已經用來在兩台裝置的價格歷史中挑出一份的規則。52 順序依 uid 而不依擺放時間,因為時間來自每支手機自己的時鐘。兩支手機能造成的衝突不只有重疊。在一支手機上擺在 (9,3) 的床,與在另一支手機上擺在 (6,4) 的床頭櫃,各自在自己的裝置上都通過完整檢查,合在一起卻切斷了展示櫃前方的兩個格子;重建時保留 uid 較小的那件,並把另一件送回。2066 由於拿走一件家具絕不會切斷通道,一層已經合法的樓層,不論以什麼順序重建都會原封不動地通過(這是我的推論;驗收測試會把它固定下來)。7.2 的起始家具是兩台裝置在玩家沒有任何動作時建立紀錄的唯一情況,所以每一件都依它的樓層與欄位得到固定的 uid,從 starter.room.1 到 starter.room-up.8,而不是隨機的:兩台冷啟動的裝置會建立同樣的十六個 uid,而不是十六對會被重建拆散在地板與收納匣之間的紀錄。在重建之前,repair 會把共用同一個 uid 的紀錄合併為一筆,就像 ModelContext.profile() 已經把同步留下的重複個人資料合併為 uid 最小的那一筆。77 當兩份複本不一致時,原因是某台裝置在另一台第一次啟動之前就移動了它的起始家具,合併時保留離開起始格的那一份;若兩份都被移動過,則保留列較小、再比欄較小的那一份(我的提案)。如果收起一件家具會刪除它的紀錄,那麼在一支手機上收起的起始家具,會在第二支手機冷啟動時回來;本規格書沒有解決這個情況。

擺放。 布置模式在房間的 RealityView 上疊一層 SwiftUI 收納匣。收納匣中的項目是 draggable 的,以它的種類作為 Transferable 酬載;房間的 view 是放置目的地。53 在 iOS 26 上,dropDestination(for:action:isTargeted:) 把 view 座標中的放置點交給 action,而這個點依鏡頭已知的整數像素比例對應到格子:cell = floor((point - mapOrigin) / tileSizeInPoints)。54 由於 Apple 的頁面把那個形式列為在 iOS 27.2 中棄用,規格書也會實作 DropSession 形式,並在 session 帶有位置時從中讀取;那一頁尚未讀過。54 擺好的家具以 SpatialTapGesture 選取、以 DragGesture 移動,一格一格吸附,佔地逐格著色,就像 Stardew 為它的圖塊著色那樣。565516

VoiceOver。 在布置模式中,每件擺好的家具都是一個輔助使用元素,例如沙發,三乘二,第 9 欄,第 8 列,帶有具名動作:向左移、向右移、向上移、向下移、轉向(僅限椅子)與收起。5758 一個名為「家具」的轉子列出房間裡的家具。60 收納匣項目的「擺放」動作會把家具放在離抵達點最近的合法格子上,並把焦點移到它身上。牆面與地板風格是可調整的元素:上下滑動即可切換。59 每一次拒絕都會以公告念出,例如無法移動:沙發會擋住書架。61 7.1 的規則是同一個函式,所以語音與觸控永遠不會不一致。

驗收條件。 一項單元測試確認 repair 不論以哪種順序都以同樣方式解決兩台裝置的重疊。雙裝置合併回歸測試,以兩種輸入順序執行這兩筆紀錄:在工坊今天原始配置的 room 上,裝置 A 在 (9,3) 放一張床,裝置 B 在 (6,4) 放一個床頭櫃;兩次擺放各自單獨通過完整檢查;兩者同步之後,repair 對兩種順序都回傳相同的房間,保留 uid 較小的那件,把另一件放進收納匣並附上指出展示櫃的訊息,而從抵達點出發的行走仍能到達展示櫃前方的兩個格子;把兩個 uid 對調後,則保留另一件。66 一項測試確認 repair 對一層 16 件的合法樓層不做任何更動,其紀錄被打亂成數種順序。一項測試確認一層樓上 17 件同步家具,除了上限之外彼此合法,回來時是 16 件,uid 最大的那件在收納匣中。一項測試確認兩台各自建立起始家具的冷啟動裝置,不論同步順序為何,最後每個起始格都只有一筆紀錄,每層 8 件,收納匣中空無一物;並確認在另一台啟動之前於一台裝置上移動過的起始家具,會停留在被移動到的位置。一項 UI 測試把收納匣項目拖到某個格子,並在該格找到一個 PlacedPiece。VoiceOver 動作處理函式的單元測試,直接在布置模型上呼叫,而不是透過 VoiceOver:「擺放」把一張椅子放在離抵達點最近的合法格子;向左移、向右移、向上移與向下移各自讓它移動一格,或附上理由拒絕;「轉向」循環切換它的朝向;「收起」把它送回收納匣;而每一次拒絕都產生畫面將會發出的公告文字。在 iOS 27 模擬器上,一項 UI 測試透過 XCUIVoiceOverService 開啟 VoiceOver,把焦點移到一件擺好的家具上,並斷言它念出的標籤,例如沙發,三乘二,第 9 欄,第 8 列。63 一次以 performAccessibilityAudit 對布置模式所做的稽核,不回報任何問題。62 一次在開啟 VoiceOver 的裝置上進行的手動檢查,記錄日期與 build,因為沒有任何 XCUIAutomation 呼叫能執行自訂動作、轉動轉子或讀取公告:從收納匣擺放一張椅子,用動作把它往四個方向移動、轉向、收起,用「家具」轉子在家具之間跳轉,並聽到每一次被拒絕的移動都被念出來。在這一切之前,先做一次試探:在模擬器中以鏡頭的實際比例把一個酬載放到房間上,記錄算出的格子與手指底下的格子,因為這個對應尚未測試過。

7.8 整理工作

  • WORLD.md 寫入房間規則,附上本規格書的數字,讓設計文件說出世界文章已經公布的內容。6710
  • interiors.py 的 docstring 與表格要一致,二擇一:要嘛 docstring 寫明小件家具是例外,要嘛把小件家具重畫成兩個圖塊。規格書保留它們。9

不在本規格書範圍內

  • 書架自身位置以外的堆疊:桌上不放家具,沒有玩偶放在桌上那種檯面。檯面的構想是上限與行走檢查驗證可行之後的下一步。
  • 被踩到時會動作的家具:沒有音效墊、沒有彈簧、沒有溜滑梯。
  • 半格擺放。網格維持在完整的 16 像素格子。
  • 布置展示大廳。它的展台仍屬於每週的展示。
  • 家具布置比賽。每週的大廳就是比賽,依它自己的規則評審。
  • 平面圖:房屋的形狀不會改變。
  • 在共同廣場之外拜訪陌生人,以及主人自身總數之外的任何房間排名。

家具布置,永遠不做的事

  1. 任何部分都不使用系列作品的詞彙:不用基地、布置用電腦、評鑑協會、紀錄混合,也不用任何裝飾的名稱。只用 Kiradex 自己的用語。本文提到這些遊戲,是在陳述已發行作品的事實,而不是當作功能。
  2. 不畫任何仿照遊戲裝飾的家具:沒有生物玩偶或坐墊、沒有任何球形的東西、沒有生物海報、沒有以招式命名的墊子。工坊的家具是家用家具,也一直會是。
  3. 任何房間裡都沒有生物。植物就是植物。
  4. 沒有購買、隨機或限量的家具(7.5)。
  5. 房間裡沒有任何東西會被瞄準、投擲、捕捉、召喚、放出來戰鬥或騎乘。
  6. App 與工坊中不含任何來自反編譯的程式碼或資料。反編譯原始碼是用來研究與量測的;上述規則都以 Kiradex 自己的用語重新表述。

驗收條件。 以一份由第 1 項的系列詞彙,加上「decoration」、「doll」、「Happy Home」與「Academy」所組成的字詞清單,對字串目錄、工坊的 PIECES 名稱與伺服器的資料表做 grep,結果為空。

重點整理

如果您負責繪製美術

  • 依角色畫格或格子的寬度繪製家具。《綠寶石》的玩家畫格是一個 16 像素的格子寬,裝飾平均 2.43 格;Kiradex 的收藏者站在兩個圖塊寬的格子裡,家具平均 3.04 格。2729
  • 讓型錄以小件為主,再加上幾件大型的定錨家具:《綠寶石》120 件裝飾中有 71 件是單格,而它的桌子與 3 乘 3 的墊子就是讓小件立於其上的定錨。224
  • 以規則與色階繪製壁紙與地板,讓一種風格就是一個繪製器加一套調色盤,而不是一幅新畫:兩種牆面各三道色階、三種地板各兩道色階,給了 Kiradex 各六種。64

如果您負責打造引擎

  • 把一件擺放的家具儲存為種類、格子與朝向。《綠寶石》以兩個位元組儲存一件,一座基地 32 個位元組。1112
  • 先逐格檢查擺放,再走一遍。《綠寶石》的逐格檢查從不問門口是否仍然通得到任何東西;在 Kiradex 的一樓,229,891 組合法的額外家具組合中,有 713 組在沒有蓋住樓梯、主人或展示品的情況下把它們切斷。420
  • 搭配 CloudKit 同步時,SwiftData 無法強制唯一性約束,所以「每格一件」就是一個純粹的修復函式,在每台裝置上挑出同一個勝出者。讓它以固定順序,讓整個房間重新通過完整的擺放檢查(包括行走):兩個各自在自己手機上通過的編輯,合在一起就可能把展示品切斷。225266
  • 給 VoiceOver 動詞,而不是一張要逐格滑過的網格:具名的移動動作、一個列出家具的轉子,以及每一次拒絕都有一則公告。586061

如果您負責設計遊戲循環

  • 限制的是擺放的數量,而不是持有的數量:《綠寶石》擺 16 件、存 150 件。上限就是構圖。2
  • 在固定的日子評分並列出每一項來源,只在房屋擴建時提高門檻,而且絕不懲罰錯過的一週:wiki 的房屋頁面沒有指明是哪一款遊戲,說房屋至少一週疏於照顧就會出現蟑螂,而 New Horizons 的 HHA 表每隻蟑螂扣 2,500 分。56
  • 靠遊玩讓型錄成長。《綠寶石》120 件裝飾中有 24 件從不以金錢販售,另有兩件只能用走路收集的火山灰換取;Stardew 在第一次擴建後販售一份 200,000g 的型錄,之後再以 0g 販售它所列出的家具;而對兒童 App 來說,Habbo 稀缺與隨機的家具就是反例。14151617
  • 訪客只看不碰,每天留下一個讚,累積在一個永不下降的總數上。51819

常見問題

玩家在一個房間裡應該能擺放多少件物品?

多到足以構圖,少到需要取捨。《寶可夢 綠寶石》把一座基地的擺放上限定為 16 件裝飾、臥室 12 件,大約是一座平均基地 60.2 個可行走格子的四分之一。依 Nookipedia 的說法,Animal Crossing: New Horizons 每個房間允許 150 件,包括牆面與天花板物品;Final Fantasy XIV 自 Patch 7.5 起,室內依住宅大小允許 150 到 600 件。對於沒有牆面或天花板圖層的 2D 房間,我的門檻是上限維持在每三個地板格不到一件,《綠寶石》的 0.27 略低於這個門檻;Kiradex 提議的 16 件則是每個地板格 0.13。2579

《寶可夢 綠寶石》的裝飾系統是怎麼運作的?

它是資料。120 件裝飾各帶有一個類別、一個 1 到 9 格的形狀、一個價格,以及五種權限之一(實心地板、可踩過的地板、後方、牆面或 sprite)。CanPlaceDecoration 會檢查每個被覆蓋的格子:普通地板、沒有物件,以及在玩家所站的那一格上沒有非一般圖層類型的圖塊;海報需要牆面格;玩偶與坐墊需要承托格,大多數小格或大格都可以,1 乘 2 的玩偶則需要大格。有 18 件裝飾提供小型承托格:七張桌子、三塊磚、一個輪胎與七張墊子。擺放的裝飾各以一個 ID 與一個位置位元組儲存。2411

住家評鑑協會如何為房間評分?

依 Nookipedia 的表格,而這些表格沒有為 New Horizons 註明任何來源:大多數星期天早上以郵件寄出評鑑,等級為 B、A 與 S,S 級門檻隨房屋擴建從 15,000 提高到 90,000,一個房間的物品達到 6、10、15 與 20 件時各給 1,000 分,系列、套組、類別、主色與風水有逐件加分,蟑螂、垃圾與面向牆壁的物品則會扣分。逐件分數在 wiki 上是尚待補充的段落。6

搭配 CloudKit 同步時,SwiftData 能強制每個網格格子只放一件物品嗎?

用 #Unique 不行。Apple 關於同步 SwiftData 的指南,把唯一性約束列在「that CloudKit doesn’t support natively」(CloudKit 原生不支援)的功能之中,而且 CloudKit 要求每個關聯都是選擇性的。為每個屬性設定預設值,不讓任何東西是唯一的,並用一個純函式解決兩台裝置放在同一格上的家具:它在每次讀取時執行,在每台裝置上挑出同一個勝出者。讓這個函式以固定順序,讓房間重新通過完整的擺放檢查,而不只是重疊測試,因為兩個各自合法的編輯,合在一起可能違反某條規則。225166

要怎麼讓拖放式的家具網格能讓 VoiceOver 使用?

讓每件擺好的家具成為一個輔助使用元素,標籤中包含它的尺寸與格子;給它往四個方向各移動一格、轉向與收起的具名動作;加入一個列出房間家具的轉子;並在每一次移動被拒絕時連同理由發出公告。Apple 的 accessibilityAction(named:_:)、accessibilityRotor(_:entries:) 與 AccessibilityNotification.Announcement 涵蓋了每個部分,而 performAccessibilityAudit 會在 UI 測試中檢查畫面。稽核不會按下具名動作,而在 Xcode 27.0 中也沒有任何 XCUIAutomation 呼叫能做到,所以請直接測試動作的處理函式,並在開啟 VoiceOver 的情況下以手動方式檢查動作、轉子與公告。5860616263

兒童遊戲應該販售家具嗎?

我的解讀是,對分級 9+ 的 App 來說,不應該:一個靠購買、隨機或稀缺驅動的裝飾循環,會招來遊戲不需要的戰利品箱與施壓問題。《綠寶石》120 件裝飾中有 24 件從不以金錢販售(它們是獎品、對戰點數兌換品與贈品),另有兩件只能用走路收集的火山灰換取;而 Stardew 在第一次房屋擴建完成後,以 200,000g 販售家具型錄,之後再以 0g 販售它所列出的家具,不過仍有一些家具只能從博物館、節慶與其他來源取得。Habbo 以購買為主的經濟,加上稀有家具與隨機的可敲開家具,則展示了另一個極端。14151617

為什麼這些遊戲的訪客只有唯讀權限?

依我的解讀,因為房間是主人的表達。Animal Crossing 的訪客不能「modify the interior in any way」(以任何方式改動室內),Habbo 的訪客沒有主人的權限就不能放下或移動家具,而 Club Penguin 的擁有者可以布置自己的冰屋,「but not other player’s igloos」(但不能布置其他玩家的冰屋)。訪客得到的,是一個會被計入的標記:Club Penguin 每個設計每天一個讚,累積在一個永不減少的總讚數上。518819

本站相關文章:像素藝術建築:iPhone 上的房屋、廳堂與室內打造了這份規格書所要布置的室內,它的室內一節描述了被 build 26 取代的 14 乘 11 房間;iPhone 上的像素藝術世界是十六件的上限、唯讀拜訪、每日一讚與星期天報告首次公布的地方;像素藝術人物:iPhone 上的角色與角色創建器介紹了那位以自身寬度決定家具節奏的收藏者;像素藝術的城鎮布局:從真新鎮到鵜鶘鎮的實測量測了這些房屋所在的城鎮;像素藝術的動態:iPhone 上的走路、鏡頭與門介紹了訪客走進來時的行走與門;而像素藝術的道路:iPhone 上的地圖連接與區域則介紹了城鎮之外的土地。

資料來源


  1. pret,pokeemerald/include/constants/global.h(SECRET_BASES_COUNT 20、DECOR_MAX_SECRET_BASE 16、DECOR_MAX_PLAYERS_HOUSE 12),commit 731ad5b(2026年10月1日),2026年10月5日閱讀,https://github.com/pret/pokeemerald/blob/master/include/constants/global.h。 ↩↩↩↩↩

  2. 作者的量測,2026年10月5日:measure_emerald_decor.py,存放於作者為本文準備的證據資料夾中,輸出存為 measure_emerald_decor.out,對 pret 的 pokeemerald 在 commit 731ad5b 的淺層複製執行。它從 include/constants/global.h 讀取上限;從 src/data/decoration/header.h 讀取 120 件裝飾(不含 DECOR_NONE)的類別、形狀、權限與價格;從 include/global.h 讀取各類別的持有清單陣列;從 data/tilesets/secondary/secret_base/metatile_attributes.bin 讀取每個裝飾 metatile 的行為位元組;並從 data/layouts/layouts.json 及其 map.bin 讀取 24 種基地布局與臥室,以碰撞位元計算可行走格子。每件裝飾的格數取自形狀名稱;上限除以平均可行走格子 16 / 60.2 = 0.27。 ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩

  3. pret,pokeemerald/src/data/decoration/header.h(gDecorations[]:每件裝飾的名稱、權限、形狀、類別、價格與圖塊;第 0 項 DECOR_NONE),commit 731ad5b,2026年10月5日閱讀,https://github.com/pret/pokeemerald/blob/master/src/data/decoration/header.h。 ↩↩↩↩

  4. pret,pokeemerald/src/decoration.c(CanPlaceDecoration、DecorationItemsMenuAction_AttemptPlace、isPlayerRoom、gText_CantPlaceInRoom;sprite 裝飾的承托格檢查),commit 731ad5b,2026年10月5日閱讀,https://github.com/pret/pokeemerald/blob/master/src/decoration.c。在 sprite 的情況下,1 乘 2 的裝飾需要 MetatileBehavior_HoldsLargeDecoration,其他則小型或大型承托格皆可;IsntInitialPosition 只在裝飾於該格的圖塊圖層類型不是 METATILE_LAYER_TYPE_NORMAL 時,才拒絕玩家的起始格。「函式中沒有任何一處檢查通道」是作者對它的解讀。 ↩↩↩↩↩↩↩↩↩↩

  5. Nookipedia,「Player house」(每個房間的上限:Wild World 24、City Folk 64、New Leaf 48、New Horizons 150「including wall and ceiling-mounted furniture」;New Horizons 的擴建表與房間尺寸;「only the main room can be expanded in size」;2.0 版起的主題牆;訪客不得「modify the interior in any way」;玩家若「for at least one week」疏於照顧房屋,家中就會出現蟑螂),2026年10月5日擷取並存為房間證據資料夾中的 nook-player-house.html 與 .txt,https://nookipedia.com/wiki/Player_house。每塊圖塊 4.2 件是作者的算術(150 / 36)。 ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩

  6. Nookipedia,「Happy Home Academy」(星期天的評鑑;每次擴建的 B、A 與 S 等級;New Horizons 的加分與扣分;New Horizons 逐件分數一節的「This section is a stub」;獎勵表的附註「Includes data sourced from the Data Spreadsheet for Animal Crossing New Horizons」),2026年10月5日擷取並存為 nook-hha.html 與 .txt,https://nookipedia.com/wiki/Happy_Home_Academy。頁面上的加分與扣分表沒有註明出處。 ↩↩↩↩↩↩↩↩↩↩↩↩↩

  7. Square Enix,The Lodestone,Final Fantasy XIV Patch 7.5 更新說明(2026年5月13日更新;住宅一節調整前後的室內與室外家具上限;400 件家具的顯示說明;「Furnishings from the FFXIV Furnishing Design Contest have been added」),2026年10月5日擷取並存為 ffxiv-patch-7-5.html 與 .txt,https://eu.finalfantasyxiv.com/lodestone/topics/detail/9beb8a7b5c46944cd80ed92b2b8e972395fbea80/。住宅遊玩指南 https://na.finalfantasyxiv.com/lodestone/playguide/contentsguide/housing/ 同一天回傳 HTTP 404;本文沒有為地塊尺寸提供來源。 ↩↩↩↩↩↩↩↩↩↩

  8. Club Penguin Wiki(Fandom),「Furniture」(大多數家具 99 件的持有上限;「igloos have a limit preventing more than 99 units of furniture being placed at once」;擁有者可布置自己的冰屋,「but not other player’s igloos」;2012年7月26日給非會員的六件免費物品),透過該 wiki 的 MediaWiki API 閱讀,並於2026年10月5日存為 cp-fandom-furniture.html 與 .txt,https://clubpenguin.fandom.com/wiki/Furniture。單一來源。 ↩↩↩↩↩↩

  9. 作者的量測,2026年10月5日:measure_kiradex_interiors.py,存放於房間證據資料夾,輸出為 measure_kiradex_interiors.out,以 Python 的 ast 解析 Kiradex 程式庫的 scripts/forge/interiors.py(該檔案從未被匯入或執行):PIECES 的 23 個條目及其寬、高與獨立擺放旗標;ROOM、ROOM_UP 與 HALL 字母地圖及其家具清單與位置;地板格以字母 o t s r 計算;可移動家具不包括 stairs、stairs_down 與 pillar。 ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩

  10. Blake Crosley,「Pixel-Art Worlds on iPhone: What the 16-Bit Masters Knew」,blakecrosley.com,2026年10月3日(房間規則:「a furnishing cap of sixteen」、唯讀拜訪、一天一次的讚、永不減少的總數、一份「with its point sources listed」的星期天報告;同一段中的「Patron flair stays on the name-plate and the room skin」;Palworld 訴訟中點名的專利;每個 texel 對應裝置像素的鏡頭適配),https://blakecrosley.com/blog/pixel-art-world-on-iphone。 ↩↩↩↩↩↩↩↩↩↩

  11. pret,pokeemerald/include/global.h(秘密基地紀錄中的 u8 decorations[DECOR_MAX_SECRET_BASE] 與 u8 decorationPositions[DECOR_MAX_SECRET_BASE];存檔中各類別的裝飾持有陣列),commit 731ad5b,2026年10月5日閱讀,https://github.com/pret/pokeemerald/blob/master/include/global.h。 ↩↩↩↩↩↩↩

  12. pret,pokeemerald/src/secret_base.c(讀取裝飾位置時 x 在高半位元組、y 在低半位元組),commit 731ad5b,2026年10月5日閱讀,https://github.com/pret/pokeemerald/blob/master/src/secret_base.c。 ↩↩↩

  13. pret,pokeemerald/include/decoration.h(enum DecorationPermission 及其註解「The nomenclature here describes collision and placement permissions, in that order.」;enum DecorationShape,其中 DECORSHAPE_3x1 與 DECORSHAPE_1x3 標示為未使用),commit 731ad5b,2026年10月5日閱讀,https://github.com/pret/pokeemerald/blob/master/include/decoration.h。 ↩↩↩

  14. 作者的量測,2026年10月5日:measure_emerald_sources.py,存放於房間證據資料夾,輸出為 measure_emerald_sources.out,在 pokeemerald 731ad5b 中搜尋每一個 DECOR_ 常數,範圍包括 data/maps/*/scripts.inc 的 pokemartdecoration 清單、獎品兌換處的腳本、src/data/battle_frontier/battle_frontier_exchange_corner.h 中對戰設施的兌換表,以及 data/ 底下任何地方 givedecoration 與 adddecoration 的第一個參數:90 件在五家商店以金錢販售,3 件獎品兌換,15 件對戰點數兌換,10 件由腳本贈送,24 件從不以金錢販售,6 件不在上述任何一處。這六件中有兩件是玻璃工坊的那一對(註 15);其餘四件,三個傳說玩偶與另一個玩偶,未追查。 ↩↩↩↩↩↩↩↩

  15. pret,pokeemerald/data/maps/Route113_GlassWorkshop/scripts.inc(PRETTY_CHAIR_PRICE, 6000、PRETTY_DESK_PRICE, 8000,以 VAR_ASH_GATHER_COUNT 支付;Route113_GlassWorkshop_EventScript_NotEnoughAsh 與 _NotEnoughAshForItem 把價格減去數量,並把差額顯示為還要走的步數;在 src、data 與 include 中搜尋,VAR_ASH_GATHER_COUNT 除了定義之外只用於該處與 field_tasks.c)與 pokeemerald/src/field_tasks.c(攜帶袋子穿過火山灰草叢時每步一份,最多 9999),commit 731ad5b,2026年10月5日閱讀,https://github.com/pret/pokeemerald/blob/master/data/maps/Route113_GlassWorkshop/scripts.inc 與 https://github.com/pret/pokeemerald/blob/master/src/field_tasks.c。 ↩↩↩↩↩↩↩

  16. Stardew Valley Wiki,「Furniture」,修訂版 193159(Robin 與旅行貨車「offer a random selection of furniture each day they are open」;「After the first Farmhouse upgrade, Robin also offers the Furniture Catalogue for sale」;0g「in unlimited quantity」;型錄表中家具型錄那一列,在木匠鋪(Carpenter’s Shop)以 200,000g 販售;「Some furniture can be obtained only by donating items to the Museum」、節慶、賭場與其他來源;綠色與紅色的擺放方塊),2026年10月5日擷取並存為 sdv-furniture.html 與 .txt,https://stardewvalleywiki.com/Furniture。 ↩↩↩↩↩↩↩↩↩↩↩

  17. Sulake,Habbo 說明中心,「Furni Definitions」(為交易者保留「4-12 months」的活動家具、「will never be re-released」的稀有家具、可敲開的稀有家具、「in limited quantities」的 LTD),文章內文於2026年10月5日存入房間證據資料夾中的 habbo-articles.txt,https://help.habbo.com/hc/en-us/articles/360011621699-Furni-Definitions。 ↩↩↩↩↩↩↩

  18. Sulake,Habbo 說明中心,「What is Furni?」(「To buy furni you need credits, diamonds or duckets」;「You can’t drop or manipulate furni in other Habbo’s rooms unless you’re a member of a group」),文章內文於2026年10月5日存入 habbo-articles.txt,https://help.habbo.com/hc/en-us/articles/360011512940-What-is-Furni。 ↩↩↩↩↩↩↩

  19. That Penguin Game,「Your Igloo」(Club Penguin 說明頁的轉載;其出處未經查證:「You can Like any igloo design once every day!」;個別讚數與總讚數;「Likes are permanent, so your Grand Total Likes will never decrease, even if you delete an igloo design!」;Everyone 與 Friends 設定),2026年10月5日擷取並存為 tpg-your-igloo.html 與 .txt,https://thatpenguingame.com/club-penguin-help/game-help/your-igloo/。單一來源。 ↩↩↩↩↩↩

  20. 作者的量測,2026年10月5日:measure_path_rule.py,與本文草稿放在一起,輸出為 measure_path_rule.out 與 path_rule.json。它以 ast 解析 Kiradex 程式庫的 scripts/forge/interiors.py,像 export() 那樣重建匯出的字母,從每層樓的抵達點出發在 otsrUD(工坊 check() 的可行走集合)上往四個方向行走,並回報樓梯、主人與每個已布置位置的前方格(越過書架與牆帶後正南方的第一個格子)。接著它在 room 中嘗試一件額外獨立家具(10 種圖:椅子算一次、床算一次)在避開抵達、樓梯與主人格的空地板上的每一種擺法(733 種擺法;44 種切斷某個目標,全部都是直接蓋住它),以及兩件避開所有目標、彼此不重疊的額外家具的每一種組合(689 種單件擺法,229,891 種組合;713 種切斷某個目標)。 ↩↩↩↩↩↩↩↩↩↩

  21. 作者的計算,2026年10月5日:score_max.py,位於草稿資料夾的 figures/,輸出為 score_max.out 與 score_max.json:規格書的評分表以兩層各 16 件、每一行都達到上限加總(3,200 + 3,000 + 6,400 + 9,600 + 500 + 600 + 300 = 23,600),以及一層 8 件、沒有套組、色階或位置加分(800 + 500 = 1,300)。同一支腳本以表格本身的各行,為訂出提案房屋等級門檻的三棟參考房屋評分:兩層各 10 件且書架填滿,4,500;兩層各 16 件且書架、展示櫃與牆板都填滿,7,600;同一棟房屋每層再加 8 件套組與 70% 單一色階,14,000;以及起始房屋,兩層各 8 件,2,600。 ↩↩↩↩

  22. Apple Developer Documentation,「Syncing model data across a person’s devices」(SwiftData;「does include a small number of features that CloudKit doesn’t support natively, such as unique constraints and nonoptional relationships」;「CloudKit requires all relationships to be optional」),文件 JSON 於2026年10月5日存為 apple-swiftdata-cloudkit-sync.html 與 .txt,https://developer.apple.com/documentation/swiftdata/syncing-model-data-across-a-persons-devices。 ↩↩↩↩↩↩

  23. pret,pokeemerald/src/new_game.c(NewGameInitData 呼叫 ClearDecorationInventories())與 pokeemerald/src/decoration_inventory.c(ClearDecorationInventory 把每個類別的每個欄位設為 DECOR_NONE),commit 731ad5b,2026年10月5日閱讀,https://github.com/pret/pokeemerald/blob/master/src/new_game.c。 ↩

  24. 作者的圖表,2026年10月5日:make_figures.py,位於草稿資料夾的 figures/,它讀取 pokeemerald 731ad5b 的 src/data/decoration/header.h、註 1、5、8 與 7 所引用的上限、path_rule.json 與 score_max.json,將每個總數與上述量測逐一比對斷言,並輸出 emerald-decor-sizes.svg、caps.svg、room-path-rule.svg 與 score-sources.svg。佔地類別(1 格、2 格、4 到 6 格、8 或 9 格)依覆蓋的格數將形狀分組。調色盤以 dataviz 驗證器檢查;不含任何遊戲美術。 ↩↩↩↩↩

  25. pret,pokeemerald/include/constants/metatile_behaviors.h(MB_SECRET_BASE_* 行為、MB_SLIDE_SOUTH、MB_HOLDS_SMALL_DECORATION、MB_HOLDS_LARGE_DECORATION),commit 731ad5b,2026年10月5日閱讀,https://github.com/pret/pokeemerald/blob/master/include/constants/metatile_behaviors.h。 ↩

  26. pret,pokeemerald/data/layouts/layouts.json(24 個 LAYOUT_SECRET_BASE_* 布局與兩棟玩家住家的二樓,含寬與高)以及每個布局的 map.bin,commit 731ad5b,2026年10月5日閱讀,https://github.com/pret/pokeemerald/blob/master/data/layouts/layouts.json。寬、高與面積的範圍是作者依量測輸出整理的。 ↩

  27. 作者的量測,2026年10月5日:measure_character_cell.py,存放於房間證據資料夾,輸出為 measure_character_cell.out,讀取 pokeemerald 的 graphics/object_events/pics/people/brendan/walking.png(144 乘 32)與 decorating.png(16 乘 32)的 PNG 標頭,以及 Kiradex 程式庫 Kiradex/Resources/World/characters.json 中的格子(32 乘 40)、figureTop(7)與 underFeet(2)。這支腳本量的是格子與人物的高度(31 像素),而不是畫出來的人物寬度。 ↩↩↩↩↩

  28. pret,pokeemerald/src/data/items.h(ITEM_SOOT_SACK,.fieldUseFunc = ItemUseOutOfBattle_CannotUse)與 pokeemerald/src/data/text/item_descriptions.h(sSootSackDesc,一段固定的三行說明),commit 731ad5b,2026年10月5日閱讀,https://github.com/pret/pokeemerald/blob/master/src/data/items.h。 ↩↩

  29. Nookipedia,「Furniture」(尺寸「ranging from 1.0×0.5」到 3 乘 3;以半個圖塊推動;New Horizons 的家具類型與 2.0 版起的天花板裝飾;截至 3.0.2 版共 2,076 件家具,其中 1,074 件於更新中加入;一個系列是「around 10 furniture items, as well as a matching wallpaper and flooring」),2026年10月5日擷取並存為 nook-furniture.html 與 .txt,https://nookipedia.com/wiki/Furniture。 ↩↩↩↩

  30. Nookipedia,「Wallpaper」(截至 1.9.0 版,New Horizons 有 262 種壁紙),2026年10月5日擷取並存為 nook-wallpaper.html 與 .txt,https://nookipedia.com/wiki/Wallpaper。 ↩

  31. Nookipedia,「Flooring」(New Horizons 有 215 種),2026年10月5日擷取並存為 nook-flooring.html 與 .txt,https://nookipedia.com/wiki/Flooring。 ↩

  32. Nookipedia,「Luna」(New Leaf 一節:「any changes will not be saved and items cannot be brought back to the real world」;New Horizons 一節:她的服務「work the same as they do in New Leaf」,以及每次上傳可得一張價值 5,000 鈴錢的 Dream Bell Exchange Ticket),2026年10月5日擷取並存為 nook-luna.html 與 .txt,https://nookipedia.com/wiki/Luna。 ↩↩

  33. Sulake,Habbo 說明中心,「Rooms」(「You can own 200 rooms.」;「Wallpaper and flooring are stuck down」),2026年10月5日擷取並存為 habbo-rooms.html 與 .txt,https://help.habbo.com/hc/en-us/articles/360011512640-Rooms。 ↩↩↩↩

  34. Sulake,Habbo 說明中心,「What is Builders Club?」(家具額度每個會員月「increased by 250」;在平面圖編輯器中儲存自訂房間布局)與「What can I buy in Habbo?」(「Furni limits never go down.」),文章內文於2026年10月5日存入 habbo-articles.txt,https://help.habbo.com/hc/en-us/articles/360011621659-What-is-Builders-Club 與 https://help.habbo.com/hc/en-us/articles/360011620599-What-can-I-buy-in-Habbo。 ↩

  35. Sulake,Habbo 說明中心,「Player to player trading in Habbo」(需要交易通行證;「There is no guide to what items of furni are worth」),文章內文於2026年10月5日存入 habbo-articles.txt,https://help.habbo.com/hc/en-us/articles/4408726781842-Player-to-player-trading-in-Habbo。 ↩↩

  36. Sulake,Habbo 說明中心,「Guidelines on user created games in Habbo」(「Placing more than three chance elements will disable all randomiser functions in the room.」),文章內文於2026年10月5日存入 habbo-articles.txt,https://help.habbo.com/hc/en-us/articles/360011513060-Guidelines-on-user-created-games-in-Habbo。 ↩

  37. Sulake,Habbo 說明中心搜尋 API,「furni」、「stack」與「floor plan」的結果集,2026年10月5日存為房間證據資料夾中的 habbo-search-furni、habbo-search-stack 與 habbo-search-floorplan(.html 與 .txt);沒有任何一個結果提到網格尺寸、堆疊高度或每個房間的家具上限。 ↩

  38. billsonnn,nitro-react/src/components/floorplan-editor/common/Constants.ts(MAX_NUM_TILE_PER_AXIS = 64、HEIGHT_SCHEME = 'x0123456789abcdefghijklmnopq',空白的 x 之後共 27 個高度級),一個第三方開源 Habbo 用戶端,依建築那篇文章於2026年10月3日的引用,https://github.com/billsonnn/nitro-react/blob/main/src/components/floorplan-editor/common/Constants.ts。堆疊高度工具的範圍(0 到 40,以 0.01 為步進)來自我先前的筆記,讀自同一個用戶端,2026年10月5日沒有重讀。 ↩

  39. Blake Crosley,「Pixel-Art Structures: Houses, Halls and Interiors on iPhone」,blakecrosley.com,2026年10月3日(室內「each 14 by 11, with a two-row wall band」;build 22、23,以及晚間補充中的 35;「Rooms have no wallpaper or floor choice and no placeable furniture; the house is dressed from the collection and nothing else」;room:npc-… 傳送點;物件網格;Stardew 的農舍內部「10×7」,註 54;Nitro 常數,註 62),https://blakecrosley.com/blog/pixel-art-structures-on-iphone。 ↩↩↩↩↩↩↩↩

  40. 作者的量測,2026年10月5日:measure_sdv_catalogue.py,存放於房間證據資料夾,輸出為 measure_sdv_catalogue.out:在存檔的 Furniture 頁面(修訂版 193159)上,依每一列的名稱連結逐區塊計算家具列數,跳過列使用另一種標記的 Movie Posters 區塊,以及 References、History 與 Exploits 區塊;在存檔的 Wallpaper(修訂版 193801)與 Flooring(修訂版 190285)頁面上,以不同圖示計算壁紙與地板數;家具型錄作為來源的列數依其連結計算。這些是 wiki 列數與圖示數,不是遊戲資料檔的數量。 ↩↩↩↩

  41. Stardew Valley Wiki,「Wallpaper」,修訂版 193801(「Wallpapers are single-use and do not stack. They cover the whole room they’re placed in」;「the character must be facing up by its wall」),2026年10月5日擷取並存為 sdv-wallpaper.html 與 .txt,https://stardewvalleywiki.com/Wallpaper。 ↩↩↩↩

  42. Stardew Valley Wiki,「Flooring」,修訂版 190285,2026年10月5日擷取並存為 sdv-flooring.html 與 .txt,https://stardewvalleywiki.com/Flooring。 ↩

  43. Club Penguin Wiki(Fandom),「Igloo」(歷史:2005年11月1日,非會員玩家可擁有冰屋,但他們「were unable to decorate them with furniture」;2012年7月26日,新的冰屋體驗與「a set of six items available without membership」),透過 MediaWiki API 閱讀,並於2026年10月5日存為 cp-fandom-igloo.html 與 .txt,https://clubpenguin.fandom.com/wiki/Igloo。它所引用的封存官方部落格文章,在2026年10月5日兩度從 Wayback Machine 回傳 HTTP 429。單一來源。 ↩↩

  44. Club Penguin Wiki(Fandom),「Igloo Contests」(第一場比賽刊於第 9 期,2005年12月15日,一位優勝者獲得 5,000 枚金幣;優勝者於「After one to four weeks」公布;自 2008 年萬聖節比賽起由角色擔任評審;自 2009 年起的提交按鈕;最後一場比賽於 2012 年 12 月;2008 年 Ye Olde Igloo Contest 的十位優勝者各得 25,000 枚金幣、二十位入圍者各得 15,000 枚),透過 MediaWiki API 閱讀,並於2026年10月5日存為 cp-fandom-contests.html 與 .txt,https://clubpenguin.fandom.com/wiki/Igloo_Contests。單一來源。 ↩

  45. Tristan Donovan,「The Replay Interviews: Will Wright」,Game Developer,2011年5月23日,2026年10月5日擷取並存為 gd-replay-wright.html 與 .txt,https://www.gamedeveloper.com/business/the-replay-interviews-will-wright。沒有找到 Wright 在 1999 或 2000 年談建造模式的任何錄音或錄影演講。 ↩↩

  46. The Sims Wiki(Fandom),「Buy mode」(「purchase items from an object catalog and place them on the current lot」;The Sims 與 The Sims 2 依功能分類;綠色與紅色佔地那一句,在頁面上標註於 The Sims 2 與 The Sims 3),透過 MediaWiki API 閱讀,並於2026年10月5日存為 sims-fandom-buymode.html 與 .txt,https://sims.fandom.com/wiki/Buy_mode。單一來源。 ↩↩↩

  47. The Sims Wiki(Fandom),「Environment」(Room 與 Environment 動機,它「analyzes the design and content of the room that the Sim is currently standing in」),透過 MediaWiki API 閱讀,並於2026年10月5日存為 sims-fandom-environment.html 與 .txt,https://sims.fandom.com/wiki/Environment。單一來源。 ↩

  48. 作者的擷取,2026年10月5日:measure_apple_docs.py,存放於房間證據資料夾,輸出為 measure_apple_docs.out:從每一份存檔的 Apple 文件 JSON 頁面擷取標題、宣告、摘要與平台資訊。為 dropDestination(for:action:isTargeted:) 引用的棄用欄位,也讀自同一份存檔的 JSON。 ↩

  49. Apple Developer Documentation,Model()(SwiftData 巨集;「Converts a Swift class into a stored model that’s managed by SwiftData」;iOS 17.0),JSON 於2026年10月5日存為 apple-swiftdata-model.html 與 .txt,https://developer.apple.com/documentation/swiftdata/model()。 ↩

  50. Apple Developer Documentation,Unique(_:)(SwiftData 巨集;「Specifies the key-paths that SwiftData uses to enforce the uniqueness of model instances」;iOS 18.0),JSON 於2026年10月5日存為 apple-swiftdata-unique.html 與 .txt,https://developer.apple.com/documentation/swiftdata/unique(_:)。 ↩

  51. Kiradex 程式庫,Kiradex/Models/CardCopy.swift(doc 註解:「every property has a default and nothing is unique, for CloudKit」),2026年10月5日閱讀。 ↩↩↩

  52. Kiradex 程式庫,Kiradex/Models/PriceHistory.swift(doc 註解:「Shaped for CloudKit like CollectionEntry: defaults everywhere, nothing unique」;PriceBook 把兩台裝置的歷史合併「into the one with the smallest uid」),2026年10月5日閱讀。 ↩↩↩↩

  53. Apple Developer Documentation,draggable(_:)(SwiftUI;「Activates this view as the source of a drag and drop operation」;Transferable 酬載;iOS 16.0),JSON 於2026年10月5日存為 apple-swiftui-draggable.html 與 .txt,https://developer.apple.com/documentation/swiftui/view/draggable(_:)。 ↩↩

  54. Apple Developer Documentation,dropDestination(for:action:isTargeted:)(SwiftUI;型別為 ([T], CGPoint) -> Bool 的 action,第二個參數為「the drop location in this view’s coordinate space」;iOS 16.0;iOS 的平台條目列出 deprecatedAt 27.2,訊息為「Use dropDestination(for:isEnabled:action:) with an action that takes a DropSession parameter instead.」;DropSession 的參照,「A description of a drop that is in progress」),JSON 於2026年10月5日存為 apple-swiftui-dropdestination.html 與 .txt,https://developer.apple.com/documentation/swiftui/view/dropdestination(for:action:istargeted:)。 ↩↩↩↩↩

  55. Apple Developer Documentation,DragGesture(SwiftUI;「A dragging motion that invokes an action as the drag-event sequence changes」;iOS 13.0),JSON 於2026年10月5日存為 apple-swiftui-draggesture.html 與 .txt,https://developer.apple.com/documentation/swiftui/draggesture。 ↩↩

  56. Apple Developer Documentation,SpatialTapGesture(SwiftUI;「A gesture that recognizes one or more taps and reports their location」;iOS 16.0),JSON 於2026年10月5日存為 apple-swiftui-spatialtap.html 與 .txt,https://developer.apple.com/documentation/swiftui/spatialtapgesture。 ↩↩

  57. Apple Developer Documentation,accessibilityElement(children:)(SwiftUI;iOS 13.0),JSON 於2026年10月5日存為 apple-swiftui-accessibilityelement.html 與 .txt,https://developer.apple.com/documentation/swiftui/view/accessibilityelement(children:)。 ↩↩

  58. Apple Developer Documentation,accessibilityAction(named:_:)(SwiftUI;「Actions allow assistive technologies, such as the VoiceOver, to interact with the view by invoking the action」;iOS 16.0),JSON 於2026年10月5日存為 apple-swiftui-accessibilityaction-named.html 與 .txt,https://developer.apple.com/documentation/swiftui/view/accessibilityaction(named:_:)。 ↩↩↩↩

  59. Apple Developer Documentation,accessibilityAdjustableAction(_:)(SwiftUI;接收 AccessibilityAdjustmentDirection 的處理函式;iOS 13.0),JSON 於2026年10月5日存為 apple-swiftui-adjustable.html 與 .txt,https://developer.apple.com/documentation/swiftui/view/accessibilityadjustableaction(_:)。 ↩↩

  60. Apple Developer Documentation,accessibilityRotor(_:entries:)(SwiftUI;「Create an Accessibility Rotor with the specified user-visible label」;iOS 16.0),JSON 於2026年10月5日存為 apple-swiftui-accessibilityrotor.html 與 .txt,https://developer.apple.com/documentation/swiftui/view/accessibilityrotor(_:entries:)。 ↩↩↩↩

  61. Apple Developer Documentation,AccessibilityNotification.Announcement(Accessibility;「A notification that an app posts when it needs to convey an announcement to an assistive app」;iOS 17.0),JSON 於2026年10月5日存為 apple-swiftui-accessibilitynotification.html 與 .txt,https://developer.apple.com/documentation/accessibility/accessibilitynotification/announcement。 ↩↩↩↩

  62. Apple Developer Documentation,performAccessibilityAudit(for:_:)(XCUIAutomation,XCUIApplication;iOS 17.0;JSON 列出 Xcode 16.3 且沒有摘要),JSON 於2026年10月5日存為 apple-xctest-accessibilityaudit.html 與 .txt,https://developer.apple.com/documentation/xcuiautomation/xcuiapplication/performaccessibilityaudit(for:_:)。 ↩↩↩

  63. Apple,Xcode 27.0(build 27A266a)中的 XCUIAutomation 框架標頭檔,iPhone Simulator 平台,2026年10月5日閱讀:XCUIApplication.h(performAccessibilityAuditWithAuditTypes:issueHandler:error:,「Runs an accessibility audit on the current view」);XCUIVoiceOverService.h(「Provides programmatic control of VoiceOver for UI testing」;開啟、關閉、向前、向後、移入與移出,以及目前的語音內容;iOS 27.0);XCUIDevice.h(voiceOverService 屬性,iOS 27.0)。在這個框架的每一個標頭檔中搜尋自訂動作、轉子與公告,一無所獲。 ↩↩↩

  64. Kiradex 程式庫,scripts/forge/interiors.py(模組 docstring,包括「every piece at least two tiles on one axis」;ROOM、ROOM_UP 與 HALL 字母地圖、家具清單、位置與傳送點;host 的註解「where the owner stands when you visit」;PIECES、FACES、EXPORT_LETTERS、export() 與 check(),其位置檢查涵蓋抵達、樓梯、主人、書架、展示櫃、書桌與展台位置,但不包括牆板,而其重疊迴圈讀取的是 data["objects"],也就是匯出的獨立家具),2026年10月5日閱讀,從未執行。 ↩↩↩↩↩↩↩↩↩↩↩↩↩

  65. Kiradex 程式庫,docs/TestFlight.md,第 46 項(「Build 26: the interior kit. Rooms sixteen wide with a three-row wall band (cornice, face, wainscot)」),2026年10月5日閱讀。檔案中沒有註明這個 build 的日期;它介於 build 23 與 build 35 之間,建築那篇文章把兩者都標為2026年10月3日。 ↩↩

  66. 作者的計算,2026年10月5日:merge_regression.py,位於草稿資料夾的 figures/,輸出為 merge_regression.out。在工坊原始配置的 room 上(13 件可移動家具,讀自 path_rule.json,並以 ast 讀取 Kiradex 程式庫的 scripts/forge/interiors.py),單獨一張 (9,3) 的床與單獨一個 (6,4) 的床頭櫃,各自都通過格子規則、上限與行走檢查;兩者合在一起則切斷 (7,3) 與 (8,3),也就是展示櫃前方的兩個格子。一個依 uid 排序這兩件、並讓每件通過完整檢查的修復程序,保留 uid 較小的那件並送回另一件,兩種輸入順序與兩種 uid 指派都得到相同的結果。713 組會切斷的組合中每一組都逐件合法,這一點可從 measure_path_rule.out 推得:沒有任何避開目標的單件擺放會切斷目標。 ↩↩↩↩↩

  67. Kiradex 程式庫,docs/WORLD.md(第 4 節:金幣商店的「clothes, hats, backpacks, card-display frames and emotes」、level = floor(sqrt(XP / 40)) + 1、「that cannot be bought」的等級鎖定外觀、包括 Full Set、Vintage 與 Across the Sea 在內的徽章;收藏者為「a 30-pixel figure in a 32 × 40 cell」),2026年10月5日閱讀;搜尋了 sixteen、cap、decoration、furnishing、Sunday、once a day 與 read-only,沒有任何一處陳述房間規則。 ↩↩↩↩↩↩

  68. Kiradex 程式庫,server/app/rooms.py(模組 docstring:「Everything here is in memory; a room empties when its last collector leaves and nothing of them is kept.」),2026年10月5日閱讀。 ↩↩↩↩

  69. Kiradex 程式庫,docs/research/pixel-art/06-collecting-and-hubs.md,5.3 節(「Rooms: a budget, a Sunday letter, and a like that never goes down」:16 件的上限、作為「a pixel object that appears on the doormat」的星期天的信、唯讀拜訪、每位訪客每天一次的讚、「a stamp on their passport per room visited, with ranks at 30 / 100 / 500」)與 5.7 節(贊助者物品包括「one room skin」,且「never a bigger furnishing cap」),2026年10月5日閱讀。 ↩↩↩↩↩

  70. Kiradex 程式庫,Kiradex/World/Quests.swift(Quest.current(on:calendar:),以日曆的 weekOfYear 區間為鍵,該區間從日曆自己的每週第一天開始)與 Kiradex/World/HallView.swift(註解「The week’s theme is the week’s quest」,以及讀取每週任務標題作為主題),2026年10月5日閱讀。 ↩↩↩

  71. Kiradex 程式庫,docs/PROTOTYPES.md(每週任務原型給測試者的問題「Weekly reset on your calendar’s week: say if it feels wrong.」),2026年10月5日閱讀。 ↩

  72. 作者的計算,2026年10月5日:layout_size.py,位於草稿資料夾的 figures/,輸出為 layout_size.out:種類數,今天工坊的 21 種(17 種地板家具與 4 種牆面物品)加上 7.5 節新增的 3 種,共 24 種;儲存的房屋布局(兩層樓,每層 16 件,每件含種類、x、y 與朝向,每層兩個風格 ID),以帶具名鍵的精簡 JSON 表示,每個種類都設為最長的提案名稱(pressed_flower,14 個字元)、每個風格 ID 為 16 個字元,1,974 位元組;每個種類都補齊到 16 個字元時,2,038 位元組;32 件以純整數四元組表示,385 位元組。 ↩↩

  73. Kiradex 程式庫,Kiradex/World/PlazaStage.swift(@AppStorage("plaza.id"),「Who this device is to the plaza」;@AppStorage("quests.stamps"),以逗號串接的任務印章清單,QuestBoardView 與 MeView 也會讀取;@AppStorage("plaza.ribbons"),「Ribbons from the town’s collectors」),2026年10月5日閱讀;在 App 的 Swift 原始碼中搜尋儲存的步數或拜訪過的房間紀錄,一無所獲,在 App 與伺服器中搜尋計步器、步數類型或護照也一樣(唯一的 steps 是 TileMap.swift 與 PlazaRig.swift 中的行走路徑)。 ↩↩↩↩↩

  74. Kiradex 程式庫,Kiradex/Models/Progression.swift(Progression 的 doc 註解:經驗值、等級與徽章是「worked out from the collection as it is, so every device agrees without anything being stored」),2026年10月5日閱讀。 ↩↩

  75. Apple Developer Documentation,AppStorage(SwiftUI;「A property wrapper type that reflects a value from UserDefaults」;iOS 14.0),JSON 於2026年10月5日存為 apple-swiftui-appstorage.html 與 .txt,https://developer.apple.com/documentation/swiftui/appstorage。 ↩↩

  76. 作者的計算,2026年10月5日:skin_denominator.py,位於草稿資料夾的 figures/,輸出為 skin_denominator.out,依規格書的色階各行計算:一層 16 件家具,其中 15 件與地板風格屬於同一色階家族、牆面不屬於,是 18 中的 16(88.9%),在 70% 那一行得 1,600 分;把牆面從比例中拿掉,則是 17 中的 16(94.1%),在 90% 那一行得 4,800 分,多出 3,200 分。 ↩

  77. Kiradex 程式庫,Kiradex/Models/Profile.swift(同步個人資料上的 var lookData: Data = Data();var uid: String = "",在 init(handle:) 中設為 UUID().uuidString,PriceHistory 與 CardCopy 也同樣帶有 uid;ModelContext.profile(),第 224 到 237 行,依 uid 排序取得個人資料,保留第一筆並刪除其餘,其註解稱之為合併到最小的 uid),2026年10月5日閱讀。 ↩↩↩

相關文章

Pixel-Art Structures: Houses, Halls and Interiors on iPhone

How Pokémon, the SNES RPGs and Stardew build houses, interiors and floors, measured from source, and how Kiradex's town …

80 分鐘閱讀

像素藝術的道路:iPhone 上的地圖連接與街區

寶可夢、星露谷物語與動物森友會如何打造城鎮之間的土地:從原始碼實測道路、接縫、關口與地圖畫面,再為 Kiradex 寫出一份規格書。

77 分鐘閱讀

為 iPhone Duo 準備您的 App:一個完整的實作範例

為 iPhone Duo 準備並送審 App:決定版面的 SDK 版本標記、一款真實 App 逐一走過每種姿態、兩塊螢幕的截圖,以及 App Store 的規則。

37 分鐘閱讀