Archive for category Java

Java Lesson 10: Finish Up the Game

Lesson 10 Source Files
Resource Files

This lesson adds in a robust light handler. lights.txt defines the light sources and map.lights.txt defines where those lights are positioned in the map. There are 7 values that define each light:

character,r,g,b,colored,global

Character is the single character that is used in the map file to indicate where the light is positioned. r,g and b are the color values from 0-255. colored is either 0 or 1. If a light is not colored then the color values are ignored. Otherwise the color value is applied to the pixel which is being lit up. If a light is global then walls are ignored when calculating the lighting of a pixel. When a light is not global the walls cast shadows. Although non-global lighting is allowed it is highly expensive and will cause a huge drop in the framerate.

light_handler.java does all the heavy lifting with the lighting effects. You can trade off shadow accuracy with rendering speed if you want.

1
2
3
4
5
6
7
8
	dx = (sx-tx)/(dist*16.0);
	dy = (sy-ty)/(dist*16.0);
	for(j=0;j<dist*16.0;j++)
	{
		if(map.wall_tile_s((int)tx, (int)ty)>-1) { visible=false; j=dist;}
		tx+=dx;
		ty+=dy;
	}

dist is the map location. If you multiply by 1 the render speed is the fastest but the light appears to wrap around walls a bit. If you multiply by 64 (which converts it to pixel location) then you get pixel level accuracy. However you’re now doing 64 times as many checks for the light hitting a wall which really slows things down. 16 gives a decent accuracy and allows for a reasonable framerate with 1 non-global light source. Your results will vary based on the system you’re running.

Notice that we’re tracing the light from the light source to the pixel we’re rendering. This allows us to use less accurate tracing to improve speed. If we went the other way we may start off inside the wall we’re checking for illumination. There’s probably ways to fix that but going from the light to the destination I think makes more sense anyway. If we hit a wall before we get the the destination we set visible to false and the light source is ignored. And that’s how shadows are created. You may notice that it looks very similar to the raycasting code that renders the level. It’s the same concept except that instead of shooting a ray from the camera we shoot it from a light.

Global lighting is very fast since the most expensive calculation is a squareroot. On modern processors the squareroot is calculated by the CPU which is very fast. Back when Wolfenstein 3D came out the sqrt function was the bane of game programming because it was not a native instruction on the CPU. Carmack came up with a C function to do the calculation very fast but that is still much slower than a modern CPU.

And that concludes our real time software rendering tutorials using Java.

No Comments

Java Lesson 9: Finish Up the Game

Lesson 9 Source Files
Resource Files

We now have constants. Java doesn’t have global constants like languages like C. Instead you have to define a static class with static members. You can then reference those members without having to instantiate the static class. This allows us to use constants to define state values rather than having to remember that the title screen is state 1.

Since we now have an easy way to define states some files have been renamed to indicate they define a state. wolf5k.java is now state_wolf5k.java, etc. You’ll also find that there is no longer a thread running the game. Instead we’re using Java’s messaging system and the main loop is no longer a clearly defined loop. In the paint and update methods which were previously unused we call the run() method which does one game loop iteration as well as halts the main applet thread for a millisecond to let the CPU breathe. Once that method completes, the repaint() applet method is called which puts an event into Java which will trigger the paint or update method to be called. And that’s how our game loop is called until the applet exits. Of all the ways to get a language to loop, that is by far the most convoluted. But, with Java it actually gains us a few FPS and things just work more smoothly.

I say paint or update because that’s just how Java works. So, both those functions need to call the main method to render our game. There’s no way to determine which will be called on a repaint() event.

Now that we have a font that we can print to the screen we can finally display a timer. In doing so I discovered that the timer was running way too fast. Turns out I was calculating the time it took to render a frame the wrong way which sped up time. That is now corrected so 1 second of game time is 1 second of real time. Previously, one second of real time was several seconds of game time. We also now have a bunny counter so we can see how many of them are bouncing around. You’ve been able to shoot the bunnies. Now you can see how many you’ve killed.

