Skip to content

Commit 0c91f61

Browse files
committedMar 6, 2017
Add Racer game classes
1 parent f694d82 commit 0c91f61

6 files changed

+1219
-0
lines changed
 

‎Ball.java

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* Models a simple solid sphere.
3+
* This class represents a Ball object. When combined with the GameArena class,
4+
* instances of the Ball class can be displayed on the screen.
5+
*/
6+
public class Ball
7+
{
8+
// The following instance variables define the
9+
// information needed to represent a Ball
10+
// Feel free to more instance variables if you think it will
11+
// support your work...
12+
13+
private double xPosition; // The X coordinate of this Ball
14+
private double yPosition; // The Y coordinate of this Ball
15+
private double size; // The diameter of this Ball
16+
private String colour = "WHITE"; // The colour of this Ball
17+
18+
// Permissable colours are 8 bit hexadecimal
19+
// RGB values in the format #RRGGBB. e.g.
20+
//
21+
// Pure red is FF0000
22+
// Pure lime is 00FF00
23+
// Pure blue is 0000FF
24+
25+
/**
26+
* Obtains the current position of this Ball.
27+
* @return the X coordinate of this Ball within the GameArena.
28+
*/
29+
public double getXPosition()
30+
{
31+
return xPosition;
32+
}
33+
34+
/**
35+
* Obtains the current position of this Ball.
36+
* @return the Y coordinate of this Ball within the GameArena.
37+
*/
38+
public double getYPosition()
39+
{
40+
return yPosition;
41+
}
42+
43+
/**
44+
* Moves the current position of this Ball to the given co-ordinates
45+
* @param x the new x co-ordinate of this Ball
46+
*/
47+
public void setXPosition(double x)
48+
{
49+
this.xPosition = x;
50+
}
51+
52+
/**
53+
* Moves the current position of this Ball to the given co-ordinates
54+
* @param y the new y co-ordinate of this Ball
55+
*/
56+
public void setYPosition(double y)
57+
{
58+
this.yPosition = y;
59+
}
60+
61+
/**
62+
* Obtains the size of this Ball.
63+
* @return the diameter of this Ball,in pixels.
64+
*/
65+
public double getSize()
66+
{
67+
return size;
68+
}
69+
70+
/**
71+
* Sets the size of this Ball to the given value.
72+
* @param size Teh new size of the Ball, in pixels.
73+
*/
74+
public void setSize(double size)
75+
{
76+
this.size = size;
77+
}
78+
79+
/**
80+
* Obtains the colour of this Ball.
81+
* @return a textual description of the colour of this Ball.
82+
*/
83+
public String getColour()
84+
{
85+
return colour;
86+
}
87+
88+
/**
89+
* Changes the colour of this Ball
90+
* @param input the new colour of this Ball
91+
*/
92+
93+
public void setColour(String input)
94+
{
95+
this.colour = input;
96+
}
97+
98+
99+
public Ball(double x, double y, double diameter, String col)
100+
{
101+
xPosition = x;
102+
yPosition = y;
103+
104+
size = diameter;
105+
colour = col;
106+
}
107+
}

‎Car.java

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/**
2+
* Models a simple Car for the game Racer.
3+
*
4+
* @author Joe Finney (joe@comp.lancs.ac.uk)
5+
*/
6+
public class Car
7+
{
8+
public static String CAR_COLOUR = "#FF0000";
9+
public static String WHEEL_COLOUR = "#404040";
10+
11+
private Rectangle[] parts = new Rectangle[7];
12+
private double xPosition;
13+
private double yPosition;
14+
private GameArena arena;
15+
16+
private double xSpeed;
17+
private double ySpeed;
18+
19+
/**
20+
* Creates a new Car, at the given location.
21+
*
22+
* @param x The x position on the screen of the centre of the car.
23+
* @param y The y position on the screen of the centre of the car.
24+
* @param a The GameArena upon which to draw this car.
25+
*/
26+
public Car(double x, double y, GameArena a)
27+
{
28+
parts[0] = new Rectangle(10, 20, 10, 20, WHEEL_COLOUR);
29+
parts[1] = new Rectangle(10, 80, 10, 20, WHEEL_COLOUR);
30+
parts[2] = new Rectangle(50, 20, 10, 20, WHEEL_COLOUR);
31+
parts[3] = new Rectangle(50, 80, 10, 20, WHEEL_COLOUR);
32+
parts[4] = new Rectangle(30, 50, 40, 70, CAR_COLOUR);
33+
parts[5] = new Rectangle(15, 18, 5, 5, "WHITE");
34+
parts[6] = new Rectangle(45, 18, 5, 5, "WHITE");
35+
36+
arena = a;
37+
this.setXPosition(x);
38+
this.setYPosition(y);
39+
40+
for (int i=0; i < parts.length; i++)
41+
arena.addRectangle(parts[i]);
42+
}
43+
44+
/**
45+
* Changes the position of this car to the given location
46+
*
47+
* @param x The new x positition of this car on the screen.
48+
*/
49+
public void setXPosition(double x)
50+
{
51+
double dx = x - xPosition;
52+
53+
for (int i=0; i < parts.length; i++)
54+
parts[i].setXPosition(parts[i].getXPosition() + dx);
55+
56+
xPosition = x;
57+
}
58+
59+
/**
60+
* Changes the position of this car to the given location
61+
*
62+
* @param y The new y positition of this car on the screen.
63+
*/
64+
public void setYPosition(double y)
65+
{
66+
double dy = y - yPosition;
67+
for (int i=0; i < parts.length; i++)
68+
parts[i].setYPosition(parts[i].getYPosition() + dy);
69+
70+
yPosition = y;
71+
}
72+
73+
/**
74+
* Determines the position of this car on the screen
75+
*
76+
* @return The x position of the centre of this car on the screen.
77+
*/
78+
public double getXPosition()
79+
{
80+
return xPosition;
81+
}
82+
83+
/**
84+
* Determines the position of this car on the screen
85+
*
86+
* @return The y co-ordinate of the centre of this car on the screen.
87+
*/
88+
public double getYPosition()
89+
{
90+
return yPosition;
91+
}
92+
93+
/**
94+
* Sets the speed of this car in the X axis - i.e. the number of pixels it moves in the X axis every time move() is called.
95+
*
96+
* @param s The new speed of this car in the x axis
97+
*/
98+
public void setXSpeed(double s)
99+
{
100+
xSpeed = s;
101+
}
102+
103+
/**
104+
* Sets the speed of this car in the Y axis - i.e. the number of pixels it moves in the Y axis every time move() is called.
105+
*
106+
* @param s The new speed of this car in the y axis
107+
*/
108+
public void setYSpeed(double s)
109+
{
110+
ySpeed = s;
111+
}
112+
113+
/**
114+
* Updates the position of this car by a small amount, depending upon its speed.
115+
* see setXSpeed() and setYSpeed() methods.
116+
*/
117+
public void move()
118+
{
119+
this.setXPosition(xPosition + xSpeed);
120+
this.setYPosition(yPosition + ySpeed);
121+
}
122+
123+
/**
124+
* Determines if this car is touching the given RoadSegment.
125+
*
126+
* @param s The segment of road to test against.
127+
* @return true of this car is touching the given road segment, false otherwise.
128+
*/
129+
public boolean isTouching(RoadSegment s)
130+
{
131+
Rectangle[] roadParts = s.getParts();
132+
133+
for (int i=0; i < parts.length; i++)
134+
for (int j=0; j < roadParts.length; j++)
135+
if(parts[i].isTouching(roadParts[j]))
136+
return true;
137+
138+
return false;
139+
140+
}
141+
}

‎GameArena.java

+456
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,456 @@
1+
import java.util.*;
2+
import java.util.Collections;
3+
import java.util.concurrent.locks.*;
4+
import javafx.scene.input.KeyEvent;
5+
6+
import javax.swing.JFrame;
7+
import javax.swing.SwingUtilities;
8+
import javafx.application.Platform;
9+
import javafx.animation.AnimationTimer;
10+
import javafx.embed.swing.JFXPanel;
11+
import javafx.event.*;
12+
import javafx.scene.*;
13+
import javafx.scene.paint.*;
14+
import javafx.scene.layout.*;
15+
import javafx.scene.control.*;
16+
import javafx.scene.input.KeyCode;
17+
18+
import java.awt.event.WindowEvent;
19+
20+
/**
21+
* This class provides a simple window in which grahical objects can be drawn.
22+
*
23+
* Instances of the Ball and Rectangle classes can be added to an instance of this class to
24+
* draw and animate simple shapes on the screen.
25+
*
26+
* @see Ball
27+
* @see Rectangle
28+
*
29+
* @author Joe Finney
30+
*/
31+
public class GameArena
32+
{
33+
//Colours to chose from
34+
public static final String BLACK = "#000000";
35+
public static final String BLUE = "#0000ff";
36+
public static final String CYAN = "#00ffff";
37+
public static final String DARKGREY = "#a9a9a9";
38+
public static final String GREY = "#808080";
39+
public static final String GREEN = "#008000";
40+
public static final String LIGHTGREY = "#d3d3d3";
41+
public static final String MAGENTA = "#ff00ff";
42+
public static final String ORANGE = "#ffa500";
43+
public static final String PINK = "#ffc0cb";
44+
public static final String RED = "#ff0000";
45+
public static final String WHITE = "#ffffff";
46+
public static final String YELLOW = "#ffff00";
47+
48+
// Size of window
49+
private int arenaWidth;
50+
private int arenaHeight;
51+
private JFrame window;
52+
53+
private boolean exiting = false;
54+
private final static int MAXIMUM_OBJECTS = 100000;
55+
56+
// Collections of primitives. These now relate 1:1 to JavaFX Nodes, since moving from AWT.
57+
private List<Object> addList = new ArrayList<Object>();
58+
private List<Object> removeList = new ArrayList<Object>();
59+
private Map<Ball, javafx.scene.shape.Circle> balls = new HashMap<>();
60+
private Map<Rectangle, javafx.scene.shape.Rectangle> rectangles = new HashMap<>();
61+
private int objectCount;
62+
63+
// Basic button state
64+
private boolean up = false;
65+
private boolean down = false;
66+
private boolean left = false;
67+
private boolean right = false;
68+
69+
// JavaFX containers
70+
private Scene scene;
71+
private Group root;
72+
private JFXPanel jfxPanel;
73+
74+
// Lock used to reduce flicker when rendering of large numbers of objects.
75+
private ReentrantLock renderLock;
76+
77+
/**
78+
* Constructor. Creates an instance of the GameArena class, and displays a window on the
79+
* screen upon which shapes can be drawn.
80+
*
81+
* @param width The width of the window, in pixels.
82+
* @param height The height of the window, in pixels.
83+
*/
84+
public GameArena(int width, int height)
85+
{
86+
this(width, height, true);
87+
}
88+
89+
/**
90+
* Constructor. Creates an instance of the GameArena class, and displays a window on the
91+
* screen upon which shapes can be drawn.
92+
*
93+
* @param width The width of the window, in pixels.
94+
* @param height The height of the window, in pixels.
95+
* @param createWindow Determines if a JFrame containing the GameArena should be created (true) or not (false).
96+
*/
97+
public GameArena(int width, int height, boolean createWindow)
98+
{
99+
this.arenaWidth = width;
100+
this.arenaHeight = height;
101+
this.objectCount = 0;
102+
103+
// Create a lock to reduce flicker on rendering
104+
renderLock = new ReentrantLock();
105+
106+
// Create a JavaFX canvas as a Swing panel.
107+
jfxPanel = new JFXPanel();
108+
jfxPanel.setPreferredSize(new java.awt.Dimension(width, height));
109+
110+
// Create a window, if necessary.
111+
if (createWindow)
112+
{
113+
window = new JFrame();
114+
window.setTitle("Let's Play!");
115+
window.setContentPane(jfxPanel);
116+
window.setResizable(false);
117+
window.pack();
118+
window.setVisible(true);
119+
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
120+
}
121+
122+
root = new Group();
123+
scene = new Scene(root, arenaWidth, arenaHeight, Color.BLACK);
124+
125+
renderLock.lock();
126+
127+
Platform.runLater(new Runnable() {
128+
@Override
129+
public void run() {
130+
initFX();
131+
}
132+
});
133+
}
134+
135+
private void initFX() {
136+
137+
EventHandler<KeyEvent> keyDownHandler = new EventHandler<KeyEvent>() {
138+
public void handle(final KeyEvent keyEvent) {
139+
if (keyEvent.getCode() == KeyCode.UP)
140+
up = true;
141+
if (keyEvent.getCode() == KeyCode.DOWN)
142+
down = true;
143+
if (keyEvent.getCode() == KeyCode.LEFT)
144+
left = true;
145+
if (keyEvent.getCode() == KeyCode.RIGHT)
146+
right = true;
147+
}
148+
};
149+
150+
EventHandler<KeyEvent> keyUpHandler = new EventHandler<KeyEvent>() {
151+
public void handle(final KeyEvent keyEvent) {
152+
if (keyEvent.getCode() == KeyCode.UP)
153+
up = false;
154+
if (keyEvent.getCode() == KeyCode.DOWN)
155+
down = false;
156+
if (keyEvent.getCode() == KeyCode.LEFT)
157+
left = false;
158+
if (keyEvent.getCode() == KeyCode.RIGHT)
159+
right = false;
160+
}
161+
};
162+
163+
scene.setOnKeyPressed(keyDownHandler);
164+
scene.setOnKeyReleased(keyUpHandler);
165+
166+
jfxPanel.setScene(scene);
167+
168+
new AnimationTimer() {
169+
@Override
170+
public void handle(long now) {
171+
frameUpdate();
172+
}
173+
}.start();
174+
}
175+
176+
/**
177+
* Close this GameArena window.
178+
*
179+
*/
180+
public void exit()
181+
{
182+
this.exiting = true;
183+
}
184+
185+
/**
186+
* A method called by the operating system to draw onto the screen - <p><B>YOU DO NOT (AND SHOULD NOT) NEED TO CALL THIS METHOD.</b></p>
187+
*/
188+
private void frameUpdate ()
189+
{
190+
if (this.exiting)
191+
{
192+
addList.clear();
193+
removeList.clear();
194+
rectangles.clear();
195+
balls.clear();
196+
objectCount = 0;
197+
198+
root.getChildren().clear();
199+
200+
if (window != null)
201+
window.dispatchEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSING));
202+
203+
this.exiting = false;
204+
205+
return;
206+
}
207+
208+
// Remove any deleted objects from the scene.
209+
renderLock.lock();
210+
211+
for (Object o: removeList)
212+
{
213+
if (o instanceof Ball)
214+
{
215+
Ball b = (Ball) o;
216+
javafx.scene.shape.Circle c = balls.get(b);
217+
root.getChildren().remove(c);
218+
219+
balls.remove(b);
220+
}
221+
222+
if (o instanceof Rectangle)
223+
{
224+
Rectangle r = (Rectangle) o;
225+
javafx.scene.shape.Rectangle rectangle = rectangles.get(r);
226+
root.getChildren().remove(rectangle);
227+
228+
rectangles.remove(r);
229+
}
230+
}
231+
232+
removeList.clear();
233+
234+
// Add any new objects to the scene.
235+
for (Object o: addList)
236+
{
237+
if (o instanceof Ball)
238+
{
239+
Ball b = (Ball) o;
240+
javafx.scene.shape.Circle c = new javafx.scene.shape.Circle(0,0,b.getSize());
241+
root.getChildren().add(c);
242+
balls.put(b, c);
243+
}
244+
245+
if (o instanceof Rectangle)
246+
{
247+
Rectangle r = (Rectangle) o;
248+
javafx.scene.shape.Rectangle rectangle = new javafx.scene.shape.Rectangle(0, 0, r.getWidth(), r.getHeight());
249+
root.getChildren().add(rectangle);
250+
rectangles.put(r, rectangle);
251+
}
252+
}
253+
254+
addList.clear();
255+
256+
for(Map.Entry<Ball, javafx.scene.shape.Circle> entry : balls.entrySet())
257+
{
258+
Ball b = entry.getKey();
259+
javafx.scene.shape.Circle c = entry.getValue();
260+
261+
c.setRadius(b.getSize());
262+
c.setTranslateX(b.getXPosition());
263+
c.setTranslateY(b.getYPosition());
264+
c.setFill(getColourFromString(b.getColour()));
265+
}
266+
267+
for(Map.Entry<Rectangle, javafx.scene.shape.Rectangle> entry : rectangles.entrySet())
268+
{
269+
Rectangle r = entry.getKey();
270+
javafx.scene.shape.Rectangle rectangle = entry.getValue();
271+
rectangle.setWidth(r.getWidth());
272+
rectangle.setHeight(r.getHeight());
273+
rectangle.setTranslateX(r.getXPosition() - r.getWidth()/2);
274+
rectangle.setTranslateY(r.getYPosition() - r.getHeight()/2);
275+
rectangle.setFill(getColourFromString(r.getColour()));
276+
}
277+
278+
renderLock.unlock();
279+
}
280+
281+
//
282+
// Derive a Color object from a given string representation
283+
//
284+
private Color getColourFromString(String col)
285+
{
286+
Color colour = Color.web(col);
287+
return colour;
288+
}
289+
290+
/**
291+
* Adds a given Ball to the GameArena.
292+
* Once a Ball is added, it will automatically appear on the window.
293+
*
294+
* @param b the ball to add to the GameArena.
295+
*/
296+
public void addBall(Ball b)
297+
{
298+
synchronized (this)
299+
{
300+
if (objectCount > MAXIMUM_OBJECTS)
301+
{
302+
System.out.println("\n\n");
303+
System.out.println(" ********************************************************* ");
304+
System.out.println(" ***** Only 100000 Objects Supported per Game Arena! ***** ");
305+
System.out.println(" ********************************************************* ");
306+
System.out.println("\n");
307+
System.out.println("-- Joe\n\n");
308+
309+
System.exit(0);
310+
}
311+
312+
// Add this ball to the draw list. Initially, with a null JavaFX entry, which we'll fill in later to avoid cross-thread operations...
313+
removeList.remove(b);
314+
addList.add(b);
315+
objectCount++;
316+
}
317+
}
318+
319+
/**
320+
* Remove a Ball from the GameArena.
321+
* Once a Ball is removed, it will no longer appear on the window.
322+
*
323+
* @param b the ball to remove from the GameArena.
324+
*/
325+
public void removeBall(Ball b)
326+
{
327+
synchronized (this)
328+
{
329+
addList.remove(b);
330+
removeList.add(b);
331+
objectCount--;
332+
}
333+
}
334+
335+
/**
336+
* Adds a given rectangle to the GameArena.
337+
* Once a Rectangle is added, it will automatically appear on the window.
338+
*
339+
* @param r the rectangle to add to the GameArena.
340+
*/
341+
public void addRectangle(Rectangle r)
342+
{
343+
synchronized (this)
344+
{
345+
if (objectCount > MAXIMUM_OBJECTS)
346+
{
347+
System.out.println("\n\n");
348+
System.out.println(" ********************************************************* ");
349+
System.out.println(" ***** Only 100000 Objects Supported per Game Arena! ***** ");
350+
System.out.println(" ********************************************************* ");
351+
System.out.println("\n");
352+
System.out.println("-- Joe\n\n");
353+
354+
System.exit(0);
355+
}
356+
357+
// Add this ball to the draw list. Initially, with a null JavaFX entry, which we'll fill in later to avoid cross-thread operations...
358+
removeList.remove(r);
359+
addList.add(r);
360+
objectCount++;
361+
}
362+
}
363+
364+
/**
365+
* Remove a Rectangle from the GameArena.
366+
* Once a Rectangle is removed, it will no longer appear on the window.
367+
*
368+
* @param r the rectangle to remove from the GameArena.
369+
*/
370+
public void removeRectangle(Rectangle r)
371+
{
372+
synchronized (this)
373+
{
374+
addList.remove(r);
375+
removeList.add(r);
376+
objectCount--;
377+
}
378+
}
379+
380+
/**
381+
* Pause for a 1/50 of a second.
382+
* This method causes your program to delay for 1/50th of a second. You'll find this useful if you're trying to animate your application.
383+
*/
384+
public void pause()
385+
{
386+
renderLock.unlock();
387+
388+
try { Thread.sleep(18); }
389+
catch (Exception e) {};
390+
391+
renderLock.lock();
392+
}
393+
394+
/**
395+
* Gets the width of the GameArena window, in pixels.
396+
* @return the width in pixels
397+
*/
398+
public int getArenaWidth()
399+
{
400+
return arenaWidth;
401+
}
402+
403+
/**
404+
* Gets the height of the GameArena window, in pixels.
405+
* @return the height in pixels
406+
*/
407+
public int getArenaHeight()
408+
{
409+
return arenaHeight;
410+
}
411+
412+
/**
413+
* Determines if the user is currently pressing the cursor up button.
414+
* @return true if the up button is pressed, false otherwise.
415+
*/
416+
public boolean upPressed()
417+
{
418+
return up;
419+
}
420+
421+
/**
422+
* Determines if the user is currently pressing the cursor down button.
423+
* @return true if the down button is pressed, false otherwise.
424+
*/
425+
public boolean downPressed()
426+
{
427+
return down;
428+
}
429+
430+
/**
431+
* Determines if the user is currently pressing the cursor left button.
432+
* @return true if the left button is pressed, false otherwise.
433+
*/
434+
public boolean leftPressed()
435+
{
436+
return left;
437+
}
438+
439+
/**
440+
* Determines if the user is currently pressing the cursor right button.
441+
* @return true if the right button is pressed, false otherwise.
442+
*/
443+
public boolean rightPressed()
444+
{
445+
return right;
446+
}
447+
448+
/**
449+
* Acquires the JPanel containing this GameArena.
450+
* @return The JPanel object containing this GameArena.
451+
*/
452+
public JFXPanel getPanel()
453+
{
454+
return jfxPanel;
455+
}
456+
}

