Ke hana ʻana i kahi pāʻani Pūnaewele .io Multiplayer

Ke hana ʻana i kahi pāʻani Pūnaewele .io Multiplayer
Hoʻokuʻu ʻia i ka makahiki 2015 Agar.io lilo i kupuna no kekahi ano hou pāʻani .ioi ulu a kaulana mai ia manawa. Ua ʻike pilikino wau i ka piʻi ʻana o ka kaulana o nā pāʻani .io: i nā makahiki ʻekolu i hala iho nei, ua loaʻa iaʻu hana a kūʻai aku i ʻelua pāʻani o kēia ʻano..

Inā ʻaʻole ʻoe i lohe mua i kēia mau pāʻani, he mau pāʻani pūnaewele Multiplayer manuahi kēia i maʻalahi ke pāʻani (ʻaʻole koi ʻia kahi moʻokāki). Kūʻē lākou i nā mea pāʻani kūʻē he nui ma ke kahua hoʻokahi. Nā pāʻani .io kaulana ʻē aʻe: ʻO Slither.io и Diep.io.

Ma kēia pou, e ʻimi mākou pehea hana i .io pāʻani mai ka wā kahiko. No kēia mea, lawa ka ʻike o Javascript: pono ʻoe e hoʻomaopopo i nā mea like syntax ES6, huaʻōlelo this и olelo pomaikai. ʻOiai ʻaʻole ʻoe ʻike pono iā Javascript, hiki iā ʻoe ke hoʻomaopopo i ka hapa nui o ka pou.

laʻana pāʻani .io

No ke kōkua aʻo ʻana, e nānā mākou laʻana pāʻani .io. E ho'āʻo e pāʻani!

Ke hana ʻana i kahi pāʻani Pūnaewele .io Multiplayer
He mea maʻalahi ka pāʻani: ke hoʻomalu nei ʻoe i kahi moku ma kahi kahua kahi e loaʻa ai nā mea pāʻani ʻē aʻe. Hoʻopau koke kāu moku i nā projectiles a hoʻāʻo ʻoe e pā i nā mea pāʻani ʻē aʻe me ka pale ʻana i kā lākou projectiles.

1. Hōʻike pōkole pōkole o ka papahana

Manaʻo wau kiʻi kumu kumu laʻana pāʻani i hiki iā ʻoe ke hahai mai iaʻu.

Hoʻohana ka laʻana i kēia:

  • ka hoike ʻo ia ka mea kaulana loa ʻo Node.js pūnaewele puni honua e hoʻokele nei i ka kikowaena pūnaewele o ka pāʻani.
  • kumu.io - he waihona waihona no ka hoʻololi ʻana i ka ʻikepili ma waena o kahi polokalamu kele pūnaewele a me kahi kikowaena.
  • Pūnaewele Pūnaewele - manakia module. Hiki iā ʻoe ke heluhelu e pili ana i ke kumu e hoʻohana ai i ka Webpack. maanei.

Penei ke ano o ka papa kuhikuhi papahana:

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

lehulehu/

ʻO nā mea a pau i loko o kahi waihona public/ e waiho ʻia e ke kikowaena. IN public/assets/ aia nā kiʻi i hoʻohana ʻia e kā mākou papahana.

src /

Aia nā code kumu a pau i loko o ka waihona src/. Nā poʻo inoa client/ и server/ olelo no lakou iho a shared/ Loaʻa i kahi faila mau i lawe ʻia e ka mea kūʻai aku a me ke kikowaena.

2. Nā hoʻonohonoho hui/papahana

E like me ka mea i ʻōlelo ʻia ma luna, hoʻohana mākou i ka mana module e kūkulu i ka papahana. Pūnaewele Pūnaewele. E nānā i kā mākou hoʻonohonoho 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',
    }),
  ],
};

ʻO nā laina nui loa ma aneʻi:

  • src/client/index.js ʻo ia ka helu komo o ka mea kūʻai Javascript (JS). E hoʻomaka ana ʻo Webpack mai ʻaneʻi a huli hou i nā faila i lawe ʻia mai.
  • E loaʻa i ka papa kuhikuhi ka puka JS o kā mākou Webpack kūkulu dist/. E kapa wau i kēia faila i kā mākou js pūʻolo.
  • Hoʻohana mākou 'o Babel, a me ka hoʻonohonoho pono @babel/preset-env e unuhi i kā mākou JS code no nā polokalamu kele kahiko.
  • Ke hoʻohana nei mākou i kahi plugin e unuhi i nā CSS āpau i kuhikuhi ʻia e nā faila JS a hoʻohui iā lākou i kahi hoʻokahi. E kapa aku au iā mākou pūʻolo css.

