Si numquam ante hos ludos audisti, liberi sunt, lusoribus interretialibus ludis faciles ad ludendum (nulla ratio requiritur). Solent in una arena fovere multos histriones repugnantes. Ludi celebres .io; Slither.io ΠΈ Diep.io.
In hoc post nos instar sicco quomodo creare per .io ludum a VULNUS. Ad hoc, sola cognitio Javascripti satis erit: res ut syntaxin intelligere debes ES6, keyword this ΠΈ promissa. Etiamsi Javascript perfecte non cognoscis, plus tamen intelligere potes.
Lusus est admodum simplex: navem in arena cum aliis lusoribus moderaris. Navis tua sponte projectilia accendit et alios histriones evitando proiectis ferire conaris.
Omnia in folder public/ stabiliter tradetur a servo. IN' public/assets/ imagines continet a nostro delineatio adhibitas.
sRC /
Omnis fons code est in folder src/. Π°Π·Π²Π°Π½ΠΈΡ client/ ΠΈ server/ dicere pro se et shared/ continentes fasciculi constantes ab utroque cliente et servo importati sunt.
2. Conventibus / project parametri
Ut dictum est, procurator utimur moduli ad opus faciendum Webpack. Vide nostram Webpack configuratione:
src/client/index.js punctum aculeus Javascript (JS) clientis est. Webpack hinc incipiet et recursively alia documenta importata quaere.
The output JS of our Webpack constructum erit in indicem dist/. Hanc limam appellabo nostram JS sarcina.
Utimur Babel,et in specie de configuratione @babel/preset-env ut nostrum codicem JS ad navigatores vetustiores traduceremus.
Plugino utimur ad omnes CSS referenced per JS lima extrahendum et eas in unum locum iungendum. Dicam nostrum CSS sarcina.
Ut notavi sarcina nova nomina file '[name].[contenthash].ext'. Continent filename substitutio Webpack: [name] reponendum erit cum nomine initus punctum (in nostro casu est game), Autem [contenthash] Nullam tabellariorum erit reponi cum contentis. Nos hoc facere optimize in project ad hashing - navigatores indicare possumus ut sarcinas nostras emittat JS quia indefinite si sarcina mutatur, eius nomen lima mutat (mutationes contenthash). Effectus effectus erit tabella sententiarum nomen game.dbeee76e91a97d0c7207.js.
lima webpack.common.js - Haec est basis configurationis fasciculi, quem in evolutione et perfecto schematis figurationibus importamus. Exempli gratia: hic explicatio schematismi est:
Ad efficientiam utimur in processu evolutionis webpack.dev.jsAc permutat webpack.prod.js, ad optimize magnitudinum sarcinarum tendentes ad productionem.
Locus setup
Commendo instituendo consilium in machina locali tui ut gradus in hoc poste recensiti sequi possis. Setup simplex est: primo, ratio habere debet nodi ΠΈ NPM. Deinde debes facere
$ git clone https://github.com/vzhou842/example-.io-game.git
$ cd example-.io-game
$ npm install
et ire parati estis! Incipere progressionem servo, modo currere
$ npm run develop
et vade ad navigatrum tuum localhost: 3000. Procurator progressus statim sarcinas JS et CSS reaedificabit sicut in codice mutationes occurrunt - modo refice paginam ad omnes mutationes vide!
3. Klients ingressum puncta
Descendamus ad ipsum codicem ludum. Primum opus est paginam index.htmlcum locum invisis, navigatrum illud prius oneres. Nostra pagina satis simplex erit;
index.html
Exemplum .io game PLAY
Exemplar hoc codice leviter ad evidenciam facilior est, et idem faciam cum multis aliis exemplis in fronte. Semper spectare potes ad plenam codice at Github.
Hoc sonare potest perplexum, sed non multum hic agitur;
Plures alias files JS importare.
Import CSS (ita Webpack novit ut eas includant in sarcina nostra CSS).
ΠΠ°ΠΏΡΡΠΊ connect() constituere nexum in calculonis servi ac satus downloadAssets() ad imagines detrahendas opus est ad ludum reddendum.
Peracta gradu III " elencho ostenditur (playMenu).
Prorex "fabula" button click tracto. Cum puga pyga premetur, signum ludum initialem facit et servo narrat nos parati sumus ad ludendum.
Praecipuum "cibum" logicae clientis nostri est in iis fasciculis quae tabella invecta sunt index.js. Nunc omnes ordine spectabimus.
4. Commutatio clientis notitia
In hoc ludo utimur bibliotheca nota vulgata ad communicandum cum servo socket.io. Socket.io aedificavit in subsidium WebSocketsquae bene apta sunt ad communicationem duarum viarum: nuntios mittere possumus servo ΠΈ Servus nuntios ad nos mittere potest per eundem nexum.
Unum file habebimus src/client/networking.jsqui cura ab omnibus communications cum servo:
Resource procuratio non est difficilis ad efficiendum! Praecipuum illud est condere aliquid assets, qui clavem filename ad valorem obiecti obligabit Image. Cum subsidium oneratur, servamus illud assets pro celeribus receptis in futurum. Quando singulae resource deprimendae erunt (id est, detrahebo) omnes facultates) concedimus downloadPromise.
Acceptis opibus, potes reddere. Ut supra dictum est, ad hauriendam in pagina interreti utimur HTML5 Canvas (<canvas>). Ludus noster est admodum simplex, ut solum sequentia reddere oporteat;
background
Ludio ludius navis
Alii lusores in ludo
Shells
Hic sunt momenti excerpta src/client/render.jsquae quatuor punctis supra recensitis exacte trahunt;
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);
}
Codex hic ad claritatem etiam abbreviatus est.
render() munus principale huius fasciculi est. startRendering() ΠΈ stopRendering() moderari activationem cycli reddendi ad 60 FPS.
Imprimis exsecutiones singularum functionum adiutorium reddendi (exempli gratia) renderBullet()) non magni momenti, sed unum simplex exemplum.
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,
);
}
Nota quod modum utendi sumus getAsset()quod prius visum est asset.js!
Si curae es in explorandis aliis functionibus adiutoris reddendis, tunc lege cetera src/client/render.js.
6. Client input
Aliquam ludum facere playable! Ratio moderatio valde simplex erit: directionem motus mutare, murem (in computatrale) uti potes vel velum (in mobili fabrica). Ad efficiendum hoc nos subcriptio res Listeners pro Mure et Tactu certe.
Haec omnia curabo src/client/input.js:
input.js
import { updateDirection } from './networking';
function onMouseInput(e) {
handleInput(e.clientX, e.clientY);
}
function onTouchInput(e) {
const touch = e.touches[0];
handleInput(touch.clientX, touch.clientY);
}
function handleInput(x, y) {
const dir = Math.atan2(x - window.innerWidth / 2, window.innerHeight / 2 - y);
updateDirection(dir);
}
export function startCapturingInput() {
window.addEventListener('mousemove', onMouseInput);
window.addEventListener('touchmove', onTouchInput);
}
export function stopCapturingInput() {
window.removeEventListener('mousemove', onMouseInput);
window.removeEventListener('touchmove', onTouchInput);
}
onMouseInput() ΠΈ onTouchInput() sunt Event auditores vocationis updateDirection() (de networking.js) cum initus evenit (exempli gratia cum mure movetur). updateDirection() commercium epistularum cum servo agit, quod eventum inputationis processit et statum lusum updates renovat.
7. Client status
Difficillima est haec sectio in prima parte poste. Noli esse pusillanimes, si primum illud legisti non intellegis! Etiam illud praeterire potes et ad illud postea redi.
Ultima pars puzzle opus perficere client-servo codice is statum. Mementote codicem PRAECISIO e Client Reddendo sectionem?
render.js
import { getCurrentState } from './state';
function render() {
const { me, others, bullets } = getCurrentState();
// Do the rendering
// ...
}
getCurrentState() nobis praebere possit in re publica vena lusus in clientelam umquam secundum updates accepi a calculonis servi. En exemplum ludi renovationis ut minister mitteret:
t: Servo indicatione temporis designata haec renovatio creata est.
me: Informationes de ludio ludius accipiendo hanc renovationem.
alii: Ordinatio informationum circa alios lusores in eodem ludo participando.
indicibus: ordinata informationum de projectionibus in ludo.
leaderboard: Praesens data auctrix. Nos eos in hac statione non inputamus.
7.1 Client scriptor simplicem statum
Simplex exsecutionem getCurrentState() nonnisi directe notitias ex recentissimo ludo renovationis receptas referre potest.
rustica-state.js
let lastGameUpdate = null;
// Handle a newly received game update.
export function processGameUpdate(update) {
lastGameUpdate = update;
}
export function getCurrentState() {
return lastGameUpdate;
}
Pulchra et clara! sed si modo esset ille simplex. Una e causis haec exsecutio est problematica; determinat ad ratem reddendam frame servo celeritas horologii.
Modus imaginis: numerus tabularum (i.e. vocat render()) secundo, vel FPS. Ludi plerumque contendunt ut saltem 60 FPS.
Tick ββββRate: Frequentia quacum servo updates ludum mittit ad clientes. Est saepe humiliores superficie frame rate. In ludo nostro, servo ricini per alterum ad 30 currit.
Si iustam renovationem lusus recentem reddemus, FPS essentialiter numquam 30 excedere poterit, quia nos non plus quam XXX updates per secundam a servo. Etiam si vocamus render() 60 vicibus secundo, medium horum vocat simpliciter redraxabit idem, essentialiter nihil faciens. Alterum problema de simplici exsecutione est sub mora. Ad idealem Interreti celeritatem, client lusum renovationis prorsus omnem 33 ms accipiet (30 per secundam);
Donec non purus nulla. Verior pictura esset;
Simplex exsecutio est fere pessimum casum cum latency adveniens. Si ludus renovatio recipitur cum 50ms mora, tunc in clientis retardari ab extra 50ms quia adhuc statum ludum e renovatione praecedente reddens. Potes fingere quam incommodum sit histrionis: propter arbitrariam tarditates, ludus hiulcus et instabilis videbuntur.
7.2 Melior status clientis
Aliquot emendationes dabimus ad simplicem exsecutionem. Uno modo utimur reddens mora ex C ma. Id significat statum clientis semper 100ms esse post ludum civitatis in calculonis servi. Exempli gratia, si servo tempus est 150, cliens rempublicam reddet , in qua tunc temporis minister erat 50:
Hoc nobis 100ms quiddam praebet ut inaestimabile leo ludi updates:
Pretium huius rei perpetuum erit initus lag ex C ma. Hoc sacrificium est minoris pro lusu levi - plerique histriones (praesertim casuales) ne moras quidem animadvertunt. Multo facilius est homines ad constantem 100ms latentiam accommodare quam cum inopinata latency ludere.
Alia ars uti possumus "clientem-partem forecasting"quae bene deminuendi latencem percipit, sed in hac statione non discutietur.
Alius melius utimur est linearibus interpolationem. Propter pigritiam reddendam, solemus unum saltem renovatio ante tempus hodiernum in clientelam. cum dicitur getCurrentState(), implere possumus linearibus interpolationem inter ludum updates mox ante et post current tempus in clientelam;
Hoc problema solvendum replo- sationem solvit: nunc singulas tabulas reddere possumus quovis rato pacto nobis opus!
7.3 an meliorem statum clientem deducentes
Exemplum exsecutionis in src/client/state.js utitur utraque mora et interpolatione lineari, sed hoc non diuturnum. In duas partes codicem rumpamus. Hic est primum:
state.js, part 1
const RENDER_DELAY = 100;
const gameUpdates = [];
let gameStart = 0;
let firstServerTimestamp = 0;
export function initState() {
gameStart = 0;
firstServerTimestamp = 0;
}
export function processGameUpdate(update) {
if (!firstServerTimestamp) {
firstServerTimestamp = update.t;
gameStart = Date.now();
}
gameUpdates.push(update);
// Keep only one game update before the current server time
const base = getBaseUpdate();
if (base > 0) {
gameUpdates.splice(0, base);
}
}
function currentServerTime() {
return firstServerTimestamp + (Date.now() - gameStart) - RENDER_DELAY;
}
// Returns the index of the base update, the first game update before
// current server time, or -1 if N/A.
function getBaseUpdate() {
const serverTime = currentServerTime();
for (let i = gameUpdates.length - 1; i >= 0; i--) {
if (gameUpdates[i].t <= serverTime) {
return i;
}
}
return -1;
}
Primum illud quod debes facere est instar ex eo quod facit currentServerTime(). Ut antea vidimus, omnis renovatio ludi servo indicationem includit. Latitudinem reddere volumus ut imaginem 100morum post servo reddendam esse volumus, sed numquam scire current tempus in calculonis serviquia nescimus quantum temporis ad nos pervenerit ad quemlibet ex updates pervenire. Interreti vagus est et celeritas eius valde variari potest!
Ut circa hanc problema, approximatione rationabili uti possumus: nos lets simulare primum update venit statim. Si hoc verum esset, tunc temporis calculonis servi sciremus! Condimus servo indicatione in firstServerTimestamp et salvare nostrum loci (Cliens) indicatione temporis in eodem tempore gameStart.
Oh, expecta paulisper. Non debet tempus esse in servo = tempus in cliente est? Cur distinguimus inter "servum indicationem" et "per indicationem clientis"? Magna quaestio haec est! Hoc non idem evenit. Date.now() varia indicia reddet in cliente et servo et hoc pendet a factoribus localibus ad has machinas. Numquam id in omnibus machinis indicatione temporis idem erit.
Nunc intelligimus quid agat currentServerTime(): redit server indicatione temporis de current reddens tempore. Aliis verbis, hoc est tempus currentis server (firstServerTimestamp <+ (Date.now() - gameStart)) Minus mora reddens (RENDER_DELAY).
Nunc inspiciamus quomodo updates lusum tractamus. Cum renovatio a servo recipitur, appellatur processGameUpdate()et novam renovationem ad ordinem servamus gameUpdates. Deinde, ad usum memoriae reprimendum, omnes veteres updates to removemus basis renovatioquia eis amplius non opus est.
Quid est "cor renovatio"? Hoc primam renovationem invenimus movendo retrorsum ab hodierna servo tempore. Memento huius schematis?
Ludus update directe ad sinistram "Client Render Time" est basis renovationis.
Quid est basis update propter? Quid possumus updates ut basis stilla? Ut hoc notum sit, scriptor tandem Intueamur exsequendam getCurrentState():
state.js, part 2
export function getCurrentState() {
if (!firstServerTimestamp) {
return {};
}
const base = getBaseUpdate();
const serverTime = currentServerTime();
// If base is the most recent update we have, use its state.
// Else, interpolate between its state and the state of (base + 1).
if (base < 0) {
return gameUpdates[gameUpdates.length - 1];
} else if (base === gameUpdates.length - 1) {
return gameUpdates[base];
} else {
const baseUpdate = gameUpdates[base];
const next = gameUpdates[base + 1];
const r = (serverTime - baseUpdate.t) / (next.t - baseUpdate.t);
return {
me: interpolateObject(baseUpdate.me, next.me, r),
others: interpolateObjectArray(baseUpdate.others, next.others, r),
bullets: interpolateObjectArray(baseUpdate.bullets, next.bullets, r),
};
}
}
Tres casus tractamus:
base < 0 significat nullas esse updates dum temporis praesentis reddendi (vide supra exsecutionem getBaseUpdate()). Hoc contingere potest statim in initio ludi ob TARDO reddendo. In hoc casu recentissima renovatio recepta utimur.
base recentissimus est update habemus. Hoc contingere potest ob latentiam retis vel interretialem interretialem connexionem. In hoc quoque casu utimur recentissima renovatione quam habemus.
Renovatio habemus tam ante quam post hodiernam tempus reddere, ut possumus interpolate!
Quidquid reliquit in state.js est exsecutio interpolationis linearis simplicis (sed taedi) math. Si vis ipsum explorare, aperi state.js on Github.
Ad gubernationem interretialem server utemur populari compage pro Node.js vocati express. a nostro servo viscus punctus file configurabitur src/server/server.js:
server.js, part 1
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackConfig = require('../../webpack.dev.js');
// Setup an Express server
const app = express();
app.use(express.static('public'));
if (process.env.NODE_ENV === 'development') {
// Setup Webpack for development
const compiler = webpack(webpackConfig);
app.use(webpackDevMiddleware(compiler));
} else {
// Static serve the dist/ folder in production
app.use(express.static('dist'));
}
// Listen on port
const port = process.env.PORT || 3000;
const server = app.listen(port);
console.log(`Server listening on port ${port}`);
Memento quod in prima parte Webpack egimus? Hoc est ubi figurationibus Webpack nostris utemur. Dupliciter applicabimus eas;
ad usum webpack-dev-media ut sponte nostram progressionem reficere packages, seu "
Statically transferre folder dist/in quas Webpack scribet tabularia nostra post fabricationem fabricandi.
Alius momenti negotium server.js ex servo usque ad constituendum socket.ioquae simpliciter coniungit cum servo Express:
Postquam feliciter nexum socket.io constituendum cum servo, res tracto pro novis nervum configuramus. Eventus tracto processus nuntii ab clientibus acceptis ad objectum singleton game:
server.js, part 3
const Game = require('./game');
// ...
// Setup the Game
const game = new Game();
function joinGame(username) {
game.addPlayer(this, username);
}
function handleInput(dir) {
game.handleInput(this, dir);
}
function onDisconnect() {
game.removePlayer(this);
}
Ludum .io creamus, uno tantum exemplari egebimus Game ("Ludus") - omnes lusores in eadem arena ludunt! In altera sectione videbimus quomodo hoc genus opera sit Game.
2. Ludus servers
ΠΠ»Π°ΡΡ Game maxime momenti servo latus continet logicam. Duo praecipua opera habet: ludio ludius procuratio ΠΈ ludum simulation.
Incipiamus a primo munere β histriones administrandi.
game.js, part 1
const Constants = require('../shared/constants');
const Player = require('./player');
class Game {
constructor() {
this.sockets = {};
this.players = {};
this.bullets = [];
this.lastUpdateTime = Date.now();
this.shouldSendUpdate = false;
setInterval(this.update.bind(this), 1000 / 60);
}
addPlayer(socket, username) {
this.sockets[socket.id] = socket;
// Generate a position to start this player at.
const x = Constants.MAP_SIZE * (0.25 + Math.random() * 0.5);
const y = Constants.MAP_SIZE * (0.25 + Math.random() * 0.5);
this.players[socket.id] = new Player(socket.id, username, x, y);
}
removePlayer(socket) {
delete this.sockets[socket.id];
delete this.players[socket.id];
}
handleInput(socket, dir) {
if (this.players[socket.id]) {
this.players[socket.id].setDirection(dir);
}
}
// ...
}
In hoc ludo lusores ex agro cognoscemus id nervum eorum socket.io (si confusi, revertere ad server.js). Socket.io ipsa singula nervum singularem assignat id, ne de eo solliciti sitis . Vocabo eum Ludio ludius ID.
Hoc animo perscrutemur instantia variabilium in genere Game:
sockets obiectum est quod ligat lusor ID ad nervum quod cum scaenicis coniungitur. Accessum ad bases per ids lusoriis per tempus nobis concedit.
players objectum ligat ludio id in codice> ludius objectum
bullets est ordinata obiectorum Bulletnon habens certum ordinem. lastUpdateTime - Hoc indicatione temporis ultimi ludi renovationis. Videbimus quomodo mox usus est. shouldSendUpdate auxilia variabilis est. Usum quoque ejus mox videbimus.
modi addPlayer(), removePlayer() ΠΈ handleInput() non opus est explicare, adhibita sunt server.js. Si refrigerio opus est, paulo altius repete.
Ultima linea constructor() incipit in update exolvuntur ludi (cum frequentia 60 updates/s);
game.js, part 2
const Constants = require('../shared/constants');
const applyCollisions = require('./collisions');
class Game {
// ...
update() {
// Calculate time elapsed
const now = Date.now();
const dt = (now - this.lastUpdateTime) / 1000;
this.lastUpdateTime = now;
// Update each bullet
const bulletsToRemove = [];
this.bullets.forEach(bullet => {
if (bullet.update(dt)) {
// Destroy this bullet
bulletsToRemove.push(bullet);
}
});
this.bullets = this.bullets.filter(
bullet => !bulletsToRemove.includes(bullet),
);
// Update each player
Object.keys(this.sockets).forEach(playerID => {
const player = this.players[playerID];
const newBullet = player.update(dt);
if (newBullet) {
this.bullets.push(newBullet);
}
});
// Apply collisions, give players score for hitting bullets
const destroyedBullets = applyCollisions(
Object.values(this.players),
this.bullets,
);
destroyedBullets.forEach(b => {
if (this.players[b.parentID]) {
this.players[b.parentID].onDealtDamage();
}
});
this.bullets = this.bullets.filter(
bullet => !destroyedBullets.includes(bullet),
);
// Check if any players are dead
Object.keys(this.sockets).forEach(playerID => {
const socket = this.sockets[playerID];
const player = this.players[playerID];
if (player.hp <= 0) {
socket.emit(Constants.MSG_TYPES.GAME_OVER);
this.removePlayer(socket);
}
});
// Send a game update to each player every other time
if (this.shouldSendUpdate) {
const leaderboard = this.getLeaderboard();
Object.keys(this.sockets).forEach(playerID => {
const socket = this.sockets[playerID];
const player = this.players[playerID];
socket.emit(
Constants.MSG_TYPES.GAME_UPDATE,
this.createUpdate(player, leaderboard),
);
});
this.shouldSendUpdate = false;
} else {
this.shouldSendUpdate = true;
}
}
// ...
}
modum update() probabiliter maxima pars logicae ministrantis continet. Indicemus omnia quae agat ordine:
Computat quod tempus est dt suus 'been ab ultimo update().
Unumquodque proiectum reficit et, si necesse est, destruit. Exsecutionem huius functionis postea videbimus. Nunc enim satis est nobis scire bullet.update()redit truesi proiectum delendum est (Exiit arenam).
Quisque ludio ludius renovat et proiectum, si necesse est, creat. Hanc exsecutionem postea etiam videbimus - player.update()redire potest objectum Bullet.
Checks in concursu inter proiectos et histriones utens applyCollisions()quae reddit agmine telorum pulsantium. Pro unoquoque reddito proiectum augemus ustulo lusoris qui illum incendit (utendo" player.onDealtDamage()) Et remove proiectum ab ordinata bullets.
Notificat et destruit omnes histriones occidit.
Ludum update omnibus histriones mittit omne secundo quando dicitur update(). Auxiliaris variabilis, de qua supra, hanc indagare nos adiuvat shouldSendUpdate. As update() appellatus 60 times/s, lusus updates mittimus 30 times/s. Sic, horologium frequency server cyclos horologii 30/s (de horologii frequentia in prima parte locuti sumus).
Cur mitto ludum updates only per tempus ? Ut in varius sapien. 30 updates per secundam ludum multum est!
Cur ergo non tantum vocas? update() XXX vicibus secundo? Ad meliorem ludum simulationem. Quod saepius dicitur update()quanto accuratior simulatio lusus erit. Sed non nimis rapiuntur numero provocationum update()quoniam haec computationally sumptuosa est β 60 secundo satis est.
Reliqua classis Game ex adiutorio modi usus est update():
getLeaderboard() Suus 'pulchellus simplex est β scaenicorum genera per score, summum quinque capit, et usoris et ustulo pro singulis reddit.
createUpdate() in update() ad ludum creare updates qui lusoribus distribuuntur. Praecipuum opus est vocare modos serializeForUpdate(), implemented for classes Player ΠΈ Bullet. Nota quod notitias tantum transmittit ad unumquemque ludionum de proxima histriones et missilia - non opus est ut informationes transmittant de obiectis venationibus longe ab histrionum locatis!
3. Ludus obiecti in calculonis servi
In ludo nostro, missilia et histriones sunt actu simillima: abstracta sunt circa objecta ludi mobilia. Ut hac similitudine utamur inter histriones et missilia, incipiamus turpissimum genus efficere Object:
Nihil complicatum hic geritur. Hoc genus bonum dilatationis initium erit. Videamus quomodo classis Bullet usus Object:
bullet.js
const shortid = require('shortid');
const ObjectClass = require('./object');
const Constants = require('../shared/constants');
class Bullet extends ObjectClass {
constructor(parentID, x, y, dir) {
super(shortid(), x, y, dir, Constants.BULLET_SPEED);
this.parentID = parentID;
}
// Returns true if the bullet should be destroyed
update(dt) {
super.update(dt);
return this.x < 0 || this.x > Constants.MAP_SIZE || this.y < 0 || this.y > Constants.MAP_SIZE;
}
}
Π Π΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ Bullet brevissimum! Adiecimus Object modo sequentes extensiones;
Uti ad sarcina shortid nam temere generation id proiectum.
Addit agro parentIDut indagare possis histrionem qui hoc proiectum creavit.
Addit reditus valorem ad update()quod est aequalis truesi proiectum extra arenam est (de hoc superiore sectione meminimus).
Lets proficiscantur Player:
player.js
const ObjectClass = require('./object');
const Bullet = require('./bullet');
const Constants = require('../shared/constants');
class Player extends ObjectClass {
constructor(id, username, x, y) {
super(id, x, y, Math.random() * 2 * Math.PI, Constants.PLAYER_SPEED);
this.username = username;
this.hp = Constants.PLAYER_MAX_HP;
this.fireCooldown = 0;
this.score = 0;
}
// Returns a newly created bullet, or null.
update(dt) {
super.update(dt);
// Update score
this.score += dt * Constants.SCORE_PER_SECOND;
// Make sure the player stays in bounds
this.x = Math.max(0, Math.min(Constants.MAP_SIZE, this.x));
this.y = Math.max(0, Math.min(Constants.MAP_SIZE, this.y));
// Fire a bullet, if needed
this.fireCooldown -= dt;
if (this.fireCooldown <= 0) {
this.fireCooldown += Constants.PLAYER_FIRE_COOLDOWN;
return new Bullet(this.id, this.x, this.y, this.direction);
}
return null;
}
takeBulletDamage() {
this.hp -= Constants.BULLET_DAMAGE;
}
onDealtDamage() {
this.score += Constants.SCORE_BULLET_HIT;
}
serializeForUpdate() {
return {
...(super.serializeForUpdate()),
direction: this.direction,
hp: this.hp,
};
}
}
Histriones magis implicati quam missiles sunt, ideo hoc genus paucos agros magis condere debet. eius methodum update() plus operatur, in speciali reverso proiecto novo creato, si desunt nulli supersunt fireCooldown (meminimus de hoc in praecedenti articulo locuti sumus?). Etiam modum extendit serializeForUpdate()propterea quod additos agros pro lusori in ludo renovationis includere oportet.
Availability of a base class Object - magni gradus ad vitare codice repetitio. Exempli gratia, sine classe Object omne ludum quod idem exsecutionem distanceTo()et omnia haec exsecutiones per multiplices tabulas exscribere visio nocturna esset. Hoc fit maxime momenti pro magnis inceptisCum numerus expanding Object genera nascuntur.
4. Occursum deprehendatur
Sola res superest ut cognoscamus cum proiecta histriones feriunt! Memento huius codicis PRAECISIO ex methodo update() in genere Game:
game.js
const applyCollisions = require('./collisions');
class Game {
// ...
update() {
// ...
// Apply collisions, give players score for hitting bullets
const destroyedBullets = applyCollisions(
Object.values(this.players),
this.bullets,
);
destroyedBullets.forEach(b => {
if (this.players[b.parentID]) {
this.players[b.parentID].onDealtDamage();
}
});
this.bullets = this.bullets.filter(
bullet => !destroyedBullets.includes(bullet),
);
// ...
}
}
Opus efficiendi modum applyCollisions()quae reddit omnia proiecta pulsantia. Fortunate, hoc non difficile est facere
Omnia objecta concurrentia sunt circuli, et haec figura simplicissima est ad concursum deprehendendum deducendi.
Nos iam modum distanceTo(), quam in priori sectione inseruimus Object.
Haec nostra deprehensio concursus colliculorum similis est:
collisiones.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;
}
Hoc simplex occursum deprehensio fundatur in eo quod duo circuli inter se colliduntur, si distantia centra ipsorum minor est quam summa earum radiorum. Hic casus est ubi distantia centra duorum circulorum prorsus aequalis est summae radiorum eorum;
Hic debes animadvertere duobus aspectibus pluribus:
Proiectum ludio ludius qui eum creavit non debet. Hoc potest fieri per comparationem bullet.parentID Ρ player.id.
Proiectum solum semel in extrema casu feriendi plures lusores simul ferire debet. Hanc quaestionem solvemus usura operator breakCum histrio cum proiecto impactus est inventus, quaerendo cessamus et progredimur ad proximum proiectum.
finis
Ita est! Perteximus omnia quae scire debes creare ludum interretialem .io. Quid suus 'postero? .io ludum pro- prium construe!
Omne exemplum codicem fons apertum est et on missum Github.