เชฎเชฒเซเชŸเชฟเชชเซเชฒเซ‡เชฏเชฐ .io เชตเซ‡เชฌ เช—เซ‡เชฎ เชฌเชจเชพเชตเชตเซ€

เชฎเชฒเซเชŸเชฟเชชเซเชฒเซ‡เชฏเชฐ .io เชตเซ‡เชฌ เช—เซ‡เชฎ เชฌเชจเชพเชตเชตเซ€
2015 เชฎเชพเช‚ เชฐเชฟเชฒเซ€เช เชฅเชˆ เช…เช—เชฐ.เชฏเซ‹ เชจเชตเซ€ เชถเซˆเชฒเซ€เชจเชพ เชชเซ‚เชฐเซเชตเชœ เชฌเชจเซเชฏเชพ เชฐเชฎเชคเซ‹ .io, เชœเซ‡เชจเซ€ เชฒเซ‹เช•เชชเซเชฐเชฟเชฏเชคเชพ เชคเซเชฏเชพเชฐเชฅเซ€ เช–เซ‚เชฌ เชตเชงเซ€ เช›เซ‡. เชฎเซ‡เช‚ เชฎเชพเชฐเซ€ เชœเชพเชคเซ‡ .io เช—เซ‡เชฎเซเชธเชจเซ€ เชฒเซ‹เช•เชชเซเชฐเชฟเชฏเชคเชพเชฎเชพเช‚ เชตเชงเชพเชฐเซ‹ เช…เชจเซเชญเชตเซเชฏเซ‹ เช›เซ‡: เช›เซ‡เชฒเซเชฒเชพ เชคเซเชฐเชฃ เชตเชฐเซเชทเชฎเชพเช‚, I เช† เชถเซˆเชฒเซ€เชจเซ€ เชฌเซ‡ เชฐเชฎเชคเซ‹ เชฌเชจเชพเชตเซ€ เช…เชจเซ‡ เชตเซ‡เชšเซ€..

เชœเซ‹ เชคเชฎเซ‡ เช† เช—เซ‡เชฎเซเชธ เชตเชฟเชถเซ‡ เชชเชนเซ‡เชฒเชพเช‚ เช•เซเชฏเชพเชฐเซ‡เชฏ เชธเชพเช‚เชญเชณเซเชฏเซเช‚ เชจ เชนเซ‹เชฏ, เชคเซ‹ เช† เชฎเชซเชค เชฎเชฒเซเชŸเชฟเชชเซเชฒเซ‡เชฏเชฐ เชตเซ‡เชฌ เช—เซ‡เชฎเซเชธ เช›เซ‡ เชœเซ‡ เชฐเชฎเชตเชพ เชฎเชพเชŸเซ‡ เชธเชฐเชณ เช›เซ‡ (เช•เซ‹เชˆ เชเช•เชพเช‰เชจเซเชŸ เชœเชฐเซ‚เชฐเซ€ เชจเชฅเซ€). เชคเซ‡เช“ เชธเชพเชฎเชพเชจเซเชฏ เชฐเซ€เชคเซ‡ เชเช• เชœ เชฎเซ‡เชฆเชพเชจเชฎเชพเช‚ เช˜เชฃเชพ เชตเชฟเชฐเซ‹เชงเซ€ เช–เซ‡เชฒเชพเชกเซ€เช“เชจเซ‹ เชธเชพเชฎเชจเซ‹ เช•เชฐเซ‡ เช›เซ‡. เช…เชจเซเชฏ เชชเซเชฐเช–เซเชฏเชพเชค .io เชฐเชฎเชคเซ‹: เชธเซเชฒเชฟเชŸเชฐ.เช“ ะธ เชกเชพเช‡เชช.เช“.

เช† เชชเซ‹เชธเซเชŸเชฎเชพเช‚, เช…เชฎเซ‡ เช•เซ‡เชตเซ€ เชฐเซ€เชคเซ‡ เช…เชจเซเชตเซ‡เชทเชฃ เช•เชฐเซ€เชถเซเช‚ เชถเชฐเซ‚เช†เชคเชฅเซ€ .io เช—เซ‡เชฎ เชฌเชจเชพเชตเซ‹. เช† เชฎเชพเชŸเซ‡, เชฎเชพเชคเซเชฐ Javascriptเชจเซเช‚ เชœเซเชžเชพเชจ เชชเซ‚เชฐเชคเซเช‚ เชนเชถเซ‡: เชคเชฎเชพเชฐเซ‡ เชธเชฟเชจเซเชŸเซ‡เช•เซเชธ เชœเซ‡เชตเซ€ เชฌเชพเชฌเชคเซ‹เชจเซ‡ เชธเชฎเชœเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡ ES6, เช•เซ€เชตเชฐเซเชก this ะธ เชตเชšเชจเซ‹. เชœเชพเชตเชพเชธเซเช•เซเชฐเชฟเชชเซเชŸเชจเซเช‚ เชคเชฎเชพเชฐเซเช‚ เชœเซเชžเชพเชจ เชธเช‚เชชเซ‚เชฐเซเชฃ เชจ เชนเซ‹เชฏ เชคเซ‹ เชชเชฃ เชคเชฎเซ‡ เชฎเซ‹เชŸเชพเชญเชพเช—เชจเซ€ เชชเซ‹เชธเซเชŸ เชธเชฎเชœเซ€ เชถเช•เซ‹ เช›เซ‹.

.io เชฐเชฎเชคเชจเซเช‚ เช‰เชฆเชพเชนเชฐเชฃ

เชถเซ€เช–เชตเชพเชจเซ€ เชธเชนเชพเชฏ เชฎเชพเชŸเซ‡, เช…เชฎเซ‡ เชธเช‚เชฆเชฐเซเชญ เชฒเชˆเชถเซเช‚ io เชฐเชฎเชคเชจเซเช‚ เช‰เชฆเชพเชนเชฐเชฃ. เชคเซ‡เชจเซ‡ เชฐเชฎเชตเชพเชจเซ‹ เชชเซเชฐเชฏเชพเชธ เช•เชฐเซ‹!

เชฎเชฒเซเชŸเชฟเชชเซเชฒเซ‡เชฏเชฐ .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] เชซเชพเช‡เชฒเชจเชพ เชธเชฎเชพเชตเชฟเชทเซเชŸเซ‹เชจเชพ เชนเซ‡เชถ เชธเชพเชฅเซ‡ เชฌเชฆเชฒเชตเชพเชฎเชพเช‚ เช†เชตเชถเซ‡. เช…เชฎเซ‡ เชคเซ‡เชจเซ‡ เช•เชฐเซ€เช เช›เซ€เช เชนเซ‡เชถเชฟเช‚เช— เชฎเชพเชŸเซ‡ เชชเซเชฐเซ‹เชœเซ‡เช•เซเชŸเชจเซ‡ เช‘เชชเซเชŸเชฟเชฎเชพเช‡เช เช•เชฐเซ‹ - เชคเชฎเซ‡ เชฌเซเชฐเชพเช‰เชเชฐเซเชธเชจเซ‡ เช…เชฎเชพเชฐเชพ 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เช‰เชคเซเชชเชพเชฆเชจเชฎเชพเช‚ เชœเชฎเชพเชตเชŸ เช•เชฐเชคเซ€ เชตเช–เชคเซ‡ เชชเซ‡เช•เซ‡เชœ เชฎเชพเชชเชจเซ‡ เช‘เชชเซเชŸเชฟเชฎเชพเช‡เช เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡.

เชธเซเชฅเชพเชจเชฟเช• เชธเซ‡เชŸเชฟเช‚เช—

เชนเซเช‚ เชธเซเชฅเชพเชจเชฟเช• เชฎเชถเซ€เชจ เชชเชฐ เชชเซเชฐเซ‹เชœเซ‡เช•เซเชŸ เช‡เชจเซเชธเซเชŸเซ‹เชฒ เช•เชฐเชตเชพเชจเซ€ เชญเชฒเชพเชฎเชฃ เช•เชฐเซเช‚ เช›เซเช‚ เชœเซ‡เชฅเซ€ เชคเชฎเซ‡ เช† เชชเซ‹เชธเซเชŸเชฎเชพเช‚ เชธเซ‚เชšเชฟเชฌเชฆเซเชง เชชเช—เชฒเชพเช‚เชจเซ‡ เช…เชจเซเชธเชฐเซ€ เชถเช•เซ‹. เชธเซ‡เชŸเช…เชช เชธเชฐเชณ เช›เซ‡: เชชเซเชฐเชฅเชฎ, เชธเชฟเชธเซเชŸเชฎ เช‡เชจเซเชธเซเชŸเซ‹เชฒ เช•เชฐเซ‡เชฒเซ€ เชนเซ‹เชตเซ€ เชœเซ‹เชˆเช เชจเซ‹เชก ะธ เชเชจ.เชชเซ€.เชเชฎ.. เช†เช—เชณ เชคเชฎเชพเชฐเซ‡ เช•เชฐเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡

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

เช…เชจเซ‡ เชคเชฎเซ‡ เชœเชตเชพ เชฎเชพเชŸเซ‡ เชคเซˆเชฏเชพเชฐ เช›เซ‹! เชตเชฟเช•เชพเชธ เชธเชฐเซเชตเชฐ เชถเชฐเซ‚ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡, เชซเช•เซเชค เชšเชฒเชพเชตเซ‹

$ npm run develop

เช…เชจเซ‡ เชตเซ‡เชฌ เชฌเซเชฐเชพเช‰เชเชฐ เชชเชฐ เชœเชพเช“ เชฒเซ‹เช•เชฒเชนเซ‹เชธเซเชŸ: 3000. เช•เซ‹เชก เชฌเชฆเชฒเชพเชคเชพ เชœ เชกเซ‡เชตเชฒเชชเชฎเซ‡เชจเซเชŸ เชธเชฐเซเชตเชฐ เช†เชชเชฎเซ‡เชณเซ‡ JS เช…เชจเซ‡ CSS เชชเซ‡เช•เซ‡เชœเซ‹เชจเซ‡ เชซเชฐเซ€เชฅเซ€ เชฌเชจเชพเชตเชถเซ‡ - เชฌเชงเชพ เชซเซ‡เชฐเชซเชพเชฐเซ‹ เชœเซ‹เชตเชพ เชฎเชพเชŸเซ‡ เชซเช•เซเชค เชชเซƒเชทเซเช เชจเซ‡ เชคเชพเชœเซเช‚ เช•เชฐเซ‹!

3. เช•เซเชฒเชพเชฏเชจเซเชŸ เชเชจเซเชŸเซเชฐเซ€ เชชเซ‹เชˆเชจเซเชŸเซเชธ

เชšเชพเชฒเซ‹ เช—เซ‡เชฎ เช•เซ‹เชก เชชเชฐ เชœ เชจเซ€เชšเซ‡ เชœเชˆเช. เชชเซเชฐเชฅเชฎ เช†เชชเชฃเชจเซ‡ เชเช• เชชเซƒเชทเซเช เชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡ index.html, เชธเชพเช‡เชŸเชจเซ€ เชฎเซเชฒเชพเช•เชพเชค เชฒเซ‡เชคเซ€ เชตเช–เชคเซ‡, เชฌเซเชฐเชพเช‰เชเชฐ เชคเซ‡เชจเซ‡ เชชเชนเซ‡เชฒเชพ เชฒเซ‹เชก เช•เชฐเชถเซ‡. เช…เชฎเชพเชฐเซเช‚ เชชเซƒเชทเซเช  เช–เซ‚เชฌ เชธเชฐเชณ เชนเชถเซ‡:

index.html

เชเช• เช‰เชฆเชพเชนเชฐเชฃ .io เชฐเชฎเชค  เชฐเชฎ

เช† เช•เซ‹เชก เช‰เชฆเชพเชนเชฐเชฃ เชธเซเชชเชทเซเชŸเชคเชพ เชฎเชพเชŸเซ‡ เชฅเซ‹เชกเซเช‚ เชธเชฐเชณ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซเชฏเซเช‚ เช›เซ‡, เช…เชจเซ‡ เชนเซเช‚ เชฌเซ€เชœเชพ เช˜เชฃเชพ เชชเซ‹เชธเซเชŸ เช‰เชฆเชพเชนเชฐเชฃเซ‹ เชธเชพเชฅเซ‡ เชคเซ‡ เชœ เช•เชฐเซ€เชถ. เชธเช‚เชชเซ‚เชฐเซเชฃ เช•เซ‹เชก เชนเช‚เชฎเซ‡เชถเชพ เชœเซ‹เชˆ เชถเช•เชพเชฏ เช›เซ‡ Github.

เช…เชฎเชพเชฐเซ€ เชชเชพเชธเซ‡:

  • 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);
  };
});

เช† เช—เซ‚เช‚เชšเชตเชฃเชญเชฐเซเชฏเซเช‚ เชฒเชพเช—เซ‡ เช›เซ‡, เชชเชฐเช‚เชคเซ เช…เชนเซ€เช‚ เช˜เชฃเซเช‚ เชฌเชงเซเช‚ เชฅเชˆ เชฐเชนเซเชฏเซเช‚ เชจเชฅเซ€:

  1. เช…เชจเซเชฏ เช˜เชฃเซ€ JS เชซเชพเชˆเชฒเซ‹ เช†เชฏเชพเชค เช•เชฐเชตเซ€.
  2. CSS เช†เชฏเชพเชค (เชœเซ‡เชฅเซ€ Webpack เชคเซ‡เชฎเชจเซ‡ เช…เชฎเชพเชฐเชพ CSS เชชเซ‡เช•เซ‡เชœเชฎเชพเช‚ เชธเชพเชฎเซ‡เชฒ เช•เชฐเชตเชพเชจเซเช‚ เชœเชพเชฃเซ‡ เช›เซ‡).
  3. เชฒเซ‹เช‚เชš connect() เชธเชฐเซเชตเชฐ เชธเชพเชฅเซ‡ เชœเซ‹เชกเชพเชฃ เชธเซเชฅเชพเชชเชฟเชค เช•เชฐเชตเชพ เช…เชจเซ‡ เชšเชฒเชพเชตเชตเชพ เชฎเชพเชŸเซ‡ downloadAssets() เชฐเชฎเชค เชฐเซ‡เชจเซเชกเชฐ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชœเชฐเซ‚เชฐเซ€ เช›เชฌเซ€เช“ เชกเชพเช‰เชจเชฒเซ‹เชก เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡.
  4. เชธเซเชŸเซ‡เชœ 3 เชชเซ‚เชฐเซเชฃ เชฅเชฏเชพ เชชเช›เซ€ เชฎเซเช–เซเชฏ เชฎเซ‡เชจเซ เชชเซเชฐเชฆเชฐเซเชถเชฟเชค เชฅเชพเชฏ เช›เซ‡ (playMenu).
  5. "เชชเซเชฒเซ‡" เชฌเชŸเชจ เชฆเชฌเชพเชตเชตเชพ เชฎเชพเชŸเซ‡ เชนเซ‡เชจเซเชกเชฒเชฐ เชธเซ‡เชŸ เช•เชฐเซ€ เชฐเชนเซเชฏเซเช‚ เช›เซ‡. เชœเซเชฏเชพเชฐเซ‡ เชฌเชŸเชจ เชฆเชฌเชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡, เชคเซเชฏเชพเชฐเซ‡ เช•เซ‹เชก เชฐเชฎเชค เชถเชฐเซ‚ เช•เชฐเซ‡ เช›เซ‡ เช…เชจเซ‡ เชธเชฐเซเชตเชฐเชจเซ‡ เช•เชนเซ‡ เช›เซ‡ เช•เซ‡ เช…เชฎเซ‡ เชฐเชฎเชตเชพ เชฎเชพเชŸเซ‡ เชคเซˆเชฏเชพเชฐ เช›เซ€เช.

