Android(java) threadが非常に低速になる

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
奥兵

Android(java) threadが非常に低速になる

#1

投稿記事 by 奥兵 » 11年前

Android向けにOpenGLESを使用しシューティングゲームを制作しています。

起動画面(タイトル)のActivityからMain(ゲーム部分)となるActivityに遷移しゲームが始まります。ここまで正常に動作するのですが、
その後、ゲームオーバーやバックキーでタイトル画面に戻った後、再びスタートを押してゲームを始めるとゲームが非常に低速になってしまいます。(1fps以下)
getStateメソッドで確認するとthreadの状態がTIMED_WATINGになっていました。
実現したいことは、ポーズ画面など特定、任意の状態でthreadを一時停止させることと、一度スタート画面のActivityに戻ったのち正常にthreadを走らせることです。

↓のページ同様にwait等も試してみたのですが、下のページの質問者同様に応答がなくなってしまいました。
https://groups.google.com/forum/#!topic ... G1H-1TJm9k

皆様のお知恵を拝借したくトピックを作成させていただきました。アドバイス等、どうかよろしくお願いします。


MainActivity(ゲーム本体部分のActivity)

コード:

package com.example.gamebase;

import java.util.LinkedList;

import javax.microedition.khronos.opengles.GL10;

import Tasks.BackGround;
import Tasks.Task;
import Tasks.UnitMgr;
import android.content.Context;
import android.util.Log;

public class GameMgr extends Thread{
 	
		private static GameMgr gMgr = new GameMgr();
	
		private static boolean threadRunning = true;
		private static LinkedList<Task> _taskList = new LinkedList<Task>(); //タスクリスト
		public static GameContext gameContext;
		static Context context;
		
		private GameMgr(){}
		
		public static GameMgr getInstance(Context _context, GL10 gl){
			context = _context;
			gameContext = new GameContext(gl);
			_taskList.add( new FpsController() );
        	_taskList.add( new BackGround(context, gameContext) );
        	_taskList.add( new UnitMgr(context, gameContext) );
			return gMgr;
		}
		
		public static void threadStopper(){
			threadRunning = false;
		}
		
        @Override
        public void run() {

        	while ((_taskList!=null) && (threadRunning == true)) {	
        		if(gameContext.runState == gameContext.RUN){
		        	gameContext.loopInc();
		        	for(int i=0; i<_taskList.size(); i++){
			               if(_taskList.get(i).onUpdate(gameContext) == false){ //更新失敗なら
			                    _taskList.remove(i);   	//そのタスクを消す
			                    i--;
			               }       
		        	}
        		}else{
	        	//	Log.d("thread","thread Pause");
	        	}
        	}
        	Log.d("threadState","thread Stoped");
        	return;
		}
       
        public void mainDraw(GL10 gl) {
                for(int i=0; i<_taskList.size(); i++){
                        _taskList.get(i).mainDraw(gl);//描画
                }
        }
        
}
MainRenderer (MainActivityのコンストラクタで初期化されるGLViewのコンストラクタで初期化されます。このクラスがThreadのインスタンスを持っています。)

コード:

package glView;

import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.GLU;
import android.util.Log;

import com.example.gamebase.GameMgr;

public class MainRenderer implements Renderer {

	
	private Context context;
	public  GameMgr gameMgrThread;
	
	final float VIEW_WIDTH = 1080;
	final float VIEW_HEIGHT = 1776;
	public float scale;
	
	public MainRenderer(Context _context){
		context = _context;
	}
	
	@Override
	public void onDrawFrame(GL10 gl) {
		
		// 画面を塗りつぶす
        gl.glClearColor(0.f, 0.4f, 0.2f, 1.f);
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

        gameMgrThread.mainDraw(gl);
       
	}
//*
	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		
		//サイズ対応部分
		float scaleX = width / VIEW_WIDTH;
        float scaleY = height /  VIEW_HEIGHT;
        if(scaleX > scaleY){
        	scale = scaleY;
        }else{
        	scale = scaleX;
        }
        
        int xAdjustment = (int)((width-VIEW_WIDTH*scale)/ 2);
        int yAdjustment = (int)((height-VIEW_HEIGHT*scale)/ 2);
        
        gameMgrThread.gameContext.scale = scale;
        gameMgrThread.gameContext.xAdjustment = xAdjustment;
        gameMgrThread.gameContext.yAdjustment = yAdjustment;
        
        
        // GLで利用する領域を設定
        gl.glViewport(xAdjustment, yAdjustment, (int)(VIEW_WIDTH*scale), (int)(VIEW_HEIGHT*scale));
//        gl.glViewport(0, 0, width, height);
        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();
        
        GLU.gluOrtho2D(gl, 0.0f, (int)(VIEW_WIDTH), (int)(VIEW_HEIGHT), 0.0f);// pixel指定
        
        //F02E w:1080 h:1776
	}
	/*/
	public void onSurfaceChanged(GL10 gl, int w, int h) {
        gl.glViewport(0, 0, w, h);
        gl.glMatrixMode(GL10.GL_PROJECTION);
        GLU.gluOrtho2D(gl, 0.0f, 1080, 1776, 0.0f);//1776 1920
    }
/**/
	
	@Override
	public void onSurfaceCreated(GL10 gl,javax.microedition.khronos.egl.EGLConfig config) {
		
		//背景色をクリア
		gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
		//ディザを無効化
		gl.glDisable(GL10.GL_DITHER);
		//gl.glEnable(GL10.GL_DEPTH_TEST);
		//テクスチャ機能ON
		gl.glEnable(GL10.GL_TEXTURE_2D);
		//透明可能に
		gl.glEnable(GL10.GL_ALPHA_TEST);
		//ブレンド可能に
		gl.glEnable(GL10.GL_BLEND);		
		gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
		gl.glDepthFunc(GL10.GL_LEQUAL);
		
		context.getResources();
		
		gameMgrThread = GameMgr.getInstance(context, gl);//new GameMgr(context, gl);
		gameMgrThread.gameContext.gl = gl;
		if(gameMgrThread.isAlive()){
			gameMgrThread.gameContext.runState = gameMgrThread.gameContext.RUN;
		}else{	
			Log.d("thread state" , gameMgrThread.getState().toString());
			gameMgrThread.start();
		}
	}
	
}
GameMgr (Thread部分)

コード:

package com.example.gamebase;

import java.util.LinkedList;

import javax.microedition.khronos.opengles.GL10;

import Tasks.BackGround;
import Tasks.Task;
import Tasks.UnitMgr;
import android.content.Context;
import android.util.Log;

public class GameMgr extends Thread{
 	
		private static GameMgr gMgr = new GameMgr();
		private final static Object lock = new Object();//String("gameMgrLock");
		
		private static boolean threadRunning = true;
		private static LinkedList<Task> _taskList = new LinkedList<Task>(); //タスクリスト
		public static GameContext gameContext;
		static Context context;
		
		private GameMgr(){}
		
		public static GameMgr getInstance(Context _context, GL10 gl){
			context = _context;
			gameContext = new GameContext(gl);
			_taskList.add( new FpsController() );
        	_taskList.add( new BackGround(context, gameContext) );
        	_taskList.add( new UnitMgr(context, gameContext) );
			return gMgr;
		}
		
