threerings / playn

Legacy 1.x version of PlayN library.
http://playn.io/
Apache License 2.0
195 stars 66 forks source link

Bind the platform's lifecyle to the game controller #67

Closed tomfisher closed 9 years ago

tomfisher commented 9 years ago

Expose PlayN as a normal iOS view controller and bind the game lifecycle with it. This also makes both integration and customisation easier, which always happens in a view controller.

RoboGameController could be more accurate name than RoboRootViewController which may or may not be a root view controller now in the current context and sounds a little bit confusion. I did not change that for the sake of patch reviewing.

I'v tested with my application and also wrote a simple application demonstrating how to use PlayN as a standalone application or an embedded application. Probably it should be included in the play-smaples project.

package tripleplay.demo;

import org.robovm.apple.foundation.NSArray; import org.robovm.apple.foundation.NSAutoreleasePool; import org.robovm.apple.uikit.UIApplication; import org.robovm.apple.uikit.UIApplicationDelegateAdapter; import org.robovm.apple.uikit.UIApplicationLaunchOptions; import org.robovm.apple.uikit.UIBarButtonItem; import org.robovm.apple.uikit.UIBarButtonItem.OnClickListener; import org.robovm.apple.uikit.UIBarButtonSystemItem; import org.robovm.apple.uikit.UIButton; import org.robovm.apple.uikit.UIControl; import org.robovm.apple.uikit.UIControl.OnTouchUpInsideListener; import org.robovm.apple.uikit.UIControlState; import org.robovm.apple.uikit.UIEvent; import org.robovm.apple.uikit.UINavigationController; import org.robovm.apple.uikit.UIScreen; import org.robovm.apple.uikit.UIViewController; import org.robovm.apple.uikit.UIWindow;

import playn.core.CanvasImage; import playn.core.Color; import playn.core.Game; import playn.core.ImageLayer; import playn.core.PlayN; import playn.core.Pointer; import playn.core.Pointer.Event; import playn.robovm.RoboRootViewController; import tripleplay.demo.Main.GameDemo.Exit;

public class Main {

public static void main(String[] args) {
    NSAutoreleasePool pool = new NSAutoreleasePool();
    UIApplication.main(args, null, StandaloneApplication.class);
    // UIApplication.main(args, null, EmbeddedApplication.class);
    pool.close();
}

public static class StandaloneApplication extends UIApplicationDelegateAdapter {
    private UIWindow window;
    @Override
    public boolean didFinishLaunching(UIApplication app, UIApplicationLaunchOptions launchOpts) {
        window = new UIWindow(UIScreen.getMainScreen().getBounds());
        RoboRootViewController gameController = new RoboRootViewController();
        gameController.run(new GameDemo());
        // Setup our game controller as the root controller
        window.setRootViewController(gameController);
        window.makeKeyAndVisible();
        addStrongRef(window);
        return true;
    }
}

public static class EmbeddedApplication extends UIApplicationDelegateAdapter {
    private UIWindow window;

    @Override
    public boolean didFinishLaunching(UIApplication app, UIApplicationLaunchOptions launchOpts) {
        window = new UIWindow(UIScreen.getMainScreen().getBounds());
        UINavigationController root = new UINavigationController();
        root.addChildViewController(new WelcomeViewController());
        // Setup a navigation controller as the root controller to manage the application.
        window.setRootViewController(root);

        window.makeKeyAndVisible();
        addStrongRef(window);
        return true;
    }
}

public static class WelcomeViewController extends UIViewController {

    public WelcomeViewController() {
        super();
        UIButton button = new UIButton();
        button.setTitle("Run", UIControlState.Normal);
        button.addOnTouchUpInsideListener(new OnTouchUpInsideListener() {
            @Override public void onTouchUpInside(UIControl control, UIEvent event) { run();}
        });
        setView(button);
    }

    @Override
    public void viewWillAppear(boolean animated) {
        super.viewDidAppear(animated);
        UIBarButtonItem button = new UIBarButtonItem(UIBarButtonSystemItem.Bookmarks, new OnClickListener() {
            @Override public void onClick(UIBarButtonItem barButtonItem) { run();};
        });

        NSArray<UIBarButtonItem> items = new NSArray<UIBarButtonItem>(button);
        getNavigationItem().setRightBarButtonItems(items);
        getNavigationItem().setTitle("Welcome");
    }

    protected void run() {
        RoboRootViewController gameViewController = new RoboRootViewController();
        gameViewController.run(new GameDemo(new Exit() {
            @Override public void exit() { getNavigationController().popViewController(true);}
        }));
        getNavigationController().pushViewController(gameViewController, true);
    }

}

public static class GameDemo extends Game.Default {
    public static interface Exit {
        public void exit();
    }

    final private Exit service;

    public GameDemo() {
        this(null);
    }

    public GameDemo(Exit service) {
        super(25);
        this.service = service;
    }

    @Override
    public void init() {
        CanvasImage image = PlayN.graphics().createImage(100, 100);
        image.canvas().setFillColor(Color.rgb(255, 0, 0));
        image.canvas().fillRect(0, 0, image.width(), image.height());
        ImageLayer imageLayer = PlayN.graphics().createImageLayer(image);
        imageLayer.addListener(new Pointer.Adapter() {
            @Override
            public void onPointerEnd(Event event) {
                if (service != null) service.exit();
            }
        });
        PlayN.graphics().rootLayer().addAt(imageLayer, 50, 50);
    }
}

}

samskivert commented 9 years ago

FYI: I like this, and I'll merge it soon, but I want to be sure that the "normal" use case is as simple as it is today. So I need to do a bit of tidying, which I should have time for soon.

tomfisher commented 9 years ago

I am very glad to hear you will merge it soon since the project I am working on is highly dependent on PlayN. The second submission could be one of simplest solutions and just needs a few lines of integration code:

public class Main {
  public static class GameApplicationAdaptor extends RoboApplicationAdaptor{
    @Override protected Game createWith(Config config) {
      config.iPadLikePhone = true;
      return new Game.Default(30) {@Override public void init() {}};
    }
  }

  public static void main(String[] args) {
    NSAutoreleasePool pool = new NSAutoreleasePool();
    UIApplication.main(args, null, GameApplicationAdaptor.class);
    pool.close();
  }
}