#java #android #thread-sleep
#java #Android #поток-сон
Вопрос:
Я создаю памятную игру для школьного проекта, и у меня проблема с моим Thread.sleep.
Когда игрок возвращает две карты, программа проверяет, идентичны ли они. Если это не так, то они поворачиваются лицевой стороной вниз. Поскольку программа делает это почти мгновенно, я бы хотел сделать паузу, чтобы у игрока было время увидеть вторую карту. Проблема в том, что разрыв происходит до того, как карта поворачивается
if (deck1.contains(carte)) {
if (carte.getEtat() == 0) {
carte.setEtat(1);
cartesRetournees1.add(carte);
majAffichage(carte);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
if (cartesRetournees1.size() == 2) {
int nouveauEtat = 0;
if (cartesRetournees1.get(0).getMotif() == cartesRetournees1.get(1).getMotif()) {
nouveauEtat = -1;
this.nbPairsTrouvees ;
}
cartesRetournees1.get(0).setEtat(nouveauEtat);
cartesRetournees1.get(1).setEtat(nouveauEtat);
majAffichage(cartesRetournees1.get(0));
majAffichage(cartesRetournees1.get(1));
cartesRetournees1.remove(1);
cartesRetournees1.remove(0);
if (nbPairsTrouvees == this.plateau.size()) System.out.println("GAGNE !");
}
}
В моей игре Thread.sleep работает перед majAffichage(carte), и я не понимаю, почему.
Я не знаю, как решить мою проблему, и если вы, ребята, можете мне помочь, я был бы очень благодарен 🙂
Ответ №1:
Это из-за того, как работает пользовательский интерфейс и рисование. Внутри платформы Android в основном потоке существует гигантский цикл обработки сообщений. Одним из таких сообщений является сообщение о выводе. Экран отображается только при обработке этого сообщения. Сообщения могут обрабатываться только в верхней части цикла. Итак, если поток спит, он никогда не обрабатывает новые сообщения во время этого сна, и экран не будет обновляться.
Если вы хотите что-то сделать с такой задержкой, есть два варианта:
1) Вместо того, чтобы переходить в режим ожидания, отправьте Runnable в основной цикл с помощью postDelayed, чтобы runnable запускался в указанное вами время в будущем.
2) Используйте второй поток и запускайте в нем всю свою игровую логику, передавая ему сообщения при возникновении событий пользовательского интерфейса и передавая события обратно в основной поток, когда ему нужно рисовать.
Ответ №2:
Во-первых, вы приостанавливаете не основной поток, а фоновый поток, потому что вы не хотите замораживать все приложение.
Во-вторых, используйте System.sleep() вместо Thread.sleep(), потому что первый не генерирует исключение.
Ответ №3:
Спасибо за ваш ответ! Я создал второй поток.
package com.example.jeudumemo;
import android.graphics.Canvas;
public class GameLoopThread extends Thread
{
private final static int FRAMES_PER_SECOND = 30;
private final static int SKIP_TICKS = 1000 / FRAMES_PER_SECOND;
private final Game view;
private boolean running = false;
public GameLoopThread(Game view) {
this.view = view;
}
public void setRunning(boolean run) {
running = run;
}
@Override
public void run()
{
long startTime;
long sleepTime;
while (running)
{
startTime = System.currentTimeMillis();
synchronized (view.getHolder()) {view.update();}
Canvas c = null;
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {view.draw(c);}
}
finally
{
try {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
} catch(IllegalArgumentException iae) { iae.printStackTrace(); }
}
sleepTime = SKIP_TICKS-(System.currentTimeMillis() - startTime);
try {
if (sleepTime >= 0) {sleep(sleepTime);}
}
catch (Exception e) {}
}
}
}
и я вызываю это в своем основном классе :
@Override
public void surfaceCreated(SurfaceHolder holder) {
if(gameLoopThread.getState()==Thread.State.TERMINATED) {
gameLoopThread=new GameLoopThread(this);
}
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
gameLoopThread.setRunning(false);
while (retry) {
try {
gameLoopThread.join();
retry = false;
}
catch (InterruptedException e) {}
}
}
public void controleJeu(Carte carte) {
if(deck1.contains(carte)) {
if (carte.getEtat() == 0) {
carte.setEtat(1);
cartesRetournees1.add(carte);
majAffichage(carte);
}
if (cartesRetournees1.size() == 2) {
try {
gameLoopThread.sleep(500);
}catch(InterruptedException ie) { ie.printStackTrace(); }
int nouveauEtat = 0;
if (cartesRetournees1.get(0).getMotif() == cartesRetournees1.get(1).getMotif()) {
nouveauEtat = -1;
this.nbPairsTrouvees ;
}
cartesRetournees1.get(0).setEtat(nouveauEtat);
cartesRetournees1.get(1).setEtat(nouveauEtat);
majAffichage(cartesRetournees1.get(0));
majAffichage(cartesRetournees1.get(1));
cartesRetournees1.remove(1);
cartesRetournees1.remove(0);
}
}
else if(deck2.contains(carte)) {
if (carte.getEtat() == 0) {
carte.setEtat(1);
cartesRetournees2.add(carte);
majAffichage(carte);
}
if (cartesRetournees2.size() == 2) {
try {
gameLoopThread.sleep(500);
}catch(InterruptedException ie) { ie.printStackTrace(); }
int nouveauEtat = 0;
if (cartesRetournees2.get(0).getMotif() == cartesRetournees2.get(1).getMotif()) {
nouveauEtat = -1;
this.nbPairsTrouvees ;
}
cartesRetournees2.get(0).setEtat(nouveauEtat);
cartesRetournees2.get(1).setEtat(nouveauEtat);
majAffichage(cartesRetournees2.get(0));
majAffichage(cartesRetournees2.get(1));
cartesRetournees2.remove(1);
cartesRetournees2.remove(0);
}
}
if (nbPairsTrouvees == nbDeck*6) { showPopupVictoire(this); this.victory = true; }
System.out.println(nbDeck);
}
Моя проблема все еще продолжается. Я думаю, что я не использовал свой GameLoopThread, поскольку он должен использоваться. Есть замечания?