首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用Java 8和Slick的越狱游戏

使用Java 8和Slick的越狱游戏
EN

Code Review用户
提问于 2015-04-26 12:07:26
回答 1查看 841关注 0票数 6

我已经编写了一个使用Java 8& Slick (大约400行代码)实现B爆发的基本实现。请让我知道任何可以进行的设计/面向对象的改进(欢迎所有的改进,但我特别寻求设计和面向对象的改进)。

游戏-主类

代码语言:javascript
运行
复制
package breakout;

import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.StateBasedGame;

public class Game extends StateBasedGame{
    public static final String gameName = "Breakout!";
    public static final int play = 0;
    public static final int gameWon = 1;
    public static final int gameOver = 2;

    public static final int FRAME_HEIGHT = 500;
    public static final int FRAME_WIDTH = 640;

    public Game(String gameName){
        super(gameName);
        UserInfo userInfo = new UserInfo();
        this.addState(new Play(userInfo));
        this.addState(new GameWon(userInfo));
        this.addState(new GameOver(userInfo));
    }

    @Override
    public void initStatesList(GameContainer gc) {
        try {
            this.getState(play).init(gc, this);
            this.enterState(play);
        }catch(SlickException e){
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        // a game container that displays the game as a stand alone application
        AppGameContainer appGC;
        try{
            appGC = new AppGameContainer(new Game(gameName), FRAME_WIDTH, FRAME_HEIGHT, false);
            appGC.setVSync(true);   // sets FPS to screen's refresh rate
            appGC.start();
        }catch(SlickException e){
            e.printStackTrace();
        }
    }
}

Ball类-设置位置并处理与砖块的碰撞

代码语言:javascript
运行
复制
package breakout;

import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;

public class Ball implements Collision {
    private Image image;
    private int ballDiameter;

    private int positionX = 450;
    private int positionY = 250;
    private int velocityX;
    private int velocityY;


    public Ball(String imageLocation) throws SlickException{
        float scalingFactor = 0.06f;
        image = new Image(imageLocation);
        image = image.getScaledCopy((int) (scalingFactor * image.getWidth()), (int) (scalingFactor * image.getHeight()));
        ballDiameter = image.getWidth();

        velocityX = -3;
        velocityY = 3;
    }

    public void move(){
        positionX += velocityX;
        positionY += velocityY;
        collideWithVerticalWall();
        collideWithHorizontalWall();
    }

    public void collide(Brick brick){
        int tolerance = 3;
        if(!brick.isDestroyed()) {
            if (Collision.topSide(this, brick, tolerance) || Collision.bottomSide(this, brick, tolerance)) {
                flipVelocityY();
                brick.changeImage();
            }

            if (Collision.leftSide(this, brick, tolerance) || Collision.rightSide(this, brick, tolerance)) {
                flipVelocityX();
                brick.changeImage();
            }
        }
    }

    private void collideWithHorizontalWall(){
        if(positionY <= 0){     // ignore this comment: || positionY >= Game.FRAME_HEIGHT - ballDiameter
            flipVelocityY();
        }
    }

    private void collideWithVerticalWall(){
        if(positionX <= 0 || positionX >= Game.FRAME_WIDTH - ballDiameter){
            flipVelocityX();
        }
    }

    public void flipVelocityX(){
        velocityX = -velocityX;
    }

    public void flipVelocityY(){
        velocityY = -velocityY;
    }

    // GETTERS
    @Override
    public int getHeight(){
        return ballDiameter;
    }

    public Image getImage(){
        return image;
    }

    @Override
    public int getPositionX(){
        return positionX;
    }

    @Override
    public int getPositionY(){
        return positionY;
    }

