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
  • New Forum
  • About Us
    • Contact Us
  • Our Games
    • TUMBL: FallDown
  • Facebook
  • Twitter

GAME DEVELOPMENT TUTORIAL: DAY 2-10: Painting Tilemap

11/15/2012

47 Comments

 
Picture
Hello and welcome! This is the last lesson of Unit 2. 

More fun is just around the corner in Unit 3. Let's finish strong!

In today's lesson, we will be building upon what we covered in Day 9.
If you have not looked at Day 9 yet, please do so before proceeding!

What we will be covering:
1. We will be creating a two-dimensional Array that holds a random set of values.
2. Using this two-dimensional array, we will print colored squares to the screen.

What is the significance of this lesson?
When we continue our 2D game development, we will be creating levels (maps) using text files. In each text file, we will represent objects in the game with keyboard characters, and our game will parse the file to create levels that we can interact with.

This lesson will be an application of what we learned in Day 9. After this, we should be able to accomplish the above task with ease in the coming lessons.

Note: We will be creating a NEW class today. You do not need to open any previous files.
Let's begin.

Lesson #2-28: Creating the Tilemap
1. Begin by creating a class named Renderer in any test Project. 
(Tip: You can create a new Project by right-clicking on the Package Explorer >> New >> Java Project.)

2. Extend Applet and import it.
- add "extends Applet" at the end of the class declaration.
- Press Ctrl+Shift+O to organize imports automatically.

It should look like this:
import java.applet.Applet;


public class Renderer extends Applet{

}


3. Override the init() and paint() methods.
Since we have extended an Applet (inheritance), Java will look for the paint() and init() methods. As we will be defining these methods ourselves rather than using the one in the Applet method, so we use the "@Override" keyword.

The two methods will look like this (Make sure to import Graphics) (changes in Bold):
import java.applet.Applet;
import java.awt.Graphics;

public class Renderer extends Applet{

@Override
public void init() {

}

@Override
public void paint(Graphics g) {

}

}
4. Define the init() method.


In the init() method, we want to do the following:

- Set the size of the window.
- Set the color of the Background
- Create the Tilemap.

So we add the following lines of code:

setSize(800, 480);
setBackground(Color.BLACK);
createTilemap();

Eclipse will give you an error as follows:
Picture
Since we have never created a createTilemap() method, we must do so right now.

5. Click on the 1 quickfix: Create method 'createTilemap()'.

This should create a method as follows:


private void createTilemap() {



}


We must do two things in the Tilemap() method.

1. We need to create a two-dimensional array. We will call it tilemap and call its height rows and its width columns.
2. Using a random number generator, we will fill tilemap with integers between 0 and 4, inclusive.



Let's do these two things:

1. Creating a 2D Array
Before we begin, to make things easier, we will create three static variables at the top of the class.

- Directly below "public class Renderer extends Applet {", add the following:


   static int[][] tilemap;
   static int rows, columns;

This creates a static (class-wide) two-dimensional Array called tilemap and two integers called rows and columns.

Scroll back inside the createTilemap() method. We will now define the three variables by declaring the following:


   tilemap = new int[50][30];
   rows = tilemap.length;
   columns = tilemap[1].length;


- The first line creates a new 2D Array with 50 elements, which each contain 30 elements.
- tilemap.length is 50, as tilemap contains 50 elements.
- Each of these 50 elements contain 30 elements. Therefore, tilemap[1] refers to the element with index 1 in this 50 element list (the second element of fifty).

Note: Remember that indexes start at 0.
Note 2: We can refer to all 50 elements in tilemap using the indexes: 0 through 49.


2. Randomly Fill tilemap Array


- To fill the tilemap Array randomly, we first create a new Random object:
Random r = new Random();


- Then we use two for loops to fill each index with a random number between 0 and 4, inclusive:

               for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
tilemap[i][j] = r.nextInt(5);
}
}


Note: Recall that tilemap[i][j] refers to the spot with coordinates (i, j) inside the 2D array. 
Refer to this simplified illustration if you are confused!
Picture
Here's what the code will look like as of now:

FIGURE 2-36: Renderer Class, createTilemap()

import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;

public class Renderer extends Applet {

static int[][] tilemap;
static int rows, columns;

@Override
public void init() {
setSize(800, 480);
setBackground(Color.BLACK);
createTilemap();
}

private void createTilemap() {

tilemap = new int[50][30];


rows = tilemap.length;
columns = tilemap[49].length;


Random r = new Random();


for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
tilemap[i][j] = r.nextInt(5);
}
}
}

@Override
public void paint(Graphics g) {

}

}

With the tilemap created, we can now paint it to the screen!

Lesson #2-29: Painting the Tilemap
This is what we are trying to accomplish:

1. Create a square with side length 16 pixels - 50 across and 30 down (800 x 480).
2. Give this square a random color based on the integer value (0 to 4, inclusive) inside the tilemap.

- We begin Scroll to the paint(Graphics g) method. - Add the following code inside the method: 


for (int i = 0; i < rows; i++) {
   for (int j = 0; j < columns; 
   
   }
}


This is the same nested for loop we saw in the createTilemap() method. We will use i and j to refer to locations in the 2D Array.

We want to translate this (i, j) location into a coordinate system of (16*i, 16*j), because we want to represent, for example, (50, 30), as (800, 480).

So within the inner for loop, we add the following in BOLD:

for (int i = 0; i < rows; i++) {
   for (int j = 0; j < columns; 
      int mod_i = 16*i;
      int mod_j = 16*j;
   }
}

Note: The underscore "_" does not have any meaning. It is just part of the variable names, which stand for modified I and modified J.


Now directly below the two variable declarations (and still within the inner for loop), we need to create a switch that takes tilemap[i][j] as the key (recall that switches are used to compare the value of a key against cases) and compare with the integers 0 through 4, inclusive.

For each case, we will set the color of g, which needs to be set directly before you paint an object, and then paint a rectangle on the screen at the coordinates (mod_i, mod_j).

The full class will now look like this:

FIGURE 2-37: RENDERER CLASS, Completed

import java.applet.Applet;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.util.Random;

public class Renderer2 extends Applet {

static int[][] tilemap;
static int rows, columns;

@Override
public void init() {
setSize(800, 480);
setBackground(Color.BLACK);
createTilemap();

}

@Override
public void paint(Graphics g) {
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {

int mod_i = 16*i;
int mod_j = 16*j;

switch (tilemap[i][j]) {
case 0:
g.setColor(Color.RED);
g.fillRect(mod_i, mod_j, 16, 16);
break;
case 1:
g.setColor(Color.BLUE);
g.fillRect(mod_i, mod_j, 16, 16);
break;
case 2:
g.setColor(Color.YELLOW);
g.fillRect(mod_i, mod_j, 16, 16);
break;
case 3:
g.setColor(Color.WHITE);
g.fillRect(mod_i, mod_j, 16, 16);
break;
case 4:
g.setColor(Color.GREEN);
g.fillRect(mod_i, mod_j, 16, 16);
break;

}

}

}

}

private void createTilemap() {

tilemap = new int[50][30];

rows = tilemap.length;
columns = tilemap[49].length;


Random r = new Random();

for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
tilemap[i][j] = r.nextInt(5);
}
}
}

}

Now press Play! You should see a beautiful painting like this:
Picture

And with that, our mini-break from 2D game development (Days 9 and 10) is over, and so concludes Unit 2! 

In the next lessons, we will be applying these techniques to create our levels, and eventually introduce collision detection.

I hope that this lesson made sense to everyone! I know that everyone is at different levels of Java experience and understanding, and I try to find a good balance as to not bore people and to not leave people behind. I might assume that you remember something from a previous lesson, but you might have forgotten it already. So let me know if you need anything better explained. I'd be glad to do that for you.

And thank you to everyone who liked Kilobolt Studios on Facebook! We have surpassed 1,000 likes and it is all because of your help.

In the next few months, Kilobolt will expand and offer more tutorials, better games, and a super-cool website. If you would like to help us, please consider donating!

Go to Day 9: 2-D Arrays
Continue to Unit 3
47 Comments
John G.
11/16/2012 01:47:04 am

Really Helpful Tutorial. Thank you and continue the awesome work! cheers!!

Reply
Abhinay
11/16/2012 01:31:03 pm

So within the inner for loop, we add the following in BOLD:

for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns;
int mod_i = 16*i;
int mod_j = 16*j;
}
}

yu missed a "j++" here..mi8 consider to edit it..

btw..nyc tutorial ;)

Reply
Reece
11/20/2012 04:36:04 pm

Thank you very much.
I'm really keen to see the next tutorial!

Reply
Theis81 link
11/21/2012 12:05:11 am

Thanks again James, great tutorials!

Reply
RZ
12/26/2012 05:56:03 am

Hey James. You are natural born teacher, thank you for your work. 8). A little suggestion to code - put color defining code to separate method - then code looks a little bit clear and easy to understand.

public void paint(Graphics g) {
for (int i = 0; i < myArray.length; i++) {
for (int j = 0; j < myArray[0].length; j++) {
int mod_i = 16*i;
int mod_j = 16*j;

g.setColor(this.getColor(myArray[i][j]));
g.fillRect(mod_i, mod_j, 16, 16);
}
}

}

private Color getColor(int number) {
switch (number) {
case 0:
return Color.RED;
case 1:
return Color.YELLOW;
case 2:
return Color.WHITE;
case 3:
return Color.BLUE;
case 4:
return Color.GREEN;
}

return Color.BLACK;
}

Reply
Chris Vetrano
1/18/2013 05:08:07 pm

Very nice!

Reply
LukeF
1/20/2013 04:17:12 am

Hi, really enjoying these so far, and I am looking forward to the next Unit, and also the upcoming App series!

My question is this; Suppose we want an image to rotate as well as be translated, such as a car going around a corner. How can this be achieved?

I should point out my skill level is novice. I have used MATLAB for years so understand loops etc etc, but this is my first real programming.

Reply
James C.
1/20/2013 08:35:00 am

The easiest way would be to use multiple images for rotation, as rotation is not supported in the framework. However, there are methods available. Look at some suggested answers on stackoverflow:

http://stackoverflow.com/questions/2687926/how-can-i-rotate-an-image-using-java-swing-and-then-set-its-origin-to-0-0

Reply
LukeF
1/20/2013 11:28:31 am

Thanks for that James. Also a quick question, when we are updating the location of our robot we simply add the speed which we defined. Should we not be multiplying this by a deta time? Such as the time since the last run?

LukeF
1/20/2013 11:28:40 am

Thanks for that James. Also a quick question, when we are updating the location of our robot we simply add the speed which we defined. Should we not be multiplying this by a delta time? Such as the time since the last run?

Noah
8/19/2014 09:25:58 am

delta time is constant because we are updating the frame at a "constant" rate. it is not "really" constant because the game loop does not always update at the same time. But it is constant enough for us humans.

Tobias P.
1/25/2013 05:55:51 am

@ James C.

I really like your tutorials. I didn't know java at all (only some basic C) and I find it really informative and easy to follow. I like that you do not explain every detail and encourage the reader to examine the code themselves. That's how you learn the most in my opinion.

@ LukeF
I thing generally you're right, but since we use a fixed frame rate (a new frame every 17ms) the delta time is always the same, therefore it doesn't matter.

Reply
daniel
2/10/2013 04:46:55 am

Nice job! ...Should be renderer and not renderer2 (fig2-37)

Reply
Kevin link
2/19/2013 10:45:22 pm

Great tutorials! Anyone can learn from them... And they are fun and addicting, I can't wait to get to each new lesson so I need to remind myself to slow down and absorb.
QUESTION: I am going through your tutorial and making it my own by changing the background, characters and I even have my character shooting fireballs! Anyway, I've cobbled together some artwork I found online from Google images and edited using PAINT.NET for the background but would like to know if there are other free sources of graphics game developers use... Or, free graphics editors with an easy learning curve...?

Reply
TheUndesigner link
5/5/2013 03:09:32 pm

I use Graphics Gale Free for my sprite editing; it's a dedicated pixel editor with more functions than Paint without being overwhelming, and it can save in many formats. The paid version can even save .gifs, including animated ones, but that's not really necessary for game work. Just google 'Graphics Gale' and you'll find it.

Reply
Tomer
3/23/2013 01:55:39 am

Amazing, Thanks a lot!
I have been completed Unit 1 + 2 and you couldn't explain it better!
Looking forward to implement it in Androaid environment.

Reply
Cheesemonkey
4/30/2013 11:24:39 am

I think I understand the array stuff correctly, but I want to be sure.
for
rows = tilemap.length;
columns = tilemap[1].length;

is there any particular reason for using [1] instead of any other number 0-49?

Reply
James
4/30/2013 11:25:48 am

They would all be same.

Reply
Cheesemonkey
4/30/2013 11:31:20 am

So then Tilemap.length refers to the size of the tilemap array of 50, and tilemap[*].length would refer to the size of the array of an element of the tilemap array?

That sounds confusing, i'm sorry!

James
4/30/2013 12:13:39 pm

That's exactly right. :)

Cheesemonkey
5/2/2013 08:59:07 am

Thanks! I bought the app, thanks for your time and effort put in this. It's quite helpful. If I make any good money making games, i'll make sure to donate

Javier
6/17/2013 11:31:24 am

Wow Cheesemonkey I was confused with the same thing. Good thing that I read your question and now also understand it.

Reply
Bill Oddie
6/26/2013 02:23:47 am

Yeah me too Cheesemonkey!

Thanks for this James, it is so so good!

Reply
Danny
7/3/2013 06:51:44 am

I'm a little confused why this Renderer class works similar to the Tilemap class but the rows and columns are in opposite places i.e

Tilemap
int[][] tilemap = new int[30][50];

Renderer
tilemap = new int[50][30];

yet provide the same outcome, i simply modified the column and row for Renderer so it fits correct columns of 50 and rows of 30

Reply
kreek
7/14/2013 08:13:45 am

I am confused about this as well. I've googled it and haven't came up with a solution. To my understanding int[30][50] creates 30 rows which each having 50 columns.. am I wrong or not understanding something.

Reply
NeoI
11/9/2013 12:58:38 am

me too. i don't get it...

Reply
Mario
8/26/2014 08:53:45 am

Hi,
I was confused about the order of rows and columns too, but after a few hours trying to understand it, I think I figured it out. The variables "rows" and "columns" are just made up names created by the programmer. They are not Java keywords.

Therefore, if we swap the items [30] [50] of the array in the tilemap variable declaration (inside createTilemap method) and then swap the "rows" and "columns" variables declarations:

columns = tilemap.length;
rows = tilemap[49].length;

the number of rows and columns in the applet will be consistent.

Another way of keeping the consistency of the number of rows and columns in the applet with the variables declared is to swap the items of the array:
tilemap = new int[30][50];,

delcare "rows" and "columns" variables:
rows = tilemap.length;
columns = tilemap[29].length;

and swap the order of "mod_i" and "mod_j" coordinates to paint the rectangle in the switch statement inside the paint method:
g.fillRect(mod_j, mod_i, 16, 16)

Murtuza link
7/12/2013 11:19:54 pm

I have 20 warnings in the Problems tab.. What to do? The game runs fine so far.. but I did'nt understand.. Why did we create Renderer class in some other project and not in our own project? are we not gona use it?
Also in your final code.. the class name is Renderer2 is that something different>

Reply
jay link
7/23/2013 11:11:57 pm

Tried donating using my paypal... but got a response that Paypal does not currently support donation from my country... anyway great tutorial.

Reply
Gib
7/24/2013 09:22:12 pm

Hi,

Loving the tutorials however in the inner loop for the random generator you need to add j++.

Currently it is like

for(int i = 0; i < rows; i++){
for(int j = 0; j < columns;){
tilemap[i][j] = r.nextInt(5);

but should be like

for(int i = 0; i < rows; i++){
for(int j = 0; j < columns; j++){
tilemap[i][j] = r.nextInt(5);

Reply
Axel
8/22/2013 09:08:18 am

First I've to say great tutorials!

The switch-case will look nicer if you just put the g.fillRect() after the switch-case instead of adding it to every possible case.

Like this:
switch(tilemap[i][j]){
case 0:
g.setColor(Color.BLUE);
break;

case 1:
g.setColor(Color.GREEN);

break;

case 2:
g.setColor(Color.ORANGE);

break;

case 3:
g.setColor(Color.RED);

break;

case 4:
g.setColor(Color.WHITE);

break;
}
g.fillRect(mod_i, mod_j, 16, 16);

Reply
Lance
3/28/2014 10:43:13 am

The code would look even nicer if an array of Colors was used instead of ints. So tilemap would become an array of type Color[][] (instead of int[][]) and it would be initialized by randomly selecting a color from an array of available colors:

Color[] colors = new Color[]{Color.BLUE, Color.GREEN, Color.ORANGE, Color.RED, Color.WHITE};
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
tilemap[i][j] = colors[r.nextInt(5)];
}
}

Then, paint() would only need to be:

for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {

int mod_i = 16*i;
int mod_j = 16*j;

g.setColor(tilemap[i][j]);
g.fillRect(mod_i, mod_j, 16, 16);
}
}

No switch statement required! :)

Reply
neo
11/9/2013 12:59:48 am

Great Tutorials!
thank you very much!

Reply
neo
11/9/2013 12:59:57 am

Great Tutorials!
Thank you very much!

Reply
neo
11/9/2013 01:00:05 am

Great Tutorials!
Thank you very much!
/

Reply
Kiwee
12/29/2013 09:42:23 am

Hey man! Great tutorial as always. Only one thing, if you don't mind. On the table above where you showed the indexes of the 2D-array, you should use a[0][0] instead of a[1][1]. Otherwise, great job!

Reply
Sean
3/8/2014 01:33:58 pm

How can i turn this game into a standalone app?

Reply
vikas
3/31/2014 06:33:26 pm

can you again explain how it's work,i cant understand how the case work in this code...
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {

int mod_i = 16*i;
int mod_j = 16*j;

switch (tilemap[i][j]) {
case 0:
g.setColor(Color.RED);
g.fillRect(mod_i, mod_j, 16, 16);
break;
case 1:
g.setColor(Color.BLUE);
g.fillRect(mod_i, mod_j, 16, 16);
break;
case 2:
g.setColor(Color.YELLOW);
g.fillRect(mod_i, mod_j, 16, 16);
break;
case 3:
g.setColor(Color.WHITE);
g.fillRect(mod_i, mod_j, 16, 16);
break;
case 4:
g.setColor(Color.GREEN);
g.fillRect(mod_i, mod_j, 16, 16);
break;

}

Reply
Kevin
4/7/2014 12:43:51 pm

Imagine we have a 2D array tilemap[3][3] with random numbers filled within it from 0 to 4. So it can be something like,

tilemap[0][0] = 0 tilemap[0][1] = 3 tilemap[0][2] = 4

tilemap[1][0] = 2 tilemap[1][1] = 2 tilemap[1][2] = 0

tilemap[2][0] = 1 tilemap[2][1] = 4 tilemap[2][2] = 1

then the first time through the for loops called switch will check tilemap[0][0] and in this case it is 0, so it will check
case 0:
g.setColor(Color.RED);
g.fillRect(mod_i, mod_j, 16, 16);

this just paints a red box 16 by 16 at coordinates 0,0. Next time through the loop, switch will check tilemap[0][1], in this case it is 3, so it goes to case 3, and draws a WHITE box and so on and so forth. Hope that helps.

Reply
Jeff
7/12/2014 03:10:46 am

It looks like we never actually call paint(parameter), yet it is still running. How is this possible? I was expecting that I would have to type "paint(something)", otherwise it wouldn't be referenced/run.

Reply
Alexey
8/3/2014 03:26:46 pm

Hey James! Thanks for great tutorial, I have two short questions:
1. In class Robot.java, what's the point of initializing speedY by 1. It doesn't make sense for me, I've tried to change it to 0 and it didn't have any impact on game. Can you clarify this?
2. In class Animation.java in "update" method we used this loop "while (animTime > getFrame(currentFrame).endTime)". Why we didn't use "if" loop? Is it more clear?

Reply
Noah
8/19/2014 09:23:00 am

If is used for conditions. There is not looping behavior with if.

Reply
Noah
8/19/2014 08:51:08 am

James,

I have noticed that when I click the "x" in the upper left (I'm on mac) the frame in which the graphics are displayed disappears but the window is still open. I must click the close button twice for the application to close completely.

I was wondering if you have a quick fix for this? I have not looked through the Applet API, but I'm sure it is in there some were. Just wondering if I missed something.

Thanks for the tutorial, its pretty awesome.

Reply
Noah
8/21/2014 09:38:50 am

So one thing that I noticed. We are using Applets, which are normally embedded in something...like a web page. I forgot that James had mentioned this. I was wondering why I has to click the X button twice to fully close my application. Well I just successfully converted my Applet into a JFrame. That way I get the results I'm looking for. Thanks again for the tutorials James, Im now moving to unit 3

Reply
Marrek
10/17/2014 04:03:24 am

hi there,

who calls paint method?
why we didnt call start() method?
applet calls paint method anyway?

thx, have nice day

Reply
Hernan Izaguirre
4/17/2015 01:57:02 pm

i think the init (not start) method is called automatically,but i wonderer whos call the paint method ?

Reply
Derek
8/10/2015 09:58:19 am

Great series and I am really glad this is here! Helping me learn a ton! However there is a typo in your code under Lesson #2-29. You need to close the condition statement of the for loops with a parentheses instead of a semicolon.

Thanks again for the great java tutorial!

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.