
Fa'ailoa mai ile 2015 na avea ma tupuaga o se ituaiga fou , o lona lauiloa ua tupu tele talu mai lena taimi. Ua ou oo i le si'itia o le lauiloa o taaloga .io a'u lava ia: i le tolu tausaga ua tuana'i, I .
Afai e te le'i fa'alogo muamua i nei ta'aloga, e leai se totogi, ta'aloga i luga ole laiga multiplayer e faigofie ona ta'alo (leai se tala e mana'omia). E masani ona latou fa'afeto'ai le tele o tama ta'a'alo fa'afeagai i le malae e tasi. Isi ta'aloga ta'uta'ua .io: Šø .
I lenei pou o le a tatou iloa pe faapefea fatu se taaloga .io mai le sasa. Ina ia faia lenei mea, naʻo le malamalama o Javascript o le a lava: e tatau ona e malamalama i mea e pei o le syntax , upu autu this и . E tusa lava pe e te le iloa lelei le Javascript, e mafai lava ona e malamalama i le tele o le pou.
Faataitaiga o se taaloga .io
Mo fesoasoani a'oa'oga o le a matou fa'asino i ai . Taumafai e ta'alo!

O le taʻaloga e fai si faigofie: e te pulea se vaʻa i se malae ma isi tagata taʻalo. O lau va'a e otometi lava ona fa'a'a'a'ia fa'apolokalame ma e taumafai e lavea isi ta'a'alo a'o 'alo'ese mai a latou fa'ata.
1. Va'aiga puupuu/fa'atulagaina o galuega
Laʻasaga faataitaiga taaloga ina ia mafai ona e mulimuli mai ia te au.
O le faʻataʻitaʻiga e faʻaaogaina mea nei:
- o le taʻiala sili ona lauiloa i luga o le upega tafaʻilagi mo Node.js e faʻatautaia le upega tafaʻilagi a le taaloga.
- ā faletusi websocket mo le fesuiaiga o faamatalaga i le va o le browser ma le server.
- - pule o le module. E mafai ona e faitau pe aisea e faʻaaoga ai Webpack .
O le mea lea e foliga mai ai le fausaga o le lisi o galuega:
lautele/ aseta/ ... src/ client/ css/ ... html/ index.html index.js ... server/ server.js ... shared/ constants.js
lautele/
O loʻo i totonu o le faila mea uma public/ o le a fa'asalalauina fa'amau e le 'au'aunaga. IN public/assets/ o lo'o i ai ata na fa'aaogaina e la matou poloketi.
src /
O lo'o i totonu o le fa'ailoga mea uma src/. Suafa client/ Šø server/ tautala mo i latou lava ma shared/ o lo'o i ai se faila tumau e fa'aulufale mai e le kalani ma le server.
2. Fonotaga/poloketi tapula'a
E pei ona taʻua i luga, matou te faʻaogaina se pule o le module e fausia ai le poloketi . Sei o tatou vaʻai i la matou faʻatulagaga Webpack:
webpack.common.js:
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: {
game: './src/client/index.js',
},
output: {
filename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ['@babel/preset-env'],
},
},
},
{
test: /.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
},
'css-loader',
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css',
}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'src/client/html/index.html',
}),
],
};O laina sili ona taua iinei o mea nei:
src/client/index.jso le nofoaga e ulufale ai le tagata o tausia Javascript (JS). Webpack o le a amata mai iinei ma toe suʻe isi faila faʻaulufale mai.- O le JS o le matou Webpack build o le a maua i totonu o le lisi
dist/. O le a ou taʻua lenei faila o le matou JS afifi. - Matou te faʻaaogaina , aemaise le faatulagaga e fa'aliliu le tatou JS code mo su'esu'ega tuai.
- Matou te faʻaogaina se faʻapipiʻi e aveese uma CSS faʻasino e JS faila ma tuʻufaʻatasia i latou i se nofoaga e tasi. O le a ou faaigoaina o matou CSS afifi.
Atonu na e maitauina igoa uiga ese o faila faila '[name].[contenthash].ext'. O loo i ai Upega tafaʻilagi: [name] o le a suia i le igoa o le mea e tuʻuina atu (i la matou tulaga o le game), ma [contenthash] o le a suia i se hash o le faila faila. Matou te faia lenei mea - e mafai ona matou ta'u atu i tagata su'esu'e e teu a matou afifi JS e le gata ona afai e sui se afifi, e sui foi lona igoa faila (suiga contenthash). O le taunuuga mae'a o le igoa faila o le vaaiga game.dbeee76e91a97d0c7207.js.
faila webpack.common.js - O le faila fa'avae fa'avae lea matou te fa'aulufale mai i totonu o le atina'e ma fa'amae'aina le fa'atulagaina o galuega. Mo se faʻataʻitaʻiga, o le faʻatulagaina lea o le atinaʻe:
webpack.dev.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
}); Mo le lelei, matou te faʻaaogaina i le faagasologa o le atinaʻe webpack.dev.js, ma sui i webpack.prod.js, ia fa'amalieina le tele o afifi pe a fa'apipi'i i le gaosiga.
Seti fa'apitonu'u
Ou te fautuaina le faʻapipiʻiina o le poloketi i luga o lau masini faʻapitonuʻu ina ia mafai ona e mulimuli i laasaga o loʻo lisiina i lenei pou. E faigofie le seti: muamua, e tatau ona i ai le faiga и . Sosoo ai e tatau ona e faia
$ git clone https://github.com/vzhou842/example-.io-game.git
$ cd example-.io-game
$ npm installma ua e sauni e alu! Ina ia amata le server atinae, na o le tamoe
$ npm run developma alu i lau 'upega tafaʻilagi . O le a otometi lava ona toe fausia e le server development ia afifi JS ma CSS pe a o'o mai suiga fa'ailoga - na'o le toe fa'afouina o le itulau e va'ai i suiga uma!
3. Mea e ulufale ai tagata fa'atau
Se'i tatou o ifo i le ta'aloga lava ia. Muamua tatou te manaʻomia se itulau index.html, pe a e asiasi i le 'upega tafaʻilagi, o le a faʻapipiʻi muamua e le browser. O la matou itulau o le a fai si faigofie:
index.html
O se fa'ata'ita'iga .io ta'aloga TALI
O lenei faʻataʻitaʻiga faʻataʻitaʻiga ua faʻafaigofieina laʻititi mo le manino, ma o le a ou faia tutusa i le tele o isi faʻataʻitaʻiga i le pou. E mafai ona e va'ai i le code atoa i .
E iai sau sikaipi:
- (
<canvas>), lea o le a matou faʻaaogaina e tuʻuina atu ai le taʻaloga. <link>e fa'aopoopo le matou pusa CSS.<script>e fa'aopoopo le matou Javascript package.- Menu autu ma le username
<input>ma le faamau āTALIā (<button>).
O le taimi lava e utaina ai le itulau autu, o le a amata e le browser ona faʻaogaina le Javascript code, amata i le faila JS faila: src/client/index.js.
index.js
import { connect, play } from './networking';
import { startRendering, stopRendering } from './render';
import { startCapturingInput, stopCapturingInput } from './input';
import { downloadAssets } from './assets';
import { initState } from './state';
import { setLeaderboardHidden } from './leaderboard';
import './css/main.css';
const playMenu = document.getElementById('play-menu');
const playButton = document.getElementById('play-button');
const usernameInput = document.getElementById('username-input');
Promise.all([
connect(),
downloadAssets(),
]).then(() => {
playMenu.classList.remove('hidden');
usernameInput.focus();
playButton.onclick = () => {
// Play!
play(usernameInput.value);
playMenu.classList.add('hidden');
initState();
startCapturingInput();
startRendering();
setLeaderboardHidden(false);
};
});Atonu e foliga faigata lenei mea, ae o le mea moni e le tele se mea e tupu iinei:
- Auina mai nisi faila JS.
- Fa'aulufale mai CSS (ina ia iloa e le Webpack e fa'aofi i totonu o la matou pusa CSS).
- Tatala
connect()e faʻavae se fesoʻotaʻiga i le server ma amatadownloadAssets()e la'u mai ata e mana'omia e fa'atino ai le ta'aloga. - A mae'a le laasaga 3 o lo'o fa'aalia le lisi autu (
playMenu). - Fa'atulaga le "PLAY" kiliki le fa'amau. A oomi le ki, o le code e amataina le taaloga ma taʻu atu i le server ua matou sauni e taʻalo.
O le "aano o manufasi" autu o le matou client-server logic o loʻo i totonu o faila na faʻaulufale mai e le faila index.js. O lea la o le a tatou vaavaai atu ia i latou uma i le faasologa.
4. Fetufaaiga o fa'amaumauga a tagata o tausia
I lenei taʻaloga matou te faʻaogaina se faletusi lauiloa e fesoʻotaʻi ma le server . Socket.io o loʻo iai le lagolago faʻapipiʻi , lea e fetaui lelei mo fesoʻotaʻiga e lua: e mafai ona matou lafoina feʻau i le 'auʻaunaga и e mafai e le 'auʻaunaga ona lafo mai ni feʻau ia i matou i luga o le fesoʻotaʻiga tutusa.
E tasi le matou faila src/client/networking.jso ai na te tausia tagata uma feso'ota'iga ma le 'au'aunaga:
networking.js
import io from 'socket.io-client';
import { processGameUpdate } from './state';
const Constants = require('../shared/constants');
const socket = io(`ws://${window.location.host}`);
const connectedPromise = new Promise(resolve => {
socket.on('connect', () => {
console.log('Connected to server!');
resolve();
});
});
export const connect = onGameOver => (
connectedPromise.then(() => {
// Register callbacks
socket.on(Constants.MSG_TYPES.GAME_UPDATE, processGameUpdate);
socket.on(Constants.MSG_TYPES.GAME_OVER, onGameOver);
})
);
export const play = username => {
socket.emit(Constants.MSG_TYPES.JOIN_GAME, username);
};
export const updateDirection = dir => {
socket.emit(Constants.MSG_TYPES.INPUT, dir);
};O lenei code e fa'apu'upu'u teisi foi mo le manino.
E tolu mea taua e tupu i lenei faila:
- Matou te taumafai e faʻafesoʻotaʻi i le server.
connectedPromisena'o le fa'atagaina pe a matou fa'atuina se feso'ota'iga. - Afai e manuia le feso'ota'iga, matou te resitalaina galuega toe fo'i (
processGameUpdate()иonGameOver()) mo feʻau e mafai ona matou mauaina mai le 'auʻaunaga. - Matou te auina atu i fafo
play()иupdateDirection()ina ia mafai e isi faila ona faʻaaogaina.
5. Fa'asinomaga Client
Ua oo i le taimi e faaali ai le ata i luga o le lau!
...ae tatou te leʻi faia lenei mea, e manaʻomia ona tatou siiina uma ata (punaoa) e manaʻomia mo lenei. Sei o tatou tusi se pule o punaoa:
aseta.js
const ASSET_NAMES = ['ship.svg', 'bullet.svg'];
const assets = {};
const downloadPromise = Promise.all(ASSET_NAMES.map(downloadAsset));
function downloadAsset(assetName) {
return new Promise(resolve => {
const asset = new Image();
asset.onload = () => {
console.log(`Downloaded ${assetName}`);
assets[assetName] = asset;
resolve();
};
asset.src = `/assets/${assetName}`;
});
}
export const downloadAssets = () => downloadPromise;
export const getAsset = assetName => assets[assetName]; O le puleaina o punaoa e le faigata tele ona faʻatinoina! O le manatu autu o le teuina o se mea assets, lea o le a fusifusia ai le faila igoa ki i le mea taua Image. A uta le punaoa, matou te teuina i se mea assets mo le mauaina vave i le lumanaʻi. O afea e faʻatagaina ai le laʻuina o punaoa taʻitasi (o lona uiga, o le a download uma punaoa), matou te faatagaina downloadPromise.
A uma ona la'uina mai punaoa, e mafai ona e amata fa'aliliuina. E pei ona taʻua muamua, e tusi i luga o se itulau web matou te faʻaaogaina (<canvas>). O la matou taʻaloga e faigofie tele, o lea e naʻo matou manaʻomia le tuʻuina atu o mea nei:
- Talaaga
- Vaa tagata taalo
- O isi tagata taaalo i le taaloga
- atigi atigi
O vaega taua nei src/client/render.js, lea e tusi tonu ai vaega e fa o loo lisiina i luga:
render.js
import { getAsset } from './assets';
import { getCurrentState } from './state';
const Constants = require('../shared/constants');
const { PLAYER_RADIUS, PLAYER_MAX_HP, BULLET_RADIUS, MAP_SIZE } = Constants;
// Get the canvas graphics context
const canvas = document.getElementById('game-canvas');
const context = canvas.getContext('2d');
// Make the canvas fullscreen
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
function render() {
const { me, others, bullets } = getCurrentState();
if (!me) {
return;
}
// Draw background
renderBackground(me.x, me.y);
// Draw all bullets
bullets.forEach(renderBullet.bind(null, me));
// Draw all players
renderPlayer(me, me);
others.forEach(renderPlayer.bind(null, me));
}
// ... Helper functions here excluded
let renderInterval = null;
export function startRendering() {
renderInterval = setInterval(render, 1000 / 60);
}
export function stopRendering() {
clearInterval(renderInterval);
}E faapuupuuina foi lenei code mo le manino.
render() o le galuega autu o lenei faila. startRendering() Šø stopRendering() pulea le fa'agaoioia o le fa'asologa o fa'aliliuga ile 60 FPS.
Fa'atinoga fa'apitoa o galuega fesoasoani fesoasoani ta'ito'atasi (mo se fa'ata'ita'iga renderBullet()) e le taua tele, ae o se tasi lea o faʻataʻitaʻiga faigofie:
render.js
function renderBullet(me, bullet) {
const { x, y } = bullet;
context.drawImage(
getAsset('bullet.svg'),
canvas.width / 2 + x - me.x - BULLET_RADIUS,
canvas.height / 2 + y - me.y - BULLET_RADIUS,
BULLET_RADIUS * 2,
BULLET_RADIUS * 2,
);
} Manatua o loʻo matou faʻaaogaina le metotia getAsset(), lea sa vaaia muamua i asset.js!
Afai e te fiafia e su'esu'e isi galuega fesoasoani fesoasoani, ona faitau lea o le vaega o totoe .
6. Fa'asoa mai tagata fa'atau
Ua oo i le taimi e fai ai se taaloga ta'alo! O le faʻatonuga o le a faigofie tele: e sui le itu o le gaioiga, e mafai ona e faʻaogaina le kiore (i luga o se komepiuta) pe paʻi i le lau (i luga o se masini feaveaʻi). Ina ia faʻatinoina lenei mea o le a matou lesitala mo mea o le Isu ma le Pa'i.
O le a taulimaina nei mea uma src/client/input.js:
input.js
import { updateDirection } from './networking';
function onMouseInput(e) {
handleInput(e.clientX, e.clientY);
}
function onTouchInput(e) {
const touch = e.touches[0];
handleInput(touch.clientX, touch.clientY);
}
function handleInput(x, y) {
const dir = Math.atan2(x - window.innerWidth / 2, window.innerHeight / 2 - y);
updateDirection(dir);
}
export function startCapturingInput() {
window.addEventListener('mousemove', onMouseInput);
window.addEventListener('touchmove', onTouchInput);
}
export function stopCapturingInput() {
window.removeEventListener('mousemove', onMouseInput);
window.removeEventListener('touchmove', onTouchInput);
} onMouseInput() и onTouchInput() o Event Listeners e valaau updateDirection() (o networking.js) pe a tupu se mea faʻapipiʻi (mo se faʻataʻitaʻiga, pe a lue le isumu). updateDirection() e feagai ma le fefaʻatauaʻiga o feʻau ma le 'auʻaunaga, lea e faʻagasolo ai le mea na tupu ma faʻafouina le taʻaloga e tusa ai.
7. Tulaga Client
O lenei vaega e sili ona faigata i le vaega muamua o le pou. Aua e te lotovaivai pe afai e te le malamalama i le taimi muamua e te faitau ai! E mafai fo'i ona e faamisi ma toe sau i ai mulimuli ane.
O le vaega mulimuli o le paso e mana'omia e fa'atumu ai le code client-server o le tulaga. Manatua le snippet code mai le Client Rendering vaega?
render.js
import { getCurrentState } from './state';
function render() {
const { me, others, bullets } = getCurrentState();
// Do the rendering
// ...
} getCurrentState() e tatau ona mafai ona tuʻuina mai ia i matou le tulaga o taʻaloga o loʻo i ai nei i le kalani i soo se taimi fa'avae i fa'afouga na maua mai le 'au'aunaga. O se fa'ata'ita'iga lea o se fa'afouga ta'aloga e ono lafo mai e le 'au'aunaga:
{
"t": 1555960373725,
"me": {
"x": 2213.8050880413657,
"y": 1469.370893425012,
"direction": 1.3082443894581433,
"id": "AhzgAtklgo2FJvwWAADO",
"hp": 100
},
"others": [],
"bullets": [
{
"id": "RUJfJ8Y18n",
"x": 2354.029197099604,
"y": 1431.6848318262666
},
{
"id": "ctg5rht5s",
"x": 2260.546457727445,
"y": 1456.8088728920968
}
],
"leaderboard": [
{
"username": "Player",
"score": 3
}
]
}O fa'afouga ta'aloga ta'itasi o lo'o iai fa'ato'aga tutusa e lima:
- t: Fa'ailoga taimi o lo'o ta'u mai ai le taimi na faia ai lenei fa'afouga.
- me: Fa'amatalaga e uiga i le tagata ta'alo o lo'o mauaina lenei fa'afouga.
- isi: Se fa'asologa o fa'amatalaga e uiga i isi ta'aalo o lo'o auai i le ta'aloga lava e tasi.
- pulu: fa'asologa o fa'amatalaga e uiga i projectiles i le ta'aloga.
- leaderboard: Fa'amatalaga ta'ita'i o lo'o iai nei. Matou te le amanaia i latou i lenei pou.
7.1 Tulaga valea ole tagata fa'atau
Fa'atino fa'avalevalea getCurrentState() e mafai na'o le toe fa'afo'i sa'o mai fa'amatalaga mai le fa'afouga ta'aloga sili ona lata mai.
naive-state.js
let lastGameUpdate = null;
// Handle a newly received game update.
export function processGameUpdate(update) {
lastGameUpdate = update;
}
export function getCurrentState() {
return lastGameUpdate;
}Matagofie ma manino! Ae pe ana faapea e faigofie. O se tasi o mafuaʻaga o lenei faʻatinoga e faʻafitauli: e fa'atapula'aina le fua fa'avaa fa'asolo i le saoasaoa o le uati a le server.
Fua Fa'atatau: numera o fa'avaa (ie vala'au
render()) i le sekone, poʻo le FPS. O ta'aloga e masani ona taumafai e ausia le 60 FPS.
Fua Fa'ailoga: Ole taimi e lafo ai e le server fa'afouga ta'aloga i tagata fa'atau. E masani ona maualalo ifo nai lo le fua fa'avaa. I la matou ta'aloga, e tamo'e le server i le 30 ticks i le sekone.
Afai tatou te tuʻuina atu le faʻafouga fou o taʻaloga, ona le mafai lea e le FPS ona sili atu i le 30 ona matou te le maua lava le sili atu i le 30 faʻafouga i le sekone mai le 'auʻaunaga. E tusa lava pe tatou te valaau render() 60 taimi i le sekone, ona toe fai lea o le afa o nei telefoni i le mea lava e tasi, e leai se mea e faia. O le isi faʻafitauli i se faʻatinoga faʻavalevalea o le mea lea e fa'atuai. I le saoasaoa lelei o le Initaneti, o le a maua e le kalani se fa'afouga ta'aloga i le 33 ms (30 i le sekone):

Ae paga lea, e leai se mea e atoatoa. O se ata sili atu ona moni o le a:

O se fa'atinoga fa'avalea e sili ona leaga pe a o'o mai i le fa'agasolo. Afai e maua se fa'afouga ta'aloga ma le 50ms tuai, ona ua fa'agesegese le tagata o tausia e se 50ms faaopoopo ona o loʻo faʻaalia pea le taʻaloga mai le faʻafouga muamua. E mafai ona e mafaufauina le faigata o lenei mea mo le tagata taʻalo: ona o le faʻagesegese faʻafuaseʻi, o le taaloga o le a foliga mai e le mautonu ma le mautu.
7.2 Fa'aleleia tulaga o tagata o tausia
O le a matou faia ni faʻaleleia atili i le faʻatinoga faʻavalevalea. Muamua, matou te faʻaaogaina fa'atuai tu'u e 100 ms. O lona uiga o le tulaga "i le taimi nei" o le kalani o le a 100ms i tua atu o le taʻaloga i luga o le server. Mo se faʻataʻitaʻiga, pe afai o le taimi o le server 150, ona tu'uina atu lea e le kalani le tulaga sa i ai le server i le taimi 50:

E maua mai ai ia i matou se 100ms buffer e ola ai i le taimi le mautonu o faʻafouga taʻaloga:

O le tau mo lenei mea o le a tumau e 100 ms. O se taulaga la'ititi lea mo ta'aloga lamolemole - o le tele o ta'aalo (aemaise lava ta'aloga) latou te le'o matauina lenei tuai. E sili atu ona faigofie mo tagata ona fetuutuunai i se 100ms le tumau nai lo le taʻalo ma le le mautonu.
E mafai ona tatou faʻaogaina se isi metotia e taʻua , lea e faia se galuega lelei e faʻaitiitia ai le le mautonu, ae o le a le talanoaina i lenei pou.
O le isi faʻaleleia atili matou te faʻaaogaina o le feso'ota'iga laina. Ona o le tu'uina atu o le tuai, e masani lava ona tasi le fa'afouga i luma atu o le taimi nei i le kalani. Ina ua valaauina getCurrentState(), e mafai ona tatou faataunuuina i le va o fa'afouga ta'aloga a'o le'i mae'a le taimi nei i le kalani:

