Kilobolt
  • Home
  • Tutorials
    • Game Development Tutorial >
      • Unit 1: Beginning Java >
        • Before you begin...
        • Day 1: Setting Up
        • Day 2: Java Basics
        • Day 3: More Basics
        • Day 4: Java Math
        • Day 5: More Math
        • Day 6: If... else...
        • Day 7: More Control Flow
        • Day 8: Looping
        • Day 9: More on Looping
        • Day 10: Inheritance, Interface
        • Day 11: Threads and Graphics
      • Unit 2: Creating a Game I >
        • Day 1: Foundations
        • Day 2: Basic Framework
        • Day 3: Taking User Input
        • Day 4: Enter the Robot
        • Day 5: Background and Sprites
        • Day 6: Adding Enemies
        • Day 7: Shooting Bullets
        • Day 8: Animations
        • Day 9: 2D-Arrays
        • Day 10: Painting the Tilemap
      • Unit 3: Creating a Game II >
        • Day 1: Level Creation - Part 1
        • Day 2: Level Creation - Part 2
        • Day 3: Level Creation - Part 3
        • Collision Detection Basics
        • Day 4: Collision Detection Part 1
        • Day 5: Collision Detection Part 2
        • Day 6: Collision Detection Part 3
        • Day 7: Health System & Death
        • Day 8: Basic AI & Final Touches
      • Unit 4: Android Game Development >
        • Day 1: Introduction to Android
        • Day 2: Setting up for Development
        • Day 3: Creating our First Android Application
        • Day 4: Parts of an Android Application
        • Day 5: The Android Game Framework: Part I
        • Day 6: The Android Game Framework: Part II
        • Create an Android Game From Scratch (or port your existing game)
        • Day 7: Creating an Android Game (From Start to Finish)
      • Reference Sheet
    • Zombie Bird Tutorial (Flappy Bird Remake) >
      • Unit 1: Building the Game >
        • Introduction
        • Day 1: Flappy Bird - An In-depth Analysis
        • Day 2: Setting up libGDX
        • Day 3: Understanding the libGDX Framework
        • Day 4: GameWorld and GameRenderer and the Orthographic Camera
        • Day 5: The Flight of the Dead - Adding the Bird
        • Day 6: Adding Graphics - Welcome to the Necropolis
        • Day 7: The Grass, the Bird and the Skull Pipe
        • Day 8: Collision Detection and Sound Effects
        • Day 9: Finishing Gameplay and Basic UI
        • Day 10: GameStates and High Score
        • Day 11: Supporting iOS/Android + SplashScreen, Menus and Tweening
        • Day 12: Completed UI & Source Code
    • Android Application Development Tutorial >
      • Unit 1: Writing Basic Android Apps >
        • Before you begin...
        • Day 1: Android 101
        • Day 2: Getting to Know the Android Project
        • Day 3: The Development Machine
        • Day 4: Building a Music App - Part 1: Building Blocks
        • Day 5: Building a Music App - Part 2: Intents
        • Day 6: Building a Music App - Part 3: Activity Lifecycles
  • Forum
  • About Us
    • Contact Us
  • Our Games
    • TUMBL: FallDown
  • Facebook
  • Twitter

GAME DEVELOPMENT TUTORIAL: DAY 3-1: Level Creation Part I

11/26/2012

65 Comments

 
Picture
Welcome to Unit 3. In the next several lessons, we will put our finishing touches on the game, adding levels, collision detection, AI, and so on.

We are about half way finished with our first Java game. You've almost made it. Let's give it a final push.

Today, we will begin a three part series on Level Creation. This series will entail reading a text file to create tile maps, which will then be used to create objects that the character can interact with on the screen.

We are implementing concepts that we have covered in the last 2 lessons of Unit 2. If you ever get confused, refer back to them. This lesson will be here when you come back.

Part 1: Common Ground

We will be picking up right where we left off at the end of Unit 2, Day 8. 
If you have misplaced your files, or just want to start fresh, download the source code below:
Picture
unit_3_begin.zip
File Size: 269 kb
File Type: zip
Download File

Instructions on Importing Eclipse Projects:
Click Here (Eclipse.org)
Downloading Tiles & Background

If you downloaded the source code above (unit3begin.zip), you can skip the downloading and just read through. I already did this for you.


Using your own code? Follow along.
1. Download these two awesome tiles I created (please excuse my programmer art).
Place 'em in the data folder. 

Picture
tiledirt.png
File Size: 3 kb
File Type: png
Download File

Picture
tileocean.png
File Size: 3 kb
File Type: png
Download File

