Author Archive
Create a scrollable Map with Cells – Part II
by Martin on Mar.10, 2010, under how to, tutorial
The second part of this series will show you how you can scroll smoothly over the simple 2D Map which was created in the first part.
Note: I changed my coding style to fit the Java/Android coding style. Please be aware that variables like _mapSite are now mMapSize.
The performance issue we discovered in the first part was awful and no one will play a game which needs seconds to draw another frame. But why do we have this performance issue?
Do you remember how we draw the Map? We go trough the map in a loop and draw each cell. If our map has only a size of 10, everything is fine, but if we go to 100 and more, we draw a lot of cells and most of them are not on our display. And thats the mistake: We use resources and time to draw cells we don’t see.
To fix that, we should add a calculation to define the start and end of our loop.
Thats the current onDraw():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | @Override public void onDraw(Canvas canvas) { // help variables int x = 0; int y = 0; for (int i = 0; i < mMapSize; i++) { // get the row mRow = mMapCells.get(i); // calculate the correct y for the row y = i * mCellSize - mYOffset; // go through the row for (int j = 0; j < mMapSize; j++) { // calculate the x coordinate for the columns x = j * mCellSize - mXOffset; mRow.get(j).draw(canvas, paint, x, y); } } } |
We see that we always go through the full map.
Ok before we change the start and stop of the loop, we need to calculate these borders. Therefore we create a function to do that.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // loop borders private int mStartRow; private int mMaxRow; private int mStartCol; private int mMaxCol; // atomic offset for pixelwise scrolling private int mXScrollOffset = 0; private int mYScrollOffset = 0; private void calculateLoopBorders() { mStartRow = mYOffset / mCellSize; mMaxRow = (int) Math.min(mMapSize, mStartRow + Math.floor(getHeight() / mCellSize) + 2); mStartCol = mXOffset / mCellSize; mMaxCol = (int) Math.min(mMapSize, mStartCol + Math.floor(getWidth() / mCellSize) + 2); mXScrollOffset = (int) (((mXOffset / (float) mCellSize) - (mXOffset / mCellSize)) * mCellSize); mYScrollOffset = (int) (((mYOffset / (float) mCellSize) - (mYOffset / mCellSize)) * mCellSize); } |
On line 2 and 4 we simply calculate the start of both loops by dividing the offset by the cell size. The result is the number of cells we have above or left of the point of origin.
On line 3 and 5 we calculate how many rows/columns have space on the screen with a extra buffer of 2 to make sure that we always see a cell at the edge, even if we have only one pixel left.
Line 6 and 7 are the most important, because they calculate the atomic scroll offset we need to refine our scrolling. (Hint: If you remove that variable, the scrolling will be by cell, not by pixel)
The next step is, we need to call this calculation whenever we scroll the map. We only scroll the map while we have the ACTION_MOVE event, so we will call the function at the last line in our if/else construct:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @Override public boolean onTouchEvent(MotionEvent event) { // touch down if (event.getAction() == MotionEvent.ACTION_DOWN) { // snipped } else if (event.getAction() == MotionEvent.ACTION_MOVE) { // snipped // store the last position mXTouch = (int) event.getX(); mYTouch = (int) event.getY(); calculateLoopBorders(); } else if (event.getAction() == MotionEvent.ACTION_UP) { // snipped } return true; } |
The borders are by default 0 or at least the display size + 2. To be sure that getWidth() and getHeight() are returning the right sizes, we need to add the very first call to calculate the borders inside the onSurfaceCreated() method. If we don’t do that, the very first screen will be black and we have to make a touch to display the map.
1 2 3 4 5 6 7 8 9 | @Override public void surfaceCreated(SurfaceHolder holder) { if (!mMapThread.isAlive()) { mMapThread = new MapThread(this); } mMapThread._run = true; mMapThread.start(); calculateLoopBorders(); } |
So, after we have created the borders and the call for their calculation, we have to refactor the loop.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // help variables private int mX = 0; private int mY = 0; private int mI = 0; private int mJ = 0; @Override public void onDraw(Canvas canvas) { mI = 0; for (int i = mStartRow; i < mMaxRow; i++) { mJ = 0; // get the row mRow = mMapCells.get(i); // calculate the correct y for the row mY = mI * mCellSize - mYScrollOffset; // go through the row for (int j = mStartCol; j < mMaxCol; j++) { // calculate the x coordinate for the columns mX = mJ * mCellSize - mXScrollOffset; mRow.get(j).draw(canvas, mPaint, mX, mY); mJ++; } mI++; } } |
Now step by step:
The onDraw() method will be called as often as possible, so we should reuse objects and variables. To do that, we changed the helper variables to be member variables. We also added mI and mJ, which will help us to store the row/column index on the screen because the “display index” is not the index we can use while going through the map.
On line 10 we start the loop with the calculated start and end and on line 13 we get the complete row to go through, again with the calculated borders and draw the cells (on line 18).
The code comments should explain the rest.
Now you can change the mMapSize to 300 and you can scroll smoothly.
If you think, we have done everything right, take a look at the Garbage Collector in your LogCat.
On the Nexus One I can use a mMapSize of 500 but I am near the limit, the GC frees every 600ms at least 1.6MB.
Project source: ScrollMap – Part II
Android 2.1 SDK Update
by Martin on Jan.13, 2010, under news
Android 2.1 SDK is out. The API level changed to 7 but its mainly a minor update.
New stuff: the animated background you already know from Nexus One and a new SignalStrength class which provides information about the device’s current network signal.
Anything else are mainly new methods on already existing classes.
The update is available as a download or as an update in your SDK Manager.
Create a scrollable Map with Cells – Part I
by Martin on Jan.04, 2010, under how to, tutorial
This how to will show you how you can create a simple 2D Map with Cells to place stuff on it. Just like the old school SimCity.
The first thing you need is an Activity with a SurfaceView and a Thread to trigger the drawing. Who doesn’t know these fundamentals, please read my series on 2d graphics first.
Lets start with the smallest unit for our map: the Cell.
Each Cell will have a background color and a unique ID.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | package com.droidnova.android.games; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; /** * A part of the map. */ public class Cell { public int _id = 0; public int _backgroundColor = Color.GREEN; /** * Konstruktor. * @param id */ public Cell(int id) { _id = id; } /** * Draw the cell * * @param canvas Canvas to draw on. * @param paint Color of the "pencil". * @param x X coordinate. * @param y Y coordinate. */ public void draw(Canvas canvas, Paint paint, int x, int y) { paint.setColor(_backgroundColor); canvas.drawRect(x, y, x + CellMap._cellSize, y + CellMap._cellSize, paint); paint.setColor(Color.BLACK); canvas.drawText("" + _id, x + 1, y + 10, paint); } } |
On line 32 you see, how we draw the cell. The variable _cellSize is a static variable from CellMap, which will be introduced later. Everything else should be already known.
(continue reading…)
Layout technique: Center a TextView with a Button left and right
by Martin on Oct.29, 2009, under layout technique
While working on an application which should look similar to an iPhone application, I have to create a bar at the top where I have a back and a forward button (each on one side) and a dynamic text centered in the middle between them.
To get this done, I searched quite a lot and got it finally to work.
The surrounding layout should be the RelativeLayout with two Buttons (in my case I used ImageViews) and a TextView.
(continue reading…)
Ubuntu 9.10 with Eclipse and ADT – Problems
by Martin on Oct.26, 2009, under how to
Sometimes I am a bit over motivated. Thats the reason I upgraded my Ubuntu to 9.10 RC1 yesterday.
Nearly everything worked fine and after 90 minutes downloading and installing Ubuntu 9.10 booted the first time.
The disillusion followed today: What happened with my Eclipse installation?
The LogCat view has no logging type buttons, the Devices view has lost its button for stopping processes and some windows don’t accept a mouse click on their buttons (mostly the OK button). I have to press Enter to get them work.
After a short search I found this bug report for ubuntu: https://bugs.launchpad.net/ubuntu/+source/gtk+2.0/+bug/442078
A workaround is also mentioned there:
Create a launch script for eclipse with the following code:
#!/bin/sh GDK_NATIVE_WINDOWS=1 /path/to/eclipse
I am not sure if Ubuntu is responsible or Eclipse, but after using this launch script, everything worked fine.
How to: Create a splash screen
by Martin on Oct.14, 2009, under how to
Many Applications, mostly games, on the market show splash screens. With this screen they prompt a logo for the application and/or the author.
I will show you a short way to implement a splash screen which will occur on every startup, will stay for a number of seconds you can define, will close on touching the screen and will not reappear on pressing the back button.
I created an empty project named SplashScreen with the activity SplashScreen. This activity will display the splash screen, so we have to create a new activity which will be the first real view you want to display. In my case this activity is named MyApp.
(continue reading…)
Android SDK 1.6 (Donut) Sources
by Martin on Sep.27, 2009, under tutorial
Every new Android SDK Version means that someone has to collect the sources and bundle them. Thanks to the supporter of the missing source jar issue a new source jar is provided. Also please star the issue to flag that issue as important!
Install instructions:
To use it in Eclipse, create a directory sources/ inside your
/platforms/android-1.6/ directory, and unzip the archive there. Start
Eclipse and check that you can see the source, for example ask for the definition of
the Gesture class (new in 1.6), you should see the source code, not the decompiled
byte code.
We will mirror the sources to, so feel free to download and use it!
Android SDK 1.6 sources
Droidnova move finished
by Martin on Sep.22, 2009, under news
The blog moved successfully.
If you recognize misbehavior, missing files or images, please let me know!
Greetings
Droidnova will be moved to another server
by Martin on Sep.21, 2009, under news
Hello,
droidnova will be moved to a new and faster server tonight (European time).
I will inform you if everything is done. I hope you will enjoy the improved performance on the new server.
It is possible, that every new comment done in the next 24 hours may not survive the move. I will do my very best to prevent that.
I keep you informed!
Greetings
Android 3D game tutorial – Part VI
by Martin on Sep.02, 2009, under tutorial
Updated to be Android 2.0.1 compatible.
You are new to this series? Please start with the first part.
The sixth part of this series will show you how you create the correct perspective because 3D is nothing without the correct perspective.
Before we start we should discuss the two possible “views” OpenGL offers: orthographic and perspective.
(continue reading…)