【问题标题】:Code works sometime but not others (memory or thread issue)代码有时有效,但有时无效(内存或线程问题)
【发布时间】:2012-12-11 13:22:01
【问题描述】:

我遇到了这个奇怪的问题,我不确定是什么原因造成的。有时这个问题甚至不存在。根据我的猜测,这是 Java 内存问题还是某种线程问题。

我有一个Ship,飞船会发射Bullets 如果我按住Space 键,飞船会发射子弹。我将子弹设置为每 200 毫秒发射一次。有时他们射击得很好并且以相同的速度移动!其他时候,他们射击他们以不同的速度移动。这是什么原因造成的?

package JGame.Actions;

import JGame.GameObject.GameObject;
import javax.swing.AbstractAction;

public class MoveAction extends Action implements Runnable{

    protected GameObject obj;
    protected int endX = 0, endY = 0;
    protected int moveAmount = 0;
    protected Thread thread;

    public void moveToY(GameObject obj, int y, int amount, AbstractAction complete){
        this.obj = obj;
        this.endY = y;
        this.moveAmount = amount;
        this.complete = complete;
        thread = new Thread(this);
        thread.start();
    }

    public void run(){
        try{
            boolean run = true;
            while(run){
                int objY = obj.getY();
                if(objY > this.endY){
                    obj.setY(obj.getY() - 1);
                }else if(objY < this.endY){
                    obj.setY(obj.getY() + 1);
                }else{
                    run = false;
                    this.actionComplete();
                }
                thread.sleep(moveAmount);
            }
        }catch(Exception e){
        }
    }
}

动作完成:

package JGame.Actions;

import javax.swing.AbstractAction;

public class Action {
    protected boolean actionComplete = false;
    protected AbstractAction complete;

    public void actionComplete(){
        complete.actionPerformed(null);
    }
}

在我的代码中,我调用moveToY 这是一个非常简单的调用,但有时Bullets 以不同的速度移动(错误),而其他人则以相同的速度移动(正确)。我不知道提到子弹移动时有时会减慢一两秒钟然后加速回到正确的速度是否会有所帮助。

编辑:主线程

以下是我与paintComponent的主线程

@Override
public void run(){
    try{
        while(true){
            // Check for key press events
            Iterator actions = KeyboardMap.map.entrySet().iterator();
            while(actions.hasNext()){
                Map.Entry ap = (Map.Entry)actions.next();
                Mapping mp = (Mapping)ap.getValue();
                if(mp.pressed){
                    mp.run();
                }
            }

            // Check for click mouse events
            Iterator actions2 = MouseMap.map.entrySet().iterator();
            while(actions2.hasNext()){
                Map.Entry ap = (Map.Entry)actions2.next();
                Mapping mp = (Mapping)ap.getValue();
                if(mp.pressed){
                    mp.run();
                }
            }

            for(GameObject go : gameObjects){
                if(!go.getLeaveScreen()){
                    int goWidth = go.getWidth();
                    int goHeight = go.getHeight();
                    int goX = go.getX();
                    int goY = go.getY();
                    int gameWidth = Game.width;
                    int gameHeight = Game.height;
                    if(goX + goWidth >= gameWidth){
                        go.setX(gameWidth - goWidth);
                    }
                    if(goX <= 0){
                        go.setX(0);
                    }
                    if(goY + goHeight >= gameHeight){
                        go.setY(gameHeight - goHeight);
                    }
                    if(goY <= 0){
                        go.setY(0);
                    }
                }
            }
            this.repaint();
            Thread.sleep(roomSpeed);
        }
    }catch(Exception e){
    }
}

public void paintComponent(Graphics g){
    try{
        g.drawImage(bg, 0, 0, this);
        for(int i = 0; i < gameObjects.size(); i++){
            GameObject go = gameObjects.get(i);
            g.drawImage(go.getSprite(), go.getX(), go.getY(), this);
        }
    }catch(Exception e){
    }
}

【问题讨论】:

  • +1 到 JBNizet,我建议重塑你的游戏循环以更新实体移动等,这样你就不必通过其他线程执行此操作,因此单个线程将处理所有实体的移动等屏幕。请参阅此固定时间步长游戏循环示例:stackoverflow.com/questions/13999506/threads-with-key-bindings/…
  • 没错。一个循环来统治他们。

标签: java multithreading swing


【解决方案1】:

好吧,我猜你有大量的并发线程在运行(一个通过移动子弹),并且你希望每个线程在精确的 moveAmount 毫秒后唤醒。您不能有这样的保证,因为线程调度程序允许每个线程一次运行一段时间,因此您可能会出现故障。

另一个问题是你似乎在事件调度线程之外对 Swing 组件执行修改,Swing's threading policy 明确禁止这样做。

【讨论】:

  • 我不认为我做任何修改 Swing 指数,我在哪里做呢?
  • this.actionComplete() 在 Swing AsbtractAction 上调用 actionPerformed(),我想这会改变 GUI 中的某些内容。此外,您没有显示在循环的每次迭代中如何重新绘制 GUI,但我猜您正在以某种方式重新绘制,否则这个循环的意义何在?
  • 我添加了更多代码,我感觉我误解的是我不应该在 Swing 中使用线程,但我不需要主游戏线程吗?如果是这样,它不应该在有人玩游戏时修改窗口中的对象吗?
  • 阅读我给你的链接,以及链接指向的教程。每次您想从游戏线程访问 Swing 组件(或其模型)时,您必须使用SwingUtilities.invoke[Later|AndWait]()。不过,repaint() 可以从任何线程调用。
【解决方案2】:

了解多线程的使用以及如何正确制作游戏的处理结构。 对您的情况最重要的信息:您应该只使用一个线程来处理/移动所有子弹,然后渲染它们。

【讨论】:

    【解决方案3】:

    更改代码最重要的一点是不要用Thread.sleep 模拟时间。使用Swing Timer,您将安排它每moveAmount 毫秒执行一次,作为奖励,代码在事件调度线程上执行,您无需付出任何努力。

    【讨论】:

    • 取决于 GameObject 是什么,即如果它是一个 JComponent EDT 将适用,如果不是,即 OP 使用扩展的 Rectangle/Shape 类而不是 Swing 计时器将不是必要的,因为改变形状的坐标/Rectangle 不会修改任何 Swing UI 组件
    • 无论是否需要 EDT,出于所有其他原因,这仍然是要走的路。
    • 如果没有 Swing 组件被操纵,我会不同意。我认为在 EDT 中执行不必要的代码(不修改 Swing 组件的代码)是不正确的,TimerTask 应该比 IMO 使用得好
    • 您没有考虑到,如果所涉及的计算对 EDT 没有真正的负担,那么使用这种方法会管理和浪费全新的资源。
    猜你喜欢
    • 2019-03-14
    • 1970-01-01
    • 2020-01-14
    • 2018-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多