我正在制作蛇游戏(for those who don't know),其中蛇由AI控制,使用不同的算法捕捉食物。常规游戏的不同之处在于,除非AI发出命令,否则蛇不会移动。
我的问题是,只要我运行AI,AI就会创建一堆命令来执行以捕获食物但我的GUI只是冻结;可能是因为它无法跟上移动堆栈造成的重绘量。通过控制台日志,我可以看到AI和游戏逻辑仍在运行。
我在每次移动后尝试Thread.sleep()
,但我想这只会使整个程序包括GUI睡眠。我的Timer
也有一个paintComponent
,但似乎无法改变任何内容。
如何使程序保持睡眠状态,以便GUI可以了解正在发生的事情?
编辑:
好的,我尝试了你的解决方案,它仍然无法正常工作。我真的不想在这里转储代码,但我真的输了。我有一个计时器,应该以140毫秒的间隔重新绘制(这是DELAY的值),这些命令是在一个不同的线程上发送的,每次按键后都会进入休眠状态1000毫秒,之后我调用repaint()每次调用move()......这是相关代码(没有我修改的原始代码here):
private void initGame() {
dots = 5;
for (int z = 0; z < dots; z++) {
x[z] = 50 - z * 10;
y[z] = 50;
}
locateApple();
for (int k = blockNb - 1; k > 0; k--) {
locateBlock(k, apple_x, apple_y);
}
if (blocks) {
locateBlock(0, apple_x, apple_y);
}
timer = new Timer(DELAY, this);
timer.start();
startAi();
}
// AI CONTROLLER
public void startAi() {
Ai theAi = new Ai();
String move = "";
switch (ai) {
case "BFS":
move = theAi.bfs(this);
break;
}
//AI returns a string where each char is a move command
autoMove(move);
}
public void autoMove(String move) {
try {
Robot robot = new Robot();
System.out.println(move);
if (move != "#No") {
Thread thread = new Thread(new Runnable() {
public void run() {
for (int j = 0; j < move.length(); j++) {
if (move.charAt(j) == 'l') {
robot.keyPress(KeyEvent.VK_LEFT);
robot.keyRelease(KeyEvent.VK_LEFT);
}
if (move.charAt(j) == 'r') {
robot.keyPress(KeyEvent.VK_RIGHT);
robot.keyRelease(KeyEvent.VK_RIGHT);
}
if (move.charAt(j) == 'u') {
robot.keyPress(KeyEvent.VK_UP);
robot.keyRelease(KeyEvent.VK_UP);
}
if (move.charAt(j) == 'd') {
robot.keyPress(KeyEvent.VK_DOWN);
robot.keyRelease(KeyEvent.VK_DOWN);
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
thread.run();
}
} catch (AWTException e) {
e.printStackTrace();
}
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
private void doDrawing(Graphics g) {
if (inGame) {
g.drawImage(apple, apple_x, apple_y, this);
for (int j = 0; j < blockNb; j++) {
g.drawImage(block, block_x[j], block_y[j], this);
}
for (int z = 0; z < dots; z++) {
if (z == 0) {
g.drawImage(head, x[z], y[z], this);
} else {
g.drawImage(ball, x[z], y[z], this);
}
}
Toolkit.getDefaultToolkit().sync();
} else {
// gameOver(g);
}
}
private void move() {
for (int z = dots; z > 0; z--) {
x[z] = x[(z - 1)];
y[z] = y[(z - 1)];
}
if (leftDirection) {
x[0] -= DOT_SIZE;
}
if (rightDirection) {
x[0] += DOT_SIZE;
}
if (upDirection) {
y[0] -= DOT_SIZE;
}
if (downDirection) {
y[0] += DOT_SIZE;
}
}
@Override
public void actionPerformed(ActionEvent e) {
if (inGame) {
repaint();
}
}
private class TAdapter extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if ((key == KeyEvent.VK_LEFT) && (!rightDirection)) {
leftDirection = true;
upDirection = false;
downDirection = false;
}
if ((key == KeyEvent.VK_RIGHT) && (!leftDirection)) {
rightDirection = true;
upDirection = false;
downDirection = false;
}
if ((key == KeyEvent.VK_UP) && (!downDirection)) {
upDirection = true;
rightDirection = false;
leftDirection = false;
}
if ((key == KeyEvent.VK_DOWN) && (!upDirection)) {
downDirection = true;
rightDirection = false;
leftDirection = false;
}
move();
checkApple();
checkCollision();
repaint();
}
}
编辑2:另外,我只想指出我试图在不依赖机器人的情况下移动但无济于事。
答案 0 :(得分:1)
你需要有某种游戏调度程序循环并理解它是如何工作的。
以下是java swing的一些示例代码:
http://staticvoidgames.com/tutorials/swing/swingTimers
与计划程序一起简化事情的另一种方法是首先使游戏变为基础。也就是说,当玩家移动1turn(即输入)时,游戏会完成所有处理并阻止用户输入,直到处理完成(即单循环)。
答案 1 :(得分:1)
第1部分:
Thread.sleep:
之间的差异在主线程(java用于运行程序的线程)中使用它时
例如,使用A Thread时(按照代码)
new Thread(new Runnable(){
public void run(){
//do something...
while(flag==false)
Thread.sleep(a given time) //it need and try catch
else
//do your move
});
然后只有这个Thread冻结(给定时间)或(无论你将它转换为冻结)。
在您的情况下,您可以使用标记,以便每次用户点击一个commant
标志为真然后再次为false以保持停止部分
你想要的游戏,但你的程序需要工作的主线程仍然有效 (如果它是一个窗口或任何东西......)
第2部分:(线程的基本形式)(我使用的标志必须被所有方法看到)(公共或私人)
new Thread(new Runnable() {
public void run() {
flag=true;
while(flag==true){
if(move.equals("yes")){
//your code
move="no";
}else
Thread.sleep(20); //Sleep For a While(and then check again)
}
//That means that your Thread will run all over your game
//until game over (when game over just do the flag=false;)
//and The Thread will stop-exit
}});
*关于重绘方法(不要太快调用重绘方法) 仅在玩家移动时调用它(这是帧的时间 需要重新绘制[好吧,如果你的游戏中有 .gif图片,就看不到这个]
第3部分:(当我做了类似的游戏我做了什么) 在几个月之前,我尝试过类似的游戏。基本的想法是一个必须经过一个迷宫的玩家......
对于每个级别,我有一个这样的课程......
public abstarct class Level2(){
//The game was into a panel like your.
//Here i added .setFocusable and .addKeyListener to panel
//Here it was an
public void actionListener(){
//The actionListener checked if the player found the door(for next level)
//Here it was the repaint so every time something happened repaint()
repaint();
}//End of Action Listener
//Use the paint or paintComponent what you need..
public void paint[or paintComponent](Graphics g){
//the 'squares' and the icons the had to be *repainted* again
}
//Here i had an extra class into this for the KeyListeners
//And I added the KeyListener like your game(up,down,left,right)
//I i said before i added this KeyListener to the panel
private class TAdapter extends KeyAdapter {
//KeyPressed,Released...etc
}
}
这就是我想的基本想法和你的游戏。 线程这是一个额外的选项我无法帮助你必须找到方法......
答案 2 :(得分:0)
每当你的游戏AI输出一个新的动作时,你需要调用paint,但是你还需要等到绘画完成之后调用的任何事件,然后输出你的下一步动作,你可能想要等待一个第二个更长的东西,玩弄它
伪代码
def fun 0 // entry point for program
// hook paint done event to func 1
call fun 2
def fun 1 // event handler method for when painting done
call fun 2
def fun 2 // function that does AI work
// do game logic
// call paint