Проблема с представлением Invalidate() и обработчиком
Я пытаюсь решить эту проблему в течение более 2 дней и стал довольно отчаянным.
Я хочу написать настольную игру в стиле "шашки" для 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 завершено. Итак, оба хода нарисованы одновременно.
Интересно, КАК это сделать: -Клики игрока -Зеленый шар нарисован -А я рассчитываю -Красный шар нарисован -и так далее..
При поиске в Интернете я нашел много примеров игровых циклов, но им всем нужен поток с постоянным опросом... это должно быть возможно без этого, так как вся программа работает последовательно... верно?
Надеюсь на совет.
спасибо Дэйв
3 ответа
Вот как ваша игра может работать:
- пользователь делает ход
- вид игры обновлен
- игра переключается на ход процессора
- сон для симуляции мышления игрока процессора (если вычисление хода тривиально)
- вычислить движение процессора
- вид игры обновлен
- игра переключается на ход игрока
В пошаговой игре, подобной этой, нет необходимости в постоянном опросе. Единственное место, где вы должны спать - это шаг 4, так что вы можете удалить его из других областей (старый не нужен) update()
метод, который является просто отложенным вызовом updateGame()
).
Один из способов реализовать это - просто отложить вызов calcAIMove()
а также switchMove()
положив его в Runnable
и используя Handler.postDelayed()
или похожие.
Я не вижу, как вы отключаете сенсорные события, когда наступает очередь процессора, что может привести к множеству других проблем, если switchMove()
а также update()
все еще называют...
В вашем игровом цикле вы можете использовать экземпляр TimerTask, чтобы обеспечить определенную задержку между greenBall и некоторыми другими задачами рисования. Учебные пособия по игре, такие как Snake и LunarLander, являются отличными справочными материалами о том, когда и если вам нужно аннулировать свой вид. Надеюсь, это немного поможет!
Хорошо, так что ответ, предоставленный 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);
}
}