‎Racer.java

+227
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
import javax.swing.*;
2+
/**
3+
* uses the GameArena APIs to implement a simple top down racing game.
4+
*
5+
* The graphical output of the game is provided as a Swing component,
6+
* so that it can be added into any Swing application, just like a JButton etc.
7+
*
8+
* To allow users to control the game as they see fit, start(), stop() and update()
9+
* methods are provided. start() should be used to create a new game, stop() to terminate
10+
* a running game, and update() should be called in a loop to update gameplay and graphics
11+
*
12+
* Simple example of use:
13+
*
14+
* <pre>
15+
*
16+
* JFrame window = new JFrame();
17+
* Racer r = new Racer();
18+
* window.setTitle("Racer");
19+
* window.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
20+
* window.setContentPane(r.getPanel());
21+
* window.setVisible(true);
22+
*
23+
* r.start();
24+
*
25+
* while(r.isPlaying())
26+
* r.update();
27+
*
28+
* </pre>
29+
*
30+
* @author Joe Finney (joe@comp.lancs.ac.uk)
31+
*/
32+
public class Racer
33+
{
34+
public static final double PLAYER_SPEED = 5;
35+
public static final int ROAD_SEGMENT_WIDTH = 160;
36+
public static final int ROAD_SEGMENT_HEIGHT= 10;
37+
public static final int ROAD_CURVE_SPEED = 5;
38+
public static final int SCREEN_WIDTH = 800;
39+
public static final int SCREEN_HEIGHT = 600;
40+
41+
private GameArena arena;
42+
private Car player;
43+
private RoadSegment[] road = new RoadSegment[SCREEN_HEIGHT / ROAD_SEGMENT_HEIGHT + 1];
44+
45+
private double currentRoadX = SCREEN_WIDTH/2;
46+
private double speed = 2.0;
47+
private boolean playing = false;
48+
private int score = 0;
49+
50+
/**
51+
* Creates a new instance of the Racer racing game.
52+
*/
53+
public Racer()
54+
{
55+
arena = new GameArena(SCREEN_WIDTH, SCREEN_HEIGHT, false);
56+
}
57+
58+
/**
59+
* Provides a Swing component in which the Racer game runs.
60+
* This component can be added to a Swing panel to display the game on screen.
61+
*
62+
* @return A Swing component for this game.
63+
*/
64+
public JComponent getPanel()
65+
{
66+
return arena.getPanel();
67+
}
68+
69+
/**
70+
* Provides the player's current score in the game.
71+
* @return The player's current score.
72+
*/
73+
public int getScore()
74+
{
75+
return score;
76+
}
77+
78+
/**
79+
* Starts a new game, if the game is not alreayd running.
80+
*/
81+
public void start()
82+
{
83+
if(!playing)
84+
{
85+
// Create the player's car
86+
player = new Car(SCREEN_WIDTH/2, SCREEN_HEIGHT - 150, arena);
87+
88+
// Create the initial road layout
89+
for (int s = road.length-1; s >= 0 ; s--)
90+
{
91+
road[s] = nextRoadSegment();
92+
road[s].setYPosition(s*ROAD_SEGMENT_HEIGHT);
93+
}
94+
95+
score = 0;
96+
playing = true;
97+
}
98+
}
99+
100+
/**
101+
* Stops a currently running game.
102+
*/
103+
public void stop()
104+
{
105+
if(playing)
106+
{
107+
playing = false;
108+
arena.exit();
109+
}
110+
}
111+
112+
/**
113+
* Determines if the game is currently being played.
114+
*
115+
* @return false if the game has not been started or on game over, true if the game is actively running.
116+
*/
117+
public boolean isPlaying()
118+
{
119+
return playing;
120+
}
121+
122+
/**
123+
* Updates the game state to allow the road and player character to move on the screen.
124+
*
125+
* This method should be called in a loop (once per frame) to advance gameplay in response to time
126+
* and user input. The method uses the GameArena pause() method to ensure the game runs at a constant speed.
127+
*/
128+
public void update()
129+
{
130+
if(playing)
131+
{
132+
score++;
133+
134+
double speed = 0;
135+
if (arena.leftPressed())
136+
speed -= PLAYER_SPEED;
137+
138+
if (arena.rightPressed())
139+
speed += PLAYER_SPEED;
140+
141+
player.setXSpeed(speed);
142+
143+
player.move();
144+
for (int i=0; i<road.length; i++)
145+
{
146+
if (road[i] != null)
147+
road[i].move();
148+
}
149+
150+
// Recycle any segments that have crolled off screen...
151+
recycleRoadSegments();
152+
153+
if (hasCrashed())
154+
stop();
155+
}
156+
157+
arena.pause();
158+
}
159+
160+
/**
161+
* Provides a randomly generated, thin slice of road.
162+
* This method is used periodically to create new road on the screen in front of the player's car.
163+
*
164+
* @return A new randomly generated RoadSegment
165+
*/
166+
private RoadSegment nextRoadSegment()
167+
{
168+
currentRoadX += Math.random() * 2 * ROAD_CURVE_SPEED - ROAD_CURVE_SPEED;
169+
RoadSegment s = new RoadSegment(currentRoadX, -ROAD_SEGMENT_HEIGHT, ROAD_SEGMENT_WIDTH, ROAD_SEGMENT_HEIGHT, arena);
170+
s.setYSpeed(speed);
171+
return s;
172+
}
173+
174+
/**
175+
* Removes any parts of road that have scrolled off the bottom of the screen.
176+
*/
177+
private void recycleRoadSegments()
178+
{
179+
for (int i=0; i<road.length; i++)
180+
{
181+
if (road[i].getYPosition() > SCREEN_HEIGHT)
182+
{
183+
double y = road[i].getYPosition();
184+
road[i].remove();
185+
road[i] = nextRoadSegment();
186+
road[i].setYPosition(y - SCREEN_HEIGHT - ROAD_SEGMENT_HEIGHT);
187+
}
188+
}
189+
}
190+
191+
/**
192+
* Determines if the player has crased (driven off road)
193+
*
194+
* @return true is the player is touching the kerb/grass, false otherwise.
195+
*/
196+
private boolean hasCrashed()
197+
{
198+
for (int i=0; i<road.length; i++)
199+
{
200+
if (player.isTouching(road[i]))
201+
return true;
202+
}
203+
204+
return false;
205+
}
206+
207+
/**
208+
* A simple example of usage
209+
*
210+
* @param args unused.
211+
*/
212+
public static void main(String[] args)
213+
{
214+
JFrame window = new JFrame();
215+
Racer r = new Racer();
216+
217+
window.setTitle("Racer");
218+
window.setSize(SCREEN_WIDTH, SCREEN_HEIGHT);
219+
window.setContentPane(r.getPanel());
220+
window.setVisible(true);
221+
222+
r.start();
223+
224+
while(r.isPlaying())
225+
r.update();
226+
}
227+
}

‎Rectangle.java

+135
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
/**
2+
* Models a simple, solid rectangle.
3+
* This class represents a Rectabgle object. When combined with the GameArena class,
4+
* instances of the Rectangle class can be displayed on the screen.
5+
*/
6+
public class Rectangle
7+
{
8+
// The following instance variables define the
9+
// information needed to represent a Rectangle
10+
// Feel free to more instance variables if you think it will
11+
// support your work...
12+
13+
private double xPosition; // The X coordinate of centre of this Rectangle
14+
private double yPosition; // The Y coordinate of centre of this Rectangle
15+
private double width; // The width of this Rectangle
16+
private double height; // The height of this Rectangle
17+
private String colour = "WHITE"; // The colour of this Rectangle
18+
19+
// Permissable colours are 8 bit hexadecimal
20+
// RGB values in the format #RRGGBB. e.g.
21+
//
22+
// Pure red is FF0000
23+
// Pure lime is 00FF00
24+
// Pure blue is 0000FF
25+
26+
/**
27+
* Obtains the current position of this Rectangle.
28+
* @return the X coordinate of this Rectangle within the GameArena.
29+
*/
30+
public double getXPosition()
31+
{
32+
return xPosition;
33+
}
34+
35+
/**
36+
* Obtains the current position of this Rectangle.
37+
* @return the Y coordinate of this Rectangle within the GameArena.
38+
*/
39+
public double getYPosition()
40+
{
41+
return yPosition;
42+
}
43+
44+
/**
45+
* Moves the current position of this Rectangle to the given X co-ordinate
46+
* @param x the new x co-ordinate of this Rectangle
47+
*/
48+
public void setXPosition(double x)
49+
{
50+
this.xPosition = x;
51+
}
52+
53+
/**
54+
* Moves the current position of this Rectangle to the given Y co-ordinate
55+
* @param y the new y co-ordinate of this Rectangle
56+
*/
57+
public void setYPosition(double y)
58+
{
59+
this.yPosition = y;
60+
}
61+
62+
/**
63+
* Obtains the width of this Rectangle.
64+
* @return the width of this Rectangle,in pixels.
65+
*/
66+
public double getWidth()
67+
{
68+
return width;
69+
}
70+
71+
/**
72+
* Defines a new width for this Rectangle.
73+
* @param width The new width of this rectangle, in pixels.
74+
*/
75+
public void setWidth(double width)
76+
{
77+
this.width = width;
78+
}
79+
80+
/**
81+
* Obtains the height of this Rectangle.
82+
* @return the height of this Rectangle,in pixels.
83+
*/
84+
public double getHeight()
85+
{
86+
return height;
87+
}
88+
89+
/**
90+
* Defines a new width for this Rectangle.
91+
* @param height The new height of this rectangle, in pixels.
92+
*/
93+
public void setHeight(double height)
94+
{
95+
this.height = height;
96+
}
97+
98+
/**
99+
* Obtains the colour of this Rectangle.
100+
* @return a textual description of the colour of this Rectangle.
101+
*/
102+
public String getColour()
103+
{
104+
return colour;
105+
}
106+
107+
/**
108+
* Changes the colour of this Rectangle
109+
* @param input the new colour of this Rectangle
110+
*/
111+
112+
public void setColour(String input)
113+
{
114+
this.colour = input;
115+
}
116+
117+
public Rectangle(double x, double y, double w, double h, String col)
118+
{
119+
xPosition = x;
120+
yPosition = y;
121+
width = w;
122+
height = h;
123+
colour = col;
124+
}
125+
126+
public boolean isTouching(Rectangle r)
127+
{
128+
return (xPosition - width/2 < r.getXPosition() + r.getWidth()/2 &&
129+
xPosition + width/2 > r.getXPosition() - r.getWidth()/2 &&
130+
yPosition - height/2 < r.getYPosition() + r.getHeight()/2 &&
131+
yPosition + height/2 > r.getYPosition() - r.getHeight()/2);
132+
}
133+
134+
135+
}

