[android] openGLのRendererについて

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

[android] openGLのRendererについて

#1

投稿記事 by LL » 5年前

現在インプレスジャパンの[OpenGLで作るAndroidSDKゲームプログラミング]と言う本を参考にしてopenGLESゲームの構築をしているのですが。
この本では214ページでRendererは本来グラフィックの描画が役割なので各種演算はゲームスレッド(マルチスレッド)を導入して分けると言うことが書かれていました。
実際のソースコードもスレッド側では計算しかしていません。(プログラム自体は起動時に直接ゲーム画面になる為、シーンという概念が無い)

コード:

GameThread
    |
    ----オブジェクトの演算
Renderer
    |
    ----onDrawFrame
                    |
                    ----GameThreadから演算情報を取得して描画
ですが今回自分が作りたいプログラムは下記の構成になっているのでテキストどおりには作れません。

コード:

・ゲーム全体の構成(シーン。これらは全て一つのアクティビティに収める)
        |
        |----TITLE
        |----MENU
        |----GAME_MAIN
        |----RESULT
        |----END
//シーンは目的に応じて追加できるようにする(GAME_MAINをSTAGE0,STAGE1と言う風に変更したり追加したり)

コード:

・シーン一つの構成
        |
        |----INIT   (オブジェクト等のリソースの読み込み、座標などの初期化)
        |----UPDATE(ゲームループ処理)
        |----END(シーン中に使用したリソースの破棄等)
これらの構成の為に現在の状態を管理できるグローバル変数を追加

コード:

public class Global {
	//二つの定数を組み合わせてゲームの基本ループは処理される(例:TITLEシーンのINIT等)
	//シーン(タイトル画面か?それともゲーム画面か等)
	public enum Scene{
		TITLE,
		GAME_MODE1,
		GAME_MODE2,
		RESULT,
		ENDING
	};
	//シーンステータス(初期化か、更新か、破棄か)
	public enum Status{
		INIT,
		UPDATE,
		END
	}
	
	//現在のシーン
	public static Scene scene;
	//シーンの状態
	public static Status status;
	
	//アクティビティのコンテキストを保持する変数
	public static Context context;
	
	//GLコンテキストを保持する変数
	public static GL10 gl;
	
	//MainActivity(GameActivity)を保持させる(Activity内のメソッドを呼び出すため)
	public static MainActivity mainActivity;
	
	//乱数を生成する
	public static Random rand = new Random(System.currentTimeMillis());
}
そしてAndroidの館で紹介されているTaskとGameMgrを参考に同機能の物を作りました
Task.java

コード:

public interface Task {
	//GLコンテキストが関わってくるリソースなどの読み込み(RendererのSurfaceChanged等で使用)
	public boolean load();
	//初期化時の処理(数値の初期化のみ)
	public boolean init();	
	//更新時の処理
	public boolean update();
	//描画時の処理
	public void draw(GL10 gl);
	//終了時の処理(オブジェクトやリソースの解放など)
	public boolean end();
}
上記をimplementsした各種シーンクラスを作る。
TitleSceneClass
MenuSceneClass
GameSceneClass
etc...
これらを使って組んでいきたいのですが、
参考書に記述されているプログラムではGLコンテキストを取得できるタイミングがRendererクラスのonSurfaceChangedな為に、別クラスのスレッドでSceneを利用した場合、loadメソッドが使用出来ませんでした。
(参考書では独立したGameThreadクラスを作りGLコンテキスト等に一切関与していない。またスレッドはMainActivityのonCreateで直接newしてstartしている)
参考書のプログラムを見せた方が伝えやすいのですが著作権とかでややこしいのであげられません・・・(ただ配布はされているので本のタイトルで少し調べれば出てくると思いますが)
シーン毎にリソースの読み込みなどを行いたいのでAndroidの館の
http://dixq.net/Android/s02_01.html
ページを参考にRendererにRunnnableをimplementsし、Renderer内に直接スレッドのrunメソッドを作ると言うことをしました。

コード:

package jp.denpa.glgametest;

//以下importに注意(似たものがあるため)
import jp.denpa.data.Global;
import jp.denpa.data.GraphicUtil;
import jp.denpa.data.Color;

import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.egl.EGLConfig;

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

public class MyRenderer implements GLSurfaceView.Renderer, Runnable{	
	//コンテキスト
	private Context mContext;
	//ゲームスレッド(マルチスレッド)
	private Thread mThread;
	
	//ゲームマネージャー
	private GameMgr mGameMgr;
	
	//画面サイズの保持
	private int mScreen_Width;
	private int mScreen_Height;
	
	//タッチ時の座標をGL座標系で保持
	private float mGL_Touch_x;
	private float mGL_Touch_y;
	
	//コンストラクタ
	public MyRenderer(Context context){
		this.mContext = context;
//		this.mGameThread = gameThread;
		mThread = new Thread(this);
		mGameMgr = new GameMgr();
	}
	
	//描画を行う処理
	public void renderMain(GL10 gl){
		//描画処理
		mGameMgr.draw(gl);
	}
	
	//描画処理の記述
	@Override
	public void onDrawFrame(GL10 gl){
		//描画の初期化----------------------------------------------------------
		//透過処理、加算合成の設定(ポリゴン毎に設定するのが好ましいか?)
		//gl.glEnable(GL10.GL_BLEND);
		//gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);

		//OpenGLの座標系などの設定
		//"描画可能"領域の指定
		gl.glViewport(0, 0, mScreen_Width, mScreen_Height);		//glViewport(オフセットX, オフセットY, 幅, 高さ);
		gl.glMatrixMode(GL10.GL_PROJECTION);	
		gl.glLoadIdentity();
		//OpenGLの座標系を指定している(この場合は左端[-1.0]右端[1.0]下端[-1.5]上端[1.5]手前(Z)[0.5f]奥(Z)[-0.5])
		gl.glOrthof(-1.0f, 1.0f, -1.5f, 1.5f, 0.5f, -0.5f);			//このメソッドはgmMatrixMode、glLoadIdentityメソッドの二つが呼ばれた直後に呼び出すこと
		gl.glMatrixMode(GL10.GL_MODELVIEW);
		gl.glLoadIdentity();
		
		//画面のクリア(背景の設定)
		gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);		//背景色の設定
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
		//ここまで--------------------------------------------------------------
		
		//メイン描画部分
		renderMain(gl);

		
	}
	
	//画面生成時、画面向き変更時に呼び出される。初期化処理などを行う
	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height){
		this.mScreen_Width = width;
		this.mScreen_Height = height;
		
		//GLコンテキスト取得(グローバル化)
		Global.gl = gl;
		
		//リソースの読み込み
		mGameMgr.load();
		//情報の初期化
		mGameMgr.init();
		//シーンステータスを更新にする
		Global.status = Global.Status.UPDATE;
		
		//スレッドのスタート
		mThread.start();
	}
	
	//画面生成時、画面向き変更時に呼び出される。初期化処理などを行う
	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		
	}
	
	//画面がタッチされたときに呼ばれるメソッド
	public void touched(float x, float y){
		Log.i(getClass().toString(), String.format("touched! x= %f y= %f", x, y));
		mGL_Touch_x = x;
		mGL_Touch_y = y;
	}

	//マルチスレッドでゲームループを作る(リソースの読み込み、初期化、更新のみで描画はRendererのonDrawFrameに任せる)
	@Override
	public void run() {
		while(mThread != null){
			//シーンステータス毎の処理
			switch(Global.status){
				case INIT:
					//リソースの読み込み
					mGameMgr.load();
					mGameMgr.init();
					//シーンステータスを更新にする
					Global.status = Global.Status.UPDATE;
					break;
				case UPDATE:
					//座標などの数値情報の更新
					mGameMgr.update();				
					break;
				case END:
					//リソースの破棄処理
					mGameMgr.destroy();
					//シーンステータスを初期化にする(次のシーンへ移行)
					Global.status = Global.Status.INIT;
					break;
				default:
					break;
			}
		}
	}
}
Rendererクラスはこれ以上触る必要が無い状態になっています(GameMgrの中身と各種シーンクラスを書き換えればいいようにする)

