Tsim Multiplayer .io Web Game

Tsim Multiplayer .io Web Game
Tso tawm xyoo 2015 Agar.io los ua tus progenitor ntawm ib hom ntawv tshiab games .iouas tau loj hlob nyob rau hauv muaj koob meej txij thaum ntawd los. Kuv tus kheej tau ntsib qhov nce hauv qhov nrov ntawm .io games: dhau peb xyoos dhau los, kuv muaj tsim thiab muag ob qho kev ua si ntawm hom ntawv no..

Nyob rau hauv rooj plaub koj tsis tau hnov ​​​​txog cov kev ua si ua ntej, cov no yog dawb multiplayer web games uas yooj yim ua si (tsis xav tau tus account). Lawv feem ntau ntsib ntau tus neeg tawm tsam hauv tib lub chaw sib tw. Lwm yam nto moo .io games: Slither.io ΠΈ Diep.io.

Hauv tsab xov xwm no, peb yuav tshawb xyuas yuav ua li cas tsim ib qho kev ua si .io los ntawm kos. Rau qhov no, tsuas yog kev paub txog Javascript yuav txaus: koj yuav tsum nkag siab tej yam xws li syntax ES6, lo lus tseem ceeb this ΠΈ Cov lus cog tseg. Txawm hais tias koj qhov kev paub ntawm Javascript tsis zoo tag nrho, koj tseem tuaj yeem nkag siab feem ntau ntawm cov ncej.

.io game piv txwv

Rau kev pab kawm, peb yuav xa mus rau .io game piv txwv. Sim ua si!

Tsim Multiplayer .io Web Game
Qhov kev ua si yog qhov yooj yim heev: koj tswj lub nkoj hauv ib qho chaw uas muaj lwm tus players. Koj lub nkoj cia li tua hluav taws thiab koj sim ntaus lwm tus players thaum zam lawv cov phiaj xwm.

1. Cov ntsiab lus luv luv / qauv ntawm qhov project

Kuv pom zoo download tau qhov chaws piv txwv game kom koj ua raws kuv.

Piv txwv siv cov hauv qab no:

  • qhia yog Node.js lub vev xaib nrov tshaj plaws uas tswj hwm qhov kev ua si lub vev xaib.
  • socket.io ib - lub tsev qiv ntawv websocket rau kev sib pauv cov ntaub ntawv ntawm browser thiab server.
  • webpack - tus thawj tswj module. Koj tuaj yeem nyeem txog vim li cas thiaj siv Webpack. no.

Nov yog qhov project directory qauv zoo li:

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

pej xeem/

Txhua yam hauv ib daim nplaub tshev public/ yuav statically xa los ntawm server. IN public/assets/ muaj cov duab siv los ntawm peb qhov project.

src /

Tag nrho cov code yog nyob rau hauv lub folder src/. Cov npe client/ ΠΈ server/ hais lus rau lawv tus kheej thiab shared/ muaj cov ntaub ntawv tas mus li uas tau muab los ntawm ob tus neeg siv khoom thiab tus neeg rau zaub mov.

2. Kev teeb tsa / qhov project

Raws li tau hais los saum no, peb siv tus tswj hwm module los tsim qhov project. webpack. Cia peb saib ntawm peb Webpack configuration:

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',
    }),
  ],
};

Cov kab tseem ceeb tshaj plaws ntawm no yog:

  • src/client/index.js yog lub ntsiab lus nkag ntawm Javascript (JS) tus neeg siv khoom. Webpack yuav pib los ntawm no thiab tshawb nrhiav recursively rau lwm cov ntaub ntawv imported.
  • Cov zis JS ntawm peb Webpack tsim yuav nyob rau hauv phau ntawv qhia dist/. Kuv yuav hu daim ntawv no rau peb js pob.
  • Peb siv Babel, thiab tshwj xeeb tshaj yog configuration @babel/preset-env los transpiling peb JS code rau cov laus browsers.
  • Peb tab tom siv lub plugin los rho tawm tag nrho cov CSS hais los ntawm JS cov ntaub ntawv thiab muab tso rau hauv ib qho chaw. Kuv yuav hu nws peb css pob.

Tej zaum koj yuav tau pom txawv pob filenames '[name].[contenthash].ext'. Lawv muaj hloov npe filename Hnab ntim khoom: [name] yuav raug hloov nrog lub npe ntawm qhov chaw nkag (hauv peb rooj plaub, qhov no game), thiab [contenthash] yuav hloov nrog hash ntawm cov ntaub ntawv cov ntsiab lus. Peb ua nws rau optimize qhov project rau hashing - koj tuaj yeem qhia cov browsers kom cache peb cov pob JS tsis tas li, vim yog ib pob hloov, nws cov ntaub ntawv npe hloov ib yam nkaus (hloov contenthash). Qhov kawg tshwm sim yuav yog lub npe ntawm cov ntaub ntawv saib game.dbeee76e91a97d0c7207.js.

