Ṣiṣẹda kan Pupọ .io Web Game

Ṣiṣẹda kan Pupọ .io Web Game
Ti tu silẹ ni ọdun 2015 Agar.io di progenitor ti a titun oriṣi awọn ere .ioeyiti o ti dagba ni olokiki lati igba naa. Mo ti ni iriri tikalararẹ igbega ni olokiki ti awọn ere .io: ni ọdun mẹta sẹhin, Mo ni ṣẹda ati ta awọn ere meji ti oriṣi yii..

Ni ọran ti o ko tii gbọ ti awọn ere wọnyi tẹlẹ, iwọnyi jẹ awọn ere wẹẹbu elere pupọ ọfẹ ti o rọrun lati mu ṣiṣẹ (ko si akọọlẹ ti o nilo). Won maa koju ọpọlọpọ awọn titako awọn ẹrọ orin ni kanna arena. Awọn ere .io olokiki miiran: Slither.io и Diep.io.

Ninu ifiweranṣẹ yii, a yoo ṣawari bii ṣẹda .io ere lati ibere. Fun eyi, imọ nikan ti Javascript yoo to: o nilo lati ni oye awọn nkan bii sintasi ES6, koko this и Awọn ileri. Paapaa ti imọ rẹ ti Javascript ko ba pe, o tun le loye pupọ julọ ifiweranṣẹ naa.

.io ere apẹẹrẹ

Fun iranlọwọ ẹkọ, a yoo tọka si .io ere apẹẹrẹ. Gbiyanju lati mu ṣiṣẹ!

Ṣiṣẹda kan Pupọ .io Web Game
Ere naa rọrun pupọ: o ṣakoso ọkọ oju-omi kekere kan ni gbagede nibiti awọn oṣere miiran wa. Ọkọ oju omi rẹ ṣe ina awọn ohun-ọṣọ laifọwọyi ati pe o gbiyanju lati kọlu awọn oṣere miiran lakoko ti o yago fun awọn iṣẹ akanṣe wọn.

1. Finifini Akopọ / be ti ise agbese

Mo ṣe iṣeduro download orisun koodu ere apẹẹrẹ ki o le tẹle mi.

Apẹẹrẹ lo awọn atẹle wọnyi:

  • han jẹ ilana wẹẹbu Node.js olokiki julọ ti o ṣakoso olupin wẹẹbu ere naa.
  • iho.io - ile-ikawe websocket kan fun paṣipaarọ data laarin ẹrọ aṣawakiri ati olupin kan.
  • Oju-iwe wẹẹbu - module faili. O le ka nipa idi ti lati lo Webpack. nibi.

Eyi ni ohun ti ilana ilana iṣẹ akanṣe dabi:

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

gbangba /

Ohun gbogbo ninu folda public/ yoo wa ni statically silẹ nipasẹ olupin. IN public/assets/ ni awọn aworan ti a lo nipasẹ iṣẹ akanṣe wa.

src /

Gbogbo koodu orisun wa ninu folda src/. Awọn akọle client/ и server/ sọ fun ara wọn ati shared/ ni faili ibakan ti o jẹ agbewọle nipasẹ alabara ati olupin.

2. Assemblies / ise agbese eto

Gẹgẹbi a ti sọ loke, a lo oluṣakoso module lati kọ iṣẹ naa. Oju-iwe wẹẹbu. Jẹ ki a wo iṣeto Webpack wa:

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

Awọn ila pataki julọ nibi ni:

  • src/client/index.js jẹ aaye titẹsi ti alabara Javascript (JS). Apo wẹẹbu yoo bẹrẹ lati ibi ati wa leralera fun awọn faili miiran ti a ko wọle.
  • Ijade JS ti apamọ wẹẹbu wa yoo wa ninu itọsọna naa dist/. Emi yoo pe faili yii wa js package.
  • A lo Babel, ati ni pato iṣeto ni @babel/tito-env si gbigbe koodu JS wa fun awọn aṣawakiri agbalagba.
  • A nlo ohun itanna kan lati yọ gbogbo CSS ti a tọka si nipasẹ awọn faili JS ki o si darapọ wọn ni aye kan. Emi yoo pe e ni tiwa css package.

O le ti ṣe akiyesi awọn orukọ faili package ajeji '[name].[contenthash].ext'. Wọn ninu filename substitutions Apo apamọ wẹẹbu: [name] yoo rọpo pẹlu orukọ aaye titẹ sii (ninu ọran wa, eyi game), ṣugbọn [contenthash] yoo rọpo pẹlu hash ti akoonu faili naa. A ṣe si je ki ise agbese fun hashing - o le sọ fun awọn aṣawakiri lati kaṣe awọn idii JS wa titilai, nitori Ti package ba yipada, lẹhinna orukọ faili tun yipada (ayipada contenthash). Abajade ikẹhin yoo jẹ orukọ faili wiwo game.dbeee76e91a97d0c7207.js.

Ọna webpack.common.js jẹ faili iṣeto ipilẹ ti a gbe wọle sinu idagbasoke ati awọn atunto iṣẹ akanṣe. Eyi ni iṣeto idagbasoke apẹẹrẹ:

webpack.dev.js

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

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

Fun ṣiṣe, a lo ninu ilana idagbasoke webpack.dev.js, o si yipada si webpack.prod.jslati mu iwọn package pọ si nigbati o ba nlo si iṣelọpọ.

Eto agbegbe

Mo ṣeduro fifi sori ẹrọ akanṣe lori ẹrọ agbegbe kan ki o le tẹle awọn igbesẹ ti a ṣe akojọ si ni ifiweranṣẹ yii. Eto naa rọrun: akọkọ, eto naa gbọdọ ti fi sii ipade и NPM. Nigbamii o nilo lati ṣe

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

ati pe o ti ṣetan lati lọ! Lati bẹrẹ olupin idagbasoke, kan ṣiṣẹ

$ npm run develop

ki o si lọ si ẹrọ aṣawakiri wẹẹbu Agbegbe: 3000. Olupin idagbasoke yoo tun awọn idii JS ati CSS ṣe laifọwọyi bi koodu ṣe yipada - kan sọ oju-iwe naa sọ lati rii gbogbo awọn ayipada!

3. Awọn aaye titẹ sii alabara

Jẹ ki a sọkalẹ lọ si koodu ere funrararẹ. Ni akọkọ a nilo oju-iwe kan index.html, nigbati o ba n ṣabẹwo si aaye naa, ẹrọ aṣawakiri yoo kọkọ ṣajọpọ rẹ. Oju-iwe wa yoo rọrun pupọ:

index.html

Ohun apẹẹrẹ .io game  ERE

Apeere koodu yii ti jẹ irọrun diẹ fun mimọ, ati pe Emi yoo ṣe kanna pẹlu ọpọlọpọ awọn apẹẹrẹ ifiweranṣẹ miiran. Awọn koodu ni kikun le nigbagbogbo wa ni bojuwo ni Github.

A ni:

  • HTML5 kanfasi ano (<canvas>) eyiti a yoo lo lati ṣe ere naa.
  • <link> lati ṣafikun package CSS wa.
  • <script> lati ṣafikun package JavaScript wa.
  • Akojọ aṣyn akọkọ pẹlu orukọ olumulo <input> ati bọtini PLAY (<button>).

Lẹhin ikojọpọ oju-iwe ile, ẹrọ aṣawakiri yoo bẹrẹ ṣiṣe koodu Javascript, bẹrẹ lati aaye titẹsi JS faili: src/client/index.js.

atọka.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);
  };
});

Eyi le dun idiju, ṣugbọn ko si ohun ti n lọ pupọ nibi:

  1. Gbigbe awọn faili JS lọpọlọpọ miiran wọle.
  2. Gbe wọle CSS (nitorina Webpack mọ lati fi wọn sinu package CSS wa).
  3. Запуск connect() lati fi idi kan asopọ pẹlu olupin ati ṣiṣe awọn downloadAssets() lati ṣe igbasilẹ awọn aworan ti o nilo lati ṣe ere naa.
  4. Lẹhin ipari ipele 3 akojọ aṣayan akọkọ ti han (playMenu).
  5. Ṣiṣeto olutọju fun titẹ bọtini "PLAY". Nigbati awọn bọtini ti wa ni e, initializes awọn koodu si awọn olupin ti a ba wa setan lati mu.

“Eran” akọkọ ti oye-ọrọ olupin-alabara wa wa ninu awọn faili wọnyẹn ti faili naa ko wọle index.js. Bayi a yoo ro gbogbo wọn ni ibere.

4. Paṣipaarọ data onibara

Ninu ere yii, a lo ile-ikawe olokiki kan lati ṣe ibasọrọ pẹlu olupin naa iho.io. Socket.io ni atilẹyin abinibi ayelujara iho, eyiti o baamu daradara fun ibaraẹnisọrọ ọna meji: a le fi awọn ifiranṣẹ ranṣẹ si olupin naa и olupin le fi awọn ifiranṣẹ ranṣẹ si wa lori asopọ kanna.

