apotapov/gdx-artemis — pull request #42 by vkvam (2014-06-25 21:54:55+00:00)
Title: FiniteStateMachine for Components

So, I've been working a while on an FiniteStateMachine solution for components. My brain hurts pretty bad, but I think I've found a way that requires minimal change to existing code, just a few additions, while still being quite efficient and flexible.

The solution is inspired by http://www.richardlord.net/blog/finite-state-machines-with-ash. Have a look when you find some time, and see if it is something we could use.

Quite a few tests are in place to check both functionality and pooling. I've also profiled to check performance, which seems pretty decent.

Component are provided by componentproviders that are added to the EntityStateMachine through EntityState.

* For each individual component that may exist on an entity, there should be one provider.
* If a provider is shared by two states, components will not be added or removed from the entity on stateChange.
* If two states have providers of the same component type, but different provider instances, the component will be swapped, triggering an entity change.

Simple example (tests: com.artemis.fsm.Simple):
```java
package com.artemis.fsm;

import com.artemis.Component;
import com.artemis.Entity;
import com.artemis.World;
import junit.framework.Assert;
import org.junit.Test;

/**
* Created by Vemund Kvam on 25/06/14.
*/
public class Simple {

public static class ComponentA implements Component {
public int valueA;
@Override
public void reset() {
}
}

public static class ComponentB implements Component {
public int valueA;
@Override
public void reset() {
}
}

public static class ComponentProviderA extends ComponentProvider {
public int value, initialValue;
@Override
protected void onProviderInit() {
value = initialValue;
}
@Override
protected void onRemove(ComponentA component) {
value = component.valueA;
}
@Override
protected void onAdd(ComponentA component) {
component.valueA = value;
}
}

public static class ComponentProviderB extends ComponentProvider {
public int value, initialValue;
@Override
protected void onProviderInit() {
value = initialValue;
}
@Override
protected void onRemove(ComponentB component) {
value = component.valueA;
}
@Override
protected void onAdd(ComponentB component) {
component.valueA = value;
}
}

@Test
public void testAPISimple() {
World world = new World();
Entity entity = world.createEntity();

EntityStateMachine machine = entity.getEntityStateMachine();
ComponentProviderA providerA = machine.createComponentProvider(ComponentProviderA.class);
providerA.initialValue = 1;

ComponentProviderB providerB = machine.createComponentProvider(ComponentProviderB.class);
providerB.initialValue = 2;

machine.createState("State A").add(providerA);
machine.createState("State B").add(providerA).add(providerB);

entity.activateFiniteState("State B");
world.process();
ComponentA componentAFromStateB = entity.getComponent(ComponentA.class);
// Assert initialValue was set.
Assert.assertEquals(1,componentAFromStateB.valueA);
// Set a value to test restore functionality given by providerB.onRemove(component) and providerB.onAdd(component);
entity.getComponent(ComponentB.class).valueA=-1;

entity.activateFiniteState("State A");
world.process();
// Assert ComponentB was removed
Assert.assertNull(entity.getComponent(ComponentB.class));
// Assert ComponentA was untouched as State A and State B uses the same provider instance for ComponentA
Assert.assertEquals(componentAFromStateB,entity.getComponent(ComponentA.class));

entity.activateFiniteState("State B");
world.process();
// Assert value was transferred back to component from provider.
Assert.assertEquals(-1,entity.getComponent(ComponentB.class).valueA);
}
}
```