简介
在本深入指南中,我们将逐步使用 Firebase 和 React 构建实时多人游戏,并使用 Gladiator Taunt Wars 中的详细示例。在这种游戏模式中,玩家进行策略性的嘲讽决斗,轮流选择和回应嘲讽,以减少对手的生命值(HP)。本文将涵盖构建此类游戏的各个方面,包括 Firebase 设置、匹配、游戏状态管理、动画、实时更新和基于 ELO 的排行榜集成。最后,您将深入了解如何实现响应灵敏、引人入胜的实时多人游戏体验。
设置 Firebase 和项目初始化
Firebase 设置
使用 Firestore 和身份验证初始化 Firebase,以进行实时数据处理和玩家验证。这些将为存储和管理比赛数据、玩家信息和实时排行榜更新提供基础。确保您设置 Firestore 规则来限制对比赛数据的访问,仅允许经过身份验证的玩家查看和更新相关信息。
React 项目结构
将您的 React 项目组织成可重用的组件,这些组件将代表每个游戏元素,例如匹配系统、游戏板、排行榜和聊天。按层次结构构建组件,以获得清晰且可维护的架构。
游戏的关键组件
匹配逻辑
startSearching 函数通过将玩家添加到 Firestore 中的队列来启动匹配过程。如果找到对手,则会创建一个新的比赛文档,存储双方玩家的 ID 并初始化游戏参数。
const startSearching = async () => { const user = auth.currentUser; if (user && db) { try { const matchmakingRef = collection(db, 'tauntWars_matchmaking'); const userDocRef = doc(matchmakingRef, user.uid); await runTransaction(db, async (transaction) => { const userDoc = await transaction.get(userDocRef); if (!userDoc.exists()) { transaction.set(userDocRef, { userId: user.uid, status: 'waiting', timestamp: serverTimestamp() }); } else { transaction.update(userDocRef, { status: 'waiting', timestamp: serverTimestamp() }); } const q = query(matchmakingRef, where('status', '==', 'waiting')); const waitingPlayers = await getDocs(q); if (waitingPlayers.size > 1) { // Pairing logic } }); } catch (error) { setIsSearching(false); } } };
该功能使用 Firestore 交易来确保玩家不会出现双重匹配,否则会破坏匹配系统。 Firebase 的 serverTimestamp 函数在这里很有用,可以确保跨多个时区的时间戳一致。
useEffect(() => { const matchRef = doc(db, 'tauntWars_matches', matchId); const unsubscribe = onSnapshot(matchRef, (docSnapshot) => { if (docSnapshot.exists()) { setMatchData(docSnapshot.data()); if (docSnapshot.data().currentTurn === 'response') { setResponses(getAvailableResponses(docSnapshot.data().selectedTaunt)); } } }); return () => unsubscribe(); }, [matchId]);
Handling Game Phases
Players alternate turns, each choosing a taunt or response. The currentTurn attribute indicates which action phase the game is in. Each action is updated in Firestore, triggering real-time synchronization across both clients. For instance, a player selecting a taunt switches currentTurn to “response,” alerting the opponent to choose a response.
const handleTauntSelection = async (taunt) => { const otherPlayer = currentPlayer === matchData.player1 ? matchData.player2 : matchData.player1; await updateDoc(doc(db, 'tauntWars_matches', matchId), { currentTurn: 'response', turn: otherPlayer, selectedTaunt: taunt.id, }); };
The Timer component restricts the duration of each turn. This timeout function maintains a steady game flow and penalizes players who fail to act in time, reducing their HP.
const Timer = ({ isPlayerTurn, onTimeUp }) => { const [timeLeft, setTimeLeft] = useState(30); useEffect(() => { if (isPlayerTurn) { const interval = setInterval(() => { setTimeLeft(prev => { if (prev <= 1) { clearInterval(interval); onTimeUp(); return 0; } return prev - 1; }); }, 1000); return () => clearInterval(interval); } }, [isPlayerTurn, onTimeUp]); };
const animateAttack = useCallback((attacker, defender) => { const targetX = attacker === 'player1' ? player1Pos.x + 50 : player2Pos.x - 50; const attackerRef = attacker === 'player1' ? player1Ref : player2Ref; attackerRef.current.to({ x: targetX, duration: 0.2, onFinish: () => attackerRef.current.to({ x: player1Pos.x, duration: 0.2 }) }); });
By simulating attacks in this way, we visually indicate the power and result of each taunt or response, creating a more immersive experience.
const ChatBox = ({ matchId }) => { const [messages, setMessages] = useState([]); useEffect(() => { const chatRef = collection(db, 'tauntWars_matches', matchId, 'chat'); const unsubscribe = onSnapshot(chatRef, (snapshot) => { setMessages(snapshot.docs.map((doc) => doc.data())); }); return () => unsubscribe(); }, [matchId]); };
Each message is rendered conditionally based on the user’s ID, differentiating sent and received messages with distinct styling.
const EloLeaderboard = () => { const [players, setPlayers] = useState([]); useEffect(() => { const q = query(collection(db, 'users'), orderBy('tauntWars.elo', 'desc'), limit(100)); const unsubscribe = onSnapshot(q, (querySnapshot) => { setPlayers(querySnapshot.docs.map((doc, index) => ({ rank: index + 1, username: doc.data().username, elo: doc.data().tauntWars.elo, }))); }); return () => unsubscribe(); }, []); };
The leaderboard ranks players based on their ELO, providing competitive motivation and a way for players to track their progress.
Technical Challenges and Best Practices
Consistency with Firestore Transactions
Using transactions ensures that simultaneous reads/writes to Firestore maintain data integrity, especially during matchmaking and scoring updates.
Optimizing Real-Time Listeners
Employ listener cleanup using unsubscribe() to prevent memory leaks. Also, limiting queries can help reduce the number of Firestore reads, optimizing costs and performance.
画布响应式设计
CanvasComponent 根据视口调整其大小,使游戏能够跨设备响应。使用react-konva库可以实现交互元素的稳健渲染,通过动画为玩家提供视觉反馈。
处理边缘情况
考虑玩家在游戏中断开连接的情况。为此,实现一个清理功能,以确保更新比赛数据并关闭任何废弃的比赛实例。
总结
借助 Firebase 和 React,您可以创建适应实时用户操作的快节奏多人游戏体验。 Gladiator Taunt Wars 中的示例演示了如何集成实时更新、安全交易和动态动画来制作引人入胜且具有视觉吸引力的游戏。
结论
为《角斗士之战》构建《角斗士嘲讽战争》是一次收获颇丰的旅程,它将 React、Firebase 和沉浸式游戏机制结合在一起,在一款基于 Web 的游戏中捕捉罗马竞技场战斗的激烈程度。利用 Firebase 的实时 Firestore 数据库、安全身份验证和强大的托管功能,我们能够创建无缝、社区驱动的体验,让玩家可以在战略战斗中进行对决。集成 GitHub Actions 进行持续部署也简化了开发,让我们能够专注于增强游戏玩法和用户交互。
随着我们继续扩展《角斗士嘲讽战争》,我们对新功能的潜力感到兴奋,包括人工智能驱动的对手和增强的比赛策略,这将加深游戏体验,让每场战斗都更加身临其境。历史策略与现代技术的结合为玩家提供了一种参与角斗士世界的动态方式。
本系列的后续文章将深入探讨使用 Firebase 创建交互式 Web 应用程序的技术细节,包括优化实时数据流、管理复杂的游戏状态以及利用 AI 增强玩家参与度。我们将探索桥接前端和后端服务的最佳实践,以创建响应迅速的实时多人游戏环境。
无论您是开发自己的互动游戏还是对 Gladiators Battle 背后的技术感到好奇,本系列都提供了有关使用 Firebase 构建现代 Web 应用程序的宝贵见解。加入我们,我们将继续将古代历史与尖端技术相融合,为当今的数字世界重新构想角斗士战斗的刺激。
?发现更多:
探索角斗士之战:潜入罗马世界,体验策略和战斗 https://gladiatorsbattle.com
查看我们的 GitHub:查看我们的代码库和贡献:https://github.com/HanGPIErr/Gladiators-Battle-Documentation。
在 LinkedIn 上联系:在 LinkedIn 上关注我的项目更新 https://www.linkedin.com/in/pierre-romain-lopez/
还有我的 X 帐户:https://x.com/GladiatorsBT
以上是使用 React 和 Firebase 构建实时多人游戏:角斗士嘲讽战争的详细内容。更多信息请关注PHP中文网其他相关文章!