These two tiles are 40x40 pixels each. Since we are using a screen of size 800x480, we will be able to fit 20 across and 12 down (recall that the origin is at the top left).

2. Also, we will be replacing my horrible background with this one:
Picture
background.png
File Size: 32 kb
File Type: png
Download File

Download and replace the original background.png!

Part 2: Creating the Tile Class

What are we downloading all these images for?

This is the desired effect:
Picture
The full Tile Class is to the right.
Here's what I am doing (Ctrl+ to zoom):

1. Create a new class called Tile.

2. Add the following variables:
Integers: tileX, tileY, speedX, type.
Images: tileImage

tileX represents the x coordinate (horizontal position) of the tile.
tileY represents the y coordinate (vertical position) of the tile).
speedX is equal to the speed of the tile.
type indicates whether the tile is an ocean tile or dirt tile.

3. Create a Background object called bg, and  point it to the bg1 object in StartingClass.
This allows us to reference the bg1 object within our tile class.

4. Create a constructor.
As I mentioned, each tile will have the value of 40 pixels. We will be creating Tiles using indexes, rather than pixels. This is why we multiple each index by 40 to get the pixel location.

For example, the tile in the upper left corner will have index (0, 0), so we multiply 40 and still have (0, 0,) as tileX, tileY.

However, for the adjacent tile (1, 0), the tileX and tileY will be (40, 0), just as we want it to be.

5. Create an update() method.
As with those of other classes, this update method will run on every loop (of the game loop).

Type 1 is the ocean tile. We want it to move slowly in the background; however, when the background is scrolling, we want the ocean to scroll faster to accommodate movement. 

Also, you will notice that this ocean scrolls slower than our character moves. This makes sense, because the character is moving right in front of the camera, while the ocean is in the mid-ground. This is parallax scrolling at work, albeit simplified. And the same reason is why the ocean will scroll 5x faster than the background layer (sky and clouds - we will be slowing it down).

