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.
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..
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"
.
box2dlib
, en is belangrijk voor tekenfuncties (ik zal hier ook enkele belangrijke delen uitleggen).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)
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););
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
.
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
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)box2dutils.js
, we hebben een functie gemaakt, genaamdcreateBox
. Hiermee wordt een statische rechthoek gemaakt.
Box2DBody
EEN Box2DBody
heeft enkele unieke kenmerken:
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.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?
Box2DCircle
Zoals ik eerder heb opgemerkt, volgt dit hetzelfde creatieproces van een box, maar nu moet je een aantal nieuwe parameters instellen.
Box2DBody
- Meer eigenschappenWe 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.
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);
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.
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!
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;
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.