แž€แžถแžšแž”แž„แŸ’แž€แžพแžแž แŸ’แž‚แŸแž˜แž”แžŽแŸ’แžแžถแž‰ .io แžขแŸ’แž“แž€แž›แŸแž„แž…แŸ’แžšแžพแž“แŸ”

แž€แžถแžšแž”แž„แŸ’แž€แžพแžแž แŸ’แž‚แŸแž˜แž”แžŽแŸ’แžแžถแž‰ .io แžขแŸ’แž“แž€แž›แŸแž„แž…แŸ’แžšแžพแž“แŸ”
แž…แŸแž‰แž•แŸ’แžŸแžถแž™แž“แŸ…แž†แŸ’แž“แžถแŸ† 2015 แžขแžถแž แŸ’แž‚แŸ€แŸ” แž”แžถแž“แž€แŸ’แž›แžถแž™แž‡แžถแž”แžปแž–แŸ’แžœแž”แžปแžšแžŸแž“แŸƒแž”แŸ’แžšแž—แŸแž‘แžแŸ’แž˜แžธแŸ” แž แŸ’แž‚แŸแž˜ .ioแžŠแŸ‚แž›โ€‹แž”แžถแž“โ€‹แž€แžพแž“โ€‹แžกแžพแž„โ€‹แž“แŸ…โ€‹แž€แŸ’แž“แžปแž„โ€‹แž”แŸ’แžšแž‡แžถแž”แŸ’แžšแžทแž™แž—แžถแž–โ€‹แž…แžถแž”แŸ‹โ€‹แžแžถแŸ†แž„โ€‹แž–แžธโ€‹แž–แŸแž›โ€‹แž“แŸ„แŸ‡โ€‹แž˜แž€โ€‹แŸ” แžแŸ’แž‰แžปแŸ†แž”แžถแž“แž‡แžฝแž”แž”แŸ’แžšแž‘แŸ‡แžŠแŸ„แž™แž•แŸ’แž‘แžถแž›แŸ‹แž“แžผแžœแž€แžถแžšแž€แžพแž“แžกแžพแž„แž“แŸƒแž”แŸ’แžšแž‡แžถแž”แŸ’แžšแžทแž™แž—แžถแž–แž“แŸƒแž แŸ’แž‚แŸแž˜ .ioแŸ– แž€แŸ’แž“แžปแž„แžšแž™แŸˆแž–แŸแž›แž”แžธแž†แŸ’แž“แžถแŸ†แž€แž“แŸ’แž›แž„แž˜แž€แž“แŸแŸ‡ แžแŸ’แž‰แžปแŸ†แž˜แžถแž“ แž”แžถแž“แž”แž„แŸ’แž€แžพแž แž“แžทแž„แž›แž€แŸ‹แž แŸ’แž‚แŸแž˜แž–แžธแžšแž”แŸ’แžšแž—แŸแž‘แž“แŸแŸ‡แŸ”.

แž€แŸ’แž“แžปแž„แž€แžšแžŽแžธแžŠแŸ‚แž›แžขแŸ’แž“แž€แž˜แžทแž“แž’แŸ’แž›แžถแž”แŸ‹แž”แžถแž“แžฎแžขแŸ†แž–แžธแž แŸ’แž‚แŸแž˜แž‘แžถแŸ†แž„แž“แŸแŸ‡แž–แžธแž˜แžปแž“แž˜แž€ แž‘แžถแŸ†แž„แž“แŸแŸ‡แž‚แžบแž‡แžถแž แŸ’แž‚แŸแž˜แžขแž“แžกแžถแž‰แžŠแŸ‚แž›แž˜แžถแž“แžขแŸ’แž“แž€แž›แŸแž„แž…แŸ’แžšแžพแž“แžŠแŸ„แž™แžฅแžแž‚แžทแžแžแŸ’แž›แŸƒ แžŠแŸ‚แž›แž„แžถแž™แžŸแŸ’แžšแžฝแž›แž›แŸแž„ (แž˜แžทแž“แž…แžถแŸ†แž”แžถแž…แŸ‹แž˜แžถแž“แž‚แžŽแž“แžธ)แŸ” แž‡แžถแž’แž˜แŸ’แž˜แžแžถแž–แžฝแž€แž‚แŸแž”แŸ’แžšแžˆแž˜แž˜แžปแžแž“แžนแž„แžขแŸ’แž“แž€แž›แŸแž„แž”แŸ’แžšแž†แžถแŸ†แž„แž‡แžถแž…แŸ’แžšแžพแž“แž“แŸ…แž€แŸ’แž“แžปแž„แžŸแž„แŸ’แžœแŸ€แž“แžแŸ‚แž˜แžฝแž™แŸ” แž แŸ’แž‚แŸแž˜ .io แž›แŸ’แž”แžธแŸ—แž•แŸ’แžŸแŸแž„แž‘แŸ€แžแŸ– Slither.io ะธ แžŒแžธแž—แžธแžขแžผแžขแžผ.

แž“แŸ…แž€แŸ’แž“แžปแž„แž€แžถแžšแž”แŸ’แžšแž€แžถแžŸแž“แŸแŸ‡แž™แžพแž„แž“แžนแž„แžŸแŸ’แžœแŸ‚แž„แž™แž›แŸ‹แž–แžธแžšแž”แŸ€แž” แž”แž„แŸ’แž€แžพแžแž แŸ’แž‚แŸแž˜ .io แž–แžธแžŠแŸ†แž”แžผแž„. แž…แŸ†แž–แŸ„แŸ‡แž”แž‰แŸ’แž แžถแž“แŸแŸ‡ แž˜แžถแž“แžแŸ‚แž…แŸ†แžŽแŸแŸ‡แžŠแžนแž„แžขแŸ†แž–แžธ Javascript แž”แŸ‰แžปแžŽแŸ’แžŽแŸ„แŸ‡แž“แžนแž„แž‚แŸ’แžšแž”แŸ‹แž‚แŸ’แžšแžถแž“แŸ‹แŸ– แžขแŸ’แž“แž€แžแŸ’แžšแžผแžœแž™แž›แŸ‹แžขแŸ’แžœแžธแŸ—แžŠแžผแž…แž‡แžถแžœแžถแž€แŸ’แž™แžŸแž˜แŸ’แž–แŸแž“แŸ’แž’ ES6, แž–แžถแž€แŸ’แž™แž‚แž“แŸ’แž›แžนแŸ‡ this ะธ แž€แžถแžšแžŸแž“แŸ’แž™แžถแŸ”. แž‘แŸ„แŸ‡แž”แžธแž‡แžถแž…แŸ†แžŽแŸแŸ‡แžŠแžนแž„แžšแž”แžŸแŸ‹แžขแŸ’แž“แž€แžขแŸ†แž–แžธ Javascript แž˜แžทแž“แž›แŸ’แžขแžฅแžแžแŸ’แž…แŸ„แŸ‡แž€แŸแžŠแŸ„แž™ แžขแŸ’แž“แž€แž“แŸ…แžแŸ‚แžขแžถแž…แž™แž›แŸ‹แž—แžถแž‚แž…แŸ’แžšแžพแž“แž“แŸƒแž”แŸ’แžšแž€แžถแžŸแŸ”

แžงแž‘แžถแž แžšแžŽแŸแž“แŸƒแž แŸ’แž‚แŸแž˜ .io

แžŸแž˜แŸ’แžšแžถแž”แŸ‹แž‡แŸ†แž“แžฝแž™แž€แžถแžšแžšแŸ€แž“แžŸแžผแžแŸ’แžš แž™แžพแž„แž“แžนแž„แž™แŸ„แž„แž‘แŸ… แžงแž‘แžถแž แžšแžŽแŸแž“แŸƒแž แŸ’แž‚แŸแž˜ .io. แžŸแžถแž€แž›แŸแž„แž‘แŸ…!

แž€แžถแžšแž”แž„แŸ’แž€แžพแžแž แŸ’แž‚แŸแž˜แž”แžŽแŸ’แžแžถแž‰ .io แžขแŸ’แž“แž€แž›แŸแž„แž…แŸ’แžšแžพแž“แŸ”
แž แŸ’แž‚แŸแž˜แž“แŸแŸ‡แž‚แžบแžŸแžถแž˜แž‰แŸ’แž‰แžŽแžถแžŸแŸ‹แŸ– แžขแŸ’แž“แž€แž‚แŸ’แžšแž”แŸ‹แž‚แŸ’แžšแž„แž€แž”แŸ‰แžถแž›แŸ‹แž“แŸ…แž€แŸ’แž“แžปแž„แžŸแž„แŸ’แžœแŸ€แž“แžŠแŸ‚แž›แž˜แžถแž“แžขแŸ’แž“แž€แž›แŸแž„แž•แŸ’แžŸแŸแž„แž‘แŸ€แžแŸ” แž€แž”แŸ‰แžถแž›แŸ‹แžšแž”แžŸแŸ‹แžขแŸ’แž“แž€แž”แžถแž‰แŸ‹แž€แžถแŸ†แž‡แŸ’แžšแžฝแž…แžŠแŸ„แž™แžŸแŸ’แžœแŸแž™แž”แŸ’แžšแžœแžแŸ’แžแžท แž แžพแž™แžขแŸ’แž“แž€แž–แŸ’แž™แžถแž™แžถแž˜แžœแžถแž™แž”แŸ’แžšแž แžถแžšแžขแŸ’แž“แž€แž›แŸแž„แž•แŸ’แžŸแŸแž„แž‘แŸ€แž แžแžŽแŸˆแž–แŸแž›แžŠแŸ‚แž›แž‡แŸ€แžŸแžœแžถแž„แž€แžถแžšแž”แžถแž‰แŸ‹แžšแž”แžŸแŸ‹แž–แžฝแž€แž‚แŸแŸ”

1. แž‘แžทแžŠแŸ’แž‹แž—แžถแž–แžŸแž„แŸ’แžแŸแž” / แžšแž…แž“แžถแžŸแž˜แŸ’แž–แŸแž“แŸ’แž’แž“แŸƒแž‚แž˜แŸ’แžšแŸ„แž„

