Skip to content

3. Feed the monster : your content

Benoît edited this page Feb 26, 2016 · 6 revisions

Here is the typical entry point of an Alchemist application :

public class myApplication extends Alchemist {
	
	public static void main(String[] args) {
		launch(args);
	}
	
	@Override
	protected void onIntialize() {
		... your code here.
	}
}

That will launch an empty editor, with only built in components. From that point, you will have to feed Alchmist with three things :


#components These class are pure data with a very specific shape. Look at the built-in Naming componnent :

public class Naming implements EntityComponent {
	private final String name;
	
	/**
	 * Default constructor, used only in the editor when adding an empty component.
	 */
	public Naming() {
		name = "Unnamed";
	}
	
	public Naming(@JsonProperty("name")String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}
}

A component implements the EntityComponent interface and stores primitive and very standard values only (int, double, string, list, map...). It includes :

  • a default constructor, for Alchemist to create an empty version,
  • a parametrized constructor, for the component to be deserialized using Jackson syntax,
  • getters for each field.

In a future version, components will also include annotations to control their presentation in the inspector.

It is allowed to store more complex data but since Alchemist don't know it, it won't be shown at edit time. You can plug your own things to adapt the editor to your needs : Train your monster.

##injection into Alchemist Nothing to do. Alchemist scans the project classes to find your components.

##about immutability You may have noticed that component's fields are always final in Alchemist. It is considered a best practice to have immutable components but it is not mandatory.

Pros :

  • the editor and the entity engine know every data change, because change a data means create a new component.
  • serialization is consistent, no clone is needed.

Cons :

  • some code may become uselessly heavy.
  • please tell me !

##note about future devloppements If you respect the encouraged way, you will find components writting laborious. We plan to create a component editor (#2) and help is welcomed.

Components will gain annotations to be presented correctly in the GUI, like a UI name, description, drawing order, limits for numeric types, etc.


#processors

If components are data, then processors are the logic. Processors are basically appStates specialized in entity management. They register to one or more component classes, and perform on entities that hold those components, at each tick.

Alchemist provides a built in implementation : BaseProcessor. It simplify things by giving some convenient methods to manage your entities. You can extend BaseProcessor or choose to create your own Processor.

Here is a fictual exemple of a processor managing the model placement of an entity.

public class ModelProc extends BaseProcessor {
	
	@Override
	protected void registerSets() {
		// This processor will process entities holding at least a Model and a Position component.
		registerDefault(Model.class, Position.class);
	}
	
	@Override
	protected void onEntityRemoved(Entity e) {
		// Entity 'e' has lost the Model component. We remove its spatial from the scene.
		RendererPlatform.getMainSceneNode().detachChild(SpatialPool.models.get(e.getId()));
	}

	@Override
	protected void onEntityAdded(Entity e) {
		// Entity 'e' has gain the Model component. We add a new spatial to the scene.
		Model modelComp = e.get(Model.class);
		Spatial s = // getPrototype(modelComp.getPath());
		SpatialPool.models.put(e.getId(), s);
		RendererPlatform.getMainSceneNode().attachChild(s);
	}
	
	@Override
	protected void onEntityUpdated(Entity e) {
		// The model or Position component has chnage. We update the entity position
		Spatial s = SpatialPool.models.get(e.getId());
		Position pos = e.get(Position.class);
		// apply position on s...
		// apply rotation on s...
	}
	
	@Override
	protected void onUpdated() {
		
	}
	
	@Override
	protected void onEntityEachTick(Entity e) {

	}
}

With BaseProcessors, you need to register one or more sets of components. Then, you can override the methods shown above and create behaviors for each event. There is a million ways to create processors and you will have to find your own.

Please read the Zay-ES blog to discover the Entity Component System architecture and watch the best practices in action. You can also dig into the sample project code provided with Alchemist to find more inspiration.

##injection into Alchemist

the only true tricky part

Processors are injected via Pipelines. Add your processors into a pipeline in the order you want them to be executed.

A pipeline is a set of ordered processors that are executed in the same thread. They are of two types :

  • Pipelines ran by the renderer : they will be updated at each rendering frame. If the rendering slows down, the pipeline slows down too. Used for models, particles, sprites, audio, etc...
  • Independant pipelines : they will run at a fixed 50 ticks per second in their own thread. Usefull to have a consistent time per frame, and capitalize on the power of our multi-core devices. Used for game logic.

Note : Only processors in a renderer pipeline are allowed to modify the renderer scene !

When you create a pipeline, you also choose if it will be executed at edition time or not.

Important note : You can't instanciate a pipeline. Call PipelineManager.createPipeline(...) to get a new pipeline. Alchemist does not support pipeline or processor change after initialization. Only the pipelines and processors declared in the onInitialize() method will be used.

Processor are also usefull to put non-ECS logic into your game. You can create a processor with any kind of code into its update method and put it in the correct pipeline to see it executed.


#game input listener

When you click on "play" (preview time), Alchemist will disconnect all its input listeners and will use yours instead. This allow you to get the key and mouse events that occur on the scene view.

You will have to create your own listener by implementing the SceneInputListener interface.

##injection into Alchemist

Just set it out :

@Override
protected void onIntialize() {
	...
	// adding the preview time listener
	ViewPlatform.setGameInputListener(new GameInputListener());
}

Now you have everything needed to implement your game into Alchemist. But you will certainly find limitations in the editor and will want to enhance it to your own needs. When that time has come, please read the next section : Train the monster