diff --git a/doc/tutorial/index.html b/doc/tutorial/index.html new file mode 100644 index 00000000..c6eeca8d --- /dev/null +++ b/doc/tutorial/index.html @@ -0,0 +1,157 @@ + + + + + + + +Lightweight Java Game Library Tutorial Index + + + + + +

Lightweight Java Game Library

+

Tutorial

+

Written by Caspian Rychlik-Prince

+

Please direct comments, errata, and flames to the author at cix_foo@users.sourceforge.net

+

Last modified 23 August 2002 +

+

 

+

1.0 Introduction

+

The Lightweight Java Game Library (LWJGL) is a solution aimed directly at +professional and amateur Java programmers alike to enable commercial quality +games to be written in Java. This tutorial is nonetheless aimed at experienced +programmers and won't be explaining some obvious techniques. Currently the +LWJGL supports only the Win32 platform, and JDK1.4. 

+

LWJGL is not meant to make writing games particularly easy; it is +primarily an enabling technology which allows developers to get at +resources that are simply otherwise unavailable or poorly implemented on the +existing Java platform. We anticipate that the LWJGL will, through evolution and +extension, become the foundation for more complete game libraries and "game +engines" as they have popularly become known, and hide some of the new +evils we have had to expose in the APIs.

+

2.0 Contents

+

 

+

3.0 Aims & Design

+

Because the LWJGL API is not meant to be a fully featured game engine it has +been ruthlessly pruned of all non-essential code. Its ultimate philosophy is +that it provides the bare minimum of API functionality which will let a +game programmer produce games in Java without having to write native code in +order to get performance or access some hardware feature not exposed +by Java 2. We settled on using two other open technologies as our major +foundations, namely OpenGL and OpenAL for graphics and sound respectively.

+

A sub-requirement of the LWJGL is that it be freed Java programmers from the +requirement to ship a whole JRE with their games. Currently the Sun licensing +terms dictate that J2SE be shipped in its entirety, even for the tiniest of +demos. As this could easily triple the size of a demo and discourage end users +with configuration issues we have made it a primary concern that games written +using LWJGL can be compiled into completely standalone native binary executables +by compilers such as GNU's GCJ and Excelsior's JET. We have done this by +implementing the library in such a way that no dependencies on Sun's +proprietry JRE binaries are present in the library.

+

The final aim of LWJGL is to make the library available for Win32 systems +above all others, for that is what most commercial programmers need.

+

Linuxians and Macophiles do not despair! There's nothing inherently +non-portable about the LWJGL - we just don't have the time and expertise to do +it yet. But it will happen.

+

3.1 General API

+

The general API gives us the foundations of game programming: we have a +Display class, for initialising the display and querying its available modes; we +have a Math class to provide us with some floating point maths functions (rather +than the double-precision ones provided by Java), and matrix batch operations; +and a Sys class, which gives us our most useful gaming functions: the +ability to get the address of a direct byte buffer so we can cache it on the +Java side of the JNI barrier, and hence access all those lovely hardware calls +we need for performance; and the ability to use the system's hires timer, +which is so critical for animation timing. In addition we can also create a +direct byte buffer at any address in memory.

+

The Evil Of Pointers And What It Means For Security

+

Yes, we have exposed pointers to Java programmers. Yes, you can write +to just about any bit of memory you so please, and cause untold havoc. You can +break things. You can bypass security constraints and exploit the dreaded buffer +vuln.

+

But why? Because without pointers, all those nice easy native API +calls would suddenly become complex and behave slightly differently to their C +counterparts, and require us to pass direct ByteBuffers to JNI for every call. +This requires that every single call which takes a pointer calls the JNI method +GetDirectBufferAddress every time, which is an unnecessary overhead.

+

The implications for security are simple and final: your game can no +longer be considered secure and part of the Java security model. This puts +it in exactly the same boat as any other application on the user's system. This +also means you will not be able to use it in applets or with Webstart without +getting your code signed and trusted. LWJGL itself will not be signed nor +trusted; you are expected to deliver it bundled in with every application you +ship and verify that your entire distribution is safe.

+

We feel that our target developer, the commercial game developer, should not +be concerned with this issue as the status quo is merely maintained from the old +ways of programming with any other language; and used wisely, your exposure to +pointers is unlikely to cause you any problems.