There are four states currently. There’s the title screen, the game, the win and the lose. If you get the bunny counter down to 0 you win. If the bunny counter gets up to 100 you lose. How does the bunny counter go up you may ask. Bunnies multiply after 20 plus a random amount of seconds. After a bunny multiplies it will take longer and longer to multiply again until it reaches a certain age where it can’t multiply any longer. If you don’t shoot any bunnies the game will be over in a couple minutes which is pretty reasonable. Each bunny spawns one additional bunny at a time.

This tutorial would have been up sooner except for one minor detail: it was an applet that didn’t run in a browser. The reason it would not run in a browser was because it was loading resources using FILEs rather than URLs. That’s been fixed so now you can use the APPLET tag in an HTML document to load up Bunnies.

And so now you have a complete game in Java. In Lesson 10 my goal is to improve the rendering so things look better. Right now we have no lighting at all. We’re just plotting pixels with no consideration for any light source.

The tutorials end at Lesson 10 but I have more coming.

No Comments

Java Lesson 8: Titles and Text

Lesson 8 Source Files
Resource Files

This is a very important lesson. We finally can have a title screen and render text. To accomplish this I’ve added a simple blitter that renders an entire texture to a rectangular area. I’ve also reorganized the code to clean some things up. The video buffer class now handles all the pixel plotting and blitting functions.

Along with title screens comes the notion of “states.” Each state has a loading process and an execution process. When the state changes all the resources for that state are loaded in. While the state continues the same we just process the logic for that state. Garbage collection takes care of clearing out the unneeded resources. The trick is keeping track of what states are what number and which state can go to what other states and how. To get from the title screen to the game hit enter. To get back to the title screen press escape. To halt the program, press esc from the title screen.

To simplify some things we really should be using constants. Java doesn’t have constants. We’ll take care of how to get around that in Lesson 9.

The major advance that this lesson brings is being able to write text to the screen. The font_gen class is currently quite simple. It finds all the fonts that the current system supports and then generates a bitmap containing all the printable characters rendered at 36 pixels in height using the color red and the font “Ariel.” Needless to say, in Lesson 9 we’ll be abstracting this class a little more so we can adjust the font properties at load time and maybe add in the ability to have more than one font. The really nice thing about letting the language handle rendering a bitmap font rather than doing it yourself with a paint program is that we can let the language tell us the location and size of each character. You don’t need to go one letter at a time and tell the program how large each character is.

For no particular reason the generated font map is saved to a PNG file so you can see what it looks like. Keep in mind that this font generator will only work with Java. Other languages have other ways of doing this task. I’ve personally done it with C++ using the Windows API.

Our video_buffer class now has a “Print” function which takes the font class, the text and the height (in pixels) of the text. It then takes care of everything else. We’ll be improving that later as well.

Believe it or not you now have all the basic tools you need to make a complete game. In lesson 9 we’ll finally turn this tutorial into a complete game. In Lesson 10 we’ll start to push the limits with some fancy rendering.

No Comments

Java Lesson 7: Map Files and Beach Balls

Lesson 7 Source Files
Resource Files

Prior to this lesson everything was just randomly placed. Now we’ve added a “levels” directory with numbered sub-directories for each level. In those directories are the files which define the graphics we’ll be using for walls and objects. We also define the level itself. A maximum of 256 graphics can be used for objects and 256 graphics can be used for wall, floors and ceilings. Each level can define different graphics to use so we’re not too restricted.

  • tile.txt - this file defines our wall, floor and ceiling textures as well as what character is used in the map file to reference them. The format is texture number, character code, texture, mask. The character codes must be a single character. The mask is optional.
  • obj.txt - this file defines our object textures, floor and ceiling textures. You only need to load the textures for the objects you’ll be using in the level but the texture number must always be the same. Those are hard coded in the object’s class file. The format is texture number, texture, mask. The mask is optional but objects should always have a mask.
  • obj.map.txt - this file defines our object character codes. There is a hash map in the object handler that associates an object type number with an object type string. This file associates a character code with the object type string. Currently we have “bunny,” “player,” and “tree.” When the map is loaded the player object is returned to the wolf5k class which needs to know what object is the player. There should only be one player character code per map. If there are multiple player codes in the map the last one will be used as the real player object. All others will do nothing. The format for this file is character code, object type string.
  • map.*.txt - We have four files which define the map; wall, floor, ceil and obj. The wall, floor and ceil files all use the tile.txt codes. The obj file uses the obj.map.txt codes. The wall file defines the size of the map. The dimensions of the map are determined by the size of the last row and the size of the total map string. Line returns are ignored. “.”’s indicate that the default texture or object is used which is usually no texture or object at all. Only the floor is set to have a default texture which is grass. You files should all have the same dimensions and every line should be the same length. If not, things won’t render correctly.

