3D Mac / iOS App

This page goes through how to build the 3D game using both Mac OS/X for the Apple Mac Desktop and iOS (iPhone, iPad)

Create a new project in Xcode. Select the iOS Game first.

This image has an empty alt attribute; its file name is 3dmacgame1-1024x740.png

Type the name of the game. I logged into my Apple account under Team. Select Objective-C and Metal.

This image has an empty alt attribute; its file name is 3dmacgame2-1024x742.png

Select the location for your project

This image has an empty alt attribute; its file name is 3dmacgame3-1024x567.png

Click on the Blue Blindfate iOS and add a new Target by clicking on the plus sign underneath “TARGETS”.

This image has an empty alt attribute; its file name is 3dmacgame4.png

Select the macOS Game this time:

This image has an empty alt attribute; its file name is 3dmacgame5-1024x729.png

Pick a name – this time with MacOS

This image has an empty alt attribute; its file name is 3dmacgame6-1024x734.png

Select the MacOS version, and press the Play button

This image has an empty alt attribute; its file name is 3dmacgame7.png

You will see a rotating coloured cube as below

This image has an empty alt attribute; its file name is 3dmacgame8-1024x727.png

Now select the iOS app and your device of your choice via a Simulator. You can also build it directly to your phone or even iPad (you will need to give your app permission to run under General -> Settings -> Device Management -> Apple Development)

This image has an empty alt attribute; its file name is 3dmacgame9.png

You will see the 3D rotating cube on the iOS device:

This image has an empty alt attribute; its file name is 3dmacgame10-169x300.png

Apple iOS & MacOS Code Base

The documentation of Metal is here. This is a game template that that provides a starting point for our game. We will then modify it.

Note that the template has code for a 3D spinning cube.

My intention is to look at the sample code for metal. In particular, I will make use of the sample code project from “Modern Rendering with Metal“. This sample is written using Objective C.

A screenshot from the sample code is below. You can move around with the W,S,A,D keys and rotate around with your mouse.

This image has an empty alt attribute; its file name is metalsample-1024x523.png

Some metal tutorials are here as well as tutorial videos on Metal with iOS.

Setting up the App

Documentation on setting up an app for iOS and MacOS is via the apple developer documentation. To develop for iOS, you use UIKit and for MacOS, you use AppKit. This documentation talks about Swift, but you can translate it to Objective C. Information on using Objective C is here plus a good Objective C Tutorial is here.

Objects in Objective C are derived from NSObject (or other built it objects) and then classes can be created using @interface, then you implement methods using @implementation and access public values using @property – hence this is a little bit different in syntax to using the C++ class{}, although I have seen @class used as well (see the sample above). But Objective C does work pretty similar to C++.

If you look at the code, the iOS app is separate from the MacOS app. Also some objects for the iOS app are named differently to the MacOS app.

The file main.m (desktop) and main.m (iOS) runs the main window for the app. It returns UIApplicationMain for iOS and NSApplicationMain for MacOS.

For iOS, UIApplication is the iOS app and for MacOS, NSApplication is the MacOS app. Built into the application is an event handler which runs in a loop – this will be also our game loop.

The files AppDelegate.h and AppDelegate.m (desktop) and AppDelegate.h and AppDelegate.m (iOS) contains the code to handle events for the open window of the app whether it’s on the computer monitor or phone. For example, for iOS, you can check to see if the app entered the foreground or background. For MacOS, you can check if the user terminated or quit the app.

The files GameViewController.h and GameViewController.m (desktop) and GameViewController.h and GameViewController.m (iOS) contains the code that controls what you see on the app itself (the screen part of the app). What you see is called the view – iOS has UIViewController and MacOS has NSViewController, and the object GameViewController is created based on these. The view also calls the renderer to render objects on the screen (the 3D cube).

Setting up Metal and Rendering Graphics

Metal seems to have a similar structure and attributes as DirectX12. The initialisation of Metal and rendering are done in an object created called Renderer.

The files Renderer.h and Renderer.m (desktop) and Renderer.h and Renderer.m (iOS) contain the code to render the graphics to the app window or screen and the files ShaderTypes.h and Shaders.metal contain the shaders.

In Renderer.h, the object or class Renderer calls the initWithMetalKitView method which points to the view (or app window). Behind the scenes with metal kit, there is a game timer and allows updates to be made per frame.

The implementation of the Renderer object is in Renderer.m

The steps to setup metal and render graphics is initialising some variables to use for setting up metal and then using initWithMetalKitView to

  • create the view device
  • create a semaphore (thread) to initialise buffers (memory)
  • load metal with the view via the method _loadMetalWithView
  • load the graphics assets

The method _loadMetalWithView does the following:

  • setup the view with defining the pixel formats (colour and depth stencil)
  • setup vertex descriptors
  • setup the graphics pipeline states and create the pipeline
  • setup the Depth Stencil
  • create the back buffer
  • create a command queue

The method _loadAssets does the following:

  • create a mesh buffer
  • create the mesh – in this case, it’s a 3D box using the function newBoxWithDimensions()
  • load vertex descriptors
  • create the metal kit mesh
  • create and load textures using a specific colour map

Another method is drawInMTKView which renders or draws the graphics to the view (app screen) once per frame. The steps involved are:

  • setup the command queue
  • update the back buffer
  • update the game state (rotate the cube)
  • get the render pass descriptor to prep for rendering
  • send rendering commands to the command queue
  • set the cull mode
  • set the graphics pipeline state
  • set the depth stencil state
  • set the vertex buffer
  • set the fragment buffer
  • set the fragment texture
  • cycle through meshes (if more than one) and draw them
  • present the graphics to the view (app screen)

The method _updateGameState is called by drawInMTKView once per frame. It is involved in rotating the cube and setting up the view to see the graphics on the screen as the cube rotates.

The method drawableSizeWillChange will respond if the view size changes (user resizes the app window). This method also sets up the _projectionMatrix which sets up the camera. _projectionMatrix is also in the _updateGameState method.

Where to put Game specific code

We can alter the existing files as needed and add new files as required.