Java中的图形口吃(太空侵略者)

时间:2017-01-21 23:29:30

标签: java swing graphics

我正在创建一个只有一个入侵者的太空入侵者游戏。图像是口吃的,只有10%的时间可见。我的代码出了什么问题?

package spaceinvader;

import javax.swing.*;
import java.awt.event.*;

import java.awt.*;

public class spaceInvaders extends JApplet implements KeyListener, ActionListener
{
//Declare components and variables

JPanel mainPanel = new JPanel();
ImageIcon carImage = new ImageIcon("ship.png");
ImageIcon invaderImage = new ImageIcon("invader.png");

int intPosX = 240;
int intPosY = 330;
int intXAmount = 15;
boolean shipMoveLeft = false;
boolean shipMoveRight = false;
Timer shipTimer = new Timer(100,this);

int intBulletX = -50;
int intBulletY = -50;
boolean bulletMove = false;
boolean bulletActive = false;
Timer bulletTimer = new Timer(50,this);

int intInvaderX = 0;
int intInvaderY = 0;
int invaderXAmount = 10;
boolean invaderMove = true;
Timer invaderTimer= new Timer(1000,this);

public void init()
{
    addKeyListener(this);
    setFocusable(true);

    resize(600,400);
    setContentPane(mainPanel);

    shipTimer.start();
    bulletTimer.start();
    invaderTimer.start();
}

public void actionPerformed(ActionEvent e)
{
    requestFocus();
    if(shipMoveLeft)
        intPosX += intXAmount;
    else if(shipMoveRight)
        intPosX -= intXAmount;
    if(bulletMove && bulletActive){
        intBulletY -= 15;
        if(intBulletY <= -50){
            bulletMove = false;
            bulletActive = false;
        }
    }
    if(invaderMove){
        intInvaderX += invaderXAmount;
        if(intInvaderX > getWidth() - 60 || intInvaderX < 0){
            intInvaderY += 40;
            invaderXAmount *= -1;
        }
    }
    repaint();
}

public void keyPressed(KeyEvent e)
{
    int key = e.getKeyCode();
    if (key == 37){
        shipMoveRight = true;
    }
    else if (key == 39){
        shipMoveLeft = true;
    }
    else if (key == 32){
        if(bulletActive == false){
            intBulletX = intPosX;
            intBulletY = intPosY;
        }
        bulletMove = true;
        bulletActive = true;
    }
}

public void keyReleased(KeyEvent e)
{
    shipMoveLeft = false;
    shipMoveRight = false;
}

public void keyTyped(KeyEvent e)
{

}

public void paint(Graphics gr)
{
    super.paint(gr);

    gr.setColor(Color.red);
    gr.fillOval(intBulletX, intBulletY, 10, 25);

    carImage.paintIcon(this,gr, intPosX, intPosY); //Draw image in new spot

    invaderImage.paintIcon(this,gr, intInvaderX, intInvaderY);
 }
}

1 个答案:

答案 0 :(得分:3)

所以有很多问题会立即跳出来......

  • 小程序是死路一条,大多数浏览器会主动阻止它们和/或放弃对插件的支持
  • 您正在向小程序添加JPanel,但会覆盖小程序的paint方法,因为绘画可以起作用的方式,可以独立于小程序绘制面板,使其绘制无论你有什么画,
  • 您不需要多个计时器,只需要有更好的delta值(越小越快)
  • KeyListener是检测键盘输入的不良选择,它的级别相当低,并且使用Key Bindings API可以轻松克服与焦点相关的问题

首先,请查看Performing Custom PaintingPainting in AWT and SwingHow to Use Key Bindings了解更多详情。

那么你将如何开始修复它?首先使用JPanel作为基本容器,覆盖它paintComponent并将所有绘制逻辑放在此处。

使用一个Timer来管理更新

你可以采取多种方法,但我首先要定义一些合同来定义游戏中可以发生的事情

public interface GameSpace extends ImageObserver {
    public Dimension getGameSpace();
    public boolean hasInput(Input input);
}

