Te hanga i tetahi keemu tukutuku maha i roto i te momo .io

Te hanga i tetahi keemu tukutuku maha i roto i te momo .io
I tukuna i te tau 2015 Agar.io ka noho hei tupuna mo tetahi momo hou kēmu.io, kua nui haere tona rongonui mai i tera wa. Kua kite ahau i te pikinga ake o te rongonui o nga keemu .io ahau ano: i roto i nga tau e toru kua hipa, I i hanga me te hoko i nga keemu e rua i tenei momo..

Mena kare ano koe i rongo mo enei keemu i mua, he kore utu, he keemu tukutuku maha e ngawari ana ki te takaro (kaore he putea e hiahiatia). I te nuinga o te wa ka uru ratou ki te maha o nga kaitakaro whakahē ki te papa kotahi. Ko etahi atu keemu .io rongonui: Slither.io и Diep.io.

I tenei pou ka mohio tatou me pehea hanga he keemu .io mai i te wahanga. Hei mahi i tenei, ko te matauranga anake mo te Javascript ka nui: me mohio koe ki nga mea penei i te syntax ES6, kupu matua this и mau fafauraa. Ahakoa kaore koe e tino mohio ki te Javascript, ka marama tonu koe ki te nuinga o te panui.

He tauira o te keemu .io

Mo te awhina whakangungu ka tirohia e matou kēmu tauira .io. Whakamātauria ki te takaro!

Te hanga i tetahi keemu tukutuku maha i roto i te momo .io
He maamaa noa te keemu: kei te whakahaere koe i tetahi kaipuke i roto i te papaahi me etahi atu kaitakaro. Ka tahuna aunoatia e to kaipuke nga kaupapa ka ngana koe ki te patu i etahi atu kaitakaro i a koe e karo ana i a raatau kaupapa.

1. He tirohanga poto/hanganga kaupapa

Ka tūtohu ahau tikiake waehere puna tauira kēmu kia taea e koe te whai i ahau.

Ka whakamahia e te tauira enei e whai ake nei:

  • A faaite i Ko te tino angamahi tukutuku rongonui mo Node.js e whakahaere ana i te tūmau tukutuku o te kēmu.
  • turanga.io — puna ipurangi mo te whakawhiti raraunga i waenga i te kaitirotiro me te tūmau.
  • Pūtataetukutuku - kaiwhakahaere kōwae. Ka taea e koe te panui mo te aha te whakamahi i te Paetukutuku konei.

Koinei te ahua o te hanganga whaiaronga kaupapa:

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

tūmatanui/

Ko nga mea katoa kei roto i te kōpaki public/ ka tukuna e te tūmau. IN public/assets/ kei roto nga whakaahua e whakamahia ana e to maatau kaupapa.

src /

Ko nga waehere puna katoa kei roto i te kōpaki src/. Nga taitara client/ и server/ korero mo ratou ano shared/ kei roto he konae taumau i kawemai e te kiritaki me te tūmau.

2. Nga tawhā hui/kaupapa

Ka rite ki te korero i runga ake nei, ka whakamahia e matou he kaiwhakahaere kōwae hei hanga i te kaupapa Pūtataetukutuku. Me titiro ki ta maatau whirihoranga 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',
    }),
  ],
};

Ko nga rarangi tino nui i konei ko nga mea e whai ake nei:

  • src/client/index.js ko te waahi urunga o te kiritaki Javascript (JS). Ka timata te Peeke Tukutuku mai i konei ka titiro whakamuri mo etahi atu konae kawemai.
  • Ko te putanga JS o to tatou hanga Tukutuku ka noho ki te raarangi dist/. Ka kiia e ahau tenei konae ko to maatau kete JS.
  • Ka whakamahia e maatau Papel, otirā te whirihoranga @babel/preset-env ki te whakawhiti i to maatau waehere JS mo nga kaitirotiro tawhito.
  • Ka whakamahia e matou he mono hei tango i nga CSS katoa e tohuhia ana e nga konae JS ka whakakotahi ki te waahi kotahi. Ka kiia e ahau na matou mōkihi CSS.

Kua kite pea koe i nga ingoa konae kete kee '[name].[contenthash].ext'. Kei roto whakakapinga ingoa kōnae Paetukutuku: [name] ka whakakapihia ki te ingoa o te tohu whakauru (i roto i ta maatau keehi game), me [contenthash] ka whakakapia ki te hash o nga ihirangi kōnae. Ka mahia e matou tenei ki arotau i te kaupapa mo te hashing - ka taea e matou te kii atu ki nga kaitirotiro kia keteroki a matou kete JS mo nga wa katoa na te mea ki te huri te kete, ka huri ano tona ingoa konae (huringa contenthash). Ko te hua kua oti ko te ingoa konae o te tirohanga game.dbeee76e91a97d0c7207.js.

kōnae webpack.common.js - Koinei te konae whirihoranga turanga e kawemai ana ki roto i te whanaketanga me nga whirihoranga kaupapa kua oti. Hei tauira, koinei te whirihoranga whanaketanga:

webpack.dev.js

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

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

Mo te pai, ka whakamahia e matou i roto i te tukanga whanaketanga webpack.dev.js, ka huri ki webpack.prod.js, ki te arotau i nga rahi o te kete ka tukuna ki te whakaputa.

Tatūnga rohe

Ka tūtohu ahau ki te whakauru i te kaupapa ki runga i to miihini o to rohe kia taea ai e koe te whai i nga waahanga kua whakarārangitia i tenei pou. He ngawari te tatūnga: tuatahi, me whai te punaha Node и NPM. I muri mai me mahi koe

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

a kua rite koe ki te haere! Hei timata i te tūmau whanaketanga, rere noa

$ npm run develop

ka haere ki to kaitirotiro paetukutuku localhost: 3000. Ka hanga aunoatia e te tūmau whanaketanga nga kete JS me CSS ka puta nga huringa waehere - whakamauhia te wharangi kia kite i nga huringa katoa!

3. Nga waahi urunga kiritaki

Kia heke iho ki te waehere keemu ake. Tuatahi ka hiahia tatou i tetahi whaarangi index.html, ka toro koe ki te pae, ka utaina e te kaitirotiro i te tuatahi. Ka tino ngawari to maatau wharangi:

index.html

He tauira .io keemu  TAKARO

Ko tenei tauira waehere kua ngawari ake mo te maarama, ka pena ano ahau me etahi atu tauira i roto i te panui. Ka taea e koe te titiro i nga wa katoa i te waehere katoa i GitHub.

Kei a matou:

  • Huānga kānawehi HTML5 (<canvas>), ka whakamahia e matou hei whakaputa i te keemu.
  • <link> hei taapiri i ta maatau kete CSS.
  • <script> hei taapiri i to maatau kete Javascript.
  • Tahua matua me te ingoa kaiwhakamahi <input> me te paatene “PLAY” (<button>).

Kia utaina te wharangi kainga, ka timata te kaitirotiro ki te whakamahi i te waehere Javascript, timata mai i te konae JS urunga: src/client/index.js.

taurangi.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 ahua uaua tenei, engari kaore i te nui te mahi i konei:

  1. Kawemai etahi atu kōnae JS.
  2. Kawemai CSS (kia mohio a Webpack ki te whakauru i a raatau ki roto i ta maatau kete CSS).
  3. Te whakatairanga connect() ki te whakarite hononga ki te tūmau ka timata downloadAssets() ki te tango i nga whakaahua e hiahiatia ana hei whakaputa i te keemu.
  4. I muri i te whakaoti i te wahanga 3 ka whakaatuhia te tahua matua (playMenu).
  5. Te whakatu i te kaihautu patene paatene "PLAY". Ka pehia te paatene, ka arawhiti te waehere i te keemu ka korero ki te kaimau kua reri tatou ki te takaro.

Ko te "kai" matua o to tatou arorau kiritaki-tūmau kei roto i aua konae i kawemai e te konae index.js. Inaianei ka ata tirohia e tatou katoa.

4. Whakawhitinga raraunga kiritaki

I tenei keemu ka whakamahia e matou he whare pukapuka rongonui ki te korero ki te kaimau turanga.io. Kei te tautoko a Socket.io Paetukutuku, e pai ana mo te whakawhitiwhiti korero-rua: ka taea e matou te tuku karere ki te tūmau и ka taea e te tūmau te tuku karere ki a matou i runga i taua hononga.

