View Invalidate() 和 Handler 的问题

发布于 2024-10-12 04:54:39 字数 2476 浏览 2 评论 0原文

我已经尝试解决这个问题超过两天了,并且已经变得非常绝望。

我想为 Android 编写一个“类似西洋跳棋”的棋盘游戏。游戏引擎本身有点完整,但我在更新视图时遇到问题。

我写了一个小示例类来演示我的问题:

public class GameEngineView extends View {

private static final String TAG = GameEngineView.class.getSimpleName();

private int px;
private int py;
private int cx;
private int cy;

private boolean players_move;
private int clickx;
private int clicky;

Random rgen;

private RefreshHandler mRedrawHandler = new RefreshHandler();

class RefreshHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {            
        GameEngineView.this.update();
        GameEngineView.this.invalidate();
        Log.d(TAG, "invalidate()");            
    }

    public void sleep(long delayMillis) {
        this.removeMessages(0);
        sendMessageDelayed(obtainMessage(0), delayMillis);
    }
};

public GameEngineView(Context context) {
    super(context);
    setFocusable(true);
    players_move = true;
    rgen = new Random();
}

public void update() {
    updateGame();
    Log.d(TAG, "update -> sleep handler");
    mRedrawHandler.sleep(100);
}

public void updateGame() {
    if(players_move) {
        px = clickx;
        py = clicky;
    } else {
        calcAIMove();
        switchMove();
    }        
}

public void switchMove() {
    players_move = !players_move;
}

public void calcAIMove() {
    for(int i = 0; i < 20000; i++) {
        cx = rgen.nextInt(getWidth());
        cy = rgen.nextInt(getHeight());
    }
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.d(TAG, "event");
    int eventaction = event.getAction();
    if(eventaction == MotionEvent.ACTION_DOWN) {
        Log.d(TAG, "action_down");
        clickx = (int) event.getX();
        clicky = (int) event.getY();
        switchMove();
        update();
    }
    return super.onTouchEvent(event);
}

@Override
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Paint green = new Paint();
    green.setColor(Color.GREEN);
    Paint red = new Paint();
    red.setColor(Color.RED);
    canvas.drawColor(Color.BLACK);
    canvas.drawCircle(px, py, 25, green);
    canvas.drawCircle(cx, cy, 25, red);
}

}

函数 calcAIMove() 只是消耗时间来模拟棋盘游戏中位置的真实评估。

现在我的问题是:如果玩家点击(移动),当人工智能移动计算完成时,首先绘制绿球。所以两个动作是同时绘制的。

我想知道如何实现这一点: - 玩家点击 - 绘制绿球 -AI计算 - 红球被抽出 -等等..

在网上搜索时,我发现了很多游戏循环示例,但它们都需要一个不断轮询的线程..没有这个应该是可能的,因为整个程序是按顺序运行的..对吧?

希望得到建议。

谢谢, 戴夫

i'm trying to fix this problem for more than 2 days now and have become quite desperate.

I want to write a 'Checkers-like' board game for android. The game engine itself is kinda complete but i have problems with updating the views.

I wrote a little example class to demonstrate my problem:

public class GameEngineView extends View {

private static final String TAG = GameEngineView.class.getSimpleName();

private int px;
private int py;
private int cx;
private int cy;

private boolean players_move;
private int clickx;
private int clicky;

Random rgen;

private RefreshHandler mRedrawHandler = new RefreshHandler();

class RefreshHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {            
        GameEngineView.this.update();
        GameEngineView.this.invalidate();
        Log.d(TAG, "invalidate()");            
    }

    public void sleep(long delayMillis) {
        this.removeMessages(0);
        sendMessageDelayed(obtainMessage(0), delayMillis);
    }
};

public GameEngineView(Context context) {
    super(context);
    setFocusable(true);
    players_move = true;
    rgen = new Random();
}

public void update() {
    updateGame();
    Log.d(TAG, "update -> sleep handler");
    mRedrawHandler.sleep(100);
}

public void updateGame() {
    if(players_move) {
        px = clickx;
        py = clicky;
    } else {
        calcAIMove();
        switchMove();
    }        
}

public void switchMove() {
    players_move = !players_move;
}

public void calcAIMove() {
    for(int i = 0; i < 20000; i++) {
        cx = rgen.nextInt(getWidth());
        cy = rgen.nextInt(getHeight());
    }
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.d(TAG, "event");
    int eventaction = event.getAction();
    if(eventaction == MotionEvent.ACTION_DOWN) {
        Log.d(TAG, "action_down");
        clickx = (int) event.getX();
        clicky = (int) event.getY();
        switchMove();
        update();
    }
    return super.onTouchEvent(event);
}

@Override
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Paint green = new Paint();
    green.setColor(Color.GREEN);
    Paint red = new Paint();
    red.setColor(Color.RED);
    canvas.drawColor(Color.BLACK);
    canvas.drawCircle(px, py, 25, green);
    canvas.drawCircle(cx, cy, 25, red);
}

}

The function calcAIMove() just burns time to simulate a real evaluation of the position in a board game.

Now my Problem is: If the player clicks(makes a move) the green ball is first drawn when the ai move calculation has been complete. So both moves are drawn at the same time.

I wonder HOW to accomplish this:
-Player clicks
-green Ball is drawn
-AI calculates
-red ball is drawn
-and so on..

When searching the web i found a lot of game loop examples but they all need a Thread with constant polling.. it should be possible without this since the whole program runs sequentially .. right?

Hoping for advice.

thanks,
Dave

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

难以启齿的温柔 2024-10-19 04:54:39

您的游戏的工作方式如下:

  1. 用户进行移动
  2. 游戏视图更新
  3. 游戏切换到 CPU 的回合
  4. 睡眠以模拟 CPU 玩家思维(如果移动计算微不足道)
  5. 计算 CPU 的移动
  6. 游戏视图更新
  7. 游戏切换轮到玩家

在这样的回合制游戏中不需要不断地轮询。唯一应该睡眠的地方是在步骤 4 中,因此您可以将其从其他区域中删除(不需要旧的 update() 方法,该方法只是延迟调用 updateGame( ))。

实现此目的的一种方法是简单地延迟对 calcAIMove()switchMove() 的调用,方法是将其放入 Runnable 并使用 Handler.postDelayed() 或类似的。

我不明白你如何在轮到 CPU 时禁用触摸事件,如果 switchMove()update() 被禁用,这可能会导致许多其他问题仍然被称为...

Here is how your game could work:

  1. user makes a move
  2. game view is updated
  3. game switches to CPU's turn
  4. sleep to simulate CPU player thinking (if move computation is trivial)
  5. compute CPU's move
  6. game view is updated
  7. game switches to player's turn

There is no need for constant polling in a turn-based game like this. The only place you should have sleep is in step 4, so you can remove it from the other areas (no need for the old update() method which is just a delayed call to updateGame()).

One way to implement this is to simply delay the call to calcAIMove() and switchMove() by putting it into a Runnable and using Handler.postDelayed() or similar.

I don't see how you are disabling touch events when it is the CPU's turn, which can lead to a host of other problems if switchMove() and update() are still being called...

莫言歌 2024-10-19 04:54:39

在游戏循环中,您可以使用 TimerTask 的实例来确保 greenBall 和其他绘图任务之间存在一定的延迟。像 Snake 和 LunarLander 这样的游戏教程是关于何时以及是否需要使视图失效的很好的参考。希望这会有所帮助!

In your game loop you can use a instance of TimerTask to ensure certain delay between greenBall and some other drawing task. Game tutorials like Snake and LunarLander are great references on when and if you need to invalidate your View. Hope this helps a little!

甜妞爱困 2024-10-19 04:54:39

好的,antonyt 提供的答案帮助我解决了这个问题。我为其他感兴趣的人提供了正确的代码。