Ua ʻike paha ʻoe i nā inoa faila ʻē aʻe '[name].[contenthash].ext'. Loaʻa iā lākou hoʻololi inoa faila Pūnaewele: [name] e hoʻololi ʻia me ka inoa o ka helu helu (i kā mākou hihia, kēia game), aii [contenthash] e hoʻololi ʻia me ka hash o ka waihona. Hana mākou ia hoʻonui i ka papahana no ka hashing - hiki iā ʻoe ke haʻi aku i nā mākaʻikaʻi e hūnā i kā mākou pūʻolo JS no ka pau ʻole, no ka mea inā hoʻololi kekahi pūʻolo, a laila hoʻololi pū kona inoa faila (hoʻololi contenthash). ʻO ka hopena hope ka inoa o ka faila ʻike game.dbeee76e91a97d0c7207.js.

waihona webpack.common.js - ʻO kēia ka waihona hoʻonohonoho kumu a mākou e hoʻokomo ai i ka hoʻomohala a hoʻopau i nā hoʻonohonoho papahana. Eia kekahi laʻana hoʻonohonoho hoʻomohala:

webpack.dev.js

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

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

No ka pono, hoʻohana mākou i ka hana hoʻomohala webpack.dev.js, a hoʻololi i webpack.prod.jse hoʻonui i ka nui o ka pūʻolo i ka wā e kau ai i ka hana.

Hoʻonohonoho kūloko

Manaʻo wau e kau i ka papahana ma kāu mīkini kūloko i hiki iā ʻoe ke hahai i nā ʻanuʻu i helu ʻia ma kēia pou. He maʻalahi ka hoʻonohonoho: ʻo ka mua, pono ka ʻōnaehana wahi и NPM. A laila pono ʻoe e hana

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

a ua mākaukau ʻoe e hele! No ka hoʻomaka ʻana i ke kikowaena hoʻomohala, holo wale

$ npm run develop

a hele i ka polokalamu kele pūnaewele localhost: 3000. E kūkulu hou ke kikowaena hoʻomohala i nā pūʻolo JS a me CSS i ka wā e loli ai nā code - e hōʻoluʻolu wale i ka ʻaoʻao e ʻike i nā loli āpau!

3. Nā Manaʻo Hoʻokomo Mea Kūʻai

E iho kākou i ke code game pono'ī. Pono mua mākou i kahi ʻaoʻao index.html, i ka wā e kipa aku ai i ka pūnaewele, e hoʻouka mua ka polokalamu kele pūnaewele. E maʻalahi loa kā mākou ʻaoʻao:

index.html

He laʻana .io pāʻani  PALI

Ua maʻalahi iki kēia hiʻohiʻona code no ka akaka, a e hana like wau me ka nui o nā hiʻohiʻona post. Hiki ke nānā 'ia ke code piha ma Github.

Aia iā mākou:

  • ʻElemu canvas HTML5 (<canvas>) a mākou e hoʻohana ai e hoʻolilo i ka pāʻani.
  • <link> e hoʻohui i kā mākou pūʻolo CSS.
  • <script> e hoʻohui i kā mākou pūʻolo Javascript.
  • Papa kuhikuhi nui me ka mea hoʻohana <input> a me ke pihi PLAY (<button>).

Ma hope o ka hoʻouka ʻana i ka ʻaoʻao home, e hoʻomaka ka polokalamu kele e hoʻokō i ka code Javascript, e hoʻomaka ana mai ka waihona JS helu komo: 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);
  };
});

He paʻakikī paha kēia, akā ʻaʻole nui ka hana ma aneʻi:

  1. Ke lawe mai nei i kekahi mau faila JS ʻē aʻe.
  2. CSS import (no laila ʻike ʻo Webpack e hoʻokomo iā lākou i kā mākou pūʻolo CSS).
  3. Hoʻollo connect() e hoʻokumu i kahi pilina me ke kikowaena a holo downloadAssets() e kiʻi i nā kiʻi pono e hoʻolilo i ka pāʻani.
  4. Ma hope o ka pau ʻana o ka pae 3 hōʻike ʻia ka papa kuhikuhi nui (playMenu).
  5. Hoʻonohonoho i ka mea hoʻohana no ke kaomi ʻana i ke pihi "PLAY". Ke paʻi ʻia ke pihi, hoʻomaka ke code i ka pāʻani a haʻi i ke kikowaena ua mākaukau mākou e pāʻani.

ʻO ka "ʻiʻo" nui o kā mākou mea kūʻai aku-server logic aia i kēlā mau faila i lawe ʻia e ka faila index.js. I kēia manawa e noʻonoʻo mākou iā lākou a pau i ka hoʻonohonoho.

4. Hoʻololi i ka ʻikepili o ka mea kūʻai aku

Ma kēia pāʻani, hoʻohana mākou i kahi hale waihona puke kaulana e kamaʻilio me ke kikowaena kumu.io. Loaʻa iā Socket.io ke kākoʻo maoli Pūnaewele Pūnaewele, i kūpono no ke kamaʻilio ʻelua ala: hiki iā mākou ke hoʻouna i nā leka i ke kikowaena и hiki i ke kikowaena ke hoʻouna i nā leka iā mākou ma ka pilina like.

