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
Open

Conversation

vkvam
Copy link

@vkvam vkvam commented Jun 25, 2014

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):

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<ComponentA> {
        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<ComponentB> {
        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);
    }
}

@apotapov
Copy link
Owner

Sorry been a busy couple of weeks. Thank you very much for putting it together. I'll take a look at this over the weekend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants