Browse Source

Merge branch 'next' of https://github.com/blackberry/GamePlay into next

Adam Blake 13 years ago
parent
commit
996c517a51

+ 5 - 0
gameplay/src/Game.cpp

@@ -563,6 +563,11 @@ void Game::loadConfig()
                 FileSystem::loadResourceAliases(aliases);
             }
         }
+        else
+        {
+            // Create an empty config
+            _properties = new Properties();
+        }
     }
 }
 

+ 4 - 4
gameplay/src/Mesh.cpp

@@ -109,10 +109,10 @@ Mesh* Mesh::createQuadFullscreen()
 
     float vertices[] =
     {
-        x, y2,   0, 0,
-        x, y,    0, 1,
-        x2, y2,  1, 0,
-        x2, y,   1, 1
+        x, y2,   0, 1,
+        x, y,    0, 0,
+        x2, y2,  1, 1,
+        x2, y,   1, 0
     };
 
     VertexFormat::Element elements[] =

+ 202 - 8
gameplay/src/PhysicsVehicle.cpp

@@ -1,9 +1,13 @@
 #include "Base.h"
 #include "Game.h"
+#include "MathUtil.h"
 #include "Node.h"
 #include "PhysicsVehicle.h"
 #include "PhysicsVehicleWheel.h"
 
+#define AIR_DENSITY (1.2f)
+#define KPH_TO_MPS (1.0f / 3.6f)
+
 namespace gameplay
 {
 
@@ -76,7 +80,7 @@ private:
 };
 
 PhysicsVehicle::PhysicsVehicle(Node* node, const PhysicsCollisionShape::Definition& shape, const PhysicsRigidBody::Parameters& parameters)
