用 Babylon.js 和 JavaScript 写 3D 游戏——从入门到精通(1)

By | 2022/12/25

OK,我们可以正式开始了,在此之前,我们先看看我们最终能获得的游戏成品应该是个什么东西,这样可以让我们对将来的编码之旅提升信心。

星河叛变

海王星中某处正在秘密研究制作宇宙中最强、最冷血的生化战士以便征服地球,强是一位星际货柜车的驾驶在宇宙中跑单,某日认识伙伴麦克和辛蒂二人,在同时也得罪了货运站中最强最有势力的公司,急需离开太空货运站的强在地下货运站中接了一批急件送往地球的诡异货物,途中经过小行星陨石区遭受到了陨石强烈撞击,又遭到星际大盗的洗劫,但这次星际大盗强劫了致命的死神,强和麦克、辛蒂三人如何逃离星际大盗和生化战士的魔掌解救地球的毁灭浩劫?

《Space Truckers》 虽然直译是“星际卡车”,不过被翻译成了《星河叛变》,显得很有档次…… 该片1996年由斯图尔特·戈登执导的冒险片,丹尼斯·霍珀主演。我们做的游戏搬了按照这个电影的背景,让我们驾驶卡车在3D的星际内自由穿梭,达到我们的目标。

BabylonJS 模板

千里之行,始于足下。我们当然不可能一开始就做一个复杂的3D游戏,我们从最最简单的一个模板开始。

尽管被Babylon是采用了TypeScript编写的,如果你使用type script会有非常好的支持,但为了降低入手的难度,让更多的人能够快速进入状态,我们还是使用JavaScript来编写整个代码。后面在合适的时候我们也会使用type script和Vite之类的工具来进行一次演示。

先来看一下使用Babylon的一个HTML模板,后面的代码演示中不会重复html的部分,只会专注讲解JS代码:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
        <title>Babylon Template</title>
        <style>
            html, body { overflow: hidden; width: 100%; height: 100%; margin: 0; padding: 0; }
            #renderCanvas { width: 100%; height: 100%; touch-action: none; }
        </style>
        <script src="https://cdn.bootcdn.net/ajax/libs/babylonjs/5.37.0/babylon.max.js"></script>
    </head>
   <body>
	<canvas id="renderCanvas"></canvas>
	<script>
        const canvas = document.getElementById("renderCanvas");
        const engine = new BABYLON.Engine(canvas, true);
        const createScene = function () {
            // 我们的魔法代码在这里施展
        };
        const scene = createScene();
        engine.runRenderLoop(function () {
            scene.render();
        });
        window.addEventListener("resize", function () {
            engine.resize();
        });
	</script>
   </body>
</html>

下面对这个模板来进行一些简单的讲解。

首先,我们引入了BabylonJS,当然我们这里是通过CDN引入的,如果你愿意也可以直接下载下来,在本地通过路径引用。然后我们在HTML中准备了一个Canvas画板供3D引擎使用,要说明一点的是,Babylon其实是可以在服务器端运行的,也就是说没有Canvas元素也能够运行,当然暂时对我们来说是不需要的。

在后面的JS代码中,我们获得了这个canvas元素,并使用Babylon的默认引擎创建了一个Engine。随后,我们有一个自定义函数来定义舞台,使用了引擎的方法不停地绘制我们的舞台。最后我们注册了一个resize事件,这样当浏览器窗口发生变化的时候引擎可以把绘制的对象自动的缩放以适应我们浏览器窗口的大小。

此时我们如果打开这个网页的话,什么都不会发生。因为我们虽然准备了舞台,但是没有演员,也没有灯光,没有摄像机,所以什么都看不到,所以接下来我们就在这个舞台里添加一些最基本的东西。

开始魔法

var createScene = function () {
    var scene = new BABYLON.Scene(engine);
    // 需要增加摄像机
    var sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 2, segments: 32}, scene);
    return scene;
};

我们后面所有的工作基本都是在写createScene这个函数。这里我们在这个函数中创造了一个场景,然后在场景中增加了一个球。此时运行这个代码还是什么都看不到。为什么呢?因为我们没有摄像机。

    var camera = new BABYLON.ArcRotateCamera("camera", 0, BABYLON.Tools.ToRadians(65), 10, BABYLON.Vector3.Zero(), scene);
    camera.setTarget(BABYLON.Vector3.Zero());
    camera.attachControl(canvas, true);

我们这里创建了一个arc rotate camera。后面会详细介绍这个摄像机的详细参数和作用,暂时只要理解成一个始终对着中心旋转的摄像机就好,第3句代码实际上就是说明我们可以通过鼠标来控制这个摄像机的位置。然后我们运行这个代码:

这……

居然是一团黑色的球,这到底是怎么回事?原来我们还没有加灯光,所以3D的效果完全看不出来。

    var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);

在创建摄像机的代码之后加入这个“神说,要有光”的代码,然后再运行一下,是不是感觉一切都不一样了?我们终于得到了一个3D样子的球。

我们甚至可以用鼠标左键按住了改变视角看看不同的光影

当然仅仅一个球,有的屋子太过无聊了。我们为这个场景中加入更多的东西。下面是完整的代码:

        var createScene = function () {
            var scene = new BABYLON.Scene(engine);

            var camera = new BABYLON.ArcRotateCamera("camera", 0, BABYLON.Tools.ToRadians(65), 10, BABYLON.Vector3.Zero(), scene);
            camera.setTarget(BABYLON.Vector3.Zero());
            camera.attachControl(canvas, true);

            var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
            light.intensity = 0.7; // 把灯光调暗一些

            var sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 2, segments: 32}, scene);
            sphere.position.y = 1; // 把球的位置往“上”提一点让它坐落在平面上
            var ballMat = new BABYLON.StandardMaterial("sphere material", scene);
            ballMat.diffuseColor = BABYLON.Color3.Green();
            ballMat.alpha = 0.6;
            sphere.material = ballMat;

            // 再创建一个平面
            var ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 6, height: 6}, scene);
            const groundMaterial = new BABYLON.StandardMaterial("Ground Material", scene);
            ground.material = groundMaterial;
            const groundTexture = new BABYLON.Texture("noutfound.png", scene);
            ground.material.diffuseTexture = groundTexture;

            return scene;
      };

这个代码看起来复杂多了,但其实大部分都是上面讲述过的代码,只是加了一个方格棋盘平面而已,然后稍微把球的材质颜色改变了一下。当我们运行这段代码的时候,就能得到一个比较复杂比较有趣的场景,我们可以看到一个透明的玻璃球在棋盘上,如果我们用鼠标转动这个场景的时候,不仅能够看到光影的变化,还能看到玻璃材质后棋盘图像。

如果对3D引擎和模型稍微有一点了解的同学可能会有点奇怪,玻璃球的绿色和透明可以直接通过代码调整,但后面的棋格上图样是哪来的,我们并没有一个叫”notfound.png”的贴图啊?这里其实使用了一个很没用的小技巧,当Babylon里面无法加载贴图的时候,它的贴图就是这样棋盘的形状,正好让我们来当作棋盘的贴图。

当然,代码中还有非常多的疑问,比如灯光和摄像机的参数和类型是什么意思,以及最后一个例子中出现的BABYLON.StandardMaterial这样一个奇怪的东西,它又是什么呢?肯定会有一些习惯探究根本的同学会觉得很不明白,甚至有些云里雾里。没有关系我们在下个章节中,就会把这些东西讲解清楚的,今天其实知识为了让大家,创建一个3D的场景是如何的简单。

额外说明

下面是对一些没有太多代码基础的人的一些友好提示。

  1. 再次重申,你应该有一些代码基础,了解HTML,CSS和JS才能进入我们的教程。
  2. 建议使用vs code这个编辑器来编写我们的代码,它不仅免费,而且插件众多。,运行的也非常高效,对JS和ts都有很高的支持程度。
  3. 我们可以在vs code中增加live preview这样的插件。这样的话,当你编写代码的时候就可以直接看到HTML结果。 同时因为这个插件实际上是启动了一个HTTP服务器,所以后面如果我们使用一些图片模型等素材,使用这样的这个插件也可以避免因为安全限制而导致本地文件加载失败的问题。
    当然,如果你有一些自己喜欢的方式,比如Python、node或者什么工具启动一个服务器来运行的话也可以。

4 thoughts on “用 Babylon.js 和 JavaScript 写 3D 游戏——从入门到精通(1)

      1. xishui Post author

        会有的,初期入门用最简单的,后面比较完整的例子会用“现代化”的方式来。

        Reply
  1. Alex

    今天加班心想到大学时看过的目光博客,在想现在博主是不是还在鸽。结果上来一看博主还开了新坑。虽然时过境迁物是人非,但总有些故人旧事留下丝丝痕迹,真好!

    Reply

发表评论

您的电子邮箱地址不会被公开。