2017-01-21 4 views
-1

Я создаю игру Space Invaders только с одним захватчиком. Изображения заикаются и видны только в 10% случаев. Что не так с моим кодом?Графика Заикание в Java (Space Invaders)

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); 
} 
} 
+0

Вам не нужно три Таймеры все связаны с тем же ActionListener, используйте один и позволяют модели принимать решения о том, что на каждом ти ск. Измените дельта объектов, чтобы изменить скорость.Предпочитайте переопределить paintComponent вместо краски – MadProgrammer

+1

Переместите свою картину на пользовательский компонент, mainPanel и метод переопределенной краски могут мешать друг другу – MadProgrammer

+0

BufferedImage может быть лучше, чем ImageIcon – MadProgrammer

ответ

3

Таким образом, существует целый ряд вопросов, которые выскакивают сразу ...

  • апплетов тупика, большинство браузеров будут активно блокировать их и/или прекратили поддержку плагина
  • Вы добавляете в апплет JPanel, но переопределяя метод апплета paint, из-за того, что картина может работать, панель может быть нарисована независимо от апплета, заставляя ее рисовать все, что вы могли бы нарисовать.
  • Вам не нужно несколько таймеров, вам просто нужно иметь лучшие значения дельта (меньше было быстрее)
  • KeyListener - это плохой выбор для обнаружения ввода на клавиатуре, он довольно низкий и имеет проблемы с фокусировкой, которые легко преодолеть, используя клавиатурных комбинации API

Я бы начать иметь взгляд на Performing Custom Painting, Painting in AWT and Swing и How 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); 
     } 
    } 

} 

Теперь вы можете пойти немного дальше и создать класс «двигатель», который контролирует объекты и выполняет все необходимые обновления, но я ленивая;)

это действительно грубое представление о некоторых основных понятий, которые необходимо развивать, чтобы иметь возможность двигаться вперед, надеюсь, что это помогает

+0

Может ли только один объяснить причину downvote? Поскольку никаких новых нисходящих или закрытых голосов не было добавлено к вопросу, я могу только предположить, что вопрос не возникает, поскольку новые ответы не добавлены. Я могу только предположить, что вы не знаете, как решить проблему самостоятельно и лично отвечаю ... – MadProgrammer

+0

Не знаю. Кажется, это солидный ответ. Проголосовали соответственно. –