Inspiration

My inspiration for "Sushi Tycoon" was to create an addictive simulation game that captured the essence of classic "tycoon" games, but built from the ground up with a "Mobile-First" approach for the Horizon Worlds competition. I wanted a satisfying game loop (generate, sell, upgrade) that was intuitive on mobile and performed great.

What it does

"Sushi Tycoon" is a simulation game where players claim a plot (PlotClaimer.ts) and build their own sushi restaurant. Players start with an empty plot, purchase their first floor (PurchaseTrigger.ts), and then acquire sushi-generating machines (SushiGenerator.ts).

These machines (manual or automatic) produce sushi (PooledObject.ts) that travels on a conveyor belt to a collection point (CollectionPoint.ts), generating uncollected earnings. Players must go to the cash register (CashRegister.ts) to collect their earnings and increase their money (MoneyUI.ts). With more money, they can buy more machines and upgrades (BuildingVisibility.ts), expanding their empire.

How we built it

The game was built on a robust and scalable TypeScript architecture. The logic is driven by network events (SharedEvents.ts). When a player buys an upgrade (PurchaseTrigger.ts), an onBuildingStateChanged event is fired. This event notifies other systems, such as BuildingVisibility.ts (to show the building) and ButtonAppearanceController.ts (to update the buttons), creating a flexible system.

All progress (money, buildings) is saved securely using PlayerPersistentStorage through a PlayerTycoonData interface. For "Mobile-First" optimization, we implemented an "Object Pooling" system (ObjectPoolManager.ts, PooledObject.ts) that reuses sushi entities instead of spawning and destroying them, maintaining high and stable performance.

Challenges we ran into

The main challenge was true mobile optimization. To avoid FPS drops, we implemented the "Object Pooling" system (ObjectPoolManager.ts) which reuses sushi entities.

The second, more difficult challenge was state synchronization in a multiplayer environment. Ensuring that purchase buttons (ButtonAppearanceController.ts), building visibility (BuildingVisibility.ts), and player money (MoneyUIManager.ts) were always correct for every player required a robust event-driven architecture and careful debugging of race conditions.

Accomplishments that we're proud of

We are especially proud of the "Object Pooling" system. It's an advanced optimization technique that ensures the game runs smoothly on mobile devices, even with many active generator machines.

We are also proud of the event-driven architecture (SharedEvents.ts), which completely decouples scripts (like purchase triggers from UI buttons) and makes the project easy to maintain and expand.

'''TypeScript

// Example from PlotClaimer.ts // Assigning a new plot to a player with default data const newPlotData: PlayerTycoonData = { currentPlotID: this.props.plotID, money: 200, uncollectedEarnings: 0, buildings: { floorLevel: 0 } }; this.world.persistentStorage.setPlayerVariable(player, this.PLAYER_PLOT_PPV, newPlotData); '''

What we learned

This project taught us several critical, advanced lessons beyond simple scripting:

How to Solve "Heisenbugs": We discovered race conditions between persistentStorage writes and NetworkEvent broadcasts. A DebugDisplay.log call would "fix" the bug by adding a micro-delay! We learned to solve this robustly by wrapping event-receiving logic in a short async.setTimeout (200-500ms) to ensure the database write always completes first.

Decouple UI from Logic: We learned to separate button logic into two scripts: ButtonAppearanceController.ts (runs locally, updates visibility 24/7) and PurchaseTrigger.ts (runs on the server, handles the actual purchase). This separation is vital for a clean, responsive, and secure UI.

The State Manager Pattern: We created a PlayerStateManager.ts to manage the OnPlayerEnterWorld event. This script sends a single, targeted network event to the joining player with all initial state data, solving difficult state-sync bugs on world re-entry.

"Mobile-First" is Non-Negotiable: We learned that "Mobile-First" is not just a suggestion. Our "Object Pooling" system was essential for maintaining high FPS and proves the importance of managing entity generation from day one.

What's next for Sushi Tycoon

We plan to expand the game with more sushi types (each with different profits), staff upgrades (chefs, waiters) that passively increase speed or value, and global leaderboards to foster competition among sushi tycoons

Built With

Share this project:

Updates