A yoo ni faili kan src/client/networking.jsti yoo toju gbogbo eniyan ibaraẹnisọrọ pẹlu olupin:

nẹtiwọki.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);
};

Yi koodu ti a ti tun kuru die-die fun wípé.

Awọn iṣe akọkọ mẹta wa ninu faili yii:

  • A n gbiyanju lati sopọ si olupin naa. connectedPromise nikan laaye nigba ti a ba ti iṣeto a asopọ.
  • Ti asopọ naa ba ṣaṣeyọri, a forukọsilẹ awọn iṣẹ ipe pada (processGameUpdate() и onGameOver()) fun awọn ifiranṣẹ ti a le gba lati olupin.
  • A okeere play() и updateDirection()ki awọn faili miiran le lo wọn.

5. Rendering onibara

O to akoko lati fi aworan han loju iboju!

…ṣugbọn ki a to le ṣe iyẹn, a nilo lati ṣe igbasilẹ gbogbo awọn aworan (awọn orisun) ti o nilo fun eyi. Jẹ ki a kọ oluṣakoso ohun elo kan:

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

Isakoso awọn oluşewadi kii ṣe pe o nira lati ṣe! Ero akọkọ ni lati tọju ohun kan assets, eyi ti yoo so bọtini orukọ faili mọ iye ohun naa Image. Nigbati awọn oluşewadi ti wa ni ti kojọpọ, a fipamọ sinu ohun kan assets fun awọn ọna wiwọle ni ojo iwaju. Nigbawo ni awọn orisun kọọkan yoo gba laaye lati ṣe igbasilẹ (iyẹn ni, gbogbo awọn orisun), a gba laaye downloadPromise.

Lẹhin igbasilẹ awọn orisun, o le bẹrẹ ṣiṣe. Gẹgẹbi a ti sọ tẹlẹ, lati fa lori oju-iwe wẹẹbu, a lo HTML5 kanfasi (<canvas>). Ere wa rọrun pupọ, nitorinaa a nilo lati fa atẹle wọnyi:

  1. Abẹlẹ
  2. Ọkọ ẹrọ orin
  3. Miiran awọn ẹrọ orin ni awọn ere
  4. Ikarahun

Eyi ni awọn snippets pataki src/client/render.js, eyi ti o ṣe deede awọn ohun mẹrin ti a ṣe akojọ loke:

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

Yi koodu ti wa ni tun kuru fun wípé.

render() jẹ iṣẹ akọkọ ti faili yii. startRendering() и stopRendering() ṣakoso imuṣiṣẹ ti lupu mu ṣiṣẹ ni 60 FPS.

Awọn imuṣẹ ni pato ti awọn iṣẹ oluranlọwọ onisọtọ (fun apẹẹrẹ renderBullet()) kii ṣe pataki, ṣugbọn eyi ni apẹẹrẹ ti o rọrun kan:

mú.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 akiyesi pe a nlo ọna naa getAsset(), eyi ti a ti ri tẹlẹ ninu asset.js!

Ti o ba nifẹ lati kọ ẹkọ nipa awọn oluranlọwọ ti n ṣe iranlọwọ, lẹhinna ka iyoku. src / onibara / render.js.

6. input ose

O to akoko lati ṣe ere kan playable! Ilana iṣakoso yoo rọrun pupọ: lati yi itọsọna gbigbe pada, o le lo Asin (lori kọnputa) tabi fọwọkan iboju (lori ẹrọ alagbeka). Lati ṣe eyi, a yoo forukọsilẹ Iṣẹlẹ Awọn olutẹtisi fun Asin ati Fọwọkan iṣẹlẹ.
Yoo toju gbogbo eyi src/client/input.js:

igbewọle.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() jẹ Awọn olutẹtisi Iṣẹlẹ ti o pe updateDirection() (lati networking.js) nigbati iṣẹlẹ titẹ sii ba waye (fun apẹẹrẹ, nigbati a ba gbe eku). updateDirection() n kapa fifiranṣẹ pẹlu olupin, eyiti o mu iṣẹlẹ titẹ sii ati ṣe imudojuiwọn ipo ere ni ibamu.

7. Onibara Ipo

Abala yii ni o nira julọ ni apakan akọkọ ti ifiweranṣẹ naa. Maṣe rẹwẹsi ti o ko ba loye rẹ ni igba akọkọ ti o ka! O le paapaa foju rẹ ki o pada si ọdọ rẹ nigbamii.

