見縫插針小遊戲
將針射入旋轉的圓盤,避免針針相撞,挑戰 10 關速度極限
什麼是見縫插針小遊戲?
見縫插針是一款節奏簡單但越往後越緊張的休閒小遊戲:螢幕中央是一個勻速旋轉的白色圓盤,你需要從下方依次把針發射出去,讓針穩穩釘在圓盤上,並且不能撞到已經釘住的針。每關給定固定數量的針,全部成功釘入即過關,任何一次相撞都立刻失敗。它的原型是手機端非常出名的 AA、Pin Out 這類小遊戲,玩法極簡,看似隨便點點就能玩,真正上手後卻很考驗時機判斷和心理控制。本工具把它搬到瀏覽器裡,無需註冊、不要下載,電腦用滑鼠點擊,手機用手指輕點螢幕即可發射。
使用方法
操作步驟
- 頁面開啟後遊戲會自動進入第 1 關,圓盤開始勻速旋轉
- 點擊遊戲區域(或按空白鍵 / Enter 鍵)發射一根針
- 針朝中心飛行並釘入圓盤,需要確保不撞到圓盤上已有的針
- 把本關所有針全部安全釘入即過關,自動進入下一關
- 任何一次相撞都判定失敗,可以選擇「重玩本關」或「重新開始」(注意:失敗後按空白鍵 / Enter 會觸發「重新開始」回到第 1 關,想重玩本關需要點按鈕)
通關小竅門
- 圓盤是勻速旋轉的,找節奏比靠手速更重要:盯著空隙的中線再點。
- 看到旁邊針剛剛掃過的瞬間發射,針飛行需要約 120ms,給點提前量。
- 可以連續點擊連發,最多緩衝 3 發依次飛出,超出會被丟棄,所以前一發未落地前不必狂點。
- 越往後剩餘空位越窄、轉速越快,寧可少一次試探也別貪點。
關卡難度說明
- 第 1-3 關:針多、空隙大,主要熟悉操作節奏。
- 第 4-7 關:圓盤已有針變多,需要預判轉速。
- 第 8-10 關:圓盤幾乎被插滿,每根針都得算準時機。
使用場景
技術原理
遊戲的主迴圈由 requestAnimationFrame 驅動,每幀把內部 rotation 增加一個固定的 speed 值(單位:度/幀),然後遍歷所有已釘入的針元素,把 transform 設為 rotate(baseAngle + rotation)。因為 transform 是合成層屬性,瀏覽器可以把旋轉交給 GPU 合成執行緒處理,不會觸發版面或重繪,所以即使最後一關同時有 15 根針在旋轉也能穩定 60fps。 命中檢測採用「角度差轉弦長」的方式:每根針在圓盤座標系下用它的 baseAngle 表示(相對圓盤的固定位置,不隨旋轉改變)。新針抵達中心時,它在世界座標系下的角度是 TARGET_ANGLE = 0,對應到圓盤座標系下就是 normalizeAngle(0 - rotation)。然後比較新 baseAngle 和所有已有 baseAngle 的角度差 Δθ,轉換為針頂端圓點中心間的弦長 2R·sin(Δθ/2)(R = 111px 是頂端圓點中心到圓心的距離)。如果小於 24px 閾值就算撞針,立即結束本局。 發射動作做了簡單的狀態機控制:shootLocked 標記一根針在飛行中,防止同一根針的 transitionend 和兜底 setTimeout 觸發兩次結算;針的飛行時間由 CSS transition 控制為 120ms,180ms 的 setTimeout 兜底是為了在極少數情況下 transitionend 沒有觸發(例如 DOM 在過渡完成前被改動)時仍能完成結算。pendingShots 緩衝玩家在飛行中連續點擊的發射請求,上限為 3 發,超過 3 發的點擊直接丟棄,這樣既能保留快速連射體驗,也不會讓多根針同時擠向中心。失敗和過關都把 gameOver 置為 true,主迴圈雖然繼續運轉,但不再更新 rotation,相當於讓畫面定格。 為了適配不同解析度,遊戲整體保持 420×720 的設計尺寸,外層根據視口寬高計算縮放比例,套在容器上用 transform: scale。當前實現把上限鎖在 1,所以小螢幕會等比縮小、桌面端大螢幕不會進一步放大;命中檢測用的全部都是設計座標系下的數字,不需要為每個螢幕重新計算閾值,邏輯簡單且任何螢幕上判定都一致。
- 主迴圈:requestAnimationFrame,每幀 rotation += speed,所有針元素 transform: rotate(baseAngle + rotation)。
- GPU 加速:旋轉使用 transform 而非 left/top,瀏覽器合成層處理,不觸發版面/重繪。
- 命中檢測:把世界座標的 0° 反推回圓盤座標 normalizeAngle(0 - rotation),再用弦長公式 2R·sin(Δθ/2) 與 24px 閾值比較。
- 狀態機:shootLocked 防止重複結算,pendingShots 上限 3 發並截斷超出(不是排隊),gameOver 後凍結 rotation 更新。
- 結算回呼:CSS 飛行 120ms,transitionend 主結算 + 180ms setTimeout 兜底,應對 transitionend 偶發不觸發的情況。
- 適配方案:固定 420×720 設計座標系,外層 transform: scale 按視口縮放(上限鎖定 1,桌面端大螢幕保持原始尺寸)。
- 資料儲存:僅在 localStorage 中記錄歷史最佳關卡,不上傳任何資料,沒有帳號體系。
範例
10 關速度與針數配置
關卡 起始針數 本關發射 轉速(度/幀)
1 2 8 1.35
2 3 9 1.50
3 3 10 1.65
4 4 11 1.80
5 4 12 2.00
6 5 13 2.20
7 5 14 2.45
8 6 15 2.70
9 7 15 3.00
10 8 15 3.35典型一局:第 5 關失敗
第 5 關:初始 4 根針,需要發射 12 根。
第 1-7 發:依次插入空隙,剩餘 5 根。
第 8 發:你在前一根落地前搶點,發射時機太早。
判定:新針 baseAngle 與第 6 發只差 8°,小於 DOT_HIT_DISTANCE。
結果:相撞,本局結束,提示「遊戲失敗」。
可以點「重玩本關」從第 5 關重新開始。全部通關結算
通關耗時:約 2 分 40 秒
累計發射:117 根
失敗次數:3
歷史最佳:第 10 關
提示:「全部通關!」按鈕顯示「再玩一次」。
歷史最佳寫入 localStorage,重新整理頁面仍然保留。常見問題
為什麼我明明點中了空位還是失敗了?
因為針不是瞬間到位的,從你點擊到針真正接觸圓盤大約有 120ms 的飛行時間。這段時間圓盤還在轉,如果點擊時空隙正好夠大,但飛行中圓盤轉過去把另一根針帶到了正前方,新針落下時就會和它撞上。簡單辦法是在空隙剛掃過中線時就發射,預留出轉動時間。
可以一直按住螢幕連發嗎?
可以連續點擊,但發射節流是確定的:上一發針沒落地前不會發射下一發。在飛行期間最多緩衝 3 次點擊依次飛出,超過 3 次的點擊會被直接丟棄,不會排隊。後期關卡建議放慢點擊節奏,等針落地再判斷下一發。
遊戲會越來越快是按什麼規則?
每一關都有獨立配置的轉速、起始針數和本關需要發射的針數。從第 1 關的 1.35 度/幀到第 10 關的 3.35 度/幀,轉速接近 2.5 倍。同時起始針數也從 2 漲到 8,留給你的空隙越來越小,所以越到後面越需要穩,而不只是手快。
失敗後是從頭來還是從本關來?
兩種都支援,但要分清觸發方式。點「重玩本關」按鈕會從你失敗的那一關重開,針數和轉速保持不變;點「重新開始」按鈕、或在失敗畫面下按空白鍵 / Enter 鍵,都會回到第 1 關從頭打。鍵盤快捷鍵不會觸發「重玩本關」,習慣按空白鍵繼續的玩家要留意。歷史最佳關卡始終保留在 localStorage 中。
為什麼遊戲區域顯得比較窄?
遊戲的設計尺寸是 420×720,仿照手機直式比例。這樣在手機上是天然全螢幕;在桌面瀏覽器中外層會按視口大小等比縮放,但縮放上限鎖定在 1,所以大螢幕不會進一步放大,看起來就維持在原始 420×720。如果你想要更大的畫面,目前只能保持原始比例;遊戲不會因為視窗大小改變判定規則。
遊戲資料會上傳到伺服器嗎?
不會。整個遊戲完全在瀏覽器本地執行,唯一儲存的是「歷史最佳關卡」這一個數字,存在 localStorage 裡。清除瀏覽器網站資料或換瀏覽器就會遺失。我們沒有排行榜,也不收集任何分數或操作紀錄。
手機上玩有沒有什麼額外注意?
建議在瀏覽器中開啟全螢幕模式,避免下拉手勢誤觸重新整理。橫式其實也能玩,但直式比例更接近原版手感。低階機如果出現輕微掉幀,可以試著關閉其他背景分頁或減小瀏覽器視窗裡的其他動畫元素,本遊戲單幀只在做幾個 transform 計算,硬體佔用極低。