เบเบฒเบ™เบชเป‰เบฒเบ‡เป€เบเบกเป€เบงเบฑเบšเบ—เบตเปˆเบกเบตเบœเบนเป‰เบซเบผเบดเป‰เบ™เบซเบผเบฒเบเบ„เบปเบ™เปƒเบ™เบ›เบฐเป€เบžเบ” .io

เบเบฒเบ™เบชเป‰เบฒเบ‡เป€เบเบกเป€เบงเบฑเบšเบ—เบตเปˆเบกเบตเบœเบนเป‰เบซเบผเบดเป‰เบ™เบซเบผเบฒเบเบ„เบปเบ™เปƒเบ™เบ›เบฐเป€เบžเบ” .io
เบ›เปˆเบญเบเบญเบญเบเบกเบฒเป€เบกเบทเปˆเบญเบ›เบต 2015 Agario เป„เบ”เป‰เบเบฒเบเป€เบ›เบฑเบ™ progenitor เบ‚เบญเบ‡เบ›เบฐเป€เบžเบ”เปƒเบซเบกเปˆ games.io, เบ„เบงเบฒเบกเบ™เบดเบเบปเบกเบ‚เบญเบ‡เป€เบ‚เบปเบฒเป€เบˆเบปเป‰เบฒเป„เบ”เป‰เป€เบžเบตเปˆเบกเบ‚เบถเป‰เบ™เบขเปˆเบฒเบ‡เบซเบผเบงเบ‡เบซเบผเบฒเบเบ™เบฑเบšเบ•เบฑเป‰เบ‡เปเบ•เปˆเบ™เบฑเป‰เบ™เบกเบฒ. เบ‚เป‰เบฒเบžเบฐเป€เบˆเบปเป‰เบฒเป„เบ”เป‰เบ›เบฐเบชเบปเบšเบเบฑเบšเบเบฒเบ™เป€เบžเบตเปˆเบกเบ‚เบถเป‰เบ™เบ‚เบญเบ‡เบ„เบงเบฒเบกเบ™เบดเบเบปเบกเบ‚เบญเบ‡เป€เบเบก .io เบ”เป‰เบงเบเบ•เบปเบ™เป€เบญเบ‡: เปƒเบ™เป„เบฅเบเบฐเบชเบฒเบกเบ›เบตเบ—เบตเปˆเบœเปˆเบฒเบ™เบกเบฒ, เบ‚เป‰เบฒเบžเบฐเป€เบˆเบปเป‰เบฒ เบชเป‰เบฒเบ‡เปเบฅเบฐเบ‚เบฒเบเบชเบญเบ‡เป€เบเบกเปƒเบ™เบ›เบฐเป€เบžเบ”เบ™เบตเป‰..

เปƒเบ™โ€‹เบเปโ€‹เบฅเบฐโ€‹เบ™เบตโ€‹เบ—เบตเปˆโ€‹เบ—เปˆเบฒเบ™โ€‹เบšเปเปˆโ€‹เป€เบ„เบตเบโ€‹เป„เบ”เป‰โ€‹เบเบดเบ™โ€‹เบ‚เบญเบ‡โ€‹เป€เบเบกโ€‹เป€เบซเบผเบปเปˆเบฒโ€‹เบ™เบตเป‰โ€‹เบกเบฒโ€‹เบเปˆเบญเบ™โ€‹, เบžเบงเบโ€‹เป€เบ‚เบปเบฒโ€‹เป€เบˆเบปเป‰เบฒโ€‹เปเบกเปˆเบ™โ€‹เบŸเบฃเบตโ€‹, เป€เบเบกโ€‹เบซเบผเบฒเบโ€‹เป€เบงเบฑเบšโ€‹เบ—เบตเปˆโ€‹เบ‡เปˆเบฒเบโ€‹เบ—เบตเปˆโ€‹เบˆเบฐโ€‹เบซเบผเบดเป‰เบ™ (เบšเปเปˆโ€‹เบ•เป‰เบญเบ‡โ€‹เบเบฒเบ™โ€‹เบšเบฑเบ™โ€‹เบŠเบตโ€‹)โ€‹. เบ›เบปเบเบเบฐเบ•เบดเปเบฅเป‰เบง เบžเบงเบเป€เบ‚เบปเบฒเบ•เบตเบœเบนเป‰เบซเบผเบดเป‰เบ™เบเปˆเบฒเบเบ„เป‰เบฒเบ™เบซเบผเบฒเบเบ„เบปเบ™เบขเบนเปˆเปƒเบ™เบชเบฐเบซเบ™เบฒเบกเบเบดเบฅเบฒเบ”เบฝเบง. เป€เบเบก .io เบ—เบตเปˆเบกเบตเบŠเบทเปˆเบชเบฝเบ‡เบญเบทเปˆเบ™เป†: Slither.io ะธ Diep.io.

เปƒเบ™เบšเบปเบ”เบ‚เบฝเบ™เบ™เบตเป‰, เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเบžเบดเบˆเบฒเบฅเบฐเบ™เบฒเบงเบดเบ—เบตเบเบฒเบ™ เบชเป‰เบฒเบ‡เป€เบเบก .io เบ•เบฑเป‰เบ‡เปเบ•เปˆเป€เบฅเบตเปˆเบกเบ•เบปเป‰เบ™. เป€เบžเบทเปˆเบญเป€เบฎเบฑเบ”เบชเบดเปˆเบ‡เบ™เบตเป‰, เบžเบฝเบ‡เปเบ•เปˆเบ„เบงเบฒเบกเบฎเบนเป‰เบ‚เบญเบ‡ Javascript เบˆเบฐเบžเบฝเบ‡เบžเป: เบ—เปˆเบฒเบ™เบˆเปเบฒเป€เบ›เบฑเบ™เบ•เป‰เบญเบ‡เป€เบ‚เบปเป‰เบฒเปƒเบˆเบชเบดเปˆเบ‡เบ•เปˆเบฒเบ‡เป†เป€เบŠเบฑเปˆเบ™ syntax ES6, เบ„เปเบฒเบชเปเบฒเบ„เบฑเบ™ this ะธ เบ„เบณ เปเบฑเป‰เบ™ เบชเบฑเบ™เบเบฒ. เป€เบ–เบดเบ‡เปเบกเปˆเบ™เบงเปˆเบฒเบ—เปˆเบฒเบ™เบšเปเปˆเบฎเบนเป‰ Javascript เบขเปˆเบฒเบ‡เบชเบปเบกเบšเบนเบ™, เบ—เปˆเบฒเบ™เบเบฑเบ‡เบชเบฒเบกเบฒเบ”เป€เบ‚เบปเป‰เบฒเปƒเบˆเบ‚เปเป‰เบ„เบงเบฒเบกเบชเปˆเบงเบ™เปƒเบซเบเปˆ.

เบ•เบปเบงเบขเปˆเบฒเบ‡เบ‚เบญเบ‡เป€เบเบก .io

เบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เบŠเปˆเบงเบเป€เบซเบผเบทเบญเบเบฒเบ™เบเบถเบเบญเบปเบšเบฎเบปเบกเบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเบญเป‰เบฒเบ‡เป€เบ–เบดเบ‡ เบ•เบปเบงเบขเปˆเบฒเบ‡เป€เบเบก .io. เบฅเบญเบ‡เบซเบผเบดเป‰เบ™เป€เบšเบดเปˆเบ‡!

เบเบฒเบ™เบชเป‰เบฒเบ‡เป€เบเบกเป€เบงเบฑเบšเบ—เบตเปˆเบกเบตเบœเบนเป‰เบซเบผเบดเป‰เบ™เบซเบผเบฒเบเบ„เบปเบ™เปƒเบ™เบ›เบฐเป€เบžเบ” .io
เป€เบเบกเปเบกเปˆเบ™เบ‚เป‰เบญเบ™เบ‚เป‰เบฒเบ‡เบ‡เปˆเบฒเบเบ”เบฒเบ: เบ—เปˆเบฒเบ™เบ„เบงเบšเบ„เบธเบกเป€เบฎเบทเบญเปƒเบ™เบ™เบฒเบกเบซเบฅเบดเป‰เบ™เบเบดเบฅเบฒเบเบฑเบšเบœเบนเป‰เบ™เบญเบทเปˆเบ™เป†. เป€เบฎเบทเบญเบ‚เบญเบ‡เป€เบˆเบปเป‰เบฒเบˆเบฐเบเบดเบ‡เบฅเบนเบเบ›เบทเบ™เบญเบฑเบ”เบ•เบฐเป‚เบ™เบกเบฑเบ” เปเบฅเบฐเป€เบˆเบปเป‰เบฒเบžเบฐเบเบฒเบเบฒเบกเบ•เบตเป€เบ„เบทเปˆเบญเบ‡เบซเบผเบดเป‰เบ™เบญเบทเปˆเบ™เปƒเบ™เบ‚เบฐเบ™เบฐเบ—เบตเปˆเบซเบผเบตเบเบฅเปˆเบฝเบ‡เบฅเบนเบเบ›เบทเบ™เบ‚เบญเบ‡เป€เบ‚เบปเบฒเป€เบˆเบปเป‰เบฒ.

1. เบžเบฒเบšเบฅเบงเบกเป‚เบ”เบเบซเบเปเป‰/เป‚เบ„เบ‡เบชเป‰เบฒเบ‡เป‚เบ„เบ‡เบเบฒเบ™

เบ‚เป‰เบฒเบžเบฐเป€เบˆเบปเป‰เบฒเปเบ™เบฐเบ™เปเบฒเปƒเบซเป‰ เบ”เบฒเบงเป‚เบซเบฅเบ”เบฅเบฐเบซเบฑเบ”เปเบซเบผเปˆเบ‡ เป€เบเบกเบ•เบปเบงเบขเปˆเบฒเบ‡เบ”เบฑเปˆเบ‡เบ™เบฑเป‰เบ™เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เบ›เบฐเบ•เบดเบšเบฑเบ”เบ•เบฒเบกเบ‚เป‰เบฒเบžเบฐเป€เบˆเบปเป‰เบฒ.

เบ•เบปเบงเบขเปˆเบฒเบ‡เปƒเบŠเป‰เบ”เบฑเปˆเบ‡เบ•เปเปˆเป„เบ›เบ™เบตเป‰:

  • เบชเบฐเปเบ”เบ‡ เปเบกเปˆเบ™เบเบญเบšเป€เบงเบฑเบšเบ—เบตเปˆเบ™เบดเบเบปเบกเบ—เบตเปˆเบชเบธเบ”เบชเปเบฒเบฅเบฑเบš Node.js เบ—เบตเปˆเบˆเบฑเบ”เบเบฒเบ™เป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบเป€เบงเบฑเบšเบ‚เบญเบ‡เป€เบเบก.
  • socket.io โ€” เบซเป‰เบญเบ‡เบชเบฐเปเบธเบ” websocket เบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เปเบฅเบเบ›เปˆเบฝเบ™เบ‚เปเป‰เบกเบนเบ™เบฅเบฐเบซเบงเปˆเบฒเบ‡ browser เปเบฅเบฐ server.
  • Webpack - เบœเบนเป‰เบˆเบฑเบ”เบเบฒเบ™เป‚เบกเบ”เบนเบ™. เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เบญเปˆเบฒเบ™เบงเปˆเบฒเป€เบ›เบฑเบ™เบซเบเบฑเบ‡เบ•เป‰เบญเบ‡เปƒเบŠเป‰ Webpack เบ—เบตเปˆเบ™เบตเป‰.

เบ™เบตเป‰เปเบกเปˆเบ™เบชเบดเปˆเบ‡เบ—เบตเปˆเป‚เบ„เบ‡เบชเป‰เบฒเบ‡เป„เบ”เป€เบฅเบเบฐเบ—เปเบฅเบตเบ‚เบญเบ‡เป‚เบ„เบ‡เบเบฒเบ™เป€เบšเบดเปˆเบ‡เบ„เบทเบงเปˆเบฒ:

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. เบกเบฒเป€เบšเบดเปˆเบ‡เบเบฒเบ™เบ•เบฑเป‰เบ‡เบ„เปˆเบฒ Webpack เบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒ:

webpack.common.js:

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  entry: {
    game: './src/client/index.js',
  },
  output: {
    filename: '[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ['@babel/preset-env'],
          },
        },
      },
      {
        test: /.css$/,
        use: [
          {
            loader: MiniCssExtractPlugin.loader,
          },
          'css-loader',
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
    }),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'src/client/html/index.html',
    }),
  ],
};

