Fair warning tho: this post might be a bit long, and will require you to get used to new software, which is actually pretty cool and easy to use ^^.
Note: I'm adding a game map, not a World Map. In terms of Pokémon games, we're talking about those that change when you enter a building or switch zones, not the view you get when you use Fly or check the map, that'd be a World Map.
First of all, I'm going to be using maps generated with Tiled Map Editor, cause, imho, there's nothing better. You can download it here, and they provide a nice (if a little long) manual that you can read here.
I won't explain step by step how to use Tiled, but I'll show you how to make a sample map for your game with a sort of picture tutorial. First of all, you'll need a tileset, you can use this one.
- Choose a map size and base tile size
- Add a tileset
- Place tiles on the map layers (you can add as many layers as you want)
- Export your map as "Flare map files (*.txt)"
Few things to mention here... I'll be using 3 (for now) layers. 1st one is for terrain, this layer is going to be completely filled. 2nd layer is for objects, squares where our hero won't be able to step on (very important to add it now, since we'll need this for path-finding). And last but not least, the 3rd layer is for the roof part of objects, stuff that will display over other objects and even our hero. Here's the tut!
On our exported .txt we'll have something like this, that's the data we'll need.
So, back to Android Studio now, first thing we'll need to do, is add our tileset image to our /res/drawable directory. Now, it's time to make a new Java Class, name it GameMap and this will be, well, our game map class, duh! :P
GameMap.java
The code has comments for pretty much everything, we basically take 3 (more in the future, probably) arrays of ids that we generated with Tiled and draw them on 2 canvases (of the size of the whole map, usually bigger than the device screen), one to be drawn before our hero (and NPCs, in the future) which contains the ground elements and the objects where our hero won't be able to walk through (note, this isn't implemented yet), and the other with elements that might cover our hero, or objects. The main idea is that it looks exactly like it does on Tiled, with our hero character moving around it.
Now, let's see changes in other classes necessary for this to work.
GamePanel.java
Note: this is showing ONLY the changed/added variables/methods, not the whole code, update accordingly.
We added a GamePanel variable to the class and initialise it on our constructor (there are 3 int[] arrays, those are from our generated .txt from Tiled, as shown in the picture tutorial above, mines are probably different than yours, use the ones you get! They're, in order, terrain, object and roof).
The Hero initialising method now has one more int in it (with value 64), as it'll be required for the changes made to the class.
We call both GameMap's render methods on our render(Canvas canvas), the first one before drawing our hero, the other after, as previously discussed. There's a minor change to our update() method, just to move the character from left to right, got tired of watching it moving from top to bottom :).
Finally, some changes in our Hero class to complete this post.
Hero.java
Note: this is showing ONLY the changed/added variables/methods, not the whole code, update accordingly.
As we mentioned, the constructor takes a new int parameter, this is to change the size of the sprite rendered on screen, which will allow our game to do things like zoom in/out, in the future. The only other change is on the update() method, this is to move our hero from left to right.
That's it! Run it now and you'll see our hero moving left to right on the map you designed :P
Hope you guys enjoyed it! Share this post and follow me on social networks! :) I appreciate it!!!
Take care!!!
On our exported .txt we'll have something like this, that's the data we'll need.
So, back to Android Studio now, first thing we'll need to do, is add our tileset image to our /res/drawable directory. Now, it's time to make a new Java Class, name it GameMap and this will be, well, our game map class, duh! :P
GameMap.java
package ve.com.biocraft.biocraft; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; import android.util.Log; public class GameMap { private static final String TAG = GameMap.class.getSimpleName(); private Bitmap bitmap; // The image of our map's tiles private int[] terrain; // Array of terrain for background buffer private int[] object; // Array of objects for background buffer (hero can't move through this) private int[] roof; // Array of elements for the top buffer private int width; // Numbers of tiles in a row from bitmap private int square; // Screen square size private Bitmap buffer; // Buffer for background elements private Bitmap roofBuffer; // Buffer for top elements (covers bottom, hero, NPCs, etc) public GameMap(Bitmap bitmap, int[] terrain, int[] object, int[] roof, int width, int square) { this.bitmap = bitmap; this.terrain = terrain; this.object = object; this.roof = roof; this.width = width; this.square = square; fillBuffers(); Log.d(TAG, "GameMap: Map ready!"); } private void fillBuffers() { // We crate 2 image buffers to be drawn on each render // One for background elements (buffer) // Another for top elements (roofBuffer) buffer = Bitmap.createBitmap(width * square, (terrain.length / width) * square, Bitmap.Config.ARGB_8888); roofBuffer = Bitmap.createBitmap(width * square, (terrain.length / width) * square, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(buffer); Canvas roofCanvas = new Canvas(roofBuffer); // Draw each array on the corresponding buffer, useful if there's need to add more layers of elements draw(terrain, canvas); draw(object, canvas); draw(roof, roofCanvas); } private void draw(int[] ints, Canvas canvas) { // Draw an array of sprite ids into a canvas int spriteSize = (bitmap.getWidth() / 8); for (int i = 0; i < (ints.length / width); i++) { for (int j = 0; j < width; j++) { int sprite = ints[(i * width) + j] - 1; if (sprite >= 0) { Rect src = new Rect((sprite % 8) * spriteSize, (int) Math.floor(sprite / 8) * spriteSize, (sprite % 8) * spriteSize + spriteSize, (int) Math.floor(sprite / 8) * spriteSize + spriteSize); Rect dst = new Rect(j * square, i * square, (j + 1) * square, (i + 1) * square); canvas.drawBitmap(bitmap, src, dst, null); } } } } public void renderBottom(Canvas canvas) { canvas.drawBitmap(buffer, 0, 0, null); } public void renderTop(Canvas canvas) { canvas.drawBitmap(roofBuffer, 0, 0, null); } }
The code has comments for pretty much everything, we basically take 3 (more in the future, probably) arrays of ids that we generated with Tiled and draw them on 2 canvases (of the size of the whole map, usually bigger than the device screen), one to be drawn before our hero (and NPCs, in the future) which contains the ground elements and the objects where our hero won't be able to walk through (note, this isn't implemented yet), and the other with elements that might cover our hero, or objects. The main idea is that it looks exactly like it does on Tiled, with our hero character moving around it.
Now, let's see changes in other classes necessary for this to work.
GamePanel.java
public class GamePanel extends SurfaceView implements SurfaceHolder.Callback { private GameMap gameMap; public GamePanel(Context context) { super(context); // Adding callback (this) to surface holder to catch events getHolder().addCallback(this); // Create our hero and load it's bitmap hero = new Hero(BitmapFactory.decodeResource(getResources(), R.drawable.lyon), 0, 448, 2, 64); // Create our game map, load bitmap, fill buffers gameMap = new GameMap(BitmapFactory.decodeResource(getResources(), R.drawable.tileset), new int[] {2,24,32,2,2,32,2,24,32,2, 2,24,32,24,32,2,2,32,24,32, 1,1,1,1,1,2,32,2,32,2, 8,8,16,8,16,16,1,2,2,32, 8,16,16,8,8,16,8,16,8,8, 31,31,31,31,31,8,8,16,16,8, 5,6,6,7,31,31,31,31,31,31, 13,14,14,15,31,31,31,34,35,35, 13,14,14,15,31,31,31,44,29,30, 21,22,22,23,31,31,31,44,37,38}, new int[] {39,40,0,0,0,0,0,0,0,0, 0,0,0,39,40,0,0,71,0,0, 0,0,0,0,68,0,0,0,65,66, 55,0,0,0,0,0,0,0,0,0, 0,0,0,72,0,0,0,0,0,0, 0,0,0,0,0,0,0,72,0,0, 0,0,0,0,0,0,0,0,0,48, 0,0,0,0,0,0,0,0,0,0, 0,49,0,0,63,64,0,0,0,0, 0,0,0,0,0,0,0,0,0,0}, new int[] {0,0,0,0,0,0,0,0,0,0, 0,0,0,59,60,61,0,0,57,58, 47,0,0,67,0,69,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,59,60, 0,33,0,0,0,0,0,0,67,0, 0,41,0,0,57,58,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0}, 10, 64); // Create the Game Loop (the thread) thread = new MainThread(getHolder(), this); // Make GamePanel able to focus so it can handle events setFocusable(true); } public void render(Canvas canvas) { // Fill screen with black canvas.drawColor(Color.BLACK); gameMap.renderBottom(canvas); // Tell our hero to draw itself hero.draw(canvas); gameMap.renderTop(canvas); } public void update() { //Check if hero reached right wall if (hero.getX() + 64 >= getWidth()) { hero.setX(0); } // Tell our hero to update itself hero.update(); } }
Note: this is showing ONLY the changed/added variables/methods, not the whole code, update accordingly.
We added a GamePanel variable to the class and initialise it on our constructor (there are 3 int[] arrays, those are from our generated .txt from Tiled, as shown in the picture tutorial above, mines are probably different than yours, use the ones you get! They're, in order, terrain, object and roof).
The Hero initialising method now has one more int in it (with value 64), as it'll be required for the changes made to the class.
We call both GameMap's render methods on our render(Canvas canvas), the first one before drawing our hero, the other after, as previously discussed. There's a minor change to our update() method, just to move the character from left to right, got tired of watching it moving from top to bottom :).
Finally, some changes in our Hero class to complete this post.
Hero.java
public class Hero { public Hero(Bitmap bitmap, int x, int y, int direction, int square) { this.bitmap = bitmap; this.x = x; this.y = y; this.direction = direction; this.sprite = 0; this.spriteHeight = bitmap.getHeight() / 4; // 4 directions this.spriteWidth = bitmap.getWidth() / 3; // 3 sprites per direction this.src = new Rect(0, direction * spriteHeight, spriteWidth, (direction + 1) * spriteHeight); this.dst = new Rect(x, y, x + square, y + square); Log.d(TAG, "Hero created!"); } public void update() { x += 5; dst.left = x; dst.right = x + spriteWidth; sprite++; src.left += spriteWidth; src.right += spriteWidth; if (sprite > 2) { sprite = 0; src.left = 0; src.right = spriteWidth; } } }
Note: this is showing ONLY the changed/added variables/methods, not the whole code, update accordingly.
As we mentioned, the constructor takes a new int parameter, this is to change the size of the sprite rendered on screen, which will allow our game to do things like zoom in/out, in the future. The only other change is on the update() method, this is to move our hero from left to right.
That's it! Run it now and you'll see our hero moving left to right on the map you designed :P
Hope you guys enjoyed it! Share this post and follow me on social networks! :) I appreciate it!!!
Take care!!!
No comments:
Post a Comment
Got something to say? Speak your mind!