How ByteDance’s SAR Creator Powers the ‘Lucky Dragon’ New Year Interactive Experience
This article details the technical implementation of the "Lucky Dragon" Spring Festival interactive game in Douyin, covering the cross‑platform framework, SAR Creator engine, scene construction, animation control, coordinate synchronization, terrain management, and performance‑focused scene switching strategies.
Introduction
During the 2024 Spring Festival, ByteDance’s Douyin launched the "Happy Chinese New Year" series, featuring the "Lucky Dragon" interactive game. The article explains the front‑end and cross‑platform technologies used to build this experience.
Technology Stack
The project relies on a cross‑platform framework that provides fast first‑screen rendering and a Canvas environment for the SAR Creator engine, a TypeScript‑based high‑performance interactive solution.
Gameplay Overview
Players interact with five mini‑games, including "Lucky Dragon", "Dragon Treasure Hunt", "Shake Fortune", "Guard Cash" and "Red Packet Rain". The "Lucky Dragon" game consists of a "Home Scene" and a "Treasure Scene" that transition via an animated dragon.
Implementation of the Home Scene
The Home Scene mixes 3D models (girl, dragon, ground, snow) with 2D sprites (fireworks, houses, speech bubbles). Designers build the scene in the SAR Creator editor, arranging entities in a hierarchy where parent Transform3D components affect children. Custom scripts attached to entities drive behavior.
Animation Playback
Fourteen animation clips are defined in an enum HomeAnimName. The Animator component’s crossFade method transitions between clips, while on('finished') callbacks handle post‑animation logic. Loop count and clamp behavior are controlled via loopCount and clampWhenFinished properties.
export enum HomeAnimName{HomeSleep='home_sleep',HomeAwake='home_awake',HomeIdle='home_idle',HomeClick='home_click',HomeClickA='home_click_a',HomeClickB='home_click_b',HomeHappy='home_happy',HomeGoHome='home_gohome',HomeHoldBox='home_hold_box',HomeOpenBox='home_open_box',HomeCloseBox='home_close_box',HomeCloseBoxIdle='home_close_box_idle',HomeOpenBoxIdle='home_open_box_idle',HomeGoOut='home_goout'}Developers use an AnimationGraph to chain these clips, allowing designers to drag animations into a visual graph and configure transitions.
Coordinate Synchronization
To keep 2D UI elements (e.g., speech bubbles) aligned with moving 3D characters, the system converts a bone’s world position to UI canvas coordinates each frame.
const TEMP_VEC3 = new Vector3();
export const threeD2UICanvas = (entity, camera) => {
entity?.object?.getWorldPosition(TEMP_VEC3);
const vec3 = camera?.project(TEMP_VEC3) || new Vector3();
const x = vec3.x * 375; // canvas width
const y = vec3.y * 500; // canvas height
return { x, y };
};Treasure Scene Implementation
The Treasure Scene is a pure 2D interactive area with a scrolling orthographic camera. It loads terrain prefabs dynamically, recycles them, and places detectors that trigger dragon animations and reward items.
Camera Logic
The camera moves continuously along the X‑axis based on delta time and a configurable speed.
this._camEntity.position.x += deltaTime * this._moveSpeed;Terrain Management
Each theme provides two terrain prefabs (e.g., map_0_a.prefab, map_0_b.prefab) that are stitched together to form an endless scrolling background. The first prefab loads synchronously, the second asynchronously.
async _loadTerrains(travelScene2D){
const terrainNames = TerrainNamesByTheme[this._theme];
const firstTerrainName = terrainNames[0];
this._firstTerrainPromise = this._loadTerrain(firstTerrainName);
const terrainPromises = terrainNames.filter((_,idx)=>idx!==0).map(i=>this._loadTerrain(i));
void Promise.all(terrainPromises).then(async()=>{
await this._tryCreateFirstTerrainBlock(travelScene2D);
let lastTerrainBlock = this._firstPrefabBlock;
const terrainPos = this._terrainOffset.clone();
for(const terrainPromise of terrainPromises){
if(lastTerrainBlock!==undefined){
const terrainEntity = await terrainPromise;
terrainPos.x += lastTerrainBlock.getBlockSize();
lastTerrainBlock = this._createTerrainBlock(travelScene2D, terrainEntity, false, terrainPos);
}
}
});
await this._tryCreateFirstTerrainBlock(travelScene2D);
}Detector Logic
Detectors are placed on terrain blocks. When a detector crosses the screen center, it triggers a dragon animation and optionally spawns a red‑packet (reward) after configurable delays. The system tracks an isTriggered flag to avoid missing triggers during frame drops.
Dragon and Reward Animation
Dragon animations are driven by a AnimationController. When a detector fires, the controller sets a case variable to select the appropriate animation. Red‑packet appearance uses Spine for collision effects and Lottie for a large reward animation displayed at screen center.
const animationController = this.entity.getScript(AnimationController);
if(showAwake){
animationController.setValue("case",1); // play awake
}else if(showIdle){
animationController.setValue("case",2); // play idle
}Scene Management Strategy
Both Home and Treasure scenes are pre‑loaded, cached, or destroyed based on device capability and memory availability. High‑end devices keep scenes in memory (cache), while low‑end devices dispose of unused resources.
Pre‑loading
const preloadTravel = () => {
const {bundle} = assetManager.loadBundle('travel');
bundle.load('Travel.prefab');
};
const preload = () => {
if(isHome){ preloadTravel(); } else { preloadHome(); }
};Cache vs. Destroy
Using SAR Creator’s ability to detach nodes without freeing their assets enables caching. When memory is constrained, entity.dispose() recursively releases textures, materials, and geometry.
class SceneManager{
async loadHomeRoot(){
if(!this.homeRoot){ this.homeRoot = await bundle.load('HomeRoot.prefab'); }
if(this.homeRoot){ scene.addChild(this.homeRoot); }
}
async loadTravelRoot(){
if(!this.travelRoot){ this.travelRoot = await bundle.load('TravelRoot.prefab'); }
}
dispose(){
if(USE_STORAGE){ this.homeRoot?.parent?._deleteEntityFromChildren(this.homeRoot); }
else { entity.dispose(); }
}
}Conclusion
The article demonstrates how a combination of a cross‑platform framework, the SAR Creator engine, and careful performance engineering enables a rich, low‑latency interactive experience on Douyin’s task page, while adapting resource usage to device capabilities.
ByteFE
Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.
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.