cov ntaub ntawv webpack.common.js yog lub hauv paus configuration ntaub ntawv uas peb import rau hauv txoj kev loj hlob thiab tiav qhov project configurations. Ntawm no yog ib qho piv txwv kev txhim kho configuration:

webpack.dev.js

const merge = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'development',
});

Rau kev ua tau zoo, peb siv nyob rau hauv txoj kev loj hlob webpack.dev.js, thiab hloov mus rau webpack.prod.jskom optimize pob qhov ntau thiab tsawg thaum deploying rau ntau lawm.

Kev teeb tsa hauv zos

Kuv pom zoo kom txhim kho qhov project ntawm lub tshuab hauv zos kom koj tuaj yeem ua raws li cov kauj ruam hauv qab no. Kev teeb tsa yooj yim: ua ntej, lub kaw lus yuav tsum tau teeb tsa Ntawm ΠΈ NPM. Tom ntej no koj yuav tsum ua

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

thiab koj npaj mus! Txhawm rau pib qhov kev txhim kho server, tsuas yog khiav

$ npm run develop

thiab mus rau qhov browser localhost: 3000. Cov neeg rau zaub mov kev txhim kho yuav rov tsim kho JS thiab CSS pob raws li cov cai hloov pauv - tsuas yog rov kho nplooj ntawv kom pom tag nrho cov kev hloov pauv!

3. Cov ntsiab lus nkag rau cov neeg siv khoom

Cia peb nqis mus rau qhov game code nws tus kheej. Ua ntej peb xav tau ib nplooj ntawv index.html, thaum mus saib qhov chaw, browser yuav thauj khoom ua ntej. Peb nplooj ntawv yuav yooj yim zoo nkauj:

index.html

Ib qho piv txwv .io game  Ua si

Qhov kev piv txwv no tau ua kom yooj yim me ntsis kom pom tseeb, thiab kuv yuav ua tib yam nrog rau ntau qhov piv txwv ntawm lwm tus ncej. Tag nrho cov lej tuaj yeem saib tau ntawm github.

Peb muaj:

  • HTML5 canvas element (<canvas>) uas peb yuav siv los ua qhov kev ua si.
  • <link> ntxiv peb CSS pob.
  • <script> ntxiv rau peb pob Javascript.
  • Main zaub mov nrog username <input> thiab lub PLAY khawm (<button>).

Tom qab thauj cov nplooj ntawv hauv tsev, qhov browser yuav pib ua Javascript code, pib los ntawm nkag mus rau JS cov ntaub ntawv: 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);
  };
});

Qhov no yuav zoo li nyuaj, tab sis tsis muaj dab tsi tshwm sim ntawm no:

  1. Importing ob peb lwm cov ntaub ntawv JS.
  2. CSS ntshuam (yog li Webpack paub suav nrog lawv hauv peb pob CSS).
  3. Pib connect() tsim kom muaj kev sib txuas nrog server thiab khiav downloadAssets() mus download tau cov duab xav tau los ua qhov kev ua si.
  4. Tom qab ua tiav theem 3 cov ntawv qhia zaub mov tseem ceeb tau tshwm sim (playMenu).
  5. Teem lub handler rau nias lub "PLAY" khawm. Thaum lub pob raug nias, tus lej pib pib qhov kev ua si thiab qhia rau tus neeg rau zaub mov tias peb npaj ua si.

Lub ntsiab "cov nqaij" ntawm peb cov neeg siv-server logic yog nyob rau hauv cov ntaub ntawv uas tau imported los ntawm cov ntaub ntawv index.js. Tam sim no peb yuav xav txog lawv txhua tus nyob rau hauv kev txiav txim.

4. Sib pauv cov ntaub ntawv cov neeg siv khoom

Hauv qhov kev ua si no, peb siv lub tsev qiv ntawv paub zoo los sib txuas lus nrog tus neeg rau zaub mov socket.io ib. Socket.io muaj kev txhawb nqa ib txwm muaj lub vev xaib, uas yog qhov zoo rau kev sib txuas lus ob txoj kev: peb tuaj yeem xa cov lus rau lub server ΠΈ tus neeg rau zaub mov tuaj yeem xa cov lus rau peb ntawm tib qho kev sib txuas.

Peb yuav muaj ib daim ntawv src/client/networking.jsleej twg yuav saib xyuas sawv daws kev sib txuas lus nrog server:

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