長々と書きましたがようやく本題です。
・RendererクラスにRunnableし、中にrunメソッドを書くという行為は問題ないか?
・現在RendererにGameMgrを持たせその中でSceneごとの処理をし、さらにそのSceneの中で敵やプレイヤーなどのオブジェクトを持たせる(敵は最終的にコンテナで管理)しようとしているが、この様な深い階層の中で、さらに階層毎にコンテナを持たせる行為(GameMgrの中でScene等のTaskを管理するコンテナ、さらにSceneの中で敵を管理するコンテナ)を想定しているがこの様な根深く、コンテナなどを多用するような構造は深刻な問題が無いだろうか?
・上記以外でも現在の構築は何かしら危険を孕んでいないだろうか?
をお伺いしたいと思います。

非常に下手な説明、伝わりにくい内容で申し訳ございませんがよろしくお願いします。
(GlobalでGLコンテキストを取得しているのに一部のメソッドの引数[Task等]でGLコンテキストを取得しているのはまだ実験段階だからです。これらは修正予定に入っています)

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: [android] openGLのRendererについて

#2

投稿記事 by softya(ソフト屋) » 5年前

AndroidでJavaでゲーム組んでいないので適当すぎるかもしれませんが、やって見たらどうでしょうか?
※ Cocos2d-xでC++でAnroidのゲームを作っているため。
経験でしか学べないことも多いです。試行錯誤が自身を育てると言っても過言ではないので、やってみてから考えたほうが良いかと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
へにっくす
記事: 628
登録日時: 7年前
住所: 東京都

Re: [android] openGLのRendererについて

#3

投稿記事 by へにっくす » 5年前

LL さんが書きました:長々と書きましたがようやく本題です。
ほんとに長いっすね 笑
しかしぶっちゃけ言わせてもらえば、自分が管理できるならそれでいいと思いますよ。
一般的な解なんてあるわけありませんし、
ゲームのジャンルによってこう構築するべきだなんて、それこそ机上の空論でしかないと思います。
オフトピック
なんでもかんでも教科書通りに作ってたら、世の中こんなにゲーム出尽くしてないのでは?
まあ自分の作るゲームで、ああっ、だんだん何だか複雑な構造になってしまったっ
と不安になることはよくあることですね。
なので、いったん、クラス図でも書いて、整理できるか確認してみては?

私自身、Androidの開発は毛ほどしかやっていないのでこのやり方は危険!というのは知りません。
ただ、個人的にはRendererとRunnableをいっしょに派生してるのはよくない気がしますが・・・
とりあえず以下が参考になるかな?
http://www.techdoctranslator.com/resour ... urfaceview

追記:staticはちと気をつけた方がいいよ。
Androidでstatic変数が勝手にクリアされる問題と対処法 - (旧) 猫好きモバイルアプリケーション開発者記録
written by へにっくす

LL

Re: [android] openGLのRendererについて

#4

投稿記事 by LL » 5年前

お返事遅くなってしまいました。
一応イメージしている形に組んでみて、動くところまでこぎつけました。が、これが正しいかは未だに自信はありません・・・(一応正常に動いており、特に変な問題は起きていません。)
確かにこれらの不安は卓上の理論なのかもしれませんが個人で作っている以上、どうしても自分の作っている物が正しいのかが分からずこんな形で質問することになってしまいました。
(ここ以外で相談できる相手もいませんし・・・)
確かにへにっくすさんのおっしゃるとおり、なんでもかんでも教科書どおりと言うわけにはいかないのですが元々マニュアル小僧の節があるために中々参考元を崩すのが不安な一面あり、今一歩前に進めませんでした・・・

もう少しソースに手を加えてからここに途中経過の作成物をあげようかと思いますので解決はもう少しお待ちください。

ISLe()

Re: [android] openGLのRendererについて

#5

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

クラス(オブジェクト)の相関図を書いて、上位から下位に向かって三角形を成すようにする、というのが、基本に忠実で教科書どおりと言えるのではないでしょうかね。

自然とAndroidのライフサイクルに対応することにもなるでしょうし。

いまのコードはそうなってないと思います。

閉鎖

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