I’ve also fixed the hopping code. It’s much simpler now. Also I determined that the distance calculation was off by a multiple of 64 when using it to render sprites. 64 pixels is now 64 pixels at 64 pixels from the player and 32 pixels when 128 pixels from the player like it should be. The tree has been replaced by a bouncing beach ball.

One of the problems that shows up when doing time based movement is that things don’t always collide like they should because objects move too fast. I’ve improved the collision detection to track the movement from the previous location to the new location using time slices equal to 1/50th of the time difference from the previous frame. The collision detection is now essentially running at 50 times the frame rate of the game itself. Nothing is moving particularly fast in this game so it’s accurate enough.

This isn’t perfect. The real way to do it is to first figure out if the two object collide using simple alegbra and the line formula. Then you calculate the time it takes for both objects to get to that point (distance to the point divided by the render time) and if they both get there at the same time, there’s a collision. That only checks for them being at the exact same point however. It’s a little more complex when you take into account a collision radius around the objects. We’ll stick to the simple way for now. Maybe in lesson 8 I’ll improve it. The current way does hurt the frame rate noticably.

We’re also only checking the x and y position. Bunnies can’t hop over your beach balls at this time.

The main thing in the next lesson will be to finally render title screens and status bars.

No Comments

Java Lesson 6: Optimizations and Ceilings

Lesson 6 Source Files
Resource Files

In the last lesson I added in floors but didn’t want to assume anything about ceilings. I’m actually glad I waited because adding ceilings and floors caused the framerate to plummet. This lesson I was then able to use to get my framerate back up and finish up floors and ceilings.

The map now has three arrays of equal size. One for the walls, one for the ceiling and one for the floor. This allows us to texture anything however we want. I also added in transparency to the floors so players can see through the floor to the background texture. I also removed the default fence around the parimeter. That stopped working when I optimized the raycaster. Now that I think about it, I’ll probably remove the transparent walls. They don’t really work except on the outer edge anyway.

The original Wolf5K based everything on 64 pixels which resulted in a lot of excess math once we stopped working in pixels and started working in percentages. I’ve removed all that now and the raycaster works in map coordinates instead of pixels. All the multiply and divide by 64s are now gone. Before the optimization 320×240 ran at about 15fps. Now it runs at about 21fps. That’s pretty much the same speed it was running when only walls/objects were being rendered.

One of the things I’ve been wanting to do since I deobfuscated the Wolf5K javascript code was rename all the variables back to something meaningful. I’ve done that now with most of the raycasting code. It makes a lot more sense now and I was able to find some minor optimizations.

For the most part our raycaster is complete. We have walls, floors, and ceilings which is more than most raycasters out there. The only component missing is lighting. We’ll also be enhancing our 2D graphics capabilities. All games need title screens and menus as well as status bars. There’s no shortage of things that go into a rendering engine.

No Comments

Java Lesson 5: Noisy Bunnies and Floors

Lesson 5 Source Files
Resource Files

I have been banging my head against the wall trying to render floors. There’s a standard equation floating around the internet that I had previously tried using but for some reason it would just not work with the Wolf5K raycaster. The solid bright green floor in previous tutorials was the most I could get the work. If you look closely the green gets darker towards the edges of the map.

