注:Babylon.js 采用单一仓库架构,除了Babylon.js核心模块,还包含众多工具和应用程序,构建系统较为复杂; Babylon.js仓库中的buildSystem.md(commit d8e6de1)文档,对构建系统进行了较为详细的说明。本文为个人在学习中,对该文档进行的翻译,分享出来,希望有所裨益。如有纰漏改进之处,欢迎留言。
开始之前
除非特别说明,所有本文档中提及的命令都应该在项目的根目录下(BABYLON.js/)执行。
开始
前置需求
本仓库基于NPM workspace架构,要求npm版本为7及以上。推荐npm版本是8,其推荐Node的版本为14及以上。
如果你还没有安装node,请到这里下载安装:nodejs.org/en/download…,或者使用nvm(稍后解释)管理安装的版本。
安装后,使用如下命令检查所安装的npm和node版本:
npm --version
node --version
如果你需要安装多个版本的node和npm,使用nvm工具进行管理:
- windows:github.com/coreybutler…
- Unix-like:github.com/nvm-sh/nvm
这个可能也可以使用: github.com/coreybutler…
注意有时候在使用nvm更新npm的时候,命令npm update npm -g
可能不能正确工作(主要是在windows上,且从npm 6之上更新时)。这里有一些建议方案。有一个我们亲测有效:
- 在AppData/Roaming/nvm中 cd 到正确的node版本的目录
- 执行
npm install npm
这里的建议可能有用:github.com/coreybutler…
建议
使用VSCode时,如果想完全在VSCode中进行调试,则需要安装仓库推荐的VSCode扩展插件。
安装必需的库
在根目录下执行 npm install
,安装所有仓库所需的库。
建议在开始之前执行 npm run build:dev
,以准备好所有代码。
不想再等,立即试用
使用VSCode吗?如果是,请安装推荐的扩展插件,然后启动 Run and Watch Dev Host
命令进行编译并启动 esm版本的dev host。 如果基于经典的UMD模式(该模式下提供BABYLON命名空间)开发,请运行 Run and Watch Babylon Server
。
执行 npm run build:dev
命令对源码,着色器和资源进行构建。
【译注】dev host 是一个应用工具,基于esm方式是babylon.js的模块代码,在开发中用于测试babylon.js。
执行命令 npm run serve -w @tools/dev-host
,将会在http://localhost:1338地址上启动dev host服务,服务的代码在packages/tools/devHost/src。
注意,以上命令启动的服务不会监视 dev目录录下的代码变化,只能监视到packages/tools/devHost/src 目录下的变化。
要监视dev包的变化,应在另一个终端上执行 npm run watch:dev
。这样便能监视dev包目录下的所有变化,包括着色器和资源,比如scss文件,图片文件等。
除了dev host,还有很多其它使用该仓库的选择,下文将进行阐述。
TODO列表
- 完成构建工具文档
- 完成 CDN 包(如post production和serializers)【译注】post production和serializers是dev中的包
- 添加更多调试目标和任务(确保VSCode能覆盖一切调试工作)
- 完成HTTPS支持
- 将recast-detour转移到这个仓库
- 升级recast-detour到基于模块架构
- 将所有的当前测试从主目录中移动到相应的包目录下
- 为materials, procedural textures 添加测试
- 在测试目录下添加测试并正确运行
- 简化LTS的开发(使用LTS监视进行开发)
- 运行一个依赖项目的终极测试
- 修改日志(带标签)
简要指南
这个章节适用于没有耐心的读者。不探讨具体的原理,只是为开发本仓库列举必要的工作任务。
在VSCode中使用快键键Ctrl+P (mac上为 Command+P)打开任务面板,输入 “task”,然后输空格,从下拉框中选择想要执行的任务。
运行 babylon server
【译注】babylon server基于UMD模式构建babylon,可以提供babylon.js的cdn服务。
使用 VSCode:
- 运行 “CDNServe and watch (Dev)” 任务
- 如果要调试, 则从调试菜单中选择运行 “Run and watch Babylon Server”
使用命令行:
- 执行
npm run watch:dev
(如果想更改 dev 包. 否则执行npm run build:dev
) - 在另一个终端中执行
npm run serve -w @tools/babylon-server
babylon server提供两种形态 – js 和 ts。通过http://localhost:1337访问js版本(默认),通过http://localhost:1337/index-ts访问ts版本。对应的代码为babylon server包的src目录下的sceneJs.js 和 sceneTs.ts,可以进行编辑。
下一节将会介绍,babylon server还提供了playground的片段调试能力。
playground 片段调试
要调试一个代码片段(snippet),需在index后添加片段并将babylon server在调试模式下运行起来,例如:
http://localhost:1337#IQN716#9
如果是在VSCode中启动的服务,则可以直接在IDE中进行调试。
注意,hash值变化后,加载程序会进行响应(重新加载场景),但不会保存新的数据。需要运行playground本身才行。
运行 dev host
使用VSCode:
- 启动 “Run and watch Dev Host (Dev)” 任务
- 或者只是测试而不更改babylon核心包, 启动 “Run Dev Host (Demo)” 任务
- 如果需要调试, 从调试菜案运行 “Run and watch dev host (Dev)”
使用命令行:
- 执行
npm run watch:dev
(如果想更改 dev 包. 否则执行npm run build:dev
) - 在另一个终端中执行
npm run serve -w @tools/dev-host
在浏览器中打开 http://localhost:1338 。
运行 playground
使用VSCode:
- 启动任务 “Playground Serve for core (Dev)”
- 如果只是开发Playground本身, 则运行任务 “Playground Serve (Dev)”
- 如果需要调试, 则需从调试菜单运行 “Launch Playground (chrome)” (或者 “Playground development”)
使用命令行:
- 执行
npm run watch:dev
(如果想更改 dev 包. 否则执行npm run build:dev
) - 另起命令行窗口执行
npm run serve -w @tools/babylon-server
- 另起命令行窗口执行
npm run serve -w @tools/playground
快捷方式
- 执行
npx build-tools --command dev-watch --watch-assets --watch-declarations --serve
(同时启动监控和服务) - 另起命令行窗口执行
npm run serve -w @tools/playground
在浏览器中打开 http://localhost:1338 。
运行 sandbox
使用VSCode:
- 启动任务 “Sandbox Serve for core (Dev)”
- 如果只是开发sandbox本身, 则运行任务 “Sandbox Serve (Dev)”
- 如果需要调试, 则从调试菜单运行 “Launch Sandbox (chrome)” (或者 “Sandbox development”)
使用命令行:
- 执行
npm run watch:dev
(如果想更改 dev 包. 否则执行npm run build:dev
) - 另起命令行窗口执行
npm run serve -w @tools/babylon-server
- 另起命令行窗口执行
npm run serve -w @tools/sandbox
快捷方式
- 执行
npx build-tools --command dev-watch --watch-assets --watch-declarations --serve
(同时启动监控和服务) - 另起命令行窗口执行
npm run serve -w @tools/sandbox
在浏览器中打开 http://localhost:1338 。
运行 gui editor
使用VSCode:
- 启动任务 “GUI Editor Serve for core (Dev)”
- 如果只是开发gui editor本身, 则运行任务 “GUI Editor Serve (Dev)”
- 如果需要调试, 则从调试菜单运行 “Launch GUI Editor (chrome)” (或者 “GUI Editor development”)
使用命令行:
- 执行
npm run watch:dev
(如果想更改 dev 包. 否则执行npm run build:dev
) - 另起命令行窗口执行
npm run serve -w @tools/babylon-server
- 另起命令行窗口执行
npm run serve -w @tools/gui-editor
快捷方式?
- 执行
npx build-tools --command dev-watch --watch-assets --watch-declarations --serve
(同时启动监控和服务) - 另起命令行窗口执行
npm run serve -w @tools/gui-editor
在浏览器中打开 http://localhost:1338
运行 tests
使用VSCode:
- 启动任务
Run unit tests
或run visualization tests
- 调试 – 启动
Run and debug unit tests
或Run and debug visualization tests
使用命令行:
- 执行
npm run test:unit
或npm run test:visualization
- 要运行所有测试,执行
npm run test
或npx jest
将公开项目连接到外部项目
一个示例是连接core
。也可以是其它包。
- 构建你想连接的公开包:
-
npx nx build babylonjs
(当构建es6包时去除@babylonjs前缀,即npx nx build core
)
【译注】npx nx build babylonjs
项目在public/umd下,npx nx build core
对应项目在public/@babylon之下。 - 如果使用es6包,运行
npm run prepublishOnly -w @babylonjs/core
- 运行
npm link -w @babylonjs/core
【译注】npm link
命令将包发布到本地的全局node_modules中(其实是在全局node_modules中创建符号链接到发布的包)
-
在外部项目中:
- 确认连接项目的版本是正确的
- 执行
npm link @babylonjs/core
当被连接的包发生更新,需要重新构建即可。不需要重新连接,除非重新安装了公开的库。
注意:
- 这个过程很快将进行简化。
- 你可以连接任何包,不仅限公开的包。但是,你不能重命名包。所以,如果你连接 @dev/core, 你需要将 @dev/core加入的项目的依赖表里面。
给项目添加依赖
执行 npm install packageName -w @namespace/package
(添加 -D 来添加 dev-dependency中)
对应项目的package.json 内容将会更新。
注 – npm有一个缺陷, 有些情况下只安装包但为添加依赖到 package.json. 如果发生这种情况,请再执行一次命令.
仓库结构
这个仓库架构类似单仓库架构。每一个包都有自己的package.json且可以独立使用,当然,需要考虑依赖项。
dev
和 lts
目录下的包是组合项目(www.typescriptlang.org/tsconfig#co…),可以用一条命令编译。当进行监视时,所依赖的包在需要时也将自动进行编译。
【译注】composites 项目需要符合一些限定,以便可以进行增量编译,在typescript中可以被其它包引用。
所有的包(除了公共的es6包【译注】:即public/@babylon)的目录结构都是同样的:
- src 目录包含typescript源码和资源
- test 目录包含测试代码(见 测试)
- dist 目录包含编译后的代码,包括代码映射文件和申明文件,以及资源(如果有配置)
- public 目录(可选)包含当项目启动的服务所需的公开的资源(如若适用)
项目的build/watch/test脚本会用到这种目录结构。
在开发阶段,所有的引用指向项目的src目录;当构建时,所有的引用指向项目的dist目录。这意味着,编译项目之前,它所依赖的项目需要编译完成。这一点在构建和监视中需要注意。
所有项目的依赖被提升到了根目录,所以仓库只有一个package-lock.json文件和一个node_modules目录。
NPM 工作空间使得你可以在根目录中执行所有包的命令,包括根包。
在仓库所有的包上, 执行命令 npm <command> -ws
.
在指定包上, 执行命令 npm <command> -w <package>
.
在根包上执行, 执行命令 npm <command>
.
更多关于工作空间的信息参考 – docs.npmjs.com/cli/v7/usin…
名称习惯
- npm 包名称采用小写字母短横线(kebab)式 (如 gui-editor)
- 目录名采用驼峰式(camel)(如 guiEditor)
包的类型
有四种不同包类型:
dev 包
包名称以 @dev
打头. 这是日常开发工作使用的包。
这些包在其dist目录下包含原始资源 , 可以被任何打包器使用 (如 webpack, rollup).
lts 包
包名称以 @lts
打头. 这些包是为仓库的长期支持版. 不时有一些代码被移动到 LTS 包 (例如一些带副作用或者被废弃的功能). LTS 包用于生成公开包.
LTS 包是dev 包的扩展,永远不要有代码重复. 更多请参考LTS.
Tools 包
包名称以 @tools
打头. 这些包包含一些在仓库中使用的工具.
工具有 playground, sandbox, node 和 gui editor.
Public 包
公开包是公开发行到npm的包. 主要基于 LTS包 或者 Tools 包构建. 它们是仅有的不在package.json中标记为 “private” 的包.
运行脚本
要运行一个包中的脚本,可以运行在该包的目录下运行npm run scriptname
,也可以在根目录下运行npm run scriptname -w @namespace/package-name
,后者是推荐的做法。
例如, 构建dev下的 core 包,可以在根目录下:
npm run build -w @dev/core
如果要运行package.json中没有定义的脚本,可以将脚本添加到package.json(不要忘记提交更新),或者在包的目录下使用npx
命令。例如,运行所安装的typescript程序(非全局安装的),可以在包的目录下执行:
npx tsc ....
大部分包都包含有以下脚本:
- build
- compile
- clean
- test
- format
- lint
- watch (工具包里为serve)
这项工作仍在进行中. 这些脚本将被添加到所有包中. 如果你在尝试使用某个脚本时,发现包里没有定义,请告诉我们。
开发
代码构建和监视
要编译最新的代码,请执行:
npm run build:dev
要启动监视dev包的代码更新, 执行下面任一命令:
"format:check"; // 格式检查,
"format:fix"; // 格式修复,
"lint:check"; // 代发分析,
"lint:fix"; // 修复代码分析中的问题,
当代码仓库的issue都解决时,format:check 和 lint:check 会在CI过程执行。
处理资源
dev 包 和 lts 包会将二进制资源文件当作dis目录的一部分。 当打包这些资源时,打包器会决定如何处理(比如webpack中使用url-loader 或者 file-loader)。这些资源,包括二进制媒体文件和(s)css 文件会被监视并在过程中被自动拷贝或处理。要显示的监视指定项目的资源文件,可以在指定项目目录下执行build-tools的 “process assets” 任务(该文档后面会详细记录):
npx build-tools -c process-assets --isCore --watch
同时,在各包的package.json中,应该定义有 build:assets
和 watch:assets
脚本。
着色器也被看作资源。对着色器的处理有一些不同,但也是用同样的脚本。一个着色器(.fx
文件)会生成一个typescript文件,然后在库的构建构成中作为编译的一部分。当构建时,build:assets
脚本会在compile:source
之前运行。build
脚本会做好这些。例如@dev/core
有如下脚本:
"build": "npm run clean && npm run compile",
"precompile": "npm run compile:assets",
"compile": "npm run compile:source",
"compile:source": "tsc -b tsconfig.build.json",
"compile:assets": "build-tools -c process-assets --isCore",
【译注】compile:assets
命令会将着色器(.fx
文件)转换生成.ts文件并放置在.fx文件同样的位置。注:在VSCode的项目浏览器看不到生成的.ts,是因为在.VSCode/settings.json中将其隐藏了。
代码格式化和语法检查(lint)
在仓库的根目录有一个全局的eslint和prettier配置。它可以统一代码结构并在仓库范围进行代码格式化。这些工具同时在.js和.tsx文件上执行。
当使用VSCode是,为了能实时进行代码检查,建议同时使用prettier formatter扩展和eslint。
在命令行中执行代码检查, 运行:
npm run lint
配置选项
大部分包都不需要额外的配置就可以进行编译。然而,在有些情况下,你可能想要控制文件被编译和服务的方式。一个好的例子是打开https支持,或者改变标准的babylon及其它工具的服务端口。
有几种方法可以将配置传入不同的包
命令行参数
构建工具(build tools,稍后解释)和webpack(用于启动工具服务)都接受命令行参数。下面的示例没有展示所有不同的选项。所有的选项列表在本文档后面有介绍。
当运行dev的监视时,可以指定所要监视的包。标准启动dev监视的命令是npm run watch:dev
,你可以按如下两种方法之一添加监控的包:
# 选项 1, 扩展 npm 命令 watch:dev
npm run watch:dev -- --packages core,gui
# 选项 2, 直接运行dev watcher (监视代码和资源)
npx build-tools --command dev-watch --watch-assets --packages core,gui
启动服务命令也是同样的。例如,使用生产模式启动babylon server:
# 选项 1, 扩展 npm 命令 watch:dev
npm run serve -w @tools/babylon-server -- --env=mode=production
# 选项 2 - 在babylon server包下直接使用webpack
npx webpack serve --env=mode=production
上面这个特别的例子中,还可以使用 npm run serve:prod
.
使用 .env 文件
同时运行playground和babylon server,本地有两个服务。playground服务,CDN服务在另一个地址上。如果我们想要同时配置这两个服务,可以使用.env文件去扩展命令行参数。
使用.evn文件,首先在仓库根的目录下创建.env
文件,.env文件将被自动使用。
可以将.env文件当成是扩展process.env
的一种方式。第一个我们需要知道的重要事实是它只在构建期间被使用,而不会在编译的代码中使用到。所以,所设定的参数可以在构建的脚本和构建工具(build tools)里使用,但是不能在playground源代码目录下的.tsx
文件中使用。
要使用.env 文件设置命令行参数,将参数名称转换为大写,并将所有的-
替换成_
。比如要将上面示例的指定包的参数,可以在.env 文件中添加下面这行:
PACKAGES="core,gui"
如果想要在运行dev监视时总是打开资源监视,可以在.env文件中添加下面这行:
WATCH_ASSETS=true
要打开热重启(另一个开启webpack的包的选项),在.env文件中添加下面这行:
ENABLE_HOT_RELOAD=true
ENABLE_LIVE_RELOAD=true
要设置所有(webpack)的构建使用生产模式,在.env文件中如下设置:
NODE_ENV=production
在.env文件中还可以设置的变量有::
TEST_ENGINE="webgl2" # 设置可视化测试使用的引擎
TOOLS_PORT=1338 # 设置工具(如playground 或 the node editor)的端口
CDN_PORT=1337 # 设置babylon server的端口
注意的是.env文件不应该在仓库中更新,它是被仓库忽略的。
可以在文档中查询每个命令所有可用的选项。
项目连接
项目之间的依赖关系是在其package.json中定义的。然而,当使用dev或者lts包时,你可以直接引用相关文件而不需要使用npm包。这意味着,你可以(且应当)使用基本包名直接从所需的库中导入。例如,inspector当中的imports(advancedDynamicTextureTreeItemComponent.tsx)。
// 从 core 包中加载
import { Nullable } from "core/types";
import { Observer, Observable } from "core/Misc/observable";
import { IExplorerExtensibilityGroup } from "core/Debug/debugLayer";
// 从 gui 包中加载
import { Control } from "gui/2D/controls/control";
import { AdvancedDynamicTexture } from "gui/2D/advancedDynamicTexture";
使用dev包名 (在build tools的packageMapping.ts定义) 可以让构建工具选择使用哪个项目 – dev 或者 lts.
保存仓库
有两种基本方式使用dev代码:
Dev Host
【译注】dev host是tools下的一个用户开放的工具性项目
dev host 可以使用typescript 或者 javascript, 利用 webpack打包, 并启动服务. 唯一不可以重命名的文件是 index.ts, 它是主程序入口.
dev host类似我们提供的es6包 – 所有库都需要导入,且没有BABYLON名字空间 如果你想测试一个playground场景,参见下面的Babylon server
如下命令运行dev host:
npm run serve -w @tools/dev-host
这个命令会启动监视dev-host的src目录,同时执行其中的代码。
当在dev host中从不同的包加载时,你需要使用这个包名。例如,如果从dev core包中加载scene对象,可以:
import { Scene } from "@dev/core";
导入代码时需要保持一致,这很重要。 即,不能一部分导入@lts包,而另一部分导入@dev包,如果这样,typescript将会抱怨它们不是相同的对象。
dev host 项目被配置的更加宽容。最好的例子是noImplicitAny
被设置为false。这样你可以同时加载.js 文件和 typescript 文件。这也是allowJs
被设置为true的原因。
dev host当前的简单结构允许你从playground中拷贝代码,做一些必要的更改就可以在dev host中运行。 例如,将下面的playground代码:
var createScene = function () {
// This creates a basic Babylon Scene object (non-mesh)
var scene = new BABYLON.Scene(engine);
// This creates and positions a free camera (non-mesh)
var camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene);
// This targets the camera to scene origin
camera.setTarget(BABYLON.Vector3.Zero());
// This attaches the camera to the canvas
camera.attachControl(canvas, true);
// This creates a light, aiming 0,1,0 - to the sky (non-mesh)
var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
// Default intensity is 1. Let's dim the light a small amount
light.intensity = 0.7;
// Our built-in 'sphere' shape.
var sphere = BABYLON.MeshBuilder.CreateSphere("sphere", { diameter: 2, segments: 32 }, scene);
// Move the sphere upward 1/2 its height
sphere.position.y = 1;
// Our built-in 'ground' shape.
var ground = BABYLON.MeshBuilder.CreateGround("ground", { width: 6, height: 6 }, scene);
return scene;
};
转换成下面这样即可:
import { canvas, engine } from "./index";
import { FreeCamera, HemisphericLight, MeshBuilder, Scene, Vector3 } from "@dev/core";
export const createScene = function () {
// This creates a basic Babylon Scene object (non-mesh)
const scene = new Scene(engine);
// This creates and positions a free camera (non-mesh)
const camera = new FreeCamera("camera1", new Vector3(0, 5, -10), scene);
// This targets the camera to scene origin
camera.setTarget(Vector3.Zero());
// This attaches the camera to the canvas
camera.attachControl(canvas, true);
// This creates a light, aiming 0,1,0 - to the sky (non-mesh)
const light = new HemisphericLight("light", new Vector3(0, 1, 0), scene);
// Default intensity is 1. Let's dim the light a small amount
light.intensity = 0.7;
// Our built-in 'sphere' shape.
const sphere = MeshBuilder.CreateSphere("sphere", { diameter: 2, segments: 32 }, scene);
// Move the sphere upward 1/2 its height
sphere.position.y = 1;
// Our built-in 'ground' shape.
MeshBuilder.CreateGround("ground", { width: 6, height: 6 }, scene);
return scene;
};
更简单的方法是将整个 @dev/core 包加载成为BABYLON:
import { canvas, engine } from "./index";
import * as BABYLON from "@dev/core";
export const createScene = function () {
// This creates a basic Babylon Scene object (non-mesh)
const scene = new BABYLON.Scene(engine);
// This creates and positions a free camera (non-mesh)
const camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -10), scene);
// This targets the camera to scene origin
camera.setTarget(BABYLON.Vector3.Zero());
// This attaches the camera to the canvas
camera.attachControl(canvas, true);
// This creates a light, aiming 0,1,0 - to the sky (non-mesh)
const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
// Default intensity is 1. Let's dim the light a small amount
light.intensity = 0.7;
// Our built-in 'sphere' shape.
const sphere = BABYLON.MeshBuilder.CreateSphere("sphere", { diameter: 2, segments: 32 }, scene);
// Move the sphere upward 1/2 its height
sphere.position.y = 1;
// Our built-in 'ground' shape.
BABYLON.MeshBuilder.CreateGround("ground", { width: 6, height: 6 }, scene);
return scene;
};
注意这里的BABYLON不是真正的BABYLON名字空间,它只是core名字空间,因此即使导入了也并不包含加载器模块。
下面这个扩展的示例展示了怎样利用dev host集成GUI,Loaders 和 inspector。
import { canvas, engine } from "./index";
import "@dev/loaders";
import { Inspector } from "@dev/inspector";
import { ArcRotateCamera, CubeTexture, Scene, SceneLoader } from "@dev/core";
import { AdvancedDynamicTexture, Button } from "@dev/gui";
export const createScene = async function () {
const scene = new Scene(engine);
scene.createDefaultCameraOrLight(true);
const hdrTexture = new CubeTexture("https://playground.babylonjs.com/textures/SpecularHDR.dds", scene);
scene.createDefaultSkybox(hdrTexture, true, 10000);
// The first parameter can be used to specify which mesh to import. Here we import all meshes
SceneLoader.AppendAsync("https://assets.babylonjs.com/meshes/webp/", "webp.gltf", scene, function (_newMeshes) {
scene.activeCamera!.attachControl(canvas, false);
// scene.activeCamera!.alpha += Math.PI; // camera +180
(scene.activeCamera as ArcRotateCamera).radius = 80;
});
const advancedTexture = AdvancedDynamicTexture.CreateFullscreenUI("UI");
const button1 = Button.CreateSimpleButton("but1", "Click Me");
button1.width = "150px"
button1.height = "40px";
button1.color = "white";
button1.cornerRadius = 20;
button1.background = "green";
button1.onPointerUpObservable.add(function() {
alert("you did it!");
});
advancedTexture.addControl(button1);
Inspector.Show(scene, {});
return scene;
};
dev host项目被配置成支持热重载和实时重载,保证当有任何dev host原代码变化时,dev host就会更新(或重新加载)页面。然而,它并不会响应dev 包中的更新,因为它使用的是编译之后的dev包。
如果你想要监视dev包中的源码更新,你需要手动启动监视,或者使用配置好的VSCode 任务(后面详述)。推荐但并非必须使用VSCode。
编译dev包以获得最新的dev host,详见[Initial source build and watch](#Initial source build and watch).
注意dev host并没有采用生产环境的最佳做法,它只是辅助开发dev包的工具。不应该在生产环境中使用。
Babylon Server
Babylon Server引入的新包,是CDN结构的直接拷贝,Babylon Server可以提供javascript文件,以及代码映射和类型声明文件。
和dev host类似,Babylon Server使用最新的从dev(或者lts)包中编译的代码,并提供给浏览器。默认的CDN访问地址是http://localhost:1337。
babylon server的index.html引用了所有的公开包,并以BABYLON名字空间提供出来,类似playground的做法。当仓库初始化时,服务生成两个文件-createScene 和 createEngine。这两个文件不是git仓库的一部分,你可以随意更改。如果需要createScene可以是异步。
如果你想在不运行playground 的情况下调试playground的场景,可以编辑createScene.js(typescript的情况下是sceneTs.ts),然后打开http://localhost:1337/index.html 或者 http://localhost:1337/index-ts.html。
要打开检测器,按 Ctrl+Shift+U (mac Cmd+Shift+U).
要启动 babylon server, 运行:
npm run serve -w @tools/babylon-server
这个命令会在端口1337上启动一个新的服务提供所有需要的文件,包括一个非常简单的index.html文件,使得cdn工作起来。
babylon server还提供其它文件,使得工具如playground可以正确工作起来。它还提供如physics engine, earcut, draco decoder等库,详细信息见 babylon server包下的public
目录。这些文件以static文件的形式提供。
同dev host一样,如果你想要监视其它包的代码变化,你需要手动运行监视/编译,或者使用VSCode配置好的任务(后面详述)。推荐但并非必须使用VSCode。编译和监视dev包的内容,详见[Initial source build and watch](#Initial source build and watch).
所有需要编译的包的情况都可以使用babylon server,正如playground那样。
为了使用babylon server,先运行babylon server (npm run serve -w @tools/babylon-server
),然后在另一个终端启动playground。我们总是为playground在VSCode中准备好了任务和启动配置。
几个注意点:
- 考虑到性能 – 最小化的代码加载更快(至少应该如此)。这意味着在生产模式下运行服务(使用
npm run serve:prod -w @tools/babylon-server
)会有利于依赖编译后的代码运行的包更快的运行。然而,构建的时间大概会两倍之慢。如果你想在使用 gui editor 或者 playground工具时用到它(基于它们而非同这些包一起工作 – 即更新这些包的代码),我们推荐在生产环境中使用babylon server。 - 考虑到由babylon-server创建的这些包 – 它们非常类似于公开的UMD包,但又不是完全一样。babylon-server并不是为生产环境准备的!它只是一个开发工具。
正如所有webpack提供的包一样,有一些属性可以配置(使用CLA 或者 .env 文件)
- source: 所用代码的类型,可以是’dev’ 或 ‘lts’. 默认 ‘dev’.
- mode: 使用的构建类型,可以是’production’ 或 ‘development’. 默认 ‘development’.
- cdnPort: babylon server的服务端口,默认 1337.
- enableHttps: 是否启动https. 默认 false.
- enableHotReload: 是否启动热重载. 默认false. 生产环境总是不启动。
- enableLiveReload: 是否启动实时重载. 默认false. 生产环境总是不启动。
工具
我们为使用者准备了几个工具。他们(playground, sandbox, node editor 和 the gui editor)都是用同样的架构引用core包 – UMD包(cdn中提供的文件)。这就是为什么需要在后台启动babylon server后它们才可以工作。详见[Babylon Server](#Babylon Server)。
所有的工具都是用webpack打包,配置也相类似。
可使用serve
命令启动工具:
npm run serve -w @tools/package-name
大部分包的工作方式都类似,没有全部都写文档。
注意:
- 如果playground使用HTTPS,那么Babylon server 也需要打开HTTPS。
- 要加载构建好的文件(不使用babylon-server),在url中添加?dist=true
- 所有的工具都在http://localhost:1338地址上
Playground
playground不是公开的包,而是在playground.babylonjs.com上对外开放的。它是给开发者测试其工作的工具。
要让Playground在生产环境工作,你需要一个运行的babylon server 或者使用dist参数。
启动playground:
npm run serve -w @tools/playground
有VSCode 任务可以启动playground用于开发工作,同时启动dev包的监视。第一个选项是给playground开发者,第二个是给dev包的开发者使用playground进行测试工作。
注意:
- 从core中加载文件,引用
@dev/core
包而不需要设置目录。原因是类最终还是从BABYLON名字空间中加载,在playground可以使用。 - 使用dev host可能比playground能更快的测试你的代码。
调试
所有的包在不同阶段都可以使用浏览器(推荐chrome)或者VSCode调试器进行调试。
在VSCode中启动相应的任务可以开始调试。VSCode可能需要一点时间去加载和初始化调试的文件。
所有包的构建都会生成sourcemaps文件(非生产模式),所以打开浏览器选择要调试的文件后可以进行typescript代码调试。
单元测试也可以使用VSCode调试。在调试模式下,测试会串行而非并线执行。在运行测试前设置断点。初始运行可能需要一点时间,一旦调试器连接上就会停止再断点。
调试过程中若出现任何问题,可以和我们联系。
直到修复了所有代码检查中的问题,否则在启动VSCode调试任务时需要选择”Debug anyways”。
监视
参考 build-tools 的 dev 监视。
添加新的包
根据以下步骤添加新的包:
- 选择包的位置
- 在包的类别添加一个新的目录
- 添加package.json文件夹(你可以从另一个类似的包中拷贝一个)。如果不是公开的包,确保包被标记为”private”。
- 给包正确的命名 (如 @tools/package-name)
- 如上文所介绍的为包添加代码 (src目录包含代码和资源,test目录包含测试,等)
要安装依赖的包,可以手动添加到package.json,也可以在仓库的根目录下运行npm install package-to-install -w @namespace/package-name
。 如果安装开发依赖包,添加 -D
选项。
安装包后,你可以在这个名字空间中使用这个包(使用npm命令时使用-w
参数)。
你可以在另一个包中引用这个包,将他们连接在一起。完成之后(这个包会被添加到另一个本地包的依赖列表中)就会出现在node_modules之下。
构建
要构建仓库中各个包,运行npm run build -w @namespace/package-name
。然而,还有一个更加快速高效的办法去构建包含依赖项的包。
nx
已经集成到仓库,可以作为本地的资源库更快的执行构建工作。当使用nx运行npm脚本是,它会自动的将该命令应用到包的本地依赖项目,并保证正确的执行顺序。例如,当使用npx nx run build babylonjs-gui
构建公开目录下的babylonjs-gui
包,nx会将下列项目添加到运行列表:
- @dev/build-tools (所有dev包都依赖的)
- @dev/core
- @lts/core
- babylonjs
- @dev/gui
- @lts/gui
- babylonjs-gui
然后回按顺序构建(根据预定义的依赖关系),如果包在上次构建以来没有发生任何更新,将会被跳过。所以,运行npx nx run build babylonjs
将会构建dev,lts 和 public,但构建babylonjs-gui时,因为已经构建好了,不会再次被构建。
在本仓库中,只有你需要构建公开包的时,才应该使用这个,主要用于CI。 然而,nx是一个很强大的工具,将来还会更多应用到我们的仓库。关于nx更多的资料见:nx.dev/getting-sta…
【译注】nx工具是为单一仓库架构而设计的智能、快速、强大的构建工具。
测试
本仓库中有3种不同的测试配置:
- 单元测试
- 可视化测试
- 集成测试 (TBD)
每个包都有一个特定结构的”test”目录。该目录结构使得可以在仓库的根目录下自动运行测试(也可以在单个包目录下)。目录结构如下:
- 单元测试位于目录
test/unit
. - 可视化测试位于目录
test/visualization
.
所有的文件名必须按如下格式:
(.*).test.{js, ts, tsx}
,例如,materials.test.ts 或 babylon.physicsEngine.test.js
这些文件会被jest自动检索到并作为测试脚本的一部分,并使用对应的环境运行(单元测试用jsdom/node,可视化测试用puppeteer)
可能的测试环境有单元测试的jsdom/node以及可视化测和集成测试的puppeteer,(see jestjs.io/docs/config…)。默认的测试环境是node(可视化测试用puppeteer)。要改变测试环境,可以在测试文件的首行添加特定注释。例如:
/**
* @jest-environment jsdom
*/
将使用jsdom作为测试环境,同时为单元测试准备好window和document对象。
可以在终端中看到测试结果。同时,测试完成后会生成一个junit.xml文件,这主要用于CI报告。
如果任何测试失败,可视化测试会生成报告。在根目录下的jest-screenshot-report
目录下可以看到。
要使用单一命令运行所有的测试,在根目录下运行npm run test
或 npx jest
命令。
我们使用jest进行测试。要获得更多这方面的资料,可以阅读文档 – jestjs.io/docs/gettin…
单元测试
除了上面说的,些单元测试还需要知道一些其它事情。
- 单元测试是用于测试单一特定单元模块的。所有其它的模块都需要模拟。更多关于jest模拟,参考 jestjs.io/docs/mock-f…
- 如果你想测试两个或以上模块之间的连接,这个不是单元测试而是集成测试。
- 单元测试环境不建议使用puppeteer环境
- jsdom不允许给DOM添加脚本标签。所有需要从外部添加的东西都需要进行模拟。
运行所有的单元测试,执行npm run test -- --selectProjects unit
。要运行指定项目的单元测试,指定对应的工作空间运行npm命令:npm run test -w @dev/core -- --selectProjects unit
。
运行指定的单元测试,可以使用jest过滤机制,要么通过文件名:
npm run test -w @dev/core -- --selectProjects unit -i "material"
这个会运行所有dev/core下文件名称匹配 “material” 的测试。
还可以通过测试名称进行过滤(测试中的”it” 和 “describes” 函数)
npm run test -w @dev/core -- --selectProjects unit -t "material"
这个会运行所有dev/core重所有测试名称匹配 “material” 的测试。
可视化测试
在根目录下执行npm run test:visualization
开始运行可视化测试。
注意 – 运行测试的一个依赖包目前还不支持M1处理器的OSX系统,在该平台上运行测试将会失败。
配置可视化测试
可视化测试使用puppeteer运行,它是一个在node中控制chrome或firefox的接口。测试将在浏览器中运行,当有失败测试就会生成一个报告。我们选择的浏览器是一个安装了puppeteer的本地chromium。如果你要使用不同浏览器,可以在jest-puppeteer.config.js
中配置puppeteer,这个文件就在根目录下。
module.exports = {
launch: {
dumpio: false, // 要看日志吗?
timeout: 30000, // 30秒超时
headless: false, // headless模式
product: browser, // chrome 或 firefox
ignoreHTTPSErrors: true, // 是否忽略 SSL 问题, 用于文件是在本地以自签名的SSL证书提供的情况。
devtools: true, // 是否自动开启dev tools
args: browser === "chrome" ? chromeFlags : firefoxFlags, // 更多发给浏览器的参数,如打开垃圾回收。
executablePath: "C:\Program Files\Google\Chrome\Application\chrome.exe", // 指定 chrome (或 firefox) 路径
},
browserContext: "default",
};
这里列举了所有的参数 – github.com/puppeteer/p…
puppeteer打开了一个不同的运行环境上下文,不同于我们运行测试时用到的node上下文,所以,所有的代码要么提前注入好(例如使用babylon server)或者在评测时发送过去。一个简单的示例:
const random = await page.evaluate((aRandomNumber) => {
return aRandomNumber * Math.random();
}, Math.random());
这个代码将在node中生成一个随机数,然后发送到页面,在页面中和Math.random()的结果相乘。这将会返回结果并保存在random变量中。
为了在测试过程中进行调试,你可以在测试的任何位置添加下列代码(在 node 环境, 而非评测函数中):
await jestPuppeteer.debug();
执行这个代码会暂停测试(考虑timeout!),允许你打开浏览器的开发者工具进行场景调试。
这些测试被配置为自动显示浏览器控制台中的所有报错信息(firefox中不能工作)。你可以在jest的输出屏幕中看到这些错误。
截止目前,所有测试都定义在可视化文件本身,但这事临时的,直到开发工作确定后。
将来,我们建议dev包自己拥有可视化测试,而不是在外部的包中。截止目前,可视化测试位于 @tools/visualization-tests。
运行指定的测试
要在指定引擎中运行指定的测试, 执行:
npm run test:visualization -- -i "engineName" -t "test name"
如:
npm run test:visualization -- -i "webgl2" -t "Particle subemitters"
构建工具
文档工作中,敬请期待 :-)
LTS
文档工作中,敬请期待 :-)
新老系统的区别
- 为了允许架构变化,我们不再在工具的index.html中加载 .js文件,而是在index.js异步加载。这样让代码的更新变得更轻松。
- 验证测试在本地chromium中运行
- 每一个工具都用不同的命令托管,而不是一起托管。
- 工具的资源文件放在源码目录中
- 项目的依赖关系在typescript编译过程中计算。
- 不再需要index-local.html。
(仅管理员) 如何发布新版本
要发布新版本,需要在build目录下的config.json提交一个更新。
一下是一个配置的示例:
{
"npmTag": "preview",
"versionDefinition": "prerelease",
"preid": "beta",
"nonce": 1
}
versionDefinition 可以是一个完整的版本号(如 5.0.0-rc.6
) 或则为下列的npm version
版本修饰之一:
major | minor | patch | prerelease
- 如果在相同的版本定义中更新版本,你需要增加 nonce值。
- 如果想从X.X.X-alpha.X切换到beta,versionDefinition设置为
prerelease
,preid设置为beta。 - 目前,所有的包都将被更新到新的版本,在以后的发布中,只有被更新的包才会更新版本。
问题及排查
npm 安装包失败
如果npm的安装由于依赖问题失败,尝试运行npm install --legacy-peer-deps
如果在安装依赖后,npm安装由于在初始化构建过程中失败了。请联系我们,因为这是本仓库的基础功能,不应该有失败。
运行npm命令是报node.js 已经被打开
大概报如下的错误:
[5560:0216/142123.997:ERROR:display_layout.cc(562)] PlacementList must be sorted by first 8 bits of display_id
[65092:0216/142124.736:ERROR:display_layout.cc(562)] PlacementList must be sorted by first 8 bits of display_id
[42568:0216/142125.139:ERROR:broker_win.cc(56)] Error reading broker pipe: The pipe has been ended. (0x6D)
这个原因是因为npm检测到一个node.js文件(这是我们生成的文件)。这个要看你的系统配置。解决这个问题,需要从@babylonjs/core的根目录下删除node.js文件。
然后,建议运行npm run clean -w @babylonjs/core
.