ゲームのロジック (オブジェクトの動きなど) の更新レートをフレームレートに基づいて設定しないでください。つまり、描画とロジック更新コードを 2 つの別個のコンポーネント/スレッドに配置します。このようにして、ゲーム ロジックはフレームレートから完全に独立しています。
ロジックの更新は、最後の更新からの経過時間に基づいている必要があります ( と呼びましょうdelta
)。したがって、1 ピクセル/ミリ秒で移動するオブジェクトがある場合、各更新中にオブジェクトは次のように動作する必要があります。
public void update(int delta) {
this.x += this.speed * delta;
}
したがって、FPS が遅れても、オブジェクトの移動速度には影響しません。これは、デルタが大きくなり、補正のためにオブジェクトがより遠くに移動するためです (場合によっては複雑になりますが、それが要点です)。
これは、ロジック更新オブジェクト内でデルタを計算する 1 つの方法です (いくつかのスレッド ループで実行されます)。
private long lastUpdateTime;
private long currentTime;
public void update() {
currentTime = System.currentTimeMillis();
int delta = (int) (currentTime - lastUpdateTime);
lastUpdateTime = currentTime;
myGameObject.update(delta); // This would call something like the update method above.
}
それが役立つことを願っています! 他にご不明な点がございましたらお問い合わせください。私は自分でAndroidゲームを作っています。:)
サンプルコード:
これら 2 つのスニペット (1 つのアクティビティと 1 つのビュー) をコピーして、コードを実行します。その結果、FPS に関係なく、白い点が画面に滑らかに落ちるはずです。コードはやや複雑で長いように見えますが、実際には非常に単純です。コメントはすべてを説明する必要があります。
この活動クラスはあまり重要ではありません。その中のほとんどのコードは無視できます。
public class TestActivity extends Activity {
private TestView view;
public void onCreate(Bundle savedInstanceState) {
// These lines just add the view we're using.
super.onCreate(savedInstanceState);
setContentView(R.layout.randomimage);
RelativeLayout rl = (RelativeLayout) findViewById(R.id.relative_layout);
view = new TestView(this);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
10000, 10000);
rl.addView(view, params);
// This starts our view's logic thread
view.startMyLogicThread();
}
public void onPause() {
super.onPause();
// When our activity pauses, we want our view to stop updating its logic.
// This prevents your application from running in the background, which eats up the battery.
view.setActive(false);
}
}
このクラスはエキサイティングなものがある場所です!
public class TestView extends View {
// Of course, this stuff should be in its own object, but just for this example..
private float position; // Where our dot is
private float velocity; // How fast the dot's moving
private Paint p; // Used during onDraw()
private boolean active; // If our logic is still active
public TestView(Context context) {
super(context);
// Set some initial arbitrary values
position = 10f;
velocity = .05f;
p = new Paint();
p.setColor(Color.WHITE);
active = true;
}
// We draw everything here. This is by default in its own thread (the UI thread).
// Let's just call this thread THREAD_A.
public void onDraw(Canvas c) {
c.drawCircle(150, position, 1, p);
}
// This just updates our position based on a delta that's given.
public void update(int delta) {
position += delta * velocity;
postInvalidate(); // Tells our view to redraw itself, since our position changed.
}
// The important part!
// This starts another thread (let's call this THREAD_B). THREAD_B will run completely
// independent from THREAD_A (above); therefore, FPS changes will not affect how
// our velocity increases our position.
public void startMyLogicThread() {
new Thread() {
public void run() {
// Store the current time values.
long time1 = System.currentTimeMillis();
long time2;
// Once active is false, this loop (and thread) terminates.
while (active) {
try {
// This is your target delta. 25ms = 40fps
Thread.sleep(25);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
time2 = System.currentTimeMillis(); // Get current time
int delta = (int) (time2 - time1); // Calculate how long it's been since last update
update(delta); // Call update with our delta
time1 = time2; // Update our time variables.
}
}
}.start(); // Start THREAD_B
}
// Method that's called by the activity
public void setActive(boolean active) {
this.active = active;
}
}