2015 рдордзреНрдпреЗ рд░рд┐рд▓реАрдЬ рдЭрд╛рд▓рд╛
рдЬрд░ рддреБрдореНрд╣реА рдпрд╛ рдЦреЗрд│рд╛рдВрдмрджреНрджрд▓ рдпрд╛рдЖрдзреА рдХрдзреАрд╣реА рдРрдХрд▓реЗ рдирд╕реЗрд▓, рддрд░ рд╣реЗ рд╡рд┐рдирд╛рдореВрд▓реНрдп рдорд▓реНрдЯреАрдкреНрд▓реЗрдЕрд░ рд╡реЗрдм рдЧреЗрдо рдЖрд╣реЗрдд рдЬреЗ рдЦреЗрд│рдгреНрдпрд╛рд╕ рд╕реЛрдкреЗ рдЖрд╣реЗрдд (рдЦрд╛рддреНрдпрд╛рдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╛рд╣реА). рддреЗ рд╕рд╣рд╕рд╛ рдПрдХрд╛рдЪ рдореИрджрд╛рдирд╛рдд рдЕрдиреЗрдХ рд╡рд┐рд░реЛрдзреА рдЦреЗрд│рд╛рдбреВрдВрдирд╛ рдЙрднреЗ рдХрд░рддрд╛рдд. рдЗрддрд░ рдкреНрд░рд╕рд┐рджреНрдз .io рдЦреЗрд│:
рдпрд╛ рдкреЛрд╕реНрдЯрдордзреНрдпреЗ, рдЖрдореНрд╣реА рдХрд╕реЗ рддреЗ рд╢реЛрдзреВ рд╕реБрд░рд╡рд╛рддреАрдкрд╛рд╕реВрди рдПрдХ .io рдЧреЗрдо рддрдпрд╛рд░ рдХрд░рд╛. рдпрд╛рд╕рд╛рдареА, рдлрдХреНрдд Javascript рдЪреЗ рдЬреНрдЮрд╛рди рдкреБрд░реЗрд╕реЗ рдЕрд╕реЗрд▓: рддреБрдореНрд╣рд╛рд▓рд╛ рд╕рд┐рдВрдЯреЕрдХреНрд╕ рд╕рд╛рд░рдЦреНрдпрд╛ рдЧреЛрд╖реНрдЯреА рд╕рдордЬреВрди рдШреЗрдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ this
╨╕
io рдЧреЗрдордЪреЗ рдЙрджрд╛рд╣рд░рдг
рд╢рд┐рдХрдгреНрдпрд╛рдЪреНрдпрд╛ рд╕рд╣рд╛рдпреНрдпрд╛рд╕рд╛рдареА, рдЖрдореНрд╣реА рд╕рдВрджрд░реНрдн рдШреЗрдК
рдЦреЗрд│ рдЕрдЧрджреА рд╕реЛрдкрд╛ рдЖрд╣реЗ: рдЖрдкрдг рдПрдЦрд╛рджреНрдпрд╛ рд░рд┐рдВрдЧрдгрд╛рдд рдЬрд╣рд╛рдЬ рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░рддрд╛ рдЬрд┐рдереЗ рдЗрддрд░ рдЦреЗрд│рд╛рдбреВ рдЕрд╕рддрд╛рдд. рддреБрдордЪреЗ рдЬрд╣рд╛рдЬ рдЖрдкреЛрдЖрдк рдкреНрд░реЛрдЬреЗрдХреНрдЯрд╛рдЗрд▓ рдлрд╛рдпрд░ рдХрд░рддреЗ рдЖрдгрд┐ рддреБрдореНрд╣реА рдЗрддрд░ рдЦреЗрд│рд╛рдбреВрдВрдирд╛ рддреНрдпрд╛рдВрдЪреЗ рдкреНрд░реЛрдЬреЗрдХреНрдЯрд╛рдЗрд▓ рдЯрд╛рд│реВрди рдорд╛рд░рдгреНрдпрд╛рдЪрд╛ рдкреНрд░рдпрддреНрди рдХрд░рддрд╛.
1. рдкреНрд░рдХрд▓реНрдкрд╛рдЪреЗ рд╕рдВрдХреНрд╖рд┐рдкреНрдд рд╡рд┐рд╣рдВрдЧрд╛рд╡рд▓реЛрдХрди / рд░рдЪрдирд╛
рдореА рд╢рд┐рдлрд╛рд░рд╕ рдХрд░рддреЛ
рд╕реНрд░реЛрдд рдХреЛрдб рдбрд╛рдЙрдирд▓реЛрдб рдХрд░рд╛ рдЙрджрд╛рд╣рд░рдг рдЧреЗрдо рдЬреЗрдгреЗрдХрд░реВрди рддреБрдореНрд╣реА рдорд╛рдЭреЗ рдЕрдиреБрд╕рд░рдг рдХрд░реВ рд╢рдХрддрд╛.
рдЙрджрд╛рд╣рд░рдг рдЦрд╛рд▓реАрд▓ рд╡рд╛рдкрд░рддреЗ:
рд╡реНрдпрдХреНрдд рдЧреЗрдордЪреЗ рд╡реЗрдм рд╕рд░реНрд╡реНрд╣рд░ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдгрд╛рд░реЗ рд╕рд░реНрд╡рд╛рдд рд▓реЛрдХрдкреНрд░рд┐рдп Node.js рд╡реЗрдм рдлреНрд░реЗрдорд╡рд░реНрдХ рдЖрд╣реЗ.socket.io - рдмреНрд░рд╛рдЙрдЭрд░ рдЖрдгрд┐ рд╕рд░реНрд╡реНрд╣рд░ рджрд░рдореНрдпрд╛рди рдбреЗрдЯрд╛рдЪреА рджреЗрд╡рд╛рдгрдШреЗрд╡рд╛рдг рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рд╡реЗрдмрд╕реЙрдХреЗрдЯ рд▓рд╛рдпрдмреНрд░рд░реА.рд╡реЗрдмрдкреЕрдХ - рдореЙрдбреНрдпреВрд▓ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ. рд╡реЗрдмрдкреЕрдХ рдХрд╛ рд╡рд╛рдкрд░рд╛рдпрдЪреЗ рдпрд╛рдмрджреНрджрд▓ рддреБрдореНрд╣реА рд╡рд╛рдЪреВ рд╢рдХрддрд╛.рдпреЗрдереЗ .
рдкреНрд░рдХрд▓реНрдк рдирд┐рд░реНрджреЗрд╢рд┐рдХрд╛ рд░рдЪрдирд╛ рдХрд╢реА рджрд┐рд╕рддреЗ рддреЗ рдпреЗрдереЗ рдЖрд╣реЗ:
public/
assets/
...
src/
client/
css/
...
html/
index.html
index.js
...
server/
server.js
...
shared/
constants.js
рд╕рд╛рд░реНрд╡рдЬрдирд┐рдХ/
рдлреЛрд▓реНрдбрд░рдордзреНрдпреЗ рд╕рд░реНрд╡ рдХрд╛рд╣реА public/
рд╕рд░реНрд╡реНрд╣рд░рджреНрд╡рд╛рд░реЗ рд╕реНрдерд┐рд░рдкрдгреЗ рд╕рдмрдорд┐рдЯ рдХреЗрд▓реЗ рдЬрд╛рдИрд▓. IN public/assets/
рдЖрдордЪреНрдпрд╛ рдкреНрд░рдХрд▓реНрдкрд╛рджреНрд╡рд╛рд░реЗ рд╡рд╛рдкрд░рд▓реЗрд▓реНрдпрд╛ рдкреНрд░рддрд┐рдорд╛ рдЖрд╣реЗрдд.
src /
рд╕рд░реНрд╡ рд╕реНрддреНрд░реЛрдд рдХреЛрдб рдлреЛрд▓реНрдбрд░рдордзреНрдпреЗ рдЖрд╣реЗ src/
. рд╢реАрд░реНрд╖рдХреЗ client/
╨╕ server/
рд╕реНрд╡рдд: рд╕рд╛рдареА рдмреЛрд▓рд╛ рдЖрдгрд┐ shared/
рдПрдХ рд╕реНрдерд┐рд░ рдлрд╛рдЗрд▓ рдЖрд╣реЗ рдЬреА рдХреНрд▓рд╛рдпрдВрдЯ рдЖрдгрд┐ рд╕рд░реНрд╡реНрд╣рд░ рджреЛрдиреНрд╣реАрджреНрд╡рд╛рд░реЗ рдЖрдпрд╛рдд рдХреЗрд▓реА рдЬрд╛рддреЗ.
2. рдЕрд╕реЗрдВрдмреНрд▓реА/рдкреНрд░реЛрдЬреЗрдХреНрдЯ рд╕реЗрдЯрд┐рдВрдЧреНрдЬ
рд╡рд░ рдирдореВрдж рдХреЗрд▓реНрдпрд╛рдкреНрд░рдорд╛рдгреЗ, рдЖрдореНрд╣реА рдкреНрд░рдХрд▓реНрдк рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдореЙрдбреНрдпреВрд▓ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рд╡рд╛рдкрд░рддреЛ.
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',
}),
],
};
рдпреЗрдереЗ рд╕рд░реНрд╡рд╛рдд рдорд╣рддреНрд╡рд╛рдЪреНрдпрд╛ рдУрд│реА рдЖрд╣реЗрдд:
src/client/index.js
Javascript (JS) рдХреНрд▓рд╛рдпрдВрдЯрдЪрд╛ рдкреНрд░рд╡реЗрд╢ рдмрд┐рдВрджреВ рдЖрд╣реЗ. рд╡реЗрдмрдкреЕрдХ рдпреЗрдереВрди рд╕реБрд░реВ рд╣реЛрдИрд▓ рдЖрдгрд┐ рдЗрддрд░ рдЖрдпрд╛рдд рдХреЗрд▓реЗрд▓реНрдпрд╛ рдлрд╛рдпрд▓реАрдВрд╕рд╛рдареА рд╡рд╛рд░рдВрд╡рд╛рд░ рд╢реЛрдзрд╛.- рдЖрдордЪреНрдпрд╛ рд╡реЗрдмрдкреЕрдХ рдмрд┐рд▓реНрдбрдЪреЗ рдЖрдЙрдЯрдкреБрдЯ JS рдирд┐рд░реНрджреЗрд╢рд┐рдХреЗрдд рд╕реНрдерд┐рдд рдЕрд╕реЗрд▓
dist/
. рдореА рдпрд╛ рдлрд╛рдЗрд▓рд▓рд╛ рдЖрдордЪреА рдореНрд╣рдгреЗрди js рдкреЕрдХреЗрдЬ. - рдЖрдореНрд╣реА рд╡рд╛рдкрд░рддреЛ
рдЧреЛрдВрдЧрд╛рдЯ , рдЖрдгрд┐ рд╡рд┐рд╢реЗрд╖рддрдГ рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рди@babel/preset-env рдЬреБрдиреНрдпрд╛ рдмреНрд░рд╛рдЙрдЭрд░рд╕рд╛рдареА рдЖрдордЪрд╛ JS рдХреЛрдб рдмрджрд▓рдгреНрдпрд╛рд╕рд╛рдареА. - JS рдлрд╛рдпрд▓реАрдВрджреНрд╡рд╛рд░реЗ рд╕рдВрджрд░реНрднрд┐рдд рд╕рд░реНрд╡ CSS рдХрд╛рдврдгреНрдпрд╛рд╕рд╛рдареА рдЖрдгрд┐ рддреНрдпрд╛рдВрдирд╛ рдПрдХрд╛рдЪ рдард┐рдХрд╛рдгреА рдПрдХрддреНрд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрдореНрд╣реА рдкреНрд▓рдЧрдЗрди рд╡рд╛рдкрд░рдд рдЖрд╣реЛрдд. рдореА рддреНрдпрд╛рд▓рд╛ рдЖрдордЪрд╛ рдореНрд╣рдгреЗрди css рдкреЕрдХреЗрдЬ.
рддреБрдореНрд╣рд╛рд▓рд╛ рдХрджрд╛рдЪрд┐рдд рд╡рд┐рдЪрд┐рддреНрд░ рдкреЕрдХреЗрдЬ рдлрд╛рдЗрд▓рдирд╛рд╡реЗ рд▓рдХреНрд╖рд╛рдд рдЖрд▓реА рдЕрд╕рддреАрд▓ '[name].[contenthash].ext'
. рддреЗ рдЕрд╕рддрд╛рдд [name]
рдЗрдирдкреБрдЯ рдкреЙрдЗрдВрдЯрдЪреНрдпрд╛ рдирд╛рд╡рд╛рдиреЗ рдмрджрд▓рд▓реЗ рдЬрд╛рдИрд▓ (рдЖрдордЪреНрдпрд╛ рдмрд╛рдмрддреАрдд, рд╣реЗ game
), рдЖрдгрд┐ [contenthash]
рдлрд╛рдЗрд▓рдЪреНрдпрд╛ рд╕рд╛рдордЧреНрд░реАрдЪреНрдпрд╛ рд╣реЕрд╢рдиреЗ рдмрджрд▓рд▓реЗ рдЬрд╛рдИрд▓. рдЖрдореНрд╣реА рддреЗ рдХрд░рддреЛ contenthash
). рдЕрдВрддрд┐рдо рдкрд░рд┐рдгрд╛рдо рджреГрд╢реНрдп рдлрд╛рдЗрд▓рдЪреЗ рдирд╛рд╡ рдЕрд╕реЗрд▓ game.dbeee76e91a97d0c7207.js
.
рдлрд╛рдЗрд▓ webpack.common.js
рд╣реА рдмреЗрд╕ рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рди рдлрд╛рдЗрд▓ рдЖрд╣реЗ рдЬреА рдЖрдореНрд╣реА рдбреЗрд╡реНрд╣рд▓рдкрдореЗрдВрдЯ рдЖрдгрд┐ рдкреВрд░реНрдг рдЭрд╛рд▓реЗрд▓реНрдпрд╛ рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рдирдордзреНрдпреЗ рдЖрдпрд╛рдд рдХрд░рддреЛ. рдпреЗрдереЗ рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╡рд┐рдХрд╛рд╕ рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рди рдЖрд╣реЗ:
webpack.dev.js
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
});
рдХрд╛рд░реНрдпрдХреНрд╖рдорддреЗрд╕рд╛рдареА, рдЖрдореНрд╣реА рд╡рд┐рдХрд╛рд╕ рдкреНрд░рдХреНрд░рд┐рдпреЗрдд рд╡рд╛рдкрд░рддреЛ webpack.dev.js
, рдЖрдгрд┐ рд╡рд░ рд╕реНрд╡рд┐рдЪ рдХрд░рддреЗ webpack.prod.js
рдЙрддреНрдкрд╛рджрдирд╛рд╕рд╛рдареА рддреИрдирд╛рдд рдХрд░рддрд╛рдирд╛ рдкреЕрдХреЗрдЬ рдЖрдХрд╛рд░ рдСрдкреНрдЯрд┐рдорд╛рдЗрдЭ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА.
рд╕реНрдерд╛рдирд┐рдХ рд╕реЗрдЯрд┐рдВрдЧ
рдореА рд╕реНрдерд╛рдирд┐рдХ рдорд╢реАрдирд╡рд░ рдкреНрд░рдХрд▓реНрдк рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдгреНрдпрд╛рдЪреА рд╢рд┐рдлрд╛рд░рд╕ рдХрд░рддреЛ рдЬреЗрдгреЗрдХрд░реВрди рдЖрдкрдг рдпрд╛ рдкреЛрд╕реНрдЯрдордзреНрдпреЗ рд╕реВрдЪреАрдмрджреНрдз рдХреЗрд▓реЗрд▓реНрдпрд╛ рдЪрд░рдгрд╛рдВрдЪреЗ рдЕрдиреБрд╕рд░рдг рдХрд░реВ рд╢рдХрддрд╛. рд╕реЗрдЯрдЕрдк рд╕реЛрдкреЗ рдЖрд╣реЗ: рдкреНрд░рдердо, рд╕рд┐рд╕реНрдЯрдо рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ
$ git clone https://github.com/vzhou842/example-.io-game.git
$ cd example-.io-game
$ npm install
рдЖрдгрд┐ рдЖрдкрдг рдЬрд╛рдгреНрдпрд╛рд╕рд╛рдареА рддрдпрд╛рд░ рдЖрд╣рд╛рдд! рд╡рд┐рдХрд╛рд╕ рд╕рд░реНрд╡реНрд╣рд░ рд╕реБрд░реВ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рдлрдХреНрдд рдЪрд╛рд▓рд╡рд╛
$ npm run develop
рдЖрдгрд┐ рд╡реЗрдм рдмреНрд░рд╛рдЙрдЭрд░ рд╡рд░ рдЬрд╛
3. рдХреНрд▓рд╛рдпрдВрдЯ рдПрдВрдЯреНрд░реА рдкреЙрдЗрдВрдЯреНрд╕
рдЪрд▓рд╛ рдЧреЗрдо рдХреЛрдбрд╡рд░рдЪ рдЙрддрд░реВрдпрд╛. рдкреНрд░рдердо рдЖрдкрд▓реНрдпрд╛рд▓рд╛ рдПрдХ рдкреГрд╖реНрда рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ index.html
, рд╕рд╛рдЗрдЯрд▓рд╛ рднреЗрдЯ рджреЗрддрд╛рдирд╛, рдмреНрд░рд╛рдЙрдЭрд░ рдкреНрд░рдердо рддреЗ рд▓реЛрдб рдХрд░реЗрд▓. рдЖрдордЪреЗ рдкреГрд╖реНрда рдЦреВрдкрдЪ рд╕реЛрдкреЗ рдЕрд╕реЗрд▓:
Index.html
рдПрдХ рдЙрджрд╛рд╣рд░рдг .io рдЦреЗрд│ рдЦреЗрд│рд╛
рд╣реЗ рдХреЛрдб рдЙрджрд╛рд╣рд░рдг рд╕реНрдкрд╖реНрдЯрддреЗрд╕рд╛рдареА рдереЛрдбреЗрд╕реЗ рд╕реЛрдкреЗ рдХреЗрд▓реЗ рдЖрд╣реЗ рдЖрдгрд┐ рдореА рдЗрддрд░ рдЕрдиреЗрдХ рдкреЛрд╕реНрдЯ рдЙрджрд╛рд╣рд░рдгрд╛рдВрд╕рд╣ рддреЗрдЪ рдХрд░реЗрди. рдкреВрд░реНрдг рдХреЛрдб рдиреЗрд╣рдореА рдпреЗрдереЗ рдкрд╛рд╣рд┐рд▓рд╛ рдЬрд╛рдК рд╢рдХрддреЛ
рдЖрдордЪреНрдпрд╛рдХрдбреЗ рдЖрд╣реЗ:
HTML5 рдХреЕрдирд╡реНрд╣рд╛рд╕ рдШрдЯрдХ (<canvas>
) рдЬреНрдпрд╛рдЪрд╛ рд╡рд╛рдкрд░ рдЖрдореНрд╣реА рдЧреЗрдо рд░реЗрдВрдбрд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдХрд░реВ.<link>
рдЖрдордЪреЗ CSS рдкреЕрдХреЗрдЬ рдЬреЛрдбрдгреНрдпрд╛рд╕рд╛рдареА.<script>
рдЖрдордЪреЗ Javascript рдкреЕрдХреЗрдЬ рдЬреЛрдбрдгреНрдпрд╛рд╕рд╛рдареА.- рд╡рд╛рдкрд░рдХрд░реНрддрд╛рдирд╛рд╡рд╛рд╕рд╣ рдореБрдЦреНрдп рдореЗрдиреВ
<input>
рдЖрдгрд┐ рдкреНрд▓реЗ рдмрдЯрдг (<button>
).
рдореБрдЦреНрдпрдкреГрд╖реНрда рд▓реЛрдб рдХреЗрд▓реНрдпрд╛рдирдВрддрд░, рдмреНрд░рд╛рдЙрдЭрд░ рдЬрд╛рд╡рд╛рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдХреЛрдб рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд░рдгреНрдпрд╛рд╕ рдкреНрд░рд╛рд░рдВрдн рдХрд░реЗрд▓, рдПрдВрдЯреНрд░реА рдкреЙрдЗрдВрдЯ JS рдлрд╛рдЗрд▓рдкрд╛рд╕реВрди рд╕реБрд░реВ рд╣реЛрдИрд▓: 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);
};
});
рд╣реЗ рдХреНрд▓рд┐рд╖реНрдЯ рд╡рд╛рдЯреВ рд╢рдХрддреЗ, рдкрд░рдВрддреБ рдпреЗрдереЗ рдмрд░реЗрдЪ рдХрд╛рд╣реА рд╣реЛрдд рдирд╛рд╣реА:
- рдЗрддрд░ рдЕрдиреЗрдХ JS рдлрд╛рдЗрд▓реНрд╕ рдЗрдВрдкреЛрд░реНрдЯ рдХрд░рдд рдЖрд╣реЗ.
- CSS рдЖрдпрд╛рдд (рдореНрд╣рдгреВрди рд╡реЗрдмрдкреЕрдХрд▓рд╛ рддреЗ рдЖрдордЪреНрдпрд╛ CSS рдкреЕрдХреЗрдЬрдордзреНрдпреЗ рд╕рдорд╛рд╡рд┐рд╖реНрдЯ рдХрд░рдгреЗ рдорд╛рд╣рд┐рдд рдЖрд╣реЗ).
- рд▓рд╛рдБрдЪ рдХрд░рд╛
connect()
рд╕рд░реНрд╡реНрд╣рд░рд╢реА рдХрдиреЗрдХреНрд╢рди рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрдгрд┐ рдЪрд╛рд▓рд╡рд╛downloadAssets()
рдЧреЗрдо рд░реЗрдВрдбрд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрд╡рд╢реНрдпрдХ рдкреНрд░рддрд┐рдорд╛ рдбрд╛рдЙрдирд▓реЛрдб рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА. - рд╕реНрдЯреЗрдЬ 3 рдкреВрд░реНрдг рдЭрд╛рд▓реНрдпрд╛рдирдВрддрд░ рдореБрдЦреНрдп рдореЗрдиреВ рдкреНрд░рджрд░реНрд╢рд┐рдд рд╣реЛрддреЛ (
playMenu
). - "рдкреНрд▓реЗ" рдмрдЯрдг рджрд╛рдмрдгреНрдпрд╛рд╕рд╛рдареА рд╣рдБрдбрд▓рд░ рд╕реЗрдЯ рдХрд░рдд рдЖрд╣реЗ. рдЬреЗрд╡реНрд╣рд╛ рдмрдЯрдг рджрд╛рдмрд▓реЗ рдЬрд╛рддреЗ, рддреЗрд╡реНрд╣рд╛ рдХреЛрдб рдЧреЗрдо рд╕реБрд░реВ рдХрд░рддреЛ рдЖрдгрд┐ рд╕рд░реНрд╡реНрд╣рд░рд▓рд╛ рд╕рд╛рдВрдЧрддреЛ рдХреА рдЖрдореНрд╣реА рдЦреЗрд│рдгреНрдпрд╛рд╕рд╛рдареА рддрдпрд╛рд░ рдЖрд╣реЛрдд.
рдЖрдордЪреНрдпрд╛ рдХреНрд▓рд╛рдпрдВрдЯ-рд╕рд░реНрд╡реНрд╣рд░ рд▓реЙрдЬрд┐рдХрдЪреЗ рдореБрдЦреНрдп "рдорд╛рдВрд╕" рдлрд╛рдЗрд▓рджреНрд╡рд╛рд░реЗ рдЖрдпрд╛рдд рдХреЗрд▓реЗрд▓реНрдпрд╛ рдлрд╛рдЗрд▓реНрд╕рдордзреНрдпреЗ рдЖрд╣реЗ index.js
. рдЖрддрд╛ рдЖрдореНрд╣реА рддреНрдпрд╛ рд╕рд░реНрд╡рд╛рдВрдЪрд╛ рдХреНрд░рдорд╛рдиреЗ рд╡рд┐рдЪрд╛рд░ рдХрд░реВ.
4. рдЧреНрд░рд╛рд╣рдХ рдбреЗрдЯрд╛рдЪреА рджреЗрд╡рд╛рдгрдШреЗрд╡рд╛рдг
рдпрд╛ рдЧреЗрдордордзреНрдпреЗ, рдЖрдореНрд╣реА рд╕рд░реНрд╡реНрд╣рд░рд╢реА рд╕рдВрд╡рд╛рдж рд╕рд╛рдзрдгреНрдпрд╛рд╕рд╛рдареА рдПрдХ рд╕реБрдкреНрд░рд╕рд┐рджреНрдз рд▓рд╛рдпрдмреНрд░рд░реА рд╡рд╛рдкрд░рддреЛ
рдЖрдордЪреНрдпрд╛рдХрдбреЗ рдПрдХ рдлрд╛рдИрд▓ рдЕрд╕реЗрд▓ src/client/networking.js
рдХреЛрдг рдХрд╛рд│рдЬреА рдШреЗрдИрд▓ рдкреНрд░рддреНрдпреЗрдХрдЬрдг рд╕рд░реНрд╡реНрд╣рд░рд╢реА рд╕рдВрд╡рд╛рдж:
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);
};
рд╕реНрдкрд╖реНрдЯрддреЗрд╕рд╛рдареА рд╣рд╛ рдХреЛрдб рджреЗрдЦреАрд▓ рдереЛрдбрд╛ рд▓рд╣рд╛рди рдХреЗрд▓рд╛ рдЖрд╣реЗ.
рдпрд╛ рдлрд╛рдЗрд▓рдордзреНрдпреЗ рддреАрди рдореБрдЦреНрдп рдХреНрд░рд┐рдпрд╛ рдЖрд╣реЗрдд:
- рдЖрдореНрд╣реА рд╕рд░реНрд╡реНрд╣рд░рд╢реА рдХрдиреЗрдХреНрдЯ рдХрд░рдгреНрдпрд╛рдЪрд╛ рдкреНрд░рдпрддреНрди рдХрд░рдд рдЖрд╣реЛрдд.
connectedPromise
рдЖрдореНрд╣реА рдХрдиреЗрдХреНрд╢рди рд╕реНрдерд╛рдкрд┐рдд рдХреЗрд▓реНрдпрд╛рд╡рд░рдЪ рдкрд░рд╡рд╛рдирдЧреА рдЖрд╣реЗ. - рдХрдиреЗрдХреНрд╢рди рдпрд╢рд╕реНрд╡реА рдЭрд╛рд▓реНрдпрд╛рд╕, рдЖрдореНрд╣реА рдХреЙрд▓рдмреЕрдХ рдлрдВрдХреНрд╢рдиреНрд╕рдЪреА рдиреЛрдВрджрдгреА рдХрд░рддреЛ (
processGameUpdate()
╨╕onGameOver()
) рдЖрдореНрд╣реА рд╕рд░реНрд╡реНрд╣рд░рдХрдбреВрди рдкреНрд░рд╛рдкреНрдд рдХрд░реВ рд╢рдХрдгрд╛рд▒реНрдпрд╛ рд╕рдВрджреЗрд╢рд╛рдВрд╕рд╛рдареА. - рдЖрдореНрд╣реА рдирд┐рд░реНрдпрд╛рдд рдХрд░рддреЛ
play()
╨╕updateDirection()
рдЬреЗрдгреЗрдХрд░реВрди рдЗрддрд░ рдлрд╛рдпрд▓реА рддреНрдпрд╛рдВрдЪрд╛ рд╡рд╛рдкрд░ рдХрд░реВ рд╢рдХрддреАрд▓.
5. рдХреНрд▓рд╛рдпрдВрдЯ рдкреНрд░рд╕реНрддреБрддреАрдХрд░рдг
рд╕реНрдХреНрд░реАрдирд╡рд░ рдЪрд┐рддреНрд░ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рдгреНрдпрд╛рдЪреА рд╡реЗрд│ рдЖрд▓реА рдЖрд╣реЗ!
тАжрдкрд░рдВрддреБ рдЖрдореНрд╣реА рддреЗ рдХрд░рдгреНрдпрд╛рдкреВрд░реНрд╡реА, рдЖрдореНрд╣рд╛рд▓рд╛ рдпрд╛рд╕рд╛рдареА рдЖрд╡рд╢реНрдпрдХ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рд╕рд░реНрд╡ рдкреНрд░рддрд┐рдорд╛ (рд╕рдВрд╕рд╛рдзрдиреЗ) рдбрд╛рдЙрдирд▓реЛрдб рдХрд░рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ. рдЪрд▓рд╛ рд╕рдВрд╕рд╛рдзрди рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХ рд▓рд┐рд╣реВ:
assets.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];
рд╕рдВрд╕рд╛рдзрди рд╡реНрдпрд╡рд╕реНрдерд╛рдкрди рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдХрд░рдгреЗ рдЗрддрдХреЗ рдХрдареАрдг рдирд╛рд╣реА! рдореБрдЦреНрдп рдХрд▓реНрдкрдирд╛ рдореНрд╣рдгрдЬреЗ рд╡рд╕реНрддреВ рд╕рдВрдЧреНрд░рд╣рд┐рдд рдХрд░рдгреЗ assets
, рдЬреЗ рдСрдмреНрдЬреЗрдХреНрдЯрдЪреНрдпрд╛ рдореВрд▓реНрдпрд╛рд╢реА рдлрд╛рдЗрд▓ рдирд╛рд╡рд╛рдЪреА рдХреА рдмрд╛рдВрдзреЗрд▓ Image
. рдЬреЗрд╡реНрд╣рд╛ рд╕рдВрд╕рд╛рдзрди рд▓реЛрдб рдХреЗрд▓реЗ рдЬрд╛рддреЗ, рддреЗрд╡реНрд╣рд╛ рдЖрдореНрд╣реА рддреЗ рдСрдмреНрдЬреЗрдХреНрдЯрдордзреНрдпреЗ рд╕рд╛рдард╡рддреЛ assets
рднрд╡рд┐рд╖реНрдпрд╛рдд рджреНрд░реБрдд рдкреНрд░рд╡реЗрд╢рд╛рд╕рд╛рдареА. рдкреНрд░рддреНрдпреЗрдХ рд╡реИрдпрдХреНрддрд┐рдХ рд╕рдВрд╕рд╛рдзрди рдбрд╛рдЙрдирд▓реЛрдб рдХрд░рдгреНрдпрд╛рдЪреА рдкрд░рд╡рд╛рдирдЧреА рдХрдзреА рджрд┐рд▓реА рдЬрд╛рдИрд▓ (рдореНрд╣рдгрдЬреЗ, рд╕рд░реНрд╡ рд╕рдВрд╕рд╛рдзрдиреЗ), рдЖрдореНрд╣реА рдкрд░рд╡рд╛рдирдЧреА рджреЗрддреЛ downloadPromise
.
рд╕рдВрд╕рд╛рдзрдиреЗ рдбрд╛рдЙрдирд▓реЛрдб рдХреЗрд▓реНрдпрд╛рдирдВрддрд░, рддреБрдореНрд╣реА рдкреНрд░рд╕реНрддреБрддреАрдХрд░рдг рд╕реБрд░реВ рдХрд░реВ рд╢рдХрддрд╛. рдкреВрд░реНрд╡реА рдореНрд╣рдЯрд▓реНрдпрд╛рдкреНрд░рдорд╛рдгреЗ, рд╡реЗрдм рдкреГрд╖реНрдард╛рд╡рд░ рдХрд╛рдврдгреНрдпрд╛рд╕рд╛рдареА, рдЖрдореНрд╣реА рд╡рд╛рдкрд░рддреЛ <canvas>
). рдЖрдордЪрд╛ рдЦреЗрд│ рдЦреВрдкрдЪ рд╕реЛрдкрд╛ рдЖрд╣реЗ, рдореНрд╣рдгреВрди рдЖрдореНрд╣рд╛рд▓рд╛ рдлрдХреНрдд рдЦрд╛рд▓реАрд▓ рдЧреЛрд╖реНрдЯреА рдХрд╛рдврдгреНрдпрд╛рдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдЖрд╣реЗ:
- рдкрд╛рд░реНрд╢реНрд╡рднреВрдореА
- рдЦреЗрд│рд╛рдбреВ рдЬрд╣рд╛рдЬ
- рдЦреЗрд│рд╛рддреАрд▓ рдЗрддрд░ рдЦреЗрд│рд╛рдбреВ
- рдЯрд░рдлрд▓реЗ
рдпреЗрдереЗ рдорд╣рддреНрд╡рд╛рдЪреЗ рд╕реНрдирд┐рдкреЗрдЯреНрд╕ рдЖрд╣реЗрдд src/client/render.js
, рдЬреЗ рд╡рд░ рд╕реВрдЪреАрдмрджреНрдз рдХреЗрд▓реЗрд▓реНрдпрд╛ рдЪрд╛рд░ рдЖрдпрдЯрдордЪреЗ рдЕрдЪреВрдХ рд╡рд░реНрдгрди рдХрд░рддрд╛рдд:
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);
}
рд╕реНрдкрд╖реНрдЯрддреЗрд╕рд╛рдареА рд╣рд╛ рдХреЛрдб рджреЗрдЦреАрд▓ рд▓рд╣рд╛рди рдХреЗрд▓рд╛ рдЖрд╣реЗ.
render()
рдпрд╛ рдлрд╛рдИрд▓рдЪреЗ рдореБрдЦреНрдп рдХрд╛рд░реНрдп рдЖрд╣реЗ. startRendering()
╨╕ stopRendering()
60 FPS рд╡рд░ рд░реЗрдВрдбрд░ рд▓реВрдкрдЪреЗ рд╕рдХреНрд░рд┐рдпрдХрд░рдг рдирд┐рдпрдВрддреНрд░рд┐рдд рдХрд░рд╛.
рд╡реИрдпрдХреНрддрд┐рдХ рдкреНрд░рд╕реНрддреБрддреАрдХрд░рдг рд╕рд╣рд╛рдпреНрдпрдХ рдХрд╛рд░реНрдпрд╛рдВрдЪреА рдареЛрд╕ рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА (рдЙрджрд╛. renderBullet()
) рдЗрддрдХреЗ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ рдирд╛рд╣реАрдд, рдкрд░рдВрддреБ рдпреЗрдереЗ рдПрдХ рд╕рд╛рдзреЗ рдЙрджрд╛рд╣рд░рдг рдЖрд╣реЗ:
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,
);
}
рд▓рдХреНрд╖рд╛рдд рдШреНрдпрд╛ рдХреА рдЖрдореНрд╣реА рдкрджреНрдзрдд рд╡рд╛рдкрд░рдд рдЖрд╣реЛрдд getAsset()
, рдЬреЗ рдкреВрд░реНрд╡реА рджрд┐рд╕рд▓реЗ рд╣реЛрддреЗ asset.js
!
рддреБрдореНрд╣рд╛рд▓рд╛ рдЗрддрд░ рд░реЗрдВрдбрд░рд┐рдВрдЧ рд╕рд╣рд╛рдпреНрдпрдХрд╛рдВрдмрджреНрджрд▓ рдЬрд╛рдгреВрди рдШреЗрдгреНрдпрд╛рдд рд╕реНрд╡рд╛рд░рд╕реНрдп рдЕрд╕рд▓реНрдпрд╛рд╕, рдЙрд░реНрд╡рд░рд┐рдд рд╡рд╛рдЪрд╛.
src/client/render.js .
6. рдХреНрд▓рд╛рдпрдВрдЯ рдЗрдирдкреБрдЯ
рдЧреЗрдо рдмрдирд╡рдгреНрдпрд╛рдЪреА рд╡реЗрд│ рдЖрд▓реА рдЖрд╣реЗ рдЦреЗрд│рдгреНрдпрд╛рдпреЛрдЧреНрдп! рдирд┐рдпрдВрддреНрд░рдг рдпреЛрдЬрдирд╛ рдЕрдЧрджреА рд╕реЛрдкреА рдЕрд╕реЗрд▓: рд╣рд╛рд▓рдЪрд╛рд▓реАрдЪреА рджрд┐рд╢рд╛ рдмрджрд▓рдгреНрдпрд╛рд╕рд╛рдареА, рдЖрдкрдг рдорд╛рдЙрд╕ (рд╕рдВрдЧрдгрдХрд╛рд╡рд░) рд╡рд╛рдкрд░реВ рд╢рдХрддрд╛ рдХрд┐рдВрд╡рд╛ рд╕реНрдХреНрд░реАрдирд▓рд╛ рд╕реНрдкрд░реНрд╢ рдХрд░реВ рд╢рдХрддрд╛ (рдореЛрдмрд╛рдЗрд▓ рдбрд┐рд╡реНрд╣рд╛рдЗрд╕рд╡рд░). рдпрд╛рдЪреА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрдореНрд╣реА рдиреЛрдВрджрдгреА рдХрд░реВ
рдпрд╛ рд╕рдЧрд│реНрдпрд╛рдЪреА рдХрд╛рд│рдЬреА рдШреЗрдИрд▓ 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()
рдХреЙрд▓ рдХрд░рдгрд╛рд░реЗ рдХрд╛рд░реНрдпрдХреНрд░рдо рд╢реНрд░реЛрддреЗ рдЖрд╣реЗрдд updateDirection()
(рдХрдбреВрди networking.js
) рдЬреЗрд╡реНрд╣рд╛ рдПрдЦрд╛рджреА рдЗрдирдкреБрдЯ рдШрдЯрдирд╛ рдШрдбрддреЗ (рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде, рдЬреЗрд╡реНрд╣рд╛ рдорд╛рдЙрд╕ рд╣рд▓рд╡рд┐рд▓рд╛ рдЬрд╛рддреЛ). updateDirection()
рд╕рд░реНрд╡реНрд╣рд░рд╕рд╣ рд╕рдВрджреЗрд╢рди рд╣рд╛рддрд╛рд│рддреЗ, рдЬреЗ рдЗрдирдкреБрдЯ рдЗрд╡реНрд╣реЗрдВрдЯ рд╣рд╛рддрд╛рд│рддреЗ рдЖрдгрд┐ рддреНрдпрд╛рдиреБрд╕рд╛рд░ рдЧреЗрдо рд╕реНрдерд┐рддреА рдЕрджреНрдпрддрдирд┐рдд рдХрд░рддреЗ.
7. рдХреНрд▓рд╛рдпрдВрдЯ рд╕реНрдерд┐рддреА
рд╣рд╛ рд╡рд┐рднрд╛рдЧ рдкреЛрд╕реНрдЯрдЪреНрдпрд╛ рдкрд╣рд┐рд▓реНрдпрд╛ рднрд╛рдЧрд╛рдд рд╕рд░реНрд╡рд╛рдд рдХрдареАрдг рдЖрд╣реЗ. рдкрд╣рд┐рд▓реНрдпрд╛рдВрджрд╛ рд╡рд╛рдЪрддрд╛рдирд╛ рддреБрдореНрд╣рд╛рд▓рд╛ рддреЗ рд╕рдордЬрдд рдирд╕реЗрд▓ рддрд░ рдирд┐рд░рд╛рд╢ рд╣реЛрдК рдирдХрд╛! рддреБрдореНрд╣реА рддреЗ рд╡рдЧрд│реВ рд╢рдХрддрд╛ рдЖрдгрд┐ рдирдВрддрд░ рддреНрдпрд╛рд╡рд░ рдкрд░рдд рдпреЗрдК рд╢рдХрддрд╛.
рдХреНрд▓рд╛рдпрдВрдЯ/рд╕рд░реНрд╡реНрд╣рд░ рдХреЛрдб рдкреВрд░реНрдг рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрд╡рд╢реНрдпрдХ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдХреЛрдбреЗрдЪрд╛ рд╢реЗрд╡рдЯрдЪрд╛ рднрд╛рдЧ рдЖрд╣реЗ рд░рд╛рдЬреНрдп. рдХреНрд▓рд╛рдпрдВрдЯ рдкреНрд░рд╕реНрддреБрддреАрдХрд░рдг рд╡рд┐рднрд╛рдЧрд╛рддреАрд▓ рдХреЛрдб рд╕реНрдирд┐рдкреЗрдЯ рд▓рдХреНрд╖рд╛рдд рдареЗрд╡рд╛?
render.js
import { getCurrentState } from './state';
function render() {
const { me, others, bullets } = getCurrentState();
// Do the rendering
// ...
}
getCurrentState()
рдХреНрд▓рд╛рдпрдВрдЯрдордзреАрд▓ рдЧреЗрдордЪреА рд╕рджреНрдп рд╕реНрдерд┐рддреА рдЖрдореНрд╣рд╛рд▓рд╛ рджреЗрдгреНрдпрд╛рд╕ рд╕рдХреНрд╖рдо рдЕрд╕рд╛рд╡реЗ рдХреЛрдгрддреНрдпрд╛рд╣реА рд╡реЗрд│реА рд╕рд░реНрд╡реНрд╣рд░рдХрдбреВрди рдкреНрд░рд╛рдкреНрдд рдЭрд╛рд▓реЗрд▓реНрдпрд╛ рдЕрджреНрдпрддрдирд╛рдВрд╡рд░ рдЖрдзрд╛рд░рд┐рдд. рд╕рд░реНрд╡реНрд╣рд░ рдкрд╛рдард╡реВ рд╢рдХрдгрд╛рд░реНтАНрдпрд╛ рдЧреЗрдо рдЕрдкрдбреЗрдЯрдЪреЗ рдпреЗрдереЗ рдПрдХ рдЙрджрд╛рд╣рд░рдг рдЖрд╣реЗ:
{
"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
}
]
}
рдкреНрд░рддреНрдпреЗрдХ рдЧреЗрдо рдЕрдкрдбреЗрдЯрдордзреНрдпреЗ рдкрд╛рдЪ рд╕рдорд╛рди рдлреАрд▓реНрдб рдЕрд╕рддрд╛рдд:
- t: рд╣реЗ рдЕрдкрдбреЗрдЯ рдХреЗрд╡реНрд╣рд╛ рддрдпрд╛рд░ рдХреЗрд▓реЗ рдЧреЗрд▓реЗ рд╣реЗ рджрд░реНрд╢рд╡рдгрд╛рд░рд╛ рд╕рд░реНрд╡реНрд╣рд░ рдЯрд╛рдЗрдорд╕реНрдЯреЕрдореНрдк.
- me: рд╣реЗ рдЕрдкрдбреЗрдЯ рдкреНрд░рд╛рдкреНрдд рдХрд░рдгрд╛рд▒реНрдпрд╛ рдЦреЗрд│рд╛рдбреВрдмрджреНрджрд▓ рдорд╛рд╣рд┐рддреА.
- рдЗрддрд░: рд╕рдорд╛рди рдЧреЗрдордордзреНрдпреЗ рд╕рд╣рднрд╛рдЧреА рд╣реЛрдгрд╛рд░реНтАНрдпрд╛ рдЗрддрд░ рдЦреЗрд│рд╛рдбреВрдВрдмрджреНрджрд▓ рдорд╛рд╣рд┐рддреАрдЪреА рдПрдХ рд╢реНрд░реЗрдгреА.
- рдЧреЛрд│реНрдпрд╛: рдЧреЗрдордордзреАрд▓ рдкреНрд░реЛрдЬреЗрдХреНрдЯрд╛рдЗрд▓реНрд╕рдмрджреНрджрд▓ рдорд╛рд╣рд┐рддреАрдЪрд╛ рдПрдХ рдЕреЕрд░реЗ.
- рд▓реАрдбрд░рдмреЛрд░реНрдб: рд╡рд░реНрддрдорд╛рди рд▓реАрдбрд░рдмреЛрд░реНрдб рдбреЗрдЯрд╛. рдпрд╛ рдкреЛрд╕реНрдЯрдордзреНрдпреЗ, рдЖрдореНрд╣реА рддреНрдпрд╛рдВрдЪрд╛ рд╡рд┐рдЪрд╛рд░ рдХрд░рдгрд╛рд░ рдирд╛рд╣реА.
7.1 рдирд┐рд╖реНрдХрд▓рдВрдХ рдХреНрд▓рд╛рдпрдВрдЯрдЪреА рд╕реНрдерд┐рддреА
рднреЛрд│реА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА getCurrentState()
рдлрдХреНрдд рд╕рд░реНрд╡рд╛рдд рдЕрд▓реАрдХрдбреЗ рдкреНрд░рд╛рдкреНрдд рдЭрд╛рд▓реЗрд▓реНрдпрд╛ рдЧреЗрдо рдЕрдкрдбреЗрдЯрдЪрд╛ рдбреЗрдЯрд╛ рдереЗрдЯ рдкрд░рдд рдХрд░реВ рд╢рдХрддреЛ.
naive-state.js
let lastGameUpdate = null;
// Handle a newly received game update.
export function processGameUpdate(update) {
lastGameUpdate = update;
}
export function getCurrentState() {
return lastGameUpdate;
}
рдЫрд╛рди рдЖрдгрд┐ рд╕реНрдкрд╖реНрдЯ! рдкрдг рдЬрд░ рддреЗ рдЗрддрдХреЗ рд╕реЛрдкреЗ рдЕрд╕рддреЗ. рдпрд╛ рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреАрдЪреЗ рдПрдХ рдХрд╛рд░рдг рд╕рдорд╕реНрдпрд╛рдкреНрд░рдзрд╛рди рдЖрд╣реЗ: рд╣реЗ рдкреНрд░рд╕реНрддреБрддреАрдХрд░рдг рдлреНрд░реЗрдо рджрд░ рд╕рд░реНрд╡реНрд╣рд░ рдШрдбреНрдпрд╛рд│ рджрд░ рдорд░реНрдпрд╛рджрд┐рдд рдХрд░рддреЗ.
рдлреНрд░реЗрдо рджрд░: рдлреНрд░реЗрдореНрд╕рдЪреА рд╕рдВрдЦреНрдпрд╛ (рдореНрд╣рдгрдЬреЗ рдХреЙрд▓
render()
) рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдж, рдХрд┐рдВрд╡рд╛ FPS. рдЧреЗрдо рд╕рд╣рд╕рд╛ рдХрд┐рдорд╛рди 60 FPS рдорд┐рд│рд╡рдгреНрдпрд╛рдЪрд╛ рдкреНрд░рдпрддреНрди рдХрд░рддрд╛рдд.
рдЯрд┐рдХ рд░реЗрдЯ: рд╕рд░реНрд╡реНрд╣рд░ рдХреНрд▓рд╛рдпрдВрдЯрд▓рд╛ рдЧреЗрдо рдЕрдкрдбреЗрдЯ рдкрд╛рдард╡рддреЗ рддреА рд╡рд╛рд░рдВрд╡рд╛рд░рддрд╛. рд╣реЗ рдлреНрд░реЗрдо рджрд░рд╛рдкреЗрдХреНрд╖рд╛ рдЕрдиреЗрдХрджрд╛ рдХрдореА рдЕрд╕рддреЗ. рдЖрдордЪреНрдпрд╛ рдЧреЗрдордордзреНрдпреЗ, рд╕рд░реНрд╡реНрд╣рд░ рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдж 30 рдЪрдХреНрд░рд╛рдВрдЪреНрдпрд╛ рд╡рд╛рд░рдВрд╡рд╛рд░рддреЗрдиреЗ рдЪрд╛рд▓рддреЛ.
рдЬрд░ рдЖрдореНрд╣реА рдлрдХреНрдд рдЧреЗрдордЪреЗ рдирд╡реАрдирддрдо рдЕрджреНрдпрддрди рдкреНрд░рд╕реНрддреБрдд рдХреЗрд▓реЗ, рддрд░ FPS рдореВрд▓рдд: рдХрдзреАрд╣реА 30 рдЪреНрдпрд╛ рд╡рд░ рдЬрд╛рдгрд╛рд░ рдирд╛рд╣реА, рдХрд╛рд░рдг рдЖрдореНрд╣рд╛рд▓рд╛ рд╕рд░реНрд╡реНрд╣рд░рдХрдбреВрди рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдж рейреж рдкреЗрдХреНрд╖рд╛ рдЬрд╛рд╕реНрдд рдЕрдкрдбреЗрдЯреНрд╕ рдорд┐рд│рдд рдирд╛рд╣реАрдд. рдЬрд░реА рдЖрдореНрд╣реА рдлреЛрди рдХреЗрд▓рд╛ render()
рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдж 60 рд╡реЗрд│рд╛, рдирдВрддрд░ рдпрд╛рдкреИрдХреА рдЕрд░реНрдзреЗ рдХреЙрд▓ рдлрдХреНрдд рд╕рдорд╛рди рдЧреЛрд╖реНрдЯ рдкреБрдиреНрд╣рд╛ рдХрд╛рдврддреАрд▓, рдореВрд▓рдд: рдХрд╛рд╣реАрд╣реА рдХрд░рдд рдирд╛рд╣реАрдд. рднреЛрд│реЗ рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдЖрдгрдЦреА рдПрдХ рд╕рдорд╕реНрдпрд╛ рдЖрд╣реЗ рдХреА рд╡рд┐рд▓рдВрдм рд╣реЛрдгреНрдпрд╛рдЪреА рд╢рдХреНрдпрддрд╛ рдЖрд╣реЗ. рдЖрджрд░реНрд╢ рдЗрдВрдЯрд░рдиреЗрдЯ рдЧрддреАрд╕рд╣, рдХреНрд▓рд╛рдпрдВрдЯрд▓рд╛ рдкреНрд░рддреНрдпреЗрдХ 33ms (30 рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдж) рдПрдХ рдЧреЗрдо рдЕрдкрдбреЗрдЯ рдкреНрд░рд╛рдкреНрдд рд╣реЛрдИрд▓:
рджреБрд░реНрджреИрд╡рд╛рдиреЗ, рдХрд╛рд╣реАрд╣реА рдкрд░рд┐рдкреВрд░реНрдг рдирд╛рд╣реА. рдЕрдзрд┐рдХ рд╡рд╛рд╕реНрддрд╡рд╡рд╛рджреА рдЪрд┐рддреНрд░ рдЕрд╕реЗрд▓:
рдЬреЗрд╡реНрд╣рд╛ рд▓реЗрдЯрдиреНрд╕реАрдЪрд╛ рд╡рд┐рдЪрд╛рд░ рдХреЗрд▓рд╛ рдЬрд╛рддреЛ рддреЗрд╡реНрд╣рд╛ рд╕рд╛рдзреА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рд╣реА рд╡реНрдпрд╛рд╡рд╣рд╛рд░рд┐рдХрджреГрд╖реНрдЯреНрдпрд╛ рд╕рд░реНрд╡рд╛рдд рд╡рд╛рдИрдЯ рдкрд░рд┐рд╕реНрдерд┐рддреА рдЕрд╕рддреЗ. 50ms рдЪреНрдпрд╛ рд╡рд┐рд▓рдВрдмрд╛рдиреЗ рдЧреЗрдо рдЕрдкрдбреЗрдЯ рдкреНрд░рд╛рдкреНрдд рдЭрд╛рд▓реНрдпрд╛рд╕, рдирдВрддрд░ рдЧреНрд░рд╛рд╣рдХ рд╕реНрдЯреЙрд▓ рдПрдХ рдЕрддрд┐рд░рд┐рдХреНрдд 50ms рдХрд╛рд░рдг рддреЗ рдЕрджреНрдпрд╛рдк рдорд╛рдЧреАрд▓ рдЕрджреНрдпрддрдирд╛рдкрд╛рд╕реВрди рдЧреЗрдо рд╕реНрдерд┐рддреА рдкреНрд░рд╕реНрддреБрдд рдХрд░рдд рдЖрд╣реЗ. рдЖрдкрдг рдХрд▓реНрдкрдирд╛ рдХрд░реВ рд╢рдХрддрд╛ рдХреА рд╣реЗ рдЦреЗрд│рд╛рдбреВрд╕рд╛рдареА рдХрд┐рддреА рдЕрд╕реНрд╡рд╕реНрде рдЖрд╣реЗ: рдЕрдирд┐рдпрдВрддреНрд░рд┐рдд рдмреНрд░реЗрдХрд┐рдВрдЧрдореБрд│реЗ рдЧреЗрдо рдзрдХреНрдХрд╛рджрд╛рдпрдХ рдЖрдгрд┐ рдЕрд╕реНрдерд┐рд░ рд╣реЛрдИрд▓.
7.2 рд╕реБрдзрд╛рд░рд┐рдд рдХреНрд▓рд╛рдпрдВрдЯ рд╕реНрдерд┐рддреА
рдЖрдореНрд╣реА рд╕рд╛рдзреНрдпрд╛ рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреАрдд рдХрд╛рд╣реА рд╕реБрдзрд╛рд░рдгрд╛ рдХрд░реВ. рдкреНрд░рдердо, рдЖрдореНрд╣реА рд╡рд╛рдкрд░рддреЛ рдкреНрд░рд╕реНрддреБрддреАрдХрд░рдг рд╡рд┐рд▓рдВрдм 100 ms рд╕рд╛рдареА рдпрд╛рдЪрд╛ рдЕрд░реНрде рдХреНрд▓рд╛рдпрдВрдЯрдЪреА "рд╡рд░реНрддрдорд╛рди" рд╕реНрдерд┐рддреА рдиреЗрд╣рдореА рд╕рд░реНрд╡реНрд╣рд░рд╡рд░реАрд▓ рдЧреЗрдордЪреНрдпрд╛ рд╕реНрдерд┐рддреАрдкреЗрдХреНрд╖рд╛ 100ms рдорд╛рдЧреЗ рд░рд╛рд╣реАрд▓. рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде, рд╕рд░реНрд╡реНрд╣рд░рд╡рд░ рд╡реЗрд│ рдЕрд╕рд▓реНрдпрд╛рд╕ 150, рдирдВрддрд░ рдХреНрд▓рд╛рдпрдВрдЯ рддреНрдпрд╛ рд╡реЗрд│реА рд╕рд░реНрд╡реНрд╣рд░ рдЬреНрдпрд╛ рд╕реНрдерд┐рддреАрдд рд╣реЛрддрд╛ рддреЗ рд░реЗрдВрдбрд░ рдХрд░реЗрд▓ 50:
рд╣реЗ рдЖрдореНрд╣рд╛рд▓рд╛ рдЕрдкреНрд░рддреНрдпрд╛рд╢рд┐рдд рдЧреЗрдо рдЕрдкрдбреЗрдЯ рд╡реЗрд│реЗрдд рдЯрд┐рдХреВрди рд░рд╛рд╣рдгреНрдпрд╛рд╕рд╛рдареА 100ms рдмрдлрд░ рджреЗрддреЗ:
рдпрд╛рд╕рд╛рдареА рдорд┐рд│рдгрд╛рд░рд╛ рдореЛрдмрджрд▓рд╛ рдХрд╛рдпрдорд╕реНрд╡рд░реВрдкреА рдЕрд╕реЗрд▓
рдЖрдореНрд╣реА рдирд╛рд╡рд╛рдЪреЗ рджреБрд╕рд░реЗ рддрдВрддреНрд░ рджреЗрдЦреАрд▓ рд╡рд╛рдкрд░реВ рд╢рдХрддреЛ
рдХреНрд▓рд╛рдпрдВрдЯ-рд╕рд╛рдЗрдб рдЕрдВрджрд╛рдЬ , рдЬреЗ рд╕рдордЬрд▓реЗрд▓реА рд╡рд┐рд▓рдВрдмрддрд╛ рдХрдореА рдХрд░рдгреНрдпрд╛рдЪреЗ рдЪрд╛рдВрдЧрд▓реЗ рдХрд╛рд░реНрдп рдХрд░рддреЗ, рдкрд░рдВрддреБ рдпрд╛ рдкреЛрд╕реНрдЯрдордзреНрдпреЗ рд╕рдорд╛рд╡рд┐рд╖реНрдЯ рдХреЗрд▓реЗ рдЬрд╛рдгрд╛рд░ рдирд╛рд╣реА.
рдЖрдореНрд╣реА рд╡рд╛рдкрд░рдд рдЕрд╕рд▓реЗрд▓реА рдЖрдгрдЦреА рдПрдХ рд╕реБрдзрд╛рд░рдгрд╛ рдЖрд╣реЗ рд░реЗрдЦреАрдп рдкреНрд░рдХреНрд╖реЗрдкрдг. рд░реЗрдВрдбрд░рд┐рдВрдЧ рд▓реЕрдЧрдореБрд│реЗ, рдЖрдореНрд╣реА рдХреНрд▓рд╛рдпрдВрдЯрдордзреАрд▓ рд╕рдзреНрдпрд╛рдЪреНрдпрд╛ рд╡реЗрд│реЗрдкреЗрдХреНрд╖рд╛ рдХрдореАрдд рдХрдореА рдПрдХ рдЕрдкрдбреЗрдЯ рдкреБрдвреЗ рдЕрд╕рддреЛ. рдлреЛрди рдХреЗрд▓рд╛ рдЕрд╕рддрд╛ getCurrentState()
, рдЖрдореНрд╣реА рдЕрдВрдорд▓рд╛рдд рдЖрдгреВ рд╢рдХрддреЛ
рд╣реЗ рдлреНрд░реЗрдо рджрд░ рд╕рдорд╕реНрдпреЗрдЪреЗ рдирд┐рд░рд╛рдХрд░рдг рдХрд░рддреЗ: рдЖрдореНрд╣реА рдЖрддрд╛ рдЖрдореНрд╣рд╛рд▓рд╛ рдкрд╛рд╣рд┐рдЬреЗ рддреНрдпрд╛ рдлреНрд░реЗрдо рджрд░рд╛рдиреЗ рдЕрджреНрд╡рд┐рддреАрдп рдлреНрд░реЗрдореНрд╕ рд░реЗрдВрдбрд░ рдХрд░реВ рд╢рдХрддреЛ!
7.3 рд╡рд░реНрдзрд┐рдд рдХреНрд▓рд╛рдпрдВрдЯ рд╕реНрдерд┐рддреА рд▓рд╛рдЧреВ рдХрд░рдгреЗ
рдордзреНрдпреЗ рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдЙрджрд╛рд╣рд░рдг src/client/state.js
рд░реЗрдВрдбрд░ рд▓реЕрдЧ рдЖрдгрд┐ рд░реЗрдЦреАрдп рдЗрдВрдЯрд░рдкреЛрд▓реЗрд╢рди рджреЛрдиреНрд╣реА рд╡рд╛рдкрд░рддреЗ, рдкрд░рдВрддреБ рдЬрд╛рд╕реНрдд рдХрд╛рд│ рдирд╛рд╣реА. рдХреЛрдбрдЪреЗ рджреЛрди рднрд╛рдЧ рдХрд░реВ. рдпреЗрдереЗ рдкрд╣рд┐рд▓реЗ рдЖрд╣реЗ:
state.js рднрд╛рдЧ рез
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;
}
рдкрд╣рд┐рд▓реА рдкрд╛рдпрд░реА рдореНрд╣рдгрдЬреЗ рдХрд╛рдп рд╣реЗ рд╢реЛрдзрдгреЗ currentServerTime()
. рдЖрдореНрд╣реА рдЖрдзреА рдкрд╛рд╣рд┐рд▓реНрдпрд╛рдкреНрд░рдорд╛рдгреЗ, рдкреНрд░рддреНрдпреЗрдХ рдЧреЗрдо рдЕрдкрдбреЗрдЯрдордзреНрдпреЗ рд╕рд░реНрд╡реНрд╣рд░ рдЯрд╛рдЗрдорд╕реНрдЯреЕрдореНрдк рд╕рдорд╛рд╡рд┐рд╖реНрдЯ рдЕрд╕рддреЛ. рдЖрдореНрд╣рд╛рд▓рд╛ рд╕рд░реНрд╡реНрд╣рд░рдЪреНрдпрд╛ рдорд╛рдЧреЗ рдЗрдореЗрдЬ 100ms рд░реЗрдВрдбрд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рд░реЗрдВрдбрд░ рд▓реЗрдЯрдиреНрд╕реА рд╡рд╛рдкрд░рд╛рдпрдЪреА рдЖрд╣реЗ, рдкрд░рдВрддреБ рд╕рд░реНрд╡реНрд╣рд░рд╡рд░реАрд▓ рд╡рд░реНрддрдорд╛рди рд╡реЗрд│ рдЖрдореНрд╣рд╛рд▓рд╛ рдХрдзреАрдЪ рдХрд│рдгрд╛рд░ рдирд╛рд╣реА, рдХрд╛рд░рдг рдЖрдореНрд╣рд╛рд▓рд╛ рдХрд│реВ рд╢рдХрдд рдирд╛рд╣реА рдХреА рдХреЛрдгрддреЗрд╣реА рдЕрдкрдбреЗрдЯ рдЖрдордЪреНрдпрд╛рдкрд░реНрдпрдВрдд рдкреЛрд╣реЛрдЪрд╛рдпрд▓рд╛ рдХрд┐рддреА рд╡реЗрд│ рд▓рд╛рдЧрд▓рд╛. рдЗрдВрдЯрд░рдиреЗрдЯ рдЕрдкреНрд░рддреНрдпрд╛рд╢рд┐рдд рдЖрд╣реЗ рдЖрдгрд┐ рддреНрдпрд╛рдЪреА рдЧрддреА рдореЛрдареНрдпрд╛ рдкреНрд░рдорд╛рдгрд╛рдд рдмрджрд▓реВ рд╢рдХрддреЗ!
рдпрд╛ рд╕рдорд╕реНрдпреЗрдЪреЗ рдирд┐рд░рд╛рдХрд░рдг рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рдЖрдореНрд╣реА рд╡рд╛рдЬрд╡реА рдЕрдВрджрд╛рдЬреЗ рд╡рд╛рдкрд░реВ рд╢рдХрддреЛ: рдЖрдореНрд╣реА рдкреНрд░рдердо рдЕрджреНрдпрддрди рддреНрд╡рд░рд┐рдд рдЖрд▓реНрдпрд╛рдЪреА рдмрддрд╛рд╡рдгреА рдХрд░рд╛. рдЬрд░ рд╣реЗ рдЦрд░реЗ рдЕрд╕рддреЗ, рддрд░ рдЖрдореНрд╣рд╛рд▓рд╛ рдпрд╛ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХреНрд╖рдгреА рд╕рд░реНрд╡реНрд╣рд░рдЪреА рд╡реЗрд│ рдорд╛рд╣рд┐рдд рдЕрд╕рддреЗ! рдЖрдореНрд╣реА рд╕рд░реНрд╡реНрд╣рд░рдЪрд╛ рдЯрд╛рдЗрдорд╕реНрдЯреЕрдореНрдк рд╕рдВрдЧреНрд░рд╣рд┐рдд рдХрд░рддреЛ firstServerTimestamp
рдЖрдгрд┐ рдЖрдордЪреНрдпрд╛ рдареЗрд╡рд╛ рд╕реНрдерд╛рдирд┐рдХ (рдХреНрд▓рд╛рдпрдВрдЯ) рдЯрд╛рдЗрдорд╕реНрдЯреЕрдореНрдк рдордзреНрдпреЗ рддреНрдпрд╛рдЪ рдХреНрд╖рдгреА gameStart
.
рдЕрд░реЗ рдерд╛рдВрдм. рддреЛ рд╕рд░реНрд╡реНрд╣рд░ рд╡реЗрд│ = рдХреНрд▓рд╛рдпрдВрдЯ рд╡реЗрд│ рдЕрд╕реВ рдирдпреЗ? рдЖрдореНрд╣реА "рд╕рд░реНрд╡реНрд╣рд░ рдЯрд╛рдЗрдорд╕реНрдЯреЕрдореНрдк" рдЖрдгрд┐ "рдХреНрд▓рд╛рдпрдВрдЯ рдЯрд╛рдЗрдорд╕реНрдЯреЕрдореНрдк" рдордзреНрдпреЗ рдлрд░рдХ рдХрд╛ рдХрд░рддреЛ? рд╣рд╛ рдПрдХ рдореЛрдард╛ рдкреНрд░рд╢реНрди рдЖрд╣реЗ! рддреЗ рд╕рдорд╛рди рдЧреЛрд╖реНрдЯ рдирд╛рд╣реАрдд рдмрд╛рд╣реЗрд░ рд╡рд│рддреЗ. Date.now()
рдХреНрд▓рд╛рдпрдВрдЯ рдЖрдгрд┐ рд╕рд░реНрд╡реНрд╣рд░рдордзреНрдпреЗ рд╡реЗрдЧрд╡реЗрдЧрд│реЗ рдЯрд╛рдЗрдорд╕реНрдЯреЕрдореНрдк рдкрд░рдд рдХрд░рддреАрд▓ рдЖрдгрд┐ рддреЗ рдпрд╛ рдорд╢реАрдирдЪреНрдпрд╛ рд╕реНрдерд╛рдирд┐рдХ рдШрдЯрдХрд╛рдВрд╡рд░ рдЕрд╡рд▓рдВрдмреВрди рдЖрд╣реЗ. рд╕рд░реНрд╡ рдорд╢реАрдирд╡рд░ рдЯрд╛рдЗрдорд╕реНрдЯреЕрдореНрдк рд╕рд╛рд░рдЦреЗрдЪ рдЕрд╕рддреАрд▓ рдЕрд╕реЗ рдХрдзреАрд╣реА рдЧреГрд╣реАрдд рдзрд░реВ рдирдХрд╛.
рдЖрддрд╛ рдЖрдореНрд╣рд╛рд▓рд╛ рд╕рдордЬрд▓реЗ рдХреА рдХрд╛рдп рдХрд░рддреЗ currentServerTime()
: рддреЗ рдкрд░рдд рдпреЗрддреЗ рд╡рд░реНрддрдорд╛рди рд░реЗрдВрдбрд░ рд╡реЗрд│реЗрдЪрд╛ рд╕рд░реНрд╡реНрд╣рд░ рдЯрд╛рдЗрдорд╕реНрдЯреЕрдореНрдк. рджреБрд╕рд▒реНрдпрд╛ рд╢рдмреНрджрд╛рдВрдд, рд╣реА рд╕рд░реНрд╡реНрд╣рд░рдЪреА рд╡рд░реНрддрдорд╛рди рд╡реЗрд│ рдЖрд╣реЗ (firstServerTimestamp <+ (Date.now() - gameStart)
) рдЙрдгреЗ рдкреНрд░рд╕реНрддреБрдд рд╡рд┐рд▓рдВрдм (RENDER_DELAY
).
рдЖрддрд╛ рдЖрдкрдг рдЧреЗрдо рдЕрдкрдбреЗрдЯреНрд╕ рдХрд╕реЗ рд╣рд╛рддрд╛рд│рддреЛ рдпрд╛рд╡рд░ рдПрдХ рдирдЬрд░ рдЯрд╛рдХреВрдпрд╛. рдЕрдкрдбреЗрдЯ рд╕рд░реНрд╡реНрд╣рд░рдХрдбреВрди рдкреНрд░рд╛рдкреНрдд рдЭрд╛рд▓реНрдпрд╛рд╡рд░, рддреНрдпрд╛рд▓рд╛ рдХреЙрд▓ рдХреЗрд▓реЗ рдЬрд╛рддреЗ processGameUpdate()
рдЖрдгрд┐ рдЖрдореНрд╣реА рдирд╡реАрди рдЕрдкрдбреЗрдЯ рдЕреЕрд░реЗрдордзреНрдпреЗ рд╕реЗрд╡реНрд╣ рдХрд░рддреЛ gameUpdates
. рдирдВрддрд░, рдореЗрдорд░реА рд╡рд╛рдкрд░ рддрдкрд╛рд╕рдгреНрдпрд╛рд╕рд╛рдареА, рдЖрдореНрд╣реА рдЖрдзреАрдЪреЗ рд╕рд░реНрд╡ рдЬреБрдиреЗ рдЕрджреНрдпрддрди рдХрд╛рдвреВрди рдЯрд╛рдХрддреЛ рдмреЗрд╕ рдЕрдкрдбреЗрдЯрдХрд╛рд░рдг рдЖрдореНрд╣рд╛рд▓рд╛ рддреНрдпрд╛рдВрдЪреА рдЖрддрд╛ рдЧрд░рдЬ рдирд╛рд╣реА.
"рдореВрд▓рднреВрдд рдЕрджреНрдпрддрди" рдореНрд╣рдгрдЬреЗ рдХрд╛рдп? рдпрд╛ рд╕рд░реНрд╡реНрд╣рд░рдЪреНрдпрд╛ рд╡рд░реНрддрдорд╛рди рд╡реЗрд│реЗрдкрд╛рд╕реВрди рдорд╛рдЧреЗ рд╕рд░рдХреВрди рдЖрдореНрд╣рд╛рд▓рд╛ рдкрд╣рд┐рд▓реЗ рдЕрдкрдбреЗрдЯ рд╕рд╛рдкрдбрддреЗ. рд╣рд╛ рдЖрдХреГрддреАрдмрдВрдз рдЖрдард╡рддреЛ?
"рдХреНрд▓рд╛рдпрдВрдЯ рд░реЗрдВрдбрд░ рдЯрд╛рдЗрдо" рдЪреНрдпрд╛ рдбрд╛рд╡реАрдХрдбреЗ рдереЗрдЯ рдЧреЗрдо рдЕрдкрдбреЗрдЯ рдмреЗрд╕ рдЕрдкрдбреЗрдЯ рдЖрд╣реЗ.
рдмреЗрд╕ рдЕрдкрдбреЗрдЯ рдХрд╢рд╛рд╕рд╛рдареА рд╡рд╛рдкрд░рд▓рд╛ рдЬрд╛рддреЛ? рдЖрдореНрд╣реА рдмреЗрд╕рд▓рд╛рдЗрдирд╡рд░ рдЕрдкрдбреЗрдЯ рдХрд╛ рд╕реЛрдбреВ рд╢рдХрддреЛ? рд╣реЗ рд╢реЛрдзрдгреНрдпрд╛рд╕рд╛рдареА, рдЪрд▓рд╛ рдЕрдЦреЗрд░реАрд╕ рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреАрдЪрд╛ рд╡рд┐рдЪрд╛рд░ рдХрд░рд╛ getCurrentState()
:
state.js рднрд╛рдЧ рез
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),
};
}
}
рдЖрдореНрд╣реА рддреАрди рдкреНрд░рдХрд░рдгреЗ рд╣рд╛рддрд╛рд│рддреЛ:
base < 0
рдпрд╛рдЪрд╛ рдЕрд░реНрде рдЕрд╕рд╛ рдХреА рд╡рд░реНрддрдорд╛рди рд░реЗрдВрдбрд░ рд╡реЗрд│реЗрдкрд░реНрдпрдВрдд рдХреЛрдгрддреАрд╣реА рдЕрджреНрдпрддрдиреЗ рдирд╛рд╣реАрдд (рд╡рд░реАрд▓ рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдкрд╣рд╛getBaseUpdate()
). рд╣реЗ рд░реЗрдВрдбрд░рд┐рдВрдЧ рд▓реЕрдЧрдореБрд│реЗ рдЧреЗрдордЪреНрдпрд╛ рд╕реБрд░реБрд╡рд╛рддреАрд▓рд╛рдЪ рд╣реЛрдК рд╢рдХрддреЗ. рдпрд╛ рдкреНрд░рдХрд░рдгрд╛рдд, рдЖрдореНрд╣реА рдкреНрд░рд╛рдкреНрдд рдХреЗрд▓реЗрд▓реЗ рдирд╡реАрдирддрдо рдЕрджреНрдпрддрди рд╡рд╛рдкрд░рддреЛ.base
рдЖрдордЪреНрдпрд╛рдХрдбреЗ рдЕрд╕рд▓реЗрд▓реЗ рдирд╡реАрдирддрдо рдЕрдкрдбреЗрдЯ рдЖрд╣реЗ. рд╣реЗ рдиреЗрдЯрд╡рд░реНрдХ рд╡рд┐рд▓рдВрдм рдХрд┐рдВрд╡рд╛ рдЦрд░рд╛рдм рдЗрдВрдЯрд░рдиреЗрдЯ рдХрдиреЗрдХреНрд╢рдирдореБрд│реЗ рдЕрд╕реВ рд╢рдХрддреЗ. рдпрд╛ рдкреНрд░рдХрд░рдгрд╛рдд, рдЖрдореНрд╣реА рдЖрдордЪреНрдпрд╛рдХрдбреЗ рдЕрд╕рд▓реЗрд▓реЗ рдирд╡реАрдирддрдо рдЕрджреНрдпрддрди рджреЗрдЦреАрд▓ рд╡рд╛рдкрд░рдд рдЖрд╣реЛрдд.- рдЖрдордЪреНрдпрд╛рдХрдбреЗ рд╡рд░реНрддрдорд╛рди рд░реЗрдВрдбрд░ рд╡реЗрд│реЗрдкреВрд░реНрд╡реА рдЖрдгрд┐ рдирдВрддрд░ рджреЛрдиреНрд╣реА рдЕрджреНрдпрддрдиреЗ рдЖрд╣реЗрдд, рдореНрд╣рдгреВрди рдЖрдореНрд╣реА рдХрд░реВ рд╢рдХрддреЛ рдЗрдВрдЯрд░рдкреЛрд▓реЗрдЯ!
рдлрдХреНрдд рдмрд╛рдХреА рдЖрд╣реЗ state.js
рд╕рд╛рдзреЗ (рдкрд░рдВрддреБ рдХрдВрдЯрд╛рд│рд╡рд╛рдгреЗ) рдЧрдгрд┐рдд рд░реЗрдЦреАрдп рдЗрдВрдЯрд░рдкреЛрд▓реЗрд╢рдирдЪреА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдЖрд╣реЗ. рдЬрд░ рддреБрдореНрд╣рд╛рд▓рд╛ рддреЗ рд╕реНрд╡рддрдГ рдПрдХреНрд╕рдкреНрд▓реЛрд░ рдХрд░рд╛рдпрдЪреЗ рдЕрд╕реЗрд▓ рддрд░ рдЙрдШрдбрд╛ state.js
рд╡рд░
рднрд╛рдЧ 2. рдмреЕрдХрдПрдВрдб рд╕рд░реНрд╡реНрд╣рд░
рдпрд╛ рднрд╛рдЧрд╛рдд, рдЖрдореНрд╣реА Node.js рдмреЕрдХрдПрдВрдбрд╡рд░ рдПрдХ рдирдЬрд░ рдЯрд╛рдХреВ рдЬреЛ рдЖрдордЪреЗ рдирд┐рдпрдВрддреНрд░рдг рдХрд░рддреЛ
1. рд╕рд░реНрд╡реНрд╣рд░ рдПрдВрдЯреНрд░реА рдкреЙрдЗрдВрдЯ
рд╡реЗрдм рд╕рд░реНрд╡реНрд╣рд░ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрд┐рдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рдЖрдореНрд╣реА Node.js рдирд╛рд╡рд╛рдЪреНрдпрд╛ рд▓реЛрдХрдкреНрд░рд┐рдп рд╡реЗрдм рдлреНрд░реЗрдорд╡рд░реНрдХрдЪрд╛ рд╡рд╛рдкрд░ рдХрд░реВ src/server/server.js
:
server.js рднрд╛рдЧ рез
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}`);
рд▓рдХреНрд╖рд╛рдд рдареЗрд╡рд╛ рдХреА рдкрд╣рд┐рд▓реНрдпрд╛ рднрд╛рдЧрд╛рдд рдЖрдореНрд╣реА рд╡реЗрдмрдкреЕрдХрд╡рд░ рдЪрд░реНрдЪрд╛ рдХреЗрд▓реА рд╣реЛрддреА? рдпреЗрдереЗрдЪ рдЖрдореНрд╣реА рдЖрдордЪреЗ рд╡реЗрдмрдкреЕрдХ рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рди рд╡рд╛рдкрд░реВ. рдЖрдореНрд╣реА рддреЗ рджреЛрди рдкреНрд░рдХрд╛рд░реЗ рд╡рд╛рдкрд░реВ:
- рд╡рд╛рдкрд░рд╛
webpack-dev-midleware рдЖрдордЪреА рд╡рд┐рдХрд╛рд╕ рдкреЕрдХреЗрдЬреЗрд╕ рд╕реНрд╡рдпрдВрдЪрд▓рд┐рддрдкрдгреЗ рдкреБрдирд░реНрдмрд╛рдВрдзрдгреА рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рдХрд┐рдВрд╡рд╛ - рд╕реНрдерд┐рд░рдкрдгреЗ рдлреЛрд▓реНрдбрд░ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░рд╛
dist/
, рдЬреНрдпрд╛рдордзреНрдпреЗ Webpack рдЙрддреНрдкрд╛рджрди рддрдпрд╛рд░ рдХреЗрд▓реНрдпрд╛рдирдВрддрд░ рдЖрдордЪреНрдпрд╛ рдлрд╛рдпрд▓реА рд▓рд┐рд╣реАрд▓.
рдЖрдгрдЦреА рдПрдХ рдорд╣рддреНрддреНрд╡рд╛рдЪрдВ рдХрд╛рдо server.js
рд╕рд░реНрд╡реНрд╣рд░ рд╕реЗрдЯ рдХрд░рдгреЗ рдЖрд╣реЗ
server.js рднрд╛рдЧ рез
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);
});
рд╕рд░реНрд╡реНрд╣рд░рд╢реА socket.io рдХрдиреЗрдХреНрд╢рди рдпрд╢рд╕реНрд╡реАрд░рд┐рддреНрдпрд╛ рд╕реНрдерд╛рдкрд┐рдд рдХреЗрд▓реНрдпрд╛рдирдВрддрд░, рдЖрдореНрд╣реА рдирд╡реАрди рд╕реЙрдХреЗрдЯрд╕рд╛рдареА рдЗрд╡реНрд╣реЗрдВрдЯ рд╣рдБрдбрд▓рд░ рд╕реЗрдЯ рдХреЗрд▓реЗ. рдЗрд╡реНрд╣реЗрдВрдЯ рд╣рдБрдбрд▓рд░ рд╕рд┐рдВрдЧрд▓рдЯрди рдСрдмреНрдЬреЗрдХреНрдЯрд▓рд╛ рдбреЗрд▓рд┐рдЧреЗрдЯ рдХрд░реВрди рдХреНрд▓рд╛рдпрдВрдЯрдХрдбреВрди рдкреНрд░рд╛рдкреНрдд рдЭрд╛рд▓реЗрд▓реЗ рд╕рдВрджреЗрд╢ рд╣рд╛рддрд╛рд│рддрд╛рдд game
:
server.js рднрд╛рдЧ рез
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);
}
рдЖрдореНрд╣реА рдПрдХ .io рдЧреЗрдо рддрдпрд╛рд░ рдХрд░рдд рдЖрд╣реЛрдд, рддреНрдпрд╛рдореБрд│реЗ рдЖрдореНрд╣рд╛рд▓рд╛ рдлрдХреНрдд рдПрдХ рдкреНрд░рдд рд╣рд╡реА рдЖрд╣реЗ Game
("рдЧреЗрдо") - рд╕рд░реНрд╡ рдЦреЗрд│рд╛рдбреВ рдПрдХрд╛рдЪ рд░рд┐рдВрдЧрдгрд╛рдд рдЦреЗрд│рддрд╛рдд! рдкреБрдвреАрд▓ рднрд╛рдЧрд╛рдд, рд╣рд╛ рд╡рд░реНрдЧ рдХрд╕рд╛ рдХрд╛рд░реНрдп рдХрд░рддреЛ рддреЗ рдкрд╛рд╣реВ. Game
.
2. рдЧреЗрдо рд╕рд░реНрд╡реНрд╣рд░
рдХреНрд▓реЛрд╕реНрд╕ Game
рд╕рд░реНрд╡реНрд╣рд░ рдмрд╛рдЬреВрд▓рд╛ рд╕рд░реНрд╡рд╛рдд рдорд╣рддреНрд╡рд╛рдЪреЗ рддрд░реНрдХ рд╕рдорд╛рд╡рд┐рд╖реНрдЯреАрдд рдЖрд╣реЗ. рдпрд╛рдд рджреЛрди рдореБрдЦреНрдп рдХрд╛рд░реНрдпреЗ рдЖрд╣реЗрдд: рдЦреЗрд│рд╛рдбреВ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрди ╨╕ рдЧреЗрдо рд╕рд┐рдореНрдпреБрд▓реЗрд╢рди.
рдЪрд▓рд╛ рдкреНрд░рдердо рдХрд╛рд░реНрдп, рдЦреЗрд│рд╛рдбреВ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдирд╛рд╕рд╣ рдкреНрд░рд╛рд░рдВрдн рдХрд░реВрдпрд╛.
game.js рднрд╛рдЧ рез
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);
}
}
// ...
}
рдпрд╛ рдЧреЗрдордордзреНрдпреЗ, рдЖрдореНрд╣реА рдореИрджрд╛рдирд╛рдиреБрд╕рд╛рд░ рдЦреЗрд│рд╛рдбреВ рдУрд│рдЦреВ id
рддреНрдпрд╛рдВрдЪреЗ socket.io рд╕реЙрдХреЗрдЯ (рдЬрд░ рддреБрдордЪрд╛ рдЧреЛрдВрдзрд│ рдЭрд╛рд▓рд╛ рдЕрд╕реЗрд▓, рддрд░ рдкрд░рдд рдЬрд╛ server.js
). Socket.io рд╕реНрд╡рддрдГ рдкреНрд░рддреНрдпреЗрдХ рд╕реЙрдХреЗрдЯрд▓рд╛ рдПрдХ рдЕрджреНрд╡рд┐рддреАрдп рдирд┐рдпреБрдХреНрдд рдХрд░рддреЗ id
рддреНрдпрд╛рдореБрд│реЗ рдЖрдореНрд╣рд╛рд▓рд╛ рддреНрдпрд╛рдмрджреНрджрд▓ рдХрд╛рд│рдЬреА рдХрд░рдгреНрдпрд╛рдЪреА рдЧрд░рдЬ рдирд╛рд╣реА. рдореА рддреНрдпрд╛рд▓рд╛ рдХреЙрд▓ рдХрд░реЗрди рдкреНрд▓реЗрдЕрд░ рдЖрдпрдбреА.
рд╣реЗ рд▓рдХреНрд╖рд╛рдд рдШреЗрдКрди, рдЪрд▓рд╛ рд╡рд░реНрдЧрд╛рддреАрд▓ рдЙрджрд╛рд╣рд░рдг рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓реНрд╕ рдПрдХреНрд╕рдкреНрд▓реЛрд░ рдХрд░реВ Game
:
sockets
рдПрдХ рдСрдмреНрдЬреЗрдХреНрдЯ рдЖрд╣реЗ рдЬреЛ рдкреНрд▓реЗрдЕрд░ рдЖрдпрдбреАрд▓рд╛ рдкреНрд▓реЗрдЕрд░рд╢реА рд╕рдВрдмрдВрдзрд┐рдд рд╕реЙрдХреЗрдЯрд╢реА рдЬреЛрдбрддреЛ. рд╣реЗ рдЖрдореНрд╣рд╛рд▓рд╛ рддреНрдпрд╛рдВрдЪреНрдпрд╛ рдкреНрд▓реЗрдпрд░ рдЖрдпрдбреАрджреНрд╡рд╛рд░реЗ рд╕реЙрдХреЗрдЯрдордзреНрдпреЗ рд╕рддрдд рдкреНрд░рд╡реЗрд╢ рдХрд░рдгреНрдпрд╛рдЪреА рдЕрдиреБрдорддреА рджреЗрддреЗ.players
рдПрдХ рдСрдмреНрдЬреЗрдХреНрдЯ рдЖрд╣реЗ рдЬреЛ рдкреНрд▓реЗрдпрд░ рдЖрдпрдбреАрд▓рд╛ рдХреЛрдб>рдкреНрд▓реЗрдЕрд░ рдСрдмреНрдЬреЗрдХреНрдЯрд╢реА рдмрд╛рдВрдзрддреЛ
bullets
рд╡рд╕реНрддреВрдВрдЪреА рдПрдХ рд╢реНрд░реЗрдгреА рдЖрд╣реЗ Bullet
, рдЬреНрдпрд╛рдЪрд╛ рдХреЛрдгрддрд╛рд╣реА рдирд┐рд╢реНрдЪрд┐рдд рдХреНрд░рдо рдирд╛рд╣реА.
lastUpdateTime
рд╣рд╛ рдЧреЗрдо рдЕрджреНрдпрдпрд╛рд╡рдд рдХреЗрд▓реЗрд▓реНрдпрд╛ рд╢реЗрд╡рдЯрдЪреНрдпрд╛ рд╡реЗрд│реЗрдЪрд╛ рдЯрд╛рдЗрдорд╕реНрдЯреЕрдореНрдк рдЖрд╣реЗ. рддреЗ рдХрд╕реЗ рд╡рд╛рдкрд░рд▓реЗ рдЬрд╛рддреЗ рддреЗ рдЖрдкрдг рд▓рд╡рдХрд░рдЪ рдкрд╛рд╣реВ.
shouldSendUpdate
рдПрдХ рд╕рд╣рд╛рдпрдХ рдЪрд▓ рдЖрд╣реЗ. рддреНрдпрд╛рдЪрд╛ рдЙрдкрдпреЛрдЧрд╣реА рдЖрдкрдг рд▓рд╡рдХрд░рдЪ рдкрд╛рд╣реВ.
рдкрджреНрдзрддреА addPlayer()
, removePlayer()
╨╕ handleInput()
рд╕реНрдкрд╖реНрдЯ рдХрд░рдгреНрдпрд╛рдЪреА рдЧрд░рдЬ рдирд╛рд╣реА, рддреЗ рд╡рд╛рдкрд░рд▓реЗ рдЬрд╛рддрд╛рдд server.js
. рддреБрдореНрд╣рд╛рд▓рд╛ рддреБрдордЪреА рдореЗрдорд░реА рд░реАрдлреНрд░реЗрд╢ рдХрд░рд╛рдпрдЪреА рдЕрд╕рд▓реНрдпрд╛рд╕, рдереЛрдбреЗ рд╡рд░ рдЬрд╛.
рд╢реЗрд╡рдЯрдЪреА рдУрд│ constructor()
рд╕реБрд░реВ рд╣реЛрддреЗ рдЕрдкрдбреЗрдЯ рд╕рд╛рдпрдХрд▓ рдЦреЗрд│ (60 рдЕрджреНрдпрддрдиреЗ / s рдЪреНрдпрд╛ рд╡рд╛рд░рдВрд╡рд╛рд░рддреЗрд╕рд╣):
game.js рднрд╛рдЧ рез
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;
}
}
// ...
}
рдкрджреНрдзрдд update()
рдХрджрд╛рдЪрд┐рдд рд╕рд░реНрд╡реНрд╣рд░-рд╕рд╛рдЗрдб рд▓реЙрдЬрд┐рдХрдЪрд╛ рд╕рд░реНрд╡рд╛рдд рдорд╣рддреНрддреНрд╡рд╛рдЪрд╛ рднрд╛рдЧ рдЖрд╣реЗ. рддреЗ рдХрд╛рдп рдХрд░рддреЗ рддреЗ рдпреЗрдереЗ рдЖрд╣реЗ, рдХреНрд░рдорд╛рдиреЗ:
- рдХрд┐рддреА рд╡реЗрд│ рдореЛрдЬрддреЛ
dt
рдЧреЗрд▓реНрдпрд╛ рдкрд╛рд╕реВрди рдЙрддреНрддреАрд░реНрдгupdate()
. - рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рдХреНрд╖реЗрдкрдг рд░реАрдлреНрд░реЗрд╢ рдХрд░рддреЗ рдЖрдгрд┐ рдЖрд╡рд╢реНрдпрдХ рдЕрд╕рд▓реНрдпрд╛рд╕ рддреЗ рдирд╖реНрдЯ рдХрд░рддреЗ. рдпрд╛ рдХрд╛рд░реНрдпрдХреНрд╖рдорддреЗрдЪреА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдЖрдкрдг рдирдВрддрд░ рдкрд╛рд╣реВ. рдЖрддреНрддрд╛рд╕рд╛рдареА, рдЖрдореНрд╣рд╛рд▓рд╛ рддреЗ рдЬрд╛рдгреВрди рдШреЗрдгреЗ рдкреБрд░реЗрд╕реЗ рдЖрд╣реЗ
bullet.update()
рдкрд░рддрд╛рд╡рд╛true
рдЬрд░ рдкреНрд░рдХреНрд╖реЗрдкрдг рдирд╖реНрдЯ рдХреЗрд▓реЗ рдкрд╛рд╣рд┐рдЬреЗ (рддреЛ рд░рд┐рдВрдЧрдгрд╛рддреВрди рдмрд╛рд╣реЗрд░ рдкрдбрд▓рд╛). - рдкреНрд░рддреНрдпреЗрдХ рдЦреЗрд│рд╛рдбреВрд▓рд╛ рдЕрджреНрдпрддрдирд┐рдд рдХрд░рддреЗ рдЖрдгрд┐ рдЖрд╡рд╢реНрдпрдХ рдЕрд╕рд▓реНрдпрд╛рд╕ рдПрдХ рдкреНрд░рдХреНрд╖реЗрдкрдг рддрдпрд╛рд░ рдХрд░рддреЗ. рд╣реА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рджреЗрдЦреАрд▓ рдЖрдкрдг рдирдВрддрд░ рдкрд╛рд╣реВ -
player.update()
рдСрдмреНрдЬреЗрдХреНрдЯ рдкрд░рдд рдХрд░реВ рд╢рдХрддрд╛Bullet
. - рдкреНрд░реЛрдЬреЗрдХреНрдЯрд╛рдЗрд▓ рдЖрдгрд┐ рдЦреЗрд│рд╛рдбреВ рдпрд╛рдВрдЪреНрдпрд╛рддреАрд▓ рдЯрдХреНрдХрд░ рддрдкрд╛рд╕рддреЗ
applyCollisions()
, рдЬреЗ рдЦреЗрд│рд╛рдбреВрдВрдирд╛ рдорд╛рд░рдгрд╛рд░реНтАНрдпрд╛ рдкреНрд░реЛрдЬреЗрдХреНрдЯрд╛рдЗрд▓рдЪреЗ рдЕреЕрд░реЗ рдкрд░рдд рдХрд░рддреЗ. рдкрд░рдд рдЖрд▓реЗрд▓реНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рдХреНрд╖реЗрдкрдгрд╛рд╕рд╛рдареА, рдЬреНрдпрд╛ рдЦреЗрд│рд╛рдбреВрдиреЗ рддреЗ рдЙрдбрд╡рд▓реЗ рддреНрдпрд╛рдЪреЗ рдЧреБрдг рдЖрдореНрд╣реА рд╡рд╛рдврд╡рддреЛ (рд╡рд╛рдкрд░реВрдиplayer.onDealtDamage()
) рдЖрдгрд┐ рдирдВрддрд░ рдЕреЕрд░реЗрдордзреВрди рдкреНрд░реЛрдЬреЗрдХреНрдЯрд╛рдЗрд▓ рдХрд╛рдврд╛bullets
. - рд╕рд░реНрд╡ рдорд╛рд░рд▓реНрдпрд╛ рдЧреЗрд▓реЗрд▓реНрдпрд╛ рдЦреЗрд│рд╛рдбреВрдВрдирд╛ рд╕реВрдЪрд┐рдд рдХрд░рддреЗ рдЖрдгрд┐ рдирд╖реНрдЯ рдХрд░рддреЗ.
- рд╕рд░реНрд╡ рдЦреЗрд│рд╛рдбреВрдВрдирд╛ рдЧреЗрдо рдЕрдкрдбреЗрдЯ рдкрд╛рдард╡рддреЗ рдкреНрд░рддреНрдпреЗрдХ рдХреНрд╖рдгрд╛рд▓рд╛ рдЬреЗрд╡реНрд╣рд╛ рдХреЙрд▓ рдХреЗрд▓рд╛ рдЬрд╛рддреЛ
update()
. рд╣реЗ рдЖрдореНрд╣рд╛рд▓рд╛ рд╡рд░ рдирдореВрдж рдХреЗрд▓реЗрд▓реНрдпрд╛ рд╕рд╣рд╛рдпреНрдпрдХ рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓рдЪрд╛ рдорд╛рдЧреЛрд╡рд╛ рдареЗрд╡рдгреНрдпрд╛рд╕ рдорджрдд рдХрд░рддреЗ.shouldSendUpdate
. рдХрд╛рд░рдгupdate()
60 рд╡реЗрд│рд╛/рд╕реЗ рдореНрд╣рдгрддрд╛рдд, рдЖрдореНрд╣реА 30 рд╡реЗрд│рд╛/рд╕реЗ рдЧреЗрдо рдЕрджреНрдпрддрдиреЗ рдкрд╛рдард╡рддреЛ. рдЕрд╢рд╛ рдкреНрд░рдХрд╛рд░реЗ, рдШрдбреНрдпрд╛рд│ рд╡рд╛рд░рдВрд╡рд╛рд░рддрд╛ рд╕рд░реНрд╡реНрд╣рд░ рдШрдбреНрдпрд╛рд│ 30 рдШрдбреНрдпрд╛рд│реЗ/рд╕реЗ рдЖрд╣реЗ (рдЖрдореНрд╣реА рдкрд╣рд┐рд▓реНрдпрд╛ рднрд╛рдЧрд╛рдд рдШрдбреНрдпрд╛рд│рд╛рдЪреНрдпрд╛ рджрд░рд╛рдВрдмрджреНрджрд▓ рдмреЛрд▓рд▓реЛ).
рдлрдХреНрдд рдЧреЗрдо рдЕрдкрдбреЗрдЯреНрд╕ рдХрд╛ рдкрд╛рдард╡рд╛ рдХрд╛рд▓рд╛рдВрддрд░рд╛рдиреЗ ? рдЪреЕрдиреЗрд▓ рдЬрддрди рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА. рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдж 30 рдЧреЗрдо рдЕрджреНрдпрддрдиреЗ рдЦреВрдк рдЖрд╣реЗ!
рдиреБрд╕рддрд╛ рдлреЛрди рдХрд╛ рдХреЗрд▓рд╛ рдирд╛рд╣реА
update()
рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдж 30 рд╡реЗрд│рд╛? рдЧреЗрдо рд╕рд┐рдореНрдпреБрд▓реЗрд╢рди рд╕реБрдзрд╛рд░рдгреНрдпрд╛рд╕рд╛рдареА. рдЕрдзрд┐рдХ рд╡реЗрд│рд╛ рдореНрд╣рдгрддрд╛рддupdate()
, рдЧреЗрдо рд╕рд┐рдореНрдпреБрд▓реЗрд╢рди рдЬрд┐рддрдХреЗ рдЕрдЪреВрдХ рдЕрд╕реЗрд▓. рдкрд░рдВрддреБ рдЖрд╡реНрд╣рд╛рдирд╛рдВрдЪреНрдпрд╛ рд╕рдВрдЦреНрдпреЗрдиреЗ рдЬрд╛рд╕реНрдд рд╡рд╛рд╣реВрди рдЬрд╛рдК рдирдХрд╛.update()
, рдХрд╛рд░рдг рд╣реЗ рд╕рдВрдЧрдгрдХреАрдпрджреГрд╖реНрдЯреНрдпрд╛ рдорд╣рд╛рдЧ рдХрд╛рд░реНрдп рдЖрд╣реЗ - 60 рдкреНрд░рддрд┐ рд╕реЗрдХрдВрдж рдкреБрд░реЗрд╕реЗ рдЖрд╣реЗ.
рдмрд╛рдХреАрдЪреЗ рд╡рд░реНрдЧ Game
рдордзреНрдпреЗ рд╡рд╛рдкрд░рд▓реНрдпрд╛ рдЬрд╛рдгрд╛рд░реНтАНрдпрд╛ рд╕рд╣рд╛рдпреНрдпрдХ рдкрджреНрдзрддреАрдВрдЪрд╛ рд╕рдорд╛рд╡реЗрд╢ рдЖрд╣реЗ update()
:
game.js рднрд╛рдЧ рез
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()
рдЕрдЧрджреА рд╕реЛрдкреЗ - рддреЗ рдЦреЗрд│рд╛рдбреВрдВрдирд╛ рдЧреБрдгрд╛рдВрдиреБрд╕рд╛рд░ рдХреНрд░рдорд╡рд╛рд░реА рд▓рд╛рд╡рддреЗ, рд╢реАрд░реНрд╖ рдкрд╛рдЪ рдШреЗрддреЗ рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХрд╛рд╕рд╛рдареА рд╡рд╛рдкрд░рдХрд░реНрддрд╛рдирд╛рд╡ рдЖрдгрд┐ рдЧреБрдг рдорд┐рд│рд╡рддреЗ.
createUpdate()
рдордзреНрдпреЗ рд╡рд╛рдкрд░рд▓реЗ update()
рдЧреЗрдо рдЕрдкрдбреЗрдЯреНрд╕ рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЬреЗ рдЦреЗрд│рд╛рдбреВрдВрдирд╛ рд╡рд┐рддрд░рд┐рдд рдХреЗрд▓реЗ рдЬрд╛рддрд╛рдд. рддреНрдпрд╛рдЪреЗ рдореБрдЦреНрдп рдХрд╛рд░реНрдп рдкрджреНрдзрддреА рдХреЙрд▓ рдХрд░рдгреЗ рдЖрд╣реЗ serializeForUpdate()
рд╡рд░реНрдЧрд╛рдВрд╕рд╛рдареА рд▓рд╛рдЧреВ Player
╨╕ Bullet
. рд▓рдХреНрд╖рд╛рдд рдареЗрд╡рд╛ рдХреА рддреЗ рдлрдХреНрдд рдкреНрд░рддреНрдпреЗрдХ рдЦреЗрд│рд╛рдбреВрд▓рд╛ рдбреЗрдЯрд╛ рдкрд╛рд╕ рдХрд░рддреЗ рд╕рд░реНрд╡рд╛рдд рдЬрд╡рд│рдЪрд╛ рдЦреЗрд│рд╛рдбреВ рдЖрдгрд┐ рдкреНрд░реЛрдЬреЗрдХреНрдЯрд╛рдЗрд▓реНрд╕ - рдкреНрд▓реЗрдЕрд░рдкрд╛рд╕реВрди рджреВрд░ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдЧреЗрдо рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕рдмрджреНрджрд▓ рдорд╛рд╣рд┐рддреА рдкреНрд░рд╕рд╛рд░рд┐рдд рдХрд░рдгреНрдпрд╛рдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╛рд╣реА!
3. рд╕рд░реНрд╡реНрд╣рд░рд╡рд░реАрд▓ рдЧреЗрдо рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕
рдЖрдордЪреНрдпрд╛ рдЧреЗрдордордзреНрдпреЗ, рдкреНрд░реЛрдЬреЗрдХреНрдЯрд╛рдЗрд▓ рдЖрдгрд┐ рдЦреЗрд│рд╛рдбреВ рдкреНрд░рддреНрдпрдХреНрд╖рд╛рдд рдЦреВрдк рд╕рдорд╛рди рдЖрд╣реЗрдд: рддреЗ рдЕрдореВрд░реНрдд, рдЧреЛрд▓, рдЬрдВрдЧрдо рдЧреЗрдо рдСрдмреНрдЬреЗрдХреНрдЯреНрд╕ рдЖрд╣реЗрдд. рдкреНрд▓реЗрдЕрд░реНрд╕ рдЖрдгрд┐ рдкреНрд░реЛрдЬреЗрдХреНрдЯрд╛рдЗрд▓реНрд╕рдордзреАрд▓ рдпрд╛ рд╕рдорд╛рдирддреЗрдЪрд╛ рдлрд╛рдпрджрд╛ рдШреЗрдгреНрдпрд╛рд╕рд╛рдареА, рдмреЗрд╕ рдХреНрд▓рд╛рд╕ рд▓рд╛рдЧреВ рдХрд░реВрди рд╕реБрд░реБрд╡рд╛рдд рдХрд░реВрдпрд╛ 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,
};
}
}
рдпреЗрдереЗ рдХрд╛рд╣реАрд╣реА рдХреНрд▓рд┐рд╖реНрдЯ рдирд╛рд╣реА. рд╣рд╛ рд╡рд░реНрдЧ рд╡рд┐рд╕реНрддрд╛рд░рд╛рд╕рд╛рдареА рдЪрд╛рдВрдЧрд▓рд╛ рдЕрдБрдХрд░ рдкреЙрдЗрдВрдЯ рдЕрд╕реЗрд▓. рдЪрд▓рд╛ рд╡рд░реНрдЧ рдХрд╕рд╛ рдЖрд╣реЗ рддреЗ рдкрд╛рд╣реВрдпрд╛ Bullet
рд╡рд╛рдкрд░рддреЗ Object
:
рдмреБрд▓реЗрдЯ.рдЬреЗ.рдПрд╕
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
рдЦреВрдк рд▓рд╣рд╛рди! рдЖрдореНрд╣реА рдЬреЛрдбрд▓реЗ рдЖрд╣реЗ Object
рдлрдХреНрдд рдЦрд╛рд▓реАрд▓ рд╡рд┐рд╕реНрддрд╛рд░:
- рдкреЕрдХреЗрдЬ рд╡рд╛рдкрд░рдгреЗ
рд▓рд╣рд╛рди рдпрд╛рджреГрдЪреНрдЫрд┐рдХ рдкрд┐рдвреАрд╕рд╛рдареАid
рдкреНрд░рдХреНрд╖реЗрдкрдг - рдлреАрд▓реНрдб рдЬреЛрдбрдд рдЖрд╣реЗ
parentID
рдЬреЗрдгреЗрдХрд░реБрди рддреБрдореНрд╣реА рд╣реЗ рдкреНрд░рдХреНрд╖реЗрдкрдХ рддрдпрд╛рд░ рдХрд░рдгрд╛рд▒реНрдпрд╛ рдЦреЗрд│рд╛рдбреВрдЪрд╛ рдорд╛рдЧреЛрд╡рд╛ рдШреЗрдК рд╢рдХрддрд╛. - рдордзреНрдпреЗ рд░рд┐рдЯрд░реНрди рд╡реНрд╣реЕрд▓реНрдпреВ рдЬреЛрдбрдд рдЖрд╣реЗ
update()
, рдЬреЗ рд╕рдорд╛рди рдЖрд╣реЗtrue
рдЬрд░ рдкреНрд░рдХреНрд╖реЗрдкрдг рд░рд┐рдВрдЧрдгрд╛рдЪреНрдпрд╛ рдмрд╛рд╣реЗрд░ рдЕрд╕реЗрд▓ (рдЖрдореНрд╣реА рд╢реЗрд╡рдЯрдЪреНрдпрд╛ рд╡рд┐рднрд╛рдЧрд╛рдд рдпрд╛рдмрджреНрджрд▓ рдмреЛрд▓рд▓реЛ рддреЗ рд▓рдХреНрд╖рд╛рдд рдареЗрд╡рд╛?).
рдЪрд▓рд╛ рдкреБрдвреЗ рдЬрд╛рдКрдпрд╛ 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,
};
}
}
рдкреНрд▓реЗрдЕрд░реНрд╕ рдкреНрд░реЛрдЬреЗрдХреНрдЯрд╛рдЗрд▓реНрд╕рдкреЗрдХреНрд╖рд╛ рдЕрдзрд┐рдХ рдХреНрд▓рд┐рд╖реНрдЯ рдЖрд╣реЗрдд, рдореНрд╣рдгреВрди рдпрд╛ рд╡рд░реНрдЧрд╛рдд рдЖрдгрдЦреА рдХрд╛рд╣реА рдлреАрд▓реНрдб рд╕рдВрдЧреНрд░рд╣рд┐рдд рдХреЗрд▓реНрдпрд╛ рдкрд╛рд╣рд┐рдЬреЗрдд. рддреНрдпрд╛рдЪреА рдкрджреНрдзрдд update()
рдмрд░реЗрдЪ рдХрд╛рдо рдХрд░рддреЗ, рд╡рд┐рд╢реЗрд╖рддрдГ, рдЬрд░ рдХрд╛рд╣реА рд╢рд┐рд▓реНрд▓рдХ рдирд╕реЗрд▓ рддрд░ рдирд╡реАрди рддрдпрд╛рд░ рдХреЗрд▓реЗрд▓реЗ рдкреНрд░рдХреНрд╖реЗрдкрдг рдкрд░рдд рдХрд░рддреЗ fireCooldown
(рдЖрдореНрд╣реА рдорд╛рдЧреАрд▓ рд╡рд┐рднрд╛рдЧрд╛рдд рдпрд╛рдмрджреНрджрд▓ рдмреЛрд▓рд▓реЛ рддреЗ рдЖрдард╡рддреЗ?). рддрд╕реЗрдЪ рддреА рдкрджреНрдзрдд рд╡рд╛рдврд╡рддреЗ serializeForUpdate()
, рдХрд╛рд░рдг рдЖрдореНрд╣рд╛рд▓рд╛ рдЧреЗрдо рдЕрдкрдбреЗрдЯрдордзреНрдпреЗ рдЦреЗрд│рд╛рдбреВрд╕рд╛рдареА рдЕрддрд┐рд░рд┐рдХреНрдд рдлреАрд▓реНрдб рд╕рдорд╛рд╡рд┐рд╖реНрдЯ рдХрд░рдгреНрдпрд╛рдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдЖрд╣реЗ.
рдмреЗрд╕ рдХреНрд▓рд╛рд╕ рдЕрд╕рдгреЗ Object
- рдХреЛрдбрдЪреА рдкреБрдирд░рд╛рд╡реГрддреНрддреА рдЯрд╛рд│рдгреНрдпрд╛рд╕рд╛рдареА рдПрдХ рдорд╣рддреНрддреНрд╡рд╛рдЪреА рдкрд╛рдпрд░реА. рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде, рд╡рд░реНрдЧ рдирд╛рд╣реА Object
рдкреНрд░рддреНрдпреЗрдХ рдЧреЗрдо рдСрдмреНрдЬреЗрдХреНрдЯ рд╕рдорд╛рди рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдЕрд╕рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ distanceTo()
, рдЖрдгрд┐ рдпрд╛ рд╕рд░реНрд╡ рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреАрд▓рд╛ рдПрдХрд╛рдзрд┐рдХ рдлрд╛рдЗрд▓реНрд╕рд╡рд░ рдХреЙрдкреА-рдкреЗрд╕реНрдЯ рдХрд░рдгреЗ рд╣реЗ рдПрдХ рднрдпрд╛рдирдХ рд╕реНрд╡рдкреНрди рдЕрд╕реЗрд▓. рдореЛрдареНрдпрд╛ рдкреНрд░рдХрд▓реНрдкрд╛рдВрд╕рд╛рдареА рд╣реЗ рд╡рд┐рд╢реЗрд╖рддрдГ рдорд╣рддреНрд╡рд╛рдЪреЗ рдЖрд╣реЗ.рдЬреЗрд╡реНрд╣рд╛ рд╡рд┐рд╕реНрддрд╛рд░рд╛рдЪреА рд╕рдВрдЦреНрдпрд╛ Object
рд╡рд░реНрдЧ рд╡рд╛рдврдд рдЖрд╣реЗрдд.
4. рдЯрдХреНрдХрд░ рд╢реЛрдзрдгреЗ
рдЖрдордЪреНрдпрд╛рд╕рд╛рдареА рдлрдХреНрдд рдПрдХрдЪ рдЧреЛрд╖реНрдЯ рдЙрд░рддреЗ рддреА рдореНрд╣рдгрдЬреЗ рдЦреЗрд│рд╛рдбреВрдВрд╡рд░ рдкреНрд░рдХреНрд╖реЗрдкрдгрд╛рд╕реНрддреНрд░реЗ рдХрдзреА рдЖрджрд│рддрд╛рдд рд╣реЗ рдУрд│рдЦрдгреЗ! рдкрджреНрдзрддреАрддреАрд▓ рдХреЛрдбрдЪрд╛ рд╣рд╛ рднрд╛рдЧ рд▓рдХреНрд╖рд╛рдд рдареЗрд╡рд╛ update()
рд╡рд░реНрдЧрд╛рдд Game
:
game.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),
);
// ...
}
}
рдкрджреНрдзрдд рдЕрдВрдорд▓рд╛рдд рдЖрдгрд╛рдпрдЪреА рдЖрд╣реЗ applyCollisions()
, рдЬреЗ рдЦреЗрд│рд╛рдбреВрдВрдирд╛ рдорд╛рд░рдгрд╛рд░реЗ рд╕рд░реНрд╡ рдкреНрд░реЛрдЬреЗрдХреНрдЯрд╛рдЗрд▓ рдкрд░рдд рдХрд░рддреЗ. рд╕реБрджреИрд╡рд╛рдиреЗ, рд╣реЗ рдХрд░рдгреЗ рдЗрддрдХреЗ рдЕрд╡рдШрдб рдирд╛рд╣реА рдХрд╛рд░рдг
- рд╕рд░реНрд╡ рдЯрдХреНрдХрд░ рд╣реЛрдгрд╛рд░реНтАНрдпрд╛ рд╡рд╕реНрддреВ рд╡рд░реНрддреБрд│реЗ рдЖрд╣реЗрдд рдЖрдгрд┐ рдЯрдХреНрдХрд░ рд╢реЛрдз рд▓рд╛рдЧреВ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рд╣рд╛ рд╕рд░реНрд╡рд╛рдд рд╕реЛрдкрд╛ рдЖрдХрд╛рд░ рдЖрд╣реЗ.
- рдЖрдордЪреНрдпрд╛рдХрдбреЗ рдЖрдзреАрдЪ рдПрдХ рдкрджреНрдзрдд рдЖрд╣реЗ
distanceTo()
, рдЬреА рдЖрдореНрд╣реА рд╡рд░реНрдЧрд╛рдд рдорд╛рдЧреАрд▓ рд╡рд┐рднрд╛рдЧрд╛рдд рд▓рд╛рдЧреВ рдХреЗрд▓реА рд╣реЛрддреАObject
.
рдЯрдХреНрдХрд░ рд╢реЛрдзрдгреНрдпрд╛рдЪреА рдЖрдордЪреА рдЕрдВрдорд▓рдмрдЬрд╛рд╡рдгреА рдХрд╢реА рджрд┐рд╕рддреЗ рддреЗ рдпреЗрдереЗ рдЖрд╣реЗ:
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;
}
рд╣реЗ рд╕рд╛рдзреЗ рдЯрдХреНрдХрд░ рд╢реЛрдзрдгреЗ рд╡рд╕реНрддреБрд╕реНрдерд┐рддреАрд╡рд░ рдЖрдзрд╛рд░рд┐рдд рдЖрд╣реЗ рдЬрд░ рджреЛрди рд╡рд░реНрддреБрд│реЗ рддреНрдпрд╛рдВрдЪреНрдпрд╛ рдХреЗрдВрджреНрд░рд╛рдВрдордзреАрд▓ рдЕрдВрддрд░ рддреНрдпрд╛рдВрдЪреНрдпрд╛ рддреНрд░рд┐рдЬреНрдпреЗрдЪреНрдпрд╛ рдмреЗрд░реАрдЬрдкреЗрдХреНрд╖рд╛ рдХрдореА рдЕрд╕реЗрд▓ рддрд░ рддреНрдпрд╛рдВрдЪреА рдЯрдХреНрдХрд░ рд╣реЛрддреЗ. рджреЛрди рд╡рд░реНрддреБрд│рд╛рдВрдЪреНрдпрд╛ рдХреЗрдВрджреНрд░рд╛рдВрдордзреАрд▓ рдЕрдВрддрд░ рддреНрдпрд╛рдВрдЪреНрдпрд╛ рддреНрд░рд┐рдЬреНрдпреЗрдЪреНрдпрд╛ рдмреЗрд░рдЬреЗрдЗрддрдХреЗ рдЖрд╣реЗ рдЕрд╕реЗ рдпреЗрдереЗ рдЖрд╣реЗ:
рдпреЗрдереЗ рд╡рд┐рдЪрд╛рд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рд░рдЦреЗ рдЖрдгрдЦреА рдХрд╛рд╣реА рдкреИрд▓реВ рдЖрд╣реЗрдд:
- рдкреНрд░рдХреНрд╖реЗрдкрдгрд╛рдиреЗ рддреЗ рддрдпрд╛рд░ рдХрд░рдгрд╛рд▒реНрдпрд╛ рдЦреЗрд│рд╛рдбреВрд▓рд╛ рдорд╛рд░реВ рдирдпреЗ. рддреБрд▓рдирд╛ рдХрд░реВрди рд╣реЗ рд╕рд╛рдзреНрдп рдХрд░рддрд╛ рдпреЗрддреЗ
bullet.parentID
╤Бplayer.id
. - рдПрдХрд╛рдЪ рд╡реЗрд│реА рдЕрдиреЗрдХ рдЦреЗрд│рд╛рдбреВ рдЖрджрд│рдд рдЕрд╕рд▓реНрдпрд╛рдЪреНрдпрд╛ рдорд░реНрдпрд╛рджрд┐рдд рдкреНрд░рдХрд░рдгрд╛рдд рдкреНрд░рдХреНрд╖реЗрдкрдгрд╛рдиреЗ рдлрдХреНрдд рдПрдХрджрд╛рдЪ рдорд╛рд░рд▓реЗ рдкрд╛рд╣рд┐рдЬреЗ. рдЖрдореНрд╣реА рдСрдкрд░реЗрдЯрд░ рд╡рд╛рдкрд░реВрди рд╣реА рд╕рдорд╕реНрдпрд╛ рд╕реЛрдбрд╡реВ
break
: рдкреНрд░рдХреНрд╖реЗрдкрдгрд╛рд╢реА рдЯрдХреНрдХрд░ рджреЗрдгрд╛рд░рд╛ рдЦреЗрд│рд╛рдбреВ рд╕рд╛рдкрдбрддрд╛рдЪ, рдЖрдореНрд╣реА рд╢реЛрдз рдерд╛рдВрдмрд╡рддреЛ рдЖрдгрд┐ рдкреБрдвреАрд▓ рдкреНрд░рдХреНрд╖реЗрдкрдгрд╛рдХрдбреЗ рдЬрд╛рддреЛ.
рд╢реЗрд╡рдЯ
рдЗрддрдХрдВрдЪ! .io рд╡реЗрдм рдЧреЗрдо рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддреБрдореНрд╣рд╛рд▓рд╛ рдорд╛рд╣рд┐рдд рдЕрд╕рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рд╕рд░реНрд╡ рдЧреЛрд╖реНрдЯреА рдЖрдореНрд╣реА рдХрд╡реНрд╣рд░ рдХреЗрд▓реНрдпрд╛ рдЖрд╣реЗрдд. рдкреБрдвреЗ рдХрд╛рдп? рддреБрдордЪрд╛ рд╕реНрд╡рддрдГрдЪрд╛ .io рдЧреЗрдо рддрдпрд╛рд░ рдХрд░рд╛!
рд╕рд░реНрд╡ рдирдореБрдирд╛ рдХреЛрдб рдореБрдХреНрдд рд╕реНрд░реЛрдд рдЖрд╣реЗ рдЖрдгрд┐ рд╡рд░ рдкреЛрд╕реНрдЯ рдХреЗрд▓рд╛ рдЖрд╣реЗ
рд╕реНрддреНрд░реЛрдд: www.habr.com