1
2
3
4
5
6
7
8
9
10
11
12
13
	for(y=l;y<s_y;y++)
	{
		currentDist = (double)s_y / (2.0 * (double)y - (double)s_y) * 64.0;
		weight = currentDist / f;
 
		currentFloorX = weight * o + (1.0 - weight) * player.x; 
		currentFloorY = weight * p + (1.0 - weight) * player.y; 
 
		floorTexX = currentFloorX - ((int)currentFloorX/64)*64.0; 
		floorTexY = currentFloorY - ((int)currentFloorY/64)*64.0; 
 
		PlotPixel(v,y,textures.get_pixel(13,floorTexX/64.0, floorTexY/64.0));
	}

Above is the floor rendering code now in use. There were two key mistakes I was making in my previous attempts. The big giant one was currentDist. The scale was wrong. currentDist was calculating the map distance. Not the pixel distance. f is the pixel distance from the camera to the wall slice that was hit. All I had to do was multiply by 64.0 and I had floors that moved the same speed as the player. One of the things I like to see in tutorials is what happens when you do it wrong. In this case, if currentDist is calculating based on a different scale than f, the floor moves way out of sync with the camera. It moves in the correct direction, it just moves significantly faster.

Now that the scale was right the second problem was the texture location. The original code was using a simple multiply and mod. That works great when you’re working with integers. You may notice the equation I use is the same format as the equation used in the wall rendering. That equation is a quick way to mod a double. It leaves you with a value between 0 and 64. We then divide by 64 to get a value between 0 and 1 which is what we use to stretch textures.

I highly recommend messing around with the floor equations because they are a big pain and I think it would help you to see what goes wrong when you start changing things around. Once I got it right I saw pretty clearly what I was doing wrong to get the various effects I was running into earlier.

In the next tutorial we’ll show how to render the ceiling and define a floor and ceiling map so we don’t have to use a single repeating texture. The grass texture is a large grass texture I found at Gamasutra.com.

The other big addition is the ability to play sound effects. Currently no fancy formats are being used, just simple wav files. You can load all the sound effects at once at the beginning and then play any number of sounds at the same time just by calling the play method with the file name. No need to keep track of numbers. The bunny object now has a sound_effect string. When that is set, the sound handler attempts to play the referenced sound effect. The sound is started every frame so make sure that sound_effect is set to null the frame after the sound effect is set. In the bunny code you will see that the sound effect is defaulted to null (not an empty string) and only set to the file name when the bunny first initializes the hop.

I’m using some basic math to adjust the volume of the sound effect based on distance from the camera. The balance is also supposed to be adjusted but that seems to not actually work. If it did, the sound effect would play mostly out of the left speaker when the bunny is to the left of you and the right speaker if the bunny is to the right of you.

All in all this a good start with sound and floors. The next tutorial will finish up the floor and ceiling rendering and maybe have some more advanced sound playing. I’d like to use Ogg but I’m not sure what is available to do that. The way the sound effect code is organized it should be rather easy to modify the sound effect loading code without affecting anything else.

No Comments

Java Lesson 4: Hopping Bunnies and Screenshots

Lesson 4 Source Files
Resource Files

The resources zip is just the graphics for the project. All the graphics go in the “resources” sub folder of your java project space. The plan is to make the resource zip work for as many lessons as possible. This means that the graphics that are in there are pretty much the final versions. More files will be added as we progress but none will be removed. They may be altered but they won’t be removed.

One of the first things I did for this lesson was get rid of all the player specific variables from the main Wolk5K class. The player is now an object just like everything else. Bunnies now collide with each other and change direction as well. When a collision occurs the object type number is passed to the collision handling method of the object. This allows the objects to react differently to colliding with different objects. When a bunny collides with another bunny they both change direction. Pressing the space bar will launch a bullet (currently represented by a tree) in the direction you were facing. If a bullet collides with a bunny the bunny disappears. Bunnies will also change direction if they collide with the player.

