/** @return Whether the entity matches the family requirements or not */ public boolean matches (Entity entity) { Bits entityComponentBits = entity.getComponentBits(); if (!entityComponentBits.containsAll(all)) { return false; } if (!one.isEmpty() && !one.intersects(entityComponentBits)) { return false; } if (!exclude.isEmpty() && exclude.intersects(entityComponentBits)) { return false; } return true; }
public void removeEntityListener (EntityListener listener) { for (int i = 0; i < entityListeners.size; i++) { EntityListenerData entityListenerData = entityListeners.get(i); if (entityListenerData.listener == listener) { // Shift down bitmasks by one step for (Bits mask : entityListenerMasks.values()) { for (int k = i, n = mask.length(); k < n; k++) { if (mask.get(k + 1)) { mask.set(k); } else { mask.clear(k); } } } entityListeners.removeIndex(i--); } } }
private ImmutableArray<Entity> registerFamily(Family family) { ImmutableArray<Entity> entitiesInFamily = immutableFamilies.get(family); if (entitiesInFamily == null) { Array<Entity> familyEntities = new Array<Entity>(false, 16); entitiesInFamily = new ImmutableArray<Entity>(familyEntities); families.put(family, familyEntities); immutableFamilies.put(family, entitiesInFamily); entityListenerMasks.put(family, new Bits()); for (Entity entity : entities){ updateFamilyMembership(entity); } } return entitiesInFamily; }
private int getScore(Bits state, Bits tags) { if (!state.containsAll(tags)) { return Integer.MIN_VALUE; } tmp.clear(); tmp.or(state); tmp.and(tags); int score = 0; int index = 0; while ((index = tmp.nextSetBit(index)) > -1) { score++; index++; } return score; }
public Entity rayTest(Ray ray, Vector3 hitPointWorld, short belongsToFlag, short collidesWithFlag, float rayDistance, Bits layers) { rayFrom.set(ray.origin); rayTo.set(ray.direction).scl(rayDistance).add(rayFrom); callback.setCollisionObject(null); callback.setClosestHitFraction(1f); callback.setRay(ray, rayDistance); callback.setLayers(layers); callback.setCollisionFilterMask(belongsToFlag); callback.setCollisionFilterGroup(collidesWithFlag); dynamicsWorld.rayTest(rayFrom, rayTo, callback); if (callback.hasHit()) { if (hitPointWorld != null) { callback.getHitPointWorld(hitPointWorld); } long entityId = callback.getCollisionObject().getUserPointer(); return getEntity(entityId); } return null; }
/** * Get the triangle which this ray intersects. Returns null if no triangle is intersected. * * @param ray * @param distance * @param allowedMeshParts * @return */ public Triangle rayTest(Ray ray, float distance, Bits allowedMeshParts) { Triangle hitTriangle = null; tmpRayTestRayFrom.set(ray.origin); tmpRayTestRayTo.set(ray.direction).scl(distance).add(tmpRayTestRayFrom); raycastCallback.setHitFraction(1); raycastCallback.clearReport(); raycastCallback.setFrom(tmpRayTestRayFrom); raycastCallback.setTo(tmpRayTestRayTo); raycastCallback.setAllowedMeshPartIndices(allowedMeshParts); collisionShape.performRaycast(raycastCallback, tmpRayTestRayFrom, tmpRayTestRayTo); if (raycastCallback.triangleIndex != -1) { hitTriangle = graph.getTriangleFromMeshPart(raycastCallback.partId, raycastCallback.triangleIndex); } return hitTriangle; }
/** * Make a ray test at this point, using a ray spanning from far up in the sky, to far down in the ground, * along the up axis. * * @param testPoint The test point * @param out The point of intersection between ray and triangle * @param allowedMeshParts Which mesh parts to test. Null if all meshparts should be tested. * @return The triangle, or null if ray did not hit any triangles. */ public Triangle verticalRayTest(Vector3 testPoint, Vector3 out, Bits allowedMeshParts) { tmpRayVerticalRayTest.set( tmpVerticalRayTest1.set(Constants.V3_UP).scl(500).add(testPoint), tmpVerticalRayTest2.set(Constants.V3_DOWN)); Triangle hitTri = rayTest(tmpRayVerticalRayTest, 1000, allowedMeshParts); if (hitTri == null) { // TODO: Perhaps this should be Nan? out.set(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY); return null; } else { Intersector.intersectRayTriangle(tmpRayVerticalRayTest, hitTri.a, hitTri.b, hitTri.c, out); return hitTri; } }
/** * @return Whether the entity matches the family requirements or not */ public boolean matches(Entity entity) { Bits entityComponentBits = entity.getComponentBits(); if (entityComponentBits.isEmpty()) return false; for (int i = all.nextSetBit(0); i >= 0; i = all.nextSetBit(i + 1)) { if (!entityComponentBits.get(i)) return false; } if (!one.isEmpty() && !one.intersects(entityComponentBits)) { return false; } if (!exclude.isEmpty() && exclude.intersects(entityComponentBits)) { return false; } return true; }
/** * @param componentTypes list of {@link Component} classes * @return Bits representing the collection of components for quick comparison and matching. See * {@link Family#getFor(Bits, Bits, Bits)}. */ public static Bits getBitsFor (Class<? extends Component>... componentTypes) { Bits bits = new Bits(); int typesLength = componentTypes.length; for (int i = 0; i < typesLength; i++) { bits.set(ComponentType.getIndexFor(componentTypes[i])); } return bits; }
/** Private constructor, use static method Family.getFamilyFor() */ private Family (Bits all, Bits any, Bits exclude) { this.all = all; this.one = any; this.exclude = exclude; this.index = familyIndex++; }
private static String getFamilyHash (Bits all, Bits one, Bits exclude) { StringBuilder stringBuilder = new StringBuilder(); if (!all.isEmpty()) { stringBuilder.append("{all:").append(getBitsString(all)).append("}"); } if (!one.isEmpty()) { stringBuilder.append("{one:").append(getBitsString(one)).append("}"); } if (!exclude.isEmpty()) { stringBuilder.append("{exclude:").append(getBitsString(exclude)).append("}"); } return stringBuilder.toString(); }
private static String getBitsString (Bits bits) { StringBuilder stringBuilder = new StringBuilder(); int numBits = bits.length(); for (int i = 0; i < numBits; ++i) { stringBuilder.append(bits.get(i) ? "1" : "0"); } return stringBuilder.toString(); }
/** Creates an empty Entity. */ public Entity () { components = new Bag<Component>(); componentsArray = new Array<Component>(false, 16); immutableComponentsArray = new ImmutableArray<Component>(componentsArray); componentBits = new Bits(); familyBits = new Bits(); flags = 0; componentAdded = new Signal<Entity>(); componentRemoved = new Signal<Entity>(); }
public void addEntityListener (Family family, int priority, EntityListener listener) { registerFamily(family); int insertionIndex = 0; while (insertionIndex < entityListeners.size) { if (entityListeners.get(insertionIndex).priority <= priority) { insertionIndex++; } else { break; } } // Shift up bitmasks by one step for (Bits mask : entityListenerMasks.values()) { for (int k = mask.length(); k > insertionIndex; k--) { if (mask.get(k - 1)) { mask.set(k); } else { mask.clear(k); } } mask.clear(insertionIndex); } entityListenerMasks.get(family).set(insertionIndex); EntityListenerData entityListenerData = new EntityListenerData(); entityListenerData.listener = listener; entityListenerData.priority = priority; entityListeners.insert(insertionIndex, entityListenerData); }
@Test public void addAndRemoveComponent () { Entity entity = new Entity(); entity.add(new ComponentA()); assertEquals(1, entity.getComponents().size()); Bits componentBits = entity.getComponentBits(); int componentAIndex = ComponentType.getIndexFor(ComponentA.class); for (int i = 0; i < componentBits.length(); ++i) { assertEquals(i == componentAIndex, componentBits.get(i)); } assertNotNull(am.get(entity)); assertNull(bm.get(entity)); assertTrue(am.has(entity)); assertFalse(bm.has(entity)); entity.remove(ComponentA.class); assertEquals(0, entity.getComponents().size()); for (int i = 0; i < componentBits.length(); ++i) { assertFalse(componentBits.get(i)); } assertNull(am.get(entity)); assertNull(bm.get(entity)); assertFalse(am.has(entity)); assertFalse(bm.has(entity)); }
@Test public void addAndRemoveAllComponents () { Entity entity = new Entity(); entity.add(new ComponentA()); entity.add(new ComponentB()); assertEquals(2, entity.getComponents().size()); Bits componentBits = entity.getComponentBits(); int componentAIndex = ComponentType.getIndexFor(ComponentA.class); int componentBIndex = ComponentType.getIndexFor(ComponentB.class); for (int i = 0; i < componentBits.length(); ++i) { assertEquals(i == componentAIndex || i == componentBIndex, componentBits.get(i)); } assertNotNull(am.get(entity)); assertNotNull(bm.get(entity)); assertTrue(am.has(entity)); assertTrue(bm.has(entity)); entity.removeAll(); assertEquals(0, entity.getComponents().size()); for (int i = 0; i < componentBits.length(); ++i) { assertFalse(componentBits.get(i)); } assertNull(am.get(entity)); assertNull(bm.get(entity)); assertFalse(am.has(entity)); assertFalse(bm.has(entity)); }
public Entry(Animation animation, Array<Integer> tags, boolean loop) { this.animation = animation; this.loop = loop; this.tags = new Bits(); for (int tag : tags) { this.tags.set(tag); } }
private void updateLayer(SpineComponent spine, Bits state, Layer layer) { TrackEntry track = spine.state.getCurrent(layer.track); Animation current = track != null ? track.getAnimation() : null; Entry entry = getBestMatch(layer.entries, state); if (entry != null && entry.animation != current) { logger.info("new best match: " + entry.animation.getName()); spine.state.setAnimation( layer.track, entry.animation, entry.loop ); } }
private Entry getBestMatch(ImmutableArray<Entry> entries, Bits state) { Entry best = null; int maxScore = Integer.MIN_VALUE; for (Entry entry : entries) { int score = getScore(state, entry.tags); if (score > maxScore) { maxScore = score; best = entry; } } return best; }
@Override public void handleMovementRequest(Ray ray, Bits visibleLayers) { // A man only moves if is idle or already moving // For instance, the movement request will be ignored if the man is throwing the stick HumanState state = stateMachine.getCurrentState(); if (state.isIdleState() || state.isMovementState()) { followPathSteerer.calculateNewPath(ray, visibleLayers); } }
public void drawNavMesh(MyShapeRenderer shapeRenderer, SpriteBatch spriteBatch, NavMesh navMesh, GameCharacter character, Bits visibleLayers, Camera camera, BitmapFont font) { this.visibleLayers = visibleLayers; this.shapeRenderer = shapeRenderer; this.navMesh = navMesh; if (shapeRenderer.isDrawing()) { shapeRenderer.end(); } Gdx.gl.glEnable(GL20.GL_BLEND); Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA); shapeRenderer.begin(MyShapeRenderer.ShapeType.Line); drawNavMeshTriangles(); if (character != null && character.steerer instanceof FollowPathSteerer) { FollowPathSteerer fpSteerer = (FollowPathSteerer)character.steerer; drawPathTriangles(fpSteerer.navMeshGraphPath, character.getCurrentTriangle()); if (fpSteerer.navMeshPointPath.getSize() > 0) { drawPathPoints(fpSteerer.navMeshPointPath); } drawClosestPointDebug(character); } shapeRenderer.end(); Gdx.gl.glDisable(GL20.GL_BLEND); drawNavMeshIndices(spriteBatch, camera, font); }
public boolean calculateNewPath(Ray ray, Bits visibleLayers) { if (GameScreen.screen.engine.getScene().navMesh.getPath( steerableBody.getCurrentTriangle(), steerableBody.getGroundPosition(tmpVec1), ray, visibleLayers, GameSettings.CAMERA_PICK_RAY_DST, navMeshGraphPath)) { calculateNewPath0(); return true; } return false; }
/** * Get an array of the vertex indices from the mesh. Any vertices which share the same position will be counted * as a single vertex and share the same index. That is, position duplicates will be filtered out. * * @param mesh * @return */ private static short[] getUniquePositionVertexIndices(Mesh mesh) { FloatBuffer verticesBuffer = mesh.getVerticesBuffer(); int positionOffset = mesh.getVertexAttributes().findByUsage(VertexAttributes.Usage.Position).offset / 4; // Number of array elements which make up a vertex int vertexSize = mesh.getVertexSize() / 4; // The indices tell us which vertices are part of a triangle. short[] indices = new short[mesh.getNumIndices()]; mesh.getIndices(indices); // Marks true if an index has already been compared to avoid unnecessary comparisons Bits handledIndices = new Bits(mesh.getNumIndices()); for (int i = 0; i < indices.length; i++) { short indexI = indices[i]; if (handledIndices.get(indexI)) { // Index handled in an earlier iteration continue; } int vBufIndexI = indexI * vertexSize + positionOffset; float xi = verticesBuffer.get(vBufIndexI++); float yi = verticesBuffer.get(vBufIndexI++); float zi = verticesBuffer.get(vBufIndexI++); for (int j = i + 1; j < indices.length; j++) { short indexJ = indices[j]; int vBufIndexJ = indexJ * vertexSize + positionOffset; float xj = verticesBuffer.get(vBufIndexJ++); float yj = verticesBuffer.get(vBufIndexJ++); float zj = verticesBuffer.get(vBufIndexJ++); if (xi == xj && yi == yj && zi == zj) { indices[j] = indexI; } } handledIndices.set(indexI); } return indices; }
/** * Calculate a triangle graph path between two triangles which are intersected by the rays. * * @param fromRay * @param toRay * @param allowedMeshParts * @param distance * @param path * @return */ public boolean getPath(Ray fromRay, Ray toRay, Bits allowedMeshParts, float distance, NavMeshGraphPath path) { Triangle fromTri = rayTest(fromRay, distance, allowedMeshParts); if (fromTri == null) { Gdx.app.debug(TAG, "From triangle not found."); return false; } Vector3 fromPoint = new Vector3(); Intersector.intersectRayTriangle(fromRay, fromTri.a, fromTri.b, fromTri.c, fromPoint); return getPath(fromTri, fromPoint, toRay, allowedMeshParts, distance, path); }
/** * Calculate a triangle graph path from a start triangle to the triangle which is intersected by a ray. * * @param fromTri * @param fromPoint * @param toRay * @param allowedMeshParts * @param distance * @param path * @return */ public boolean getPath(Triangle fromTri, Vector3 fromPoint, Ray toRay, Bits allowedMeshParts, float distance, NavMeshGraphPath path) { Triangle toTri = rayTest(toRay, distance, allowedMeshParts); if (toTri == null) { Gdx.app.debug(TAG, "To triangle not found."); return false; } Vector3 toPoint = new Vector3(); Intersector.intersectRayTriangle(toRay, toTri.a, toTri.b, toTri.c, toPoint); return getPath(fromTri, fromPoint, toTri, toPoint, path); }
/** * Get a random triangle on the navigation mesh, on any of the allowed mesh parts. * The probability distribution is even in world space, as opposed to triangle index, * meaning large triangles will be chosen more often than small ones. * <p/> * Example usage, to get a random point on the second navigation mesh part: * allowedMeshParts.clear(); * allowedMeshParts.set(1); * Triangle randomTri = navmesh.getRandomTriangle(allowedMeshParts); * Vector3 randomPoint = new Vector3(); * randomTri.getRandomPoint(randomPoint); * * @param allowedMeshParts Bits representing allowed mesh part indices. * @return A random triangle. */ public Triangle getRandomTriangle(Bits allowedMeshParts) { tmpFloatArrayGetRandomTriangle.clear(); tmpFloatArrayGetRandomTriangle.ordered = true; tmpTriArrayGetRandomTriangle.clear(); tmpTriArrayGetRandomTriangle.ordered = true; // To get a uniform distribution over the triangles in the mesh parts // we must take areas of the triangles into account. for (int mpIndex = 0; mpIndex < graph.getMeshPartCount(); mpIndex++) { if (allowedMeshParts.get(mpIndex)) { for (int triIndex = 0; triIndex < graph.getTriangleCount(mpIndex); triIndex++) { Triangle tri = graph.getTriangleFromMeshPart(mpIndex, triIndex); float integratedArea = 0; if (tmpFloatArrayGetRandomTriangle.size > 0) { integratedArea = tmpFloatArrayGetRandomTriangle.get(tmpFloatArrayGetRandomTriangle.size - 1); } tmpFloatArrayGetRandomTriangle.add(integratedArea + tri.area()); tmpTriArrayGetRandomTriangle.add(tri); } } } if (tmpFloatArrayGetRandomTriangle.size == 0) { return null; } float r = MathUtils.random(0f, tmpFloatArrayGetRandomTriangle.get(tmpFloatArrayGetRandomTriangle.size - 1)); int i; for (i = 0; i < tmpFloatArrayGetRandomTriangle.size; i++) { if (r <= tmpFloatArrayGetRandomTriangle.get(i)) { break; } } return tmpTriArrayGetRandomTriangle.get(i); }
/** * @param componentTypes list of {@link Component} classes * @return Bits representing the collection of components for quick comparison and matching. See {@link Family#getFor(Bits, Bits, Bits)}. */ public static Bits getBitsFor(Class<? extends Component>... componentTypes) { Bits bits = new Bits(); int typesLength = componentTypes.length; for (int i = 0; i < typesLength; i++) { bits.set(ComponentType.getIndexFor(componentTypes[i])); } return bits; }
/** * Private constructor, use static method Family.getFamilyFor() */ private Family(Bits all, Bits any, Bits exclude) { this.all = all; this.one = any; this.exclude = exclude; this.index = familyIndex++; }
/** * Returns a family with the passed {@link Component} classes as a descriptor. Each set of component types will * always return the same Family instance. * * @param all entities will have to contain all of the components in the set. See {@link ComponentType#getBitsFor(Class<? extends Component> ...)}. * @param one entities will have to contain at least one of the components in the set.See {@link ComponentType#getBitsFor(Class<? extends Component> ...)}. * @param exclude entities cannot contain any of the components in the set. See {@link ComponentType#getBitsFor(Class<? extends Component> ...)}. * @return The family */ public static Family getFor(Bits all, Bits one, Bits exclude) { String hash = getFamilyHash(all, one, exclude); Family family = families.get(hash, null); if (family == null) { family = new Family(all, one, exclude); families.put(hash, family); } return family; }
private static String getFamilyHash(Bits all, Bits one, Bits exclude) { StringBuilder builder = new StringBuilder(); builder.append("all:"); builder.append(getBitsString(all)); builder.append(",one:"); builder.append(getBitsString(one)); builder.append(",exclude:"); builder.append(getBitsString(exclude)); return builder.toString(); }
private static String getBitsString(Bits bits) { StringBuilder builder = new StringBuilder(); int numBits = bits.length(); for (int i = 0; i < numBits; ++i) { builder.append(bits.get(i) ? "1" : "0"); } return builder.toString(); }
/** * Creates an empty Entity. */ public Entity() { components = new Bag<Component>(); componentsArray = new Array<Component>(false, 16); immutableComponentsArray = new ImmutableArray<Component>(componentsArray); componentBits = new Bits(); familyBits = new Bits(); flags = 0; index = nextIndex++; componentAdded = new Signal<Entity>(); componentRemoved = new Signal<Entity>(); }
@Test public void addAndRemoveComponent() { Entity entity = new Entity(); entity.add(new ComponentA()); assertEquals(1, entity.getComponents().size()); Bits componentBits = entity.getComponentBits(); int componentAIndex = ComponentType.getIndexFor(ComponentA.class); for (int i = 0; i < componentBits.length(); ++i) { assertEquals(i == componentAIndex, componentBits.get(i)); } assertNotNull(am.get(entity)); assertNull(bm.get(entity)); assertTrue(am.has(entity)); assertFalse(bm.has(entity)); entity.remove(ComponentA.class); assertEquals(0, entity.getComponents().size()); for (int i = 0; i < componentBits.length(); ++i) { assertFalse(componentBits.get(i)); } assertNull(am.get(entity)); assertNull(bm.get(entity)); assertFalse(am.has(entity)); assertFalse(bm.has(entity)); }
@Test public void addAndRemoveAllComponents() { Entity entity = new Entity(); entity.add(new ComponentA()); entity.add(new ComponentB()); assertEquals(2, entity.getComponents().size()); Bits componentBits = entity.getComponentBits(); int componentAIndex = ComponentType.getIndexFor(ComponentA.class); int componentBIndex = ComponentType.getIndexFor(ComponentB.class); for (int i = 0; i < componentBits.length(); ++i) { assertEquals(i == componentAIndex || i == componentBIndex, componentBits.get(i)); } assertNotNull(am.get(entity)); assertNotNull(bm.get(entity)); assertTrue(am.has(entity)); assertTrue(bm.has(entity)); entity.removeAll(); assertEquals(0, entity.getComponents().size()); for (int i = 0; i < componentBits.length(); ++i) { assertFalse(componentBits.get(i)); } assertNull(am.get(entity)); assertNull(bm.get(entity)); assertFalse(am.has(entity)); assertFalse(bm.has(entity)); }
public static Bits getBitsFor(String... namesProperties) { Bits bits = new Bits(); int typesLength = namesProperties.length; for (int i = 0; i < typesLength; i++) { bits.set(PropertyType.getIndexFor(namesProperties[i])); } return bits; }
protected Entity(World world, int id) { this.world = world; this.id = id; this.entityManager = world.getEntityManager(); this.componentManager = world.getComponentManager(); systemBits = new Bits(); componentBits = new Bits(); reset(); }
private void removeComponentsOfEntity(Entity e) { Bits componentBits = e.getComponentBits(); for (int i = componentBits.nextSetBit(0); i >= 0; i = componentBits.nextSetBit(i+1)) { componentsByType.get(i).set(e.getId(), null); } componentBits.clear(); }