Ka kotahi te konae src/client/networking.jsma wai e tiaki katoa whakawhitiwhitinga me te tūmau:

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

Ka whakapotohia ano tenei waehere mo te marama.

E toru nga mea nui kei roto i tenei konae:

  • E ngana ana matou ki te hono atu ki te tūmau. connectedPromise ka whakaaetia anake ina kua whakatuu hononga.
  • Mena ka angitu te hononga, ka rehita matou i nga mahi waea hoki (processGameUpdate() и onGameOver()) mo nga karere ka riro mai i a maatau mai i te tūmau.
  • Ka kaweake e matou play() и updateDirection()kia taea ai e etahi atu konae te whakamahi.

5. Te whakaputanga kiritaki

Kua tae ki te wa ki te whakaatu i te pikitia ki te mata!

...engari i mua i te mahi i tenei, me tango katoa nga whakaahua (rauemi) e hiahiatia ana mo tenei. Me tuhi he kaiwhakahaere rauemi:

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

Ko te whakahaere rauemi ehara i te mea uaua ki te whakatinana! Ko te kaupapa matua ko te penapena taonga assets, ka herea te matua ingoa kōnae ki te uara ahanoa Image. Ina utaina te rauemi, ka tiakina e matou ki tetahi mea assets mo te whiwhinga tere a muri ake nei. Ahea ka whakaaetia te tango i ia rauemi takitahi (ara, ka tango katoa rauemi), ka whakaaetia e matou downloadPromise.

I muri i te tango i nga rauemi, ka taea e koe te timata ki te whakaputa. Ka rite ki te korero i mua, ki te tuhi i runga i te wharangi paetukutuku ka whakamahia e matou HTML5 Kanawehi (<canvas>). He maamaa noa ta maatau keemu, na reira me whakaatu noa nga mea e whai ake nei:

  1. Papamuri
  2. Kaitākaro kaipuke
  3. Ko etahi atu kaitakaro i te keemu
  4. Anga anga

Anei nga wahanga nui src/client/render.js, e tuhi tika ana i nga tohu e wha kua whakarārangihia i runga ake 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);
}

Ka whakapotohia hoki tenei waehere mo te marama.

render() Ko te mahi matua o tenei kōnae. startRendering() и stopRendering() te whakahaere i te whakahohenga o te huringa whakaputa i te 60 FPS.

Ko nga whakatinanatanga motuhake o nga mahi kaiawhina tuku takitahi (hei tauira renderBullet()) ehara i te mea nui, engari he tauira ngawari tenei:

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

Kia mahara kei te whakamahi matou i te tikanga getAsset(), i kitea i mua i roto i asset.js!

Mena kei te pirangi koe ki te tirotiro i etahi atu mahi kaiawhina tuku, panuihia te toenga o src/client/render.js.

6. Te urunga a te kiritaki

Kua tae ki te wa ki te hanga keemu ka taea te purei! Ko te kaupapa whakahaere ka tino ngawari: ki te whakarereke i te ahunga o te nekehanga, ka taea e koe te whakamahi i te kiore (i runga i te rorohiko) ka pa ranei ki te mata (i runga i te taputapu waea). Hei whakatinana i tenei ka rehita matou Kaihoko Whakarongo mo nga huihuinga Kiore me te Pa.
Ka tiaki i enei mea katoa src/client/input.js:

tāuru.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() Ko nga Kaiwhakarongo Takahanga e karanga ana updateDirection() (mai networking.js) ina puta he takahanga whakauru (hei tauira, ina nekehia te kiore). updateDirection() e pa ana ki te whakawhiti karere me te tūmau, e whakahaere ana i te huihuinga whakauru me te whakahou i te ahua o te keemu.

7. Tūnga Kiritaki

Ko tenei waahanga te mea tino uaua i te waahanga tuatahi o te pou. Kaua e ngakaukore ki te kore koe e mohio i te wa tuatahi ka panui koe! Ka taea e koe te peke ka hoki mai ano i muri mai.

Ko te waahanga whakamutunga o te panga e hiahiatia ana hei whakaoti i te waehere kiritaki-tūmau kāwanatanga. Kia maumahara ki te snippet waehere mai i te waahanga Rendering Kiritaki?

render.js

import { getCurrentState } from './state';

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

  // Do the rendering
  // ...
}

getCurrentState() me kaha ki te whakarato ki a maatau te ahua keemu o naianei kei roto i te kaihoko i nga wa katoa i runga i nga whakahoutanga i whakawhiwhia mai i te tūmau. Anei tetahi tauira o te whakahou keemu ka tukuna pea e te tūmau:

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

Kei ia whakahou keemu e rima nga mara rite:

  • t: Tohuwā Tūmau e tohu ana i te wā i waihangatia ai tēnei whakahōu.
  • me: Nga korero mo te kaitakaro e whiwhi ana i tenei whakahou.
  • ētahi atu: He huinga korero mo etahi atu kaitakaro e uru ana ki te keemu kotahi.
  • matā: te maha o nga korero mo nga kaupapa i roto i te keemu.
  • leaderboard: Raraunga rangatira o naianei. E kore matou e whai whakaaro ki a raatau i tenei panui.

7.1 Te ahua o te kiritaki

Te whakatinanatanga kuware getCurrentState() ka taea anake te whakahoki mai i nga raraunga mai i te whakahou keemu kua riro tata nei.

naive-state.js

let lastGameUpdate = null;

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

export function getCurrentState() {
  return lastGameUpdate;
}

Ataahua me te marama! Engari mena he ngawari noa. Ko tetahi o nga take he raruraru tenei whakatinanatanga: ka whakawhāitihia te reiti anga tāhuahua ki te tere karaka tūmau.

Auau anga: te maha o nga papa (ara waea render()) ia hekona, FPS ranei. Ko nga keemu e ngana ana kia eke ki te 60 FPS.

Reiti Tohu: Ko te auau e tukuna ai e te tūmau nga whakahou kēmu ki nga kiritaki. He iti ake i te reiti anga. I roto i ta maatau keemu, ka rere te tūmau i te 30 tohu mo ia hekona.

Mena ka tukuna noa e matou te whakahoutanga o te keemu hou, katahi ka kore rawa te FPS e neke atu i te 30 na te mea e kore matou e whiwhi neke atu i te 30 whakahōunga ia hekona i te tūmau. Ahakoa waea mai render() 60 nga wa mo ia hekona, katahi ko te haurua o enei waea ka tuhi ano i te mea ano, kaore he mahi. Ko tetahi atu raruraru me te whakatinanatanga maamaa ko tera kei raro i nga whakaroa. I te tere Ipurangi pai, ka whiwhi te kiritaki i te whakahou keemu ia 33 ms (30 ia hekona):

Te hanga i tetahi keemu tukutuku maha i roto i te momo .io
Kia aroha mai, kahore he mea tino tika. Ko te pikitia pono ake ko:
Te hanga i tetahi keemu tukutuku maha i roto i te momo .io
Ko te whakatinanatanga poauau te ahua kino rawa atu ina tae mai ki te roanga. Mena ka tae mai he whakahou keemu me te 50ms whakaroa, na kei te puhoi te kiritaki na te 50ms taapiri na te mea kei te whakaatu tonu i te ahua o te keemu mai i te whakahoutanga o mua. Ka taea e koe te whakaaro he pehea te raru mo te kaitakaro: na te puhoi o te keemu, ka ahua kee te keemu me te kore e tau.

7.2 Kua pai ake te ahua o te kiritaki

Ka mahia e matou etahi whakapainga ki te whakatinana poauau. Tuatahi, ka whakamahia e matou whakaroa tuku i te 100 ms. Ko te tikanga ko te ahua "naianei" o te kiritaki ka 100ms i muri i te ahua keemu i runga i te tūmau. Hei tauira, mena ko te wa tūmau 150, ka tukuna e te kiritaki te ahua i noho ai te tūmau i taua wa 50:

Te hanga i tetahi keemu tukutuku maha i roto i te momo .io
Ma tenei ka homai he 100ms buffer ki te ora i te waa ohorere o nga whakahoutanga keemu:

Te hanga i tetahi keemu tukutuku maha i roto i te momo .io
Ko te utu mo tenei ka pumau tonu takamuri whakauru i te 100 ms. He patunga iti tenei mo te mahi maeneene - ko te nuinga o nga kaitakaro (ina koa ko nga mea noa) kare e kite i tenei whakaroa. He maamaa ake mo te tangata ki te whakatika ki te 100ms tohe tonu atu i te takaro me te tohenga ohorere.

Ka taea e tatou te whakamahi i tetahi atu tikanga e kiia nei "matapae taha kiritaki", he pai te mahi ki te whakaiti i te ahua o te noho roa, engari kaore e korerohia i tenei pou.

Ko tetahi atu whakapainga e whakamahia ana e matou wawaotanga raina. Na te tamuri o te whakaputanga, ko te nuinga o te waa kotahi te whakahou i mua i te waa o naianei i roto i te kaihoko. Ka karangahia getCurrentState(), ka taea e tatou te whakatutuki wawaotanga raina i waenganui i nga whakahoutanga keemu i mua noa atu i muri mai i te waa o naianei i roto i te kiritaki:

Te hanga i tetahi keemu tukutuku maha i roto i te momo .io
Ma tenei ka whakatika te raru reiti anga: ka taea e taatau te whakaputa papa ahurei ki nga reiti anga e hiahiatia ana!

7.3 Te whakatinana i te ahua pai ake o te kiritaki

Tauira whakatinanatanga i roto i src/client/state.js e whakamahi ana i te whakaroa tuku me te wawaotanga rarangi, engari kare e roa tenei. Kia wahia te waehere kia rua nga wahanga. Anei te mea tuatahi:

state.js, wahanga 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;
}

Ko te mea tuatahi me mahi koe ko te whakaaro he aha tana mahi currentServerTime(). Ka rite ki ta maatau i kite i mua, kei roto i nga whakahoutanga keemu he tohu tohu waahi. Kei te pirangi matou ki te whakamahi i te taaruatanga hei whakaputa i te ahua 100ms kei muri i te tūmau, engari e kore matou e mohio ki te wa o naianei i runga i te tūmau, na te mea kaore matou e mohio he pehea te roa ka tae mai etahi o nga whakahoutanga ki a matou. Ko te Ipurangi he ohorere, he rereke te tere!

Hei whakatika i tenei raruraru, ka taea e tatou te whakamahi i te tatai tika: tatou me kii kua tae mai te whakahou tuatahi. Mena he pono tenei, ka mohio tatou ki te wa o te kaimau i tera waa! Ka penapenahia e matou te tohu waahi tūmau ki roto firstServerTimestamp a whakaorangia to matou rohe (kiritaki) tohuwā i te wa ano i roto gameStart.

Aue, tatari mo te meneti. Me kore he wa i runga i te tūmau = te wa i runga i te kiritaki? He aha tatou i wehewehe ai i waenga i te "waituhi wa tūmau" me te "waitohu wā kiritaki"? He patai nui tenei! Te ahua nei ehara enei i te mea kotahi. Date.now() ka whakahoki mai i nga tohu waahi rereke i roto i te kiritaki me te tūmau, ka whakawhirinaki tenei ki nga ahuatanga o te rohe o enei miihini. Kaua rawa e whakaaro ka rite nga tohu wa ki nga miihini katoa.

Inaianei kua mohio tatou he aha tana mahi currentServerTime(): ka hoki mai waitohuwā tūmau o te wā tāhuahua onāianei. Arā, koinei te wā tūmau o nāianei (firstServerTimestamp <+ (Date.now() - gameStart)) haunga te whakaroa tāhuahua (RENDER_DELAY).

Inaianei me titiro tatou ki te whakahaere i nga whakahou keemu. Ina tae mai he whakahou mai i te tūmau, ka karangahia processGameUpdate(), ka tiakina e matou te whakahou hou ki tetahi huinga gameUpdates. Na, hei tirotiro i te whakamahinga mahara, ka tangohia e matou nga whakahou tawhito katoa ki whakahou turangano te mea kare e hiahiatia ana e tatou.

He aha te "whakahoutanga matua"? Tenei Ko te whakahou tuatahi ka kitea e matou ma te neke whakamuri mai i te wa tūmau o nāianei. Kia mahara ki tenei hoahoa?