Ole mea lea e fo'ia ai le fa'afitauli o le fua fa'avaa: e mafai nei ona tatou tu'uina atu fa'avaa tulaga ese i so'o se fua fa'avaa tatou te mana'omia!
7.3 Fa'atino se tulaga fa'aleleia atili o tagata o tausia
Faataitaiga faatinoga i src/client/state.js e fa'aaoga uma le fa'atuai o le tu'uina atu ma le fa'asoa laina, ae e le umi se taimi. Sei o tatou vaevaeina le code i ni vaega se lua. O le mea muamua lenei:
state.js, vaega 1
const RENDER_DELAY = 100;
const gameUpdates = [];
let gameStart = 0;
let firstServerTimestamp = 0;
export function initState() {
gameStart = 0;
firstServerTimestamp = 0;
}
export function processGameUpdate(update) {
if (!firstServerTimestamp) {
firstServerTimestamp = update.t;
gameStart = Date.now();
}
gameUpdates.push(update);
// Keep only one game update before the current server time
const base = getBaseUpdate();
if (base > 0) {
gameUpdates.splice(0, base);
}
}
function currentServerTime() {
return firstServerTimestamp + (Date.now() - gameStart) - RENDER_DELAY;
}
// Returns the index of the base update, the first game update before
// current server time, or -1 if N/A.
function getBaseUpdate() {
const serverTime = currentServerTime();
for (let i = gameUpdates.length - 1; i >= 0; i--) {
if (gameUpdates[i].t <= serverTime) {
return i;
}
}
return -1;
} O le mea muamua e tatau ona e faia o le iloa lea o le mea e fai currentServerTime(). E pei ona tatou vaʻaia muamua, o faʻafouga taʻaloga uma e aofia ai se faʻamaufaʻailoga taimi. Matou te mananaʻo e faʻaoga le tuʻuina atu o le latency e tuʻuina atu ai le ata 100ms i tua o le 'auʻaunaga, ae matou te le iloa lava le taimi nei i luga o le server, aua e le mafai ona matou iloa pe o le a le umi na alu ai mo soʻo se faʻafouga e oʻo mai ia i matou. O le Initaneti e le taumateina ma o lona saoasaoa e mafai ona matua ese!
Ina ia foia lenei faafitauli, e mafai ona tatou faʻaogaina se faʻatatau talafeagai: matou se'i fa'afoliga na o'o mai loa le fa'afouga muamua. Afai e moni lenei mea, ona matou iloa lea o le taimi o le server i lena taimi faapitoa! Matou te teuina le timestamp server i totonu firstServerTimestamp ma laveai i matou lotoifale (client) timestamp i le taimi lava e tasi i totonu gameStart.
Oi, faatali mo sina minute. E le tatau ona i ai se taimi i luga o le server = taimi i luga o le kalani? Aisea tatou te fa'aeseese ai i le va o le "server timestamp" ma le "client timestamp"? O se fesili sili lenei! E aliali mai e le tutusa ia mea. Date.now() o le a toe fa'afo'i fa'ailoga taimi eseese i le kalani ma le 'au'aunaga ma e fa'alagolago lea i mea fa'apitonu'u i nei masini. Aua ne'i manatu o le a tutusa fa'ailoga taimi i masini uma.
O lea ua tatou malamalama i le mea e fai currentServerTime(): e toe foi mai fa'amaufa'ailoga taimi o le taimi fa'aalia o lo'o iai nei. I se isi faaupuga, o le taimi nei o le server (firstServerTimestamp <+ (Date.now() - gameStart)) to'ese le tuai fa'aliliu (RENDER_DELAY).
Se'i o tatou va'ai pe fa'afefea ona tatou taulimaina fa'afouga ta'aloga. A maua se faʻafouga mai le 'auʻaunaga, e taʻua processGameUpdate(), ma matou fa'asaoina le fa'afouga fou i se fa'asologa gameUpdates. Ma, e siaki le faʻaogaina o manatua, matou te aveese uma faʻafouga tuai i fa'afou fa'avaeaua tatou te le toe manaomia.
O le a le "faafouga autu"? Lenei o le faʻafouga muamua matou te maua e ala i le faʻagasolo i tua mai le taimi o loʻo iai nei. Manatua le ata lea?

