摘要:最近刷了一部電影動物世界,感概原來簡單的剪刀石頭布游戲還可以這么燒腦,強大的數(shù)據(jù)分析能力對人性心理的靈敏嗅覺等。看完之后饒有興致,于是便利用技術,實現(xiàn)了一個動物世界多人對戰(zhàn)系統(tǒng)。
最近刷了一部電影《動物世界》,感概原來簡單的“剪刀石頭布”游戲還可以這么燒腦,強大的數(shù)據(jù)分析能力、對人性心理的靈敏嗅覺等。看完之后饒有興致,于是便利用socket技術,實現(xiàn)了一個“動物世界”多人對戰(zhàn)系統(tǒng)。
游戲背景故事講述的是男主被發(fā)小欺騙,欠下了一屁股債,為了償還債務被迫上了一艘賊船,同時上船的還有一批人,大伙兒的狀況都差不多。上船的好處是有機會還清債務并且還可能獲得一筆巨大的財富,這對于在現(xiàn)實世界中已經(jīng)生活不能自理的人來說,無疑是一次改變?nèi)松臋C會,而一旦失敗的話,就要被拉去做人體實驗(恐怖如斯)。
上船的人會進行一場賭博,就是我們小時候常玩的“石頭剪刀布”,每人初始時擁有12張卡牌,石頭、剪刀、布各4張,并且擁有3顆星,大家可以找任何一個人作為對手,每人各出一張卡牌,獲勝者將從失敗方拿走一顆星。游戲獲勝條件是手里卡牌全部消耗完并且擁有的星星不少于3顆,反之,若卡牌消耗完且星星少于3顆、或還有卡牌但星星為0,都視為失敗。
后端邏輯我們將利用koa來搭建一個socket服務器,來管理客戶端的消息接受和分發(fā)。
主要邏輯:
const io = SocketIO(server) // 建立socket連接 const users = {} // 緩存當前鏈接的用戶 const challengeData = {} // 緩存用戶發(fā)起的對戰(zhàn)信息 io.on("connection", socket => { // 客戶端連接后 const id = socket.id // 當前連接的unique標識 socket.emit("connected") // 告訴客戶端已經(jīng)連接成功 // 接收客戶端的open事件 socket.on("open", name => { // 初始化數(shù)據(jù) users[id] = { id, name, // 用戶昵稱 star: 3, // 用戶擁有的星星 stone: 4, // 用戶擁有的“石頭”卡牌數(shù)量 scissors: 4, // 用戶擁有的“剪刀”卡牌數(shù)量 paper: 4 // 用戶擁有的“布”卡牌數(shù)量 } // 通知所有人,當前所有用戶的信息 io.emit("update_users", users) }) // 用戶發(fā)起挑戰(zhàn) socket.on("challenge", data => { // data包括fromCard(發(fā)起者出示的卡牌)、toId(被挑戰(zhàn)者的id) data.fromId = id challengeData[id] = data io.to(data.toId).emit("accept_challenge", users[id]) // 告訴對方有人要和你對戰(zhàn) }) // 發(fā)起者取消了挑戰(zhàn) socket.on("cancel_challenge", () => { io.to(challengeData[id].toId).emit("cancel_challenge") // 告訴對方挑戰(zhàn)已取消 delete challengeData[id] // 刪除緩存的數(shù)據(jù) }) // 對方接受挑戰(zhàn)的信息 socket.on("respond_challenge", data => { if (data.accept) { // 接受 let cd = challengeData[data.fromId] cd.toCard = data.toCard // 被挑戰(zhàn)者出示的卡牌 // 雙方卡牌各減少1 users[cd.fromId][cd.fromCard]-- users[cd.toId][cd.toCard]-- let result = getChallengeResult(cd.fromCard, cd.toCard) // 獲得挑戰(zhàn)結(jié)果 // 比賽后的星星變更 if (result === 1) { users[cd.fromId].star++ users[cd.toId].star-- } else if (result === -1) { users[cd.fromId].star-- users[cd.toId].star++ } // 告訴挑戰(zhàn)者和被挑戰(zhàn)者,比賽的結(jié)果 io.to(cd.fromId).emit("result_challenge", result, users) io.to(cd.toId).emit("result_challenge", -result, users) } else { // 拒絕 io.to(data.fromId).emit("cancel_challenge") // 告訴發(fā)起者對方不接受挑戰(zhàn) } delete challengeData[data.fromId] }) // 比賽勝利 socket.on("success_challenge", () => { // 告訴所有人,有人獲得了勝利 socket.broadcast.emit("success_challenge", users[id]) }) // 斷開連接 socket.on("disconnect", () => { delete users[id] // 廣播用戶已退出 socket.broadcast.emit("update_users", users) }) })前端邏輯
前端使用Vue來進行頁面渲染。
import request from "@/common/request" import tips from "@axe/tips" import modal from "@axe/modal" import Loading from "./components/Loading.vue" /* eslint-disable no-alert */ export default { name: "App", components: { Loading }, data () { return { isConnected: false, id: "", selectedUserId: "", selectedCard: "", users: {}, acceptChallenge: false }; }, computed: { userInfo () { let user = this.users[this.id] || {} return { star: user.star || 0, stone: user.stone || 0, scissors: user.scissors || 0, paper: user.paper || 0 } }, totalInfo () { let info = { stone: 0, scissors: 0, paper: 0 } for (let id in this.users) { let user = this.users[id] info.stone += user.stone || 0 info.scissors += user.scissors || 0 info.paper += user.paper || 0 } return info } }, methods: { handleSelectCard (type) { this.selectedCard = type }, handleSelectUser (id) { if (this.id === id) { tips.show({ content: "不可以挑戰(zhàn)自己哦" }) return } this.selectedUserId = id }, handleChallenge () { if (this.gameover) { tips.show({ content: "游戲已結(jié)束,請重新開始" }) return } if (!this.selectedCard) { tips.show({ content: "請?zhí)暨x卡牌" }) return } if (this.users[this.id][this.selectedCard] <= 0) { tips.show({ content: "這類卡牌已耗盡" }) return } let user = this.users[this.selectedUserId] if (!user) { tips.show({ content: "請?zhí)暨x對手" }) return } if (user.star <= 0 || (user.stone + user.scissors + user.paper) <= 0) { tips.show({ content: "該用戶已不具備對戰(zhàn)能力了" }) return } if (!this.acceptChallenge) { this.socket.emit("challenge", { fromCard: this.selectedCard, toId: this.selectedUserId }) modal.show({ title: "發(fā)起挑戰(zhàn)", content: "等待對方接受中...", confirmText: "取消挑戰(zhàn)" }, t => { if (t === "confirm") { this.socket.emit("cancel_challenge") } }) } else { this.socket.emit("respond_challenge", { accept: true, fromId: this.challengeFromUser.id, toCard: this.selectedCard }) // 重置記錄 this.acceptChallenge = false } } }, mounted () { request({ url: "/api/info" }).then(data => { // 使用ip建立連接,局域網(wǎng)內(nèi)其他設備也可以訪問 this.socket = window.io.connect("http://" + data.ip + ":" + data.port) this.socket.on("connected", () => { let name = window.prompt("請輸入您優(yōu)雅高貴的稱呼") if (!name || !name.trim()) { name = this.socket.id } // 告訴服務器,有人進來了 this.socket.emit("open", name) // 已連接 this.id = this.socket.id this.isConnected = true }) this.socket.on("update_users", users => { this.users = users }) // 是否接受挑戰(zhàn) this.socket.on("accept_challenge", fromUser => { modal.show({ title: "接受挑戰(zhàn)", content: "是否接受來自【" + fromUser.name + "】的挑戰(zhàn)?", confirmText: "接受", cancelText: "拒絕" }, t => { if (t === "confirm") { // 緩存挑戰(zhàn)信息,等待用戶選擇出示的卡牌 this.selectedUserId = fromUser.id this.acceptChallenge = true this.challengeFromUser = fromUser } else { this.socket.emit("respond_challenge", { accept: false, fromId: fromUser.id }) } }) }) this.socket.on("cancel_challenge", () => { this.acceptChallenge = false modal.hide() tips.show({ content: "對方取消了挑戰(zhàn)" }) }) // 監(jiān)聽對戰(zhàn)結(jié)果 this.socket.on("result_challenge", (result, users) => { this.users = users modal.hide() tips.show({ content: result === 0 ? "平局" : (result === 1 ? "你贏了" : "你輸了") }, () => { // 檢測游戲勝利和失敗條件 let user = users[this.id] let cardCount = user.stone + user.scissors + user.paper if (user.star >= 3 && cardCount <= 0) { this.socket.emit("success_challenge") modal.show({ title: "游戲勝利", content: "恭喜你獲得了勝利!", confirmText: "再來一局" }, t => { if (t === "confirm") { window.location.reload() } }) } else if (user.star <= 0 || cardCount <= 0) { this.gameover = true modal.show({ title: "游戲結(jié)束", content: user.star <= 0 ? "你已經(jīng)沒有星星了" : "你已經(jīng)沒有卡牌了", confirmText: "重新開始" }, t => { if (t === "confirm") { window.location.reload() } }) } }) }) // 接收系統(tǒng)廣播,有人挑戰(zhàn)成功的信息 this.socket.on("success_challenge", user => { window.alert(`恭喜【${user.name}】挑戰(zhàn)成功,戰(zhàn)績(${user.star})顆星`) }) }) } }補充說明
游戲預覽
源碼地址:https://github.com/ansenhuang/node-socket
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/98887.html
摘要:在高度結(jié)構(gòu)化的數(shù)據(jù)挖掘以及通過分析來評估和改進機器學習模型方面,是國際領先的研究人員。在機器學習里,我并沒有涉及強化學習的內(nèi)容。這些準備讓讀者了解機器學習能做什么,然后我的書能幫助他們了解機器學習怎么工作。 非商業(yè)轉(zhuǎn)載請注明作譯者、出處,并保留本文的原始鏈接:http://www.ituring.com.cn/art... 訪談對象: Peter Flach,布里斯托大學人工智能教授,...
摘要:在高度結(jié)構(gòu)化的數(shù)據(jù)挖掘以及通過分析來評估和改進機器學習模型方面,是國際領先的研究人員。在機器學習里,我并沒有涉及強化學習的內(nèi)容。這些準備讓讀者了解機器學習能做什么,然后我的書能幫助他們了解機器學習怎么工作。 非商業(yè)轉(zhuǎn)載請注明作譯者、出處,并保留本文的原始鏈接:http://www.ituring.com.cn/art... 訪談對象: Peter Flach,布里斯托大學人工智能教授,...
摘要:如果這個場景足夠簡單的話,深度學習并不能表現(xiàn)出相對于其它基于傳統(tǒng)模式識別方法的優(yōu)勢。這是深度學習目前受到關注的一個非常重要的原因。通過積累大量的數(shù)據(jù)進行足夠的訓練,基于深度學習的系統(tǒng)可以給出最優(yōu)規(guī)劃。 谷歌和李世石的人機大戰(zhàn)引爆了公眾對于人工智能的關注,也讓基于深度學習的人工智能成為汽車業(yè)界關注的重點,那么深度學習在智能駕駛的應用場景下有什么幫助呢?自動駕駛最先出現(xiàn)在美國,而不是歐洲或者日本...
摘要:年新星調(diào)查中顯示,越來越流行,其熱度已經(jīng)逐漸超過了。及其框架位于全球最受歡迎使用最廣泛的技術榜榜首。本文轉(zhuǎn)載自框架的游戲年流行趨勢英文原文JavaScript 生態(tài)系統(tǒng)復雜多變,各種框架讓人眼花繚亂。究竟孰優(yōu)孰劣,如今的發(fā)展趨勢是怎樣的,用人單位又需要怎樣的人才?本文站在一個中立者的角度,客觀分析了當前這場框架的游戲中,JavaScript 的流行趨勢。 Javascript 的生態(tài)環(huán)境讓我...
閱讀 3652·2021-09-02 15:11
閱讀 4563·2021-08-16 10:47
閱讀 1560·2019-08-29 18:35
閱讀 3030·2019-08-28 17:54
閱讀 2843·2019-08-26 11:37
閱讀 1496·2019-08-23 16:51
閱讀 1799·2019-08-23 14:36
閱讀 1801·2019-08-23 14:21