Bouw je eerste spel met HTML5

HTML5 groeit sneller dan iemand ooit had kunnen bedenken. Er worden al krachtige en professionele oplossingen ontwikkeld? zelfs in de spelwereld! Bekijk de honderden .HTML5-games op Envato Market.

Vandaag maak je je eerste game met Box2D en HTML5's canvas label.


Wat is Box2D?

Box2D is een open source en populaire engine die 2D-fysica simuleert voor het maken van games en applicaties. Hoofdzakelijk geschreven in C ++, het is door community-bijdragers in meerdere talen omgezet.

Met dezelfde methoden en objecten beschikt u over de mogelijkheid om de physics van uw games in vele talen te maken, zoals Objective C (iPhone / iPad), Actionscript 3.0 (Web), HTML 5 (web), enz..


Stap 1 - Uw project opzetten

Begin hier met het ontwikkelen van de demo door de Box2D-engine voor HTML5 te downloaden. Maak vervolgens een nieuw HTML-bestand met de volgende structuur (kopieer js- en lib-directory's van box2d-js-project naar je gamemap).

Nu moet u de benodigde bestanden invoegen om box2D in uw HTML-bestand uit te voeren:

                                                                  

Ja, dat is een enorm aantal HTTP-verzoeken!

Houd er rekening mee dat het voor de implementatie ten zeerste wordt aanbevolen om al deze bronnen samen te voegen tot één script het dossier.

Maak vervolgens nog twee scripts in de / Js / map, genaamd "Box2dutils.js" en "Game.js".

  • box2dutils.js - het is een kopie en plak van sommige demo's die bij komen box2dlib, en is belangrijk voor tekenfuncties (ik zal hier ook enkele belangrijke delen uitleggen).
  • game.js - het spel zelf; dit is waar we de platforms, de speler, de toetsenbordinteracties, enz. creëren.

Kopieer en plak de volgende code in box2dutils.js. Maak je geen zorgen! Ik zal het beetje bij beetje uitleggen!

functie drawWorld (world, context) for (var j = world.m_jointList; j; j = j.m_next) drawJoint (j, context);  for (var b = world.m_bodyList; b; b = b.m_next) for (var s = b.GetShapeList (); s! = null; s = s.GetNext ()) drawShape (s, context) ;  functie drawJoint (joint, context) var b1 = joint.m_body1; var b2 = joint.m_body2; var x1 = b1.m_position; var x2 = b2.m_position; var p1 = joint.GetAnchor1 (); var p2 = joint.GetAnchor2 (); context.strokeStyle = '# 00eeee'; context.beginPath (); switch (joint.m_type) case b2Joint.e_distanceJoint: context.moveTo (p1.x, p1.y); context.lineTo (p2.x, p2.y); breken; case b2Joint.e_pulleyJoint: // TODO break; standaard: if (b1 == world.m_groundBody) context.moveTo (p1.x, p1.y); context.lineTo (x2.x, x2.y);  else if (b2 == world.m_groundBody) context.moveTo (p1.x, p1.y); context.lineTo (x1.x, x1.y);  else context.moveTo (x1.x, x1.y); context.lineTo (p1.x, p1.y); context.lineTo (x2.x, x2.y); context.lineTo (p2.x, p2.y);  pauze;  context.stroke ();  functie drawShape (shape, context) context.strokeStyle = '# 000000'; context.beginPath (); switch (shape.m_type) case b2Shape.e_circleShape: var circle = shape; var pos = circle.m_position; var r = circle.m_radius; var-segmenten = 16,0; var theta = 0,0; var dtheta = 2,0 * Math.PI / segmenten; // teken cirkel context.moveTo (pos.x + r, pos.y); for (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) 

Stap 2 - Het spel ontwikkelen

Open de index.html bestand dat u eerder hebt gemaakt en voeg een toe canvas element (600x400) binnen de lichaam element. Dit is waar we zullen werken met de HTML5-teken API:

Ook, terwijl je hier bent, referentie game.js en box2dutils.js.

 

Dat zal het voor de HTML doen! Laten we nu aan het leuke JavaScript werken!

Open game.js, en voeg de onderstaande code in:

// enkele variabelen die we gaan gebruiken in deze demo var initId = 0; var player = function () this.object = null; this.canJump = false; ; var wereld; var ctx; var canvasWidth; var canvasHeight; var-toetsen = []; // HTML5 onLoad-gebeurtenis Event.observe (venster, 'load', function () world = createWorld (); // box2DWorld ctx = $ ('game'). GetContext ('2d'); // 2 var canvasElm = $ ('game'); canvasWidth = parseInt (canvasElm.width); canvasHeight = parseInt (canvasElm.height); initGame (); // 3 step (); // 4 // 5 window.addEventListener ('keydown', handleKeyDown, true); window.addEventListener ('keyup', handleKeyUp, true););

Box2DWorld - dat is waarom we hier zijn

Oké, laten we uitzoeken wat dit stuk code doet!

Box2DWorld is een van de klassen die beschikbaar wordt gesteld, via de kern van box2d. De functie ervan is simpel: combineren alles in een klas. In box2DWorld beschik je over de body definition en collisions manager van je game of applicatie.

Houd de game.js en box2dutils.js bestanden openen en zoeken naar de createWorld () functie binnen box2dutils.js.

functie createWorld () // hier maken we onze wereldinstellingen voor botsingen var worldAABB = new b2AABB (); worldAABB.minVertex.Set (-1000, -1000); worldAABB.maxVertex.Set (1000, 1000); // set gravity vector var gravity = new b2Vec2 (0, 300); var doSleep = true; // begin onze wereld en geef haar waarde terug var world = new b2World (worldAABB, gravity, doSleep); terugkeer wereld; 

Het is vrij eenvoudig om het te maken box2DWorld.


Terug naar game.js

Raadpleeg de opmerkingennummers in de bovenstaande twee blokjes code. Op nummer twee halen we de canvas element context met behulp van de selector API (ziet eruit als jQuery of MooTools selectors, nietwaar?). Op nummer drie hebben we een nieuwe interessante functie: initGame (). Dit is waar we het landschap creëren.

Kopieer en plak de onderstaande code in game.js, en dan zullen we het samen bekijken.

function initGame () // create 2 big platforms createBox (world, 3, 230, 60, 180, true, 'ground'); createBox (world, 560, 360, 50, 50, true, 'ground'); // maak kleine platforms voor (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);  

Binnen box2dutils.js, we hebben een functie gemaakt, genaamd createBox. Hiermee wordt een statische rechthoek gemaakt.

functie createBox (world, x, y, width, height, fixed, userData) if (typeof (fixed) == 'undefined') fixed = true; // 1 var boxSd = new b2BoxDef (); if (! fixed) boxSd.density = 1.0; // 2 boxSd.userData = userData; // 3 boxSd.extents.Set (breedte, hoogte); // 4 var boxBd = new b2BodyDef (); boxBd.AddShape (boxSd); // 5 boxBd.position.Set (x, y); // 6 return world.CreateBody (boxBd)

Box2DBody

EEN Box2DBody heeft enkele unieke kenmerken:

  • Het kan statisch zijn (niet beïnvloed door botsingen van botsingen), kinematisch (het wordt niet beïnvloed door botsingen, maar het kan bijvoorbeeld met de muis worden verplaatst) of dynamisch (communiceert met alles)
  • Moet een vormdefinitie hebben en moet aangeven hoe het object wordt weergegeven
  • Kan meer dan één fixture hebben, wat aangeeft hoe het object zal reageren op botsingen
  • Zijn positie wordt bepaald door het midden van uw object, niet de linkerbovenrand zoals veel andere motoren doen.

Herziening van de code:

  1. Hier maken we één vormdefinitie die een vierkant of rechthoek is en de dichtheid instellen (hoe vaak het wordt verplaatst of geroteerd door krachten).
  2. We hebben de gebruikersgegevens, meestal stel je hier grafische objecten in, maar in dit voorbeeld stel ik alleen strings in die de identifier zijn van het type object voor botsingen. Deze parameter heeft geen invloed op algoritmen voor natuurkunde.
  3. Stel de helft van de grootte van mijn doos in (het is een lijn vanaf het positiepunt of het middelpunt van het object naar een hoek)
  4. We maken de bodydefinitie en voegen er de definitie van de doosvorm aan toe.
  5. Stel de positie in.
  6. Creëer het lichaam in de wereld en geef de waarde terug.

De balbody van de speler maken

Ik heb de speler (bal) rechtstreeks in de game.js het dossier. Het volgt dezelfde volgorde van het maken van dozen, maar deze keer is het een bal.

var ballSd = new b2CircleDef (); ballSd.density = 0,1; ballSd.radius = 12; ballSd.restitution = 0,5; ballSd.friction = 1; ballSd.userData = 'speler'; var ballBd = new b2BodyDef (); ballBd.linearDamping = .03; ballBd.allowSleep = false; ballBd.AddShape (ballSd); ballBd.position.Set (20,0); player.object = world.CreateBody (ballBd);

Dus hoe creëren we een lichaam, stap voor stap?

  1. Creëer de vorm, de fixture en de sensordefinitie
  2. Maak de bodydefinitie
  3. Voeg in het lichaam uw vorm, armaturen of sensoren toe (niet uitgelegd in dit artikel)
  4. Creëer het lichaam in de wereld

Box2DCircle

Zoals ik eerder heb opgemerkt, volgt dit hetzelfde creatieproces van een box, maar nu moet je een aantal nieuwe parameters instellen.

  • radius - Dit is de lengte van een lijn van het middelpunt van de cirkel naar een punt op de rand ervan.
  • teruggave - Hoe de bal zal verliezen, of kracht verwerft wanneer hij botst met een ander lichaam.
  • wrijving - Hoe de bal zal rollen.

Box2DBody - Meer eigenschappen

  • demping wordt gebruikt om de snelheid van het lichaam te verminderen - er is hoekdemping en lineaire demping.
  • slaap in box2D kunnen lichamen slapen om prestatieproblemen op te lossen. Laten we bijvoorbeeld aannemen dat u een platformgame ontwikkelt en dat het niveau wordt bepaald door een scherm van 6000x400. Waarom moet je natuurkunde uitvoeren voor objecten die buiten beeld zijn? Jij niet; dat is het punt! Dus de juiste keuze is om ze in slaap te brengen en de prestaties van je spel te verbeteren.

We hebben onze wereld al gecreëerd; je kunt de code die je tot nu toe hebt getest. Je ziet de speler boven het westplatform vallen.

Als je nu probeert de demo uit te voeren, moet je je afvragen waarom de pagina zo kaal is als wit papier?

Onthoud altijd: Box2D wordt niet weergegeven; het berekent alleen de fysica.


Stap 3 - Rendertijd

Laten we vervolgens de box2DWorld weergeven.

Open je game.js script en voeg de volgende code toe:

functiestap () var stepping = false; var timeStep = 1.0 / 60; var iteratie = 1; // 1 world.Step (timeStep, iteratie); // 2 ctx.clearRect (0, 0, canvasWidth, canvasHeight); drawWorld (wereld, ctx); // 3 setTimeout ('step ()', 10); 

Wat we hier bereiken:

  1. Instrueerde box2dWorld om natuurkundige simulaties uit te voeren
  2. Scherm schoongemaakt en opnieuw getekend
  3. Voer de stap() weer functioneren in tien milliseconden

Met dit stukje code werken we nu met natuurkunde en tekenen. Je kunt jezelf testen en uitkijken naar een vallende bal, zoals hieronder aangetoond:


drawWorld in box2dutils.js

functie drawWorld (world, context) for (var j = world.m_jointList; j; j = j.m_next) drawJoint (j, context);  for (var b = world.m_bodyList; b; b = b.m_next) for (var s = b.GetShapeList (); s! = null; s = s.GetNext ()) drawShape (s, context) ; 

Wat we hierboven hebben geschreven, is een foutopsporingsfunctie die onze wereld naar het canvas trekt, met behulp van de grafische API die geboden wordt door de Canvas API van HTML5.

De eerste lus tekent alle gewrichten. In dit artikel hebben we geen gewrichten gebruikt. Ze zijn een beetje ingewikkeld voor een eerste demo, maar zijn niettemin essentieel voor je games. Ze laten je toe om zeer interessante lichamen te creëren.

De tweede lus trekt alle lichamen, en daarom zijn we hier!

functie drawShape (shape, context) context.strokeStyle = '# 000000'; context.beginPath (); switch (shape.m_type) case b2Shape.e_circleShape: var circle = shape; var pos = circle.m_position; var r = circle.m_radius; var-segmenten = 16,0; var theta = 0,0; var dtheta = 2,0 * Math.PI / segmenten; // teken cirkel context.moveTo (pos.x + r, pos.y); for (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(); 

We doorlopen elke hoekpunt van het object en tekenen het met lijnen (context.moveTo en context.lineTo). Welnu, het is handig om een ​​voorbeeld te hebben? maar niet zo handig in de praktijk. Wanneer u afbeeldingen gebruikt, hoeft u alleen maar te letten op de positionering van de lichamen. U hoeft geen hoekpunten te herhalen, zoals deze demo doet.


Stap 4 - Interactiviteit

Een spel zonder interactiviteit is een film en een film met interactiviteit is een spel.

Laten we de functionaliteit van de toetsenbordpijl ontwikkelen om te springen en de bal te verplaatsen.

Voeg de volgende code toe aan uw game.js het dossier:

function handleKeyDown (evt) keys [evt.keyCode] = true;  function handleKeyUp (evt) keys [evt.keyCode] = false;  // verticaal scrollen uit pijlen uitschakelen :) document.onkeydown = function () return event.keyCode! = 38 && event.keyCode! = 40

Met handleKeyDown en handleKeyUp, we hebben een opgezet rangschikking die elke toets traceert die de gebruiker typt. Met document.onkeydown, we schakelen de oorspronkelijke verticale scrolfunctie van de browser uit voor de pijlen omhoog en omlaag. Heb je ooit een HTML5-game gespeeld en wanneer je springt, gaan de speler, vijanden en objecten van het scherm? Dat zal nu geen probleem zijn.

Voeg dit volgende stukje code toe aan het begin van uw stap() functie:

handleInteractions ();

En buiten, verklaar de functie:

function handleInteractions () // pijl omhoog // 1 var collision = world.m_contactList; player.canJump = false; if (collision! = null) if (collision.GetShape1 (). GetUserData () == 'player' || collision.GetShape2 (). GetUserData () == 'player') if ((collision.GetShape1 () .GetUserData () == 'ground' || collision.GetShape2 (). GetUserData () == 'ground')) var playerObj = (collision.GetShape1 (). GetUserData () == 'speler'? Collision.GetShape1 () .GetPosition (): collision.GetShape2 (). GetPosition ()); var groundObj = (collision.GetShape1 (). GetUserData () == 'ground'? collision.GetShape1 (). GetPosition (): collision.GetShape2 (). GetPosition ()); if (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); 

Het meest gecompliceerde stuk van de bovenstaande code is de eerste, waar we controleren op een botsing, en een aantal voorwaarden schrijven om te bepalen of de shape1 of de shape2 is de speler. Als dat zo is, controleren we of shape1 of shape2 is een grond voorwerp. Nogmaals, als dat zo is, botst de speler met de grond. Vervolgens controleren we of de speler boven de grond staat. Als dat het geval is, kan de speler springen.

Op de tweede regel met commentaar (2) halen we de Lineaire snelheid van de speler.

De derde en vierde van commentaar voorziene gebieden verifiëren of pijlen worden ingedrukt en passen dienovereenkomstig de snelheidsvector aan.

In het vijfde gebied stellen we de speler in met de nieuwe snelheidsvector.

De interacties zijn nu voltooid! Maar er is geen doel: we springen, springen, springen gewoon? en spring!


Stap 5 - "Je wint" bericht

Voeg de onderstaande code toe aan het begin van uw Lineaire snelheid functie:

 if (player.object.GetCenterPosition (). y> canvasHeight) player.object.SetCenterPosition (new b2Vec2 (20,0), 0) else if (player.object.GetCenterPosition (). x> canvasWidth-50)  showWin (); terug te keren; 
  • De eerste voorwaarde bepaalt of de speler valt en moet worden teruggebracht naar het beginpunt (boven het westplatform).
  • De tweede voorwaarde controleert of de speler zich boven het tweede platform bevindt en won het spel. Hier is de showWin () functie.
function showWin () ctx.fillStyle = '# 000'; ctx.font = '30px verdana'; ctx.textBaseline = 'top'; ctx.fillText ('Ye! you made it!', 30, 0); ctx.fillText ('thank you, andersonferminiano.com', 30, 30); ctx.fillText ('@ andferminiano', 30, 60); 

En dat is het! Je hebt net je eerste eenvoudige game voltooid met HTML5 en Box2D. Gefeliciteerd!

Als u een eenvoudigere oplossing zoekt, kunt u de selectie HTML5-games op Envato Market bekijken, waarvan vele met broncode die u kunt onderzoeken en aanpassen aan uw eigen behoeften.