E loaʻa iā mākou hoʻokahi faila src/client/networking.jsnana e malama nā mea a pau kamaʻilio me ke kikowaena:

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

Ua pōkole iki kēia code no ka maopopo.

ʻEkolu mau hana nui ma kēia faila:

  • Ke ho'āʻo nei mākou e hoʻohui i ke kikowaena. connectedPromise ʻae wale ʻia ke hoʻokumu mākou i kahi pilina.
  • Inā kūleʻa ka pilina, hoʻopaʻa inoa mākou i nā hana callback (processGameUpdate() и onGameOver()) no nā memo hiki iā mākou ke loaʻa mai ke kikowaena.
  • Lawe mākou i waho play() и updateDirection()i hiki i nā faila ʻē aʻe ke hoʻohana iā lākou.

5. Hoʻolilo Mea Kūʻai

ʻO ka manawa kēia e hōʻike i ke kiʻi ma ka pale!

...akā ma mua o ka hiki iā mākou ke hana i kēia, pono mākou e hoʻoiho i nā kiʻi āpau (nā kumuwaiwai) pono no kēia. E kākau kāua i mea hoʻokele waiwai:

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

ʻAʻole paʻakikī loa ka hoʻokō ʻana i ka hoʻokele waiwai! ʻO ka manaʻo nui ka mālama ʻana i kahi mea assets, e hoʻopaʻa i ke kī o ka filename i ka waiwai o ka mea Image. Ke hoʻouka ʻia ka punawai, mālama mākou i loko o kahi mea assets no ka hiki wawe i keia mua aku. I ka manawa hea e ʻae ʻia ai kēlā me kēia kumuwaiwai e hoʻoiho (ʻo ia hoʻi, nā mea a pau nā kumuwaiwai), ʻae mākou downloadPromise.

Ma hope o ka hoʻoiho ʻana i nā kumuwaiwai, hiki iā ʻoe ke hoʻomaka i ka hana. E like me ka mea i ʻōlelo ʻia ma mua, e huki i kahi ʻaoʻao pūnaewele, hoʻohana mākou HTML5 Canvas (<canvas>). He maʻalahi kā mākou pāʻani, no laila pono mākou e huki i kēia mau mea:

  1. Ke kumu
  2. Moku mea pāʻani
  3. ʻO nā mea pāʻani ʻē aʻe i ka pāʻani
  4. Nā nui

Eia nā ʻāpana koʻikoʻi src/client/render.js, ka mea e hāʻawi pololei i nā mea ʻehā i helu ʻia ma luna nei:

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

Hoʻopōkole ʻia kēia code no ka maopopo.

render() ʻO ia ka hana nui o kēia faila. startRendering() и stopRendering() e hoʻomalu i ka hoʻāla ʻana o ka loop render ma 60 FPS.

Nā hoʻokō paʻa o nā hana kōkua hoʻohālikelike hoʻokahi (e.g. renderBullet()) ʻaʻole ia he mea nui, akā eia kekahi laʻana maʻalahi:

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

E hoʻomaopopo ke hoʻohana nei mākou i ke ʻano getAsset(), i ike mua ia ma asset.js!

Inā makemake ʻoe e aʻo e pili ana i nā mea kōkua rendering ʻē aʻe, a laila e heluhelu i ke koena. src/client/render.js.

6. Hoʻokomo mea kūʻai

ʻO ka manawa kēia e hana ai i kahi pāʻani hiki ke pāʻani! E maʻalahi loa ka hoʻolālā mana: e hoʻololi i ke ala o ka neʻe ʻana, hiki iā ʻoe ke hoʻohana i ka ʻiole (ma ke kamepiula) a i ʻole e pā i ka pale (ma kahi polokalamu kelepona). No ka hoʻokō ʻana i kēia, e kākau inoa mākou Nā Hoʻolohe Hanana no nā hanana ʻiole a pā.
E mālama i kēia mau mea a pau src/client/input.js:

komo.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() ʻO nā mea hoʻolohe hanana e kāhea ana updateDirection() (mai networking.js) ke loaʻa kahi hanana hoʻokomo (no ka laʻana, ke neʻe ʻia ka ʻiole). updateDirection() mālama i ka leka uila me ke kikowaena, nāna e mālama i ka hanana hoʻokomo a hōʻano hou i ke kūlana pāʻani e like me ia.

7. Kūlana Kūlana

ʻO kēia māhele ka mea paʻakikī loa ma ka hapa mua o ka pou. Mai hoʻonāwaliwali inā ʻaʻole ʻoe maopopo i ka manawa mua āu i heluhelu ai! Hiki iā ʻoe ke lele a hoʻi hou i hope.

ʻO ka ʻāpana hope o ka puzzle e pono ai e hoʻopiha i ka code client/server moku'āina. E hoʻomanaʻo i ka snippet code mai ka ʻāpana Client Rendering?

render.js

import { getCurrentState } from './state';

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

  // Do the rendering
  // ...
}