เบชเบฒเบโ€‹เบ—เบตเปˆโ€‹เบชเปเบฒโ€‹เบ„เบฑเบ™โ€‹เบ—เบตเปˆโ€‹เบชเบธเบ”โ€‹เปเบกเปˆเบ™โ€‹เบ”เบฑเปˆเบ‡โ€‹เบ•เปเปˆโ€‹เป„เบ›โ€‹เบ™เบตเป‰โ€‹:

  • src/client/index.js เปเบกเปˆเบ™เบˆเบธเบ”เป€เบ‚เบปเป‰เบฒเบ‚เบญเบ‡เบฅเบนเบเบ„เป‰เบฒ Javascript (JS). Webpack เบˆเบฐเป€เบฅเบตเปˆเบกเบˆเบฒเบเบšเปˆเบญเบ™เบ™เบตเป‰ เปเบฅเบฐเบŠเบญเบเบซเบฒเป„เบŸเบฅเปŒเบ—เบตเปˆเบ™เบณเป€เบ‚เบปเป‰เบฒเบญเบทเปˆเบ™เบŠเป‰เบณเป†.
  • เบœเบปเบ™เบœเบฐเบฅเบดเบ” JS เบ‚เบญเบ‡ Webpack build เบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเบ•เบฑเป‰เบ‡เบขเบนเปˆเปƒเบ™เป„เบ”เป€เบฅเบเบฐเบ—เปเบฅเบต dist/. เบ‚เป‰เบญเบเบˆเบฐเป‚เบ—เบซเบฒเป„เบŸเบฅเปŒเบ™เบตเป‰เบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒ เบŠเบธเบ” JS.
  • เบžเบงเบเป€เบฎเบปเบฒเปƒเบŠเป‰ Babel, เปเบฅเบฐเป‚เบ”เบเบชเบฐเป€เบžเบฒเบฐเบเบฒเบ™เบ•เบฑเป‰เบ‡เบ„เปˆเบฒ @babel/preset-env เป€เบžเบทเปˆเบญเบ–เบญเบ”เบฅเบฐเบซเบฑเบ” JS เบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒเบชเปเบฒเบฅเบฑเบšเบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ—เบตเปˆเป€เบเบปเปˆเบฒเบเบงเปˆเบฒ.
  • เบžเบงเบเป€เบฎเบปเบฒเปƒเบŠเป‰ plugin เป€เบžเบทเปˆเบญเบชเบฐเบเบฑเบ” CSS เบ—เบฑเบ‡เบซเบกเบปเบ”เบ—เบตเปˆเบญเป‰เบฒเบ‡เบญเบดเบ‡เป‚เบ”เบเป„เบŸเบฅเปŒ JS เปเบฅเบฐเบฅเบงเบกเบžเบงเบเบกเบฑเบ™เป€เบ‚เบปเป‰เบฒเป„เบ›เปƒเบ™เบšเปˆเบญเบ™เบ”เบฝเบง. เบ‚เป‰เบฒเบžเบฐเป€เบˆเบปเป‰เบฒเบˆเบฐเป‚เบ—เบซเบฒเบกเบฑเบ™เบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒ เบŠเบธเบ” CSS.

เบ—เปˆเบฒเบ™เบญเบฒเบ”เบˆเบฐเป„เบ”เป‰เบชเบฑเบ‡เป€เบเบ”เป€เบซเบฑเบ™เบŠเบทเปˆเป„เบŸเบฅเปŒเปเบžเบฑเบเป€เบเบฑเบ”เบ—เบตเปˆเปเบ›เบเบ›เบฐเบซเบผเบฒเบ” '[name].[contenthash].ext'. เบžเบงเบเป€เบ‚เบปเบฒเป€เบˆเบปเป‰เบฒเบ›เบฐเบเบญเบšเบ”เป‰เบงเบ เบเบฒเบ™เบ›เปˆเบฝเบ™เบŠเบทเปˆเป„เบŸเบฅเปŒ Webpack: [name] เบˆเบฐเบ–เบทเบเปเบ—เบ™เบ—เบตเปˆเบ”เป‰เบงเบเบŠเบทเปˆเบ‚เบญเบ‡เบˆเบธเบ”เบ›เป‰เบญเบ™เบ‚เปเป‰เบกเบนเบ™ (เปƒเบ™เบเปเบฅเบฐเบ™เบตเบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒเบกเบฑเบ™เปเบกเปˆเบ™ game), เบ [contenthash] เบˆเบฐเบ–เบทเบเปเบ—เบ™เบ—เบตเปˆเบ”เป‰เบงเบ hash เบ‚เบญเบ‡เป€เบ™เบทเป‰เบญเปƒเบ™เป„เบŸเบฅเปŒ. เบžเบงเบเป€เบฎเบปเบฒเป€เบฎเบฑเบ”เบชเบดเปˆเบ‡เบ™เบตเป‰เป€เบžเบทเปˆเบญ เป€เบžเบตเปˆเบกเบ›เบฐเบชเบดเบ”เบ—เบดเบžเบฒเบšเป‚เบ„เบ‡เบเบฒเบ™เบชเปเบฒเบฅเบฑเบšเบเบฒเบ™ hashing - เบžเบงเบโ€‹เป€เบฎเบปเบฒโ€‹เบชเบฒโ€‹เบกเบฒเบ”โ€‹เบšเบญเบโ€‹เบ•เบปเบงโ€‹เบ—เปˆเบญเบ‡โ€‹เป€เบงเบฑเบšโ€‹เป€เบžเบทเปˆเบญ cache packages JS เบ‚เบญเบ‡โ€‹เบžเบงเบโ€‹เป€เบฎเบปเบฒโ€‹เบขเปˆเบฒเบ‡โ€‹เบšเปเปˆโ€‹เบกเบตโ€‹เบเปเบฒโ€‹เบ™เบปเบ”โ€‹เป€เบ™เบทเปˆเบญเบ‡โ€‹เบˆเบฒเบโ€‹เบงเปˆเบฒโ€‹ เบ–เป‰เบฒเปเบžเบฑเบเป€เบเบฑเบ”เบ›เปˆเบฝเบ™, เบŠเบทเปˆเป„เบŸเบฅเปŒเบ‚เบญเบ‡เบกเบฑเบ™เบ›เปˆเบฝเบ™เบ„เบทเบเบฑเบ™ (เบเบฒเบ™โ€‹เบ›เปˆเบฝเบ™โ€‹เปเบ›เบ‡โ€‹ 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, เป€เบžเบทเปˆเบญเป€เบžเบตเปˆเบกเบ›เบฐเบชเบดเบ”เบ—เบดเบžเบฒเบšเบ‚เบฐเบซเบ™เบฒเบ”เบŠเบธเบ”เปƒเบ™เป€เบงเบฅเบฒเบ—เบตเปˆ deploying เบเบฑเบšเบเบฒเบ™เบœเบฐเบฅเบดเบ”.

เบเบฒเบ™เบ•เบฑเป‰เบ‡เบ„เปˆเบฒเบ—เป‰เบญเบ‡เบ–เบดเปˆเบ™

เบ‚เป‰เบฒเบžเบฐเป€เบˆเบปเป‰เบฒเปเบ™เบฐเบ™เปเบฒเปƒเบซเป‰เบ•เบดเบ”เบ•เบฑเป‰เบ‡เป‚เบ„เบ‡เบเบฒเบ™เบขเบนเปˆเปƒเบ™เป€เบ„เบทเปˆเบญเบ‡เบ—เป‰เบญเบ‡เบ–เบดเปˆเบ™เบ‚เบญเบ‡เบ—เปˆเบฒเบ™เป€เบžเบทเปˆเบญเปƒเบซเป‰เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เบ›เบฐเบ•เบดเบšเบฑเบ”เบ•เบฒเบกเบ‚เบฑเป‰เบ™เบ•เบญเบ™เบ—เบตเปˆเบฅเบฐเบšเบธเป„เบงเป‰เปƒเบ™เบ‚เปเป‰เบ„เบงเบฒเบกเบ™เบตเป‰. เบเบฒเบ™เบ•เบดเบ”เบ•เบฑเป‰เบ‡เปเบกเปˆเบ™เบ‡เปˆเบฒเบเบ”เบฒเบ: เบ—เปเบฒเบญเบดเบ”, เบฅเบฐเบšเบปเบšเบ•เป‰เบญเบ‡เบกเบต node ะธ เบ›. เบ•เปเปˆเป„เบ›เบ—เปˆเบฒเบ™เบˆเปเบฒเป€เบ›เบฑเบ™เบ•เป‰เบญเบ‡เป€เบฎเบฑเบ”

$ git clone https://github.com/vzhou842/example-.io-game.git
$ cd example-.io-game
$ npm install

เปเบฅเบฐเป€เบˆเบปเป‰เบฒเบžเป‰เบญเบกเบ—เบตเปˆเบˆเบฐเป„เบ›! เป€เบžเบทเปˆเบญเป€เบฅเบตเปˆเบกเบ•เบปเป‰เบ™เป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบเบเบฒเบ™เบžเบฑเบ”เบ—เบฐเบ™เบฒ, เบžเบฝเบ‡เปเบ•เปˆเบ”เปเบฒเป€เบ™เบตเบ™เบเบฒเบ™

$ npm run develop

เปเบฅเบฐเป„เบ›เบ—เบตเปˆเบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบ‚เบญเบ‡เบ—เปˆเบฒเบ™ localhost: 3000. เป€เบŠเบตเบšเป€เบงเบตเบžเบฑเบ”เบ—เบฐเบ™เบฒเบˆเบฐเบชเป‰เบฒเบ‡เปเบžเบฑเบเป€เบเบฑเบ” JS เปเบฅเบฐ CSS เป‚เบ”เบเบญเบฑเบ”เบ•เบฐเป‚เบ™เบกเบฑเบ”เป€เบกเบทเปˆเบญเบเบฒเบ™เบ›เปˆเบฝเบ™เปเบ›เบ‡เบฅเบฐเบซเบฑเบ”เป€เบเบตเบ”เบ‚เบทเป‰เบ™ - เบžเบฝเบ‡เปเบ•เปˆเป‚เบซเบผเบ”เบซเบ™เป‰เบฒเปƒเบซเบกเปˆเป€เบžเบทเปˆเบญเป€เบšเบดเปˆเบ‡เบเบฒเบ™เบ›เปˆเบฝเบ™เปเบ›เบ‡เบ—เบฑเบ‡เบซเบกเบปเบ”!

3. เบˆเบธเบ”เป€เบ‚เบปเป‰เบฒเบฅเบนเบเบ„เป‰เบฒ

เปƒเบซเป‰เบžเบงเบเป€เบฎเบปเบฒเบฅเบปเบ‡เป„เบ›เบซเบฒเบฅเบฐเบซเบฑเบ”เป€เบเบกเบ•เบปเบงเบกเบฑเบ™เป€เบญเบ‡. เบ—เปเบฒเบญเบดเบ”เบžเบงเบเป€เบฎเบปเบฒเบ•เป‰เบญเบ‡เบเบฒเบ™เบซเบ™เป‰เบฒ index.html, เป€เบกเบทเปˆเบญเบ—เปˆเบฒเบ™เป„เบ›เบขเป‰เบฝเบกเบขเบฒเบกเป€เบงเบฑเบšเป„เบŠเบ—เปŒ, เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบˆเบฐเป‚เบซเบฅเบ”เบกเบฑเบ™เบเปˆเบญเบ™. เบซเบ™เป‰เบฒเบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเบ‚เป‰เบญเบ™เบ‚เป‰เบฒเบ‡เบ‡เปˆเบฒเบเบ”เบฒเบ:

index.html

เบ•เบปเบงเบขเปˆเบฒเบ‡เป€เบเบก .io  เบซเบผเบดเป‰เบ™

เบ•เบปเบงเบขเปˆเบฒเบ‡เบฅเบฐเบซเบฑเบ”เบ™เบตเป‰เป„เบ”เป‰เบ–เบทเบเบ›เบฑเบšเบ›เบธเบ‡เป€เบฅเบฑเบเบ™เป‰เบญเบเป€เบžเบทเปˆเบญเบ„เบงเบฒเบกเบŠเบฑเบ”เป€เบˆเบ™, เปเบฅเบฐเบ‚เป‰เบญเบเบˆเบฐเป€เบฎเบฑเบ”เปเบšเบšเบ”เบฝเบงเบเบฑเบ™เบเบฑเบšเบซเบผเบฒเบเป†เบ•เบปเบงเบขเปˆเบฒเบ‡เบญเบทเปˆเบ™เป†เปƒเบ™เบเบฒเบ™เบ•เบญเบš. เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เป€เบšเบดเปˆเบ‡เบฅเบฐเบซเบฑเบ”เป€เบ•เบฑเบกเบขเบนเปˆเบชเบฐเป€เบซเบกเบต GitHub.

เบžเบงเบโ€‹เป€เบฎเบปเบฒโ€‹เบกเบต:

  • เบญเบปเบ‡เบ›เบฐเบเบญเบšเบ‚เบญเบ‡ HTML5 Canvas (<canvas>), เป€เบŠเบดเปˆเบ‡เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเปƒเบŠเป‰เป€เบžเบทเปˆเบญเบชเบฐเปเบ”เบ‡เป€เบเบก.
  • <link> เป€เบžเบทเปˆเบญเป€เบžเบตเปˆเบกเบŠเบธเบ” CSS เบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒ.
  • <script> เป€เบžเบทเปˆเบญเป€เบžเบตเปˆเบกเบŠเบธเบ” Javascript เบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒ.
  • เป€เบกเบ™เบนเบซเบผเบฑเบเบ—เบตเปˆเบกเบตเบŠเบทเปˆเบœเบนเป‰เปƒเบŠเป‰ <input> เปเบฅเบฐเบ›เบธเปˆเบก โ€œPLAYโ€ (<button>).

เป€เบกเบทเปˆเบญเบซเบ™เป‰เบฒเบ—เปเบฒเบญเบดเบ”เป‚เบซเบผเบ”, เบ•เบปเบงเบ—เปˆเบญเบ‡เป€เบงเบฑเบšเบˆเบฐเป€เบฅเบตเปˆเบกเบ›เบฐเบ•เบดเบšเบฑเบ”เบฅเบฐเบซเบฑเบ” Javascript, เป€เบฅเบตเปˆเบกเบ•เบปเป‰เบ™เบ”เป‰เบงเบเป„เบŸเบฅเปŒ 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);
  };
});