		public void threadPoser(){
			synchronized (lock) {
				try {
					lock.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		
		public void threadResumer(){
			synchronized (lock) {
					lock.notifyAll();
			}
		}
		
		public static void threadStopper(){
			threadRunning = false;
		}
		
        @Override
        public void run() {

        	while ((_taskList!=null) && (threadRunning == true)) {	
        		if(gameContext.runState == gameContext.RUN){
		        	gameContext.loopInc();
		        	for(int i=0; i<_taskList.size(); i++){
			               if(_taskList.get(i).onUpdate(gameContext) == false){ //更新失敗なら
			                    _taskList.remove(i);   	//そのタスクを消す
			                    i--;
			               }       
		        	}
        		}else{
	        	//	Log.d("thread","thread Pause");
	        	}
        	}
        	Log.d("threadState","thread Stoped");
        	return;
		}
       
        public void mainDraw(GL10 gl) {
                for(int i=0; i<_taskList.size(); i++){
                        _taskList.get(i).mainDraw(gl);//描画
                }
        }
        
}

ISLe()

Re: Android(java) threadが非常に低速になる

#2

投稿記事 by ISLe() » 11年前

本来ひとつしか必要のないスレッドを大量に生成してしまって実行速度が低下しているのではないでしょうかね。

スレッドの実行継続を表すフラグの変数がstaticなので生成・破棄のタイミングによっては旧スレッドが終了しません。
スレッドを管理するメンバもインスタンスに含め、終了する際は自己消滅する形にしないと安定した動作は期待できません。

waitメソッドはそれを呼び出したスレッドが停止するものなので、停止したいスレッドが自発的に呼び出します。
スレッドを安全に安定して動作するようにできないとwaitメソッドを使うこともできないというわけです。

奥兵

Re: Android(java) threadが非常に低速になる

#3

投稿記事 by 奥兵 » 11年前

threadを外部から積極的に操作するような方向性でthreadやそのまわりを組んでいました。
自己完結というか、ご指摘いただいたあたりを意識して組みなおしてみます。

奥兵

Re: Android(java) threadが非常に低速になる

#4

投稿記事 by 奥兵 » 11年前

thread継承クラスの設計を変更してwait()でのポーズとnotifyAll()でのポーズの解除はできました。↓のサイトを参考にしました
( http://blog.makotoishida.com/2012/01/an ... ander.html )
しかし、ゲームオーバーやバックキー等で一度ゲームを終了した後に、もう一度ゲームを起動するとthreadの担当するゲームのメインとなる部分が、やはり超低速になってしまいます。
複数のthreadが走ってるのではないかと思い、run()のループ内でthreadが開始された時刻をログ出力して調べてみましたが、最初に走らせたthreadと同じものが動いていました。
ゲームのメインのActivity内ではwait()と再開はおよそ正常に動いてくれるのですが、一度Activityを離れたからか、もしくは別の部分で問題があったのか次の起動では正常に動作しません。いろいろ実験してみましたが解決の糸口が見えません。どうか、アドバイス等お願いします。

ISLe()

Re: Android(java) threadが非常に低速になる

#5

投稿記事 by ISLe() » 11年前

GameMgr#getInstanceが呼び出されるたびに繰り返しタスクが追加されるように見えますがその辺りは問題ないのでしょうか。
タスクに渡しているコンテキストによるリークの可能性も気になります。

スレッドは適度に終了するようにしたほうが良いと思います。
Activityの再作成で、新Activity作成→旧Activity破棄となるとき、staticなメンバからはライフサイクルイベントの発生順が滅茶苦茶に見えます(が正常です)。
同じスレッドがずっと動いているというほうが危険性が高いのではないかと思います。

奥兵

Re: Android(java) threadが非常に低速になる

#6

投稿記事 by 奥兵 » 11年前

アドバイスありがとうございます。それと返事が遅くて失礼しました。

ゲーム中のポーズは設計を修正して、threadのwatとnotifyallを使い実装しました。
GameMgrのメソッドもリファクタリングしてご指摘していただいた部分も修正しました。ありがとうございます。
threadはゲームのMainのActivityがonPauseになった瞬間に終了させるようにしました。

ポーズ・thread周りで目指す動作として残るできていない部分は、電話がかかってきたり、ホームボタンが押された際などに
ゲームを中断し、再びActivityが全面に来た際に続きからプレイできるようにするところです。

MainActivityのonPauseでGameMgrクラスが持っているLinkedList(threadのループ内で呼ばれるゲーム本体のインスタンス)を貰ってきて保存しておき、onResumeでThreadを作り、LinkedListを戻してthreadを開始する方法で実装しようとしています。
現在その方法でゲームをホームキーで中断し、再び始めると画面が真っ白になってしまいます。
OpenGL関係の何らかの部分でミスをしていると思うのですが見当がつきません。ちなみにゲーム自体は動いていてほかっておくと、その状態からゲームオーバーでActivityの遷移をします

ISLe()

Re: Android(java) threadが非常に低速になる

#7

投稿記事 by ISLe() » 11年前

奥兵 さんが書きました:MainActivityのonPauseでGameMgrクラスが持っているLinkedList(threadのループ内で呼ばれるゲーム本体のインスタンス)を貰ってきて保存しておき、onResumeでThreadを作り、LinkedListを戻してthreadを開始する方法で実装しようとしています。
現在その方法でゲームをホームキーで中断し、再び始めると画面が真っ白になってしまいます。
OpenGL関係の何らかの部分でミスをしていると思うのですが見当がつきません。ちなみにゲーム自体は動いていてほかっておくと、その状態からゲームオーバーでActivityの遷移をします
先の投稿にちょろっと書いたのですが、タスクが古いコンテキストを握っているのが良くないのではないかと思います。
古いコンテキストで作成したテクスチャなどが無効になってはいませんか。
サーフェスが再構築されたあとにコンテキストに対して再度初期設定を行っていますか?
コンテキストに依存したオブジェクトを再作成していますか?
その辺りをチェックしてみてください。

奥兵

Re: Android(java) threadが非常に低速になる

#8

投稿記事 by 奥兵 » 11年前

とても時間を空けてしまいまして失礼しました。
いろいろ試していたのですがどうもうまくいかず、とても時間がかかってしまいました。
調べているとこのページを見つけました。 http://blog.livedoor.jp/sen_ritsu/archi ... 44432.html 
ActivityのonPauseでテクスチャは自動で解放されてしまうようです。
再読み込みすればいいと思うのですが、設計もグチャグチャとしてきて思うようにいかず、リファクタリングを繰り返していました。
解決できてから報告しとうと思っていたのですが、私の実力全く埒があかずとても時間がかかってしまいました。

ポーズと再開はおかげさまで実装できたのと、これ以上は原因も判明しており今回の質問の本題からそれてしまう為、この質問は解決とさせて頂きます。
お力添えありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る