    @Override
    public int getWidth(){
        return ballDiameter;
    }

}

Brick类-包含每个砖块

的图像等

代码语言:javascript
运行
复制
package breakout;

import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;

import java.util.stream.IntStream;

public class Brick implements Collision {
    public static final String[] imageLocations = {"res/brick_pink.png", "res/brick_pink_cracked.png",
            "res/brick_transparent.png"};
    public static final int[] points = {0, 10, 20};
    public static final int pointsPerBrick = IntStream.of(points).sum();

    private Image[] images;
    private int imageIndex = 0;
    private int brickHeight;
    private int brickWidth;
    private int positionX;
    private int positionY;

    private UserInfo userInfo;


    public Brick(String[] imageLocations, UserInfo userInfo, int positionX, int positionY) throws SlickException{
        float scalingFactor = 0.25f;
        images = new Image[imageLocations.length];

        for(int i = 0; i < imageLocations.length; i++) {
            images[i] = new Image(imageLocations[i]);
            images[i] = images[i].getScaledCopy((int) (scalingFactor * images[i].getWidth()),
                    (int) (scalingFactor * images[i].getHeight()));
        }

        brickHeight = images[imageIndex].getHeight();
        brickWidth = images[imageIndex].getWidth();

        this.userInfo = userInfo;
        this.positionX = positionX;
        this.positionY = positionY;
    }


    public void changeImage(){
        if(imageIndex < images.length - 1) {
            imageIndex++;
            brickHeight = images[imageIndex].getHeight();
            brickWidth = images[imageIndex].getWidth();
            userInfo.incrementScore(points[imageIndex]);
        }
    }

    public boolean isDestroyed(){
        return imageIndex == images.length - 1;
    }


    // GETTERS
    @Override
    public int getHeight(){
        return brickHeight;
    }

    public Image getImage(){
        return images[imageIndex];
    }

    @Override
    public int getPositionX(){
        return positionX;
    }

    @Override
    public int getPositionY(){
        return positionY;
    }

    @Override
    public int getWidth(){
        return brickWidth;
    }
}

冲突接口-确定对象是否碰撞(包含默认方法)的接口

代码语言:javascript
运行
复制
package breakout;

// Deals with the collisions in this game. Any class that implements this interface have objects that are "Collidable"
// and can therefore use these methods.
public interface Collision {
    // TODO need to neaten these conditions up. Not sure how.
    static boolean bottomSide(Collision self, Collision other, int tolerance){
        // tolerance is included as the ball's velocity is 3, so the ball may not exactly touch the paddle when it
        // reaches it because it's moving in increments of 3.
        return other.getPositionX() + other.getWidth() >= self.getPositionX()
                && other.getPositionX() <= self.getPositionX() + self.getWidth()
                && other.getPositionY() <= self.getPositionY() + self.getHeight()
                && other.getPositionY() >= self.getPositionY() + self.getHeight() - tolerance;
    }

    static boolean leftSide(Collision self, Collision other, int tolerance){
        return other.getPositionY() + other.getHeight() >= self.getPositionY()
                && other.getPositionY() <= self.getPositionY() + self.getHeight()
                && other.getPositionX() + other.getWidth() >= self.getPositionX()
                && other.getPositionX() + other.getWidth() <= self.getPositionX() + tolerance;
    }

    static boolean rightSide(Collision self, Collision other, int tolerance){
        return other.getPositionY() + other.getHeight() >= self.getPositionY()
                && other.getPositionY() <= self.getPositionY() + self.getHeight()
                && other.getPositionX() <= self.getPositionX() + self.getWidth()
                && other.getPositionX() >= self.getPositionX() + self.getWidth() - tolerance;
    }

    static boolean topSide(Collision self, Collision other, int tolerance){
        return other.getPositionX() + other.getWidth() >= self.getPositionX()
                && other.getPositionX() <= self.getPositionX() + self.getWidth()
                && other.getPositionY() + other.getHeight() >= self.getPositionY()
                && other.getPositionY() + other.getHeight() <= self.getPositionY() + tolerance;
    }


    // GETTERS
    int getPositionX();

    int getPositionY();

    int getWidth();

    int getHeight();
}

GameWon类游戏状态,它告诉用户他们已经完成了

级别

代码语言:javascript
运行
复制
package breakout;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;

public class GameWon extends BasicGameState {
    private UserInfo userInfo;

    public GameWon(UserInfo userInfo){
        this.userInfo = userInfo;
    }

    @Override
    public int getID(){
        return Game.gameWon;
    }

    @Override
    public void init(GameContainer gc, StateBasedGame sbg){

    }

    @Override
    public void render(GameContainer gc, StateBasedGame sbg, Graphics g) {
        try{
            Image backgroundImage = new Image("res/background.jpg");
            g.drawImage(backgroundImage, 0, 0);
            g.drawString("Well done, you completed the game! Your score: " + userInfo.getScore(), 100, 100);
        }catch(SlickException e){
            e.printStackTrace();
        }
    }

    @Override
    public void update(GameContainer gc, StateBasedGame sbg, int delta){

    }
}

GameOver类游戏状态,它在无法完成级别

后告诉用户他们的得分。

代码语言:javascript
运行
复制
package breakout;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;

public class GameOver extends BasicGameState {
    private UserInfo userInfo;

    public GameOver(UserInfo userInfo){
        this.userInfo = userInfo;
    }

    @Override
    public int getID(){
        return Game.gameOver;
    }

    @Override
    public void init(GameContainer gc, StateBasedGame sbg){

    }

    @Override
    public void render(GameContainer gc, StateBasedGame sbg, Graphics g) {
        try{
            Image backgroundImage = new Image("res/background.jpg");
            g.drawImage(backgroundImage, 0, 0);
            g.drawString("Game over. Your score: " + userInfo.getScore(), 150, 100);
        }catch(SlickException e){
            e.printStackTrace();
        }
    }

    @Override
    public void update(GameContainer gc, StateBasedGame sbg, int delta){

    }
}

Paddle类-包含与paddle

相关的所有方法等等。

代码语言:javascript
运行
复制
package breakout;

import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;

public class Paddle implements Collision {
    private Image image;
    private int paddleHeight;
    private int paddleWidth;

    private int positionX;
    private int positionY;
    private int velocity = 3;


    public Paddle(String imageLocation) throws SlickException{
        double scalingFactor = 0.2;
        this.image = new Image(imageLocation);
        image = image.getScaledCopy((int) (scalingFactor * image.getWidth()), (int) (scalingFactor * image.getHeight()));
        paddleHeight = image.getHeight();
        paddleWidth = image.getWidth();
        positionX = (Game.FRAME_WIDTH - image.getWidth()) / 2;
        positionY = Game.FRAME_HEIGHT - image.getHeight();
    }


    public void collide(Ball ball){
        int tolerance = 3;
        // collide with top side
        if(Collision.topSide(this, ball, tolerance) || Collision.bottomSide(this, ball, tolerance)){
            ball.flipVelocityY();
        }
        // collide with left or right side
        if(Collision.leftSide(this, ball, tolerance) || Collision.rightSide(this, ball, tolerance)){
            ball.flipVelocityX();
        }
    }

    public void move(Input input){
        // Prevents paddle from moving outside of the frame
        if(getPositionX() > 0) {
            moveLeft(input);
        }
        if (getPositionX() < Game.FRAME_WIDTH - paddleWidth){
            moveRight(input);
        }
    }

    private void moveLeft(Input input){
        if(input.isKeyDown(Input.KEY_LEFT)){
            adjustPositionX(-velocity);
        }
    }

    private void moveRight(Input input){
        if(input.isKeyDown(Input.KEY_RIGHT)){
            adjustPositionX(velocity);
        }
    }


    // GETTERS
    @Override
    public int getHeight(){
        return paddleHeight;
    }

    public Image getImage(){
        return image;
    }

    @Override
    public int getPositionX(){
        return positionX;
    }

