Menu

Making a Game with AGE

g-dollar

As of R28 you must use Android API level 14 or higher.

Getting started is really simple:

As of R28:

  • Create a subclass of GameplayFragment. This encapsulates the top-level component connections.
  • Override the fragment's onCreateView method and inflate your view and return it.
  • If not using layout-based fragments, override the activity's onCreate and install the GameplayFragment to your layout via a FragmentTransaction.
  • Override the create method to return an instance of your game instance.
  • Create a subclass of GameCycle as the game instance.
  • Override methods as appropriate, beginning with startLoading.

Otherwise:

  • Create a subclass of GameplayActivity. This encapsulates the top-level component connections.
  • Override activity's onCreate to set the layout of your view.
  • Override the create method to return an instance of your game instance.
  • Create a subclass of GameCycle as the game instance.
  • Override methods as appropriate, beginning with startLoading.

Game State Updates

AGE is set up so your GameObject instances operate in a single-threaded environment. This greatly simplifies how you need to think about your logic.

This is accomplished by using top-level contexts that hold the update lock while calling GameObject methods:

  • (Game Cycle) During TimerCallback.execute.
  • (Game Cycle) During LoadedCallback.loaded.
  • (Game Cycle) During UnloadedCallback.unloaded.
  • (Game Cycle) During EventCallback.event.

There is one other path into GameObject code, but it is not synchronized:

  • (Resource Loader) During ResourceLoader.load.

This callback does not provide any means to locate other GOs, because the GO has not yet "entered" the system.

Locator

Any callback where it is safe to access your GOs has a Locator in the signature. This component encourages indirect, just-in-time access to other components in the game.

A GO is automatically registered in the Locator via the Install Pipeline (and unregistered via Uninstall pipeline), if it has its locatable flag set to true.

Top-level contexts have this in their signature.

Installer

Any callback where it is safe to use [Pipelines] has an Installer in the signature.

Since the Pipelines are asynchronous, calls to install/uninstall/event are merely requests to start the Pipeline for that GO.

Top-level contexts have this in their signature.

Routes

When a GO enters a Pipeline, you may specify (a list of) the GOs you want it to visit in the route. This results in XXXCallback on each of the GOs in the route.

If you specify a null route, only the "core" part of the Pipeline is executed, and no additional callbacks are made.

The [Pipelines] are set up for you to put all of the "connection" code in the callbacks, and not at the point where you call install/uninstall/event. By setting an appropriate route you are in full control of what "sees" a GO when it "comes out" the other end.

Install Pipeline

Creating GOs and adding them to the system fall to the Install Pipeline. This pipeline includes a dedicated Resource Loader task, with marker interface RequireResourceLoader. This code runs in a limited context, and it is responsible for all time-consuming initialization and acquiring GL resources if rendering graphics (of course you are rendering, rofl).

The Resource Loader forwards each completed GO to the GameCycle, for the continuation of the pipeline.

To maintain the overall sequence of install operations, every GO sent to install visits the Resource Loader task, even if it does not implement the marker interface. This is to "hold its place in line" so it doesn't complete out-of-sequence with other calls to install.

Loaded Callbacks

Some GOs have pre-defined LoadedCallback behavior, e.g. a GameObjectCollection and Scene accept GameObject instances for collection membership.

You should leverage the LoadedCallback in your own GO classes to provide desired behavior when "accepting" GOs on its callback.

Uninstall Pipeline

Removing GOs from the system is done via the Uninstall Pipeline. This does not "damage" the GO in any way; it can be subsequently re-submitted to the Install Pipeline. You must track whether you need to re-execute any code when the RequireResourceLoader interface is called again.

Event Pipeline

An important way to drive the game is the Event Pipeline and its EventCallback. You use GameObject as an event, and specify the receivers of the event in the route. This is very convenient; a target may receive a GO as an event and subsequently decide to install it.

Pipeline Callbacks

The GameCycle class you extend contains abstract methods that receive callbacks from [Pipelines] before they are processed. These methods are always called, so you have an opportunity to "preview" what is happening, or provide functionality for the null route.

GL Resources

AGE provides classes to deal with OpenGL resources:

  • Vertex Attribute Arrays
  • Shader programs
  • Textures
  • Framebuffers
  • Vertex Buffer/Index Objects

Vertex Attribute Arrays

The base class of this is the Geometry class. AGE provides implementations for both Interleaved and Parallel Indexed storage of vertex data.

AGE also provides an OBJ loader, so you can readily convert models (stored in resources) into Geometry instances.

You should use as few models as you can get away with; they are made to be shared liberally between GOs.

Geometry

This sets up the Shader attributes for the Vertex Attributes, and issues GL drawing primitives.

Material

Closely related to the vertex data is the color data, which is often stored with interleaved Vertex Attribute Data (per-vertex color), but with Shaders this is not necessarily always the case. Materials are meant to represent this information.

This sets up the Shader with the attributes and/or uniforms for the color and/or texture unit.

Shader

This represents a linked vertex/fragment GLSL shader program. AGE provides a small library of commonly-used shaders, so you can get started without knowing much about writing them.

Shaders are located symbolically, e.g. "cpv" is the color-per-vertex shader program, "tex" is the texture-mapping shader program.

The shader chosen must correspond with both the Geometry and the Material, because both of these manipulate the Shader attributes and uniforms by pre-defined names.

Custom Shaders

You can register your own GLSL vertex and fragment shaders for AGE, but you must use the pre-defined names for attributes and uniforms.

If you need to use other names, then you must create matching Material implementations to configure them.

Properties

Like most things in AGE, property values are set up as an indirect structure, literally a java.util.Map with Integer keys and Object values.

By avoiding coupling with Java classes just for variance in values, there can be fewer subclasses overall; DrawableGameObject is directly usable if you have a Geometry and a Material.

Property Change Notification

The GameObjectWithProperties class implements the Properties interface, and provides an overrideable method for detecting when Properties.set has been called. This is useful for re-computing other internal values or properties that depend on that property's new value.

Reference Property Values

In general, you can freely modify any property value of reference type without calling Properties.set, because this does not change the actual instance stored in the map. However, this fails to trigger property notifications a GO may have implemented (via GameObjectWithProperties override). A good example of this is the Transform property (see below).

To trigger notifications, re-set the property with the same value retrieved from Properties.getAs. This does not re-save the property (avoids Map.put), but it triggers the notifications.

Scalar Property Values

For scalar property values like int, float, boolean, long, you must use the boxed types that correspond to these: Integer, Float, Boolean, and Long.

This is extremely important! The reason for this is Java's auto-boxing, which is very convenient to your code, but it generates garbage. One way out is to use the valueOf methods of those classes, because they use caching, which avoids making garbage. This is especially true for the integral types, e.g. Integer, Boolean, Long.

If there are many such scalar properties, a strategy to avoid all boxing is to create a separate class to contain all of the scalar data, and use this property whenever changing one of the values. The important issue to be aware of here, is you may need to call Properties.set to trigger notifications.

DrawableGameObject

This is the base class for anything that renders. It contains the basic logic for using a Geometry and a Material to configure a Shader and execute drawing primitives.

For simple tasks, this class can be used as-is, but there is always a need to create subclasses with specific logic attached.

Transform

The most important property of DGO is the Transform, which contains the translate/scale/rotate parameters used to create the local model matrix for the MVP matrix in a Shader.

It is very important to call Properties.set on the Transform property after modifications are complete, to trigger property notification bookkeeping.

Scenes

The mechanism for displaying graphics is to install, build up, and start a Scene. The Scene must be installed first, because it immediately becomes part of the route for the GOs that make it up.

  1. Create and install a Scene with a null route. Nothing displays until the final step (below).
  2. Create and install all GOs that are part of the Scene; specify the Scene in the route.
  3. Create and install a SceneCompleteSentinel with a null route. The Install Pipeline automatically picks this up and passes the Scene to RenderService to start rendering.

Related

Wiki: Home
Wiki: Pipelines

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.