ห้องและล็อบบี้เกมผู้เล่นหลายคน
เกมปาร์ตี้ ควิซ บอร์ดเกม และเกมการ์ด: ห้องที่เข้าด้วยรหัส ที่นั่ง หัวห้อง สถานะพร้อม และแชต สร้างบนช่อง presence และอีเวนต์ฝั่งไคลเอนต์โดยไม่ต้องมีเกมเซิร์ฟเวอร์
เกมปาร์ตี้ ควิซ บอร์ดเกม เกมการ์ด และห้องดูด้วยกัน เริ่มต้นแบบเดียวกันหมด: รหัสห้อง รายชื่อคนที่อยู่ในห้อง ปุ่มพร้อม และหัวห้องที่กดเริ่ม ส่วนนี้ไม่ต้องใช้เกมเซิร์ฟเวอร์ ห้องคือช่อง presence และแพ็กเกจโอเพนซอร์ส @hibira-io/lobby จะเปลี่ยนมันเป็นที่นั่ง หัวห้อง สถานะพร้อม การเตะออก การเริ่มเกม และแชตในห้องบนเบราว์เซอร์
สิ่งที่ผู้เล่นได้รับ
- ห้องที่เข้าได้ด้วยรหัสหรือลิงก์ ที่นั่งค่อยๆ เต็ม และเมื่อเต็มจะขึ้นว่า "ห้องเต็ม" ชัดเจน
- สถานะพร้อม ป้ายหัวห้อง และปุ่มเริ่ม ที่ทุกคนเห็นพร้อมกัน
- แชตในห้องพร้อมสถานะ "กำลังพิมพ์…" และเมื่อหัวห้องออก คนถัดไปจะเป็นหัวห้องแทน
ฟีเจอร์ที่ใช้
- ช่อง presence (
presence-) สำหรับรายชื่อคนในแต่ละห้อง - อีเวนต์ฝั่งไคลเอนต์ (
client-*) สำหรับสถานะพร้อม การตั้งค่า การเริ่มเกม แชต และการเล่นระหว่างเกม ส่งจากเบราว์เซอร์ถึงเบราว์เซอร์ - HTTP API (
GET /channels?filter_by_prefix=presence-room-&info=user_count) ทำรายการห้องได้โดยไม่ต้องมีฐานข้อมูล - @hibira-io/lobby ที่รวมทั้งหมดนี้เป็นตรรกะของห้อง
ขั้นที่ 1: ยืนยันสิทธิ์เข้าห้องบนเซิร์ฟเวอร์
endpoint ยืนยันสิทธิ์จะตัดสินว่าใครเข้าได้ ลงลายเซ็นเวลาที่ผู้เล่นแต่ละคนเข้าห้อง และปฏิเสธเมื่อห้องเต็ม ผู้เล่นทุกคนเรียงลำดับห้องตามเวลานี้ จึงเห็นตรงกันว่าใครนั่งที่ไหนและใครเป็นหัวห้อง:
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() จะไปถึงผู้เล่นทุกคน เช่น จังหวะของแต่ละรอบ seed สุ่ม หรือที่อยู่ของเกมเซิร์ฟเวอร์:
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
คืนเวลาที่เข้าห้องจาก callback ของช่อง presence แล้วใช้ช่องของ 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