One of the things the original Wolfenstein game didn’t have was objects that moved vertically. Everything just slid across the floor. In our game the bunnies actually hop. When I first added the hopping ability I just subtracted the height of the bunny from the starting height value that the raycaster calculates. Then I noticed that the bunnies far away seemed to be jumping rediculously high. I had forgotten to multiply the height they were at by the inverse of the distance from the player.

Another new addition to the game is rotating sprites. This seems difficult until you figure out what the raycaster is already calculating.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
	public int get_texture(double p_angle)
	{
		double t = Math.PI*2-this.facing + Math.PI/2.0 + p_angle + this.angle;
 
		while(t<0)	t+=2.0*Math.PI;
		while(t>2.0*Math.PI) t-=2.0*Math.PI;
		t*=180.0/Math.PI;
		sel_texture = 0;
		if(t>0+22.5) sel_texture = 1;
		if(t>45+22.5) sel_texture = 2;
		if(t>90+22.5) sel_texture = 3;
		if(t>135+22.5) sel_texture = 4;
		if(t>180+22.5) sel_texture = 5;
		if(t>225+22.5) sel_texture = 6;
		if(t>270+22.5) sel_texture = 7;
		if(t>315+22.5) sel_texture = 0;
 
		return textures[sel_texture];
	}

This code is quite inefficient but it makes it very easy to see what we’re doing. We subtract the direction that the object is facing from 360 degree because we’re looking at the object. We’re not going from the object’s perspective. We thing add 90 degrees so 0 degrees is north instead of east. p_angle is the angle that the player object is facing. We add that so 0 degrees is facing the same direction as the player. And finally we add the actual angle between the player and the object. This is calculated when rendering the objects.

Next we ensure that the angle of the texture is between 0 and 360 degrees. Adding and subtracting 2*PI is the slow way but it works. When programming make it work then make it fast. And finally we pick the texture we need based on the perspective corrected rotation of the object. Texture 0 is facing east and go counter clockwise from there in 45 degree increments.

No Comments

Java Lesson 3: Better Graphics and Organization

Lesson 3 Files

The code is now being broken into components. There is a generic object class which is extended by various entity classes. Code is being stored in various directories to keep things organized. Java has built in functions for loading JPG images which will save you disk space but the compression artifacts make it difficult to mask things. Also, JPG images take significantly longer to load than bitmaps. Since it’s all the same size in memory there’s no reason to use JPG images. It’s just diskspace and as long as we keep it below 650MB (the size of a CD) we’re fine.

One thing I would really like to add but havn’t figured out how to do yet, is floors. Most raycasters out there just do walls. Those that do render floors do it in a way which doesn’t seem to be compatible with the rest of my raycaster. For now I’m going to ignore rendering issues and continue building the game it self. Graphics can always be improved later.

One thing you may notice in the code that’s different from Wolf5K is that the mask is applied once. Rather than comparing color values every frame for every object being rendered, the code now just sets all the non displayed color values to a set value which is ignored by the plot pixel function. Pixels can only use 24 bits so I set the 25th bit to 1 when the pixel isn’t to be rendered. This saves us a lot of memory and speeds up rendering. Those extra 8 bits are reserved for the alpha channel which isn’t used.

In the next lesson I’m going to add basic physics to the bunny object so that it can hop around rather than just slide around the level bouncing off walls.

I’d also like to get sound effects going. OGG Vorbis is a free MP3 alternative. MP3 requires you to pay a license fee if you intend to use it in a commercial project. OGG doesn’t. Many commerical games use OGG and my experience with it in C++ was good. We’ll see how well Java does with it.

The controls are just the arrow keys which move you around. You can’t shoot or be shot.

No Comments

Java Lesson 2: Basic Implementation of Wolf5k

Lesson 2 Files

I’m not going to bother fully implementing or explaining Wolf5k in Java. It’s been done in Javascript and C++. The above code gets you going with basic raycasting and a ridiculous amount of objects moving around. The code is virtually identical to the C++ code although some slight modifications have been made to link it with the Java software rendering code. The resolution is hard coded so you can’t render at a higher resolution than 128×64.

