だからすぐに飛び出し、多くの問題...
- アプレットは行き止まりですありますが、ほとんどのブラウザは、積極的にそれらをブロックしますおよび/またはプラグインのサポートを落としている
- アプレットに
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;
}
}
は基本的に、彼らは彼らの仕事を行って取得するために必要なロジックが含まれている必要があります。
最後に、あなたは今、あなたはさらに少し行くとエンティティを制御し、必要なすべての更新を行い、「エンジン」クラスを生成しますが、私は可能性が
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);
}
}
}
セットアップに実際のゲームのUIを必要とします怠惰な、あなたが前進することができるように開発する必要があるいくつかの基本的な概念の本当にラフなアイデアです)
、それは
に役立ちます願っていますが、いずれかを使用、すべて同じのActionListenerにリンクされている3つのタイマーを必要としませんモデルが各tiの何をするのかを決定できるようにするck。エンティティのデルタを変更して速度を変更します。ペイントの代わりにpaintComponentをオーバーライドすることをお勧めします。 – MadProgrammer
ペイントをカスタムコンポーネントに移動すると、mainPanelとオーバーライドされたペイントメソッドが互いに干渉する可能性があります。 – MadProgrammer
BufferedImageがよりよくなる可能性があります。ImageIcon – MadProgrammer