getCurrentState() hiki iā ia ke hāʻawi iā mākou i ke kūlana o kēia manawa o ka pāʻani i ka mea kūʻai aku i kēlā me kēia manawa ma muli o nā mea hou i loaʻa mai ke kikowaena. Eia kekahi laʻana o ka hōʻano hou pāʻani i hiki i ke kikowaena ke hoʻouna:

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

Loaʻa i kēlā me kēia pāʻani he ʻelima mau kahua like:

  • t: Hōʻailona manawa kikowaena e hōʻike ana i ka wā i hana ʻia ai kēia hōʻano hou.
  • me: ʻIke e pili ana i ka mea pāʻani e loaʻa ana i kēia hōʻano hou.
  • kekahi: He pūʻulu ʻike e pili ana i nā mea pāʻani ʻē aʻe e komo ana i ka pāʻani hoʻokahi.
  • nā hua'ōlelo: he mau ʻike e pili ana i nā projectiles i ka pāʻani.
  • alakaʻi: ʻIkepili papa alakaʻi o kēia manawa. Ma kēia pou, ʻaʻole mākou e noʻonoʻo iā lākou.

7.1 Kūlana naive mea kūʻai aku

Hoʻokō naive getCurrentState() hiki ke hoʻihoʻi pololei i ka ʻikepili o ka mea hou loa i loaʻa i ka pāʻani.

naive-state.js

let lastGameUpdate = null;

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

export function getCurrentState() {
  return lastGameUpdate;
}

Maikaʻi a maopopo! Akā inā he mea maʻalahi ia. ʻO kekahi o nā kumu he pilikia kēia hoʻokō: e kaupalena ana i ka helu o ka hoʻolilo ʻana i ka uaki kikowaena.

Pākuʻi kiʻi: helu o nā papa (ʻo ia hoʻi render()) no kekona, a i ʻole FPS. Hoʻoikaika pinepine nā pāʻani e hoʻokō ma kahi o 60 FPS.

Ka helu kuhi: ʻO ke alapine o ka hoʻouna ʻana o ke kikowaena i nā mea hou pāʻani i nā mea kūʻai aku. ʻOi aku ka haʻahaʻa ma mua o ka helu kiʻi. I kā mākou pāʻani, holo ke kikowaena ma ke alapine o 30 mau pōʻai i kēlā me kēia kekona.

Inā hāʻawi wale mākou i ka mea hou o ka pāʻani, a laila ʻaʻole e hele ka FPS ma luna o 30, no ka mea ʻAʻole loaʻa iā mākou ma mua o 30 mau mea hou i kēlā me kēia kekona mai ke kikowaena. ʻOiai inā mākou e kāhea render() 60 mau manawa i kēlā me kēia kekona, a laila ʻo ka hapalua o kēia mau kelepona e unuhi hou i ka mea like, ʻaʻohe hana maoli. ʻO kekahi pilikia me ka hoʻokō naive ʻo ia pili i ka lohi. Me ka wikiwiki pūnaewele maikaʻi loa, e loaʻa i ka mea kūʻai kahi hōʻano pāʻani i kēlā me kēia 33ms (30 mau kekona):

Ke hana ʻana i kahi pāʻani Pūnaewele .io Multiplayer
ʻO ka mea pōʻino, ʻaʻohe mea kūpono. ʻO kahi kiʻi ʻoi aku ka ʻoiaʻiʻo:
Ke hana ʻana i kahi pāʻani Pūnaewele .io Multiplayer
ʻO ka hoʻokō naive ʻo ia ka hihia maikaʻi loa i ka wā e pili ana i ka latency. Inā loaʻa kahi hōʻano pāʻani me ka lohi o 50ms, a laila nā hale kūʻai he 50ms hou aʻe no ka mea e hāʻawi mau ana ia i ke kūlana pāʻani mai ka hoʻohou mua. Hiki iā ʻoe ke noʻonoʻo i ka hōʻoluʻolu ʻole o kēia no ka mea pāʻani: ʻo ka hoʻopaʻa ʻana i ka pāʻani e hoʻoneʻe ʻia ka pāʻani a paʻa ʻole.

7.2 Hoʻomaikaʻi ʻia ke kūlana o ka mea kūʻai aku

E hoʻomaikaʻi mākou i ka hoʻokō naive. ʻO ka mua, hoʻohana mākou ka hoʻopaneʻe ʻana no 100 ms. ʻO ia ke ʻano o ke kūlana "i kēia manawa" o ka mea kūʻai aku e lohi mau ma hope o ka mokuʻāina o ka pāʻani ma ke kikowaena e 100ms. No ka laʻana, inā ʻo ka manawa ma ke kikowaena 150, a laila e hāʻawi ka mea kūʻai aku i ka mokuʻāina i noho ai ke kikowaena i kēlā manawa 50:

Ke hana ʻana i kahi pāʻani Pūnaewele .io Multiplayer
Hāʻawi kēia iā mākou i kahi buffer 100ms e ola i nā manawa hou o ka pāʻani hiki ʻole.

Ke hana ʻana i kahi pāʻani Pūnaewele .io Multiplayer
E mau ana ka uku no keia hoʻokomo lag no 100 ms. He mōhai liʻiliʻi kēia no ka pāʻani maʻalahi - ʻaʻole ʻike ka hapa nui o nā mea pāʻani (ʻoi aku nā mea pāʻani maʻamau) i kēia lohi. ʻOi aku ka maʻalahi o ka poʻe e hoʻololi i ka latency 100ms mau ma mua o ka pāʻani me kahi latency hiki ʻole ke ʻike ʻia.

Hiki iā mākou ke hoʻohana i kekahi ʻenehana i kapa ʻia wānana ʻaoʻao kūʻai, he hana maikaʻi ia no ka hōʻemi ʻana i ka latency i ʻike ʻia, akā ʻaʻole e uhi ʻia ma kēia pou.

ʻO kekahi hoʻomaikaʻi hou a mākou e hoʻohana nei linear interpolation. Ma muli o ka hoʻokuʻu ʻana i ka lag, ʻoi aku ka liʻiliʻi ma mua o ka manawa o ka mea kūʻai aku. Ke kāhea ʻia getCurrentState(), hiki iā mākou ke hoʻokō linear interpolation ma waena o nā hoʻolaha pāʻani ma mua a ma hope o ka manawa o kēia manawa i ka mea kūʻai:

Ke hana ʻana i kahi pāʻani Pūnaewele .io Multiplayer
Hoʻopau kēia i ka pilikia frame rate: hiki iā mākou ke hāʻawi i nā kiʻi kūʻokoʻa i kēlā me kēia frame rate a mākou e makemake ai!

7.3 Ka hoʻokō ʻana i ke kūlana mea kūʻai aku i hoʻonui ʻia

Laʻana hoʻokō ma src/client/state.js hoʻohana i ka render lag a me linear interpolation, akā ʻaʻole no ka lōʻihi. E ʻoki i ke code i ʻelua ʻāpana. Eia ka mea mua:

state.js, māhele 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;
}

ʻO ka hana mua e ʻike i ka mea currentServerTime(). E like me kā mākou i ʻike ai ma mua, ʻo kēlā me kēia pāʻani pāʻani e loaʻa kahi kikowaena manawa. Makemake mākou e hoʻohana i ka render latency e hāʻawi i ke kiʻi 100ms ma hope o ke kikowaena, akā ʻaʻole mākou e ʻike i ka manawa o kēia manawa ma ke kikowaena, no ka mea ʻaʻole hiki iā mākou ke ʻike i ka lōʻihi o ka loaʻa ʻana o kekahi o nā mea hou iā mākou. ʻAʻole hiki ke ʻike ʻia ka Pūnaewele a hiki ke ʻano like ʻole kona wikiwiki!

No ka hoʻopau ʻana i kēia pilikia, hiki iā mākou ke hoʻohana i kahi hoʻohālikelike kūpono: mākou e hoʻohālike ua hiki koke mai ka mea hou mua. Inā he ʻoiaʻiʻo kēia, a laila ʻike mākou i ka manawa kikowaena i kēia manawa kūikawā! Mālama mākou i ka hōʻailona manawa o ke kikowaena firstServerTimestamp a mālama i kā mākou kūloko (mea kūʻai aku) timestamp i ka manawa like i loko gameStart.

E kali. ʻAʻole pono he manawa ma ke kikowaena = manawa ma ka mea kūʻai? No ke aha mākou e hoʻokaʻawale ai ma waena o "ka leka manawa kikowaena" a me ka "ka manawa manawaleʻa"? He nīnau nui kēia! ʻIke ʻia ʻaʻole like lākou. Date.now() e hoʻihoʻi i nā manawa like ʻole i ka mea kūʻai aku a me ke kikowaena, a hilinaʻi ia i nā kumu kūloko o kēia mau mīkini. Mai noʻonoʻo e like nā kaha manawa ma nā mīkini a pau.

I kēia manawa maopopo mākou i ka hana currentServerTime(): hoi mai ka hōʻailona manawa kikowaena o ka manawa hāʻawi i kēia manawa. I nā huaʻōlelo ʻē aʻe, ʻo kēia ka manawa o ke kikowaena o kēia manawa (firstServerTimestamp <+ (Date.now() - gameStart)) hōʻemi i ka hoʻopaneʻe ʻana (RENDER_DELAY).

I kēia manawa, e nānā kākou i ke ʻano o kā mākou lawelawe ʻana i nā mea hou pāʻani. I ka loaʻa ʻana mai ka server update, ua kapa ʻia processGameUpdate()a mālama mākou i ka mea hou i kahi array gameUpdates. A laila, e nānā i ka hoʻohana ʻana i ka hoʻomanaʻo, wehe mākou i nā mea hou kahiko ma mua hoʻonui kumuno ka mea, ʻaʻole pono mākou iā lākou.

