どのタイプのゲームを作成するかは非常に重要です。 ゲームが動的な場合、デバイスの画像を常に更新する必要があります。 つまり、最初にオブジェクトの新しい座標を適用して作成し、それを画面に表示する必要があります。 私たちのビジョンは、0.04秒以内にそれを行うと、オブジェクトの動きが連続的に見えるように設計されています。 ただし、オブジェクトは描画の複雑さが異なる場合があり、プレイするデバイスの速度は異なる場合があります。
一部のタブレットや携帯電話では、アプリケーションが「飛ぶ」ため、ユーザーはプレイする時間すらありませんが、他のデバイスでは速度が低下し、ユーザーがデバイスから削除する可能性が高くなります。 すべてのデバイスで0.04秒(1秒あたり25フレーム(サイクル))で1ゲームサイクルを実行するというアイデアがあります。 すべてのデバイスで実行できれば、すべてうまくいきます。 ゲーム内に相互作用する10個の動的オブジェクトがあり、衝突の爆発などの新しいオブジェクトを作成するとします。 また、音を鳴らすことを忘れずに、ゲームへのユーザーの参加に対応してください。 私たちの周りの世界の現実的なグラフィックは言うまでもありません。
一部のシーンでゲームサイクルを作成する時間がデバイスにない場合はどうすればよいですか? 解決策はいくつかありますが、そのうちの1つを以下で検討します。
ゲームループを作成する
主なアイデアは、フレームを描画するためのリアルタイムがフレームを更新するための推定時間よりも長い場合(つまり、システムに時間がない場合)、このフレームを犠牲にして次のフレームを描画するための時間を増やすということです。 システムがすぐにフレームを描画することに成功した場合、可能な限り迅速にそれを行いますが、フローはリードタイムの間中断され、フレーム数、つまりプログラム実行速度が設定され、デバイス自体の速度に依存しません。
このアプローチにより、デバイスのエネルギー消費を削減し、異なるデバイスでほぼ同じゲームをプレイできます。 毎秒25フレームのリフレッシュレートで、Android 2.2.1を使用して携帯電話上のオブジェクトが不快にぴくぴく動くことに気付きました(特に高速で移動する場合)が、毎秒30フレームではすべてがうまくいきました。 Android 4.2.2を搭載したタブレットでは毎秒50フレームで、すべてが完全に機能しましたが、電話では、特にオブジェクトの高速で、顕著なフレームドロップが発生することがありました。
コードをさらに詳しく考えてみましょう。
Threadクラスから継承するMainThreadクラスを作成します。 将来的には、GameViewゲームクラスで呼び出します。
public class MainThread extends Thread {
public class MainThread extends Thread {
1秒あたりのフレーム数(MAX_FPS)を30に設定します。
private final static int MAX_FPS = 30;
ディスプレイで犠牲にする(スキップする)ことのできる1秒あたりの最大フレーム数を4とします。この数は実際に私が実験的に見つけたもので、これらの数で試してみてください。
private final static int MAX_FRAME_SKIPS = 4;
表示フレームの期間をミリ秒で計算します。
private final static int FRAME_PERIOD = 1000 / MAX_FPS;
オブジェクトが描画されるサーフェスを作成するメソッドを宣言します。
private SurfaceHolder surfaceHolder;
オブジェクトを描画し、その座標を更新するクラスを宣言します。
private GameView gameView;
ゲーム状態変数を宣言し、trueの場合、ストリームが再生されます。
private boolean running;
クラスコンストラクターを作成します。
public MainThread(SurfaceHolder surfaceHolder, GameView gameView) { super(); this.surfaceHolder = surfaceHolder; this.gameView = gameView; } @ Override public void run() { Canvas canvas;</code> long beginTime;
それでは、GameViewクラスの機能を詳しく見ていきましょう。
public class GameView extends SurfaceView implements SurfaceHolder.Callback { private final Drawable mAsteroid; private int widthAsteroid; private int heightAsteroid; private int leftAsteroid; private int xAsteroid1 = 30; private int rightAsteroid; private int topAsteroid; private int yAsteroid = -30; private int bottomAsteroid; private int centerAsteroid; private int height; private int width; private int speedAsteroid = 5; private int xAsteroid; private MainThread thread; public GameView(Context context) { super(context);
このメソッドを使用すると、たとえば画面を回転させるときに描画面を変更できます。
@Override public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) { }
ペイント用の表面を作成するメソッド。
@Override public void surfaceCreated(SurfaceHolder holder) { thread.setRunning(true); thread.start(); }
このメソッドは、フローが停止した場合に描画されるサーフェスを削除します。
@Override public void surfaceDestroyed(SurfaceHolder holder) { thread.setRunning(false); boolean retry = true; while (retry) { try { thread.join(); retry = false; } catch (InterruptedException e) {
このメソッドでは、画面の表面にオブジェクトを描画します。
public void render(Canvas canvas) {
濃い青色の背景を作成します。
canvas.drawColor(Color.argb(255, 2, 19, 151));
後で画面サイズに応じてオブジェクトのサイズを拡大縮小するために、デバイスの画面の高さと幅を調べます。
height = canvas.getHeight(); width = canvas.getWidth();
小惑星の主要な寸法(写真の幅と高さ)を設定します。 左端をXに沿った座標にアタッチし、画像の上部をYに沿った座標にアタッチします。したがって、画像の左上隅の座標を決定しました。 これらの座標を変更すると、画像が動き始めます。 png形式の小惑星の描画を描画可能フォルダに入れることを忘れないでください。
画像の座標を更新し、上から下に強制的に移動します。 画像の左上隅のY座標が画面の高さよりも大きくなった場合、Yをゼロにすると、小惑星が画面の上部にジャンプします。 小惑星を飛行させるために、座標にspeedAsteroidの数値を追加します。これは、5から15までランダムに決定されます(speedAsteroid = 5+ rnd.nextInt(10);)。
public void update() { if (yAsteroid > height) { yAsteroid = 0;
小惑星が毎回同じ場所から飛び出さないように、X座標は画面幅から小惑星の幅を引いた範囲内でランダムな法則に従って決定されます。 アプリケーションの起動時に、変数private int xAsteroid = 30;を宣言したときに指定した初期座標で小惑星がクラッシュします。 private int yAsteroid = -30;。 将来、飛行速度と初期座標はランダムに変化します。
現在のアプリケーションファイル
AhdroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.adc2017gmail.moonbase" > <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" android:screenOrientation="portrait" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".SecondActivity" android:label="@string/title_activity_second" android:screenOrientation="portrait" > </activity> </application> </manifest>
Gameview.java package com.adc2017gmail.moonbase; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.view.SurfaceHolder; import android.view.SurfaceView; import java.util.Random; public class GameView extends SurfaceView implements SurfaceHolder.Callback { private final Drawable mAsteroid; private int widthAsteroid; private int heightAsteroid; private int leftAsteroid; private int rightAsteroid; private int topAsteroid; private int yAsteroid = -30; private int bottomAsteroid; private int centerAsteroid; private int height; private int width; private int speedAsteroid = 5; private int xAsteroid = 30; private MainThread thread; public GameView(Context context) { super(context);
MainThread.java package com.adc2017gmail.moonbase; import android.graphics.Canvas; import android.view.SurfaceHolder; public class MainThread extends Thread { private final static int MAX_FPS = 30;
MainActivity.java package com.adc2017gmail.moonbase; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.MenuItem; import android.view.View; import android.widget.ImageButton; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final ImageButton imgbtn8 = (ImageButton)findViewById(R.id.image_button8); imgbtn8.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { imgbtn8.setImageResource(R.drawable.btn2); Intent intent8 = new Intent(MainActivity.this, SecondActivity.class); startActivity(intent8); } }); } @Override public boolean onOptionsItemSelected(MenuItem item) {
SecondActivity.java package com.adc2017gmail.moonbase; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; public class SecondActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new GameView(this)); } @Override public boolean onCreateOptionsMenu(Menu menu) {