Permalink
Browse files

Revamped Game interface, added Game.Default. Added PlayN.tick().

TL;DR: change "implements Game" to "extends Game.Default", delete your
updateRate() implementation and instead pass that value to Game.Default's
constructor.

Details:

- PlayN.tick() returns the number of ms that have elapsed since game startup
  (as an int, which is safe to use in JavaScript or otherwise).

- Game.update/paint/updateRate went away (sort of) to be replaced by Game.tick.
  A game that wants to handle updating and painting via some custom mechanism
  now just implements Game.tick and does their own simulation updating and
  scene graph interoplation.

  "Normal" games now extend Game.Default instead of implementing Game.
  Game.Default provides update(int delta) (which eliminates a pointless and
  problematic use of floats) and paint(float alpha). Game.updateRate goes away
  and is passed to Game.Default's constructor. You can no longer change your
  game's update rate on the fly, which was wacky.

  Game.Default implements the update/paint approach that was previously (sort
  of) mandated for all games. There is no longer the "updateRate = 0" hackery
  because games that want to do custom things just override tick() and don't
  have to second guess what the platform is doing.

  Game.Default.update differs from the old Game.update in that updates will be
  coalesced if the game is running slowly. See Game.Default.update's javadocs
  for details.

  Game.Default.paint differs from the old Game.paint in that alpha can be
  greater than one if your game is running slowly (but this is really the right
  thing to do and your code will probably just do the right thing if you're
  doing interpolation math normally).

  Game.Default's calculation of the 'alpha' value passed to paint is more
  accurate and should result in smoother display in games that actually use it
  to interpolate between simulation updates.
  • Loading branch information...
1 parent ee8283a commit 30ba79059f3c46d64caa11b469a8eebbe4ffa3bf @samskivert samskivert committed Mar 20, 2013
Showing with 257 additions and 367 deletions.
  1. +2 −4 android/src/playn/android/AndroidGraphics.java
  2. +8 −2 android/src/playn/android/AndroidPlatform.java
  3. +0 −101 android/src/playn/android/GameLoop.java
  4. +17 −17 android/src/playn/android/GameViewGL.java
  5. +6 −7 benchmark/core/src/playn/bench/core/Bench.java
  6. +85 −34 core/src/playn/core/Game.java
  7. +2 −0 core/src/playn/core/Platform.java
  8. +7 −0 core/src/playn/core/PlayN.java
  9. +6 −0 core/src/playn/core/StubPlatform.java
  10. +4 −9 core/src/playn/core/gl/GL20Context.java
  11. +9 −28 flash/src/playn/flash/FlashPlatform.java
  12. +1 −1 html/src/playn/html/HtmlGraphics.java
  13. +1 −2 html/src/playn/html/HtmlGraphicsCanvas.java
  14. +1 −2 html/src/playn/html/HtmlGraphicsDom.java
  15. +2 −8 html/src/playn/html/HtmlGraphicsGL.java
  16. +25 −29 html/src/playn/html/HtmlPlatform.java
  17. +9 −14 ios/src/playn/ios/IOSGLContext.java
  18. +12 −18 ios/src/playn/ios/IOSGameView.java
  19. +2 −4 ios/src/playn/ios/IOSGraphics.java
  20. +10 −20 ios/src/playn/ios/IOSPlatform.java
  21. +3 −5 java/src/playn/java/JavaGraphics.java
  22. +11 −23 java/src/playn/java/JavaPlatform.java
  23. +3 −3 tests/core/src/main/java/playn/tests/core/ClearBackgroundTest.java
  24. +2 −2 tests/core/src/main/java/playn/tests/core/ClippedGroupTest.java
  25. +4 −3 tests/core/src/main/java/playn/tests/core/ImageScalingTest.java
  26. +2 −2 tests/core/src/main/java/playn/tests/core/ImmediateTest.java
  27. +2 −2 tests/core/src/main/java/playn/tests/core/ShaderTest.java
  28. +3 −3 tests/core/src/main/java/playn/tests/core/SubImageTest.java
  29. +4 −6 tests/core/src/main/java/playn/tests/core/SurfaceTest.java
  30. +8 −11 tests/core/src/main/java/playn/tests/core/Test.java
  31. +6 −7 tests/core/src/main/java/playn/tests/core/TestsGame.java
@@ -206,10 +206,8 @@ protected SurfaceGL createSurface(float width, float height) {
return new AndroidSurfaceGL(platform.activity.getCacheDir(), ctx, width, height);
}
- void paint(Game game, float paintAlpha) {
- ctx.preparePaint(rootLayer);
- game.paint(paintAlpha); // run the game's custom painting code
- ctx.paintLayers(rootLayer); // paint the scene graph
+ void paint() {
+ ctx.paint(rootLayer);
}
IPoint transformTouch(float x, float y) {
@@ -47,6 +47,7 @@
private final AndroidStorage storage;
private final TouchImpl touch;
private final Json json;
+ private final long start = System.nanoTime();
protected AndroidPlatform(GameActivity activity, AndroidGL20 gl20) {
super(new AndroidLog(activity));
@@ -181,6 +182,11 @@ public double time() {
}
@Override
+ public int tick() {
+ return (int)((System.nanoTime() - start) / 1000000L);
+ }
+
+ @Override
public void setPropagateEvents(boolean propagate) {
touch.setPropagateEvents(propagate);
pointer.setPropagateEvents(propagate);
@@ -208,10 +214,10 @@ protected void onExit() {
super.onExit();
}
- void update(float delta) {
+ void update() {
runQueue.execute();
if (game != null) {
- game.update(delta);
+ game.tick(tick());
}
}
}
@@ -1,101 +0,0 @@
-/**
- * Copyright 2011 The PlayN Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package playn.android;
-
-import static playn.core.PlayN.log;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-public class GameLoop implements Runnable {
-
- private static final boolean LOG_FPS = false;
- private static final int MAX_DELTA = 100;
-
- private AtomicBoolean running = new AtomicBoolean();
- private final AndroidPlatform platform;
-
- private long timeOffset = System.currentTimeMillis();
-
- private int updateRate;
- private float accum;
- private int lastTime;
-
- private float totalTime;
- private int framesPainted;
-
- public GameLoop(AndroidPlatform platform) {
- this.platform = platform;
- }
-
- public boolean running() {
- return running.get();
- }
-
- public void start() {
- if (!running.get()) {
- AndroidPlatform.debugLog("Starting game loop");
- this.updateRate = platform.game.updateRate();
- running.set(true);
- }
- }
-
- public void pause() {
- AndroidPlatform.debugLog("Pausing game loop");
- running.set(false);
- }
-
- @Override
- public void run() {
- // The thread can be stopped between runs.
- if (!running.get())
- return;
-
- int now = time();
- float delta = now - lastTime;
- if (delta > MAX_DELTA)
- delta = MAX_DELTA;
- lastTime = now;
-
- if (updateRate == 0) {
- platform.update(delta);
- accum = 0;
- } else {
- accum += delta;
- while (accum >= updateRate) {
- platform.update(updateRate);
- accum -= updateRate;
- }
- }
-
- platform.graphics().paint(platform.game, (updateRate == 0) ? 0 : accum / updateRate);
-
- if (LOG_FPS) {
- totalTime += delta / 1000;
- framesPainted++;
- if (totalTime > 1) {
- log().info("FPS: " + framesPainted / totalTime);
- totalTime = framesPainted = 0;
- }
- }
- }
-
- private int time() {
- // System.nanoTime() would be better here, but it's busted on the HTC EVO
- // 2.3 update. Instead we use an offset from a known time to keep it within
- // int range.
- return (int) (System.currentTimeMillis() - timeOffset);
- }
-}
@@ -15,6 +15,7 @@
*/
package playn.android;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
@@ -25,11 +26,12 @@
private final AndroidPlatform platform;
private final AndroidGL20 gl20;
- private GameLoop loop;
+ private AtomicBoolean started = new AtomicBoolean(false);
+ private AtomicBoolean paused = new AtomicBoolean(true);
- public GameViewGL(Context context, AndroidPlatform platform, AndroidGL20 gl20) {
+ public GameViewGL(Context context, AndroidPlatform plat, AndroidGL20 gl20) {
super(context);
- this.platform = platform;
+ this.platform = plat;
this.gl20 = gl20;
setFocusable(true);
@@ -48,25 +50,25 @@ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GameViewGL.this.platform.graphics().onSizeChanged(width, height);
- // we defer the start of the game until we've received our initial surface size; thus we
- // create our game loop lazily, then initialize the game and start the loop
- if (loop == null)
+ // we defer the start of the game until we've received our initial surface size
+ if (!started.get())
startGame();
}
@Override
public void onDrawFrame(GL10 gl) {
- loop.run();
+ if (!paused.get()) {
+ platform.update();
+ platform.graphics().paint();
+ }
}
});
setRenderMode(RENDERMODE_CONTINUOUSLY);
}
@Override
public void onPause() {
- // in theory loop should never be null, but I saw a crash in the field, so somehow someone
- // managed to start and pause the app before the renderer was able to render a single frame
- if (loop != null)
- loop.pause();
+ // pause our game updates
+ paused.set(true);
// this is a terribly unfortunate hack; we would like to override surfaceDestroyed and indicate
// that our surface was lost only when that method was called, but surfaceDestroyed is not
// called when the screen is locked while our game is running (even though we do in fact lose
@@ -83,19 +85,17 @@ public void run () {
@Override
public void onResume() {
super.onResume();
- // the very first time we're resumed, our loop will still be null because we wait to create it
- // until after our UI has been laid out and we know the size of the game screen
- if (loop != null)
- loop.start();
+ // unpause our game updates
+ paused.set(false);
}
void startGame() {
- loop = new GameLoop(platform);
+ started.set(true);
queueEvent(new Runnable() {
@Override
public void run() {
platform.activity.main();
- loop.start();
+ paused.set(false);
}
});
}
@@ -20,7 +20,7 @@
import playn.core.Game;
import playn.core.GroupLayer;
-public class Bench implements Game {
+public class Bench extends Game.Default {
private static final TimeTest[] TESTS = new TimeTest[] {
new SurfaceTimeTest(),
@@ -31,13 +31,17 @@
private int curTextIndex = -1;
private TimeTest curTest;
+ public Bench () {
+ super(33);
+ }
+
@Override
public void init() {
nextTest();
}
@Override
- public void update(float delta) {
+ public void update(int delta) {
}
@Override
@@ -50,11 +54,6 @@ public void paint(float alpha) {
}
}
- @Override
- public int updateRate() {
- return 33;
- }
-
private void cleanup() {
if (curTest != null) {
curTest.cleanup();
Oops, something went wrong.

0 comments on commit 30ba790

Please sign in to comment.