Te hanga i tetahi keemu tukutuku maha i roto i te momo .io
Ko te whakahoutanga o te keemu ki te taha maui o te "Waima Render Kiritaki" ko te whakahou turanga.

He aha te kaupapa whakahou i whakamahia? He aha e taea ai e tatou te tuku whakahōutanga ki te turanga? Kia mohio ki tenei, me i te mutunga kia titiro tatou ki te whakatinanatanga getCurrentState():

state.js, wahanga 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),
    };
  }
}

Ka whakahaerehia e matou nga keehi e toru:

  1. base < 0 ko te tikanga karekau he whakahōutanga tae noa ki te wa whakaputa o naianei (tirohia te whakatinanatanga i runga ake nei getBaseUpdate()). Ka taea tenei i te timatanga o te keemu na te mea he takamuri te tuku. I tenei keehi, ka whakamahia e matou te whakahou hou kua tae mai.
  2. base Ko te whakahōutanga hou kei a matou. Ka puta pea tenei na te roa o te whatunga, te ngoikore ranei o te hononga ipurangi. I tenei keehi ano ka whakamahia e matou te whakahou hou kei a matou.
  3. Kei a matou he whakahou i mua i muri mai i te wa tuku o naianei, na reira ka taea whakawhiti!

Ko nga mea katoa e toe ana ki roto state.js he whakatinanatanga o te wawaotanga rarangi he pangarau ngawari (engari hoha). Mena kei te pirangi koe ki te torotoro i a koe ano, katahi ka tuwhera state.js i runga i GitHub.

Wāhanga 2. Tūmau tuara

I tenei wahanga ka titiro tatou ki te Node.js backend e whakahaere ana i a tatou tauira o te keemu .io.

1. Tohu urunga a te tūmau

Hei whakahaere i te tūmau tukutuku ka whakamahia e matou he anga tukutuku rongonui mo Node.js e kiia ana A faaite i. Ka whirihorahia e to maatau konae tohu urunga src/server/server.js:

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

Kia mahara ki tera i te wahanga tuatahi i korerohia e matou a Webpack? Koinei te waahi ka whakamahia e matou o maatau whirihoranga Tukutuku. Ka whakamahia e matou i roto i nga huarahi e rua:

  • Whakamahia webpack-dev-waewaewae ki te hanga aunoa i o tatou kete whanaketanga, ranei
  • Whakawhitihia he kōpaki dist/, ka tuhia e Webpack a maatau konae i muri i te hangahanga.

Ko tetahi atu mahi nui server.js ko te whakatu i te tūmau turanga.ioe hono noa ana ki te tūmau Express:

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

Whai muri i te whakaturanga i te hononga socket.io me te tūmau, ka whirihorahia e matou nga kaikawe huihuinga mo te turanga hou. Ka tukatukahia e nga kaihautu takahanga nga karere ka riro mai i nga kaihoko ma te tuku ki tetahi ahanoa kotahi game:

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

Kei te hanga e matou he keemu .io, no reira kotahi anake te kape Game (“Kemu”) – ka takaro nga kaitakaro katoa ki te papa kotahi! I te wahanga e whai ake nei ka kite tatou i te mahi o tenei karaehe Game.

2. Tūmau kēmu

Класс Game kei roto te tino arorau taha tūmau. E rua nga mahi matua: whakahaere kaitākaro и whaihanga kēmu.

Me timata ki te mahi tuatahi - te whakahaere i nga kaitakaro.

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

  // ...
}

I tenei keemu ka tautuhia e matou nga kaitakaro ma te mara id to ratou turanga turanga.io (mehemea kei te rangirua koe, ka hoki ki server.js). Ko te Socket.io ake ka whakawhiwhia ki ia turanga he ahurei id, no reira kare e tika kia maaharahara. Ka karanga ahau ki a ia ID kaitākaro.

I runga i tera whakaaro, me titiro ki nga taurangi tauira i roto i te akomanga Game:

  • sockets he ahanoa hei here i te ID kaitākaro ki te turanga e hono ana ki te kaitākaro. Ka taea e maatau te uru ki nga turanga ma o raatau ID kaitakaro i roto i te waa.
  • players he ahanoa hei here i te ID kaitākaro ki te waehere> ahanoa kaitākaro