เบ™เบตเป‰เบญเบฒเบ”เบˆเบฐเป€เบšเบดเปˆเบ‡เบ„เบทเบงเปˆเบฒเบชเบฑเบšเบชเบปเบ™, เปเบ•เปˆเบ•เบปเบงเบˆเบดเบ‡เปเบฅเป‰เบงเบšเปเปˆเบกเบตเบซเบเบฑเบ‡เป€เบเบตเบ”เบ‚เบถเป‰เบ™เบขเบนเปˆเบ—เบตเปˆเบ™เบตเป‰:

  1. เบ™เปเบฒเป€เบ‚เบปเป‰เบฒเป„เบŸเบฅเปŒ JS เบญเบทเปˆเบ™เป†เบˆเปเบฒเบ™เบงเบ™เบซเบ™เบถเปˆเบ‡.
  2. เบ™เปเบฒเป€เบ‚เบปเป‰เบฒ CSS (เบ”เบฑเปˆเบ‡เบ™เบฑเป‰เบ™ Webpack เบฎเบนเป‰เบงเปˆเบฒเบˆเบฐเบฅเบงเบกเบžเบงเบเบกเบฑเบ™เบขเบนเปˆเปƒเบ™เบŠเบธเบ” CSS เบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒ).
  3. เป€เบ›เบตเบ”เบ•เบปเบง connect() เป€เบžเบทเปˆเบญเบชเป‰เบฒเบ‡เบเบฒเบ™เป€เบŠเบทเปˆเบญเบกเบ•เปเปˆเบเบฑเบšเป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบเปเบฅเบฐเป€เบฅเบตเปˆเบกเบ•เบปเป‰เบ™ downloadAssets() เป€เบžเบทเปˆเบญเบ”เบฒเบงเป‚เบซเบฅเบ”เบฎเบนเบšเบžเบฒเบšเบ—เบตเปˆเบˆเปเบฒเป€เบ›เบฑเบ™เป€เบžเบทเปˆเบญเบชเบฐเปเบ”เบ‡เป€เบเบก.
  4. เบซเบผเบฑเบ‡โ€‹เบˆเบฒเบโ€‹เบชเปเบฒโ€‹เป€เบฅเบฑเบ”โ€‹เบ‚เบฑเป‰เบ™โ€‹เบ•เบญเบ™โ€‹เบ—เบต 3โ€‹ เป€เบกเบ™เบนเบซเบผเบฑเบเปเบกเปˆเบ™เบชเบฐเปเบ”เบ‡ (playMenu).
  5. เบ•เบฑเป‰เบ‡เบ„เปˆเบฒเบ•เบปเบงเบˆเบฑเบ”เบเบฒเบ™เบเบฒเบ™เบ„เบฅเบดเบเบ›เบธเปˆเบก โ€œPLAYโ€. เป€เบกเบทเปˆเบญเบเบปเบ”เบ›เบธเปˆเบก, เบฅเบฐเบซเบฑเบ”เป€เบฅเบตเปˆเบกเบ•เบปเป‰เบ™เป€เบเบกเปเบฅเบฐเบšเบญเบเป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบเบงเปˆเบฒเบžเบงเบเป€เบฎเบปเบฒเบžเป‰เบญเบกเบ—เบตเปˆเบˆเบฐเบซเบผเบดเป‰เบ™.

"เบŠเบตเป‰เบ™" เบซเบผเบฑเบเบ‚เบญเบ‡เป€เบซเบ”เบœเบปเบ™เบ‚เบญเบ‡เป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบเบฅเบนเบเบ„เป‰เบฒเบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒเปเบกเปˆเบ™เบขเบนเปˆเปƒเบ™เป„เบŸเบฅเปŒเป€เบซเบผเบปเปˆเบฒเบ™เบฑเป‰เบ™เบ—เบตเปˆเบ–เบทเบเบ™เปเบฒเป€เบ‚เบปเป‰เบฒเป‚เบ”เบเป„เบŸเบฅเปŒ index.js. เปƒเบ™เบ›เบฑเบ”เบˆเบธเบšเบฑเบ™เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเป€เบšเบดเปˆเบ‡เบžเบงเบเป€เบ‚เบปเบฒเบ—เบฑเบ‡เบซเบกเบปเบ”เปƒเบ™เบ„เปเบฒเบชเบฑเปˆเบ‡.

4. เบเบฒเบ™เปเบฅเบเบ›เปˆเบฝเบ™เบ‚เปเป‰เบกเบนเบ™เบฅเบนเบเบ„เป‰เบฒ

เปƒเบ™เป€เบเบกเบ™เบตเป‰เบžเบงเบเป€เบฎเบปเบฒเปƒเบŠเป‰เบซเป‰เบญเบ‡เบชเบฐเบซเบกเบธเบ”เบ—เบตเปˆเบกเบตเบŠเบทเปˆเบชเบฝเบ‡เป€เบžเบทเปˆเบญเบ•เบดเบ”เบ•เปเปˆเบชเบทเปˆเบชเบฒเบ™เบเบฑเบšเป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบ socket.io. Socket.io เบกเบตเบเบฒเบ™เบชเบฐเบซเบ™เบฑเบšเบชเบฐเบซเบ™เบนเบ™เปƒเบ™เบ•เบปเบง WebSockets, เบ—เบตเปˆเป€เบซเบกเบฒเบฐเบชเบปเบกเบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เบชเบทเปˆเบชเบฒเบ™เบชเบญเบ‡เบ—เบฒเบ‡: เบžเบงเบเป€เบฎเบปเบฒเบชเบฒเบกเบฒเบ”เบชเบปเปˆเบ‡เบ‚เปเป‰เบ„เบงเบฒเบกเป„เบ›เบซเบฒเป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบ ะธ เป€เบŠเบตเบšเป€เบงเบตเบชเบฒเบกเบฒเบ”เบชเบปเปˆเบ‡เบ‚เปเป‰เบ„เบงเบฒเบกเบซเบฒเบžเบงเบเป€เบฎเบปเบฒเบœเปˆเบฒเบ™เบเบฒเบ™เป€เบŠเบทเปˆเบญเบกเบ•เปเปˆเบ”เบฝเบงเบเบฑเบ™.

เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเบกเบตเป„เบŸเบฅเปŒเบซเบ™เบถเปˆเบ‡ 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. เบเบฒเบ™เบชเบฐเปเบ”เบ‡เบฅเบนเบเบ„เป‰เบฒ

เบกเบฑเบ™เป€เบ–เบดเบ‡เป€เบงเบฅเบฒเบ—เบตเปˆเบˆเบฐเบชเบฐเปเบ”เบ‡เบฎเบนเบšเบžเบฒเบšเปƒเบ™เบซเบ™เป‰เบฒเบˆเป!

...เปเบ•เปˆโ€‹เบเปˆเบญเบ™โ€‹เบ—เบตเปˆโ€‹เบžเบงเบโ€‹เป€เบฎเบปเบฒโ€‹เบˆเบฐโ€‹เบชเบฒโ€‹เบกเบฒเบ”โ€‹เป€เบฎเบฑเบ”โ€‹เป„เบ”เป‰, เบžเบงเบโ€‹เป€เบฎเบปเบฒโ€‹เบˆเปเบฒโ€‹เป€เบ›เบฑเบ™โ€‹เบ•เป‰เบญเบ‡โ€‹เป„เบ”เป‰โ€‹เบ”เบฒเบงโ€‹เบ™โ€‹เปŒโ€‹เป‚เบซเบฅเบ”โ€‹เบฎเบนเบšโ€‹เบžเบฒเบšโ€‹เบ—เบฑเบ‡โ€‹เบซเบกเบปเบ” (เปเบซเบผเปˆเบ‡โ€‹เบ‚เปเป‰โ€‹เบกเบนเบ™โ€‹) เบ—เบตเปˆโ€‹เบˆเปเบฒโ€‹เป€เบ›เบฑเบ™โ€‹เบชเปเบฒโ€‹เบฅเบฑเบšโ€‹เบเบฒเบ™โ€‹เบ™เบตเป‰โ€‹. เบกเบฒเบ‚เบฝเบ™เบ•เบปเบงเบˆเบฑเบ”เบเบฒเบ™เบŠเบฑเบšเบžเบฐเบเบฒเบเบญเบ™:

เบŠเบฑเบšเบชเบดเบ™.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.

เบซเบผเบฑเบ‡โ€‹เบˆเบฒเบโ€‹เบเบฒเบ™โ€‹เบ”เบฒเบงโ€‹เป‚เบซเบผเบ”โ€‹เบŠเบฑเบšโ€‹เบžเบฐโ€‹เบเบฒโ€‹เบเบญเบ™โ€‹, เบ—เปˆเบฒเบ™โ€‹เบชเบฒโ€‹เบกเบฒเบ”โ€‹เป€เบฅเบตเปˆเบกโ€‹เบ•เบปเป‰เบ™โ€‹เบเบฒเบ™โ€‹เป€เบฎเบฑเบ”โ€‹เปƒเบซเป‰โ€‹. เบ”เบฑเปˆเบ‡เบ—เบตเปˆเป„เบ”เป‰เบเปˆเบฒเบงเบเปˆเบญเบ™เบซเบ™เป‰เบฒเบ™เบตเป‰, เป€เบžเบทเปˆเบญเปเบ•เป‰เบกเบขเบนเปˆเปƒเบ™เบซเบ™เป‰เบฒเป€เบงเบฑเบšเบ—เบตเปˆเบžเบงเบเป€เบฎเบปเบฒเปƒเบŠเป‰ HTML5 Canvas (<canvas>). เป€เบเบกเบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒเปเบกเปˆเบ™เบ‚เป‰เบญเบ™เบ‚เป‰เบฒเบ‡เบ‡เปˆเบฒเบเบ”เบฒเบ, เบชเบฐเบ™เบฑเป‰เบ™เบžเบงเบเป€เบฎเบปเบฒเบžเบฝเบ‡เปเบ•เปˆเบ•เป‰เบญเบ‡เบเบฒเบ™เปƒเบซเป‰เบ”เบฑเปˆเบ‡เบ•เปเปˆเป„เบ›เบ™เบตเป‰:

  1. เบ„เบงเบฒเบกเป€เบ›เบฑเบ™เบกเบฒ
  2. เป€เบฎเบทเบญเบœเบนเป‰เบ™
  3. เบœเบนเป‰เบ™เบญเบทเปˆเบ™เป†เปƒเบ™เป€เบเบก
  4. เบซเบญเบ

เบ™เบตเป‰เปเบกเปˆเบ™เบžเบฒเบเบชเปˆเบงเบ™เบ—เบตเปˆเบชเปเบฒเบ„เบฑเบ™ 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: เบ‚เปเป‰เบกเบนเบ™เบเปˆเบฝเบงเบเบฑเบšเบœเบนเป‰เบ™เป„เบ”เป‰เบฎเบฑเบšเบเบฒเบ™เบญเบฑเบšเป€เบ”เบ”เบ™เบตเป‰.
  • เบ„เบปเบ™เบญเบทเปˆเบ™: array เบ‚เบญเบ‡เบ‚เปเป‰เบกเบนเบ™เบเปˆเบฝเบงเบเบฑเบšเบœเบนเป‰เบ™เบญเบทเปˆเบ™เป†เบ—เบตเปˆเป€เบ‚เบปเป‰เบฒเบฎเปˆเบงเบกเปƒเบ™เป€เบเบกเบ”เบฝเบงเบเบฑเบ™.
  • เบฅเบนเบเบ›เบทเบ™: array เบ‚เบญเบ‡เบ‚เปเป‰เบกเบนเบ™เบเปˆเบฝเบงเบเบฑเบš projectiles เปƒเบ™เป€เบเบก.
  • เบเบฐเบ”เบฒเบ™ เบ™เบณ: เบ‚เปเป‰เบกเบนเบ™เบเบฐเบ”เบฒเบ™เบˆเบฑเบ”เบญเบฑเบ™เบ”เบฑเบšเบ›เบฑเบ”เบˆเบธเบšเบฑเบ™. เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเบšเปเปˆเป€เบญเบปเบฒเบžเบงเบเบกเบฑเบ™เป€เบ‚เบปเป‰เบฒเป„เบ›เปƒเบ™เบšเบฑเบ™เบŠเบตเปƒเบ™เบ‚เปเป‰เบ„เบงเบฒเบกเบ™เบตเป‰.

7.1 เบชเบฐเบ–เบฒเบ™เบฐ naive เบ‚เบญเบ‡เบฅเบนเบเบ„เป‰เบฒ

เบเบฒเบ™เบ›เบฐเบ•เบดเบšเบฑเบ” Naive getCurrentState() เบชเบฒเบกเบฒเบ”เบชเบปเปˆเบ‡เบ„เบทเบ™เบ‚เปเป‰เบกเบนเบ™เป‚เบ”เบเบเบปเบ‡เบˆเบฒเบเบเบฒเบ™เบญเบฑเบšเป€เบ”เบ”เป€เบเบกเบ—เบตเปˆเป„เบ”เป‰เบฎเบฑเบšเบซเบผเป‰เบฒเบชเบธเบ”.

