我正在研究一个小型2D游戏引擎,只是为了试验一些设计思路。 我喜欢实体被赋予渲染自己和注册自己的听众的负担的想法 - 实体或“演员”应该定义它自己的所有行为。
问题是,我正在画的JPanel是“涂抹”。描述它的最简单的方法是给你们一个SSCCE,你是如此倾向。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class SSCCE_BufferImageRender {
public static void main(String[] args) {
SSCCE_BufferImageRender sbim = new SSCCE_BufferImageRender();
Environment e = sbim.new Environment();
Stage s = sbim.new Stage(e);
e.addStage(s);
e.setVisible(true);
s.addActor(sbim.new Actor(s));
}
class Environment extends JFrame implements Runnable{
private static final long serialVersionUID = 1L;
private ScheduledExecutorService mainService = Executors.newSingleThreadScheduledExecutor();
private List<Stage> environmentStages;
private Stage activeStage;
public Environment() {
super();
setSize(new Dimension(400,400));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Timer timer = new Timer(17, new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
run();
}
});
environmentStages = new ArrayList<Stage>();
timer.start();
}
@Override
public void run() {
if(activeStage != null)
activeStage.act();
}
public void addStage(Stage stage) {
if(environmentStages.size() == 0) {
activeStage = stage;
setContentPane(stage);
}
environmentStages.add(stage);
}
public KeyListener[] getKeyListeners() {
return getListeners(KeyListener.class);
}
}
/**
* Stage - Basically where everything will be painted
* */
class Stage extends JPanel {
private Environment stageEnvironment;
private List<Actor> stageActors;
private int stageHeight;
private int stageWidth;
public Stage(Environment environment) {
super();
stageEnvironment = environment;
stageHeight= stageEnvironment.getHeight();
stageWidth = stageEnvironment.getWidth();
this.setSize(stageWidth, stageHeight);
stageActors = new ArrayList<Actor>();
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for(Actor actor : stageActors) {
BufferedImage bi = actor.render();
g2d.drawImage(bi, 0, 0, null);
}
g2d.drawString("Hello World!", 50, 50);
g2d.dispose();
}
public void act() {
for(Actor actor : stageActors) {
actor.act();
}
repaint();
}
public void addActor(Actor actor){
stageActors.add(actor);
}
public Environment getStageEnvironment() {
return stageEnvironment;
}
public int getWidth() {
return stageWidth;
}
public int getHeight() {
return stageHeight;
}
public void setStageEnvironment(Environment stageEnvironment) {
this.stageEnvironment = stageEnvironment;
}
}
class Actor {
private String actorID;
private Stage actorStage;
private BufferedImage actorImage;
private int x,y,radius;
public Actor(Stage stage) {
actorStage = stage;
x = y = radius = 20;
setActorImage(new BufferedImage(actorStage.getWidth(), actorStage.getHeight(), BufferedImage.TYPE_INT_ARGB));
if(getKeyListener() != null) {
System.out.println("Adding new listener from actor");
stage.getStageEnvironment().addKeyListener(getKeyListener());
}
}
public Stage getActorStage() {
return actorStage;
}
public void setActorStage(Stage actorStage) {
this.actorStage = actorStage;
}
public BufferedImage getActorImage() {
return actorImage;
}
public void setActorImage(BufferedImage actorImage) {
this.actorImage = actorImage;
}
public void act(){
// Do nothing
}
public BufferedImage render() {
Graphics2D g2d = getActorImage().createGraphics();
g2d.setColor(Color.red);
g2d.fillOval(x, y, radius, radius);
g2d.dispose();
return getActorImage();
}
private KeyListener getKeyListener(){
return new KeyListener() {
@Override
public void keyPressed(KeyEvent arg0) {}
@Override
public void keyReleased(KeyEvent arg0) {
switch(arg0.getKeyChar()) {
case '4': x-=5;break;
case '6': x+=5;break;
case '8': y-=5;break;
case '2': y+=5;break;
default:break;
}
}
@Override
public void keyTyped(KeyEvent arg0) {}
};
}
}
}
你会注意到我正在使用一个Timer来模拟一个线程...我也有一些代码,我使用的是ScheduledExecutor,但是我看到其他地方弄乱了EDT如何处理Swing组件会导致问题。
那么,任何想法为什么我的JPanel没有正确绘画?
答案 0 :(得分:1)
问题在于你Actor
...
public BufferedImage render() {
Graphics2D g2d = getActorImage().createGraphics();
g2d.setColor(Color.red);
g2d.fillOval(x, y, radius, radius);
g2d.dispose();
return getActorImage();
}
在这里,您将获得Graphics
的{{1}}上下文并为其绘画......但您还没有删除之前绘制到BufferedImage
的内容,所以它只是&#34;添加&#34;它。
虽然无法清除BufferedImage
,但更好的解决方案是更改渲染的方式。
而不是BufferedImage
呈现给它自己的Actor
,当你扩展它时,会给你的记忆带来负担,你应该传递{的引用{1}}要绘制的上下文,例如......
BufferedImage
在Graphics
方法中,只需将组件public void render(Graphics2D g2d) {
g2d.setColor(Color.red);
g2d.fillOval(x, y, radius, radius);
}
传递给它......
paintComponent
如果您担心Graphics
对@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Actor actor : stageActors) {
actor.render(g2d);
}
g2d.drawString("Hello World!", 50, 50);
g2d.dispose();
}
上下文做了一些奇怪的事情,您可以为每个内容创建一个单独的副本....
Actor
此外,您的Graphics
方法没有理由成为@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Actor actor : stageActors) {
Graphics2D g2d = (Graphics2D) g.create();
actor.render(g2d);
g2d.dispose();
}
g.drawString("Hello World!", 50, 50);
}
,因为您从不希望任何人直接调用它。