Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
epoxy
GitHub Repository: epoxy/proj11
Path: blob/master/SLICK_HOME/src/org/newdawn/slick/GameContainer.java
1456 views
1
package org.newdawn.slick;
2
3
import java.io.IOException;
4
import java.util.Properties;
5
6
import org.lwjgl.LWJGLException;
7
import org.lwjgl.Sys;
8
import org.lwjgl.input.Cursor;
9
import org.lwjgl.opengl.Display;
10
import org.lwjgl.opengl.Drawable;
11
import org.lwjgl.opengl.Pbuffer;
12
import org.lwjgl.opengl.PixelFormat;
13
import org.newdawn.slick.gui.GUIContext;
14
import org.newdawn.slick.openal.SoundStore;
15
import org.newdawn.slick.opengl.CursorLoader;
16
import org.newdawn.slick.opengl.ImageData;
17
import org.newdawn.slick.opengl.renderer.Renderer;
18
import org.newdawn.slick.opengl.renderer.SGL;
19
import org.newdawn.slick.util.Log;
20
import org.newdawn.slick.util.ResourceLoader;
21
22
/**
23
* A generic game container that handles the game loop, fps recording and
24
* managing the input system
25
*
26
* @author kevin
27
*/
28
public abstract class GameContainer implements GUIContext {
29
/** The renderer to use for all GL operations */
30
protected static SGL GL = Renderer.get();
31
/** The shared drawable if any */
32
protected static Drawable SHARED_DRAWABLE;
33
34
/** The time the last frame was rendered */
35
protected long lastFrame;
36
/** The last time the FPS recorded */
37
protected long lastFPS;
38
/** The last recorded FPS */
39
protected int recordedFPS;
40
/** The current count of FPS */
41
protected int fps;
42
/** True if we're currently running the game loop */
43
protected boolean running = true;
44
45
/** The width of the display */
46
protected int width;
47
/** The height of the display */
48
protected int height;
49
/** The game being managed */
50
protected Game game;
51
52
/** The default font to use in the graphics context */
53
private Font defaultFont;
54
/** The graphics context to be passed to the game */
55
private Graphics graphics;
56
57
/** The input system to pass to the game */
58
protected Input input;
59
/** The FPS we want to lock to */
60
protected int targetFPS = -1;
61
/** True if we should show the fps */
62
private boolean showFPS = true;
63
/** The minimum logic update interval */
64
protected long minimumLogicInterval = 1;
65
/** The stored delta */
66
protected long storedDelta;
67
/** The maximum logic update interval */
68
protected long maximumLogicInterval = 0;
69
/** The last game started */
70
protected Game lastGame;
71
/** True if we should clear the screen each frame */
72
protected boolean clearEachFrame = true;
73
74
/** True if the game is paused */
75
protected boolean paused;
76
/** True if we should force exit */
77
protected boolean forceExit = true;
78
/** True if vsync has been requested */
79
protected boolean vsync;
80
/** Smoothed deltas requested */
81
protected boolean smoothDeltas;
82
/** The number of samples we'll attempt through hardware */
83
protected int samples;
84
85
/** True if this context supports multisample */
86
protected boolean supportsMultiSample;
87
88
/** True if we should render when not focused */
89
protected boolean alwaysRender;
90
91
/**
92
* Create a new game container wrapping a given game
93
*
94
* @param game The game to be wrapped
95
*/
96
protected GameContainer(Game game) {
97
this.game = game;
98
lastFrame = getTime();
99
100
getBuildVersion();
101
Log.checkVerboseLogSetting();
102
}
103
104
/**
105
* Set the default font that will be intialised in the graphics held in this container
106
*
107
* @param font The font to use as default
108
*/
109
public void setDefaultFont(Font font) {
110
if (font != null) {
111
this.defaultFont = font;
112
} else {
113
Log.warn("Please provide a non null font");
114
}
115
}
116
117
/**
118
* Indicate whether we want to try to use fullscreen multisampling. This will
119
* give antialiasing across the whole scene using a hardware feature.
120
*
121
* @param samples The number of samples to attempt (2 is safe)
122
*/
123
public void setMultiSample(int samples) {
124
this.samples = samples;
125
}
126
127
/**
128
* Check if this hardware can support multi-sampling
129
*
130
* @return True if the hardware supports multi-sampling
131
*/
132
public boolean supportsMultiSample() {
133
return supportsMultiSample;
134
}
135
136
/**
137
* The number of samples we're attempting to performing using
138
* hardware multisampling
139
*
140
* @return The number of samples requested
141
*/
142
public int getSamples() {
143
return samples;
144
}
145
146
/**
147
* Indicate if we should force exitting the VM at the end
148
* of the game (default = true)
149
*
150
* @param forceExit True if we should force the VM exit
151
*/
152
public void setForceExit(boolean forceExit) {
153
this.forceExit = forceExit;
154
}
155
156
/**
157
* Indicate if we want to smooth deltas. This feature will report
158
* a delta based on the FPS not the time passed. This works well with
159
* vsync.
160
*
161
* @param smoothDeltas True if we should report smooth deltas
162
*/
163
public void setSmoothDeltas(boolean smoothDeltas) {
164
this.smoothDeltas = smoothDeltas;
165
}
166
167
/**
168
* Check if the display is in fullscreen mode
169
*
170
* @return True if the display is in fullscreen mode
171
*/
172
public boolean isFullscreen() {
173
return false;
174
}
175
176
/**
177
* Get the aspect ratio of the screen
178
*
179
* @return The aspect ratio of the display
180
*/
181
public float getAspectRatio() {
182
return getWidth() / getHeight();
183
}
184
185
/**
186
* Indicate whether we want to be in fullscreen mode. Note that the current
187
* display mode must be valid as a fullscreen mode for this to work
188
*
189
* @param fullscreen True if we want to be in fullscreen mode
190
* @throws SlickException Indicates we failed to change the display mode
191
*/
192
public void setFullscreen(boolean fullscreen) throws SlickException {
193
}
194
195
/**
196
* Enable shared OpenGL context. After calling this all containers created
197
* will shared a single parent context
198
*
199
* @throws SlickException Indicates a failure to create the shared drawable
200
*/
201
public static void enableSharedContext() throws SlickException {
202
try {
203
SHARED_DRAWABLE = new Pbuffer(64, 64, new PixelFormat(8, 0, 0), null);
204
} catch (LWJGLException e) {
205
throw new SlickException("Unable to create the pbuffer used for shard context, buffers not supported", e);
206
}
207
}
208
209
/**
210
* Get the context shared by all containers
211
*
212
* @return The context shared by all the containers or null if shared context isn't enabled
213
*/
214
public static Drawable getSharedContext() {
215
return SHARED_DRAWABLE;
216
}
217
218
/**
219
* Indicate if we should clear the screen at the beginning of each frame. If you're
220
* rendering to the whole screen each frame then setting this to false can give
221
* some performance improvements
222
*
223
* @param clear True if the the screen should be cleared each frame
224
*/
225
public void setClearEachFrame(boolean clear) {
226
this.clearEachFrame = clear;
227
}
228
229
/**
230
* Renitialise the game and the context in which it's being rendered
231
*
232
* @throws SlickException Indicates a failure rerun initialisation routines
233
*/
234
public void reinit() throws SlickException {
235
}
236
237
/**
238
* Pause the game - i.e. suspend updates
239
*/
240
public void pause()
241
{
242
setPaused(true);
243
}
244
245
/**
246
* Resumt the game - i.e. continue updates
247
*/
248
public void resume()
249
{
250
setPaused(false);
251
}
252
253
/**
254
* Check if the container is currently paused.
255
*
256
* @return True if the container is paused
257
*/
258
public boolean isPaused() {
259
return paused;
260
}
261
262
/**
263
* Indicates if the game should be paused, i.e. if updates
264
* should be propogated through to the game.
265
*
266
* @param paused True if the game should be paused
267
*/
268
public void setPaused(boolean paused)
269
{
270
this.paused = paused;
271
}
272
273
/**
274
* True if this container should render when it has focus
275
*
276
* @return True if this container should render when it has focus
277
*/
278
public boolean getAlwaysRender () {
279
return alwaysRender;
280
}
281
282
/**
283
* Indicate whether we want this container to render when it has focus
284
*
285
* @param alwaysRender True if this container should render when it has focus
286
*/
287
public void setAlwaysRender (boolean alwaysRender) {
288
this.alwaysRender = alwaysRender;
289
}
290
291
/**
292
* Get the build number of slick
293
*
294
* @return The build number of slick
295
*/
296
public static int getBuildVersion() {
297
try {
298
Properties props = new Properties();
299
props.load(ResourceLoader.getResourceAsStream("version"));
300
301
int build = Integer.parseInt(props.getProperty("build"));
302
Log.info("Slick Build #"+build);
303
304
return build;
305
} catch (Exception e) {
306
Log.error("Unable to determine Slick build number");
307
return -1;
308
}
309
}
310
311
/**
312
* Get the default system font
313
*
314
* @return The default system font
315
*/
316
public Font getDefaultFont() {
317
return defaultFont;
318
}
319
320
/**
321
* Check if sound effects are enabled
322
*
323
* @return True if sound effects are enabled
324
*/
325
public boolean isSoundOn() {
326
return SoundStore.get().soundsOn();
327
}
328
329
/**
330
* Check if music is enabled
331
*
332
* @return True if music is enabled
333
*/
334
public boolean isMusicOn() {
335
return SoundStore.get().musicOn();
336
}
337
338
/**
339
* Indicate whether music should be enabled
340
*
341
* @param on True if music should be enabled
342
*/
343
public void setMusicOn(boolean on) {
344
SoundStore.get().setMusicOn(on);
345
}
346
347
/**
348
* Indicate whether sound effects should be enabled
349
*
350
* @param on True if sound effects should be enabled
351
*/
352
public void setSoundOn(boolean on) {
353
SoundStore.get().setSoundsOn(on);
354
}
355
356
/**
357
* Retrieve the current default volume for music
358
* @return the current default volume for music
359
*/
360
public float getMusicVolume() {
361
return SoundStore.get().getMusicVolume();
362
}
363
364
/**
365
* Retrieve the current default volume for sound fx
366
* @return the current default volume for sound fx
367
*/
368
public float getSoundVolume() {
369
return SoundStore.get().getSoundVolume();
370
}
371
372
/**
373
* Set the default volume for sound fx
374
* @param volume the new default value for sound fx volume
375
*/
376
public void setSoundVolume(float volume) {
377
SoundStore.get().setSoundVolume(volume);
378
}
379
380
/**
381
* Set the default volume for music
382
* @param volume the new default value for music volume
383
*/
384
public void setMusicVolume(float volume) {
385
SoundStore.get().setMusicVolume(volume);
386
}
387
388
/**
389
* Get the width of the standard screen resolution
390
*
391
* @return The screen width
392
*/
393
public abstract int getScreenWidth();
394
395
/**
396
* Get the height of the standard screen resolution
397
*
398
* @return The screen height
399
*/
400
public abstract int getScreenHeight();
401
402
/**
403
* Get the width of the game canvas
404
*
405
* @return The width of the game canvas
406
*/
407
public int getWidth() {
408
return width;
409
}
410
411
/**
412
* Get the height of the game canvas
413
*
414
* @return The height of the game canvas
415
*/
416
public int getHeight() {
417
return height;
418
}
419
420
/**
421
* Set the icon to be displayed if possible in this type of
422
* container
423
*
424
* @param ref The reference to the icon to be displayed
425
* @throws SlickException Indicates a failure to load the icon
426
*/
427
public abstract void setIcon(String ref) throws SlickException;
428
429
/**
430
* Set the icons to be used for this application. Note that the size of the icon
431
* defines how it will be used. Important ones to note
432
*
433
* Windows window icon must be 16x16
434
* Windows alt-tab icon must be 24x24 or 32x32 depending on Windows version (XP=32)
435
*
436
* @param refs The reference to the icon to be displayed
437
* @throws SlickException Indicates a failure to load the icon
438
*/
439
public abstract void setIcons(String[] refs) throws SlickException;
440
441
/**
442
* Get the accurate system time
443
*
444
* @return The system time in milliseconds
445
*/
446
public long getTime() {
447
return (Sys.getTime() * 1000) / Sys.getTimerResolution();
448
}
449
450
/**
451
* Sleep for a given period
452
*
453
* @param milliseconds The period to sleep for in milliseconds
454
*/
455
public void sleep(int milliseconds) {
456
long target = getTime()+milliseconds;
457
while (getTime() < target) {
458
try { Thread.sleep(1); } catch (Exception e) {}
459
}
460
}
461
462
/**
463
* Set the mouse cursor to be displayed - this is a hardware cursor and hence
464
* shouldn't have any impact on FPS.
465
*
466
* @param ref The location of the image to be loaded for the cursor
467
* @param hotSpotX The x coordinate of the hotspot within the cursor image
468
* @param hotSpotY The y coordinate of the hotspot within the cursor image
469
* @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor
470
*/
471
public abstract void setMouseCursor(String ref, int hotSpotX, int hotSpotY) throws SlickException;
472
473
/**
474
* Set the mouse cursor to be displayed - this is a hardware cursor and hence
475
* shouldn't have any impact on FPS.
476
*
477
* @param data The image data from which the cursor can be construted
478
* @param hotSpotX The x coordinate of the hotspot within the cursor image
479
* @param hotSpotY The y coordinate of the hotspot within the cursor image
480
* @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor
481
*/
482
public abstract void setMouseCursor(ImageData data, int hotSpotX, int hotSpotY) throws SlickException;
483
484
/**
485
* Set the mouse cursor based on the contents of the image. Note that this will not take
486
* account of render state type changes to images (rotation and such). If these effects
487
* are required it is recommended that an offscreen buffer be used to produce an appropriate
488
* image. An offscreen buffer will always be used to produce the new cursor and as such
489
* this operation an be very expensive
490
*
491
* @param image The image to use as the cursor
492
* @param hotSpotX The x coordinate of the hotspot within the cursor image
493
* @param hotSpotY The y coordinate of the hotspot within the cursor image
494
* @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor
495
*/
496
public abstract void setMouseCursor(Image image, int hotSpotX, int hotSpotY) throws SlickException;
497
498
/**
499
* Set the mouse cursor to be displayed - this is a hardware cursor and hence
500
* shouldn't have any impact on FPS.
501
*
502
* @param cursor The cursor to use
503
* @param hotSpotX The x coordinate of the hotspot within the cursor image
504
* @param hotSpotY The y coordinate of the hotspot within the cursor image
505
* @throws SlickException Indicates a failure to load the cursor image or create the hardware cursor
506
*/
507
public abstract void setMouseCursor(Cursor cursor, int hotSpotX, int hotSpotY) throws SlickException;
508
509
/**
510
* Get a cursor based on a image reference on the classpath. The image
511
* is assumed to be a set/strip of cursor animation frames running from top to
512
* bottom.
513
*
514
* @param ref The reference to the image to be loaded
515
* @param x The x-coordinate of the cursor hotspot (left -> right)
516
* @param y The y-coordinate of the cursor hotspot (bottom -> top)
517
* @param width The x width of the cursor
518
* @param height The y height of the cursor
519
* @param cursorDelays image delays between changing frames in animation
520
*
521
* @throws SlickException Indicates a failure to load the image or a failure to create the hardware cursor
522
*/
523
public void setAnimatedMouseCursor(String ref, int x, int y, int width, int height, int[] cursorDelays) throws SlickException
524
{
525
try {
526
Cursor cursor;
527
cursor = CursorLoader.get().getAnimatedCursor(ref, x, y, width, height, cursorDelays);
528
setMouseCursor(cursor, x, y);
529
} catch (IOException e) {
530
throw new SlickException("Failed to set mouse cursor", e);
531
} catch (LWJGLException e) {
532
throw new SlickException("Failed to set mouse cursor", e);
533
}
534
}
535
536
/**
537
* Set the default mouse cursor - i.e. the original cursor before any native
538
* cursor was set
539
*/
540
public abstract void setDefaultMouseCursor();
541
542
/**
543
* Get the input system
544
*
545
* @return The input system available to this game container
546
*/
547
public Input getInput() {
548
return input;
549
}
550
551
/**
552
* Get the current recorded FPS (frames per second)
553
*
554
* @return The current FPS
555
*/
556
public int getFPS() {
557
return recordedFPS;
558
}
559
560
/**
561
* Indicate whether mouse cursor should be grabbed or not
562
*
563
* @param grabbed True if mouse cursor should be grabbed
564
*/
565
public abstract void setMouseGrabbed(boolean grabbed);
566
567
/**
568
* Check if the mouse cursor is current grabbed. This will cause it not
569
* to be seen.
570
*
571
* @return True if the mouse is currently grabbed
572
*/
573
public abstract boolean isMouseGrabbed();
574
575
/**
576
* Retrieve the time taken to render the last frame, i.e. the change in time - delta.
577
*
578
* @return The time taken to render the last frame
579
*/
580
protected int getDelta() {
581
long time = getTime();
582
int delta = (int) (time - lastFrame);
583
lastFrame = time;
584
585
return delta;
586
}
587
588
/**
589
* Updated the FPS counter
590
*/
591
protected void updateFPS() {
592
if (getTime() - lastFPS > 1000) {
593
lastFPS = getTime();
594
recordedFPS = fps;
595
fps = 0;
596
}
597
fps++;
598
}
599
600
/**
601
* Set the minimum amount of time in milliseonds that has to
602
* pass before update() is called on the container game. This gives
603
* a way to limit logic updates compared to renders.
604
*
605
* @param interval The minimum interval between logic updates
606
*/
607
public void setMinimumLogicUpdateInterval(int interval) {
608
minimumLogicInterval = interval;
609
}
610
611
/**
612
* Set the maximum amount of time in milliseconds that can passed
613
* into the update method. Useful for collision detection without
614
* sweeping.
615
*
616
* @param interval The maximum interval between logic updates
617
*/
618
public void setMaximumLogicUpdateInterval(int interval) {
619
maximumLogicInterval = interval;
620
}
621
622
/**
623
* Update and render the game
624
*
625
* @param delta The change in time since last update and render
626
* @throws SlickException Indicates an internal fault to the game.
627
*/
628
protected void updateAndRender(int delta) throws SlickException {
629
if (smoothDeltas) {
630
if (getFPS() != 0) {
631
delta = 1000 / getFPS();
632
}
633
}
634
635
input.poll(width, height);
636
637
Music.poll(delta);
638
if (!paused) {
639
storedDelta += delta;
640
641
if (storedDelta >= minimumLogicInterval) {
642
try {
643
if (maximumLogicInterval != 0) {
644
long cycles = storedDelta / maximumLogicInterval;
645
for (int i=0;i<cycles;i++) {
646
game.update(this, (int) maximumLogicInterval);
647
}
648
649
int remainder = (int) (delta % maximumLogicInterval);
650
if (remainder > minimumLogicInterval) {
651
game.update(this, (int) (delta % maximumLogicInterval));
652
storedDelta = 0;
653
} else {
654
storedDelta = remainder;
655
}
656
} else {
657
game.update(this, (int) storedDelta);
658
storedDelta = 0;
659
}
660
661
} catch (Throwable e) {
662
Log.error(e);
663
throw new SlickException("Game.update() failure - check the game code.");
664
}
665
}
666
} else {
667
game.update(this, 0);
668
}
669
670
if (hasFocus() || getAlwaysRender()) {
671
if (clearEachFrame) {
672
GL.glClear(SGL.GL_COLOR_BUFFER_BIT | SGL.GL_DEPTH_BUFFER_BIT);
673
}
674
675
GL.glLoadIdentity();
676
677
graphics.resetFont();
678
graphics.resetLineWidth();
679
graphics.setAntiAlias(false);
680
try {
681
game.render(this, graphics);
682
} catch (Throwable e) {
683
Log.error(e);
684
throw new SlickException("Game.render() failure - check the game code.");
685
}
686
graphics.resetTransform();
687
688
if (showFPS) {
689
defaultFont.drawString(10, 10, "FPS: "+recordedFPS);
690
}
691
692
GL.flush();
693
}
694
695
if (targetFPS != -1) {
696
Display.sync(targetFPS);
697
}
698
}
699
700
/**
701
* Indicate if the display should update only when the game is visible
702
* (the default is true)
703
*
704
* @param updateOnlyWhenVisible True if we should updated only when the display is visible
705
*/
706
public void setUpdateOnlyWhenVisible(boolean updateOnlyWhenVisible) {
707
}
708
709
/**
710
* Check if this game is only updating when visible to the user (default = true)
711
*
712
* @return True if the game is only updated when the display is visible
713
*/
714
public boolean isUpdatingOnlyWhenVisible() {
715
return true;
716
}
717
718
/**
719
* Initialise the GL context
720
*/
721
protected void initGL() {
722
Log.info("Starting display "+width+"x"+height);
723
GL.initDisplay(width, height);
724
725
if (input == null) {
726
input = new Input(height);
727
}
728
input.init(height);
729
// no need to remove listeners?
730
//input.removeAllListeners();
731
if (game instanceof InputListener) {
732
input.removeListener((InputListener) game);
733
input.addListener((InputListener) game);
734
}
735
736
if (graphics != null) {
737
graphics.setDimensions(getWidth(), getHeight());
738
}
739
lastGame = game;
740
}
741
742
/**
743
* Initialise the system components, OpenGL and OpenAL.
744
*
745
* @throws SlickException Indicates a failure to create a native handler
746
*/
747
protected void initSystem() throws SlickException {
748
initGL();
749
setMusicVolume(1.0f);
750
setSoundVolume(1.0f);
751
752
graphics = new Graphics(width, height);
753
defaultFont = graphics.getFont();
754
}
755
756
/**
757
* Enter the orthographic mode
758
*/
759
protected void enterOrtho() {
760
enterOrtho(width, height);
761
}
762
763
/**
764
* Indicate whether the container should show the FPS
765
*
766
* @param show True if the container should show the FPS
767
*/
768
public void setShowFPS(boolean show) {
769
showFPS = show;
770
}
771
772
/**
773
* Check if the FPS is currently showing
774
*
775
* @return True if the FPS is showing
776
*/
777
public boolean isShowingFPS() {
778
return showFPS;
779
}
780
781
/**
782
* Set the target fps we're hoping to get
783
*
784
* @param fps The target fps we're hoping to get
785
*/
786
public void setTargetFrameRate(int fps) {
787
targetFPS = fps;
788
}
789
790
/**
791
* Indicate whether the display should be synced to the
792
* vertical refresh (stops tearing)
793
*
794
* @param vsync True if we want to sync to vertical refresh
795
*/
796
public void setVSync(boolean vsync) {
797
this.vsync = vsync;
798
Display.setVSyncEnabled(vsync);
799
}
800
801
/**
802
* True if vsync is requested
803
*
804
* @return True if vsync is requested
805
*/
806
public boolean isVSyncRequested() {
807
return vsync;
808
}
809
810
/**
811
* True if the game is running
812
*
813
* @return True if the game is running
814
*/
815
protected boolean running() {
816
return running;
817
}
818
819
/**
820
* Inidcate we want verbose logging
821
*
822
* @param verbose True if we want verbose logging (INFO and DEBUG)
823
*/
824
public void setVerbose(boolean verbose) {
825
Log.setVerbose(verbose);
826
}
827
828
/**
829
* Cause the game to exit and shutdown cleanly
830
*/
831
public void exit() {
832
running = false;
833
}
834
835
/**
836
* Check if the game currently has focus
837
*
838
* @return True if the game currently has focus
839
*/
840
public abstract boolean hasFocus();
841
842
/**
843
* Get the graphics context used by this container. Note that this
844
* value may vary over the life time of the game.
845
*
846
* @return The graphics context used by this container
847
*/
848
public Graphics getGraphics() {
849
return graphics;
850
}
851
852
/**
853
* Enter the orthographic mode
854
*
855
* @param xsize The size of the panel being used
856
* @param ysize The size of the panel being used
857
*/
858
protected void enterOrtho(int xsize, int ysize) {
859
GL.enterOrtho(xsize, ysize);
860
}
861
}
862
863