He aha ka "hōʻano hou"? ʻO kēia ʻo ka mea hou mua i loaʻa iā mākou ma ka neʻe ʻana i hope mai ka manawa o ke kikowaena. E hoʻomanaʻo i kēia kiʻi?

Ke hana ʻana i kahi pāʻani Pūnaewele .io Multiplayer
ʻO ka hōʻano hou o ka pāʻani ma ka ʻaoʻao hema o "Client Render Time" ʻo ia ka waihona kumu.

He aha ka hoʻonui kumu i hoʻohana ʻia no? No ke aha e hiki ai iā mākou ke hoʻokuʻu i nā mea hou i ka baseline? No ka hoʻomaopopo ʻana i kēia, e ʻae ma ka hopena e noʻonoʻo i ka hoʻokō getCurrentState():

state.js, māhele 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),
    };
  }
}

Hana mākou i ʻekolu mau hihia:

  1. base < 0 ʻo ia hoʻi, ʻaʻohe mea hou a hiki i ka manawa hāʻawi o kēia manawa (e ʻike i ka hoʻokō ma luna getBaseUpdate()). Hiki ke hana i kēia ma ka hoʻomaka ʻana o ka pāʻani ma muli o ka hana lag. I kēia hihia, hoʻohana mākou i ka mea hou loa i loaʻa.
  2. base ʻo ia ka mea hou loa i loaʻa iā mākou. Ma muli paha o ka lohi o ka pūnaewele a i ʻole ka pili pūnaewele maikaʻi ʻole. I kēia hihia, ke hoʻohana nei mākou i ka mea hou loa i loaʻa iā mākou.
  3. Loaʻa iā mākou kahi mea hou ma mua a ma hope o ka manawa hāʻawi i kēia manawa, no laila hiki iā mākou interpolate!

ʻO nā mea a pau i koe i loko state.js he hoʻokō ʻia o ka hoʻopili laina laina maʻalahi (akā ʻoluʻolu). Inā makemake ʻoe e ʻimi iā ʻoe iho, a laila wehe state.js maluna o Github.

Mahele 2. Kahua hope

Ma kēia ʻāpana, e nānā mākou i ka Node.js backend nāna e hoʻomalu i kā mākou laʻana pāʻani .io.

1. Lae komo komo

No ka mālamaʻana i ka pūnaewele pūnaewele, e hoʻohana mākou i kahi pūnaewele pūnaewele kaulana no Node.js i kapaʻia ka hoike. E hoʻonohonoho ʻia e kā mākou kikowaena kikowaena helu waihona src/server/server.js:

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

E hoʻomanaʻo i ka hapa mua a mākou i kūkākūkā ai iā Webpack? ʻO kēia kahi e hoʻohana ai mākou i kā mākou hoʻonohonoho Webpack. E hoʻohana mākou iā lākou ma nā ala ʻelua:

  • Hoʻohana webpack-dev-waena e hana hou i kā mākou mau pūʻolo hoʻomohala, a i ʻole
  • waihona hoʻololi statically dist/, kahi e kākau ai ʻo Webpack i kā mākou mau faila ma hope o ke kūkulu ʻana.

ʻO kekahi hana nui server.js ʻo ka hoʻonohonoho ʻana i ke kikowaena kumu.ioe pili wale ana i ke kikowaena Express:

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

Ma hope o ka hoʻokumu ʻana i kahi pilina socket.io i ke kikowaena, hoʻonohonoho mākou i nā mea lawelawe hanana no ke kumu hou. Mālama nā mea lawelawe i nā memo i loaʻa mai nā mea kūʻai mai ma ka hāʻawi ʻana i kahi mea hoʻokahi game:

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

Ke hana nei mākou i kahi pāʻani .io, no laila pono mākou i hoʻokahi kope Game ("Paʻani") - pāʻani nā mea pāʻani a pau ma ke kahua hoʻokahi! Ma ka ʻāpana aʻe e ʻike mākou i ka hana ʻana o kēia papa Game.

2. Nā kikowaena pāʻani

Papa Game loaʻa i ka loina koʻikoʻi ma ka ʻaoʻao kikowaena. ʻElua mau hana nui: hoʻokele pāʻani и pāʻani hoʻohālike.

E hoʻomaka kākou me ka hana mua, ka hoʻokele pāʻani.

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

  // ...
}

Ma kēia pāʻani, e ʻike mākou i nā mea pāʻani ma ke kahua id kā lākou socket.io socket (inā huikau ʻoe, a laila e hoʻi i server.js). Hāʻawi ʻo Socket.io i kēlā me kēia kumu i kahi kū hoʻokahi idno laila ʻaʻole pono mākou e hopohopo no ia mea. E kāhea wau iā ia ID mea pāʻani.