Txoj cai no kuj tau luv me ntsis kom meej.

Muaj peb yam tseem ceeb hauv cov ntaub ntawv no:

  • Peb tab tom sim txuas rau lub server. connectedPromise tsuas yog tso cai thaum peb tau tsim kev sib txuas.
  • Yog tias qhov kev sib txuas ua tiav, peb sau npe hu rov qab ua haujlwm (processGameUpdate() ΠΈ onGameOver()) rau cov lus peb tuaj yeem tau txais los ntawm lub server.
  • Peb export play() ΠΈ updateDirection()kom lwm cov ntaub ntawv siv tau.

5. Client Rendering

Nws yog lub sijhawm los tso saib daim duab ntawm qhov screen!

... tab sis ua ntej peb tuaj yeem ua tau, peb yuav tsum rub tawm tag nrho cov duab (peev txheej) uas yuav tsum tau ua rau qhov no. Wb sau ib tus neeg saib xyuas kev pab:

khoom.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];

Kev tswj cov peev txheej tsis yog qhov nyuaj rau kev siv! Lub tswv yim tseem ceeb yog khaws cov khoom assets, uas yuav khi tus yuam sij ntawm filename rau tus nqi ntawm cov khoom Image. Thaum cov peev txheej thauj khoom, peb khaws cia rau hauv ib qho khoom assets rau kev nkag ceev rau yav tom ntej. Thaum twg txhua tus neeg siv khoom raug tso cai rub tawm (uas yog, tag nrho cov peev txheej), peb tso cai downloadPromise.

Tom qab rub tawm cov peev txheej, koj tuaj yeem pib rendering. Raws li tau hais ua ntej, kos rau ntawm nplooj ntawv web, peb siv HTML5 Canvas (<canvas>). Peb qhov kev ua si yog qhov yooj yim zoo nkauj, yog li peb tsuas yog yuav tsum kos cov hauv qab no:

  1. Tom Qab
  2. Player nkoj
  3. Lwm cov players hauv qhov kev ua si
  4. Plhaub

Nov yog cov ntsiab lus tseem ceeb src/client/render.js, uas ua raws nraim plaub yam uas tau teev tseg saum toj no:

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

Cov cai no kuj tau luv luv rau qhov tseeb.

render() yog lub luag haujlwm tseem ceeb ntawm cov ntaub ntawv no. startRendering() ΠΈ stopRendering() tswj kev ua kom lub voj voog ntawm 60 FPS.

Kev siv pob zeb ntawm tus neeg ua haujlwm pab ua haujlwm (piv txwv li. renderBullet()) tsis yog qhov tseem ceeb, tab sis ntawm no yog ib qho piv txwv yooj yim:

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

Nco ntsoov tias peb tab tom siv txoj kev getAsset(), uas tau pom yav dhau los hauv asset.js!

Yog tias koj txaus siab kawm txog lwm cov kev pab cuam, ces nyeem cov seem. src/client/render.js.

6. Cov neeg siv khoom nkag

Nws yog lub sijhawm ua si ua si! Lub tswv yim tswj yuav yooj yim heev: hloov cov kev taw qhia ntawm kev txav, koj tuaj yeem siv tus nas (ntawm lub computer) lossis kov lub vijtsam (ntawm lub xov tooj ntawm tes). Txhawm rau ua qhov no, peb yuav sau npe Cov Kws Mloog Lus rau nas thiab Kov cov xwm txheej.
Yuav saib xyuas txhua yam no 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() yog Event Listeners uas hu updateDirection() (ntawm networking.js) thaum muaj kev tshwm sim tshwm sim (piv txwv li, thaum tus nas tsiv). updateDirection() tuav kev xa xov nrog tus neeg rau zaub mov, uas tswj hwm qhov kev tshwm sim tawm tswv yim thiab hloov kho lub xeev game raws li.

7. Cov neeg siv khoom zoo

Tshooj lus no yog qhov nyuaj tshaj plaws nyob rau hauv thawj ntu ntawm tus ncej. Tsis txhob poob siab yog tias koj tsis nkag siab nws thawj zaug koj nyeem nws! Koj tseem tuaj yeem hla nws thiab rov qab los rau nws tom qab.

Qhov kawg ntawm cov puzzle xav tau los ua kom tiav cov neeg siv khoom / server code yog lub xeev. Nco ntsoov cov cai snippet los ntawm Client Rendering section?

render.js

import { getCurrentState } from './state';

function render() {
  const { me, others, bullets } = getCurrentState();

  // Do the rendering
  // ...
}