naive-state.js

let lastGameUpdate = null;

// Handle a newly received game update.
export function processGameUpdate(update) {
  lastGameUpdate = update;
}

export function getCurrentState() {
  return lastGameUpdate;
}

เบ‡เบฒเบกเปเบฅเบฐเบŠเบฑเบ”เป€เบˆเบ™! เปเบ•เปˆเบ–เป‰เบฒเบซเบฒเบเบงเปˆเบฒเบžเบฝเบ‡เปเบ•เปˆเบกเบฑเบ™เปเบกเปˆเบ™เบ‡เปˆเบฒเบเบ”เบฒเบเบ—เบตเปˆ. เบซเบ™เบถเปˆเบ‡เปƒเบ™เป€เบซเบ”เบœเบปเบ™เบเบฒเบ™เบ›เบฐเบ•เบดเบšเบฑเบ”เบ™เบตเป‰เปเบกเปˆเบ™เบšเบฑเบ™เบซเบฒ: เบกเบฑเบ™เบˆเปเบฒเบเบฑเบ”เบญเบฑเบ”เบ•เบฒเป€เบŸเบฃเบกเบเบฒเบ™เบชเบฐเปเบ”เบ‡เป€เบ–เบดเบ‡เบ„เบงเบฒเบกเป„เบงเป‚เบกเบ‡เป€เบŠเบตเบŸเป€เบงเบต.

เบญเบฑเบ”เบ•เบฒเป€เบŸเบฃเบก: เบˆเปเบฒเบ™เบงเบ™เบ‚เบญเบ‡เบเบญเบš (i. e. เป‚เบ— render()) เบ•เปเปˆเบงเบดเบ™เบฒเบ—เบต, เบซเบผเบท FPS. เป€เบเบกเบ›เบปเบเบเบฐเบ•เบดเปเบฅเป‰เบงเบžเบฐเบเบฒเบเบฒเบกเบšเบฑเบ™เบฅเบธเบขเปˆเบฒเบ‡เบซเบ™เป‰เบญเบ 60 FPS.

เบญเบฑเบ”โ€‹เบ•เบฒโ€‹เบเบฒเบ™โ€‹เบซเบกเบฒเบโ€‹เบ•เบดเบโ€‹: เบ„เบงเบฒเบกเบ–เบตเปˆเบ—เบตเปˆเป€เบŠเบตเบšเป€เบงเบตเบชเบปเปˆเบ‡เบญเบฑเบšเป€เบ”เบ”เป€เบเบกเปƒเบซเป‰เบเบฑเบšเบฅเบนเบเบ„เป‰เบฒ. เบกเบฑเบ™เบกเบฑเบเบˆเบฐเบ•เปˆเปเบฒเบเบงเปˆเบฒเบญเบฑเบ”เบ•เบฒเบเบญเบš. เปƒเบ™เป€เบเบกเบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒ, เป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบเปเบฅเปˆเบ™เบขเบนเปˆเบ—เบตเปˆ 30 เบซเบกเบฒเบเบ•เบดเบเบ•เปเปˆเบงเบดเบ™เบฒเบ—เบต.

เบ–เป‰เบฒเบžเบงเบเป€เบฎเบปเบฒเบžเบฝเบ‡เปเบ•เปˆเป€เบฎเบฑเบ”เบเบฒเบ™เบญเบฑเบšเป€เบ”เบ”เป€เบเบกเบซเบฅเป‰เบฒเบชเบธเบ”, FPS เบ—เบตเปˆเบชเปเบฒเบ„เบฑเบ™เบˆเบฐเบšเปเปˆเบชเบฒเบกเบฒเบ”เป€เบเบตเบ™ 30 เป€เบžเบฒเบฐเบงเปˆเบฒ เบžเบงเบเป€เบฎเบปเบฒเบšเปเปˆเป€เบ„เบตเบเป„เบ”เป‰เบฎเบฑเบšเบเบฒเบ™เบญเบฑเบšเป€เบ”เบ”เบซเบผเบฒเบเบเบงเปˆเบฒ 30 เป€เบ—เบทเปˆเบญเบ•เปเปˆเบงเบดเบ™เบฒเบ—เบตเบˆเบฒเบเป€เบŠเบตเบšเป€เบงเบต. เป€เบ–เบดเบ‡เปเบกเปˆเบ™เบงเปˆเบฒเบžเบงเบเป€เบฎเบปเบฒเป‚เบ—เบซเบฒ render() 60 เป€เบ—เบทเปˆเบญเบ•เปเปˆเบงเบดเบ™เบฒเบ—เบต, เบซเบผเบฑเบ‡เบˆเบฒเบเบ™เบฑเป‰เบ™เป€เบ„เบดเปˆเบ‡เบซเบ™เบถเปˆเบ‡เบ‚เบญเบ‡เบเบฒเบ™เป‚เบ—เป€เบซเบผเบปเปˆเบฒเบ™เบตเป‰เบžเบฝเบ‡เปเบ•เปˆเบˆเบฐเป€เบฎเบฑเบ”เบŠเป‰เปเบฒเบชเบดเปˆเบ‡เบ”เบฝเบงเบเบฑเบ™, เบ—เบตเปˆเบชเปเบฒเบ„เบฑเบ™เบšเปเปˆเป€เบฎเบฑเบ”เบซเบเบฑเบ‡เป€เบฅเบตเบ. เบšเบฑเบ™เบซเบฒเบญเบตเบเบ›เบฐเบเบฒเบ™เบซเบ™เบถเปˆเบ‡เบ—เบตเปˆเบกเบตเบเบฒเบ™เบ›เบฐเบ•เบดเบšเบฑเบ” naive เปเบกเปˆเบ™เบงเปˆเบฒเบกเบฑเบ™ เบ‚เบถเป‰เบ™เบเบฑเบšเบ„เบงเบฒเบกเบฅเปˆเบฒเบŠเป‰เบฒ. เปƒเบ™เบ„เบงเบฒเบกเป„เบงเบญเบดเบ™เป€เบ•เบตเป€เบ™เบฑเบ”เบ—เบตเปˆเป€เบซเบกเบฒเบฐเบชเบปเบก, เบฅเบนเบเบ„เป‰เบฒเบˆเบฐเป„เบ”เป‰เบฎเบฑเบšเบเบฒเบ™เบญเบฑเบšเป€เบ”เบ”เป€เบเบกเปเบ™เปˆเบ™เบญเบ™เบ—เบธเบเป† 33 ms (30 เบ•เปเปˆเบงเบดเบ™เบฒเบ—เบต):

เบเบฒเบ™เบชเป‰เบฒเบ‡เป€เบเบกเป€เบงเบฑเบšเบ—เบตเปˆเบกเบตเบœเบนเป‰เบซเบผเบดเป‰เบ™เบซเบผเบฒเบเบ„เบปเบ™เปƒเบ™เบ›เบฐเป€เบžเบ” .io
เปเบ•เปˆเบซเบ™เป‰เบฒเป€เบชเบเบ”เบฒเบ, เบšเปเปˆเบกเบตเบซเบเบฑเบ‡เบ—เบตเปˆเบชเบปเบกเบšเบนเบ™เปเบšเบš. เบฎเบนเบšโ€‹เบžเบฒเบšโ€‹เบ—เบตเปˆโ€‹เป€เบ›เบฑเบ™โ€‹เบˆเบดเบ‡โ€‹เบซเบผเบฒเบโ€‹เบเบงเปˆเบฒโ€‹เบˆเบฐโ€‹เป€เบ›เบฑเบ™โ€‹:
เบเบฒเบ™เบชเป‰เบฒเบ‡เป€เบเบกเป€เบงเบฑเบšเบ—เบตเปˆเบกเบตเบœเบนเป‰เบซเบผเบดเป‰เบ™เบซเบผเบฒเบเบ„เบปเบ™เปƒเบ™เบ›เบฐเป€เบžเบ” .io
เบเบฒเบ™เบ›เบฐเบ•เบดเบšเบฑเบ”เบ—เบตเปˆเป„เบฎเป‰เบ”เบฝเบ‡เบชเบฒเปเบกเปˆเบ™เบ‚เป‰เบญเบ™เบ‚เป‰เบฒเบ‡เป€เบ›เบฑเบ™เบเปเบฅเบฐเบ™เบตเบ—เบตเปˆเบฎเป‰เบฒเบเปเบฎเบ‡เบ—เบตเปˆเบชเบธเบ”เปƒเบ™เป€เบงเบฅเบฒเบ—เบตเปˆเบกเบฑเบ™เบกเบฒเบเบฑเบš latency. เบ–เป‰เบฒเป„เบ”เป‰เบฎเบฑเบšเบเบฒเบ™เบญเบฑเบšเป€เบ”เบ”เป€เบเบกเบ”เป‰เบงเบเบ„เบงเบฒเบกเบฅเปˆเบฒเบŠเป‰เบฒ 50ms, เบซเบผเบฑเบ‡เบˆเบฒเบเบ™เบฑเป‰เบ™ เบฅเบนเบเบ„เป‰เบฒเบŠเป‰เบฒเบฅเบปเบ‡ เป‚เบ”เบ 50ms เบžเบดเป€เบชเบ”เป€เบ™เบทเปˆเบญเบ‡เบˆเบฒเบเบงเปˆเบฒเบกเบฑเบ™เบเบฑเบ‡เบชเบฐเปเบ”เบ‡เบชเบฐเบ–เบฒเบ™เบฐเป€เบเบกเบˆเบฒเบเบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เบ—เบตเปˆเบœเปˆเบฒเบ™เบกเบฒ. เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เบˆเบดเบ™เบ•เบฐเบ™เบฒเบเบฒเบ™เบงเปˆเบฒเบ™เบตเป‰เบšเปเปˆเบชเบฐเบ”เบงเบเบชเปเบฒเบฅเบฑเบšเบœเบนเป‰เบซเบผเบดเป‰เบ™: เป€เบ™เบทเปˆเบญเบ‡เบˆเบฒเบเบเบฒเบ™เบŠเป‰เบฒเบฅเบปเบ‡เป‚เบ”เบเบ•เบปเบ™เป€เบญเบ‡, เป€เบเบกเบˆเบฐเป€เบšเบดเปˆเบ‡เบ„เบทเบงเปˆเบฒ jerky เปเบฅเบฐเบšเปเปˆเบซเบกเบฑเป‰เบ™เบ„เบปเบ‡.

7.2 เบ›เบฑเบšเบ›เบธเบ‡เบชเบฐเบ–เบฒเบ™เบฐเบฅเบนเบเบ„เป‰เบฒ

เบžเบงเบโ€‹เป€เบฎเบปเบฒโ€‹เบˆเบฐโ€‹เป€เบฎเบฑเบ”โ€‹เปƒเบซเป‰โ€‹เบเบฒเบ™โ€‹เบ›เบฑเบšโ€‹เบ›เบธเบ‡โ€‹เบšเบฒเบ‡โ€‹เบขเปˆเบฒเบ‡โ€‹เปƒเบ™โ€‹เบเบฒเบ™โ€‹เบ›เบฐโ€‹เบ•เบดโ€‹เบšเบฑเบ” naive. เบเปˆเบญเบ™เบญเบทเปˆเบ™ เปเบปเบ”, เบžเบงเบเป€เบฎเบปเบฒเปƒเบŠเป‰ เบ„เบงเบฒเบกเบฅเปˆเบฒเบŠเป‰เบฒเบ‚เบญเบ‡เบเบฒเบ™เบชเบฐเปเบ”เบ‡เบœเบปเบ™ เป‚เบ”เบ 100 ms. เบ™เบตเป‰เบซเบกเบฒเบเบ„เบงเบฒเบกเบงเปˆเบฒเบชเบฐเบ–เบฒเบ™เบฐ "เบ›เบฐเบˆเบธเบšเบฑเบ™" เบ‚เบญเบ‡เบฅเบนเบเบ„เป‰เบฒเบˆเบฐเบขเบนเปˆ 100ms เบซเบฅเบฑเบ‡เบชเบฐเบ–เบฒเบ™เบฐเป€เบเบกเบขเบนเปˆเปƒเบ™เป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบ. เบ•เบปเบงเบขเปˆเบฒเบ‡, เบ–เป‰เบฒเป€เบงเบฅเบฒเบ‚เบญเบ‡เป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบเปเบกเปˆเบ™ 150, เบซเบผเบฑเบ‡เบˆเบฒเบเบ™เบฑเป‰เบ™เบฅเบนเบเบ„เป‰เบฒเบˆเบฐเบชเบฐเปเบ”เบ‡เบชเบฐเบ–เบฒเบ™เบฐเบ—เบตเปˆเป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบเบขเบนเปˆเปƒเบ™เป€เบงเบฅเบฒเบ™เบฑเป‰เบ™ 50:

