package com.fdangelo.circleworld.universeengine; import java.util.ArrayList; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.ShortArray; import com.fdangelo.circleworld.universeengine.objects.Avatar; import com.fdangelo.circleworld.universeengine.objects.FollowParentParameters; import com.fdangelo.circleworld.universeengine.objects.Ship; import com.fdangelo.circleworld.universeengine.objects.UniverseObject; import com.fdangelo.circleworld.universeengine.tilemap.Planet; import com.fdangelo.circleworld.universeengine.utils.UEProfiler; import com.fdangelo.circleworld.utils.Mathf; public class Universe { // Max number of things public static final int MAX_THINGS = 8192; // 360 degress in radians private static final float TWO_PI = MathUtils.PI * 2.0f; // (degrees to radians / 100) private static final float DEG_TO_RAD_OVER_100 = 0.000174532925f; private static final float POSITIONS_TIME_SCALE = 0.01f; private final Thing[] things; private final ThingPosition[] thingsPositions; private short thingsAmount; private final short[] thingsToRender = new short[MAX_THINGS]; private short thingsToRenderAmount; private short startingPlanet; private final ArrayList<Planet> planets = new ArrayList<Planet>(); private final ArrayList<UniverseObject> tilemapObjects = new ArrayList<UniverseObject>(); private final UniverseFactory universeFactory = new UniverseFactory(); private float time; private Avatar avatar; private Ship ship; private IUniverseListener listener; public final short getStartingPlanet() { return startingPlanet; } public final Avatar getAvatar() { return avatar; } public final Ship getShip() { return ship; } public final Thing[] getThings() { return things; } public final ThingPosition[] getThingsPositions() { return thingsPositions; } public final short[] getThingsToRender() { return thingsToRender; } public final short getThingsToRenderAmount() { return thingsToRenderAmount; } public final IUniverseListener getListener() { return listener; } public final void setListener(final IUniverseListener value) { listener = value; } public Universe() { things = new Thing[MAX_THINGS]; for (int i = 0; i < MAX_THINGS; i++) { things[i] = new Thing(); } thingsPositions = new ThingPosition[MAX_THINGS]; for (int i = 0; i < MAX_THINGS; i++) { thingsPositions[i] = new ThingPosition(); } } public final void init(final int seed, final IUniverseListener listener) { this.listener = listener; time = 0.0f; thingsAmount = new UniverseGeneratorDefault().generate(seed, things); updateThingsToRender(); startingPlanet = thingsToRender[1]; updateUniverse(0); addAvatar(); addShip(); } private final void updateThingsToRender() { thingsToRenderAmount = 0; for (int i = 0; i < thingsAmount; i++) { final short type = things[i].type; if (type == ThingType.Sun || type == ThingType.Planet || type == ThingType.Moon) { thingsToRender[thingsToRenderAmount++] = (short) i; } } } public final void updateUniverse(final float deltaTime) { time += deltaTime; UEProfiler.BeginSample("Universe.UpdatePositions"); updatePositions(time); UEProfiler.EndSample(); for (int i = 0; i < planets.size(); i++) { planets.get(i).update(deltaTime); } for (int i = 0; i < tilemapObjects.size(); i++) { tilemapObjects.get(i).update(deltaTime); } } private final void updatePositions(float time) { time *= POSITIONS_TIME_SCALE; for (int index = 1; index < thingsAmount; index++) { final Thing thing = things[index]; final float parentX = thingsPositions[thing.parent].x; final float parentY = thingsPositions[thing.parent].y; float angle = thing.angle * DEG_TO_RAD_OVER_100; final float distance = thing.distance; float normalizedOrbitalPeriod = time * thing.orbitalPeriodInv; normalizedOrbitalPeriod -= (int) normalizedOrbitalPeriod; float normalizedRotationPeriod = time * thing.rotationPeriodInv; normalizedRotationPeriod -= (int) normalizedRotationPeriod; angle += TWO_PI * normalizedOrbitalPeriod; thingsPositions[index].x = parentX + (Mathf.cos(angle)) * distance; thingsPositions[index].y = parentY + (Mathf.sin(angle)) * distance; thingsPositions[index].rotation = normalizedRotationPeriod * TWO_PI; thingsPositions[index].radius = thing.radius; } } public final Thing getThing(final short thingIndex) { return things[thingIndex]; } public final ThingPosition getThingPosition(final short thingIndex) { return thingsPositions[thingIndex]; } public final Planet getPlanet(final short thingIndex) { for (int i = 0; i < planets.size(); i++) { if (planets.get(i).getThingIndex() == thingIndex) { return planets.get(i); } } if (things[thingIndex].type != ThingType.Sun && things[thingIndex].type != ThingType.Planet && things[thingIndex].type != ThingType.Moon) { return null; } final Planet planet = universeFactory.getPlanet(Planet.getPlanetHeightWithRadius(things[thingIndex].radius)); planet.initPlanet(this, thingIndex); planets.add(planet); return planet; } public final void returnPlanet(final Planet planet) { if (planets.remove(planet)) { if (listener != null) { listener.onPlanetReturned(planet); } universeFactory.returnPlanet(planet); } } private final void addAvatar() { final Planet planet = getPlanet(startingPlanet); avatar = universeFactory.getAvatar(); final Vector2 defaultPosition = planet.getPositionFromTileCoordinate(0, planet.getHeight()); avatar.init(0.75f, 1.05f, planet, FollowParentParameters.Default, defaultPosition.x, defaultPosition.y, 0.0f); addUniverseObject(avatar); } private final void addShip() { ship = universeFactory.getShip(); final Vector2 defaultPosition = avatar.getParent().getPositionFromTileCoordinate(0, avatar.getParent().getHeight() + 5); ship.init(10.0f, 5.0f, avatar.getParent(), FollowParentParameters.None, defaultPosition.x, defaultPosition.y, MathUtils.PI * 0.5f); addUniverseObject(ship); } public final void addUniverseObject(final UniverseObject universeObject) { tilemapObjects.add(universeObject); if (listener != null) { listener.onUniverseObjectAdded(universeObject); } } public final int findClosestRenderedThing(final float worldPosX, final float worldPosY, final float searchRadius) { short closestThingIndex = Short.MAX_VALUE; float closestThingDistance = Float.MAX_VALUE; for (short i = 0; i < thingsToRenderAmount; i++) { final ThingPosition thingPosition = thingsPositions[thingsToRender[i]]; final float dx = worldPosX - thingPosition.x; final float dy = worldPosY - thingPosition.y; final float distance = (dx * dx + dy * dy); if (distance < (thingPosition.radius + searchRadius) * (thingPosition.radius + searchRadius) && distance < closestThingDistance) { closestThingIndex = thingsToRender[i]; closestThingDistance = distance; } } if (closestThingIndex != Short.MAX_VALUE) { return closestThingIndex; } else { return -1; } } public final ShortArray findClosestRenderedThings(final float worldPosX, final float worldPosY, final float searchRadius, ShortArray toReturn) { if (toReturn == null) { toReturn = new ShortArray(); } else { toReturn.clear(); } for (short i = 0; i < thingsToRenderAmount; i++) { final ThingPosition thingPosition = thingsPositions[thingsToRender[i]]; final float dx = worldPosX - thingPosition.x; final float dy = worldPosY - thingPosition.y; final float distance = (dx * dx + dy * dy); if (distance < (thingPosition.radius + searchRadius) * (thingPosition.radius + searchRadius)) { toReturn.add(thingsToRender[i]); } } // TODO: Sort the array based on world pos distance!!! // ThingDistanceComparerReferenceX = worldPosX; // ThingDistanceComparerReferenceY = worldPosY; // Arrays.sort(toReturn.items, thingDistanceComparer); return toReturn; } // private float ThingDistanceComparerReferenceX; // private float ThingDistanceComparerReferenceY; /* * private int ThingDistanceComparer(ushort index1, ushort index2) { Vector2 * p1 = new Vector2(thingsPositions[index1].x, thingsPositions[index1].y) - * ThingDistanceComparerReference; Vector2 p2 = new * Vector2(thingsPositions[index2].x, thingsPositions[index2].y) - * ThingDistanceComparerReference; float diff = p1.sqrMagnitude - * p2.sqrMagnitude; if (diff < 0) return -1; else if (diff > 0) return 1; * else return 0; } */ }