2015-11-06 112 views
1

我已经尝试过不同的方法来为这个蛇游戏制作两个背景,一个黑色的菜单和一个白色的游戏行。我找到的最佳解决方案是使用setBackground。但是当我运行游戏时,Thread.sleep变得混乱了,现在蛇变得非常快。为了尝试解决这个问题,我将多个值放入Thread.sleep中,但不管值如何,蛇都以相同的速度传播。setBackground正在搞乱Thread.sleep

import javax.swing.*; 
import java.awt.*; 
import java.awt.event.*; 
import java.lang.Thread; 
import java.util.Random; 

public class Snake extends JPanel implements KeyListener, MouseListener{ 
    public boolean right = false; 
    public boolean left = false; 
    public boolean up = false; 
    public boolean down = false; 

    public int snakex[] = new int[10000000]; 
    public int snakey[] = new int[10000000]; 
    public int snakeLength = 0; 

    public int applex; 
    public int appley; 

    public int buttonX = 150; 
    public int buttonY = 125; 

    public boolean appleEaten = true; 

    public static boolean reset = false; 
    public static boolean ingame = false; 
    public static boolean menu = true; 

    public static int speed = 200; 

    public void forLogic(){ 
     for(int i = snakeLength; i > 1; i--){ 
      if(snakeLength > 4 && snakex[0] == snakex[i] && snakey[0] == snakey[i]){ 
       System.out.println("You Loose \n Your Score was: " + snakeLength); 
       ingame = false; 
      } 
     } 

     Movement(); 

     if(snakex[0] >= 30*20){ 
      snakex[0] = 0; 
     } 
     if(snakex[0] < 0){ 
      snakex[0] = 29*20; 
     } 
     if(snakey[0] >= 25*20){ 
      snakey[0] = 0; 
     } 
     if(snakey[0] < 0){ 
      snakey[0] = 24*20; 
     } 

     if(snakex[0] == applex*20 && snakey[0] == appley*20) { 
      appleEaten = true; 
      snakeLength++; 
      //System.out.println(snakeLength); 
     } 

     if(appleEaten){ 
      appleLocation(); 
      appleEaten = false; 
     } 
    } 

    public void appleLocation(){ 
     boolean goodToGo = false; 
     Random rand = new Random(); 
     while(!goodToGo){ 
      applex = rand.nextInt(30); 
      appley = rand.nextInt(25); 
      boolean checker = false; 
      for(int i = snakeLength; i > 0; i--) { 
       if (applex == snakex[i]||appley == snakey[i]) { 
        checker = true; 
       } 
      } 
      if(!checker){goodToGo = true;} 
     } 
    } 

    public void Movement(){ 
     if(reset){ 
      left = false; 
      right = false; 
      up = false; 
      down = false; 

      snakex[0] = 0; 
      snakey[0] = 0; 
      snakeLength = 1; 
      appleLocation(); 
      reset = false; 
     } 

     if(right){ 
      snakex[0] += 20; 
     } 
     if(left){ 
      snakex[0] -= 20; 
     } 
     if(up){ 
      snakey[0] -= 20; 
     } 
     if(down){ 
      snakey[0] += 20; 
     } 
    } 

    public void mouseEntered(MouseEvent e){} 

    public void mouseExited(MouseEvent e){} 

    public void mousePressed(MouseEvent e){ 
     int mouseX = e.getX(); 
     int mouseY = e.getY(); 
     if(mouseX > buttonX && mouseX < buttonX + 300 && mouseY > buttonY && mouseY < buttonY + 75){ 
      ingame = true; 
     } 
    } 

    public void mouseReleased(MouseEvent e){} 

    public void mouseClicked(MouseEvent e){} 

    public void keyTyped(KeyEvent e) {} 

    public void keyPressed(KeyEvent e) { 
     int key = e.getKeyCode(); 
     if(key == 39 && !left) { 
      right = true; 
      up = false; 
      down = false; 
     } 
     if(key == 37 && !right){ 
      left = true; 
      up = false; 
      down = false; 
     } 
     if(key == 38 && !down){ 
      up = true; 
      left = false; 
      right = false; 
     } 
     if(key == 40 && !up){ 
      down = true; 
      left = false; 
      right = false; 
     } 
     if(key == 82){ 
      reset = true; 
     } 
    } 

    public void keyReleased(KeyEvent e) {} 

    @SuppressWarnings("serial") 
    public void paint(Graphics g) { 
     super.paintComponent(g); 
     if(menu){ 
      setBackground(Color.BLACK); 
      g.setColor(Color.green); 
      g.setFont(new Font("Courier New", Font.BOLD, 50)); 
      g.drawString("Snake Game", 150, 50); 
      g.drawRect(buttonX, buttonY, 300, 75); 
      g.setFont(new Font("Courier New", Font.BOLD, 40)); 
      g.drawString("PLAY", 250, 175); 
     } 
     if(ingame) { 
      setBackground(Color.WHITE); 
      int x = 0; 
      int y = 0; 
      for (x = 0; x < 30; x++) { 
       for (y = 0; y < 25; y++) { 
        g.setColor(Color.black); 
        g.fillRect(x * 20, y * 20, 19, 19); 
       } 
      } 

      g.setColor(Color.red); 
      g.fillOval(applex * 20, appley * 20, 19, 19); 

      forLogic(); 

      g.setColor(Color.green); 
      for (int i = snakeLength; i > 0; i--) { 
       snakex[i] = snakex[(i - 1)]; 
       snakey[i] = snakey[(i - 1)]; 
       g.fillRect(snakex[i], snakey[i], 19, 19); 
      } 
     } 
    } 
    public static void main(String[] args) throws InterruptedException { 
     JFrame jframe = new JFrame("Snake Game"); 
     Snake snake = new Snake(); 
     jframe.add(snake); 
     snake.addMouseListener(snake); 
     snake.addKeyListener(snake); 
     jframe.setSize(615, 540); 
     jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     jframe.setFocusable(true); 
     jframe.setVisible(true); 
     snake.requestFocusInWindow(); 
     jframe.setLocationRelativeTo(null); 

     while(true) { 
      if (!menu) { 
       ingame = true; 
      } 
      if (menu == ingame) { 
       ingame = false; 
      } 

      if (menu) { 
       snake.repaint(); 
      } 

      if (ingame) { 
       while (true) { 
        Thread.sleep(200); 
        snake.repaint(); 
       } 
      } 
     } 
    } 
} 
+4

您在该代码中遇到了一些主要问题,包括使用'while(true)'循环和'Thread.sleep(... )调用你的Swing代码,覆盖paint方法,并在其中调用超级paintComponent(一个不匹配的超级方法)(???),并在绘画方法中使用游戏逻辑。这表明使用第一原则进行重写会非常有益:使用Swing Timer作为游戏循环,在Swing代码中不使用其他延迟代码,重写paintComponent并在重写中调用相同的超级方法,将绘画与逻辑分离。 –

+3

...并在paint方法中调用'setBackground'!这可能会触发重绘,并关闭逻辑。应该在JPanel创建时调用此方法,而不是在绘画方法中重复。 –

+3