+

If you are concerned about security, or wish to write games which will run as +applets or from Webstart, or would rather have a full game library which takes +care of things for you, you don't want to use LWJGL at all - it's that simple! +What you need is Sun's Java Gaming Profile, or Java3D. If you feel a need to +argue, you're using LWJGL for the wrong reasons.

+

3.2 Graphics

+

Graphics is based on the latest OpenGL1.4, and all the extensions we could +implement that might be vaguely useful for games programmers. These include all +of Nvidia's and ATI's proprietry extensions, and all the ARB extensions, +and most of the EXT extensions, as well as numerous other miscellaneous ones.

+

For Windows programmers, our primary target, the WGL extensions are present.

+

All OpenGL functions that take pointers are passed ints. These pointers can +be obtained from direct ByteBuffers using the Sys.getDirectBufferAddress() +method. There are a very few native methods that return pointers as ints as +well. Be sure to read the caveat about using pointers in Java!

+

3.3 Sounds

+

Sound is based on the latest OpenAL1.0 specification, which comes with but +one extension, EAX, for interesting environmental effects. The LWJGL binary +distribution includes the OpenAL .dlls for Win32.

+

3.4 Input

+

Input can be a complicated topic. A user can have all sorts of strange fancy +force-feedback hardware installed on their systems, with scrolly knobs and +twistgrips and bristling with many buttons. However, the vast majority of gamers +have just a keyboard and a mouse; some of them have analogue joysticks too, and +some of them have a gamepad attached from some console or other. For our first +cut of the input library we've just kept it all rather simple, and decided that +there is but one of each of these devices, and that force feedback and multiple +potentiometers is a feature we may add another time.

+

So in the interests of keeping things simple, the four input classes - +Keyboard, Mouse, Gamepad and Joystick - are all static, and can all be polled +once per game loop iteration to determine where they've moved since you last +looked and what buttons are down at the time. The Gamepad and Keyboard may +additionally support buffering which is a more reliable way of detecting +rapid changes of state which may occur rather more quickly than your framerate.

+

3.5 Maths

+

Java's maths performance leaves much to be desired, particularly with respect +to bulk operations for 3D rendering engines. The main problem is that the +existing maths libraries use double precision when single precision is entirely +adequate for most realtime games programming; and that no clever +processor-specific optimisations can be done because the Hotspot compiler is +simply not supplied enough semantic context to understand that it could use some +special SIMD instruction to achieve the effect you desire in a fraction of the +cycles. Furthermore, all maths in Java is done in Java - and once you've +computed the results you usually have to subsequently copy them into a buffer to +pass to a native rendering method in OpenGL anyway.

+

The Math class provides two totally generic vector operators for unary +and binary vector operations performed on direct ByteBuffers containing packed +floating point vectors. The idea is to set up the source(s) of the operations +and then perform a single call to JNI to perform a highly optimised operation +on the whole lot in one go. The JNI code is specially optimised for the common +cases in 3d games programming to use processor-specific instructions and take +advantage, where feasible, of memory caching architecture. And the end result is +placed directly back in memory, ready to simply send as a pointer direct to +OpenGL or some other API.

+

In addition we provide implementations of common Vector and Matrix sizes +similar to those provided by the javax.vecmath package, but ours are open +source and available without downloading the whole of Java3D.

+

 

+ + + + diff --git a/doc/tutorial/skeleton_code.html b/doc/tutorial/skeleton_code.html new file mode 100644 index 00000000..8fe91caf --- /dev/null +++ b/doc/tutorial/skeleton_code.html @@ -0,0 +1,279 @@ + + + + + + + +4 + + + + +

4.0 Skeleton Game Code

+

So now you want to know how to just get stuck in and start writing games. +First you need to understand a couple of game programming design patterns and +why they'll make your life easier; and then you'll want to fill in the gaps in a +bit of skeleton code that we're going to discuss below.

+

4.1 Design Decisions

+

4.1.1 Static Game Class

+

How many instances of your game do you expect to be running in a JVM? One, +and only one. Given this, you may feel the urge to implement the singleton +pattern on your game, where your game class holds a single instance of itself +and a method to retrieve that instance, something like:

+

public +class Game {
+   
+    /** The single instance of the Game */
+    private final Game game = new Game();
+
+    /** Don't allow construction from anywhere else */
+    private Game() {

+    }
+

+    /** @return the game instance */
+    public static Game getGame() {
+        return game;

+    }
+
+    /** Start a new game */
+    public void newGame() {
+        // etc
+    }
+
+    /** The main game loop */
+    public void mainLoop() {
+        // etc
+    }
+
+    // etc
+}

+

However this does result in code all over your application calling +Game.getGame().someMethod() - and to what end? It might look like +object-oriented code but in reality you're just making things harder for +yourself. It's far more intuitive to write your class purely statically, like +this:

+

public +class Game {
+
+    /** Don't allow construction from anywhere else */
+    private Game() {

+    }
+
+    /** Start a new game */
+    public static void newGame() {
+        // etc
+    }
+
+    /** The main game loop */
+    public static void mainLoop() {
+        // etc
+    }
+
+    // etc
+}

+

Now you can just call your methods directly on the Game class. We're going to +use this pattern in the forthcoming skeleton game code.

+

4.1.2 About Threads and Timeslicing

+

There is a tendency for Java programs to be heavily multithreaded, simply +because you can in Java, whereas it is exceedingly difficult to do +properly in C++. However, you are likely to fall into a number of pitfalls in +game programming above and beyond those experienced in general multithreaded +programming:

+

Firstly, threads were designed a long time ago for a specific purpose, and +that purpose is keeping the processor busy whilst some slow I/O operation is +being carried out. How many slow I/O operations are you going to do in your +game? Apart from loading the graphics, sounds, and level data at the start - +none (unless you're doing networking but that's a whole other issue).

+

What does this mean for you? Well, in normal Java, programmers will tend to +set off a number of threads and expect them to more or less behave like time +slices rather than threads. Time slices work in a rather different +way to threads: each is executed, in turn, for a finite amount of time. You can +more or less guarantee that a slice will execute for its allotted time, and that +all slices will get a look-in.

+

Threads don't work like this. To put it bluntly, you can't accurately +predict when a thread is going to execute (or if it will ever be executed), or +even how long it will execute for. Therefore you should not be using threads to +do stuff that you expect to occur in a timely fashion. This includes collision +detection, animation, sound playback and device input, and the +ultimate blow, event handling.

+

In the absence of threads to do this useful stuff, we have to resort to a single-threaded +model where the game effectively runs in one, very busy, thread - the +"main loop". This is quite convenient, however, because it highlights +another problem with multithreaded game code that is not immediately obvious.

+

4.1.3 About Threads and Hardware

+

Your machine's hardware is only ever in one state at a time, unless +it is some kind of quantum computer. The device drivers for the hardware - +namely OpenGL and the like - keep track of this state in order to send the +correct hardware setup commands to the device. Can you imagine what might happen +if one thread is trying to do some drawing at the same time that another thread +is trying to do some drawing? Yes, you get a mess. You will discover that in +order to draw in a thread in OpenGL that you need to associate the current +rendering thread with the current state of the hardware (the +"context"). Suddenly you need to synchronize every method which can +alter the hardware state. Suddenly your application looks incredibly +complicated and runs like a dog! So much for threads.

+

So be aware of this next time you think it's a good idea to load your +textures in a background thread, and remember that only one thread - the main +thread - is allowed to execute any commands on your hardware devices.

+

It is for this reason that we use polling to read input devices as +well. Not to mention the fact that we can't guarantee that an event loop will be +executed every game frame, and so our input will be erratic.

+

4.1.4 Object-orientation

+

Object-orientation is good when it's done right. It makes your code +much, much, easier to understand and work on. However you may be led astray by parrots +who repeat mantras. There are some programmers that say everything should +be private and you should expose your instance variables with getters and +setters. The Hotspot virtual machine is even cleverly optimised to make this +operation almost entirely free. But wait! Ask yourself why you're filling +your classes up with getters and setters when a dot would do the same job +without any need to maintain it.

+

You're writing a game: the source code is probably only going to be used +once, or by a very few people. Most of your game code is so unique to your +actual game that it is disposable code. So save yourself some effort and +use a dot if you can. A critical mistake I have observed time and time again in +object-oriented projects is to get so bogged down trying to design a perfect +object-oriented model that the project takes absolutely ages to design. Before +you know it there are hundreds of getters and setters to maintain, and +interfaces all over the place to keep the reuse of your classes to a maximum - +when all along the project design goal was not reuse but to get the bloody +thing finished on time and under budget!

+

Your mileage may vary...

+

4.2 Show Me The Money

+

Ok, ok, without further ado, here is a skeleton listing which you can use to +write a fullscreen Java game using LWJGL. Because we're games programmers, we +don't want to do a Hello World as we'd probably rather shoot it. But before we +can make bullets we must pay homage to the rotating square!

+

public +final class Game {
+static {
+try {
+DisplayMode[] modes = Display.getAvailableDisplayModes();
+System.out.println("Available display modes:");
+for (int i = 0; i < modes.length; i ++)
+System.out.println(modes[i]);
+// For now let's just pick a mode we're certain to have
+Display.create(new DisplayMode(640, 480, 16, 60), true);
+System.out.println("Created display.");
+} catch (Exception e) {
+System.err.println("Failed to create display due to "+e);
+System.exit(1);
+}
+}
+public static final GL gl = new GL(16, 0, 16, 8);
+static {
+try {
+gl.create();
+System.out.println("Created OpenGL.");
+} catch (Exception e) {
+System.err.println("Failed to create OpenGL due to "+e);
+System.exit(1);
+}
+}
+/** A handy number formatter for use displaying FPS and the like */
+public static NumberFormat fmt = new DecimalFormat();
+static {
+fmt.setMaximumFractionDigits(1);
+fmt.setMinimumFractionDigits(1);
+fmt.setGroupingUsed(false);
+}
+/** Is the game finished? */
+private static boolean finished;
+/** A rotating square! */
+private static float angle;
+/**
+* No construction allowed
+*/
+private Game() {
+}
+public static void main(String[] arguments) {
+try {
+init();
+int frames = 0;
+float fps = 0.0f;
+Timer timer = new Timer();
+timer.reset();
+timer.resume();
+while (!finished) {
+AbsMouse.poll();
+Keyboard.poll();
+Keyboard.read();
+gl.clear(GL.COLOR_BUFFER_BIT);
+mainLoop();
+render();
+gl.swapBuffers();
+// Count the frames per second. Do with this what you will..
+frames++;
+float time = timer.getTime();
+if (time >= 1.0f) {
+fps = (int) (frames / time);
+timer.reset();
+frames = 0;
+}
+}
+} catch (Throwable t) {
+t.printStackTrace();
+} finally {
+cleanup();
+}
+}
+
+/**
+* All calculations are done in here
+*/
+private static void mainLoop() {
+angle += 1f;
+if (angle > 360.0f)
+angle = 0.0f;
+
+if (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE))
+finished = true;
+}
+
+/**
+* All rendering is done in here
+*/
+private static void render() {
+
+gl.pushMatrix();
+gl.translatef(Display.getWidth() / 2, Display.getHeight() / 2, 0.0f);
+gl.rotatef(angle, 0, 0, 1.0f);
+gl.begin(GL.QUADS);
+gl.vertex2i(-50, -50);
+gl.vertex2i(50, -50);
+gl.vertex2i(50, 50);
+gl.vertex2i(-50, 50);
+gl.end();
+gl.popMatrix();
+}
+/**
+* Initialize
+*/
+private static void init() throws Exception {
+Keyboard.create();
+Keyboard.enableBuffer();
+Mouse.create();
+// Go into orthographic projection mode.
+gl.matrixMode(GL.PROJECTION);
+gl.loadIdentity();
+gl.glu.ortho2D(0, Display.getWidth(), 0, Display.getHeight());
+gl.matrixMode(GL.MODELVIEW);
+gl.loadIdentity();
+gl.viewport(0, 0, Display.getWidth(), Display.getHeight());
+// Fix the refresh rate to the display frequency.
+gl.wglSwapIntervalEXT(1);
+}
+/**
+* Cleanup
+*/
+private static void cleanup() {
+Keyboard.destroy();
+Mouse.destroy();
+gl.destroy();
+Display.destroy();
+}
+}

+ + + +