Multiplayer .io แƒ•แƒ”แƒ‘ แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒจแƒ”แƒฅแƒ›แƒœแƒ

Multiplayer .io แƒ•แƒ”แƒ‘ แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒจแƒ”แƒฅแƒ›แƒœแƒ
แƒ’แƒแƒ›แƒแƒ•แƒ˜แƒ“แƒ 2015 แƒฌแƒ”แƒšแƒก Agar.io แƒ’แƒแƒฎแƒ“แƒ แƒแƒฎแƒแƒšแƒ˜ แƒŸแƒแƒœแƒ แƒ˜แƒก แƒฌแƒ˜แƒœแƒแƒ›แƒแƒ แƒ‘แƒ”แƒ“แƒ˜ แƒ—แƒแƒ›แƒแƒจแƒ”แƒ‘แƒ˜ .ioแƒ แƒแƒ›แƒ”แƒšแƒ˜แƒช แƒ›แƒแƒก แƒจแƒ”แƒ›แƒ“แƒ”แƒ’ แƒ’แƒแƒ˜แƒ–แƒแƒ แƒ“แƒ แƒžแƒแƒžแƒฃแƒšแƒแƒ แƒแƒ‘แƒ˜แƒ—. แƒ›แƒ” แƒžแƒ˜แƒ แƒแƒ“แƒแƒ“ แƒ’แƒแƒœแƒ•แƒ˜แƒชแƒ“แƒ˜แƒ“แƒ˜ .io แƒ—แƒแƒ›แƒแƒจแƒ”แƒ‘แƒ˜แƒก แƒžแƒแƒžแƒฃแƒšแƒแƒ แƒแƒ‘แƒ˜แƒก แƒ–แƒ แƒ“แƒแƒก: แƒ‘แƒแƒšแƒ แƒกแƒแƒ›แƒ˜ แƒฌแƒšแƒ˜แƒก แƒ’แƒแƒœแƒ›แƒแƒ•แƒšแƒแƒ‘แƒแƒจแƒ˜ แƒ›แƒฅแƒแƒœแƒ“แƒ แƒจแƒ”แƒฅแƒ›แƒœแƒ แƒ“แƒ แƒ’แƒแƒงแƒ˜แƒ“แƒ แƒแƒ แƒ˜ แƒ—แƒแƒ›แƒแƒจแƒ˜ แƒแƒ› แƒŸแƒแƒœแƒ แƒจแƒ˜..

แƒ—แƒฃ แƒแƒฅแƒแƒ›แƒ“แƒ” แƒแƒ แƒแƒกแƒแƒ“แƒ”แƒก แƒ’แƒกแƒ›แƒ”แƒœแƒ˜แƒแƒ— แƒแƒ› แƒ—แƒแƒ›แƒแƒจแƒ”แƒ‘แƒ˜แƒก แƒจแƒ”แƒกแƒแƒฎแƒ”แƒ‘, แƒ”แƒก แƒแƒ แƒ˜แƒก แƒฃแƒคแƒแƒกแƒ แƒ›แƒ แƒแƒ•แƒแƒšแƒ›แƒแƒ—แƒแƒ›แƒแƒจแƒ˜แƒแƒœแƒ˜ แƒ•แƒ”แƒ‘ แƒ—แƒแƒ›แƒแƒจแƒ”แƒ‘แƒ˜, แƒ แƒแƒ›แƒ”แƒšแƒ—แƒ แƒ—แƒแƒ›แƒแƒจแƒ˜ แƒ›แƒแƒ แƒขแƒ˜แƒ•แƒ˜แƒ (แƒแƒ  แƒแƒ แƒ˜แƒก แƒกแƒแƒญแƒ˜แƒ แƒ แƒแƒœแƒ’แƒแƒ แƒ˜แƒจแƒ˜). แƒ แƒแƒ’แƒแƒ แƒช แƒฌแƒ”แƒกแƒ˜, แƒ˜แƒกแƒ˜แƒœแƒ˜ แƒ”แƒ แƒ—แƒกแƒ แƒ“แƒ แƒ˜แƒ›แƒแƒ•แƒ” แƒแƒ แƒ”แƒœแƒแƒ–แƒ” แƒ‘แƒ”แƒ•แƒ  แƒ›แƒแƒฌแƒ˜แƒœแƒแƒแƒฆแƒ›แƒ“แƒ”แƒ’แƒ” แƒ›แƒแƒ—แƒแƒ›แƒแƒจแƒ”แƒก แƒฎแƒ•แƒ“แƒ”แƒ‘แƒ˜แƒแƒœ. แƒกแƒฎแƒ•แƒ แƒชแƒœแƒแƒ‘แƒ˜แƒšแƒ˜ .io แƒ—แƒแƒ›แƒแƒจแƒ”แƒ‘แƒ˜: Slither.io ะธ Diep.io.

แƒแƒ› แƒžแƒแƒกแƒขแƒจแƒ˜ แƒฉแƒ•แƒ”แƒœ แƒ’แƒแƒ•แƒแƒ แƒ™แƒ•แƒ”แƒ•แƒ— แƒ แƒแƒ’แƒแƒ  แƒจแƒ”แƒฅแƒ›แƒ”แƒœแƒ˜แƒ— .io แƒ—แƒแƒ›แƒแƒจแƒ˜ แƒœแƒฃแƒšแƒ˜แƒ“แƒแƒœ. แƒแƒ›แƒ˜แƒกแƒแƒ—แƒ•แƒ˜แƒก แƒกแƒแƒ™แƒ›แƒแƒ แƒ˜แƒกแƒ˜ แƒ˜แƒฅแƒœแƒ”แƒ‘แƒ แƒ›แƒฎแƒแƒšแƒแƒ“ Javascript-แƒ˜แƒก แƒชแƒแƒ“แƒœแƒ: แƒ—แƒฅแƒ•แƒ”แƒœ แƒฃแƒœแƒ“แƒ แƒ’แƒ”แƒกแƒ›แƒแƒ“แƒ”แƒ— แƒ˜แƒกแƒ”แƒ—แƒ˜ แƒ แƒแƒฆแƒแƒชแƒ”แƒ”แƒ‘แƒ˜, แƒ แƒแƒ’แƒแƒ แƒ˜แƒชแƒแƒ แƒกแƒ˜แƒœแƒขแƒแƒฅแƒกแƒ˜ ES6, แƒกแƒแƒ™แƒ•แƒแƒœแƒซแƒ แƒกแƒ˜แƒขแƒงแƒ•แƒ this ะธ แƒ“แƒแƒžแƒ˜แƒ แƒ”แƒ‘แƒ”แƒ‘แƒ˜. แƒ›แƒแƒจแƒ˜แƒœแƒแƒช แƒ™แƒ˜, แƒ—แƒฃ Javascript แƒกแƒ แƒฃแƒšแƒงแƒแƒคแƒ˜แƒšแƒแƒ“ แƒแƒ  แƒ˜แƒชแƒ˜แƒ—, แƒ›แƒแƒ˜แƒœแƒช แƒ’แƒ”แƒกแƒ›แƒ˜แƒ— แƒžแƒแƒกแƒขแƒ˜แƒก แƒฃแƒ›แƒ”แƒขแƒ”แƒกแƒ˜ แƒœแƒแƒฌแƒ˜แƒšแƒ˜.

.io แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒ›แƒแƒ’แƒแƒšแƒ˜แƒ—แƒ˜

แƒกแƒแƒกแƒฌแƒแƒ•แƒšแƒ แƒ“แƒแƒฎแƒ›แƒแƒ แƒ”แƒ‘แƒ˜แƒกแƒ—แƒ•แƒ˜แƒก แƒ›แƒ˜แƒ•แƒ›แƒแƒ แƒ—แƒแƒ•แƒ— แƒ›แƒแƒ’แƒแƒšแƒ˜แƒ—แƒ˜ แƒ—แƒแƒ›แƒแƒจแƒ˜ .io. แƒกแƒชแƒแƒ“แƒ” แƒ˜แƒ—แƒแƒ›แƒแƒจแƒ!

Multiplayer .io แƒ•แƒ”แƒ‘ แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒจแƒ”แƒฅแƒ›แƒœแƒ
แƒ—แƒแƒ›แƒแƒจแƒ˜ แƒกแƒแƒ™แƒ›แƒแƒแƒ“ แƒ›แƒแƒ แƒขแƒ˜แƒ•แƒ˜แƒ: แƒ—แƒฅแƒ•แƒ”แƒœ แƒแƒ™แƒแƒœแƒขแƒ แƒแƒšแƒ”แƒ‘แƒ— แƒฎแƒแƒ›แƒแƒšแƒ“แƒก แƒแƒ แƒ”แƒœแƒแƒ–แƒ” แƒกแƒฎแƒ•แƒ แƒ›แƒแƒ—แƒแƒ›แƒแƒจแƒ”แƒ”แƒ‘แƒ—แƒแƒœ แƒ”แƒ แƒ—แƒแƒ“. แƒ—แƒฅแƒ•แƒ”แƒœแƒ˜ แƒ’แƒ”แƒ›แƒ˜ แƒแƒ•แƒขแƒแƒ›แƒแƒขแƒฃแƒ แƒแƒ“ แƒ˜แƒกแƒ•แƒ แƒ˜แƒก แƒญแƒฃแƒ แƒ•แƒ”แƒ‘แƒก แƒ“แƒ แƒ—แƒฅแƒ•แƒ”แƒœ แƒชแƒ“แƒ˜แƒšแƒแƒ‘แƒ— แƒ“แƒแƒแƒ แƒขแƒงแƒแƒ— แƒกแƒฎแƒ•แƒ แƒ›แƒแƒ—แƒแƒ›แƒแƒจแƒ”แƒ”แƒ‘แƒก, แƒฎแƒแƒšแƒ แƒ—แƒแƒ•แƒ˜แƒ“แƒแƒœ แƒแƒ˜แƒชแƒ˜แƒšแƒแƒ— แƒ›แƒแƒ—แƒ˜ แƒญแƒฃแƒ แƒ•แƒ”แƒ‘แƒ˜.

1. แƒžแƒ แƒแƒ”แƒฅแƒขแƒ˜แƒก แƒ›แƒแƒ™แƒšแƒ” แƒ›แƒ˜แƒ›แƒแƒฎแƒ˜แƒšแƒ•แƒ/แƒกแƒขแƒ แƒฃแƒฅแƒขแƒฃแƒ แƒ