    @Override
    public int getPositionY(){
        return positionY;
    }

    @Override
    public int getWidth(){
        return paddleWidth;
    }


    // SETTERS
    public void adjustPositionX(float delta){
        positionX += delta;
    }
}

当用户正在播放级别

时,

玩类游戏状态。

代码语言:javascript
运行
复制
package breakout;

import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;


public class Play extends BasicGameState{
    private Image backgroundImage;
    private UserInfo userInfo;
    private Paddle paddle;
    private Ball ball;
    private Brick[] bricks;
    private int numBricks = 3;

    public Play(UserInfo userInfo){
        this.userInfo = userInfo;
    }


    @Override
    public int getID(){
        return Game.play;
    }

    @Override
    public void init(GameContainer gc, StateBasedGame sbg){
        bricks = new Brick[numBricks];

        try{
            backgroundImage = new Image("res/background.jpg");
            paddle = new Paddle("res/bat_yellow.png");
            ball = new Ball("res/ball_red.png");

            for(int i = 0; i < bricks.length; i++) {
                // the maths here is used to position the bricks in rows of 10
                bricks[i] = new Brick(Brick.imageLocations, userInfo, (i % 10) * 60 + 20, ((i / 10) + 1) * 40);
            }
        } catch(SlickException e){
            e.printStackTrace();
        }
    }

    @Override
    public void render(GameContainer gc, StateBasedGame sbg, Graphics g){
        g.drawImage(backgroundImage, 0, 0);
        g.drawImage(paddle.getImage(), paddle.getPositionX(), paddle.getPositionY());
        g.drawImage(ball.getImage(), ball.getPositionX(), ball.getPositionY());

        for(Brick brick : bricks) {
            g.drawImage(brick.getImage(), brick.getPositionX(), brick.getPositionY());
        }

        g.drawString("Score: " + userInfo.getScore(), 520, 10);
    }

    @Override
    public void update(GameContainer gc, StateBasedGame sbg, int delta){
        Input input = gc.getInput();
        paddle.move(input);
        ball.move();
        paddle.collide(ball);

        for(Brick brick : bricks) {
            ball.collide(brick);
        }

        // Player loses when ball goes out of screen
        if(ball.getPositionY() > Game.FRAME_HEIGHT){
            sbg.enterState(Game.gameOver);
        }

        // Player wins when all bricks have been destroyed
        if(userInfo.getScore() == numBricks * Brick.pointsPerBrick){
            sbg.enterState(Game.gameWon);
        }
    }
}

UserInfo类-一个包含用户游戏信息的类(目前只包含分数)

代码语言:javascript
运行
复制
package breakout;

public class UserInfo {
    private int score;

    // GETTERS
    public int getScore(){
        return score;
    }

