...

ドキュメントはここです(PDF 410 Kbytes)

by user

on
Category: Documents
13

views

Report

Comments

Transcript

ドキュメントはここです(PDF 410 Kbytes)
VC.NET 上でスクラッチから
BREW+Open GL/ES アプレットを作る
西森丈俊
平成 16 年 10 月 15 日
目次
第 1 章 はじめに
3
第 2 章 SDK インストール
6
6
6
2.1
2.2
SDK のインストール . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
OpenGL/ES SDK のインストール . . . . . . . . . . . . . . . . . . . . . . . . .
第 3 章 VC.NET 上にプロジェクトを作る
3.1
3.2
3.3
3.4
VC.NET のウィザードで空プロジェクトを作る . .
プロジェクトへ sdk/src/AEE. . . .c ファイルの追加 .
メインのソースファイル作成 . . . . . . . . . . . . .
プロジェクトのコンフィギュレーション . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7
7
8
8
9
第 4 章 MIF エディタで MIF ファイルの作成
4.1
4.2
4.3
11
起動 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
.bid ファイルの作成 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
.mif ファイルの記録 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
第 5 章 リソースエディタでリソースファイルの作成
5.1
5.2
5.3
5.4
5.5
起動 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
文字列リソースの追加 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
ビットマップリソースの追加 . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
リソースファイルの記録 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
リソースのコンパイル . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
第 6 章 プロジェクトのビルド
6.1
6.2
12
12
12
13
13
13
14
ファイルの確認 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
ビルド . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
第 7 章 ソースの解説
7.1
7.2
15
アプレットの起動とアプレット構造体 . . . . . . . . . . . . . . . . . . . . . . . . 15
イベントハンドラ関数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
7.3
プログラミング上の制約 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
第 8 章 Hello World!の表示
8.1
21
ソースの修正 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
8.2
ビルドと実行 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
第 9 章 リソース中のビットマップの表示
28
9.1
ソースの修正 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
9.2
ビルドと実行 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
1
第 10 章 ゲームアプリケーションフレームワーク
10.1 簡便用の共通ヘッダの作成 .
10.2 Game クラスの追加 . . . . .
10.2.1 game.h の追加 . . .
10.2.2 game.cpp の追加 . .
10.3 test1.cpp の修正 . . . . . .
10.4 ビルドと実行 . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
30
30
30
30
32
33
36
.
.
.
.
.
.
.
.
.
37
37
37
38
38
39
45
45
45
47
第 11 章 Open GL/ES
11.1 sdk/src/GL.c を追加 . . . .
11.2 common.h ヘッダの修正 . . .
11.3 Renderer クラスの追加 . . .
11.3.1 renderer.h の追加 . .
11.3.2 renderer.cpp の追加
11.4 Game クラスの修正 . . . . . .
11.4.1 game.h の修正 . . . .
11.4.2 game.cpp の修正 . . .
11.5 ビルドと実行 . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
第 12 章 デバッガを使う
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
48
12.1 準備 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
12.2 ブレークポイントの設定と実行 . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
2
第 1 章 はじめに
BREW 用のプログラムを作っているときに、フリーのドキュメントをあさっていて次のことに
気がつきました。
• BREW アプリ開発を初めて行う場合のガイドにできる文書は英語ばかり。
• BREW Open GL/ES に関して同様のガイドとなる日本語の文書はない。
• 文書は1つになっていない。
そこで、これらをある程度まとめて日本語の入門ガイドになるようなフリーの文書を作ろう、と
考えてこの文書を書き始めました。
対象読者とする読者は次のような人を想定しています。
• C/C++プログラミングはそれなりにわかる
• VC.NET を使ったことがない
• 携帯プログラミングもしたことがない
• OpenGL は少し知っている
• 英語読むのが嫌い
読みながら、スクラッチから Open GL/ES の相当簡単なゲーム (っぽい) アプレットを、シミュ
レーターレベルで作成できればいいな、と考えています。
実は、http://brew.qualcomm.com/brew/において、Visual Studio 6.0 や.NET 用にプロジェ
クトウィザード等の機能を追加する Add-In が提供されています。これを使うと、本ドキュメン
ト中のいくつかの作業については、楽をできますが、
• チョー楽になる、というほどでもない。
• ウィザードの出力するソースは詳しいコメントがつくが英語。
• ウィザードを使うと詳しいプロジェクトの構成が理解しづらいかも。
という理由で使っていません (本当は、このドキュメントを書いているときは存在を知らなかっ
ただけ)。
対象 VC.NET
本ドキュメントの作業やプログラムは、以下の VC.NET にて確認をしています。VC.NET は
インストール済みとします。
• Microsoft Development Environment 2002 Version 7.0.9486
• Microsoft .NET Framework 1.0 Version 1.0.3705
3
参考文献
参考にした文献は次のものです。これらの文献は本文中で、[4] のように表記しています。文
献の出自については巻末にも番号順で列記しています。
• OpenGL ES 1.1 Specification[2]
• OpenGL ES Native Platform Graphics Interface 1.1 Specification[1]
どちらも KHRONOS から提供される OpenGL/ES の仕様書。しかし英語。
• BREW API リファレンス [4]
BREW SDK 添付のリファレンスマニュアル。ヘルプ形式。日本語で提供されている。
• BREW プログラミングの概念 [5]
主に、BREW におけるプログラミングや開発の作法について書かれた文書。日本語だが、
あまり読みやすくはない。ヘルプ形式。最初に通読するような文書ではなく、開発中の疑
問を解消するために読むようなものかも。BREW SDK に添付されている。
• Creating a BREW Application from Scratch[6]
スクラッチから BREW アプレットを作るためのガイド。とてもわかりやすい。ただし英
語。qualcomm のサイトから提供されている。
• Creating BREW Applications Using Visual Studio .NET[7]
.NET を使って BREW アプレットプロジェクトを作る方法についての文書。Add-In によ
るウィザードがあるのであまり存在意義がないがわかりやすい。やはり英語。qualcomm
のサイトから提供されている。
• Starting with BREW[9]
BREW アプレット開発時のおおまかなガイド。図が多くわかりやすい。もちろん英語。
qualcomm のサイトから提供されている。
• An Introduction to BREW and OpenGL ES[3]
BREW Open GL/ES の開発をはじめるときにガイドとして利用できる文献。ソースが多
く示されていてわかりやすい。やっぱり英語。本文書の 10 章や 11 章では、この文献の
ソースを流用している。gamedev.net で提供されている。
• OpenGL ES Extension API Reference[8]
2 章の OpenGL ES SDK をインストールするときにダウンロードするアーカイブに含ま
れている、BREW OpenGL/ES Extension のリファレンスマニュアル。ヘルプ形式。当
然英語。しかも、
See Khronos documentation for details
(詳しくは KHRONOS のドキュメント (Open GL/ES 仕様書) を読んでね)
が多すぎる。
4
プログラムについて
10 章と 11 章のプログラムは、An Introduction to BREW and OpenGL ES[3] にあるものを
修正して使用しています。プログラムの原作者はこの文献の著者である Alan Kemp 氏です。
著作権について
この文書の著作権者は西森丈俊 ([email protected]) です。この文書はフリーです。よって、自由
に複製、再配布、改変できますが、その場合は原著作権者名と元の文書の名前を明記してくだ
さい。
そしてもし改変した場合は、できれば私に教えてください…。
商標表示について
商標表示は省略しています。
謝辞
本文書の記述にあたり、株式会社1 のカサイヒデユキ氏に多大なご協力をいただきました。
また、Alan Kemp 氏にはプログラムの使用を快く許諾していただきました。
1 http://www.ibrains.co.jp/
5
第 2 章 SDK インストール
開発に先立ち、BREW SDK 3.0 と、BREW SDK Extension for OpenGL/ES のインストール
をする1 。
2.1
SDK のインストール
1. http://brew.qualcomm.com/brew/へいく。Explorer5.5 以上。
2. download the SDK から BREW SDK 3.0 を選ぶ。ActiveX で自動的にダウンロードとイ
ンストールが始まる。インストールは日本語版 (になってくれるはず)。
3. 以下、インストールディレクトリは c:/Program files/BREW.../ とする。
2.2
OpenGL/ES SDK のインストール
1. https://brewx.qualcomm.com/brew/sdk/download.jsp?page=dx/devmisc へ行く。
2. BREW SDK Extension for OpenGL ES をダウンロード (なぜかリンクの表記は install
になっていますが. . . )
3. ダウンロードしたアーカイブをどこかに展開。
4. アーカイブ中のものを次のようにコピー。
• inc/の下のすべてを c:/Program files/BREW.../sdk/inc/の下へ。
• src/の下のすべてを c:/Program files/BREW.../sdk/src/の下へ。
• BREW 3.X/の下の dll ファイルを c:/Program files/BREW.../sdk/bin/modules/
の下へ。
• devices/の下のすべてを c:/Program files/BREW.../sdk/devices/の下へ。
1 Open GL/ES は別に 2.1 でも動作する (らしい) が試してない…。SDK 3.0 + OpenGL/ES という実機端末は
まだこの世にないらしい…。
6
第 3 章 VC.NET 上にプロジェクトを作る
この章では VC.NET 上で BREW アプレット開発用の最も簡単なプロジェクト1 を作成する。
この章で行う設定は以下の表のとおりである。VC.NET 経験者はこの表のとおりにプロジェ
クトを生成、設定すればよい。
プロジェクト
プロジェクト名
テンプレート
プロジェクトの位置
アプリケーション種別
追加オプション
追加ソースファイル
新規ソースファイル
C/C++コンパイラ
追加インクルードディレクトリ
64 ビット移植への対応
追加プロセッサ定義
プリコンパイル済みヘッダ
コンパイル言語
リンカ
出力ファイル
3.1
test1
Win32 Project
c:/
DLL
空のプロジェクト
c:/Program Files/BREW.../sdk/src/AEEAppGen.c
c:/Program Files/BREW.../sdk/src/AEEModGen.c
test1.cpp
ソースの内容は 8 ページのもの。
c:/Program Files/BREW. . . /inc
いいえ (しない)
AEE SIMULATOR
使用しない
既定値
test1.dll($(OutDir)/は除く)
VC.NET のウィザードで空プロジェクトを作る
まず、VC.NET のアプリケーションウィザードで空のプロジェクトを作る。このとき、BREW
用にいくつかの設定も行う。
1. ファイル >新規作成 >プロジェクトでアプリケーションウィザードを起動。
2. 左のペインから Visual C++ Projects を選ぶ。
3. 右のペインから Win32 Project を選ぶ。
4. プロジェクト名やフォルダの場所は適当に。以下プロジェクト名は test1、ディレクトリ
は c:/test1 とする。
5. 「OK」する。
6. 次のダイアログでアプリケーションの設定を左側から選ぶ。
7. アプリケーションの種類は dll をチェック。
8. 追加のオプションで空のプロジェクトをチェック。
9. 「完了」ボタンを押す。
以上で、BREW 用の空のプロジェクトが作られる。次に、このプロジェクトへソースファイ
ルの追加を行う。
1 VC.NET
ではソリューションというけど気持ち悪い. . .
7
3.2
プロジェクトへ sdk/src/AEE. . . .c ファイルの追加
BREW アプリケーション開発上便利な、いくつかのユーティリティ関数が定義されたソース
ファイルを、SDK のディレクトリから直接プロジェクトへインポートする2 。11.1 節での GL.c
ファイルを追加も同様に行う。
1. ソリューションエクスプローラから作成したプロジェクト (test1) を選択。
2. プロジェクト内の「ソースファイル」を右クリックしてメニューを出す。
3. 追加 >既存項目の追加を選ぶ。
4. ファイル選択ダイアログが出るので、以下のファイルを選んで追加。
• c:/Program files/BREW.../sdk/src/AEEAppGen.c
• c:/Program files/BREW.../sdk/src/AEEModGen.c
3.3
メインのソースファイル作成
アプレットプログラムのメインとなるソースファイルをプロジェクトに追加する。まず、空
のソースファイルを新しくプロジェクトに追加する。
1. 前述の AEE....c ファイルの追加と同じだが、追加 >新しい項目の追加をする。
2. ダイアログの右側のペインで C++ファイルを選び、test1.cpp というファイルを作る
(test1 はプロジェクト名)。
次に、追加した test1.cpp をソリューションエクスプローラ内でダブルクリックしてエディ
タを開き、エディタ上で以下のソースを記述する。記述後は CTRL+S、もしくはエディタの上に
あるタブを右クリックしてメニュー内の test1.cpp の保存を選択する。ソースについては 7 章
で詳しく説明する。
#include
#include
#include
#include
"AEEModGen.h"
"AEEAppGen.h"
"test1.bid" // MIF エディタが生成
"test1.brh" // リソースエディタが生成
static boolean
test1_HandleEvent(IApplet* pi, AEEEvent eCode, uint16 wParam, uint32 dwParam);
int
AEEClsCreateInstance(AEECLSID ClsId, IShell* pIShell, IModule* po, void** ppObj) {
*ppObj = NULL;
if (ClsId == AEECLSID_TEST1) {
if (AEEApplet_New(
sizeof(AEEApplet),
ClsId,
pIShell,
po,
(IApplet**)ppObj,
(AEEHANDLER)test1_HandleEvent,
NULL) == TRUE) {
return AEE_SUCCESS;
}
}
2 これらのソースファイルに定義されている関数を使用する場合、同名のヘッダもインクルードする必要がある。ヘッ
ダは sdk/inc/にあり、後のプロジェクトコンフィギュレーションでインクルードディレクトリとして設定する。
8
return EFAILED;
}
static boolean
test1_HandleEvent(IApplet* pi, AEEEvent eCode, uint16 wParam, uint32 dwParam) {
switch (eCode) {
case EVT_APP_START:
return TRUE;
case EVT_APP_STOP:
return TRUE;
default:
break;
}
return FALSE;
}
3.4
プロジェクトのコンフィギュレーション
BREW アプレット用のビルドをさせるために、プロジェクトのコンフィギュレーションをい
くつか変更する。
• プロジェクトエクスプローラから test1 プロジェクトを選択して右クリック。
• メニュー内からプロパティを選択。プロパティダイアログが出る。
• 以下の作業をすべて完了したら、OK ボタンを押す。
構成プロパティ >C/C++ >全般
この項目ではコンパイルの設定を行う。
• 左側のペインの中の構成プロパティの下に C/C++ >全般という項目があるので、そ
れを選択。
• 右側のペインに設定項目がリストされるので、次のように設定。
– 追加のインクルードディレクトリ
1. この項目を左クリックする。右端に. . . というボタンが出る。
2. このボタンを押すと追加のインクルードディレクトリダイアログがでる。
3. このダイアログの右上のフォルダアイコンのボタンを押す。1項目文リスト内
に追加されれ。
4. 追加された項目の右端に”. . . ”というボタンが出る。
5. このボタンを押すと、ディレクトリの置き換えというファイル選択ダイアログ
が出る。
6. C:/Program Files/BREW.../inc を選択して、開くボタンを押す。
– 64 ビット移植への対応
1. この項目を左クリックする。右端にプルダウンメニューボタンが出る。
2. ボタンを押すとプルダウンメニューが開くのでいいえを選ぶ。
9
構成プロパティ >C/C++ >プリプロセッサ
この項目では、シミュレーター用のコンパイルであることを設定する。
• 構成プロパティの下に C/C++ >プリプロセッサという項目があるので選択。
• 右側のペインに設定項目がリストされるので、次のように設定。
– プロセッサの定義
1. この項目を左クリックする。右端に. . . というボタンが出る。
2. このボタンを押すとプロセッサの定義ダイアログがでる。
3. 上側のテキストボックスの中に、新しく AEE SIMULATOR という一行を追
加する。
構成プロパティ >C/C++ >プリコンパイル済みヘッダー
この項目では、ヘッダのプリコンパイル処理機能を設定する。
• 構成プロパティの下に C/C++ >プリコンパイル済みヘッダーという項目があるので
選択。
• 右側のペインに設定項目がリストされるので、次のように設定。
– プリコンパイル済みヘッダーの作成/使用
1. この項目を左クリックする。右端にプルダウンメニューボタンが出る。
2. ボタンを押すとプルダウンメニューが開くので、プリコンパイル済みヘッダー
を使用しないを選ぶ。
構成プロパティ >C/C++ >詳細
この項目では、コンパイル言語 (C か、C++か) を選択する。
• 構成プロパティの下に C/C++ >詳細という項目があるので選択。
• 右側のペインに設定項目がリストされるので、次のように設定。
– コンパイル言語の選択
1. この項目を左クリックする。右端にプルダウンメニューボタンが出る。
2. ボタンを押すとプルダウンメニューが開くので、既定値を選ぶ。
構成プロパティ >リンカ >全般
この項目ではリンカの出力ファイルの設定を行う。
• 構成プロパティの下にリンカ >全般という項目があるので選択。
• 右側のペインに設定項目がリストされるので、次のように設定。
– 出力ファイル
1. この項目を左クリックする。右側に$(OutDir)/test1.dll となっている。
2. この行を直接クリックして編集。test1.dll にする。
10
第 4 章 MIF エディタで MIF ファイルの
作成
MIF ファイルは、BREW アプレット固有の情報をファイルにしたものである。端末 (シミュレー
ター含む) は、この MIF ファイルによってアプレットの区別を行っている。
MIF ファイルはアプレット情報を持つが、リソース (文字列や画像等) は含まない。よって、
開発中に頻繁に更新することはない。
4.1
起動
MIF ファイルはスタートメニューから、プログラム >BREW SDK 3.??? 日本語版 >MIF
エディタと選んで起動する。
4.2
.bid ファイルの作成
起動時アプレットというタブウィンドウが開いている。
1. 左下の新規ボタンを押す。BREW ClassID の指定というダイアログが出る。
2. 新規作成という枠内に、ローカルというチェックがあるのでチェック。すると、ダイアロ
グの一番上の ClassID というエントリが入力可能になる。
3. このエントリに 10101010 と入力する1 。
4. その横のクラス名エントリにはプロジェクト名 (test1) を入力する。
5. OK ボタンを押す。BREW ClassID をローカルに作成してよろしいですか?と聞いて
くる。
6. はいボタンを押す。すると.bid ファイルを作る場所を選ぶファイルダイアログが出る。
7. プロジェクトのディレクトリ内に.bid ファイルを作成する。今回の例の場合、c:/test1/test1.bid
を指定する。
4.3
.mif ファイルの記録
1. メニューのファイル >保存を選ぶ。
2. ダイアログの下のファイルの種類のところで BREW MIF v 2 ファイル (*.mif ) を
選ぶ。
3. ファイルダイアログが出るので、プロジェクトディレクトリと同じ場所にプロジェクト
名.mif として保存する。今回の場合 c:/test1.mif とする。
1 この ID は、端末がアプレットを区別するために利用する。後述するシミュレーターで、複数のアプレットを起動
する場合、アプレットそれぞれにことなる ID を割り振るようにする。
11
第 5 章 リソースエディタでリソースファイ
ルの作成
リソースエディタはアプレット用の文字列や画像などを含むリソースファイルの生成をするた
めのツールである。図 5.1 のように、リソースエディタは文字列や画像等のデータをまとめて、
1 つのリソースファイルと C/C++用のインクルードファイルを出力する。プログラムはインク
ルードファイルで定義される ID を利用して、リソースファイル中のデータの読み込みを行う。
図 5.1: リソースエディタの仕事
5.1
起動
リソースエディタはスタートメニューから、プログラム >BREW SDK 3.. . . 日本語版>
リソースエディタとメニューから選んで起動する。
5.2
文字列リソースの追加
1. リソース >新規文字列を選ぶ。自動で新しい文字列リソースがひとつ追加される。
2. 右側ペインに追加された文字列リソースの内容が表示される。
3. 右側ペインの中に属性 >データという項目がある。この項目が文字列データ本体なので
Hello World!とする。
4. 右側ペインの一番下の適用ボタンを押す。すると、真ん中のペインが更新される。
12
5. ここで名前の項目は、リソースをプログラム中で使うときの定数マクロ名 (リソース ID)
となるので確認しておく。
5.3
ビットマップリソースの追加
1. リソース >新規オブジェクトを選ぶ。自動で新しいオブジェクトリソースがひとつ追加
される。
2. 右側ペインに追加されたオブジェクトリソースの内容が表示される。
3. 右側ペインの中に属性 >データという項目がある。この項目を選択する。右端に. . . とい
うボタンが出る。
4. この. . . ボタンを押すと、ファイル選択ダイアログが出るので適当にビットマップファイ
ルを選ぶ。
5. 右側ペインの下側のオブジェクトのプレビューにビットマップのプレビューが出る。
6. 右側ペインの一番下の適用ボタンを押す。すると、真ん中のペインが更新される。
7. 文字列のときと同様に、名前の項目を確認しておく。
5.4
リソースファイルの記録
1. メニューのファイル >名前をつけて保存を選ぶ。ファイル選択ダイアログがでる。
2. プロジェクトディレクトリ (c:/test1) の下に test1 と名前をつけて保存。拡張子は.brx。
c:/test1/test1.brx となる。
5.5
リソースのコンパイル
1. メニューの作成 >リソーススクリプトのコンパイルを選ぶ。
2. 作成に成功した旨のダイアログがでる。コンパイルで作成されるファイルは、
• c:/test1/test1.bar
• c:/test1/test1.brh
の2つ。
13
第 6 章 プロジェクトのビルド
6.1
ファイルの確認
ここまでの作業で、ファイルは以下のものができている。
• c:/test1.mif
• c:/test1/test1.bid
• c:/test1/test1.cpp
• c:/test1/test1.bar
• c:/test1/test1.brh
• c:/test1/test1.brx
• c:/test1/test1.mfx
• c:/test1/その他 VC.NET の作成するファイル
6.2
ビルド
1. VC.NET のメニューからビルド >ソリューションのビルドを選択する。
2. ビルドが成功すれば、c:/test1/test1.dll という dll ファイルができる。
14
第 7 章 ソースの解説
この章では、3 章の 3.3 節で挙げたソースファイルについて説明する。この章で出てくる BREW
API 関数は AEEClsCreateInstance 関数と AEEApplet New 関数以外すべてが API リファレン
ス [4] に記載されている。
7.1
アプレットの起動とアプレット構造体
BREW システムは次のようにアプレットを起動する。
1. アプレットプログラムをロード
2. アプレットプログラム中の AEEClsCreateInstance 関数を呼び出す
よってアプレットプログラムは起動時のエントリポイントとして AEEClsCreateInstance 関数
を定義しなければならない1 。
また、BREW システムは AEEApplet 構造体を用いて動作中のアプレットプログラムを管理
するが、この構造体の生成と初期化はエントリポイントである AEEClsCreateInstance 関数の
責任となっている (図 7.1)。
AEEClsCreateInstance 関数は次のように実装する。
int
AEEClsCreateInstance(AEECLSID ClsId, IShell* pIShell, IModule* po, void** ppObj) {
*ppObj = NULL;
if (ClsId == AEECLSID_TEST1) {
if (AEEApplet_New(
sizeof(AEEApplet),
ClsId,
pIShell,
po,
(IApplet**)ppObj,
(AEEHANDLER)test1_HandleEvent,
NULL) == TRUE) {
return AEE_SUCCESS;
}
}
return EFAILED;
}
この関数が呼び出されるとき、引数として次のものが与えられる。
ClsId 生成するクラス ID。4 章の MIF エディタで指定した ClassID がシステムから引き渡さ
れる。AEEClsCreateInstance 関数は、この ID が自分が生成しようとしているクラスの
ものかどうかを確認しなければならない2 。
1 さらに言えば、この関数は外部に公開されなければならないので、他のアプレットプログラムの関数のように static
宣言してはならない。
2 なんでこの確認が必要かは不明ですが、恐らく実機で未確認なアプレットなどが動作したりしないようにするため
でしょう。実機のプログラミングをしたことがないのでわかりませんが…。
15
図 7.1: AEEApplet 構造体とアプレット
pIShell IShell インターフェース。IShell*型。IShell インターフェースは、BREW アプ
レットに対する最も基本的なサービスを提供するインターフェース。これも、起動時にシ
ステム側から引き渡される。
po IModule インターフェース。IModule*型。このインターフェースは単体のアプレットを作っ
ている上ではほぼ利用しない。
ppObj AEEClsCreateInstance 関数が返り値として要求される、生成した AEEApplet 構造体
インスタンスを格納する変数アドレス。
これらの引数を利用して、AEEClsCreateInstance 関数は、AEEApplet 構造体インスタンス
を生成、初期化し、システムにそれを返す処理をする。
AEEApplet 構造体の生成、初期化には、AEEApplet New 関数を使用する3 。AEEApplet New
関数が必要とする引数は引数順に次のようになっている。
1. 生成、初期化する構造体のサイズ。整数型。
2. ClassID。AEECLSID 型。
3. IShell インターフェース。IShell*型。
4. IModule インターフェース。IModule*型。
5. 生成した AEEApplet 構造体へのポインタを格納する変数アドレス。IApplet**型。
6. イベントハンドラ関数へのポインタ。AEEHANDLER 型。
3 この関数は、AEEAppGen.c
で定義される関数なので、BREW API リファレンスには載っていない!
16
7. アプレットデータ開放関数へのポインタ。PFNFREEAPPDATA 型。
第 1 引数の構造体のサイズは、sizeof(AEEApplet) 以上でなければならない。AEEApplet New
関数は、このサイズのメモリを用意し 0 クリアし引数から適切な初期化を行い第 5 引数の変数
へそのアドレスを格納する4 。
第 6 引数は、後述するイベントハンドラ関数へのポインタを渡す。次の 7.2 節で説明するが、
この関数はアプレット起動後「ボタンを押す」といったなんらかのイベント毎に起動される。ア
プレットの起動時以外の処理はすべてこの関数がエントリポイントとなる。関数のシグネチャは、
boolean handler(IApplet* pi);
となっており、AEEHANDLER と typedef されている。
第 7 引数は、アプレット終了時のデータ開放処理を行う関数へのポインタを渡す。この引数
は、後の 8 章で実際に使用するが、ない場合は、この例のように NULL を指定する。関数のシグ
ネチャは、
void freefunc(IApplet* pi);
となっており、PFNFREEAPPDATA と typedef されている。
7.2
イベントハンドラ関数
BREW アプレットの動作は、BREW システムからボタン押下などのイベント発生毎にイ
ベントに応じた処理をする、というイベントドリブン形式で進行する。イベントの処理は、
AEEApplet New 関数に引数で渡したイベントハンドラ関数が行う (図 7.2) 。
イベントハンドラ関数は次のように定義する。
static boolean
test1_HandleEvent(IApplet* pi, AEEEvent eCode, uint16 wParam, uint32 dwParam) {
switch (eCode) {
case EVT_APP_START:
return TRUE;
case EVT_APP_STOP:
return TRUE;
default:
break;
}
return FALSE;
}
イベントハンドラに渡される引数は次のようになっている。
pi AEEApplet New 関数で生成した構造体へのポインタ。
eCode イベントコード。実は整数。
wParam イベントに対応した情報その 1。short 整数。
4 AEEApplet
New 関数へ渡す構造体サイズの扱いは正確には次のようになっている。
• sizeof(AEEApplet) 以上ならそのサイズ。
• sizeof(AEEApplet) 以下なら、そのサイズ+sizeof(AEEApplet)
なんかイマイチよくわからない実装。詳しくは、AEEAppGen.c 参照。
また、AEEApplet New 関数が返す構造体は MALLOC ヘルパ関数を使用して確保されるが、MALLOC ヘルパ関数はディ
フォルトで 0 クリア機能があるため、結果として構造体も 0 クリアされる。MALLOC が 0 クリアしないようにすること
もできる。
さらに、AEEApplet New 関数内部では、処理のディスパッチ等のための仮想テーブルサイズ分、実際の構造体サイ
ズより大きいメモリが確保される (当然このテーブルの初期化も行う)。詳細は AEEAppGen.[c|h] や sdk/inc/AEE.h
等を参照。
17
図 7.2: アプレットのハンドラ関数
dwParam イベントに対応した情報その 2。long 整数。
eCode はイベント種別を表す。イベントには EVT APP START(アプレットの開始時)、EVT APP STOP(ア
プレット停止時)、EVT KEY PRESS(キー押下時) といったものが用意されている。すべてのイベ
ントは API リファレンス [4] の AEE イベントのページに記載されている。
キー押下時であれば押されたキーコードが必要となる。このような情報を付随させるために
第 3 引数と第 4 引数が用意されている。これらの引数に渡される値はイベントに依存する。こ
の引数についても詳細は API リファレンス [4] の AEE イベントのページに記載されている。
7.3
プログラミング上の制約
BREW アプレットプログラムを開発する場合、いくつかの制約を満たさなければならない。
これらの制約や作法については、BREW プログラミングの概念 [5] の BREW 開発ガイドライ
ンのページで詳しく説明されている。
ここでは、その中でも一般的な C/C++プログラミングと明らかに異なる点や、BREW に特
徴的なものを挙げる。
static、グローバル変数を使用できない
BREW アプレットプログラムでは、static 変数やグローバル変数は使用できない。これは、
BREW アプレットプログラムが動的にダウンロードされるため、これらの種類のデータを管理
することができないことが理由になっている。
18
標準 C ライブラリや標準 C++ライブラリを利用できない
標準 C ライブラリは利用できない。標準ライブラリの多くが、
malloc 標準関数を利用したり、内部の static 変数を利用するため、BREW のプログラム形態
にそぐわないことが理由と考えられる。代わりに利用頻度が高いものについては、MALLOC のよ
うに同等な機能を持つヘルパー関数が用意されている5 。また、DBGPRINTF のように、デバッ
グ用に用意された printf もある。ヘルパー関数については、API リファレンス [4] のヘルパー
関数のページに詳しいことが記載されている。
同様に標準 C++ライブラリも利用することはできない6 。
確保したリソースは必ず開放しなければならない
アプレットプログラムは確保したリソースを必ず開放することが要求される。9 章や 11 章で
も説明するが、BREW でのオブジェクトはすべて参照カウントにより管理されている (図 7.3)
7
。このため、リソースへの参照を生成した場合 (例えば、9 章での ISHELL LoadResImage 関数
は、ビットマップイメージへの参照を生成する)、その参照の開放 (つまり、参照カウントを1
減らす) をしないと、システムに「いらないもの」として認識されず、永遠に「ゴミ」としてメ
モリ上で行き続けることになる (図 7.4)。
i-mode 等の JVM 端末の場合、アプリケーションはは JVM 上で完全にコントロールされる
ため、このようなリソース開放ミス (最悪単なるバグ) があっても、アプリケーションの動作範
囲内しか影響がないが、BREW はネイティブでアプレットを動作させていることから、このよ
うなバグは他のアプリケーションや端末の動作にも影響を与える原因となる8 。
図 7.3: 参照カウントによるリソース管理
5 ヘルパー関数は
malloc なら MALLOC のように標準関数名を大文字にした名前になっている。
については未確認。ただし、少なくとも、ストリームに関連した関数やメンバは、iostream と密接に関連す
るため、やはり利用することが難しいと思われる (チョーいい加減予想)。
7 IShell インターフェースや IDisplay インターフェースも参照カウントに支配されている。
8 もしかしたら端末が壊れるかも!文献 [3] 参照:-P
6 STL
19
図 7.4: 正しくリソース開放をしなかった場合
20
第 8 章 Hello World!の表示
3 章のサンプルは実行可能であるが、画面には何も出力されない。この章では画面上にリソー
スエディタで定義した”Hello World!”の文字列を表示するように前章のプログラムを修正する。
8.1
ソースの修正
インクルードファイル追加
以下で使用する DBGPRINTF 関数などを使用するために必要なインクルードを追加する。
#include "AEEStdLib.h"
AEEStdLib.h はヘルパー関数を定義するヘッダファイルである。ヘルパー関数については、
BREW API リファレンス [4] のヘルパー関数のページに詳しく説明されている。使用するヘル
パー関数については、そのつど本文中で説明する。
アプレット用の構造体追加
インクルードの下に以下のコードを追加する。
// アプレット構造体
struct CAppTest1 {
AEEApplet a; // AEEApplet と CAppTest1 の構造を一致させるため先頭
IShell* shell; // 簡便用
IDisplay* display; // 簡便用
int swidth, sheight; // 画面の幅と高さ
};
7 章のサンプルは、AEEApplet 構造体を直接使っていたが、ここでは新しく作るアプレット
専用の構造体を用意する。
AEEApplet New 関数は AEEApplet 構造体を初期化するときに、同時に構造体のサイズも受け
取る。AEEApplet New 関数は、このサイズのメモリを用意して AEEApplet 構造体とみなして初
期化を行い、そのアドレスを返すようになっている。
このことを利用して、AEEApplet 構造体と同じメモリマッピングで、アプレット専用のデー
タを格納できるだけのサイズを指定すれば、アプレットはこの「空き」部分に独自のデータを
保持できる (図 8.1)。static 変数を使用できない BREW アプレットプログラムでは、これが
唯一のグローバルなデータの管理方法となっている。
上記の CAppTest1 構造体は先頭に AEEApplet 構造体をおくことで、マッピングを AEEApplet
に一致させ、さらに、独自のデータを持つメンバ変数を含んでいる。
追加する関数のプロトタイプ宣言
以下のプロトタイプ宣言を CAppTest1 構造体の宣言の後に追加する。
21
図 8.1: アプレット専用の AEEApplet を作る
// 新しく追加する関数 (CAppTest1 の初期化処理)
static boolean test1_InitAppData(IApplet* pi);
// 新しく追加する関数 (CAppTest1 の開放処理)
static void test1_FreeAppData(IApplet* pi);
この2つの関数はそれぞれアプレット専用のデータの初期化と開放処理を行う。次の AEEClsCreateInstance
関数で、これらの関数を使用するのでここでプロトタイプ宣言している。
アプレットインスタンス生成の変更
起動時のエントリポイントである AEEClsCreateInstance 関数を次のように修正する。
*ppObj = NULL;
if (ClsId == AEECLSID_TEST1) {
if (AEEApplet_New(
sizeof(CAppTest1), // 上記で定義した構造体サイズに変更
ClsId,
pIShell,
po,
(IApplet**)ppObj,
(AEEHANDLER)test1_HandleEvent,
(PFNFREEAPPDATA)test1_FreeAppData) == TRUE) {
if (test1_InitAppData((IApplet*)*ppObj) == TRUE) {
return AEE_SUCCESS;
22
} else {
return EFAILED;
}
}
}
return (EFAILED);
変更点は次の3つである。
• sizeof(AEEApplet) から sizeof(CAppTest1) へ変更
• test1 FreeAppData を開放処理関数として指定
• test1 InitAppData を使ってアプレット専用の初期化を追加
前述したようにアプレット専用のデータを格納するだけのメモリを要求するために、引数で
渡す構造体サイズを修正している。
AEEApplet New 関数は最後の引数に開放処理専用の関数を受け取り、開放が必要なときに
BREW システム側から呼び出されるようにする (7 章の図 7.2 参照)。通常はこの関数内でアプ
レットのデータの開放処理を行う。
test1 InitAppData は AEEApplet 構造体の初期化とアプレットプログラム自体の初期化を
分ける目的がある。
test1 InitAppData 関数の追加
次に、アプレット専用の初期化処理を行う test1 InitAppData 関数を追加する。この関数の
仕事は CAppTest1 構造体の AEEApplet 以外の初期化を行うことである。
static boolean
test1_InitAppData(IApplet* pi) {
CAppTest1* app = (CAppTest1*)pi;
// 簡便用のメンバ初期化
app->shell = app->a.m_pIShell;
app->display = app->a.m_pIDisplay;
// デバイスの情報を得る (画面サイズ)
AEEDeviceInfo di;
ISHELL_GetDeviceInfo(app->shell, &di);
app->swidth = di.cxScreen;
app->sheight = di.cyScreen;
DBGPRINTF("test1_InitAppData accomplished");
return TRUE;
}
やっていることは次の2つである。
• IShell や IDisplay インターフェースを簡便に扱えるようにする
23
• 画面サイズの取得
IDisplay はデバイス画面を表現するインターフェースである。
IShell や IDisplay インターフェースは AEEApplet 構造体の中に含まれているが、アプレッ
ト専用の構造体を導入することで app->a.m pIShell と少し冗長な書き方をしなければならな
い。そこで、CAppTest1 構造体直下にもこれらへの参照を持たせることで app->shell と少し
短い書き方にできる。
画面サイズの取得も行っている。ISHELL GetDeviceInfo 関数は、IDisplay インターフェー
スからデバイス画面の情報を AEEDeviceInfo 構造体に格納する。
最後に初期化が終了したら test1 InitAppData accomplished と出力する。この出力はシ
ミュレーターの出力ウィンドウに表示される (このウィンドウはシミュレーターのメニューから表示
>出力ウィンドウと選んで出すことができる)。DBGPRINTF ヘルパ関数は実行の状況を見る最も有
効な手段である。DBGPRINTF は C 標準ライブラリの printf と似て、void DBGPRINTF(format,
...); というシグネチャを持ち、
int n = 10;
DBGPRINTF("int value = %d", n);
のように、format に従った引数の表示が可能になっている。printf と最も重要な違いは勝手
に改行してくれる、という点である。
開放処理関数の追加
アプレット終了時の開放処理関数を追加する。この章のサンプルでは開放するものがないの
で何もしない。
static void
test1_FreeAppData(IApplet* pi) {
}
イベントハンドラ関数の修正
イベントハンドラ関数 test1 HandleEvent を次のように修正。
static boolean
test1_HandleEvent(IApplet* pi, AEEEvent eCode, uint16 wParam, uint32 dwParam)
{
CAppTest1* app = (CAppTest1*)pi; // 渡されるのは CAppTest1
// 文字列リソース取得用
static const int STR_CAPA = 64;
AECHAR buf[STR_CAPA];
switch (eCode) {
case EVT_APP_START:
case EVT_APP_RESUME:
// IDISPLAY_ClearScreen(display)
// 画面を消す
// ISHELL_LoadResString
24
// リソースから文字列を得る。リソースは test1.brh にある。
// IDISPLAY_DrawText
// 文字列の表示。
// IDSIPLAY_Update(display)
// 画面を更新する
IDISPLAY_ClearScreen(app->display);
ISHELL_LoadResString(
app->shell,
TEST1_RES_FILE,
IDS_STRING_1001,// 文字列 ID。リソースエディタで変更可能。
buf,
STR_CAPA);
IDISPLAY_DrawText(
app->display,
AEE_FONT_NORMAL,
(AECHAR*)buf,
-1, // 表示長さ。-1 なら自動計算。
app->swidth / 5, // x 位置
app->sheight / 2, // y 位置
0, // クリッピング境界指定 (なし)
0); // 描画フラグ
IDISPLAY_Update(app->display);
return (TRUE);
case EVT_APP_STOP:
return (TRUE);
default:
break;
}
return FALSE;
}
画面に文字列を出力するコードを追加している。それぞれの関数の機能を説明する。
IDISPLAY ClearScreen(display)
画面のクリアを行う。引数には IDisplay インターフェースを与える。
ISHELL LoadResString(shell, file, id, buf, capa)
リソースから文字列を取得する。
shell IShell インターフェースを指定する。
file リソース名。リソースエディタでリソースをコンパイルしたときに生成される.brh ファイ
ル内で定義される。
id 文字列リソース ID。リソースエディタで文字列リソースを追加したときに、「名前」で指定
されるリテラルがそのまま.brh ファイル内で定数マクロとして定義される。
buf 文字列を取得するバッファ。このバッファに文字列が格納される。
25
capa 引数で渡すバッファ(buf) のサイズ。バッファオーバーフローを避けるために指定する。
リソース文字列がこのサイズより大きくても、buf がこのサイズ以上に書き込まれること
はない。
ISHELL DrawText(display, font, buf, len, x, y, clip, flag)
文字列を描画する。
display IDisplay インターフェースを指定する。
font フォントを指定する。フォントには AEE FONT NORMAL,AEE FONT BOLD 等がある。
buf 表示する文字列を格納しているバッファを指定する。
len 表示する文字列長。-1 を指定すると自動で判別する (C 文字列として扱う)。
x,y 表示位置。ピクセルで指定。
clip AEERect 型のクリッピングエリアの指定。AEERext は、int16 x, y, dx, dy という矩形
を表す。クリッピングしない場合は NULL を指定する。
flag 描画フラグ。用意されているフラグの論理和で指定する。フラグには、IDF ALIGN RIGHT(右
揃え) や IDF TEXT UNDERLINE 等がある。特に指定がないときは 0 にする。
IDISPLAY Update(display)
この関数を呼び出すことで、それまでに実行した描画処理を表示に反映させることができる。
逆に言えば、この関数を実行しなければ表示は変更されない。
一般的な Open GL アプリケーションやリアルタイムグラフィクスプログラムでは表示用と
描画用に2枚のピクセルバッファを持たなければならないが ( ダブルバッファリング図 8.2 )、
BREW の場合、バッファ1枚だけでちらつきや描画途中を見せずに画面表示の更新が可能にな
る (図 8.3) 1 。
8.2
ビルドと実行
VC.NET から 6 章と同じように再度ビルドを行う。その後次のようにして実行する。
1. スタートメニューからプログラム>BREW SDK 日本語版>BREW シミュレータを
選び、シミュレータを実行する。
2. シミュレータのファイル >アプレットディレクトリの変更を選び、選択ダイアログを出す。
3. c:/を選択 (プロジェクトのあるディレクトリ)。その後 OK ボタンを押す。
4. 適切にファイルが作られて修正されていれば、シミュレータ内の端末の矢印キーを使って
test1 を選べる。
5. 選んだら、やはりシミュレータ内の決定キーを押して、実行。
6. シミュレータ内に Hello World!が表示される。
1 別に
BREW に限ったことではなく、多くの携帯端末ではこのような仕組みであることが多い。
26
図 8.2: 一般的な Open GL や DirectX の表示、描画のしくみ
図 8.3: BREW の場合の表示、描画のしくみ
27
第 9 章 リソース中のビットマップの表示
本章では、5 章で作ったビットマップリソースを画面に表示する修正を行う。大まかに次の 4 つ
の修正が必要となる。
• ビットマップの管理のための変数を用意
• ビットマップをロードする
• ビットマップデータを開放する
• ビットマップを描画する
9.1
ソースの修正
アプレット構造体へメンバ追加
アプレット構造体 CAppTest1 に、イメージ管理のためのメンバ変数を次のように追加する。
IImage はイメージデータを表すインターフェースである。
struct CAppTest1 {
// :
// :
IImage* img; // ビットマップイメージ
};
イメージをリソースからロード
リソースからビットマップイメージをロードして、IImage オブジェクトとして得る。以下の
コードを test1 InitAppData 関数内に追加する。
// リソースから絵をロード。
app->img = ISHELL_LoadResImage(app->shell, TEST1_RES_FILE, IDI_OBJECT_5001);
if (app->img == NULL) {
return FALSE;
}
IShell LoadResImage 関数はリソースからイメージデータを取得する API 関数である。第
3 引数はリソース ID で、8 章の ISHELL LoadResString 関数と同様に、.brh ファイル内で定
義されているリソース ID を指定する。
開放処理関数の修正
ロードしたイメージは開放しなければならない。開放関数である test1 FreeAppData 関数に
その処理を追加する。test1 FreeAppData 関数にその処理を追加する。
28
static void
test1_FreeAppData(IApplet* pi) {
CAppTest1* app = (CAppTest1*)pi;
if (app->img != NULL) {
IIMAGE_Release(app->img);
app->img = NULL;
}
}
IIMAGE Release 関数はイメージの参照数を 1 減らす API 関数である。その後、リソースを
参照しているかどうかを正しく判断できるようにするために、img には NULL を代入している。
アプレット内でリソースを頻繁に生成、廃棄する場合に必要なテクニックである。
イメージ描画
テキストの描画と同じ場所に次のイメージ描画コードを追加する。
IIMAGE_Draw(
app->img, // イメージオブジェクト
app->swidth / 2, // x 位置
app->sheight * 2 / 3); // y 位置
IIMAGE Draw 関数は、指定したイメージの描画を行う。引数には、イメージオブジェクトと
描画位置を指定する1 。
9.2
ビルドと実行
Hello World!のときと同様にビルドして実行すると、リソースエディタで設定したビットマッ
プも表示されるようになる。
1 奇妙なことだが、IIMAGE
Draw 関数は IDisplay インターフェースを必要としない。なぜだろう?
29
第 10 章 ゲームアプリケーションフレーム
ワーク
ここでは簡単なゲームアプレットの雛形の導入をする。この章で修正、追加するプログラムは、
文献 [3] のものとほぼ同じものである。
10.1
簡便用の共通ヘッダの作成
まず、多くのソースファイルで共通に利用できるヘッダを新しく定義する。
1. ソリューションエクスプローラーのヘッダファイルを選択し、右クリック。
2. メニューの追加 >新しい項目の追加を選ぶ。ダイアログが出る。
3. ダイアログの右側のペインのヘッダーファイルを選ぶ。
4. ダイアログの下側のファイル名のところを common.h とする。
5. 開くボタンを押す。
このヘッダファイルをソリューションエクスプローラから選択し、次のように追加記述する。
#ifndef __common_h_included__
#define __common_h_included__
#include "AEEModGen.h"
#include "AEEAppGen.h"
#include "AEEStdLib.h"
#endif // __common_h_included__
後の章では、このヘッダに Open GL/ES 用のインクルードやマクロ関数の定義などを追加
する。
10.2
Game クラスの追加
8 章のように test.cpp にすべてのプログラムを記述することはあまり現実的ではない。そこ
で、アプレット全体の処理やデータを管理する Game クラスを導入する。
10.2.1
game.h の追加
common.h の場合と同様に新しく game.h を追加する。game.h は次のように記述する。
30
#ifndef __common_h_included__
# include "common.h"
#endif // __common_h_included__
#ifndef __game_h_included__
#define __game_h_included__
class Game {
public:
// 初期化
boolean initialize(IShell* shell, IDisplay* display);
// 開放処理
void destroy();
// 1 フレーム分の処理
void tick(int time);
// 取得用メンバ関数
IShell* get_shell() { return shell; }
const IShell* get_shell() const { return shell; }
IDisplay* get_display() { return display; }
const IDisplay* get_display() const { return display; }
int get_width() const { return deviceInfo.cxScreen; }
int get_height() const { return deviceInfo.cyScreen; }
boolean is_key_on(int keyCode) const {
return keysPressed[keyCode - AVK_FIRST];
}
// キー入力受付処理
void key_pressed(int keyCode);
void key_released(int keyCode);
private:
IDisplay* display;
IShell* shell;
AEEDeviceInfo deviceInfo;
boolean keysPressed[AVK_LAST - AVK_FIRST];
};
#endif
// __game_h_included__
メンバ変数の display、shell 及び deviceInfo は、8 章のものと同じで、initialize メン
バ関数で初期化される。
keysPressed 配列はキーの押下状態を記憶するもので、is key on 関数を使ってキーの押下
状態を確認をできる。keysPressed 配列は、イベントハンドラ関数のイベント分岐処理内から、
ボタン押下時に呼ばれる key pressed と、ボタンを離したときに呼ばれる key released 関数
31
により、適宜要素値が設定される。
10.2.2
game.cpp の追加
test1.h の場合と同様に新しく game.cpp を追加する。game.cpp は次のように記述する。
initialize 関数、destroy 関数の定義
initialize 関数及び destroy 関数は次のように定義する。
boolean
Game::initialize(IShell* shell, IDisplay* display) {
this->shell = shell;
this->display = display;
deviceInfo.wStructSize = sizeof(deviceInfo);
ISHELL_GetDeviceInfo(shell, &deviceInfo);
return TRUE;
}
void
Game::destroy() {
}
initialize 関数は 8 章の初期化と同じことをしている。destroy 関数には開放処理を記述
するが、この章では使用するリソース等は特にないので何もしない。
key pressed 関数、key released 関数の定義
key pressed 関数及び key released 関数は次のように定義する。
void
Game::key_pressed(int keyCode) {
keyCode -= AVK_FIRST;
if (0 <= keyCode && keyCode < AVK_LAST - AVK_FIRST) {
keysPressed[keyCode] = true;
}
}
void
Game::key_released(int keyCode) {
keyCode -= AVK_FIRST;
if (0 <= keyCode && keyCode < AVK_LAST - AVK_FIRST) {
keysPressed[keyCode] = false;
}
}
AVK FIRST 及び AVK LAST マクロは、キー ID の (一番小さい ID) と (一番大きい ID+1) の値
を定義している。よって、配列もこの値の範囲となるように定義している。
key pressed はイベントハンドラからキー押下時に呼ばれ、対応する配列要素を true にす
る。逆に key release はキーが離されたときに呼ばれ、対応する配列要素を false にする。こ
32
のしくみにより、keysPressed 配列には常にボタンが押されているかどうかが論理値で格納さ
れる。
tick 関数の定義
tick 関数は、ゲームの処理 (時間) を少しだけ進めるための処理を行う1 。tick 関数は次のよ
うに記述する。
void
Game::tick(int time) {
AECHAR buffer[16];
WSPRINTF(buffer, 16, L"FPS: %d", 1000 / time);
IDISPLAY_DrawText(
display,
AEE_FONT_BOLD,
buffer,
-1, 5, 5, NULL, 0);
IDISPLAY_Update(display);
}
tick 関数に引数として渡される値は経過時間をミリ秒で表した整数である。これについては
後のタイマー処理の節で説明する。
BREW で使用する文字はワイド文字である。しかし、C 標準ライブラリの wsprintf は BREW
の制約から使用できない。代わりにヘルパー関数 WSPRINTF を使用する。WSPRINTF はフォーマッ
ト文字列にワイド文字列を取る。よって、引数には通常の C 時文字列"..."ではなく L"..."を
用いる。
10.3
test1.cpp の修正
最後に追加した Game クラスにあわせて test.cpp を修正する。
インクルードの修正
common.h を作成し、game.h でそれをインクルードしていることから、test.cpp のインク
ルードは次のように game.h だけとなる。
#include "game.h"
CAppTest1 構造体の修正
IShell や IDisplay インターフェースは Game クラスで管理するようにしたことから、CAppTest1
構造体は次のように変更する。
struct CAppTest1 {
AEEApplet a;
Game game;
int oldTime;
};
1 要するに
1 フレーム分の処理
33
oldTime メンバ変数は後述するタイマー処理で最後に tick 関数を実行した時間を記憶する
ために使用する。
test1 InitAppData 関数の修正
test1 InitAppData 関数は game メンバ変数の初期化を行うように変更する。
static boolean
test1_InitAppData(IApplet* pi) {
CAppTest1* app = (CAppTest1*)pi;
if (app->game.initialize(app->a.m_pIShell, app->a.m_pIDisplay) != TRUE) {
return FALSE;
}
return TRUE;
}
タイマー処理の追加
一定間隔で Game クラスの tick 関数を呼び出すためのタイマー処理を追加する2 。
// 1秒間に何フレーム処理したいか?
static const int WANTED_FPS = 20;
// WANTED_FPS フレームは1フレーム何ミリ秒か?
static const int TICK_TIME = 1000 / WANTED_FPS;
static void test1_setTimer(CAppTest1* data);
static void test1_timerTick(void* data) {
CAppTest1* app = (CAppTest1*)data;
int time = GETUPTIMEMS() - app->oldTime;
app->oldTime = GETUPTIMEMS();
app->game.tick(time);
test1_setTimer(app);
}
static void test1_setTimer(CAppTest1* app) {
int result = ISHELL_SetTimer(
app->game.get_shell(),
TICK_TIME,
test1_timerTick,
(void*)app);
if (result != SUCCESS) {
DBGPRINTF("*** test1_setTimer failed");
}
}
タイマー処理の概観は次のようになっている。
2 このコードの中では static const を使っている。static const はコンパイラによって定数として扱われるので
実行時には存在せず、BREW アプレットプログラムでも使用することができる (多分)。まずければ#define を使って
しまえばいいだけだが、C++を使ってコードを書いているので、できればプリプロセッサは避けたいなぁ…。
34
1. ISHELL SetTimer 関数で、時間間隔と呼び出すタイマ処理関数を設定。
2. タイマ処理関数は、必要な処理を行い、再度 1 を行う。
2 にあたるのが、test1 timerTick 関数で、1 にあたるのが、test1 setTimer 関数である。
ISHELL SetTimer 関数は、呼出し時点から指定された時間後 (定数 TICK TIME ) に指定された
ハンドラ (test1 timerTick) が自動的に呼ばれるように設定する。ハンドラ test1 timerTick
の中では、tick 関数を処理し、再度 test1 setTimer 関数を呼び出して ISHELL SetTimer 関
数で次のハンドラ呼び出しを設定している (図 10.1 参照)。この仕組みにより、一定時間おきに
決まった関数 (tick) が実行される。
図 10.1: タイマーの仕組み
イベントハンドラの修正
タイマ処理やキー操作処理、開放処理をイベントハンドラに追加する。
static boolean
test1_HandleEvent(IApplet* pi, AEEEvent eCode, uint16 wParam, uint32 dwParam)
{
CAppTest1* app = (CAppTest1*)pi;
switch (eCode) {
case EVT_APP_START:
test1_setTimer(app);
return TRUE;
case EVT_APP_RESUME:
return TRUE;
case EVT_KEY_PRESS:
35
app->game.key_pressed(wParam);
return TRUE;
case EVT_KEY_RELEASE:
app->game.key_released(wParam);
return TRUE;
case EVT_APP_STOP:
app->game.destroy();
return TRUE;
default:
break;
}
return FALSE;
}
キーを押したり離した時のイベント発生時 ( EVT KEY PRESS,EVT KEY RELEASED ) は、Game
クラスの key pressed,key released を呼び出す。
また、アプレット開始時 (EVT APP START) にタイマー初期化をするため test1 setTimer 関
数を呼び出している。
10.4
ビルドと実行
8 章の場合と同じくビルドと実行を行う。実行すると画面上に FPS が表示される。
36
第 11 章 Open GL/ES
この章では、Open GL/ES の簡単なプログラムの導入する。この章のプログラムも、文献 [3]
と内容がほぼ同じものとなっている。
Open GL/ES は Open GL のサブセットとなっている。Open GL にあって、Open GL/ES
では制限されている機能として次のものがある。
• glVertex...、glNormal...関数がない。
• ディスプレイリストが使えない。
これらの制約は主に、組み込み機器で要求される効率性のために導入されている。Open GL/ES で
の描画は、頂点や法線を配列にして一度にエンジンに引き渡す glVertexPointer や glNormalPointer
等の関数を使用する。
Open GL と Open GL/ES の違いや、BREW Open GL/ES の詳しい仕様については、仕様
書 [2, 1] や Open GL ES Extension API Reference[8] に説明されている。
11.1
sdk/src/GL.c を追加
3.2 節での AEEAppGen.c ファイルの追加と同じようにして、c:/Program files/BREW.../sdk/src/GL.c
の追加をする。このソースは BREW Open GL/ES extension の関数を利用するときに、本来
の Open GL/ES 仕様へのラッパーの働きをする。詳しくは後述する。
11.2
common.h ヘッダの修正
common.h ヘッダに、Open GL/ES を使う上で必要なヘッダや便利なマクロを定義する。
//
:
//
:
#include "IGL.h"
#include "AEEGL.h"
#include "gles/egl.h"
#define ITOFP(x) ((x)<<16)
#define FTOFP(x) ((int)((x) * 65536.0))
//
//
:
:
Open GL/ES は数値として固定小数値と単精度浮動小数値をサポートするが、BREW では、
固定小数のみを扱う。固定小数は下位 16 ビットを小数部とする形式である。プログラム中での
頻繁な<< 16 という記述を避けるために、ITOFP というマクロで、与えられた整数を固定小数
での実数値に変換できるようにする。
また、FTOFP は実数記述から、固定小数への変換マクロで、FTOFP(1.5) のようにして使う。
実数はソフトウェアエミュレーションであるため、実行速度に問題があるが、プログラム中で
37
FTOFP(1.5) のように定数で利用する場合、コンパイラがコンパイル時に定数計算してくれる
ことから、実行時に浮動小数としてあつかわれない1 。
11.3
Renderer クラスの追加
Open GL/ES の初期化やダブルバッファの管理をする Renderer クラスを導入する。
11.3.1
renderer.h の追加
10.2.1 節の game.h の追加と同じようして Renderer クラスを定義する renderer.h を追加す
る。renderer.h の記述は以下のとおり。
#ifndef __common_h_included__
# include "common.h"
#endif // __common_h_included__
#ifndef __renderer_h_included__
#define __renderer_h_included__
class Renderer {
public:
// 初期化処理
boolean initialize(IShell* shell, IDisplay* display);
// 開放処理
void destroy();
// 描画バッファと表示バッファの入れ替え
void swap_buffer();
private:
IGL* igl;
IEGL* iegl;
EGLDisplay eglDisplay;
EGLConfig eglConfig;
EGLSurface eglSurface;
EGLContext eglContext;
};
#endif // __renderer_h_included__
BREW では画面の描画、表示にダブルバッファリングをする必要がないが、Open GL/ES で
はこのモデルを前提としているので、Renderer クラスにも swap buffer というメンバ関数を
用意する。
メンバ変数の定義に使われるクラスは次のものがある。
1 ARM
コンパイラでは未確認。あくまでシミュレーターのレベルでの話。
38
IGL
OpenGL/ES 関数を提供するインターフェースクラス。メンバ関数として、IGL glPushMatrix
や IGL glRotatex 等が用意されている。
IEGL
IGL とプラットフォームのウィンドウシステムの間に位置する、レンダリング surface 2 等を
扱うインターフェースクラス。ダブルバッファの管理等で使用する。
EGLDisplay
GL/ES のために抽象化された物理画面を表すクラス。
EGLConfig
カラー深度やバッファサイズなどの属性を扱う整数ID。このIDからディスプレイのもつ
属性値を得ることができる。IDには EGL BUFFER SIZE(バッファサイズ)、EGL DEPTH SIZE(Z
バッファの深さ) などがある。
EGLSurface
描画面を表す「何か」。ハンドルと捉えることができる (実際、void*として定義されている)。
EGLContext
GL/ES の状態を表す整数。初期化関数の返り値などで利用する。
11.3.2
renderer.cpp の追加
Renderer クラスの実装を定義する renderer.cpp を追加する。renderer.cpp の記述は以下
のとおり。initialize 関数は多少長いため、記述順にわけて説明する。
initialize 関数 (IGL,EIGL)
initialize 関数は最初、IGL インターフェースと IEGL インターフェースを生成する。
boolean
Renderer::initialize(IShell* shell, IDisplay* display) {
if (ISHELL_CreateInstance(shell, AEECLSID_GL, (void**)&igl) != SUCCESS) {
destroy();
return FALSE;
}
if (ISHELL_CreateInstance(shell, AEECLSID_EGL, (void**)&iegl) != SUCCESS) {
destroy();
return FALSE;
}
2 日本語でなんていうの?面?
39
インスタンスの生成は、他の BREW API のインターフェースクラスの生成と同じく、ISHELL CreateInstance
関数を使用する。それぞれの生成では失敗時に destroy 関数を呼ぶことで、初期化に失敗した
場合でも、リソースがきちんと開放されるようにしている。
IGL,IEGL をラッパ (GL.c) 用に登録
ラッパ関数群は GL.c で定義される。ラッパー関数群を使用するには前もって IGL インター
フェースと IEGL インターフェースがラッパー対象となるように設定しなければならない。設
定は次のように記述する。
IGL_Init(igl);
IEGL_Init(iegl);
BREW Open GL/ES extension では Open GL/ES の関数が、IGL や IEGL インターフェース
中に IGL glClear や IEGL eglGetDisplay として定義されている。これでは、Open GL/ES の
仕様と合わないため、GL.c でその仕様にあったラッパーの機能を提供している。例えば glClear
関数は GL.c で次のように定義されている3 。
GL_API void GL_APIENTRY glClear(GLbitfield mask)
{
IGL_glClear(GPIGL, mask);
}
この処理以降は、IGL glClear や IEGL eglGetDisplay メンバ関数は使用せず、かわりにラッ
パで提供される、GL/ES 仕様書 ([2, 1]) が規定する glClear や eglGetDisplay を使用するこ
とができる。
EGLDisplay インスタンスを得る
IGL,IEGL インターフェースを獲得して最初にやることは、ディスプレイインスタンスの取得
である。
eglDisplay = eglGetDisplay(display);
if (eglDisplay == EGL_NO_DISPLAY) {
destroy();
return FALSE;
}
先の IGL,IEGL 同様、失敗時は destroy を呼び出すようにしている。eglGetDisplay は GL.c
で定義されるラッパ関数である。
EGL の初期化
次に、取得したディスプレイを使って EGL の初期化をする。
EGLint major = 0;
EGLint minor = 0;
3 このサンプルの変数 GPIGL はグローバル変数だが、しかし BREW ではグローバル変数が禁止されている。実は、
このラッパーはシミュレーター専用 (VC.NET 専用) であり、グローバル変数を使っても特に困らないのだ!(つまり
シミュレーターレベルであればグローバル変数を使ってもOK!) 。しかし実機の開発を行うときは当然ダメなので、
やはりグローバル変数を使わないようにしてプログラミングしなければならない。ということは、実機ではどうやって
このラッパーを使うのでしょう…?
40
if (eglInitialize(eglDisplay, &major, &minor) == FALSE) {
destroy();
return FALSE;
}
EGL の初期化関数は返り値として成功の真偽以外に、バージョン番号も引数返り値として返
す。必要であれば、アプレットはこのバージョンを利用することができる。
この文書を書いている現在、BREW OpenGL/ES のバージョンは 1.0 であるが、最新の Open
GL/ES のバージョンは 1.1 である。1.1 では次のような機能が導入されている。
• バッファオブジェクト機能。メモリ上に頂点や法線データをまとめてのせて一度にレンダ
リングできる。
• ハードウェア MIPMAP。
• マルチテクスチャ、バンプマップ、per-pixel ライティング
• マトリクスパレット機能。スキニングなどで利用する。
詳しくは、http://www.khronos.org/opengles/spec.html に記述されている。
EGLConfig の取得
ディスプレイに関する情報を得る。
EGLint numConfigs = 1;
if (eglGetConfigs(eglDisplay, &eglConfig, 1, &numConfigs) == FALSE) {
destroy();
return FALSE;
}
eglGetConfigs 関数の第2引数は、コンフィグを格納する配列、第3引数はその配列のキャ
パシティ(配列サイズ)、第4引数は結果として得られたコンフィグ数を格納する整数へのアド
レスを指定する。この章のサンプルでは、単純にコンフィグが得られるかどうかのチェックの
みを行っているが、第2引数の配列に得られるコンフィグ ID を、eglChooseConfig 関数に引
き渡して、各コンフィグの持つ値を得ることもできる。
コンフィグは、プラットフォームに依存した情報を扱わなければならない場合のために、Open
GL/ES の仕様で規定されている。
デバイスビットマップを得る
IDISPLAY Update が呼び出されたときに表示すべきピクセルバッファ(IBitmap インターフェー
ス) を得る。描画のターゲットとなる場所である。
IBitmap* dbmap = NULL;
if (IDISPLAY_GetDeviceBitmap(display, &dbmap) != SUCCESS) {
destroy();
return FALSE;
}
IBitmap は BREW の描画可能なピクセルバッファを表す。IDISPLAY GetDeviceBitmap は、
IDisplay インターフェースからデバイス表示に使用している
tt IBitmap を得る。
41
NativePixmapType インスタンスを得る
デバイスビットマップを得られたら、そこから NativePixmapType (OpenGL/ES で言うピク
セルバッファ) を取得し、第2引数の返り値引数となる変数へ格納する。成功したかどうかは、
返り値で確認する。
NativePixmapType pixmap = NULL;
if (IBITMAP_QueryInterface(dbmap, AEECLSID_DIB, (void**)&pixmap) != SUCCESS) {
IBITMAP_Release(dbmap);
destroy();
return FALSE;
}
IBITMAP QueryInterface 関数は、IQI QueryInterface という指定したオブジェクトが持
つ別のインターフェースを得るための関数の IBitmap 版である。この場合、BREW のピクセル
バッファに対して、OpenGL/ES 用に NativePixmapType インターフェースを要求している。
IBitmap は BREW 用のピクセルバッファを表し、NativePixmapType は OpenGL/ES が規
定するピクセルバッファを表す。BREW ではこの間を取り持つために、IBitmap の指すオブ
ジェクトに NativePixmapType のインターフェース機能を持たせている (図 11.1 参照)。これに
より、IBitmap を NativePixmapType として扱うことができるようになっている。
図 11.1: イメージオブジェクトと IBitmap、NativePixmapType の関係
OpenGL/ES の仕様書 [2, 1] で NativePixmapType として定義されている「モノ」は BREW で
は、typedef IDIB* NativePixmapType; として定義されている。よって、NativePixmapType
を得るためには、ID として AEECLSID DIB を使用しなければならない。IDIB は IBitmap と
違い、デバイスに依存しないビットマップを表す。
処理が失敗した場合に、dbmap を IBITMAP Release に渡している点は注意が必要である。
IDISPLAY GetDeviceBitmap 関数で得られた dbmap は、この関数が成功した時点でアプレット
から参照されているとみなされて、参照カウントが 1 増えている。dbmap は NativePixmapType
42
の取得以降は使用しないため、メモリ上の「ゴミ」にならないように、参照カウントを 1 減ら
してやらなければならない。IBITMAP Release はこの処理を行う。
レンダリング面を得る
NativePixmapType が得られれば、レンダリング面である EGLSurface が得られる。
eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, pixmap, NULL);
IDIB_Release(pixmap);
IBITMAP_Release(dbmap);
if (eglSurface == EGL_NO_SURFACE) {
destroy();
return FALSE;
}
eglCreateWindowSurface 関数は、ディスプレイ、コンフィギュレーションや描画ウィンド
ウ (この場合先に得た pixmap) から、レンダリング面を返す関数である。返り値は成功すれば
レンダリング面、そうでなければ EGL NO SURFACE を返す。
dbmap はこの処理以降使用しないため、IBITMAP Release で開放している4 。同様に、pixmap
も eglCreateWindowSurface 関数処理後に参照することはないため、IDIB Release 関数で開
放している5 。
第4引数にはプラットフォーム環境に依存した属性値リスト (コンフィグレーション) を指定
する。プラットフォーム依存であるため OpenGL/ES では内容は規定されてない。通常は NULL
を指定する。
レンダリングコンテキストの初期化
次にレンダリング面から、レンダリングコンテキストの初期化を行う。レンダリングコンテキ
ストは、レンダリングバッファの管理などの Open GL/ES エンジンの状態を管理を担当する。
eglContext = eglCreateContext(eglDisplay, eglConfig, NULL, NULL);
if (eglContext == EGL_NO_CONTEXT) {
destroy();
return FALSE;
}
eglCreateContext の第3引数は、複数のコンテキスト間で情報の共有を行うときに指定する。
通常は NULL を指定する。第4引数にはプラットフォーム環境に依存した属性値リスト (コン
フィグ) を指定する。プラットフォーム依存であるため OpenGL/ES では内容は規定されてな
い。通常は NULL を指定する。
カレントコンテキスト設定
最後に描画コンテキストの設定を行う。
if (eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext) == FALSE) {
destroy();
return FALSE;
4 先の
IBITMAP Release は初期化失敗時の処理であることに注意
== IDIB!
5 NativePixmapType
43
}
return TRUE;
// initialize 関数の終わり
}
eglMakeCurrent 関数は、描画用と表示用の2枚のレンダリング面を指定する。ダブルバッ
ファリングを行うプラットフォームでは、ここで異なるバッファを指定し毎フレームこのバッ
ファの入れ替えを行うが、BREW の場合、バッファの変更をしても IDISPLAY Update 関数を
呼び出さない限り画面表示が更新されることはない (8 章とその図 8.3 参照)。よって、1枚のレ
ンダリング面だけで描画と表示の切り替えをすることができる。
destroy 関数
destroy 関数は、initialize 関数と逆に、すべての開放処理を行う。
void
Renderer::destroy() {
// 全インスタンスを生成と逆順に開放
eglMakeCurrent(
EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (eglContext != NULL) {
eglDestroyContext(eglDisplay, eglContext);
eglContext = NULL;
}
if (eglSurface != NULL) {
eglDestroySurface(eglDisplay, eglSurface);
eglSurface = NULL;
}
if (eglDisplay != NULL) {
eglTerminate(eglDisplay);
eglDisplay = NULL;
}
if (iegl != NULL) {
IEGL_Release(iegl);
iegl = NULL;
}
if (igl != NULL) {
IGL_Release(igl);
igl = NULL;
}
}
swap buffer 関数
swap buffer 関数は、ダブルバッファの入れ替えや描画リクエストのフラッシュを行う。
void
Renderer::swap_buffer() {
eglSwapBuffers(eglDisplay, eglSurface);
}
44
本章のプログラムではバッファは1つしかないため、入れ替え自体には意味がないが、Open
GL/ES エンジン内部のフラッシュ処理として必要である。
11.4
Game クラスの修正
Renderer クラスを Game クラスに導入する。
11.4.1
game.h の修正
まず、Game クラスに、Renderer インスタンスをメンバ変数として持たせる。
class Game {
public:
//
:
//
:
private:
//
:
//
:
Renderer renderer;
int rotateAngle;
};
rotateAngle 変数は、簡単なアニメーションをするためのワーク変数である。
また、Renderer クラスを使えるようにするために、game.h の先頭で、renderer.h をイン
クルードしなければならない。
#ifndef __renderer_h_included__
# include "renderer.h"
#endif // __renderer_h_included__
11.4.2
game.cpp の修正
次に、Renderer クラスの導入に伴った game.cpp の修正を行う。
intialize 関数の修正
initialize 関数内の、ISHELL GetDeviceInfo 関数呼び出しの後に以下の、renderer メン
バ変数の初期化をするコードを追加する。
if (renderer.initialize(shell, display) == FALSE) {
return FALSE;
}
さらに、このあと次のような、一般的な Open GL(/ES) の初期化処理を追加する。
// Z バッファon
glEnable(GL_DEPTH_TEST);
// viewport サイズの初期化
45
glViewport(0, 0, get_width(), get_height());
// 投影マトリクス初期化
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// ライティングとαブレンディング初期化
glDisable(GL_LIGHTING);
glDisable(GL_BLEND);
// クリッピング用 frustum の初期化
glFrustumx(ITOFP(-5), ITOFP(5), ITOFP(-5), ITOFP(5), ITOFP(10),
ITOFP(100));
// モデルマトリクス初期化
glMatrixMode(GL_MODELVIEW );
glLoadIdentity();
// 頂点と色を配列で与えるモードにする
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
// アニメーション用の変数初期化
rotateAngle = 0;
Open GL(/ES) の初期化については、ほぼ、通常の Open GL アプレットと同様のものとなっ
ている。違いは以下の2点である。
• 浮動小数値ではなく固定小数値を使う。
• GL VERTEX ARRAY モードなどの設定をする。
最後の rotateAngle は次に示す簡単なアニメーションのためのワーク変数である。
tick 関数の修正
tick 関数では次のことをする処理を追加する。
• グローシェードされた 3D の三角形板を表示。
• 矢印キーで左右に回転。
以下のコード FPS の表示前に追加する。
// 描画面クリア、モデルマトリクス設定
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glLoadIdentity();
glTranslatex(0,
0, ITOFP(-15));
// 左右の矢印キーで3度回転。回転はY軸中心。
if (is_key_on(AVK_LEFT) == TRUE) {
46
rotateAngle -= 3;
}
if (is_key_on(AVK_RIGHT) == TRUE) {
rotateAngle += 3;
}
if (rotateAngle < 0) rotateAngle += 360;
if (rotateAngle > 360) rotateAngle -= 360;
glRotatex(ITOFP(rotateAngle), ITOFP(0), ITOFP(1), ITOFP(0));
// 三角形の頂点データ
int faceData[] = {
-ITOFP(2), -ITOFP(2), ITOFP(0), // 最初の頂点
ITOFP(2), -ITOFP(2), ITOFP(0), // 二番目の頂点
-ITOFP(0), ITOFP(2), ITOFP(0) // 三番目の頂点
};
// 三角形の頂点色データ
int colorData[] = {
ITOFP(1), ITOFP(0), ITOFP(0), ITOFP(0), // 最初の頂点色
ITOFP(0), ITOFP(1), ITOFP(0), ITOFP(0), // 二番目の頂点色
ITOFP(0), ITOFP(0), ITOFP(1), ITOFP(0) // 三番目の頂点色
};
uint8 indexData[3] = {0, 1, 2}; // 三角形の頂点のインデックス
// 頂点と頂点色をエンジンに渡す
glVertexPointer(3, GL_FIXED, 0, faceData);
glColorPointer(4, GL_FIXED, 0, colorData);
// 三角形の描画
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, indexData);
glPopMatrix();
renderer.swap_buffer();
11.5
ビルドと実行
ビルドは、8 章と同じように行う。
実行に際しては、シミュレーターでデバイスの設定をする。
1. シミュレーターを起動する。
2. メニューのファイル >デバイスの選択を選択。
3. デバイスの選択ダイアログが出るので、Blackcap16.qsc を選択。
4. 開くを押す。
その後は、test1 を選択して、決定ボタンで実行を開始する。正しくビルドされバグがなけ
れば、グロー三角形を左右矢印キーで Y 軸回転させることができる。
47
第 12 章 デバッガを使う
この章では VC.NET のデバッガを使ったデバッグの方法を説明する。
12.1
準備
VC.NET 上でビルドしているプログラムは test1.dll という DLL ファイルで、シミュレー
ター上でなければ動作しない。よって、準備として、デバッグ時にシミュレーター上で DLL を
動作させるように指定する。
1. ソリューションエクスプローラで test1 プロジェクトを右クリック
2. プロパティページダイアログ中の構成プロパティ >デバッグを選ぶ
3. 右側のペインの「動作」の下に「コマンド」項目があるのでクリック
4. 右端にプルダウンメニューボタン (▼) が出るので、押してメニューを出す。
5. メニューの中の「参照. . . 」を選ぶ
6. ファイル選択ダイアログが出るので、C:/Program Files/BREW.../sdk/bin/BREW Simulator.exe
を選択する。
7. プロパティダイアログの OK ボタンを押して閉じる。
12.2
ブレークポイントの設定と実行
ここでは、前章までで制作したプログラム中にブレークポイントを置き、プログラムを実行
してブレークポイントで止めてみる。
1. ソリューションエクスプローラから test1.cpp をダブルクリックしてエディタを開く
2. イベントハンドラ内の
case EVT_KEY_PRESS:
app->game.key_pressed(wParam);
の key pressed(..) の位置で右クリックする。ポップアップメニューが出る。
3. メニュー内にブレークポイントの挿入があるので選択する。行の左側にブレークポイント
を示すマークが出る。
4. VC.NET のメニューのデバッグ >開始を選択する。
5. 「シンボル情報なし」というダイアログが出る。このダイアログは「シミュレータープロ
グラムにデバッグ情報がない」と言っているだけなので無視してよい。OK ボタンを押す。
6. シミュレーターが起動するので、test1 アプレットを実行する。
48
7. キーを押すと VC.NET にウィンドウが切り替わる。エディタ上のブレークポイントの位
置に「→」が表示され、プログラムがそこで停止していることを表示する。
この時点で、変数ウォッチウィンドウが VC.NET ウィンドウの下部に出ます。ウィンドウに
は、「自動変数」「ローカル」「ウォッチ」というタブがあり、変数の状況を見ることができる。
また、デバッグメニューの中には、
• 続行 (動作を継続する)
• ブレークポイントの操作 (解除等)
• ステップ実行
等の選択があり、様々なデバッグ操作ができる。
49
文献等
[1] KHRONOS group. EGL 1.1 - openGL ES Native Platform Graphics Interface 1.1 Specification(英語). http://www.khronos.org/opengles/spec.html.
[2] KHRONOS
group.
OpenGL
http://www.khronos.org/opengles/spec.html.
ES
1.1
Specification(英 語).
[3] Alan
Kemp.
An
introduction
to
BREW
and
opengl
http://www.gamedev.net/reference/programming/features/brewopengles/.
es(英 語).
[4] QUALCOMM. BREW(tm) API リファレンス (日本語). BREW SDK に同梱.
[5] QUALCOMM. BREW プログラミングの概念 (日本語). BREW SDK に同梱.
[6] QUALCOMM.
Creating a
http://brew.qualcomm.com/brew/.
BREW
Application
from
Scratch(英 語).
[7] QUALCOMM.
Creating BREW Applications Using Visual Studio .NET(英 語).
http://brew.qualcomm.com/brew/.
[8] QUALCOMM. OpenGL ES Extension API Reference(英語). BREW SDK Extension for
OpenGL ES に同梱.
[9] QUALCOMM. Starting with BREW(英語). http://brew.qualcomm.com/brew/.
50
Fly UP