Breakout Game!
Hej!
Jag håller på med att implementera spelet Breakout, där en del Kod är redan given.
Workflow:
1. Let model for now consist of a only a Breakout and a Ball object (= model_v1). Add some
sensible instance variables for the Ball (this is trial and error).
som lösning på 1) så har jag adderat till Klassen Ball följande:
public class Ball {
private double x; //x-koor.
private double y; //y-koor.
private double hx; //hastighet i x-led
private double hy; //.....=......y-led
private double d; //diameter
private boolean canMove;
//......Constructor;
public Ball(double x, double y, double hx, double hy, double d, boolean canMove) {
this.x = x;
this.y = y;
this.hx = hx;
this.hy = hy;
this.d = d;
this.canMove = canMove;
}
//.......getters && setters...
public double getX() {return x;}
public void setX(double x) {
this.x = x;
}
public double getY() {return y;}
public void setY(double y) {
this.y = y;
}
public double getHx() {return hx;}
public void setHx(double dx) {
this.hx = dx;
}
public double getHy() {return hy;}
public void setHy(double dy) {
this.hy = dy;
}
public double getD() {return d;}
public boolean canMove() {return canMove;}
public void setCanMove(boolean canMove) {
this.canMove = canMove;
}
}
I klassen Breakout finns följande (Kod som redan given):
/*
* Overall all logic for the Breakout Game
* Model class representing the full game
* This class should use other objects delegate work.
*
* NOTE: Nothing visual here
*
*/
public class Breakout {
public static final double GAME_WIDTH = 400;
public static final double GAME_HEIGHT = 400;
public static final double BALL_SPEED_FACTOR = 1.05; // Increase ball speed
public static final long SEC = 1_000_000_000; // Nano seconds used by JavaFX
private int nBalls = 5;
int playerPoints;
// TODO Constructor that accepts all objects needed for the model
// -------- Game Logic -------------
private long timeForLastHit; // To avoid multiple collisions
public void update(long now) {
// TODO Main game loop, start functional decomposition from here
}
// ----- Helper methods--------------
// Used for functional decomposition
// --- Used by GUI ------------------------
public List<IPositionable> getPositionables() {// TODO return all objects to be rendered by GUI
return null;
}
public int getPlayerPoints() {
return playerPoints;
}
public int getnBalls() {
return nBalls;
}
}
2. In BreakoutGUI > newGame(). Try to build the the model_v1. Pass in dependencies of other
object using constructors. Add constructors as needed. Debug to inspect the model.
Are the objects there and connected?
Som lösning på två så har jag skapat en objekt ball och objekt cirkle under newGame() metoden.
Klassen BreakoutGUI:
/*
* The GUI for the Breakout game (except the menu).
* No application logic here just GUI and event handling
*
* Run this to run the game
*
* See: https://en.wikipedia.org/wiki/Breakout_(video_game)
*
*/
public class BreakoutGUI extends Application implements IEventHandler {
private Breakout breakout; // The only reference to the model allowed
private boolean running = false; // Is game running?
// ------- Keyboard events ----------------------------------
private void keyPressed(KeyEvent event) {
if (!running) {
return;
}
KeyCode kc = event.getCode();
switch (kc) {
case LEFT:
// TODO
break;
case RIGHT:
// TODO
break;
default: // Nothing
}
}
private void keyReleased(KeyEvent event) {
if (!running) {
return;
}
KeyCode kc = event.getCode();
switch (kc) {
case LEFT: // No break, fall through
case RIGHT:
// TODO
break;
default: // Nothing
}
}
// ---------- Menu actions ---------------------
private void newGame() {
// GUI handling
menu.fixMenusNewGame();
renderBackground();
// --- Build the model -----
// TODO Build the model (also: see methods below)
Ball ball = new Ball(GAME_WIDTH/2, GAME_HEIGHT/2, 1, -1, 20, true);
Ellipse circle = new Ellipse(ball.getX(), ball.getY(), ball.getD(),ball.getD());
// Bind bricks to images
//bindBricks(bricks); // TODO
// Start game
timer.start();
running =true;
}
private void killGame() {
timer.stop();
menu.fixMenusKillGame();
renderSplash();
running = false;
}
3. Try to make the ball appear on the screen. The method getPositionables in Breakout must
be implemented. The GUI will periodically call the method to get a list of objects
to draw (all must implement positionable).
Har får jag problem med att implementera metoden getPositionables som är given i klassen Breakout.
Det står att metoden skall returnera en lista, ska jag då under metoden getPositionables skapa en töm lista och addera objekt cirkle till den för att sedan returnera listan?
Ja, objekten i listan är de du vill ha utritade. Objekten ska implementera interfacet IPositionable så det måste du lägga till. Eftersom jag inte vet hur interfacet ser ut är det svårt att veta om Ball förväntas ha interfacet eller om du ska ha en grafisk representation av Ball som returneras. Skulle tro att getPositionables() kan returnera samma lista varje gång, den där du har dina objekt som Ball(s).
Därför känns det lite tveksamt att du skapar Ellipse i newGame(), det ser ut som att modellen ska skapas där och inte den grafiska representationen av modellen. Känns mer som du borde addera Ball till Breakout eftersom Breakout är hela modellen. Nu skapar du Ball och gör inget med den.
Om Breakout har hela modellen kan du ha en metod för att skapa en Ball och lägga till modellen.
Men som sagt, jag gissar lite i blindo.
Programmeraren skrev:Ja, objekten i listan är de du vill ha utritade. Objekten ska implementera interfacet IPositionable så det måste du lägga till. Eftersom jag inte vet hur interfacet ser ut är det svårt att veta om Ball förväntas ha interfacet eller om du ska ha en grafisk representation av Ball som returneras. Skulle tro att getPositionables() kan returnera samma lista varje gång, den där du har dina objekt som Ball(s).
Därför känns det lite tveksamt att du skapar Ellipse i newGame(), det ser ut som att modellen ska skapas där och inte den grafiska representationen av modellen. Känns mer som du borde addera Ball till Breakout eftersom Breakout är hela modellen. Nu skapar du Ball och gör inget med den.
Om Breakout har hela modellen kan du ha en metod för att skapa en Ball och lägga till modellen.
Men som sagt, jag gissar lite i blindo.
Har nu märkt att det finns en till Klass:
Specification for anything that can be positioned in the world.
NOTE: This must be fulfilled by any object the GUI shall render
*/
public interface IPositionable {
double getX(); // Min x and y is upper left corner (y-axis pointing donw)
double getY();
double getWidth();
double getHeight();
}
den klassen har redan några variabler, ska man använda de variabler ?
Ett interface är ett sätt att fördefiniera en klass va? så att alla klasser håller samma struktur.
ska interface IPositionable innehålla metoder som andra klasser ska ha?
Ett interface är ett sätt att frikoppla gränssnittet mot klassen från implementation av klassen och på det sätt kan man ändra implementationen vid behov utan att du som använder den behöver ändra, eller ens kompilera om, din kod.
Tänk dig att någon annan tillhandahåller objekt som implementerar interfacet och se interfacet som ett kontrakt som klassimplementatören har förbundit sig att upprätthålla. Du behöver inte bekymra dig om implementationen.
Ett typiskt exempel är en metod som returnerar en lista av grafiska objekt. Tänk dig att dessa implementerar ett interface och att listan är en lista av just dessa interface - typ List<Graphicable> - Den kan innehålla t ex cirklar, ellipser, kvadrater etc och om interfacet innehåller en metod för att hämta objektets area så kan du anropa den för alla objekten och veta att den finns på objektet utan att bry dig om vilken typ av grafiskt objekt det är.
Hej!
Har nu lyckats med en del av implementationen av spelet.
1) har fått ball, Paddle att dyka up.
2) fixat collisionen mellan ballen och (top,left,right) walls, så att bollen studsar.
Min fråga är:
hur ska jag fixa collisionen mellan ball och paddle ?
koden jag har hittills är :
public void checkPaddle(Ball ball, Paddle paddle) {
if (ball.getX() > paddle.getX() && ball.getX() < paddle.getX() - Paddle.PADDLE_WIDTH){
if (ball.getY() > paddle.getY() && ball.getY() < paddle.getY() - Paddle.PADDLE_HEIGHT) {
ball.setHy(ball.getY() * -1);
ball.setHx(ball.getX() * -1);
}
}
}
Ball studsar inte när den möter paddle. vart kan felet ligga ?
Det ska nog vara + WIDTH och inte - WIDTH, osv.
Nu är det dags att addera bricks till spelet, enligt följande :
9. In the same manner add Bricks. To avoid multiple collisions use the time supplied by
JavaFX (parameter now in update(), variable timeForLastHit).
De här två metoder är givna och skall användas:
// Create the formation of bricks
private List<Brick> getBricks(int nRows, int nCols) {
List<Brick> bricks = new ArrayList<>();
int bw = (int) BRICK_WIDTH;
int bh = (int) BRICK_HEIGHT;
int offset = 5;
int points = 300;
for (int y = 10 * offset; y < nRows * (bh + offset); y += bh + offset) {
for (int x = offset - 2; x < nCols * (bw + offset); x += bw + offset) {
//TODO Brick b = new Brick(BRICK_WIDTH*nCols,BRICK_HEIGHT*nRows,true);
bricks.add(b);
}
points -= 100;
}
return bricks;
}
// Bind bricks to images
private void bindBricks(List<Brick> bricks) { // TODO
for (Brick b : bricks) {
switch (//TODO) {
case 100:
assets.bind(b, assets.greenTile);
break;
case 200:
assets.bind(b, assets.blueTile);
break;
case 300:
assets.bind(b, assets.redTile);
break;
default:
; // Nothing
}
}
}
På första metoden ska jag addera HL i raden som är markerat med //TODO, vilket jag gjorde.
På andra metoden ska jag ha något int värde i Switch(...), vilket jag inte har någon aning om.
Här är logiken för hela spelet än så länge:
public class Breakout {
public static final double GAME_WIDTH = 400;
public static final double GAME_HEIGHT = 400;
public static final double BALL_SPEED_FACTOR = 1.05; // Increase ball speed
public static final long SEC = 1_000_000_000; // Nano seconds used by JavaFX
private int nBalls = 5;
int playerPoints;
//Constructor with all objects needed for the model;
public Breakout(Ball ball, Wall wall, Paddle paddle, Brick brick) {
this.ball = ball;
this.wall = wall;
this.paddle = paddle;
this.brick = brick;
}
Wall wall;
Ball ball;
Paddle paddle;
Brick brick;
// -------- Game Logic -------------
private long timeForLastHit; // To avoid multiple collisions
public void update(long now) {
moveBall(ball);
checkWalls(ball);
checkPaddle(ball,paddle);
}
// ----- Helper methods--------------
// Used for functional decomposition
//Ball movements;
public void moveBall(Ball ball) {
if (ball.isCanMove()) {
ball.setX(ball.getX() + ball.getHx());
ball.setY(ball.getY() + ball.getHy());
}
}
public void checkWalls(Ball ball){
moveBall(ball);
if (ball.getX() > GAME_WIDTH - 5 || ball.getX() < 5 ){
ball.setHx(ball.getHx() * -1);
}
if (ball.getY() < 5){
ball.setHy(ball.getHy() * -1);
}
if (ball.getY() > GAME_HEIGHT){
ball.setCanMove(false);
nBalls--;
ball.setX(GAME_WIDTH/2);
ball.setY(GAME_HEIGHT/2);
ball.setHx(Math.random() * 5);
ball.setHy(-2);
}
}
// Paddel Movements;
public void moveRight() {
ball.setCanMove(true);
if (paddle.getX() >= GAME_WIDTH-65) {
paddle.setX(GAME_WIDTH-65);
}else
if (paddle.isCanMove()) {
paddle.setX(paddle.getX() + 20);
}
}
public void moveLeft() {
ball.setCanMove(true);
if (paddle.getX() <20) {
paddle.setX(5);
}else{
if (paddle.isCanMove()) {
paddle.setX(paddle.getX() - 20);
}
}
}
public void checkPaddle(Ball ball, Paddle paddle) {
if (ball.getX() > paddle.getX() && ball.getX() < paddle.getX() + paddle.getWidth()
&& ball.getY() > paddle.getY() - ball.getD()/2 && ball.getY() < paddle.getY()+2){
ball.setHy(-ball.getHy());
}
}
// --- Used by GUI ------------------------
public List<IPositionable> getPositionables() {
List<IPositionable> shapes = new ArrayList<>();
shapes.add(ball);
shapes.add(wall);
shapes.add(paddle);
shapes.add(brick);
return shapes;
}
public int getPlayerPoints() {
return playerPoints;
}
public int getnBalls() {
return nBalls;
}
}
Här är GUI och event handling:
public class BreakoutGUI extends Application implements IEventHandler {
private Breakout breakout; // The only reference to the model allowed
private boolean running = false; // Is game running?
// ------- Keyboard events ----------------------------------
private void keyPressed(KeyEvent event) {
if (!running) {
return;
}
KeyCode kc = event.getCode();
switch (kc) {
case LEFT:
breakout.moveLeft();
break;
case RIGHT:
breakout.moveRight();
break;
default: // Nothing
}
}
private void keyReleased(KeyEvent event) {
if (!running) {
return;
}
KeyCode kc = event.getCode();
switch (kc) {
case LEFT: // No break, fall through
case RIGHT:
// TODO
break;
default: // Nothing
}
}
// ---------- Menu actions ---------------------
private void newGame() {
// GUI handling
menu.fixMenusNewGame();
renderBackground();
// --- Build the model -----
Ball ball = new Ball(GAME_WIDTH/2, GAME_HEIGHT/2, Math.random()*5, -2, 12, false);
Wall wall = new Wall();
Paddle paddle = new Paddle(GAME_WIDTH/2, GAME_HEIGHT-30, Paddle.PADDLE_WIDTH,Paddle.PADDLE_HEIGHT,true);
Brick brick = new Brick(BRICK_WIDTH,BRICK_HEIGHT,true);
breakout = new Breakout(ball, wall, paddle, brick);
bindBricks(getBricks(3,16));
// Bind bricks to images
//bindBricks(bricks); // TODO
// Start game
timer.start();
running =true;
}
private void killGame() {
timer.stop();
menu.fixMenusKillGame();
renderSplash();
running = false;
}
// ---------- Helper to build model ------------
// Create the formation of bricks
private List<Brick> getBricks(int nRows, int nCols) {
List<Brick> bricks = new ArrayList<>();
int bw = (int) BRICK_WIDTH;
int bh = (int) BRICK_HEIGHT;
int offset = 5;
int points = 300;
for (int y = 10 * offset; y < nRows * (bh + offset); y += bh + offset) {
for (int x = offset - 2; x < nCols * (bw + offset); x += bw + offset) {
Brick b = new Brick(BRICK_WIDTH*nCols,BRICK_HEIGHT*nRows,true);
bricks.add(b);
}
points -= 100;
}
return bricks;
}
// Bind bricks to images
private void bindBricks(List<Brick> bricks) { // TODO
for (Brick b : bricks) {
switch (100) {
case 100:
assets.bind(b, assets.greenTile);
break;
case 200:
assets.bind(b, assets.blueTile);
break;
case 300:
assets.bind(b, assets.redTile);
break;
default:
; // Nothing
}
}
}
// -------- Event handling (events sent from model to GUI) -----------
@Override
public void onModelEvent(ModelEvent evt) {
if (evt.type == ModelEvent.Type.BALL_HIT_PADDLE) {
// TODO Play a sound
} else if (evt.type == ModelEvent.Type.BALL_HIT_BRICK) {
// TODO Play a sound
}
}
Klassen Brick :
public class Brick implements IPositionable {
public static final double BRICK_WIDTH = 20; // Default values, use in constructors, not directly
public static final double BRICK_HEIGHT = 10;
private double x;
private double y;
private boolean status;
public Brick(double x, double y, boolean status) {
this.x = x;
this.y = y;
this.status = status;
}
@Override
public double getX() {
return x;
}
@Override
public double getY() {
return y;
}
@Override
public double getWidth() {
return BRICK_WIDTH;
}
@Override
public double getHeight() {
return BRICK_HEIGHT;
}
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
}
Har inte riktigt koll om min klass Brick funkar bra, och om hur ska jag koppla ihop objektet brick med de två metoder som skall användas för att få bricks att dyka upp i spelet.
Switchen med 100, 200 och 300 verkar bestämma färg, så du kan lägga till ett attribut för det i Brick, och sätta det till nåt av värdena slumpmässigt eller systematiskt.
Varför de har just 100, 200 och 300 vet jag inte.