Construiți primul joc cu HTML5

HTML5 este în creștere mai repede decât oricine ar fi putut imagina. Soluții puternice și profesionale sunt deja dezvoltate? chiar și în lumea jocurilor! Check out sutele de jocuri HTML5 de pe Envato Market.

Astăzi, veți face primul joc utilizând Box2D și HTML5 pânză etichetă.


Ce este Box2D?

Box2D este un motor open source și popular care simulează fizica 2D pentru a face jocuri și aplicații. Mai întâi scris în C ++, a fost convertit în numeroase limbi de către contribuabili din comunitate.

Cu aceleași metode și obiecte, aveți posibilitatea să faceți fizica jocurilor în mai multe limbi, cum ar fi Obiectiv C (iPhone / iPad), Actionscript 3.0 (Web), HTML 5 (Web), etc.


Pasul 1 - Configurarea proiectului

Pentru a începe să vă dezvoltați demo-ul, descărcați aici motorul Box2D pentru HTML5. Apoi, creați un nou fișier HTML cu următoarea structură (copiați directoarele js și lib din proiectul box2d-js în dosarul dvs. de joc).

Acum, trebuie să introduceți fișierele necesare pentru a rula box2D în fișierul HTML:

                                                                  

Da, acesta este un număr mare de cereri HTTP!

Rețineți că, pentru desfășurare, este foarte recomandat să concatenați toate aceste resurse într-una singură scenariu fişier.

Apoi, creați încă două script-uri în interiorul / Js / dosarul, numit "box2dutils.js" și "game.js".

  • box2dutils.js - este o copie și lipire de la unele demo-uri care vin cu box2dlib, și este important pentru desenarea funcțiilor (voi explica, de asemenea, câteva părți importante aici).
  • game.js - jocul, în sine; acesta este locul unde creăm platformele, playerul, aplicăm interacțiunile tastaturii etc..

Copiați și lipiți următorul cod box2dutils.js. Nu vă faceți griji! O să-i explic puțin câte puțin!

funcția drawWorld (lumea, contextul) pentru (var j = world.m_jointList; j; j = j.m_next) tragereJoint (j, context);  pentru (var b = lume.m_bodyList; b; b = b.m_next) pentru (var s = b.GetShapeList (); s! = null; s = s.GetNext ;  funcția drawJoint (articulație, context) var b1 = joint.m_body1; var b2 = joint.m_body2; var x1 = poziția b1.m; var x2 = poziția b2.m; var p1 = comun.GetAnchor1 (); var p2 = comun.GetAnchor2 (); context.strokeStyle = '# 00eeee'; context.beginPath (); switch (comun.m_type) caz b2Joint.e_distanceJoint: context.moveTo (p1.x, p1.y); context.lineTo (p2.x, p2.y); pauză; cazul b2Joint.e_pulleyJoint: // TODO break; implicit: dacă (b1 == world.m_groundBody) context.moveTo (p1.x, p1.y); context.lineTo (x2.x, x2.y);  altfel dacă (b2 == world.m_groundBody) context.moveTo (p1.x, p1.y); context.lineTo (x1.x, x1.y);  altfel context.moveTo (x1.x, x1.y); context.lineTo (p1.x, p1.y); context.lineTo (x2.x, x2.y); context.lineTo (p2.x, p2.y);  pauză;  context.stroke ();  funcție drawShape (formă, context) context.strokeStyle = '# 000000'; context.beginPath (); comutator (shape.m_type) caz b2Shape.e_circleShape: var circle = forma; var pos = circle.m_position; var r = circle.m_radius; var = 16,0; var theta = 0,0; var dtheta = 2.0 * Math.PI / segmente; // atrage cercul context.moveTo (pos.x + r, pos.y); pentru (var i = 0; i < segments; i++)  var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta)); var v = b2Math.AddVV(pos, d); context.lineTo(v.x, v.y); theta += dtheta;  context.lineTo(pos.x + r, pos.y); // draw radius context.moveTo(pos.x, pos.y); var ax = circle.m_R.col1; var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y); context.lineTo(pos2.x, pos2.y);  break; case b2Shape.e_polyShape:  var poly = shape; var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0])); context.moveTo(tV.x, tV.y); for (var i = 0; i < poly.m_vertexCount; i++)  var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i])); context.lineTo(v.x, v.y);  context.lineTo(tV.x, tV.y);  break;  context.stroke();  function createWorld()  var worldAABB = new b2AABB(); worldAABB.minVertex.Set(-1000, -1000); worldAABB.maxVertex.Set(1000, 1000); var gravity = new b2Vec2(0, 300); var doSleep = true; var world = new b2World(worldAABB, gravity, doSleep); return world;  function createGround(world)  var groundSd = new b2BoxDef(); groundSd.extents.Set(1000, 50); groundSd.restitution = 0.2; var groundBd = new b2BodyDef(); groundBd.AddShape(groundSd); groundBd.position.Set(-500, 340); return world.CreateBody(groundBd)  function createBall(world, x, y)  var ballSd = new b2CircleDef(); ballSd.density = 1.0; ballSd.radius = 20; ballSd.restitution = 1.0; ballSd.friction = 0; var ballBd = new b2BodyDef(); ballBd.AddShape(ballSd); ballBd.position.Set(x,y); return world.CreateBody(ballBd);  function createBox(world, x, y, width, height, fixed, userData)  if (typeof(fixed) == 'undefined') fixed = true; var boxSd = new b2BoxDef(); if (!fixed) boxSd.density = 1.0; boxSd.userData = userData; boxSd.extents.Set(width, height); var boxBd = new b2BodyDef(); boxBd.AddShape(boxSd); boxBd.position.Set(x,y); return world.CreateBody(boxBd) 

Pasul 2 - Dezvoltarea jocului

Deschide index.html fișier pe care l-ați creat anterior și adăugați a pânză element (600x400) în cadrul corp element. Aici vom lucra cu API-ul de desen HTML5:

De asemenea, în timp ce sunteți aici, referință game.js și box2dutils.js.

 

Asta va face pentru HTML! Să lucrăm la distractivul JavaScript acum!

Deschis game.js, și introduceți codul de mai jos:

// unele variabile pe care le vom folosi în această demonstrație var initId = 0; var player = funcția () this.object = null; this.canJump = false; ; var lume; var ctx; var canvasWidth; var canvasHeight; tastele var = = []; // HTML5 onLoad eveniment Event.observe (fereastra, 'load', function () world = createWorld (); // box2DWorld ctx = $ ('joc') getContext (2d) $ canvasWidth = parseInt (canvasElm.width); canvasHeight = parseInt (canvasElm.height); initGame (); // 3 step (); // 4 // 5 window.addEventListener ('keydown' handleKeyDown, true); window.addEventListener ('keyup', handleKeyUp, true););

Box2DWorld - de aceea suntem aici

Bine, să ne dăm seama ce face această bucată de cod!

Box2DWorld este una dintre clasele care este pusă la dispoziție, prin intermediul nucleului box2d. Funcția sa este simplă: combinați Tot într-o clasă. În box2DWorld, aveți definirea corpurilor și managerul de coliziuni al jocului sau al aplicației.

Păstrează game.js și box2dutils.js fișierele deschise și căutați createWorld () funcția în interiorul box2dutils.js.

funcția createWorld () // aici creăm setările noastre globale pentru coliziuni var worldAABB = new b2AABB (); worldAABB.minVertex.Set (-1000, -1000); worldAABB.maxVertex.Set (1000, 1000); // set vector gravitațional var gravitatea = nou b2Vec2 (0, 300); var doSleep = adevărat; // init lumea noastra si returneaza valoarea ei var = nou b2World (worldAABB, gravitate, doSleep); lumea întoarcerii; 

Este foarte simplu să creați box2DWorld.


Înapoi la game.js

Consultați numerele comentate din cele două blocuri de cod de mai sus. Pe numărul doi, vom prelua pânză elementul prin folosirea selectorului API (seamănă cu selectorii jQuery sau MooTools, nu?). Pe locul trei, avem o nouă funcție interesantă: initGame (). Aici creăm peisajul.

Copiați și inserați codul de mai jos în game.js, și apoi vom examina împreună.

funcția initGame () // crea 2 platforme mari createBox (lume, 3, 230, 60, 180, true, 'teren'); createBox (lume, 560, 360, 50, 50, adevărat, "teren"); // creați platforme mici pentru (var i = 0; i < 5; i++) createBox(world, 150+(80*i), 360, 5, 40+(i*15), true, 'ground');  // create player ball var ballSd = new b2CircleDef(); ballSd.density = 0.1; ballSd.radius = 12; ballSd.restitution = 0.5; ballSd.friction = 1; ballSd.userData = 'player'; var ballBd = new b2BodyDef(); ballBd.linearDamping = .03; ballBd.allowSleep = false; ballBd.AddShape(ballSd); ballBd.position.Set(20,0); player.object = world.CreateBody(ballBd);  

Interior box2dutils.js, am creat o funcție, numită createBox. Acest lucru creează un corp dreptunghiular static.

funcția createBox (lume, x, y, lățime, înălțime, fix, userData) if (typeof (fix) == 'undefined') fix = true; // 1 var casetaSd = noua b2BoxDef (); dacă (! fix) boxSd.density = 1.0; // 2 boxSd.userData = userData; // 3 boxSd.extents.Set (lățime, înălțime); // 4 caseta varBD = new b2BodyDef (); boxBd.AddShape (boxSd); // 5 boxBd.position.Set (x, y); // 6 retur world.CreateBody (boxBd)

Box2DBody

A Box2DBody are câteva caracteristici unice:

  • Ea poate fi statică (nu este afectată de impacturile coliziunilor), cinematică (nu este afectată de coliziuni, dar poate fi mișcată de mouse-ul dvs. de exemplu) sau dinamică (interacționează cu totul)
  • Trebuie să aibă o definiție a formei și ar trebui să indice modul în care apare obiectul
  • Pot avea mai multe dispozitive de fixare, ceea ce indică modul în care obiectul va interacționa cu coliziunile
  • Poziția sa este stabilită de centrul obiectului dvs., nu de marginea superioară din stânga, așa cum fac și alte motoare.

Revizuirea codului:

  1. Aici vom crea o definiție a formei care va fi un pătrat sau un dreptunghi și va configura densitatea sa (cât de des va fi mutată sau rotită prin forțe).
  2. Am setat datele utilizatorului, de obicei configurați obiecte grafice aici, dar în acest exemplu, am setat doar șiruri care vor fi identificatorul tipului de obiect pentru coliziuni. Acest parametru nu afectează algoritmii fizicii.
  3. Configurați jumătate din dimensiunea casetei mele (este o linie din punctul de poziție sau punctul central al obiectului într-un colț)
  4. Creați definiția corpului și adăugați definiția formei cutiei.
  5. Configurați poziția.
  6. Creați corpul din lume și returnați valoarea acestuia.

Crearea corpului mingii jucătorului

Am codificat jucătorul (mingea) direct în game.js fişier. Urmează aceeași secvență de creare de cutii, dar, de data aceasta, este o minge.

var ballSd = nou b2CircleDef (); ballSd.density = 0,1; ballSd.radius = 12; Rezoluția bătăii = 0.5; ballSd.friction = 1; ballSd.userData = 'jucător'; var ballBd = noua b2BodyDef (); ballBd.linearDamping = .03; ballBd.allowSleep = false; ballBd.AddShape (ballSd); ballBd.position.Set (20,0); player.object = world.CreateBody (ballBd);

Deci, cum să creăm un corp, pas cu pas?

  1. Creați forma, fixarea și definiția senzorului
  2. Creați definiția corpului
  3. Adăugați în corp forma, corpurile sau senzorii (care nu sunt explicați în acest articol)
  4. Creați corpul din lume

Box2DCircle

După cum am observat mai devreme, aceasta urmează același proces de creare a unei casete, dar acum trebuie să setați niște parametri noi.

  • rază - Aceasta este lungimea unei linii de la centrul cercului până la orice punct de pe marginea sa.
  • restituire - Cum va pierde mingea sau va câștiga forță atunci când se ciocnește cu alt corp.
  • frecare - Cum se va rostogoli mingea.

Box2DBody - Mai multe proprietăți

  • amortizare este folosit pentru a reduce viteza corpului - există amortizare unghiulară și amortizare liniară.
  • dormi în caseta2D, corpurile pot dormi pentru a rezolva problemele de performanță. De exemplu, să presupunem că dezvoltați un joc pe platformă, iar nivelul este definit de un ecran de 6000x400. De ce trebuie să faci fizică pentru obiectele care nu sunt pe ecran? Nu știți; asta e punctul! Așadar, alegerea corectă este de a le face să doarmă și de a îmbunătăți performanța jocului.

Am creat deja lumea noastră; puteți testa codul pe care îl aveți până acum. Veți vedea că jucătorul va cădea deasupra platformei de vest.

Acum, dacă ați încercat să rulați demo-ul, ar trebui să vă întrebați de ce pagina este goală ca hârtia albă?

Întotdeauna rețineți: Box2D nu face; calculează doar fizica.


Pasul 3 - Timpul de redare

Apoi, să redați caseta2DWorld.

Deschide-ți game.js script și adăugați următorul cod:

pasul funcției () var stepping = false; var timeStep = 1.0 / 60; var iterație = 1; // 1 etapa mondială (etapa de timp, iterație); // 2 ctx.clearRect (0, 0, canvasWidth, canvasHeight); drawWorld (lume, ctx); // 3 setTimeout ('pas ()', 10); 

Ce realizăm aici:

  1. Instruit box2dWorld pentru a efectua simulări fizice
  2. Ștergeți panza și trageți din nou
  3. Executați Etapa() funcționează din nou în zece milisecunde

Cu acest cod de cod, lucrăm acum cu fizica și desenul. Puteți să vă testați și să urmăriți o minge care se încadrează, după cum se arată mai jos:


drawWorld în box2dutils.js

funcția drawWorld (lumea, contextul) pentru (var j = world.m_jointList; j; j = j.m_next) tragereJoint (j, context);  pentru (var b = lume.m_bodyList; b; b = b.m_next) pentru (var s = b.GetShapeList (); s! = null; s = s.GetNext ; 

Ceea ce am scris mai sus este o funcție de depanare care atrage lumea noastră în pânză, folosind graficul API furnizat de Canvas API-ul HTML5.

Prima buclă atrage toate îmbinările. Nu am folosit articulații în acest articol. Ele sunt un pic complex pentru un prim demo, dar, totuși, ele sunt esențiale pentru jocurile tale. Ele vă permit să creați corpuri foarte interesante.

Cea de-a doua bucla atrage toate corpurile, de aceea suntem aici!

funcția drawShape (formă, context) context.strokeStyle = '# 000000'; context.beginPath (); comutator (shape.m_type) caz b2Shape.e_circleShape: var circle = forma; var pos = circle.m_position; var r = circle.m_radius; var = 16,0; var theta = 0,0; var dtheta = 2.0 * Math.PI / segmente; // atrage cercul context.moveTo (pos.x + r, pos.y); pentru (var i = 0; i < segments; i++)  var d = new b2Vec2(r * Math.cos(theta), r * Math.sin(theta)); var v = b2Math.AddVV(pos, d); context.lineTo(v.x, v.y); theta += dtheta;  context.lineTo(pos.x + r, pos.y); // draw radius context.moveTo(pos.x, pos.y); var ax = circle.m_R.col1; var pos2 = new b2Vec2(pos.x + r * ax.x, pos.y + r * ax.y); context.lineTo(pos2.x, pos2.y);  break; case b2Shape.e_polyShape:  var poly = shape; var tV = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[0])); context.moveTo(tV.x, tV.y); for (var i = 0; i < poly.m_vertexCount; i++)  var v = b2Math.AddVV(poly.m_position, b2Math.b2MulMV(poly.m_R, poly.m_vertices[i])); context.lineTo(v.x, v.y);  context.lineTo(tV.x, tV.y);  break;  context.stroke(); 

Ne întoarcem prin fiecare vârf al obiectului și îl trasăm cu linii (context.moveTo și context.lineTo). Acum, este util să aveți un exemplu? dar nu atât de util în practică. Atunci când utilizați grafică, trebuie să acordați atenție poziționării organismelor. Nu aveți nevoie să faceți buclă vertexelor, așa cum face acest demo.


Pasul 4 - Interactivitate

Un joc fără interactivitate este un film, iar un film cu interactivitate este un joc.

Să dezvoltăm funcția săgeată de tastatură pentru a sari și a muta mingea.

Adăugați următorul cod la adresa dvs. game.js fişier:

funcția handleKeyDown (evt) chei [evt.keyCode] = adevărat;  funcția handleKeyUp (evt) chei [evt.keyCode] = false;  // dezactivează derularea verticală de la săgeți :) document.onkeydown = funcția () return event.keyCode! = 38 && event.keyCode! = 40

Cu handleKeyDown și handleKeyUp, am setat un mulțime care urmărește fiecare cheie pe care o tipizează utilizatorul. Cu document.onkeydown, dezactivați funcția de defilare verticală nativă a browserului pentru săgețile sus și jos. Ați jucat vreodată un joc HTML5 și când sari, jucătorul, dușmanii și obiectele vor ieși din ecran? Asta nu va fi o problemă acum.

Adăugați următorul cod de cod la începutul paginii Etapa() funcţie:

handleInteractions ();

Și în afară, declară funcția:

funcția handleInteractions () // săgeată sus // 1 var collision = world.m_contactList; player.canJump = false; dacă (collision.GetShape1 () GetUserData () == 'player' || collision.GetShape2 () GetUserData () == 'jucător') if (collision.GetShape1 .GetUserData () == 'ground' || collision.GetShape2 () GetUserData () == 'teren')) var playerObj = (collision.GetShape1 () GetUserData () == 'player'? Collision.GetShape1 () .GetPosition (): collision.GetShape2 () .GetPosition ()); var groundObj = (collision.GetShape1 () GetUserData () == 'sol' collision.GetShape1 () GetPosition (): collision.GetShape2 () GetPosition ()); dacă (playerObj.y < groundObj.y) player.canJump = true;     // 2 var vel = player.object.GetLinearVelocity(); // 3 if (keys[38] && player.canJump) vel.y = -150;  // 4 // left/right arrows if (keys[37]) vel.x = -60;  else if (keys[39]) vel.x = 60;  // 5 player.object.SetLinearVelocity(vel); 

Cea mai complicată parte din codul de mai sus este prima, unde verificăm o coliziune și scriem câteva condiții pentru a determina dacă shape1 sau shape2 este jucătorul. Dacă este, verificăm dacă shape1 sau shape2 este un motiv obiect. Din nou, dacă da, jucătorul se ciocnește cu solul. Apoi, verificăm dacă jucătorul este deasupra solului. În acest caz, jucătorul poate sări.

În al doilea rând comentat (2), vom prelua LinearVelocity al jucătorului.

Regiunile 3 și 4 comentate verifică dacă sunt apăsate săgețile și ajustează vectorul de viteză în consecință.

În a cincea regiune, setăm jucătorul cu noul vector de viteză.

Interacțiunile sunt acum terminate! Dar nu există nici un obiectiv, doar să sărim, să sărim, să sarăm? și sari!


Pasul 5 - Mesajul "Ai câștigat"

Adăugați codul de mai jos la începutul paginii LinearVelocity funcţie:

 dacă player.object.GetCenterPosition (). y> canvasHeight) player.object.SetCenterPosition (nou b2Vec2 (20,0), 0) altceva dacă (player.object.GetCenterPosition () x> canvasWidth-50) showWin (); întoarcere; 
  • Prima condiție determină dacă jucătorul cade și trebuie transportat înapoi la punctul de plecare (deasupra platformei de vest).
  • A doua condiție verifică dacă jucătorul se află deasupra celei de-a doua platforme și a câștigat jocul. Iată-l showWin () funcţie.
funcția showWin () ctx.fillStyle = '# 000'; ctx.font = '30px verdana'; ctx.textBaseline = 'top'; ctx.fillText ("Voi ați reușit!", 30, 0); ctx.fillText ("mulțumesc, andersonferminiano.com", 30, 30); ctx.fillText ("@ andferminiano", 30, 60); 

Si asta e! Tocmai ați terminat primul joc simplu cu HTML5 și Box2D. Felicitări!

Dacă aveți nevoie de o soluție mai simplă, puteți verifica selecția jocurilor HTML5 de pe Envato Market, dintre care mulți vin cu codul sursă pentru a investiga și personaliza pentru a se potrivi nevoilor dvs..

Cod