Awọn ti o kẹhin nkan ti awọn adojuru nilo lati pari awọn ose/koodu olupin jẹ ipinle. Ranti snippet koodu lati apakan Rendering Client?

mú.js

import { getCurrentState } from './state';

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

  // Do the rendering
  // ...
}

getCurrentState() yẹ ki o ni anfani lati fun wa ni awọn ti isiyi ipo ti awọn ere ni ose ni eyikeyi ojuami ni akoko da lori awọn imudojuiwọn gba lati olupin. Eyi ni apẹẹrẹ ti imudojuiwọn ere ti olupin le firanṣẹ:

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

Imudojuiwọn ere kọọkan ni awọn aaye kannaa marun:

  • t: Server timestamp nfihan igba ti imudojuiwọn yi da.
  • me: Alaye nipa ẹrọ orin gbigba imudojuiwọn yii.
  • awọn miran: Ohun orun ti alaye nipa miiran awọn ẹrọ orin kopa ninu kanna game.
  • awako: orun ti alaye nipa projectiles ni awọn ere.
  • adari: Lọwọlọwọ leaderboard data. Ninu ifiweranṣẹ yii, a kii yoo gbero wọn.

7.1 Naive ni ose ipinle

Naive imuse getCurrentState() le nikan taara pada awọn data ti awọn julọ laipe gba game imudojuiwọn.

òfo-ipinlẹ.js

let lastGameUpdate = null;

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

export function getCurrentState() {
  return lastGameUpdate;
}

O dara ati kedere! Ṣugbọn ti o ba jẹ pe o rọrun. Ọkan ninu awọn idi ti imuse yii jẹ iṣoro: o se idinwo awọn Rendering fireemu oṣuwọn to olupin aago oṣuwọn.

Iwọn fireemu: nọmba awọn fireemu (ie awọn ipe render()) fun iṣẹju-aaya, tabi FPS. Awọn ere nigbagbogbo ngbiyanju lati ṣaṣeyọri o kere ju 60 FPS.

Oṣuwọn ami si: Awọn igbohunsafẹfẹ ni eyi ti awọn olupin rán game awọn imudojuiwọn to ibara. Nigbagbogbo o kere ju iwọn fireemu lọ. Ninu ere wa, olupin naa n ṣiṣẹ ni igbohunsafẹfẹ ti awọn iyipo 30 fun iṣẹju kan.

Ti a ba kan mu imudojuiwọn tuntun ti ere naa, lẹhinna FPS kii yoo ni pataki ju 30 lọ, nitori a ko gba diẹ sii ju awọn imudojuiwọn 30 fun iṣẹju kan lati olupin naa. Paapa ti a ba pe render() Awọn akoko 60 fun iṣẹju kan, lẹhinna idaji awọn ipe wọnyi yoo kan tun ṣe ohun kanna, ni pataki ko ṣe nkankan. Iṣoro miiran pẹlu imuse ti o rọrun ni pe o prone to idaduro. Pẹlu iyara Intanẹẹti pipe, alabara yoo gba imudojuiwọn ere ni deede ni gbogbo 33ms (30 fun iṣẹju kan):

Ṣiṣẹda kan Pupọ .io Web Game
Laanu, ko si ohun ti o pe. Aworan ti o daju diẹ sii yoo jẹ:
Ṣiṣẹda kan Pupọ .io Web Game
Imuse ti o rọrun jẹ iṣe ọran ti o buru julọ nigbati o ba de lairi. Ti imudojuiwọn ere ba gba pẹlu idaduro 50ms, lẹhinna onibara ibùso afikun 50ms nitori pe o tun n funni ni ipo ere lati imudojuiwọn iṣaaju. O le fojuinu bawo ni eyi ṣe korọrun fun ẹrọ orin: braking lainidii yoo jẹ ki ere naa rilara ati riru.

7.2 Dara si ni ose ipinle

A yoo ṣe diẹ ninu awọn ilọsiwaju si imuse rọrun. Ni akọkọ, a lo idaduro Rendering fun 100 ms. Eyi tumọ si pe ipo “lọwọlọwọ” ti alabara nigbagbogbo yoo dinku lẹhin ipo ere lori olupin nipasẹ 100ms. Fun apẹẹrẹ, ti akoko lori olupin ba jẹ 150, lẹhinna alabara yoo funni ni ipo ti olupin naa wa ni akoko naa 50:

Ṣiṣẹda kan Pupọ .io Web Game
Eyi fun wa ni ifipamọ 100ms lati ye awọn akoko imudojuiwọn ere ti a ko sọ asọtẹlẹ:

Ṣiṣẹda kan Pupọ .io Web Game
Isanwo fun eyi yoo wa titi lailai aisun igbewọle fun 100 ms. Eyi jẹ irubọ kekere fun imuṣere oriire - pupọ julọ awọn oṣere (paapaa awọn oṣere lasan) kii yoo paapaa ṣe akiyesi idaduro yii. O rọrun pupọ fun eniyan lati ṣatunṣe si aiduro 100ms igbagbogbo ju ti o jẹ lati mu ṣiṣẹ pẹlu airi airotẹlẹ.

A tun le lo ilana miiran ti a npe ni ose-ẹgbẹ asọtẹlẹ, eyi ti o ṣe iṣẹ ti o dara lati dinku idinku ti a ti fiyesi, ṣugbọn kii yoo bo ni ipo yii.

Ilọsiwaju miiran ti a nlo ni laini interpolation. Nitori aisun Rendering, a nigbagbogbo jẹ o kere ju imudojuiwọn kan ṣaaju akoko lọwọlọwọ ni alabara. Nigbati a npe ni getCurrentState(), a le ṣiṣẹ laini interpolation laarin awọn imudojuiwọn ere ṣaaju ati lẹhin akoko lọwọlọwọ ni alabara:

Ṣiṣẹda kan Pupọ .io Web Game
Eyi yanju ọran oṣuwọn fireemu: a le ṣe awọn fireemu alailẹgbẹ ni oṣuwọn fireemu eyikeyi ti a fẹ!

7.3 Ṣiṣe imudara ipo alabara

Apẹẹrẹ imuse ni src/client/state.js nlo mejeeji jigbe aisun ati laini interpolation, sugbon ko fun gun. Jẹ ki a fọ ​​koodu naa si awọn ẹya meji. Eyi ni akọkọ:

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

Ni igba akọkọ ti Igbese ni lati ro ero ohun ti currentServerTime(). Gẹgẹbi a ti rii ni iṣaaju, gbogbo imudojuiwọn ere pẹlu akoko olupin olupin kan. A fẹ lati lo lairi lati mu aworan naa 100ms lẹhin olupin, ṣugbọn a kii yoo mọ akoko lọwọlọwọ lori olupin naa, nitori a ko le mọ bi o gun ti o gba fun eyikeyi ninu awọn imudojuiwọn lati gba si wa. Intanẹẹti jẹ aisọtẹlẹ ati iyara rẹ le yatọ pupọ!

Lati wa ni ayika isoro yi, a le lo a reasonable isunmọ: a dibọn akọkọ imudojuiwọn de lesekese. Ti eyi ba jẹ otitọ, lẹhinna a yoo mọ akoko olupin ni akoko pataki yii! A tọju awọn timestamp olupin sinu firstServerTimestamp ki o si pa wa mọ agbegbe (onibara) timestamp ni akoko kanna ni gameStart.

Oh duro. Ṣe ko yẹ ki o jẹ akoko olupin = akoko alabara? Kilode ti a fi ṣe iyatọ laarin "tamp olupin" ati "timestamp onibara"? Eyi jẹ ibeere nla! O wa ni pe wọn kii ṣe ohun kanna. Date.now() yoo da pada orisirisi timestamps ni ose ati olupin, ati awọn ti o da lori awọn okunfa agbegbe si awọn wọnyi ero. Maṣe ro pe awọn akoko akoko yoo jẹ kanna lori gbogbo awọn ẹrọ.

Bayi a loye ohun ti o ṣe currentServerTime(): o pada timestamp olupin ti akoko imupada lọwọlọwọ. Ni awọn ọrọ miiran, eyi ni akoko lọwọlọwọ olupin (firstServerTimestamp <+ (Date.now() - gameStart)) iyokuro idaduro idaduro (RENDER_DELAY).

Bayi jẹ ki ká wo ni bi a ti mu awọn imudojuiwọn ere. Nigbati o ba gba lati ọdọ olupin imudojuiwọn, o pe processGameUpdate()ati pe a fipamọ imudojuiwọn tuntun si opo kan gameUpdates. Lẹhinna, lati ṣayẹwo lilo iranti, a yọ gbogbo awọn imudojuiwọn atijọ kuro ṣaaju ipilẹ imudojuiwọnnitori a ko nilo wọn mọ.

Kini "imudojuiwọn ipilẹ"? Eyi imudojuiwọn akọkọ ti a rii nipa gbigbe sẹhin lati akoko lọwọlọwọ olupin naa. Ranti aworan atọka yii?

Ṣiṣẹda kan Pupọ .io Web Game
Imudojuiwọn ere taara si apa osi ti “Aago Tunṣe Onibara” jẹ imudojuiwọn ipilẹ.

Kini imudojuiwọn ipilẹ ti a lo fun? Kini idi ti a le fi awọn imudojuiwọn silẹ si ipilẹ? Lati ro ero eyi, jẹ ki a nipari ro imuse getCurrentState():

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

A ṣe itọju awọn ọran mẹta:

  1. base < 0 tumọ si pe ko si awọn imudojuiwọn titi di akoko ṣiṣe lọwọlọwọ (wo imuse loke getBaseUpdate()). Eleyi le ṣẹlẹ ọtun ni awọn ibere ti awọn ere nitori lati Rendering aisun. Ni idi eyi, a lo imudojuiwọn tuntun ti o gba.
  2. base jẹ imudojuiwọn to ṣẹṣẹ julọ ti a ni. Eyi le ṣẹlẹ nitori airi nẹtiwọki tabi asopọ intanẹẹti ti ko dara. Ni idi eyi, a tun nlo imudojuiwọn tuntun ti a ni.
  3. A ni imudojuiwọn mejeeji ṣaaju ati lẹhin akoko ṣiṣe lọwọlọwọ, nitorinaa a le interpolate!

Gbogbo nkan ti o ku ninu state.js jẹ imuse ti interpolation laini ti o rọrun (ṣugbọn alaidun) mathimatiki. Ti o ba fẹ lati ṣawari rẹ funrararẹ, lẹhinna ṣii state.js on Github.

Apá 2. Backend server

Ni apakan yii, a yoo wo ẹhin Node.js ti o ṣakoso wa .io ere apẹẹrẹ.

1. Server titẹsi Point

Lati ṣakoso olupin wẹẹbu, a yoo lo ilana wẹẹbu olokiki fun Node.js ti a pe han. Yoo jẹ tunto nipasẹ faili aaye titẹsi olupin wa src/server/server.js:

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

Ranti pe ni apakan akọkọ ti a sọrọ lori Webpack? Eyi ni ibi ti a yoo lo awọn atunto Webpack wa. A yoo lo wọn ni awọn ọna meji:

  • Lo webpack-dev-middleware lati tun awọn idii idagbasoke wa laifọwọyi, tabi
  • statically gbigbe folda dist/, sinu eyiti Webpack yoo kọ awọn faili wa lẹhin iṣelọpọ iṣelọpọ.

Iṣẹ pataki miiran server.js oriširiši eto soke olupin iho.ioeyi ti o kan sopọ si olupin Express:

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

Lẹhin ti iṣeto ni ifijišẹ socket.io asopọ si olupin naa, a ṣeto awọn olutọju iṣẹlẹ fun iho tuntun naa. Awọn oluṣakoso iṣẹlẹ mu awọn ifiranṣẹ ti a gba lati ọdọ awọn alabara lọwọ nipasẹ fifisilẹ si ohun kan ṣoṣo game:

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

A n ṣẹda ere .io, nitorinaa a nilo ẹda kan nikan Game ("Ere") - gbogbo awọn oṣere ṣere ni aaye kanna! Ni apakan ti o tẹle, a yoo rii bi kilasi yii ṣe n ṣiṣẹ. Game.

2. game apèsè

Класс Game ni awọn pataki kannaa lori olupin ẹgbẹ. O ni awọn iṣẹ akọkọ meji: player isakoso и ere kikopa.

Jẹ ká bẹrẹ pẹlu awọn akọkọ-ṣiṣe, player isakoso.

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

  // ...
}

Ninu ere yii, a yoo ṣe idanimọ awọn oṣere nipasẹ aaye id socket.io socket wọn (ti o ba ni idamu, lẹhinna pada si server.js). Socket.io funrararẹ n fun iho kọọkan ni alailẹgbẹ idnitorina a ko nilo lati ṣe aniyan nipa iyẹn. Emi yoo pe e ID ẹrọ orin.

Pẹlu iyẹn ni lokan, jẹ ki a ṣawari awọn oniyipada apẹẹrẹ ni kilasi kan Game:

  • sockets jẹ ohun ti o sopọ ID ẹrọ orin si iho ti o ni nkan ṣe pẹlu ẹrọ orin. O gba wa laaye lati wọle si awọn iho nipasẹ awọn ID ẹrọ orin wọn ni akoko igbagbogbo.
  • players jẹ ohun kan ti o sopọ ID ẹrọ orin si koodu>Ohun ẹrọ orin

bullets jẹ ẹya orun ti ohun Bullet, eyi ti o ni ko si definite ibere.
lastUpdateTime ni timestamp ti o kẹhin akoko awọn ere ti a imudojuiwọn. A yoo rii bi o ṣe nlo laipẹ.
shouldSendUpdate jẹ oluranlọwọ oniyipada. A yoo tun rii lilo rẹ laipẹ.
Awọn ọna addPlayer(), removePlayer() и handleInput() ko si ye lati se alaye, ti won ti wa ni lo ninu server.js. Ti o ba nilo lati sọ iranti rẹ sọtun, lọ sẹhin diẹ ga julọ.

Laini ikẹhin constructor() bẹrẹ soke imudojuiwọn ọmọ awọn ere (pẹlu igbohunsafẹfẹ ti awọn imudojuiwọn 60 / s):

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

  // ...
}

Ọna update() ni boya nkan pataki julọ ti ọgbọn ẹgbẹ olupin. Eyi ni ohun ti o ṣe, ni ibere:

  1. Iṣiro ohun ti akoko ti o jẹ dt koja niwon awọn ti o kẹhin update().
  2. Ntu kọọkan projectile ati run wọn ti o ba wulo. A yoo rii imuse ti iṣẹ yii nigbamii. Fun bayi o ti to fun wa lati mọ iyẹn bullet.update() pada trueti o ba ti projectile yẹ ki o run (o jade kuro ni gbagede).
  3. Mu kọọkan player ati spawns a projectile ti o ba wulo. A yoo tun rii imuse yii nigbamii - player.update() le da ohun kan pada Bullet.
  4. Sọwedowo fun collisions laarin projectiles ati awọn ẹrọ orin pẹlu applyCollisions(), eyi ti o pada ohun orun ti projectiles ti o lu awọn ẹrọ orin. Fun kọọkan projectile pada, a mu awọn ojuami ti awọn ẹrọ orin ti o kuro lenu ise (lilo player.onDealtDamage()) ati ki o si yọ awọn projectile lati orun bullets.
  5. Notifies ati ki o run gbogbo awọn ẹrọ orin pa.
  6. Fi imudojuiwọn ere ranṣẹ si gbogbo awọn oṣere gbogbo iṣẹju igba nigba ti a npe ni update(). Eyi ṣe iranlọwọ fun wa lati tọju oniyipada oniranlọwọ ti a mẹnuba loke. shouldSendUpdate. Bi update() ti a npe ni 60 igba / s, a fi game awọn imudojuiwọn 30 igba / s. Bayi, aago igbohunsafẹfẹ Aago olupin jẹ awọn aago 30 / s (a sọrọ nipa awọn oṣuwọn aago ni apakan akọkọ).

Kini idi ti o fi awọn imudojuiwọn ere ranṣẹ nikan nipasẹ akoko ? Lati fi ikanni pamọ. Awọn imudojuiwọn ere 30 fun iṣẹju keji jẹ pupọ!

Idi ti ko kan pe update() 30 igba fun keji? Lati mu kikopa ere dara si. Awọn diẹ igba ti a npe ni update(), deede diẹ sii kikopa ere yoo jẹ. Ṣugbọn maṣe gbe lọ pẹlu nọmba awọn italaya. update(), nitori eyi jẹ iṣẹ-ṣiṣe ti o gbowolori ni iṣiro - 60 fun iṣẹju kan ti to.

Awọn iyokù ti awọn kilasi Game ni awọn ọna oluranlọwọ ti a lo ninu update():

game.js apakan 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() lẹwa o rọrun - o lẹsẹsẹ awọn ẹrọ orin nipa Dimegilio, gba awọn oke marun, ati ki o pada awọn orukọ olumulo ati Dimegilio fun kọọkan.

createUpdate() lo ninu update() lati ṣẹda awọn imudojuiwọn ere ti o pin si awọn ẹrọ orin. Awọn oniwe-akọkọ-ṣiṣe ni lati pe awọn ọna serializeForUpdate()muse fun awọn kilasi Player и Bullet. Akiyesi pe o nikan koja data si kọọkan player nipa sunmọ julọ awọn oṣere ati awọn iṣẹ akanṣe - ko si iwulo lati atagba alaye nipa awọn nkan ere ti o jinna si ẹrọ orin naa!

3. Ere ohun lori olupin

Ninu ere wa, awọn ẹrọ akanṣe ati awọn oṣere jọra gaan: wọn jẹ áljẹbrà, yika, awọn nkan ere gbigbe. Lati lo anfani ti ibajọra yii laarin awọn oṣere ati awọn iṣẹ akanṣe, jẹ ki a bẹrẹ nipasẹ imuse kilasi mimọ Object:

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

Ko si ohun idiju ti o ṣẹlẹ nibi. Kilasi yii yoo jẹ aaye oran ti o dara fun itẹsiwaju naa. Jẹ ká wo bi awọn kilasi Bullet awọn lilo Object:

ọta ibọn.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;
  }
}

Imuse Bullet kukuru pupọ! A ti fi kun si Object nikan awọn amugbooro wọnyi:

  • Lilo package kan shortid fun ID iran id projectile.
  • Fifi aaye kan kun parentIDki o le orin ẹrọ orin ti o da yi projectile.
  • Fifi iye pada si update(), eyiti o dọgba si trueti o ba ti projectile ni ita arene (ranti a ti sọrọ nipa yi ni awọn ti o kẹhin apakan?).

Jẹ ki a lọ si Player:

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

Awọn oṣere jẹ eka sii ju awọn iṣẹ akanṣe, nitorinaa awọn aaye diẹ diẹ sii yẹ ki o wa ni ipamọ ni kilasi yii. Ọna rẹ update() ṣe kan pupo ti ise, ni pato, pada awọn rinle da projectile ti o ba ti nibẹ ni o wa kò osi fireCooldown (ranti a ti sọrọ nipa eyi ni apakan ti tẹlẹ?). Bakannaa o gbooro ọna naa serializeForUpdate(), nitori a nilo lati ni awọn aaye afikun fun ẹrọ orin ni imudojuiwọn ere.

Nini kilasi mimọ Object - ohun pataki igbese lati yago fun tun koodu. Fun apẹẹrẹ, ko si kilasi Object gbogbo ohun ere gbọdọ ni imuse kanna distanceTo(), ati daakọ-sọ gbogbo awọn imuse wọnyi kọja awọn faili lọpọlọpọ yoo jẹ alaburuku. Eyi di pataki paapaa fun awọn iṣẹ akanṣe nla.nigbati awọn nọmba ti jù Object awọn kilasi ti wa ni dagba.

4. Iwari ijamba

Awọn nikan ohun osi fun a da nigbati awọn projectiles lu awọn ẹrọ orin! Ranti nkan koodu yii lati ọna naa update() ninu kilasi Game:

ere.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),
    );

    // ...
  }
}

A nilo lati lo ọna naa applyCollisions(), eyi ti o pada gbogbo awọn projectiles ti o lu awọn ẹrọ orin. Ni Oriire, kii ṣe pe o nira lati ṣe nitori

  • Gbogbo awọn nkan ikọlu jẹ awọn iyika, eyiti o jẹ apẹrẹ ti o rọrun julọ lati ṣe iwari ijamba.
  • A ti ni ọna kan tẹlẹ distanceTo(), eyiti a ṣe ni apakan ti tẹlẹ ninu kilasi naa Object.

Eyi ni ohun ti imuse wiwa ikọlu wa dabi:

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

Wiwa ijamba ti o rọrun yii da lori otitọ pe awọn iyika meji kọlu ti aaye laarin awọn ile-iṣẹ wọn kere ju iye awọn radi wọn lọ. Eyi ni ọran nibiti aaye laarin awọn ile-iṣẹ ti awọn iyika meji jẹ deede deede si apao awọn radii wọn:

Ṣiṣẹda kan Pupọ .io Web Game
Awọn abala tọkọtaya diẹ sii wa lati ronu nibi:

  • Awọn projectile gbọdọ ko lu awọn ẹrọ orin ti o da o. Eyi le ṣee ṣe nipa ifiwera bullet.parentID с player.id.
  • Awọn projectile gbọdọ nikan lu ni kete ti ni awọn idiwon nla ti ọpọ awọn ẹrọ orin colliding ni akoko kanna. A yoo yanju iṣoro yii nipa lilo oniṣẹ break: ni kete ti ẹrọ orin colliding pẹlu awọn projectile ti wa ni ri, a da awọn àwárí ati ki o gbe lori si tókàn projectile.

ipari

Gbogbo ẹ niyẹn! A ti bo ohun gbogbo ti o nilo lati mọ lati ṣẹda ere wẹẹbu .io kan. Kini atẹle? Kọ ere .io tirẹ!

Gbogbo koodu ayẹwo wa ni ṣiṣi orisun ati firanṣẹ lori Github.

orisun: www.habr.com

Fi ọrọìwòye kun