Skip to content

Commit

Permalink
Add a clone method to the entity class
Browse files Browse the repository at this point in the history
  • Loading branch information
richardlord committed Jan 12, 2012
1 parent 488bee0 commit b8e0105
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 19 deletions.
68 changes: 67 additions & 1 deletion src/net/richardlord/ash/core/Entity.as
Original file line number Diff line number Diff line change
@@ -1,9 +1,27 @@
package net.richardlord.ash.core
{
import flash.utils.Dictionary;
import net.richardlord.ash.signals.Signal2;

import flash.utils.Dictionary;
import flash.utils.describeType;

/**
* A game entity is a collection object for components. Sometimes, the entities in a game
* will mirror the actual characters and objects in the game, but this is not necessary.
*
* <p>Components are simple value objects that contain data relevant to the entity. Entities
* with similar functionality will have instances of the same components. So we might have
* a position component</p>
*
* <p><code>public class PositionComponent
* {
* public var x : Number;
* public var y : Number;
* }</code></p>
*
* <p>All entities that have a position in the game world, will have an instance of the
* position component. Systems operate on entities based on the components they have.</p>
*/
public class Entity
{
public var name : String;
Expand All @@ -22,6 +40,14 @@ package net.richardlord.ash.core
components = new Dictionary();
}

/**
* Add a component to the entity.
*
* @param component The component object to add.
* @param componentClass The class of the component. This is only necessary if the component
* extends another component class and you want the framework to treat the component as of
* the base class type. If not set, the class type is determined directly from the component.
*/
public function add( component : Object, componentClass : Class = null ) : void
{
if ( !componentClass )
Expand All @@ -36,6 +62,11 @@ package net.richardlord.ash.core
componentAdded.dispatch( this, componentClass );
}

/**
* Remove a component from the entity.
*
* @param componentClass The class of the component to be removed.
*/
public function remove( componentClass : Class ) : void
{
if ( components[ componentClass ] )
Expand All @@ -45,14 +76,49 @@ package net.richardlord.ash.core
}
}

/**
* Get a component from the entity.
*
* @param componentClass The class of the component requested.
* @return The component, or null if none was found.
*/
public function get( componentClass : Class ) : *
{
return components[ componentClass ];
}

/**
* Does the entity have a component of a particular type.
*
* @param componentClass The class of the component sought.
* @return true if the entity has a component of the type, false if not.
*/
public function has( componentClass : Class ) : Boolean
{
return components[ componentClass ] != null;
}

/**
* Make a copy of the entity
*
* @return A new entity with new components that are copies of the components on the
* original entity.
*/
public function clone() : Entity
{
var copy : Entity = new Entity();
for each( var component : Object in components )
{
var names : XMLList = describeType( component ).variable.@name;
var componentClass : Class = component["constructor"];
var newComponent : * = new componentClass();
for each( var key : String in names )
{
newComponent[key] = component[key];
}
copy.add( newComponent );
}
return copy;
}
}
}
71 changes: 53 additions & 18 deletions test/src/net/richardlord/ash/core/EntityTests.as
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,39 @@ package net.richardlord.ash.core
import asunit.framework.IAsync;

import org.hamcrest.assertThat;
import org.hamcrest.object.equalTo;
import org.hamcrest.object.isFalse;
import org.hamcrest.object.isTrue;
import org.hamcrest.object.nullValue;
import org.hamcrest.object.sameInstance;

public class EntityTests
{
[Inject]
public var async : IAsync;

private var entity : Entity;

[Before]
public function createEntity() : void
{
entity = new Entity();
}

[After]
public function clearEntity() : void
{
entity = null;
}

[Test]
public function canStoreAndRetrieveComponent() : void
{
var component : MockComponent = new MockComponent();
entity.add( component );
assertThat( entity.get( MockComponent ), sameInstance( component ) );
}

[Test]
public function canStoreAndRetrieveMultipleComponents() : void
{
Expand All @@ -45,7 +46,7 @@ package net.richardlord.ash.core
assertThat( entity.get( MockComponent ), sameInstance( component1 ) );
assertThat( entity.get( MockComponent2 ), sameInstance( component2 ) );
}

[Test]
public function canReplaceComponent() : void
{
Expand All @@ -55,7 +56,7 @@ package net.richardlord.ash.core
entity.add( component2 );
assertThat( entity.get( MockComponent ), sameInstance( component2 ) );
}

[Test]
public function canStoreBaseAndExtendedComponents() : void
{
Expand All @@ -66,35 +67,35 @@ package net.richardlord.ash.core
assertThat( entity.get( MockComponent ), sameInstance( component1 ) );
assertThat( entity.get( MockComponentExtended ), sameInstance( component2 ) );
}

[Test]
public function canStoreExtendedComponentAsBaseType() : void
{
var component : MockComponentExtended = new MockComponentExtended();
entity.add( component, MockComponent );
assertThat( entity.get( MockComponent ), sameInstance( component ) );
}

[Test]
public function getReturnNullIfNoComponent() : void
{
assertThat( entity.get( MockComponent ), nullValue() );
}

[Test]
public function hasComponentIsFalseIfComponentTypeNotPresent() : void
{
entity.add( new MockComponent2() );
assertThat( entity.has( MockComponent ), isFalse() );
}

[Test]
public function hasComponentIsTrueIfComponentTypeIsPresent() : void
{
entity.add( new MockComponent() );
assertThat( entity.has( MockComponent ), isTrue() );
}

[Test]
public function canRemoveComponent() : void
{
Expand All @@ -103,15 +104,15 @@ package net.richardlord.ash.core
entity.remove( MockComponent );
assertThat( entity.has( MockComponent ), isFalse() );
}

[Test]
public function storingComponentTriggersAddedSignal() : void
{
var component : MockComponent = new MockComponent();
entity.componentAdded.add( async.add() );
entity.add( component );
}

[Test]
public function removingComponentTriggersRemovedSignal() : void
{
Expand All @@ -120,15 +121,15 @@ package net.richardlord.ash.core
entity.componentRemoved.add( async.add() );
entity.remove( MockComponent );
}

[Test]
public function componentAddedSignalContainsCorrectParameters() : void
{
var component : MockComponent = new MockComponent();
entity.componentAdded.add( async.add( testSignalContent, 10 ) );
entity.add( component );
}

[Test]
public function componentRemovedSignalContainsCorrectParameters() : void
{
Expand All @@ -137,7 +138,41 @@ package net.richardlord.ash.core
entity.componentRemoved.add( async.add( testSignalContent, 10 ) );
entity.remove( MockComponent );
}


[Test]
public function cloneIsNewReference() : void
{
entity.add( new MockComponent() );
var clone : Entity = entity.clone();
assertThat( clone == entity, isFalse() );
}

[Test]
public function cloneHasChildComponent() : void
{
entity.add( new MockComponent() );
var clone : Entity = entity.clone();
assertThat( clone.has( MockComponent ), isTrue() );
}

[Test]
public function cloneChildComponentIsNewReference() : void
{
entity.add( new MockComponent() );
var clone : Entity = entity.clone();
assertThat( clone.get( MockComponent ) == entity.get( MockComponent ), isFalse() );
}

[Test]
public function cloneChildComponentHasSameProperties() : void
{
var component : MockComponent = new MockComponent();
component.value = 5;
entity.add( component );
var clone : Entity = entity.clone();
assertThat( clone.get( MockComponent ).value, equalTo( 5 ) );
}

private function testSignalContent( signalEntity : Entity, componentClass : Class ) : void
{
assertThat( signalEntity, sameInstance( entity ) );
Expand Down

0 comments on commit b8e0105

Please sign in to comment.