Javaにおけるゲームループパターン:スムーズなゲームメカニズムの習得
別名
- ゲームサイクル
- メインゲームループ
ゲームループデザインパターンの目的
ゲームループデザインパターンは、ゲームを継続的に実行することで、スムーズでインタラクティブなゲーム体験を作成するために不可欠です。各ループサイクルは入力を処理し、ゲームの状態を更新し、ゲームの状態を画面にレンダリングすることで、すべてのハードウェア設定で一貫したパフォーマンスを確保します。
実際の例を用いたゲームループパターンの詳細な説明
実世界の例
ゲームループの実際的な例えとして、ジェットコースターのような遊園地の乗り物が挙げられます。乗り物がループ内で動作し、状態を更新してスムーズな動作を確保するのと同じように、ゲームループは継続的に入力を処理し、ゲームの状態を更新してシームレスなゲーム体験を実現します。ジェットコースターは連続したループで動作し、乗り物の状態(コースターの位置と速度)は、乗り物が稼働している間継続的に更新されます。ジェットコースターの制御システムは、車が軌道に沿ってスムーズに移動し、速度を調整し、乗り物の安全システムをリアルタイムで処理することを保証します。ゲームループと同様に、この制御システムは、乗り物の duration 全体にわたって目的の動作を維持するために、入力(現在の速度や位置など)を繰り返し処理し、状態を更新し、出力(ブレーキの調整や車の加速など)をトリガーします。
簡単な言葉で
ゲームループパターンは、ゲーム時間がすべての異なるハードウェア設定で同じ速度で進行することを保証します。
Wikipediaによると
プログラミングの観点から見ると、あらゆるゲームの中心的なコンポーネントはゲームループです。ゲームループにより、ユーザーの入力の有無にかかわらず、ゲームはスムーズに実行できます。
Javaにおけるゲームループパターンのプログラム例
Javaの例では、弾丸の動きを制御し、その位置を更新し、スムーズなレンダリングを保証し、ユーザー入力に応答する単純なゲームループを示します。ゲームループは、すべての最新のゲームに存在するすべてのゲームレンダリングスレッドを駆動する主要なプロセスです。入力処理、内部状態の更新、レンダリング、AI、およびその他のプロセスを処理します。単純な `Bullet` クラスから始めて、ゲームにおける弾丸の動きを示し、デモンストレーションの目的で1次元位置に焦点を当てます。
public class Bullet {
private float position;
public Bullet() {
position = 0.0f;
}
public float getPosition() {
return position;
}
public void setPosition(float position) {
this.position = position;
}
}
`GameController` は、前述の弾丸を含む、ゲーム内のオブジェクトの移動を担当します。
public class GameController {
protected final Bullet bullet;
public GameController() {
bullet = new Bullet();
}
public void moveBullet(float offset) {
var currentPosition = bullet.getPosition();
bullet.setPosition(currentPosition + offset);
}
public float getBulletPosition() {
return bullet.getPosition();
}
}
ここでゲームループを紹介します。実際、このデモでは3つの異なるゲームループがあります。最初に基本クラス `GameLoop` を見てみましょう。
public enum GameStatus {
RUNNING, STOPPED
}
public abstract class GameLoop {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
protected volatile GameStatus status;
protected GameController controller;
private Thread gameThread;
public GameLoop() {
controller = new GameController();
status = GameStatus.STOPPED;
}
public void run() {
status = GameStatus.RUNNING;
gameThread = new Thread(this::processGameLoop);
gameThread.start();
}
public void stop() {
status = GameStatus.STOPPED;
}
public boolean isGameRunning() {
return status == GameStatus.RUNNING;
}
protected void processInput() {
try {
var lag = new Random().nextInt(200) + 50;
Thread.sleep(lag);
} catch (InterruptedException e) {
logger.error(e.getMessage());
}
}
protected void render() {
var position = controller.getBulletPosition();
logger.info("Current bullet position: " + position);
}
protected abstract void processGameLoop();
}
最初のゲームループの実装である `FrameBasedGameLoop` を次に示します。
public class FrameBasedGameLoop extends GameLoop {
@Override
protected void processGameLoop() {
while (isGameRunning()) {
processInput();
update();
render();
}
}
protected void update() {
controller.moveBullet(0.5f);
}
}
2番目のゲームループの実装である `FixedStepGameLoop` を次に示します。
public class FixedStepGameLoop extends GameLoop {
/**
* 20 ms per frame = 50 FPS.
*/
private static final long MS_PER_FRAME = 20;
@Override
protected void processGameLoop() {
var previousTime = System.currentTimeMillis();
var lag = 0L;
while (isGameRunning()) {
var currentTime = System.currentTimeMillis();
var elapsedTime = currentTime - previousTime;
previousTime = currentTime;
lag += elapsedTime;
processInput();
while (lag >= MS_PER_FRAME) {
update();
lag -= MS_PER_FRAME;
}
render();
}
}
protected void update() {
controller.moveBullet(0.5f * MS_PER_FRAME / 1000);
}
}
そして3番目のゲームループの実装である `VariableStepGameLoop` です。
public class VariableStepGameLoop extends GameLoop {
@Override
protected void processGameLoop() {
var lastFrameTime = System.currentTimeMillis();
while (isGameRunning()) {
processInput();
var currentFrameTime = System.currentTimeMillis();
var elapsedTime = currentFrameTime - lastFrameTime;
update(elapsedTime);
lastFrameTime = currentFrameTime;
render();
}
}
protected void update(Long elapsedTime) {
controller.moveBullet(0.5f * elapsedTime / 1000);
}
}
最後に、すべてのゲームループの動作を示します。
public static void main(String[] args) {
try {
LOGGER.info("Start frame-based game loop:");
var frameBasedGameLoop = new FrameBasedGameLoop();
frameBasedGameLoop.run();
Thread.sleep(GAME_LOOP_DURATION_TIME);
frameBasedGameLoop.stop();
LOGGER.info("Stop frame-based game loop.");
LOGGER.info("Start variable-step game loop:");
var variableStepGameLoop = new VariableStepGameLoop();
variableStepGameLoop.run();
Thread.sleep(GAME_LOOP_DURATION_TIME);
variableStepGameLoop.stop();
LOGGER.info("Stop variable-step game loop.");
LOGGER.info("Start fixed-step game loop:");
var fixedStepGameLoop = new FixedStepGameLoop();
fixedStepGameLoop.run();
Thread.sleep(GAME_LOOP_DURATION_TIME);
fixedStepGameLoop.stop();
LOGGER.info("Stop variable-step game loop.");
} catch (InterruptedException e) {
LOGGER.error(e.getMessage());
}
}
プログラム出力
Start frame-based game loop:
Current bullet position: 0.5
Current bullet position: 1.0
Current bullet position: 1.5
Current bullet position: 2.0
Current bullet position: 2.5
Current bullet position: 3.0
Current bullet position: 3.5
Current bullet position: 4.0
Current bullet position: 4.5
Current bullet position: 5.0
Current bullet position: 5.5
Current bullet position: 6.0
Stop frame-based game loop.
Start variable-step game loop:
Current bullet position: 6.5
Current bullet position: 0.038
Current bullet position: 0.084
Current bullet position: 0.145
Current bullet position: 0.1805
Current bullet position: 0.28
Current bullet position: 0.32
Current bullet position: 0.42549998
Current bullet position: 0.52849996
Current bullet position: 0.57799995
Current bullet position: 0.63199997
Current bullet position: 0.672
Current bullet position: 0.778
Current bullet position: 0.848
Current bullet position: 0.8955
Current bullet position: 0.9635
Stop variable-step game loop.
Start fixed-step game loop:
Current bullet position: 0.0
Current bullet position: 1.086
Current bullet position: 0.059999995
Current bullet position: 0.12999998
Current bullet position: 0.24000004
Current bullet position: 0.33999994
Current bullet position: 0.36999992
Current bullet position: 0.43999985
Current bullet position: 0.5399998
Current bullet position: 0.65999967
Current bullet position: 0.68999964
Current bullet position: 0.7299996
Current bullet position: 0.79999954
Current bullet position: 0.89999944
Current bullet position: 0.98999935
Stop variable-step game loop.
Javaでゲームループパターンを使用する場合
ゲームループパターンは、継続的な状態の更新とスムーズなフレームレートが重要なリアルタイムシミュレーションとゲームに最適です。
Javaにおけるゲームループパターンの実際のアプリケーション
- さまざまなプラットフォームにわたる2Dおよび3Dのビデオゲーム。
- ロジックとレンダリングの更新に安定したフレームレートを必要とするリアルタイムシミュレーション。
ゲームループパターンの利点とトレードオフ
利点
- ゲームがスムーズかつ確実に進行することを保証します。
- ゲームの状態、ユーザー入力、および画面レンダリング間の同期を促進します。
- ゲーム開発者がゲームのダイナミクスとタイミングを管理するための明確な構造を提供します。
トレードオフ
- ループが適切に管理されていない場合、特にリソースを大量に消費する更新やレンダリングでは、パフォーマンスの問題が発生する可能性があります。
- 異なるハードウェア間で変化するフレームレートの管理が難しい。
関連するJavaデザインパターン
- 状態:ゲームループ内で、ゲームのさまざまな状態(例:メニュー、プレイ中、一時停止)を管理するためによく使用されます。関係性は、状態固有の動作を管理し、ゲームループ内でスムーズに移行することです。
- オブザーバー:ゲームエンティティがイベント(例:衝突、スコアリング)をサブスクライブして反応できるイベント処理にゲームループで役立ちます。