LogoLogo
  • Get Started
  • Getting Started
    • Initial Setup
    • Create Your First Game
    • API Reference
    • Build Your First World Map
    • Multiplayer Testing
    • Use Templates & Examples
    • Styling & Assets
      • Modeling Guidelines
      • Texturing Guidelines
      • Default Assets
  • Build Faster With AI Tools
  • SDK Guides
    • Assets
    • Audio & SFX
      • Audio Manager
    • Blocks & Chunks
      • Block Types
      • Block Type Registry
      • Chunks
      • Chunk Lattice
    • Camera
    • Chat & Commands
    • Debugging
    • Entities
      • Animations
      • Block Entities
      • Colliders & Hitbox
      • Child Entities
      • Entity Controllers
        • Base Entity Controller
        • Pathfinding Entity Controller
        • Player Entity Controller
        • Simple Entity Controller
      • Entity Manager
      • Model Entities
      • Movement & Pathfinding
      • Player Controlled Entities
    • Events
    • Input & Controls
    • Lighting
      • Ambient Light
      • Light Manager
      • Point Lights
      • Spot Lights
      • Sun Light (Directional)
    • Mobile
    • Persisted Data
    • Players
      • Player Manager
      • Persisted Player Data
    • Plugins
    • Physics
      • Colliders
      • Collision Groups
      • Debugging
      • Gravity
      • Raycasts
      • Rigid Bodies
    • User Interface
      • Overlay UI
      • Scene UIs
      • Scene UI Manager
    • Worlds
      • Map Data Format
  • Helpful Resources
    • HYTOPIA Architecture & Platform Overview
    • Useful Third-Party Tools
Powered by GitBook
On this page
  • 1. Open your favorite code editor
  • 2. Run & Connect To Your Server
  • 3. Building a world
  • 4. startServer()
  • 5. Create an Entity
  • 6. Make the spider do something...
  • 7. Complete Code
  • 8. Recap
  • Next Steps
Export as PDF
  1. Getting Started

Create Your First Game

PreviousInitial SetupNextAPI Reference

Last updated 1 month ago

Now that you've completed the Initial Setup, let's create a simple game! In this walkthrough, we'll cover some fundamental concepts of building a game with the HYTOPIA SDK.

1. Open your favorite code editor

It's time to bust out our code editor. We recommend VSCode or Cursor, but you're free to use whatever you'd like.

Now, open up the project directory in your code editor that we create in the Initial Setup.

Your file tree should look something like this:

2. Run & Connect To Your Server

Make sure your server is running. You can learn more about how to do this in the Initial Setup guide.

Also make sure you're connected to your running game server through https://hytopia.com/play so you can see your changes in real time as you follow this guide.

3. Building a world

In this example, we'll be using the world generated by default when we performed bunx hytopia init. However, in almost all cases you'll probably want to create your own world with its own textures, look and feel. You can do that by following the Build Your First World Map guide.

4. startServer()

Open up the index.ts file in your editor. This is the entry point for your game. All HYTOPIA games are initialized with the startSever() function. This function internally handles all low-level game setup from initializing the physics engine, opening up the server to websocket connections, and more - all without you ever having to deal with any of those complexities.

startServer() expects a callback, which is the init function for your game. This init function is where all of the setup of your game should happen - loading your map, spawning initial entities, defining callback and tick callback logic, and more. After your init function is ran internally by startServer(), your server will open to connections.

startServer(world => {
  // world is the default World instance of our game
  // This is where we should do all of our game setup.
  // After your setup logic completes, your server will
  // automatically open to player connections.
});

5. Create an Entity

Entities are a core concept in games, they often represent NPCs, player characters, interactable objects like vehicles, and more. In HYTOPIA, you'll use an Entity for any object in your game that is not a terrain block.

Entities internally are represented as a Rigid Body with one or more Colliders. This is what defines how they interact with the physical world. Such as colliding with walls, bouncing off of things they hit hard enough, rolling down slopes, and more.

If you're unfamiliar with what a Rigid Body or Collider is in the context of game development, here's a quick explanation:

  • Rigid Body: In a game physics engine, a rigid body is like a solid object that doesn't bend, stretch, or change shape—it stays the same no matter what happens to it. When you assign a rigid body to something in your game, you're telling the physics engine to handle its movement and interactions realistically. This means the object can move around, fall due to gravity, collide with other objects, and respond to forces just like things do in the real world.

  • Collider: A collider is an invisible shape attached to a game object that defines its physical boundaries for detecting collisions. Think of it as the object's "collision shape." It doesn't have to look exactly like the object; it can be a simple shape like a box or a sphere that roughly matches the object's size. Colliders allow the engine to calculate when two objects touch or overlap so you can trigger events like damaging a character when they fall and hit a block or picking up an item when they walk over it, and much more.

In this example, we'll create a basic spider entity.

import {
  startServer,
  ColliderShape, // import the ColliderShape enum
  Entity, // import the Entity class
  GameServer,
  PlayerEntity,
  RigidBodyType, // improt the RigidBodyType enum
} from 'hytopia';