public interface Entity {
    public void paint(GameSpace gameSpace, Graphics2D g2d);
    public boolean update(GameSpace gameSpace);

    public Rectangle getBounds();
}

public abstract class AbstractEntity implements Entity {

    private int x;
    private int y;

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    protected abstract int getWidth();
    protected abstract int getHeight();

    public Rectangle getBounds() {
        return new Rectangle(getX(), getY(), getWidth(), getHeight());
    }

}

public abstract class AbstractImageEntity extends AbstractEntity {

    protected abstract BufferedImage getImage();

    @Override
    protected int getWidth() {
        return getImage().getWidth();
    }

    @Override
    protected int getHeight() {
        return getImage().getHeight();
    }

}

这分离了一些关键元素的管理,允许更灵活的设计。你可能有更多的实体,一个可以移动,一个可以移动,一些被绘制,一些不是,但所有这些都为核心引擎提供支持以完成工作。

一旦你有了,你可以开始定义你需要的一些核心实体

public class ShipEntity extends AbstractImageEntity {

    private BufferedImage ship;

    public ShipEntity(GameSpace gameSpace) throws IOException {
        ship = ImageIO.read(getClass().getResource("/resources/ship.png"));
        setY(gameSpace.getGameSpace().height - getBounds().height);
        setX((gameSpace.getGameSpace().width - getBounds().width) / 2);
    }

    @Override
    public BufferedImage getImage() {
        return ship;
    }

    @Override
    public void paint(GameSpace gameSpace, Graphics2D g2d) {
        g2d.drawImage(ship, getX(), getY(), gameSpace);
    }

    @Override
    public boolean update(GameSpace gameSpace) {
        int x = getX();
        if (gameSpace.hasInput(Input.LEFT)) {
            x -= 2;
        }       
        if (gameSpace.hasInput(Input.RIGHT)) {
            x += 2;
        }       

        if (x < 0) {
            x = 0;
        } else if (x + getWidth() > gameSpace.getGameSpace().width) {
            x = gameSpace.getGameSpace().width - getWidth();
        }

        setX(x);

        return true;
    } 

}

public class InvaderEntity extends AbstractImageEntity {
    private BufferedImage invader;

    public InvaderEntity() throws IOException {
        invader = ImageIO.read(getClass().getResource("/resources/Invader.png"));
    }

    @Override
    protected BufferedImage getImage() {
        return invader;
    }

    @Override
    public void paint(GameSpace gameSpace, Graphics2D g2d) {
        g2d.drawImage(invader, getX(), getY(), gameSpace);
    }

    @Override
    public boolean update(GameSpace gameSpace) {
        return true;            
    }

}

public class ProjectileEntity extends AbstractEntity {

    private int delta;

    public ProjectileEntity(int delta) {
        this.delta = delta;
    }

    @Override
    protected int getWidth() {
        return 10;
    }

    @Override
    protected int getHeight() {
        return 10;
    }

    @Override
    public void paint(GameSpace gameSpace, Graphics2D g2d) {
        g2d.setColor(Color.RED);
        int width = getWidth();
        int height = getHeight();
        g2d.fillOval(getX() - width / 2, getY() - height / 2, width, height);
    }

    @Override
    public boolean update(GameSpace gameSpace) {
        int y = getY() + delta;
        setY(getY() + delta);
        return y + getHeight() >= 0 && y + getHeight() <= gameSpace.getGameSpace().height;
    }

}

基本上,它们包含完成工作所需的逻辑。

最后,您需要设置实际的游戏UI

public class GamePane extends JPanel implements GameSpace {

    private Set<Input> inputs;

    private Entity playerEntity;
    private List<Entity> projectileEntities;
    private List<Entity> invaderEntities;

    private long timeOfLastProjectile = -1;

