当前位置:网站首页>用 Oasis 开发一个跳一跳(一)—— 场景搭建
用 Oasis 开发一个跳一跳(一)—— 场景搭建
2022-06-24 15:34:00 【HuSong】
《跳一跳》是微信小游戏里第一款制作精良,也是非常受大家喜爱的游戏。这里以一个跳一跳 MVP(最简可行版本) 版本为例,让大家了解『如何用 Oasis 开发一款 Web 3D 游戏』这一过程。最终的效果如下:
大家可以在: https://stackblitz.com/edit/oasis-jump-aeuowy?file=src/index.ts 体验和尝试修改代码。(BTW:如果访问不了或太慢,可以自行到 https://github.com/gz65555/jump-game/tree/feat/game 克隆项目到本地启动。)
我们把核心的部分分成三大部分,场景、角色和游戏逻辑,后面的教程会以这三大部分为主,实现一个跳一跳的最小可用版本(并非完整的游戏)。下图是核心模块的分析:

第一期我们准备制作最基本的场景,完成灯光,相机,基本底座的摆放。
在具体进入开发之前,我们需要先把整个项目工程搭建好。
工程搭建
我们先使用 create-oasis-app 创建项目。
使用命令的第一步需要安装 Node.js。如果已安装 Node.js(version >= 12.0.0) 可以执行命令创建 oasis 模板:
npm init @oasis-engine/oasis-app
因为我们无需额外开发前端部分,所以直接使用 Vanilla 模板即可。下面是调用命令的过程。

当执行完成后,我们进入到项目中的 terminal 里,执行:
npm install
在安装完依赖后再使用:
npm run dev
启动 dev 服务器,过程如下图所示:

再打开 http://localhost:3000 即可看到:

说明工程搭建已经完成。此时项目目录如下:
|-jump-game
|-index.html // HTML 文件
|-main.js // 入口 js 文件
|-package.json // 工程描述文件
|-src
| |-index.ts // oasis 部分代码
基本场景搭建
引擎和场景初始化
我们用 IDE 打开项目,找到 /src/index.ts
如下面代码所示:
import { Camera, Vector3, WebGLEngine, DirectLight } from "oasis-engine";
// 初始化引擎
const engine = new WebGLEngine("canvas");
// 根据页面设置 canvas 大小
engine.canvas.resizeByClientSize();
const zeroVector = new Vector3(0, 0, 0);
// 设置背景色
const scene = engine.sceneManager.activeScene;
scene.background.solidColor.setValue(208 / 255, 210 / 255, 211 / 255, 1);
scene.ambientLight.diffuseSolidColor.setValue(0.5, 0.5, 0.5, 1);
// 创建根节点
const rootEntity = scene.createRootEntity();
const cameraEntity = rootEntity.createChild("camera");
cameraEntity.transform.setPosition(-100, 100, 100);
const camera = cameraEntity.addComponent(Camera);
// 初始化相机
camera.isOrthographic = true;
cameraEntity.transform.lookAt(zeroVector);
// 初始化灯光
const directLightEntity = rootEntity.createChild("directLight");
const directLight = directLightEntity.addComponent(DirectLight);
directLight.intensity = 0.5;
directLightEntity.transform.setPosition(10, 30, 20);
directLightEntity.transform.lookAt(zeroVector);
engine.run();
此段代码创建了引擎,场景,并且初始化了相机,灯光。相机使用正交相机,朝向原点。直接光也设置为朝向原点。
完成以上步骤可以场景里还是一片灰色,我们来给场景添加底座生成和相机移动的逻辑。
场景底座初始化
我们通过创建一个 SceneScript.ts 来做场景整体的管理,并且在 rootEntity上添加 SceneScript:
const sceneScript = rootEntity.addComponent(SceneScript);
脚本是 Oasis Engine 非常核心的概念,是一种特殊的组件,组件又是挂载在实体(Entity)上的,来给引擎提供拓展的能力。更多关于实体和组件概念请查看:《实体与组件》文档。
接下来,在 SceneScript 的 onAwake 生命周期中创建一个 ground 实体,用来摆放跳一跳的底座。onAwake生命周期函数是在挂载的实体被激活时调用,一般用来放置一下初始化的代码。引擎中还有许多生命周期的钩子函数用来帮助开发者编写业务逻辑,更多脚本生命周期可以查看:《脚本》。
同时创建 TableManager 对象来控制底座的生成。
onAwake() {
this.ground = this.entity.createChild("ground");
this.tableManager = new TableManager(this._engine, this.ground);
}
我们在 TableManager 里创建可以复用的材质(Material)和网格(Mesh)。创建一个 3D 物体的渲染,需要用到 MeshRenderer 网格渲染器组件,设置好材质和网格即可。
因为是 MVP 版本,我这里只用一个红色的立方体 Table 作为示意:
import {
BlinnPhongMaterial,
Engine,
Entity,
MeshRenderer,
ModelMesh,
PrimitiveMesh,
} from "oasis-engine";
import { Config } from "./Config";
export class TableManager {
// 底座的 Mesh
private cuboidMesh: ModelMesh;
// 底座的材质
private cuboidMaterial: BlinnPhongMaterial;
constructor(engine: Engine, private sceneEntity: Entity) {
// 创建基本网格
this.cuboidMesh = PrimitiveMesh.createCuboid(
engine,
Config.tableSize,
Config.tableHeight,
Config.tableSize
);
// 创建基本材质
this.cuboidMaterial = new BlinnPhongMaterial(engine);
// 设置材质颜色
this.cuboidMaterial.baseColor.setValue(1, 0, 0, 1);
}
// 创建一个方块的底座
createCuboid(x: number, y: number, z: number) {
// 创建渲染实体
const cuboid = this.sceneEntity.createChild("cuboid");
const renderEntity = cuboid.createChild("render");
// 设置坐标
renderEntity.transform.setPosition(0, Config.tableHeight / 2, 0);
cuboid.transform.setPosition(x, y, z);
// 创建 MeshRenderer 组件
const renderer = renderEntity.addComponent(MeshRenderer);
// 设置网格
renderer.mesh = this.cuboidMesh;
// 设置设置材质
renderer.setMaterial(this.cuboidMaterial);
return { entity: cuboid, size: Config.tableSize };
}
// 清除所有底座
clearAll() {
this.sceneEntity.clearChildren();
}
}
我们可以看到上面的的 tableSize 和 tableHeight 都是在 GameConfig 里定义的,我们也需要创建一个 Config.ts 来设置游戏配置:
export module Config {
export const tableHeight = 5 / 3;
export const tableSize = 8 / 3;
}
我们通过把配置收敛到统一文件里,方便配置的修改。
我们再到 SceneScript 中添加 reset 方法:
reset() {
// 清除所有的底座
this.ground.clearChildren();
// 初始化的第一个底座
this.tableManager.createCuboid(-2.5, 0, 0);
// 初始化的第二个底座
this.tableManager.createCuboid(4.2, 0, 0);
}
reset 方法是之后每次游戏开始时和结束后都需要调用的方法。上面的几个数值都是实际开发中调试出的结果,相对来说比较接近真实的游戏。
我们在 index.ts 调用 sceneScript.reset() 即可看到效果:

总结
这篇文章讲了一下跳一跳的简单场景搭建,涉及到的知识点有:
下一期会带来场景的逻辑部分:底座生成和相机移动的部分。
本次教程代码可参考 feat/init 分支。
边栏推荐
- "Industry outlook" analysis of five major trends in China's security video surveillance industry
- CAP:多重注意力机制,有趣的细粒度分类方案 | AAAI 2021
- 【我的OpenGL学习进阶之旅】OpenGL的坐标系的学习笔记
- 实现领域驱动设计 - 使用ABP框架 - 领域逻辑 & 应用逻辑
- 【Prometheus】4. Monitoring cases
- 还在担心漏测吗?快来使用jacoco统计下代码覆盖率
- Special topic of IM code scanning login Technology (III): easy to understand. A detailed principle of IM code scanning login function is enough
- 一文详解JackSon配置信息
- Design of CAN bus controller based on FPGA (Part 2)
- Understanding openstack network
猜你喜欢

【云原生 | Kubernetes篇】Kubernetes基础入门(三)

打破内存墙的新利器成行业“热搜”!持久内存让打工人也能玩转海量数据+高维模型

如何轻松实现在线K歌房,与王心凌合唱《山海》

How to expand disk space on AWS host

刚刚阿里面软件测试回来,3+1面任职阿里P7,年薪28*15薪

实现领域驱动设计 - 使用ABP框架 - 领域逻辑 & 应用逻辑

FreeRTOS新建任务不执行问题解决办法

为什么企业实施WMS仓储管理系统很容易失败

Most common usage of vim editor
![clang: warning: argument unused during compilation: ‘-no-pie‘ [-Wunused-command-line-argument]](/img/f0/42f394dbc989d381387c7b953d2a39.jpg)
clang: warning: argument unused during compilation: ‘-no-pie‘ [-Wunused-command-line-argument]
随机推荐
Network engineers must know the network essence knowledge!
great! The novel website project is completely open source
[C language questions -- leetcode 12 questions] take you off and fly into the garbage
打破内存墙的新利器成行业“热搜”!持久内存让打工人也能玩转海量数据+高维模型
Paper: Google TPU
The 30 pictures bring the network protocol layer by layer to life. It's really fragrant!
Very exciting! 12000 words summarized the theory of network technology, reviewing the old and learning the new
Junit5中的参数化测试(Parameterized Tests)指南
practice
Nature刊登量子计算重大进展:有史以来第一个量子集成电路实现
Improving the classification of motor imagery by combining EEG and MEG signals in BCI
Cloud + community [play with Tencent cloud] essay solicitation activity winners announced
Step by step import RHEL image to Tencent cloud
一文详解JackSon配置信息
Here comes Wi Fi 7. How strong is it?
Still worried about missing measurements? Let's use Jacobo to calculate the code coverage
Motion planning of floating base robot
QTreeWidget作为单例模式以dll返回的两个问题
nifi从入门到实战(保姆级教程)——环境篇
Jenkins的便捷式安装