startServer(world => {
  // .. Other init code hidden for this example...

  // spider hitbox is automatically calculated from the model & scale.
  const spider = new Entity({
    modelUri: 'models/npcs/spider.gltf',
    modelScale: 2.5,
    modelLoopedAnimations: [ 'idle' ],
  });

  // Spawn the spider in the world.
  spider.spawn(world, { x: 0, y: 10, z: -15 });
});
```

We've spawned a spider! It doesn't do much, but we've learned a lot here!

Here's some takeaways from this example

  • You can import any .gltf model into your game to represent your entities, just throw them into your projects assets/models/ directory.

  • You have full control over your entities scale, animation state, and much more.

  • You can spawn your spider when you're ready, by providing the world it spawns into, and the coordinate to spawn it at in the world.

If all went well, we should see a spider. Also, notice those lines around our character and the spider? Those are the debug lines of our colliders attached to our Spider and Player Entity! This debug feature is super helpful for debugging our entities and their physical interactions in the world. You can enable debug lines by uncommenting world.simulation.enableDebugRendering(true) - be aware that debug lines can be laggy in larger worlds.

6. Make the spider do something...

This isn't much of a game yet... let's do something simple. How about every time our character runs into the spider it gets bounced in the direction we hit it from, and also has a bit of a spin applied to it?

We can do this with a few lines of code and using an event!

// Our previous spider entity code...
const spider = new Entity({
  // ... previous spider init code...
});

spider.on(EntityEvent.ENTITY_COLLISION, ({ otherEntity, started }) => {
  if (!started) return;

  const hitDirection = otherEntity.directionFromRotation;
  console.log(hitDirection);
  const spiderMass = spiderEntity.mass;
  spiderEntity.applyImpulse({ 
    x: -hitDirection.x * 10 * spiderMass, 
    y: 10 * spiderMass, 
    z: -hitDirection.z * 10 * spiderMass ,
  });
});

// Spawn the spider in the world.
spider.spawn(world, { x: 0, y: 10, z: -15 });

After you've updated your code in your init function to use the on()method to register an event listener for the EntityEvent.ENTITY_COLLISIONevent, your spider when you run into it should behave something like this:

7. Complete Code

Here's the complete code for our basic push the spider game.

import {
  startServer,
  Entity,
  EntityEvent,
  PlayerEntity,
  PlayerEvent
} from 'hytopia';

import worldMap from './assets/map.json';

/**
 * startServer is always the entry point for our game.
 * It accepts a single function where we should do any
 * setup necessary for our game. The init function is
 * passed a World instance which is the default
 * world created by the game server on startup.
 * 
 * Documentation: https://github.com/hytopiagg/sdk/blob/main/docs/server.startserver.md
 */

startServer(world => {
  /**
   * Enable debug rendering of the physics simulation.
   * This will overlay lines in-game representing colliders,
   * rigid bodies, and raycasts. This is useful for debugging
   * physics-related issues in a development environment.
   * For larger worlds, enabling this can cause performance
   * issues, which will be noticed as dropped frame rates
   * and higher RTT times.
   */
  // world.simulation.enableDebugRendering(true);

  /**
   * Load our map.
   * You can build your own map using https://build.hytopia.com
   * After building, hit export and drop the .json file in
   * the assets folder as map.json.
   */
  world.loadMap(worldMap);

  /**
   * Handle player joining the game. The onPlayerJoin
   * function is called when a new player connects to
   * the game. From here, we create a basic player
   * entity instance which automatically handles mapping
   * their inputs to control their in-game entity and
   * internally uses our default character controller.
   */
  world.on(PlayerEvent.JOINED_WORLD, ({ player }) => {
    const playerEntity = new PlayerEntity({
      player,
      name: 'Player',
      modelUri: 'models/players/player.gltf',
      modelLoopedAnimations: [ 'idle' ],
      modelScale: 0.5,
    });
  
    playerEntity.spawn(world, { x: 0, y: 10, z: 0 });
  });

  /**
   * Handle player leaving the game. The onPlayerLeave
   * function is called when a player leaves the game.
   * Because HYTOPIA is not opinionated on join and
   * leave game logic, we are responsible for cleaning
   * up the player and any entities associated with them
   * after they leave. We can easily do this by 
   * getting all the known PlayerEntity instances for
   * the player who left by using our world's EntityManager
   * instance.
   */
  world.on(PlayerEvent.LEFT_WORLD, ({ player }) => {
    world.entityManager.getAllPlayerEntities(player).forEach(entity => entity.despawn());
  };

  const spider = new Entity({
    modelUri: 'models/npcs/spider.gltf',
    modelScale: 2.5,
    modelLoopedAnimations: [ 'idle' ],
  });

  spider.on(EntityEvent.ENTITY_COLLISION, ({ otherEntity, started }) => {
    if (!started) return;

    const hitDirection = otherEntity.directionFromRotation;
    console.log(hitDirection);
    const spiderMass = spiderEntity.mass;
    spiderEntity.applyImpulse({ 
      x: -hitDirection.x * 10 * spiderMass, 
      y: 10 * spiderMass, 
      z: -hitDirection.z * 10 * spiderMass ,
    });
  });

  // Spawn the spider in the world.
  spider.spawn(world, { x: 0, y: 3, z: -15 });
});

8. Recap

That's it! We've made a really simple game of push the spider. This covered a lot of basics such as spawning entities, creating a collision callback, applying an impulse to move the entity, and more.

The best next steps to continue quickly learning all you can do with the HYTOPIA SDK is to...

  • Build your first world map: Build Your First World Map

  • Learn more from our official examples: Use Templates & Examples

Next Steps

API Reference

Learn about the HYTOPIA SDK API Reference, one of the most useful information sources for developing.