เบเบฒเบ™เบชเป‰เบฒเบ‡เป€เบเบกเป€เบงเบฑเบšเบ—เบตเปˆเบกเบตเบœเบนเป‰เบซเบผเบดเป‰เบ™เบซเบผเบฒเบเบ„เบปเบ™เปƒเบ™เบ›เบฐเป€เบžเบ” .io
เบ™เบตเป‰เป€เบฎเบฑเบ”เปƒเบซเป‰เบžเบงเบเป€เบฎเบปเบฒเบกเบต buffer 100ms เป€เบžเบทเปˆเบญเบ„เบงเบฒเบกเบขเบนเปˆเบฅเบญเบ”เบ‚เบญเบ‡เป€เบงเบฅเบฒเบ—เบตเปˆเบšเปเปˆเบชเบฒเบกเบฒเบ”เบ„เบฒเบ”เป€เบ”เบปเบฒเป„เบ”เป‰เบ‚เบญเบ‡เบเบฒเบ™เบญเบฑเบšเป€เบ”เบ”เป€เบเบก:

เบเบฒเบ™เบชเป‰เบฒเบ‡เป€เบเบกเป€เบงเบฑเบšเบ—เบตเปˆเบกเบตเบœเบนเป‰เบซเบผเบดเป‰เบ™เบซเบผเบฒเบเบ„เบปเบ™เปƒเบ™เบ›เบฐเป€เบžเบ” .io
เบฅเบฒเบ„เบฒเบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เบ™เบตเป‰เบˆเบฐเบ–เบฒเบงเบญเบ™ input lag เป‚เบ”เบ 100 ms. เบ™เบตเป‰เปเบกเปˆเบ™เบเบฒเบ™เป€เบชเบเบชเบฐเบฅเบฐเป€เบฅเบฑเบเบ™เป‰เบญเบเบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เบซเบผเบดเป‰เบ™เป€เบเบกเบ—เบตเปˆเบฅเบฝเบšเบ‡เปˆเบฒเบ - เบœเบนเป‰เบซเบผเบดเป‰เบ™เบชเปˆเบงเบ™เปƒเบซเบเปˆ (เป‚เบ”เบเบชเบฐเป€เบžเบฒเบฐเบœเบนเป‰เบซเบผเบดเป‰เบ™เปเบšเบšเบชเบฐเบšเบฒเบเป†) เบˆเบฐเบšเปเปˆเบชเบฑเบ‡เป€เบเบ”เป€เบซเบฑเบ™เบ„เบงเบฒเบกเบฅเปˆเบฒเบŠเป‰เบฒเบ™เบตเป‰. เบกเบฑเบ™เบ‡เปˆเบฒเบเบซเบผเบฒเบเบชเบณเบฅเบฑเบšเบ„เบปเบ™เบ—เบตเปˆเบˆเบฐเบ›เบฑเบšเบ›เปˆเบฝเบ™เป€เบ›เบฑเบ™ 100ms latency เบ„เบปเบ‡เบ—เบตเปˆเบเปˆเบงเบฒเบเบฒเบ™เบซเบผเบดเป‰เบ™เบเบฑเบš latency เบ—เบตเปˆเบšเปเปˆเบชเบฒเบกเบฒเบ”เบ„เบฒเบ”เป€เบ”เบปเบฒเป„เบ”เป‰.

เบžเบงเบเป€เบฎเบปเบฒเบชเบฒเบกเบฒเบ”เบ™เปเบฒเปƒเบŠเป‰เป€เบ•เบฑเบเบ™เบดเบเบญเบทเปˆเบ™เบ—เบตเปˆเป€เบญเบตเป‰เบ™เบงเปˆเบฒ "เบเบฒเบ™โ€‹เบ„เบฒเบ”โ€‹เบ„เบฐโ€‹เป€เบ™โ€‹เบ‚เป‰เบฒเบ‡โ€‹เบฅเบนเบโ€‹เบ„เป‰เบฒโ€‹", เป€เบŠเบดเปˆเบ‡เป€เบฎเบฑเบ”เบงเบฝเบเป„เบ”เป‰เบ”เบตเปƒเบ™เบเบฒเบ™เบซเบผเบธเบ”เบœเปˆเบญเบ™เบ„เบงเบฒเบกเบฅเปˆเบฒเบŠเป‰เบฒเบ—เบตเปˆเบฎเบฑเบšเบฎเบนเป‰เป„เบ”เป‰, เปเบ•เปˆเบˆเบฐเบšเปเปˆเบ–เบทเบเบเปˆเบฒเบงเป€เบ–เบดเบ‡เปƒเบ™เป‚เบžเบชเบ™เบตเป‰.

เบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เบญเบตเบเบญเบฑเบ™เบซเบ™เบถเปˆเบ‡เบ—เบตเปˆเบžเบงเบเป€เบฎเบปเบฒเปƒเบŠเป‰เปเบกเปˆเบ™ interpolation เป€เบชเบฑเป‰เบ™. เป€เบ™เบทเปˆเบญเบ‡เบˆเบฒเบเบ„เบงเบฒเบกเบŠเบฑเบเบŠเป‰เบฒเบ‚เบญเบ‡เบเบฒเบ™เบชเบฐเปเบ”เบ‡เบœเบปเบ™, เบžเบงเบเป€เบฎเบปเบฒเบกเบฑเบเบˆเบฐเบกเบตเบขเปˆเบฒเบ‡เบซเบ™เป‰เบญเบเบซเบ™เบถเปˆเบ‡เบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เบเปˆเบญเบ™เป€เบงเบฅเบฒเบ›เบฐเบˆเบธเบšเบฑเบ™เบขเบนเปˆเปƒเบ™เบฅเบนเบเบ„เป‰เบฒ. เป€เบกเบทเปˆเบญเบ–เบทเบเป€เบญเบตเป‰เบ™ getCurrentState(), เบžเบงเบเป€เบฎเบปเบฒเบชเบฒเบกเบฒเบ”เบ›เบฐเบ•เบดเบšเบฑเบ” interpolation เป€เบชเบฑเป‰เบ™ เบฅเบฐเบซเบงเปˆเบฒเบ‡เบเบฒเบ™เบญเบฑเบšเป€เบ”เบ”เป€เบเบกเบเปˆเบญเบ™ เปเบฅเบฐเบซเบผเบฑเบ‡เป€เบงเบฅเบฒเบ›เบฑเบ”เบˆเบธเบšเบฑเบ™เบขเบนเปˆเปƒเบ™เบฅเบนเบเบ„เป‰เบฒ:

เบเบฒเบ™เบชเป‰เบฒเบ‡เป€เบเบกเป€เบงเบฑเบšเบ—เบตเปˆเบกเบตเบœเบนเป‰เบซเบผเบดเป‰เบ™เบซเบผเบฒเบเบ„เบปเบ™เปƒเบ™เบ›เบฐเป€เบžเบ” .io
เบ™เบตเป‰เปเบเป‰เป„เบ‚เบšเบฑเบ™เบซเบฒเบญเบฑเบ”เบ•เบฒเป€เบŸเบฃเบก: เบ•เบญเบ™เบ™เบตเป‰เบžเบงเบเป€เบฎเบปเบฒเบชเบฒเบกเบฒเบ”เบชเบฐเปเบ”เบ‡เป€เบŸเบฃเบกเบ—เบตเปˆเป€เบ›เบฑเบ™เป€เบญเบเบฐเบฅเบฑเบเปƒเบ™เบญเบฑเบ”เบ•เบฒเป€เบŸเบฃเบกเบ—เบตเปˆเบžเบงเบเป€เบฎเบปเบฒเบ•เป‰เบญเบ‡เบเบฒเบ™!

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(). เบ”เบฑเปˆเบ‡เบ—เบตเปˆเบžเบงเบเป€เบฎเบปเบฒเป„เบ”เป‰เป€เบซเบฑเบ™เบเปˆเบญเบ™เบซเบ™เป‰เบฒเบ™เบฑเป‰เบ™, เบ—เบธเบเป†เบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เป€เบเบกเบ›เบฐเบเบญเบšเบกเบตเป€เบงเบฅเบฒเบ‚เบญเบ‡เป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบ. เบžเบงเบเป€เบฎเบปเบฒเบ•เป‰เบญเบ‡เบเบฒเบ™เปƒเบŠเป‰ render latency เป€เบžเบทเปˆเบญ render เบฎเบนเบšเบžเบฒเบš 100ms เบซเบฅเบฑเบ‡เป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบ, เปเบ•เปˆ เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเบšเปเปˆเบฎเบนเป‰เป€เบงเบฅเบฒเบ›เบฐเบˆเบธเบšเบฑเบ™เบขเบนเปˆเปƒเบ™เป€เบŠเบตเบšเป€เบงเบต, เป€เบžเบฒเบฐเบงเปˆเบฒเบžเบงเบเป€เบฎเบปเบฒเบšเปเปˆเบชเบฒเบกเบฒเบ”เบฎเบนเป‰เบงเปˆเบฒเบกเบฑเบ™เปƒเบŠเป‰เป€เบงเบฅเบฒเบ”เบปเบ™เบ›เบฒเบ™เปƒเบ”เบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เบญเบฑเบšเป€เบ”เบ”เปƒเบ”เป†เบ—เบตเปˆเบˆเบฐเบกเบฒเบฎเบญเบ”เบžเบงเบเป€เบฎเบปเบฒ. เบญเบดเบ™เป€เบ•เบตเป€เบ™เบฑเบ”เปเบกเปˆเบ™เบšเปเปˆเบชเบฒเบกเบฒเบ”เบ„เบฒเบ”เป€เบ”เบปเบฒเป„เบ”เป‰เปเบฅเบฐเบ„เบงเบฒเบกเป„เบงเบ‚เบญเบ‡เบกเบฑเบ™เบชเบฒเบกเบฒเบ”เปเบ•เบเบ•เปˆเบฒเบ‡เบเบฑเบ™เบขเปˆเบฒเบ‡เบซเบผเบงเบ‡เบซเบผเบฒเบ!

เป€เบžเบทเปˆเบญเปƒเบซเป‰เป„เบ”เป‰เบฎเบฑเบšเบ›เบฐเบกเบฒเบ™เบšเบฑเบ™เบซเบฒเบ™เบตเป‰, เบžเบงเบเป€เบฎเบปเบฒเบชเบฒเบกเบฒเบ”เบ™เปเบฒเปƒเบŠเป‰เบ›เบฐเบกเบฒเบ™เบ—เบตเปˆเบชเบปเบกเป€เบซเบ”เบชเบปเบกเบœเบปเบ™: เบžเบงเบเป€เบฎเบปเบฒ เปƒเบซเป‰เบชเบปเบกเบกเบธเบ”เบงเปˆเบฒเบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เบ„เบฑเป‰เบ‡เบ—เปเบฒเบญเบดเบ”เบกเบฒเบฎเบญเบ”เบ—เบฑเบ™เบ—เบต. เบ–เป‰เบฒเบ™เบตเป‰เปเบกเปˆเบ™เบ„เบงเบฒเบกเบˆเบดเบ‡, เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเบฎเบนเป‰เป€เบงเบฅเบฒเป€เบŠเบตเบŸเป€เบงเบตเปƒเบ™เบŠเปˆเบงเบ‡เป€เบงเบฅเบฒเบ™เบฑเป‰เบ™! เบžเบงเบเป€เบฎเบปเบฒเป€เบเบฑเบšเบฎเบฑเบเบชเบฒเป€เบงเบฅเบฒเบ‚เบญเบ‡เป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบเบขเบนเปˆเปƒเบ™ firstServerTimestamp เปเบฅเบฐเบŠเปˆเบงเบเบ›เบฐเบขเบฑเบ”เบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒ เบ—เป‰เบญเบ‡เบ–เบดเปˆเบ™ (เบฅเบนเบโ€‹เบ„เป‰เบฒโ€‹) timestamp เปƒเบ™โ€‹เบ›เบฑเบ”โ€‹เบˆเบธโ€‹เบšเบฑเบ™โ€‹เบ”เบฝเบงโ€‹เบเบฑเบ™โ€‹เปƒเบ™โ€‹ gameStart.

เป‚เบญเป‰, เบฅเปเบ–เป‰เบฒเบ™เบฒเบ—เบต. เบšเปเปˆเบ„เบงเบ™เบกเบตเป€เบงเบฅเบฒเบขเบนเปˆเปƒเบ™เป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบ = เป€เบงเบฅเบฒเบขเบนเปˆเปƒเบ™เบฅเบนเบเบ„เป‰เบฒเบšเป? เป€เบ›เบฑเบ™เบซเบเบฑเบ‡เบžเบงเบเป€เบฎเบปเบฒเบˆเบถเปˆเบ‡เบกเบตเบ„เบงเบฒเบกเปเบ•เบเบ•เปˆเบฒเบ‡เบเบฑเบ™เบฅเบฐเบซเบงเปˆเบฒเบ‡ "เป€เบงเบฅเบฒเป€เบŠเบตเบšเป€เบงเบต" เปเบฅเบฐ "เป€เบงเบฅเบฒเบฅเบนเบเบ„เป‰เบฒ"? เบ™เบตเป‰เปเบกเปˆเบ™เบ„เปเบฒเบ–เบฒเบกเบ—เบตเปˆเบเบดเปˆเบ‡เปƒเบซเบเปˆ! เบกเบฑเบ™ turns เปƒเบซเป‰ เป€เบซเบฑเบ™ เบงเปˆเบฒ เป€เบซเบผเบปเปˆเบฒ เบ™เบตเป‰ เบšเปเปˆ เปเบกเปˆเบ™ เบชเบดเปˆเบ‡ เบ”เบฝเบง เบเบฑเบ™ . Date.now() เบˆเบฐเบชเบปเปˆเบ‡เบ„เบทเบ™เบเบฒเบ™เบชเบฐเปเบ•เบกเป€เบงเบฅเบฒเบ—เบตเปˆเปเบ•เบเบ•เปˆเบฒเบ‡เบเบฑเบ™เบขเบนเปˆเปƒเบ™เบฅเบนเบเบ„เป‰เบฒเปเบฅเบฐเป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบเปเบฅเบฐเบ™เบตเป‰เปเบกเปˆเบ™เบ‚เบถเป‰เบ™เบเบฑเบšเบ›เบฑเบ”เปƒเบˆเบ—เป‰เบญเบ‡เบ–เบดเปˆเบ™เบ‚เบญเบ‡เป€เบ„เบทเปˆเบญเบ‡เบˆเบฑเบเป€เบซเบผเบปเปˆเบฒเบ™เบตเป‰. เบขเปˆเบฒเบชเบปเบกเบกเบธเบ”เบงเปˆเบฒเบชเบฐเปเบ•เบกเป€เบงเบฅเบฒเบˆเบฐเบ„เบทเบเบฑเบ™เปƒเบ™เบ—เบธเบเป€เบ„เบทเปˆเบญเบ‡.

