Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FiniteStateMachine for Components #42

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
33 changes: 31 additions & 2 deletions src/main/java/com/artemis/Entity.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.artemis;

import com.artemis.fsm.EntityStateMachine;
import com.artemis.managers.ComponentManager;
import com.artemis.managers.EntityManager;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Bits;
import com.badlogic.gdx.utils.Pool.Poolable;
import com.badlogic.gdx.utils.Pools;

/**
* The entity class. Cannot be instantiated outside the framework, you must
Expand All @@ -27,6 +29,7 @@ public final class Entity implements Poolable {
protected World world;
protected EntityManager entityManager;
protected ComponentManager componentManager;
protected EntityStateMachine entityStateMachine;

/**
* Create an entity for the specified world with the specified id.
Expand Down Expand Up @@ -72,6 +75,10 @@ public Bits getSystemBits() {
public void reset() {
systemBits.clear();
componentBits.clear();
if(entityStateMachine !=null){
Pools.free(entityStateMachine);
entityStateMachine = null;
}
id = 0;
}

Expand All @@ -82,9 +89,9 @@ public String toString() {

/**
* Add a component to this entity.
*
*
* @param component to add to this entity
*
*
* @return this entity for chaining.
*/
public Entity addComponent(Component component) {
Expand All @@ -95,6 +102,28 @@ public Entity addComponent(Component component) {
return this;
}

/**
* Retrieves the EntityStateMachine for this entity, creates a new one if none were present.
*
* @return the entity's EntityStateMachine
*/
public EntityStateMachine getEntityStateMachine(){
if(entityStateMachine ==null) {
entityStateMachine = Pools.obtain(EntityStateMachine.class);
entityStateMachine.setEntity(this);
}
return entityStateMachine;
}

/**
* Activates a {@link com.artemis.fsm.EntityState EntityState} for this entity.
*
* @param stateId the object used to identify the EntityState
*/
public void activateFiniteState(Object stateId) {
entityStateMachine.activateState(stateId);
}

/**
* Removes the component from this entity.
*
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/artemis/World.java
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ public <T extends EntitySystem> T setSystem(T system) {
*
* @param <T> Type of entity system
* @param system the system to add.
* @param passive wether or not this system will be processed by World.process()
* @param passive whether or not this system will be processed by World.process()
* @return the added system.
*/
public <T extends EntitySystem> T setSystem(T system, boolean passive) {
Expand Down
96 changes: 96 additions & 0 deletions src/main/java/com/artemis/fsm/ComponentProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.artemis.fsm;

import com.artemis.Component;
import com.artemis.Entity;
import com.badlogic.gdx.utils.Pool;
import com.badlogic.gdx.utils.Pools;

import java.lang.reflect.ParameterizedType;

/**
* Provider of components for {@link EntityState states}. ComponentProviders are used to
* store componentValues when components are removed from the world and restore when the
* component is re-added to the world, as well as differentiating between components shared
* by states and components individual to each state.
*
* onAdd(),onRemove() and onProviderInit() may be used in any was the user sees fit, but
* the most common use-case for most will be storing and restoring component values.
*
* 1. For each individual component that may exist on an entity, there should be one provider.
* 2. If a provider instance is shared by two states, components will not be added or
* removed from the entity on stateChange between these two state.
* 3. If two states have providers of same component type, but different provider instances,
* the component will be swapped, triggering an entity change.
*
* @author Vemund Kvam on 15/06/14.
*/
public abstract class ComponentProvider<T extends Component> implements Pool.Poolable {
protected T lastComponentProduced;
protected Class<T> componentClass;
protected int classIndex=-1;
protected int instanceIndex=-1;
protected boolean addedStateMachine = false;
protected Entity entity;

public ComponentProvider() {
ParameterizedType superclass = (ParameterizedType) getClass().getGenericSuperclass();
componentClass = (Class<T>) superclass.getActualTypeArguments()[0];
}

/**
* Called before the component is removed from associated {@link com.artemis.Entity Entity}.
*
* Example usage: Save the component values until next time a component is created.
* @param component the removed component.
*/
protected abstract void onRemove(T component);

/**
* Called before the component is added to the entity.
*
* Example usage: Restore component values before adding them to the entity.
* @param component the added component
*/
protected abstract void onAdd(T component);

/**
* Called when the componentProvider is added to EntityStateMachine through EntityState
* for the first time.
*
* Example usage: Set all default values to be used by component.
*/
protected abstract void onProviderInit();


protected void setEntity(Entity entity){
this.entity = entity;
}

protected void setIndices(int classIndex, int instanceIndex){
this.classIndex=classIndex;
this.instanceIndex=instanceIndex;
addedStateMachine = true;
}

protected T createComponent(){
T component = Pools.obtain(componentClass);
lastComponentProduced = component;
onAdd(component);
return component;
}
protected void removedFromEntity(){
T component = entity.getComponent(componentClass);
if(component==lastComponentProduced){
onRemove(component);
}
}

@Override
public void reset() {
lastComponentProduced = null;
classIndex = -1;
instanceIndex = -1;
addedStateMachine =false;
entity=null;
}
}
104 changes: 104 additions & 0 deletions src/main/java/com/artemis/fsm/EntityState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.artemis.fsm;

import com.badlogic.gdx.utils.Bits;
import com.badlogic.gdx.utils.ObjectIntMap;
import com.badlogic.gdx.utils.Pool;

/**
/**
* Represents a state for an EntityStateMachine. The state contains any number of ComponentProviders which
* are used to add components to the entity when this state is entered.
*
* @author Vemund Kvam on 10/06/14.
*/
public class EntityState implements Pool.Poolable{
private EntityStateMachine entityStateMachine;
private ObjectIntMap<Integer> providerIndexForProviderComponentClassIndex = new ObjectIntMap<Integer>(4);
private int bitSize=0;
protected Bits providerIndicesBits = new Bits();;
protected Bits bitsCopy = new Bits();;

protected EntityState(){;
}

/**
* Adds a {@link ComponentProvider ComponentProvider} to this state. Components created
* by providers will be added once the state is activated wit
* {@link com.artemis.Entity#activateFiniteState(Object)} entity.activateFiniteState}.
*
* @param componentProvider to add to this state.
* @return this EntityState for chaining.
*/
public EntityState add(ComponentProvider componentProvider){
if(!componentProvider.addedStateMachine) {
componentProvider.onProviderInit();
int providerInstanceIndex = entityStateMachine.getProviderIndex(componentProvider);
int providerComponentClassIndex = entityStateMachine.getProviderComponentClassIndex(componentProvider);
componentProvider.setIndices(providerComponentClassIndex,providerInstanceIndex);
providerIndicesBits.set(providerInstanceIndex);
ensureBitCopySize(providerInstanceIndex);
providerIndexForProviderComponentClassIndex.put(providerComponentClassIndex, providerInstanceIndex);
}else {
providerIndicesBits.set(componentProvider.instanceIndex);
providerIndexForProviderComponentClassIndex.put(componentProvider.classIndex, componentProvider.instanceIndex);
}

return this;
}

/**
* Removes a {@link ComponentProvider ComponentProvider} from this state.
*
* When the componentProvider is removed from all states on the parent
* {@link EntityStateMachine EntityStateMachine}, the
* provider is pooled.
*
* @param componentProvider to remove from this state.
*/
public void remove(ComponentProvider componentProvider){
int providerIndex = componentProvider.instanceIndex;
remove(providerIndex);
}

private void remove(int providerIndex){
providerIndicesBits.clear(providerIndex);
entityStateMachine.removeComponentProvider(providerIndex);
providerIndexForProviderComponentClassIndex.remove(providerIndex, -1);
}

protected int getProviderIndex(int componentClassIndex){
return providerIndexForProviderComponentClassIndex.get(componentClassIndex,-1);
}

/**
* @param excludeState providers from this state will be excluded
* @return Bits representing the providers on this state.
*/
protected Bits getProviderIndicesCopy(EntityState excludeState){
bitsCopy.clear();
bitsCopy.or(providerIndicesBits);
bitsCopy.andNot(excludeState.providerIndicesBits);
return bitsCopy;
}

private void ensureBitCopySize(int index){
if(bitSize <index){
bitsCopy = new Bits(index);
bitSize = bitsCopy.numBits();
}
}

protected void setEntityStateMachine(EntityStateMachine entityStateMachine) {
this.entityStateMachine = entityStateMachine;
}

@Override
public void reset() {
// Remove all componentProviders
for (int i = providerIndicesBits.nextSetBit(0); i >= 0; i = providerIndicesBits.nextSetBit(i + 1)) {
remove(i);
}
providerIndicesBits.clear();
providerIndexForProviderComponentClassIndex.clear();
}
}
Loading