แžแŸ’แž‰แžปแŸ†แžŸแžผแž˜แžŽแŸ‚แž“แžถแŸ† แž‘แžถแž‰แž™แž€แž€แžผแžŠแž”แŸ’แžšแž—แž– แžงแž‘แžถแž แžšแžŽแŸแž แŸ’แž‚แŸแž˜ แžŠแžผแž…แŸ’แž“แŸแŸ‡แžขแŸ’แž“แž€แžขแžถแž…แž’แŸ’แžœแžพแžแžถแž˜แžแŸ’แž‰แžปแŸ†แŸ”

แžงแž‘แžถแž แžšแžŽแŸแž”แŸ’แžšแžพแžŠแžผแž…แžแžถแž„แž€แŸ’แžšแŸ„แž˜แŸˆ

  • Express แž‚แžบแž‡แžถแž€แŸ’แžšแž”แžแŸแžŽแŸ’แžŒแž‚แŸแž แž‘แŸ†แž–แŸแžš Node.js แžŠแŸ‚แž›แž–แŸแž‰แž“แžทแž™แž˜แž”แŸ†แž•แžปแžแžŠแŸ‚แž›แž‚แŸ’แžšแž”แŸ‹แž‚แŸ’แžšแž„แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแž‚แŸแž แž‘แŸ†แž–แŸแžšแžšแž”แžŸแŸ‹แž แŸ’แž‚แŸแž˜แŸ”
  • socket.io - แž”แžŽแŸ’แžŽแžถแž›แŸแž™ websocket แžŸแž˜แŸ’แžšแžถแž”แŸ‹แž•แŸ’แž›แžถแžŸแŸ‹แž”แŸ’แžแžผแžšแž‘แžทแž“แŸ’แž“แž“แŸแž™แžšแžœแžถแž„ browser แž“แžทแž„ server แŸ”
  • แžœแŸ‰แžถแž™แžœแŸ‚แž”แŸ” - แžขแŸ’แž“แž€แž‚แŸ’แžšแž”แŸ‹แž‚แŸ’แžšแž„แž˜แŸ‰แžผแžŒแžปแž›แŸ” แžขแŸ’แž“แž€แžขแžถแž…แžขแžถแž“แžขแŸ†แž–แžธแž˜แžผแž›แž แŸแžแžปแžŠแŸ‚แž›แžแŸ’แžšแžผแžœแž”แŸ’แžšแžพ Webpack แŸ” แž“แŸ…แž‘แžธแž“แŸแŸ‡.

แž“แŸแŸ‡แž‡แžถแžขแŸ’แžœแžธแžŠแŸ‚แž›แžšแž…แž“แžถแžŸแž˜แŸ’แž–แŸแž“แŸ’แž’แžแžแž‚แž˜แŸ’แžšแŸ„แž„แž˜แžพแž›แž‘แŸ…แžŠแžผแž…แŸ–

public/
    assets/
        ...
src/
    client/
        css/
            ...
        html/
            index.html
        index.js
        ...
    server/
        server.js
        ...
    shared/
        constants.js

แžŸแžถแž’แžถแžšแžŽแŸˆ/

แžขแŸ’แžœแžธแž‚แŸ’แžšแž”แŸ‹แž™แŸ‰แžถแž„แž“แŸ…แž€แŸ’แž“แžปแž„แžแžแž˜แžฝแž™แŸ” public/ แž“แžนแž„แžแŸ’แžšแžผแžœแž”แžถแž“แžŠแžถแž€แŸ‹แž‡แžผแž“แžŠแŸ„แž™แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแŸ” IN public/assets/ แž˜แžถแž“แžšแžผแž”แž—แžถแž–แžŠแŸ‚แž›แž”แŸ’แžšแžพแžŠแŸ„แž™แž‚แž˜แŸ’แžšแŸ„แž„แžšแž”แžŸแŸ‹แž™แžพแž„แŸ”

src /

แž€แžผแžŠแž”แŸ’แžšแž—แž–แž‘แžถแŸ†แž„แžขแžŸแŸ‹แžŸแŸ’แžแžทแžแž“แŸ…แž€แŸ’แž“แžปแž„แžแžแžฏแž€แžŸแžถแžš src/. แž…แŸ†แžŽแž„แž‡แžพแž„ client/ ะธ server/ แž“แžทแž™แžถแž™แžŸแž˜แŸ’แžšแžถแž”แŸ‹แžแŸ’แž›แžฝแž“แž‚แŸแž“แžทแž„ shared/ แž˜แžถแž“แžฏแž€แžŸแžถแžšแžแŸแžšแžŠแŸ‚แž›แžแŸ’แžšแžผแžœแž”แžถแž“แž“แžถแŸ†แž…แžผแž›แžŠแŸ„แž™แž‘แžถแŸ†แž„แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž—แŸ’แž‰แŸ€แžœ แž“แžทแž„แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแŸ”

2. แž€แžถแžšแž”แŸ’แžšแž˜แžผแž›แž•แŸ’แžแžปแŸ†/แž€แžถแžšแž€แŸ†แžŽแžแŸ‹แž‚แž˜แŸ’แžšแŸ„แž„

แžŠแžผแž…แžŠแŸ‚แž›แž”แžถแž“แžšแŸ€แž”แžšแžถแž”แŸ‹แžแžถแž„แž›แžพ แž™แžพแž„แž”แŸ’แžšแžพแž€แž˜แŸ’แž˜แžœแžทแž’แžธแž‚แŸ’แžšแž”แŸ‹แž‚แŸ’แžšแž„แž˜แŸ‰แžผแžŒแžปแž›แžŠแžพแž˜แŸ’แž”แžธแž”แž„แŸ’แž€แžพแžแž‚แž˜แŸ’แžšแŸ„แž„แŸ” แžœแŸ‰แžถแž™แžœแŸ‚แž”แŸ”. แžŸแžผแž˜แž€แŸ’แžšแžกแŸแž€แž˜แžพแž›แž€แžถแžšแž€แŸ†แžŽแžแŸ‹แžšแž…แž“แžถแžŸแž˜แŸ’แž–แŸแž“แŸ’แž’ Webpack แžšแž”แžŸแŸ‹แž™แžพแž„แŸ–

webpack.common.jsแŸ–

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

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

แž”แž“แŸ’แž‘แžถแžแŸ‹แžŸแŸ†แžแžถแž“แŸ‹แž”แŸ†แž•แžปแžแž“แŸ…แž‘แžธแž“แŸแŸ‡แž‚แžบแŸ–

  • src/client/index.js แž‚แžบแž‡แžถแž…แŸ†แžŽแžปแž…แž…แžผแž›แžšแž”แžŸแŸ‹แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž—แŸ’แž‰แŸ€แžœ Javascript (JS) แŸ” Webpack แž“แžนแž„แž…แžถแž”แŸ‹แž•แŸ’แžแžพแž˜แž–แžธแž‘แžธแž“แŸแŸ‡ แž แžพแž™แžŸแŸ’แžœแŸ‚แž„แžšแž€แžกแžพแž„แžœแžทแž‰แž“แžผแžœแžฏแž€แžŸแžถแžšแžŠแŸ‚แž›แž”แžถแž“แž“แžถแŸ†แž…แžผแž›แž•แŸ’แžŸแŸแž„แž‘แŸ€แžแŸ”
  • แž›แž‘แŸ’แž’แž•แž› JS แž“แŸƒ โ€‹โ€‹Webpack build แžšแž”แžŸแŸ‹แž™แžพแž„แž“แžนแž„แž˜แžถแž“แž‘แžธแžแžถแŸ†แž„แž“แŸ…แž€แŸ’แž“แžปแž„แžแž dist/. แžแŸ’แž‰แžปแŸ†แž“แžนแž„แž แŸ…แžฏแž€แžŸแžถแžšแž“แŸแŸ‡แžšแž”แžŸแŸ‹แž™แžพแž„แŸ” แž€แž‰แŸ’แž…แž”แŸ‹ js.
  • แž™แžพแž„โ€‹แž”แŸ’แžšแžพ แž”แžถแž”แžทแž›แž“แžทแž„แž‡แžถแž–แžทแžŸแŸแžŸแž€แžถแžšแž€แŸ†แžŽแžแŸ‹แžšแž…แž“แžถแžŸแž˜แŸ’แž–แŸแž“แŸ’แž’ @babel/preset-env แžŠแžพแž˜แŸ’แž”แžธแž…แž˜แŸ’แž›แž„แž€แžผแžŠ JS แžšแž”แžŸแŸ‹แž™แžพแž„แžŸแž˜แŸ’แžšแžถแž”แŸ‹แž€แž˜แŸ’แž˜แžœแžทแž’แžธแžšแžปแž€แžšแž€แž…แžถแžŸแŸ‹แŸ”
  • แž™แžพแž„แž€แŸ†แž–แžปแž„แž”แŸ’แžšแžพแž€แž˜แŸ’แž˜แžœแžทแž’แžธแž‡แŸ†แž“แžฝแž™แžŠแžพแž˜แŸ’แž”แžธแž‘แžถแž‰แž™แž€ CSS แž‘แžถแŸ†แž„แžขแžŸแŸ‹แžŠแŸ‚แž›แž™แŸ„แž„แžŠแŸ„แž™แžฏแž€แžŸแžถแžš JS แž แžพแž™แž”แž‰แŸ’แž…แžผแž›แž–แžฝแž€แžœแžถแž“แŸ…แž€แž“แŸ’แž›แŸ‚แž„แžแŸ‚แž˜แžฝแž™แŸ” แžแŸ’แž‰แžปแŸ†แž“แžนแž„แž แŸ…แž‚แžถแžแŸ‹แžแžถแžšแž”แžŸแŸ‹แž™แžพแž„แŸ” แž€แž‰แŸ’แž…แž”แŸ‹ css.

แžขแŸ’แž“แž€แž”แŸ’แžšแž แŸ‚แž›แž‡แžถแž”แžถแž“แž€แžแŸ‹แžŸแž˜แŸ’แž‚แžถแž›แŸ‹แžƒแžพแž‰แžˆแŸ’แž˜แŸ„แŸ‡แžฏแž€แžŸแžถแžšแž€แž‰แŸ’แž…แž”แŸ‹แž…แž˜แŸ’แž›แŸ‚แž€ '[name].[contenthash].ext'. แž–แžฝแž€แžœแžถแž˜แžถแž“ แž€แžถแžšแž‡แŸ†แž“แžฝแžŸแžˆแŸ’แž˜แŸ„แŸ‡แžฏแž€แžŸแžถแžš แž‚แŸแž แž‘แŸ†แž–แŸแžš [name] แž“แžนแž„แžแŸ’แžšแžผแžœแž”แžถแž“แž‡แŸ†แž“แžฝแžŸแžŠแŸ„แž™แžˆแŸ’แž˜แŸ„แŸ‡แž“แŸƒแž…แŸ†แžŽแžปแž…แž”แž‰แŸ’แž…แžผแž› (แž€แŸ’แž“แžปแž„แž€แžšแžŽแžธแžšแž”แžŸแŸ‹แž™แžพแž„ แž“แŸแŸ‡แŸ” game) แž“แžทแž„ [contenthash] แž“แžนแž„แžแŸ’แžšแžผแžœแž”แžถแž“แž‡แŸ†แž“แžฝแžŸแžŠแŸ„แž™ hash แž“แŸƒแž˜แžถแžแžทแž€แžถแžšแž”แžŸแŸ‹แžฏแž€แžŸแžถแžšแŸ” แž™แžพแž„แž’แŸ’แžœแžพแžœแžถแž‘แŸ… แž”แž„แŸ’แž€แžพแž“แž”แŸ’แžšแžŸแžทแž‘แŸ’แž’แž—แžถแž–แž‚แž˜แŸ’แžšแŸ„แž„แžŸแž˜แŸ’แžšแžถแž”แŸ‹ hashing - แžขแŸ’แž“แž€แžขแžถแž…แž”แŸ’แžšแžถแž”แŸ‹แž€แž˜แŸ’แž˜แžœแžทแž’แžธแžšแžปแž€แžšแž€แžฑแŸ’แž™แžšแž€แŸ’แžŸแžถแž‘แžปแž€แž€แž‰แŸ’แž…แž”แŸ‹ 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แžŠแžพแž˜แŸ’แž”แžธแž”แž„แŸ’แž€แžพแž“แž”แŸ’แžšแžŸแžทแž‘แŸ’แž’แž—แžถแž–แž‘แŸ†แž แŸ†แž€แž‰แŸ’แž…แž”แŸ‹แž“แŸ…แž–แŸแž›แžŠแžถแž€แŸ‹แž–แž„แŸ’แžšแžถแž™แž‘แŸ…แž•แž›แžทแžแž€แž˜แŸ’แž˜แŸ”

แž€แžถแžšแž€แŸ†แžŽแžแŸ‹แž€แŸ’แž“แžปแž„แžŸแŸ’แžšแžปแž€

แžแŸ’แž‰แžปแŸ†แžŸแžผแž˜แžŽแŸ‚แž“แžถแŸ†แžฑแŸ’แž™แžŠแŸ†แžกแžพแž„แž‚แž˜แŸ’แžšแŸ„แž„แž“แŸ…แž›แžพแž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แžผแž›แžŠแŸ’แž‹แžถแž“ แžŠแžผแž…แŸ’แž“แŸแŸ‡แžขแŸ’แž“แž€แžขแžถแž…แžขแž“แžปแžœแžแŸ’แžแžแžถแž˜แž‡แŸ†แž แžถแž“แžŠแŸ‚แž›แž˜แžถแž“แžšแžถแž™แž€แŸ’แž“แžปแž„แž”แŸ’แžšแž€แžถแžŸแž“แŸแŸ‡แŸ” แž€แžถแžšแžŠแŸ†แžกแžพแž„แž‚แžบแžŸแžถแž˜แž‰แŸ’แž‰: แžŠแŸ†แž”แžผแž„แž”แŸ’แžšแž–แŸแž“แŸ’แž’แžแŸ’แžšแžผแžœแžแŸ‚แžŠแŸ†แžกแžพแž„ แžแŸ’แž“แžถแŸ†แž„ ะธ NPM. แž”แž“แŸ’แž‘แžถแž”แŸ‹แžขแŸ’แž“แž€แžแŸ’แžšแžผแžœแž’แŸ’แžœแžพ

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

แž แžพแž™แžขแŸ’แž“แž€แžแŸ’แžšแŸ€แž˜แžแŸ’แž›แžฝแž“แž‘แŸ…! แžŠแžพแž˜แŸ’แž”แžธแž…แžถแž”แŸ‹แž•แŸ’แžแžพแž˜แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแžขแž—แžทแžœแžŒแŸ’แžแž“แŸ แž‚แŸ’แžšแžถแž“แŸ‹แžแŸ‚แžŠแŸ†แžŽแžพแžšแž€แžถแžš

$ npm run develop

แž แžพแž™แž…แžผแž›แž‘แŸ…แž€แžถแž“แŸ‹แž€แž˜แŸ’แž˜แžœแžทแž’แžธแžšแžปแž€แžšแž€แž”แžŽแŸ’แžŠแžถแž‰ localhost: 3000. แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแžขแž—แžทแžœแžŒแŸ’แžแž“แŸแž“แžนแž„แž”แž„แŸ’แž€แžพแžแž€แž‰แŸ’แž…แž”แŸ‹ JS แž“แžทแž„ CSS แžกแžพแž„แžœแžทแž‰แžŠแŸ„แž™แžŸแŸ’แžœแŸแž™แž”แŸ’แžšแžœแžแŸ’แžแžท แž“แŸ…แž–แŸแž›แžŠแŸ‚แž›แž€แžผแžŠแž•แŸ’แž›แžถแžŸแŸ‹แž”แŸ’แžแžผแžš - แž‚แŸ’แžšแžถแž“แŸ‹แžแŸ‚แž’แŸ’แžœแžพแžฑแŸ’แž™แž‘แŸ†แž–แŸแžšแžกแžพแž„แžœแžทแž‰แžŠแžพแž˜แŸ’แž”แžธแž˜แžพแž›แž€แžถแžšแž•แŸ’แž›แžถแžŸแŸ‹แž”แŸ’แžแžผแžšแž‘แžถแŸ†แž„แžขแžŸแŸ‹!

3. แž…แŸ†แžŽแžปแž…แž…แžผแž›แžšแž”แžŸแŸ‹แžขแžแžทแžแžทแž‡แž“

แžแŸ„แŸ‡แž…แžปแŸ‡แž€แžผแžŠแž แŸ’แž‚แŸแž˜แžแŸ’แž›แžฝแž“แžฏแž„แŸ” แžŠแŸ†แž”แžผแž„แž™แžพแž„แžแŸ’แžšแžผแžœแž€แžถแžšแž‘แŸ†แž–แŸแžš index.htmlแž“แŸ…แž–แŸแž›แž…แžผแž›แž‘แŸ…แž€แžถแž“แŸ‹แž‚แŸแž แž‘แŸ†แž–แŸแžš แž€แž˜แŸ’แž˜แžœแžทแž’แžธแžšแžปแž€แžšแž€แž“แžนแž„แž•แŸ’แž‘แžปแž€แžœแžถแž‡แžถแž˜แžปแž“แžŸแžทแž“แŸ” แž‘แŸ†แž–แŸแžšแžšแž”แžŸแŸ‹แž™แžพแž„แž“แžนแž„แž˜แžถแž“แž›แž€แŸ’แžแžŽแŸˆแžŸแžถแž˜แž‰แŸ’แž‰แŸ–

index.html

แžงแž‘แžถแž แžšแžŽแŸ .io แž แŸ’แž‚แŸแž˜  แž›แŸแž„

แžงแž‘แžถแž แžšแžŽแŸแž€แžผแžŠแž“แŸแŸ‡แžแŸ’แžšแžผแžœแž”แžถแž“แž’แŸ’แžœแžพแžฑแŸ’แž™แžŸแžถแž˜แž‰แŸ’แž‰แž”แž“แŸ’แžแžทแž…แžŸแž˜แŸ’แžšแžถแž”แŸ‹แž—แžถแž–แž…แŸ’แž”แžถแžŸแŸ‹แž›แžถแžŸแŸ‹ แž แžพแž™แžแŸ’แž‰แžปแŸ†แž“แžนแž„แž’แŸ’แžœแžพแžŠแžผแž…แž‚แŸ’แž“แžถแž‡แžถแž˜แžฝแž™แž“แžนแž„แžงแž‘แžถแž แžšแžŽแŸแž“แŸƒแž€แžถแžšแž”แž„แŸ’แž แŸ„แŸ‡แž•แŸ’แžŸแŸแž„แž‘แŸ€แžแž‡แžถแž…แŸ’แžšแžพแž“แŸ” แž›แŸแžแž€แžผแžŠแž–แŸแž‰แžขแžถแž…แž˜แžพแž›แž”แžถแž“แž‚แŸ’แžšแž”แŸ‹แž–แŸแž› Github.

แž™แžพแž„โ€‹แž˜แžถแž“:

  • แž’แžถแžแžปแž•แŸ’แž‘แžถแŸ†แž„แž€แŸ’แžšแžŽแžถแžแŸ‹ HTML5 (<canvas>) แžŠแŸ‚แž›แž™แžพแž„แž“แžนแž„แž”แŸ’แžšแžพแžŠแžพแž˜แŸ’แž”แžธแž”แž„แŸ’แž แžถแž‰แž แŸ’แž‚แŸแž˜แŸ”
  • <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. แž€แŸ†แžŽแžแŸ‹แžงแž”แž€แžšแžŽแŸแžŠแŸ„แŸ‡แžŸแŸ’แžšแžถแž™แžŸแž˜แŸ’แžšแžถแž”แŸ‹แž€แžถแžšแž…แžปแž…แž”แŸŠแžผแžแžปแž„ "แž›แŸแž„" แŸ” แž“แŸ…แž–แŸแž›แžŠแŸ‚แž›แž”แŸŠแžผแžแžปแž„แžแŸ’แžšแžผแžœแž”แžถแž“แž…แžปแž… แž›แŸแžแž€แžผแžŠแž…แžถแž”แŸ‹แž•แŸ’แžแžพแž˜แž แŸ’แž‚แŸแž˜ แž แžพแž™แž”แŸ’แžšแžถแž”แŸ‹แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแžแžถแž™แžพแž„แžšแžฝแž…แžšแžถแž›แŸ‹แž€แŸ’แž“แžปแž„แž€แžถแžšแž›แŸแž„แŸ”

"แžŸแžถแž…แŸ‹" แžŸแŸ†แžแžถแž“แŸ‹แž“แŸƒแžแž€แŸ’แž€แžœแžทแž‡แŸ’แž‡แžถแž˜แŸ‰แžถแžŸแŸŠแžธแž“แž”แž˜แŸ’แžšแžพแžขแžแžทแžแžทแž‡แž“แžšแž”แžŸแŸ‹แž™แžพแž„แž‚แžบแž“แŸ…แž€แŸ’แž“แžปแž„แžฏแž€แžŸแžถแžšแž‘แžถแŸ†แž„แž“แŸ„แŸ‡แžŠแŸ‚แž›แžแŸ’แžšแžผแžœแž”แžถแž“แž“แžถแŸ†แž…แžผแž›แžŠแŸ„แž™แžฏแž€แžŸแžถแžš 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. แž€แžถแžšแž”แž„แŸ’แž แžถแž‰แžขแžแžทแžแžทแž‡แž“

แžŠแž›แŸ‹แž–แŸแž›แž”แž„แŸ’แž แžถแž‰แžšแžผแž”แž—แžถแž–แž“แŸ…แž›แžพแžขแŸแž€แŸ’แžšแž„แŸ‹แž แžพแž™!

โ€ฆ แž”แŸ‰แžปแž“แŸ’แžแŸ‚แž˜แžปแž“แž“แžนแž„แž™แžพแž„แžขแžถแž…แž’แŸ’แžœแžพแžœแžถแž”แžถแž“ แž™แžพแž„แžแŸ’แžšแžผแžœแž‘แžถแž‰แž™แž€แžšแžผแž”แž—แžถแž–แž‘แžถแŸ†แž„แžขแžŸแŸ‹ (แž’แž“แž’แžถแž“) แžŠแŸ‚แž›แžแŸ’แžšแžผแžœแž€แžถแžšแžŸแž˜แŸ’แžšแžถแž”แŸ‹แž€แžถแžšแž“แŸแŸ‡แŸ” แžแŸ„แŸ‡แžŸแžšแžŸแŸแžšแžขแŸ’แž“แž€แž‚แŸ’แžšแž”แŸ‹แž‚แŸ’แžšแž„แž’แž“แž’แžถแž“แŸ–

แž‘แŸ’แžšแž–แŸ’แž™แžŸแž€แž˜แŸ’แž˜.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. แž€แžถแžšแž”แž‰แŸ’แž…แžผแž›แžšแž”แžŸแŸ‹แžขแžแžทแžแžทแž‡แž“

แžŠแž›แŸ‹แž–แŸแž›แž”แž„แŸ’แž€แžพแžแž แŸ’แž‚แŸแž˜แž แžพแž™แŸ” แžขแžถแž…แž›แŸแž„แž”แžถแž“แŸ”! แž‚แŸ’แžšแŸ„แž„แž€แžถแžšแžŽแŸแž”แž‰แŸ’แž‡แžถแž“แžนแž„แž˜แžถแž“แž›แž€แŸ’แžแžŽแŸˆแžŸแžถแž˜แž‰แŸ’แž‰แžŽแžถแžŸแŸ‹แŸ– แžŠแžพแž˜แŸ’แž”แžธแž•แŸ’แž›แžถแžŸแŸ‹แž”แŸ’แžแžผแžšแž‘แžทแžŸแžŠแŸ…แž“แŸƒแž…แž›แž“แžถแžขแŸ’แž“แž€แžขแžถแž…แž”แŸ’แžšแžพแž€แžŽแŸ’แžแžปแžš (แž“แŸ…แž›แžพแž€แžปแŸ†แž–แŸ’แž™แžผแž‘แŸแžš) แžฌแž”แŸ‰แŸ‡แžขแŸแž€แŸ’แžšแž„แŸ‹ (แž“แŸ…แž›แžพแžงแž”แž€แžšแžŽแŸแž…แž›แŸแž) แŸ” แžŠแžพแž˜แŸ’แž”แžธแžขแž“แžปแžœแžแŸ’แžแžœแžถแž™แžพแž„แž“แžนแž„แž…แžปแŸ‡แžˆแŸ’แž˜แŸ„แŸ‡ แžขแŸ’แž“แž€แžŸแŸ’แžแžถแž”แŸ‹แž–แŸ’แžšแžนแžแŸ’แžแžทแž€แžถแžšแžŽแŸ แžŸแž˜แŸ’แžšแžถแž”แŸ‹แž–แŸ’แžšแžนแžแŸ’แžแžทแž€แžถแžšแžŽแŸ Mouse แž“แžทแž„ TouchแŸ”
แž“แžนแž„แžแŸ‚แžšแž€แŸ’แžŸแžถแžขแŸ’แžœแžธแŸ—แž‘แžถแŸ†แž„แžขแžŸแŸ‹แž“แŸแŸ‡ 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แŸ– แž–แŸแžแŸŒแž˜แžถแž“แžขแŸ†แž–แžธแžขแŸ’แž“แž€แž›แŸแž„แžŠแŸ‚แž›แž‘แž‘แžฝแž›แž”แžถแž“แž€แžถแžšแžขแžถแž”แŸ‹แžŠแŸแžแž“แŸแŸ‡แŸ”
  • แžขแŸ’แž“แž€แž•แŸ’แžŸแŸแž„แž‘แŸ€แžแŸ– แžขแžถแžšแŸแž“แŸƒแž–แŸแžแŸŒแž˜แžถแž“แžขแŸ†แž–แžธแžขแŸ’แž“แž€แž›แŸแž„แž•แŸ’แžŸแŸแž„แž‘แŸ€แžแžŠแŸ‚แž›แž…แžผแž›แžšแžฝแž˜แž€แŸ’แž“แžปแž„แž แŸ’แž‚แŸแž˜แžŠแžผแž…แž‚แŸ’แž“แžถแŸ”
  • แž‚แŸ’แžšแžถแž”แŸ‹แž€แžถแŸ†แž—แŸ’แž›แžพแž„แŸ– แžขแžถแžšแŸแž“แŸƒแž–แŸแžแŸŒแž˜แžถแž“แžขแŸ†แž–แžธ projectiles แž“แŸ…แž€แŸ’แž“แžปแž„แž แŸ’แž‚แŸแž˜แŸ”
  • แžแžถแžšแžถแž„แž–แžทแž“แŸ’แž‘แžปแŸ– แž‘แžทแž“แŸ’แž“แž“แŸแž™แžแžถแžšแžถแž„แž–แžทแž“แŸ’แž‘แžปแž”แž…แŸ’แž…แžปแž”แŸ’แž”แž“แŸ’แž“แŸ” แž“แŸ…แž€แŸ’แž“แžปแž„แž€แžถแžšแž”แŸ’แžšแž€แžถแžŸแž“แŸแŸ‡แž™แžพแž„แž“แžนแž„แž˜แžทแž“แž–แžทแž…แžถแžšแžŽแžถแž–แžฝแž€แž‚แŸแž‘แŸแŸ”

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 แž”แŸ’แžšแžพแž‘แžถแŸ†แž„ render lag แž“แžทแž„ linear interpolation แž”แŸ‰แžปแž“แŸ’แžแŸ‚แž˜แžทแž“แž™แžผแžšแž‘แŸแŸ” แž…แžผแžšแž”แŸ†แž”แŸ‚แž€แž€แžผแžŠแž‡แžถแž–แžธแžšแž•แŸ’แž“แŸ‚แž€แŸ” แž“แŸแŸ‡แž‚แžบแž‡แžถแž‘แžธแž˜แžฝแž™แŸ–

state.js แž•แŸ’แž“แŸ‚แž€แž‘แžธ 1

const RENDER_DELAY = 100;

const gameUpdates = [];
let gameStart = 0;
let firstServerTimestamp = 0;

export function initState() {
  gameStart = 0;
  firstServerTimestamp = 0;
}

export function processGameUpdate(update) {
  if (!firstServerTimestamp) {
    firstServerTimestamp = update.t;
    gameStart = Date.now();
  }
  gameUpdates.push(update);

  // Keep only one game update before the current server time
  const base = getBaseUpdate();
  if (base > 0) {
    gameUpdates.splice(0, base);
  }
}

function currentServerTime() {
  return firstServerTimestamp + (Date.now() - gameStart) - RENDER_DELAY;
}

// Returns the index of the base update, the first game update before
// current server time, or -1 if N/A.
function getBaseUpdate() {
  const serverTime = currentServerTime();
  for (let i = gameUpdates.length - 1; i >= 0; i--) {
    if (gameUpdates[i].t <= serverTime) {
      return i;
    }
  }
  return -1;
}

แž‡แŸ†แž แžถแž“แžŠแŸ†แž”แžผแž„แž‚แžบแžแŸ’แžšแžผแžœแžŸแŸ’แžœแŸ‚แž„แž™แž›แŸ‹แž–แžธแžขแŸ’แžœแžธ currentServerTime(). แžŠแžผแž…แžŠแŸ‚แž›แž™แžพแž„แž”แžถแž“แžƒแžพแž‰แž˜แžปแž“แž“แŸแŸ‡ แžšแžถแž›แŸ‹แž€แžถแžšแžขแžถแž”แŸ‹แžŠแŸแžแž แŸ’แž‚แŸแž˜แžšแžฝแž˜แž”แž‰แŸ’แž…แžผแž›แž€แžถแžšแžแŸ’แžšแžถแž–แŸแž›แžœแŸแž›แžถแžšแž”แžŸแŸ‹แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแŸ” แž™แžพแž„แž…แž„แŸ‹แž”แŸ’แžšแžพ render latency แžŠแžพแž˜แŸ’แž”แžธแž”แž„แŸ’แž แžถแž‰แžšแžผแž”แž—แžถแž– 100ms แž“แŸ…แžแžถแž„แž€แŸ’แžšแŸ„แž™ server แž”แŸ‰แžปแž“แŸ’แžแŸ‚ แž™แžพแž„แž“แžนแž„แž˜แžทแž“แžŠแžนแž„แž–แžธแž–แŸแž›แžœแŸแž›แžถแž”แž…แŸ’แž…แžปแž”แŸ’แž”แž“แŸ’แž“แž“แŸ…แž›แžพแž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแž‘แŸแŸ”แžŠแŸ„แž™โ€‹แžŸแžถแžšโ€‹แžแŸ‚โ€‹แž™แžพแž„โ€‹แž˜แžทแž“โ€‹แžขแžถแž…โ€‹แžŠแžนแž„โ€‹แžแžถโ€‹แžแžพโ€‹แžœแžถโ€‹แžแŸ’แžšแžผแžœโ€‹แž…แŸ†แžŽแžถแž™โ€‹แž–แŸแž›โ€‹แž”แŸ‰แžปแž“แŸ’แž˜แžถแž“โ€‹แžŸแž˜แŸ’แžšแžถแž”แŸ‹โ€‹แž€แžถแžšโ€‹แžขแžถแž”แŸ‹แžŠแŸแžโ€‹แžŽแžถโ€‹แž˜แžฝแž™โ€‹แžŠแžพแž˜แŸ’แž”แžธโ€‹แž˜แž€โ€‹แžŠแž›แŸ‹โ€‹แž™แžพแž„แŸ” แžขแŸŠแžธแž“แž’แžบแžŽแŸ‚แžแž˜แžทแž“แžขแžถแž…แž‘แžถแž™แž‘แžปแž€แž‡แžถแž˜แžปแž“แž”แžถแž“ แž แžพแž™แž›แŸ’แž”แžฟแž“แžšแž”แžŸแŸ‹แžœแžถแžขแžถแž…แž”แŸ’แžšแŸ‚แž”แŸ’แžšแžฝแž›แž™แŸ‰แžถแž„แžแŸ’แž›แžถแŸ†แž„!

