Balloon Search
Lost in the Woods: Balloon Search is an engaging 3D puzzle game that invites players to navigate a whimsical forest alongside the curious Purple Guy. With intuitive controls, dynamic elements, and charming visuals, the game is a perfect example of how Niantic Studio empowers creators to build unique and engaging 3D web games. We had the opportunity to learn about their creative journey and how they brought Lost in the Woods to life.
Introduction
This experience is a 3D browser game created using Niantic Studio (Beta) in October 2024.
The main character, Purple Guy, begins at the entrance of the woods and must navigate through the grounds to collect balloons and reach the finale.
Along the way:
- There are 8 balloons scattered across the map, tracked by a ticker in the top-right corner of the screen.
- Avoid bodies of water; stepping into water resets the game and you lose all collected balloons.
- Use the arrow keys to move up, down, left, and right, and press the spacebar to toggle the welcome screen and instructions.
Project Structure
3D Scene
- Base Entities: Includes the orthographic camera and ambient/directional lights for the game environment.
- UI Entities: Contains all user interface elements, including a tutorial at the start to explain the game's main goal.
Assets
- Audio: Background music, sound effects for balloon collection, game-over, and finale.
- Models: Balloons, Purple Guy, and environmental objects.
Scripts
/colliders/
:finale-collider.js
: Detects collisions with the finale area.game-over-collider.js
: Detects collisions leading to a game-over state.score-collider.js
: Detects collisions with balloons to update the score.
balloon.js
: Registers the Balloon component as a reusable entity
throughout the game.character-controller.js
: Manages player movement, animations, and
sound effects based on input and game state.camera-follower.js
: Keeps the camera positioned relative to the character.game-controller.js
: Serves as the central hub for managing game states, transitions, and UI updates.
Implementation
The game’s core logic is managed by a series of interconnected components. Key functionality is demonstrated with code snippets below.
Game Manager (GameController
)
The game-controller.js
acts as the game manager, connecting different events and managing state transitions.
Example: State Management
// Define other states
ecs.defineState('welcome')
.initial()
.onEvent('ShowInstructionsScreen', 'instructions')
.onEnter(() => {
console.log('Entering welcome state.')
dataAttribute.set(eid, {currentScreen: 'welcome', score: 0}) //
Reset score
resetUI(world, schema, 'welcomeContainer') // Show welcome screen
})
ecs.defineState('instructions')
.onEvent('ShowMovementScreen', 'movement')
.onEnter(() => {
console.log('Entering instructions state.')
dataAttribute.set(eid, {currentScreen: 'instructions'})
resetUI(world, schema, 'instructionsContainer') // Show
instructions screen
})
ecs.defineState('movement')
.onEvent('StartGame', 'inGame')
.onEnter(() => {
console.log('Entering movement state.')
dataAttribute.set(eid, {currentScreen: 'movement'})
resetUI(world, schema, 'moveInstructionsContainer') // Show
movement screen
})
ecs.defineState('inGame')
.onEvent('gameOver', 'fall')
.onEvent('finale', 'final')
.onEnter(() => {
console.log('Entering inGame state.')
dataAttribute.set(eid, {currentScreen: 'inGame'})
resetUI(world, schema, null) // Show score UI
if (schema.character) {
world.events.dispatch(schema.character, 'start_moving') //
Dispatch start moving event
}
world.events.addListener(world.events.globalId,
'balloonCollected', balloonCollect)
world.events.addListener(world.events.globalId, 'FinaleCollision',
handleFinaleCollision)
})
.onExit(() => {
world.events.dispatch(eid, 'exitPoints') // Hide points UI
world.events.removeListener(world.events.globalId,
'balloonCollected', balloonCollect)
world.events.removeListener(world.events.globalId,
'FinaleCollision', handleFinaleCollision)
})