package org.andengine.examples.app.cityradar; import java.util.ArrayList; import java.util.HashMap; import org.andengine.engine.camera.Camera; import org.andengine.engine.camera.hud.HUD; import org.andengine.engine.options.EngineOptions; import org.andengine.engine.options.ScreenOrientation; import org.andengine.engine.options.resolutionpolicy.FillResolutionPolicy; import org.andengine.entity.IEntity; import org.andengine.entity.modifier.LoopEntityModifier; import org.andengine.entity.modifier.RotationModifier; import org.andengine.entity.primitive.Line; import org.andengine.entity.scene.Scene; import org.andengine.entity.sprite.Sprite; import org.andengine.entity.text.Text; import org.andengine.examples.adt.cityradar.City; import org.andengine.input.sensor.location.ILocationListener; import org.andengine.input.sensor.location.LocationProviderStatus; import org.andengine.input.sensor.location.LocationSensorOptions; import org.andengine.input.sensor.orientation.IOrientationListener; import org.andengine.input.sensor.orientation.OrientationData; import org.andengine.opengl.font.Font; import org.andengine.opengl.font.FontFactory; import org.andengine.opengl.texture.TextureOptions; import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas; import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory; import org.andengine.opengl.texture.atlas.bitmap.BuildableBitmapTextureAtlas; import org.andengine.opengl.texture.atlas.bitmap.source.IBitmapTextureAtlasSource; import org.andengine.opengl.texture.atlas.buildable.builder.BlackPawnTextureAtlasBuilder; import org.andengine.opengl.texture.atlas.buildable.builder.ITextureAtlasBuilder.TextureAtlasBuilderException; import org.andengine.opengl.texture.region.ITextureRegion; import org.andengine.opengl.util.GLState; import org.andengine.ui.activity.SimpleBaseGameActivity; import org.andengine.util.debug.Debug; import org.andengine.util.math.MathUtils; import org.andengine.util.modifier.ease.EaseLinear; import android.graphics.Color; import android.graphics.Typeface; import android.location.Criteria; import android.location.Location; import android.location.LocationManager; import android.os.Bundle; public class CityRadarActivity extends SimpleBaseGameActivity implements IOrientationListener, ILocationListener { // =========================================================== // Constants // =========================================================== private static final boolean USE_MOCK_LOCATION = false; private static final boolean USE_ACTUAL_LOCATION = !USE_MOCK_LOCATION; private static final int CAMERA_WIDTH = 480; private static final int CAMERA_HEIGHT = 800; private static final int GRID_SIZE = 80; // =========================================================== // Fields // =========================================================== private Camera mCamera; private BuildableBitmapTextureAtlas mBuildableBitmapTextureAtlas; private ITextureRegion mRadarPointTextureRegion; private ITextureRegion mRadarTextureRegion; private Font mFont; private Location mUserLocation; private final ArrayList<City> mCities = new ArrayList<City>(); private final HashMap<City, Sprite> mCityToCitySpriteMap = new HashMap<City, Sprite>(); private final HashMap<City, Text> mCityToCityNameTextMap = new HashMap<City, Text>(); private Scene mScene; // =========================================================== // Constructors // =========================================================== public CityRadarActivity() { this.mCities.add(new City("London", 51.509, -0.118)); this.mCities.add(new City("New York", 40.713, -74.006)); // this.mCities.add(new City("Paris", 48.857, 2.352)); this.mCities.add(new City("Beijing", 39.929, 116.388)); this.mCities.add(new City("Sydney", -33.850, 151.200)); this.mCities.add(new City("Berlin", 52.518, 13.408)); this.mCities.add(new City("Rio", -22.908, -43.196)); this.mCities.add(new City("New Delhi", 28.636, 77.224)); this.mCities.add(new City("Cape Town", -33.926, 18.424)); this.mUserLocation = new Location(LocationManager.GPS_PROVIDER); if(USE_MOCK_LOCATION) { this.mUserLocation.setLatitude(51.518); this.mUserLocation.setLongitude(13.408); } } // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== @Override public EngineOptions onCreateEngineOptions() { this.mCamera = new Camera(0, 0, CityRadarActivity.CAMERA_WIDTH, CityRadarActivity.CAMERA_HEIGHT); return new EngineOptions(true, ScreenOrientation.PORTRAIT_FIXED, new FillResolutionPolicy(), this.mCamera); } @Override public org.andengine.engine.Engine onCreateEngine(final EngineOptions pEngineOptions) { return new org.andengine.engine.Engine(pEngineOptions); } @Override public void onCreateResources() { /* Init font. */ this.mFont = FontFactory.create(this.getFontManager(), this.getTextureManager(), 256, 256, TextureOptions.BILINEAR, Typeface.DEFAULT, 12, true, Color.WHITE); this.mFont.load(); /* Init TextureRegions. */ this.mBuildableBitmapTextureAtlas = new BuildableBitmapTextureAtlas(this.getTextureManager(), 512, 256, TextureOptions.BILINEAR); BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/"); this.mRadarTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mBuildableBitmapTextureAtlas, this, "radar.png"); this.mRadarPointTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mBuildableBitmapTextureAtlas, this, "radarpoint.png"); try { this.mBuildableBitmapTextureAtlas.build(new BlackPawnTextureAtlasBuilder<IBitmapTextureAtlasSource, BitmapTextureAtlas>(0, 1, 0)); this.mBuildableBitmapTextureAtlas.load(); } catch (final TextureAtlasBuilderException e) { Debug.e(e); } } @Override public Scene onCreateScene() { this.mScene = new Scene(); final HUD hud = new HUD(); this.mCamera.setHUD(hud); /* BACKGROUND */ this.initBackground(hud); /* CITIES */ this.initCitySprites(); return this.mScene; } private void initCitySprites() { final int cityCount = this.mCities.size(); for(int i = 0; i < cityCount; i++) { final City city = this.mCities.get(i); final Sprite citySprite = new Sprite(CityRadarActivity.CAMERA_WIDTH / 2, CityRadarActivity.CAMERA_HEIGHT / 2, this.mRadarPointTextureRegion, this.getVertexBufferObjectManager()); citySprite.setColor(0, 0.5f, 0, 1f); final Text cityNameText = new Text(CAMERA_WIDTH / 2, CAMERA_HEIGHT / 2, this.mFont, city.getName(), this.getVertexBufferObjectManager()) { @Override protected void onManagedDraw(final GLState pGLState, final Camera pCamera) { /* This ensures that the name of the city is always 'pointing down'. */ this.setRotation(-CityRadarActivity.this.mCamera.getRotation()); super.onManagedDraw(pGLState, pCamera); } }; cityNameText.setRotationCenterY(- citySprite.getHeight() / 2); this.mCityToCityNameTextMap.put(city, cityNameText); this.mCityToCitySpriteMap.put(city, citySprite); this.mScene.attachChild(citySprite); this.mScene.attachChild(cityNameText); } } private void initBackground(final IEntity pEntity) { /* Vertical Grid lines. */ for(int i = CityRadarActivity.GRID_SIZE / 2; i < CityRadarActivity.CAMERA_WIDTH; i += CityRadarActivity.GRID_SIZE) { final Line line = new Line(i, 0, i, CityRadarActivity.CAMERA_HEIGHT, this.getVertexBufferObjectManager()); line.setColor(0, 0.5f, 0, 1f); pEntity.attachChild(line); } /* Horizontal Grid lines. */ for(int i = CityRadarActivity.GRID_SIZE / 2; i < CityRadarActivity.CAMERA_HEIGHT; i += CityRadarActivity.GRID_SIZE) { final Line line = new Line(0, i, CityRadarActivity.CAMERA_WIDTH, i, this.getVertexBufferObjectManager()); line.setColor(0, 0.5f, 0, 1f); pEntity.attachChild(line); } /* Radar sprite. */ final Sprite radarSprite = new Sprite(CityRadarActivity.CAMERA_WIDTH / 2 - this.mRadarTextureRegion.getWidth(), CityRadarActivity.CAMERA_HEIGHT / 2 - this.mRadarTextureRegion.getHeight(), this.mRadarTextureRegion, this.getVertexBufferObjectManager()); radarSprite.setColor(0, 1f, 0, 1f); radarSprite.setRotationCenter(radarSprite.getWidth(), radarSprite.getHeight()); radarSprite.registerEntityModifier(new LoopEntityModifier(new RotationModifier(3, 0, 360, EaseLinear.getInstance()))); pEntity.attachChild(radarSprite); /* Title. */ final Text titleText = new Text(0, 0, this.mFont, "-- CityRadar --", this.getVertexBufferObjectManager()); titleText.setPosition(CAMERA_WIDTH / 2 - titleText.getWidth() / 2, titleText.getHeight() + 35); titleText.setScale(2); titleText.setScaleCenterY(0); pEntity.attachChild(titleText); } @Override public void onGameCreated() { this.refreshCitySprites(); } @Override protected void onResume() { super.onResume(); this.enableOrientationSensor(this); final LocationSensorOptions locationSensorOptions = new LocationSensorOptions(); locationSensorOptions.setAccuracy(Criteria.ACCURACY_COARSE); locationSensorOptions.setMinimumTriggerTime(0); locationSensorOptions.setMinimumTriggerDistance(0); this.enableLocationSensor(this, locationSensorOptions); } @Override protected void onPause() { super.onPause(); this.mEngine.disableOrientationSensor(this); this.mEngine.disableLocationSensor(this); } @Override public void onOrientationAccuracyChanged(final OrientationData pOrientationData) { } @Override public void onOrientationChanged(final OrientationData pOrientationData) { this.mCamera.setRotation(-pOrientationData.getYaw()); } @Override public void onLocationChanged(final Location pLocation) { if(USE_ACTUAL_LOCATION) { this.mUserLocation = pLocation; } this.refreshCitySprites(); } @Override public void onLocationLost() { } @Override public void onLocationProviderDisabled() { } @Override public void onLocationProviderEnabled() { } @Override public void onLocationProviderStatusChanged(final LocationProviderStatus pLocationProviderStatus, final Bundle pBundle) { } // =========================================================== // Methods // =========================================================== private void refreshCitySprites() { final double userLatitudeRad = MathUtils.degToRad((float) this.mUserLocation.getLatitude()); final double userLongitudeRad = MathUtils.degToRad((float) this.mUserLocation.getLongitude()); final int cityCount = this.mCities.size(); double maxDistance = Double.MIN_VALUE; /* Calculate the distances and bearings of the cities to the location of the user. */ for(int i = 0; i < cityCount; i++) { final City city = this.mCities.get(i); final double cityLatitudeRad = MathUtils.degToRad((float) city.getLatitude()); final double cityLongitudeRad = MathUtils.degToRad((float) city.getLongitude()); city.setDistanceToUser(GeoMath.calculateDistance(userLatitudeRad, userLongitudeRad, cityLatitudeRad, cityLongitudeRad)); city.setBearingToUser(GeoMath.calculateBearing(userLatitudeRad, userLongitudeRad, cityLatitudeRad, cityLongitudeRad)); maxDistance = Math.max(maxDistance, city.getDistanceToUser()); } /* Calculate a scaleRatio so that all cities are visible at all times. */ final double scaleRatio = (CityRadarActivity.CAMERA_WIDTH / 2) / maxDistance * 0.93f; for(int i = 0; i < cityCount; i++) { final City city = this.mCities.get(i); final Sprite citySprite = this.mCityToCitySpriteMap.get(city); final Text cityNameText = this.mCityToCityNameTextMap.get(city); final float bearingInRad = MathUtils.degToRad(90 - (float) city.getBearingToUser()); final float x = (float) (CityRadarActivity.CAMERA_WIDTH / 2 + city.getDistanceToUser() * scaleRatio * Math.cos(bearingInRad)); final float y = (float) (CityRadarActivity.CAMERA_HEIGHT / 2 - city.getDistanceToUser() * scaleRatio * Math.sin(bearingInRad)); citySprite.setPosition(x - citySprite.getWidth() / 2, y - citySprite.getHeight() / 2); final float textX = x - cityNameText.getWidth() / 2; final float textY = y + citySprite.getHeight() / 2; cityNameText.setPosition(textX, textY); } } // =========================================================== // Inner and Anonymous Classes // =========================================================== /** * Note: Formulas taken from <a href="http://www.movable-type.co.uk/scripts/latlong.html">here</a>. */ private static class GeoMath { // =========================================================== // Constants // =========================================================== private static final double RADIUS_EARTH_METERS = 6371000; // =========================================================== // Fields // =========================================================== // =========================================================== // Constructors // =========================================================== // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== // =========================================================== // Methods // =========================================================== /** * @return the distance in meters. */ public static double calculateDistance(final double pLatitude1, final double pLongitude1, final double pLatitude2, final double pLongitude2) { return Math.acos(Math.sin(pLatitude1) * Math.sin(pLatitude2) + Math.cos(pLatitude1) * Math.cos(pLatitude2) * Math.cos(pLongitude2 - pLongitude1)) * RADIUS_EARTH_METERS; } /** * @return the bearing in degrees. */ public static double calculateBearing(final double pLatitude1, final double pLongitude1, final double pLatitude2, final double pLongitude2) { final double y = Math.sin(pLongitude2 - pLongitude1) * Math.cos(pLatitude2); final double x = Math.cos(pLatitude1) * Math.sin(pLatitude2) - Math.sin(pLatitude1) * Math.cos(pLatitude2) * Math.cos(pLongitude2 - pLongitude1); final float bearing = MathUtils.radToDeg((float) Math.atan2(y, x)); return (bearing + 360) % 360; } // =========================================================== // Inner and Anonymous Classes // =========================================================== } }