getCurrentState() yuav tsum muaj peev xwm muab peb lub xeev tam sim no ntawm qhov kev ua si hauv tus neeg siv khoom ntawm txhua lub sijhawm raws li kev hloov tshiab tau txais los ntawm lub server. Nov yog ib qho piv txwv ntawm kev hloov tshiab game uas tus neeg rau zaub mov xa tuaj:

{
  "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
    }
  ]
}

Txhua qhov kev ua si hloov tshiab muaj tsib qhov chaw zoo ib yam:

  • t: Server timestamp qhia thaum twg qhov hloov tshiab no tau tsim.
  • me: Cov ntaub ntawv hais txog tus neeg ua si tau txais qhov hloov tshiab no.
  • lwm leej lwm tus: Ib qho array ntawm cov ntaub ntawv hais txog lwm tus players koom hauv tib qhov kev ua si.
  • bullets: ib qho array ntawm cov ntaub ntawv hais txog qhov projectiles hauv qhov kev ua si.
  • leaderboard: Cov ntaub ntawv thawj coj tam sim no. Hauv tsab ntawv no, peb yuav tsis xav txog lawv.

7.1 Naive tus neeg siv lub xeev

Kev siv tsis tau getCurrentState() tuaj yeem ncaj qha xa rov qab cov ntaub ntawv ntawm qhov nyuam qhuav tau txais kev ua si hloov tshiab.

naive-state.js

let lastGameUpdate = null;

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

export function getCurrentState() {
  return lastGameUpdate;
}

Zoo thiab meej! Tab sis yog tias tsuas yog nws yog qhov yooj yim. Ib qho laj thawj ntawm qhov kev siv no yog teeb meem: nws txwv tus rendering ncej tus nqi rau lub server moos tus nqi.

Ncej tus nqi: tus naj npawb ntawm cov ncej (piv txwv li hu render()) ib ob, los yog FPS. Kev ua si feem ntau siv zog kom ua tiav yam tsawg kawg 60 FPS.

Tick ​​Rate: Qhov zaus ntawm lub server xa cov kev ua si hloov tshiab rau cov neeg siv khoom. Nws feem ntau qis dua tus ncej tus nqi. Hauv peb qhov kev ua si, tus neeg rau zaub mov khiav ntawm qhov zaus ntawm 30 cycles ib ob.

Yog tias peb tsuas yog ua qhov hloov tshiab tshiab ntawm qhov kev ua si, ces FPS yuav tsum tsis txhob mus dhau 30, vim peb yeej tsis tau txais ntau tshaj 30 qhov hloov tshiab ib ob los ntawm lub server. Txawm peb hu render() 60 zaug ib ob, tom qab ntawd ib nrab ntawm cov kev hu no tsuas yog rov ua dua ib yam, qhov tseem ceeb tsis ua dab tsi. Lwm qhov teeb meem nrog kev ua tsis ncaj ncees yog tias nws nquag mus qeeb. Nrog rau qhov zoo tshaj plaws Internet ceev, tus neeg siv yuav tau txais kev ua si hloov tshiab raws nraim txhua 33ms (30 ib ob):

Tsim Multiplayer .io Web Game
Hmoov tsis, tsis muaj dab tsi zoo tag nrho. Ib daim duab tiag tiag yuav yog:
Tsim Multiplayer .io Web Game
Kev siv dag zog yuam kev yog qhov phem tshaj plaws thaum nws los txog rau latency. Yog tias qhov kev ua si hloov tshiab tau txais nrog ncua ntawm 50ms, ces cov khw muag khoom ib qho ntxiv 50ms vim nws tseem ua rau lub xeev game los ntawm kev hloov tshiab dhau los. Koj tuaj yeem xav txog yuav ua li cas tsis xis nyob qhov no rau tus neeg ua si: arbitrary braking yuav ua rau qhov kev ua si zoo li jerky thiab tsis ruaj khov.

7.2 Txhim kho cov neeg siv lub xeev

Peb yuav ua qee qhov kev txhim kho rau qhov kev siv tsis zoo. Ua ntej, peb siv rendering ncua rau 100 ms. Qhov no txhais tau hais tias lub xeev "tam sim no" ntawm tus neeg siv yuav ib txwm lag tom qab lub xeev ntawm kev ua si ntawm lub server los ntawm 100ms. Piv txwv li, yog tias lub sijhawm ntawm lub server yog 150, ces tus neeg siv yuav ua lub xeev uas lub server nyob rau lub sijhawm 50:

Tsim Multiplayer .io Web Game
Qhov no muab 100ms tsis rau peb kom muaj sia nyob tsis muaj kev twv ua si hloov tshiab lub sijhawm:

Tsim Multiplayer .io Web Game
Kev them nyiaj rau qhov no yuav nyob ruaj khov input lag rau 100 ms. Qhov no yog kev txi me me rau kev ua si zoo - feem ntau cov neeg ua si (tshwj xeeb yog cov neeg ua si xws li) yuav tsis pom qhov ncua sij hawm no. Nws yog qhov yooj yim dua rau tib neeg los hloov kho mus tas li 100ms latency dua li nws yog ua si nrog qhov tsis paub tseeb latency.

Peb kuj tuaj yeem siv lwm cov txheej txheem hu ua kev twv ua ntej ntawm tus neeg siv khoom, uas ua haujlwm zoo ntawm kev txo qis kev pom latency, tab sis yuav tsis raug them rau hauv cov ntawv no.

Lwm qhov kev txhim kho peb siv yog linear interpolation. Raws li kev lag luam lag luam, peb feem ntau yog tsawg kawg ib qho hloov tshiab ua ntej lub sijhawm tam sim no hauv tus neeg siv khoom. Thaum hu getCurrentState(), peb tuaj yeem ua tau linear interpolation nruab nrab ntawm kev ua si hloov tshiab ua ntej thiab tom qab lub sijhawm tam sim no hauv tus neeg siv khoom:

Tsim Multiplayer .io Web Game
Qhov no daws qhov teeb meem tus ncej tus nqi: tam sim no peb tuaj yeem ua cov cim tshwj xeeb ntawm tus ncej tus nqi peb xav tau!

7.3 Txhim kho cov neeg siv khoom lub xeev

Kev ua piv txwv hauv src/client/state.js siv ob qho tib si render lag thiab linear interpolation, tab sis tsis ntev. Wb rhuav txoj cai ua ob ntu. Nov yog thawj tus:

xeev.js part 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;
}

Thawj kauj ruam yog los xyuas seb dab tsi currentServerTime(). Raws li peb tau pom ua ntej, txhua qhov kev ua si hloov tshiab suav nrog lub sijhawm teev sijhawm server. Peb xav siv render latency los ua cov duab 100ms qab lub server, tab sis peb yuav tsis paub lub sijhawm tam sim no ntawm lub server, vim peb tsis tuaj yeem paub ntev npaum li cas nws siv sijhawm rau ib qho kev hloov tshiab kom tau txais rau peb. Hauv Internet yog unpredictable thiab nws ceev yuav txawv heev!

Yuav kom daws tau qhov teeb meem no, peb tuaj yeem siv qhov kwv yees tsim nyog: peb ua piv txwv thawj qhov hloov tshiab tuaj txog tam sim. Yog tias qhov no muaj tseeb, ces peb yuav paub lub sijhawm server ntawm lub sijhawm tshwj xeeb no! Peb khaws cov neeg rau zaub mov lub sij hawm nyob rau hauv firstServerTimestamp thiab khaws peb hauv zos (tus neeg siv) timestamp tib lub sijhawm hauv gameStart.

Oh tos. Yuav tsum tsis yog lub sijhawm server = tus neeg siv sijhawm? Vim li cas peb thiaj paub qhov txawv ntawm "server timestamp" thiab "client timestamp"? Qhov no yog ib lo lus nug zoo! Nws hloov tawm tias lawv tsis yog tib yam. Date.now() yuav xa cov sijhawm sib txawv hauv cov neeg siv khoom thiab cov neeg rau zaub mov, thiab nws nyob ntawm cov xwm txheej hauv zos rau cov tshuab no. Tsis txhob xav tias cov ntawv teev sijhawm yuav zoo ib yam ntawm txhua lub tshuab.

Tam sim no peb to taub dab tsi ua currentServerTime(): nws rov server timestamp ntawm lub sijhawm ua tam sim no. Hauv lwm lo lus, qhov no yog server lub sijhawm tam sim no (firstServerTimestamp <+ (Date.now() - gameStart)) rho tawm render ncua (RENDER_DELAY).

Tam sim no cia peb saib yuav ua li cas peb lis kev ua si hloov tshiab. Thaum tau txais los ntawm qhov hloov tshiab server, nws raug hu processGameUpdate()thiab peb khaws qhov hloov tshiab tshiab rau ib qho array gameUpdates. Tom qab ntawd, txhawm rau txheeb xyuas kev siv lub cim xeeb, peb tshem tawm tag nrho cov kev hloov tshiab ua ntej puag hloov tshiabvim peb tsis xav tau lawv ntxiv lawm.

Dab tsi yog "qhov hloov tshiab yooj yim"? Qhov no thawj qhov hloov tshiab peb pom los ntawm kev txav rov qab los ntawm lub server lub sijhawm tam sim no. Nco daim duab no?

