Java tutorial
/** * Copyright (C) 2016 - Franois LEPAROUX * <p/> * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * <p/> * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * <p/> * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package fr.bde_eseo.eseomega; import android.app.Activity; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Typeface; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.media.MediaPlayer; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.Vibrator; import android.support.annotation.NonNull; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.IOException; import java.util.HashMap; import java.util.Random; import java.util.Timer; import java.util.TimerTask; import fr.bde_eseo.eseomega.profile.UserProfile; import fr.bde_eseo.eseomega.utils.ConnexionUtils; import fr.bde_eseo.eseomega.utils.EncryptUtils; import fr.bde_eseo.eseomega.utils.Utilities; /** * Created by Franois L. on 15/08/2015. */ public class GantierActivity extends Activity implements SensorEventListener { private SensorManager senSensorManager; private Sensor senAccelerometer; private GPGameSurfaceView view; private UserProfile profile; /** Sensors values **/ // angular speeds from gyro private float[] gyro = new float[3]; // rotation matrix from gyro data private float[] gyroMatrix = new float[9]; // orientation angles from gyro matrix private float[] gyroOrientation = new float[3]; // magnetic field vector private float[] magnet = new float[3]; // accelerometer vector private float[] accel = new float[3]; // orientation angles from accel and magnet private float[] accMagOrientation = new float[3]; // final orientation angles from sensor fusion private float[] fusedOrientation = new float[3]; // accelerometer and magnetometer based rotation matrix private float[] rotationMatrix = new float[9]; // Thread position computing public static final int TIME_CONSTANT = 25; public static final float FILTER_COEFFICIENT = 0.97f; private Timer fuseTimer = new Timer(); // MP3 Player private MediaPlayer mediaPlayer; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); view = new GPGameSurfaceView(this); setContentView(view); gyroOrientation[0] = 0.0f; gyroOrientation[1] = 0.0f; gyroOrientation[2] = 0.0f; // initialise gyroMatrix with identity matrix gyroMatrix[0] = 1.0f; gyroMatrix[1] = 0.0f; gyroMatrix[2] = 0.0f; gyroMatrix[3] = 0.0f; gyroMatrix[4] = 1.0f; gyroMatrix[5] = 0.0f; gyroMatrix[6] = 0.0f; gyroMatrix[7] = 0.0f; gyroMatrix[8] = 1.0f; senSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); profile = new UserProfile(); profile.readProfilePromPrefs(this); // Create media player only if not playing if (mediaPlayer == null) mediaPlayer = new MediaPlayer(); } @Override public void onBackPressed() { finish(); if (mediaPlayer != null) mediaPlayer.reset(); } public void initListeners() { senSensorManager.registerListener(this, senSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_FASTEST); senSensorManager.registerListener(this, senSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE), SensorManager.SENSOR_DELAY_FASTEST); senSensorManager.registerListener(this, senSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_FASTEST); } class calculateFusedOrientationTask extends TimerTask { public void run() { float oneMinusCoeff = 1.0f - FILTER_COEFFICIENT; /* * Fix for 179 <--> -179 transition problem: * Check whether one of the two orientation angles (gyro or accMag) is negative while the other one is positive. * If so, add 360 (2 * math.PI) to the negative value, perform the sensor fusion, and remove the 360 from the result * if it is greater than 180. This stabilizes the output in positive-to-negative-transition cases. */ // azimuth if (gyroOrientation[0] < -0.5 * Math.PI && accMagOrientation[0] > 0.0) { fusedOrientation[0] = (float) (FILTER_COEFFICIENT * (gyroOrientation[0] + 2.0 * Math.PI) + oneMinusCoeff * accMagOrientation[0]); fusedOrientation[0] -= (fusedOrientation[0] > Math.PI) ? 2.0 * Math.PI : 0; } else if (accMagOrientation[0] < -0.5 * Math.PI && gyroOrientation[0] > 0.0) { fusedOrientation[0] = (float) (FILTER_COEFFICIENT * gyroOrientation[0] + oneMinusCoeff * (accMagOrientation[0] + 2.0 * Math.PI)); fusedOrientation[0] -= (fusedOrientation[0] > Math.PI) ? 2.0 * Math.PI : 0; } else { fusedOrientation[0] = FILTER_COEFFICIENT * gyroOrientation[0] + oneMinusCoeff * accMagOrientation[0]; } // pitch if (gyroOrientation[1] < -0.5 * Math.PI && accMagOrientation[1] > 0.0) { fusedOrientation[1] = (float) (FILTER_COEFFICIENT * (gyroOrientation[1] + 2.0 * Math.PI) + oneMinusCoeff * accMagOrientation[1]); fusedOrientation[1] -= (fusedOrientation[1] > Math.PI) ? 2.0 * Math.PI : 0; } else if (accMagOrientation[1] < -0.5 * Math.PI && gyroOrientation[1] > 0.0) { fusedOrientation[1] = (float) (FILTER_COEFFICIENT * gyroOrientation[1] + oneMinusCoeff * (accMagOrientation[1] + 2.0 * Math.PI)); fusedOrientation[1] -= (fusedOrientation[1] > Math.PI) ? 2.0 * Math.PI : 0; } else { fusedOrientation[1] = FILTER_COEFFICIENT * gyroOrientation[1] + oneMinusCoeff * accMagOrientation[1]; } // roll if (gyroOrientation[2] < -0.5 * Math.PI && accMagOrientation[2] > 0.0) { fusedOrientation[2] = (float) (FILTER_COEFFICIENT * (gyroOrientation[2] + 2.0 * Math.PI) + oneMinusCoeff * accMagOrientation[2]); fusedOrientation[2] -= (fusedOrientation[2] > Math.PI) ? 2.0 * Math.PI : 0; } else if (accMagOrientation[2] < -0.5 * Math.PI && gyroOrientation[2] > 0.0) { fusedOrientation[2] = (float) (FILTER_COEFFICIENT * gyroOrientation[2] + oneMinusCoeff * (accMagOrientation[2] + 2.0 * Math.PI)); fusedOrientation[2] -= (fusedOrientation[2] > Math.PI) ? 2.0 * Math.PI : 0; } else { fusedOrientation[2] = FILTER_COEFFICIENT * gyroOrientation[2] + oneMinusCoeff * accMagOrientation[2]; } // overwrite gyro matrix and orientation with fused orientation // to comensate gyro drift gyroMatrix = getRotationMatrixFromOrientation(fusedOrientation); System.arraycopy(fusedOrientation, 0, gyroOrientation, 0, 3); // update sensor output in GUI //mHandler.post(updateOreintationDisplayTask); // -30 ... +30 if (view != null && view.getThread() != null) //if (Math.abs(x) > 0.2) view.getThread().moveSprite_x(fusedOrientation[2]); } } @Override public void onStop() { super.onStop(); // unregister sensor listeners to prevent the activity from draining the device's battery. senSensorManager.unregisterListener(this); } @Override protected void onPause() { super.onPause(); // unregister sensor listeners to prevent the activity from draining the device's battery. senSensorManager.unregisterListener(this); if (mediaPlayer != null) mediaPlayer.pause(); } @Override public void onResume() { super.onResume(); /* senAccelerometer = senSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); senSensorManager.registerListener(this, senAccelerometer, SensorManager.SENSOR_DELAY_GAME);*/ initListeners(); // wait for one second until gyroscope and magnetometer/accelerometer // data is initialised then scedule the complementary filter task fuseTimer.scheduleAtFixedRate(new calculateFusedOrientationTask(), 1000, TIME_CONSTANT); // if not playing if (!mediaPlayer.isPlaying()) { AssetFileDescriptor descriptor = null; try { descriptor = getAssets().openFd("wabe.mp3"); mediaPlayer.setDataSource(descriptor.getFileDescriptor(), descriptor.getStartOffset(), descriptor.getLength()); descriptor.close(); mediaPlayer.prepare(); mediaPlayer.setLooping(true); } catch (IOException e) { e.printStackTrace(); } } // Start music ! mediaPlayer.start(); } @Override public void onSensorChanged(SensorEvent event) { /* Sensor mySensor = event.sensor; if (mySensor.getType() == Sensor.TYPE_GYROSCOPE) { float x = event.values[0]; float y = event.values[1]; float z = event.values[2]; if (view != null && view.getThread() != null) if (Math.abs(x) > 0.2) view.getThread().moveSprite_x(-x); //mView.setBall_y(screen_y); }*/ switch (event.sensor.getType()) { case Sensor.TYPE_ACCELEROMETER: // copy new accelerometer data into accel array // then calculate new orientation System.arraycopy(event.values, 0, accel, 0, 3); calculateAccMagOrientation(); break; case Sensor.TYPE_GYROSCOPE: // process gyro data gyroFunction(event); break; case Sensor.TYPE_MAGNETIC_FIELD: // copy new magnetometer data into magnet array System.arraycopy(event.values, 0, magnet, 0, 3); break; } } public void calculateAccMagOrientation() { if (SensorManager.getRotationMatrix(rotationMatrix, null, accel, magnet)) { SensorManager.getOrientation(rotationMatrix, accMagOrientation); } } private static final float NS2S = 1.0f / 1000000000.0f; private float timestamp; private boolean initState = true; private float[] matrixMultiplication(float[] A, float[] B) { float[] result = new float[9]; result[0] = A[0] * B[0] + A[1] * B[3] + A[2] * B[6]; result[1] = A[0] * B[1] + A[1] * B[4] + A[2] * B[7]; result[2] = A[0] * B[2] + A[1] * B[5] + A[2] * B[8]; result[3] = A[3] * B[0] + A[4] * B[3] + A[5] * B[6]; result[4] = A[3] * B[1] + A[4] * B[4] + A[5] * B[7]; result[5] = A[3] * B[2] + A[4] * B[5] + A[5] * B[8]; result[6] = A[6] * B[0] + A[7] * B[3] + A[8] * B[6]; result[7] = A[6] * B[1] + A[7] * B[4] + A[8] * B[7]; result[8] = A[6] * B[2] + A[7] * B[5] + A[8] * B[8]; return result; } private float[] getRotationMatrixFromOrientation(float[] o) { float[] xM = new float[9]; float[] yM = new float[9]; float[] zM = new float[9]; float sinX = (float) Math.sin(o[1]); float cosX = (float) Math.cos(o[1]); float sinY = (float) Math.sin(o[2]); float cosY = (float) Math.cos(o[2]); float sinZ = (float) Math.sin(o[0]); float cosZ = (float) Math.cos(o[0]); // rotation about x-axis (pitch) xM[0] = 1.0f; xM[1] = 0.0f; xM[2] = 0.0f; xM[3] = 0.0f; xM[4] = cosX; xM[5] = sinX; xM[6] = 0.0f; xM[7] = -sinX; xM[8] = cosX; // rotation about y-axis (roll) yM[0] = cosY; yM[1] = 0.0f; yM[2] = sinY; yM[3] = 0.0f; yM[4] = 1.0f; yM[5] = 0.0f; yM[6] = -sinY; yM[7] = 0.0f; yM[8] = cosY; // rotation about z-axis (azimuth) zM[0] = cosZ; zM[1] = sinZ; zM[2] = 0.0f; zM[3] = -sinZ; zM[4] = cosZ; zM[5] = 0.0f; zM[6] = 0.0f; zM[7] = 0.0f; zM[8] = 1.0f; // rotation order is y, x, z (roll, pitch, azimuth) float[] resultMatrix = matrixMultiplication(xM, yM); resultMatrix = matrixMultiplication(zM, resultMatrix); return resultMatrix; } public void gyroFunction(SensorEvent event) { // don't start until first accelerometer/magnetometer orientation has been acquired if (accMagOrientation == null) return; // initialisation of the gyroscope based rotation matrix if (initState) { float[] initMatrix = new float[9]; initMatrix = getRotationMatrixFromOrientation(accMagOrientation); float[] test = new float[3]; SensorManager.getOrientation(initMatrix, test); gyroMatrix = matrixMultiplication(gyroMatrix, initMatrix); initState = false; } // copy the new gyro values into the gyro array // convert the raw gyro data into a rotation vector float[] deltaVector = new float[4]; if (timestamp != 0) { final float dT = (event.timestamp - timestamp) * NS2S; System.arraycopy(event.values, 0, gyro, 0, 3); getRotationVectorFromGyro(gyro, deltaVector, dT / 2.0f); } // measurement done, save current time for next interval timestamp = event.timestamp; // convert rotation vector into rotation matrix float[] deltaMatrix = new float[9]; SensorManager.getRotationMatrixFromVector(deltaMatrix, deltaVector); // apply the new rotation interval on the gyroscope based rotation matrix gyroMatrix = matrixMultiplication(gyroMatrix, deltaMatrix); // get the gyroscope based orientation from the rotation matrix SensorManager.getOrientation(gyroMatrix, gyroOrientation); } public static final float EPSILON = 0.000000001f; private void getRotationVectorFromGyro(float[] gyroValues, float[] deltaRotationVector, float timeFactor) { float[] normValues = new float[3]; // Calculate the angular speed of the sample float omegaMagnitude = (float) Math.sqrt( gyroValues[0] * gyroValues[0] + gyroValues[1] * gyroValues[1] + gyroValues[2] * gyroValues[2]); // Normalize the rotation vector if it's big enough to get the axis if (omegaMagnitude > EPSILON) { normValues[0] = gyroValues[0] / omegaMagnitude; normValues[1] = gyroValues[1] / omegaMagnitude; normValues[2] = gyroValues[2] / omegaMagnitude; } // Integrate around this axis with the angular speed by the timestep // in order to get a delta rotation from this sample over the timestep // We will convert this axis-angle representation of the delta rotation // into a quaternion before turning it into the rotation matrix. float thetaOverTwo = omegaMagnitude * timeFactor; float sinThetaOverTwo = (float) Math.sin(thetaOverTwo); float cosThetaOverTwo = (float) Math.cos(thetaOverTwo); deltaRotationVector[0] = sinThetaOverTwo * normValues[0]; deltaRotationVector[1] = sinThetaOverTwo * normValues[1]; deltaRotationVector[2] = sinThetaOverTwo * normValues[2]; deltaRotationVector[3] = cosThetaOverTwo; } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } /** View for game **/ public class GPGameSurfaceView extends SurfaceView implements SurfaceHolder.Callback { // Sizes private static final double RATIO_MAIN_PLANE = 0.3; private static final double RATIO_BOMB = 0.1201; private static final double RATIO_LIFE = 0.1201; private static final double RATIO_MISSILE = 0.035; private static final double RATIO_PATROL = 0.15; private static final double RATIO_HEART = 0.090; private static final double RATIO_HEART_SPACE = 0.055; private static final double RATIO_BUTTON = 0.25788; private static final double RATIO_BUG = 0.5; private static final int COEF_MOVE_SENSOR = 70; private static final double COEF_ORIENTATION_SENSOR = 0.050; private static final int MAX_MISSILES = 4; private static final int MAX_BOMBS = 4; // Game & graphics private Context ctx; private SurfaceHolder sh; private GPGameThread thread; private boolean retry; private int screen_w = 1000, screen_h = 1000, marginL, marginR, marginB, marginT1, marginT2, marginH1, marginH2, marginB1, marginB2, marginBB1, marginBB2; private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Paint backgrPaint = new Paint(); private final Paint textPaint = new Paint(); private final Paint textHeader = new Paint(); private final Paint scorePaint = new Paint(); private final Paint bluePaint = new Paint(); private Random rand = new Random(); private Bitmap mainPlaneLeft, mainPlaneRight, background, bomb, missile, missile_red, heart, life, bpRestart, bpQuit, bug, patrol; private MainPlane mainPlane; private BombSprite[] bombSprites = new BombSprite[MAX_BOMBS]; private MissileSprite[] missileSprites = new MissileSprite[MAX_MISSILES]; private LifeSprite lifeSprite = new LifeSprite(); private Vibrator v; private Typeface fontScore, fontEnd; public GPGameSurfaceView(Context ctx) { super(ctx); this.ctx = ctx; sh = getHolder(); sh.addCallback(this); setFocusable(true); v = (Vibrator) ctx.getSystemService(Context.VIBRATOR_SERVICE); fontScore = Typeface.createFromAsset(getAssets(), "fonts/ka1.ttf"); fontEnd = Typeface.createFromAsset(getAssets(), "fonts/PerfectDOSVGA437.ttf"); paint.setColor(Color.BLUE); paint.setStyle(Paint.Style.FILL); backgrPaint.setColor(0xdf151728); backgrPaint.setStyle(Paint.Style.FILL); textPaint.setColor(Color.WHITE); textPaint.setTextSize(28); scorePaint.setColor(Color.WHITE); textHeader.setColor(Color.WHITE); textHeader.setTypeface(fontEnd); scorePaint.setTypeface(fontScore); textPaint.setTypeface(fontEnd); bluePaint.setStyle(Paint.Style.FILL); bluePaint.setColor(0xff1b488c); } public GPGameThread getThread() { return this.thread; } @Override public void surfaceCreated(SurfaceHolder holder) { screen_w = getWidth(); screen_h = getHeight(); mainPlaneLeft = getResizedBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.trick_gp_left), (int) (screen_w * RATIO_MAIN_PLANE)); mainPlaneRight = getResizedBitmap( BitmapFactory.decodeResource(getResources(), R.drawable.trick_gp_right), (int) (screen_w * RATIO_MAIN_PLANE)); background = getResizedBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.trick_sky_better), screen_h); bomb = getResizedBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.trick_bomb), (int) (screen_w * RATIO_BOMB)); missile = getResizedBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.trick_missile), (int) (screen_w * RATIO_MISSILE)); heart = getResizedBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.trick_heart), (int) (screen_w * RATIO_HEART)); life = getResizedBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.trick_matlab), (int) (screen_w * RATIO_LIFE)); bpRestart = getResizedBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.btn_retry), (int) (screen_w * RATIO_BUTTON)); bpQuit = getResizedBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.btn_quit), (int) (screen_w * RATIO_BUTTON)); bug = getResizedBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.trick_bug), (int) (screen_w * RATIO_BUG)); patrol = getResizedBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.trick_patrol), (int) (screen_w * RATIO_PATROL)); mainPlane = new MainPlane(); mainPlane.x = screen_w / 2; mainPlane.w = mainPlaneLeft.getWidth(); mainPlane.h = mainPlaneLeft.getHeight(); marginL = mainPlane.w / 2; marginR = screen_w - marginL; marginB = screen_h - mainPlane.h / 2 - mainPlane.h / 3; mainPlane.y = marginB; marginT1 = (int) (screen_h * 0.09257); marginT2 = (int) (screen_h * 0.11995); marginH1 = (int) (screen_w * RATIO_HEART); marginH2 = (int) (screen_w * RATIO_HEART_SPACE); marginB1 = (int) (screen_w * 0.21335); marginB2 = (int) (screen_w * 0.53246); marginBB1 = (int) (0.70833 * screen_h); marginBB2 = (int) (0.70833 * screen_h); scorePaint.setTextSize((int) (0.073 * screen_h)); textHeader.setTextSize((int) (0.085 * screen_w)); textPaint.setTextSize((int) (0.065 * screen_w)); thread = new GPGameThread(sh, ctx, new Handler(), screen_w, screen_h); thread.doStart(); thread.setRunning(true); thread.start(); } @Override public boolean onTouchEvent(@NonNull MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (thread != null) { if (thread.getStatus() == GPGameThread.GAME_LOOSE) { float x = event.getX(); float y = event.getY(); if (x >= marginB1 && x < marginB1 + bpQuit.getWidth() && y >= marginBB1 && y <= marginBB1 + bpQuit.getHeight()) { thread.setRunning(false); senSensorManager.unregisterListener(GantierActivity.this); if (mediaPlayer != null) mediaPlayer.reset(); GantierActivity.this.finish(); } else if (x >= marginB2 && x < marginB2 + bpRestart.getWidth() && y >= marginBB2 && y <= marginBB2 + bpRestart.getHeight()) { thread.setStatus(GPGameThread.GAME_PLAY); thread.doStart(); } } else { thread.setGunRunning(true); } } break; case MotionEvent.ACTION_UP: if (thread != null) if (thread.getStatus() == GPGameThread.GAME_PLAY) thread.setGunRunning(false); break; } return true; } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { thread.setSurfaceSize(width, height); } @Override public void surfaceDestroyed(SurfaceHolder holder) { thread.setRunning(false); retry = true; while (retry) { try { thread.join(); retry = false; } catch (InterruptedException e) { e.printStackTrace(); } } } /** Thread for game operations **/ public class GPGameThread extends Thread { private boolean run = false; private int screen_w, screen_h; private Handler handler; private final SurfaceHolder sh; private Context ctx; private int backgrY; private long s1 = 0, s2, cnt = 0, latchCnt = 0; private boolean orientation = false; private boolean runGun = false; private int score = 0, lives = 3; public static final int GAME_PLAY = 0; public static final int GAME_LOOSE = 1; private int status = GAME_PLAY; private boolean stOnce = false; private JSONObject jsonScoresObj = null; private long lastLive = 0; private long delayBomb = 0; private boolean netError = false; private boolean srvError = false; public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public GPGameThread(SurfaceHolder sh, Context ctx, Handler handler, int screen_w, int screen_h) { this.screen_w = screen_w; this.screen_h = screen_h; this.sh = sh; this.handler = handler; this.ctx = ctx; } public void moveSprite_x(float dx) { if (status == GAME_PLAY) { mainPlane.x += dx * COEF_MOVE_SENSOR; if (mainPlane.x > marginR) mainPlane.x = marginR; if (mainPlane.x < marginL) mainPlane.x = marginL; if (dx > COEF_ORIENTATION_SENSOR) { orientation = true; } else if (dx < -COEF_ORIENTATION_SENSOR) { orientation = false; } } } public void setGunRunning(boolean runGun) { this.runGun = runGun; } public void setRunning(boolean run) { this.run = run; } // Init game public void doStart() { synchronized (sh) { backgrY = 0; for (int i = 0; i < MAX_BOMBS; i++) { bombSprites[i] = new BombSprite(); bombSprites[i].x = screen_w / 2; bombSprites[i].y = 0; bombSprites[i].wb = bomb.getWidth(); bombSprites[i].hb = bomb.getHeight(); bombSprites[i].hp = patrol.getWidth(); bombSprites[i].wp = patrol.getHeight(); bombSprites[i].step = (int) (screen_h / 67.37); bombSprites[i].stepAlien = (int) (screen_h / 202.11); } for (int i = 0; i < MAX_MISSILES; i++) { missileSprites[i] = new MissileSprite(); missileSprites[i].x = screen_w / 2; missileSprites[i].y = marginB; missileSprites[i].destroyed = false; missileSprites[i].visible = false; missileSprites[i].w = missile.getWidth(); missileSprites[i].h = missile.getHeight(); missileSprites[i].step = (int) (screen_h / 58); } lifeSprite.visible = false; lifeSprite.y = 0; lifeSprite.w = life.getWidth(); lifeSprite.h = life.getHeight(); lifeSprite.step = (int) (screen_h / 141); stOnce = false; lives = 3; score = 0; jsonScoresObj = null; netError = false; srvError = false; delayBomb = System.currentTimeMillis(); } } // Play functions public void doDraw(Canvas canvas) { canvas.restore(); // Draw background //canvas.drawPaint(backgrPaint); //canvas.drawPaint(bluePaint); canvas.drawBitmap(background, 0, -screen_h + backgrY, null); canvas.drawBitmap(background, 0, backgrY, null); // Draw life if (lifeSprite.visible) { canvas.drawBitmap(life, lifeSprite.x - lifeSprite.w / 2, lifeSprite.y - lifeSprite.h / 2, null); } // Draw bombs for (int i = 0; i < MAX_BOMBS; i++) { if (bombSprites[i].visible) { if (bombSprites[i].isAlien()) canvas.drawBitmap(patrol, bombSprites[i].x - bombSprites[i].getWidth() / 2, bombSprites[i].y - bombSprites[i].getHeight() / 2, null); else canvas.drawBitmap(bomb, bombSprites[i].x - bombSprites[i].getWidth() / 2, bombSprites[i].y - bombSprites[i].getHeight() / 2, null); // Debug //canvas.drawText("Missile" + i, missileSprites[i].x+100, missileSprites[i].y, textPaint); } } // Draw missiles for (int i = 0; i < MAX_MISSILES; i++) { if (missileSprites[i].visible) { canvas.drawBitmap(missile, missileSprites[i].x - missileSprites[i].w / 2, missileSprites[i].y - missileSprites[i].h / 2, null); } } // Draw plane if (orientation) canvas.drawBitmap(mainPlaneRight, mainPlane.x - mainPlane.w / 2, mainPlane.y - mainPlane.h / 2, null); else canvas.drawBitmap(mainPlaneLeft, mainPlane.x - mainPlane.w / 2, mainPlane.y - mainPlane.h / 2, null); // Draw score canvas.drawText(String.format("%05d", score), marginH2, marginT1, scorePaint); // Draw lifes for (int i = 0; i < lives; i++) { canvas.drawBitmap(heart, marginH2 + i * marginH1, marginT2, null); } if (status == GAME_LOOSE) { canvas.drawPaint(backgrPaint); canvas.drawText("**** SCORES ****", (int) (screen_w * 0.1187), (int) (screen_h * 0.13), textHeader); canvas.drawBitmap(bpQuit, marginB1, (int) (0.70833 * screen_h), null); canvas.drawBitmap(bpRestart, marginB2, (int) (0.70833 * screen_h), null); // do this once : if (!stOnce) { SyncScores syncScores = new SyncScores(score); syncScores.execute(); stOnce = true; } // Now draw scores if != null if (jsonScoresObj != null) { try { int rank = jsonScoresObj.getInt("rank"); int bscore = jsonScoresObj.getInt("bscore"); JSONArray array = jsonScoresObj.getJSONArray("best"); Paint paintHigh = new Paint(textPaint); paintHigh.setColor(0xc0e5e5e5); double dy = screen_h * 0.0375; boolean inRank = false; for (int i = 0; i < 10; i++) { if (i < array.length()) { JSONObject o = array.getJSONObject(i); int scoreUser = o.getInt("score"); String user = o.getString("login"); if (user.equals(profile.getId())) { inRank = true; canvas.drawText( String.format("%02d", i + 1) + " " + String.format("%05d", scoreUser) + " " + user, (int) (screen_w * 0.124), (int) (screen_h * 0.224 + dy * i), textPaint); } else canvas.drawText( String.format("%02d", i + 1) + " " + String.format("%05d", scoreUser) + " " + user, (int) (screen_w * 0.124), (int) (screen_h * 0.224 + dy * i), paintHigh); } else { canvas.drawText(String.format("%02d", i + 1) + " ----- ---------", (int) (screen_w * 0.124), (int) (screen_h * 0.224 + dy * i), paintHigh); } } if (!inRank) canvas.drawText( String.format("%02d", rank) + " " + String.format("%05d", bscore) + " " + profile.getId(), (int) (screen_w * 0.124), (int) (screen_h * 0.224 + dy * 11), textPaint); } catch (JSONException e) { e.printStackTrace(); } } else if (netError) { canvas.drawText(" -- Pas de reseau --", (int) (screen_w * 0.124), (int) (screen_h * 0.224), textPaint); canvas.drawBitmap(bug, screen_w / 2 - bug.getWidth() / 2, screen_h / 2 - bug.getHeight() / 2, null); } else if (srvError) { canvas.drawText(" -- Err. serveur --", (int) (screen_w * 0.124), (int) (screen_h * 0.224), textPaint); canvas.drawBitmap(bug, screen_w / 2 - bug.getWidth() / 2, screen_h / 2 - bug.getHeight() / 2, null); } } // Debug //canvas.drawText(latchCnt + " FPS", 10, screen_h-10, textPaint); //canvas.drawCircle(bubbleX, bubbleY, 50, paint); } public final static int MAX_FPS = 58; private final static int MAX_FRAME_SKIPS = 0; private final static int FRAME_PERIOD = 1000 / MAX_FPS; public void run() { long beginTime; // the time when the cycle begun long timeDiff; // the time it took for the cycle to execute int sleepTime; // ms to sleep (<0 if we're behind) int framesSkipped; // number of frames being skipped sleepTime = 0; while (run) { Canvas c = null; try { c = sh.lockCanvas(null); synchronized (sh) { if (c != null) { beginTime = System.currentTimeMillis(); framesSkipped = 0; c.save(); if (status == GAME_PLAY) { updatePhysics(); } doDraw(c); cnt++; timeDiff = System.currentTimeMillis() - beginTime; sleepTime = (int) (FRAME_PERIOD - timeDiff); if (sleepTime > 0) { try { Thread.sleep(sleepTime); } catch (InterruptedException e) { } } while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) { if (status == GAME_PLAY || status == GAME_LOOSE) { updatePhysics(); } sleepTime += FRAME_PERIOD; framesSkipped++; } } } } finally { if (c != null) { sh.unlockCanvasAndPost(c); } } if (System.currentTimeMillis() - s1 > 1000) { latchCnt = cnt; s1 = System.currentTimeMillis(); cnt = 0; } } } public void setSurfaceSize(int width, int height) { synchronized (sh) { screen_w = width; screen_h = height; doStart(); } } public void updatePhysics() { // Collisions double dist; backgrY = (backgrY + 2); if (backgrY > screen_h) backgrY = 0; for (int i = 0; i < MAX_MISSILES; i++) { for (int j = 0; j < MAX_BOMBS; j++) { dist = Math.sqrt( (missileSprites[i].x - bombSprites[j].x) * (missileSprites[i].x - bombSprites[j].x) + (missileSprites[i].y - bombSprites[j].y) * (missileSprites[i].y - bombSprites[j].y)); if (bombSprites[j].visible && isThereCollision(bombSprites[j].x, bombSprites[j].y, bombSprites[j].getWidth(), bombSprites[j].getHeight())) { // Alien attack bombSprites[j].visible = false; lives--; if (lives == -1) { status = GAME_LOOSE; } v.vibrate(100); } if (missileSprites[i].visible && bombSprites[j].visible && dist < bombSprites[i].getWidth() / 1.8) { bombSprites[j].actionMissile(); if (bombSprites[j].isDead()) { score += bombSprites[j].addedLifes(); bombSprites[j].visible = false; bombSprites[j].y = 0; } missileSprites[i].destroyed = true; missileSprites[i].visible = false; //missileSprites[i].y = marginB; } } } if (lifeSprite.visible && isThereCollision(lifeSprite.x, lifeSprite.y, lifeSprite.w, lifeSprite.h)) { // extra life lifeSprite.visible = false; lifeSprite.y = 0; lives++; v.vibrate(200); } // Lives bonus : appears if not visible, random boolean agreed and more than 15+ delay and +15 score if (lifeSprite.visible) { // life bonus is visible : move it down lifeSprite.y += lifeSprite.step; if (lifeSprite.y > screen_h) { lifeSprite.visible = false; lifeSprite.y = 0; } } else if (!lifeSprite.visible && score > 15 && (System.currentTimeMillis() - lastLive) > 8000) { //if (!extendLive.visible && score > 5 && (System.currentTimeMillis()-lastLive) > 15000) { if (rand.nextBoolean()) { lifeSprite.visible = true; lastLive = System.currentTimeMillis(); lifeSprite.x = marginL + rand.nextInt(marginR); } lastLive = System.currentTimeMillis(); } // Missiles for (int i = 0; i < MAX_MISSILES; i++) { // DO something only if missile is concerned if (!missileSprites[i].visible && !missileSprites[i].destroyed) { // Makes missile out if not destroyed // If previous missile is far away // If i = 0, previous is 3 int prev; if (i == 0) prev = 3; else prev = i - 1; boolean isFirst = (!missileSprites[0].visible && !missileSprites[1].visible && !missileSprites[2].visible && !missileSprites[3].visible); // Make missile out only if destroy = false if ((isFirst || (marginB - missileSprites[prev].y) >= (marginB / MAX_MISSILES)) && runGun && !missileSprites[i].destroyed) { missileSprites[i].visible = true; // Set active missileSprites[i].destroyed = false; // Fully functional missileSprites[i].x = mainPlane.x; // Set at the bottom of screen, at the top of main sprite missileSprites[i].y = marginB; } } else if (missileSprites[i].visible || (!missileSprites[i].visible && missileSprites[i].destroyed)) { // Missile is visible missileSprites[i].y -= missileSprites[i].step; // Missile is out of screen if (missileSprites[i].y < 0) { missileSprites[i].visible = false; missileSprites[i].destroyed = false; missileSprites[i].y = marginB; } } } // Aliens for (int i = 0; i < MAX_BOMBS; i++) { // DO something only if alien is concerned if (!bombSprites[i].visible) { // Alien is invisible // If previous alien is far away // If i = 0, previous is 2 int prev; if (i == 0) prev = 2; else prev = i - 1; boolean isFirst = (!bombSprites[0].visible && !bombSprites[1].visible && !bombSprites[2].visible); boolean timeoutOk = (System.currentTimeMillis() - delayBomb) > 2000; if (timeoutOk && (isFirst || (bombSprites[prev].y) > marginB / 4)) { // If score > 40 et random ~ score 1000% : new kind of alien if (score > 40 && rand.nextInt(1000) < score) { bombSprites[i].setIsAlien(true); } else { bombSprites[i].setIsAlien(false); } bombSprites[i].visible = true; // Set active bombSprites[i].x = marginL + rand.nextInt(marginR); // Set at the bottom of screen, at the top of main sprite bombSprites[i].y = 0; } } else { // Alien is visible //if (alien[i].isPatrol) //alien[i].my += PATROL_STEP_PX; //else bombSprites[i].y += bombSprites[i].getStep(); // Alien is out of screen if (bombSprites[i].y > screen_h) { bombSprites[i].visible = false; bombSprites[i].y = 0; } } } } public boolean isThereCollision(int objX, int objY, int objW, int objH) { boolean pl1 = (Math.abs(objX - mainPlane.x) < mainPlane.w / 7.3 + objW / 2) && (Math.abs(objY - mainPlane.y) < (mainPlane.h / 2.1 + objH / 2.1)); boolean pl2 = (Math.abs(objX - mainPlane.x) < mainPlane.w / 2 + objW / 2) && (Math.abs(objY - mainPlane.y) < (mainPlane.h / 3.8 + objH / 2.1)); return pl1 || pl2; } /** * Async task to synchronize sores */ private class SyncScores extends AsyncTask<String, String, String> { private int score; public SyncScores(int score) { this.score = score; } @Override protected String doInBackground(String... params) { HashMap<String, String> pairs = new HashMap<>(); pairs.put(GantierActivity.this.getResources().getString(R.string.client), profile.getId()); pairs.put(GantierActivity.this.getResources().getString(R.string.password), profile.getPassword()); pairs.put(GantierActivity.this.getResources().getString(R.string.score), String.valueOf(score)); pairs.put(GantierActivity.this.getResources().getString(R.string.hash), EncryptUtils.sha256( GantierActivity.this.getResources().getString(R.string.HEADER_SYNC_SCORES) + profile.getId() + score + profile.getPassword())); return ConnexionUtils.postServerData(Constants.URL_API_GANTIER_SCORES, pairs, GantierActivity.this); } @Override protected void onPostExecute(String data) { int retCode = -1; String err = "inconnue"; if (Utilities.isNetworkDataValid(data)) { try { JSONObject obj = new JSONObject(data); retCode = obj.getInt("status"); err = obj.getString("cause"); if (retCode == 1) { jsonScoresObj = obj.getJSONObject("data"); } } catch (JSONException e) { e.printStackTrace(); retCode = -8; } } if (retCode != 1) { if (retCode == -8) { // server error srvError = true; netError = false; } else { netError = true; srvError = false; } } } } } } /** Game JAVA dedicated Classes **/ private class MainPlane { public int x, y, w, h; } private class BombSprite { public int x, y, wb, hb, wp, hp; public boolean visible; public boolean isAlien = false; private int step, stepAlien; private int bombLife = 1; public void actionMissile() { bombLife--; } public boolean isDead() { return bombLife == 0; } public int addedLifes() { return isAlien ? 4 : 1; } public void setSteps(int step, int stepAlien) { this.step = step; this.stepAlien = stepAlien; } public boolean isAlien() { return isAlien; } public void setIsAlien(boolean isAlien) { this.isAlien = isAlien; if (isAlien) bombLife = 4; else bombLife = 1; } public int getStep() { return isAlien ? stepAlien : step; } public int getWidth() { return isAlien ? wp : wb; } public int getHeight() { return isAlien ? hp : hb; } } private class LifeSprite { public int x, y, w, h; public boolean visible; public int step = 12; } private class MissileSprite { public int x, y, w, h; public boolean visible; public boolean destroyed; public int step = 22; } // width scaling public Bitmap getResizedBitmap(Bitmap bm, int newSize) { int width = bm.getWidth(); int height = bm.getHeight(); float scale = ((float) newSize) / width; // CREATE A MATRIX FOR THE MANIPULATION Matrix matrix = new Matrix(); // RESIZE THE BIT MAP matrix.postScale(scale, scale); // "RECREATE" THE NEW BITMAP return Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false); } public Bitmap getScaledResizedBitmap(Bitmap bm, int newSize) { int width = bm.getWidth(); int height = bm.getHeight(); float scale; if (height > width) { // portrait bitmap scale = ((float) newSize) / height; } else { // landscape bitmap scale = ((float) newSize) / width; } // CREATE A MATRIX FOR THE MANIPULATION Matrix matrix = new Matrix(); // RESIZE THE BIT MAP matrix.postScale(scale, scale); // "RECREATE" THE NEW BITMAP return Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false); } }