Building a 3D Incentive Game with Oasis Engine: From Concept to Optimization
This article details how a 3D interactive incentive game was created using Oasis Engine, covering scene architecture, ECS design, script development, collision handling, camera follow, skeletal and particle animations, shader effects, lane configuration, mobile orientation, performance optimizations, testing, deployment, and key lessons learned.
In recent years many businesses have added interactive mini‑games to boost user engagement. To explore the value of such games in B‑type scenarios, we built a 0‑to‑1 incentive game called "Yidatong Break‑Egg" using Oasis Engine, aiming to drive customers from onboarding to active usage.
Scene Analysis
The game UI is divided into three layers:
DOM UI layer : 2D UI elements such as score panels and game over screens, communicating with the 3D scene via events.
3D scene layer : Built with Oasis Engine and Oasis 3D Editor, rendered on a canvas.
Background layer : The sky‑sea backdrop placed at the bottom.
Note: Do not set the canvas background via CSS; instead set gl.clearColor to transparent and place a separate background layer to avoid scene overlap after the app goes to background on WeChat or Safari.
Implementing the 3D Scene
The development process follows the typical 3D pipeline: scene construction, script creation, and logic implementation.
Scene Construction
Using Oasis Editor’s visual editing, we assembled a scene tree where each entity is a collection of components (e.g., camera, model, scripts). The character entity loads a GLTF model exported from FBX, and custom script components encode game logic.
Script Development
Oasis Editor provides a cloud code editor and event panels. Scripts are created in the editor, then pulled locally with oasis pull -s (schema only) or oasis pull (full). The game uses Git for version control and isolates 3D communication logic from business‑specific features such as forced landscape, downgrade handling, and lane configuration.
Logic Development
1. Controlling Character Trajectory
Collision detection determines actions based on the character’s interaction with blue blocks. The first tap moves the character straight on the X‑axis; the second tap adds a Y component for a parabolic jump. Collision events begin_overlop and end_overlop trigger free‑fall or horizontal movement depending on relative positions.
const cd = this.entity.addComponent(o3.CollisionDetection);
cd.on("begin_overlop", e => {
const colliderNode = e.collider._entity;
const entityType = this.getEntityType(colliderNode);
if (entityType === "reward") {
const particleComponent = colliderNode.getComponent(o3.GPUParticleSystem);
particleComponent && particleComponent.start();
this.engine.dispatch("gotReward");
}
});2. Camera Follow
The camera follows the character from behind. To avoid jitter, the follow logic is placed in onLateUpdate, which runs after all onUpdate calls.
3. Skeletal Animation
Animations are exported from C4D as a single FBX, then split in Blender into separate clips (e.g., run, wave). Clip names are read from the GLTF animation property to play the correct sequence.
4. Particle Animation
Particle systems are configured in Oasis Editor. For the energy‑block explosion, initial velocities are zero, with random X/Y offsets and a slight negative Y acceleration to simulate gravity.
const cd = this.entity.addComponent(o3.CollisionDetection);
cd.on("begin_overlop", e => {
const colliderNode = e.collider._entity;
const entityType = this.getEntityType(colliderNode);
if (entityType === "reward") {
const particleComponent = colliderNode.getComponent(o3.GPUParticleSystem);
particleComponent && particleComponent.start();
this.engine.dispatch("gotReward");
}
});5. Shader Animation
When the character hits a red block, a shader‑based disappearance effect is applied (see the linked article for the full shader code).
6. Lane Configuration
Lane data is abstracted into boxsConfig and rewardsConfig structures, allowing easy cloning, dragging, and custom difficulty levels.
interface IGameConfig {
boxsConfig: {
name: string;
position: { x: number; y: number; z: number; };
action?: string[];
}[];
rewardsConfig: {
name: string;
position: { x: number; y: number; z: number; };
}[];
}7. Skinning
Before runtime, the schema.json is edited to replace texture assets, enabling dynamic skin changes.
8. Mobile Landscape Handling
The game runs in landscape mode. On orientation change, the container size is swapped and rotated 90°, with an offset to keep the canvas centered.
const clientWidth = document.documentElement.clientWidth;
const clientHeight = document.documentElement.clientHeight;
gameContainer.style.top = (clientHeight - clientWidth) / 2 + "px";
gameContainer.style.left = 0 - (clientHeight - clientWidth) / 2 + "px";Optimizations
Canvas Energy‑Saving Mode
To balance clarity and performance on high‑DPI screens, the canvas width is set to clientWidth * devicePixelRatio / scale (scale ≈ 1.5).
Object Pool
Both blocks and energy blocks are pooled. Visible entities are fetched from the pool; out‑of‑view entities are returned, reducing GC pressure and draw calls.
getVisibleRewardConfig(positionX) {
return this.rewardsConfig.filter(item => {
const { position, entity } = item;
if (position.x < positionX + SHOW_RANGE[0] && entity) {
this.entityPool.putEntity(entity);
item.entity = null;
}
return position.x >= positionX + SHOW_RANGE[0] && position.x <= positionX + SHOW_RANGE[1];
});
}Vertical Synchronization
Disabling VSync caused frame‑rate mismatches and visual tearing. Enabling VSync ensures the GPU waits for the display refresh before rendering the next frame.
WebGL Version Switching
On older Android DingTalk versions that crash with WebGL 2.0, the engine falls back to WebGL 1.0 by setting WebGLMode to WebGL1.
Deployment and R&D Reflections
The game is packaged as a reusable component that can be embedded with a simple canvas. Initialization code looks like:
const gameContainer = canvas.parentElement;
const oasis = await boot({
canvas,
gameContainer,
level: 2,
debug: false,
handleDowngrade: () => { setDowngrade(true); }
});
const { engine } = oasis;
engine.dispatch("start");
engine.on("gotReward", () => setScore(x => x + 1));
engine.on("gameDie", () => {/* handle death */});
engine.on("gameOver", () => {/* handle finish */});Key takeaways include the importance of early design‑to‑art handoff standards (e.g., avoid cameras in FBX, use power‑of‑two textures, limit triangle count to ~50k, merge materials, strip unused nodes), leveraging visual editors for rapid iteration, using Chrome’s Performance and SpectorJS tools for profiling, and establishing robust downgrade paths for devices lacking WebGL support.
Conclusion
Interactive 3D games significantly improve user retention and activation. The experience will feed into a broader user‑growth system that combines content marketing, incentive mechanisms, and technical capabilities. An open‑source release of Oasis 3D is scheduled for February 1st, streamed on B‑station.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
