皆さんは、ご自分のプログラムに迷惑な動作をさせて、ユーザーを困らせるのがお好きですか? 「はい」と答えた読者には、必ず今月号をお楽しみいただけると思います。J2SE1.4を使用することにより、Javaプログラムでビデオ・モードを変更し、画面を完全に制御できるようになりました。皆さん自身が文字通り完全な制御を握るのですから、他の誰も楽しく使用できるようにする必要はありません。この全能とも思えるパワーは、新しいFullscreenExclusive Mode (FEM) APIによってもたらされます。
「いいえ」と答えた、他人を困らせて楽しむようなことをしない読者も、FEM APIに多くのメリットがあることをご理解いただけると思います。FEMAPIは、ビデオRAMに直接書き込みを行うことによって表示の完全な制御を可能にするため、ゲーム開発に最適ですが、それ以外にも多くの使用法があります。たとえば、プログラムの中には、特定の画面サイズにおいてその見た目や動作が良くなるものがあります。この記事を読み進めながら、皆さんの心の内にある制御への強い希望に気付いてください。
それでは、まず始めにFEM APIの
java.awt.DislayMode
クラスを考察しましょう。このクラスは、特定の表示モードの画面寸法とリフレッシュ・レートをラップします。サポートされるモードは、システムのハードウェア・サポートによって決まります。
特定のシステムでサポートされる表示モードを知るには、
GraphicsEnvironment
を使用します。この環境からデフォルトの画面デバイスである
GraphicDevice
を取得し、この画面デバイスから表示モードを取得します。リスト1はこの手順を示しています。
リスト1. 表示モードの検索
GraphicsEnvironment graphicsEnvironment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice graphicsDevice =
graphicsEnvironment.getDefaultScreenDevice();
DisplayMode displayModes[] =
graphicsDevice.getDisplayModes();
|
また、リスト2に示すように、
getDisplayMode()
メソッドを使用しても現在の表示モードを知ることができます。
リスト2. 現在の表示モードの取得
DisplayMode originalDisplayMode =
graphicsDevice.getDisplayMode();
|
モードの変更は比較的簡単ですが、まず初めに
GraphicsDevice
の
isDisplayChangeSupported()
メソッドを使用して、関係するグラフィック・デバイスが変更をサポートしているかどうかを確認する必要があります。
それがわかったら、
setDisplayMode()
メソッドを使用してモードを変更し、新しいモードに移行します。表示モードの変更は、通常
try
/
finally
ブロックで発生し、
finally
ブロックではコードをリセットして元のモードに戻すようになっています。この処理は絶対に必要というわけではありませんが、これにより、プログラムの終了時に確実に安全な表示モードに戻すことができます。リスト3は、表示モードを変更する一般的なパターンを示しています。
リスト3. モードの変更
GraphicsDevice graphicsDevice = ...
DisplayMode originalDisplayMode = graphicsDevice.getDisplayMode();
DisplayMode newDisplayMode = ...
try {
if (graphicsDevice.isDisplayChangeSupported()) {
graphicsDevice.setDisplayMode(newDisplayMode);
}
} finally {
graphicsDevice.setDisplayMode(originalDisplayMode);
}
|
FEM APIを使用すると、リスト4のようにウィンドウを
GraphicsDevice
の
setFullScreenWindow()
メソッドに渡すだけで、簡単に全画面モードに切り替えることができます。非全画面モードに戻すときには、同じメソッドに
null
を渡します。もちろん、初めに
isFullScreenSupported()
メソッドを使って
GraphicsDevice
が全画面モードをサポートするかどうかを確認する必要があります。
リスト4. 全画面モードへの切り替え
GraphicsDevice graphicsDevice = ...
Window window = ...
try {
if (graphicsDevice.isFullScreenSupported()) {
graphicsDevice.setFullScreenWindow(window);
}
} finally {
graphicsDevice.setFullScreenWindow(null);
}
|
ここまでに学習したすべての内容を示すために、リスト5に完成された例を示します。リスト5のコードでは、表示モードを取得し、変更後の表示モードをランダムに選択してから、"Hello,World!" というテキストを表示した全画面ウィンドウを表示します。新しい表示モードの特性が表示され、ウィンドウの表示サイズ、リフレッシュ・レート、およびビット・デプスを知ることができます。
リスト5. モード変更の例
import java.awt.*;
import javax.swing.*;
import java.util.Random;
public class DisplayModes {
public static void main(String args[]) {
GraphicsEnvironment graphicsEnvironment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice graphicsDevice =
graphicsEnvironment.getDefaultScreenDevice();
DisplayMode displayModes[] = graphicsDevice.getDisplayModes();
DisplayMode originalDisplayMode = graphicsDevice.getDisplayMode();
JWindow window = new JWindow() {
public void paint(Graphics g) {
g.setColor(Color.blue);
g.drawString("Hello, World!", 50, 50);
}
};
try {
if (graphicsDevice.isFullScreenSupported()) {
graphicsDevice.setFullScreenWindow(window);
}
Random random = new Random();
int mode = random.nextInt(displayModes.length);
DisplayMode displayMode = displayModes[mode];
System.out.println(displayMode.getWidth() + "x" +
displayMode.getHeight() + " \t" + displayMode.getRefreshRate() +
" / " + displayMode.getBitDepth());
if (graphicsDevice.isDisplayChangeSupported()) {
graphicsDevice.setDisplayMode(displayMode);
}
Thread.sleep(1000);
} catch (InterruptedException e) {
} finally {
graphicsDevice.setDisplayMode(originalDisplayMode);
graphicsDevice.setFullScreenWindow(null);
}
System.exit(0);
}
}
|
リスト5で、全画面として使用されたウィンドウに、完全な機能を備えた
paint()
メソッドが含まれていることに注目してください。ただし、このウィンドウは全画面モードであるため、
paint()
メソッドが必要とするオーバーヘッドや、メソッドがクリッピングを処理する方法、およびその他の表示処理用の動作を考慮する必要はありません。実際に、標準的なパッシブ・レンダリングはやり過ぎであり、全画面アプリケーションの速度を遅くすることがわかっています。ユーザーは、オーバーラップやウィンドウのサイズ変更などのタスクを処理する必要はありません。代わりに、もっとアクティブな方法を使用して、レンダリングそのものを処理するタイト・ループを作成してください。
ダブル・バッファリングに詳しい皆さんは、この方法でメモリー内の2つの
Image
オブジェクトが管理され、どちらが現在の表示情報を持っているかに基づいてこの2つがスワップされることをご存知だと思います。一方の
Image
を表示している間にもう一方に描画を行い、各シーン間で2つの
Image
オブジェクトをスワップします。
グラフィックス・カードにもこれとよく似た概念が採用されていますが、グラフィックス・カードでは実際のJava
Image
オブジェクトを操作するのではなく、メモリー・ページをスワップします。この方法では、メモリー・ページをスワップすると表示が変更されるため、プログラムのメモリーからビデオ・メモリーに
Image
オブジェクトをコピーする必要はなく、ビデオ・ポインターを変更するだけで表示が変更されます。ダブル・バッファリングの概念はまだ健在ですが、プログラム空間で
Image
に書き込みを行う代わりに、ビデオ・メモリー空間に直接描画するようになっています。
BufferStrategy
クラスは、前述の2つのダブル・バッファリング手法のどちらが使用されるかについての情報をマスクし、システムが提供する任意のハードウェア・ベースのバッファリングを使用できるようにします。
BufferStrategy
を作成するには、
createBufferStrategy()
メソッドを使用して必要なバッファー数をシステムに知らせ、次に使用するバッファーを返す
getDrawGraphics()
メソッドを使用してバッファー間でスワップを行います。概念的にはこれが処理のすべてですが、リスト6に示すとおり、実際のコードにはもう少し工夫が必要です。
リスト6. BufferStrategyの使用
JFrame frame = ...
frame.createBufferStrategy(2); // Number of buffers to have
BufferStrategy bufferStrategy = frame.getBufferStrategy();
while (!done()) { // Some condition to end
Graphics g = null;
try {
g = bufferStrategy.getDrawGraphics();
drawNextScene(g); // Method to draw to } finally {
// Free resources
if (g != null) {
g.dispose();
}
}
bufferStrategy.show();
}
|
BufferStrategy
を使用するときには、最後にバッファーに対して描画した項目が有効に残っているとは限りません。そのため、
contentsLost()
メソッドを使用して、書き込んだ内容の有効性を確認しなければなりません。内容が失われていた場合は、バッファー全体を再描画する必要があります。バッファーの内容が残っていれば、最後に使用した時点からの変更分だけを描画すれば済みます。
BufferStrategy
を使用したバッファー・サポートに加えて、
BufferCapabilities
クラスの
getCapabilities()
メソッドを使用すると、全画面モードなど、その手法でサポートされる機能を知ることができます。
リスト7は、これまでに学んだすべての内容をまとめたサンプル・プログラムです。私にはあまり芸術的な才能がないので、洗練されたゲームを期待しないでいただけると助かります。しかし、これは、
BufferStrategy
と全画面描画モードを使用した完成された実施例です。サンプル・プログラムでは、現在のバッファーに描画されなかったものを記憶することによってバッファー間の同期をとるということを、意図的にしていません。これは、複数のバッファーが操作されていることをより明確に示すためです。このプログラムは、各描画処理に10分の1秒の遅延を持たせて画面に100個の矩形をランダムに描画します。
リスト7. 実施例
import java.awt.*;
import java.awt.image.*;
import javax.swing.*;
import java.util.Random;
public class MultipleBuffers {
public static void main(String args[]) {
GraphicsEnvironment graphicsEnvironment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice graphicsDevice =
graphicsEnvironment.getDefaultScreenDevice();
DisplayMode displayModes[] = graphicsDevice.getDisplayModes();
DisplayMode originalDisplayMode = graphicsDevice.getDisplayMode();
JFrame frame = new JFrame();
frame.setUndecorated(true);
frame.setIgnoreRepaint(true);
try {
if (graphicsDevice.isFullScreenSupported()) {
graphicsDevice.setFullScreenWindow(frame);
}
Random random = new Random();
int mode = random.nextInt(displayModes.length);
DisplayMode displayMode = displayModes[mode];
System.out.println(displayMode.getWidth() + "x" + displayMode.getHeight() +
" \t" + displayMode.getRefreshRate() +
" / " + displayMode.getBitDepth());
if (graphicsDevice.isDisplayChangeSupported()) {
graphicsDevice.setDisplayMode(displayMode);
}
frame.createBufferStrategy(2);
BufferStrategy bufferStrategy = frame.getBufferStrategy();
int width = frame.getWidth();
int height = frame.getHeight();
for (int i=0; i<100; i++) {
Graphics g = bufferStrategy.getDrawGraphics();
g.setColor(new Color(random.nextInt()));
g.fillRect(random.nextInt(width), random.nextInt(height), 100, 100);
bufferStrategy.show();
g.dispose();
Thread.sleep(100);
}
} catch (InterruptedException e) {
} finally {
graphicsDevice.setDisplayMode(originalDisplayMode);
graphicsDevice.setFullScreenWindow(null);
}
System.exit(0);
}
}
|
バッファー間の同期をとったり、バッファーの内容が失われた場合にバッファー全体を再描画する必要があるかどうかを確認するなどの機能強化を加えれば、このサンプル・プログラムはさらに良くなります。同期をとるには、最後に描画された矩形(とその色) を記憶しておく必要があるだけですが、バッファーの再描画の必要性を確認するには、描画された矩形をすべて記憶しておく必要があります。
新しいFullscreen Exclusive Mode APIの登場より、Javaによる開発はゲーム開発の主流になります。このAPIにより、DirectXやOpenGLなどのプラットフォーム固有のAPIを操作する必要がなくなり、すべてのJava対応プラットフォームで標準のAPIを使用できるようになります。これは、野心的なプログラミングの独裁者にも想像がつかないほどのすばらしい進歩です。
-
この記事に関するディスカッション・フォーラムにご参加ください (この記事の上部または下部にある
Discuss
をクリックしても、ディスカッション・フォーラムにアクセスできます)。
- John Zukowski氏のMerlinの魔術のコラムのアーカイブでは、J2SE 1.4についてのさまざまなヒントが得られます。
-
「
Retained Graphic Object API入門
」(developerWorks、2001年11月) では、Retained Graphic
Objects (RGO) APIについてBarry
Feigenbaum氏が詳しく説明しています。このAPIは、Javaプラットフォーム用の再利用可能なグラフィック・オブジェクトの作成を可能にするものです。
-
AlphaCompositeクラスは、デジタル・イメージ合成に関する12のPorter-Duffのルールのうち、これまでは8つしかサポートしていませんでしたが、12のルールすべてをサポートするようになりました。Merlinの魔術の初期の記事「
Porter-Duffのルール!
」(developerWorks、2001年9月) では、John Zukowski氏が
この12のルールをすべて説明し、インタラクティブなプログラムを使用してそのルールがどのように働いているかを示しています。
-
FEM APIの背景情報をさらに詳しくお知りになりたい方は、Sun Microsystemsの
New Fullscreen Exclusive Mode API
の資料をお読みください。
-
Bugtraq Report # 4189326
もご覧下さい (無料のログインが必要)。
-
DisplayModeクラスについてさらに学んでください。 -
GraphicsDeviceクラスについても、さらに詳細な情報があります。 -
Javaチュートリアルでは、
FEM APIの詳細情報
が提供されています。
-
Java technology zone
では、developerWorks
が提供するJavaプログラミングに関する多数の記事を参照することができます。

John Zukowskiは、JZ Ventures, Inc.の戦略的Javaコンサルティングを推進し、数多くのjGuruのコミュニティー主導のJava FAQsで常任指導者として活躍しています。最新の著書には、Apressから出版された「 Java Collections」および「 Definitive Guide to Swing for Java 2」 (第2版) があります。