เช…เชฎเชพเชฐเชพ เช•เซเชฒเชพเชฏเช‚เชŸ-เชธเชฐเซเชตเชฐ เชฒเซ‹เชœเชฟเช•เชจเซเช‚ เชฎเซเช–เซเชฏ "เชฎเชพเช‚เชธ" เชคเซ‡ เชซเชพเช‡เชฒเซ‹เชฎเชพเช‚ เช›เซ‡ เชœเซ‡ เชซเชพเช‡เชฒ เชฆเซเชตเชพเชฐเชพ เช†เชฏเชพเชค เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซ€ เชนเชคเซ€. index.js. เชนเชตเซ‡ เช…เชฎเซ‡ เชคเซ‡ เชฌเชงเชพเชจเซ‡ เช•เซเชฐเชฎเชฎเชพเช‚ เชงเซเชฏเชพเชจเชฎเชพเช‚ เชฒเชˆเชถเซเช‚.

4. เช—เซเชฐเชพเชนเช• เชกเซ‡เชŸเชพเชจเซเช‚ เชตเชฟเชจเชฟเชฎเชฏ

เช† เชฐเชฎเชคเชฎเชพเช‚, เช…เชฎเซ‡ เชธเชฐเซเชตเชฐ เชธเชพเชฅเซ‡ เชตเชพเชคเชšเซ€เชค เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชœเชพเชฃเซ€เชคเซ€ เชฒเชพเช‡เชฌเซเชฐเซ‡เชฐเซ€เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เช เช›เซ€เช socket.io. Socket.io เชจเซ‡ เชฎเซ‚เชณ เช†เชงเชพเชฐ เช›เซ‡ เชตเซ‡เชฌเชธเซ‹เช•เซ‡เชŸเซเชธ, เชœเซ‡ เชฆเซเชตเชฟ-เชฎเชพเชฐเซเช—เซ€ เชธเช‚เชšเชพเชฐ เชฎเชพเชŸเซ‡ เชฏเซ‹เช—เซเชฏ เช›เซ‡: เช…เชฎเซ‡ เชธเชฐเซเชตเชฐเชจเซ‡ เชธเช‚เชฆเซ‡เชถเชพ เชฎเซ‹เช•เชฒเซ€ เชถเช•เซ€เช เช›เซ€เช ะธ เชธเชฐเซเชตเชฐ เชธเชฎเชพเชจ เชœเซ‹เชกเชพเชฃ เชชเชฐ เช…เชฎเชจเซ‡ เชธเช‚เชฆเซ‡เชถเชพ เชฎเซ‹เช•เชฒเซ€ เชถเช•เซ‡ เช›เซ‡.

เช…เชฎเชพเชฐเซ€ เชชเชพเชธเซ‡ เชเช• เชซเชพเช‡เชฒ เชนเชถเซ‡ 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.

เชธเช‚เชธเชพเชงเชจเซ‹ เชกเชพเช‰เชจเชฒเซ‹เชก เช•เชฐเซเชฏเชพ เชชเช›เซ€, เชคเชฎเซ‡ เชฐเซ‡เชจเซเชกเชฐเชฟเช‚เช— เชถเชฐเซ‚ เช•เชฐเซ€ เชถเช•เซ‹ เช›เซ‹. เช…เช—เชพเช‰ เช•เชนเซเชฏเซเช‚ เชคเซ‡เชฎ, เชตเซ‡เชฌ เชชเซ‡เชœ เชชเชฐ เชฆเซ‹เชฐเชตเชพ เชฎเชพเชŸเซ‡ เช…เชฎเซ‡ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เช เช›เซ€เช HTML5 เช•เซ‡เชจเชตเชพเชธ (<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: เช† เช…เชชเชกเซ‡เชŸ เชฎเซ‡เชณเชตเชจเชพเชฐ เช–เซ‡เชฒเชพเชกเซ€ เชตเชฟเชถเซ‡เชจเซ€ เชฎเชพเชนเชฟเชคเซ€.
  • เช…เชจเซเชฏ: เชธเชฎเชพเชจ เชฐเชฎเชคเชฎเชพเช‚ เชญเชพเช— เชฒเซ‡เชคเชพ เช…เชจเซเชฏ เช–เซ‡เชฒเชพเชกเซ€เช“ เชตเชฟเชถเซ‡เชจเซ€ เชฎเชพเชนเชฟเชคเซ€เชจเซ€ เชถเซเชฐเซ‡เชฃเซ€.
  • เช—เซ‹เชณเซ€เช“: เชฐเชฎเชคเชฎเชพเช‚ เช…เชธเซเชคเซเชฐเซ‹ เชตเชฟเชถเซ‡เชจเซ€ เชฎเชพเชนเชฟเชคเซ€เชจเซ€ เชถเซเชฐเซ‡เชฃเซ€.
  • เชฒเซ€เชกเชฐเชฌเซ‹เชฐเซเชก: เชตเชฐเซเชคเชฎเชพเชจ เชฒเซ€เชกเชฐเชฌเซ‹เชฐเซเชก เชกเซ‡เชŸเชพ. เช† เชชเซ‹เชธเซเชŸเชฎเชพเช‚, เช…เชฎเซ‡ เชคเซ‡เชฎเชจเซ‡ เชงเซเชฏเชพเชจเชฎเชพเช‚ เชฒเชˆเชถเซเช‚ เชจเชนเซ€เช‚.

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 เชชเซเชฐเชคเชฟ เชธเซ‡เช•เชจเซเชกเซ‡) เช—เซ‡เชฎ เช…เชชเชกเซ‡เชŸ เชชเซเชฐเชพเชชเซเชค เชฅเชถเซ‡:

เชฎเชฒเซเชŸเชฟเชชเซเชฒเซ‡เชฏเชฐ .io เชตเซ‡เชฌ เช—เซ‡เชฎ เชฌเชจเชพเชตเชตเซ€
เช•เชฎเชจเชธเซ€เชฌเซ‡, เช•เช‚เชˆเชชเชฃ เชธเช‚เชชเซ‚เชฐเซเชฃ เชจเชฅเซ€. เชตเชงเซ เชตเชพเชธเซเชคเชตเชฟเช• เชšเชฟเชคเซเชฐ เชนเชถเซ‡:
เชฎเชฒเซเชŸเชฟเชชเซเชฒเซ‡เชฏเชฐ .io เชตเซ‡เชฌ เช—เซ‡เชฎ เชฌเชจเชพเชตเชตเซ€
เชœเซเชฏเชพเชฐเซ‡ เชตเชฟเชฒเช‚เชฌเชจเซ€ เชตเชพเชค เช†เชตเซ‡ เช›เซ‡ เชคเซเชฏเชพเชฐเซ‡ เชจเชฟเชทเซเช•เชชเชŸ เช…เชฎเชฒเซ€เช•เชฐเชฃ เชตเซเชฏเชตเชนเชพเชฐเซ€เช• เชฐเซ€เชคเซ‡ เชธเซŒเชฅเซ€ เช–เชฐเชพเชฌ เช•เซ‡เชธ เช›เซ‡. เชœเซ‹ เชฐเชฎเชค เช…เชชเชกเซ‡เชŸ 50ms เชจเชพ เชตเชฟเชฒเช‚เชฌ เชธเชพเชฅเซ‡ เชชเซเชฐเชพเชชเซเชค เชฅเชพเชฏ เช›เซ‡, เชคเซ‹ เชชเช›เซ€ เช—เซเชฐเชพเชนเช• เชธเซเชŸเซ‹เชฒ เชตเชงเชพเชฐเชพเชจเชพ 50ms เช•เชพเชฐเชฃ เช•เซ‡ เชคเซ‡ เชนเชœเซ เชชเชฃ เช…เช—เชพเช‰เชจเชพ เช…เชชเชกเซ‡เชŸเชฅเซ€ เชฐเชฎเชคเชจเซ€ เชธเซเชฅเชฟเชคเชฟเชจเซ‡ เชฐเซ‡เชจเซเชกเชฐ เช•เชฐเซ€ เชฐเชนเซเชฏเซเช‚ เช›เซ‡. เชคเชฎเซ‡ เช•เชฒเซเชชเชจเชพ เช•เชฐเซ€ เชถเช•เซ‹ เช›เซ‹ เช•เซ‡ เช† เช–เซ‡เชฒเชพเชกเซ€ เชฎเชพเชŸเซ‡ เช•เซ‡เชŸเชฒเซเช‚ เช…เชธเซเชตเชธเซเชฅเชคเชพ เช›เซ‡: เชฎเชจเชธเซเชตเซ€ เชฌเซเชฐเซ‡เช•เชฟเช‚เช— เชฐเชฎเชคเชจเซ‡ เช†เช‚เชšเช•เซ‹ เช…เชจเซ‡ เช…เชธเซเชฅเชฟเชฐ เชฒเชพเช—เซ‡ เช›เซ‡.

7.2 เชธเซเชงเชพเชฐเซ‡เชฒ เช•เซเชฒเชพเชฏเช‚เชŸ เชธเซเชŸเซ‡เชŸ

เช…เชฎเซ‡ เชจเชฟเชทเซเช•เชชเชŸ เช…เชฎเชฒเซ€เช•เชฐเชฃเชฎเชพเช‚ เช•เซ‡เชŸเชฒเชพเช• เชธเซเชงเชพเชฐเชพ เช•เชฐเซ€เชถเซเช‚. เชชเซเชฐเชฅเชฎ, เช…เชฎเซ‡ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เช เช›เซ€เช เชฐเซ‡เชจเซเชกเชฐเซ€เช‚เช— เชตเชฟเชฒเช‚เชฌ 100 ms เชฎเชพเชŸเซ‡ เช†เชจเซ‹ เช…เชฐเซเชฅ เช เช›เซ‡ เช•เซ‡ เช•เซเชฒเชพเชฏเช‚เชŸเชจเซ€ "เชตเชฐเซเชคเชฎเชพเชจ" เชธเซเชฅเชฟเชคเชฟ เชธเชฐเซเชตเชฐ เชชเชฐเชจเซ€ เชฐเชฎเชคเชจเซ€ เชธเซเชฅเชฟเชคเชฟ เช•เชฐเชคเชพเช‚ เชนเช‚เชฎเซ‡เชถเชพ 100ms เชชเชพเช›เชณ เชฐเชนเซ‡เชถเซ‡. เช‰เชฆเชพเชนเชฐเชฃ เชคเชฐเซ€เช•เซ‡, เชœเซ‹ เชธเชฐเซเชตเชฐ เชชเชฐเชจเซ‹ เชธเชฎเชฏ เช›เซ‡ 150, เชชเช›เซ€ เช•เซเชฒเชพเชฏเช‚เชŸ เชคเซ‡ เชฐเชพเชœเซเชฏเชจเซ‡ เชฐเซ‡เชจเซเชกเชฐ เช•เชฐเชถเซ‡ เชœเซ‡เชฎเชพเช‚ เชธเชฐเซเชตเชฐ เชคเซ‡ เชธเชฎเชฏเซ‡ เชนเชคเซเช‚ 50:

เชฎเชฒเซเชŸเชฟเชชเซเชฒเซ‡เชฏเชฐ .io เชตเซ‡เชฌ เช—เซ‡เชฎ เชฌเชจเชพเชตเชตเซ€
เช† เช…เชฎเชจเซ‡ เช…เชฃเชงเชพเชฐเซ€ เช—เซ‡เชฎ เช…เชชเชกเซ‡เชŸ เชธเชฎเชฏเชจเซ‡ เชŸเช•เซ€ เชฐเชนเซ‡เชตเชพ เชฎเชพเชŸเซ‡ 100ms เชฌเชซเชฐ เช†เชชเซ‡ เช›เซ‡:

เชฎเชฒเซเชŸเชฟเชชเซเชฒเซ‡เชฏเชฐ .io เชตเซ‡เชฌ เช—เซ‡เชฎ เชฌเชจเชพเชตเชตเซ€
เช† เชฎเชพเชŸเซ‡ เชšเซ‚เช•เชตเชฃเซ€ เช•เชพเชฏเชฎเซ€ เชฐเชนเซ‡เชถเซ‡ เช‡เชจเชชเซเชŸ เชฒเซ‡เช— 100 ms เชฎเชพเชŸเซ‡ เชธเชฐเชณ เช—เซ‡เชฎเชชเซเชฒเซ‡ เชฎเชพเชŸเซ‡ เช† เชเช• เชจเชพเชจเซ‹ เชฌเชฒเชฟเชฆเชพเชจ เช›เซ‡ - เชฎเซ‹เชŸเชพเชญเชพเช—เชจเชพ เช–เซ‡เชฒเชพเชกเซ€เช“ (เช–เชพเชธ เช•เชฐเซ€เชจเซ‡ เช•เซ‡เชเซเชฏเซเช…เชฒ เช–เซ‡เชฒเชพเชกเซ€เช“) เช† เชตเชฟเชฒเช‚เชฌเชจเซ€ เชจเซ‹เช‚เชง เชชเชฃ เชฒเซ‡เชถเซ‡ เชจเชนเซ€เช‚. เช…เชฃเชงเชพเชฐเซ€ เชฒเซ‡เชŸเชจเซเชธเซ€ เชธเชพเชฅเซ‡ เชฐเชฎเชตเชพ เช•เชฐเชคเชพเช‚ เชฒเซ‹เช•เซ‹ เชฎเชพเชŸเซ‡ เชธเชคเชค 100ms เชฒเซ‡เชŸเชจเซเชธเซ€เชฎเชพเช‚ เชธเชฎเชพเชฏเซ‹เชœเชฟเชค เช•เชฐเชตเซเช‚ เช–เซ‚เชฌ เชธเชฐเชณ เช›เซ‡.

เชจเชพเชฎเชจเซ€ เชฌเซ€เชœเซ€ เชŸเซ‡เช•เชจเชฟเช•เชจเซ‹ เชชเชฃ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€ เชถเช•เซ€เช เช›เซ€เช เช—เซเชฐเชพเชนเช• เชฌเชพเชœเซเชจเซ€ เช†เช—เชพเชนเซ€, เชœเซ‡ เช•เชฅเชฟเชค เชตเชฟเชฒเช‚เชฌเชคเชพ เช˜เชŸเชพเชกเชตเชพเชจเซเช‚ เชธเชพเชฐเซเช‚ เช•เชพเชฎ เช•เชฐเซ‡ เช›เซ‡, เชชเชฐเช‚เชคเซ เช† เชชเซ‹เชธเซเชŸเชฎเชพเช‚ เช†เชตเชฐเซ€ เชฒเซ‡เชตเชพเชฎเชพเช‚ เช†เชตเชถเซ‡ เชจเชนเซ€เช‚.

เช…เชฎเซ‡ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€ เชฐเชนเซเชฏเชพ เช›เซ€เช เชคเซ‡ เช…เชจเซเชฏ เชธเซเชงเชพเชฐเซ‹ เช›เซ‡ เชฐเซ‡เช–เซ€เชฏ เชชเซเชฐเช•เซเชทเซ‡เชช. เชฐเซ‡เชจเซเชกเชฐเชฟเช‚เช— เชฒเซ‡เช—เชจเซ‡ เช•เชพเชฐเชฃเซ‡, เช…เชฎเซ‡ เชธเชพเชฎเชพเชจเซเชฏ เชฐเซ€เชคเซ‡ เช•เซเชฒเชพเชฏเช‚เชŸเชฎเชพเช‚ เชตเชฐเซเชคเชฎเชพเชจ เชธเชฎเชฏ เช•เชฐเชคเชพเช‚ เช“เช›เชพเชฎเชพเช‚ เช“เช›เซเช‚ เชเช• เช…เชชเชกเซ‡เชŸ เช†เช—เชณ เช›เซ€เช. เชœเซเชฏเชพเชฐเซ‡ เชฌเซ‹เชฒเชพเชตเชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡ getCurrentState(), เช…เชฎเซ‡ เชšเชฒเชพเชตเซ€ เชถเช•เซ€เช เช›เซ€เช เชฐเซ‡เช–เซ€เชฏ เชชเซเชฐเช•เซเชทเซ‡เชช เช•เซเชฒเชพเชฏเช‚เชŸเชฎเชพเช‚ เชตเชฐเซเชคเชฎเชพเชจ เชธเชฎเชฏ เชชเชนเซ‡เชฒเชพ เช…เชจเซ‡ เชชเช›เซ€ เชฐเชฎเชค เช…เชชเชกเซ‡เชŸเซเชธ เชตเชšเซเชšเซ‡:

เชฎเชฒเซเชŸเชฟเชชเซเชฒเซ‡เชฏเชฐ .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(). เช†เชชเชฃเซ‡ เช…เช—เชพเช‰ เชœเซ‹เชฏเซเช‚ เชคเซ‡เชฎ, เชฆเชฐเซ‡เช• เช—เซ‡เชฎ เช…เชชเชกเซ‡เชŸเชฎเชพเช‚ เชธเชฐเซเชตเชฐ เชŸเชพเช‡เชฎเชธเซเชŸเซ‡เชฎเซเชชเชจเซ‹ เชธเชฎเชพเชตเซ‡เชถ เชฅเชพเชฏ เช›เซ‡. เช…เชฎเซ‡ เช‡เชฎเซ‡เชœเชจเซ‡ เชธเชฐเซเชตเชฐเชจเซ€ เชชเชพเช›เชณ 100ms เชฐเซ‡เชจเซเชกเชฐ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡ เชฐเซ‡เชจเซเชกเชฐ เชฒเซ‡เชŸเชจเซเชธเซ€เชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเชตเชพ เชฎเชพเช‚เช—เซ€เช เช›เซ€เช, เชชเชฐเช‚เชคเซ เช…เชฎเซ‡ เชธเชฐเซเชตเชฐ เชชเชฐเชจเซ‹ เชตเชฐเซเชคเชฎเชพเชจ เชธเชฎเชฏ เช•เซเชฏเชพเชฐเซ‡เชฏ เชœเชพเชฃเซ€ เชถเช•เซ€เชถเซเช‚ เชจเชนเซ€เช‚, เช•เชพเชฐเชฃ เช•เซ‡ เช…เชฎเซ‡ เชœเชพเชฃเซ€ เชถเช•เชคเชพ เชจเชฅเซ€ เช•เซ‡ เช•เซ‹เชˆเชชเชฃ เช…เชชเชกเซ‡เชŸ เช…เชฎเชจเซ‡ เชฎเชณเชตเชพเชฎเชพเช‚ เช•เซ‡เชŸเชฒเซ‹ เชธเชฎเชฏ เชฒเชพเช—เซเชฏเซ‹. เชˆเชจเซเชŸเชฐเชจเซ‡เชŸ เช…เชฃเชงเชพเชฐเซ€ เช›เซ‡ เช…เชจเซ‡ เชคเซ‡เชจเซ€ เชเชกเชช เชฎเซ‹เชŸเชพ เชชเซเชฐเชฎเชพเชฃเชฎเชพเช‚ เชฌเชฆเชฒเชพเชˆ เชถเช•เซ‡ เช›เซ‡!

เช† เชธเชฎเชธเซเชฏเชพเชจเซ‹ เชธเชพเชฎเชจเซ‹ เช•เชฐเชตเชพ เชฎเชพเชŸเซ‡, เช…เชฎเซ‡ เชตเชพเชœเชฌเซ€ เช…เช‚เชฆเชพเชœเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€ เชถเช•เซ€เช เช›เซ€เช: เช…เชฎเซ‡ เชชเซเชฐเชฅเชฎ เช…เชชเชกเซ‡เชŸ เชคเชฐเชค เชœ เช†เชตเชตเชพเชจเซ‹ เชกเซ‹เชณ เช•เชฐเซ‹. เชœเซ‹ เช† เชธเชพเชšเซเช‚ เชนเซ‹เชค, เชคเซ‹ เช…เชฎเชจเซ‡ เช† เชšเซ‹เช•เซเช•เชธ เช•เซเชทเชฃเซ‡ เชธเชฐเซเชตเชฐเชจเซ‹ เชธเชฎเชฏ เช–เชฌเชฐ เชนเซ‹เชค! เช…เชฎเซ‡ เชธเชฐเซเชตเชฐเชจเชพ เชŸเชพเช‡เชฎเชธเซเชŸเซ‡เชฎเซเชชเชจเซ‡ เชธเช‚เช—เซเชฐเชนเชฟเชค เช•เชฐเซ€เช เช›เซ€เช firstServerTimestamp เช…เชจเซ‡ เช…เชฎเชพเชฐเชพ เชฐเชพเช–เซ‹ เชธเซเชฅเชพเชจเชฟเช• เชฎเชพเช‚ เชคเซ‡ เชœ เช•เซเชทเชฃเซ‡ (เช•เซเชฒเชพเชฏเชจเซเชŸ) เชŸเชพเช‡เชฎเชธเซเชŸเซ‡เชฎเซเชช gameStart.

เช“เชน เชฅเซ‹เชญเซ‹. เชถเซเช‚ เชคเซ‡ เชธเชฐเซเชตเชฐ เชธเชฎเชฏ = เช•เซเชฒเชพเชฏเช‚เชŸ เชธเชฎเชฏ เชจ เชนเซ‹เชตเซ‹ เชœเซ‹เชˆเช? เชถเชพ เชฎเชพเชŸเซ‡ เช†เชชเชฃเซ‡ "เชธเชฐเซเชตเชฐ เชŸเชพเช‡เชฎเชธเซเชŸเซ‡เชฎเซเชช" เช…เชจเซ‡ "เช•เซเชฒเชพเชฏเชจเซเชŸ เชŸเชพเช‡เชฎเชธเซเชŸเซ‡เชฎเซเชช" เชตเชšเซเชšเซ‡ เชคเชซเชพเชตเชค เช•เชฐเซ€เช เช›เซ€เช? เช† เชเช• เชฎเชนเชพเชจ เชชเซเชฐเชถเซเชจ เช›เซ‡! เชคเซ‡ เชคเชพเชฐเชฃ เช†เชชเซ‡ เช›เซ‡ เช•เซ‡ เช† เชเช• เชœ เชตเชธเซเชคเซ เชจเชฅเซ€. Date.now() เช•เซเชฒเชพเชฏเช‚เชŸ เช…เชจเซ‡ เชธเชฐเซเชตเชฐเชฎเชพเช‚ เช…เชฒเช— เช…เชฒเช— เชŸเชพเช‡เชฎเชธเซเชŸเซ‡เชฎเซเชช เชชเชฐเชค เช•เชฐเชถเซ‡, เช…เชจเซ‡ เชคเซ‡ เช† เชฎเชถเซ€เชจเซ‹เชจเชพ เชธเซเชฅเชพเชจเชฟเช• เชชเชฐเชฟเชฌเชณเซ‹ เชชเชฐ เช†เชงเชพเชฐเชฟเชค เช›เซ‡. เช•เซเชฏเชพเชฐเซ‡เชฏ เชงเชพเชฐเซ‹ เชจเชนเซ€เช‚ เช•เซ‡ เชคเชฎเชพเชฎ เชฎเชถเซ€เชจเซ‹ เชชเชฐ เชŸเชพเช‡เชฎเชธเซเชŸเซ‡เชฎเซเชช เชธเชฎเชพเชจ เชนเชถเซ‡.

เชนเชตเซ‡ เช†เชชเชฃเซ‡ เชธเชฎเชœเซ€เช เช›เซ€เช เช•เซ‡ เชถเซเช‚ เช•เชฐเซ‡ เช›เซ‡ currentServerTime(): เชคเซ‡ เชชเชฐเชค เช•เชฐเซ‡ เช›เซ‡ เชตเชฐเซเชคเชฎเชพเชจ เชฐเซ‡เชจเซเชกเชฐ เชธเชฎเชฏเชจเซ‹ เชธเชฐเซเชตเชฐ เชŸเชพเช‡เชฎเชธเซเชŸเซ‡เชฎเซเชช. เชฌเซ€เชœเชพ เชถเชฌเซเชฆเซ‹เชฎเชพเช‚ เช•เชนเซ€เช เชคเซ‹, เช† เชตเชฐเซเชคเชฎเชพเชจ เชธเชฐเซเชตเชฐ เชธเชฎเชฏ เช›เซ‡ (firstServerTimestamp <+ (Date.now() - gameStart)) เชฎเชพเชˆเชจเชธ เชฐเซ‡เชจเซเชกเชฐเซ€เช‚เช— เชตเชฟเชฒเช‚เชฌ (RENDER_DELAY).

เชนเชตเซ‡ เช†เชชเชฃเซ‡ เช—เซ‡เชฎ เช…เชชเชกเซ‡เชŸเซเชธ เช•เซ‡เชตเซ€ เชฐเซ€เชคเซ‡ เชนเซ‡เชจเซเชกเชฒ เช•เชฐเซ€เช เช›เซ€เช เชคเซ‡เชจเชพ เชชเชฐ เชเช• เชจเชœเชฐ เช•เชฐเซ€เช. เชœเซเชฏเชพเชฐเซ‡ เช…เชชเชกเซ‡เชŸ เชธเชฐเซเชตเชฐเชฎเชพเช‚เชฅเซ€ เชชเซเชฐเชพเชชเซเชค เชฅเชพเชฏ เช›เซ‡, เชคเซเชฏเชพเชฐเซ‡ เชคเซ‡เชจเซ‡ เช•เชนเซ‡เชตเชพเชฎเชพเช‚ เช†เชตเซ‡ เช›เซ‡ processGameUpdate()เช…เชจเซ‡ เช…เชฎเซ‡ เชจเชตเชพ เช…เชชเชกเซ‡เชŸเชจเซ‡ เชเชฐเซ‡เชฎเชพเช‚ เชธเชพเชšเชตเซ€เช เช›เซ€เช 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. เช…เชฎเชพเชฐเซ€ เชชเชพเชธเซ‡ เชตเชฐเซเชคเชฎเชพเชจ เชฐเซ‡เชจเซเชกเชฐ เชธเชฎเชฏ เชชเชนเซ‡เชฒเชพ เช…เชจเซ‡ เชชเช›เซ€ เชฌเช‚เชจเซ‡ เช…เชชเชกเซ‡เชŸ เช›เซ‡, เชคเซ‡เชฅเซ€ เช…เชฎเซ‡ เช•เชฐเซ€ เชถเช•เซ€เช เช›เซ€เช เชชเซเชฐเช•เซเชทเซ‡เชชเชฟเชค เช•เชฐเชตเซเช‚!

เชฌเชพเช•เซ€ เช›เซ‡ เชคเซ‡ เชฌเชงเซเช‚ state.js เชฐเซ‡เช–เซ€เชฏ เชชเซเชฐเช•เซเชทเซ‡เชชเชจเซเช‚ เช…เชฎเชฒเซ€เช•เชฐเชฃ เช›เซ‡ เชœเซ‡ เชธเชฐเชณ (เชชเชฐเช‚เชคเซ เช•เช‚เชŸเชพเชณเชพเชœเชจเช•) เช—เชฃเชฟเชค เช›เซ‡. เชœเซ‹ เชคเชฎเซ‡ เชคเซ‡เชจเซ‡ เชœเชพเชคเซ‡ เช…เชจเซเชตเซ‡เชทเชฃ เช•เชฐเชตเชพ เชฎเชพเช‚เช—เซ‹ เช›เซ‹, เชคเซ‹ เชชเช›เซ€ เช–เซ‹เชฒเซ‹ state.js เชชเชฐ Github.

เชญเชพเช— 2. เชฌเซ‡เช•เชเชจเซเชก เชธเชฐเซเชตเชฐ

เช† เชญเชพเช—เชฎเชพเช‚, เช…เชฎเซ‡ 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-dev-midleware เช…เชฎเชพเชฐเชพ เชตเชฟเช•เชพเชธ เชชเซ‡เช•เซ‡เชœเซ‹เชจเซ‡ เช†เชชเชฎเซ‡เชณเซ‡ เชซเชฐเซ€เชฅเซ€ เชฌเชจเชพเชตเชตเชพ เชฎเชพเชŸเซ‡, เช…เชฅเชตเชพ
  • เชธเซเชฅเชฟเชฐ เชธเซเชฅเชพเชจเชพเช‚เชคเชฐเชฟเชค เชซเซ‹เชฒเซเชกเชฐ dist/, เชœเซ‡เชฎเชพเช‚ Webpack เชชเซเชฐเซ‹เชกเช•เซเชถเชจ เชฌเชฟเชฒเซเชก เชชเช›เซ€ เช…เชฎเชพเชฐเซ€ เชซเชพเช‡เชฒเซ‹ เชฒเช–เชถเซ‡.

เชฌเซ€เชœเซเช‚ เชฎเชนเชคเซเชตเชจเซเช‚ เช•เชพเชฐเซเชฏ server.js เชธเชฐเซเชตเชฐ เชธเซ‡เชŸ เช•เชฐเชตเชพเชจเซ‹ เชธเชฎเชพเชตเซ‡เชถ เชฅเชพเชฏ เช›เซ‡ socket.ioเชœเซ‡ เชซเช•เซเชค เชเช•เซเชธเชชเซเชฐเซ‡เชธ เชธเชฐเซเชตเชฐ เชธเชพเชฅเซ‡ เชœเซ‹เชกเชพเชฏ เช›เซ‡:

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() เชธเชฐเซเชตเชฐ-เชธเชพเช‡เชก เชฒเซ‹เชœเชฟเช•เชจเซ‹ เช•เชฆเชพเชš เชธเซŒเชฅเซ€ เชฎเชนเชคเซเชตเชชเซ‚เชฐเซเชฃ เชญเชพเช— เชธเชฎเชพเชตเซ‡ เช›เซ‡. เชคเซ‡ เช•เซเชฐเชฎเชฎเชพเช‚ เชถเซเช‚ เช•เชฐเซ‡ เช›เซ‡ เชคเซ‡ เช…เชนเซ€เช‚ เช›เซ‡:

  1. เช•เซ‡เชŸเชฒเชพ เชธเชฎเชฏเชจเซ€ เช—เชฃเชคเชฐเซ€ เช•เชฐเซ‡ เช›เซ‡ dt เช›เซ‡เชฒเซเชฒเชพ เชธเชฎเชฏเชฅเซ€ เชชเชธเชพเชฐ เชฅเชฏเซเช‚ update().
  2. เชฆเชฐเซ‡เช• เช…เชธเซเชคเซเชฐเชจเซ‡ เชคเชพเชœเซเช‚ เช•เชฐเซ‡ เช›เซ‡ เช…เชจเซ‡ เชœเซ‹ เชœเชฐเซ‚เชฐเซ€ เชนเซ‹เชฏ เชคเซ‹ เชคเซ‡เชจเซ‹ เชจเชพเชถ เช•เชฐเซ‡ เช›เซ‡. เช…เชฎเซ‡ เช† เช•เชพเชฐเซเชฏเช•เซเชทเชฎเชคเชพเชจเซ‹ เช…เชฎเชฒ เชชเช›เซ€เชฅเซ€ เชœเซ‹เชˆเชถเซเช‚. เชนเชฎเชฃเชพเช‚ เชฎเชพเชŸเซ‡, เช…เชฎเชพเชฐเชพ เชฎเชพเชŸเซ‡ เชคเซ‡ เชœเชพเชฃเชตเซเช‚ เชชเซ‚เชฐเชคเซเช‚ เช›เซ‡ bullet.update() เชชเชฐเชค เช•เชฐเซ‡ เช›เซ‡ trueเชœเซ‹ เช…เชธเซเชคเซเชฐเชจเซ‹ เชจเชพเชถ เชฅเชตเซ‹ เชœเซ‹เชˆเช (เชคเซ‡ เชเชฐเซ‡เชจเชพเชฎเชพเช‚เชฅเซ€ เชฌเชนเชพเชฐ เชจเซ€เช•เชณเซเชฏเซ‹).
  3. เชฆเชฐเซ‡เช• เช–เซ‡เชฒเชพเชกเซ€เชจเซ‡ เช…เชชเชกเซ‡เชŸ เช•เชฐเซ‡ เช›เซ‡ เช…เชจเซ‡ เชœเซ‹ เชœเชฐเซ‚เชฐเซ€ เชนเซ‹เชฏ เชคเซ‹ เช…เชธเซเชคเซเชฐ เชฌเชจเชพเชตเซ‡ เช›เซ‡. เช…เชฎเซ‡ เช† เช…เชฎเชฒเซ€เช•เชฐเชฃ เชชเชฃ เชชเช›เซ€เชฅเซ€ เชœเซ‹เชˆเชถเซเช‚ - player.update() เช‘เชฌเซเชœเซ‡เช•เซเชŸ เชชเชฐเชค เช•เชฐเซ€ เชถเช•เซ‡ เช›เซ‡ Bullet.
  4. เชธเชพเชฅเซ‡ เช…เชธเซเชคเซเชฐเซ‹ เช…เชจเซ‡ เช–เซ‡เชฒเชพเชกเซ€เช“ เชตเชšเซเชšเซ‡ เช…เชฅเชกเชพเชฎเชฃ เชฎเชพเชŸเซ‡ เชšเช•เชพเชธเซ‡ เช›เซ‡ applyCollisions(), เชœเซ‡ เช–เซ‡เชฒเชพเชกเซ€เช“เชจเซ‡ เชนเชฟเชŸ เช•เชฐเชคเชพ เช…เชธเซเชคเซเชฐเซ‹เชจเซ€ เชถเซเชฐเซ‡เชฃเซ€ เชชเชฐเชค เช•เชฐเซ‡ เช›เซ‡. เชชเชพเช›เชพ เชซเชฐเซ‡เชฒเชพ เชฆเชฐเซ‡เช• เช…เชธเซเชคเซเชฐ เชฎเชพเชŸเซ‡, เช…เชฎเซ‡ เชชเซเชฒเซ‡เชฏเชฐเชจเชพ เชชเซ‹เชˆเชจเซเชŸ เชตเชงเชพเชฐเซ€เช เช›เซ€เช เชœเซ‡เชฃเซ‡ เชคเซ‡เชจเซ‡ เชซเชพเชฏเชฐ เช•เชฐเซเชฏเซ‹ เชนเชคเซ‹ (เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ player.onDealtDamage()) เช…เชจเซ‡ เชชเช›เซ€ เชเชฐเซ‡เชฎเชพเช‚เชฅเซ€ เช…เชธเซเชคเซเชฐ เชฆเซ‚เชฐ เช•เชฐเซ‹ 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. เชจเซ‹เช‚เชง เช•เชฐเซ‹ เช•เซ‡ เชคเซ‡ เชซเช•เซเชค เชฆเชฐเซ‡เช• เช–เซ‡เชฒเชพเชกเซ€เชจเซ‡ เชกเซ‡เชŸเชพ เชชเชธเชพเชฐ เช•เชฐเซ‡ เช›เซ‡ เชจเชœเซ€เช•เชจเชพ เช–เซ‡เชฒเชพเชกเซ€เช“ เช…เชจเซ‡ เช…เชธเซเชคเซเชฐเซ‹ - เชฐเชฎเชคเชจเชพ เช‘เชฌเซเชœเซ‡เช•เซเชŸเซเชธ เชตเชฟเชถเซ‡ เชฎเชพเชนเชฟเชคเซ€ เชชเซเชฐเชธเชพเชฐเชฟเชค เช•เชฐเชตเชพเชจเซ€ เช•เซ‹เชˆ เชœเชฐเซ‚เชฐ เชจเชฅเซ€ เชœเซ‡ เช–เซ‡เชฒเชพเชกเซ€เชฅเซ€ เชฆเซ‚เชฐ เช›เซ‡!

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;
}

เช† เชธเชฐเชณ เช…เชฅเชกเชพเชฎเชฃ เชถเซ‹เชง เช เชนเช•เซ€เช•เชค เชชเชฐ เช†เชงเชพเชฐเชฟเชค เช›เซ‡ เช•เซ‡ เชฌเซ‡ เชตเชฐเซเชคเซเชณเซ‹ เช…เชฅเชกเชพเชฏ เช›เซ‡ เชœเซ‹ เชคเซ‡เชฎเชจเชพ เช•เซ‡เชจเซเชฆเซเชฐเซ‹ เชตเชšเซเชšเซ‡เชจเซเช‚ เช…เช‚เชคเชฐ เชคเซ‡เชฎเชจเซ€ เชคเซเชฐเชฟเชœเซเชฏเชพเชจเชพ เชธเชฐเชตเชพเชณเชพ เช•เชฐเชคเชพ เช“เช›เซเช‚ เชนเซ‹เชฏ. เช…เชนเซ€เช‚ เชเชตเซ‹ เช•เชฟเชธเซเชธเซ‹ เช›เซ‡ เช•เซ‡ เชœเซเชฏเชพเช‚ เชฌเซ‡ เชตเชฐเซเชคเซเชณเซ‹เชจเชพ เช•เซ‡เชจเซเชฆเซเชฐเซ‹ เชตเชšเซเชšเซ‡เชจเซเช‚ เช…เช‚เชคเชฐ เชคเซ‡เชฎเชจเซ€ เชคเซเชฐเชฟเชœเซเชฏเชพเชจเชพ เชธเชฐเชตเชพเชณเชพ เชฌเชฐเชพเชฌเชฐ เช›เซ‡:

เชฎเชฒเซเชŸเชฟเชชเซเชฒเซ‡เชฏเชฐ .io เชตเซ‡เชฌ เช—เซ‡เชฎ เชฌเชจเชพเชตเชตเซ€
เช…เชนเซ€เช‚ เชงเซเชฏเชพเชจเชฎเชพเช‚ เชฒเซ‡เชตเชพ เชฎเชพเชŸเซ‡ เช•เซ‡เชŸเชฒเชพเช• เชตเชงเซ เชชเชพเชธเชพเช“ เช›เซ‡:

  • เช…เชธเซเชคเซเชฐ เช เชฌเชจเชพเชตเชจเชพเชฐ เช–เซ‡เชฒเชพเชกเซ€เชจเซ‡ เชฎเชพเชฐเชตเซเช‚ เชœเซ‹เชˆเช เชจเชนเซ€เช‚. เช† เชธเชฐเช–เชพเชฎเชฃเซ€ เช•เชฐเซ€เชจเซ‡ เชชเซเชฐเชพเชชเซเชค เช•เชฐเซ€ เชถเช•เชพเชฏ เช›เซ‡ bullet.parentID ั player.id.
  • เชเช• เชœ เชธเชฎเชฏเซ‡ เชฌเชนเซเชตเชฟเชง เช–เซ‡เชฒเชพเชกเซ€เช“ เช…เชฅเชกเชพเชคเชพ เชนเซ‹เชตเชพเชจเชพ เชฎเชฐเซเชฏเชพเชฆเชฟเชค เช•เชฟเชธเซเชธเชพเชฎเชพเช‚ เช…เชธเซเชคเซเชฐ เชฎเชพเชคเซเชฐ เชเช• เชœ เชตเชพเชฐ เชฎเชพเชฐเชตเซ‹ เชœเซ‹เชˆเช. เช…เชฎเซ‡ เช“เชชเชฐเซ‡เชŸเชฐเชจเซ‹ เช‰เชชเชฏเซ‹เช— เช•เชฐเซ€เชจเซ‡ เช† เชธเชฎเชธเซเชฏเชพ เชนเชฒ เช•เชฐเซ€เชถเซเช‚ break: เชœเชฒเชฆเซ€ เช…เชธเซเชคเซเชฐ เชธเชพเชฅเซ‡ เช…เชฅเชกเชพเชคเซ‹ เช–เซ‡เชฒเชพเชกเซ€ เชฎเชณเซ€ เช†เชตเซ‡ เช›เซ‡, เช…เชฎเซ‡ เชถเซ‹เชง เชฌเช‚เชง เช•เชฐเซ€เช เช›เซ€เช เช…เชจเซ‡ เช†เช—เชฒเชพ เช…เชธเซเชคเซเชฐ เชคเชฐเชซ เช†เช—เชณ เชตเชงเซ€เช เช›เซ€เช.

เช…เช‚เชค

เชฌเชธ เชเชŸเชฒเซเช‚ เชœ! .io เชตเซ‡เชฌ เช—เซ‡เชฎ เชฌเชจเชพเชตเชตเชพ เชฎเชพเชŸเซ‡ เชคเชฎเชพเชฐเซ‡ เชœเซ‡ เชœเชพเชฃเชตเชพเชจเซ€ เชœเชฐเซ‚เชฐ เช›เซ‡ เชคเซ‡ เชฌเชงเซเช‚ เช…เชฎเซ‡ เช†เชตเชฐเซ€ เชฒเซ€เชงเซเช‚ เช›เซ‡. เช†เช—เชณ เชถเซเช‚ เช›เซ‡? เชคเชฎเชพเชฐเซ€ เชชเซ‹เชคเชพเชจเซ€ .io เช—เซ‡เชฎ เชฌเชจเชพเชตเซ‹!

เชฌเชงเชพ เชจเชฎเซ‚เชจเชพ เช•เซ‹เชก เช“เชชเชจ เชธเซ‹เชฐเซเชธ เช›เซ‡ เช…เชจเซ‡ เชคเซ‡เชจเชพ เชชเชฐ เชชเซ‹เชธเซเชŸ เช•เชฐเชตเชพเชฎเชพเช‚ เช†เชตเซเชฏเซ‹ เช›เซ‡ Github.

เชธเซ‹เชฐเซเชธ: www.habr.com

เชเช• เชŸเชฟเชชเซเชชเชฃเซ€ เช‰เชฎเซ‡เชฐเซ‹