请勿将状态管理与您的绘画混合使用。你的游戏应该有两个不同的视图,一个菜单和一个游戏视图,这可以让每个班级专注于单个作业,并防止不必要的复杂代码。 [例如](http://stackoverflow.com/questions/33536829/jlabel-not-showing-up-no-matter-what-i-do/33536969#33536969) – MadProgrammer

回答

3

对于生硬的问题我很遗憾,但是这个代码存在很多问题,很难知道从哪里开始。


问题:

  • 首先,你要调用setBackground(...)绘画方法,这是有可能触发重绘,通常不会是太大的问题中...
  • 但是你有你的程序逻辑从你的paint mehtod覆盖内被调用,这是一个主要问题。正如你发现你没有完全甚至是部分地控制何时或者甚至是否调用paint方法或者多久,并且在其中有程序逻辑可能致命,并且可能导致你的程序完全失效由于setBackground调用。
  • 你也是while (true)循环和Thread.sleep(...)调用你的Swing代码,如果在Swing事件线程上启动Swing代码(如应该完成的那样),它可以完全冻结你的GUI。
  • 您正在覆盖paint方法,但在其中调用超级paintComponent(一种不匹配的超级方法),这将打破Swing图形链,可能导致重大的绘画不规则性。

建议:

  • 首先,让所有程序逻辑的任何和所有的绘画方法之外。
  • 删除全部Thread.sleep(...)调用和while (true)循环。
  • 使用一个Swing Timer,并在你的Timer的ActionListener中提前你的游戏“打勾”。
  • 在此“打勾”中,更新程序中关键字段的状态
  • 然后致电repaint();
  • 仅覆盖paintComponent方法
  • 并且在此覆盖中,调用相同的超级方法。
  • 在paintComponent中,使用修改后的字段来更改绘制的内容和方式。
  • 在您的类的构造函数中调用setBackground(...)一次。具有绘画方法内的游戏逻辑的 。这表明使用第一原则进行重写会非常有益:使用Swing Timer作为游戏循环,在Swing代码中不使用其他延迟代码,重写paintComponent并在重写中调用相同的超级方法,将绘画与逻辑分离。
  • 阅读教程。你猜猜哪一个在这里不起作用(正如你发现的那样)。

更多
  • 考虑创建一些非GUI逻辑类。
  • 这可以包括,GridPoint网格上每个点的x和y位置
  • 网格类GridPoint的二维数组,它是保存蛇移动的宇宙的逻辑网格。
  • SnakePoints可以包含一个ArrayList<GridPoint>,其中包含逻辑蛇的点位置。
  • 这最后一类还可以添加一个点,移动蛇,吃苹果的方法。
  • 一个计时器,会告诉SnakePoints提前一平方
2
public void paint(Graphics g) { 
    super.paintComponent(g); 

不要重写paint()。自定义绘画是通过覆盖paintComponent()完成的。那么你仍然会调用super.paintComponent(g);

if(key == 39 && !left) { 

请勿使用幻数。该API将有变量供您使用。我猜你想要KeyEvent.VK_LEFT。

if(key == 82){ 

但我不知道那个幻数是多少。

if(ingame) { 
     setBackground(Color.WHITE); 

不要在绘画方法中更改组件的属性。绘画方法仅适用于绘画。

也许你需要像setPlayingGame(Boolean)这样的方法。然后,如果设置为true,则设置玩游戏的属性。如果为false,则设置菜单属性。

或者甚至更好,你有两个面板。一个用于菜单,另一个用于游戏。然后,您使用CardLayout和交换面板,具体取决于是否要绘制菜单或游戏。

你有太多的游戏逻辑让我调试什么可能是你的问题。

0

蛇的速度太快,因为该方法“动作”蛇被称为过于频繁。您每200毫秒重绘一次,但程序的逻辑没有睡眠。您的Thread.sleep应该在程序的逻辑部分:

public void forLogic(){ 
    for(int i = snakeLength; i > 1; i--){ 
     if(snakeLength > 4 && snakex[0] == snakex[i] && snakey[0] == snakey[i]){ 
      System.out.println("You Loose \n Your Score was: " + snakeLength); 
      ingame = false; 
     } 
    } 

    Movement(); 

    if(snakex[0] >= 30*20){ 
     snakex[0] = 0; 
    } 
    if(snakex[0] < 0){ 
     snakex[0] = 29*20; 
    } 
    if(snakey[0] >= 25*20){ 
     snakey[0] = 0; 
    } 
    if(snakey[0] < 0){ 
     snakey[0] = 24*20; 
    } 

    if(snakex[0] == applex*20 && snakey[0] == appley*20) { 
     appleEaten = true; 
     snakeLength++; 
     //System.out.println(snakeLength); 
    } 

    if(appleEaten){ 
     appleLocation(); 
     appleEaten = false; 
    } 
    try { 
     Thread.sleep(speed); 
    } catch (InterruptedException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
}