Document directory
Before starting the actual game loop, let's first display some pictures so that we can get some dimensional concepts (not quite clear about what this sentence means ). If you haven't seen the content on the thread update screen, we strongly recommend you read it first (previous blog)
Displaying images on Android is very simple.
To make the problem simple, we need a picture to display the image in the upper left corner. I have created a file named droid_1.png with a size of 20*20 pixels. You can choose your preferred tool. I use gimp or PS
To make the program usable, copy the image to the/RES/drawable-mdpi directory. I select mdpi, which means the average screen density. For details about the screen type, see the android documentation.
Modify the maingamepanel file and the ondraw function.
1 protected void onDraw(Canvas canvas) { 2 canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.droid_1), 10, 10, null); 3 }
Drawbitmap draws droid_1 at 10 or 10 coordinates.
We pass the image ID to the resource management part of the program to obtain bitmap. When we copy PNG to the Resource Directory, the plug-in will automatically generate an identifier in R. java.
It also affects part of the code of the thread and checks the following run function.
01 public void run() { 02 Canvas canvas; 03 Log.d(TAG, "Starting game loop"); 04 while (running) { 05 canvas = null; 06 // try locking the canvas for exclusive pixel editing on the surface 07 try { 08 canvas = this.surfaceHolder.lockCanvas(); 09 synchronized (surfaceHolder) { 10 // update game state 11 // draws the canvas on the panel 12 this.gamePanel.onDraw(canvas); 13 } 14 } finally { 15 // in case of an exception the surface is not left in 16 // an inconsistent state 17 if (canvas != null) { 18 surfaceHolder.unlockCanvasAndPost(canvas); 19 } 20 } // end finally 21 } 22 }
In the second line, we declare that we want to draw the canvas of the image. The canvas is the place where the surface image is to be painted, and the place where the pixels are edited. In the eighth line, we get the canvas, in row 12, we triggered the ondraw function and passed the canvas. Note that this is a synchronization block and cannot be used by others.
This function is very simple and basic. Every time you execute it, the game loop gets the canvas and passes it to the game Panel to draw things. The game Panel draws the picture to the coordinates of 10 and 10. Back to FPS, if the number of images displayed per second is less than 20, we will be noticed that our challenge is to maintain a certain level or above, and soon we will see
Run the code and we can see that the droid is displayed in the upper left corner.
Droid in top left corner
Mobile graphics
We have already shown the image. How can we move it? Use our fingers. We will implement a drag-and-drop operation. To select an image, we only touch it. When our fingers are still on the screen, our coordinates of the image will be updated. When we touch the image, the image will be placed in the last touch position.
We need to create an object to save images and coordinates.
I created droid. Java and put it under the net. obviam. droidz. Model package.
01 package net.obviam.droidz.model; 02 03 import android.graphics.Bitmap; 04 05 public class Droid { 06 07 private Bitmap bitmap; // the actual bitmap 08 private int x; // the X coordinate 09 private int y; // the Y coordinate 10 11 public Droid(Bitmap bitmap, int x, int y) { 12 this.bitmap = bitmap; 13 this.x = x; 14 this.y = y; 15 } 16 17 public Bitmap getBitmap() { 18 return bitmap; 19 } 20 public void setBitmap(Bitmap bitmap) { 21 this.bitmap = bitmap; 22 } 23 public int getX() { 24 return x; 25 } 26 public void setX(int x) { 27 this.x = x; 28 } 29 public int getY() { 30 return y; 31 } 32 public void setY(int y) { 33 this.y = y; 34 } 35 }
This is a simple class with only a few attributes and a constructor.
Droid's X and Y coordinates, as well as the bitmap to be displayed
There is nothing special, but in order to run it, we need to add some States. To keep it simple, droid has two states: Touch and touch, the Touch means to press the droid on the screen and press the droid to make sure that the touch is true. Otherwise, the touch is false.
Let's take a look at the new droid class.
1 package net.obviam.droidz.model; 02 03 import android.graphics.Bitmap; 04 import android.graphics.Canvas; 05 import android.view.MotionEvent; 06 07 public class Droid { 08 09 private Bitmap bitmap; // the actual bitmap 10 private int x; // the X coordinate 11 private int y; // the Y coordinate 12 private boolean touched; // if droid is touched/picked up 13 14 public Droid(Bitmap bitmap, int x, int y) { 15 this.bitmap = bitmap; 16 this.x = x; 17 this.y = y; 18 } 19 20 public Bitmap getBitmap() { 21 return bitmap; 22 } 23 public void setBitmap(Bitmap bitmap) { 24 this.bitmap = bitmap; 25 } 26 public int getX() { 27 return x; 28 } 29 public void setX(int x) { 30 this.x = x; 31 } 32 public int getY() { 33 return y; 34 } 35 public void setY(int y) { 36 this.y = y; 37 } 38 39 public boolean isTouched() { 40 return touched; 41 } 42 43 public void setTouched(boolean touched) { 44 this.touched = touched; 45 } 46 47 public void draw(Canvas canvas) { 48 canvas.drawBitmap(bitmap, x - (bitmap.getWidth() / 2), y - (bitmap.getHeight() / 2), null); 49 } 50 51 public void handleActionDown(int eventX, int eventY) { 52 if (eventX >= (x - bitmap.getWidth() / 2) && (eventX <= (x + bitmap.getWidth()/2))) { 53 if (eventY >= (y - bitmap.getHeight() / 2) && (y <= (y + bitmap.getHeight() / 2))) { 54 // droid touched 55 setTouched(true); 56 } else { 57 setTouched(false); 58 } 59 } else { 60 setTouched(false); 61 } 62 63 } 64 }
We added touched to record the droid status.
Let's take a look at maingamepanel, but it has changed a lot.
001 package net.obviam.droidz; 002 003 import net.obviam.droidz.model.Droid; 004 import android.app.Activity; 005 import android.content.Context; 006 import android.graphics.BitmapFactory; 007 import android.graphics.Canvas; 008 import android.graphics.Color; 009 import android.util.Log; 010 import android.view.MotionEvent; 011 import android.view.SurfaceHolder; 012 import android.view.SurfaceView; 013 014 public class MainGamePanel extends SurfaceView implements 015 SurfaceHolder.Callback { 016 017 private static final String TAG = MainGamePanel.class.getSimpleName(); 018 019 private MainThread thread; 020 private Droid droid; 021 022 public MainGamePanel(Context context) { 023 super(context); 024 // adding the callback (this) to the surface holder to intercept events 025 getHolder().addCallback(this); 026 027 // create droid and load bitmap 028 droid = new Droid(BitmapFactory.decodeResource(getResources(), R.drawable.droid_1), 50, 50); 029 030 // create the game loop thread 031 thread = new MainThread(getHolder(), this); 032 033 // make the GamePanel focusable so it can handle events 034 setFocusable(true); 035 } 036 037 @Override 038 public void surfaceChanged(SurfaceHolder holder, int format, int width, 039 int height) { 040 } 041 042 @Override 043 public void surfaceCreated(SurfaceHolder holder) { 044 // at this point the surface is created and 045 // we can safely start the game loop 046 thread.setRunning(true); 047 thread.start(); 048 } 049 050 @Override 051 public void surfaceDestroyed(SurfaceHolder holder) { 052 Log.d(TAG, "Surface is being destroyed"); 053 // tell the thread to shut down and wait for it to finish 054 // this is a clean shutdown 055 boolean retry = true; 056 while (retry) { 057 try { 058 thread.join(); 059 retry = false; 060 } catch (InterruptedException e) { 061 // try again shutting down the thread 062 } 063 } 064 Log.d(TAG, "Thread was shut down cleanly"); 065 } 066 067 @Override 068 public boolean onTouchEvent(MotionEvent event) { 069 if (event.getAction() == MotionEvent.ACTION_DOWN) { 070 // delegating event handling to the droid 071 droid.handleActionDown((int)event.getX(), (int)event.getY()); 072 073 // check if in the lower part of the screen we exit 074 if (event.getY() > getHeight() - 50) { 075 thread.setRunning(false); 076 ((Activity)getContext()).finish(); 077 } else { 078 Log.d(TAG, "Coords: x=" + event.getX() + ",y=" + event.getY()); 079 } 080 } if (event.getAction() == MotionEvent.ACTION_MOVE) { 081 // the gestures 082 if (droid.isTouched()) { 083 // the droid was picked up and is being dragged 084 droid.setX((int)event.getX()); 085 droid.setY((int)event.getY()); 086 } 087 } if (event.getAction() == MotionEvent.ACTION_UP) { 088 // touch was released 089 if (droid.isTouched()) { 090 droid.setTouched(false); 091 } 092 } 093 return true; 094 } 095 096 @Override 097 protected void onDraw(Canvas canvas) { 098 // fills the canvas with black 099 canvas.drawColor(Color.BLACK); 100 droid.draw(canvas); 101 } 102 }
Line28CreatesdroidObject at the coordinates
50, 50.
It is declared as an attribute in line20.
InonTouchEvent(Method Line71) If the action is the touch of the screen (MotionEvent.ACTION_DOWN) We want to know if our finger landed on the droid. To do this is easy. We need to check if the event's coordinates
Are inside the droid's bitmap. In order not to clutteronTouchEvent we just delegate this to the droid object. Now you can go back to
Droid.javaClass and checkhandleActionDownMethod.
The droid object is created in lines 28, at coordinates 50, 50.
In the ontouchevent method, if you touch the screen action, we confirm whether it is above the droid to see whether the touch event coordinates are within the coordinate of the image. To prevent the ontouchevent from being messy, we can put this in the droid object (in fact, I think it is okay inside the droid, if the droid class is designed), and now we return to the droid. java, let's take a look at the handleactiondown method.
public void handleActionDown(int eventX, int eventY) {if (eventX >= (x - bitmap.getWidth() / 2) && (eventX <= (x + bitmap.getWidth()/2))) {if (eventY >= (y - bitmap.getHeight() / 2) && (y <= (y + bitmap.getHeight() / 2))) {// droid touchedsetTouched(true);} else {setTouched(false);}} else {setTouched(false);}}It is easy to set the touched status to true if it is inside the droid.
Return to the ontouched method and check motionevent. action_move. If the droid is touched, update its coordinates.
The ondraw function draws the droid to the surface.
That's it.