removed more obsolete stuff
This commit is contained in:
parent
9c076f96ca
commit
f75088cb63
|
@ -1,87 +0,0 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<title>OpenAL Tutoral</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=windows-1252">
|
||||
<meta name="author" content="Brian Matzon">
|
||||
<meta name="description" content="Basic sound using OpenAL">
|
||||
</head>
|
||||
<body>
|
||||
<h1 align="center">Array Conversion<br>
|
||||
<font size="-1">(by Brian Matzon <<a href="mailto:brian@matzon.dk">brian@matzon.dk</a>>)</font><br>
|
||||
</h1>
|
||||
<b>1.0 About this document</b><br>
|
||||
This document describes the typical rules for converting arrays often used in
|
||||
C/C++ OpenAL (and indeed OpenGL too) code.<br>
|
||||
It is not bullet proof, but should handle most cases.<br>
|
||||
<br>
|
||||
<b>1.1 Array to ByteBuffer<br>
|
||||
</b>When using an array of some data type in C/C++
|
||||
you will typically convert that<br>
|
||||
to the corresponding ByteBuffer type. ie:<p
|
||||
style="border-style: solid; border-width: 1px; padding: 3px; background-color: rgb(255,255,204);"><tt>ALfloat
|
||||
floatv[3];</tt></p>
|
||||
<p>becomes</p>
|
||||
<p
|
||||
style="border-style: solid; border-width: 1px; padding: 3px; background-color: rgb(255,255,204);"><tt>
|
||||
FloatBuffer floatv = createFloatBuffer(3);</tt></p>
|
||||
<p>In this example, <tt>createFloatBuffer</tt>
|
||||
is this utility method:</p>
|
||||
<p
|
||||
style="border-style: solid; border-width: 1px; padding: 3px; background-color: rgb(255,255,204);"><tt>
|
||||
public FloatBuffer createFloatBuffer(int size) {<br>
|
||||
|
||||
//allocate bytebuffer, using 4 bytes per float<br>
|
||||
|
||||
ByteBuffer temp = ByteBuffer.allocateDirect(4*size);<br>
|
||||
|
||||
temp.order(ByteOrder.nativeOrder());<br>
|
||||
|
||||
<br>
|
||||
|
||||
return temp.asFloatBuffer();<br>
|
||||
}</tt></p>
|
||||
<p>
|
||||
<b>1.2 Examples</b><br>
|
||||
Using the above FloatBuffer, you would typically use it like this (examples
|
||||
taken from altest.c/ALTest.java):</p>
|
||||
<p>
|
||||
<b>1.2.1 Example 1</b></p>
|
||||
<p
|
||||
style="border-style: solid; border-width: 1px; padding: 3px; background-color: rgb(255,255,204);"><tt>
|
||||
alGetListenerfv(AL_POSITION, floatv);</tt></p>
|
||||
<p>
|
||||
becomes</p>
|
||||
<p
|
||||
style="border-style: solid; border-width: 1px; padding: 3px; background-color: rgb(255,255,204);"><tt>
|
||||
al.getListenerfv(AL.POSITION, Sys.getDirectBufferAddress(floatv));</tt></p>
|
||||
<p><b>1.2.2 Example 2</b></p>
|
||||
<p
|
||||
style="border-style: solid; border-width: 1px; padding: 3px; background-color: rgb(255,255,204);"><tt>
|
||||
if (floatv[0] != 100.0)) {</tt></p>
|
||||
<p>becomes:</p>
|
||||
<p
|
||||
style="border-style: solid; border-width: 1px; padding: 3px; background-color: rgb(255,255,204);"><tt>
|
||||
if (floatv.get(0) != 100.0f) {</tt></p>
|
||||
<p><b>1.2.3 Example 3</b></p>
|
||||
<p
|
||||
style="border-style: solid; border-width: 1px; padding: 3px; background-color: rgb(255,255,204);"><tt>
|
||||
alGetListener3f(AL_POSITION, &floatv[0], <br>
|
||||
|
||||
&floatv[1], <br>
|
||||
|
||||
&floatv[2]);</tt></p>
|
||||
<p>becomes</p>
|
||||
<p
|
||||
style="border-style: solid; border-width: 1px; padding: 3px; background-color: rgb(255,255,204);"><tt>
|
||||
al.getListener3f(AL.POSITION, Sys.getDirectBufferAddress(floatv), <br>
|
||||
|
||||
Sys.getDirectBufferAddress(floatv) + 4, <br>
|
||||
|
||||
Sys.getDirectBufferAddress(floatv) + 8);</tt></p>
|
||||
<p>the last case is a bit special, since we start of by getting the base
|
||||
address of the buffer, and then add the datatype size to the base address<br>
|
||||
to get the address of that specific index. This is just how it has to
|
||||
be in Java.</p>
|
||||
</body>
|
||||
</html>
|
|
@ -1,150 +0,0 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Language" content="en-gb">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
<meta name="GENERATOR" content="Microsoft FrontPage 4.0">
|
||||
<meta name="ProgId" content="FrontPage.Editor.Document">
|
||||
<title>Lightweight Java Game Library Tutorial Index</title>
|
||||
<style>
|
||||
<!--
|
||||
.document_title { text-align: Center; font-size: 24pt; letter-spacing: 4 }
|
||||
.code_snippet { font-family: Courier New }
|
||||
-->
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p class="document_title">Lightweight Java Game Library</p>
|
||||
<p class="document_title">Tutorial</p>
|
||||
<p align="center">Written by Caspian Rychlik-Prince</p>
|
||||
<p align="center">Please direct comments, errata, and flames to the author at <a href="mailto:cix_foo@users.sourceforge.net">cix_foo@users.sourceforge.net</a></p>
|
||||
<p align="center">Last modified <!--webbot bot="Timestamp" S-Type="EDITED"
|
||||
S-Format="%d %B %Y" startspan -->23 August 2002<!--webbot bot="Timestamp" endspan i-checksum="31308" -->
|
||||
</p>
|
||||
<p> </p>
|
||||
<p><a name="Introduction">1.0 Introduction</a></p>
|
||||
<p>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 <i>experienced
|
||||
</i>programmers and won't be explaining some obvious techniques.</p>
|
||||
<p>LWJGL is not meant to make writing games particularly <i>easy;</i> it is
|
||||
primarily an <i>enabling technology </i>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.</p>
|
||||
<p>2.0 Contents</p>
|
||||
<p> </p>
|
||||
<p>3.0 Aims & Design</p>
|
||||
<p>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 <i>bare minimum</i> of API functionality which will let a
|
||||
game programmer produce games in Java without having to write native code in
|
||||
order to get <i>performance </i>or <i>access some hardware feature not exposed
|
||||
by Java 2</i>. We settled on using two other open technologies as our major
|
||||
foundations, namely OpenGL and OpenAL for graphics and sound respectively.</p>
|
||||
<p>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 <i>no dependencies </i>on Sun's
|
||||
proprietry JRE binaries are present in the library.</p>
|
||||
<p>3.1 General API</p>
|
||||
<p>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 <i>most useful gaming functions:</i> 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 <i>hires timer</i>,
|
||||
which is so critical for animation timing. In addition we can also create a
|
||||
direct byte buffer at <i>any address in memory.</i></p>
|
||||
<p>The Evil Of Pointers And What It Means For Security</p>
|
||||
<p>Yes, we have exposed <i>pointers</i> 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 <i>buffer
|
||||
vuln.</i></p>
|
||||
<p>But <i>why?</i> 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.</p>
|
||||
<p>The implications for security are simple and final: your game can <b>no
|
||||
longer be considered secure</b> 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.</p>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<p>3.2 Graphics</p>
|
||||
<p>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 <i>all</i>
|
||||
of Nvidia's and ATI's proprietry extensions, and <i>all</i> the ARB extensions,
|
||||
and most of the EXT extensions, as well as numerous other miscellaneous ones.</p>
|
||||
<p>For Windows programmers, our primary target, the WGL extensions are present.</p>
|
||||
<p>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!</p>
|
||||
<p>3.3 Sounds</p>
|
||||
<p>Sound is based on the latest OpenAL1.0 specification. The LWJGL binary
|
||||
distribution includes the OpenAL libraries.</p>
|
||||
<p>3.4 Input</p>
|
||||
<p>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.</p>
|
||||
<p>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 <i>buffering</i> which is a more reliable way of detecting
|
||||
rapid changes of state which may occur rather more quickly than your framerate.</p>
|
||||
<p>3.5 Maths</p>
|
||||
<p>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.</p>
|
||||
<p>The Math class provides two <i>totally generic</i> 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 <i>highly optimised operation</i>
|
||||
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.</p>
|
||||
<p>In addition we provide implementations of common Vector and Matrix sizes
|
||||
similar to those provided by the javax.vecmath package, but ours are <i>open
|
||||
source</i> and available without downloading the whole of Java3D.</p>
|
||||
<p> </p>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,153 +0,0 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<title>OpenAL Tutoral</title>
|
||||
|
||||
<meta http-equiv="content-type"
|
||||
content="text/html; charset=windows-1252">
|
||||
<meta name="author" content="Brian Matzon">
|
||||
<meta name="description" content="Basic sound using OpenAL">
|
||||
</head>
|
||||
<body>
|
||||
<h1 align="center">OpenAL Tutorial<br>
|
||||
<font size="-1">(by Brian Matzon <<a href="mailto:brian@matzon.dk">brian@matzon.dk</a>>)</font><br>
|
||||
</h1>
|
||||
<b>1.0 OpenAL Basics</b><br>
|
||||
Before embarking on our little OpenAL adventure, some tools are needed.<br>
|
||||
<br>
|
||||
<b>1.0.1 Ingredients</b><br>
|
||||
Head on over to Creatives <a
|
||||
href="http://developer.creative.com/scripts/DC_D&H_Games-Downloads.asp?opt=2">site</a>
|
||||
and snag a copy of the OpenAL specification along with a copy of the Programmers
|
||||
reference.<br>
|
||||
If you haven't already done so, get a copy of the OpenAL runtime environment
|
||||
too (and install it).<br>
|
||||
<br>
|
||||
<b>1.1 OpenAL theory</b><br>
|
||||
Uhm... I'm not going to write this... In all honesty reading the specification
|
||||
would be a good thing before continuing - but it isn't required...<br>
|
||||
<br>
|
||||
<b>1.2 Basic setup</b><br>
|
||||
Lets start out by creating a skeleton class for some very basic sound. We'll
|
||||
start of by creating the required OpenAL object<br>
|
||||
<p
|
||||
style="border-style: solid; border-width: 1px; padding: 3px; background-color: rgb(255,255,204);"><tt>import
|
||||
org.lwjgl.openal.AL;<br>
|
||||
<br>
|
||||
public class PlayTest {<br>
|
||||
<br>
|
||||
/** OpenAL instance */<br>
|
||||
protected AL al;<br>
|
||||
<br>
|
||||
|
||||
/**<br>
|
||||
* Creates an instance of PlayTest<br>
|
||||
*/<br>
|
||||
public PlayTest() {<br>
|
||||
try {<br>
|
||||
</tt>
|
||||
<tt> al = new AL();<br>
|
||||
al.create();<br>
|
||||
} catch (Exception e) {<br>
|
||||
e.printStackTrace();<br>
|
||||
}</tt><tt><br>
|
||||
}<br>
|
||||
}</tt></p>
|
||||
<p><b>1.3 Buffer and Source creation</b><br>
|
||||
Now that we have created the AL instance, we need to create
|
||||
two things to actually get some sound. We need to create a buffer to hold
|
||||
sound data, and a source that is to play the sound data.<br>
|
||||
Lets start of by creating one source, and one buffer:</p>
|
||||
<p style="border-style: solid; border-width: 1px; padding: 3px; background-color: rgb(255,255,204);">
|
||||
<tt>
|
||||
//create one IntBuffer as buffer and one as source<br>
|
||||
//createIntBuffer is a utility method which allocates a direct ByteBuffer in native order<br>
|
||||
IntBuffer buffers = createIntBuffer(1);<br>
|
||||
IntBuffer sources = createIntBuffer(1);<br>
|
||||
<br>
|
||||
//generate buffers and sources<br>
|
||||
al.genBuffers(1, Sys.getDirectBufferAddress(buffers));<br>
|
||||
al.genSources(1, Sys.getDirectBufferAddress(sources));
|
||||
</tt>
|
||||
</p>
|
||||
<p>There, all set for actually loading some sound data into the buffer.<br>
|
||||
</p>
|
||||
<b>1.4 Loading sound data and setting up a buffer</b><br>
|
||||
Now that we have a buffer, we need to load some sound data into this buffer.
|
||||
This is done using the <tt>al.bufferData</tt> method. In our example we will
|
||||
"cheat" a bit, by using the <tt>WaveData</tt> class to load
|
||||
a wave file, and copy this into the buffer:<br>
|
||||
<p
|
||||
style="border-style: solid; border-width: 1px; padding: 3px; background-color: rgb(255,255,204);"><tt>
|
||||
//load wave data<br>
|
||||
WaveData wavefile = WaveData.create("mywavefile.wav");<br>
|
||||
<br>
|
||||
//copy to buffer<br>
|
||||
al.bufferData(buffers.get(0), wavefile.format, Sys.getDirectBufferAddress(wavefile.data), wavefile.data.capacity(), wavefile.samplerate);<br>
|
||||
<br>
|
||||
//unload file again<br>
|
||||
wavefile.dispose(); <br>
|
||||
</tt></p>
|
||||
Having loaded the data, we pass it to <tt>bufferData</tt>. Once the buffer
|
||||
has been filled with sound data, we unload it from the system using <tt>wavefile.dispose()</tt>.
|
||||
Don't worry about deleting it this soon - the sound data has been <b>copied</b>
|
||||
to the buffer.<br>
|
||||
<br>
|
||||
<b>1.5 Associating sources and buffers</b><br>
|
||||
To associate a source to a buffer we set the integer BUFFER attribute on
|
||||
the source, and assign it a value of the buffer to play:<br>
|
||||
<p
|
||||
style="border-style: solid; border-width: 1px; padding: 3px; background-color: rgb(255,255,204);"><tt>
|
||||
//set up source input<br>
|
||||
al.sourcei(sources.get(0), AL.BUFFER, buffers.get(0));</tt><tt><br>
|
||||
</tt></p>
|
||||
<b>1.6 Setting source properties</b><br>
|
||||
Having set up the source, it is time to set some attributes on the source
|
||||
- there are many that can be set, but in this example we only set the looping
|
||||
attribute to true by doing the following:<br>
|
||||
<p
|
||||
style="border-style: solid; border-width: 1px; padding: 3px; background-color: rgb(255,255,204);"><tt>
|
||||
//loop source<br>
|
||||
al.sourcei(sources.get(0), AL.LOOPING, AL.TRUE);</tt><tt><br>
|
||||
</tt></p>
|
||||
<b>1.7 Sound...<br>
|
||||
</b>There, ready to play the sound, do this using the <tt>sourcePlay </tt>method
|
||||
of the <tt>AL </tt>class. to stop and pause use <tt>sourcePause </tt>and
|
||||
<tt>sourceStop </tt>respectively, and supply the source to affect:<br>
|
||||
<p
|
||||
style="border-style: solid; border-width: 1px; padding: 3px; background-color: rgb(255,255,204);"><tt>
|
||||
//play source 0<br>
|
||||
al.sourcePlay(sources.get(0));<br>
|
||||
<br>
|
||||
//wait 5 secs<br>
|
||||
try {<br>
|
||||
System.out.println("Waiting 5 seconds for sound to complete");<br>
|
||||
Thread.sleep(5000);<br>
|
||||
} catch (InterruptedException inte) {<br>
|
||||
}<br>
|
||||
<br>
|
||||
//stop source 0<br>
|
||||
al.sourceStop(sources.get(0));</tt></p>
|
||||
<b>1.8 Cleaning up<br>
|
||||
</b>Having had loads of fun playing a sound (!), it is now time to do some
|
||||
house chores. We need to clean up what we have created, this amounts to:<br>
|
||||
- deleting source and buffer<br>
|
||||
- destroying AL<br>
|
||||
as is shown here:<b><br>
|
||||
</b>
|
||||
<p
|
||||
style="border-style: solid; border-width: 1px; padding: 3px; background-color: rgb(255,255,204);"><tt>
|
||||
//delete buffers and sources<br>
|
||||
al.deleteSources(1, Sys.getDirectBufferAddress(sources));<br>
|
||||
al.deleteBuffers(1, Sys.getDirectBufferAddress(buffers));<br>
|
||||
<br>
|
||||
//shutdown<br>
|
||||
al.destroy();<br>
|
||||
</tt>
|
||||
</p>
|
||||
There, all set. Now you should be able to play some basic sound!<br>
|
||||
This tutorial is rather short, and the above examples feature no error checking.
|
||||
For the complete source code, look at the classes in the <br>
|
||||
<a href="http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/java-game-lib/LWJGL/src/java/org/lwjgl/test/openal/" target="_blank"><tt>org.lwjgl.test.openal</tt></a> package.<br>
|
||||
</body>
|
||||
</html>
|
|
@ -1,275 +0,0 @@
|
|||
<html>
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Language" content="en-gb">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
<meta name="GENERATOR" content="Microsoft FrontPage 4.0">
|
||||
<meta name="ProgId" content="FrontPage.Editor.Document">
|
||||
<title>4</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<p>4.0 Skeleton Game Code</p>
|
||||
<p>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.</p>
|
||||
<p>4.1 Design Decisions</p>
|
||||
<p>4.1.1 Static Game Class</p>
|
||||
<p>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:</p>
|
||||
<p style="background-color: #FFFFCC; border-style: solid; border-width: 1; padding: 3"><code>public
|
||||
class Game {</code><code><br>
|
||||
<br>
|
||||
/** The single instance of the Game */<br>
|
||||
private final Game game = new Game();<br>
|
||||
<br>
|
||||
/** Don't allow construction from anywhere else */<br>
|
||||
private Game() {</code><code><br>
|
||||
}<br>
|
||||
</code><code><br>
|
||||
/** @return the game instance */<br>
|
||||
public static Game getGame() {<br>
|
||||
return game;</code><code><br>
|
||||
}<br>
|
||||
<br>
|
||||
/** Start a new game */<br>
|
||||
public void newGame() {<br>
|
||||
// etc<br>
|
||||
}<br>
|
||||
<br>
|
||||
/** The main game loop */<br>
|
||||
public void mainLoop() {<br>
|
||||
// etc<br>
|
||||
}<br>
|
||||
<br>
|
||||
// etc<br>
|
||||
}</code></p>
|
||||
<p>However this does result in code all over your application calling
|
||||
Game.getGame().someMethod() - and to what end? It might <i>look</i> 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:</p>
|
||||
<p style="background-color: #FFFFCC; border-style: solid; border-width: 1; padding: 3"><code>public
|
||||
class Game {</code><code><br>
|
||||
<br>
|
||||
/** Don't allow construction from anywhere else */<br>
|
||||
private Game() {</code><code><br>
|
||||
}<br>
|
||||
<br>
|
||||
/** Start a new game */<br>
|
||||
public static void newGame() {<br>
|
||||
// etc<br>
|
||||
}<br>
|
||||
<br>
|
||||
/** The main game loop */<br>
|
||||
public static void mainLoop() {<br>
|
||||
// etc<br>
|
||||
}<br>
|
||||
<br>
|
||||
// etc<br>
|
||||
}</code></p>
|
||||
<p>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.</p>
|
||||
<p>4.1.2 About Threads and Timeslicing</p>
|
||||
<p>There is a tendency for Java programs to be heavily multithreaded, simply
|
||||
because you <i>can</i> 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:</p>
|
||||
<p>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).</p>
|
||||
<p>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 <i>time
|
||||
slices</i> rather than <i>threads.</i> 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.</p>
|
||||
<p>Threads don't work like this. To put it bluntly, you can't <i>accurately</i>
|
||||
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 <i>collision
|
||||
detection, animation, sound playback</i> and <i>device input, </i>and the
|
||||
ultimate blow, <i>event handling.</i></p>
|
||||
<p>In the absence of threads to do this useful stuff, we have to resort to a <i>single-threaded
|
||||
model</i> 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.</p>
|
||||
<p>4.1.3 About Threads and Hardware</p>
|
||||
<p>Your machine's <i>hardware</i> is only ever in <i>one state at a time, </i>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 <i>incredibly
|
||||
complicated</i> and <i>runs like a dog!</i> So much for threads.</p>
|
||||
<p>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.</p>
|
||||
<p>It is for this reason that we use <i>polling </i>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.</p>
|
||||
<p>4.1.4 Object-orientation</p>
|
||||
<p>Object-orientation is <i>good</i> when it's done right. It makes your code
|
||||
much, much, easier to understand and work on. However you may be led astray by <i>parrots
|
||||
who repeat mantras.</i> There are some programmers that say <i>everything should
|
||||
be private</i> 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. <i>But wait! </i>Ask yourself why you're filling
|
||||
your classes up with getters and setters <i>when a dot would do the same job
|
||||
without any need to maintain it.</i></p>
|
||||
<p>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 <i>disposable code.</i> 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 <i>all along the project design goal was not reuse but to get the bloody
|
||||
thing finished on time and under budget!</i></p>
|
||||
<p>Your mileage may vary...</p>
|
||||
<p>4.2 Show Me The Money</p>
|
||||
<p>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 <i>rotating square!</i></p>
|
||||
<p style="background-color: #FFFFCC; border-style: solid; border-width: 1; padding: 3"><code>import
|
||||
org.lwjgl.*;<br>
|
||||
import org.lwjgl.opengl.*;<br>
|
||||
import org.lwjgl.input.*;<br>
|
||||
<br>
|
||||
<br>
|
||||
public
|
||||
final class Game {<br>
|
||||
static {<br>
|
||||
try {<br>
|
||||
DisplayMode[] modes = Display.getAvailableDisplayModes();<br>
|
||||
System.out.println("Available display modes:");<br>
|
||||
for (int i = 0; i < modes.length; i ++)<br>
|
||||
|
||||
System.out.println(modes[i]);<br>
|
||||
// For now let's just pick a mode we're certain to have<br>
|
||||
Display.create(new DisplayMode(640, 480, 16, 60), true);<br>
|
||||
System.out.println("Created display.");<br>
|
||||
} catch (Exception e) {<br>
|
||||
System.err.println("Failed to create display due to "+e);<br>
|
||||
System.exit(1);<br>
|
||||
}<br>
|
||||
}<br>
|
||||
<br>
|
||||
public static final GL gl = new GL(16, 0, 16, 8);<br>
|
||||
static {<br>
|
||||
try {<br>
|
||||
gl.create();<br>
|
||||
System.out.println("Created OpenGL.");<br>
|
||||
} catch (Exception e) {<br>
|
||||
System.err.println("Failed to create OpenGL due to "+e);<br>
|
||||
System.exit(1);<br>
|
||||
}<br>
|
||||
}<br>
|
||||
public static final GLU glu = new GLU(gl);<br>
|
||||
<br>
|
||||
/** Is the game finished? */<br>
|
||||
private static boolean finished;<br>
|
||||
<br>
|
||||
/** A rotating square! */<br>
|
||||
private static float angle;<br>
|
||||
<br>
|
||||
/**<br>
|
||||
* No construction allowed<br>
|
||||
*/<br>
|
||||
private Game() {<br>
|
||||
}<br>
|
||||
<br>
|
||||
public static void main(String[] arguments) {<br>
|
||||
try {<br>
|
||||
init();<br>
|
||||
while (!finished) {<br>
|
||||
|
||||
Keyboard.poll();<br>
|
||||
|
||||
mainLoop();<br>
|
||||
|
||||
render();<br>
|
||||
|
||||
gl.swapBuffers();<br>
|
||||
}</code> <code><br>
|
||||
} catch (Throwable t) {<br>
|
||||
|
||||
t.printStackTrace();<br>
|
||||
} finally {<br>
|
||||
cleanup();<br>
|
||||
}<br>
|
||||
}<br>
|
||||
<br>
|
||||
/**<br>
|
||||
* All calculations are done in here<br>
|
||||
*/<br>
|
||||
private static void mainLoop() {<br>
|
||||
angle += 1f;<br>
|
||||
if (angle > 360.0f)<br>
|
||||
angle = 0.0f;<br>
|
||||
<br>
|
||||
Keyboard.poll();
|
||||
<br>
|
||||
if (Keyboard.isKeyDown(Keyboard.KEY_ESCAPE))<br>
|
||||
finished = true;<br>
|
||||
}<br>
|
||||
<br>
|
||||
/**<br>
|
||||
* All rendering is done in here<br>
|
||||
*/<br>
|
||||
private static void render() {<br>
|
||||
gl.clear(GL.COLOR_BUFFER_BIT);<br>
|
||||
gl.pushMatrix();<br>
|
||||
gl.translatef(Display.getWidth() / 2, Display.getHeight() / 2, 0.0f);<br>
|
||||
gl.rotatef(angle, 0, 0, 1.0f);<br>
|
||||
gl.begin(GL.QUADS);<br>
|
||||
gl.vertex2i(-50, -50);<br>
|
||||
gl.vertex2i(50, -50);<br>
|
||||
gl.vertex2i(50, 50);<br>
|
||||
gl.vertex2i(-50, 50);<br>
|
||||
gl.end();<br>
|
||||
gl.popMatrix();<br>
|
||||
}<br>
|
||||
<br>
|
||||
/**<br>
|
||||
* Initialize<br>
|
||||
*/<br>
|
||||
private static void init() throws Exception {<br>
|
||||
Keyboard.create();<br>
|
||||
// Go into orthographic projection mode.<br>
|
||||
gl.matrixMode(GL.PROJECTION);<br>
|
||||
gl.loadIdentity();<br>
|
||||
glu.ortho2D(0, Display.getWidth(), 0, Display.getHeight());<br>
|
||||
gl.matrixMode(GL.MODELVIEW);<br>
|
||||
gl.loadIdentity();<br>
|
||||
gl.viewport(0, 0, Display.getWidth(), Display.getHeight());<br>
|
||||
// Fix the refresh rate to the display frequency.<br>
|
||||
gl.wglSwapIntervalEXT(1);<br>
|
||||
}<br>
|
||||
<br>
|
||||
/**<br>
|
||||
* Cleanup<br>
|
||||
*/<br>
|
||||
private static void cleanup() {<br>
|
||||
Keyboard.destroy();<br>
|
||||
</code> <code>gl.destroy();<br>
|
||||
Display.destroy();<br>
|
||||
}<br>
|
||||
}</code></p>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
Reference in New Issue