เปƒเบ™เบ›เบฑเบ”เบˆเบธเบšเบฑเบ™เบžเบงเบเป€เบฎเบปเบฒเป€เบ‚เบปเป‰เบฒเปƒเบˆเบชเบดเปˆเบ‡เบ—เบตเปˆเบกเบฑเบ™เป€เบฎเบฑเบ” currentServerTime(): เบกเบฑเบ™เบเบฑเบšเบ„เบทเบ™เบกเบฒ เป€เบงเบฅเบฒเป€เบŠเบตเบšเป€เบงเบตเบ‚เบญเบ‡เป€เบงเบฅเบฒเบเบฒเบ™เบชเบฐเปเบ”เบ‡เบœเบปเบ™เบ›เบฐเบˆเบธเบšเบฑเบ™. เปƒเบ™เบ„เปเบฒเบชเบฑเบšเบ•เปˆเบฒเบ‡เป†เบญเบทเปˆเบ™เป†, เบ™เบตเป‰เปเบกเปˆเบ™เป€เบงเบฅเบฒเบ‚เบญเบ‡เป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบเปƒเบ™เบ›เบฐเบˆเบธเบšเบฑเบ™ (firstServerTimestamp <+ (Date.now() - gameStart)) เบฅเบปเบšโ€‹เบเบฒเบ™โ€‹เบฅเปˆเบฒโ€‹เบŠเป‰เบฒโ€‹เบเบฒเบ™โ€‹เบชเบฐโ€‹เปเบ”เบ‡ (RENDER_DELAY).

เบ•เบญเบ™เบ™เบตเป‰เปƒเบซเป‰เป€เบšเบดเปˆเบ‡เบงเบดเบ—เบตเบ—เบตเปˆเบžเบงเบเป€เบฎเบปเบฒเบˆเบฑเบ”เบเบฒเบ™เบเบฑเบšเบเบฒเบ™เบญเบฑเบšเป€เบ”เบ”เป€เบเบก. เป€เบกเบทเปˆเบญเป„เบ”เป‰เบฎเบฑเบšเบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เบˆเบฒเบเป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบ, เบกเบฑเบ™เบ–เบทเบเป€เบญเบตเป‰เบ™เบงเปˆเบฒ processGameUpdate(), เปเบฅเบฐเบžเบงเบเป€เบฎเบปเบฒเบšเบฑเบ™เบ—เบถเบเบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เปƒเบซเบกเปˆเปƒเบชเปˆ array gameUpdates. เบซเบผเบฑเบ‡เบˆเบฒเบเบ™เบฑเป‰เบ™, เป€เบžเบทเปˆเบญเบเบงเบ”เป€เบšเบดเปˆเบ‡เบเบฒเบ™เบ™เปเบฒเปƒเบŠเป‰เบซเบ™เปˆเบงเบเบ„เบงเบฒเบกเบˆเปเบฒ, เบžเบงเบเป€เบฎเบปเบฒเป€เบญเบปเบฒเบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เป€เบเบปเปˆเบฒเบ—เบฑเบ‡เบซเบกเบปเบ”เบญเบญเบ เบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เบžเบทเป‰เบ™เบ–เบฒเบ™เป€เบžเบฒเบฐเบงเปˆเบฒเบžเบงเบเป€เบฎเบปเบฒเบšเปเปˆเบ•เป‰เบญเบ‡เบเบฒเบ™เบžเบงเบเบกเบฑเบ™เบญเบตเบเบ•เปเปˆเป„เบ›.

"เบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เบซเบผเบฑเบ" เปเบกเปˆเบ™เบซเบเบฑเบ‡? เบ™เบตเป‰ เบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เบ„เบฑเป‰เบ‡เบ—เปเบฒเบญเบดเบ”เบ—เบตเปˆเบžเบงเบเป€เบฎเบปเบฒเบžเบปเบšเป€เบซเบฑเบ™เป‚เบ”เบเบเบฒเบ™เบเป‰เบฒเบเบเบฑเบšเบ„เบทเบ™เป„เบ›เบšเปˆเบญเบ™เบˆเบฒเบเป€เบงเบฅเบฒเบ‚เบญเบ‡เป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบเบ›เบฐเบˆเบธเบšเบฑเบ™. เบˆเบทเปˆเปเบœเบ™เบงเบฒเบ”เบ™เบตเป‰เบšเป?

เบเบฒเบ™เบชเป‰เบฒเบ‡เป€เบเบกเป€เบงเบฑเบšเบ—เบตเปˆเบกเบตเบœเบนเป‰เบซเบผเบดเป‰เบ™เบซเบผเบฒเบเบ„เบปเบ™เปƒเบ™เบ›เบฐเป€เบžเบ” .io
เบเบฒเบ™เบญเบฑเบšเป€เบ”เบ”เป€เบเบกเป‚เบ”เบเบเบปเบ‡เป„เบ›เบ—เบฒเบ‡เบŠเป‰เบฒเบเบ‚เบญเบ‡ "เป€เบงเบฅเบฒเบเบฒเบ™เปƒเบซเป‰เบฅเบนเบเบ„เป‰เบฒ" เปเบกเปˆเบ™เบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เบžเบทเป‰เบ™เบ–เบฒเบ™.

เบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เบžเบทเป‰เบ™เบ–เบฒเบ™เบ—เบตเปˆเปƒเบŠเป‰เบชเปเบฒเบฅเบฑเบšเปเบกเปˆเบ™เบซเบเบฑเบ‡? เป€เบ›เบฑเบ™โ€‹เบซเบเบฑเบ‡โ€‹เบžเบงเบโ€‹เป€เบฎเบปเบฒโ€‹เบชเบฒโ€‹เบกเบฒเบ”โ€‹เบซเบผเบธเบ”โ€‹เบฅเบปเบ‡โ€‹เบเบฒเบ™โ€‹เบ›เบฑเบšโ€‹เบ›เบธเบ‡โ€‹เปƒเบ™โ€‹เบ–เบฒเบ™โ€‹? เป€เบžเบทเปˆเบญเป€เบ‚เบปเป‰เบฒเปƒเบˆเป€เบฅเบทเปˆเบญเบ‡เบ™เบตเป‰, เปƒเบซเป‰เบ‚เบญเบ‡ เบชเบธเบ”เบ—เป‰เบฒเบ เปƒเบซเป‰เป€เบฎเบปเบฒเป€เบšเบดเปˆเบ‡เบเบฒเบ™เบˆเบฑเบ”เบ•เบฑเป‰เบ‡เบ›เบฐเบ•เบดเบšเบฑเบ” 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),
    };
  }
}

เบžเบงเบเป€เบฎเบปเบฒเบˆเบฑเบ”เบเบฒเบ™เบชเบฒเบกเบเปเบฅเบฐเบ™เบต:

  1. base < 0 เบซเบกเบฒเบเบ„เบงเบฒเบกเบงเปˆเบฒเบšเปเปˆเบกเบตเบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เบˆเบปเบ™เบเปˆเบงเบฒเป€เบงเบฅเบฒเบเบฒเบ™เบชเบฐเปเบ”เบ‡เปƒเบ™เบ›เบฐเบˆเบธเบšเบฑเบ™ (เป€เบšเบดเปˆเบ‡เบเบฒเบ™เบ›เบฐเบ•เบดเบšเบฑเบ”เบ‚เป‰เบฒเบ‡เป€เบ—เบดเบ‡ getBaseUpdate()). เบ™เบตเป‰เบชเบฒเบกเบฒเบ”เป€เบเบตเบ”เบ‚เบถเป‰เบ™เป„เบ”เป‰เบ—เบฑเบ™เบ—เบตเปƒเบ™เบ•เบญเบ™เป€เบฅเบตเปˆเบกเบ•เบปเป‰เบ™เบ‚เบญเบ‡เป€เบเบกเป€เบ™เบทเปˆเบญเบ‡เบˆเบฒเบเบเบฒเบ™เป€เบฎเบฑเบ”เปƒเบซเป‰เบŠเบฑเบเบŠเป‰เบฒ. เปƒเบ™เบเปเบฅเบฐเบ™เบตเบ™เบตเป‰, เบžเบงเบเป€เบฎเบปเบฒเปƒเบŠเป‰เบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เบซเบผเป‰เบฒเบชเบธเบ”เบ—เบตเปˆเป„เบ”เป‰เบฎเบฑเบš.
  2. base เปเบกเปˆเบ™เบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เบซเบผเป‰เบฒเบชเบธเบ”เบ—เบตเปˆเบžเบงเบเป€เบฎเบปเบฒเบกเบต. เบญเบฑเบ™เบ™เบตเป‰เบญเบฒเบ”เบˆเบฐเป€เบเบตเบ”เบ‚เบถเป‰เบ™เป€เบ™เบทเปˆเบญเบ‡เบˆเบฒเบเบเบฒเบ™เบ•เบญเบšเบชเบฐเปœเบญเบ‡เบ‚เบญเบ‡เป€เบ„เบทเบญเบ‚เปˆเบฒเบ เบซเบผเบทเบเบฒเบ™เป€เบŠเบทเปˆเบญเบกเบ•เปเปˆเบญเบดเบ™เป€เบ•เบตเป€เบ™เบฑเบ”เบšเปเปˆเบ”เบต. เปƒเบ™เบเปเบฅเบฐเบ™เบตเบ™เบตเป‰เป€เบŠเบฑเปˆเบ™เบ”เบฝเบงเบเบฑเบ™เบžเบงเบเป€เบฎเบปเบฒเปƒเบŠเป‰เบเบฒเบ™เบ›เบฑเบšเบ›เบธเบ‡เบซเบฅเป‰เบฒเบชเบธเบ”เบ—เบตเปˆเบžเบงเบเป€เบฎเบปเบฒเบกเบต.
  3. เบžเบงเบโ€‹เป€เบฎเบปเบฒโ€‹เบกเบตโ€‹เบเบฒเบ™โ€‹เบ›เบฑเบšโ€‹เบ›เบธเบ‡โ€‹เบ—เบฑเบ‡โ€‹เบเปˆเบญเบ™โ€‹เปเบฅเบฐโ€‹เบซเบผเบฑเบ‡โ€‹เบˆเบฒเบโ€‹เบ—เบตเปˆโ€‹เปƒเบŠเป‰โ€‹เป€เบงโ€‹เบฅเบฒ render เปƒเบ™โ€‹เบ›เบฐโ€‹เบˆเบธโ€‹เบšเบฑเบ™โ€‹, เบ”เบฑเปˆเบ‡โ€‹เบ™เบฑเป‰เบ™โ€‹เบžเบงเบโ€‹เป€เบฎเบปเบฒโ€‹เบชเบฒโ€‹เบกเบฒเบ”โ€‹เป€เบฎเบฑเบ”โ€‹เป„เบ”เป‰โ€‹ interpolate!

เบ—เบฑเบ‡เบซเบกเบปเบ”เบ—เบตเปˆเป€เบซเบฅเบทเบญเบขเบนเปˆเปƒเบ™ state.js เปเบกเปˆเบ™เบเบฒเบ™เบˆเบฑเบ”เบ•เบฑเป‰เบ‡เบ›เบฐเบ•เบดเบšเบฑเบ”เบเบฒเบ™เป€เบŠเบทเปˆเบญเบกเบชเบฒเบ™เป€เบชเบฑเป‰เบ™เบŠเบทเปˆเบ—เบตเปˆเบ‡เปˆเบฒเบเบ”เบฒเบ (เปเบ•เปˆเปœเป‰เบฒเป€เบšเบทเปˆเบญ) เบ„เบฐเบ™เบดเบ”เบชเบฒเบ”. เบ–เป‰เบฒเบ—เปˆเบฒเบ™เบ•เป‰เบญเบ‡เบเบฒเบ™เบชเปเบฒเบซเบผเบงเบ”เบกเบฑเบ™เป€เบญเบ‡, เบซเบผเบฑเบ‡เบˆเบฒเบเบ™เบฑเป‰เบ™เป€เบ›เบตเบ” state.js เบชเบธเบ” GitHub.

เบชเปˆเบงเบ™เบ—เบต 2. เป€เบŠเบตเบšเป€เบงเบต Backend