bullets he huinga taonga Bullet, kaore he ota motuhake.
lastUpdateTime - Koinei te tohu wa o te whakahou keemu whakamutunga. Ka kite tatou e pehea ana te whakamahi wawe.
shouldSendUpdate he taurangi awhina. Ka kite ano tatou i tona whakamahinga apopo.
Nga tikanga addPlayer(), removePlayer() и handleInput() kahore he take ki te whakamarama, ka whakamahia ki roto server.js. Mena ka hiahia koe ki te whakahou, hoki ki runga ake.

Raina whakamutunga constructor() ka tiimata huringa huringa kēmu (me te auau o te 60 whakahōunga/s):

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

  // ...
}

Tikanga update() kei roto pea te waahanga tino nui o te arorau taha-tumau. Me whakarārangihia ngā mea katoa e mahia ana kia raupapa:

  1. Ka tātaihia he aha te wā dt mai i te wa whakamutunga update().
  2. Ka whakahou i ia kaupapa me te whakangaro i a raatau mehemea e tika ana. Ka kite tatou i te whakatinanatanga o tenei mahi a muri ake nei. Inaianei kua nui noa atu te mohio ki tena bullet.update() hoki mai true, ki te mea me whakangaro te kaupapa (i haere ia ki waho o te waahi).
  3. Ka whakahōuhia ia kaitākaro me te hanga i tetahi kaupapa ki te tika. Ka kite ano tatou i tenei whakatinanatanga i muri mai - player.update() ka taea te whakahoki i tetahi mea Bullet.
  4. Ka tirohia mo nga tukinga i waenga i nga kaupapa me nga kaitakaro e whakamahi ana applyCollisions(), e whakahoki mai ana i te maha o nga kaupapa ka pa ki nga kaitoro. Mo ia kaupapa ka hoki mai, ka whakanuia e matou te kaute o te kaitakaro nana i pupuhi (ma te whakamahi player.onDealtDamage()), ka tango i te kaupapa mai i te huinga bullets.
  5. Ka whakamohio me te whakangaro i nga kaitakaro katoa kua mate.
  6. Ka tukuna he whakahou keemu ki nga kaitakaro katoa ia hēkona nga wa ka karangahia update(). Ko te taurangi awhina kua whakahuahia i runga ake nei ka awhina i a maatau ki te whai i tenei shouldSendUpdate. No te mea update() ka karanga 60 wa/s, tukua matou whakahōunga kēmu 30 wa/s. No reira, auau karaka Ko te tūmau he 30 huringa karaka/s (i korero matou mo te auau karaka i te wahanga tuatahi).

He aha te tuku whakahōu kēmu anake na roto i te wa ? Hei tiaki hongere. 30 whakahōunga kēmu ia hēkona he nui!

He aha e kore ai e waea noa? update() 30 nga wa ia hekona? Hei whakapai ake i te whaihanga keemu. Ko te nuinga o te wa e kiia ana update(), ka tika ake te whaihanga o te keemu. Engari kaua e tino kahakina e te maha o nga wero update(), na te mea he mahi utu nui tenei - 60 ia hekona he nui rawa.

Te toenga o te akomanga Game Kei roto i nga tikanga kaiawhina e whakamahia ana i roto update():

game.js, wahanga 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() He tino ngawari - ka tohua e ia nga kaitakaro ma te kaute, ka tango i te rima o runga, ka whakahoki i te ingoa kaiwhakamahi me te kaute mo ia.

createUpdate() whakamahia i roto i update() ki te hanga i nga whakahou keemu ka tohatohahia ki nga kaitakaro. Ko tana mahi nui ko te karanga tikanga serializeForUpdate(), ka whakatinanahia mo nga karaehe Player и Bullet. Kia mahara ko te tuku raraunga anake ki ia kaitakaro tata kaitākaro me te kaupapa - kaore he take ki te tuku korero mo nga taonga keemu kei tawhiti atu i te kaitakaro!

3. Ahanoa kēmu i runga i te tūmau