แƒ•แƒฃแƒ แƒฉแƒ”แƒ• แƒฉแƒแƒ›แƒแƒขแƒ•แƒ˜แƒ แƒ—แƒ”แƒ— แƒกแƒแƒฌแƒงแƒ˜แƒกแƒ˜ แƒ™แƒแƒ“แƒ˜ แƒกแƒแƒ›แƒแƒ’แƒแƒšแƒ˜แƒ—แƒ แƒ—แƒแƒ›แƒแƒจแƒ˜, แƒแƒกแƒ” แƒ แƒแƒ› แƒ—แƒฅแƒ•แƒ”แƒœ แƒจแƒ”แƒ’แƒ˜แƒซแƒšแƒ˜แƒแƒ— แƒ›แƒแƒ›แƒงแƒ•แƒ”แƒ—.

แƒ›แƒแƒ’แƒแƒšแƒ˜แƒ—แƒ˜ แƒ˜แƒงแƒ”แƒœแƒ”แƒ‘แƒก แƒจแƒ”แƒ›แƒ“แƒ”แƒ’แƒก:

  • แƒ”แƒฅแƒกแƒžแƒ แƒ”แƒกแƒ˜ แƒแƒ แƒ˜แƒก แƒงแƒ•แƒ”แƒšแƒแƒ–แƒ” แƒžแƒแƒžแƒฃแƒšแƒแƒ แƒฃแƒšแƒ˜ Node.js แƒ•แƒ”แƒ‘ แƒฉแƒแƒ แƒฉแƒ, แƒ แƒแƒ›แƒ”แƒšแƒ˜แƒช แƒ›แƒแƒ แƒ—แƒแƒ•แƒก แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒ•แƒ”แƒ‘ แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒก.
  • แƒกแƒแƒ™แƒ”แƒขแƒ˜ โ€” แƒ•แƒ”แƒ‘แƒกแƒแƒ™แƒ”แƒขแƒ˜แƒก แƒ‘แƒ˜แƒ‘แƒšแƒ˜แƒแƒ—แƒ”แƒ™แƒ แƒ‘แƒ แƒแƒฃแƒ–แƒ”แƒ แƒกแƒ แƒ“แƒ แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒก แƒจแƒแƒ แƒ˜แƒก แƒ›แƒแƒœแƒแƒชแƒ”แƒ›แƒ—แƒ แƒ’แƒแƒชแƒ•แƒšแƒ˜แƒกแƒ—แƒ•แƒ˜แƒก.
  • แƒ•แƒ”แƒ‘แƒ’แƒ•แƒ”แƒ แƒ“แƒ˜ - แƒ›แƒแƒ“แƒฃแƒšแƒ˜แƒก แƒ›แƒ”แƒœแƒ”แƒฏแƒ”แƒ แƒ˜. แƒ—แƒฅแƒ•แƒ”แƒœ แƒจแƒ”แƒ’แƒ˜แƒซแƒšแƒ˜แƒแƒ— แƒฌแƒแƒ˜แƒ™แƒ˜แƒ—แƒฎแƒแƒ— แƒ˜แƒ›แƒ˜แƒก แƒจแƒ”แƒกแƒแƒฎแƒ”แƒ‘, แƒ—แƒฃ แƒ แƒแƒขแƒแƒ› แƒฃแƒœแƒ“แƒ แƒ’แƒแƒ›แƒแƒ˜แƒงแƒ”แƒœแƒแƒ— 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.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 แƒ“แƒแƒ˜แƒฌแƒงแƒ”แƒ‘แƒ แƒแƒฅแƒ”แƒ“แƒแƒœ แƒ“แƒ แƒ แƒ”แƒ™แƒฃแƒ แƒกแƒ˜แƒฃแƒšแƒแƒ“ แƒ›แƒแƒซแƒ”แƒ‘แƒœแƒ˜แƒก แƒกแƒฎแƒ•แƒ แƒ˜แƒ›แƒžแƒแƒ แƒขแƒ˜แƒ แƒ”แƒ‘แƒฃแƒš แƒคแƒแƒ˜แƒšแƒ”แƒ‘แƒก.
  • แƒฉแƒ•แƒ”แƒœแƒ˜ Webpack build-แƒ˜แƒก แƒ’แƒแƒ›แƒแƒ›แƒแƒ•แƒแƒšแƒ˜ JS แƒ’แƒแƒœแƒ—แƒแƒ•แƒกแƒ“แƒ”แƒ‘แƒ แƒ“แƒ˜แƒ แƒ”แƒฅแƒขแƒแƒ แƒ˜แƒแƒจแƒ˜ dist/. แƒแƒ› แƒคแƒแƒ˜แƒšแƒก แƒฉแƒ•แƒ”แƒœแƒก แƒ“แƒแƒ•แƒแƒ แƒฅแƒ›แƒ”แƒ• JS แƒžแƒแƒ™แƒ”แƒขแƒ˜.
  • แƒฉแƒ•แƒ”แƒœ แƒ•แƒ˜แƒงแƒ”แƒœแƒ”แƒ‘แƒ— Babelแƒ“แƒ แƒ™แƒ”แƒ แƒซแƒแƒ“ แƒ™แƒแƒœแƒคแƒ˜แƒ’แƒฃแƒ แƒแƒชแƒ˜แƒ @babel/preset-env แƒฉแƒ•แƒ”แƒœแƒ˜ JS แƒ™แƒแƒ“แƒ˜แƒก แƒ’แƒแƒ“แƒแƒกแƒแƒขแƒแƒœแƒแƒ“ แƒซแƒ•แƒ”แƒšแƒ˜ แƒ‘แƒ แƒแƒฃแƒ–แƒ”แƒ แƒ”แƒ‘แƒ˜แƒกแƒ—แƒ•แƒ˜แƒก.
  • แƒฉแƒ•แƒ”แƒœ แƒ•แƒ˜แƒงแƒ”แƒœแƒ”แƒ‘แƒ— แƒ“แƒแƒœแƒแƒ›แƒแƒขแƒก, แƒ แƒแƒ› แƒแƒ›แƒแƒ•แƒ˜แƒฆแƒแƒ— แƒงแƒ•แƒ”แƒšแƒ CSS, แƒ แƒแƒ›แƒ”แƒšแƒ˜แƒช แƒ›แƒ˜แƒ—แƒ˜แƒ—แƒ”แƒ‘แƒฃแƒšแƒ˜แƒ JS แƒคแƒแƒ˜แƒšแƒ”แƒ‘แƒ˜แƒ— แƒ“แƒ แƒ’แƒแƒ•แƒแƒ”แƒ แƒ—แƒ˜แƒแƒœแƒแƒ— แƒ˜แƒกแƒ˜แƒœแƒ˜ แƒ”แƒ แƒ— แƒแƒ“แƒ’แƒ˜แƒšแƒแƒก. แƒฉแƒ•แƒ”แƒœแƒก แƒ“แƒแƒ•แƒแƒ แƒฅแƒ›แƒ”แƒ• 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แƒฌแƒแƒ แƒ›แƒแƒ”แƒ‘แƒแƒจแƒ˜ แƒ’แƒแƒœแƒ—แƒแƒ•แƒกแƒ”แƒ‘แƒ˜แƒกแƒแƒก แƒžแƒแƒ™แƒ”แƒขแƒ˜แƒก แƒ–แƒแƒ›แƒ˜แƒก แƒแƒžแƒขแƒ˜แƒ›แƒ˜แƒ–แƒแƒชแƒ˜แƒ.

แƒแƒ“แƒ’แƒ˜แƒšแƒแƒ‘แƒ แƒ˜แƒ•แƒ˜ แƒ“แƒแƒงแƒ”แƒœแƒ”แƒ‘แƒ

แƒ›แƒ” แƒ’แƒ˜แƒ แƒฉแƒ”แƒ•แƒ— แƒ“แƒแƒแƒ˜แƒœแƒกแƒขแƒแƒšแƒ˜แƒ แƒแƒ— แƒžแƒ แƒแƒ”แƒฅแƒขแƒ˜ แƒแƒ“แƒ’แƒ˜แƒšแƒแƒ‘แƒ แƒ˜แƒ• แƒ›แƒแƒœแƒฅแƒแƒœแƒแƒ–แƒ”, แƒแƒกแƒ” แƒ แƒแƒ› แƒ—แƒฅแƒ•แƒ”แƒœ แƒจแƒ”แƒ’แƒ˜แƒซแƒšแƒ˜แƒแƒ— แƒ›แƒ˜แƒงแƒ•แƒ”แƒ— แƒแƒ› แƒžแƒแƒกแƒขแƒจแƒ˜ แƒฉแƒแƒ›แƒแƒ—แƒ•แƒšแƒ˜แƒš แƒœแƒแƒ‘แƒ˜แƒฏแƒ”แƒ‘แƒก. แƒ“แƒแƒงแƒ”แƒœแƒ”แƒ‘แƒ แƒ›แƒแƒ แƒขแƒ˜แƒ•แƒ˜แƒ: แƒžแƒ˜แƒ แƒ•แƒ”แƒš แƒ แƒ˜แƒ’แƒจแƒ˜, แƒกแƒ˜แƒกแƒขแƒ”แƒ›แƒ แƒฃแƒœแƒ“แƒ แƒ˜แƒงแƒแƒก แƒ“แƒแƒ˜แƒœแƒกแƒขแƒแƒšแƒ˜แƒ แƒ”แƒ‘แƒฃแƒšแƒ˜ Node ะธ NPM. แƒจแƒ”แƒ›แƒ“แƒ”แƒ’แƒ˜ แƒ—แƒฅแƒ•แƒ”แƒœ แƒฃแƒœแƒ“แƒ แƒ’แƒแƒแƒ™แƒ”แƒ—แƒแƒ—

$ 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> แƒ“แƒ แƒฆแƒ˜แƒšแƒแƒ™แƒ˜ "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-แƒก แƒแƒฅแƒ•แƒก แƒ›แƒจแƒแƒ‘แƒšแƒ˜แƒฃแƒ แƒ˜ แƒ›แƒฎแƒแƒ แƒ“แƒแƒญแƒ”แƒ แƒ แƒ•แƒ”แƒ‘ -แƒกแƒแƒ™แƒ”แƒขแƒ”แƒ‘แƒ˜, แƒ แƒแƒ›แƒšแƒ”แƒ‘แƒ˜แƒช แƒ™แƒแƒ แƒ’แƒแƒ“ แƒจแƒ”แƒ”แƒคแƒ”แƒ แƒ”แƒ‘แƒ แƒแƒ แƒ›แƒฎแƒ แƒ˜แƒ• แƒ™แƒแƒ›แƒฃแƒœแƒ˜แƒ™แƒแƒชแƒ˜แƒแƒก: แƒจแƒ”แƒ’แƒ•แƒ˜แƒซแƒšแƒ˜แƒ แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ–แƒ” แƒจแƒ”แƒขแƒงแƒแƒ‘แƒ˜แƒœแƒ”แƒ‘แƒ”แƒ‘แƒ˜แƒก แƒ’แƒแƒ’แƒ–แƒแƒ•แƒœแƒ ะธ แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒก แƒจแƒ”แƒฃแƒซแƒšแƒ˜แƒ แƒ’แƒแƒ›แƒแƒ’แƒ•แƒ˜แƒ’แƒ–แƒแƒ•แƒœแƒแƒก แƒจแƒ”แƒขแƒงแƒแƒ‘แƒ˜แƒœแƒ”แƒ‘แƒ”แƒ‘แƒ˜ แƒ˜แƒ›แƒแƒ•แƒ” แƒ™แƒแƒ•แƒจแƒ˜แƒ แƒ˜แƒ—.

แƒ’แƒ•แƒ”แƒฅแƒœแƒ”แƒ‘แƒ แƒ”แƒ แƒ—แƒ˜ แƒคแƒแƒ˜แƒšแƒ˜ src/client/networking.jsแƒ•แƒ˜แƒœแƒช แƒ˜แƒ–แƒ แƒฃแƒœแƒ”แƒ‘แƒก แƒงแƒ•แƒ”แƒšแƒแƒก แƒ›แƒ˜แƒ”แƒ  แƒ™แƒแƒ›แƒฃแƒœแƒ˜แƒ™แƒแƒชแƒ˜แƒ แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ—แƒแƒœ:

แƒฅแƒกแƒ”แƒšแƒจแƒ˜.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>). แƒฉแƒ•แƒ”แƒœแƒ˜ แƒ—แƒแƒ›แƒแƒจแƒ˜ แƒกแƒแƒ™แƒ›แƒแƒแƒ“ แƒ›แƒแƒ แƒขแƒ˜แƒ•แƒ˜แƒ, แƒแƒ›แƒ˜แƒขแƒแƒ› แƒฉแƒ•แƒ”แƒœ แƒ›แƒฎแƒแƒšแƒแƒ“ แƒจแƒ”แƒ›แƒ“แƒ”แƒ’แƒ˜ แƒฃแƒœแƒ“แƒ แƒ“แƒแƒ•แƒฎแƒแƒขแƒแƒ—:

  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:

แƒจแƒ”แƒงแƒ•แƒแƒœแƒ.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() (of networking.js) แƒ แƒแƒ“แƒ”แƒกแƒแƒช แƒฎแƒ“แƒ”แƒ‘แƒ แƒจแƒ”แƒงแƒ•แƒแƒœแƒ˜แƒก แƒ›แƒแƒ•แƒšแƒ”แƒœแƒ (แƒ›แƒแƒ’แƒแƒšแƒ˜แƒ—แƒแƒ“, แƒ›แƒแƒฃแƒกแƒ˜แƒก แƒ’แƒแƒ“แƒแƒแƒ“แƒ’แƒ˜แƒšแƒ”แƒ‘แƒ˜แƒกแƒแƒก). updateDirection() แƒ”แƒฎแƒ”แƒ‘แƒ แƒจแƒ”แƒขแƒงแƒแƒ‘แƒ˜แƒœแƒ”แƒ‘แƒ”แƒ‘แƒ˜แƒก แƒ’แƒแƒชแƒ•แƒšแƒแƒก แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ—แƒแƒœ, แƒ แƒแƒ›แƒ”แƒšแƒ˜แƒช แƒแƒ›แƒฃแƒจแƒแƒ•แƒ”แƒ‘แƒก แƒจแƒ”แƒงแƒ•แƒแƒœแƒ˜แƒก แƒ›แƒแƒ•แƒšแƒ”แƒœแƒแƒก แƒ“แƒ แƒจแƒ”แƒกแƒแƒ‘แƒแƒ›แƒ˜แƒกแƒแƒ“ แƒ’แƒแƒœแƒแƒแƒฎแƒšแƒ”แƒ‘แƒก แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒ›แƒ“แƒ’แƒแƒ›แƒแƒ แƒ”แƒแƒ‘แƒแƒก.

7. แƒ™แƒšแƒ˜แƒ”แƒœแƒขแƒ˜แƒก แƒกแƒขแƒแƒขแƒฃแƒกแƒ˜

แƒ”แƒก แƒ’แƒแƒœแƒงแƒแƒคแƒ˜แƒšแƒ”แƒ‘แƒ แƒงแƒ•แƒ”แƒšแƒแƒ–แƒ” แƒ แƒ—แƒฃแƒšแƒ˜แƒ แƒžแƒแƒกแƒขแƒ˜แƒก แƒžแƒ˜แƒ แƒ•แƒ”แƒš แƒœแƒแƒฌแƒ˜แƒšแƒจแƒ˜. แƒœแƒฃ แƒ˜แƒ›แƒ”แƒ“แƒ’แƒแƒชแƒ แƒฃแƒ”แƒ‘แƒ—, แƒ—แƒฃ แƒแƒ  แƒ’แƒ”แƒกแƒ›แƒ˜แƒ— แƒžแƒ˜แƒ แƒ•แƒ”แƒšแƒแƒ“ แƒฌแƒแƒ™แƒ˜แƒ—แƒฎแƒ•แƒ˜แƒกแƒแƒก! แƒ—แƒฅแƒ•แƒ”แƒœ แƒ™แƒ˜ แƒจแƒ”แƒ’แƒ˜แƒซแƒšแƒ˜แƒแƒ— แƒ’แƒแƒ›แƒแƒขแƒแƒ•แƒแƒ— แƒ˜แƒ’แƒ˜ แƒ“แƒ แƒ›แƒแƒ’แƒ•แƒ˜แƒแƒœแƒ”แƒ‘แƒ˜แƒ— แƒ“แƒแƒฃแƒ‘แƒ แƒฃแƒœแƒ“แƒ”แƒ— แƒ›แƒแƒก.

แƒ™แƒšแƒ˜แƒ”แƒœแƒข-แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜แƒก แƒ™แƒแƒ“แƒ˜แƒก แƒ“แƒแƒกแƒแƒกแƒ แƒฃแƒšแƒ”แƒ‘แƒšแƒแƒ“ แƒกแƒแƒญแƒ˜แƒ แƒ แƒ—แƒแƒ•แƒกแƒแƒขแƒ”แƒฎแƒ˜แƒก แƒ‘แƒแƒšแƒ แƒœแƒแƒฌแƒ˜แƒšแƒ˜แƒ แƒ˜แƒงแƒ. แƒ’แƒแƒฎแƒกแƒแƒ•แƒ— แƒ™แƒแƒ“แƒ˜แƒก แƒœแƒแƒฌแƒงแƒ•แƒ”แƒขแƒ˜ Client Rendering แƒ’แƒแƒœแƒงแƒแƒคแƒ˜แƒšแƒ”แƒ‘แƒ˜แƒ“แƒแƒœ?

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: แƒ˜แƒœแƒคแƒแƒ แƒ›แƒแƒชแƒ˜แƒ แƒ›แƒแƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒจแƒ”แƒกแƒแƒฎแƒ”แƒ‘, แƒ แƒแƒ›แƒ”แƒšแƒ˜แƒช แƒ˜แƒฆแƒ”แƒ‘แƒก แƒแƒ› แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒแƒก.
  • แƒกแƒฎแƒ•แƒ”แƒ‘แƒ˜: แƒ˜แƒœแƒคแƒแƒ แƒ›แƒแƒชแƒ˜แƒ˜แƒก แƒ›แƒแƒกแƒ˜แƒ•แƒ˜ แƒกแƒฎแƒ•แƒ แƒ›แƒแƒ—แƒแƒ›แƒแƒจแƒ”แƒ”แƒ‘แƒ˜แƒก แƒจแƒ”แƒกแƒแƒฎแƒ”แƒ‘, แƒ แƒแƒ›แƒšแƒ”แƒ‘แƒ˜แƒช แƒ›แƒแƒœแƒแƒฌแƒ˜แƒšแƒ”แƒแƒ‘แƒ”แƒœ แƒ˜แƒ›แƒแƒ•แƒ” แƒ—แƒแƒ›แƒแƒจแƒจแƒ˜.
  • แƒขแƒงแƒ•แƒ˜แƒ”แƒ‘แƒ˜: แƒ˜แƒœแƒคแƒแƒ แƒ›แƒแƒชแƒ˜แƒ˜แƒก แƒ›แƒแƒกแƒ˜แƒ•แƒ˜ แƒ—แƒแƒ›แƒแƒจแƒจแƒ˜ แƒญแƒฃแƒ แƒ•แƒ”แƒ‘แƒ˜แƒก แƒจแƒ”แƒกแƒแƒฎแƒ”แƒ‘.
  • leaderboard: แƒšแƒ˜แƒ“แƒ”แƒ แƒ‘แƒแƒ แƒ“แƒ˜แƒก แƒ›แƒ˜แƒ›แƒ“แƒ˜แƒœแƒแƒ แƒ” แƒ›แƒแƒœแƒแƒชแƒ”แƒ›แƒ”แƒ‘แƒ˜. แƒฉแƒ•แƒ”แƒœ แƒ›แƒแƒ— แƒแƒ  แƒ’แƒแƒ•แƒ˜แƒ—แƒ•แƒแƒšแƒ˜แƒกแƒฌแƒ˜แƒœแƒ”แƒ‘แƒ— แƒแƒ› แƒžแƒแƒกแƒขแƒจแƒ˜.

7.1 แƒ’แƒฃแƒšแƒฃแƒ‘แƒ แƒงแƒ•แƒ˜แƒšแƒ แƒ™แƒšแƒ˜แƒ”แƒœแƒขแƒ˜แƒก แƒ›แƒ“แƒ’แƒแƒ›แƒแƒ แƒ”แƒแƒ‘แƒ

แƒ’แƒฃแƒšแƒฃแƒ‘แƒ แƒงแƒ•แƒ˜แƒšแƒ แƒ’แƒแƒœแƒฎแƒแƒ แƒชแƒ˜แƒ”แƒšแƒ”แƒ‘แƒ getCurrentState() แƒจแƒ”แƒฃแƒซแƒšแƒ˜แƒ แƒžแƒ˜แƒ แƒ“แƒแƒžแƒ˜แƒ  แƒ“แƒแƒแƒ‘แƒ แƒฃแƒœแƒแƒก แƒ›แƒแƒœแƒแƒชแƒ”แƒ›แƒ”แƒ‘แƒ˜ แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒ‘แƒแƒšแƒ แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒ˜แƒ“แƒแƒœ.

แƒ’แƒฃแƒšแƒฃแƒ‘แƒ แƒงแƒ•แƒ˜แƒšแƒ-แƒกแƒแƒฎแƒ”แƒšแƒ›แƒฌแƒ˜แƒคแƒ.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.

Tick โ€‹โ€‹Rate: แƒกแƒ˜แƒฎแƒจแƒ˜แƒ แƒ”, แƒ แƒแƒ›แƒšแƒ˜แƒ—แƒแƒช แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜ แƒฃแƒ’แƒ–แƒแƒ•แƒœแƒ˜แƒก แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒ”แƒ‘แƒก แƒ™แƒšแƒ˜แƒ”แƒœแƒขแƒ”แƒ‘แƒก. แƒ˜แƒก แƒฎแƒจแƒ˜แƒ แƒแƒ“ แƒฃแƒคแƒ แƒ แƒ“แƒแƒ‘แƒแƒšแƒ˜แƒ แƒ•แƒ˜แƒ“แƒ แƒ” แƒ™แƒแƒ“แƒ แƒ”แƒ‘แƒ˜แƒก แƒกแƒ˜แƒฎแƒจแƒ˜แƒ แƒ”. แƒฉแƒ•แƒ”แƒœแƒก แƒ—แƒแƒ›แƒแƒจแƒจแƒ˜ แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜ แƒ›แƒฃแƒจแƒแƒแƒ‘แƒก แƒฌแƒแƒ›แƒจแƒ˜ 30 แƒขแƒ™แƒ˜แƒžแƒ–แƒ”.

แƒ—แƒฃ แƒฉแƒ•แƒ”แƒœ แƒฃแƒ‘แƒ แƒแƒšแƒแƒ“ แƒ’แƒแƒ“แƒแƒ•แƒ˜แƒขแƒแƒœแƒ— แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒฃแƒแƒฎแƒšแƒ”แƒก แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒแƒก, แƒ›แƒแƒจแƒ˜แƒœ FPS แƒแƒ แƒกแƒ”แƒ‘แƒ˜แƒ—แƒแƒ“ แƒแƒ แƒแƒกแƒแƒ“แƒ”แƒก แƒ’แƒแƒ“แƒแƒแƒญแƒแƒ แƒ‘แƒ”แƒ‘แƒก 30-แƒก, แƒ แƒแƒ“แƒ’แƒแƒœ แƒฉแƒ•แƒ”แƒœ แƒแƒ แƒแƒกแƒ“แƒ แƒแƒก แƒ•แƒ˜แƒฆแƒ”แƒ‘แƒ— 30-แƒ–แƒ” แƒ›แƒ”แƒข แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒแƒก แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜แƒ“แƒแƒœ แƒฌแƒแƒ›แƒจแƒ˜. แƒ—แƒฃแƒœแƒ“แƒแƒช แƒ“แƒแƒ•แƒฃแƒ แƒ”แƒ™แƒแƒ— render() 60-แƒฏแƒ”แƒ  แƒฌแƒแƒ›แƒจแƒ˜, แƒ›แƒแƒจแƒ˜แƒœ แƒแƒ› แƒ–แƒแƒ แƒ”แƒ‘แƒ˜แƒก แƒœแƒแƒฎแƒ”แƒ•แƒแƒ แƒ˜ แƒฃแƒ‘แƒ แƒแƒšแƒแƒ“ แƒ’แƒแƒ“แƒแƒฎแƒแƒ–แƒแƒ•แƒก แƒ˜แƒ’แƒ˜แƒ•แƒ”แƒก, แƒแƒ แƒกแƒ”แƒ‘แƒ˜แƒ—แƒแƒ“ แƒแƒ แƒแƒคแƒ”แƒ แƒก แƒแƒ™แƒ”แƒ—แƒ”แƒ‘แƒก. แƒ’แƒฃแƒšแƒฃแƒ‘แƒ แƒงแƒ•แƒ˜แƒšแƒ แƒ’แƒแƒœแƒฎแƒแƒ แƒชแƒ˜แƒ”แƒšแƒ”แƒ‘แƒ˜แƒก แƒ™แƒ˜แƒ“แƒ”แƒ• แƒ”แƒ แƒ—แƒ˜ แƒžแƒ แƒแƒ‘แƒšแƒ”แƒ›แƒ แƒแƒ แƒ˜แƒก แƒ˜แƒก แƒ”แƒฅแƒ•แƒ”แƒ›แƒ“แƒ”แƒ‘แƒแƒ แƒ”แƒ‘แƒ แƒ“แƒแƒ’แƒ•แƒ˜แƒแƒœแƒ”แƒ‘แƒแƒก. แƒ˜แƒ“แƒ”แƒแƒšแƒฃแƒ แƒ˜ แƒ˜แƒœแƒขแƒ”แƒ แƒœแƒ”แƒขแƒ˜แƒก แƒกแƒ˜แƒฉแƒฅแƒแƒ แƒ˜แƒ—, แƒ™แƒšแƒ˜แƒ”แƒœแƒขแƒ˜ แƒ›แƒ˜แƒ˜แƒฆแƒ”แƒ‘แƒก แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒแƒก แƒ–แƒฃแƒกแƒขแƒแƒ“ แƒงแƒแƒ•แƒ”แƒš 33 ms (30 แƒฌแƒแƒ›แƒจแƒ˜):

Multiplayer .io แƒ•แƒ”แƒ‘ แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒจแƒ”แƒฅแƒ›แƒœแƒ
แƒกแƒแƒ›แƒฌแƒฃแƒฎแƒแƒ แƒแƒ“, แƒ˜แƒ“แƒ”แƒแƒšแƒฃแƒ แƒ˜ แƒแƒ แƒแƒคแƒ”แƒ แƒ˜แƒ. แƒฃแƒคแƒ แƒ แƒ แƒ”แƒแƒšแƒฃแƒ แƒ˜ แƒกแƒฃแƒ แƒแƒ—แƒ˜ แƒ˜แƒฅแƒœแƒ”แƒ‘แƒ:
Multiplayer .io แƒ•แƒ”แƒ‘ แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒจแƒ”แƒฅแƒ›แƒœแƒ
แƒ’แƒฃแƒšแƒฃแƒ‘แƒ แƒงแƒ•แƒ˜แƒšแƒ แƒ’แƒแƒœแƒฎแƒแƒ แƒชแƒ˜แƒ”แƒšแƒ”แƒ‘แƒ แƒงแƒ•แƒ”แƒšแƒแƒ–แƒ” แƒฃแƒแƒ แƒ”แƒกแƒ˜ แƒจแƒ”แƒ›แƒ—แƒฎแƒ•แƒ”แƒ•แƒแƒ, แƒ แƒแƒ“แƒ”แƒกแƒแƒช แƒกแƒแƒฅแƒ›แƒ” แƒšแƒแƒขแƒ”แƒœแƒขแƒฃแƒ แƒแƒ‘แƒแƒก แƒ”แƒฎแƒ”แƒ‘แƒ. แƒ—แƒฃ แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒ แƒ›แƒ˜แƒ˜แƒฆแƒ”แƒ‘แƒ 50 ms แƒ“แƒแƒ’แƒ•แƒ˜แƒแƒœแƒ”แƒ‘แƒ˜แƒ—, แƒ›แƒแƒจแƒ˜แƒœ แƒ™แƒšแƒ˜แƒ”แƒœแƒขแƒ—แƒ แƒกแƒแƒ“แƒ’แƒแƒ›แƒ”แƒ‘แƒ˜ แƒ“แƒแƒ›แƒแƒขแƒ”แƒ‘แƒ˜แƒ— 50 ms-แƒ˜แƒ—, แƒ แƒแƒ“แƒ’แƒแƒœ แƒ˜แƒก แƒ™แƒ•แƒšแƒแƒ• แƒแƒฎแƒ“แƒ”แƒœแƒก แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒ›แƒ“แƒ’แƒแƒ›แƒแƒ แƒ”แƒแƒ‘แƒแƒก แƒฌแƒ˜แƒœแƒ แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒ˜แƒ“แƒแƒœ. แƒ—แƒฅแƒ•แƒ”แƒœ แƒฌแƒแƒ แƒ›แƒแƒ˜แƒ“แƒ’แƒ˜แƒœแƒ”แƒ—, แƒ แƒแƒ›แƒ“แƒ”แƒœแƒแƒ“ แƒ›แƒแƒฃแƒฎแƒ”แƒ แƒฎแƒ”แƒ‘แƒ”แƒšแƒ˜แƒ แƒ”แƒก แƒ›แƒแƒ—แƒแƒ›แƒแƒจแƒ˜แƒกแƒ—แƒ•แƒ˜แƒก: แƒ—แƒ•แƒ˜แƒ—แƒœแƒ”แƒ‘แƒฃแƒ แƒ˜ แƒจแƒ”แƒœแƒ”แƒšแƒ”แƒ‘แƒ˜แƒก แƒ’แƒแƒ›แƒ, แƒ—แƒแƒ›แƒแƒจแƒ˜ แƒ›แƒแƒฉแƒ•แƒ”แƒœแƒ”แƒ‘แƒ˜แƒ—แƒ˜ แƒ“แƒ แƒแƒ แƒแƒกแƒขแƒแƒ‘แƒ˜แƒšแƒฃแƒ แƒ˜ แƒ˜แƒฅแƒœแƒ”แƒ‘แƒ.

7.2 แƒ™แƒšแƒ˜แƒ”แƒœแƒขแƒ˜แƒก แƒ›แƒ“แƒ’แƒแƒ›แƒแƒ แƒ”แƒแƒ‘แƒ˜แƒก แƒ’แƒแƒฃแƒ›แƒฏแƒแƒ‘แƒ”แƒกแƒ”แƒ‘แƒ

แƒฉแƒ•แƒ”แƒœ แƒ’แƒแƒœแƒ•แƒแƒฎแƒแƒ แƒชแƒ˜แƒ”แƒšแƒ”แƒ‘แƒ— แƒ’แƒแƒ แƒ™แƒ•แƒ”แƒฃแƒš แƒ’แƒแƒฃแƒ›แƒฏแƒแƒ‘แƒ”แƒกแƒ”แƒ‘แƒแƒก แƒ’แƒฃแƒšแƒฃแƒ‘แƒ แƒงแƒ•แƒ˜แƒšแƒ แƒ’แƒแƒœแƒฎแƒแƒ แƒชแƒ˜แƒ”แƒšแƒ”แƒ‘แƒแƒจแƒ˜. แƒžแƒ˜แƒ แƒ•แƒ”แƒš แƒ แƒ˜แƒ’แƒจแƒ˜, แƒฉแƒ•แƒ”แƒœ แƒ•แƒ˜แƒงแƒ”แƒœแƒ”แƒ‘แƒ— แƒ’แƒแƒ“แƒแƒชแƒ”แƒ›แƒ˜แƒก แƒจแƒ”แƒคแƒ”แƒ แƒฎแƒ”แƒ‘แƒ 100 ms-แƒ˜แƒกแƒ—แƒ•แƒ˜แƒก. แƒ”แƒก แƒœแƒ˜แƒจแƒœแƒแƒ•แƒก, แƒ แƒแƒ› แƒ™แƒšแƒ˜แƒ”แƒœแƒขแƒ˜แƒก "แƒ›แƒ˜แƒ›แƒ“แƒ˜แƒœแƒแƒ แƒ”" แƒ›แƒ“แƒ’แƒแƒ›แƒแƒ แƒ”แƒแƒ‘แƒ แƒงแƒแƒ•แƒ”แƒšแƒ—แƒ•แƒ˜แƒก แƒฉแƒแƒ›แƒแƒ แƒฉแƒ”แƒ‘แƒ แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ–แƒ” แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒ›แƒ“แƒ’แƒแƒ›แƒแƒ แƒ”แƒแƒ‘แƒแƒก 100 ms-แƒ˜แƒ—. แƒ›แƒแƒ’แƒแƒšแƒ˜แƒ—แƒแƒ“, แƒ—แƒฃ แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ–แƒ” แƒ“แƒ แƒ แƒแƒ แƒ˜แƒก 150, แƒจแƒ”แƒ›แƒ“แƒ”แƒ’ แƒ™แƒšแƒ˜แƒ”แƒœแƒขแƒ˜ แƒ’แƒแƒ›แƒแƒ˜แƒขแƒแƒœแƒก แƒ˜แƒ› แƒ›แƒ“แƒ’แƒแƒ›แƒแƒ แƒ”แƒแƒ‘แƒแƒก, แƒ แƒแƒ›แƒ”แƒšแƒจแƒ˜แƒช แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜ แƒ˜แƒ› แƒ“แƒ แƒแƒก แƒ˜แƒ›แƒงแƒแƒคแƒ”แƒ‘แƒแƒ“แƒ 50:

Multiplayer .io แƒ•แƒ”แƒ‘ แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒจแƒ”แƒฅแƒ›แƒœแƒ
แƒ”แƒก แƒ’แƒ•แƒแƒซแƒšแƒ”แƒ•แƒก 100 ms แƒ‘แƒฃแƒคแƒ”แƒ แƒก, แƒ แƒแƒ› แƒ’แƒแƒ“แƒแƒ•แƒ แƒฉแƒ”แƒ— แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒ”แƒ‘แƒ˜แƒก แƒแƒ แƒแƒžแƒ แƒแƒ’แƒœแƒแƒ–แƒ˜แƒ แƒ”แƒ‘แƒแƒ“แƒ˜ แƒ“แƒ แƒแƒ˜แƒ—:

Multiplayer .io แƒ•แƒ”แƒ‘ แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒจแƒ”แƒฅแƒ›แƒœแƒ
แƒแƒ›แƒ˜แƒก แƒแƒœแƒแƒ–แƒฆแƒแƒฃแƒ แƒ”แƒ‘แƒ แƒ›แƒฃแƒ“แƒ›แƒ˜แƒ•แƒ˜ แƒ˜แƒฅแƒœแƒ”แƒ‘แƒ แƒจแƒ”แƒงแƒ•แƒแƒœแƒ˜แƒก แƒฉแƒแƒ›แƒแƒ แƒฉแƒ”แƒœแƒ 100 ms-แƒ˜แƒ—. แƒ”แƒก แƒแƒ แƒ˜แƒก แƒฃแƒ›แƒœแƒ˜แƒจแƒ•แƒœแƒ”แƒšแƒ แƒ›แƒกแƒฎแƒ•แƒ”แƒ แƒžแƒšแƒ˜ แƒ’แƒšแƒฃแƒ•แƒ˜ แƒ’แƒ”แƒ˜แƒ›แƒžแƒšแƒ”แƒ˜แƒกแƒ—แƒ•แƒ˜แƒก - แƒ›แƒแƒ—แƒแƒ›แƒแƒจแƒ”แƒ—แƒ แƒฃแƒ›แƒ”แƒขแƒ”แƒกแƒแƒ‘แƒ (แƒ’แƒแƒœแƒกแƒแƒ™แƒฃแƒ—แƒ แƒ”แƒ‘แƒ˜แƒ— แƒจแƒ”แƒ›แƒ—แƒฎแƒ•แƒ”แƒ•แƒ˜แƒ—แƒ˜) แƒ•แƒ”แƒ แƒช แƒ™แƒ˜ แƒจแƒ”แƒแƒ›แƒฉแƒœแƒ”แƒ•แƒก แƒแƒ› แƒจแƒ”แƒคแƒ”แƒ แƒฎแƒ”แƒ‘แƒแƒก. แƒแƒ“แƒแƒ›แƒ˜แƒแƒœแƒ”แƒ‘แƒ˜แƒกแƒ—แƒ•แƒ˜แƒก แƒ‘แƒ”แƒ•แƒ แƒแƒ“ แƒฃแƒคแƒ แƒ แƒแƒ“แƒ•แƒ˜แƒšแƒ˜แƒ แƒแƒ“แƒแƒžแƒขแƒ˜แƒ แƒ”แƒ‘แƒ แƒ›แƒฃแƒ“แƒ›แƒ˜แƒ•แƒ˜ 100ms แƒจแƒ”แƒงแƒแƒ•แƒœแƒ”แƒ‘แƒ˜แƒ—, แƒ•แƒ˜แƒ“แƒ แƒ” แƒ—แƒแƒ›แƒแƒจแƒ˜ แƒแƒ แƒแƒžแƒ แƒแƒ’แƒœแƒแƒ–แƒ˜แƒ แƒ”แƒ‘แƒแƒ“แƒ˜ แƒจแƒ”แƒงแƒแƒ•แƒœแƒ”แƒ‘แƒ˜แƒ—.

แƒฉแƒ•แƒ”แƒœ แƒแƒกแƒ”แƒ•แƒ” แƒจแƒ”แƒ’แƒ•แƒ˜แƒซแƒšแƒ˜แƒ แƒ’แƒแƒ›แƒแƒ•แƒ˜แƒงแƒ”แƒœแƒแƒ— แƒกแƒฎแƒ•แƒ แƒขแƒ”แƒฅแƒœแƒ˜แƒ™แƒ แƒ”.แƒฌ แƒ™แƒšแƒ˜แƒ”แƒœแƒขแƒ˜แƒก แƒ›แƒฎแƒ แƒ˜แƒ“แƒแƒœ แƒžแƒ แƒแƒ’แƒœแƒแƒ–แƒ˜, แƒ แƒแƒ›แƒ”แƒšแƒ˜แƒช แƒ™แƒแƒ แƒ’ แƒกแƒแƒฅแƒ›แƒ”แƒก แƒแƒ™แƒ”แƒ—แƒ”แƒ‘แƒก แƒแƒฆแƒฅแƒ›แƒฃแƒšแƒ˜ แƒจแƒ”แƒงแƒแƒ•แƒœแƒ”แƒ‘แƒ˜แƒก แƒจแƒ”แƒกแƒแƒ›แƒชแƒ˜แƒ แƒ”แƒ‘แƒšแƒแƒ“, แƒ›แƒแƒ’แƒ แƒแƒ› แƒแƒ› แƒžแƒแƒกแƒขแƒจแƒ˜ แƒแƒ  แƒ˜แƒฅแƒœแƒ”แƒ‘แƒ แƒ’แƒแƒจแƒฃแƒฅแƒ”แƒ‘แƒฃแƒšแƒ˜.

แƒ™แƒ˜แƒ“แƒ”แƒ• แƒ”แƒ แƒ—แƒ˜ แƒ’แƒแƒฃแƒ›แƒฏแƒแƒ‘แƒ”แƒกแƒ”แƒ‘แƒ, แƒ แƒแƒ›แƒ”แƒšแƒกแƒแƒช แƒฉแƒ•แƒ”แƒœ แƒ•แƒ˜แƒงแƒ”แƒœแƒ”แƒ‘แƒ— แƒแƒ แƒ˜แƒก แƒฌแƒ แƒคแƒ˜แƒ•แƒ˜ แƒ˜แƒœแƒขแƒ”แƒ แƒžแƒแƒšแƒแƒชแƒ˜แƒ. แƒ แƒ”แƒœแƒ“แƒ”แƒ แƒ˜แƒœแƒ’แƒ˜แƒก แƒจแƒ”แƒคแƒ”แƒ แƒฎแƒ”แƒ‘แƒ˜แƒก แƒ’แƒแƒ›แƒ, แƒฉแƒ•แƒ”แƒœ แƒฉแƒ•แƒ”แƒฃแƒšแƒ”แƒ‘แƒ แƒ˜แƒ• แƒ•แƒแƒ™แƒ”แƒ—แƒ”แƒ‘แƒ— แƒ›แƒ˜แƒœแƒ˜แƒ›แƒฃแƒ› แƒ”แƒ แƒ—แƒ˜ แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒแƒก แƒ™แƒšแƒ˜แƒ”แƒœแƒขแƒจแƒ˜ แƒ›แƒ˜แƒ›แƒ“แƒ˜แƒœแƒแƒ แƒ” แƒ“แƒ แƒแƒ–แƒ” แƒแƒ“แƒ แƒ”. แƒ แƒแƒชแƒ แƒ“แƒแƒฃแƒซแƒแƒฎแƒ”แƒก getCurrentState(), แƒฉแƒ•แƒ”แƒœ แƒจแƒ”แƒ’แƒ•แƒ˜แƒซแƒšแƒ˜แƒ แƒจแƒ”แƒ•แƒแƒกแƒ แƒฃแƒšแƒแƒ— แƒฌแƒ แƒคแƒ˜แƒ•แƒ˜ แƒ˜แƒœแƒขแƒ”แƒ แƒžแƒแƒšแƒแƒชแƒ˜แƒ แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒ”แƒ‘แƒก แƒจแƒแƒ แƒ˜แƒก แƒ™แƒšแƒ˜แƒ”แƒœแƒขแƒจแƒ˜ แƒ›แƒ˜แƒ›แƒ“แƒ˜แƒœแƒแƒ แƒ” แƒ“แƒ แƒแƒ˜แƒก แƒฌแƒ˜แƒœ แƒ“แƒ แƒจแƒ”แƒ›แƒ“แƒ”แƒ’:

Multiplayer .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(). แƒ แƒแƒ’แƒแƒ แƒช แƒแƒ“แƒ แƒ” แƒ•แƒœแƒแƒฎแƒ”แƒ—, แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒงแƒ•แƒ”แƒšแƒ แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒ แƒจแƒ”แƒ˜แƒชแƒแƒ•แƒก แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜แƒก แƒ“แƒ แƒแƒ˜แƒก แƒœแƒ˜แƒจแƒแƒœแƒก. แƒฉแƒ•แƒ”แƒœ แƒ’แƒ•แƒ˜แƒœแƒ“แƒ แƒ’แƒแƒ›แƒแƒ•แƒ˜แƒงแƒ”แƒœแƒแƒ— แƒ แƒ”แƒœแƒ“แƒ”แƒ แƒ˜แƒก แƒจแƒ”แƒงแƒแƒ•แƒœแƒ”แƒ‘แƒ, แƒ แƒแƒ—แƒ แƒ’แƒแƒ›แƒแƒ•แƒ˜แƒขแƒแƒœแƒแƒ— แƒกแƒฃแƒ แƒแƒ—แƒ˜ 100แƒ›แƒ›-แƒ˜แƒ— แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜แƒก แƒฃแƒ™แƒแƒœ, แƒ›แƒแƒ’แƒ แƒแƒ› แƒฉแƒ•แƒ”แƒœ แƒ•แƒ”แƒ แƒแƒกแƒแƒ“แƒ”แƒก แƒ’แƒแƒ•แƒ˜แƒ’แƒ”แƒ‘แƒ— แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ–แƒ” แƒ›แƒ˜แƒ›แƒ“แƒ˜แƒœแƒแƒ แƒ” แƒ“แƒ แƒแƒก, แƒ แƒแƒ“แƒ’แƒแƒœ แƒฉแƒ•แƒ”แƒœ แƒแƒ  แƒ•แƒ˜แƒชแƒ˜แƒ— แƒ แƒแƒ›แƒ“แƒ”แƒœแƒ˜ แƒ“แƒ แƒ แƒ“แƒแƒกแƒญแƒ˜แƒ แƒ“แƒ แƒ แƒแƒ›แƒ”แƒšแƒ˜แƒ›แƒ” แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒ˜แƒก แƒฉแƒ•แƒ”แƒœแƒแƒ›แƒ“แƒ” แƒ›แƒ˜แƒกแƒ•แƒšแƒแƒก. แƒ˜แƒœแƒขแƒ”แƒ แƒœแƒ”แƒขแƒ˜ แƒแƒ แƒแƒžแƒ แƒแƒ’แƒœแƒแƒ–แƒ˜แƒ แƒ”แƒ‘แƒแƒ“แƒ˜แƒ แƒ“แƒ แƒ›แƒ˜แƒกแƒ˜ แƒกแƒ˜แƒฉแƒฅแƒแƒ แƒ” แƒจแƒ”แƒ˜แƒซแƒšแƒ”แƒ‘แƒ แƒ›แƒœแƒ˜แƒจแƒ•แƒœแƒ”แƒšแƒแƒ•แƒœแƒแƒ“ แƒ’แƒแƒœแƒกแƒฎแƒ•แƒแƒ•แƒ“แƒ”แƒ‘แƒแƒ“แƒ”แƒก!

แƒแƒ› แƒžแƒ แƒแƒ‘แƒšแƒ”แƒ›แƒ˜แƒก แƒ’แƒแƒ“แƒแƒกแƒแƒญแƒ แƒ”แƒšแƒแƒ“, แƒฉแƒ•แƒ”แƒœ แƒจแƒ”แƒ’แƒ•แƒ˜แƒซแƒšแƒ˜แƒ แƒ’แƒแƒ›แƒแƒ•แƒ˜แƒงแƒ”แƒœแƒแƒ— แƒ’แƒแƒœแƒ˜แƒ•แƒ แƒฃแƒšแƒ˜ แƒ›แƒ˜แƒแƒฎแƒšแƒแƒ”แƒ‘แƒ: แƒฉแƒ•แƒ”แƒœ แƒ›แƒแƒ“แƒ˜แƒ— แƒ•แƒ˜แƒคแƒ˜แƒฅแƒ แƒแƒ—, แƒ แƒแƒ› แƒžแƒ˜แƒ แƒ•แƒ”แƒšแƒ˜ แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒ แƒ›แƒงแƒ˜แƒกแƒ˜แƒ”แƒ แƒแƒ“ แƒ›แƒแƒ•แƒ˜แƒ“แƒ. แƒ—แƒฃ แƒ”แƒก แƒกแƒ˜แƒ›แƒแƒ แƒ—แƒšแƒ” แƒ˜แƒงแƒ, แƒ›แƒแƒจแƒ˜แƒœ แƒฉแƒ•แƒ”แƒœ แƒ’แƒ•แƒ”แƒชแƒแƒ“แƒ˜แƒœแƒ”แƒ‘แƒ แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜แƒก แƒ“แƒ แƒ แƒ˜แƒ› แƒ™แƒแƒœแƒ™แƒ แƒ”แƒขแƒฃแƒš แƒ›แƒแƒ›แƒ”แƒœแƒขแƒจแƒ˜! แƒฉแƒ•แƒ”แƒœ แƒ•แƒ˜แƒœแƒแƒฎแƒแƒ•แƒ— แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜แƒก แƒ“แƒ แƒแƒ˜แƒก แƒœแƒ˜แƒจแƒแƒœแƒก firstServerTimestamp แƒ“แƒ แƒ’แƒแƒ“แƒแƒ•แƒแƒ แƒฉแƒ˜แƒœแƒแƒ— แƒฉแƒ•แƒ”แƒœแƒ˜ แƒแƒ“แƒ’แƒ˜แƒšแƒแƒ‘แƒ แƒ˜แƒ•แƒ˜ (แƒ™แƒšแƒ˜แƒ”แƒœแƒขแƒ˜) แƒ“แƒ แƒแƒ˜แƒก แƒแƒœแƒแƒ‘แƒ”แƒญแƒ“แƒ˜ แƒ˜แƒ›แƒแƒ•แƒ” แƒ›แƒแƒ›แƒ”แƒœแƒขแƒจแƒ˜ gameStart.

แฒ›แƒแƒ˜แƒชแƒแƒ“แƒ”. แƒ”แƒก แƒแƒ  แƒฃแƒœแƒ“แƒ แƒ˜แƒงแƒแƒก แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜แƒก แƒ“แƒ แƒ = แƒ™แƒšแƒ˜แƒ”แƒœแƒขแƒ˜แƒก แƒ“แƒ แƒ? แƒ แƒแƒขแƒแƒ› แƒ’แƒแƒœแƒ•แƒแƒกแƒฎแƒ•แƒแƒ•แƒ”แƒ‘แƒ— โ€žแƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜แƒก แƒ“แƒ แƒแƒ˜แƒก แƒœแƒ˜แƒจแƒแƒœแƒกโ€œ แƒ“แƒ โ€žแƒ™แƒšแƒ˜แƒ”แƒœแƒขแƒ˜แƒก แƒ“แƒ แƒแƒ˜แƒก แƒœแƒ˜แƒจแƒแƒœแƒกโ€œ แƒจแƒแƒ แƒ˜แƒก? แƒ”แƒก แƒ“แƒ˜แƒ“แƒ˜ แƒ™แƒ˜แƒ—แƒฎแƒ•แƒแƒ! แƒ’แƒแƒ›แƒแƒ“แƒ˜แƒก, แƒ แƒแƒ› แƒ”แƒก แƒแƒ  แƒแƒ แƒ˜แƒก แƒ”แƒ แƒ—แƒ˜ แƒ“แƒ แƒ˜แƒ’แƒ˜แƒ•แƒ”. Date.now() แƒ“แƒแƒแƒ‘แƒ แƒฃแƒœแƒ”แƒ‘แƒก แƒกแƒฎแƒ•แƒแƒ“แƒแƒกแƒฎแƒ•แƒ แƒ“แƒ แƒแƒ˜แƒก แƒœแƒ˜แƒจแƒแƒœแƒก แƒ™แƒšแƒ˜แƒ”แƒœแƒขแƒกแƒ แƒ“แƒ แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ–แƒ” แƒ“แƒ แƒ”แƒก แƒ“แƒแƒ›แƒแƒ™แƒ˜แƒ“แƒ”แƒ‘แƒฃแƒšแƒ˜แƒ แƒแƒ› แƒ›แƒแƒœแƒฅแƒแƒœแƒ”แƒ‘แƒ˜แƒก แƒแƒ“แƒ’แƒ˜แƒšแƒแƒ‘แƒ แƒ˜แƒ• แƒคแƒแƒฅแƒขแƒแƒ แƒ”แƒ‘แƒ–แƒ”. แƒแƒ แƒแƒกแƒแƒ“แƒ”แƒก แƒ˜แƒคแƒ˜แƒฅแƒ แƒแƒ—, แƒ แƒแƒ› แƒ“แƒ แƒแƒ˜แƒก แƒแƒœแƒแƒ‘แƒ”แƒญแƒ“แƒ”แƒ‘แƒ˜ แƒงแƒ•แƒ”แƒšแƒ แƒ›แƒแƒœแƒฅแƒแƒœแƒแƒ–แƒ” แƒ”แƒ แƒ—แƒœแƒแƒ˜แƒ แƒ˜ แƒ˜แƒฅแƒœแƒ”แƒ‘แƒ.

แƒแƒฎแƒšแƒ แƒฉแƒ•แƒ”แƒœ แƒ’แƒ•แƒ”แƒกแƒ›แƒ˜แƒก, แƒ แƒแƒก แƒแƒ™แƒ”แƒ—แƒ”แƒ‘แƒก currentServerTime(): แƒ‘แƒ แƒฃแƒœแƒ“แƒ”แƒ‘แƒ แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜แƒก แƒ“แƒ แƒแƒ˜แƒก แƒแƒœแƒแƒ‘แƒ”แƒญแƒ“แƒ˜ แƒ›แƒ˜แƒ›แƒ“แƒ˜แƒœแƒแƒ แƒ” แƒ แƒ”แƒœแƒ“แƒ”แƒ แƒ˜แƒก แƒ“แƒ แƒแƒ˜แƒก. แƒกแƒฎแƒ•แƒ แƒกแƒ˜แƒขแƒงแƒ•แƒ”แƒ‘แƒ˜แƒ— แƒ แƒแƒ› แƒ•แƒ—แƒฅแƒ•แƒแƒ—, แƒ”แƒก แƒแƒ แƒ˜แƒก แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜แƒก แƒ›แƒ˜แƒ›แƒ“แƒ˜แƒœแƒแƒ แƒ” แƒ“แƒ แƒ (firstServerTimestamp <+ (Date.now() - gameStart)) แƒ›แƒ˜แƒœแƒฃแƒก แƒ’แƒแƒ“แƒแƒชแƒ”แƒ›แƒ˜แƒก แƒ“แƒแƒ’แƒ•แƒ˜แƒแƒœแƒ”แƒ‘แƒ (RENDER_DELAY).

แƒแƒฎแƒšแƒ แƒ›แƒแƒ“แƒ˜แƒ— แƒจแƒ”แƒ•แƒฎแƒ”แƒ“แƒแƒ—, แƒ—แƒฃ แƒ แƒแƒ’แƒแƒ  แƒ•แƒแƒฌแƒแƒ แƒ›แƒแƒ”แƒ‘แƒ— แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒ”แƒ‘แƒก. แƒ แƒแƒ“แƒ”แƒกแƒแƒช แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒ แƒ›แƒ˜แƒ˜แƒฆแƒ”แƒ‘แƒ แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜แƒ“แƒแƒœ, แƒ›แƒแƒก แƒ”แƒซแƒแƒฎแƒ˜แƒแƒœ processGameUpdate()แƒ“แƒ แƒฉแƒ•แƒ”แƒœ แƒ•แƒ˜แƒœแƒแƒฎแƒแƒ•แƒ— แƒแƒฎแƒแƒš แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒแƒก แƒ›แƒแƒกแƒ˜แƒ•แƒจแƒ˜ gameUpdates. แƒจแƒ”แƒ›แƒ“แƒ”แƒ’, แƒ›แƒ”แƒฎแƒกแƒ˜แƒ”แƒ แƒ”แƒ‘แƒ˜แƒก แƒ’แƒแƒ›แƒแƒงแƒ”แƒœแƒ”แƒ‘แƒ˜แƒก แƒจแƒ”แƒกแƒแƒ›แƒแƒฌแƒ›แƒ”แƒ‘แƒšแƒแƒ“, แƒฉแƒ•แƒ”แƒœ แƒ•แƒจแƒšแƒ˜แƒ— แƒงแƒ•แƒ”แƒšแƒ แƒซแƒ•แƒ”แƒš แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒแƒก แƒ‘แƒแƒ–แƒ˜แƒก แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒแƒ แƒแƒ“แƒ’แƒแƒœ แƒ˜แƒกแƒ˜แƒœแƒ˜ แƒแƒฆแƒแƒ  แƒ’แƒ•แƒญแƒ˜แƒ แƒ“แƒ”แƒ‘แƒ.

แƒ แƒ แƒแƒ แƒ˜แƒก โ€žแƒซแƒ˜แƒ แƒ˜แƒ—แƒแƒ“แƒ˜ แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒโ€œ? แƒ”แƒก แƒžแƒ˜แƒ แƒ•แƒ”แƒšแƒ˜ แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒ, แƒ แƒแƒ›แƒ”แƒšแƒกแƒแƒช แƒ•แƒžแƒแƒฃแƒšแƒแƒ‘แƒ— แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜แƒก แƒ›แƒ˜แƒ›แƒ“แƒ˜แƒœแƒแƒ แƒ” แƒ“แƒ แƒแƒ˜แƒ“แƒแƒœ แƒฃแƒ™แƒแƒœ แƒ’แƒแƒ“แƒแƒแƒ“แƒ’แƒ˜แƒšแƒ”แƒ‘แƒ˜แƒ—. แƒ’แƒแƒฎแƒกแƒแƒ•แƒ— แƒ”แƒก แƒ“แƒ˜แƒแƒ’แƒ แƒแƒ›แƒ?

Multiplayer .io แƒ•แƒ”แƒ‘ แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒจแƒ”แƒฅแƒ›แƒœแƒ
แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒ แƒžแƒ˜แƒ แƒ“แƒแƒžแƒ˜แƒ  "Client Render Time"-แƒ˜แƒก แƒ›แƒแƒ แƒชแƒฎแƒœแƒ˜แƒ• แƒแƒ แƒ˜แƒก แƒกแƒแƒ‘แƒแƒ–แƒ˜แƒกแƒ แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒ.

แƒ แƒ˜แƒกแƒ—แƒ•แƒ˜แƒก แƒ’แƒแƒ›แƒแƒ˜แƒงแƒ”แƒœแƒ”แƒ‘แƒ แƒ‘แƒแƒ–แƒ˜แƒก แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒ? แƒ แƒแƒขแƒแƒ› แƒจแƒ”แƒ’แƒ•แƒ˜แƒซแƒšแƒ˜แƒ แƒฉแƒแƒ›แƒแƒ•แƒแƒ’แƒ“แƒแƒ— แƒ’แƒแƒœแƒแƒฎแƒšแƒ”แƒ‘แƒ”แƒ‘แƒ˜ แƒ‘แƒแƒ–แƒแƒ–แƒ”? แƒแƒ›แƒ˜แƒก แƒ’แƒแƒกแƒแƒ’แƒ”แƒ‘แƒแƒ“, แƒ›แƒแƒ“แƒ˜แƒ— แƒ‘แƒแƒšแƒแƒก แƒ“แƒ แƒ‘แƒแƒšแƒแƒก แƒ’แƒแƒœแƒ˜แƒฎแƒ˜แƒšแƒ”แƒ— แƒ’แƒแƒœแƒฎแƒแƒ แƒชแƒ˜แƒ”แƒšแƒ”แƒ‘แƒ 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 on Github.

แƒœแƒแƒฌแƒ˜แƒšแƒ˜ 2. Backend แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜

แƒแƒ› แƒœแƒแƒฌแƒ˜แƒšแƒจแƒ˜ แƒฉแƒ•แƒ”แƒœ แƒ’แƒแƒ“แƒแƒ•แƒฎแƒ”แƒ“แƒแƒ•แƒ— Node.js backend-แƒก, แƒ แƒแƒ›แƒ”แƒšแƒ˜แƒช แƒแƒ™แƒแƒœแƒขแƒ แƒแƒšแƒ”แƒ‘แƒก แƒฉแƒ•แƒ”แƒœแƒก .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 แƒจแƒ”แƒ“แƒ’แƒ”แƒ‘แƒ แƒกแƒ”แƒ แƒ•แƒ”แƒ แƒ˜แƒก แƒ“แƒแƒงแƒ”แƒœแƒ”แƒ‘แƒ˜แƒกแƒ’แƒแƒœ แƒกแƒแƒ™แƒ”แƒขแƒ˜แƒ แƒแƒ›แƒ”แƒšแƒ˜แƒช แƒฃแƒ‘แƒ แƒแƒšแƒแƒ“ แƒฃแƒ™แƒแƒ•แƒจแƒ˜แƒ แƒ“แƒ”แƒ‘แƒ 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 แƒ™แƒแƒ•แƒจแƒ˜แƒ แƒ˜แƒก แƒฌแƒแƒ แƒ›แƒแƒขแƒ”แƒ‘แƒ˜แƒ— แƒ“แƒแƒ›แƒงแƒแƒ แƒ”แƒ‘แƒ˜แƒก แƒจแƒ”แƒ›แƒ“แƒ”แƒ’, แƒฉแƒ•แƒ”แƒœ แƒ•แƒแƒ™แƒแƒœแƒคแƒ˜แƒ’แƒฃแƒ แƒ˜แƒ แƒ”แƒ‘แƒ— แƒ›แƒแƒ•แƒšแƒ”แƒœแƒ˜แƒก แƒ“แƒแƒ›แƒ›แƒฃแƒจแƒแƒ•แƒ”แƒ‘แƒšแƒ”แƒ‘แƒก แƒแƒฎแƒแƒšแƒ˜ แƒกแƒแƒ™แƒ”แƒขแƒ˜แƒกแƒ—แƒ•แƒ˜แƒก. แƒ›แƒแƒ•แƒšแƒ”แƒœแƒ˜แƒก แƒ“แƒแƒ›แƒ›แƒฃแƒจแƒแƒ•แƒ”แƒ‘แƒšแƒ”แƒ‘แƒ˜ แƒแƒ›แƒฃแƒจแƒแƒ•แƒ”แƒ‘แƒ”แƒœ แƒ™แƒšแƒ˜แƒ”แƒœแƒขแƒ”แƒ‘แƒ˜แƒกแƒ’แƒแƒœ แƒ›แƒ˜แƒฆแƒ”แƒ‘แƒฃแƒš แƒจแƒ”แƒขแƒงแƒแƒ‘แƒ˜แƒœแƒ”แƒ‘แƒ”แƒ‘แƒก singleton แƒแƒ‘แƒ˜แƒ”แƒฅแƒขแƒ–แƒ” แƒ“แƒ”แƒšแƒ”แƒ’แƒ˜แƒ แƒ”แƒ‘แƒ˜แƒ— 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แƒแƒกแƒ” แƒ แƒแƒ›, แƒฉแƒ•แƒ”แƒœ แƒแƒ  แƒ’แƒ•แƒญแƒ˜แƒ แƒ“แƒ”แƒ‘แƒ แƒแƒ›แƒแƒ–แƒ” แƒคแƒ˜แƒฅแƒ แƒ˜. แƒ›แƒ” แƒ›แƒแƒก แƒ“แƒแƒ•แƒฃแƒ แƒ”แƒ™แƒแƒ• แƒ›แƒแƒ—แƒแƒ›แƒแƒจแƒ˜แƒก 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:

แƒแƒ‘แƒ˜แƒ”แƒฅแƒขแƒ˜.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:

แƒขแƒงแƒ•แƒ˜แƒ.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แƒแƒกแƒ” แƒ แƒแƒ› แƒ—แƒฅแƒ•แƒ”แƒœ แƒจแƒ”แƒ’แƒ˜แƒซแƒšแƒ˜แƒแƒ— แƒ—แƒ•แƒแƒšแƒงแƒฃแƒ แƒ˜ แƒแƒ“แƒ”แƒ•แƒœแƒแƒ— แƒ›แƒแƒ—แƒแƒ›แƒแƒจแƒ”แƒก, แƒ แƒแƒ›แƒ”แƒšแƒ›แƒแƒช แƒจแƒ”แƒฅแƒ›แƒœแƒ แƒ”แƒก แƒญแƒฃแƒ แƒ•แƒ˜.
  • แƒ“แƒแƒกแƒแƒ‘แƒ แƒฃแƒœแƒ”แƒ‘แƒ”แƒšแƒ˜ แƒ›แƒœแƒ˜แƒจแƒ•แƒœแƒ”แƒšแƒแƒ‘แƒ˜แƒก แƒ“แƒแƒ›แƒแƒขแƒ”แƒ‘แƒ update(), แƒ แƒแƒ›แƒ”แƒšแƒ˜แƒช แƒขแƒแƒšแƒ˜แƒ true, แƒ—แƒฃ แƒญแƒฃแƒ แƒ•แƒ˜ แƒแƒ แƒ”แƒœแƒ˜แƒก แƒ’แƒแƒ แƒ”แƒ—แƒแƒ (แƒ’แƒแƒฎแƒกแƒแƒ•แƒก, แƒ แƒแƒ› แƒแƒ›แƒแƒ–แƒ” แƒ’แƒ•แƒฅแƒแƒœแƒ“แƒ แƒกแƒแƒฃแƒ‘แƒแƒ แƒ˜ แƒ‘แƒแƒšแƒ แƒ’แƒแƒœแƒงแƒแƒคแƒ˜แƒšแƒ”แƒ‘แƒแƒจแƒ˜?).

แƒ›แƒแƒ“แƒ˜แƒ— แƒ’แƒแƒ“แƒแƒ•แƒ˜แƒ“แƒ”แƒ— 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:

แƒ—แƒแƒ›แƒแƒจแƒ˜.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.

แƒแƒ˜, แƒ แƒแƒ’แƒแƒ  แƒ’แƒแƒ›แƒแƒ˜แƒงแƒฃแƒ แƒ”แƒ‘แƒ แƒฉแƒ•แƒ”แƒœแƒ˜ แƒจแƒ”แƒฏแƒแƒฎแƒ”แƒ‘แƒ˜แƒก แƒ’แƒแƒ›แƒแƒ•แƒšแƒ”แƒœแƒ˜แƒก แƒ’แƒแƒœแƒฎแƒแƒ แƒชแƒ˜แƒ”แƒšแƒ”แƒ‘แƒ:

แƒจแƒ”แƒฏแƒแƒฎแƒ”แƒ‘แƒ”แƒ‘แƒ˜.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;
}

แƒจแƒ”แƒฏแƒแƒฎแƒ”แƒ‘แƒ˜แƒก แƒ”แƒก แƒ›แƒแƒ แƒขแƒ˜แƒ•แƒ˜ แƒแƒฆแƒ›แƒแƒฉแƒ”แƒœแƒ แƒ”แƒคแƒฃแƒซแƒœแƒ”แƒ‘แƒ แƒ˜แƒ› แƒคแƒแƒฅแƒขแƒก, แƒ แƒแƒ› แƒแƒ แƒ˜ แƒฌแƒ แƒ” แƒ”แƒฏแƒแƒฎแƒ”แƒ‘แƒ, แƒ—แƒฃ แƒ›แƒแƒ— แƒชแƒ”แƒœแƒขแƒ แƒ”แƒ‘แƒก แƒจแƒแƒ แƒ˜แƒก แƒ›แƒแƒœแƒซแƒ˜แƒšแƒ˜ แƒœแƒแƒ™แƒšแƒ”แƒ‘แƒ˜แƒ แƒ›แƒแƒ—แƒ˜ แƒ แƒแƒ“แƒ˜แƒฃแƒกแƒ”แƒ‘แƒ˜แƒก แƒฏแƒแƒ›แƒ–แƒ”. แƒแƒฅ แƒแƒ แƒ˜แƒก แƒจแƒ”แƒ›แƒ—แƒฎแƒ•แƒ”แƒ•แƒ, แƒ แƒแƒ“แƒ”แƒกแƒแƒช แƒ›แƒแƒœแƒซแƒ˜แƒšแƒ˜ แƒแƒ แƒ˜ แƒฌแƒ แƒ˜แƒก แƒชแƒ”แƒœแƒขแƒ แƒ”แƒ‘แƒก แƒจแƒแƒ แƒ˜แƒก แƒ–แƒฃแƒกแƒขแƒแƒ“ แƒฃแƒ“แƒ แƒ˜แƒก แƒ›แƒแƒ—แƒ˜ แƒ แƒแƒ“แƒ˜แƒฃแƒกแƒ”แƒ‘แƒ˜แƒก แƒฏแƒแƒ›แƒก:

Multiplayer .io แƒ•แƒ”แƒ‘ แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒจแƒ”แƒฅแƒ›แƒœแƒ
แƒแƒฅ แƒงแƒฃแƒ แƒแƒ“แƒฆแƒ”แƒ‘แƒ แƒฃแƒœแƒ“แƒ แƒ›แƒ˜แƒแƒฅแƒชแƒ˜แƒแƒ— แƒ™แƒ˜แƒ“แƒ”แƒ• แƒ แƒแƒ›แƒ“แƒ”แƒœแƒ˜แƒ›แƒ” แƒแƒกแƒžแƒ”แƒฅแƒขแƒก:

  • แƒญแƒฃแƒ แƒ•แƒ˜ แƒแƒ  แƒฃแƒœแƒ“แƒ แƒ›แƒแƒฎแƒ•แƒ“แƒ”แƒก แƒ›แƒแƒ—แƒแƒ›แƒแƒจแƒ”แƒก, แƒ แƒแƒ›แƒ”แƒšแƒ›แƒแƒช แƒจแƒ”แƒฅแƒ›แƒœแƒ แƒ˜แƒ’แƒ˜. แƒแƒ›แƒ˜แƒก แƒ›แƒ˜แƒฆแƒฌแƒ”แƒ•แƒ แƒจแƒ”แƒกแƒแƒซแƒšแƒ”แƒ‘แƒ”แƒšแƒ˜แƒ แƒจแƒ”แƒ“แƒแƒ แƒ”แƒ‘แƒ˜แƒ— bullet.parentID ั player.id.
  • แƒญแƒฃแƒ แƒ•แƒ˜ แƒฃแƒœแƒ“แƒ แƒ›แƒแƒฎแƒ•แƒ“แƒ”แƒก แƒ›แƒฎแƒแƒšแƒแƒ“ แƒ”แƒ แƒ—แƒฎแƒ”แƒš แƒ˜แƒ› แƒจแƒ”แƒ›แƒ–แƒฆแƒฃแƒ“แƒ•แƒ”แƒšแƒ˜ แƒจแƒ”แƒ›แƒ—แƒฎแƒ•แƒ”แƒ•แƒ˜แƒกแƒ—แƒ•แƒ˜แƒก, แƒ แƒแƒ“แƒ”แƒกแƒแƒช แƒ แƒแƒ›แƒ“แƒ”แƒœแƒ˜แƒ›แƒ” แƒ›แƒแƒ—แƒแƒ›แƒแƒจแƒ” แƒ”แƒ แƒ—แƒ“แƒ แƒแƒฃแƒšแƒแƒ“ แƒจแƒ”แƒ”แƒฏแƒแƒฎแƒ”แƒ‘แƒ. แƒฉแƒ•แƒ”แƒœ แƒแƒ› แƒžแƒ แƒแƒ‘แƒšแƒ”แƒ›แƒแƒก แƒ›แƒแƒ•แƒแƒ’แƒ•แƒแƒ แƒ”แƒ‘แƒ— แƒแƒžแƒ”แƒ แƒแƒขแƒแƒ แƒ˜แƒก แƒ’แƒแƒ›แƒแƒงแƒ”แƒœแƒ”แƒ‘แƒ˜แƒ— break: แƒ แƒแƒ’แƒแƒ แƒช แƒ™แƒ˜ แƒแƒฆแƒ›แƒแƒฉแƒœแƒ“แƒ”แƒ‘แƒ แƒญแƒฃแƒ แƒญแƒ”แƒšแƒ—แƒแƒœ แƒจแƒ”แƒฏแƒแƒฎแƒ”แƒ‘แƒฃแƒšแƒ˜ แƒ›แƒแƒ—แƒแƒ›แƒแƒจแƒ”, แƒ•แƒฌแƒงแƒ•แƒ”แƒขแƒ— แƒซแƒ”แƒ‘แƒœแƒแƒก แƒ“แƒ แƒ’แƒแƒ“แƒแƒ•แƒ“แƒ˜แƒ•แƒแƒ แƒ— แƒจแƒ”แƒ›แƒ“แƒ”แƒ’ แƒญแƒฃแƒ แƒ•แƒ–แƒ”.

แƒ‘แƒแƒšแƒแƒก

แฒกแƒฃแƒš แƒ”แƒก แƒแƒ แƒ˜แƒก! แƒฉแƒ•แƒ”แƒœ แƒ’แƒแƒœแƒ•แƒ˜แƒฎแƒ˜แƒšแƒ”แƒ— แƒงแƒ•แƒ”แƒšแƒแƒคแƒ”แƒ แƒ˜, แƒ แƒแƒช แƒ—แƒฅแƒ•แƒ”แƒœ แƒฃแƒœแƒ“แƒ แƒ˜แƒชแƒแƒ“แƒ”แƒ— .io แƒ•แƒ”แƒ‘ แƒ—แƒแƒ›แƒแƒจแƒ˜แƒก แƒจแƒ”แƒกแƒแƒฅแƒ›แƒœแƒ”แƒšแƒแƒ“. แฒ แƒ แƒแƒ แƒ˜แƒก แƒจแƒ”แƒ›แƒ“แƒ”แƒ’แƒ˜? แƒจแƒ”แƒฅแƒ›แƒ”แƒœแƒ˜แƒ— แƒ—แƒฅแƒ•แƒ”แƒœแƒ˜ แƒกแƒแƒ™แƒฃแƒ—แƒแƒ แƒ˜ .io แƒ—แƒแƒ›แƒแƒจแƒ˜!

แƒงแƒ•แƒ”แƒšแƒ แƒœแƒ˜แƒ›แƒฃแƒจแƒ˜ แƒ™แƒแƒ“แƒ˜ แƒแƒ แƒ˜แƒก แƒฆแƒ˜แƒ แƒฌแƒงแƒแƒ แƒ แƒ“แƒ แƒ’แƒแƒœแƒ—แƒแƒ•แƒกแƒ”แƒ‘แƒฃแƒšแƒ˜แƒ Github.

แƒฌแƒงแƒแƒ แƒ: www.habr.com

แƒแƒฎแƒแƒšแƒ˜ แƒ™แƒแƒ›แƒ”แƒœแƒขแƒแƒ แƒ˜แƒก แƒ“แƒแƒ›แƒแƒขแƒ”แƒ‘แƒ