    public GamePane() throws IOException {
        setBackground(Color.BLACK);

        inputs = new HashSet<>(2);

        playerEntity = new ShipEntity(this);

        projectileEntities = new ArrayList<>(25);
        invaderEntities = new ArrayList<>(25);

        InvaderEntity invader = new InvaderEntity();
        invader.setX((getGameSpace().width - invader.getBounds().width) / 2);
        invader.setY((getGameSpace().height - invader.getBounds().height) / 2);

        invaderEntities.add(invader);

        addKeyBinding(Input.LEFT, "left", KeyEvent.VK_LEFT);
        addKeyBinding(Input.RIGHT, "right", KeyEvent.VK_RIGHT);
        addKeyBinding(Input.SPACE, "space", KeyEvent.VK_SPACE);

        Timer timer = new Timer(15, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                updateState();
                processCollisions();
                repaint();
            }
        });
        timer.start();
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(800, 800);
    }

    protected void updateState() {
        playerEntity.update(this);
        if (hasInput(Input.SPACE)) {
            long time = System.currentTimeMillis() - timeOfLastProjectile;
            if (time < 0 || time > 1000) {
                timeOfLastProjectile = System.currentTimeMillis();
                Rectangle bounds = playerEntity.getBounds();
                ProjectileEntity projectile = new ProjectileEntity(-1);
                int x = bounds.x + ((bounds.width - projectile.getWidth()) / 2);
                int y = bounds.y - projectile.getHeight();

                projectile.setX(x);
                projectile.setY(y);
                projectileEntities.add(projectile);
            }
        }

        for (Entity entity : invaderEntities) {
            entity.update(this);
        }

        List<Entity> outOfBounds = new ArrayList<>(25);
        for (Entity entity : projectileEntities) {
            if (!entity.update(this)) {
                outOfBounds.add(entity);
            }
        }
        projectileEntities.removeAll(outOfBounds);
    }

    protected void processCollisions() {
        Set<Entity> hitInvaders = new HashSet<>(25);
        Set<Entity> hitProjectiles = new HashSet<>(25);
        for (Entity invader : invaderEntities) {
            for (Entity projectile : projectileEntities) {
                if (projectile.getBounds().intersects(invader.getBounds())) {
                    // Maybe lots of cool explosiions
                    hitInvaders.add(invader);
                    hitProjectiles.add(projectile);
                }
            }
        }

        invaderEntities.removeAll(hitInvaders);
        projectileEntities.removeAll(hitProjectiles);
    }

    protected void addKeyBinding(Input input, String name, int virtualKey) {
        ActionMap am = getActionMap();
        InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);

        im.put(KeyStroke.getKeyStroke(virtualKey, 0, false), name + ".pressed");
        im.put(KeyStroke.getKeyStroke(virtualKey, 0, true), name + ".released");

        am.put(name + ".pressed", new KeyAction(inputs, input, true));
        am.put(name + ".released", new KeyAction(inputs, input, false));
    }

    @Override
    public Dimension getGameSpace() {
        return getPreferredSize();
    }

    @Override
    public boolean hasInput(Input input) {
        return inputs.contains(input);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);

        Graphics2D g2d = (Graphics2D) g.create();
        playerEntity.paint(this, g2d);
        g2d.dispose();

        for (Entity entity :  invaderEntities) {
            g2d = (Graphics2D) g.create();
            entity.paint(this, g2d);
            g2d.dispose();
        }
        for (Entity entity :  projectileEntities) {
            g2d = (Graphics2D) g.create();
            entity.paint(this, g2d);
            g2d.dispose();
        }
    }

}

public class KeyAction extends AbstractAction {

    private Input input;
    private Set<Input> inputs;
    private boolean pressed;

    public KeyAction(Set<Input> inputs, Input input, boolean pressed) {
        this.input = input;
        this.inputs = inputs;
        this.pressed = pressed;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (pressed) {
            inputs.add(input);
        } else {
            inputs.remove(input);
        }
    }

}

现在你可以更进一步,生成一个“引擎”类来控制实体并执行所有必需的更新,但我很懒;)

这是一个非常粗略的想法,你需要开发一些基本概念才能继续前进,希望它有所帮助