แžŠแžพแž˜แŸ’แž”แžธแžŠแŸ„แŸ‡แžŸแŸ’แžšแžถแž™แž”แž‰แŸ’แž แžถแž“แŸแŸ‡ แž™แžพแž„แžขแžถแž…แž”แŸ’แžšแžพแž€แžถแžšแž”แŸ‰แžถแž“แŸ‹แžŸแŸ’แž˜แžถแž“แžŸแž˜แž แŸแžแžปแž•แž›แŸ– แž™แžพแž„ แž’แŸ’แžœแžพแž–แžปแžแžแžถแž€แžถแžšแžขแžถแž”แŸ‹แžŠแŸแžแžŠแŸ†แž”แžผแž„แž”แžถแž“แž˜แž€แžŠแž›แŸ‹แž—แŸ’แž›แžถแž˜แŸ—. แž”แŸ’แžšแžŸแžทแž“แž”แžพแž“แŸแŸ‡แž‡แžถแž€แžถแžšแž–แžทแž แž“แŸ„แŸ‡แž™แžพแž„แž“แžนแž„แžŠแžนแž„แž–แžธแž–แŸแž›แžœแŸแž›แžถแžšแž”แžŸแŸ‹แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแž“แŸ…แž–แŸแž›แž“แŸแŸ‡! แž™แžพแž„แžšแž€แŸ’แžŸแžถแž‘แžปแž€แžแŸ’แžšแžถแž–แŸแž›แžœแŸแž›แžถแžšแž”แžŸแŸ‹แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸ 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. แž™แžพแž„แž˜แžถแž“แž€แžถแžšแžขแžถแž”แŸ‹แžŠแŸแžแž‘แžถแŸ†แž„แž˜แžปแž“ แž“แžทแž„แž€แŸ’แžšแŸ„แž™แž–แŸแž›แžœแŸแž›แžถ render แž”แž…แŸ’แž…แžปแž”แŸ’แž”แž“แŸ’แž“ แžŠแžผแž…แŸ’แž“แŸแŸ‡แž™แžพแž„แžขแžถแž…แž’แŸ’แžœแžพแž”แžถแž“แŸ” แžขแž“แŸ’แžแžšแž”แŸ‰แžผแž›แŸ”!

แžขแŸ’แžœแžธแž‘แžถแŸ†แž„แžขแžŸแŸ‹แžŠแŸ‚แž›แž“แŸ…แžŸแŸแžŸแžŸแž›แŸ‹ state.js แž‚แžบโ€‹แž‡แžถโ€‹แž€แžถแžšโ€‹แžขแž“แžปแžœแžแŸ’แžโ€‹แž“แŸƒโ€‹แž€แžถแžšโ€‹แž€แžถแžแŸ‹โ€‹แž”แž‰แŸ’แž…แžผแž›โ€‹แž›แžธแž“แŸแžขแŸŠแŸ‚แžšโ€‹แžŠแŸ‚แž›โ€‹แžŸแžถแž˜แž‰แŸ’แž‰ (แž”แŸ‰แžปแž“แŸ’แžแŸ‚โ€‹แž‚แžฝแžšโ€‹แžฑแŸ’แž™โ€‹แž’แžปแž‰โ€‹) แž‚แžŽแžทแžแžœแžทแž‘แŸ’แž™แžถแŸ” แž”แŸ’แžšแžŸแžทแž“แž”แžพแžขแŸ’แž“แž€แž…แž„แŸ‹แžšแžปแž€แžšแž€แžœแžถแžŠแŸ„แž™แžแŸ’แž›แžฝแž“แžฏแž„ แž”แž“แŸ’แž‘แžถแž”แŸ‹แž˜แž€แž”แžพแž€ state.js แž“แŸ…แž›แžพ Github.

แž•แŸ’แž“แŸ‚แž€แž‘แžธ 2. แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแž•แŸ’แž“แŸ‚แž€แžแžถแž„แž€แŸ’แžšแŸ„แž™

แž“แŸ…แž€แŸ’แž“แžปแž„แž•แŸ’แž“แŸ‚แž€แž“แŸแŸ‡ แž™แžพแž„แž“แžนแž„แž–แžทแž“แžทแžแŸ’แž™แž˜แžพแž›แž•แŸ’แž“แŸ‚แž€แžแžถแž„แž€แŸ’แžšแŸ„แž™ Node.js แžŠแŸ‚แž›แž‚แŸ’แžšแž”แŸ‹แž‚แŸ’แžšแž„แžšแž”แžŸแŸ‹แž™แžพแž„แŸ” แžงแž‘แžถแž แžšแžŽแŸแž“แŸƒแž แŸ’แž‚แŸแž˜ .io.

1. แž…แŸ†แžŽแžปแž…แž…แžผแž›แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸ

แžŠแžพแž˜แŸ’แž”แžธแž‚แŸ’แžšแž”แŸ‹แž‚แŸ’แžšแž„ web server แž™แžพแž„แž“แžนแž„แž”แŸ’แžšแžพ web framework แžŠแŸแž–แŸแž‰แž“แžทแž™แž˜แž˜แžฝแž™แžŸแž˜แŸ’แžšแžถแž”แŸ‹ Node.js แžŠแŸ‚แž›แž แŸ…แžแžถ Express. แžœแžถแž“แžนแž„แžแŸ’แžšแžผแžœแž”แžถแž“แž€แŸ†แžŽแžแŸ‹แžšแž…แž“แžถแžŸแž˜แŸ’แž–แŸแž“แŸ’แž’แžŠแŸ„แž™แžฏแž€แžŸแžถแžšแž…แŸ†แžŽแžปแž…แž”แž‰แŸ’แž…แžผแž›แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแžšแž”แžŸแŸ‹แž™แžพแž„แŸ” src/server/server.js:

server.js แž•แŸ’แž“แŸ‚แž€แž‘แžธ 1

const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackConfig = require('../../webpack.dev.js');

// Setup an Express server
const app = express();
app.use(express.static('public'));

if (process.env.NODE_ENV === 'development') {
  // Setup Webpack for development
  const compiler = webpack(webpackConfig);
  app.use(webpackDevMiddleware(compiler));
} else {
  // Static serve the dist/ folder in production
  app.use(express.static('dist'));
}

// Listen on port
const port = process.env.PORT || 3000;
const server = app.listen(port);
console.log(`Server listening on port ${port}`);

แž…แžถแŸ†แžแžถแž“แŸ…แž€แŸ’แž“แžปแž„แž•แŸ’แž“แŸ‚แž€แžŠแŸ†แž”แžผแž„แžŠแŸ‚แž›แž™แžพแž„แž–แžทแž—แžถแž€แŸ’แžŸแžถแžขแŸ†แž–แžธ Webpack? แž“แŸแŸ‡แž‡แžถแž€แž“แŸ’แž›แŸ‚แž„แžŠแŸ‚แž›แž™แžพแž„แž“แžนแž„แž”แŸ’แžšแžพแž€แžถแžšแž€แŸ†แžŽแžแŸ‹แžšแž…แž“แžถแžŸแž˜แŸ’แž–แŸแž“แŸ’แž’ Webpack แžšแž”แžŸแŸ‹แž™แžพแž„แŸ” แž™แžพแž„แž“แžนแž„แž”แŸ’แžšแžพแžœแžถแžแžถแž˜แž–แžธแžšแžœแžทแž’แžธแŸ–

  • ะ˜ัะฟะพะปัŒะทะพะฒะฐั‚ัŒ webpack-dev-middleware แžŠแžพแž˜แŸ’แž”แžธแž”แž„แŸ’แž€แžพแžแž€แž‰แŸ’แž…แž”แŸ‹แžขแž—แžทแžœแžŒแŸ’แžแž“แŸแžšแž”แžŸแŸ‹แž™แžพแž„แžกแžพแž„แžœแžทแž‰แžŠแŸ„แž™แžŸแŸ’แžœแŸแž™แž”แŸ’แžšแžœแžแŸ’แžแžท แžฌ
  • แž•แŸ’แž‘แŸแžšแžแžแžŸแŸ’แžแžทแžแžท dist/แžŠแŸ‚แž›แž€แŸ’แž“แžปแž„แž“แŸ„แŸ‡ Webpack แž“แžนแž„แžŸแžšแžŸแŸแžšแžฏแž€แžŸแžถแžšแžšแž”แžŸแŸ‹แž™แžพแž„แž”แž“แŸ’แž‘แžถแž”แŸ‹แž–แžธแž€แžถแžšแž”แž„แŸ’แž€แžพแžแž•แž›แžทแžแž€แž˜แŸ’แž˜แŸ”

แž€แžทแž…แŸ’แž…แž€แžถแžšแžŸแŸ†แžแžถแž“แŸ‹แž˜แžฝแž™แž‘แŸ€แž server.js แž‚แžบโ€‹แžŠแžพแž˜แŸ’แž”แžธโ€‹แžŠแŸ†แžกแžพแž„โ€‹แž˜แŸ‰แžถแžŸแŸŠแžธแž“โ€‹แž”แž˜แŸ’แžšแžพ socket.ioแžŠแŸ‚แž›แž‚แŸ’แžšแžถแž“แŸ‹แžแŸ‚แž—แŸ’แž‡แžถแž”แŸ‹แž‘แŸ…แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸ ExpressแŸ–

server.js แž•แŸ’แž“แŸ‚แž€แž‘แžธ 2

const socketio = require('socket.io');
const Constants = require('../shared/constants');

// Setup Express
// ...
const server = app.listen(port);
console.log(`Server listening on port ${port}`);

// Setup socket.io
const io = socketio(server);

// Listen for socket.io connections
io.on('connection', socket => {
  console.log('Player connected!', socket.id);

  socket.on(Constants.MSG_TYPES.JOIN_GAME, joinGame);
  socket.on(Constants.MSG_TYPES.INPUT, handleInput);
  socket.on('disconnect', onDisconnect);
});

แž”แž“แŸ’แž‘แžถแž”แŸ‹แž–แžธแž”แž„แŸ’แž€แžพแžแž€แžถแžšแžแž—แŸ’แž‡แžถแž”แŸ‹ socket.io แž‘แŸ…แž€แžถแž“แŸ‹แž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแžŠแŸ„แž™แž‡แŸ„แž‚แž‡แŸแž™ แž™แžพแž„แžšแŸ€แž”แž…แŸ†แž€แž˜แŸ’แž˜แžœแžทแž’แžธแžŠแŸ„แŸ‡แžŸแŸ’แžšแžถแž™แž–แŸ’แžšแžนแžแŸ’แžแžทแž€แžถแžšแžŽแŸแžŸแž˜แŸ’แžšแžถแž”แŸ‹แžšแž“แŸ’แž’แžแŸ’แž˜แžธแŸ” แžขแŸ’แž“แž€แžŠแŸ„แŸ‡แžŸแŸ’แžšแžถแž™แž–แŸ’แžšแžนแžแŸ’แžแžทแž€แžถแžšแžŽแŸแž‚แŸ’แžšแž”แŸ‹แž‚แŸ’แžšแž„แžŸแžถแžšแžŠแŸ‚แž›แž”แžถแž“แž‘แž‘แžฝแž›แž–แžธแžขแžแžทแžแžทแž‡แž“แžŠแŸ„แž™แž•แŸ’แž‘แŸแžšแžŸแžทแž‘แŸ’แž’แžทแž‘แŸ…แžœแžแŸ’แžแžป 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แžŠแžผแž…แŸ’แž“แŸแŸ‡ แž™แžพแž„โ€‹แž˜แžทแž“โ€‹แž…แžถแŸ†แž”แžถแž…แŸ‹โ€‹แž”แžถแžšแž˜แŸ’แž—โ€‹แžขแŸ†แž–แžธโ€‹แžšแžฟแž„โ€‹แž“แŸ„แŸ‡โ€‹แž‘แŸแŸ” แžแŸ’แž‰แžปแŸ†แž“แžนแž„แž แŸ…แž‚แžถแžแŸ‹ แž›แŸแžแžŸแž˜แŸ’แž‚แžถแž›แŸ‹แžขแŸ’แž“แž€แž›แŸแž„.

แžŠแŸ„แž™แž‚แžทแžแž€แŸ’แž“แžปแž„แž…แžทแžแŸ’แžแž“แŸ„แŸ‡ แž…แžผแžšแž™แžพแž„แžŸแŸ’แžœแŸ‚แž„แž™แž›แŸ‹แž–แžธแžขแžแŸแžšแžงแž‘แžถแž แžšแžŽแŸแž“แŸ…แž€แŸ’แž“แžปแž„แžแŸ’แž“แžถแž€แŸ‹แž˜แžฝแž™แŸ” Game:

  • sockets แž‚แžบโ€‹แž‡แžถโ€‹แžœแžแŸ’แžแžปโ€‹แžŠแŸ‚แž›โ€‹แž—แŸ’แž‡แžถแž”แŸ‹โ€‹แž›แŸแžโ€‹แžŸแž˜แŸ’แž‚แžถแž›แŸ‹โ€‹แžขแŸ’แž“แž€โ€‹แž›แŸแž„โ€‹แž‘แŸ…โ€‹แž“แžนแž„โ€‹แžšแž“แŸ’แž’โ€‹แžŠแŸ‚แž›โ€‹แž˜แžถแž“โ€‹แž‘แŸ†แž“แžถแž€แŸ‹แž‘แŸ†แž“แž„โ€‹แž‡แžถแž˜แžฝแž™โ€‹แž“แžนแž„โ€‹แžขแŸ’แž“แž€โ€‹แž›แŸแž„แŸ” แžœแžถแžขแž“แžปแž‰แŸ’แž‰แžถแžแžฑแŸ’แž™แž™แžพแž„แž…แžผแž›แž”แŸ’แžšแžพแžšแž“แŸ’แž’แžŠแŸ„แž™แž›แŸแžแžŸแž˜แŸ’แž‚แžถแž›แŸ‹แžขแŸ’แž“แž€แž›แŸแž„แžšแž”แžŸแŸ‹แž–แžฝแž€แž‚แŸแž€แŸ’แž“แžปแž„แžšแž™แŸˆแž–แŸแž›แžแŸแžšแŸ”
  • players แž‚แžบแž‡แžถแžœแžแŸ’แžแžปแžŠแŸ‚แž›แž—แŸ’แž‡แžถแž”แŸ‹ ID แžขแŸ’แž“แž€แž›แŸแž„แž‘แŸ…แž“แžนแž„แž€แžผแžŠ> แžœแžแŸ’แžแžปแžขแŸ’แž“แž€แž›แŸแž„

bullets แž‚แžบแž‡แžถแžขแžถแžšแŸแž“แŸƒแžœแžแŸ’แžแžป BulletแžŠแŸ‚แž›แž˜แžทแž“แž˜แžถแž“แž›แŸ†แžŠแžถแž”แŸ‹แž…แŸ’แž”แžถแžŸแŸ‹แž›แžถแžŸแŸ‹แŸ”
lastUpdateTime แž‚แžบแž‡แžถแž–แŸแž›แžœแŸแž›แžถแž…แžปแž„แž€แŸ’แžšแŸ„แž™แžŠแŸ‚แž›แž แŸ’แž‚แŸแž˜แžแŸ’แžšแžผแžœแž”แžถแž“แžขแžถแž”แŸ‹แžŠแŸแžแŸ” แž™แžพแž„โ€‹แž“แžนแž„โ€‹แžƒแžพแž‰โ€‹แž–แžธโ€‹แžšแž”แŸ€แž”โ€‹แžŠแŸ‚แž›โ€‹แžœแžถโ€‹แžแŸ’แžšแžผแžœโ€‹แž”แžถแž“โ€‹แž”แŸ’แžšแžพโ€‹แž€แŸ’แž“แžปแž„โ€‹แž–แŸแž›โ€‹แžแŸ’แž›แžธแŸ”
shouldSendUpdate แž‚แžบแž‡แžถแžขแžแŸแžšแž‡แŸ†แž“แžฝแž™แŸ” แž™แžพแž„แž€แŸแž“แžนแž„แžƒแžพแž‰แž€แžถแžšแž”แŸ’แžšแžพแž”แŸ’แžšแžถแžŸแŸ‹แžšแž”แžŸแŸ‹แžœแžถแž€แŸ’แž“แžปแž„แž–แŸแž›แž†แžถแž”แŸ‹แŸ—แž“แŸแŸ‡แž•แž„แžŠแŸ‚แžšแŸ”
แžœแžทแž’แžธแžŸแžถแžŸแŸ’แžšแŸ’แžแŸ” addPlayer(), removePlayer() ะธ handleInput() แž˜แžทแž“แž…แžถแŸ†แž”แžถแž…แŸ‹แž–แž“แŸ’แž™แž›แŸ‹แž‘แŸ แž‚แŸแž”แŸ’แžšแžพแž€แŸ’แž“แžปแž„ server.js. แž”แŸ’แžšแžŸแžทแž“แž”แžพแžขแŸ’แž“แž€แžแŸ’แžšแžผแžœแž€แžถแžšแž’แŸ’แžœแžพแžฑแŸ’แž™แž€แžถแžšแž…แž„แž…แžถแŸ†แžšแž”แžŸแŸ‹แžขแŸ’แž“แž€แžกแžพแž„แžœแžทแž‰ แžŸแžผแž˜แžแŸ’แžšแž›แž”แŸ‹แž‘แŸ…แžแŸ’แž–แžŸแŸ‹แž‡แžถแž„แž“แŸแŸ‡แž”แž“แŸ’แžแžทแž…แŸ”

แž”แž“แŸ’แž‘แžถแžแŸ‹แž…แžปแž„แž€แŸ’แžšแŸ„แž™ constructor() แž…แžถแž”แŸ‹แž•แŸ’แžแžพแž˜ แžœแžŠแŸ’แžแž’แŸ’แžœแžพแžฑแŸ’แž™แž‘แžถแž“แŸ‹แžŸแž˜แŸแž™ แž แŸ’แž‚แŸแž˜ (แž‡แžถแž˜แžฝแž™แž“แžนแž„แž—แžถแž–แž‰แžนแž€แž‰แžถแž”แŸ‹แž“แŸƒแž€แžถแžšแž’แŸ’แžœแžพแž”แž…แŸ’แž…แžปแž”แŸ’แž”แž“แŸ’แž“แž—แžถแž– 60 / s):

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()) แž แžพแž™แž”แž“แŸ’แž‘แžถแž”แŸ‹แž˜แž€แž™แž€ projectile แž…แŸแž‰แž–แžธ array bullets.
  5. แž‡แžผแž“แžŠแŸ†แžŽแžนแž„ แž“แžทแž„แž”แŸ†แž•แŸ’แž›แžถแž‰แžขแŸ’แž“แž€แž›แŸแž„แžŠแŸ‚แž›แž”แžถแž“แžŸแŸ’แž›แžถแž”แŸ‹แž‘แžถแŸ†แž„แžขแžŸแŸ‹แŸ”
  6. แž•แŸ’แž‰แžพแž€แžถแžšแžขแžถแž”แŸ‹แžŠแŸแžแž แŸ’แž‚แŸแž˜แž‘แŸ…แž€แžถแž“แŸ‹แžขแŸ’แž“แž€แž›แŸแž„แž‘แžถแŸ†แž„แžขแžŸแŸ‹แŸ” แžšแŸ€แž„แžšแžถแž›แŸ‹แžœแžทแž“แžถแž‘แžธ แž–แŸแž›แžŠแŸ‚แž›แž‚แŸแž แŸ… update(). แžœแžถแž‡แžฝแž™แž™แžพแž„แžฑแŸ’แž™แžแžถแž˜แžŠแžถแž“แžขแžแŸแžšแž‡แŸ†แž“แžฝแž™แžŠแŸ‚แž›แž”แžถแž“แžšแŸ€แž”แžšแžถแž”แŸ‹แžแžถแž„แž›แžพแŸ” shouldSendUpdate. แž‡แžถ update() แž แŸ…แžแžถ 60 แžŠแž„/แžœแžทแž“แžถแž‘แžธ แž™แžพแž„แž•แŸ’แž‰แžพแž€แžถแžšแžขแžถแž”แŸ‹แžŠแŸแžแž แŸ’แž‚แŸแž˜ 30 แžŠแž„/แžœแžทแž“แžถแž‘แžธแŸ” แžŠแžผแž…แŸ’แž“แŸแŸ‡ แž”แŸ’แžšแŸแž€แž„แŸ‹แž“แžถแžกแžทแž€แžถ แž“แžถแžกแžทแž€แžถแž˜แŸ‰แžถแžŸแŸŠแžธแž“แž˜แŸแž‚แžบ 30 แž“แžถแžกแžทแž€แžถ / แžœแžทแž“แžถแž‘แžธ (แž™แžพแž„แž”แžถแž“แž“แžทแž™แžถแž™แžขแŸ†แž–แžธแžขแžแŸ’แžšแžถแž“แžถแžกแžทแž€แžถแž“แŸ…แž€แŸ’แž“แžปแž„แž•แŸ’แž“แŸ‚แž€แžŠแŸ†แž”แžผแž„) แŸ”

แž แŸแžแžปแžขแŸ’แžœแžธแž•แŸ’แž‰แžพแžแŸ‚แž€แžถแžšแžขแžถแž”แŸ‹แžŠแŸแžแž แŸ’แž‚แŸแž˜แž”แŸ‰แžปแžŽแŸ’แžŽแŸ„แŸ‡แŸ” แžแžถแž˜แžšแž™แŸˆแž–แŸแž›แžœแŸแž›แžถ ? แžŠแžพแž˜แŸ’แž”แžธแžšแž€แŸ’แžŸแžถแž‘แžปแž€แž†แžถแž“แŸ‚แž›แŸ” แž€แžถแžšแžขแžถแž”แŸ‹แžŠแŸแžแž แŸ’แž‚แŸแž˜แž…แŸ†แž“แžฝแž“ 30 แž€แŸ’แž“แžปแž„แž˜แžฝแž™แžœแžทแž“แžถแž‘แžธแž‚แžบแž…แŸ’แžšแžพแž“แžŽแžถแžŸแŸ‹!

แž แŸแžแžปแžขแŸ’แžœแžธแž˜แžทแž“แž‚แŸ’แžšแžถแž“แŸ‹แžแŸ‚แž แŸ… update() 30 แžŠแž„แž€แŸ’แž“แžปแž„แž˜แžฝแž™แžœแžทแž“แžถแž‘แžธ? แžŠแžพแž˜แŸ’แž”แžธแž€แŸ‚แž›แž˜แŸ’แžขแž€แžถแžšแž€แŸ’แž›แŸ‚แž„แž’แŸ’แžœแžพแž แŸ’แž‚แŸแž˜แŸ” แž€แžถแžšแž แŸ…แž€แžถแž“แŸ‹แžแŸ‚แž…แŸ’แžšแžพแž“แž‡แžถแž‰แžนแž€แž‰แžถแž”แŸ‹ update()แž€แžถแžšแž€แŸ’แž›แŸ‚แž„แž’แŸ’แžœแžพแž แŸ’แž‚แŸแž˜แž“แžนแž„แž€แžถแž“แŸ‹แžแŸ‚แž˜แžถแž“แž—แžถแž–แžแŸ’แžšแžนแž˜แžแŸ’แžšแžผแžœแŸ” แž”แŸ‰แžปแž“แŸ’แžแŸ‚โ€‹แž€แžปแŸ†โ€‹แž™แž€โ€‹แž‘แŸ…โ€‹แž†แŸ’แž„แžถแž™โ€‹แž‡แžถแž˜แžฝแž™โ€‹แž“แžนแž„โ€‹แž…แŸ†แž“แžฝแž“โ€‹แžงแž”แžŸแž‚แŸ’แž‚แŸ” update()แž–แžธแž–แŸ’แžšแŸ„แŸ‡แž“แŸแŸ‡แž‚แžบแž‡แžถแž€แžทแž…แŸ’แž…แž€แžถแžšแžŠแŸ‚แž›แž˜แžถแž“แžแž˜แŸ’แž›แŸƒแžแŸ’แž›แŸƒแž€แŸ’แž“แžปแž„แž€แžถแžšแž‚แžŽแž“แžถ - 60 แž€แŸ’แž“แžปแž„แž˜แžฝแž™แžœแžทแž“แžถแž‘แžธแž‚แžบแž‚แŸ’แžšแž”แŸ‹แž‚แŸ’แžšแžถแž“แŸ‹แž แžพแž™แŸ”

แž“แŸ…แžŸแž›แŸ‹แž“แŸƒแžแŸ’แž“แžถแž€แŸ‹ Game แž˜แžถแž“แžœแžทแž’แžธแžŸแžถแžŸแŸ’แžšแŸ’แžแž‡แŸ†แž“แžฝแž™แžŠแŸ‚แž›แž”แŸ’แžšแžพแž€แŸ’แž“แžปแž„ update():

game.js แž•แŸ’แž“แŸ‚แž€แž‘แžธ 3

class Game {
  // ...

  getLeaderboard() {
    return Object.values(this.players)
      .sort((p1, p2) => p2.score - p1.score)
      .slice(0, 5)
      .map(p => ({ username: p.username, score: Math.round(p.score) }));
  }

  createUpdate(player, leaderboard) {
    const nearbyPlayers = Object.values(this.players).filter(
      p => p !== player && p.distanceTo(player) <= Constants.MAP_SIZE / 2,
    );
    const nearbyBullets = this.bullets.filter(
      b => b.distanceTo(player) <= Constants.MAP_SIZE / 2,
    );

    return {
      t: Date.now(),
      me: player.serializeForUpdate(),
      others: nearbyPlayers.map(p => p.serializeForUpdate()),
      bullets: nearbyBullets.map(b => b.serializeForUpdate()),
      leaderboard,
    };
  }
}

getLeaderboard() แžŸแžถแž˜แž‰แŸ’แž‰แžŽแžถแžŸแŸ‹ - แžœแžถแžแž˜แŸ’แžšแŸ€แž”แžขแŸ’แž“แž€แž›แŸแž„แžแžถแž˜แž–แžทแž“แŸ’แž‘แžป แž™แž€แž€แŸ†แž–แžผแž›แž‘แžถแŸ†แž„แž”แŸ’แžšแžถแŸ† แž แžพแž™แžแŸ’แžšแžกแž”แŸ‹แžˆแŸ’แž˜แŸ„แŸ‡แžขแŸ’แž“แž€แž”แŸ’แžšแžพแž”แŸ’แžšแžถแžŸแŸ‹ แž“แžทแž„แž–แžทแž“แŸ’แž‘แžปแžŸแž˜แŸ’แžšแžถแž”แŸ‹แž˜แŸ’แž“แžถแž€แŸ‹แŸ—แŸ”

createUpdate() แž”แžถแž“แž”แŸ’แžšแžพแž“แŸ…แž€แŸ’แž“แžปแž„ update() แžŠแžพแž˜แŸ’แž”แžธแž”แž„แŸ’แž€แžพแžแž”แž…แŸ’แž…แžปแž”แŸ’แž”แž“แŸ’แž“แž—แžถแž–แž แŸ’แž‚แŸแž˜แžŠแŸ‚แž›แžแŸ’แžšแžผแžœแž”แžถแž“แž…แŸ‚แž€แž…แžถแž™แžŠแž›แŸ‹แžขแŸ’แž“แž€แž›แŸแž„แŸ” แž—แžถแžšแž€แžทแž…แŸ’แž…แž…แž˜แŸ’แž”แž„แžšแž”แžŸแŸ‹แžœแžถแž‚แžบแžŠแžพแž˜แŸ’แž”แžธแž แŸ…แžœแžทแž’แžธแžŸแžถแžŸแŸ’แžšแŸ’แž serializeForUpdate()แž”แžถแž“แžขแž“แžปแžœแžแŸ’แžแžŸแž˜แŸ’แžšแžถแž”แŸ‹แžแŸ’แž“แžถแž€แŸ‹ Player ะธ Bullet. แž…แŸ†แžŽแžถแŸ†แžแžถแžœแžถแž”แž‰แŸ’แž‡แžผแž“แžแŸ‚แž‘แžทแž“แŸ’แž“แž“แŸแž™แž‘แŸ…แžขแŸ’แž“แž€แž›แŸแž„แž˜แŸ’แž“แžถแž€แŸ‹แŸ—แžขแŸ†แž–แžธ แž‡แžทแžแž”แŸ†แž•แžปแžแŸ” แžขแŸ’แž“แž€แž›แŸแž„แž“แžทแž„แž€แžถแŸ†แž‡แŸ’แžšแžฝแž… - แž˜แžทแž“แž…แžถแŸ†แž”แžถแž…แŸ‹แž”แž‰แŸ’แž‡แžผแž“แž–แŸแžแŸŒแž˜แžถแž“แžขแŸ†แž–แžธแžœแžแŸ’แžแžปแž แŸ’แž‚แŸแž˜แžŠแŸ‚แž›แž“แŸ…แž†แŸ’แž„แžถแž™แž–แžธแžขแŸ’แž“แž€แž›แŸแž„แž‘แŸ!

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:

bullet.js

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

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

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

ะ ะตะฐะปะธะทะฐั†ะธั Bullet แžแŸ’แž›แžธโ€‹แžŽแžถแžŸแŸ‹! แž™แžพแž„แž”แžถแž“แž”แž“แŸ’แžแŸ‚แž˜แž‘แŸ… Object แž˜แžถแž“แžแŸ‚แž•แŸ’แž“แŸ‚แž€แž”แž“แŸ’แžแŸ‚แž˜แžแžถแž„แž€แŸ’แžšแŸ„แž˜แž”แŸ‰แžปแžŽแŸ’แžŽแŸ„แŸ‡แŸ–

  • แžŠแŸ„แž™แž”แŸ’แžšแžพแž€แž‰แŸ’แž…แž”แŸ‹แž˜แžฝแž™แŸ” แžแŸ’แž›แžธ แžŸแž˜แŸ’แžšแžถแž”แŸ‹แž‡แŸ†แž“แžถแž“แŸ‹แž…แŸƒแžŠแž“แŸ’แž™ id แž€แžถแŸ†แž‡แŸ’แžšแžฝแž…แŸ”
  • แž€แžถแžšแž”แž“แŸ’แžแŸ‚แž˜แžœแžถแž›แž˜แžฝแž™แŸ” parentIDแžŠแžผแž…แŸ’แž“แŸแŸ‡โ€‹แžขแŸ’แž“แž€โ€‹แžขแžถแž…โ€‹แžแžถแž˜โ€‹แžŠแžถแž“โ€‹แžขแŸ’แž“แž€โ€‹แž›แŸแž„โ€‹แžŠแŸ‚แž›โ€‹แž”แžถแž“โ€‹แž”แž„แŸ’แž€แžพแžโ€‹แž€แžถแŸ†แž‡แŸ’แžšแžฝแž…โ€‹แž“แŸแŸ‡แŸ”
  • แž€แžถแžšแž”แž“แŸ’แžแŸ‚แž˜แžแž˜แŸ’แž›แŸƒแžแŸ’แžšแžกแž”แŸ‹แž‘แŸ… 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() แž’แŸ’แžœแžพแž€แžถแžšแž„แžถแžšแž…แŸ’แžšแžพแž“ แž‡แžถแž–แžทแžŸแŸแžŸ แžแŸ’แžšแžกแž”แŸ‹ projectile แžŠแŸ‚แž›แž‘แžพแž”แž”แž„แŸ’แž€แžพแžแžแŸ’แž˜แžธ แž”แŸ’แžšแžŸแžทแž“แž”แžพแž‚แŸ’แž˜แžถแž“แžŸแž›แŸ‹ fireCooldown (แž…แžถแŸ†แžแžถแž™แžพแž„แž”แžถแž“แž“แžทแž™แžถแž™แžขแŸ†แž–แžธแžšแžฟแž„แž“แŸแŸ‡แž“แŸ…แž€แŸ’แž“แžปแž„แž•แŸ’แž“แŸ‚แž€แž˜แžปแž“แž‘แŸ?) แžœแžถแž€แŸแž–แž„แŸ’แžšแžธแž€แžœแžทแž’แžธแžŸแžถแžŸแŸ’แžšแŸ’แžแž•แž„แžŠแŸ‚แžšแŸ” serializeForUpdate()แž–แžธแž–แŸ’แžšแŸ„แŸ‡แž™แžพแž„แžแŸ’แžšแžผแžœแž”แž‰แŸ’แž…แžผแž›แžœแžถแž›แž”แž“แŸ’แžแŸ‚แž˜แžŸแž˜แŸ’แžšแžถแž”แŸ‹แžขแŸ’แž“แž€แž›แŸแž„แž€แŸ’แž“แžปแž„แž€แžถแžšแžขแžถแž”แŸ‹แžŠแŸแžแž แŸ’แž‚แŸแž˜แŸ”

แž˜แžถแž“แžแŸ’แž“แžถแž€แŸ‹แž˜แžผแž›แžŠแŸ’แž‹แžถแž“ Object - แž‡แŸ†แž แžถแž“แžŸแŸ†แžแžถแž“แŸ‹แž˜แžฝแž™แžŠแžพแž˜แŸ’แž”แžธแž…แŸ€แžŸแžœแžถแž„แž€แžถแžšแžŸแžšแžŸแŸแžšแž€แžผแžŠแžกแžพแž„แžœแžทแž‰. แžงแž‘แžถแž แžšแžŽแŸแž‚แŸ’แž˜แžถแž“แžแŸ’แž“แžถแž€แŸ‹ Object แžšแžถแž›แŸ‹แžœแžแŸ’แžแžปแž แŸ’แž‚แŸแž˜แžแŸ’แžšแžผแžœแžแŸ‚แž˜แžถแž“แž€แžถแžšแžขแž“แžปแžœแžแŸ’แžแžŠแžผแž…แž‚แŸ’แž“แžถแŸ” distanceTo()แž แžพแž™แž€แžถแžšแž…แž˜แŸ’แž›แž„-แž”แžทแž‘แž—แŸ’แž‡แžถแž”แŸ‹แž€แžถแžšแžขแž“แžปแžœแžแŸ’แžแž‘แžถแŸ†แž„แžขแžŸแŸ‹แž“แŸแŸ‡แž“แŸ…แž›แžพแžฏแž€แžŸแžถแžšแž‡แžถแž…แŸ’แžšแžพแž“แž“แžนแž„แž€แŸ’แž›แžถแž™แž‡แžถแžŸแžปแž”แžทแž“แŸ’แžแžขแžถแž€แŸ’แžšแž€แŸ‹แŸ” แžœแžถแž˜แžถแž“แžŸแžถแžšแŸˆแžŸแŸ†แžแžถแž“แŸ‹แž‡แžถแž–แžทแžŸแŸแžŸแžŸแž˜แŸ’แžšแžถแž”แŸ‹แž‚แž˜แŸ’แžšแŸ„แž„แž’แŸ†แŸ—แŸ”แž“แŸ…แž–แŸแž›แžŠแŸ‚แž›แž…แŸ†แž“แžฝแž“แž“แŸƒแž€แžถแžšแž–แž„แŸ’แžšแžธแž€ Object แžแŸ’แž“แžถแž€แŸ‹แžšแŸ€แž“แž€แŸ†แž–แžปแž„แž€แžพแž“แžกแžพแž„แŸ”

4. แž€แžถแžšแžšแž€แžƒแžพแž‰แž€แžถแžšแž”แŸ‰แŸ‡แž‘แž„แŸ’แž‚แžทแž…

แžขแŸ’แžœแžธโ€‹แžŠแŸ‚แž›โ€‹แž“แŸ…โ€‹แžŸแž›แŸ‹โ€‹แžŸแž˜แŸ’แžšแžถแž”แŸ‹โ€‹แž™แžพแž„โ€‹แž‚แžบโ€‹แž€แžถแžšโ€‹แž‘แž‘แžฝแž›โ€‹แžŸแŸ’แž‚แžถแž›แŸ‹โ€‹แž“แŸ…โ€‹แž–แŸแž›โ€‹แžŠแŸ‚แž›โ€‹แž€แžถแžšโ€‹แž”แžถแž‰แŸ‹โ€‹แž”แŸ’แžšแž แžถแžšโ€‹แž‘แŸ…โ€‹แž›แžพโ€‹แž€แžธแžกแžถแž€แžšโ€‹! แž…แž„แž…แžถแŸ†แž”แŸ†แžŽแŸ‚แž€แž“แŸƒแž€แžผแžŠแž“แŸแŸ‡แž–แžธแžœแžทแž’แžธแžŸแžถแžŸแŸ’แžแŸ’แžš update() แž“แŸ…แž€แŸ’แž“แžปแž„แžแŸ’แž“แžถแž€แŸ‹ Game:

game.js

const applyCollisions = require('./collisions');

class Game {
  // ...

  update() {
    // ...

    // Apply collisions, give players score for hitting bullets
    const destroyedBullets = applyCollisions(
      Object.values(this.players),
      this.bullets,
    );
    destroyedBullets.forEach(b => {
      if (this.players[b.parentID]) {
        this.players[b.parentID].onDealtDamage();
      }
    });
    this.bullets = this.bullets.filter(
      bullet => !destroyedBullets.includes(bullet),
    );

    // ...
  }
}

แž™แžพแž„แžแŸ’แžšแžผแžœแžขแž“แžปแžœแžแŸ’แžแžœแžทแž’แžธแžŸแžถแžŸแŸ’แžšแŸ’แž applyCollisions()แžŠแŸ‚แž›แž“แžนแž„แžแŸ’แžšแž›แž”แŸ‹แž˜แž€แžœแžทแž‰แž“แžผแžœแžšแžถแž›แŸ‹แž€แžถแžšแž”แžถแž‰แŸ‹แž”แŸ’แžšแž แžถแžšแžŠแŸ‚แž›แžœแžถแž™แž”แŸ’แžšแž แžถแžšแžขแŸ’แž“แž€แž›แŸแž„แŸ” แžŸแŸ†แžŽแžถแž„แž แžพแž™ แžœแžถแž˜แžทแž“แž–แžทแž”แžถแž€แž’แŸ’แžœแžพแž‘แŸ แž–แŸ’แžšแŸ„แŸ‡

  • แžœแžแŸ’แžแžปแžŠแŸ‚แž›แž”แžปแž€แž‚แŸ’แž“แžถแž‘แžถแŸ†แž„แžขแžŸแŸ‹แž‚แžบแž‡แžถแžšแž„แŸ’แžœแž„แŸ‹ แžŠแŸ‚แž›แž‡แžถแžšแžผแž”แžšแžถแž„แžŸแžถแž˜แž‰แŸ’แž‰แž”แŸ†แž•แžปแžแžŠแžพแž˜แŸ’แž”แžธแžขแž“แžปแžœแžแŸ’แžแž€แžถแžšแžšแž€แžƒแžพแž‰แž€แžถแžšแž”แŸ‰แŸ‡แž‘แž„แŸ’แž‚แžทแž…
  • แž™แžพแž„แž˜แžถแž“แžœแžทแž’แžธแžŸแžถแžŸแŸ’แžšแŸ’แžแžšแžฝแž…แž แžพแž™ 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

แž”แž“แŸ’แžแŸ‚แž˜แž˜แžแžทแž™แŸ„แž”แž›แŸ‹