Программирование с SurfaceView и стратегия потоков для разработки игр - PullRequest
15 голосов
/ 30 октября 2010

Я использую SurfaceView и поток рендеринга для разработки игры, основанной на такой структуре, как LunarLander.

Однако я столкнулся со многими проблемами, и здесь я хочу указать на них все. Желаю всем, кто хочет разработать игру, больше не нужно с ними бороться. И любой, кто имеет лучшее представление о структуре, может поделиться своим опытом, потому что я все еще новичок в Android и хочу учиться:)

[1] Функция thread.start() не должна вызываться более одного раза.

Во многих статьях упоминается, что для создания потока при создании поверхности необходимо выполнить рендеринг после приостановки действия с использованием метода:

public void surfaceCreated(SurfaceHolder holder) {
    // start the thread here so that we don't busy-wait in run()
    // waiting for the surface to be created
    if (thread.getState() == Thread.State.TERMINATED)
    {
        thread = new LunarThread(getHolder(), getContext(), getHandler());
    }
    thread.setRunning(true);
    thread.start();
}

Вы можете видеть, что если поток не завершен, а функция вызвана, действие завершается сбоем.

[2] Если вы нажали кнопку «питание» или «красный телефон», или телефон находится в режиме ожидания в течение нескольких минут, активность будет в состоянии onPause(), но поток все еще работает. Это очень плохая практика, поэтому мне нужно найти метод, позволяющий остановить поток и начать снова, когда onResume().

[3] Если блокировка экрана - книжная / альбомная, а ваша игра привязана к другой ориентации, блокировка экрана заставила вас «ориентироваться» один раз. Это означает, что нужно начать деятельность еще раз. Я до сих пор не могу найти решение для этого. (как я уже упоминал в ошибка ориентации экрана Android )

Вот мой код для решения этих проблем:

UIThread

public class UIThread extends Activity
{
    private gameView gameview;
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        gameview = (gameView) findViewById(R.id.gameview);
    }
    protected void onPause()
    {
        super.onPause();
        Log.v("MY_ACTIVITY", "onPause");
        gameview.terminateThread();
        System.gc();
    }
    protected void onResume()
    {
        super.onResume();
        Log.v("MY_ACTIVITY", "onResume");
        if (gameview.surfaceCreated == true)
        {
            gameview.createThread(gameview.getHolder());
        }
    }
}

Gameview

public class gameView extends SurfaceView implements SurfaceHolder.Callback
{
    boolean surfaceCreated;
    class gameThread extends Thread{}
    public gameView(Context context, AttributeSet attrs)
    {
        super(context, attrs);context.getResources();
            Log.v("MY_ACTIVITY", "gameView");
        surfaceCreated = false;
    }
    public void createThread (SurfaceHolder holder)
    {
            thread = new gameThread(holder);
        thread.run = true;
        thread.start();
    }
    public void terminateThread ()
    {
        thread.run = false;
        try
        {
            thread.join();
        }
        catch (InterruptedException e)
        {
            Log.e("FUNCTION", "terminateThread corrupts");
        }       
    }
    public void surfaceCreated(SurfaceHolder holder)
    {
        Log.v("MY_ACTIVITY", "surfaceCreated");
        if (surfaceCreated == false)
        {
            createThread(holder);
            surfaceCreated = true;
        }
    }
    public void surfaceDestroyed(SurfaceHolder holder) 
    {
        Log.v("MY_ACTIVITY", "surfaceDestroyed");
        surfaceCreated = false;
    }
}

Manifest

<activity android:name=".UIThread"
          android:screenOrientation="landscape"
          android:configChanges="orientation">

Я использую onResume() вместо surfaceCreated() для новой темы. И я установил логическое значение surfaceCreated, чтобы знать, что onResume() появляется при первом создании приложения или из ситуации «выключения экрана».

Таким образом, нить умирает каждый раз, когда вызывается onPause(). Кажется, это хорошая практика. Еще один способ остановить поток и возобновить его снова - синхронизировать объект и вызвать wait / notify. Но я не знаю, лучше это или нет.

Есть ли лучший способ управления потоком рендеринга?

Ответы [ 3 ]

3 голосов
/ 29 апреля 2012

решение здесь

public void surfaceCreated(SurfaceHolder holder){

            if (plot_thread.getState() == Thread.State.TERMINATED) {
                plot_thread = new WaveformPlotThread(getHolder(), this);
                plot_thread.setRunning(true);
                plot_thread.start();
            }
            else {
                plot_thread.setRunning(true);
                plot_thread.start();
            }
        }
3 голосов
/ 13 июня 2013

Я надеюсь, что это может помочь

@Override
public void surfaceCreated(SurfaceHolder holder) {

   //if it is the first time the thread starts
   if(thread.getState() == Thread.State.NEW){
    thread.setRunning(true);//riga originale
    thread.start();//riga originale
   }

   //after a pause it starts the thread again
   else
   if (thread.getState() == Thread.State.TERMINATED){
       thread = new MainThread(getHolder(), this);
       thread.setRunning(true);
       thread.start();  // Start a new thread
       }
   }

и это

    @Override
    protected void onPause() {
    Log.d(TAG, "Pausing...");
    MainThread.running=false;//this is the value for stop the loop in the run()
    super.onPause();
    }
1 голос
/ 18 января 2011

Я имел дело с той же проблемой.Я изучаю разработку игр для Android, а также основываю свой первый проект на примере Lunar Lander.Я обнаружил, что проект SpriteMethodTest , созданный Крисом Пруеттом, реализует гораздо лучший подход с точки зрения управления потоками.Однако все дело в использовании методов уведомления и ожидания.Я не могу сказать, лучше ли это, чем ваше решение или нет.

...