O le fa'afouga o le ta'aloga sa'o i le itu agavale o le "Client Render Time" o le fa'afouga fa'avae.
O le a le mea e fa'aoga ai le fa'afouga fa'avae? Aisea e mafai ai ona tatou lafoa'i fa'afouga ile fa'avae? Ina ia malamalama i lenei mea, tatou mulimuli ane sei tatou tilotilo i le faatinoga getCurrentState():
state.js, vaega 2
export function getCurrentState() {
if (!firstServerTimestamp) {
return {};
}
const base = getBaseUpdate();
const serverTime = currentServerTime();
// If base is the most recent update we have, use its state.
// Else, interpolate between its state and the state of (base + 1).
if (base < 0) {
return gameUpdates[gameUpdates.length - 1];
} else if (base === gameUpdates.length - 1) {
return gameUpdates[base];
} else {
const baseUpdate = gameUpdates[base];
const next = gameUpdates[base + 1];
const r = (serverTime - baseUpdate.t) / (next.t - baseUpdate.t);
return {
me: interpolateObject(baseUpdate.me, next.me, r),
others: interpolateObjectArray(baseUpdate.others, next.others, r),
bullets: interpolateObjectArray(baseUpdate.bullets, next.bullets, r),
};
}
}Matou te taulimaina mataupu e tolu:
base < 0o lona uiga e leai ni faʻafouga seʻia oʻo i le taimi o le faʻaaliga o loʻo iai nei (silasila i le faʻatinoga i lugagetBaseUpdate()). E mafai ona tupu sa'o i le amataga o le ta'aloga ona o le fa'aletonu o le fa'aliliuina. I lenei tulaga, matou te faʻaogaina le faʻafouga aupito lata mai na maua.baseo le fa'afouga aupito lata mai lea ua tatou maua. E ono tupu lenei mea ona o le le lava o feso'ota'iga po'o le leaga o le feso'ota'iga initaneti. I lenei tulaga foi matou te faʻaaogaina le faʻafouga fou ua matou maua.- E i ai le matou fa'afouga a'o le'i mae'a ma le taimi e tu'uina atu ai nei, ina ia mafai fefiloi!
O mea uma o totoe i totonu state.js o se fa'atinoga o fa'atasiga laina e faigofie (ae le manaia) numera. Afai e te manaʻo e suʻesuʻe oe lava ia, ona tatala lea state.js i .
Vaega 2. Backend server
I lenei vaega o le a tatou vaʻavaʻai i le Node.js backend o loʻo pulea a tatou .
1. Mea e ulufale ai le server
Ina ia pulea le 'upega tafaʻilagi o le a matou faʻaogaina se taʻutaʻua upega tafaʻilagi mo Node.js taʻua . O le a fa'apipi'iina e la matou 'au'aunaga fa'ailoga faila src/server/server.js:
server.js, vaega 1
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackConfig = require('../../webpack.dev.js');
// Setup an Express server
const app = express();
app.use(express.static('public'));
if (process.env.NODE_ENV === 'development') {
// Setup Webpack for development
const compiler = webpack(webpackConfig);
app.use(webpackDevMiddleware(compiler));
} else {
// Static serve the dist/ folder in production
app.use(express.static('dist'));
}
// Listen on port
const port = process.env.PORT || 3000;
const server = app.listen(port);
console.log(`Server listening on port ${port}`);Manatua i le vaega muamua na matou talanoaina Webpack? O le mea lea o le a matou faʻaogaina ai a matou Webpack configurations. O le a matou faʻaaogaina i ni auala se lua:
- Faʻaaoga e otometi ona toe fausia a tatou afifi atinae, pe
- Fa'aliliuina fa'amau se faila
dist/, i totonu o le Webpack o le a tusia ai a tatou faila pe a uma le gaosiga o le fausiaina.
O le isi galuega taua server.js e aofia ai le setiina o le server lea e na'o le feso'ota'i atu i le 'au'aunaga Express:
server.js, vaega 2
const socketio = require('socket.io');
const Constants = require('../shared/constants');
// Setup Express
// ...
const server = app.listen(port);
console.log(`Server listening on port ${port}`);
// Setup socket.io
const io = socketio(server);
// Listen for socket.io connections
io.on('connection', socket => {
console.log('Player connected!', socket.id);
socket.on(Constants.MSG_TYPES.JOIN_GAME, joinGame);
socket.on(Constants.MSG_TYPES.INPUT, handleInput);
socket.on('disconnect', onDisconnect);
}); A maeʻa ona faʻatūina se socket.io soʻotaga ma le 'auʻaunaga, matou te faʻapipiʻi mea e fai mo le socket fou. O lo'o fa'agasolo e le au fai fa'alavelave fe'au na maua mai tagata fa'atau e ala i le tu'uina atu i se mea e tasi game:
server.js, vaega 3
const Game = require('./game');
// ...
// Setup the Game
const game = new Game();
function joinGame(username) {
game.addPlayer(this, username);
}
function handleInput(dir) {
game.handleInput(this, dir);
}
function onDisconnect() {
game.removePlayer(this);
} O loŹ»o matou fatuina se taŹ»aloga .io, o lea e naŹ»o le tasi le kopi matou te manaŹ»omia Game (āTaalogaā) ā e taaalo uma tagata taaalo i le malae e tasi! I le isi vaega o le a tatou vaŹ»ai pe faŹ»apefea ona galue lenei vasega Game.
2. Ta'aloga ta'aloga
Vasega Game o lo'o i ai le manatu pito sili ona taua a le server-side. E lua ana galuega autu: pulega ta'aalo Šø fa'ata'ita'iga ta'aloga.
Tatou amata i le galuega muamua - pulea le au taaalo.
game.js, vaega 1
const Constants = require('../shared/constants');
const Player = require('./player');
class Game {
constructor() {
this.sockets = {};
this.players = {};
this.bullets = [];
this.lastUpdateTime = Date.now();
this.shouldSendUpdate = false;
setInterval(this.update.bind(this), 1000 / 60);
}
addPlayer(socket, username) {
this.sockets[socket.id] = socket;
// Generate a position to start this player at.
const x = Constants.MAP_SIZE * (0.25 + Math.random() * 0.5);
const y = Constants.MAP_SIZE * (0.25 + Math.random() * 0.5);
this.players[socket.id] = new Player(socket.id, username, x, y);
}
removePlayer(socket) {
delete this.sockets[socket.id];
delete this.players[socket.id];
}
handleInput(socket, dir) {
if (this.players[socket.id]) {
this.players[socket.id].setDirection(dir);
}
}
// ...
} I lenei taaloga o le a tatou iloa ai tagata taaalo e ala i le malae id la latou socket socket.io (afai e te le mautonu, ona toe foʻi lea i server.js). Socket.io lava ia e tuʻuina atu soʻo taʻitasi se tulaga ese id, o lea e le tatau ai ona tatou popole i ai. ou te valaau atu ia te ia ID tagata taalo.
Faatasi ai ma lena mea i le mafaufau, seʻi o tatou suʻesuʻeina faʻataʻitaʻiga fesuiaiga i totonu o le vasega Game:
socketso se mea e fusifusia ai le ID tagata ta'alo i le socket e feso'ota'i ma le tagata ta'alo. E mafai ai ona matou maua sockets e latou ID taʻaloga i le taimi.playerso se mea faitino e fusifusia ai le ID tagata taʻalo i le code> Mea taʻalo
bullets o se seti o mea faitino Bullet, e leai se fa'atonuga fa'apitoa.
lastUpdateTime - Ole fa'ailoga taimi lea ole fa'afouga ta'aloga mulimuli. O le a tatou vaʻai pe faʻapefea ona faʻaaogaina vave.
shouldSendUpdate o se fesuiaiga ausilali. O le a tatou vaʻai foʻi i lona faʻaaogaina lata mai.
Metotia addPlayer(), removePlayer() и handleInput() e le manaʻomia ona faʻamatalaina, o loʻo faʻaaogaina i totonu server.js. Afai e te manaʻomia se toe faʻaleleia, toe foʻi i tua teisi maualuga.
Laina mulimuli constructor() amata i luga fa'afouga ta'amilosaga ta'aloga (fa'atasi ai ma le 60 fa'afouga/s):
game.js, vaega 2
const Constants = require('../shared/constants');
const applyCollisions = require('./collisions');
class Game {
// ...
update() {
// Calculate time elapsed
const now = Date.now();
const dt = (now - this.lastUpdateTime) / 1000;
this.lastUpdateTime = now;
// Update each bullet
const bulletsToRemove = [];
this.bullets.forEach(bullet => {
if (bullet.update(dt)) {
// Destroy this bullet
bulletsToRemove.push(bullet);
}
});
this.bullets = this.bullets.filter(
bullet => !bulletsToRemove.includes(bullet),
);
// Update each player
Object.keys(this.sockets).forEach(playerID => {
const player = this.players[playerID];
const newBullet = player.update(dt);
if (newBullet) {
this.bullets.push(newBullet);
}
});
// Apply collisions, give players score for hitting bullets
const destroyedBullets = applyCollisions(
Object.values(this.players),
this.bullets,
);
destroyedBullets.forEach(b => {
if (this.players[b.parentID]) {
this.players[b.parentID].onDealtDamage();
}
});
this.bullets = this.bullets.filter(
bullet => !destroyedBullets.includes(bullet),
);
// Check if any players are dead
Object.keys(this.sockets).forEach(playerID => {
const socket = this.sockets[playerID];
const player = this.players[playerID];
if (player.hp <= 0) {
socket.emit(Constants.MSG_TYPES.GAME_OVER);
this.removePlayer(socket);
}
});
// Send a game update to each player every other time
if (this.shouldSendUpdate) {
const leaderboard = this.getLeaderboard();
Object.keys(this.sockets).forEach(playerID => {
const socket = this.sockets[playerID];
const player = this.players[playerID];
socket.emit(
Constants.MSG_TYPES.GAME_UPDATE,
this.createUpdate(player, leaderboard),
);
});
this.shouldSendUpdate = false;
} else {
this.shouldSendUpdate = true;
}
}
// ...
} Metotia update() atonu o lo'o i ai le vaega pito sili ona taua o le manatu o le server-side. Sei o tatou lisi mea uma e fai i le faasologa:
- Fa'atatau le taimi
dttalu mai le taimi mulimuliupdate(). - Fa'afou ta'iala ta'itasi ma fa'aumatia pe a mana'omia. O le a tatou vaʻai i le faʻatinoga o lenei faʻatinoga mulimuli ane. Mo le taimi nei ua lava lo tatou iloa o lena mea
bullet.update()toe fo'i maitrue, pe afai e tatau ona faʻaumatia le projectile (na alu o ia i fafo o le malae). - Fa'afou ta'a'alo ta'itasi ma fai se projectile pe a mana'omia. O le a tatou vaʻai foi i lenei faʻatinoga mulimuli ane -
player.update()mafai ona toe faafoi se meaBullet. - Siaki mo fete'ena'iga i le va o projectiles ma tagata ta'alo o lo'o fa'aogaina
applyCollisions(), lea e toe fa'afo'i mai ai se fa'aputuga o mata'itusi e taia ai tagata ta'aalo. Mo ta'aloga ta'itasi e toe fa'afo'i mai, matou te fa'aopoopoina le sikoa a le tagata ta'alo na fanaina (fa'aaogaplayer.onDealtDamage()), ona aveese lea o le projectile mai le lainabullets. - Fa'ailoa ma fa'aumatia tagata ta'aalo uma.
- Auina atu se fa'afouga ta'aloga i ta'aalo uma i sekone ta'itasi taimi pe a valaauina
update(). O le fesuiaiga o ausilali o loʻo taʻua i luga e fesoasoani ia i tatou e siaki lenei meashouldSendUpdate. Auaupdate()valaau 60 taimi / s, matou auina atu faafouga taaloga 30 taimi / s. O lea, taimi ole uati server o le 30 uati taamilosaga / s (sa matou talanoa e uiga i le taimi o le uati i le vaega muamua).
Aisea e lafo ai na'o fa'afouga ta'aloga i le taimi ? Ina ia faasaoina le alaleo. 30 faʻafouga taʻaloga i le sekone e tele!
Aisea e le na o le valaau atu i lena taimi?
update()30 taimi i le sekone? Ina ia faʻaleleia le faʻataʻitaʻiga o taʻaloga. O le tele o taimi e ta'ua aiupdate(), o le a sili atu le saʻo o le faʻataʻitaʻiga o le taʻaloga. Ae aua e te soona faaseseina i le tele o luitauupdate(), aua o se galuega taugata faʻatusatusa - 60 i le sekone ua lava.
O le isi vaega o le vasega Game e aofia ai auala fesoasoani o loʻo faʻaaogaina i update():
game.js, vaega 3
class Game {
// ...
getLeaderboard() {
return Object.values(this.players)
.sort((p1, p2) => p2.score - p1.score)
.slice(0, 5)
.map(p => ({ username: p.username, score: Math.round(p.score) }));
}
createUpdate(player, leaderboard) {
const nearbyPlayers = Object.values(this.players).filter(
p => p !== player && p.distanceTo(player) <= Constants.MAP_SIZE / 2,
);
const nearbyBullets = this.bullets.filter(
b => b.distanceTo(player) <= Constants.MAP_SIZE / 2,
);
return {
t: Date.now(),
me: player.serializeForUpdate(),
others: nearbyPlayers.map(p => p.serializeForUpdate()),
bullets: nearbyBullets.map(b => b.serializeForUpdate()),
leaderboard,
};
}
} getLeaderboard() E faigofie lava - e fa'avasega tagata ta'alo i sikoa, ave le lima pito i luga, ma toe fa'afo'i le igoa ole igoa ma sikoa mo ta'itasi.
createUpdate() faʻaaogaina i update() e fatu ai fa'afouga ta'aloga e tufatufa atu i tagata ta'aalo. O lana galuega autu o le valaau metotia serializeForUpdate(), fa'atinoina mo vasega Player и Bullet. Manatua e na'o le tu'uina atu o fa'amatalaga i tagata ta'alo ta'itasi e uiga i lata ane tagata taʻalo ma projectiles - e leai se manaʻoga e tuʻuina atu faʻamatalaga e uiga i mea taʻaloga e mamao ese mai le tagata taʻalo!
3. Taaloga mea faitino i luga o le server
I la tatou ta'aloga, e matua'i tutusa lava ta'aloga ma ta'a'alo: o mea ta'a'alo fa'ata'amilo e fealua'i. Ina ia faʻaogaina lenei mea tutusa i le va o taʻaloga ma projectiles, tatou amata i le faʻatinoina o se vasega faavae Object:
object.js
class Object {
constructor(id, x, y, dir, speed) {
this.id = id;
this.x = x;
this.y = y;
this.direction = dir;
this.speed = speed;
}
update(dt) {
this.x += dt * this.speed * Math.sin(this.direction);
this.y -= dt * this.speed * Math.cos(this.direction);
}
distanceTo(object) {
const dx = this.x - object.x;
const dy = this.y - object.y;
return Math.sqrt(dx * dx + dy * dy);
}
setDirection(dir) {
this.direction = dir;
}
serializeForUpdate() {
return {
id: this.id,
x: this.x,
y: this.y,
};
}
} E leai se mea faigata e tupu iinei. O lenei vasega o le a avea ma amataga lelei mo le faʻalauteleina. Sei o tatou vaai pe faapefea le vasega Bullet faʻaaoga Object:
pulu.js
const shortid = require('shortid');
const ObjectClass = require('./object');
const Constants = require('../shared/constants');
class Bullet extends ObjectClass {
constructor(parentID, x, y, dir) {
super(shortid(), x, y, dir, Constants.BULLET_SPEED);
this.parentID = parentID;
}
// Returns true if the bullet should be destroyed
update(dt) {
super.update(dt);
return this.x < 0 || this.x > Constants.MAP_SIZE || this.y < 0 || this.y > Constants.MAP_SIZE;
}
} Š ŠµŠ°Š»ŠøŠ·Š°ŃŠøŃ Bullet puupuu tele! Ua matou faaopoopo i Object na'o fa'aopoopoga nei:
- Fa'aaogaina o le afifi mo tupulaga fai fua
idfa'ailoga. - Fa'aopoopoina se fanua
parentID, ina ia mafai ona e siaki le tagata taʻalo na faia lenei poloketi. - Fa'aopoopo le tau fa'afo'i i
update(), lea e tutusatrue, pe afai o le projectile o loʻo i fafo atu o le malae (manatua na matou talanoa e uiga i lenei mea i le vaega mulimuli?).
Sei o tatou agai i luma Player:
player.js
const ObjectClass = require('./object');
const Bullet = require('./bullet');
const Constants = require('../shared/constants');
class Player extends ObjectClass {
constructor(id, username, x, y) {
super(id, x, y, Math.random() * 2 * Math.PI, Constants.PLAYER_SPEED);
this.username = username;
this.hp = Constants.PLAYER_MAX_HP;
this.fireCooldown = 0;
this.score = 0;
}
// Returns a newly created bullet, or null.
update(dt) {
super.update(dt);
// Update score
this.score += dt * Constants.SCORE_PER_SECOND;
// Make sure the player stays in bounds
this.x = Math.max(0, Math.min(Constants.MAP_SIZE, this.x));
this.y = Math.max(0, Math.min(Constants.MAP_SIZE, this.y));
// Fire a bullet, if needed
this.fireCooldown -= dt;
if (this.fireCooldown <= 0) {
this.fireCooldown += Constants.PLAYER_FIRE_COOLDOWN;
return new Bullet(this.id, this.x, this.y, this.direction);
}
return null;
}
takeBulletDamage() {
this.hp -= Constants.BULLET_DAMAGE;
}
onDealtDamage() {
this.score += Constants.SCORE_BULLET_HIT;
}
serializeForUpdate() {
return {
...(super.serializeForUpdate()),
direction: this.direction,
hp: this.hp,
};
}
} O tagata taaalo e sili atu le lavelave nai lo projectiles, o lea e tatau ai i lenei vasega ona teuina ni nai fanua. O lana metotia update() e tele atu galuega, ae maise le toe fa'afo'iina o le projectile fou pe a leai se mea o totoe fireCooldown (manatua na tatou talanoa e uiga i lenei mea i le vaega muamua?). E faʻalauteleina ai foi le auala serializeForUpdate(), aua tatou te manaʻomia le faʻaopoopoina o fanua faʻaopoopo mo le tagata taʻalo i le faʻafouina o le taʻaloga.
Avanoa o se vasega faavae Object - o se laasaga taua e aloese ai mai le toe fai tulafono. Mo se faataitaiga, e aunoa ma se vasega Object o mea ta'aloga uma e tatau ona tutusa le fa'atinoga distanceTo(), ma kopi-fa'apipi'i uma nei fa'atinoga i le tele o faila o le a avea ma se miti leaga. E taua tele lenei mea mo galuega tetele, pe a o le numera o le faʻalauteleina Object ua faatupulaia vasega.
4. Su'esu'eina o fa'alavelave
Pau lava le mea tatou te faia o le iloa lea pe a taia e le au taʻavale le au taʻaalo! Manatua lenei snippet code mai le metotia update() i le vasega Game:
taaloga.js
const applyCollisions = require('./collisions');
class Game {
// ...
update() {
// ...
// Apply collisions, give players score for hitting bullets
const destroyedBullets = applyCollisions(
Object.values(this.players),
this.bullets,
);
destroyedBullets.forEach(b => {
if (this.players[b.parentID]) {
this.players[b.parentID].onDealtDamage();
}
});
this.bullets = this.bullets.filter(
bullet => !destroyedBullets.includes(bullet),
);
// ...
}
} E tatau ona tatou faʻatinoina le metotia applyCollisions(), lea e toe fa'afo'i mai ai projectiles uma na lavea ai tagata ta'aalo. O le mea e lelei ai, e le faigata tele ona fai aua
- O mea feto'ai uma o li'o, ma o le foliga sili lea ona faigofie e fa'atino ai le su'eina o fa'alavelave.
- Ua uma ona i ai sa matou metotia
distanceTo(), lea na matou faʻatinoina i le vasega i le vaega muamuaObject.
O le mea lea e foliga mai ai le faʻatinoina o le faʻalavelave faʻafuaseʻi:
collisions.js
const Constants = require('../shared/constants');
// Returns an array of bullets to be destroyed.
function applyCollisions(players, bullets) {
const destroyedBullets = [];
for (let i = 0; i < bullets.length; i++) {
// Look for a player (who didn't create the bullet) to collide each bullet with.
// As soon as we find one, break out of the loop to prevent double counting a bullet.
for (let j = 0; j < players.length; j++) {
const bullet = bullets[i];
const player = players[j];
if (
bullet.parentID !== player.id &&
player.distanceTo(bullet) <= Constants.PLAYER_RADIUS + Constants.BULLET_RADIUS
) {
destroyedBullets.push(bullet);
player.takeBulletDamage();
break;
}
}
}
return destroyedBullets;
}O lenei su'esu'ega fa'afefe faigofie e fa'avae i le mea moni lena e lua li'o e feto'ai pe afai o le mamao i le va o latou nofoaga tutotonu e itiiti ifo i le aofa'i o o latou radii. O se tulaga lea o le mamao i le va o totonugalemu o li'o e lua e tutusa lelei ma le aofa'i o latou radii:

O iinei e tatau ona e gauai totoʻa i ni nai vaega:
- E le tatau ona lavea le tagata taalo na faia. E mafai ona ausia lenei mea e ala i le faʻatusatusa
bullet.parentIDŃplayer.id. - E na'o le tasi lava le lavea o le projectile i le tulaga ogaoga o le taia o le tele o tagata taaalo i le taimi e tasi. O le a matou foia lenei faŹ»afitauli e faŹ»aaoga ai le tagata faŹ»aoga
break: O le taimi lava e maua ai se tagata taalo o loʻo fetoʻai ma se mea faʻapipiʻi, matou te tuʻu le suʻega ae faʻaauau i le isi mea e sosoo ai.
Le iuga
Pau lava lena! Ua matou ufiufi mea uma e te manaʻomia e te iloa e fai ai se taʻaloga i luga ole laiga .io. O le a le isi mea? Fausia lau lava .io taaloga!
O faʻataʻitaʻiga code uma e tatala ma faʻapipiʻi i luga .
puna: www.habr.com