Tsim Multiplayer .io Web Game
Qhov kev ua si hloov tshiab ncaj qha mus rau sab laug ntawm "Client Render Time" yog lub hauv paus hloov tshiab.

Lub hauv paus hloov tshiab siv rau dab tsi? Vim li cas peb tuaj yeem tso cov kev hloov tshiab rau hauv paus? Yuav kom paub qhov no, cia peb kawg li xav txog kev siv getCurrentState():

xeev.js part 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),
    };
  }
}

Peb daws peb qhov teeb meem:

  1. base < 0 txhais tau hais tias tsis muaj qhov hloov tshiab kom txog rau thaum lub sijhawm ua haujlwm tam sim no (saib qhov kev siv saum toj no getBaseUpdate()). Qhov no tuaj yeem tshwm sim thaum pib ntawm qhov kev ua si vim muaj kev ua lag luam. Hauv qhov no, peb siv qhov hloov tshiab tshiab tau txais.
  2. base yog qhov hloov tshiab tshiab peb muaj. Qhov no tej zaum yuav yog vim lub network qeeb lossis kev sib txuas hauv Internet tsis zoo. Hauv qhov no, peb kuj tseem siv qhov hloov tshiab tshiab uas peb muaj.
  3. Peb muaj qhov hloov tshiab ob qho tib si ua ntej thiab tom qab lub sijhawm ua haujlwm tam sim no, yog li peb tuaj yeem ua tau interpolate!

Txhua yam uas nyob hauv state.js yog ib qho kev siv ntawm linear interpolation uas yooj yim (tab sis tho txawv) lej. Yog tias koj xav tshawb xyuas koj tus kheej, ces qhib state.js rau github.

Ntu 2. Backend server

Hauv ntu no, peb yuav saib ntawm Node.js backend uas tswj peb .io game piv txwv.

1. Server nkag Point

Txhawm rau tswj hwm lub vev xaib server, peb yuav siv lub vev xaib nrov rau Node.js hu ua qhia. Nws yuav raug teeb tsa los ntawm peb cov ntaub ntawv nkag mus rau tus neeg rau zaub mov src/server/server.js:

