這篇在講什麼
推箱子是《Endless End》裡很重要的一個互動元素。它不是單純讓物件被碰撞後亂滑,而是要能精準地跟玩家輸入同步,並且在被其他箱子擋住時穩定停下來。
我希望箱子有什麼行為
我先把需求整理成幾點:
- 玩家碰到箱子並持續朝同方向移動時,能進入推動狀態
- 箱子要和玩家維持一致的移動方向與節奏
- 如果前方被其他箱子擋住,就不能再推
- 在特殊表裡世界切換狀態下,要能改變是否參與碰撞

而當箱子彼此卡住時,則必須停下來:



箱子本身需要哪些結構
從物件結構來看,箱子其實不複雜:
- 一個箱子物件
- 一個 collider
- 一支控制腳本

需要的狀態大多圍繞在左右是否可推、速度與碰撞控制上。

箱子怎麼判斷自己能不能被推
我最後採用的方式,是讓箱子自己往左右各打一條短 raycast,專門檢查同樣屬於箱子 layer 的物件。
如果左邊有箱子,就代表不能再往左推;右邊同理。

這樣箱子本身就可以維護:
canPushLeftcanPushRight
而不是把所有判斷都丟回玩家控制器。
推動時怎麼處理位移
真正推動時,我沒有完全交給物理力去處理,而是讓玩家端呼叫箱子的 Push(...) 方法,把方向與速度傳進去。
箱子再根據:
- 玩家目前移動方向
canPushLeftcanPushRight
來決定要不要真的更新自己的位置。

這種做法的優點是:
- 行為可控
- 箱子和玩家節奏比較容易同步
- 不會太依賴物理模擬的偶發性
表裡世界切換時怎麼處理碰撞
這個專案裡,玩家可以在表裡世界之間切換,而箱子也是會跟著改變碰撞狀態的物件之一。
我的做法是切 layer collision 規則,讓箱子自己決定是否忽略和玩家的碰撞。
這裡特別要注意的一點是:
Physics2D.IgnoreLayerCollision(...)需要的是 layer index- 不是直接把
LayerMask當成 layer index 傳進去

這是當時我踩過的一個小坑。LayerMask 的值本質上是 bitmask,不等於實際 layer 編號。
玩家端要配合什麼
玩家控制器也需要做前向檢查。
當玩家朝某個方向移動時,先往前打一條短 raycast,確認前方是否是箱子。若命中:
- 取得該箱子的控制腳本
- 讓角色切進推動狀態
- 呼叫箱子的
Push(...)

這樣責任就很清楚:
- 玩家判斷「我現在要不要推」
- 箱子判斷「我現在能不能動」
我自己的整理
這篇最後留下來的核心判斷是:解謎型推箱子,不一定要追求完全真實的物理感,反而更該追求行為穩定與除錯容易。
只要玩家預期能被穩定滿足,這種可控位移通常比純物理更適合。
先記住的重點
- 推箱子的核心不是碰撞,而是可控且穩定的互動節奏
- 玩家負責決定是否嘗試推,箱子負責決定自己能否移動
- 箱子自己做左右阻擋檢查,通常比全部堆在玩家端更清楚
- 使用
IgnoreLayerCollision時,要分清 layer index 和 layer mask