Een eenvoudig 3D physics-spel maken met Three.js en Physijs

Wat je gaat creëren

In deze zelfstudie leert u Physi.js te gebruiken om spelfysica toe te voegen aan een 3D-scène die is gemaakt met Three.js. We zullen een eenvoudig spel maken waarin we een kar rond het verzamelen van items rijden, met behulp van elementaire physics-vormen en beperkingen in de fysica.

Deze tutorial bouwt voort op de concepten die ik in mijn vorige Three.js-zelfstudie heb gebruikt. Ik zou je willen vragen om het te lezen als je Three.js nieuw bent en voor het maken van 3D-scènes. 

Vanwege een technische beperking bij het hosten van op webwerknemers gebaseerde oplossingen op JSFiddle, kunnen we het interactieve spel niet in deze zelfstudiepagina insluiten. Gebruik de meegeleverde broncode om het werkvoorbeeld van eventuele cloud-gebaseerde IDE's of door self-hosting te bekijken.

1. 3D Physics op het web

Er zijn momenteel meerdere frameworks en engines beschikbaar die kunnen worden gebruikt om 3D-content voor het web met physics te maken. Enkele van de vermeldenswaardige zijn Turbulenz, BabylonJS, PlayCanvas en de voor de hand liggende Unity WebGL-build. Maar als het gaat om populariteit en gebruiksgemak, willen de meeste mensen Three.js gebruiken voor hun experimenten. Aangezien Three.js een rendering-engine is en geen geïntegreerde fysica heeft, moeten we aanvullende frameworks verkennen om de fysische mogelijkheden toe te voegen.

Een zeer populaire JavaScript-fysica-oplossing is Ammo.js, een directe haven voor bullet-fysica. Hoewel Ammo.js direct kan worden gebruikt, is het niet erg beginnersvriendelijk en heeft het veel boilerplate-code voor elk aspect. Omdat het niet handmatig is geschreven maar geporteerd met Emscripten, is de code niet gemakkelijk te begrijpen. 

Een alternatieve oplossing is om Cannon.js of Physijs te gebruiken. Het interessante aan Physijs is dat de focus altijd ligt op het maken van dingen eenvoudiger, wat het de ideale keuze maakt voor beginners. Het is gebouwd op basis van Ammo.js en heeft zelfs een Cannon.js-filiaal. In deze zelfstudie worden Physijs en Three.js gebruikt om een ​​prototype voor een werkgame te maken met natuurkundige mogelijkheden. 

Nog een andere optie, hoewel te simpel, zou zijn om het Whitestorm-raamwerk te gebruiken, dat een op componenten gebaseerd raamwerk is gebaseerd op Three.js en Physijs. 

2. Opzetten van Physijs

We moeten de bestanden ammo.js, physi.js, physijs_worker.js en three.js in onze mappenstructuur of coderingsomgeving gebruiken om Physijs te gebruiken. Physijs gebruikt een webwerker om verschillende threads te gebruiken voor natuurkundige berekeningen. Dus de eerste stap in het integratieproces is om de webwerker in te stellen zoals hieronder.

Physijs.scripts.worker = 'lib / physijs_worker.js'; Physijs.scripts.ammo = 'ammo.js';

Op dit punt is de setup voltooid en kunnen we het fysica-framework gaan gebruiken. Physijs heeft ervoor gezorgd dat het de codeerstijl van Three.js heeft gevolgd, en de meeste concepten zijn eenvoudige vervangingen van het bijbehorende Three.js-concept.

Basisstappen

In plaats van THREE.Scene, we moeten gebruiken Physijs.Scene.

Er zijn meerdere mazen beschikbaar in Physijs die moeten worden gebruikt in plaats van THREE.Mesh. De beschikbare opties zijn PlaneMesh, BoxMesh, SphereMesh, CylinderMesh, ConeMesh, CapsuleMesh, ConvexMesh, ConcaveMesh, en HeighfieldMesh.

We moeten de scene.simulate methode om de natuurkundige berekeningen in de geven methode of met frequente intervallen. Laat me je eraan herinneren dat de natuurkundige berekeningen plaatsvinden in een andere thread en niet synchroon of zo snel als de scene renderen lus. 

Zelfs de volgende oproep aan scene.simulate kan gebeuren terwijl de vorige berekeningen nog steeds lopen. Om het goed in overeenstemming te brengen met de natuurkundige berekeningen, kunnen we de Physijs-scènes gebruiken bijwerken evenement.

scene.addEventListener ('update', function () // uw code. physics-berekeningen zijn bijgewerkt);

Om een ​​botsing te registreren op een Physijs mesh-object met de naam arbitrair als kubus, we kunnen luisteren naar de botsing evenement.

cube.addEventListener ('collision', function (objCollidedWith, linearVelOfCollision, angularVelOfCollision) );

Binnen de bovenstaande methode, deze zal verwijzen naar kubus, terwijl objCollidedWith is het object kubus is in botsing gekomen.

3. Voorbeeld Game Prototype

Voor deze zelfstudie maken we een heel eenvoudig op fysica gebaseerd spel waarin we fysische beperkingen gebruiken om een ​​voertuig te maken. De speler kan met de pijltjestoetsen het voertuig besturen en een stuiterende bal verzamelen die willekeurig in het speelveld verschijnt. 

Interessant is dat Physijs al een special heeft voertuig functie die direct kan worden gebruikt voor het maken van voertuigen, maar we zullen deze niet gebruiken.

The Game World

Onze spelwereld is een uitgestrekt terrein met aan de vier zijden muren zoals hieronder.

We gebruiken Physijs.BoxMesh voor de grond en de vier muren zoals weergegeven in de onderstaande code.

ground_material = Physijs.createMaterial (nieuw THREE.MeshStandardMaterial (color: 0x00ff00), frictie, .9 // lage restitutie); // Ground ground = new Physijs.BoxMesh (nieuwe THREE.BoxGeometry (150, 1, 150), ground_material, 0 // massa); ground.receiveShadow = true; scene.add (ground);

Let op het gebruik van Physijs.createMaterial om de benodigde fysische materialen te maken door een wrijvingswaarde en een restitutiewaarde door te geven. De wrijvingswaarde bepaalt de grip op de grond en de restitutiewaarde bepaalt de veerkracht. Een belangrijk ding om op te merken is dat wanneer we een massawaarde van bieden 0, we maken een stationair netobject.

De wagen

We gaan een speciaal voertuig maken met twee verbonden onderdelen. Het voorste deel, dat drie wielen heeft, fungeert als de motor, en het achterste deel, met twee wielen, zal fungeren als een koets. Het slededeel is verbonden met het motoronderdeel met behulp van een scharnierverbinding geïmplementeerd met behulp van a Physijs.HingeContraint

De wielen gebruiken Physijs.DOFConstraint, wat een vrijheidsbeperking is om aan het lichaam van het voertuig te worden bevestigd, terwijl het de mogelijkheid behoudt om onafhankelijk te draaien. Ik nodig u uit om de officiële documentatie over de verschillende beperkingen die beschikbaar zijn in Physijs te lezen.

Het motorhuis en de wagenbak zijn eenvoudig BoxMesh objecten zoals de grond hierboven weergegeven, maar met een bepaalde massawaarde. Ze zijn met elkaar verbonden via een scharnierverbinding, zoals weergegeven in de volgende code. Een scharniergewricht beperkt de beweging van het verbonden object zoals dat van een normale deur.

car.carriage_constraint = nieuwe Physijs.HingeConstraint (car.carriage, // Eerste object dat beperkt moet worden car.body, // beperkt tot deze nieuwe THREE.Vector3 (6, 0, 0), // op dit moment nieuwe THREE.Vector3 (0, 1, 0) // langs deze as); scene.addConstraint (car.carriage_constraint); car.carriage_constraint.setLimits (-Math.PI / 3, // minimale bewegingshoek, in radialen Math.PI / 3, // maximale bewegingshoek, in radialen 0, // toegepast als factor om fout te beperken 0 / / controles stuiteren op limiet (0,0 == geen stuitering));

Het tweede deel van de code past limieten toe op de rotatie van het scharnier, wat in dit geval tussen ligt -Math.PI / 3 en Math.PI / 3

De wielen gebruiken een mate van vrijheidsbeperking die kan worden gebruikt om limieten in te stellen voor zowel lineaire beweging als hoekbeweging in alle drie de assen. Een methode addWheel is gemaakt voor het toevoegen van wielen, waarbij meerdere parameters in rekening worden gebracht. 

De parameters zijn de positie van het wiel, het gewicht van het wiel, of het wiel groot of klein is, en het wielreferentieobject. De methode retourneert een nieuw gemaakte DOFConstraint, die wordt gebruikt voor het besturen van het voertuig.

functie addWheel (wiel, pos, isBig, gewicht) var geometry = wheel_geometry; if (isBig) geometry = big_wheel_geometry;  wiel = nieuwe Physijs.CylinderMesh (geometrie, wielmateriaal, gewicht); wheel.name = "cart"; wheel.rotation.x = Math.PI / 2; wheel.position.set (pos.x, pos.y, pos.z); wheel.castShadow = true; scene.add (wiel); wheel.setDamping (0, demping); var wheelConstraint = new Physijs.DOFConstraint (wheel, car.body, pos); if (isBig) wheelConstraint = nieuwe Physijs.DOFConstraint (wheel, car.carriage, pos);  scene.addConstraint (wheelConstraint); wheelConstraint.setAngularLowerLimit (x: 0, y: 0, z: 0); wheelConstraint.setAngularUpperLimit (x: 0, y: 0, z: 0); terug wheelConstraint; 

De grote wielen moeten aan de slede worden bevestigd en de kleine wielen aan de motor. De wielen krijgen een dempingswaarde zodat hun hoeksnelheid afneemt als er geen externe kracht wordt uitgeoefend. Dit zorgt ervoor dat het voertuig langzamer gaat rijden als we het gaspedaal loslaten. 

We zullen de sturende logica in een later hoofdstuk verkennen. Elk wielgaas samen met de auto-mazen krijgt de naam van kar voor identificatiedoeleinden tijdens de collision call back. Elke wielbeperking heeft verschillende hoeklimieten, die onafhankelijk worden ingesteld zodra ze zijn gemaakt. Hier is bijvoorbeeld de code voor het voorste middelste wiel van de motor, car.wheel_fm, en de bijbehorende beperking, car.wheel_fm_constraint.

car.wheel_fm_constraint = addWheel (car.wheel_fm, nieuwe THREE.Vector3 (-7.5, 6.5, 0), false, 300); car.wheel_fm_constraint.setAngularLowerLimit (x: 0, y: -Math.PI / 8, z: 1); car.wheel_fm_constraint.setAngularUpperLimit (x: 0, y: Math.PI / 8, z: 0);

De bal

De bal is een Physijs.SphereMesh object met een lagere massawaarde van 20. Wij gebruiken de releaseBall methode om de bal willekeurig in ons spelgebied te plaatsen wanneer het wordt verzameld. 

function addBall () var ball_material = Physijs.createMaterial (nieuw THREE.MeshStandardMaterial (color: 0x0000ff, shading: THREE.FlatShading), frictie, .9 // goede restitutie); var ball_geometry = nieuwe THREE.SphereGeometry (2,16,16); bal = nieuwe Physijs.SphereMesh (ball_geometry, ball_material, 20); ball.castShadow = true; releaseBall (); scene.add (bal); ball.setDamping (0,0.9); ball.addEventListener ('collision', onCollision);  function releaseBall () var bereik = 10 + Math.random () * 30; ball.position.y = 16; ball.position.x = ((2 * Math.floor (Math.random () * 2)) - 1) * bereik; ball.position.z = ((2 * Math.floor (Math.random () * 2)) - 1) * bereik; bal .__ dirtyPosition = true; // forceer nieuwe positie // Je moet ook de snelheid van het object annuleren ball.setLinearVelocity (nieuwe THREE.Vector3 (0, 0, 0)); ball.setAngularVelocity (nieuwe THREE.Vector3 (0, 0, 0)); 

Wat het vermelden waard is, is dat we de positiewaarden die door de fysische simulatie zijn ingesteld, moeten vervangen om onze bal te verplaatsen. Hiervoor gebruiken we de __dirtyPosition vlag, die ervoor zorgt dat de nieuwe positie wordt gebruikt voor verdere fysica-simulatie.

De bal wordt verzameld als deze botst met een deel van het voertuig dat in de onCollision luisteraar methode.

function onCollision (other_object, linear_velocity, angular_velocity) if (other_object.name === "winkelwagentje") score ++; releaseBall (); scoreText.innerHTML = score.toString (); 

Rijden met het voertuig

We voegen gebeurtenislisteners toe voor de onKeyDown en onkeyup gebeurtenissen van het document, waar we het bepalen sleutelcode om de waarden van de corresponderende wielbeperkingen in te stellen. De theorie is dat het enkele voorwiel van de motor het draaien van ons voertuig bestuurt, en dat de twee wielen aan de achterkant van de motor de versnelling en vertraging regelen. De wielen op de wagen spelen geen rol bij het rijden.

document.onkeydown = handleKeyDown; document.onkeyup = handleKeyUp; function handleKeyDown (keyEvent) switch (keyEvent.keyCode) case 37: // Left car.wheel_fm_constraint.configureAngularMotor (1, -Math.PI / 3, Math.PI / 3, 1, 200); car.wheel_fm_constraint.enableAngularMotor (1); breken; case 39: // Right car.wheel_fm_constraint.configureAngularMotor (1, -Math.PI / 3, Math.PI / 3, -1, 200); car.wheel_fm_constraint.enableAngularMotor (1); breken; case 38: // Up car.wheel_bl_constraint.configureAngularMotor (2, 1, 0, 6, 2000); car.wheel_br_constraint.configureAngularMotor (2, 1, 0, 6, 2000); car.wheel_bl_constraint.enableAngularMotor (2); car.wheel_br_constraint.enableAngularMotor (2); breken; case 40: // Down car.wheel_bl_constraint.configureAngularMotor (2, 1, 0, -6, 2000); car.wheel_br_constraint.configureAngularMotor (2, 1, 0, -6, 2000); car.wheel_bl_constraint.enableAngularMotor (2); car.wheel_br_constraint.enableAngularMotor (2); breken;  function handleKeyUp (keyEvent) switch (keyEvent.keyCode) case 37: // Left car.wheel_fm_constraint.disableAngularMotor (1); breken; case 39: // Right car.wheel_fm_constraint.disableAngularMotor (1); breken; case 38: // Up car.wheel_bl_constraint.disableAngularMotor (2); car.wheel_br_constraint.disableAngularMotor (2); breken; case 40: // Down car.wheel_bl_constraint.disableAngularMotor (2); car.wheel_br_constraint.disableAngularMotor (2); breken; 

De DOFConstraint gebruikt de enableAngularMotor methode om hoeksnelheid toe te passen op het wiel dat het wiel draait op basis van de aswaarde die als parameter wordt opgegeven. In principe draaien we alleen de wielen en de beweging van het voertuig gebeurt als gevolg van de wrijvingsrespons van de grond, zoals in de echte wereld.

We kunnen de werkgame niet insluiten op deze pagina zoals vermeld aan het begin van de zelfstudie. Ga alsjeblieft door de volledige bron om te begrijpen hoe alles met elkaar verbonden is.

Conclusie

Dit is een heel eenvoudige inleiding tot het implementeren van fysica in je Three.js 3D-wereld. De documentatie van Physijs ontbreekt nauwelijks, maar er zijn al veel voorbeelden beschikbaar die de moeite van het bekijken waard zijn. Physijs is een zeer beginnersvriendelijk framework, zoals Three.js, als je kijkt naar de complexiteit die aanwezig is onder de motorkap. 

JavaScript is duidelijk populair voor zowel gameontwikkeling als webontwikkeling. Het is niet zonder zijn leercurven, en er zijn ook genoeg kaders en bibliotheken om je bezig te houden. Als u op zoek bent naar extra bronnen om te studeren of te gebruiken in uw werk, kijk dan wat we beschikbaar hebben op de Envato-marktplaats.

Ik hoop dat deze tutorial je helpt bij het verkennen van de interessante wereld van 3D-webgame-fysica.