Three.js 飞行小游戏(源码)

文化   2024-09-15 07:00   河南  

使用 Three.js 的 WebGL 小实验。鼠标控制的飞行小游戏。

实现代码

HTML:

<!--Three.js Tutorial 'The Aviator'--><!--https://tympanus.net/codrops/2016/04/26/the-aviator-animating-basic-3d-scene-threejs/--><div id="world"></div>
CSS:
#world {  position: absolute;  width: 100vw;  height: 100vh;  overflow: hidden;  background: linear-gradient(moccasin, palegoldenrod);}

JAVASCRIPT:

var Colors = {  red: 0xf25346,  white: 0xd8d0d1,  brown: 0x59332e,  pink: 0xf5986e,  brownDark: 0x23190f,  blue: 0x609dc8};

var matRed = new THREE.MeshPhongMaterial({ color: Colors.red, shading: THREE.FlatShading});var matWhite = new THREE.MeshPhongMaterial({ color: Colors.white, shading: THREE.FlatShading});var matBrown = new THREE.MeshPhongMaterial({ color: Colors.brown, shading: THREE.FlatShading});var matBrownDark = new THREE.MeshPhongMaterial({ color: Colors.brownDark, shading: THREE.FlatShading});

var mousePos = { x: 0, y: 0 };

var sky, sea, airplane;

var hemisphereLight, shadowLight, ambientLight;

var scene, camera, fieldOfView, aspectRatio, nearPlane, farPlane, HEIGHT, WIDTH, renderer, container;

var Pilot = function () { this.mesh = new THREE.Object3D(); this.mesh.name = "Pilot"; this.angleHairs = 0;

var bodyGeom = new THREE.BoxGeometry(15, 15, 15); var body = new THREE.Mesh(bodyGeom, matBrown); body.position.set(2, -12, 0); this.mesh.add(body);

var faceGeom = new THREE.BoxGeometry(10, 10, 10); var faceMat = new THREE.MeshLambertMaterial({ color: Colors.pink }); var face = new THREE.Mesh(faceGeom, faceMat); this.mesh.add(face);

var hairGeom = new THREE.BoxGeometry(4, 4, 4); var hairMat = new THREE.MeshLambertMaterial({ color: Colors.brown }); var hair = new THREE.Mesh(hairGeom, hairMat); hair.geometry.applyMatrix(new THREE.Matrix4().makeTranslation(0, 2, 0));

var hairs = new THREE.Object3D(); this.hairsTop = new THREE.Object3D();

for (var i = 0; i < 12; i++) { var h = hair.clone(); var col = i % 3; var row = Math.floor(i / 3); var startPosZ = -4; var startPosX = -4; h.position.set(startPosX + row * 4, 0, startPosZ + col * 4); this.hairsTop.add(h); } hairs.add(this.hairsTop);

var hairSideGeom = new THREE.BoxGeometry(12, 4, 2); hairSideGeom.applyMatrix(new THREE.Matrix4().makeTranslation(-6, 0, 0)); var hairSideR = new THREE.Mesh(hairSideGeom, hairMat); var hairSideL = hairSideR.clone(); hairSideR.position.set(8, -2, 6); hairSideL.position.set(8, -2, -6); hairs.add(hairSideR); hairs.add(hairSideL);

var hairBackGeom = new THREE.BoxGeometry(2, 8, 10); var hairBack = new THREE.Mesh(hairBackGeom, hairMat); hairBack.position.set(-1, -4, 0); hairs.add(hairBack); hairs.position.set(-5, 5, 0);

this.mesh.add(hairs);

var glassGeom = new THREE.BoxGeometry(5, 5, 5); var glassMat = new THREE.MeshLambertMaterial({ color: Colors.brown }); var glassR = new THREE.Mesh(glassGeom, glassMat); glassR.position.set(6, 0, 3); var glassL = glassR.clone(); glassL.position.set(6, 0, -3);

var glassAGeom = new THREE.BoxGeometry(11, 1, 11); var glassA = new THREE.Mesh(glassGeom, glassMat); this.mesh.add(glassR); this.mesh.add(glassL); this.mesh.add(glassA);

var earGeom = new THREE.BoxGeometry(2, 3, 2); var earR = new THREE.Mesh(earGeom, faceMat); earR.position.set(0, 0, 6); var earL = earR.clone(); earL.position.set(0, 0, -6);

this.mesh.add(earR); this.mesh.add(earL);};

Pilot.prototype.updateHairs = function () { var hairs = this.hairsTop.children; var l = hairs.length; for (var i = 0; i < l; i++) { var h = hairs[i]; h.scale.y = 0.75 + Math.cos(this.angleHairs + i / 3) * 0.25; } this.angleHairs += 0.16;};

var AirPlane = function () { this.mesh = new THREE.Object3D(); this.mesh.name = "airPlane"; //cockpit var geomCockpit = new THREE.BoxGeometry(80, 50, 50, 1, 1, 1); geomCockpit.vertices[4].y -= 10; geomCockpit.vertices[4].z += 20; geomCockpit.vertices[5].y -= 10; geomCockpit.vertices[5].z -= 20; geomCockpit.vertices[6].y += 30; geomCockpit.vertices[6].z += 20; geomCockpit.vertices[7].y += 30; geomCockpit.vertices[7].z -= 20; var cockpit = new THREE.Mesh(geomCockpit, matRed); cockpit.castShadow = true; cockpit.receiveShadow = true; this.mesh.add(cockpit);

//engine var geomEngine = new THREE.BoxGeometry(20, 50, 50, 1, 1, 1); var engine = new THREE.Mesh(geomEngine, matWhite); engine.position.x = 50; engine.castShadow = true; engine.receiveShadow = true; this.mesh.add(engine);

//tail var geomTailPlane = new THREE.BoxGeometry(15, 20, 5, 1, 1, 1); var tailPlane = new THREE.Mesh(geomTailPlane, matRed); tailPlane.position.set(-40, 25, 0); tailPlane.castShadow = true; tailPlane.receiveShadow = true; this.mesh.add(tailPlane);

//wings var geomSideWing = new THREE.BoxGeometry(30, 5, 120, 1, 1, 1); var sideWing = new THREE.Mesh(geomSideWing, matRed); sideWing.position.set(0, 15, 0); sideWing.castShadow = true; sideWing.receiveShadow = true; this.mesh.add(sideWing);

//windshield var geomWindShield = new THREE.BoxGeometry(3, 15, 20, 1, 1, 1); var matWindShield = new THREE.MeshPhongMaterial({ color: Colors.white, transparent: true, opacity: 0.3, shading: THREE.FlatShading }); var windshield = new THREE.Mesh(geomWindShield, matWhite); windshield.position.set(5, 27, 0); windshield.castShadow = true; windshield.receiveShadow = true; this.mesh.add(windshield);

//propeller var geomPropeller = new THREE.BoxGeometry(20, 10, 10, 1, 1, 1); geomPropeller.vertices[4].y -= 5; geomPropeller.vertices[4].z += 5; geomPropeller.vertices[5].y -= 5; geomPropeller.vertices[5].z -= 5; geomPropeller.vertices[6].y += 5; geomPropeller.vertices[6].z += 5; geomPropeller.vertices[7].y += 5; geomPropeller.vertices[7].z -= 5;

this.propeller = new THREE.Mesh(geomPropeller, matBrown); this.propeller.castShadow = true; this.propeller.receiveShadow = true;

//blades var geomBlade = new THREE.BoxGeometry(1, 80, 10, 1, 1, 1); var blade1 = new THREE.Mesh(geomBlade, matBrownDark); blade1.position.set(8, 0, 0); blade1.castShadow = true; blade1.receiveShadow = true;

var blade2 = blade1.clone(); blade2.rotation.x = Math.PI / 2; blade2.castShadow = true; blade2.receiveShadow = true;

this.propeller.add(blade1); this.propeller.add(blade2); this.propeller.position.set(60, 0, 0); this.mesh.add(this.propeller);

//wheels var wheelProtecGeom = new THREE.BoxGeometry(30, 15, 10, 1, 1, 1); var wheelProtecR = new THREE.Mesh(wheelProtecGeom, matRed); wheelProtecR.position.set(25, -20, 25); this.mesh.add(wheelProtecR);

var wheelProtecL = wheelProtecR.clone(); wheelProtecL.position.z = -wheelProtecR.position.z; this.mesh.add(wheelProtecL);

var wheelGeom = new THREE.BoxGeometry(24, 24, 4); var wheelR = new THREE.Mesh(wheelGeom, matBrownDark); wheelR.position.set(25, -28, 25);

var wheelAxisGeom = new THREE.BoxGeometry(10, 10, 6); var wheelAxis = new THREE.Mesh(wheelAxisGeom, matBrown); wheelR.add(wheelAxis); this.mesh.add(wheelR);

var wheelL = wheelR.clone(); wheelL.position.z = -wheelR.position.z; this.mesh.add(wheelL);

var wheelB = wheelR.clone(); wheelB.scale.set(0.5, 0.5, 0.5); wheelB.position.set(-35, -5, 0); this.mesh.add(wheelB);

var suspensionGeom = new THREE.BoxGeometry(4, 20, 4); suspensionGeom.applyMatrix(new THREE.Matrix4().makeTranslation(0, 10, 0)); var suspension = new THREE.Mesh(suspensionGeom, matRed); suspension.position.set(-35, -5, 0); suspension.rotation.z = -0.3; this.mesh.add(suspension);

this.pilot = new Pilot(); this.pilot.mesh.position.set(-10, 27, 0); this.mesh.add(this.pilot.mesh);

this.mesh.castShadow = true; this.mesh.receiveShadow = true;};

var Cloud = function () { this.mesh = new THREE.Object3D(); var geom = new THREE.BoxGeometry(20, 20, 20); var mat = new THREE.MeshPhongMaterial({ color: Colors.white });

var nBlocs = 3 + Math.floor(Math.random() * 3); for (var i = 0; i < nBlocs; i++) { var m = new THREE.Mesh(geom, mat);

var s = 0.1 + Math.random() * 0.9;

m.position.x = i * 15; m.position.y = Math.random() * 10; m.position.z = -5 + Math.random() * 5;

m.rotation.z = Math.random() * Math.PI * 2; m.rotation.y = Math.random() * Math.PI * 2;

m.scale.set(s, s, s); m.castShadow = true; m.receiveShadow = true;

this.mesh.add(m); }};

var Sky = function () { this.mesh = new THREE.Object3D(); this.nClouds = 20; var stepAngle = (Math.PI * 2) / this.nClouds;

for (var i = 0; i < this.nClouds; i++) { var c = new Cloud(); var a = stepAngle * i; var h = 750 + Math.random() * 200; var s = 1 + Math.random() * 2;

c.mesh.position.y = Math.sin(a) * h; c.mesh.position.x = Math.cos(a) * h; c.mesh.position.z = -400 - Math.random() * 300;

c.mesh.rotation.z = a + Math.PI / 2;

c.mesh.scale.set(s, s, s);

this.mesh.add(c.mesh); }};

var Sea = function () { var geom = new THREE.SphereGeometry(700, 50, 50);

geom.applyMatrix(new THREE.Matrix4().makeRotationX(-Math.PI / 2)); geom.mergeVertices();

var l = geom.vertices.length; this.waves = [];

for (var i = 0; i < l; i++) { var v = geom.vertices[i]; this.waves.push({ x: v.x, y: v.y, z: v.z, ang: Math.random() * Math.PI * 2, amp: 5 + Math.random() * 15, speed: 0.016 + Math.random() * 0.032 }); }

var mat = new THREE.MeshLambertMaterial({ color: Colors.blue, shading: THREE.FlatShading, transparent: true, opacity: 0.8 });

this.mesh = new THREE.Mesh(geom, mat); this.mesh.receiveShadow = true;};

Sea.prototype.moveWaves = function () { var verts = this.mesh.geometry.vertices; var l = verts.length;

for (var i = 0; i < l; i++) { var v = verts[i]; var vprops = this.waves[i];

v.x = vprops.x + Math.cos(vprops.ang) * vprops.amp; v.y = vprops.y + Math.sin(vprops.ang) * vprops.amp;

vprops.ang += vprops.speed; }

this.mesh.geometry.verticesNeedUpdate = true; sea.mesh.rotation.z += 0.005;};

function normalize(v, vmin, vmax, tmin, tmax) { var nv = Math.max(Math.min(v, vmax), vmin); var dv = vmax - vmin; var pc = (nv - vmin) / dv; var dt = tmax - tmin; var tv = tmin + pc * dt; return tv;}

function createPlane() { airplane = new AirPlane(); airplane.mesh.scale.set(0.25, 0.25, 0.25); airplane.mesh.position.y = -100; scene.add(airplane.mesh);}

function updatePlane() { var targetX = normalize(mousePos.x, -1, 1, -100, 100); var targetY = normalize(mousePos.y, -1, 1, 25, 175);

airplane.mesh.position.y += (targetY - airplane.mesh.position.y) * 0.1;

airplane.mesh.rotation.x = (airplane.mesh.position.y - targetY) * 0.0064; airplane.mesh.rotation.z = (targetY - airplane.mesh.position.y) * 0.0128; airplane.propeller.rotation.x += 0.3;}

function createSky() { sky = new Sky(); sky.mesh.position.y = -600; sky.mesh.position.z = 400; scene.add(sky.mesh);}

function createSea() { sea = new Sea(); sea.mesh.position.y = -700; sea.mesh.position.z = -300; scene.add(sea.mesh);}

function createLights() { hemisphereLight = new THREE.HemisphereLight(0xaaaaaa, 0x000000, 0.9);

ambientLight = new THREE.AmbientLight(0xdc8874, 0.5);

shadowLight = new THREE.DirectionalLight(0xffffff, 0.9);

shadowLight.position.set(150, 350, 350); shadowLight.castShadow = true;

shadowLight.shadow.camera.left = -400; shadowLight.shadow.camera.right = 400; shadowLight.shadow.camera.top = 400; shadowLight.shadow.camera.bottom = -400; shadowLight.shadow.camera.near = 1; shadowLight.shadow.camera.far = 1000;

shadowLight.shadow.mapSize.width = 2048; shadowLight.shadow.mapSize.height = 2048;

scene.add(hemisphereLight); scene.add(ambientLight); scene.add(shadowLight);}

function handleWindowResize() { HEIGHT = window.innerHeight; WIDTH = window.innerWidth; renderer.setSize(WIDTH, HEIGHT); camera.aspect = WIDTH / HEIGHT; camera.updateProjectionMatrix();}

function handleMouseMove(e) { var tx = -1 + (e.clientX / WIDTH) * 2; var ty = 1 - (e.clientY / HEIGHT) * 2; mousePos = { x: tx, y: ty };}

function createScene() { HEIGHT = window.innerHeight; WIDTH = window.innerWidth;

scene = new THREE.Scene(); scene.fog = new THREE.Fog(0xc9c9b0, 100, 950);

aspectRatio = WIDTH / HEIGHT; fieldOfView = 65; nearPlane = 0.1; farPlane = 10000;

camera = new THREE.PerspectiveCamera( fieldOfView, aspectRatio, nearPlane, farPlane ); camera.position.x = 0; camera.position.y = 100; camera.position.z = 170;

renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); renderer.setSize(WIDTH, HEIGHT); renderer.shadowMap.enabled = true;

container = document.getElementById("world"); container.appendChild(renderer.domElement);

window.addEventListener("resize", handleWindowResize, false);}

function loop() { airplane.pilot.updateHairs(); sea.moveWaves(); sky.mesh.rotation.z += 0.005; updatePlane(); renderer.render(scene, camera); requestAnimationFrame(loop);}

function init() { createScene(); createLights(); createPlane(); createSea(); createSky(); document.addEventListener("mousemove", handleMouseMove, false); loop();}

window.addEventListener("load", init, false);


源码:

https://codepen.io/seanfree/pen/gwpmPJ


体验:

https://codepen.io/seanfree/full/gwpmPJ



感谢您的阅读      

在看点赞 好文不断  

初识Threejs
初识 Three.js 的奇妙世界,走进三维空间,与小编一起拥抱前端技术,涉及WebGL、WebGPU、Threejs、Shader、GIS、VR/AR、数字孪生、3D图形学等。
 最新文章