I roto i a maatau keemu, he tino rite nga kaupapa me nga kaitakaro: he taonga keemu neke a tawhio noa. Ki te tango painga o tenei ritenga i waenga i nga kaitakaro me nga kaupapa, me timata ma te whakatinana i tetahi karaehe turanga Object:

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

Karekau he mea uaua kei konei. Ka noho tenei karaehe hei timatanga pai mo te roha. Kia kite tatou pehea te karaehe Bullet whakamahinga Object:

matā.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 tino poto! Kua tapiritia e matou ki Object ko nga toronga e whai ake nei anake:

  • Te whakamahi i te kohinga poto mo te whakatipuranga tupurangi id kaupapa.
  • Te taapiri mara parentID, kia taea e koe te whai i te kaitakaro nana i hanga tenei kaupapa.
  • Te taapiri i te uara whakahoki ki update(), he rite tonu true, mehemea kei waho te kaupapa i te papa (maharatia i korero matou mo tenei i te waahanga whakamutunga?).

Kia neke tatou ki Player:

kaitākaro.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,
    };
  }
}

He uaua ake nga kaitakaro i nga kaupapa, no reira me pupuri e tenei karaehe etahi atu mara. Ko tana tikanga update() ka nui ake nga mahi, ina koa te whakahoki i te kaupapa hou i hangaia mena karekau he toenga fireCooldown (mahara i korero matou mo tenei i te waahanga o mua?). Ka whakawhānuihia ano te tikanga serializeForUpdate(), na te mea me whakauru etahi atu mara mo te kaitakaro ki te whakahou i te keemu.

Te waatea o te karaehe turanga Object - he mahi nui hei karo i te tukurua waehere. Hei tauira, kaore he karaehe Object me rite te whakatinanatanga o ia ahanoa keemu distanceTo(), me te kape-whakapiri i enei whakatinanatanga katoa puta noa i nga konae maha ka waiho hei moemoea. He mea nui tenei mo nga kaupapa nui, ka te maha o te whakawhānui Object kei te tipu haere nga karaehe.

4. Te kitenga tukinga

Ko te mea anake e toe ana ma tatou ko te mohio ki te wa i pa ai nga kaupapa ki nga kaitakaro! Kia mahara ki tenei waahanga waehere mai i te tikanga update() i roto i te akomanga Game:

kēmu.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),
    );

    // ...
  }
}

Me whakatinana i te tikanga applyCollisions(), e whakahoki mai ana i nga kaupapa katoa i pa ki nga kaitakaro. Waimarie, ehara tenei i te mea uaua ki te mahi na te mea

  • He porowhita nga mea tukinga katoa, a koinei te ahua ngawari ki te whakatinana i te kitenga tukinga.
  • Kua whai tikanga tatou distanceTo(), i whakatinanahia e matou i roto i te akomanga i te waahanga o mua Object.

Koinei te ahua o to maatau whakatinanatanga o te kitenga tukinga:

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

Ko tenei kitenga tukinga ngawari kei runga i te meka e ka tuki nga porowhita e rua ki te iti iho te tawhiti i waenganui i o raua pokapu i te tapeke o o raua radii. Anei he take e rite ana te tawhiti i waenganui i nga pokapu o nga porowhita e rua ki te tapeke o o ratou radii:

Te hanga i tetahi keemu tukutuku maha i roto i te momo .io
I konei me aro nui koe ki etahi atu waahanga:

  • Kaua te kaupapa e pa ki te kaitakaro nana i hanga. Ka taea tenei ma te whakataurite bullet.parentID с player.id.
  • Kia kotahi noa te patu i te kaupapa i roto i te keehi tino nui o te patu i nga kaitakaro maha i te wa kotahi. Ka whakatauhia e matou tenei raruraru ma te whakamahi i te kaiwhakahaere break: Kia kitea he kaipara e tukinga ana ki te kaupapa, ka mutu te rapu ka haere ki te kaupapa e whai ake nei.

Ko te Whakamutunga

Heoi ano! Kua hipokina e matou nga mea katoa e hiahia ana koe ki te mohio ki te hanga i tetahi keemu paetukutuku .io. He aha to muri mai? Hangaia taau ake keemu .io!

Ko nga tauira tauira katoa he puna tuwhera me te whakairi ki runga GitHub.

Source: will.com

Tāpiri i te kōrero