-    : PhysicsCollisionObject(node)
+    : PhysicsCollisionObject(node), _speedSmoothed(0)
 {
     // Note that the constructor for PhysicsRigidBody calls addCollisionObject and so
     // that is where the rigid body gets added to the dynamics world.
@@ -86,7 +90,7 @@ PhysicsVehicle::PhysicsVehicle(Node* node, const PhysicsCollisionShape::Definiti
 }
 
 PhysicsVehicle::PhysicsVehicle(Node* node, PhysicsRigidBody* rigidBody)
-    : PhysicsCollisionObject(node)
+    : PhysicsCollisionObject(node), _speedSmoothed(0)
 {
     _rigidBody = rigidBody;
 
@@ -117,6 +121,42 @@ PhysicsVehicle* PhysicsVehicle::create(Node* node, Properties* properties)
         {
             vehicle->setDrivingForce(properties->getFloat());
         }
+        else if (strcmp(name, "steerdownSpeed") == 0)
+        {
+            vehicle->_steerdownSpeed = properties->getFloat();
+        }
+        else if (strcmp(name, "steerdownGain") == 0)
+        {
+            vehicle->_steerdownGain = properties->getFloat();
+        }
+        else if (strcmp(name, "brakedownStart") == 0)
+        {
+            vehicle->_brakedownStart = properties->getFloat();
+        }
+        else if (strcmp(name, "brakedownFull") == 0)
+        {
+            vehicle->_brakedownFull = properties->getFloat();
+        }
+        else if (strcmp(name, "drivedownStart") == 0)
+        {
+            vehicle->_drivedownStart = properties->getFloat();
+        }
+        else if (strcmp(name, "drivedownFull") == 0)
+        {
+            vehicle->_drivedownFull = properties->getFloat();
+        }
+        else if (strcmp(name, "boostSpeed") == 0)
+        {
+            vehicle->_boostSpeed = properties->getFloat();
+        }
+        else if (strcmp(name, "boostGain") == 0)
+        {
+            vehicle->_boostGain = properties->getFloat();
+        }
+        else if (strcmp(name, "downforce") == 0)
+        {
+            vehicle->_downforce = properties->getFloat();
+        }
         else
         {
             // Ignore this case (we've already parsed the rigid body parameters).
@@ -134,6 +174,11 @@ void PhysicsVehicle::initialize()
     setSteeringGain(0.5f);
     setBrakingForce(350.0f);
     setDrivingForce(2000.0f);
+    setSteerdown(0, 1);
+    setBrakedown(1000, 0);
+    setDrivedown(1000, 0);
+    setBoost(0, 1);
+    setDownforce(0);
 
     // Create the vehicle and add it to world
     btRigidBody* body = static_cast<btRigidBody*>(_rigidBody->getCollisionObject());
@@ -204,8 +249,28 @@ float PhysicsVehicle::getSpeedKph() const
     return _vehicle->getCurrentSpeedKmHour();
 }
 
+float PhysicsVehicle::getSpeedSmoothKph() const
+{
+    return _speedSmoothed;
+}
+
 void PhysicsVehicle::update(float elapsedTime, float steering, float braking, float driving)
 {
+    float v = getSpeedKph();
+    MathUtil::smooth(&_speedSmoothed, v, elapsedTime, 0, 1200);
+    applyDownforce();
+
+    // Adjust control inputs based on vehicle speed.
+    steering = getSteering(v, steering);
+    driving = getDriving(v, driving, braking);
+    braking = getBraking(v, braking);
+
+    // Allow braking to take precedence over driving.
+    if (driving > 0 && braking > 0)
+    {
+        driving = 0;
+    }
+
     PhysicsVehicleWheel* wheel;
     for (int i = 0; i < _vehicle->getNumWheels(); i++)
     {
@@ -217,12 +282,6 @@ void PhysicsVehicle::update(float elapsedTime, float steering, float braking, fl
         }
         else
         {
-            // Allow braking to take precedence over driving.
-            if (driving > 0 && braking > 0)
-            {
-                driving = 0;
-            }
-
             _vehicle->applyEngineForce(driving * _drivingForce, i);
             _vehicle->setBrake(braking * _brakingForce, i);
         }
@@ -232,6 +291,67 @@ void PhysicsVehicle::update(float elapsedTime, float steering, float braking, fl
     }
 }
 
+void PhysicsVehicle::reset()
+{
+    _rigidBody->setLinearVelocity(Vector3::zero());
+    _rigidBody->setAngularVelocity(Vector3::zero());
+    _speedSmoothed = 0;
+}
+
+float PhysicsVehicle::getSteering(float v, float rawSteering) const
+{
+    float gain = 1;
+    if (_steerdownSpeed > MATH_FLOAT_SMALL)
+    {
+        gain = max(_steerdownGain, 1 - (1 - _steerdownGain) * fabs(v) / _steerdownSpeed);
+    }
+
+    return rawSteering * gain;
+}
+
+float PhysicsVehicle::getBraking(float v, float rawBraking) const
+{
+    float reduc = 0;
+    float delta = _brakedownFull - _brakedownStart;
+    if (delta > MATH_FLOAT_SMALL)
+    {
+        reduc = max(0.0f, (v - _brakedownStart) / delta);
+        reduc *= reduc;
+    }
+
+    return max(0.0f, rawBraking - reduc);
+}
+
+float PhysicsVehicle::getDriving(float v, float rawDriving, float rawBraking) const
+{
+    float reduc = 0;
+    float delta = _drivedownFull - _drivedownStart;
+    if (rawBraking == 0 && delta > MATH_FLOAT_SMALL)
+    {
+        reduc = max(0.0f, (v - _drivedownStart) / delta);
+        reduc *= reduc;
+    }
+
+    float gain = 1;
+    if (_boostSpeed > MATH_FLOAT_SMALL)
+    {
+        gain = max(1.0f, _boostGain - (_boostGain - 1) * fabs(v) / _boostSpeed);
+    }
+
+    return gain * rawDriving - reduc;
+}
+
+void PhysicsVehicle::applyDownforce()
+{
+    float v = _speedSmoothed * KPH_TO_MPS;
+
+    // dynamic pressure
+    float q = 0.5f * AIR_DENSITY * v * v;
+
+    // _downforce is the product of reference area and the aerodynamic coefficient
+    _rigidBody->applyForce(Vector3(0, -_downforce * q, 0));
+}
+
 float PhysicsVehicle::getSteeringGain() const
 {
     return _steeringGain;
@@ -262,4 +382,78 @@ void PhysicsVehicle::setDrivingForce(float drivingForce)
     _drivingForce = drivingForce;
 }
 
+float PhysicsVehicle::getSteerdownSpeed() const
+{
+    return _steerdownSpeed;
+}
+
+float PhysicsVehicle::getSteerdownGain() const
+{
+    return _steerdownGain;
+}
+
+void PhysicsVehicle::setSteerdown(float steerdownSpeed, float steerdownGain)
+{
+    _steerdownSpeed = steerdownSpeed;
+    _steerdownGain = steerdownGain;
+}
+
+float PhysicsVehicle::getBrakedownStart() const
+{
+    return _brakedownStart;
+}
+
+float PhysicsVehicle::getBrakedownFull() const
+{
+    return _brakedownFull;
+}
+
+void PhysicsVehicle::setBrakedown(float brakedownStart, float brakedownFull)
+{
+    _brakedownStart = brakedownStart;
+    _brakedownFull = brakedownFull;
+}
+
+float PhysicsVehicle::getDrivedownStart() const
+{
+    return _drivedownStart;
+}
+
+float PhysicsVehicle::getDrivedownFull() const
+{
+    return _drivedownFull;
+}
+
+void PhysicsVehicle::setDrivedown(float drivedownStart, float drivedownFull)
+{
+    _drivedownStart = drivedownStart;
+    _drivedownFull = drivedownFull;
+}
+
+float PhysicsVehicle::getBoostSpeed() const
+{
+    return _boostSpeed;
+}
+
+float PhysicsVehicle::getBoostGain() const
+{
+    return _boostGain;
+}
+
+void PhysicsVehicle::setBoost(float boostSpeed, float boostGain)
+{
+    _boostSpeed = boostSpeed;
+    _boostGain = boostGain;
+}
+
+float PhysicsVehicle::getDownforce() const
+{
+    return _downforce;
+}
+
+void PhysicsVehicle::setDownforce(float downforce)
+{
+    _downforce = downforce;
+}
+
 }

+ 215 - 0
gameplay/src/PhysicsVehicle.h

@@ -33,6 +33,25 @@ class PhysicsVehicleWheel;
         steeringGain   = <float>    // steering at full deflection
         brakingForce   = <float>    // braking force at full braking
         drivingForce   = <float>    // driving force at full throttle
+
+        // Steering gain reduction with speed (optional)
+        steerdownSpeed = <float>    // steering gain fades to this point
+        steerdownGain  = <float>    // gain value at that point (less than 1)
+
+        // Brake force reduction at high speeds (optional)
+        brakedownStart = <float>    // braking fades above this speed
+        brakedownFull  = <float>    // braking is fully faded at this speed
+
+        // Driving force reduction at high speeds (optional)
+        drivedownStart = <float>    // driving force fades above this speed
+        drivedownFull  = <float>    // driving force is fully faded at this speed
+
+        // Driving force boost at low speeds (optional)
+        boostSpeed     = <float>    // Boost fades to 1 at this point
+        boostGain      = <float>    // Boost at zero speed (greater than 1)
+
+        // Aerodynamic downforce effect (optional)
+        downforce      = <float>    // proportional control of downforce
     }
  @endverbatim
  */
@@ -80,6 +99,12 @@ public:
      */
     float getSpeedKph() const;
 
+    /**
+     * Returns a lagged version of vehicle speed in kilometers per hour,
+     * for example that might be used to control engine sounds.
+     */
+    float getSpeedSmoothKph() const;
+
     /**
      * Updates the vehicle state using the specified normalized command
      * inputs, and updates the transform on the visual node for each wheel.
@@ -91,6 +116,11 @@ public:
      */
     void update(float elapsedTime, float steering, float braking, float driving);
 
+    /**
+     * Resets the vehicle's state, for example in preparation for a reposition.
+     */
+    void reset();
+
     /**
      * Gets steering gain at full deflection.
      *
@@ -133,6 +163,151 @@ public:
      */
     void setDrivingForce(float drivingForce);
 
+    /**
+     * Returns speed at the point of reduced steering, in km/h.
+     * A point of reduced steering is defined by speed and gain.
+     * Steering authority will reduce linearly with speed up to
+     * this point, and remain constant above that.
+     *
+     * @return speed at the point of reduced steering, in km/h.
+     */
+    float getSteerdownSpeed() const;
+
+    /**
+     * Returns gain at the point of reduced steering, typically
+     * less than 1.
+     * A point of reduced steering is defined by speed and gain.
+     * Steering authority will reduce linearly with speed up to
+     * this point, and remain constant above that.
+     *
+     * @return gain at the point of reduced steering.
+     */
+    float getSteerdownGain() const;
+
+    /**
+     * Sets the point of reduced steering, defined by speed and
+     * gain. Typically the gain value is less than 1.
+     * Steering authority will reduce linearly with speed up to
+     * this point, and remain constant above that.
+     *
+     * @param steerdownSpeed speed at the point of reduced steering,
+     *     in km/h.
+     * @param steerdownGain gain at the point of reduced steering.
+     *     A gain of 1 will effectively disable the feature.
+     */
+    void setSteerdown(float steerdownSpeed, float steerdownGain);
+
+    /**
+     * Returns speed where braking starts to fade, in km/h.
+     *
+     * @return speed where braking starts to fade, in km/h.
+     */
+    float getBrakedownStart() const;
+
+    /**
+     * Returns speed where braking is fully faded, in km/h.
+     * This speed is typically greater than the brakedownStart
+     * speed.
+     *
+     * @return speed where braking is fully faded, in km/h.
+     */
+    float getBrakedownFull() const;
+
+    /**
+     * Sets points that control fade of brake force with speed,
+     * in km/h.
+     *
+     * @param brakedownStart braking fades above this speed.
+     *     A very large value will effectively disable the feature.
+     * @param brakedownFull braking is fully faded at this speed.
+     *     This speed is typically greater than the brakedownStart
+     *     speed.
+     */
+    void setBrakedown(float brakedownStart, float brakedownFull);
+
+    /**
+     * Returns speed where driving force starts to fade, in km/h.
+     *
+     * @return speed where driving force starts to fade, in km/h.
+     */
+    float getDrivedownStart() const;
+
+    /**
+     * Returns speed where driving force is fully faded, in km/h.
+     * This speed is typically greater than the drivedownStart
+     * speed.
+     *
+     * @return speed where driving force is fully faded, in km/h.
+     */
+    float getDrivedownFull() const;
+
+    /**
+     * Sets points that control fade of driving force with speed,
+     * in km/h.
+     *
+     * @param drivedownStart driving force fades above this speed.
+     *     A very large value will effectively disable the feature.
+     * @param drivedownFull driving force is fully faded at this speed.
+     *     This speed is typically greater than the drivedownStart
+     *     speed.
+     */
+    void setDrivedown(float drivedownStart, float drivedownFull);
+
+    /**
+     * Returns upper limit of low-speed boost effect, in km/h.
+     * Driving force is boosted by a specified factor at zero speed,
+     * and that factor fades linearly with speed reaching 1 at
+     * this speed.
+     *
+     * @return upper limit of low-speed boost effect, in km/h.
+     */
+    float getBoostSpeed() const;
+
+    /**
+     * Returns boost gain at zero speed, typically greater than 1.
+     * Driving force is boosted by this factor at zero speed, and
+     * that factor fades linearly with speed reaching 1 at a
+     * specified speed.
+     *
+     * @return boost gain at zero speed.
+     */
+    float getBoostGain() const;
+
+    /**
+     * Sets parameters that define low-speed boost of the driving force.
+     * Driving force is boosted by the specified factor at zero speed,
+     * and that factor fades linearly with speed reaching 1 at the
+     * specified speed.
+     *
+     * @param boostSpeed upper limit of low-speed boost effect, in km/h.
+     * @param boostGain boost gain at zero speed, typically greater than 1.
+     *     A gain of 1 will effectively disable the feature.
+     */
+    void setBoost(float boostSpeed, float boostGain);
+
+    /**
+     * Returns the lumped constant that controls aerodynamic downforce.
+     * Technically speaking, this constant lumps together the reference
+     * area and the down-force coefficient, and is in world-units squared.
+     * The actual aerodynamic down-force is calculated as a function of
+     * current speed, and is proportional to this constant.
+     *
+     * @return the lumped constant that controls aerodynamic downforce.
+     */
+    float getDownforce() const;
+
+    /**
+     * Sets the lumped constant that controls aerodynamic downforce.
+     * Technically speaking, this constant lumps together the reference
+     * area and the down-force coefficient, and is in world-units squared.
+     * The actual aerodynamic down-force is calculated as a function of
+     * current speed, and is proportional to this constant.
+     *
+     * @param downforce the lumped constant that controls aerodynamic downforce.
+     *     A value of 0 will effectively disable this feature.
+     */
+    void setDownforce(float downforce);
+
 protected:
 
     /**
@@ -186,6 +361,36 @@ private:
      */
     void initialize();
 
+    /**
+     * Returns adjusted steering value.
+     *
+     * @param v vehicle speed.
+     * @param rawSteering raw steering command.
+     */
+    float getSteering(float v, float rawSteering) const;
+
+    /**
+     * Returns adjusted braking force value.
+     *
+     * @param v vehicle speed.
+     * @param rawBraking raw braking force command.
+     */
+    float getBraking(float v, float rawBraking) const;
+
+    /**
+     * Returns adjusted driving force value.
+     *
+     * @param v vehicle speed.
+     * @param rawDriving raw driving force command.
+     * @param rawBraking raw braking force command.
+     */
+    float getDriving(float v, float rawDriving, float rawBraking) const;
+
+    /**
+     * Applies effect of aerodynamic downforce.
+     */
+    void applyDownforce();
+
     /**
      * Destructor.
      */
@@ -194,6 +399,16 @@ private:
     float _steeringGain;
     float _brakingForce;
     float _drivingForce;
+    float _steerdownSpeed;
+    float _steerdownGain;
+    float _brakedownStart;
+    float _brakedownFull;
+    float _drivedownStart;
+    float _drivedownFull;
+    float _boostSpeed;
+    float _boostGain;
+    float _downforce;
+    float _speedSmoothed;
     PhysicsRigidBody* _rigidBody;
     btRaycastVehicle::btVehicleTuning _vehicleTuning;
     btVehicleRaycaster* _vehicleRaycaster;

+ 74 - 38
gameplay/src/PlatformiOS.mm

@@ -161,7 +161,7 @@ int getKey(unichar keyCode);
         multisampleDepthbuffer = 0;
         swapInterval = 1;        
         updating = FALSE;
-        game = Game::getInstance();
+        game = nil;
         
         // Set the resource path and initalize the game
         NSString* bundlePath = [[[NSBundle mainBundle] bundlePath] stringByAppendingString:@"/"];
@@ -172,7 +172,8 @@ int getKey(unichar keyCode);
 
 - (void) dealloc
 {
-    game->exit();
+    if (game)
+        game->exit();
     [self deleteFramebuffer];
     
     if ([EAGLContext currentContext] == context)
@@ -193,7 +194,10 @@ int getKey(unichar keyCode);
 {
     // Called on 'resize'.
     // Mark that framebuffer needs to be updated.
-    updateFramebuffer = YES;
+    // NOTE: Current disabled since we need to have a way to reset the default frame buffer handle
+    // in FrameBuffer.cpp (for FrameBuffer:bindDefault). This means that changing orientation at
+    // runtime is currently not supported until we fix this.
+    //updateFramebuffer = YES;
 }
 
 - (BOOL)createFramebuffer
@@ -209,7 +213,7 @@ int getKey(unichar keyCode);
     GL_ASSERT( glGenRenderbuffers(1, &colorRenderbuffer) );
     GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer) );
     
-    // Associate our render buffer with CAEAGLLauyer so that the rendered content is display on our UI layer.
+    // Associate render buffer storage with CAEAGLLauyer so that the rendered content is display on our UI layer.
     [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
     
     // Attach the color buffer to our frame buffer
@@ -219,35 +223,52 @@ int getKey(unichar keyCode);
     GL_ASSERT( glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &framebufferWidth) );
     GL_ASSERT( glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &framebufferHeight) );
     
+    NSLog(@"width: %d, height: %d", framebufferWidth, framebufferHeight);
+    
     // If multisampling is enabled in config, create and setup a multisample buffer
-    Properties* config = game->getConfig()->getNamespace("window");
+    Properties* config = Game::getInstance()->getConfig()->getNamespace("window", true);
     int samples = config ? config->getInt("samples") : 0;
     if (samples < 0)
         samples = 0;
     if (samples)
     {
+        // Create multisample framebuffer
         GL_ASSERT( glGenFramebuffers(1, &multisampleFramebuffer) );
         GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, multisampleFramebuffer) );
         
+        // Create multisample render and depth buffers
         GL_ASSERT( glGenRenderbuffers(1, &multisampleRenderbuffer) );
-        GL_ASSERT( glBindRenderbuffer(1, multisampleRenderbuffer) );
-        GL_ASSERT( glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, samples, GL_RGBA8_OES, framebufferWidth, framebufferHeight) );
-        GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, multisampleRenderbuffer) );
-        
         GL_ASSERT( glGenRenderbuffers(1, &multisampleDepthbuffer) );
-        GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, multisampleDepthbuffer) );
-        GL_ASSERT( glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, samples, GL_DEPTH_COMPONENT24_OES, framebufferWidth, framebufferHeight) );
-        GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, multisampleDepthbuffer) );
-        
-        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+
+        // Try to find a supported multisample configuration starting with the defined sample count
+        while (samples)
         {
-            NSLog(@"Failed to make complete multisample buffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
-            [self deleteFramebuffer];
-            return NO;
+            GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, multisampleRenderbuffer) );
+            GL_ASSERT( glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, samples, GL_RGBA8_OES, framebufferWidth, framebufferHeight) );
+            GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, multisampleRenderbuffer) );
+
+            GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, multisampleDepthbuffer) );
+            GL_ASSERT( glRenderbufferStorageMultisampleAPPLE(GL_RENDERBUFFER, samples, GL_DEPTH_COMPONENT24_OES, framebufferWidth, framebufferHeight) );
+            GL_ASSERT( glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, multisampleDepthbuffer) );
+            
+            if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
+                break; // success!
+            
+            NSLog(@"Creation of multisample buffer with samples=%d failed. Attempting to use configuration with samples=%d instead: %x", samples, samples / 2, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+            samples /= 2;
         }
         
         // Re-bind the default framebuffer
         GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer) );
+        
+        if (samples == 0)
+        {
+            // Unable to find a valid/supported multisample configuratoin - fallback to no multisampling
+            GL_ASSERT( glDeleteRenderbuffers(1, &multisampleRenderbuffer) );
+            GL_ASSERT( glDeleteRenderbuffers(1, &multisampleDepthbuffer) );
+            GL_ASSERT( glDeleteFramebuffers(1, &multisampleFramebuffer) );
+            multisampleFramebuffer = multisampleRenderbuffer = multisampleDepthbuffer = 0;
+        }
     }
     
     // Create default depth buffer and attach to the frame buffer.
@@ -269,6 +290,12 @@ int getKey(unichar keyCode);
         return NO;
     }
     
+    // If multisampling is enabled, set the currently bound framebuffer to the multisample buffer
+    // since that is the buffer code should be drawing into (and FrameBuffr::initialize will detect
+    // and set this bound buffer as the default one during initialization.
+    if (multisampleFramebuffer)
+        GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, multisampleFramebuffer) );
+    
     return YES;
 }
 
@@ -279,32 +306,32 @@ int getKey(unichar keyCode);
         [EAGLContext setCurrentContext:context];        
         if (defaultFramebuffer) 
         {
-            glDeleteFramebuffers(1, &defaultFramebuffer);
+            GL_ASSERT( glDeleteFramebuffers(1, &defaultFramebuffer) );
             defaultFramebuffer = 0;
         }        
         if (colorRenderbuffer) 
         {
-            glDeleteRenderbuffers(1, &colorRenderbuffer);
+            GL_ASSERT( glDeleteRenderbuffers(1, &colorRenderbuffer) );
             colorRenderbuffer = 0;
         }
         if (depthRenderbuffer) 
         {
-            glDeleteRenderbuffers(1, &depthRenderbuffer);
+            GL_ASSERT( glDeleteRenderbuffers(1, &depthRenderbuffer) );
             depthRenderbuffer = 0;
         }
         if (multisampleFramebuffer)
         {
-            glDeleteFramebuffers(1, &multisampleFramebuffer);
+            GL_ASSERT( glDeleteFramebuffers(1, &multisampleFramebuffer) );
             multisampleFramebuffer = 0;
         }
         if (multisampleRenderbuffer)
         {
-            glDeleteRenderbuffers(1, &multisampleRenderbuffer);
+            GL_ASSERT( glDeleteRenderbuffers(1, &multisampleRenderbuffer) );
             multisampleRenderbuffer = 0;
         }
         if (multisampleDepthbuffer)
         {
-            glDeleteRenderbuffers(1, &multisampleDepthbuffer);
+            GL_ASSERT( glDeleteRenderbuffers(1, &multisampleDepthbuffer) );
             multisampleDepthbuffer = 0;
         }
     }
@@ -335,16 +362,16 @@ int getKey(unichar keyCode);
         if (multisampleFramebuffer)
         {
             // Multisampling is enabled: resolve the multisample buffer into the default framebuffer
-            glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, defaultFramebuffer);
-            glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, multisampleFramebuffer);
-            glResolveMultisampleFramebufferAPPLE();
+            GL_ASSERT( glBindFramebuffer(GL_DRAW_FRAMEBUFFER_APPLE, defaultFramebuffer) );
+            GL_ASSERT( glBindFramebuffer(GL_READ_FRAMEBUFFER_APPLE, multisampleFramebuffer) );
+            GL_ASSERT( glResolveMultisampleFramebufferAPPLE() );
             
             if (oglDiscardSupported)
             {
                 // Performance hint that the GL driver can discard the contents of the multisample buffers
                 // since they have now been resolved into the default framebuffer
                 const GLenum discards[]  = { GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT };
-                glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 2, discards);
+                GL_ASSERT( glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 2, discards) );
             }
         }
         else
@@ -353,22 +380,25 @@ int getKey(unichar keyCode);
             {
                 // Performance hint to the GL driver that the depth buffer is no longer required.
                 const GLenum discards[]  = { GL_DEPTH_ATTACHMENT };
-                glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer);
-                glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards);
+                GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, defaultFramebuffer) );
+                GL_ASSERT( glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards) );
             }
         }
         
         // Present the color buffer
-        glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
+        GL_ASSERT( glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer) );
         [context presentRenderbuffer:GL_RENDERBUFFER];
     }
 }
 
 - (void)startGame 
 {
-    game = Game::getInstance();
-    __timeStart = getMachTimeInMilliseconds();
-    game->run();
+    if (game == nil)
+    {
+        game = Game::getInstance();
+        __timeStart = getMachTimeInMilliseconds();
+        game->run();
+    }
 }
 
 - (void)startUpdating
@@ -378,7 +408,8 @@ int getKey(unichar keyCode);
         displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update:)];
         [displayLink setFrameInterval:swapInterval];
         [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
-        game->resume();
+        if (game)
+            game->resume();
         updating = TRUE;
     }
 }
@@ -387,7 +418,8 @@ int getKey(unichar keyCode);
 {
     if (updating)
     {
-        game->pause();
+        if (game)
+            game->pause();
         [displayLink invalidate];
         displayLink = nil;
         updating = FALSE;
@@ -407,8 +439,14 @@ int getKey(unichar keyCode);
             updateFramebuffer = NO;
             [self deleteFramebuffer];
             [self createFramebuffer];
+            
+            // Start the game after our framebuffer is created for the first time.
+            if (game == nil)
+            {
+                [self startGame];
+            }
         }
-        
+
         // Bind our framebuffer for rendering.
         // If multisampling is enabled, bind the multisample buffer - otherwise bind the default buffer
         GL_ASSERT( glBindFramebuffer(GL_FRAMEBUFFER, multisampleFramebuffer ? multisampleFramebuffer : defaultFramebuffer) );
@@ -628,8 +666,6 @@ int getKey(unichar keyCode);
     {
         __view = (View*)self.view;
     }
-    // Start the game after assigning __view so Platform object knows about it on game start
-    [(View*)self.view startGame];
 }
 
 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation