Java程序辅导

C C++ Java Python Processing编程在线培训 程序编写 软件开发 视频讲解

客服在线QQ:2653320439 微信:ittutor Email:itutor@qq.com
wx: cjtutor
QQ: 2653320439
Real-Time Data Transfer Using Vue and Socket.IO [Part 3 of 3] | A²I² Artificial Intelligence at Deakin Skip to content About Research Publications Themes Grants Awards Industry Projects Careers Study with us News Blog Contact . Home About Research Themes Grants Awards Publications Industry Projects Study with us News Blog Contact Home/Blog Real-Time Data Transfer Using Vue and Socket.IO [Part 3 of 3] Blog / Rob Hyndman / May 31, 2021 In the first post we covered setting up a simple server and client which communicate with each other using websockets. The second post focused on the creation of a chat room. This third and final post in the series will build upon the techniques that we covered in the previous posts to create a simple multiplayer game “Fill Frenzy!”. Add Real-Time Game The second of the main components we will cover will be the game. The game will allow multiple users to interact with the game board, with the ultimate goal being to fill in every tile on the game board while the server clears one of the tiles on a set interval. This step will follow much the same pattern as the chat component, but as it will also be lengthy and touch multiple files, I will break it down into manageable sections. We will start by implementing the game server. Create server/game.js and add the following code. let socketio = undefined; let allTilesActive = false; let deactivationTime = 1000; let activeTiles = {}; Firstly, we create a variable socketio to use as a reference to our main Socket.IO object, and variables to keep track of the state of the tiles in the game. const difficultySettings = { easy: { name: 'Easy', gridSize: 5, tileSize: 128 }, medium: { name: 'Medium', gridSize: 10, tileSize: 64 }, hard: { name: 'Hard', gridSize: 20, tileSize: 32 } }; let gameDifficulty = difficultySettings.easy; Secondly, we add some difficulty settings so that we can make the game challenging when we want, but also give us a nice easy mode to test our code with. const initialise = (io) => { socketio = io; setInterval(() => { deactivateTile(); }, deactivationTime); } Next we create an initialise function which we can use to set our socketio reference. We also start a repeating timer to call the deactivateTile function on a set interval. const run = (socket) => { // new socket connected, send active tiles and game difficulty socket.emit('activeTiles', activeTiles); socket.emit('setDifficultyLevels', difficultySettings); socket.emit('gameDifficulty', gameDifficulty); if (allTilesActive) { // game is already completed, notify new connection socket.emit('gameCompleted'); } handleActivateTile(socket); handleAllTilesActive(socket); handleResetGame(socket); } The run function will handle the main functionality for the game for each individual client socket which is passed to the function as an argument. The function will be called only once per socket when a new connection is made, so we will take advantage of this to send the current state of the app to the socket by emitting the event activeTiles which passes the corresponding information. const handleActivateTile = (socket) => { // listen for 'activateTile' events socket.on('activateTile', (tile) => { // attach a tile id const key = `${tile.x},${tile.y}`; tile.id = key; // add the tile to the active tile collection if (!activeTiles.hasOwnProperty(key)) { activeTiles[key] = tile; // send the activated tile to all connected sockets socketio.emit('activateTile', tile); } }); } The handleActivateTile function listens for the activateTile event, and will respond appropriately. We receive a tile from the client, add an ID and save it to our activeTiles collection, and then update all clients with the new information. const handleAllTilesActive = (socket) => { // listen for 'allTilesActive' events socket.on('allTilesActive', (tileCount) => { if (allTilesActive) { return; } // count the number of active tiles const totalTiles = Object.keys(activeTiles).length; // compare the count of active tiles to the tile count received if (totalTiles === tileCount) { // complete the game socketio.emit('gameCompleted'); allTilesActive = true; } }); } The handleAllTilesActive function listens for the allTilesActive event, and will respond appropriately. We receive a count of the maximum number of tiles from the client, and then compare it to the number of tiles that we have tracked as active on the server. If the counts match we then send the gameCompleted event to all clients. const handleResetGame = (socket) => { // listen for 'resetGame' events socket.on('resetGame', (difficulty) => { resetGame(difficulty); }); } The handleResetGame function listens for the resetGame event, and will also take a difficulty setting to use for the next game. const randomTile = () => { // find existing keys let keys = Object.keys(activeTiles); // get a random key const randomKey = keys[(keys.length * Math.random()) << 0]; // return the random tile return activeTiles[randomKey]; } The randomTile function picks a random tile to return from the activeTiles collection. const deactivateTile = () => { if (allTilesActive) { return; } // select a random tile let tile = randomTile(); if (tile) { // deactivate the tile socketio.emit('deactivateTile', tile.id); delete activeTiles[tile.id]; } } The deactivateTile function is called on a set interval, and will select a random tile from the activeTiles collection to deactivate. It sends the deactivateTile event to all clients and specifies which tile id has been deactivated. const resetGame = (difficulty) => { // reset all tiles allTilesActive = false; activeTiles = {}; // set game difficulty gameDifficulty = difficultySettings[difficulty.toLowerCase()]; socketio.emit('gameDifficulty', gameDifficulty); socketio.emit('resetGame'); } // export these functions for external use module.exports = { initialise, run }; The resetGame function resets the game state back to default settings, and sends the resetGame event to all clients. Update server/index.js to include our new functionality. const express = require('express'); const http = require('http').Server(express); const socketio = require('socket.io')(http, { pingTimeout: 60000 }); const chat = require('./chat'); const game = require('./game'); const port = 3030; chat.initialise(socketio); game.initialise(socketio); socketio.on('connection', (socket) => { // new socket connected chat.run(socket); game.run(socket); }); http.listen(port, () => { console.log('Server started on port', port); }); After including the game.js file we created, we pass the socketio instance to the initialise function. Then, when the connection event triggers we pass the new socket connection through to the run function. Next we will implement the game client. Create src/store/game.js and add the following code. import Vue from 'vue'; export default { strict: false, namespaced: true, state: () => ({ tiles: {} }), getters: { GET_TILES: (state) => { return state.tiles; } }, mutations: { SET_TILES(state, tiles) { state.tiles = tiles; }, ADD_TILE(state, tile) { Vue.set(state.tiles, tile.id, tile); }, REMOVE_TILE(state, tileID) { Vue.delete(state.tiles, tileID); } }, actions: { RECEIVE_TILES({ commit }, tiles) { commit('SET_TILES', tiles); }, ACTIVATE_TILE({ commit }, tile) { commit('ADD_TILE', tile); }, DEACTIVATE_TILE({ commit }, tileID) { commit('REMOVE_TILE', tileID); } } }; The game store is similar to the chat store, with two main differences. There is only one set of data contained within the store, and we have added functionality to remove data. Update src/store/index.js with the following code. import Vue from 'vue'; import Vuex from 'vuex'; import * as socketio from '../plugins/socketio'; import chat from './chat'; import game from './game'; Vue.use(Vuex); export default new Vuex.Store({ strict: false, actions: { SEND_EVENT({}, event) { socketio.sendEvent(event); } }, modules: { chat, game } }); Loading our new file into the store as a module allows us to maintain good object separation and the usage of namespaces to access particular parts of the store. Create src/components/Game.vue and add the following code. The last part of the template adds a difficulty setting dropdown and a reset button if the game isCompleted. We also add a handy little tracker that presents the percentage of game completion. The usage of the v-if and v-else directives means that only one of these components is rendered at a time. Similar to the computed property, the watch property is also something a little different. It allows us to keep an eye on a variable (in this case the computed value of percentCompleted) and allows us to write some code that runs when the watched value changes. When our watched value changes, we see if the newValue equals 100, and if so we send the allTilesActive event to the server asking if it agrees that we’ve finished the game. We send the total number of game tiles along for the server to perform calculations with. The Game component makes use of the