mirror of
https://github.com/shadowfacts/lwjgl2-arm64.git
synced 2026-04-05 14:35:58 +00:00
Rough incomplete draft
This commit is contained in:
parent
918af28c2b
commit
ba2877a75e
2 changed files with 436 additions and 0 deletions
279
doc/tutorial/skeleton_code.html
Normal file
279
doc/tutorial/skeleton_code.html
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
<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>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>
|
||||
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>
|
||||
/** A handy number formatter for use displaying FPS and the like */<br>
|
||||
public static NumberFormat fmt = new DecimalFormat();<br>
|
||||
static {<br>
|
||||
fmt.setMaximumFractionDigits(1);<br>
|
||||
fmt.setMinimumFractionDigits(1);<br>
|
||||
fmt.setGroupingUsed(false);<br>
|
||||
}<br>
|
||||
/** Is the game finished? */<br>
|
||||
private static boolean finished;<br>
|
||||
/** A rotating square! */<br>
|
||||
private static float angle;<br>
|
||||
/**<br>
|
||||
* No construction allowed<br>
|
||||
*/<br>
|
||||
private Game() {<br>
|
||||
}<br>
|
||||
public static void main(String[] arguments) {<br>
|
||||
try {<br>
|
||||
init();<br>
|
||||
int frames = 0;<br>
|
||||
float fps = 0.0f;<br>
|
||||
Timer timer = new Timer();<br>
|
||||
timer.reset();<br>
|
||||
timer.resume();<br>
|
||||
while (!finished) {<br>
|
||||
AbsMouse.poll();<br>
|
||||
Keyboard.poll();<br>
|
||||
Keyboard.read();<br>
|
||||
gl.clear(GL.COLOR_BUFFER_BIT);<br>
|
||||
mainLoop();<br>
|
||||
render();<br>
|
||||
gl.swapBuffers();<br>
|
||||
// Count the frames per second. Do with this what you will..<br>
|
||||
frames++;<br>
|
||||
float time = timer.getTime();<br>
|
||||
if (time >= 1.0f) {<br>
|
||||
fps = (int) (frames / time);<br>
|
||||
timer.reset();<br>
|
||||
frames = 0;<br>
|
||||
}<br>
|
||||
}<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>
|
||||
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>
|
||||
<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>
|
||||
* Initialize<br>
|
||||
*/<br>
|
||||
private static void init() throws Exception {<br>
|
||||
Keyboard.create();<br>
|
||||
Keyboard.enableBuffer();<br>
|
||||
Mouse.create();<br>
|
||||
// Go into orthographic projection mode.<br>
|
||||
gl.matrixMode(GL.PROJECTION);<br>
|
||||
gl.loadIdentity();<br>
|
||||
gl.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>
|
||||
* Cleanup<br>
|
||||
*/<br>
|
||||
private static void cleanup() {<br>
|
||||
Keyboard.destroy();<br>
|
||||
Mouse.destroy();<br>
|
||||
gl.destroy();<br>
|
||||
Display.destroy();<br>
|
||||
}<br>
|
||||
}</code></p>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue