多人遊戲房間與大廳
派對、問答、桌遊與卡牌遊戲:用代碼加入的房間、座位、房主、準備狀態和聊天,建立在 presence 頻道與用戶端事件上,不需要遊戲伺服器。
派對遊戲、問答、桌遊與卡牌遊戲、一起看的房間,開頭都一樣:一組房間代碼、誰在裡面的名單、一顆「準備」按鈕,還有一位按下開始的房主。這部分不需要遊戲伺服器。房間就是一個 presence 頻道,開源套件 @hibira-io/lobby 會在瀏覽器裡把它變成座位、房主、準備狀態、踢人、開始和房間聊天。
玩家得到什麼
- 用代碼或連結就能加入的房間,座位一個個坐滿,滿了會清楚顯示「房間已滿」。
- 準備狀態、房主標記和開始按鈕,所有人同時看到。
- 有「輸入中…」的房間聊天,房主離開時自動換人。
用到的功能
- Presence 頻道(
presence-),記錄每個房間裡有誰。 - 用戶端事件(
client-*),準備狀態、設定、開始、聊天和遊戲操作都在瀏覽器之間直接傳遞。 - HTTP API(
GET /channels?filter_by_prefix=presence-room-&info=user_count),不用資料庫就能做房間列表。 - @hibira-io/lobby,把上面這些組合成房間的邏輯。
步驟 1:在伺服器授權房間
授權端點決定誰能加入、簽上每位玩家加入的時間,房間滿了就拒絕。所有玩家都用這個時間排順序,所以大家對座位和房主的看法一致:
import Pusher from 'pusher';
import { authorizeRoom, listRooms, RoomFullError } from '@hibira-io/lobby/server';
const hibira = new Pusher({ appId, key, secret, host: 'ws.hibira.io', useTLS: true });
app.post('/rooms/auth', requireLogin, express.urlencoded({ extended: false }), async (req, res) => {
try {
res.json(await authorizeRoom(hibira, {
socketId: req.body.socket_id,
channel: req.body.channel_name,
userId: req.user.id,
userInfo: { name: req.user.name },
capacity: 4,
}));
} catch (err) {
res.status(err instanceof RoomFullError ? 403 : 500).end();
}
});
// Room browser: [{ id: 'KQTZ', channel: 'presence-room-KQTZ', members: 3 }]
app.get('/rooms', async (req, res) => res.json(await listRooms(hibira)));步驟 2:在瀏覽器加入房間
先在應用程式設定裡打開 允許用戶端事件,然後:
import { Room } from '@hibira-io/lobby';
const room = Room.join(pusher, 'presence-room-KQTZ', { capacity: 4 });
room.on('members', ({ players, host }) => renderSeats(players, host));
room.on('message', (m) => chat.append(m.info.name, m.text));
room.on('full', () => show('This room is full'));
readyButton.onclick = () => room.setReady(!room.me.ready);
chatForm.onsubmit = () => room.send(input.value);
input.oninput = () => room.sendTyping();room.players 依座位順序列出玩家,每位都有 ready 和 host。自己的操作會立刻顯示;只有房主能送的事件,如果是別人送的會被忽略。
步驟 3:開始遊戲
大家都準備好後由房主開始。傳給 start() 的資料會送到每位玩家,例如每回合的時間、亂數種子或遊戲伺服器的位址:
if (room.isHost && room.allReady) room.start({ seed: 1234, rounds: 5 });
room.on('start', ({ data }) => game.begin(data));遊戲中的操作可以用同一個頻道的用戶端事件 room.channel.trigger('client-move', move),需要遊戲伺服器的遊戲也可以改走伺服器。
Laravel 的寫法
在 presence 頻道的 callback 回傳加入時間,再使用 Echo.join() 的頻道:
// routes/channels.php
Broadcast::channel('room.{code}', function ($user, $code) {
return ['id' => $user->id, 'name' => $user->name, 'joined_at' => (int) floor(microtime(true) * 1000)];
});const room = new Room(Echo.join(`room.${code}`).subscription, { capacity: 4 });試玩看看
Reaction rooms 範例 是一個完整的 Next.js 遊戲:房間列表、四個座位、準備、聊天,還有五回合的反應速度遊戲。免費方案的應用程式就能跑。
小提醒
- 一個房間最多 100 人,也就是 presence 頻道的上限。
- 每個用戶端事件算一則訊息,不管有幾位玩家收到。四個人玩一局短遊戲大約幾十則。
- 遊戲開始後是信任用戶端的。如果作弊是個問題,請在伺服器檢查結果,或把遊戲狀態放在遊戲伺服器上,房間只拿來當大廳。
- 用 Unity 開發?請看 Unity 大廳、配對與排行榜。