    // SETTERS
    public synchronized void incrementScore(int value){
        score += value;
    }
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2015-04-27 00:22:02

在游戏中:

代码语言:javascript
运行
复制
public static final int play = 0;
public static final int gameWon = 1;
public static final int gameOver = 2;

用一个枚举来做这个。

在布洛克:

代码语言:javascript
运行
复制
public boolean isDestroyed(){
    return imageIndex == images.length - 1;
}

不要滥用状态变量。如果你有需要循环动画的火块,这将给你最奇怪的错误。存储类似healthhitsRemaining之类的内容,以跟踪销毁进度。

代码语言:javascript
运行
复制
public interface Collision {

这是违反姓名的行为。接口不应该是名词。这是一件事,不是一种能力。试试Collidable (尽管你的拼写检查器会告诉你这不是一个词...它可能是)。这个取名的事真把我绊倒了。

代码语言:javascript
运行
复制
static boolean bottomSide(Collision self, Collision other, int tolerance){
    // tolerance is included as the ball's velocity is 3, so the ball may not exactly touch the paddle when it
    // reaches it because it's moving in increments of 3.
    return other.getPositionX() + other.getWidth() >= self.getPositionX()
            && other.getPositionX() <= self.getPositionX() + self.getWidth()
            && other.getPositionY() <= self.getPositionY() + self.getHeight()
            && other.getPositionY() >= self.getPositionY() + self.getHeight() - tolerance;
}

static boolean leftSide(Collision self, Collision other, int tolerance){
    return other.getPositionY() + other.getHeight() >= self.getPositionY()
            && other.getPositionY() <= self.getPositionY() + self.getHeight()
            && other.getPositionX() + other.getWidth() >= self.getPositionX()
            && other.getPositionX() + other.getWidth() <= self.getPositionX() + tolerance;
}

static boolean rightSide(Collision self, Collision other, int tolerance){
    return other.getPositionY() + other.getHeight() >= self.getPositionY()
            && other.getPositionY() <= self.getPositionY() + self.getHeight()
            && other.getPositionX() <= self.getPositionX() + self.getWidth()
            && other.getPositionX() >= self.getPositionX() + self.getWidth() - tolerance;
}

static boolean topSide(Collision self, Collision other, int tolerance){
    return other.getPositionX() + other.getWidth() >= self.getPositionX()
            && other.getPositionX() <= self.getPositionX() + self.getWidth()
            && other.getPositionY() + other.getHeight() >= self.getPositionY()
            && other.getPositionY() + other.getHeight() <= self.getPositionY() + tolerance;
}

嗯。

您考虑过定义这些函数吗?

代码语言:javascript
运行
复制
int getTopY();
int getBottomY();
int getLeftX();
int getRightX();

还有..。你在处理长方形。这是一个很大的假设--记住这一点,否则就会有圆圈碰撞,它们明显地经过了。

在Paddle:

代码语言:javascript
运行
复制
public class Paddle implements Collision {
    private Image image;
    private int paddleHeight;
    private int paddleWidth;

Paddle.paddleHeight是多余的。使用Paddle.height代替。有几个例外,但大多数情况下,如果类名在变量前面,则应该删除它。如果您有某种类型的解析器,其中一个例外就是Line.lineNumber -- Line.number太武断了。

代码语言:javascript
运行
复制
public void move(Input input){
    // Prevents paddle from moving outside of the frame
    if(getPositionX() > 0) {
        moveLeft(input);
    }
    if (getPositionX() < Game.FRAME_WIDTH - paddleWidth){
        moveRight(input);
    }
}

private void moveLeft(Input input){
    if(input.isKeyDown(Input.KEY_LEFT)){
        adjustPositionX(-velocity);
    }
}

private void moveRight(Input input){
    if(input.isKeyDown(Input.KEY_RIGHT)){
        adjustPositionX(velocity);
    }
}

把你的功能转过来。在move中执行键盘检查,在moveLeftmoveRight中执行边界检查。那你就不用到处传递输入了。

在游戏中:

代码语言:javascript
运行
复制
    for(Brick brick : bricks) {
        ball.collide(brick);
    }

函数命名,再次。对我来说,这句话是“球(移动到碰撞或)与砖块相撞”。事实并非如此。这是一个hitTest,如果碰到它,就会处理碰撞。考虑为这个函数找到一个更好的名称。我没有任何建议,因为这个函数是做两件事(检查碰撞和处理后果),但是分裂它似乎需要做两次碰撞测试,这似乎是一个耻辱。

代码语言:javascript
运行
复制
    // Player wins when all bricks have been destroyed
    if(userInfo.getScore() == numBricks * Brick.pointsPerBrick){

对比一下这个片段:

代码语言:javascript
运行
复制
private boolean checkPlayerWon(){
    for(Brick b : bricks){
        if(!b.isDestroyed()){
            return false;
        }
    }
    return true;
}

通过这种方式,你可以检查所有的盖帽是否都被摧毁了,而不是把你的得分机制绑在一个胜利的条件下。

票数 4
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/88022

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档