返回列表 发新帖

babylonjs教程(29)-用Babylon.js创建一个小的3D游戏

[复制链接]

138

主题

171

帖子

2564

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
2564
发表于 2016-6-23 23:49:27 | 显示全部楼层 | 阅读模式
理解DeviceOrientation事件通过使用Babylon.js创建一个小的3D游戏

IE11对一些新的十分酷的DOM事件做了支持:DeviceOrientation事件。这是事件提供了当前硬件物理的方向(orientation)和运动。

W3C发布了这个事件的说明书:http://www.w3.org/TR/orientation-event/

这篇文章将会展示在一个具有"Amiga"球的3D游戏中如何使用这个事件。

想试一试吗?去这里吧(你可以使用设备方向或者光标键)

DeviceOrientation事件和Babylon.js

在详细看DeviceOrientation说明书之前,你可以看下面这个视频展示了device orientation在babylon.js的场景里面的使用。正如你所看到的它在摇动!

DeviceOrientation事件是如何工作的

这里有两种类型的数据暴露给DeviceOrientation事件:

  • Orientation (deviceorientation): 这个值定义了物理设备的方向相对于地图为中心的坐标系。他用度表示。三个坐标被提供:

                1.  alpha: 绕z轴渲染的值
                2. beta: 绕x轴渲染的值
                3. gamma: 绕y轴渲染的值
这个坐标轴定义使用右手法则:

4263.image_thumb_61665066.png

为了理解这个值,让我从你手中的设备开始吧:

5280.image_thumb_3024397B.png

alpha方向改变是你让设备沿着z轴移动:

8802.image_thumb_19AE489C.png

beta方向改变是你让设备沿着x轴移动:

1205.image_76173BA5.png

最后,gamma方向改变是你让设备沿着y轴移动:

3632.image_thumb_1FFEA047.png

2.  Motion (devicemotion): 定义了沿着每个轴(x,y,z)的加速度。这个值用m/s²表示并且还能包括(或者不)重力的影响。这个值也能提供沿每个轴的旋转速率(deg/s)
要想获得更多的信息,可以看MSDN文档.aspx)
方向获取你可以通过使用window上触发的 “deviceorientation” 事件:
  1. window.addEventListener("deviceorientation", moveBall);
  2. function moveBall(evt) {
  3.     if (evt.alpha < 5 || evt.alpha > 355) {
  4.         ball.position.z += 0.1;

  5.     }
  6. }
复制代码

运动获取你可以通过使用window上触发的 “devicemotion” 事件:
  1. window.addEventListener("devicemotion", detectShake); function detectShake(evt) { var accl = evt.acceleration; if (accl.x > 1.5 || accl.y > 1.5 || accl.z > 1.5) { // Tilt onLose(); } }
复制代码

这些事件的第一个明显的用途是控制一个游戏。你还可以将它们用于手势识别或者用于检测使用者的方位为了与使用相应的地图。

创建球游戏

这个游戏会创建一个简单的球,在游戏中你必须控制球并让它移动到指定的点,来增加分数。

6866.image_thumb_367B8192.png

粒子系统在又上角定了了球要移动到的位置。一旦球移动到了好的位置,你将会得分。

每次你获得一分,球的速度就会增加,游戏场会旋转来增加难度。

所以首先,你要创建一个简单的html文件并引用babylon.js(难以置信的!)
  1. <!DOCTYPE html>
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4.     <title>Device orientation - ball game</title>
  5.     <link href="index.css" rel="stylesheet" />
  6.     <script src="babylon.js"></script>
  7. </head>
  8. <body>
  9.     <canvas id="renderCanvas"></canvas>   
  10.     <div id="score">Score: 0</div>
  11.     <div id="speed">Speed: 1</div>
  12.     <div id="gameOver" class="hidden">
  13.         <div id="gameOverText">Game Over</div>
  14.     </div>
  15.     <script src="index.js"></script>
  16. </body>
  17. </html>
复制代码

在index.js里面,我们可以创建我们游戏需要的3D环境。第一件事情是创建引擎和场景:
  1. var canvas = document.getElementById("renderCanvas");
  2. canvas.width = canvas.clientWidth;
  3. canvas.height = canvas.clientHeight;

  4. if (BABYLON.Engine.isSupported()) {

  5.     var engine = new BABYLON.Engine(canvas, true);
  6.     var scene = new BABYLON.Scene(engine);
  7.     var light = new BABYLON.DirectionalLight("light", new BABYLON.Vector3(2, -10, 5), scene);
  8.     var camera = new BABYLON.ArcRotateCamera("camera", 3 * Math.PI / 2.0, Math.PI / 4.0, 20.0, new BABYLON.Vector3(0, 0, 0), scene);

  9.     scene.activeCamera = camera;
复制代码

你也需要一个渲染循环来确保没一帧被绘制在canvas上面:
  1. engine.runRenderLoop(function () {
  2.     scene.render();

  3.     if (!started) {
  4.         return;
  5.     }

  6. });
复制代码

对于现在,屏幕有点空:

1638.image_thumb_120E9F63.png

然后我们创建一个星空来得到一个比较酷的背景。将作为粒子系统被创建。
  1. // 星空
  2. var starfield = new BABYLON.ParticleSystem("particles", 4000, scene);
  3. starfield.particleTexture = new BABYLON.Texture("star.png", scene);
  4. starfield.minAngularSpeed = -4.5;
  5. starfield.maxAngularSpeed = 4.5;
  6. starfield.minSize = 0.5;
  7. starfield.maxSize = 1.0;
  8. starfield.minLifeTime = 0.5;
  9. starfield.maxLifeTime = 2.0;
  10. starfield.minEmitPower = 0.5;
  11. starfield.maxEmitPower = 1.0;
  12. starfield.emitRate = 600;
  13. starfield.blendMode = BABYLON.ParticleSystem.BLENDMODE_ONEONE;
  14. starfield.minEmitBox = new BABYLON.Vector3(-25, 0, -25);
  15. starfield.maxEmitBox = new BABYLON.Vector3(25, 0, 25);
  16. starfield.direction1 = new BABYLON.Vector3(0, 1, 0);
  17. starfield.direction2 = new BABYLON.Vector3(0, 1, 0);
  18. starfield.color1 = new BABYLON.Color4(0, 0, 0, 1);
  19. starfield.color2 = new BABYLON.Color4(1, 1, 1, 1);
  20. starfield.gravity = new BABYLON.Vector3(0, 5, 0);
  21. starfield.emitter = new BABYLON.Vector3(0, -2, 0);
  22. starfield.start();
复制代码

想获得更多粒子系统的信息,可以去这里
现在开始有一个好的样子了:

4111.image_thumb_73C6791D.png

然后我们创建一个球(一个简单的球体),添加了一个材料和准备的一个动画(当得到一分的时候使用)
  1. // Ball
  2. var ball = BABYLON.Mesh.CreateSphere("ball", 16, 1.0, scene, false);
  3. var ballMaterial = new BABYLON.StandardMaterial("ballMaterial", scene);
  4. ballMaterial.diffuseColor = new BABYLON.Color3(1, 0, 0);
  5. ballMaterial.diffuseTexture = new BABYLON.Texture("amiga.jpg", scene);
  6. ballMaterial.diffuseTexture.uScale = 3;
  7. ballMaterial.diffuseTexture.vScale = 4;
  8. ball.material = ballMaterial;
  9. ball.position = new BABYLON.Vector3(0, 0.5, 0);
  10. ball.renderingGroupId = 1;
  11. ball.rotationQuaternion = BABYLON.Quaternion.RotationYawPitchRoll(0, 0, 0);
  12. var animationScale = new BABYLON.Animation("scale", "scaling", 30, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
  13. animationScale.setKeys([{ frame: 0, value: new BABYLON.Vector3(1, 1, 1) }, { frame: 20, value: new BABYLON.Vector3(2.0, 2.0, 2.0) },
  14.                         { frame: 40, value: new BABYLON.Vector3(1, 1, 1) }]);
  15. ball.animations.push(animationScale);
复制代码

请注意ball.renderingGroupId = 1的使用它允许球(和操场在星空不同的层上来避免星星穿越操场)。
更多创建简单对象的信息可以在这里找到。
更多关于材料的信息可以在这里找到。
更多关于动画的信息可以在这里找到。
球在世界的中心:

2746.image_thumb_46D3A6F3.png

操作场是一个简单的平面纹理有木头图片
  1. // Playground
  2. var ground = BABYLON.Mesh.CreateGround("ground", 20, 20, 1, scene, false);
  3. var groundMaterial = new BABYLON.StandardMaterial("groundMaterial", scene);
  4. groundMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
  5. groundMaterial.diffuseTexture = new BABYLON.Texture("wood.png", scene);
  6. groundMaterial.diffuseTexture.uScale = 2;
  7. groundMaterial.diffuseTexture.vScale = 2;
  8. ground.material = groundMaterial;
  9. ground.receiveShadows = true;
  10. ground.renderingGroupId = 1;
复制代码

游戏基本准备好:

0042.image_thumb_7E416A34.png

为了添加更真实的效果,让我们添加一些阴影:
  1. // Shadows
  2. var shadowCaster = new BABYLON.ShadowGenerator(1024, light);
  3. light.position = new BABYLON.Vector3(-4, 14, -12.5);
  4. shadowCaster.useVarianceShadowMap = true;
  5. shadowCaster.getShadowMap().renderList.push(ball);
复制代码

你可以获得更多关于阴影的信息在这里
现在看起来十分的棒:

1682.image_thumb_27071242.png

最后是添加它为目标(即到哪里会获得一分),这里我们也使用粒子系统:
  1. // Target
  2. var target = new BABYLON.ParticleSystem("particles", 4000, scene);
  3. target.particleTexture = new BABYLON.Texture("star.png", scene);
  4. target.minAngularSpeed = -4.5;
  5. target.maxAngularSpeed = 4.5;
  6. target.minSize = 0.5;
  7. target.maxSize = 3.0;
  8. target.minLifeTime = 0.5;
  9. target.maxLifeTime = 2.0;
  10. target.minEmitPower = 0.5;
  11. target.maxEmitPower = 1.0;
  12. target.emitRate = 200;
  13. target.blendMode = BABYLON.ParticleSystem.BLENDMODE_ONEONE;
  14. target.minEmitBox = new BABYLON.Vector3(-1, 0, -1);
  15. target.maxEmitBox = new BABYLON.Vector3(1, 0, 1);
  16. target.direction1 = new BABYLON.Vector3(0, 1, 0);
  17. target.direction2 = new BABYLON.Vector3(0, 1, 0);
  18. target.color1 = new BABYLON.Color4(1, 1, 0, 1);
  19. target.color2 = new BABYLON.Color4(1, 1, 1, 1);
  20. target.gravity = new BABYLON.Vector3(0, 5, 0);
  21. target.emitter = new BABYLON.Vector3(8, 0, 8);
  22. target.renderingGroupId = 1;
  23. target.start();
复制代码

我们的游戏已经准备好了被玩:

7167.image_thumb_418E415F.png

添加DeviceOrientation事件到我们的游戏

使用DeviceOrientation事件是十分简单的。球将会被设备旋转控制。要做到这样,你需要一些变量来存储当前和以前的旋转值:
  1. var orientationGamma = 0;
  2. var orientationBeta = 0;
  3. var initialOrientationGamma = 0;
  4. var initialOrientationBeta = 0;
复制代码

使用这些变量,这里有代码来检测旋转变化:
  1. // Orientation
  2. window.addEventListener("deviceorientation", moveBall);
  3. function moveBall(evt) {
  4.     if (!started) {
  5.         return;
  6.     }
  7.     if (!initialOrientationGamma) {
  8.         initialOrientationGamma = evt.gamma;
  9.         initialOrientationBeta = evt.beta;
  10.     }

  11.     orientationGamma = evt.gamma;
  12.     orientationBeta = evt.beta;
  13. }

  14. window.addEventListener("devicemotion", detectShake);
  15. function detectShake(evt) {
  16.     var accl = evt.acceleration;
  17.     if (accl.x > 1.5 || accl.y > 1.5 || accl.z > 1.5) {
  18.         // Tilt
  19.         onLose();
  20.     }
  21. }
复制代码

你可以注意到这里gamma和beta值被使用。devicemotion用来被模拟倾斜当你足够快的摇晃设备。
为了简单起见,我这里不会包含onLose(和onWin)函数的代码,但是你可以发现它们在下面可用的游戏源码里面。

然后,你必须使用这些值来更新renderLoop:
  1. engine.runRenderLoop(function () {
  2.     scene.render();


  3.     // Compute direction
  4.     if (orientationGamma) {
  5.         var z = (initialOrientationBeta - orientationBeta) * 0.05;
  6.         var x = (initialOrientationGamma - orientationGamma) * -0.05;
  7.         direction.addInPlace(new BABYLON.Vector3(0, 0, z * speed * scale));
  8.         direction.addInPlace(new BABYLON.Vector3(x * speed * scale, 0, 0));
  9.     }

  10.     // Moving and rotating ball
  11.     ball.position.addInPlace(direction);
  12.     var rotationToApply = BABYLON.Quaternion.RotationYawPitchRoll(0, direction.z * 1.5, -direction.x * 1.5);
  13.     ball.rotationQuaternion = rotationToApply.multiply(ball.rotationQuaternion);

  14.     direction.scaleInPlace(0.95);

  15.     // Collisions
  16.     checkCollisions();
  17. });
复制代码

Gamma控制x方向旋转,beta控制z方向旋转。然后球相应的旋转一点在正确的方向用来模拟滚动。

checkCollisions函数是用来检测球是否在操作场里面和目标是否到达(然后调用onWin函数):
  1. // Collisions
  2. var checkCollisions = function() {
  3.     // Target met
  4.     if (BABYLON.Vector3.Distance(ball.position, target.emitter) < 1.2) {
  5.         onWin();
  6.         return;
  7.     }

  8.     var point = ball.position.clone();
  9.     point.y -= 0.5;
  10.     if (!ground.intersectsPoint(point)) {
  11.         onLose();
  12.     }
  13. };
复制代码

就是这样。现在你有一个现代,美观和“device orientation控制的”游戏。

完整的游戏

网站的游戏也支持光标键。你可以在这里得到代码。随意的使用它作为你自己应用的基础。

回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表