เปƒเบ™เบชเปˆเบงเบ™เบ™เบตเป‰เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเป€เบšเบดเปˆเบ‡ backend Node.js เบ—เบตเปˆเบ„เบงเบšเบ„เบธเบกเบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒ เบ•เบปเบงเบขเปˆเบฒเบ‡เบ‚เบญเบ‡เป€เบเบก .io.

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 เบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒ. เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเบ™เปเบฒเปƒเบŠเป‰เบžเบงเบเบกเบฑเบ™เปƒเบ™เบชเบญเบ‡เบงเบดเบ—เบต:

  • เปƒเบŠเป‰ webpack-dev-middleware เบญเบฑเบ”เบ•เบฐเป‚เบ™เบกเบฑเบ”เบชเป‰เบฒเบ‡เบŠเบธเบ”เบเบฒเบ™เบžเบฑเบ”เบ—เบฐเบ™เบฒเบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒ, เบซเบผเบท
  • เป‚เบญเบ™เบเป‰เบฒเบเป‚เบŸเบ™เป€เบ”เบตเปเบšเบšเบ„เบปเบ‡เบ—เบตเปˆ dist/, เบ—เบตเปˆ Webpack เบˆเบฐเบ‚เบฝเบ™เป„เบŸเบฅเปŒเบ‚เบญเบ‡เบžเบงเบเป€เบฎเบปเบฒเบซเบผเบฑเบ‡เบˆเบฒเบเบเบฒเบ™เบเปเปˆเบชเป‰เบฒเบ‡เบเบฒเบ™เบœเบฐเบฅเบดเบ”.

เบงเบฝเบเบ‡เบฒเบ™เบ—เบตเปˆเบชเปเบฒเบ„เบฑเบ™เบญเบตเบเบญเบฑเบ™เบซเบ™เบถเปˆเบ‡ server.js เบ›เบฐเบเบญเบšเบ”เป‰เบงเบเบเบฒเบ™เบ•เบฑเป‰เบ‡เบ„เปˆเบฒเป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบ socket.ioเป€เบŠเบดเปˆเบ‡เบžเบฝเบ‡เปเบ•เปˆเป€เบŠเบทเปˆเบญเบกเบ•เปเปˆเบเบฑเบšเป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบ Express:

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 เบ•เบปเบงเบ‚เบญเบ‡เบกเบฑเบ™เป€เบญเบ‡เบเปเบฒเบ™เบปเบ”เปเบ•เปˆเบฅเบฐ socket เป€เบ›เบฑเบ™เป€เบญเบเบฐเบฅเบฑเบ id, เบ”เบฑเปˆเบ‡เบ™เบฑเป‰เบ™เบžเบงเบเป€เบฎเบปเบฒเบšเปเปˆเบˆเปเบฒเป€เบ›เบฑเบ™เบ•เป‰เบญเบ‡เบเบฑเบ‡เบงเบปเบ™เบเปˆเบฝเบงเบเบฑเบšเบกเบฑเบ™. เบ‚เป‰เบญเบเบˆเบฐเป‚เบ—เบซเบฒเบฅเบฒเบง ID เบœเบนเป‰เบซเบผเบดเป‰เบ™.

เบ”เป‰เบงเบเบงเปˆเบฒเบขเบนเปˆเปƒเบ™เปƒเบˆ, เปƒเบซเป‰เบเบงเบ”เป€เบšเบดเปˆเบ‡เบ•เบปเบงเปเบ›เบ•เบปเบงเบขเปˆเบฒเบ‡เปƒเบ™เบŠเบฑเป‰เบ™เบฎเบฝเบ™ Game:

  • sockets เปเบกเปˆเบ™เบงเบฑเบ”เบ–เบธเบ—เบตเปˆเบœเบนเบ ID เบœเบนเป‰เบ™เบเบฑเบš socket เบ—เบตเปˆเบเปˆเบฝเบงเบ‚เป‰เบญเบ‡เบเบฑเบšเบœเบนเป‰เบ™เป„เบ”เป‰. เบกเบฑเบ™เบญเบฐเบ™เบธเบเบฒเบ”เปƒเบซเป‰เบžเบงเบเป€เบฎเบปเบฒเป€เบ‚เบปเป‰เบฒเป€เบ–เบดเบ‡เป€เบ•เบปเป‰เบฒเบฎเบฑเบšเป‚เบ”เบ ID เบœเบนเป‰เบ™เบ‚เบญเบ‡เป€เบ‚เบปเบฒเป€เบˆเบปเป‰เบฒเปƒเบ™เป„เบฅเบเบฐเป€เบงเบฅเบฒ.
  • players เป€เบ›เบฑเบ™เบงเบฑเบ”เบ–เบธเบ—เบตเปˆเบœเบนเบ ID เบœเบนเป‰เบ™เบเบฑเบšเบฅเบฐเบซเบฑเบ”> เบงเบฑเบ”เบ–เบธเบœเบนเป‰เบ™

bullets เป€เบ›เบฑเบ™ array เบ‚เบญเบ‡เบงเบฑเบ”เบ–เบธ 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() เบญเบฒเบ”เบˆเบฐเป€เบ›เบฑเบ™เบชเปˆเบงเบ™เบ—เบตเปˆเบชเปเบฒเบ„เบฑเบ™เบ—เบตเปˆเบชเบธเบ”เบ‚เบญเบ‡เป€เบซเบ”เบœเบปเบ™เบ”เป‰เบฒเบ™เป€เบŠเบตเบŸเป€เบงเบต. เบ‚เปเปƒเบซเป‰เบˆเบฑเบ”เบฅเบฒเบเบเบฒเบ™เบ—เบธเบเบขเปˆเบฒเบ‡เบ—เบตเปˆเบกเบฑเบ™เป€เบฎเบฑเบ”เบ•เบฒเบกเบฅเปเบฒเบ”เบฑเบš:

  1. เบ„เบดเบ”เป„เบฅเปˆเบงเปˆเบฒเป€เบงเบฅเบฒเปƒเบ” dt เบกเบฑเบ™ เป€เบ›เบฑเบ™ เบกเบฒ เบ•เบฑเป‰เบ‡ เปเบ•เปˆ เบชเบธเบ” เบ—เป‰เบฒเบ update().
  2. เบ›เบฑเบšเบ›เบธเบ‡เปเบ•เปˆเบฅเบฐ projectile เปเบฅเบฐเบ—เปเบฒเบฅเบฒเบเบžเบงเบเบกเบฑเบ™เบ–เป‰เบฒเบˆเปเบฒเป€เบ›เบฑเบ™. เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเป€เบซเบฑเบ™เบเบฒเบ™เบ›เบฐเบ•เบดเบšเบฑเบ”เบซเบ™เป‰เบฒเบ—เบตเปˆเบ™เบตเป‰เบ•เปเปˆเบกเบฒ. เบชเปเบฒเบฅเบฑเบšเปƒเบ™เบ›เบฑเบ”เบˆเบธเบšเบฑเบ™เบกเบฑเบ™เบžเบฝเบ‡เบžเปเบชเปเบฒเบฅเบฑเบšเบžเบงเบเป€เบฎเบปเบฒเบ—เบตเปˆเบˆเบฐเบฎเบนเป‰เบงเปˆเบฒ bullet.update() เบเบฑเบšเบ„เบทเบ™ true, เบ–เป‰เบฒ projectile เบ•เป‰เบญเบ‡เป„เบ”เป‰เบฎเบฑเบšเบเบฒเบ™เบ—เปเบฒเบฅเบฒเบ (เบฅเบฒเบงเบญเบญเบเป„เบ›เบ™เบญเบเบชเบฐเปœเบฒเบกเบเบดเบฅเบฒ).
  3. เบ›เบฑเบšเบ›เบธเบ‡เบœเบนเป‰เบ™เปเบ•เปˆเบฅเบฐเบ„เบปเบ™เปเบฅเบฐเบชเป‰เบฒเบ‡ projectile เบ–เป‰เบฒเบˆเปเบฒเป€เบ›เบฑเบ™. เบžเบงเบเป€เบฎเบปเบฒเบเบฑเบ‡เบˆเบฐเป€เบซเบฑเบ™เบเบฒเบ™เบ›เบฐเบ•เบดเบšเบฑเบ”เบ™เบตเป‰เบ•เปเปˆเบกเบฒ - player.update() เบชเบฒเบกเบฒเบ”เบชเบปเปˆเบ‡เบ„เบทเบ™เบงเบฑเบ”เบ–เบธ Bullet.
  4. เบเบงเบ”โ€‹เบชเบญเบšโ€‹เบเบฒเบ™โ€‹เบ›เบฐโ€‹เบ—เบฐโ€‹เบเบฑเบ™โ€‹เบฅเบฐโ€‹เบซเบงเปˆเบฒเบ‡ projectiles เปเบฅเบฐโ€‹เบœเบนเป‰เบ™โ€‹เบเบฒเบ™โ€‹เบ™เปเบฒโ€‹เปƒเบŠเป‰โ€‹ applyCollisions(), เป€เบŠเบดเปˆเบ‡เบชเบปเปˆเบ‡เบ„เบทเบ™ array เบ‚เบญเบ‡ projectiles เบ—เบตเปˆเบ•เบตเบœเบนเป‰เบ™. เบชเปเบฒเบฅเบฑเบšเปเบ•เปˆเบฅเบฐ projectile เบเบฑเบšเบ„เบทเบ™, เบžเบงเบเป€เบฎเบปเบฒเป€เบžเบตเปˆเบกเบ„เบฐเปเบ™เบ™เบ‚เบญเบ‡เบœเบนเป‰เบ™เบœเบนเป‰เบ—เบตเปˆเบเบดเบ‡เบกเบฑเบ™ (เบเบฒเบ™เบ™เปเบฒเปƒเบŠเป‰ player.onDealtDamage()), เปเบฅเบฐเบซเบผเบฑเบ‡เบˆเบฒเบเบ™เบฑเป‰เบ™เป€เบญเบปเบฒ projectile เบญเบญเบเบˆเบฒเบ array เป„เบ”เป‰ bullets.
  5. เปเบˆเป‰เบ‡โ€‹เบเบฒเบ™โ€‹เปเบฅเบฐโ€‹เบ—เปเบฒโ€‹เบฅเบฒเบโ€‹เบœเบนเป‰เบ™โ€‹เบ•เบฒเบโ€‹เบ—เบฑเบ‡โ€‹เบซเบกเบปเบ”โ€‹.
  6. เบชเบปเปˆเบ‡เบญเบฑเบšเป€เบ”เบ”เป€เบเบกเปƒเบซเป‰เบเบฑเบšเบœเบนเป‰เบซเบผเบดเป‰เบ™เบ—เบฑเบ‡เปเบปเบ” เบ—เบธเบเป†เบงเบดเบ™เบฒเบ—เบต เป€เบงเบฅเบฒเป€เบญเบตเป‰เบ™ 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. เปƒเบซเป‰เบชเบฑเบ‡เป€เบเบ”เบงเปˆเบฒเบกเบฑเบ™เบžเบฝเบ‡เปเบ•เปˆเบชเบปเปˆเบ‡เบ‚เปเป‰เบกเบนเบ™เป„เบ›เบซเบฒเบœเบนเป‰เบ™เปเบ•เปˆเบฅเบฐเบ„เบปเบ™เบเปˆเบฝเบงเบเบฑเบš เปƒเบเป‰เบ—เบตเปˆเบชเบธเบ” เบœเบนเป‰เบ™เปเบฅเบฐ projectiles - เบšเปเปˆเบˆเปเบฒเป€เบ›เบฑเบ™เบ•เป‰เบญเบ‡เบชเบปเปˆเบ‡เบ‚เปเป‰เบกเบนเบ™เบเปˆเบฝเบงเบเบฑเบšเบงเบฑเบ”เบ–เบธเป€เบเบกเบ—เบตเปˆเบ•เบฑเป‰เบ‡เบขเบนเปˆเป„เบเบˆเบฒเบเบœเบนเป‰เบ™เป„เบ”เป‰!

3. เบงเบฑเบ”เบ–เบธเป€เบเบกเบขเบนเปˆเปƒเบ™เป€เบ„เบทเปˆเบญเบ‡เปเบกเปˆเบ‚เปˆเบฒเบ

เปƒเบ™โ€‹เป€เบเบกโ€‹เบ‚เบญเบ‡โ€‹เบžเบงเบโ€‹เป€เบฎเบปเบฒโ€‹, projectiles เปเบฅเบฐโ€‹เบœเบนเป‰เบ™โ€‹เปเบกเปˆเบ™โ€‹เบ•เบปเบงโ€‹เบˆเบดเบ‡โ€‹เปเบฅเป‰เบงโ€‹เบ„เป‰เบฒเบโ€‹เบ„เบทโ€‹เบเบฑเบ™โ€‹เบซเบผเบฒเบโ€‹: เบžเบงเบโ€‹เป€เบ‚เบปเบฒโ€‹เป€เบˆเบปเป‰เบฒโ€‹เปเบกเปˆเบ™โ€‹เบšเปเปˆโ€‹เบกเบตโ€‹เบ•เบปเบงโ€‹เบ•เบปเบ™โ€‹เบ‚เบญเบ‡โ€‹เป€เบเบกโ€‹เป€เบ„เบทเปˆเบญเบ™โ€‹เบเป‰เบฒเบโ€‹เบฎเบญเบšโ€‹. เป€เบžเบทเปˆเบญเปƒเบŠเป‰เบ›เบฐเป‚เบซเบเบ”เบˆเบฒเบเบ„เบงเบฒเบกเบ„เป‰เบฒเบเบ„เบทเบเบฑเบ™เบ™เบตเป‰เบฅเบฐเบซเบงเปˆเบฒเบ‡เป€เบ„เบทเปˆเบญเบ‡เบซเบผเบดเป‰เบ™ เปเบฅเบฐเบฅเบนเบเบ›เบทเบ™, เปƒเบซเป‰เป€เบฅเบตเปˆเบกเบ•เบปเป‰เบ™เบ”เป‰เบงเบเบเบฒเบ™เบ›เบฐเบ•เบดเบšเบฑเบ”เบŠเบฑเป‰เบ™เบžเบทเป‰เบ™เบ–เบฒเบ™ 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:

