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 เชซเชพเชเชฒเชฅเซ เชถเชฐเซ เชเชฐเซเชจเซ Javascript เชเซเชกเชจเซ เช
เชฎเชฒ เชเชฐเชตเชพเชจเซเช เชถเชฐเซ เชเชฐเชถเซ: 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 เชเชฏเชพเชค (เชเซเชฅเซ Webpack เชคเซเชฎเชจเซ เช เชฎเชพเชฐเชพ 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 เชฅเซ เชตเชงเซ เชจเชนเซเช เชเชพเชฏ, เชเชพเชฐเชฃ เชเซ เช
เชฎเชจเซ เชธเชฐเซเชตเชฐ เชคเชฐเชซเชฅเซ เชชเซเชฐเชคเชฟ เชธเซเชเชจเซเชก 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-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
เชเช เชเชฌเซเชเซเชเซเช เชเซ เชเซ เชชเซเชฒเซเชฏเชฐ ID เชจเซ เชชเซเชฒเซเชฏเชฐ เชธเชพเชฅเซ เชธเชเชเชณเชพเชฏเซเชฒ เชธเซเชเซเช เชธเชพเชฅเซ เชเซเชกเซ เชเซ. เชคเซ เช เชฎเชจเซ เชธเชคเชค เชธเชฎเชฏเชฎเชพเช เชคเซเชฎเชจเชพ เชชเซเชฒเซเชฏเชฐ ID เชฆเซเชตเชพเชฐเชพ เชธเซเชเซเชเซเชธ เชเชเซเชธเซเชธ เชเชฐเชตเชพเชจเซ เชฎเชเชเซเชฐเซ เชเชชเซ เชเซ.players
เชเช เชเชฌเซเชเซเชเซเช เชเซ เชเซ เชชเซเชฒเซเชฏเชฐ ID เชจเซ เชเซเชก>เชชเซเชฒเซเชฏเชฐ เชเชฌเซเชเซเชเซเช เชธเชพเชฅเซ เชเซเชกเซ เชเซ
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 เชเชกเชฟเชฏเชพเชณเซ/เชธเซเชเชเชก เชเซ (เช เชฎเซ เชชเชนเซเชฒเชพ เชญเชพเชเชฎเชพเช เชเชกเชฟเชฏเชพเชณเชจเชพ เชฆเชฐเซ เชตเชฟเชถเซ เชตเชพเชค เชเชฐเซ เชนเชคเซ).
เชถเชพ เชฎเชพเชเซ เชซเชเซเชค เชฐเชฎเชค เช เชชเชกเซเชเซเชธ เชฎเซเชเชฒเซ เชธเชฎเชฏ เชฆเซเชตเชพเชฐเชพ ? เชเซเชจเชฒ เชธเชพเชเชตเชตเชพ เชฎเชพเชเซ. เชธเซเชเชจเซเชก เชฆเซเช 30 เชฐเชฎเชค เช เชชเชกเซเชเซเชธ เชเชฃเซเช เชเซ!
เชถเชพ เชฎเชพเชเซ เชฎเชพเชคเซเชฐ เชซเซเชจ เชจเชฅเซ
update()
เชธเซเชเชจเซเชก เชฆเซเช 30 เชตเชเชค? เชฐเชฎเชค เชธเชฟเชฎเซเชฏเซเชฒเซเชถเชจ เชธเซเชงเชพเชฐเชตเชพ เชฎเชพเชเซ. เชตเชงเซ เชตเชเชค เชเชนเซเชตเชพเชฏ เชเซ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