‎RoadSegment.java

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/**
2+
* Models a simple slice of road, a kerb and offroad grass for the game Racer.
3+
*
4+
* @author Joe Finney (joe@comp.lancs.ac.uk)
5+
*/
6+
public class RoadSegment
7+
{
8+
public static String ROUGH_COLOUR = "#00FF00";
9+
public static String KERB_COLOUR = "#808080";
10+
public static double KERB_WIDTH = 10;
11+
12+
private Rectangle[] parts = new Rectangle[4];
13+
private double xPosition;
14+
private double yPosition;
15+
private double width;
16+
private double height;
17+
private GameArena arena;
18+
19+
private double xSpeed;
20+
private double ySpeed;
21+
22+
/**
23+
* Creates a new RoadSegment, at the given location and size.
24+
*
25+
* @param x The x position on the screen of the road segment.
26+
* @param y The y position on the screen of the road segment.
27+
* @param width The width of this road segment (in pixels)
28+
* @param height The height of this road segment (in pixels)
29+
* @param a The GameArena upon which to draw this road segment.
30+
*/
31+
public RoadSegment(double x, double y, double width, double height, GameArena a)
32+
{
33+
this.width = width;
34+
this.height = height;
35+
arena = a;
36+
37+
double roughWidth1 = x - width/2;
38+
double roughWidth2 = arena.getArenaWidth() - (x + width/2);
39+
40+
double roughX1 = -width/2 - roughWidth1/2;
41+
double roughX2 = width/2 + roughWidth2/2;
42+
43+
parts[0] = new Rectangle(roughX1, height/2, roughWidth1, height, ROUGH_COLOUR);
44+
parts[1] = new Rectangle(roughX2, height/2, roughWidth2, height, ROUGH_COLOUR);
45+
parts[2] = new Rectangle(-width/2-KERB_WIDTH/2, height/2, KERB_WIDTH, height, KERB_COLOUR);
46+
parts[3] = new Rectangle(width/2+KERB_WIDTH/2, height/2, KERB_WIDTH, height, KERB_COLOUR);
47+
48+
this.setXPosition(x);
49+
this.setYPosition(y);
50+
51+
for (int i=0; i < parts.length; i++)
52+
arena.addRectangle(parts[i]);
53+
}
54+
55+
/**
56+
* Changes the position of this RoadSegment to the given location
57+
*
58+
* @param x The new x positition of this RoadSegment on the screen.
59+
*/
60+
public void setXPosition(double x)
61+
{
62+
double dx = x - xPosition;
63+
64+
for (int i=0; i < parts.length; i++)
65+
parts[i].setXPosition(parts[i].getXPosition() + dx);
66+
67+
xPosition = x;
68+
}
69+
70+
/**
71+
* Changes the position of this RoadSegment to the given location
72+
*
73+
* @param y The new y positition of this RoadSegment on the screen.
74+
*/
75+
public void setYPosition(double y)
76+
{
77+
double dy = y - yPosition;
78+
for (int i=0; i < parts.length; i++)
79+
parts[i].setYPosition(parts[i].getYPosition() + dy);
80+
81+
yPosition = y;
82+
}
83+
84+
/**
85+
* Determines the position of this RoadSegment on the screen
86+
*
87+
* @return The x position of the centre of this RoadSegment on the screen.
88+
*/
89+
public double getXPosition()
90+
{
91+
return xPosition;
92+
}
93+
94+
/**
95+
* Determines the position of this RoadSegment on the screen
96+
*
97+
* @return The y position of the centre of this RoadSegment on the screen.
98+
*/
99+
public double getYPosition()
100+
{
101+
return yPosition;
102+
}
103+
104+
/**
105+
* Provides a list (array) containing all the GameArena Rectangle objects that
106+
* make up this road segment.
107+
*
108+
* @return The list of Rectangles that makes up this RoadSegment
109+
*/
110+
public Rectangle[] getParts()
111+
{
112+
return parts;
113+
}
114+
115+
/**
116+
* Sets the speed of this RoadSegment in the X axis - i.e. the number of pixels it moves in the X axis every time move() is called.
117+
*
118+
* @param s The new speed of this RoadSegment in the x axis
119+
*/
120+
public void setXSpeed(double s)
121+
{
122+
xSpeed = s;
123+
}
124+
125+
/**
126+
* Sets the speed of this RoadSegment in the Y axis - i.e. the number of pixels it moves in the Y axis every time move() is called.
127+
*
128+
* @param s The new speed of this RoadSegment in the y axis
129+
*/
130+
public void setYSpeed(double s)
131+
{
132+
ySpeed = s;
133+
}
134+
135+
/**
136+
* Updates the position of this RoadSegment by a small amount, depending upon its speed.
137+
* see setXSpeed(0 and setYSpeed() methods.
138+
*/
139+
public void move()
140+
{
141+
this.setXPosition(xPosition + xSpeed);
142+
this.setYPosition(yPosition + ySpeed);
143+
}
144+
145+
/**
146+
* Delete all the Rectangles that make up this RoadSegment from the screen
147+
*/
148+
public void remove()
149+
{
150+
for (int i=0; i < parts.length; i++)
151+
arena.removeRectangle(parts[i]);
152+
}
153+
}

0 commit comments

Comments
 (0)
Please sign in to comment.