6. The rest of the class is just getters and setters (generated by right-clicking >> Source >> Generate Getters and Setters.



Tile Class

package kiloboltgame;

import java.awt.Image;

public class Tile {

    private int tileX, tileY, speedX, type;
    public Image tileImage;

    private Background bg = StartingClass.getBg1();

    public Tile(int x, int y, int typeInt) {
        tileX = x * 40;
        tileY = y * 40;

        type = typeInt;

        if (type == 1) {
            tileImage = StartingClass.tileocean;
        } else if (type == 2) {

            tileImage = StartingClass.tiledirt;
        }

    }

    public void update() {
        // TODO Auto-generated method stub
        if (type == 1) {
            if (bg.getSpeedX() == 0){
                speedX = -1;
            }else{
                speedX = -2;
            }

        } else {
            speedX = bg.getSpeedX()*5;
        }

        tileX += speedX;
    }

    public int getTileX() {
        return tileX;
    }

    public void setTileX(int tileX) {
        this.tileX = tileX;
    }

    public int getTileY() {
        return tileY;
    }

    public void setTileY(int tileY) {
        this.tileY = tileY;
    }

    public Image getTileImage() {
        return tileImage;
    }

    public void setTileImage(Image tileImage) {
        this.tileImage = tileImage;
    }

}

Part 3: Making a Few Changes

1. Open up your Robot class. Now that we have multiple layers of backgrounds, we want the farthest one to scroll at a slower speed.

Locate the 4th if statement in the update() method inside the Robot class.

Make the following changes:

if (speedX > 0 && centerX > 200){
   bg1.setSpeedX(-MOVESPEED/5);
   bg2.setSpeedX(-MOVESPEED/5);
}

Now the background will move at 1/5 the character's speed.

2. Now open up your Enemy class and make the following changes to the update method (for the same reasons):

// Behavioral Methods
public void update() {
   centerX += speedX;
   speedX = bg.getSpeedX()*5;

}

Part 4: Adding the Tiles to StartingClass

We will be working with the StartingClass for Part 4.


1. We begin at the class declaration. Add the following changes in bold.

 public class StartingClass extends Applet implements Runnable, KeyListener {

private Robot robot;
private Heliboy hb, hb2;
private Image image, currentSprite, character, character2, character3,
characterDown, characterJumped, background, heliboy, heliboy2,
heliboy3, heliboy4, heliboy5;

public static Image tiledirt, tileocean;

private Graphics second;
private URL base;
private static Background bg1, bg2;
private Animation anim, hanim;

private ArrayList<Tile> tilearray = new ArrayList<Tile>();


We have created two Image objects: tiledirt and tileocean, which will point to our PNG files. We have also created an ArrayList of Tiles called tilearray.
2. Then we scroll down to the //Image Setups section inside the init() method. Make the changes here:

  // Image Setups
character = getImage(base, "data/character.png");
character2 = getImage(base, "data/character2.png");
character3 = getImage(base, "data/character3.png");

characterDown = getImage(base, "data/down.png");
characterJumped = getImage(base, "data/jumped.png");

heliboy = getImage(base, "data/heliboy.png");
heliboy2 = getImage(base, "data/heliboy2.png");
heliboy3 = getImage(base, "data/heliboy3.png");
heliboy4 = getImage(base, "data/heliboy4.png");
heliboy5 = getImage(base, "data/heliboy5.png");

background = getImage(base, "data/background.png");

tiledirt = getImage(base, "data/tiledirt.png");
tileocean = getImage(base, "data/tileocean.png");

3. Next, we must initialize the tilearray we created in step 1. Scroll down to the start() method, and make these changes.


@Override
public void start() {
bg1 = new Background(0, 0);
bg2 = new Background(2160, 0);

// Initialize Tiles

for (int i = 0; i < 200; i++) {
for (int j = 0; j < 12; j++) {

if (j == 11) {
Tile t = new Tile(i, j, 2);
tilearray.add(t);

} if (j == 10) {
Tile t = new Tile(i, j, 1);
tilearray.add(t);

}
}
}

hb = new Heliboy(340, 360);
hb2 = new Heliboy(700, 360);
robot = new Robot();

Thread thread = new Thread(this);
thread.start();
}
We utilize a double for loop (as in Days 9, 10 of Unit 2), representing the x index of the tiles as i, and y index as j. 

The numbers i < 200, j < 12 creates 2400 possible locations for tiles. Of those, we fill 400 (i = 200, j = 2).
4. With the tilearray created, we must now update and paint the individual tiles inside the ArrayList.

Create these two methods. Add them below the paint() method for organization:

  private void updateTiles() {

for (int i = 0; i < tilearray.size(); i++) {
Tile t = (Tile) tilearray.get(i);
t.update();
}


}


private void paintTiles(Graphics g) {
for (int i = 0; i < tilearray.size(); i++) {
Tile t = (Tile) tilearray.get(i);
g.drawImage(t.getTileImage(), t.getTileX(), t.getTileY(), this);
}
}
Both of these methods just utilize 1 index, looking at the size (the number of tiles) in tilearray. In the for loops, the number i increases from 0 to 2399, and we therefore update our 2400 tiles and paint them.

Look at the parameters inside the g.drawImage method, and make sense of them. We are drawing the image associated with the tile at the tile's X coordinate, and Y coordinate.
5. All we now have to do is call these two methods. 


First. The updateTiles() method will be called inside the run() method. Place it above this statement like so:
updateTiles();
hb.update();

Second. The paintTiles()  method will be called inside the paint() method. We want the tiles to appear on top of the background but behind the character and enemies, so we place it below the two g.drawImage(background) methods like so:

g.drawImage(background, bg1.getBgX(), bg1.getBgY(), this);
g.drawImage(background, bg2.getBgX(), bg2.getBgY(), this);
paintTiles(g);

Finished!

Here's the final StartingClass code:
package kiloboltgame;

import java.applet.Applet;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.URL;
import java.util.ArrayList;
import kiloboltgame.framework.Animation;

public class StartingClass extends Applet implements Runnable, KeyListener {

private Robot robot;
private Heliboy hb, hb2;
private Image image, currentSprite, character, character2, character3,
characterDown, characterJumped, background, heliboy, heliboy2,
heliboy3, heliboy4, heliboy5;

public static Image tiledirt, tileocean;

private Graphics second;
private URL base;
private static Background bg1, bg2;
private Animation anim, hanim;

private ArrayList<Tile> tilearray = new ArrayList<Tile>();

@Override
public void init() {

setSize(800, 480);
setBackground(Color.BLACK);
setFocusable(true);
addKeyListener(this);
Frame frame = (Frame) this.getParent().getParent();
frame.setTitle("Q-Bot Alpha");
try {
base = getDocumentBase();
} catch (Exception e) {
// TODO: handle exception
}

// Image Setups
character = getImage(base, "data/character.png");
character2 = getImage(base, "data/character2.png");
character3 = getImage(base, "data/character3.png");

characterDown = getImage(base, "data/down.png");
characterJumped = getImage(base, "data/jumped.png");

heliboy = getImage(base, "data/heliboy.png");
heliboy2 = getImage(base, "data/heliboy2.png");
heliboy3 = getImage(base, "data/heliboy3.png");
heliboy4 = getImage(base, "data/heliboy4.png");
heliboy5 = getImage(base, "data/heliboy5.png");

background = getImage(base, "data/background.png");

tiledirt = getImage(base, "data/tiledirt.png");
tileocean = getImage(base, "data/tileocean.png");

anim = new Animation();
anim.addFrame(character, 1250);
anim.addFrame(character2, 50);
anim.addFrame(character3, 50);
anim.addFrame(character2, 50);

hanim = new Animation();
hanim.addFrame(heliboy, 100);
hanim.addFrame(heliboy2, 100);
hanim.addFrame(heliboy3, 100);
hanim.addFrame(heliboy4, 100);
hanim.addFrame(heliboy5, 100);
hanim.addFrame(heliboy4, 100);
hanim.addFrame(heliboy3, 100);
hanim.addFrame(heliboy2, 100);

currentSprite = anim.getImage();
}

@Override
public void start() {
bg1 = new Background(0, 0);
bg2 = new Background(2160, 0);

// Initialize Tiles

for (int i = 0; i < 200; i++) {
for (int j = 0; j < 12; j++) {

if (j == 11) {
Tile t = new Tile(i, j, 2);
tilearray.add(t);

} if (j == 10) {
Tile t = new Tile(i, j, 1);
tilearray.add(t);

}

}
}

hb = new Heliboy(340, 360);
hb2 = new Heliboy(700, 360);
robot = new Robot();

Thread thread = new Thread(this);
thread.start();
}

@Override
public void stop() {
// TODO Auto-generated method stub
}

@Override
public void destroy() {
// TODO Auto-generated method stub
}

@Override
public void run() {
while (true) {
robot.update();
if (robot.isJumped()) {
currentSprite = characterJumped;
} else if (robot.isJumped() == false && robot.isDucked() == false) {
currentSprite = anim.getImage();
}

ArrayList projectiles = robot.getProjectiles();
for (int i = 0; i < projectiles.size(); i++) {
Projectile p = (Projectile) projectiles.get(i);
if (p.isVisible() == true) {
p.update();
} else {
projectiles.remove(i);
}
}

updateTiles();
hb.update();
hb2.update();
bg1.update();
bg2.update();
animate();
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public void animate() {
anim.update(10);
hanim.update(50);
}

@Override
public void update(Graphics g) {
if (image == null) {
image = createImage(this.getWidth(), this.getHeight());
second = image.getGraphics();
}

second.setColor(getBackground());
second.fillRect(0, 0, getWidth(), getHeight());
second.setColor(getForeground());
paint(second);

g.drawImage(image, 0, 0, this);

}

@Override
public void paint(Graphics g) {
g.drawImage(background, bg1.getBgX(), bg1.getBgY(), this);
g.drawImage(background, bg2.getBgX(), bg2.getBgY(), this);
paintTiles(g);

ArrayList projectiles = robot.getProjectiles();
for (int i = 0; i < projectiles.size(); i++) {
Projectile p = (Projectile) projectiles.get(i);
g.setColor(Color.YELLOW);
g.fillRect(p.getX(), p.getY(), 10, 5);
}

g.drawImage(currentSprite, robot.getCenterX() - 61,
robot.getCenterY() - 63, this);
g.drawImage(hanim.getImage(), hb.getCenterX() - 48,
hb.getCenterY() - 48, this);
g.drawImage(hanim.getImage(), hb2.getCenterX() - 48,
hb2.getCenterY() - 48, this);
}

private void updateTiles() {

for (int i = 0; i < tilearray.size(); i++) {
Tile t = (Tile) tilearray.get(i);
t.update();
}

}

private void paintTiles(Graphics g) {
for (int i = 0; i < tilearray.size(); i++) {
Tile t = (Tile) tilearray.get(i);
g.drawImage(t.getTileImage(), t.getTileX(), t.getTileY(), this);
}
}

@Override
public void keyPressed(KeyEvent e) {

switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
System.out.println("Move up");
break;

case KeyEvent.VK_DOWN:
currentSprite = characterDown;
if (robot.isJumped() == false) {
robot.setDucked(true);
robot.setSpeedX(0);
}
break;

case KeyEvent.VK_LEFT:
robot.moveLeft();
robot.setMovingLeft(true);
break;

case KeyEvent.VK_RIGHT:
robot.moveRight();
robot.setMovingRight(true);
break;

case KeyEvent.VK_SPACE:
robot.jump();
break;

case KeyEvent.VK_CONTROL:
if (robot.isDucked() == false && robot.isJumped() == false) {
robot.shoot();
}
break;

}

}

@Override
public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
System.out.println("Stop moving up");
break;

case KeyEvent.VK_DOWN:
currentSprite = anim.getImage();
robot.setDucked(false);
break;

case KeyEvent.VK_LEFT:
robot.stopLeft();
break;

case KeyEvent.VK_RIGHT:
robot.stopRight();
break;

case KeyEvent.VK_SPACE:
break;

}

}

@Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub

}

public static Background getBg1() {
return bg1;
}

public static Background getBg2() {
return bg2;
}

}
When you run it:
Picture
We will pick up from here in Day 2.

That's It. Thanks for reading!

Picture
Picture
unit_3_day_1.zip
File Size: 271 kb
File Type: zip
Download File

Go to Unit 3: Day 2
65 Comments
Karl Bonitz link
11/26/2012 11:01:22 am

Excellent tutorial as always! Thank you

Reply
Aswin Kumar
11/26/2012 03:58:42 pm

very nice.
the helliboy does not get updated whyi.e not looped how to do it pls explain

Reply
James C.
11/26/2012 04:03:15 pm

Aswin,

Thanks for commenting! If you could be slightly more specific, I can help you out.

Is animation working? How about scrolling? What is not updated exactly?

Reply
Aswin Kumar
11/26/2012 06:50:34 pm

i have mailed u the project.yes animation works,scrolling goes well for some frames then helliboy is not appering

Anthony
11/26/2012 06:19:05 pm

Loving this series. Thanks. I've noticed one thing though. If you scroll right for long enough (it does take a while to be fair) the ground disappears. The ocean, sky and robot continue as normal. Is this something I've mistyped?

Reply
James C.
11/26/2012 06:21:53 pm

That's working as intended. Look carefully at step 3 of Part 4. :)

Of course, we won't be having this problem once we start reading from a text file. I created a finite number of tiles and I did not set them to return to the opposite side of the screen.

Reply
Wv
3/11/2014 02:46:14 am

Actually ocean also disappear if you keep going. Makes sense since we've created tiles in start method and haven't tried to recycle or recreate tiles.

Reece
11/27/2012 04:00:47 pm

Great stuff thank you! I'm really looking forward to getting this game finished. I now have a much better apprieciation as to how much work is involved!

Just one thing to clarify, when running the game, (no keyboard input) : heliboy's rotors are swirling, robots eyes are blinking, and the waves are moving.
is that right?

Thanks again!

Reply
James
11/27/2012 04:08:16 pm

Yep that sounds about right :)

Reply
Richard Burns
11/29/2012 06:03:55 am

Would it to be possible to have a very large picture as the entire background minus a few interactive spites i.e. hedges and the such. I have seen sonic games that have the entire level as one image. would it be possible on a phone to do that?

Reply
James C.
11/29/2012 07:24:57 am

It all depends on how you handle memory. As long as you keep your memory relatively empty, it's fine if you use a large image. But if you have a small RAM, you would probably want to free up memory as often as possible.

A simple way to calculate memory usage:
pixel count x pixel depth = memory usage in bits.

So 1000 height x 1000 width x 32 (if transparent, I believe) bits = 32000000 bits.

We convert that to bytes (divide by 8) and find that such an image would take up 4 MB.

To put that into perspective, lower end Android phones only allocates 16MB per application. Some of the newer ones are about 64MB.

That means on lower devices, your game would crash after you just load just one 2000 x 2000 x 32 bits image.

Of course you can reduce the size of the image and scale it at runtime, but that is VERY slow.

Reply
Sean
11/30/2012 01:42:00 am

My code works fine but no ocean or dirt tiles appear? is there a layout method I'm missing maybe?

Reply
James C.
11/30/2012 01:47:26 am

Try changing the image variable in the g.drawImage() statement inside the paintTiles to another image that works. If you see the tiles then, it's a problem with how you initialized the images or your images are not in the right folder. You can also try "cleaning" the project.

Reply
Sean (a different Sean)
3/21/2013 03:23:45 am

I'm having the same problem. I think because my name is also Sean. Or, that is me from the future.

"cleaning" the project erases tiledirt and tileocean images from the folder. if I re-add them and refresh the data folder they still won't appear.

Sean Again
3/21/2013 03:25:39 am

Please respond to this comment, I missed checking the "notify me of new comments" button on the other.

dude
8/3/2013 10:27:55 pm

its probably because the directory path given for those images are wrong, or at least dont work for some of us

the paths james shows in the tutorial are:

tiledirt = getImage(base, "data/tiledirt.png");
tileocean = getImage(base, "data/tileocean.png");

for me this does not work either, try this instead:

tiledirt = getImage(base, "../data/tiledirt.png");
tileocean = getImage(base, "../data/tileocean.png");

keep in mind to add the "../" to any future directory paths when assigning image files
i keep forgetting this myself *facepalm*

ut after i did this it worked perfectly :D

paul
12/8/2012 10:24:48 pm

Hi there,
first off thanks for the nice tutorial.
At step 3. you write:
"The numbers i < 200, j < 12 creates 2400 tiles: 200 across, and 12 down."
Aren´t we only creating 400 tiles (200x2) since we only fill the lower 2/12 of the screen? Maybe you could clarify this in the tutorial.

Regards Paul

Reply
James C.
12/9/2012 12:14:05 am

Fixed. Thanks!

Reply
Vaibhav
1/16/2015 06:31:04 pm

Hey James,for this code,
or (int i = 0; i < 200; i++) {
for (int j = 0; j < 12; j++) {

if (j == 11) {
Tile t = new Tile(i, j, 2);
tilearray.add(t);

} if (j == 10) {
Tile t = new Tile(i, j, 1);
tilearray.add(t);

}
}
}

I think we can start the second loop directly from 10, as we are filling the list only at 10 and 11 index. It should save some time, because the loop wont check for those conditions from 0-9 index. Not sure if it is killing any performance or not. Otherwise also it is working good.

Paul
12/14/2012 06:06:25 am

One question. I can't add new images to my project. I copy them to the data folder but Eclipse doesn't see them. When I do Project >> Clean they also disappear.. Even if I copied your code to my project it doesn't work. How can I solve this? Because so far I've been importing your code.

Reply
James C.
12/14/2012 01:25:35 pm

Hmm I've never heard of such a problem before. I don't think its a programming issue if the files are disappearing. The two are not connected.

Try creating a new project perhaps?

Reply
Peter
8/17/2013 01:05:33 am

Hello James and thanks for the tutorial. That ALSO hapened to me. the images didnt appear in the project when i cleaned it, and it never did with other projects, what did made the images appear, was right clicking on the project folder and selecting "refresh", which would refresh the project, to verify for new files, or deleted files. it always works that way for me

hey
12/22/2012 02:29:30 am

I had this problem before too. What I did was right clicking the project in the package explorer and go to build path. From there you will have to source, remove the folder that doesnt work and link create a new one where you link to the data folder. Make sure you tick it under order and export.

Reply
Nicolas
1/5/2013 12:30:10 am

Thanks for this great tutorial. It's the best.
But i have encountered a litle problem.

When i downloaded the zip forva fresh start. I get an error in eclips.
I think this is because your project uses jre 1.7 and i think my eclips has only support files for 1.6

Should i convert your project to 1.6 ? (how?)
Or should i find newer version of jre (java) for eclips (mac)

Thans in avance

Reply
Nicolas
1/5/2013 12:39:25 am

I am not at a computer right now, but I think you can change the compiler version by right clicking the Project, properties, Java compiler. There should be an option to change it.

Reply
sam
1/14/2013 11:53:57 am

a

FTdevelopment
1/24/2013 08:09:28 am

Hii. I didnt understand this part.

for (int i = 0; i < 200; i++) {
for (int j = 0; j < 12; j++) {
if (j == 11) {
Tile t = new Tile(i, j, 2);
tilearray.add(t);
}
if (j == 10) {
Tile t = new Tile(i, j, 1);
tilearray.add(t);
}
}

1 : Why are we doing that "j == 11" (or j == 10) ?
2 : Why are we doing that "tilearray.add(t)" on there ?

Reply
James C.
1/25/2013 10:03:48 am

Try removing the two if statements (and the statements inside) and see how the game changes. That should help you understand.

Also, the tilearray is the collection of tiles, and we are adding the new tile that we created into this tilearray, so that we can refer to all the tiles as a group.

Reply
Dawid
1/27/2013 05:52:27 am

I'm not supposed to see a green line above the dirt tile am I?

Great tutorial, thanks alot! :)

Reply
James c
1/27/2013 07:27:05 am

That would be the grass ;)

Reply
Dawid
1/27/2013 09:04:35 am

Hmm... did you change the dirt tile just now?
I swear mine was the all-brown tile at this part of the tutorial :o

Dawid
1/27/2013 09:07:02 am

The thumbnail and download-file are indeed different, look for yourself :)

Holodoc
2/23/2013 10:34:19 pm

Hi,
Great tutorial.
Currrently there is a mistake in the programm on updating the tiles: After one tile is outside of the screen, it should not be updated again. Otherwise you will get an overflow and the tiles came back again after some time. Funny thing: the tiles are not hanging together when they come to the screen the second time....

Regards,

holodoc

Reply
juan arango
4/28/2013 07:31:05 am

hi man your tutorial its great! nut i have a problem my code does work altought i download you code but it still doesnt work it say that the arraylist have to be parameterized and my code is the same that yours! ( sorry my english, i speak spanish )

Reply
juan arango
4/28/2013 07:34:26 am

thats what appears
java.lang.UnsupportedClassVersionError: kiloboltgame/StartingClass : Unsupported major.minor version 51.0
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(Unknown Source)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.access$000(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.applet.AppletClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.applet.AppletClassLoader.loadCode(Unknown Source)
at sun.applet.AppletPanel.createApplet(Unknown Source)
at sun.applet.AppletPanel.runLoader(Unknown Source)
at sun.applet.AppletPanel.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

Reply
juan arango
4/28/2013 07:34:38 am

thats what appears
java.lang.UnsupportedClassVersionError: kiloboltgame/StartingClass : Unsupported major.minor version 51.0
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(Unknown Source)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.access$000(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.applet.AppletClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.applet.AppletClassLoader.loadCode(Unknown Source)
at sun.applet.AppletPanel.createApplet(Unknown Source)
at sun.applet.AppletPanel.runLoader(Unknown Source)
at sun.applet.AppletPanel.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

Reply
juan arango
4/28/2013 07:34:47 am

thats what appears
java.lang.UnsupportedClassVersionError: kiloboltgame/StartingClass : Unsupported major.minor version 51.0
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClassCond(Unknown Source)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.access$000(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.applet.AppletClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.applet.AppletClassLoader.loadCode(Unknown Source)
at sun.applet.AppletPanel.createApplet(Unknown Source)
at sun.applet.AppletPanel.runLoader(Unknown Source)
at sun.applet.AppletPanel.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

Reply
Tpt
5/12/2013 10:06:05 pm

Thanks for your amazing tutorial.
Some comments about this page:
Instead of
for (int i = 0; i < 200; i++) {
for (int j = 0; j < 12; j++) {
if (j == 11) {
Tile t = new Tile(i, j, 2);
tilearray.add(t);
}
if (j == 10) {
Tile t = new Tile(i, j, 1);
tilearray.add(t);
}
}
why not use
for (int i = 0; i < 200; i++) {
Tile tocean = new Tile(i, 10, 1);
tilearray.add(tocean);
Tile tdirt = new Tile(i, 11, 2);
tilearray.add(tdirt);
}
that does exactly the same thing?

About Tile class, instead of having "if (type == 1) { ... } else { ... }" everywhere I think it may be a good idea to create
an abstract class Tile and two subclasses TilOcean and TilDirt that manage the two types of Tiles. It's a good occasion to
talk a second time about inheritance.

And, more cosmetic, it may be a good idea to introduce the "for (Tile tile : tilearray) { ... }" loop instead of
"for (int i = 0; i < tilearray.size(); i++) { tile = (Tile) tilearray.get(i); ... }" in the StartingClass implementation.

What do you think about these proposals?

Reply
Rasmus link
10/18/2013 02:41:07 pm

another thing given that our background is only 2* 2160 108 tiles should be enough to cover both.
this would save a little calculation as well :)
also the tiles does not wrap so if you go far enough they disappear.

solution is to ad
if (tileX <= -2140) {
tileX += 4320;
}
to the update function

Reply
rasmus link
10/18/2013 02:44:49 pm

OK forget it reading the next lesson makes the code chioces obvious

Hernaldo link
6/25/2013 01:43:31 am

Excelente tutorial gracias. Explicado en términos simples y paso a paso.
Excellent tutorial thanks. Explained in simple terms and step by step.

Reply
Robert link
7/9/2013 07:14:41 am

Im having an issue with the enemies appearing. The first Heliboy does not appear, but the second one does. i cant seem to figure out whats going on, ive checked my coding against the source code and it looks the same for every reference of the heliboy. Yet only one is displaying..

Reply
Jason
7/14/2013 06:22:52 am

Awesome tutorial! I can understand just about all of it while i was following along and writing it to my code. When i ran the game however, i encountered a problem: everything moved WAY to fast (Animations, updates, etc.) I figured this was a problem with the run method not waiting 17 milliseconds between loops but i have no idea what could cause this. Any help or advice would be appreciated! Thanks!

Reply
AlexNovember
8/14/2013 04:41:34 pm

The background floats away to the top left of the screen as the robot moves along.. I even had to copy your code and put it instead of mine because of some stupid error I didn't feel like trying to find.
Still does it.

Reply
Yaniv
8/19/2013 05:10:21 pm

Great tutorial for beginners (like me)
One question though, can you add the ocean?
You didnt use it...

Reply
Kyle Leon Scam link
10/1/2013 03:58:48 pm

I really appreciate people helping her out in her bad times. Lets just hope for the best. All the best jane we are always there for you.

Reply
Meghana Joshi
10/3/2013 06:20:13 am

OMG! I love your site. I have developed projects in java but the way you teach... It is awesome! Because of you people, I am soon planning to launch my own android game, which I learnt mostly coz of this site... and more games if it is succesful... And if I do, get to earn some money as an Indie developer, and if this site is still running, I surely would love to donate if you people promise to keep going with your tutorials. Thank you so much!! Really!!

Reply
Ethan
11/26/2013 04:30:06 am

I am not sure why, but if i have my robot move a certain distance, then the dirt tile disappears. Then if I go the same distance yet again, the waves disappear leaving only the background.

My code is the same as what you posted.

Reply
Ethan
11/26/2013 04:44:09 am

actually, for the waves, if I just sit there, it will eventually scroll off and disappear.

Reply
Ethan
11/26/2013 04:57:54 am

found the bug.

In the tile class, in the update method you have:

"public void update() {
// TODO Auto-generated method stub
if (type == 1) {
if (bg.getSpeedX() == 0){
speedX = -1;
}else{
speedX = -2;
}

} else {
speedX = bg.getSpeedX()*5;
}

tileX += speedX;
}"

If should be:

public void update() {
if (type == 1) {
if (bg.getSpeedX() == 0) {
speedX = -1;
} else {
speedX = -2;
}

} else {
speedX = bg.getSpeedX() * 5;
}

tileX += speedX;

if (tileX <= -2160){
tileX += 4320;
}
}

Reply
Carlos
2/22/2014 04:53:44 am

Great tutorial , just one question, what if I want my character to jump just once per key press?

Reply
fatehb
3/2/2014 08:49:05 am

hey

is there a way i can email you a image of what my output looks like?
my background isnt working properly and the heliboys are out of place.

thanks

Reply
NeverMind10
3/11/2014 11:53:58 pm

Hi,
How can I make a random map which never ends?
What method should I use to create that?

Reply
Jay
5/2/2014 07:33:57 am

Used my code and your code. When finished with this section of the tutorial, my applet screen is all white...

Reply
Opal
5/4/2014 02:12:09 pm

Could you reply to this comment with the link of an app that you made that you featured in one of these tutorials? I couldn't find where it was mentioned. Thx in advance! (if it helps I think it was called Tumble or something)

Reply
Rahul Kalra
5/7/2014 05:29:13 am

Hi...
First of all Amazing Tutorial, thank you for that.

Can u tell how can I make the background repeat it self again, You know after all the tiles have been placed it start it self once again

Reply
raviteja
7/19/2014 01:09:10 am

Hi james,
The tutorial is extraordinary and way too easy for a beginner.I followed through it thoroughly but i got a problem.After croosing the first two heliboys there are no heliboys appearing .Have i made a mistake.??

Reply
Bluel3ull
10/1/2014 02:12:21 am

Hello, iv looked all over and iv tryed some stuff. but i cant figure out how to change the background for different levels, any one know the answer and thank you.

Reply
Rohit
10/23/2014 11:23:11 pm

The background gets slower and slower as the character moves rightwards.
I think this is due to the MOVESPEED/5 modification you did in this tutorial. Is this intended or I did some mistake?

Reply
Mr.lOuD
12/21/2014 01:15:39 am

Hi,

When i run the code and the window opens up i can run from left to right but my characters image switches from the the character.png to the enemy.png. I can see the dirt tiles and ocean tiles, they have animation but I can not see the background image.

Reply
YOUR_MOTHER_LOL_JK_GOT_YOU_XD_GET_REKT_LOSER_LERN_2_SCRIPT_YOU_SCRUBLORD link
5/6/2015 04:07:48 am

no

Reply
Arjay
7/8/2015 07:11:36 pm

I dont understand why we create random for the tilemap. Can someone explain?

Reply
Arjay
7/8/2015 07:18:30 pm

hmmm my mistake. didnt use random at this page i think

Reply
John
8/17/2015 10:11:15 pm

I am so..... LOST

Reply
A very n00bish coder
11/5/2015 11:25:07 am

Just to clarify, are the heliboys supposed to move, or just kind of scroll with the background?

Reply



Leave a Reply.

    Author

    James Cho is the lead developer at Kilobolt Studios. He is a college student at Duke University and loves soccer, music, and sharing knowledge.

© 2014 Kilobolt, LLC. All rights reserved.