Now that we’ve got the basics under way, it’s time to catch up with the C++ version.

The controls are just the arrow keys which move you around. You can’t shoot or be shot.

No Comments

Java Lesson 1: Initializing the screen and creating some static

Lesson 1 Files

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*
 * This code is subject to the following license
 * http://code.dawnofthegeeks.com/about/
 * 
 * This code may not be used in conjunction with the GPL or any similar license
 */
 
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
 
public final class lesson01 extends Applet implements Runnable
{
	//This is expected to be defined
	static final long serialVersionUID = 1;
 
	//we'll be running in a thread
	Thread gameThread;
	boolean running = true;
 
	//everything we need to define our buffers
	Graphics video;			//our main video screen
	int video_buffer[];  	//our video buffer
	int s_x,s_y;			//our screen resolution
	Image off_screen;		//gets our off screen to the video screen
	MemoryImageSource mis;	//video buffer manager
 
	//rendering statistics
	int frames;				//frame counter
	int fps;				//frames rendered in one second
	long timer;				//frame timer
 
	//generic temporariy variables
	int x,y;
 
	//initialize the video buffers and start the gameThread
	public void init()
	{
		//use the applet's height and width
		s_x = getSize().width;
		s_y = getSize().height;
 
		//initialize our stats
		frames = 0;
		fps = 0;
 
		//initialize our video buffers
		video_buffer = new int[s_x * s_y];
        mis=new MemoryImageSource(s_x,  s_y,  video_buffer,  0,  s_x);
        mis.setAnimated(true);
        off_screen=createImage(mis);
		video = getGraphics();
 
		//start our gameThread to get things moving
        gameThread= new Thread(this);
		gameThread.start();
	}
 
	//clean up our program when we close the applet
	public void destroy()
	{
		running = false;
		gameThread = null;
	}
 
	//NOT optional
    public void update(Graphics g) 
    {
    	/*
    	 * Although this function is empty it needs to be defined
    	 * Failing to define this function results in flicker
    	 */
	}
 
	//NOT optional
    public void paint(Graphics g) 
    {
    	/*
    	 * Although this function is empty it needs to be defined
    	 * Failing to define this function results in flicker
    	 */
	}
 
    //Our Main Loop
	public void run()
	{
		//set our timer to 1 second in the future
		timer = System.currentTimeMillis() + 1000;
 
		while (running)
		{
			try
			{
				if(System.currentTimeMillis() > timer)
				{//if 1 second has passed calculate the FPS and reset our values
					timer = System.currentTimeMillis() + 1000;
					fps = frames;
					frames = 0;
					getAppletContext().showStatus(fps+ " FPS");
				}
 
				//render the screen
				//0xFF000000 is the alpha value
				//leaving this off will prevent 
				//the pixels from showing up
				for(x = 0;x<s_x;x++)
					for(y = 0;y<s_y;y++)
						video_buffer[x+y*s_x] = 0xFF000000 + (int)(Math.random() * 256*256*256);
 
 
	            mis.newPixels(); //tell our handler that the pixels have changed
	            video.drawImage(off_screen,0,0,this); //update the visible screen
	            //NOTE: drawImage is supposed to return true on success or false otherwise
	            //It returns false when using MemoryImageSource even when it succeeds
 
	            frames++; //increment the frame counter
				gameThread.sleep(0); //let our CPU take a breath
			}
			catch(InterruptedException e)
			{
 
			}
		}
	}
}

The comments should be enough to get you going. This is a basic applet which initializes the video buffer and links it to our one dimensional integer array which will be plotting pixels to. Every pixel is then set to a random color and when we’re done, it’s displayed to the user. This script is plotting about 4 million pixels per second on a 1.7ghz system. Future tutorials will benchmark against this number. The closer we get to 4 million pixels per second we get, the more efficient we know our calculations are.

It’s important that you call the sleep method on the gameThread or your applet will hog all your system resources. There’s no need to set the number higher than 5. 0 may still cause some problems.

No Comments