Entities

In the Zeta engine, an entity is any object in a level. This can be as simple as a collision brush or a point light, or as complicated as a player character or an AI-driven NPC. All entities must follow the same framework:

  • They all inherit from com.thirds.zeta.entity.Entity
  • All non-abstract entity classes must have only a single, public constructor, which takes an EntityData object. This data is the position, life, physics information and entity ID loaded from level files and save files.

Creating entities

In a level file

Coming soon - the level editor isn’t implemented yet!

Programmatically

Sometimes, you will want to create entities on the fly during a level. Never call the entity’s constructor directly since it won’t get registered properly into the level; instead, use the EntityFactory. Provide it a class and some entity data and it will handle calling relevant constructors and registering the entity into the level for you.

The entity will instantly become available for you to call methods on, but its update/render methods won’t fire until the next frame. This ensures that each entity always gets one complete frame’s worth of updates at a time - if update was called, you know that preUpdate was also called just before.

Never reuse the same EntityData for initialising multiple entities! This will mess with the ID system.

EntityData in detail

Here is a detailed description of the EntityData class. You can get any entity’s EntityData by calling getData.

Coordinates

Entities use a 3-dimensional vector to store position data. The first two components comprise the x- and y-coordinates of the entity. The bottom left of the screen is (0, 0). The z-coordinate is used to control which entities get updated and rendered before which others, but you shouldn’t need to worry about it in most cases. This is discussed in more detail below.

Size

An entity’s size expands upwards and to the right (fitting with the coordinate system). Size has little in-built usage in Zeta apart from with physics and collision detection. The method getCentre finds the coordinates of the centre of the entity by using the size.

Physics

Entities have no collision detection by default. By setting the physics object, you can enable this functionality. You should ensure that any physics object you create matches the size of the entity. By calling setPhysicsObjectToAABB, Zeta can create a rectangular physics object that matches the size of the object. If a physics object is solid, players can’t walk through them. Otherwise it is only used for custom collision detection, including triggers.

Life

By calling getLife, it tells you how many seconds the entity has been alive (in the level) for.

Name

You can give entities a custom name when in the level editor. This has no intrinsic functionality, apart from being useful when trying to find specific entities programmatically.

Event methods and invocation order

Event methods

Entities are event-driven. Once they are created, they will have a number of methods automatically called by the level that they’re in.

preInit, init, postInit
Called as soon as the entity is created. Always use these methods instead of constructors to initialise data, because constructors can be quite volatile when dealing with save files (and they’re harder to work with when modding), so Zeta provides this useful alternative.
preUpdate, update, postUpdate
Called once every frame while the game is not paused. Takes in a single argument, delta, which is the time in seconds since the last frame.
preRender, render, postRender
Called once every frame whether or not the game is paused. Takes in a single argument, delta, which is the time in seconds since the last frame. Uses the level’s coordinate system, drawing below all UI objects.
preRenderUI, renderUI, postRenderUI
Called once every frame whether or not the game is paused. Takes in a single argument, delta, which is the time in seconds since the last frame. Uses the screen coordinate system, drawing above all game objects.
dispose
Called when the entity is removed from the level and when you might need to clean up data or child entities.
resize
Called when the window changes size.

For detailed descriptions on how to use the render methods, look at LibGDX’s documentation on how to use the SpriteBatch, since Zeta uses their libraries for rendering. Don’t use your own sprite batch, just use the one provided in the render methods! If tinting the sprite batch, reset it to white after you’re done with rendering your entity.

Event rules

As a rule of thumb, put game mechanics in the update method family and put purely graphical mechanics in the render method family. Try not to mix-and-match; weird things may happen when the game is paused or running on a slow machine!

Invocation order

Every frame, the event based methods are called on every entity (as in, all preUpdates happen before any updates or postUpdates) in the following order:

  • preUpdate (and updateMovement if applicable)
  • update
  • postUpdate
  • preRender
  • render
  • postRender

Highest first z-order

The order in which entities get updated within this list is determined by their z-order, more specifically a highest first order. For example, an entity with a z-coordinate of 100 will be rendered before one with a z-coordinate of -250. When changing the position of an entity, Zeta automatically adjusts its z-coordinate to match its y-coordinate (you can do this manually using the setZFromY method). This means that entities are updated and rendered from the top of the screen downwards, ensuring that entities at the bottom of the screen correctly visually overlay entities that should appear behind them.

Other methods

Die

By calling this method, the entity is removed from the game at the start of the next frame, and its dispose method is called. You can also call LevelScreen.getInstance().getLevel().removeEntity(ent).

Save files

Zeta’s save file system serialises all entities, and allows you to choose which fields to serialise and which to freshly initialise yourself when loading from a save file. By marking a field as transient (e.g. private transient int variableName;), this tells Zeta not to save it into the save file. This allows the save file to be smaller and faster to load. Here is a list of object types you may want to make transient:

  • Graphics objects, because they are easy to initialise in your init method, for example:
    • AtlasRegions
    • Textures
  • Other asset files like sounds and music, for the same reason.
  • Other entities as fields

Do not reference another entity as a field in your entity (TODO warn/error when this occurs). When loading back from a save file, it won’t load into the level properly and may even create two copies of the entity; one from the level itself and one from the field. In the worst case, two entities reference each other and produce an infinite loop trying to save all the data! Instead, always use EntityRefs to reference other entities.

Calling the method ref on an entity gives you a reference that can be queried to retrieve the original entity, and can also be saved as a field. This EntityRef object has a method, get, which returns the original entity that was referenced. Another (perhaps more useful) method is refs, or “reference specific”. This gives you an EntityRef.Specific which enforces that the entity it references is of the correct class.

Bear in mind that if you want to put an Entity in a transient field, that is fine because transient fields don’t get saved.

Queued actions

QueuedActions are like alarms that fire events at some specified time in the future, during one (or more) of the entity’s update and render methods. To create a queued action, always use a concrete class not a lambda, since save files can’t reconstruct anonymous classes. Use the queue method to install it into the entity. The life parameter in the QueuedAction constructor shows how many seconds it should wait for before executing the action.