/** * Copyright 2010 The ForPlay 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 forplay.java; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.io.IOException; import javax.swing.JComponent; import javax.swing.JFrame; import forplay.core.Analytics; import forplay.core.Audio; import forplay.core.ForPlay; import forplay.core.Game; import forplay.core.Json; import forplay.core.Keyboard; import forplay.core.Log; import forplay.core.Net; import forplay.core.Platform; import forplay.core.Pointer; import forplay.core.Mouse; import forplay.core.Touch; import forplay.core.Storage; import forplay.core.RegularExpression; public class JavaPlatform implements Platform { // Maximum delta time to consider between update() calls (in milliseconds). If the delta between // two update()s is greater than MAX_DELTA, we clamp to MAX_DELTA. private static final float MAX_DELTA = 100; // Minimum time between any two paint() calls (in milliseconds). We will paint every // FRAME_TIME ms, which is equivalent to (1000 * 1 / FRAME_TIME) frames per second. // TODO(pdr): this is set ridiculously low because we're using Java's software renderer which // causes the paint loop to be quite slow. Setting this to 10 prevents hitching that occurs when // we try to squeeze a paint() near max bound of FRAME_TIME. private static final float FRAME_TIME = 10; public static JavaPlatform register() { JavaPlatform platform = new JavaPlatform(); ForPlay.setPlatform(platform); platform.init(); return platform; } private JComponent component; private JFrame frame; private Game game; private JavaRegularExpression regularExpression = new JavaRegularExpression(); private JavaAudio audio = new JavaAudio(); private JavaGraphics graphics; private JavaJson json = new JavaJson(); private JavaKeyboard keyboard; private JavaLog log = new JavaLog(); private JavaNet net = new JavaNet(); private JavaPointer pointer; private JavaMouse mouse; private JavaStorage storage = new JavaStorage(); private JavaAssetManager assetManager = new JavaAssetManager(); private int updateRate = 0; private Analytics analytics = new JavaAnalytics(); private JavaPlatform() { ensureFrame(); graphics = new JavaGraphics(frame, component); keyboard = new JavaKeyboard(frame); pointer = new JavaPointer(component); mouse = new JavaMouse(component); } private void init () { storage.init(); } @Override public Audio audio() { return audio; } @Override public JavaGraphics graphics() { return graphics; } @Override public Json json() { return json; } @Override public Keyboard keyboard() { return keyboard; } @Override public Log log() { return log; } @Override public Net net() { return net; } @Override public Pointer pointer() { return pointer; } @Override public Mouse mouse() { return mouse; } @Override public Touch touch() { // TODO(pdr): need to implement this. throw new UnsupportedOperationException("Touch is not yet supported on the Java platform"); } @Override public Storage storage() { return storage; } @Override public Analytics analytics() { return analytics; } @Override public JavaAssetManager assetManager() { return assetManager; } @Override public float random() { return (float) Math.random(); } @Override public void run(final Game game) { this.updateRate = game.updateRate(); this.game = game; game.init(); frame.setVisible(true); } @Override public double time() { return System.currentTimeMillis(); } private void ensureFrame() { frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); component = new JComponent() { private float accum = updateRate; private double lastUpdateTime; private double lastPaintTime; private boolean isPaintDirty; @Override public void paint(Graphics g) { isPaintDirty = false; // clean by default if (game != null) { double now = time(); float updateDelta = (float)(now - lastUpdateTime); if (updateDelta > 1) { updateDelta = updateDelta > MAX_DELTA ? MAX_DELTA : updateDelta; lastUpdateTime = now; if (updateRate == 0) { game.update(updateDelta); accum = 0; isPaintDirty = true; // we made a mess } else { accum += updateDelta; while (accum > updateRate) { game.update(updateRate); accum -= updateRate; isPaintDirty = true; // we made a mess } } } float paintDelta = (float)(now - lastPaintTime); if (isPaintDirty || paintDelta > FRAME_TIME) { if (updateRate == 0) { game.paint(0); } else { game.paint(accum / updateRate); } int width = component.getWidth(); int height = component.getHeight(); JavaCanvas canvas = new JavaCanvas((Graphics2D) g, width, height); graphics.rootLayer().paint(canvas); lastPaintTime = now; } } try { Thread.sleep(1L); } catch (InterruptedException e) { // ignore } repaint(); } }; component.setOpaque(true); // ensures graphics context is not cleared automatically frame.add(component); frame.setResizable(false); component.setPreferredSize(new Dimension(640, 480)); frame.pack(); } @Override public RegularExpression regularExpression() { return regularExpression; } @Override public void openURL(String url) { System.out.println("Opening url: " + url); String browser = "chrome "; if (System.getProperty("os.name", "-").contains("indows")) browser = "rundll32 url.dll,FileProtocolHandler "; try { Runtime.getRuntime().exec(browser + url); } catch (IOException e) { } } /** * Sets the title of the window. * * @param title the window title */ public void setTitle(String title) { frame.setTitle(title); } }