server.js part 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}`);

Nco ntsoov tias thawj ntu peb tham txog Webpack? Qhov no yog qhov uas peb yuav siv peb Webpack configurations. Peb yuav siv lawv nyob rau hauv ob txoj kev:

  • Siv webpack-dev-middleware kom rov tsim kho peb cov pob khoom txhim kho, lossis
  • statically hloov folders dist/, uas Webpack yuav sau peb cov ntaub ntawv tom qab tsim cov khoom tsim.

Lwm txoj haujlwm tseem ceeb server.js yog los teeb tsa lub server socket.io ibuas tsuas yog txuas mus rau Express server:

server.js part 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);
});

Tom qab ua tiav kev tsim kom muaj kev sib txuas ntawm socket.io rau lub server, peb teeb tsa cov neeg ua haujlwm rau lub qhov (socket) tshiab. Cov neeg tuav haujlwm tuav cov lus tau txais los ntawm cov neeg siv khoom los ntawm kev xa mus rau ib qho khoom siv singleton game:

server.js part 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);
}

Peb tab tom tsim ib qho kev ua si .io, yog li peb tsuas yog xav tau ib daim qauv xwb Game ("Kev ua si") - txhua tus players ua si hauv tib lub chaw sib tw! Hauv seem tom ntej, peb yuav pom tias chav kawm no ua haujlwm li cas. Game.

2. Game servers

Класс Game muaj cov logic tseem ceeb tshaj plaws nyob rau sab server. Nws muaj ob txoj haujlwm tseem ceeb: kev tswj cov neeg ua si и kev ua si simulation.

Cia peb pib nrog thawj txoj haujlwm, kev tswj hwm tus neeg ua si.

game.js, part 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);
    }
  }

  // ...
}

Hauv qhov kev ua si no, peb yuav txheeb xyuas cov players los ntawm thaj chaw id lawv socket.io socket (yog tias koj tsis meej pem, ces rov qab mus rau server.js). Socket.io nws tus kheej muab txhua lub qhov (socket) ib qho tshwj xeeb idyog li peb tsis tas yuav txhawj txog qhov ntawd. Kuv yuav hu nws Player ID.

Nrog rau qhov ntawd hauv siab, cia peb tshawb cov piv txwv hloov pauv hauv chav kawm Game:

  • sockets yog ib yam khoom uas khi tus neeg ua si ID rau lub qhov (socket) uas cuam tshuam nrog tus neeg ua si. Nws tso cai rau peb nkag mus rau lub qhov (sockets) los ntawm lawv cov neeg ua si IDs nyob rau lub sijhawm tas li.
  • players yog ib yam khoom uas khi tus neeg uas ua ntawv ID rau cov code>Player khoom

bullets yog ib qho array ntawm cov khoom Bullet, uas tsis muaj kev txiav txim meej.
lastUpdateTime yog lub timestamp ntawm lub sijhawm kawg qhov kev ua si tau hloov kho. Peb mam li saib seb nws siv sai npaum li cas.
shouldSendUpdate yog ib qho auxiliary variable. Peb kuj yuav pom nws siv sai sai.
Txoj kev addPlayer(), removePlayer() ΠΈ handleInput() tsis tas yuav piav, lawv siv hauv server.js. Yog tias koj xav tau rov qab koj lub cim xeeb, rov qab mus dua me ntsis.

Kab kawg constructor() pib li hloov tshiab lub voj voog kev ua si (nrog rau qhov zaus ntawm 60 hloov tshiab / s):

game.js, part 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;
    }
  }

  // ...
}

Txujci update() muaj tej zaum qhov tseem ceeb tshaj plaws ntawm server-sab logic. Nov yog qhov nws ua, hauv kev txiav txim:

  1. Muab xam ntev npaum li cas dt dhau txij thaum kawg update().
  2. Refreshes txhua qhov projectile thiab rhuav tshem lawv yog tias tsim nyog. Peb yuav pom qhov kev siv ntawm no functionality tom qab. Txog tam sim no, nws txaus rau peb paub qhov ntawd bullet.update() rov qab trueyog tias qhov projectile yuav tsum raug rhuav tshem (nws mus sab nraum lub arena).
  3. Hloov kho txhua tus neeg ua si thiab nthuav tawm qhov projectile yog tias tsim nyog. Peb tseem yuav pom qhov kev siv no tom qab - player.update() tuaj yeem rov qab tau ib yam khoom Bullet.
  4. Tshawb xyuas kev sib tsoo ntawm qhov projectiles thiab cov players nrog applyCollisions(), uas xa rov qab ib array ntawm projectiles ntaus players. Rau txhua qhov projectile rov qab los, peb nce cov ntsiab lus ntawm cov neeg uas ua ntawv uas raug rho tawm haujlwm (siv player.onDealtDamage()) thiab tom qab ntawd tshem tawm qhov projectile los ntawm array bullets.
  5. Ceeb toom thiab rhuav tshem tag nrho cov neeg tua tuag.
  6. Xa ib qho kev ua si hloov tshiab rau txhua tus players txhua thib ob lub sij hawm thaum hu update(). Qhov no pab peb taug qab cov pab pawg sib txawv uas tau hais los saum toj no. shouldSendUpdate. Raws li update() hu 60 zaug/s, peb xa cov kev ua si hloov tshiab 30 zaug/s. Yog li, moos zaus server moos yog 30 moos / s (peb tau tham txog cov nqi moos hauv thawj ntu).

Vim li cas thiaj xa cov kev ua si hloov tshiab nkaus xwb dhau lub sijhawm ? Txuag channel. 30 kev ua si hloov tshiab ib ob yog ntau heev!

Ua cas ho tsis hu update() 30 zaug ib ob? Txhawm rau txhim kho qhov kev ua si simulation. Feem ntau hu ua update(), qhov tseeb ntau qhov kev ua si simulation yuav yog. Tab sis tsis txhob dhau mus dhau nrog cov kev cov nyom. update(), vim qhov no yog ib txoj hauj lwm kim kim - 60 ib ob yog txaus.

Tus so ntawm chav kawm Game muaj cov txheej txheem pab siv hauv update():

game.js, part 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() yooj yim zoo nkauj - nws txheeb cov players los ntawm cov qhab nia, siv cov tsib saum toj kawg nkaus, thiab rov qab cov npe siv thiab cov qhab nia rau txhua tus.

createUpdate() siv hauv update() los tsim cov kev ua si hloov tshiab uas muab faib rau cov players. Nws lub luag haujlwm tseem ceeb yog hu rau txoj kev serializeForUpdate()siv rau cov chav kawm Player ΠΈ Bullet. Nco ntsoov tias nws tsuas yog kis cov ntaub ntawv rau txhua tus neeg ua si txog nyob ze players thiab projectiles - tsis tas yuav xa cov ntaub ntawv hais txog cov khoom ua si uas nyob deb ntawm cov neeg uas ua ntawv!

3. Cov khoom ua si ntawm lub server

Hauv peb qhov kev ua si, qhov projectiles thiab players yeej zoo sib xws: lawv yog abstract, round, movable game khoom. Txhawm rau ua kom zoo dua qhov zoo sib xws ntawm cov neeg ua si thiab cov phiaj xwm, cia peb pib los ntawm kev siv lub hauv paus chav kawm Object:

ob.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,
    };
  }
}

Tsis muaj ib yam dab tsi nyuaj mus ntawm no. Cov chav kawm no yuav yog lub ntsiab lus zoo rau kev txuas ntxiv. Cia peb saib yuav ua li cas hauv chav kawm Bullet siv 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;
  }
}

Kev siv Bullet luv heev! Peb tau ntxiv rau Object tsuas yog cov nram qab no extensions:

  • Siv pob luv rau tiam random id projectile.
  • Ntxiv ib daim teb parentIDyog li koj tuaj yeem taug qab tus neeg ua si uas tsim qhov projectile.
  • Ntxiv tus nqi rov qab rau update(), uas yog sib npaug trueyog hais tias lub projectile nyob sab nraum lub arena (nco ntsoov peb tham txog qhov no nyob rau hauv lub xeem seem?).

Wb mus rau 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,
    };
  }
}

Cov neeg ua si yog qhov nyuaj dua li qhov projectiles, yog li ob peb lub teb ntxiv yuav tsum tau khaws cia hauv chav kawm no. Nws txoj kev update() ua haujlwm ntau heev, tshwj xeeb, rov qab qhov projectile tsim tshiab yog tias tsis muaj qhov seem fireCooldown (Nco ntsoov peb tau tham txog qhov no hauv ntu dhau los?). Tsis tas li ntawd nws ncua txoj kev serializeForUpdate(), vim tias peb yuav tsum suav nrog cov teb ntxiv rau cov neeg ua si hauv qhov hloov tshiab game.

Muaj chav kawm hauv paus Object - ib qho tseem ceeb kauj ruam kom tsis txhob rov qab code. Piv txwv li, tsis muaj chav kawm Object txhua yam khoom ua si yuav tsum muaj kev siv tib yam distanceTo(), thiab luam tawm tag nrho cov kev siv no hla ntau cov ntaub ntawv yuav yog npau suav phem. Qhov no dhau los ua qhov tseem ceeb tshwj xeeb rau cov haujlwm loj.thaum tus naj npawb ntawm nthuav Object cov chav kawm loj tuaj.

4. Kev kuaj pom kev sib tsoo

Qhov tsuas yog qhov uas tshuav rau peb yog kom paub txog thaum lub projectiles ntaus cov players! Nco ntsoov daim code no los ntawm txoj kev update() hauv chav kawm 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),
    );

    // ...
  }
}

Peb yuav tsum siv txoj kev applyCollisions(), uas rov qab tag nrho cov projectiles uas ntaus players. Hmoov zoo, nws tsis yog qhov nyuaj ua vim

  • Tag nrho cov khoom sib tsoo yog lub voj voog, uas yog cov duab yooj yim tshaj plaws los ua kom pom kev sib tsoo.
  • Peb twb muaj ib txoj kev distanceTo(), uas peb tau siv nyob rau hauv nqe lus dhau los hauv chav kawm Object.

Nov yog peb qhov kev siv ntawm kev sib tsoo kom pom zoo li:

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

Qhov kev sib tsoo yooj yim no nrhiav tau los ntawm qhov tseeb tias ob lub voj voog sib tsoo yog tias qhov kev ncua deb ntawm lawv qhov chaw tsawg dua qhov sib npaug ntawm lawv cov radii. Nov yog rooj plaub uas qhov kev ncua deb ntawm qhov chaw ntawm ob lub voj voog yog qhov sib npaug ntawm qhov sib npaug ntawm lawv cov radii:

Tsim Multiplayer .io Web Game
Muaj ob peb yam ntxiv los xav txog ntawm no:

  • Lub projectile yuav tsum tsis txhob ntaus tus neeg uas ua ntawv uas tsim nws. Qhov no tuaj yeem ua tiav los ntawm kev sib piv bullet.parentID с player.id.
  • Lub projectile yuav tsum tsuas yog ntaus ib zaug hauv qhov txwv ntawm ntau tus players sib tsoo tib lub sijhawm. Peb yuav daws qhov teeb meem no siv tus neeg teb xov tooj break: sai li sai tau thaum tus neeg ua si sib tsoo nrog qhov projectile pom, peb nres qhov kev tshawb nrhiav thiab txav mus rau qhov projectile tom ntej.

Qhov kawg

Yog tag nrho! Peb tau npog txhua yam koj xav paub los tsim .io web game. Tom ntej no yog dab tsi? Tsim koj tus kheej .io game!

Tag nrho cov qauv code yog qhib qhov chaw thiab muab tso rau github.

Tau qhov twg los: www.hab.com

Ntxiv ib saib