bullet.js

const shortid = require('shortid');
const ObjectClass = require('./object');
const Constants = require('../shared/constants');

class Bullet extends ObjectClass {
  constructor(parentID, x, y, dir) {
    super(shortid(), x, y, dir, Constants.BULLET_SPEED);
    this.parentID = parentID;
  }

  // Returns true if the bullet should be destroyed
  update(dt) {
    super.update(dt);
    return this.x < 0 || this.x > Constants.MAP_SIZE || this.y < 0 || this.y > Constants.MAP_SIZE;
  }
}

ะ ะตะฐะปะธะทะฐั†ะธั Bullet เบชเบฑเป‰เบ™เบซเบผเบฒเบ! เบžเบงเบเป€เบฎเบปเบฒเป„เบ”เป‰เป€เบžเบตเปˆเบกเปƒเบชเปˆ Object เบžเบฝเบ‡เปเบ•เปˆเบชเปˆเบงเบ™เบ‚เบฐเบซเบเบฒเบเบ•เปเปˆเป„เบ›เบ™เบตเป‰:

  • เบเบฒเบ™เบ™เปเบฒเปƒเบŠเป‰เบŠเบธเบ” เบชเบฑเป‰เบ™ เบชเปเบฒเบฅเบฑเบšเบเบฒเบ™เบœเบฐเบฅเบดเบ”เปเบšเบšเบชเบธเปˆเบก id เบฅเบนเบเบชเบญเบ™เป„เบŸ.
  • เบเบณเบฅเบฑเบ‡เป€เบžเบตเปˆเบกเบŠเปˆเบญเบ‡เบ‚เปเป‰เบกเบนเบ™ parentID, เบ”เบฑเปˆเบ‡เบ™เบฑเป‰เบ™เบ—เปˆเบฒเบ™เบชเบฒเบกเบฒเบ”เบ•เบดเบ”เบ•เบฒเบกเบœเบนเป‰เบ™เบœเบนเป‰เบ—เบตเปˆเบชเป‰เบฒเบ‡ projectile เบ™เบตเป‰.
  • เบเบฒเบ™เป€เบžเบตเปˆเบกเบกเบนเบ™เบ„เปˆเบฒเบเบฑเบšเบ„เบทเบ™เป„เบ›เบซเบฒ update(), เป€เบŠเบดเปˆเบ‡เป€เบ—เบปเปˆเบฒเบเบฑเบš true, เบ–เป‰เบฒ projectile เปเบกเปˆเบ™เบขเบนเปˆเบ™เบญเบเบชเบฐเบซเบ™เบฒเบกเบเบดเบฅเบฒ (เบˆเบทเปˆเบžเบงเบเป€เบฎเบปเบฒเป„เบ”เป‰เป€เบงเบปเป‰เบฒเบเปˆเบฝเบงเบเบฑเบšเป€เบฅเบทเปˆเบญเบ‡เบ™เบตเป‰เปƒเบ™เบžเบฒเบเบชเบธเบ”เบ—เป‰เบฒเบ?).

เบ‚เปเปƒเบซเป‰เบเป‰เบฒเบงเป„เบ›เบชเบนเปˆ 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,
    };
  }
}

เบœเบนเป‰เบ™เปเบกเปˆเบ™เบชเบฐเบฅเบฑเบšเบชเบฑเบšเบŠเป‰เบญเบ™เบซเบผเบฒเบเบเปˆเบงเบฒ projectiles, เบชเบฐเบ™เบฑเป‰เบ™เบซเป‰เบญเบ‡เบฎเบฝเบ™เบ™เบตเป‰เบ„เบงเบ™เบˆเบฐเป€เบเบฑเบšเบฎเบฑเบเบชเบฒเบŠเปˆเบญเบ‡เบ‚เปเป‰เบกเบนเบ™เบˆเปเบฒเบ™เบงเบ™เบซเบ™เบถเปˆเบ‡. เบงเบดเบ—เบตเบเบฒเบ™เบ‚เบญเบ‡เบฅเบฒเบง update() เป€เบฎเบฑเบ”เบงเบฝเบเบซเบผเบฒเบเบ‚เบถเป‰เบ™, เป‚เบ”เบเบชเบฐเป€เบžเบฒเบฐเบเบฒเบ™เบชเบปเปˆเบ‡เบ„เบทเบ™ projectile เบ—เบตเปˆเบชเป‰เบฒเบ‡เบ‚เบถเป‰เบ™เปƒเบซเบกเปˆเบ–เป‰เบฒเบšเปเปˆเบกเบตเบญเบฑเบ™เปƒเบ”เป€เบซเบผเบทเบญเป„เบงเป‰ 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(), เป€เบŠเบดเปˆเบ‡เบชเบปเปˆเบ‡เบ„เบทเบ™ projectiles เบ—เบฑเบ‡เบซเบกเบปเบ”เบ—เบตเปˆเบ•เบตเบœเบนเป‰เบ™. เป‚เบŠเบเบ”เบต, เบ™เบตเป‰เบšเปเปˆเปเบกเปˆเบ™เบเบฒเบ™เบเบฒเบเบ—เบตเปˆเบˆเบฐเป€เบฎเบฑเบ”เป€เบžเบฒเบฐเบงเปˆเบฒ

  • เบงเบฑเบ”เบ–เบธเบ—เบตเปˆเบ•เบณเบเบฑเบ™เบ—เบฑเบ‡เปเบปเบ”เปเบกเปˆเบ™เบฎเบนเบšเบงเบปเบ‡เบกเบปเบ™, เปเบฅเบฐเบ™เบตเป‰เปเบกเปˆเบ™เบฎเบนเบšเบฎเปˆเบฒเบ‡เบ—เบตเปˆเบ‡เปˆเบฒเบเบ”เบฒเบเบ—เบตเปˆเบชเบธเบ”เป€เบžเบทเปˆเบญเบ›เบฐเบ•เบดเบšเบฑเบ”เบเบฒเบ™เบเบงเบ”เบซเบฒเบเบฒเบ™เบ›เบฐเบ—เบฐเบเบฑเบ™.
  • เบžเบงเบเป€เบฎเบปเบฒเบกเบตเบงเบดเบ—เบตเบเบฒเบ™เปเบฅเป‰เบง 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;
}

เบเบฒเบ™เบŠเบญเบเบ„เบปเป‰เบ™เบซเบฒ collision เบ‡เปˆเบฒเบเบ”เบฒเบเบ™เบตเป‰เปเบกเปˆเบ™เบญเบตเบ‡เปƒเบชเปˆเบ„เบงเบฒเบกเบˆเบดเบ‡เบ—เบตเปˆเบงเปˆเบฒ เบงเบปเบ‡เบกเบปเบ™เบชเบญเบ‡เบญเบฑเบ™เบ‚เบฑเบ”เบเบฑเบ™เบ–เป‰เบฒเป„เบฅเบเบฐเบซเปˆเบฒเบ‡เบฅเบฐเบซเบงเปˆเบฒเบ‡เบˆเบธเบ”เบชเบนเบ™เบเบฒเบ‡เบ‚เบญเบ‡เบžเบงเบเบกเบฑเบ™เปœเป‰เบญเบเบเบงเปˆเบฒเบœเบปเบ™เบฅเบงเบกเบ‚เบญเบ‡ radii. เบ™เบตเป‰เปเบกเปˆเบ™เบเปเบฅเบฐเบ™เบตเบ—เบตเปˆเป„เบฅเบเบฐเบซเปˆเบฒเบ‡เบฅเบฐเบซเบงเปˆเบฒเบ‡เบชเบนเบ™เบเบฒเบ‡เบ‚เบญเบ‡เบชเบญเบ‡เบงเบปเบ‡เปเบกเปˆเบ™เป€เบ—เบปเปˆเบฒเบเบฑเบšเบœเบปเบ™เบฅเบงเบกเบ‚เบญเบ‡ radii:

เบเบฒเบ™เบชเป‰เบฒเบ‡เป€เบเบกเป€เบงเบฑเบšเบ—เบตเปˆเบกเบตเบœเบนเป‰เบซเบผเบดเป‰เบ™เบซเบผเบฒเบเบ„เบปเบ™เปƒเบ™เบ›เบฐเป€เบžเบ” .io
เปƒเบ™เบ—เบตเปˆเบ™เบตเป‰เบ—เปˆเบฒเบ™เบ•เป‰เบญเบ‡เป€เบญเบปเบฒเปƒเบˆเปƒเบชเปˆเบชเบญเบ‡เบ”เป‰เบฒเบ™เป€เบžเบตเปˆเบกเป€เบ•เบตเบก:

  • projectile เบˆเบฐเบ•เป‰เบญเบ‡เบšเปเปˆเบกเบปเบ™เบ•เบตเบœเบนเป‰เบ™เบœเบนเป‰เบ—เบตเปˆเบชเป‰เบฒเบ‡เบกเบฑเบ™. เบ™เบตเป‰เบชเบฒเบกเบฒเบ”เบšเบฑเบ™เบฅเบธเป„เบ”เป‰เป‚เบ”เบเบเบฒเบ™เบ›เบฝเบšเบ—เบฝเบš bullet.parentID ั player.id.
  • เบฅเบนเบเบชเบญเบ™เป„เบŸเบ„เบงเบ™เบ•เบตเบžเบฝเบ‡เบ„เบฑเป‰เบ‡เบ”เบฝเบงเปƒเบ™เบเปเบฅเบฐเบ™เบตเบ—เบตเปˆเบฎเป‰เบฒเบเปเบฎเบ‡เบ‚เบญเบ‡เบเบฒเบ™เบ•เบตเบœเบนเป‰เบซเบผเบดเป‰เบ™เบซเบผเบฒเบเบ„เบปเบ™เปƒเบ™เป€เบงเบฅเบฒเบ”เบฝเบงเบเบฑเบ™. เบžเบงเบเป€เบฎเบปเบฒเบˆเบฐเปเบเป‰เป„เบ‚เบšเบฑเบ™เบซเบฒเบ™เบตเป‰เป‚เบ”เบเปƒเบŠเป‰เบ•เบปเบงเบ›เบฐเบ•เบดเบšเบฑเบ”เบเบฒเบ™ break: เป€เบกเบทเปˆเบญเบžเบปเบšเบœเบนเป‰เบซเบผเบดเป‰เบ™เบ—เบตเปˆเบ›เบฐเบ—เบฐเบเบฑเบ™เบเบฑเบšเบฅเบนเบเบชเบญเบ™เป„เบŸ, เบžเบงเบเป€เบฎเบปเบฒเบขเบธเบ”เบเบฒเบ™เบŠเบญเบเบซเบฒ เปเบฅเบฐเบเป‰เบฒเบงเป„เบ›เบชเบนเปˆเบฅเบนเบเบชเบญเบ™เป„เบŸเบ•เปเปˆเป„เบ›.

เบ—เบตเปˆเบชเบธเบ”

เบซเบกเบปเบ”โ€‹เป€เบ—เบปเปˆเบฒโ€‹เบ™เบตเป‰! เบžเบงเบเป€เบฎเบปเบฒเป„เบ”เป‰เบเบงเบกเป€เบญเบปเบฒเบ—เบธเบเบขเปˆเบฒเบ‡เบ—เบตเปˆเป€เบˆเบปเป‰เบฒเบ•เป‰เบญเบ‡เบเบฒเบ™เบฎเบนเป‰เป€เบžเบทเปˆเบญเบชเป‰เบฒเบ‡เป€เบเบกเป€เบงเบฑเบš .io. เบ•เปเปˆเป„เบ›เปเบกเปˆเบ™เบซเบเบฑเบ‡? เบชเป‰เบฒเบ‡เป€เบเบก .io เบ‚เบญเบ‡เบ—เปˆเบฒเบ™เป€เบญเบ‡!

เบฅเบฐเบซเบฑเบ”เบ•เบปเบงเบขเปˆเบฒเบ‡เบ—เบฑเบ‡เปเบปเบ”เปเบกเปˆเบ™เปเบซเบผเปˆเบ‡เป€เบ›เบตเบ” เปเบฅเบฐ เบ›เบฐเบเบฒเบ”เปƒเบ™ GitHub.

เปเบซเบผเปˆเบ‡เบ‚เปเป‰เบกเบนเบ™: www.habr.com

เป€เบžเบตเปˆเบกเบ„เบงเบฒเบกเบ„เบดเบ”เป€เบซเบฑเบ™