public class GameEngineView extends View {

private static final String TAG = GameEngineView.class.getSimpleName();

private int px;
private int py;
private int cx;
private int cy;

private boolean players_move;
private int clickx;
private int clicky;

Random rgen;

/**
 * Create a simple handler that we can use to cause animation to happen.  We
 * set ourselves as a target and we can use the sleep()
 * function to cause an update/invalidate to occur at a later date.
 */
private RefreshHandler mRedrawHandler = new RefreshHandler();

class RefreshHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        GameEngineView.this.update();
    }

    public void sleep(long delayMillis) {
        this.removeMessages(0);
        sendMessageDelayed(obtainMessage(0), delayMillis);
    }
};

public GameEngineView(Context context) {
    super(context);
    setFocusable(true);
    players_move = true;
    rgen = new Random();
}

public void update() {
    if(players_move) {
        Log.d(TAG, "new player x");
        px = clickx;
        py = clicky;
        GameEngineView.this.invalidate();
        switchMove();
        mRedrawHandler.sleep(100);
    } else {
        Log.d(TAG, "new ai x");
        calcAIMove();
        GameEngineView.this.invalidate();
        switchMove();            
    }

    Log.d(TAG, "update -> sleep handler");        
}


public void switchMove() {
    players_move = !players_move;
}

public void calcAIMove() {
    for(int i = 0; i < 100000; i++) {
        cx = rgen.nextInt(getWidth());
        cy = rgen.nextInt(getHeight());
    }
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.d(TAG, "event");
    int eventaction = event.getAction();
    if(players_move && eventaction == MotionEvent.ACTION_DOWN) {
        Log.d(TAG, "action_down");
        clickx = (int) event.getX();
        clicky = (int) event.getY();
        update();
    }
    return super.onTouchEvent(event);
}

@Override
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawColor(Color.BLACK);
    Paint green = new Paint();
    green.setColor(Color.GREEN);
    Paint red = new Paint();
    red.setColor(Color.RED);

    canvas.drawCircle(px, py, 25, green);
    canvas.drawCircle(cx, cy, 25, red);
}

}

ok so the answer provided by antonyt helped me solve this.. I provide the corrected code for other interested ones.

public class GameEngineView extends View {

private static final String TAG = GameEngineView.class.getSimpleName();

private int px;
private int py;
private int cx;
private int cy;

private boolean players_move;
private int clickx;
private int clicky;

Random rgen;

/**
 * Create a simple handler that we can use to cause animation to happen.  We
 * set ourselves as a target and we can use the sleep()
 * function to cause an update/invalidate to occur at a later date.
 */
private RefreshHandler mRedrawHandler = new RefreshHandler();

class RefreshHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        GameEngineView.this.update();
    }

    public void sleep(long delayMillis) {
        this.removeMessages(0);
        sendMessageDelayed(obtainMessage(0), delayMillis);
    }
};

public GameEngineView(Context context) {
    super(context);
    setFocusable(true);
    players_move = true;
    rgen = new Random();
}

public void update() {
    if(players_move) {
        Log.d(TAG, "new player x");
        px = clickx;
        py = clicky;
        GameEngineView.this.invalidate();
        switchMove();
        mRedrawHandler.sleep(100);
    } else {
        Log.d(TAG, "new ai x");
        calcAIMove();
        GameEngineView.this.invalidate();
        switchMove();            
    }

    Log.d(TAG, "update -> sleep handler");        
}


public void switchMove() {
    players_move = !players_move;
}

public void calcAIMove() {
    for(int i = 0; i < 100000; i++) {
        cx = rgen.nextInt(getWidth());
        cy = rgen.nextInt(getHeight());
    }
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.d(TAG, "event");
    int eventaction = event.getAction();
    if(players_move && eventaction == MotionEvent.ACTION_DOWN) {
        Log.d(TAG, "action_down");
        clickx = (int) event.getX();
        clicky = (int) event.getY();
        update();
    }
    return super.onTouchEvent(event);
}

@Override
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawColor(Color.BLACK);
    Paint green = new Paint();
    green.setColor(Color.GREEN);
    Paint red = new Paint();
    red.setColor(Color.RED);

    canvas.drawCircle(px, py, 25, green);
    canvas.drawCircle(cx, cy, 25, red);
}

}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文