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.
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.
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.
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.
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.
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.
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 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 ();
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.
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.