Temporary Sprites. Android Game Programming 8.
If you remember from the first tutorial in the series, when you press over a character, it disappears and a blood stain appears. After some milliseconds this blood disappears. In this tutorial we are going to develop this functionality using a new kind of sprites.
These new sprites are going to have no movement or speed. Their position is going to be defined at construction time and it is going to be the one where we press. There is no animation or movie. These sprites are in most senses simpler than the first we created. The only new complexity comes from the fact that they have to disappear after a while. Let's start with a new class called TempSprite.
package com.edu4java.android.killthemall;
import java.util.List;
import android.graphics.Bitmap;
import android.graphics.Canvas;
public class TempSprite {
private float x;
private float y;
private Bitmap bmp;
private int life = 15;
private List<TempSprite> temps;
public TempSprite(List<TempSprite>
temps, GameView gameView, float x,
float y, Bitmap bmp) {
this.x = Math.min(Math.max(x - bmp.getWidth() / 2, 0),
gameView.getWidth() - bmp.getWidth());
this.y = Math.min(Math.max(y - bmp.getHeight() / 2, 0),
gameView.getHeight() - bmp.getHeight());
this.bmp = bmp;
this.temps = temps;
}
public void onDraw(Canvas canvas) {
update();
canvas.drawBitmap(bmp, x, y, null);
}
private void update() {
if (--life < 1) {
temps.remove(this);
}
}
}
In order to make the sprite disappear we introduce a new field called life. It will contain the number of ticks the sprite is going to be alive. A tick is a mark for each time the update method is called in our game loop. The frame per seconds FPS is a mark for each onDraw method call in a second. In our game FPS and update ticks are synchronized but it hasn´t got to be like that.
Each time the update method is called we decrease the life value by one and when it gets under 1 we remove our sprite from the temporary sprite list temps. The temps list holds all temporal sprites and it is passed as a parameter by the view in the TempSprite constructor.
The constructor receives in addition as parameters; a reference to the view, the (x,y) position (where we press) and a bitmap. The (x,y) position is adjusted in the constructor taking in consideration various factors:
- The blood bitmap center has to be in the (x,y) coordinates. If it is not we are going to get the sensation that the blood is down and left from where we press. We get this with: x - bmp.getWidth() / 2
- (x,y) cannot be passed outside of the screen. If we do that we can get unexpected behaviors and errors. Then:
- x and y have to be greater than 0. Math.max(x - bmp.getWidth() / 2, 0)
- x has to be minor than gameView.getWidth() discounting the bitmap width and y has to be minor than gameView.getHeight() discounting the bitmap height. Math.min( ... , gameView.getWidth() - bmp.getWidth());
this.x = Math.min(Math.max(x - bmp.getWidth()
/ 2, 0), gameView.getWidth() - bmp.getWidth());
this.y = Math.min(Math.max(y - bmp.getHeight()
/ 2, 0), gameView.getHeight() - bmp.getHeight());
Then bitmap is printed entirely in the calculated (x,y) position in the onDraw method.
A few more changes are necessary. We have to copy the next image to the resource folder
and add the next code line as the last line in the view constructor
bmpBlood = BitmapFactory.decodeResource(getResources(), R.drawable.blood1);
Include the temps field
private
List<TempSprite> temps
= new ArrayList<TempSprite>();
Add the temps sprites drawing
to the onDraw method.
for (int i = temps.size() - 1; i
>= 0; i--) {
temps.get(i).onDraw(canvas);
}
We iterate backwards to avoid errors when the sprites
are removed. We write this code before the others sprites drawing to give the
sensation that the blood is in the background.
Lastly we add
temps.add(new TempSprite(temps, this, x, y, bmpBlood));
just after the sprite is
removed in the onTouchEventy method.
package com.edu4java.android.killthemall;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
public class GameView extends SurfaceView
{
private GameLoopThread gameLoopThread;
private List<Sprite> sprites = new ArrayList<Sprite>();
private List<TempSprite> temps = new ArrayList<TempSprite>();
private long lastClick;
private Bitmap bmpBlood;
public GameView(Context context) {
super(context);
gameLoopThread = new GameLoopThread(this);
getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
gameLoopThread.setRunning(false);
while (retry) {
try {
gameLoopThread.join();
retry = false;
}
catch (InterruptedException e) {}
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
createSprites();
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
}
});
bmpBlood = BitmapFactory.decodeResource(getResources(), R.drawable.blood1);
}
private void createSprites() {
sprites.add(createSprite(R.drawable.bad1));
sprites.add(createSprite(R.drawable.bad2));
sprites.add(createSprite(R.drawable.bad3));
sprites.add(createSprite(R.drawable.bad4));
sprites.add(createSprite(R.drawable.bad5));
sprites.add(createSprite(R.drawable.bad6));
sprites.add(createSprite(R.drawable.good1));
sprites.add(createSprite(R.drawable.good2));
sprites.add(createSprite(R.drawable.good3));
sprites.add(createSprite(R.drawable.good4));
sprites.add(createSprite(R.drawable.good5));
sprites.add(createSprite(R.drawable.good6));
}
private Sprite createSprite(int resouce) {
Bitmap bmp = BitmapFactory.decodeResource(getResources(), resouce);
return new Sprite(this, bmp);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
for (int i = temps.size() - 1; i
>= 0; i--) {
temps.get(i).onDraw(canvas);
}
for (Sprite sprite : sprites) {
sprite.onDraw(canvas);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (System.currentTimeMillis() - lastClick > 300) {
lastClick = System.currentTimeMillis();
float x = event.getX();
float y = event.getY();
synchronized (getHolder()) {
for (int i = sprites.size() - 1; i
>= 0; i--) {
Sprite
sprite = sprites.get(i);
if (sprite.isCollision(x, y)) {
sprites.remove(sprite);
temps.add(new TempSprite(temps, this, x, y, bmpBlood));
break;
}
}
}
}
return true;
}
}
<< Touch Events and Sprite Collision Detection | Index >> |