2015 āĻ¸āĻžāĻ˛ā§ āĻŽā§āĻā§āĻ¤āĻŋ āĻĒāĻžāĻ¯āĻŧ
āĻāĻĒāĻ¨āĻŋ āĻ¯āĻĻāĻŋ āĻāĻ āĻā§āĻŽāĻā§āĻ˛āĻŋāĻ° āĻāĻĨāĻž āĻāĻā§ āĻāĻāĻ¨āĻ āĻļā§āĻ¨ā§āĻ¨ āĻ¨āĻŋ, āĻ¤āĻŦā§ āĻāĻāĻā§āĻ˛āĻŋ āĻŦāĻŋāĻ¨āĻžāĻŽā§āĻ˛ā§āĻ¯ā§ āĻŽāĻžāĻ˛ā§āĻāĻŋāĻĒā§āĻ˛ā§āĻ¯āĻŧāĻžāĻ° āĻāĻ¯āĻŧā§āĻŦ āĻā§āĻŽ āĻ¯āĻž āĻā§āĻ˛āĻž āĻ¸āĻšāĻ (āĻā§āĻ¨ āĻ
ā§āĻ¯āĻžāĻāĻžāĻāĻ¨ā§āĻā§āĻ° āĻĒā§āĻ°āĻ¯āĻŧā§āĻāĻ¨ āĻ¨ā§āĻ)āĨ¤ āĻ¤āĻžāĻ°āĻž āĻ¸āĻžāĻ§āĻžāĻ°āĻŖāĻ¤ āĻāĻāĻ āĻ
āĻā§āĻāĻ¨ā§ āĻ
āĻ¨ā§āĻ āĻŦāĻŋāĻ°ā§āĻ§ā§ āĻā§āĻ˛ā§āĻ¯āĻŧāĻžāĻĄāĻŧā§āĻ° āĻŽā§āĻā§āĻŽā§āĻāĻŋ āĻšāĻ¯āĻŧāĨ¤ āĻ
āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ āĻŦāĻŋāĻā§āĻ¯āĻžāĻ¤ .io āĻā§āĻŽ:
āĻāĻ āĻĒā§āĻ¸ā§āĻā§, āĻāĻŽāĻ°āĻž āĻāĻŋāĻāĻžāĻŦā§ āĻ
āĻ¨ā§āĻŦā§āĻˇāĻŖ āĻāĻ°āĻŦ āĻ¸ā§āĻā§āĻ°ā§āĻ¯āĻžāĻ āĻĨā§āĻā§ āĻāĻāĻāĻŋ .io āĻā§āĻŽ āĻ¤ā§āĻ°āĻŋ āĻāĻ°ā§āĻ¨. āĻāĻ āĻāĻ¨ā§āĻ¯, āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻāĻžāĻāĻžāĻ¸ā§āĻā§āĻ°āĻŋāĻĒā§āĻ āĻā§āĻāĻžāĻ¨ āĻ¯āĻĨā§āĻˇā§āĻ āĻšāĻŦā§: āĻāĻĒāĻ¨āĻŋ āĻ¸āĻŋāĻ¨āĻā§āĻ¯āĻžāĻā§āĻ¸ āĻŽāĻ¤ āĻāĻŋāĻ¨āĻŋāĻ¸ āĻŦā§āĻāĻ¤ā§ āĻšāĻŦā§ this
и
io āĻā§āĻŽā§āĻ° āĻāĻĻāĻžāĻšāĻ°āĻŖ
āĻļā§āĻāĻžāĻ° āĻ¸āĻšāĻžāĻ¯āĻŧāĻ¤āĻžāĻ° āĻāĻ¨ā§āĻ¯, āĻāĻŽāĻ°āĻž āĻāĻ˛ā§āĻ˛ā§āĻ āĻāĻ°āĻŦ
āĻā§āĻŽāĻāĻŋ āĻŦā§āĻļ āĻ¸āĻšāĻ: āĻāĻĒāĻ¨āĻŋ āĻāĻŽāĻ¨ āĻāĻāĻāĻŋ āĻ
āĻā§āĻāĻ¨ā§ āĻāĻāĻāĻŋ āĻāĻžāĻšāĻžāĻ āĻ¨āĻŋāĻ¯āĻŧāĻ¨ā§āĻ¤ā§āĻ°āĻŖ āĻāĻ°ā§āĻ¨ āĻ¯ā§āĻāĻžāĻ¨ā§ āĻ
āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ āĻā§āĻ˛ā§āĻ¯āĻŧāĻžāĻĄāĻŧ āĻ°āĻ¯āĻŧā§āĻā§āĨ¤ āĻāĻĒāĻ¨āĻžāĻ° āĻāĻžāĻšāĻžāĻ āĻ¸ā§āĻŦāĻ¯āĻŧāĻāĻā§āĻ°āĻŋāĻ¯āĻŧāĻāĻžāĻŦā§ āĻĒā§āĻ°āĻā§āĻā§āĻāĻžāĻāĻ˛ āĻĢāĻžāĻ¯āĻŧāĻžāĻ° āĻāĻ°ā§ āĻāĻŦāĻ āĻāĻĒāĻ¨āĻŋ āĻ¤āĻžāĻĻā§āĻ° āĻĒā§āĻ°āĻā§āĻā§āĻāĻžāĻāĻ˛ āĻāĻĄāĻŧāĻŋāĻ¯āĻŧā§ āĻ
āĻ¨ā§āĻ¯ āĻā§āĻ˛ā§āĻ¯āĻŧāĻžāĻĄāĻŧāĻĻā§āĻ° āĻāĻāĻžāĻ¤ āĻāĻ°āĻžāĻ° āĻā§āĻˇā§āĻāĻž āĻāĻ°ā§āĻ¨āĨ¤
1. āĻĒā§āĻ°āĻāĻ˛ā§āĻĒā§āĻ° āĻ¸āĻāĻā§āĻˇāĻŋāĻĒā§āĻ¤ āĻŦāĻŋāĻŦāĻ°āĻŖ / āĻāĻžāĻ āĻžāĻŽā§
āĻāĻŽāĻŋ āĻ¸ā§āĻĒāĻžāĻ°āĻŋāĻļ āĻāĻ°āĻāĻŋ
āĻ¸ā§āĻ°ā§āĻ¸ āĻā§āĻĄ āĻĄāĻžāĻāĻ¨āĻ˛ā§āĻĄ āĻāĻ°ā§āĻ¨ āĻāĻĻāĻžāĻšāĻ°āĻŖ āĻā§āĻŽ āĻ¯āĻžāĻ¤ā§ āĻāĻĒāĻ¨āĻŋ āĻāĻŽāĻžāĻā§ āĻ āĻ¨ā§āĻ¸āĻ°āĻŖ āĻāĻ°āĻ¤ā§ āĻĒāĻžāĻ°ā§āĻ¨āĨ¤
āĻāĻĻāĻžāĻšāĻ°āĻŖ āĻ¨āĻŋāĻŽā§āĻ¨āĻ˛āĻŋāĻāĻŋāĻ¤ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻāĻ°ā§:
āĻĒā§āĻ°āĻāĻžāĻļ āĻāĻ°āĻž āĻ¸āĻŦāĻā§āĻ¯āĻŧā§ āĻāĻ¨āĻĒā§āĻ°āĻŋāĻ¯āĻŧ Node.js āĻāĻ¯āĻŧā§āĻŦ āĻĢā§āĻ°ā§āĻŽāĻāĻ¯āĻŧāĻžāĻ°ā§āĻ āĻ¯āĻž āĻā§āĻŽā§āĻ° āĻāĻ¯āĻŧā§āĻŦ āĻ¸āĻžāĻ°ā§āĻāĻžāĻ° āĻĒāĻ°āĻŋāĻāĻžāĻ˛āĻ¨āĻž āĻāĻ°ā§āĨ¤socket.io - āĻŦā§āĻ°āĻžāĻāĻāĻžāĻ° āĻāĻŦāĻ āĻ¸āĻžāĻ°ā§āĻāĻžāĻ°ā§āĻ° āĻŽāĻ§ā§āĻ¯ā§ āĻĄā§āĻāĻž āĻāĻĻāĻžāĻ¨-āĻĒā§āĻ°āĻĻāĻžāĻ¨ā§āĻ° āĻāĻ¨ā§āĻ¯ āĻāĻāĻāĻŋ āĻāĻ¯āĻŧā§āĻŦāĻ¸āĻā§āĻ āĻ˛āĻžāĻāĻŦā§āĻ°ā§āĻ°āĻŋāĨ¤webpack - āĻŽāĻĄāĻŋāĻāĻ˛ āĻŽā§āĻ¯āĻžāĻ¨ā§āĻāĻžāĻ°āĨ¤ āĻāĻĒāĻ¨āĻŋ āĻā§āĻ¨ Webpack āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻāĻ°āĻŦā§āĻ¨ āĻ¸ā§ āĻ¸āĻŽā§āĻĒāĻ°ā§āĻā§ āĻĒāĻĄāĻŧāĻ¤ā§ āĻĒāĻžāĻ°ā§āĻ¨āĨ¤āĻāĻāĻžāĻ¨ā§ .
āĻāĻāĻžāĻ¨ā§ āĻĒā§āĻ°āĻā§āĻā§āĻ āĻĄāĻŋāĻ°ā§āĻā§āĻāĻ°āĻŋ āĻāĻ āĻ¨ āĻĻā§āĻāĻ¤ā§ āĻā§āĻŽāĻ¨:
public/
assets/
...
src/
client/
css/
...
html/
index.html
index.js
...
server/
server.js
...
shared/
constants.js
āĻ¸āĻ°ā§āĻŦāĻāĻ¨ā§āĻ¨/
āĻāĻāĻāĻŋ āĻĢā§āĻ˛ā§āĻĄāĻžāĻ°ā§ āĻ¸āĻŦāĻāĻŋāĻā§ public/
āĻ¸āĻžāĻ°ā§āĻāĻžāĻ° āĻĻā§āĻŦāĻžāĻ°āĻž āĻ¸ā§āĻĨāĻŋāĻ°āĻāĻžāĻŦā§ āĻāĻŽāĻž āĻĻā§āĻāĻ¯āĻŧāĻž āĻšāĻŦā§āĨ¤ āĻāĻŋāĻ¤āĻ°ā§ 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
āĻāĻžāĻāĻžāĻ¸ā§āĻā§āĻ°āĻŋāĻĒā§āĻ (JS) āĻā§āĻ˛āĻžāĻ¯āĻŧā§āĻ¨ā§āĻā§āĻ° āĻāĻ¨ā§āĻā§āĻ°āĻŋ āĻĒāĻ¯āĻŧā§āĻ¨ā§āĻāĨ¤ āĻāĻ¯āĻŧā§āĻŦāĻĒā§āĻ¯āĻžāĻ āĻāĻāĻžāĻ¨ āĻĨā§āĻā§ āĻļā§āĻ°ā§ āĻšāĻŦā§ āĻāĻŦāĻ āĻ āĻ¨ā§āĻ¯āĻžāĻ¨ā§āĻ¯ āĻāĻŽāĻĻāĻžāĻ¨āĻŋ āĻāĻ°āĻž āĻĢāĻžāĻāĻ˛āĻā§āĻ˛āĻŋāĻ° āĻāĻ¨ā§āĻ¯ āĻŦāĻžāĻ°āĻŦāĻžāĻ° āĻ āĻ¨ā§āĻ¸āĻ¨ā§āĻ§āĻžāĻ¨ āĻāĻ°āĻŦā§āĨ¤- āĻāĻŽāĻžāĻĻā§āĻ° āĻāĻ¯āĻŧā§āĻŦāĻĒā§āĻ¯āĻžāĻ āĻŦāĻŋāĻ˛ā§āĻĄā§āĻ° āĻāĻāĻāĻĒā§āĻ JS āĻĄāĻŋāĻ°ā§āĻā§āĻāĻ°āĻŋāĻ¤ā§ āĻ
āĻŦāĻ¸ā§āĻĨāĻŋāĻ¤ āĻšāĻŦā§
dist/
. āĻāĻŽāĻŋ āĻāĻ āĻĢāĻžāĻāĻ˛āĻāĻŋāĻā§ āĻāĻŽāĻžāĻĻā§āĻ° āĻāĻ˛ āĻāĻ°āĻŦ js āĻĒā§āĻ¯āĻžāĻā§āĻ. - āĻāĻŽāĻ°āĻž āĻŦā§āĻ¯āĻžāĻŦāĻšāĻžāĻ° āĻāĻ°āĻŋ
āĻšāĻā§āĻāĻā§āĻ˛ , āĻāĻŦāĻ āĻŦāĻŋāĻļā§āĻˇ āĻāĻ°ā§ āĻāĻ¨āĻĢāĻŋāĻāĻžāĻ°ā§āĻļāĻ¨@babel/preset-env āĻĒā§āĻ°āĻžāĻ¨ā§ āĻŦā§āĻ°āĻžāĻāĻāĻžāĻ°āĻā§āĻ˛āĻŋāĻ° āĻāĻ¨ā§āĻ¯ āĻāĻŽāĻžāĻĻā§āĻ° āĻā§āĻāĻ¸ āĻā§āĻĄ āĻā§āĻ°āĻžāĻ¨ā§āĻ¸āĻĒāĻŋāĻ˛āĻŋāĻ āĻāĻ°āĻ¤ā§āĨ¤ - āĻāĻŽāĻ°āĻž āĻāĻāĻāĻŋ āĻĒā§āĻ˛āĻžāĻāĻāĻ¨ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻāĻ°āĻāĻŋ 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>
āĻāĻŽāĻžāĻĻā§āĻ° āĻāĻžāĻāĻžāĻ¸ā§āĻā§āĻ°āĻŋāĻĒā§āĻ āĻĒā§āĻ¯āĻžāĻā§āĻ āĻ¯ā§āĻ āĻāĻ°āĻ¤ā§.- āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻāĻžāĻ°ā§āĻ° āĻ¨āĻžāĻŽ āĻ¸āĻš āĻĒā§āĻ°āĻ§āĻžāĻ¨ āĻŽā§āĻ¨ā§
<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 āĻĢāĻžāĻāĻ˛ āĻāĻŽāĻĻāĻžāĻ¨āĻŋ āĻāĻ°āĻž āĻšāĻā§āĻā§āĨ¤
- āĻ¸āĻŋāĻāĻ¸āĻāĻ¸ āĻāĻŽāĻĻāĻžāĻ¨āĻŋ (āĻ¤āĻžāĻ āĻāĻ¯āĻŧā§āĻŦāĻĒā§āĻ¯āĻžāĻ āĻāĻŽāĻžāĻĻā§āĻ° āĻ¸āĻŋāĻāĻ¸āĻāĻ¸ āĻĒā§āĻ¯āĻžāĻā§āĻā§ āĻ¤āĻžāĻĻā§āĻ° āĻ āĻ¨ā§āĻ¤āĻ°ā§āĻā§āĻā§āĻ¤ āĻāĻ°āĻ¤ā§ āĻāĻžāĻ¨ā§)āĨ¤
- āĻāĻ°āĻŽā§āĻ
connect()
āĻ¸āĻžāĻ°ā§āĻāĻžāĻ°ā§āĻ° āĻ¸āĻžāĻĨā§ āĻ¸āĻāĻ¯ā§āĻ āĻ¸ā§āĻĨāĻžāĻĒāĻ¨ āĻāĻ°āĻ¤ā§ āĻāĻŦāĻ āĻāĻžāĻ˛āĻžāĻ¤ā§downloadAssets()
āĻā§āĻŽāĻāĻŋ āĻ°ā§āĻ¨ā§āĻĄāĻžāĻ° āĻāĻ°āĻžāĻ° āĻāĻ¨ā§āĻ¯ āĻĒā§āĻ°āĻ¯āĻŧā§āĻāĻ¨ā§āĻ¯āĻŧ āĻāĻŦāĻŋ āĻĄāĻžāĻāĻ¨āĻ˛ā§āĻĄ āĻāĻ°āĻ¤ā§āĨ¤ - āĻĒāĻ°ā§āĻ¯āĻžāĻ¯āĻŧ 3 āĻ¸āĻŽāĻžāĻĒā§āĻ¤āĻŋāĻ° āĻĒāĻ° āĻĒā§āĻ°āĻ§āĻžāĻ¨ āĻŽā§āĻ¨ā§ āĻĒā§āĻ°āĻĻāĻ°ā§āĻļāĻŋāĻ¤ āĻšāĻ¯āĻŧ (
playMenu
). - "PLAY" āĻŦā§āĻ¤āĻžāĻŽ āĻāĻŋāĻĒāĻžāĻ¨ā§āĻ° āĻāĻ¨ā§āĻ¯ āĻšā§āĻ¯āĻžāĻ¨ā§āĻĄāĻ˛āĻžāĻ° āĻ¸ā§āĻ āĻāĻ°āĻž āĻšāĻā§āĻā§ā§ˇ āĻŦā§āĻ¤āĻžāĻŽ āĻāĻŋāĻĒāĻ˛ā§, āĻā§āĻĄāĻāĻŋ āĻā§āĻŽāĻāĻŋ āĻļā§āĻ°ā§ āĻāĻ°ā§ āĻāĻŦāĻ āĻ¸āĻžāĻ°ā§āĻāĻžāĻ°āĻā§ āĻŦāĻ˛ā§ āĻ¯ā§ āĻāĻŽāĻ°āĻž āĻā§āĻ˛āĻ¤ā§ āĻĒā§āĻ°āĻ¸ā§āĻ¤ā§āĻ¤āĨ¤
āĻāĻŽāĻžāĻĻā§āĻ° āĻā§āĻ˛āĻžāĻ¯āĻŧā§āĻ¨ā§āĻ-āĻ¸āĻžāĻ°ā§āĻāĻžāĻ° āĻ˛āĻāĻŋāĻā§āĻ° āĻĒā§āĻ°āĻ§āĻžāĻ¨ "āĻŽāĻžāĻāĻ¸" āĻ¸ā§āĻ āĻĢāĻžāĻāĻ˛āĻā§āĻ˛āĻŋāĻ¤ā§ āĻ°āĻ¯āĻŧā§āĻā§ āĻ¯āĻž āĻĢāĻžāĻāĻ˛ āĻĻā§āĻŦāĻžāĻ°āĻž āĻāĻŽāĻĻāĻžāĻ¨āĻŋ āĻāĻ°āĻž āĻšāĻ¯āĻŧā§āĻāĻŋāĻ˛ 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 āĻ āĻāĻļ 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;
}
āĻĒā§āĻ°āĻĨāĻŽ āĻ§āĻžāĻĒ āĻšāĻ˛ āĻāĻŋ āĻŦā§āĻ° āĻāĻ°āĻž currentServerTime()
. āĻāĻŽāĻ°āĻž āĻāĻā§ āĻĻā§āĻā§āĻāĻŋ, āĻĒā§āĻ°āĻ¤āĻŋāĻāĻŋ āĻā§āĻŽ āĻāĻĒāĻĄā§āĻā§ āĻāĻāĻāĻŋ āĻ¸āĻžāĻ°ā§āĻāĻžāĻ° āĻāĻžāĻāĻŽāĻ¸ā§āĻā§āĻ¯āĻžāĻŽā§āĻĒ āĻ
āĻ¨ā§āĻ¤āĻ°ā§āĻā§āĻā§āĻ¤ āĻĨāĻžāĻā§āĨ¤ āĻāĻŽāĻ°āĻž āĻ¸āĻžāĻ°ā§āĻāĻžāĻ°ā§āĻ° āĻĒāĻŋāĻāĻ¨ā§ 100ms āĻāĻŽā§āĻ āĻ°ā§āĻ¨ā§āĻĄāĻžāĻ° āĻāĻ°āĻ¤ā§ āĻ°ā§āĻ¨ā§āĻĄāĻžāĻ° āĻ˛ā§āĻā§āĻ¨ā§āĻ¸āĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻāĻ°āĻ¤ā§ āĻāĻžāĻ, āĻāĻŋāĻ¨ā§āĻ¤ā§ āĻāĻŽāĻ°āĻž āĻāĻāĻ¨āĻ āĻ¸āĻžāĻ°ā§āĻāĻžāĻ°ā§ āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ¸āĻŽāĻ¯āĻŧ āĻāĻžāĻ¨āĻ¤ā§ āĻĒāĻžāĻ°āĻŦ āĻ¨āĻž, āĻāĻžāĻ°āĻŖ āĻāĻŽāĻ°āĻž āĻāĻžāĻ¨āĻŋ āĻ¨āĻž āĻ¯ā§ āĻā§āĻ¨ā§ āĻāĻĒāĻĄā§āĻ āĻāĻŽāĻžāĻĻā§āĻ° āĻāĻžāĻā§ āĻĒā§āĻ¤ā§ āĻāĻ¤āĻā§āĻˇāĻŖ āĻ˛ā§āĻā§āĻā§āĨ¤ āĻāĻ¨ā§āĻāĻžāĻ°āĻ¨ā§āĻ āĻ
āĻĒā§āĻ°āĻ¤ā§āĻ¯āĻžāĻļāĻŋāĻ¤ āĻāĻŦāĻ āĻāĻ° āĻāĻ¤āĻŋ āĻŦā§āĻ¯āĻžāĻĒāĻāĻāĻžāĻŦā§ āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻŋāĻ¤ āĻšāĻ¤ā§ āĻĒāĻžāĻ°ā§!
āĻāĻ āĻ¸āĻŽāĻ¸ā§āĻ¯āĻžāĻāĻŋ āĻĒā§āĻ¤ā§, āĻāĻŽāĻ°āĻž āĻāĻāĻāĻŋ āĻ¯ā§āĻā§āĻ¤āĻŋāĻ¸āĻā§āĻāĻ¤ āĻ
āĻ¨ā§āĻŽāĻžāĻ¨ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻāĻ°āĻ¤ā§ āĻĒāĻžāĻ°āĻŋ: āĻāĻŽāĻ°āĻž āĻĒā§āĻ°āĻĨāĻŽ āĻāĻĒāĻĄā§āĻ āĻ
āĻŦāĻŋāĻ˛āĻŽā§āĻŦā§ āĻāĻāĻ¤ āĻāĻžāĻ¨. āĻ¯āĻĻāĻŋ āĻāĻāĻŋ āĻ¸āĻ¤ā§āĻ¯ āĻšāĻ¯āĻŧ, āĻ¤āĻžāĻšāĻ˛ā§ āĻāĻŽāĻ°āĻž āĻāĻ āĻŦāĻŋāĻļā§āĻˇ āĻŽā§āĻšā§āĻ°ā§āĻ¤ā§ āĻ¸āĻžāĻ°ā§āĻāĻžāĻ°ā§āĻ° āĻ¸āĻŽāĻ¯āĻŧ āĻāĻžāĻ¨āĻ¤ā§ āĻĒāĻžāĻ°āĻ¤āĻžāĻŽ! āĻāĻŽāĻ°āĻž āĻ¸āĻžāĻ°ā§āĻāĻžāĻ°ā§āĻ° āĻāĻžāĻāĻŽāĻ¸ā§āĻā§āĻ¯āĻžāĻŽā§āĻĒ āĻ¸āĻāĻ°āĻā§āĻˇāĻŖ āĻāĻ°āĻŋ firstServerTimestamp
āĻāĻŦāĻ āĻāĻŽāĻžāĻĻā§āĻ° āĻ°āĻžāĻāĻž āĻ¸ā§āĻĨāĻžāĻ¨ā§āĻ¯āĻŧ (āĻā§āĻ˛āĻžāĻ¯āĻŧā§āĻ¨ā§āĻ) āĻāĻāĻ āĻŽā§āĻšā§āĻ°ā§āĻ¤ā§ āĻāĻžāĻāĻŽāĻ¸ā§āĻā§āĻ¯āĻžāĻŽā§āĻĒ gameStart
.
āĻ
āĻĒā§āĻā§āĻˇāĻž āĻāĻ°. āĻāĻāĻŋ āĻ¸āĻžāĻ°ā§āĻāĻžāĻ° āĻ¸āĻŽāĻ¯āĻŧ = āĻā§āĻ˛āĻžāĻ¯āĻŧā§āĻ¨ā§āĻ āĻ¸āĻŽāĻ¯āĻŧ āĻšāĻāĻ¯āĻŧāĻž āĻāĻāĻŋāĻ¤ āĻ¨āĻ¯āĻŧ? āĻā§āĻ¨ āĻāĻŽāĻ°āĻž "āĻ¸āĻžāĻ°ā§āĻāĻžāĻ° āĻāĻžāĻāĻŽāĻ¸ā§āĻā§āĻ¯āĻžāĻŽā§āĻĒ" āĻāĻŦāĻ "āĻā§āĻ˛āĻžāĻ¯āĻŧā§āĻ¨ā§āĻ āĻāĻžāĻāĻŽāĻ¸ā§āĻā§āĻ¯āĻžāĻŽā§āĻĒ" āĻāĻ° āĻŽāĻ§ā§āĻ¯ā§ āĻĒāĻžāĻ°ā§āĻĨāĻā§āĻ¯ āĻāĻ°āĻŋ? āĻāĻāĻāĻž āĻāĻāĻāĻž āĻāĻžāĻ˛ā§ āĻĒā§āĻ°āĻļā§āĻ¨! āĻĻā§āĻāĻž āĻ¯āĻžāĻā§āĻā§ āĻ¯ā§ āĻ¤āĻžāĻ°āĻž āĻāĻāĻ āĻāĻŋāĻ¨āĻŋāĻ¸ āĻ¨āĻ¯āĻŧāĨ¤ Date.now()
āĻā§āĻ˛āĻžāĻ¯āĻŧā§āĻ¨ā§āĻ āĻāĻŦāĻ āĻ¸āĻžāĻ°ā§āĻāĻžāĻ°ā§ āĻŦāĻŋāĻāĻŋāĻ¨ā§āĻ¨ āĻāĻžāĻāĻŽāĻ¸ā§āĻā§āĻ¯āĻžāĻŽā§āĻĒ āĻĢāĻŋāĻ°āĻŋāĻ¯āĻŧā§ āĻĻā§āĻŦā§ āĻāĻŦāĻ āĻāĻāĻŋ āĻāĻ āĻŽā§āĻļāĻŋāĻ¨āĻā§āĻ˛āĻŋāĻ° āĻ¸ā§āĻĨāĻžāĻ¨ā§āĻ¯āĻŧ āĻāĻžāĻ°āĻŖāĻā§āĻ˛āĻŋāĻ° āĻāĻĒāĻ° āĻ¨āĻŋāĻ°ā§āĻāĻ° āĻāĻ°ā§āĨ¤ āĻāĻāĻ¨āĻ āĻ
āĻ¨ā§āĻŽāĻžāĻ¨ āĻāĻ°āĻŦā§āĻ¨ āĻ¨āĻž āĻ¯ā§ āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻŽā§āĻļāĻŋāĻ¨ā§ āĻāĻžāĻāĻŽāĻ¸ā§āĻā§āĻ¯āĻžāĻŽā§āĻĒ āĻāĻāĻ āĻšāĻŦā§āĨ¤
āĻāĻāĻ¨ āĻāĻŽāĻ°āĻž āĻŦā§āĻāĻ¤ā§ āĻĒāĻžāĻ°āĻŋ āĻāĻŋ āĻāĻ°ā§ currentServerTime()
: āĻāĻāĻž āĻĢāĻŋāĻ°ā§ āĻāĻ¸ā§ āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ°ā§āĻ¨ā§āĻĄāĻžāĻ° āĻ¸āĻŽāĻ¯āĻŧā§āĻ° āĻ¸āĻžāĻ°ā§āĻāĻžāĻ° āĻāĻžāĻāĻŽāĻ¸ā§āĻā§āĻ¯āĻžāĻŽā§āĻĒ. āĻ
āĻ¨ā§āĻ¯ āĻāĻĨāĻžāĻ¯āĻŧ, āĻāĻāĻŋ āĻ¸āĻžāĻ°ā§āĻāĻžāĻ°ā§āĻ° āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ¸āĻŽāĻ¯āĻŧ (firstServerTimestamp <+ (Date.now() - gameStart)
) āĻŦāĻŋāĻ¯āĻŧā§āĻ āĻ°ā§āĻ¨ā§āĻĄāĻžāĻ° āĻŦāĻŋāĻ˛āĻŽā§āĻŦ (RENDER_DELAY
).
āĻāĻāĻ¨ āĻāĻ¸ā§āĻ¨ āĻāĻŽāĻ°āĻž āĻā§āĻŽ āĻāĻĒāĻĄā§āĻāĻā§āĻ˛āĻŋ āĻā§āĻāĻžāĻŦā§ āĻĒāĻ°āĻŋāĻāĻžāĻ˛āĻ¨āĻž āĻāĻ°āĻŋ āĻ¤āĻž āĻĻā§āĻā§ āĻ¨ā§āĻāĻ¯āĻŧāĻž āĻ¯āĻžāĻāĨ¤ āĻāĻĒāĻĄā§āĻ āĻ¸āĻžāĻ°ā§āĻāĻžāĻ° āĻĨā§āĻā§ āĻĒā§āĻ°āĻžāĻĒā§āĻ¤ āĻšāĻ˛ā§, āĻāĻāĻŋ āĻŦāĻ˛āĻž āĻšāĻ¯āĻŧ processGameUpdate()
āĻāĻŦāĻ āĻāĻŽāĻ°āĻž āĻāĻāĻāĻŋ āĻ
ā§āĻ¯āĻžāĻ°ā§āĻ¤ā§ āĻ¨āĻ¤ā§āĻ¨ āĻāĻĒāĻĄā§āĻ āĻ¸āĻāĻ°āĻā§āĻˇāĻŖ āĻāĻ°āĻŋ gameUpdates
. āĻ¤āĻžāĻ°āĻĒāĻ°, āĻŽā§āĻŽāĻ°āĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻĒāĻ°ā§āĻā§āĻˇāĻž āĻāĻ°āĻžāĻ° āĻāĻ¨ā§āĻ¯, āĻāĻŽāĻ°āĻž āĻāĻā§ āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻĒā§āĻ°āĻžāĻ¨ā§ āĻāĻĒāĻĄā§āĻ āĻŽā§āĻā§ āĻĢā§āĻ˛āĻŋ āĻŦā§āĻ¸ āĻāĻĒāĻĄā§āĻāĻāĻžāĻ°āĻŖ āĻāĻŽāĻžāĻĻā§āĻ° āĻāĻ° āĻ¤āĻžāĻĻā§āĻ° āĻĻāĻ°āĻāĻžāĻ° āĻ¨ā§āĻāĨ¤
āĻāĻāĻāĻŋ "āĻŽā§āĻ˛āĻŋāĻ āĻāĻĒāĻĄā§āĻ" āĻāĻŋ? āĻāĻ āĻ¸āĻžāĻ°ā§āĻāĻžāĻ°ā§āĻ° āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ¸āĻŽāĻ¯āĻŧ āĻĨā§āĻā§ āĻĒāĻŋāĻāĻ¨ā§ āĻ¸āĻ°ā§ āĻāĻŋāĻ¯āĻŧā§ āĻāĻŽāĻ°āĻž āĻĒā§āĻ°āĻĨāĻŽ āĻāĻĒāĻĄā§āĻāĻāĻŋ āĻĒāĻžāĻ. āĻāĻ āĻāĻŋāĻ¤ā§āĻ°āĻāĻŋ āĻŽāĻ¨ā§ āĻāĻā§?
"āĻā§āĻ˛āĻžāĻ¯āĻŧā§āĻ¨ā§āĻ āĻ°ā§āĻ¨ā§āĻĄāĻžāĻ° āĻāĻžāĻāĻŽ" āĻāĻ° āĻŦāĻžāĻŽā§ āĻ¸āĻ°āĻžāĻ¸āĻ°āĻŋ āĻā§āĻŽ āĻāĻĒāĻĄā§āĻāĻāĻŋ āĻŦā§āĻ¸ āĻāĻĒāĻĄā§āĻāĨ¤
āĻŦā§āĻ¸ āĻāĻĒāĻĄā§āĻ āĻāĻŋ āĻāĻ¨ā§āĻ¯ āĻŦā§āĻ¯āĻŦāĻšā§āĻ¤ āĻšāĻ¯āĻŧ? āĻā§āĻ¨ āĻāĻŽāĻ°āĻž āĻŦā§āĻ¸āĻ˛āĻžāĻāĻ¨ā§ āĻāĻĒāĻĄā§āĻ āĻĄā§āĻ°āĻĒ āĻāĻ°āĻ¤ā§ āĻĒāĻžāĻ°āĻŋ? āĻāĻ āĻŦā§āĻ° āĻāĻ°āĻžāĻ° āĻāĻ¨ā§āĻ¯, āĻāĻ¸ā§āĻ¨ āĻĒāĻ°āĻŋāĻļā§āĻˇā§ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻŦāĻŋāĻŦā§āĻāĻ¨āĻž āĻāĻ°ā§āĻ¨ getCurrentState()
:
state.js āĻ āĻāĻļ 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),
};
}
}
āĻāĻŽāĻ°āĻž āĻ¤āĻŋāĻ¨āĻāĻŋ āĻŽāĻžāĻŽāĻ˛āĻž āĻĒāĻ°āĻŋāĻāĻžāĻ˛āĻ¨āĻž āĻāĻ°āĻŋ:
base < 0
āĻŽāĻžāĻ¨ā§ āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ°ā§āĻ¨ā§āĻĄāĻžāĻ° āĻ¸āĻŽāĻ¯āĻŧ āĻĒāĻ°ā§āĻ¯āĻ¨ā§āĻ¤ āĻā§āĻ¨ āĻāĻĒāĻĄā§āĻ āĻ¨ā§āĻ (āĻāĻĒāĻ°ā§āĻ° āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻĻā§āĻā§āĻ¨getBaseUpdate()
) āĻ°ā§āĻ¨ā§āĻĄāĻžāĻ°āĻŋāĻ āĻ˛ā§āĻ¯āĻžāĻā§āĻ° āĻāĻžāĻ°āĻŖā§ āĻā§āĻŽā§āĻ° āĻļā§āĻ°ā§āĻ¤ā§ āĻāĻāĻŋ āĻāĻāĻ¤ā§ āĻĒāĻžāĻ°ā§āĨ¤ āĻāĻ āĻā§āĻˇā§āĻ¤ā§āĻ°ā§, āĻāĻŽāĻ°āĻž āĻĒā§āĻ°āĻžāĻĒā§āĻ¤ āĻ¸āĻ°ā§āĻŦāĻļā§āĻˇ āĻāĻĒāĻĄā§āĻ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻāĻ°āĻŋāĨ¤base
āĻāĻŽāĻžāĻĻā§āĻ° āĻāĻā§ āĻ¸āĻ°ā§āĻŦāĻļā§āĻˇ āĻāĻĒāĻĄā§āĻ. āĻāĻāĻŋ āĻ¨ā§āĻāĻāĻ¯āĻŧāĻžāĻ°ā§āĻ āĻŦāĻŋāĻ˛āĻŽā§āĻŦ āĻŦāĻž āĻĻā§āĻ°ā§āĻŦāĻ˛ āĻāĻ¨ā§āĻāĻžāĻ°āĻ¨ā§āĻ āĻ¸āĻāĻ¯ā§āĻā§āĻ° āĻāĻžāĻ°āĻŖā§ āĻšāĻ¤ā§ āĻĒāĻžāĻ°ā§āĨ¤ āĻāĻ āĻā§āĻˇā§āĻ¤ā§āĻ°ā§, āĻāĻŽāĻ°āĻž āĻāĻŽāĻžāĻĻā§āĻ° āĻāĻžāĻā§ āĻ¸āĻ°ā§āĻŦāĻļā§āĻˇ āĻāĻĒāĻĄā§āĻāĻāĻŋāĻ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻāĻ°āĻāĻŋāĨ¤- āĻāĻŽāĻžāĻĻā§āĻ° āĻŦāĻ°ā§āĻ¤āĻŽāĻžāĻ¨ āĻ°ā§āĻ¨ā§āĻĄāĻžāĻ° āĻ¸āĻŽāĻ¯āĻŧā§āĻ° āĻāĻā§ āĻāĻŦāĻ āĻĒāĻ°ā§ āĻāĻāĻ¯āĻŧāĻ āĻāĻĒāĻĄā§āĻ āĻāĻā§, āĻ¤āĻžāĻ āĻāĻŽāĻ°āĻž āĻāĻ°āĻ¤ā§ āĻĒāĻžāĻ°āĻŋ āĻāĻ¨ā§āĻāĻžāĻ°āĻĒā§āĻ˛ā§āĻ!
āĻ¯ā§ āĻ¸āĻŦ āĻŦāĻžāĻāĻŋ āĻāĻā§ state.js
āĻ˛āĻŋāĻ¨āĻŋāĻ¯āĻŧāĻžāĻ° āĻāĻ¨ā§āĻāĻžāĻ°āĻĒā§āĻ˛ā§āĻļāĻ¨ā§āĻ° āĻāĻāĻāĻŋ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻ¯āĻž āĻ¸āĻšāĻ (āĻāĻŋāĻ¨ā§āĻ¤ā§ āĻŦāĻŋāĻ°āĻā§āĻ¤āĻŋāĻāĻ°) āĻāĻŖāĻŋāĻ¤āĨ¤ āĻ¯āĻĻāĻŋ āĻāĻĒāĻ¨āĻŋ āĻ¨āĻŋāĻā§āĻ āĻāĻāĻŋ āĻ
āĻ¨ā§āĻŦā§āĻˇāĻŖ āĻāĻ°āĻ¤ā§ āĻāĻžāĻ¨, āĻ¤āĻžāĻ°āĻĒāĻ° āĻā§āĻ˛ā§āĻ¨ state.js
āĻāĻĒāĻ°
āĻĒāĻžāĻ°ā§āĻ 2. āĻŦā§āĻ¯āĻžāĻāĻāĻ¨ā§āĻĄ āĻ¸āĻžāĻ°ā§āĻāĻžāĻ°
āĻāĻ āĻ
āĻāĻļā§, āĻāĻŽāĻ°āĻž Node.js āĻŦā§āĻ¯āĻžāĻāĻāĻ¨ā§āĻĄā§āĻ° āĻĻāĻŋāĻā§ āĻ¨āĻāĻ° āĻĻā§āĻŦ āĻ¯āĻž āĻāĻŽāĻžāĻĻā§āĻ° āĻ¨āĻŋāĻ¯āĻŧāĻ¨ā§āĻ¤ā§āĻ°āĻŖ āĻāĻ°ā§
1. āĻ¸āĻžāĻ°ā§āĻāĻžāĻ° āĻāĻ¨ā§āĻā§āĻ°āĻŋ āĻĒāĻ¯āĻŧā§āĻ¨ā§āĻ
āĻāĻ¯āĻŧā§āĻŦ āĻ¸āĻžāĻ°ā§āĻāĻžāĻ° āĻĒāĻ°āĻŋāĻāĻžāĻ˛āĻ¨āĻž āĻāĻ°āĻ¤ā§, āĻāĻŽāĻ°āĻž Node.js āĻ¨āĻžāĻŽā§ āĻāĻāĻāĻŋ āĻāĻ¨āĻĒā§āĻ°āĻŋāĻ¯āĻŧ āĻāĻ¯āĻŧā§āĻŦ āĻĢā§āĻ°ā§āĻŽāĻāĻ¯āĻŧāĻžāĻ°ā§āĻ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻāĻ°āĻŦ src/server/server.js
:
server.js āĻĒāĻžāĻ°ā§āĻ 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}`);
āĻŽāĻ¨ā§ āĻāĻā§ āĻĒā§āĻ°āĻĨāĻŽ āĻĒāĻ°ā§āĻŦā§ āĻāĻŽāĻ°āĻž Webpack āĻ¨āĻŋāĻ¯āĻŧā§ āĻāĻ˛ā§āĻāĻ¨āĻž āĻāĻ°ā§āĻāĻŋ? āĻāĻāĻžāĻ¨ā§āĻ āĻāĻŽāĻ°āĻž āĻāĻŽāĻžāĻĻā§āĻ° āĻāĻ¯āĻŧā§āĻŦāĻĒā§āĻ¯āĻžāĻ āĻāĻ¨āĻĢāĻŋāĻāĻžāĻ°ā§āĻļāĻ¨ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻāĻ°āĻŦāĨ¤ āĻāĻŽāĻ°āĻž āĻ¤āĻžāĻĻā§āĻ° āĻĻā§āĻāĻŋ āĻāĻĒāĻžāĻ¯āĻŧā§ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻāĻ°āĻŦ:
- āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻāĻ°āĻ¤ā§
webpack-dev-midleware āĻ¸ā§āĻŦāĻ¯āĻŧāĻāĻā§āĻ°āĻŋāĻ¯āĻŧāĻāĻžāĻŦā§ āĻāĻŽāĻžāĻĻā§āĻ° āĻāĻ¨ā§āĻ¨āĻ¯āĻŧāĻ¨ āĻĒā§āĻ¯āĻžāĻā§āĻ āĻĒā§āĻ¨āĻ°ā§āĻ¨āĻŋāĻ°ā§āĻŽāĻžāĻŖ āĻāĻ°āĻ¤ā§, āĻŦāĻž - āĻ¸ā§āĻĨāĻŋāĻ°āĻāĻžāĻŦā§ āĻĢā§āĻ˛ā§āĻĄāĻžāĻ° āĻ¸ā§āĻĨāĻžāĻ¨āĻžāĻ¨ā§āĻ¤āĻ°
dist/
, āĻ¯ā§āĻāĻŋāĻ¤ā§ Webpack āĻĒā§āĻ°ā§āĻĄāĻžāĻāĻļāĻ¨ āĻŦāĻŋāĻ˛ā§āĻĄ āĻāĻ°āĻžāĻ° āĻĒāĻ°ā§ āĻāĻŽāĻžāĻĻā§āĻ° āĻĢāĻžāĻāĻ˛āĻā§āĻ˛āĻŋ āĻ˛āĻŋāĻāĻŦā§āĨ¤
āĻāĻ°ā§āĻāĻāĻŋ āĻā§āĻ°ā§āĻ¤ā§āĻŦāĻĒā§āĻ°ā§āĻŖ āĻāĻžāĻ server.js
āĻ¸āĻžāĻ°ā§āĻāĻžāĻ° āĻ¸ā§āĻ āĻāĻĒ āĻāĻ°āĻ¤ā§ āĻšāĻ¯āĻŧ
server.js āĻĒāĻžāĻ°ā§āĻ 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);
});
āĻ¸āĻžāĻ°ā§āĻāĻžāĻ°ā§ āĻ¸āĻĢāĻ˛āĻāĻžāĻŦā§ āĻāĻāĻāĻŋ socket.io āĻ¸āĻāĻ¯ā§āĻ āĻ¸ā§āĻĨāĻžāĻĒāĻ¨ āĻāĻ°āĻžāĻ° āĻĒāĻ°, āĻāĻŽāĻ°āĻž āĻ¨āĻ¤ā§āĻ¨ āĻ¸āĻā§āĻā§āĻ° āĻāĻ¨ā§āĻ¯ āĻāĻā§āĻ¨ā§āĻ āĻšā§āĻ¯āĻžāĻ¨ā§āĻĄāĻ˛āĻžāĻ° āĻ¸ā§āĻ āĻāĻĒ āĻāĻ°āĻŋāĨ¤ āĻāĻā§āĻ¨ā§āĻ āĻšā§āĻ¯āĻžāĻ¨ā§āĻĄāĻ˛āĻžāĻ°āĻ°āĻž āĻāĻāĻāĻŋ āĻ¸āĻŋāĻā§āĻāĻ˛āĻāĻ¨ āĻ
āĻŦāĻā§āĻā§āĻā§ āĻ
āĻ°ā§āĻĒāĻŖ āĻāĻ°ā§ āĻā§āĻ˛āĻžāĻ¯āĻŧā§āĻ¨ā§āĻāĻĻā§āĻ° āĻāĻžāĻ āĻĨā§āĻā§ āĻĒā§āĻ°āĻžāĻĒā§āĻ¤ āĻŦāĻžāĻ°ā§āĻ¤āĻžāĻā§āĻ˛āĻŋ āĻĒāĻ°āĻŋāĻāĻžāĻ˛āĻ¨āĻž āĻāĻ°ā§ game
:
server.js āĻĒāĻžāĻ°ā§āĻ 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);
}
āĻāĻŽāĻ°āĻž āĻāĻāĻāĻŋ .io āĻā§āĻŽ āĻ¤ā§āĻ°āĻŋ āĻāĻ°āĻāĻŋ, āĻ¤āĻžāĻ āĻāĻŽāĻžāĻĻā§āĻ° āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻāĻāĻāĻŋ āĻāĻĒāĻŋ āĻĻāĻ°āĻāĻžāĻ° Game
("āĻā§āĻŽ") - āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻā§āĻ˛ā§āĻ¯āĻŧāĻžāĻĄāĻŧ āĻāĻāĻ āĻ
āĻā§āĻāĻ¨ā§ āĻā§āĻ˛ā§! āĻĒāĻ°āĻŦāĻ°ā§āĻ¤ā§ āĻŦāĻŋāĻāĻžāĻā§, āĻāĻŽāĻ°āĻž āĻĻā§āĻāĻŦ āĻāĻŋāĻāĻžāĻŦā§ āĻāĻ āĻā§āĻ˛āĻžāĻ¸ āĻāĻžāĻ āĻāĻ°ā§āĨ¤ Game
.
2. āĻā§āĻŽ āĻ¸āĻžāĻ°ā§āĻāĻžāĻ°
ĐĐģĐ°ŅŅ Game
āĻ¸āĻžāĻ°ā§āĻāĻžāĻ°ā§āĻ° āĻĻāĻŋāĻā§ āĻ¸āĻŦāĻā§āĻ¯āĻŧā§ āĻā§āĻ°ā§āĻ¤ā§āĻŦāĻĒā§āĻ°ā§āĻŖ āĻ¯ā§āĻā§āĻ¤āĻŋ āĻ°āĻ¯āĻŧā§āĻā§āĨ¤ āĻāĻ° āĻĻā§āĻāĻŋ āĻĒā§āĻ°āĻ§āĻžāĻ¨ āĻāĻžāĻ āĻ°āĻ¯āĻŧā§āĻā§: āĻā§āĻ˛ā§āĻ¯āĻŧāĻžāĻĄāĻŧ āĻŦā§āĻ¯āĻŦāĻ¸ā§āĻĨāĻžāĻĒāĻ¨āĻž и āĻā§āĻŽ āĻ¸āĻŋāĻŽā§āĻ˛ā§āĻļāĻ¨.
āĻĒā§āĻ°āĻĨāĻŽ āĻāĻžāĻ, āĻĒā§āĻ˛ā§āĻ¯āĻŧāĻžāĻ° āĻŽā§āĻ¯āĻžāĻ¨ā§āĻāĻŽā§āĻ¨ā§āĻ āĻĻāĻŋāĻ¯āĻŧā§ āĻļā§āĻ°ā§ āĻāĻ°āĻž āĻ¯āĻžāĻāĨ¤
game.js āĻĒāĻžāĻ°ā§āĻ 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);
}
}
// ...
}
āĻāĻ āĻā§āĻ˛āĻžāĻ¯āĻŧ, āĻāĻŽāĻ°āĻž āĻŽāĻžāĻ ā§āĻ° āĻĻā§āĻŦāĻžāĻ°āĻž āĻā§āĻ˛ā§āĻ¯āĻŧāĻžāĻĄāĻŧāĻĻā§āĻ° āĻāĻŋāĻšā§āĻ¨āĻŋāĻ¤ āĻāĻ°āĻŦ id
āĻ¤āĻžāĻĻā§āĻ° socket.io āĻ¸āĻā§āĻ (āĻ¯āĻĻāĻŋ āĻāĻĒāĻ¨āĻŋ āĻŦāĻŋāĻā§āĻ°āĻžāĻ¨ā§āĻ¤ āĻšāĻ¨, āĻ¤āĻžāĻšāĻ˛ā§ āĻĢāĻŋāĻ°ā§ āĻ¯āĻžāĻ¨ server.js
) Socket.io āĻ¨āĻŋāĻā§āĻ āĻĒā§āĻ°āĻ¤āĻŋāĻāĻŋ āĻ¸āĻā§āĻāĻā§ āĻāĻāĻāĻŋ āĻ
āĻ¨āĻ¨ā§āĻ¯ āĻŦāĻ°āĻžāĻĻā§āĻĻ āĻāĻ°ā§ id
āĻ¤āĻžāĻ āĻāĻŽāĻžāĻĻā§āĻ° āĻāĻāĻž āĻ¨āĻŋāĻ¯āĻŧā§ āĻāĻŋāĻ¨ā§āĻ¤āĻž āĻāĻ°āĻžāĻ° āĻĻāĻ°āĻāĻžāĻ° āĻ¨ā§āĻāĨ¤ āĻāĻŽāĻŋ āĻ¤āĻžāĻā§ āĻĄāĻžāĻāĻŦ āĻĒā§āĻ˛ā§āĻ¯āĻŧāĻžāĻ° āĻāĻāĻĄāĻŋ.
āĻāĻāĻŋ āĻŽāĻžāĻĨāĻžāĻ¯āĻŧ āĻ°ā§āĻā§, āĻāĻ¸ā§āĻ¨ āĻāĻāĻāĻŋ āĻā§āĻ˛āĻžāĻ¸ā§ āĻāĻ¨āĻ¸ā§āĻā§āĻ¯āĻžāĻ¨ā§āĻ¸ āĻā§āĻ°āĻŋāĻ¯āĻŧā§āĻŦāĻ˛āĻā§āĻ˛āĻŋ āĻ
āĻ¨ā§āĻŦā§āĻˇāĻŖ āĻāĻ°āĻŋ Game
:
sockets
āĻāĻāĻāĻŋ āĻŦāĻ¸ā§āĻ¤ā§ āĻ¯āĻž āĻĒā§āĻ˛ā§āĻ¯āĻŧāĻžāĻ° āĻāĻāĻĄāĻŋāĻā§ āĻ¸āĻā§āĻā§āĻ° āĻ¸āĻžāĻĨā§ āĻ¸āĻāĻ¯ā§āĻā§āĻ¤ āĻāĻ°ā§ āĻ¯āĻž āĻĒā§āĻ˛ā§āĻ¯āĻŧāĻžāĻ°ā§āĻ° āĻ¸āĻžāĻĨā§ āĻ¯ā§āĻā§āĻ¤āĨ¤ āĻāĻāĻŋ āĻāĻŽāĻžāĻĻā§āĻ° āĻāĻāĻāĻŋ āĻ§ā§āĻ°ā§āĻŦāĻ āĻ¸āĻŽāĻ¯āĻŧā§ āĻ¤āĻžāĻĻā§āĻ° āĻĒā§āĻ˛ā§āĻ¯āĻŧāĻžāĻ° āĻāĻāĻĄāĻŋ āĻĻā§āĻŦāĻžāĻ°āĻž āĻ¸āĻā§āĻ āĻ ā§āĻ¯āĻžāĻā§āĻ¸ā§āĻ¸ āĻāĻ°āĻžāĻ° āĻ āĻ¨ā§āĻŽāĻ¤āĻŋ āĻĻā§āĻ¯āĻŧāĨ¤players
āĻāĻāĻāĻŋ āĻ āĻŦāĻā§āĻā§āĻ āĻ¯āĻž āĻĒā§āĻ˛ā§āĻ¯āĻŧāĻžāĻ° āĻāĻāĻĄāĻŋāĻā§ āĻā§āĻĄ>āĻĒā§āĻ˛ā§āĻ¯āĻŧāĻžāĻ° āĻ āĻŦāĻā§āĻā§āĻā§ āĻāĻŦāĻĻā§āĻ§ āĻāĻ°ā§
bullets
āĻŦāĻ¸ā§āĻ¤ā§āĻ° āĻāĻāĻāĻŋ āĻ
ā§āĻ¯āĻžāĻ°ā§ Bullet
, āĻ¯āĻžāĻ° āĻā§āĻ¨ āĻ¨āĻŋāĻ°ā§āĻĻāĻŋāĻˇā§āĻ āĻāĻĻā§āĻļ āĻ¨ā§āĻāĨ¤
lastUpdateTime
āĻā§āĻŽāĻāĻŋ āĻāĻĒāĻĄā§āĻ āĻāĻ°āĻž āĻļā§āĻˇ āĻ¸āĻŽāĻ¯āĻŧā§āĻ° āĻāĻžāĻāĻŽāĻ¸ā§āĻā§āĻ¯āĻžāĻŽā§āĻĒāĨ¤ āĻāĻŽāĻ°āĻž āĻļā§āĻā§āĻ°āĻ āĻāĻāĻŋ āĻāĻŋāĻāĻžāĻŦā§ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻāĻ°āĻž āĻšāĻ¯āĻŧ āĻĻā§āĻāĻ¤ā§ āĻšāĻŦā§.
shouldSendUpdate
āĻāĻāĻāĻŋ āĻ¸āĻšāĻžāĻ¯āĻŧāĻ āĻĒāĻ°āĻŋāĻŦāĻ°ā§āĻ¤āĻ¨āĻļā§āĻ˛āĨ¤ āĻāĻŽāĻ°āĻž āĻā§āĻŦ āĻļā§āĻā§āĻ°āĻ āĻāĻ° āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻ āĻĻā§āĻāĻ¤ā§ āĻĒāĻžāĻŦāĨ¤
āĻĒāĻĻā§āĻ§āĻ¤āĻŋ addPlayer()
, removePlayer()
и handleInput()
āĻŦā§āĻ¯āĻžāĻā§āĻ¯āĻž āĻāĻ°āĻžāĻ° āĻĒā§āĻ°āĻ¯āĻŧā§āĻāĻ¨ āĻ¨ā§āĻ, āĻ¤āĻžāĻ°āĻž āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻāĻ°āĻž āĻšāĻ¯āĻŧ server.js
. āĻāĻĒāĻ¨āĻŋ āĻ¯āĻĻāĻŋ āĻāĻĒāĻ¨āĻžāĻ° āĻ¸ā§āĻŽā§āĻ¤āĻŋ āĻ°āĻŋāĻĢā§āĻ°ā§āĻļ āĻāĻ°āĻ¤ā§ āĻāĻžāĻ¨ āĻ¤āĻŦā§ āĻāĻāĻā§ āĻāĻāĻā§āĻ¤ā§ āĻĢāĻŋāĻ°ā§ āĻ¯āĻžāĻ¨āĨ¤
āĻļā§āĻˇ āĻ˛āĻžāĻāĻ¨ constructor()
āĻļā§āĻ°ā§ āĻāĻ°āĻž āĻāĻĒāĻĄā§āĻ āĻāĻā§āĻ° āĻā§āĻŽāĻ¸ (60 āĻāĻĒāĻĄā§āĻ / āĻ¸ā§āĻā§āĻ¨ā§āĻĄā§āĻ° āĻĢā§āĻ°āĻŋāĻā§āĻ¯āĻŧā§āĻ¨ā§āĻ¸āĻŋ āĻ¸āĻš):
game.js āĻĒāĻžāĻ°ā§āĻ 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;
}
}
// ...
}
āĻĒāĻĻā§āĻ§āĻ¤āĻŋ update()
āĻ¸āĻŽā§āĻāĻŦāĻ¤ āĻ¸āĻžāĻ°ā§āĻāĻžāĻ°-āĻ¸āĻžāĻāĻĄ āĻ˛āĻāĻŋāĻā§āĻ° āĻ¸āĻŦāĻā§āĻ¯āĻŧā§ āĻā§āĻ°ā§āĻ¤ā§āĻŦāĻĒā§āĻ°ā§āĻŖ āĻ
āĻāĻļ āĻ°āĻ¯āĻŧā§āĻā§āĨ¤ āĻā§āĻ°āĻŽāĻžāĻ¨ā§āĻ¸āĻžāĻ°ā§ āĻāĻāĻŋ āĻ¯āĻž āĻāĻ°ā§ āĻ¤āĻž āĻāĻāĻžāĻ¨ā§:
- āĻāĻ¤āĻā§āĻˇāĻŖ āĻšāĻŋāĻ¸āĻžāĻŦ āĻāĻ°ā§
dt
āĻļā§āĻˇ āĻĨā§āĻā§ āĻĒāĻžāĻ¸update()
. - āĻĒā§āĻ°āĻ¤āĻŋāĻāĻŋ āĻĒā§āĻ°āĻā§āĻā§āĻāĻžāĻāĻ˛ āĻ°āĻŋāĻĢā§āĻ°ā§āĻļ āĻāĻ°ā§ āĻāĻŦāĻ āĻĒā§āĻ°āĻ¯āĻŧā§āĻāĻ¨ā§ āĻ¤āĻžāĻĻā§āĻ° āĻ§ā§āĻŦāĻāĻ¸ āĻāĻ°ā§āĨ¤ āĻāĻŽāĻ°āĻž āĻĒāĻ°ā§ āĻāĻ āĻāĻžāĻ°ā§āĻ¯āĻāĻžāĻ°āĻŋāĻ¤āĻž āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻĻā§āĻāĻ¤ā§ āĻšāĻŦā§. āĻāĻĒāĻžāĻ¤āĻ¤ āĻāĻŽāĻžāĻĻā§āĻ° āĻāĻ¨ā§āĻ¯ āĻ¸ā§āĻāĻž āĻāĻžāĻ¨āĻžāĻ āĻ¯āĻĨā§āĻˇā§āĻ
bullet.update()
āĻĒā§āĻ°āĻ¤ā§āĻ¯āĻžāĻŦāĻ°ā§āĻ¤āĻ¨true
āĻ¯āĻĻāĻŋ āĻĒā§āĻ°āĻā§āĻˇāĻŋāĻĒā§āĻ¤ āĻ§ā§āĻŦāĻāĻ¸ āĻāĻ°āĻž āĻāĻāĻŋāĻ¤ (āĻ¤āĻŋāĻ¨āĻŋ āĻŽāĻ¯āĻŧāĻĻāĻžāĻ¨ āĻĨā§āĻā§ āĻŦā§āĻ°āĻŋāĻ¯āĻŧā§ āĻāĻ¸ā§āĻ¨)āĨ¤ - āĻĒā§āĻ°āĻ¤āĻŋāĻāĻŋ āĻā§āĻ˛ā§āĻ¯āĻŧāĻžāĻĄāĻŧāĻā§ āĻāĻĒāĻĄā§āĻ āĻāĻ°ā§ āĻāĻŦāĻ āĻĒā§āĻ°āĻ¯āĻŧā§āĻāĻ¨ā§ āĻāĻāĻāĻŋ āĻĒā§āĻ°āĻā§āĻā§āĻāĻžāĻāĻ˛ āĻ¤ā§āĻ°āĻŋ āĻāĻ°ā§āĨ¤ āĻāĻŽāĻ°āĻž āĻĒāĻ°ā§ āĻāĻ āĻŦāĻžāĻ¸ā§āĻ¤āĻŦāĻžāĻ¯āĻŧāĻ¨ āĻĻā§āĻāĻ¤ā§ āĻĒāĻžāĻŦ-
player.update()
āĻāĻāĻāĻŋ āĻŦāĻ¸ā§āĻ¤ā§ āĻĢā§āĻ°āĻ¤ āĻĻāĻŋāĻ¤ā§ āĻĒāĻžāĻ°ā§āĻ¨Bullet
. - āĻĒā§āĻ°āĻā§āĻā§āĻāĻžāĻāĻ˛ āĻāĻŦāĻ āĻĒā§āĻ˛ā§āĻ¯āĻŧāĻžāĻ°āĻĻā§āĻ° āĻŽāĻ§ā§āĻ¯ā§ āĻ¸āĻāĻāĻ°ā§āĻˇā§āĻ° āĻāĻ¨ā§āĻ¯ āĻĒāĻ°ā§āĻā§āĻˇāĻž āĻāĻ°ā§
applyCollisions()
, āĻ¯āĻž āĻā§āĻ˛ā§āĻ¯āĻŧāĻžāĻĄāĻŧāĻĻā§āĻ° āĻāĻāĻžāĻ¤āĻāĻžāĻ°ā§ āĻĒā§āĻ°āĻā§āĻā§āĻāĻžāĻāĻ˛ā§āĻ° āĻāĻāĻāĻŋ āĻ ā§āĻ¯āĻžāĻ°ā§ āĻĒā§āĻ°āĻĻāĻžāĻ¨ āĻāĻ°ā§āĨ¤ āĻĒā§āĻ°āĻ¤ā§āĻ¯āĻžāĻŦāĻ°ā§āĻ¤āĻŋāĻ¤ āĻĒā§āĻ°āĻ¤āĻŋāĻāĻŋ āĻĒā§āĻ°āĻā§āĻā§āĻāĻžāĻāĻ˛ā§āĻ° āĻāĻ¨ā§āĻ¯, āĻ¯ā§ āĻĒā§āĻ˛ā§āĻ¯āĻŧāĻžāĻ°āĻāĻŋ āĻāĻāĻŋāĻā§ āĻā§āĻ˛āĻŋ āĻāĻ°ā§āĻā§ āĻāĻŽāĻ°āĻž āĻ¤āĻžāĻ° āĻĒāĻ¯āĻŧā§āĻ¨ā§āĻ āĻŦāĻžāĻĄāĻŧāĻžāĻ (āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻāĻ°ā§player.onDealtDamage()
) āĻāĻŦāĻ āĻ¤āĻžāĻ°āĻĒāĻ° āĻ ā§āĻ¯āĻžāĻ°ā§ āĻĨā§āĻā§ āĻĒā§āĻ°āĻā§āĻā§āĻāĻžāĻāĻ˛āĻāĻŋ āĻ¸āĻ°āĻžāĻ¨bullets
. - āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻ¨āĻŋāĻšāĻ¤ āĻā§āĻ˛ā§āĻ¯āĻŧāĻžāĻĄāĻŧāĻā§ āĻ āĻŦāĻšāĻŋāĻ¤ āĻāĻ°ā§ āĻāĻŦāĻ āĻ§ā§āĻŦāĻāĻ¸ āĻāĻ°ā§āĨ¤
- āĻ¸āĻŽāĻ¸ā§āĻ¤ āĻā§āĻ˛ā§āĻ¯āĻŧāĻžāĻĄāĻŧāĻĻā§āĻ° āĻāĻāĻāĻŋ āĻā§āĻŽ āĻāĻĒāĻĄā§āĻ āĻĒāĻžāĻ āĻžāĻ¯āĻŧ āĻĒā§āĻ°āĻ¤āĻŋ āĻŽā§āĻšā§āĻ°ā§āĻ¤ āĻŦāĻžāĻ° āĻ¯āĻāĻ¨ āĻĄāĻžāĻāĻž āĻšāĻ¯āĻŧ
update()
. āĻāĻāĻŋ āĻāĻŽāĻžāĻĻā§āĻ° āĻāĻĒāĻ°ā§ āĻāĻ˛ā§āĻ˛āĻŋāĻāĻŋāĻ¤ āĻ āĻā§āĻāĻŋāĻ˛āĻŋāĻ¯āĻŧāĻžāĻ°ā§ āĻā§āĻ°āĻŋāĻ¯āĻŧā§āĻŦāĻ˛ā§āĻ° āĻā§āĻ°ā§āĻ¯āĻžāĻ āĻ°āĻžāĻāĻ¤ā§ āĻ¸āĻžāĻšāĻžāĻ¯ā§āĻ¯ āĻāĻ°ā§āĨ¤shouldSendUpdate
. āĻšāĻŋāĻ¸āĻžāĻŦā§update()
60 āĻŦāĻžāĻ°/āĻ¸ā§āĻā§āĻ¨ā§āĻĄ āĻŦāĻ˛āĻž āĻšāĻ¯āĻŧ, āĻāĻŽāĻ°āĻž 30 āĻŦāĻžāĻ°/āĻ¸ā§āĻā§āĻ¨ā§āĻĄ āĻā§āĻŽ āĻāĻĒāĻĄā§āĻ āĻĒāĻžāĻ āĻžāĻāĨ¤ āĻāĻāĻāĻžāĻŦā§, āĻāĻĄāĻŧāĻŋ āĻĢā§āĻ°āĻŋāĻā§āĻ¯āĻŧā§āĻ¨ā§āĻ¸āĻŋ āĻ¸āĻžāĻ°ā§āĻāĻžāĻ° āĻāĻĄāĻŧāĻŋ āĻšāĻ˛ 30 āĻāĻĄāĻŧāĻŋ/āĻ¸ā§ (āĻāĻŽāĻ°āĻž āĻĒā§āĻ°āĻĨāĻŽ āĻ āĻāĻļā§ āĻāĻĄāĻŧāĻŋāĻ° āĻšāĻžāĻ° āĻ¸āĻŽā§āĻĒāĻ°ā§āĻā§ āĻāĻĨāĻž āĻŦāĻ˛ā§āĻāĻŋ)āĨ¤
āĻā§āĻ¨ āĻļā§āĻ§ā§āĻŽāĻžāĻ¤ā§āĻ° āĻā§āĻŽ āĻāĻĒāĻĄā§āĻ āĻĒāĻžāĻ āĻžāĻ¨ āĻ¸āĻŽāĻ¯āĻŧ āĻŽāĻžāĻ§ā§āĻ¯āĻŽā§ ? āĻā§āĻ¯āĻžāĻ¨ā§āĻ˛ āĻ¸āĻāĻ°āĻā§āĻˇāĻŖ āĻāĻ°āĻ¤ā§āĨ¤ āĻĒā§āĻ°āĻ¤āĻŋ āĻ¸ā§āĻā§āĻ¨ā§āĻĄā§ ā§Šā§ĻāĻāĻŋ āĻā§āĻŽ āĻāĻĒāĻĄā§āĻ āĻ āĻ¨ā§āĻ!
āĻļā§āĻ§ā§ āĻĄāĻžāĻāĻŦā§ āĻ¨āĻž āĻā§āĻ¨
update()
āĻĒā§āĻ°āĻ¤āĻŋ āĻ¸ā§āĻā§āĻ¨ā§āĻĄā§ ā§Šā§Ļ āĻŦāĻžāĻ°? āĻā§āĻŽ āĻ¸āĻŋāĻŽā§āĻ˛ā§āĻļāĻ¨ āĻāĻ¨ā§āĻ¨āĻ¤ āĻāĻ°āĻ¤ā§. āĻāĻ°ā§ āĻĒā§āĻ°āĻžāĻ¯āĻŧāĻ āĻŦāĻ˛āĻž āĻšāĻ¯āĻŧupdate()
, āĻā§āĻŽ āĻ¸āĻŋāĻŽā§āĻ˛ā§āĻļāĻ¨ āĻāĻ°ā§ āĻ¨āĻŋāĻ°ā§āĻā§āĻ˛ āĻšāĻŦā§. āĻ¤āĻŦā§ āĻā§āĻ¯āĻžāĻ˛ā§āĻā§āĻā§āĻ° āĻ¸āĻāĻā§āĻ¯āĻž āĻ¨āĻŋāĻ¯āĻŧā§ āĻā§āĻŦ āĻŦā§āĻļāĻŋ āĻĻā§āĻ°ā§ āĻāĻ˛ā§ āĻ¯āĻžāĻŦā§āĻ¨ āĻ¨āĻžāĨ¤update()
, āĻāĻžāĻ°āĻŖ āĻāĻāĻŋ āĻāĻāĻāĻŋ āĻāĻŖāĻ¨āĻžāĻāĻ¤āĻāĻžāĻŦā§ āĻŦā§āĻ¯āĻ¯āĻŧāĻŦāĻšā§āĻ˛ āĻāĻžāĻ - āĻĒā§āĻ°āĻ¤āĻŋ āĻ¸ā§āĻā§āĻ¨ā§āĻĄā§ 60 āĻ¯āĻĨā§āĻˇā§āĻāĨ¤
āĻŦāĻžāĻāĻŋ āĻā§āĻ˛āĻžāĻ¸ Game
āĻ¸āĻžāĻšāĻžāĻ¯ā§āĻ¯āĻāĻžāĻ°ā§ āĻĒāĻĻā§āĻ§āĻ¤āĻŋ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ° āĻāĻ°āĻž āĻšāĻ¯āĻŧ update()
:
game.js āĻĒāĻžāĻ°ā§āĻ 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()
āĻŦā§āĻļ āĻ¸āĻšāĻ - āĻāĻāĻŋ āĻā§āĻ˛ā§āĻ¯āĻŧāĻžāĻĄāĻŧāĻĻā§āĻ° āĻ¸ā§āĻā§āĻ° āĻ
āĻ¨ā§āĻ¸āĻžāĻ°ā§ āĻ¸āĻžāĻāĻžāĻ¯āĻŧ, āĻļā§āĻ°ā§āĻˇ āĻĒāĻžāĻāĻāĻāĻŋ āĻ¨ā§āĻ¯āĻŧ āĻāĻŦāĻ āĻĒā§āĻ°āĻ¤āĻŋāĻāĻŋāĻ° āĻāĻ¨ā§āĻ¯ āĻŦā§āĻ¯āĻŦāĻšāĻžāĻ°āĻāĻžāĻ°ā§āĻ° āĻ¨āĻžāĻŽ āĻāĻŦāĻ āĻ¸ā§āĻā§āĻ° āĻĒā§āĻ°āĻĻāĻžāĻ¨ āĻāĻ°ā§āĨ¤
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