title: Bitmap explosion animation spinning in one place, not moving outward
body: <p>I have a short game using a SurfaceView. My "ship" is just a white rectangle, and the obstacles are bitmaps of asteroids. When an asteroid collides with the ship, an explosion animation is supposed to take place where 20 pieces of debris (just a 10 x 10 white dot) fly out in all directions. </p>
  
  <p>The problem is that when the collision occurs, all the debris just sits in one place spinning in a circle, as shown here: </p>
  
  <p><a href="https://i.stack.imgur.com/augqc.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/augqc.png" alt="screenshot of explosion"></a></p>
  
  <p>The explosion does indeed start at the exact position of my ship. The particles  just don't move outward. Have I placed the code in the wrong place? Does anyone know where I have gone wrong with this? </p>
  
  <p>Here is the class: </p>
  
  <pre><code>import android.content.Context;
  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.Point;
  import android.graphics.Rect;
  import android.util.DisplayMetrics;
  import android.util.Log;
  import android.view.MotionEvent;
  import android.view.SurfaceHolder;
  import android.view.SurfaceView;
  import java.util.Random;
  
  
  public class GameView extends SurfaceView implements Runnable, SurfaceHolder.Callback {
  
      private Thread gameViewThread = null;
      SurfaceHolder surfaceHolder;
      boolean okToRun;
  
      /*
      SHIP AND ASTEROID STUFF
       */
      private Ship ship;
      private Point shipPoint;
      private Rect textRect = new Rect();
      private float oldX;
      private float oldY;
      private AsteroidController asteroidController;
      /*
      END SHIP AND ASTEROID STUFF
       */
  
      /*
      GAME OVER STUFF
       */
      private boolean isMoving = false;
      private boolean isGameOver = false;
      private long waitTime;
      /*
      END GAME OVER STUFF
       */
  
      /*
      EXPLOSION STUFF
       */
      Bitmap explosionBMP;
      private Matrix[] explosion = new Matrix[20];
      boolean isDestroyed = false;
      boolean explosionStarted = false;
      float[] explosionXPosition = new float[20];
      float[] explosionYPosition = new float[20];
      float explosionSpeed = 20.0f;
      float[] explosionRotation = new float[20];
      /*
      END EXPLOSION STUFF
       */
  
       /*
      SCREEN DIMENSIONS
       */
      DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
      int gameWidth = displayMetrics.widthPixels;
      int gameHeight = displayMetrics.heightPixels;
      /*
      END SCREEN DIMENSIONS
       */
  
      public GameView(Context context) {
          super(context);
  
          surfaceHolder = this.getHolder();
          okToRun = true;
  
          ship = new Ship(new Rect(100, 100, 200, 200), Color.WHITE);
          shipPoint = new Point(gameWidth / 2, 3 * gameHeight / 4);
          ship.update(shipPoint);
  
          asteroidController = new AsteroidController(context);
  
          //JUST SPAWNING ONE ASTEROID FOR NOW
          asteroidController.createAsteroid(1);
  
          //JUST A 10 x 10 WHITE DOT
          explosionBMP = BitmapFactory.decodeResource(getResources(), R.drawable.explosion_debris);
  
          //DON'T WANT A NULL POINTER RIGHT OFF THE BAT
          for(int i = 0; i &lt; explosion.length; i++) {
              explosion[i] = new Matrix();
          }
  
          setFocusable(true);
      }
  
      @Override
      public void run() {
          while(okToRun) {
              if(!surfaceHolder.getSurface().isValid()) {
                  continue;
              }
              Canvas gameCanvas = surfaceHolder.lockCanvas();
  
              this.update();
              this.draw(gameCanvas);
              surfaceHolder.unlockCanvasAndPost(gameCanvas);
          }
      }
  
      @Override
      public boolean onTouchEvent(MotionEvent event) {
  
          switch (event.getAction()) {
              case MotionEvent.ACTION_DOWN:
  
                  isMoving = true;
                  oldX = event.getX();
                  oldY = event.getY();
                  if (isGameOver &amp;&amp; System.currentTimeMillis() - waitTime &gt;= 3000) {
                      isGameOver = false;
                      resetGame();
                  }
                  break;
              case MotionEvent.ACTION_MOVE:
                  if (!isGameOver &amp;&amp; isMoving) {
  
                      /*
                      THIS CODE ALLOWS TOUCH EVENT TO BE ANYWHERE ON SCREEN WHILE
                      SHIP MOVES ACCORDINGLY
                       */
                      float newX = event.getX();
                      float newY = event.getY();
                      float deltaX = getOffsetCoords(oldX, newX, shipPoint.x);
                      float deltaY = getOffsetCoords(oldY, newY, shipPoint.y);
                      shipPoint.set((int) deltaX, (int) deltaY);
                      oldX = newX;
                      oldY = newY;
                  }
                  return false;
          }
          return true;
      }
  
      /*
      GET THE DIFFERENCE IN SHIP COORDS AND TOUCH COORDS
       */
      private float getOffsetCoords(float oldVal, float newVal, float current) {
          return current + (newVal - oldVal);
      }
  
  
  
  
      @Override
      public void draw(Canvas canvas) {
          super.draw(canvas);
  
          //RANDOMS FOR STARS
          Random random0 = new Random();
          Random random1 = new Random();
  
          //BACKGROUND
          canvas.drawColor(Color.BLACK);
          Bitmap single_pixel_star = BitmapFactory.decodeResource(getResources(), R.drawable.single_pixel_star);
          Bitmap three_pixel_star = BitmapFactory.decodeResource(getResources(), R.drawable.three_pixel_star);
  
          //Draw random stars on the canvas
          canvas.drawBitmap(single_pixel_star, random0.nextInt(canvas.getWidth() - single_pixel_star.getWidth()), random0.nextInt(canvas.getHeight() - three_pixel_star.getHeight()), null);
          canvas.drawBitmap(three_pixel_star, random1.nextInt(canvas.getWidth() - three_pixel_star.getWidth()), random1.nextInt(canvas.getHeight() - three_pixel_star.getHeight()), null);
  
          //DRAW ASTEROID(s)
          asteroidController.render(canvas);
          //DRAW SHIP
          ship.draw(canvas);
  
  
          if (isGameOver) {
  
              ship.removeShip();
  
              isDestroyed = true;
              explosionStarted = true;
  
              Paint gameOverPaint = new Paint();
              gameOverPaint.setTextSize(200);
              gameOverPaint.setColor(Color.RED);
              showGameOver(canvas, gameOverPaint, "YOU LOSE!");
  
              /*
              SHIP GOES KABOOM!
               */
              for(int i = 0; i &lt; explosion.length; i++) {
                  canvas.drawBitmap(explosionBMP, explosion[i], null);
              }
  
          }
      }
      public void update() {
          if (!isGameOver) {
              ship.update(shipPoint);
              asteroidController.update();
  
              if (asteroidController.isCollision(ship)) {
                  isGameOver = true;
                  waitTime = System.currentTimeMillis();
              }
          }
          Matrix[] localExplosionDebris = new Matrix[20];
          if(explosionStarted) {
              for(int i = 0; i &lt; explosionXPosition.length; i++) {
                  explosionXPosition[i] = getShipPointX();
                  explosionYPosition[i] = getShipPointY();
                  Random rand = new Random();
                  explosionRotation[i] = (float) rand.nextInt(360);
              }
              explosionStarted = false;
          }
          for(int i = 0; i &lt; localExplosionDebris.length; i++) {
              localExplosionDebris[i] = new Matrix();
          }
          if(isDestroyed) {
              for(int i = 0; i &lt; localExplosionDebris.length; i++) {
                  float debrisXSpeed = (float) Math.sin(explosionRotation[i]*(Math.PI/180)) * explosionSpeed - .2f;
                  float debrisYSpeed = (float) Math.cos(explosionRotation[i]*(Math.PI/180)) * explosionSpeed - .2f;
  
                  explosionXPosition[i] += debrisXSpeed;
                  explosionYPosition[i] -= debrisYSpeed;
  
                  localExplosionDebris[i].postRotate(0, explosionBMP.getWidth()/2, explosionBMP.getHeight()/2);
                  localExplosionDebris[i].postTranslate(explosionXPosition[i], explosionYPosition[i]);
  
                  explosion[i].set(localExplosionDebris[i]);
              }
          }
      }
  
      /*
      SHOW GAME OVER TEXT ON THE CENTER OF THE SCREEN UNTIL 3 SECONDS HAVE PASSED AND TOUCH EVENT OCCURS
       */
      private void showGameOver(Canvas canvas, Paint paint, String gameOverString) {
          paint.setTextAlign(Paint.Align.LEFT);
          canvas.getClipBounds(textRect);
          int canvasH = textRect.height();
          int canvasW = textRect.width();
          paint.getTextBounds(gameOverString, 0, gameOverString.length(), textRect);
          float textX = canvasW / 2f - textRect.width() / 2f - textRect.left;
          float textY = canvasH / 2f - textRect.height() / 2f - textRect.bottom;
          canvas.drawText(gameOverString, textX, textY, paint);
      }
  
      /*
      RESET SHIP TO ORIGINAL LOCATION AND COLOR
       */
      public void resetGame() {
          ship = new Ship(new Rect(100, 100, 200, 200), Color.WHITE);
          shipPoint = new Point(gameWidth / 2, 3 * gameHeight / 4);
  
          //TODO STOP BG MUSIC ONCE FILE IS ADDED
          //TODO RESET SCORE ONCE ADDED
      }
  
  
      public void pause() {
          okToRun = false;
          while(true) {
              try {
                  gameViewThread.join();
              } catch(InterruptedException e) {
                  Log.d("ERROR", e.getMessage());
              }
              break;
          }
          gameViewThread = null;
      }
  
      public void resume() {
          okToRun = true;
          gameViewThread = new Thread(this);
          gameViewThread.start();
  
      }
  
      /*
      SELF EXPLANATORY
       */
      public int getShipPointX() {
          return shipPoint.x;
      }
  
      public int getShipPointY() {
          return shipPoint.y;
      }
  
      @Override
      public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
  
      }
  
      @Override
      public void surfaceCreated(SurfaceHolder holder) {
  
      }
  
      @Override
      public void surfaceDestroyed(SurfaceHolder holder) {
  
      }
  }
  </code></pre>