com.artemis.Aspect Maven / Gradle / Ivy
package com.artemis;
import com.artemis.utils.BitVector;
import java.util.Collection;
import com.artemis.utils.Bag;
/**
* An Aspect is used by systems as a matcher against entities, to check if a
* system is interested in an entity.
*
* Aspects define what sort of component types an entity must possess, or not
* possess.
*
* This creates an aspect where an entity must possess A and B and C:
* {@code Aspect.all(A.class, B.class, C.class)}
*
* This creates an aspect where an entity must possess A and B and C, but must
* not possess U or V.
* {@code Aspect.all(A.class, B.class, C.class).exclude(U.class, V.class)}
*
* This creates an aspect where an entity must possess A and B and C, but must
* not possess U or V, but must possess one of X or Y or Z.
* {@code Aspect.all(A.class, B.class, C.class).exclude(U.class, V.class).one(X.class, Y.class, Z.class)}
*
* You can create and compose aspects in many ways:
* {@code Aspect.one(X.class, Y.class, Z.class).all(A.class, B.class, C.class).exclude(U.class, V.class)}
* is the same as:
* {@code Aspect.all(A.class, B.class, C.class).exclude(U.class, V.class).one(X.class, Y.class, Z.class)}
*
*
* @author Arni Arent
*/
public class Aspect {
/** Component bits the entity must all possess. */
BitVector allSet;
/** Component bits the entity must not possess. */
BitVector exclusionSet;
/** Component bits of which the entity must possess at least one. */
BitVector oneSet;
private Aspect() {
this.allSet = new BitVector();
this.exclusionSet = new BitVector();
this.oneSet = new BitVector();
}
/**
* Get a BitVector containing bits of components the entity must all possess.
*
* @return
* the "all" BitVector
*/
public BitVector getAllSet() {
return allSet;
}
/**
* Get a BitVector containing bits of components the entity must not possess.
*
* @return
* the "exclusion" BitVector
*/
public BitVector getExclusionSet() {
return exclusionSet;
}
/**
* Get a BitVector containing bits of components of which the entity must
* possess atleast one.
*
* @return
* the "one" BitVector
*/
public BitVector getOneSet() {
return oneSet;
}
/**
* Returns whether this Aspect would accept the given Entity.
*/
public boolean isInterested(Entity e){
return isInterested(e.getComponentBits());
}
/**
* Returns whether this Aspect would accept the given set.
*/
public boolean isInterested(BitVector componentBits){
// Check if the entity possesses ALL of the components defined in the aspect.
if(!allSet.isEmpty() && !componentBits.containsAll(allSet))
return false;
// If we are STILL interested,
// Check if the entity possesses ANY of the exclusion components,
// if it does then the system is not interested.
if (!exclusionSet.isEmpty() && exclusionSet.intersects(componentBits))
return false;
// If we are STILL interested,
// Check if the entity possesses ANY of the components in the oneSet.
// If so, the system is interested.
if (!oneSet.isEmpty() && !oneSet.intersects(componentBits))
return false;
return true;
}
/**
* Returns an aspect that matches all entities.
*
* @return an aspect that can be matched against entities
*/
@SuppressWarnings("unchecked")
public static Aspect.Builder all() {
return new Builder().all();
}
/**
* Returns an aspect where an entity must possess all of the specified
* component types.
*
* @param types
* a required component type
*
* @return an aspect that can be matched against entities
*/
@SafeVarargs
public static Aspect.Builder all(Class extends Component>... types) {
return new Builder().all(types);
}
/**
* Returns an aspect where an entity must possess all of the specified
* component types.
*
* @param types
* a required component type
*
* @return an aspect that can be matched against entities
*/
public static Aspect.Builder all(Collection> types) {
return new Builder().all(types);
}
/**
* Excludes all of the specified component types from the aspect.
*
* A system will not be interested in an entity that possesses one of the
* specified exclusion component types.
*
*
* @param types
* component type to exclude
*
* @return an aspect that can be matched against entities
*/
@SafeVarargs
public static Aspect.Builder exclude(Class extends Component>... types) {
return new Builder().exclude(types);
}
/**
* Excludes all of the specified component types from the aspect.
*
* A system will not be interested in an entity that possesses one of the
* specified exclusion component types.
*
*
* @param types
* component type to exclude
*
* @return an aspect that can be matched against entities
*/
public static Aspect.Builder exclude(Collection> types) {
return new Builder().exclude(types);
}
/**
* Returns an aspect where an entity must possess one of the specified
* component types.
*
* @param types
* one of the types the entity must possess
*
* @return an aspect that can be matched against entities
*/
@SafeVarargs
public static Aspect.Builder one(Class extends Component>... types) {
return new Builder().one(types);
}
/**
* Returns an aspect where an entity must possess one of the specified
* component types.
*
* @param types
* one of the types the entity must possess
*
* @return an aspect that can be matched against entities
*/
public static Aspect.Builder one(Collection> types) {
return new Builder().one(types);
}
/**
* Constructs instances of {@link Aspect}.
*/
public static class Builder {
private final Bag> allTypes;
private final Bag> exclusionTypes;
private final Bag> oneTypes;
private Builder() {
allTypes = new Bag>();
exclusionTypes = new Bag>();
oneTypes = new Bag>();
}
/**
* Returns an aspect where an entity must possess all of the specified
* component types.
*
* @param types
* a required component type
*
* @return an aspect that can be matched against entities
*/
public Builder all(Class extends Component>... types) {
for (Class extends Component> t : types) {
allTypes.add(t);
}
return this;
}
/**
* @return new instance of this builder.
*/
public Builder copy() {
Builder b = new Builder();
b.allTypes.addAll(allTypes);
b.exclusionTypes.addAll(exclusionTypes);
b.oneTypes.addAll(oneTypes);
return b;
}
/**
* Returns an aspect where an entity must possess all of the specified
* component types.
*
* @param types
* a required component type
*
* @return an aspect that can be matched against entities
*/
public Builder all(Collection> types) {
for (Class extends Component> t : types) {
allTypes.add(t);
}
return this;
}
/**
* Returns an aspect where an entity must possess one of the specified
* component types.
*
* @param types
* one of the types the entity must possess
*
* @return an aspect that can be matched against entities
*/
public Builder one(Class extends Component>... types) {
for (Class extends Component> t : types)
oneTypes.add(t);
return this;
}
/**
* Returns an aspect where an entity must possess one of the specified
* component types.
*
* @param types
* one of the types the entity must possess
*
* @return an aspect that can be matched against entities
*/
public Builder one(Collection> types) {
for (Class extends Component> t : types)
oneTypes.add(t);
return this;
}
/**
* Excludes all of the specified component types from the aspect.
*
* A system will not be interested in an entity that possesses one of the
* specified exclusion component types.
*
*
* @param types
* component type to exclude
*
* @return an aspect that can be matched against entities
*/
public Builder exclude(Class extends Component>... types) {
for (Class extends Component> t : types)
exclusionTypes.add(t);
return this;
}
/**
* Excludes all of the specified component types from the aspect.
*
* A system will not be interested in an entity that possesses one of the
* specified exclusion component types.
*
*
* @param types
* component type to exclude
*
* @return an aspect that can be matched against entities
*/
public Builder exclude(Collection> types) {
for (Class extends Component> t : types)
exclusionTypes.add(t);
return this;
}
/**
* Bake an aspect.
* @param world
* @return Instance of Aspect.
*/
public Aspect build(World world) {
ComponentTypeFactory tf = world.getComponentManager().typeFactory;
Aspect aspect = new Aspect();
associate(tf, allTypes, aspect.allSet);
associate(tf, exclusionTypes, aspect.exclusionSet);
associate(tf, oneTypes, aspect.oneSet);
return aspect;
}
private static void associate(ComponentTypeFactory tf, Bag> types, BitVector componentBits) {
for (Class extends Component> t : types) {
componentBits.set(tf.getIndexFor(t));
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Builder builder = (Builder) o;
if (!allTypes.equals(builder.allTypes))
return false;
if (!exclusionTypes.equals(builder.exclusionTypes))
return false;
if (!oneTypes.equals(builder.oneTypes))
return false;
return true;
}
@Override
public int hashCode() {
int result = allTypes.hashCode();
result = 31 * result + exclusionTypes.hashCode();
result = 31 * result + oneTypes.hashCode();
return result;
}
@Override
public String toString() {
return "Aspect[" +
"all=" + append(allTypes) +
", one=" + append(oneTypes) +
", exclude=" + append(exclusionTypes) +
']';
}
private StringBuilder append(Bag> types) {
StringBuilder sb = new StringBuilder();
String delim = "";
sb.append("(");
for (Class extends Component> type : types) {
sb.append(delim).append(type.getSimpleName());
delim = ", ";
}
sb.append(")");
return sb;
}
}
}