One of the most highly sought after features requested by users was the ability to integrate a CocosSharp game within a Xamarin.Forms application. Specifically, users were after a way to incorporate Xamarin.Forms controls alongside an embeddable game view that does not necessarily occupy the entire screen.
Unfortunately, due to a variety of limitations imposed by our dependencies, prior to CocosSharp 1.7.0.0, such functionality was previously not possible. Hence, to realise our goal, we have had to start from the ground-up and substantially redesign how a CocosSharp game is initialised.
The end of CCApplication — introducing the new CCGameView
Users of CocosSharp will be familiar with CCApplication
's role in kick-starting their game. For example, on iOS, we would do the following:
class Program : NSApplicationDelegate
{
public override void DidFinishLaunching (MonoMac.Foundation.NSNotification notification)
{
CCApplication application = new CCApplication(false, new CCSize(1024f, 768f));
// GameAppDelegate is a subclass of CCApplicationDelegate, a container class for loading the game scene
application.ApplicationDelegate = new GameAppDelegate();
application.StartGame();
}
....
The problem implicit in this setup was that CCApplication
took full control over your application, insisting that your game view was not only full-screen but that it was the sole, root view displayed.
In CocosSharp 1.7.0.0, we have replaced CCApplication
with the new CCGameView
class that serves the dual role of both setting up your game as well as rendering your game content. Depending on the targeted platform, CCGameView
inherits from a corresponding native view class as showcased below:
The benefit of this design is that now a user simply treats CCGameView
as they would any other native view. In particular, that means that on iOS, a CCGameView
can now be specified within a .xib
, while on Android we can make use of our resource .axml
and similarly on WindowsPhone setup our view within a .xaml
. For instance, a simple Android layout resource file for an activity with a game view could be
< ?xml version="1.0" encoding="utf-8"? />
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<CocosSharp.CCGameView
android:id="@+id/GameView"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
Overall, we now have a much more natural and cohesive setup for your game, and importantly users can not only specify the dimensions of their view, but when their game view appears within their application.
Replacing CCApplicationDelegate with the ViewCreated event
As highlighted in the previous section, a CCApplication
instance was paired with a corresponding CCApplicaitonDelegate
that was in charge of loading your game content. For example,
public class GameAppDelegate : CCApplicationDelegate
{
public override void ApplicationDidFinishLaunching (CCApplication application, CCWindow mainWindow)
{
// Specify default world dimensions
CCScene.SetDefaultDesignResolution(1024.f, 768.f, CCSceneResolutionPolicy.ShowAll);
CCScene scene = new CCScene (mainWindow);
// Setup your scene
// ...
mainWindow.RunWithScene (scene);
}
}
With the introduction of the new CCGameView
we have removed this cumbersome approach of subclassing CCApplicationDelegate
, replacing it with the CCGameView
event ViewCreated
, which users hook up to in order to initialise their game content. So for example, on iOS, for a game view associated with a custom view controller we could do the following,
[Register ("ViewController")]
partial class ViewController
{
[Outlet]
CocosSharp.CCGameView GameView { get; set; }
...
and subsequently a user would then hook up to the ViewCreated
event
public partial class ViewController : UIViewController
{
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
if (GameView != null) {
// Set loading event to be called once game view is fully initialised
GameView.ViewCreated += LoadGame;
}
// Called once the game view has been fully initialised
void LoadGame (object sender, EventArgs e)
{
CCGameView gameView = sender as CCGameView;
if (gameView != null) {
// Set world dimensions
gameView.DesignResolution = new CCSizeI (1024, 768);
gameView.ContentManager.SearchPaths = new List () { "Fonts", "Sounds", "Images" };
CCScene gameScene = new CCScene (gameView);
gameScene.AddLayer (new GameLayer ());
gameView.RunWithScene (gameScene);
}
}
}
....
Similarly, for an Android app, connecting to ViewCreated
would be performed during the corresponding activity's initialisation,
public class MainActivity : Activity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
// Get our game view from the layout resource,
// and attach the view created event to it
CCGameView gameView = (CCGameView)FindViewById (Resource.Id.GameView);
if (gameView != null)
gameView.ViewCreated += LoadGame;
}
void LoadGame (object sender, EventArgs e)
{
// Same as above
}
....
while finally on WindowsPhone, a custom Page
with a xaml-specified game view would be setup within the constructor
public sealed partial class MainPage : Page
{
public MainPage ()
{
this.InitializeComponent ();
if (GameView != null)
GameView.ViewCreated += LoadGame;
}
void LoadGame (object sender, EventArgs e)
{
// Same as above
}
....
CocosSharpView for Xamarin.Forms
We have also introduced the new custom Xamarin.Forms View CocosSharpView
, whose custom view renderers are built on top of our CCGameView
stack as highlighted below
Initialising a CocosSharpView
is just as easy as creating any other Xamarin.Forms control:
public class GamePage : ContentPage
{
CocosSharpView gameView;
public GamePage ()
{
gameView = new CocosSharpView () {
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
// Set the game world dimensions
DesignResolution = new Size (1024, 768),
// Set the method to call once the view has been initialised
ViewCreated = LoadGame
};
Content = gameView;
}
void LoadGame (object sender, EventArgs e)
{
var nativeGameView = sender as CCGameView;
if (nativeGameView != null) {
// As in past examples
}
}
....
Limitations
Firstly, please be aware that for our initial prerelease, CocosSharp 1.7.0.0-pre1, we have reduced the scope of our package to only target iOS, Android or Windows Phone 8.1 projects. In subsequent releases, we will broaden the range of platforms supported.
More importantly, despite all the advances that are introduced by the new CCGameView
class, there are still some constraints imposed by our dependencies. In particular, while a user can create, destroy and subsequently recreate multiple instances of a CCGameView
, currently we do not support the ability to have multiple concurrent CCGameView
instances. In other words, for a given ViewController, Activity, Page etc., there can only be one CCGameView
active at any point in time.
Will I necessarily need Xamarin.Forms to make use of these new features?
No. To make it perfectly clear — CCGameView
is a native view implementation, meaning that you're free to incorporate this class within a native iOS, Android or WindowsPhone project.
For Xamarin.Forms users, the benefit of using the Forms-specific CocosSharpView
lies in the ability to specify all the UI elements of your game within a single, cross-platform project.
And remember...
as a prerelease we'll be constantly looking to fix and improve what is a substantial redesign — something that is only possible with your support and feedback.