From b8e0105bdcb72a1d319d1f47e4f32f365ea2e774 Mon Sep 17 00:00:00 2001 From: Richard Lord Date: Thu, 12 Jan 2012 21:06:23 +0000 Subject: [PATCH] Add a clone method to the entity class --- src/net/richardlord/ash/core/Entity.as | 68 +++++++++++++++++- .../net/richardlord/ash/core/EntityTests.as | 71 ++++++++++++++----- 2 files changed, 120 insertions(+), 19 deletions(-) diff --git a/src/net/richardlord/ash/core/Entity.as b/src/net/richardlord/ash/core/Entity.as index a3cfe7a..16adfb1 100644 --- a/src/net/richardlord/ash/core/Entity.as +++ b/src/net/richardlord/ash/core/Entity.as @@ -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. + * + *

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

+ * + *

public class PositionComponent + * { + * public var x : Number; + * public var y : Number; + * }

+ * + *

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.

+ */ public class Entity { public var name : String; @@ -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 ) @@ -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 ] ) @@ -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; + } } } diff --git a/test/src/net/richardlord/ash/core/EntityTests.as b/test/src/net/richardlord/ash/core/EntityTests.as index e5de823..2476a74 100644 --- a/test/src/net/richardlord/ash/core/EntityTests.as +++ b/test/src/net/richardlord/ash/core/EntityTests.as @@ -3,30 +3,31 @@ 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 { @@ -34,7 +35,7 @@ package net.richardlord.ash.core entity.add( component ); assertThat( entity.get( MockComponent ), sameInstance( component ) ); } - + [Test] public function canStoreAndRetrieveMultipleComponents() : void { @@ -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 { @@ -55,7 +56,7 @@ package net.richardlord.ash.core entity.add( component2 ); assertThat( entity.get( MockComponent ), sameInstance( component2 ) ); } - + [Test] public function canStoreBaseAndExtendedComponents() : void { @@ -66,7 +67,7 @@ package net.richardlord.ash.core assertThat( entity.get( MockComponent ), sameInstance( component1 ) ); assertThat( entity.get( MockComponentExtended ), sameInstance( component2 ) ); } - + [Test] public function canStoreExtendedComponentAsBaseType() : void { @@ -74,27 +75,27 @@ package net.richardlord.ash.core 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 { @@ -103,7 +104,7 @@ package net.richardlord.ash.core entity.remove( MockComponent ); assertThat( entity.has( MockComponent ), isFalse() ); } - + [Test] public function storingComponentTriggersAddedSignal() : void { @@ -111,7 +112,7 @@ package net.richardlord.ash.core entity.componentAdded.add( async.add() ); entity.add( component ); } - + [Test] public function removingComponentTriggersRemovedSignal() : void { @@ -120,7 +121,7 @@ package net.richardlord.ash.core entity.componentRemoved.add( async.add() ); entity.remove( MockComponent ); } - + [Test] public function componentAddedSignalContainsCorrectParameters() : void { @@ -128,7 +129,7 @@ package net.richardlord.ash.core entity.componentAdded.add( async.add( testSignalContent, 10 ) ); entity.add( component ); } - + [Test] public function componentRemovedSignalContainsCorrectParameters() : void { @@ -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 ) );