-
Notifications
You must be signed in to change notification settings - Fork 1
/
Game.snap
336 lines (290 loc) · 11 KB
/
Game.snap
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
import awt.Color;
import awt.Canvas;
import awt.Dimension;
import swing.JFrame;
/**
* The main hook of our game. This class with both act as a manager
* for the display and central mediator for the game logic.
*
* Display management will consist of a loop that cycles round all
* entities in the game asking them to move and then drawing them
* in the appropriate place. With the help of an inner class it
* will also allow the player to control the main ship.
*
* As a mediator it will be informed when entities within our game
* detect events (e.g. alient killed, played died) and will take
* appropriate game actions.
*/
class Game {
/** The stragey that allows us to use accelerate page flipping */
var strategy;
/** True if the game is currently "running", i.e. the game loop is looping */
var gameRunning = true;
/** The list of all the entities that exist in our game */
var entities = [];
/** The list of entities that need to be removed from the game this loop */
var removeList = [];
/** The entity representing the player */
var ship;
/** The speed at which the player's ship should move (pixels/sec) */
var moveSpeed = 300.0;
/** The time at which last fired a shot */
var lastFire = 0.0;
/** The interval between our players shot (ms) */
var firingInterval = 500.0;
/** The number of aliens left on the screen */
var alienCount;
/** The message to display which waiting for a key press */
var message = "";
/** True if we're holding up game play until a key has been pressed */
var waitingForKeyPress = true;
/** True if the left cursor key is currently pressed */
var leftPressed = false;
/** True if the right cursor key is currently pressed */
var rightPressed = false;
/** True if we are firing */
var firePressed = false;
/** True if game logic needs to be applied this loop, normally as a result of a game event */
var logicRequiredThisLoop = false;
/**
* Construct our game and set it running.
*/
new() {
// create a frame to contain our game
var container = new JFrame("Space Invaders 101");
// get hold the content of the frame and set up the resolution of the game
var panel = container.getContentPane();
panel.setPreferredSize(new Dimension(800,600));
panel.setLayout(null);
var canvas = new Canvas();
// setup our canvas size and put it into the content of the frame
canvas.setBounds(0,0,800,600);
panel.add(canvas);
// Tell AWT not to bother repainting our canvas since we're
// going to do that our self in accelerated mode
canvas.setIgnoreRepaint(true);
// finally make the window visible
container.pack();
container.setResizable(false);
container.setVisible(true);
// add a listener to respond to the user closing the window. If they
// do we'd like to exit the game
//container.addWindowListener(new WindowAdapter() {
// public void windowClosing(WindowEvent e) {
// System.exit(0);
// }
//});
// add a key input system (defined below) to our canvas
// so we can respond to key pressed
canvas.addKeyListener(new GameController(this));
// request the focus so key events come to us
canvas.requestFocus();
// create the buffering strategy which will allow AWT
// to manage our accelerated graphics
canvas.createBufferStrategy(2);
strategy = canvas.getBufferStrategy();
// initialise the entities in our game so there's something
// to see at startup
initEntities();
}
/**
* Start a fresh game, this should clear out any old data and
* create a new set.
*/
startGame() {
// clear out any existing entities and intialise a new set
entities.clear();
initEntities();
// blank out any keyboard settings we might currently have
leftPressed = false;
rightPressed = false;
firePressed = false;
}
/**
* Initialise the starting state of the entities (ship and aliens). Each
* entitiy will be added to the overall list of entities in the game.
*/
initEntities() {
// create the player ship and place it roughly in the center of the screen
ship = new ShipEntity(this, "/spaceinvaders/ship.gif",370,550);
entities.add(ship);
// create a block of aliens (5 rows, by 12 aliens, spaced evenly)
alienCount = 0;
for (var row=0;row<5;row++) {
for (var x=0;x<12;x++) {
var alien = new AlienEntity(this,"/spaceinvaders/alien.gif",100+(x*50),50+row*30);
entities.add(alien);
alienCount++;
}
}
}
/**
* Notification from a game entity that the logic of the game
* should be run at the next opportunity (normally as a result of some
* game event)
*/
updateLogic() {
logicRequiredThisLoop = true;
}
/**
* Remove an entity from the game. The entity removed will
* no longer move or be drawn.
*
* @param entity The entity that should be removed
*/
removeEntity(entity) {
removeList.add(entity);
}
/**
* Notification that the player has died.
*/
notifyDeath() {
message = "Oh no! They got you, try again?";
waitingForKeyPress = true;
}
/**
* Notification that the player has won since all the aliens
* are dead.
*/
notifyWin() {
message = "Well done! You Win!";
waitingForKeyPress = true;
}
/**
* Notification that an alien has been killed
*/
notifyAlienKilled() {
// reduce the alient count, if there are none left, the player has won!
alienCount--;
if (alienCount == 0) {
notifyWin();
}
// if there are still some aliens left then they all need to get faster, so
// speed up all the existing aliens
for (var i=0;i<entities.size();i++) {
var entity = entities.get(i);
if (entity.class == AlienEntity) {
// speed up by 2%
entity.setHorizontalMovement(entity.getHorizontalMovement() * 1.02);
}
}
}
/**
* Attempt to fire a shot from the player. Its called "try"
* since we must first check that the player can fire at this
* point, i.e. has he/she waited long enough between shots
*/
tryToFire() {
// check that we have waiting long enough to fire
if (System.currentTimeMillis() - lastFire < firingInterval) {
return;
}
// if we waited long enough, create the shot entity, and record the time.
lastFire = System.currentTimeMillis();
var shot = new ShotEntity(this,"/spaceinvaders/shot.gif",ship.getX()+10,ship.getY()-30);
entities.add(shot);
}
/**
* The main game loop. This loop is running during all game
* play as is responsible for the following activities:
* <p>
* - Working out the speed of the game loop to update moves
* - Moving the game entities
* - Drawing the screen contents (entities, text)
* - Updating game events
* - Checking Input
* <p>
*/
gameLoop() {
var lastLoopTime = System.currentTimeMillis();
// keep looping round til the game ends
while (gameRunning) {
// work out how long its been since the last update, this
// will be used to calculate how far the entities should
// move this loop
var delta = System.currentTimeMillis() - lastLoopTime;
lastLoopTime = System.currentTimeMillis();
// Get hold of a graphics context for the accelerated
// surface and blank it out
var g = strategy.getDrawGraphics();
var stars = SpriteStore.getSprite("/spaceinvaders/stars.png");
stars.value.draw(g,0,0);
//g.setColor(Color.black);
//g.fillRect(0,0,800,600);
// cycle round asking each entity to move itself
if (!waitingForKeyPress) {
for (var i=0;i<entities.size();i++) {
var entity = entities.get(i);
entity.move(delta);
}
}
// cycle round drawing all the entities we have in the game
for (var i=0;i<entities.size();i++) {
var entity = entities.get(i);
entity.draw(g);
}
// brute force collisions, compare every entity against
// every other entity. If any of them collide notify
// both entities that the collision has occured
for (var p=0;p<entities.size();p++) {
for (var s=p+1;s<entities.size();s++) {
try {
var me = entities.get(p);
var him = entities.get(s);
if (me.collidesWith(him)) {
me.collidedWith(him);
him.collidedWith(me);
}
}catch(e){
// avoid clear
}
}
}
// remove any entity that has been marked for clear up
entities.removeAll(removeList);
removeList.clear();
// if a game event has indicated that game logic should
// be resolved, cycle round every entity requesting that
// their personal logic should be considered.
if (logicRequiredThisLoop) {
for (var i=0;i<entities.size();i++) {
var entity = entities.get(i);
entity.doLogic();
}
logicRequiredThisLoop = false;
}
// if we're waiting for an "any key" press then draw the
// current message
if (waitingForKeyPress) {
g.setColor(Color.white);
g.drawString(message,(800-g.getFontMetrics().stringWidth(message))/2,250);
g.drawString("Press any key",(800-g.getFontMetrics().stringWidth("Press any key"))/2,300);
}
// finally, we've completed drawing so clear up the graphics
// and flip the buffer over
g.dispose();
strategy.show();
// resolve the movement of the ship. First assume the ship
// isn't moving. If either cursor key is pressed then
// update the movement appropraitely
ship.setHorizontalMovement(0);
if ((leftPressed) && (!rightPressed)) {
ship.setHorizontalMovement(-moveSpeed);
} else if ((rightPressed) && (!leftPressed)) {
ship.setHorizontalMovement(moveSpeed);
}
// if we're pressing fire, attempt to fire
if (firePressed) {
tryToFire();
}
// finally pause for a bit. Note: this should run us at about
// 100 fps but on windows this might vary each loop due to
// a bad implementation of timer
try {
Thread.sleep(10);
} catch (e) {
e.printStackTrace();
}
}
}
}