前面是是废话,不爱看的直接从"//Here"开始看
简单说一下,这是我刚学完JavaSE自己在做一个小的练手项目时遇到的问题。
我把遇到的问题写了下面这个简单的测试程序来凸显这个问题。
主要是这样的,我最开始写移动的功能,是在paint方法里改坐标然后起个线程repaint,后来发现这样实在不好,当手动重绘时(比如最小化和最大化,拖动窗口在屏幕边上乱晃),由于重绘时调用paint的次数会更高,程序中运动的图形就会明显变快了。后来在CSDN提问,得到了解答,不要在paint上增加坐标,直接起个线程调用repaint和move方法。后来程序越来越复杂,又要加容器,又要加这个那个,删了又删,改了又改,到了现在这种情况。
//Here
这个测试程序主要的功能就是killer敲一下A键打出一个子弹,消灭前面的一个somebody,为了表达更清楚,其他的功能都没写。
Gun(或者说子弹)是用ArrayList装的,只有在List是!isEmpty时才会调用线程中if语句里的移动和重绘,这样会大大减少CPU资源的浪费。但当打到对面的somebody时本应该是要子弹和对方一起消失的,把当前的子弹从List中remove掉这时肯定的,但可惜的是remove掉以后List将是空的,不能repaint,子弹和被打中的家伙就停在那里哥俩好了。大家可以试试,按一下A键打中后就停在那里了,想刷掉就再打一颗就能调用repaint了。
还有一个问题就是为什么程序总是莫名其妙的报ConcurrentModificationException呢,有时候是打前3颗子弹就报,有时候打了几百发也不报,完全没有规律,不晓得这到底是什么。
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;public class Main {
public static void main(String[] args) {
new MainFrame();
}
}class MainFrame extends Frame {
Image offScreenImage = null;
List<Gun> guns = new ArrayList<Gun>();
People killer = new People(50, 150, true, this);
People sb = new People(300, 150, false, this);
private class GunThread implements Runnable {
public void run() {
while(true) {
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(!guns.isEmpty()) {
repaint();
for(Gun gun : guns) {
gun.move();
}
}
}
}
}
public MainFrame() {
this.setSize(400, 400);
this.setBackground(Color.BLUE);
this.setVisible(true);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
setVisible(false);
System.exit(0);
}
});
addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent e) {
killer.KeyReleased(e);
}
});
new Thread(new GunThread()).start();
} public void paint(Graphics g) {
killer.draw(g);
sb.draw(g);
for(int i=0; i<guns.size(); i++) {
Gun gun = guns.get(i);
gun.killsb(sb);
if(null != gun) {
gun.draw(g);
}
}
}
public void update(Graphics g) {
if(null == offScreenImage) {
offScreenImage = this.createImage(400, 400);
}
Graphics gOffScreen = offScreenImage.getGraphics();
Color c = gOffScreen.getColor();
gOffScreen.setColor(Color.BLUE);
gOffScreen.fillRect(0, 0, 400, 400);
gOffScreen.setColor(c);
paint(gOffScreen);
g.drawImage(offScreenImage, 0, 0, null);
}
}class People {
MainFrame mf = null;
private boolean live = true;
private boolean kill = true;
private int x;
private int y;
public People(int x, int y, boolean kill, MainFrame mf) {
this.x = x;
this.y = y;
this.setKill(kill);
this.mf = mf;
}
void draw(Graphics g) {
if (!isLive()) {
return;
}
Color c = g.getColor();
if (isKill()) {
g.setColor(Color.RED);
} else {
g.setColor(Color.YELLOW);
}
g.fillOval(x, y, 40, 40);
g.setColor(c);
}
public Rectangle getRect() {
return new Rectangle(x, y, 40, 40);
}
public void KeyReleased(KeyEvent e) {
int key = e.getKeyCode();
if(KeyEvent.VK_A == key) {
mf.guns.add(fire());
}
}
public Gun fire() {
Gun gun = new Gun(x, y, this.mf);
return gun;
}
public boolean isLive() {
return live;
}
public void setLive(boolean live) {
this.live = live;
}
public boolean isKill() {
return kill;
}
public void setKill(boolean kill) {
this.kill = kill;
}
}class Gun {
private boolean live = true; private int x;
private int y;
private MainFrame mf = null; public Gun(int x, int y, MainFrame mf) {
this.x = x;
this.y = y;
this.mf = mf;
this.setLive(true);
} void draw(Graphics g) {
if (!isLive()) {
mf.guns.remove(this);
}
g.fillOval(x, y, 10, 10);
}
public boolean killsb(People p) {
if(getRect().intersects(p.getRect())) {
p.setLive(false);
this.setLive(false);
return true;
}
return false;
}
public Rectangle getRect() {
return new Rectangle(x, y, 10, 10);
}
void move() {
x += 3;
}
public boolean isLive() {
return live;
} public void setLive(boolean live) {
this.live = live;
}
}
简单说一下,这是我刚学完JavaSE自己在做一个小的练手项目时遇到的问题。
我把遇到的问题写了下面这个简单的测试程序来凸显这个问题。
主要是这样的,我最开始写移动的功能,是在paint方法里改坐标然后起个线程repaint,后来发现这样实在不好,当手动重绘时(比如最小化和最大化,拖动窗口在屏幕边上乱晃),由于重绘时调用paint的次数会更高,程序中运动的图形就会明显变快了。后来在CSDN提问,得到了解答,不要在paint上增加坐标,直接起个线程调用repaint和move方法。后来程序越来越复杂,又要加容器,又要加这个那个,删了又删,改了又改,到了现在这种情况。
//Here
这个测试程序主要的功能就是killer敲一下A键打出一个子弹,消灭前面的一个somebody,为了表达更清楚,其他的功能都没写。
Gun(或者说子弹)是用ArrayList装的,只有在List是!isEmpty时才会调用线程中if语句里的移动和重绘,这样会大大减少CPU资源的浪费。但当打到对面的somebody时本应该是要子弹和对方一起消失的,把当前的子弹从List中remove掉这时肯定的,但可惜的是remove掉以后List将是空的,不能repaint,子弹和被打中的家伙就停在那里哥俩好了。大家可以试试,按一下A键打中后就停在那里了,想刷掉就再打一颗就能调用repaint了。
还有一个问题就是为什么程序总是莫名其妙的报ConcurrentModificationException呢,有时候是打前3颗子弹就报,有时候打了几百发也不报,完全没有规律,不晓得这到底是什么。
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;public class Main {
public static void main(String[] args) {
new MainFrame();
}
}class MainFrame extends Frame {
Image offScreenImage = null;
List<Gun> guns = new ArrayList<Gun>();
People killer = new People(50, 150, true, this);
People sb = new People(300, 150, false, this);
private class GunThread implements Runnable {
public void run() {
while(true) {
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(!guns.isEmpty()) {
repaint();
for(Gun gun : guns) {
gun.move();
}
}
}
}
}
public MainFrame() {
this.setSize(400, 400);
this.setBackground(Color.BLUE);
this.setVisible(true);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
setVisible(false);
System.exit(0);
}
});
addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent e) {
killer.KeyReleased(e);
}
});
new Thread(new GunThread()).start();
} public void paint(Graphics g) {
killer.draw(g);
sb.draw(g);
for(int i=0; i<guns.size(); i++) {
Gun gun = guns.get(i);
gun.killsb(sb);
if(null != gun) {
gun.draw(g);
}
}
}
public void update(Graphics g) {
if(null == offScreenImage) {
offScreenImage = this.createImage(400, 400);
}
Graphics gOffScreen = offScreenImage.getGraphics();
Color c = gOffScreen.getColor();
gOffScreen.setColor(Color.BLUE);
gOffScreen.fillRect(0, 0, 400, 400);
gOffScreen.setColor(c);
paint(gOffScreen);
g.drawImage(offScreenImage, 0, 0, null);
}
}class People {
MainFrame mf = null;
private boolean live = true;
private boolean kill = true;
private int x;
private int y;
public People(int x, int y, boolean kill, MainFrame mf) {
this.x = x;
this.y = y;
this.setKill(kill);
this.mf = mf;
}
void draw(Graphics g) {
if (!isLive()) {
return;
}
Color c = g.getColor();
if (isKill()) {
g.setColor(Color.RED);
} else {
g.setColor(Color.YELLOW);
}
g.fillOval(x, y, 40, 40);
g.setColor(c);
}
public Rectangle getRect() {
return new Rectangle(x, y, 40, 40);
}
public void KeyReleased(KeyEvent e) {
int key = e.getKeyCode();
if(KeyEvent.VK_A == key) {
mf.guns.add(fire());
}
}
public Gun fire() {
Gun gun = new Gun(x, y, this.mf);
return gun;
}
public boolean isLive() {
return live;
}
public void setLive(boolean live) {
this.live = live;
}
public boolean isKill() {
return kill;
}
public void setKill(boolean kill) {
this.kill = kill;
}
}class Gun {
private boolean live = true; private int x;
private int y;
private MainFrame mf = null; public Gun(int x, int y, MainFrame mf) {
this.x = x;
this.y = y;
this.mf = mf;
this.setLive(true);
} void draw(Graphics g) {
if (!isLive()) {
mf.guns.remove(this);
}
g.fillOval(x, y, 10, 10);
}
public boolean killsb(People p) {
if(getRect().intersects(p.getRect())) {
p.setLive(false);
this.setLive(false);
return true;
}
return false;
}
public Rectangle getRect() {
return new Rectangle(x, y, 10, 10);
}
void move() {
x += 3;
}
public boolean isLive() {
return live;
} public void setLive(boolean live) {
this.live = live;
}
}
while(true) {
try {
Thread.sleep(40);
} catch (Exception e) {
e.printStackTrace();
}
if(!guns.isEmpty()) {
for(Gun gun : guns) {
gun.move();
}
}
repaint();
}
把repaint();放出来,第二个问题我这里运行没有出来,所以不知道怎么回答楼主。
楼主的问题我想了想 假如使用isEmpty()是一种方法的话 那么只需要控制一下睡眠就可以了 打到后先执行repaint() 睡一会再remove() 相应的改小一下重绘线程的睡眠时间和move()的距离 不用测试 把update()注掉就能知道重画没重画了 但这也会导致很多问题 比如子弹速度不均匀 ConcurrentModificationException发生几率成倍增长 这些都是绝对可以修改的 仔细分析一下流程 debug一下 多分析分析 改改位置就可以了 至于ConcurrentModificationException 我看楼主还是有一定水平的 这个异常的发生原因很固定 解决方法也很固定 你自己google一下便可知
1、用一个标志位控制在guns为空之后,最后repaint()一次
2、当guns里还有本元素只时,才调用remove,防止异常import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;public class Main {
public static void main(String[] args) {
new MainFrame();
}
}class MainFrame extends Frame {
Image offScreenImage = null;
List<Gun> guns = new ArrayList<Gun>();
People killer = new People(50, 150, true, this);
People sb = new People(300, 150, false, this);
boolean emtpy = false;
private class GunThread implements Runnable {
public void run() {
while(true) {
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(!guns.isEmpty()) {
repaint();
for(Gun gun : guns) {
gun.move();
}
} else {
if (!emtpy) {//用这个标志控制guns为空之后最后repaint()一次;
repaint();
emtpy = true;
}
}
}
}
}
public MainFrame() {
this.setSize(400, 400);
this.setBackground(Color.BLUE);
this.setVisible(true);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
setVisible(false);
System.exit(0);
}
});
addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent e) {
killer.KeyReleased(e);
}
});
new Thread(new GunThread()).start();
} public void paint(Graphics g) {
killer.draw(g);
sb.draw(g);
for(int i=0; i<guns.size(); i++) {
Gun gun = guns.get(i);
gun.killsb(sb);
if(null != gun) {
gun.draw(g);
}
}
}
public void update(Graphics g) {
if(null == offScreenImage) {
offScreenImage = this.createImage(400, 400);
}
Graphics gOffScreen = offScreenImage.getGraphics();
Color c = gOffScreen.getColor();
gOffScreen.setColor(Color.BLUE);
gOffScreen.fillRect(0, 0, 400, 400);
gOffScreen.setColor(c);
paint(gOffScreen);
g.drawImage(offScreenImage, 0, 0, null);
}
}class People {
MainFrame mf = null;
private boolean live = true;
private boolean kill = true;
private int x;
private int y;
public People(int x, int y, boolean kill, MainFrame mf) {
this.x = x;
this.y = y;
this.setKill(kill);
this.mf = mf;
}
void draw(Graphics g) {
if (!isLive()) {
return;
}
Color c = g.getColor();
if (isKill()) {
g.setColor(Color.RED);
} else {
g.setColor(Color.YELLOW);
}
g.fillOval(x, y, 40, 40);
g.setColor(c);
}
public Rectangle getRect() {
return new Rectangle(x, y, 40, 40);
}
public void KeyReleased(KeyEvent e) {
int key = e.getKeyCode();
if(KeyEvent.VK_A == key) {
mf.guns.add(fire());
mf.emtpy = false;//标志位变化
}
}
public Gun fire() {
Gun gun = new Gun(x, y, this.mf);
return gun;
}
public boolean isLive() {
return live;
}
public void setLive(boolean live) {
this.live = live;
}
public boolean isKill() {
return kill;
}
public void setKill(boolean kill) {
this.kill = kill;
}
}class Gun {
private boolean live = true; private int x;
private int y;
private MainFrame mf = null; public Gun(int x, int y, MainFrame mf) {
this.x = x;
this.y = y;
this.mf = mf;
this.setLive(true);
} void draw(Graphics g) {
if (!isLive()) {
if (mf.guns.contains(this)) {//防异常处理
mf.guns.remove(this);
}
}
g.fillOval(x, y, 10, 10);
}
public boolean killsb(People p) {
if(getRect().intersects(p.getRect())) {
p.setLive(false);
this.setLive(false);
return true;
}
return false;
}
public Rectangle getRect() {
return new Rectangle(x, y, 10, 10);
}
void move() {
x += 3;
}
public boolean isLive() {
return live;
} public void setLive(boolean live) {
this.live = live;
}
}
如果楼主还希望越写越大的话
最好先把结构定好了再开始写然后,随着你的程序“越来越大”
你这样的repaint()控制可能还是无法满足少占资源的要求
你可能需要用算法来控制,仅仅repaint() Frame中必须要repaint()的区域
而不是每次repaint()都是repaint()整个Frame