Me kēlā noʻonoʻo, e ʻimi kākou i nā ʻano hoʻololi i loko o kahi papa Game:

  • sockets he mea e hoʻopaʻa ai i ka mea pāʻani ID i ke kumu i pili me ka mea pāʻani. Hiki iā mākou ke komo i nā kumu e kā lākou mau mea pāʻani i ka manawa mau.
  • players He mea ia e hoʻopaʻa ai i ka mea pāʻani i ke code> mea pāʻani

bullets he laha o na mea Bullet, ʻaʻohe ʻano o ka hoʻonohonoho.
lastUpdateTime ʻo ia ka hōʻailona manawa o ka manawa hope i hōʻano hou ʻia ka pāʻani. E ʻike mākou pehea e hoʻohana koke ʻia ai.
shouldSendUpdate he mea hoololi kokua. E ʻike koke mākou i kona hoʻohana ʻana.
Nā Palapala addPlayer(), removePlayer() и handleInput() ʻAʻole pono e wehewehe, hoʻohana ʻia lākou i loko server.js. Inā pono ʻoe e hōʻano hou i kou hoʻomanaʻo, e hoʻi i kahi kiʻekiʻe iki.

Laina hope constructor() hoʻomaka pōʻai hou nā pāʻani (me ka pinepine o 60 mau mea hou/s):

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

  // ...
}

Palapala update() aia paha ka ʻāpana koʻikoʻi o ka loina ʻaoʻao server. Eia kāna hana, ma ke ʻano:

  1. E helu ana i ka lōʻihi dt ua hala mai ka hope update().
  2. Hoʻohou i kēlā me kēia projectile a luku iā lākou inā pono. E ʻike mākou i ka hoʻokō ʻana o kēia hana ma hope. I kēia manawa, ua lawa iā mākou ke ʻike i kēlā bullet.update() hoʻi trueinā e luku ʻia ka pahu pahu (heʻe ʻo ia i waho o ke kahua pāʻani).
  3. Hoʻohou i kēlā me kēia mea pāʻani a hoʻoulu i kahi projectile inā pono. E ʻike pū mākou i kēia hoʻokō ma hope − player.update() hiki ke hoihoi i kekahi mea Bullet.
  4. E nānā no ka hui ʻana ma waena o nā projectiles a me nā mea pāʻani me applyCollisions(), ka mea e hoʻihoʻi mai i ke ʻano o nā projectiles i pā i nā mea pāʻani. No kēlā me kēia projectile i hoʻi mai, hoʻonui mākou i nā helu o ka mea pāʻani nāna i puhi iā ia (hoʻohana player.onDealtDamage()) a laila e wehe i ka projectile mai ka array bullets.
  5. Hoʻomaopopo a luku i nā mea pāʻani i pepehi ʻia.
  6. Hoʻouna i kahi hōʻano pāʻani i nā mea pāʻani a pau kēlā me kēia kekona manawa i kaheaia update(). Kōkua kēia iā mākou e mālama i ka mea hoʻololi kōkua i ʻōlelo ʻia ma luna. shouldSendUpdate. No ka mea update() i kāhea ʻia 60 mau manawa / s, hoʻouna mākou i nā mea hou pāʻani 30 mau manawa / s. Pela, alapine o ka uaki ʻO ka uaki server he 30 mau hola / s (ua kamaʻilio mākou e pili ana i nā helu uaki ma ka hapa mua).

No ke aha e hoʻouna ai i nā mea hou pāʻani wale nō ma o ka manawa ? E mālama i ke kahawai. ʻO 30 mau pāʻani i kēlā me kēia kekona he nui!

No ke aha e kelepona ʻole ai update() 30 manawa i kekona? No ka hoʻomaikaʻi ʻana i ka simulation pāʻani. ʻO ka mea i kapa pinepine ʻia update(), ʻoi aku ka pololei o ka simulation pāʻani. Akā, mai lawe nui i ka nui o nā pilikia. update(), no ka mea, he hana pili kālā kēia - ua lawa ka 60 kekona.

ʻO ke koena o ka papa Game aia nā ʻano kōkua i hoʻohana ʻia ma update():

game.js hapa 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() maʻalahi loa - hoʻokaʻawale i nā mea pāʻani ma ka helu, lawe i ka ʻelima kiʻekiʻe, a hoʻihoʻi i ka inoa inoa a me ka helu no kēlā me kēia.

createUpdate() hoʻohana ʻia i update() e hana i nā mea hou pāʻani i hāʻawi ʻia i nā mea pāʻani. ʻO kāna hana nui ke kāhea ʻana i nā ala serializeForUpdate()hoʻokō ʻia no nā papa Player и Bullet. E hoʻomaopopo i ka hāʻawi wale ʻana i ka ʻikepili i kēlā me kēia mea pāʻani e pili ana kokoke loa nā mea pāʻani a me nā projectiles - ʻaʻohe pono e hoʻouna i ka ʻike e pili ana i nā mea pāʻani i mamao loa mai ka mea pāʻani!

3. Nā mea pāʻani ma ke kikowaena

I loko o kā mākou pāʻani, ua like loa nā projectiles a me nā mea pāʻani: he mau mea pāʻani holoʻokoʻa lākou. No ka hoʻohana ʻana i kēia like ma waena o nā mea pāʻani a me nā projectiles, e hoʻomaka kākou ma ka hoʻokō ʻana i kahi papa kumu Object:

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

ʻAʻohe mea paʻakikī e hana nei. E lilo kēia papa i wahi heleuma maikaʻi no ka hoʻonui. E ʻike kākou pehea ka papa Bullet hoʻohana 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;
  }
}

Ka hoʻokō Bullet pōkole loa! Ua hoʻohui mākou i Object nā mea hoʻonui wale nō:

  • Ke hoʻohana nei i kahi pūʻolo pōkole no ka hanauna maalea id pahuhana.
  • Hoʻohui i kahi kahua parentIDi hiki iā ʻoe ke hahai i ka mea pāʻani nāna i hana i kēia projectile.
  • Hoʻohui i kahi waiwai hoʻihoʻi i update(), ua like ia me trueinā aia ka projectile ma waho o ke kahua (e hoʻomanaʻo mākou i kamaʻilio e pili ana i kēia ma ka pauku hope?).

E neʻe kākou i ka Player:

mea pāʻani.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,
    };
  }
}

ʻOi aku ka paʻakikī o nā mea pāʻani ma mua o nā projectiles, no laila pono e mālama ʻia kekahi mau māla i kēia papa. ʻO kāna ʻano hana update() he nui nā hana, ʻo ia hoʻi, e hoʻihoʻi i ka projectile hou i hana ʻia inā ʻaʻohe mea i koe fireCooldown (hoʻomanaʻo mākou i kamaʻilio e pili ana i kēia ma ka pauku mua?). Hoʻonui ia i ke ʻano serializeForUpdate(), no ka mea pono mākou e hoʻokomo i nā māla hou no ka mea pāʻani i ka hoʻonui pāʻani.

Loaʻa i kahi papa kumu Object - he hana koʻikoʻi e pale i ka hana hou ʻana i ke code. No ka laʻana, ʻaʻohe papa Object pono ka hoʻokō like ʻana o kēlā me kēia mea pāʻani distanceTo(), a ʻo ka hoʻopaʻa ʻana i kēia mau hoʻokō āpau ma nā faila he mea pōʻino. He mea nui kēia no nā papahana nui.i ka nui o ka hoonui ana Object ke ulu nei na papa.

4. ʻIke hoʻokuʻi

ʻO ka mea wale nō i koe iā mākou, ʻo ia ka ʻike i ka wā e pā ai nā projectiles i nā mea pāʻani! E hoʻomanaʻo i kēia ʻāpana code mai ke ʻano update() ma ka papa Game:

pāʻani.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),
    );

    // ...
  }
}

Pono mākou e hoʻokō i ke ʻano applyCollisions(), ka mea e hoʻihoʻi i nā projectiles a pau i pā i nā mea pāʻani. ʻO ka mea pōmaikaʻi, ʻaʻole paʻakikī ke hana no ka mea

  • He mau pōʻai nā mea kuʻi a pau, a ʻo ia ke ʻano maʻalahi loa e hoʻokō i ka ʻike ʻana.
  • Loaʻa iā mākou kahi ala distanceTo(), a mākou i hoʻokō ai i ka pauku mua ma ka papa Object.

Eia ke ʻano o kā mākou hoʻokō ʻana i ka ʻike hui ʻana:

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

Hoʻokumu ʻia kēia ʻike kuʻi maʻalahi ma ka ʻoiaʻiʻo hui 'elua mau poai ke emi ka mamao ma waena o ko laua mau kikowaena ma mua o ka huina o ko laua radii. Eia ke ano o ka loihi o na kikowaena o na poai elua me ka huina o ko laua radii.

Ke hana ʻana i kahi pāʻani Pūnaewele .io Multiplayer
Aia kekahi mau mea hou aʻe e noʻonoʻo ai ma aneʻi:

  • ʻAʻole pono e pā ka projectile i ka mea pāʻani nāna ia i hana. Hiki ke hoʻokō ʻia kēia ma ka hoʻohālikelike ʻana bullet.parentID с player.id.
  • Hoʻokahi wale nō paʻi o ka projectile i ka hihia palena o nā mea pāʻani he nui i ka manawa like. E hoʻoponopono mākou i kēia pilikia me ka hoʻohana ʻana i ka mea hoʻohana break: i ka loa'a 'ana o ka mea hookani pila me ka projectile, ho'opau mākou i ka huli 'ana a ne'e aku i ka projectile a'e.

Ka hopena

ʻo ia wale nō! Ua uhi mākou i nā mea āpau e pono ai ʻoe e ʻike e hana i kahi pāʻani pūnaewele .io. He aha ka hope? E kūkulu i kāu pāʻani .io ponoʻī!

Loaʻa nā kumu hoʻohālike āpau a kau ʻia ma Github.

Source: www.habr.com

Pākuʻi i ka manaʻo hoʻopuka