Пример #1
0
        private void ShowPatientDetails(ARReferenceImage detectedImage, SCNNode node)
        {
            // Highlight the QR Code?
            var width     = detectedImage.PhysicalSize.Width;
            var length    = detectedImage.PhysicalSize.Height;
            var planeNode = new PlaneNode(0, width, length, new SCNVector3(0, 0, 0), "");

            planeNode.Opacity = 1f;
            float angle = (float)(-Math.PI / 2);

            planeNode.EulerAngles = new SCNVector3(angle, 0, 0);
            //node.AddChildNode(planeNode);

            // Get and Show information panels
            foreach (var informationPanelNode in GetPatientInformationPanels())
            {
                var waitAction     = SCNAction.Wait(0.1 * informationPanelNode.Number);
                var fadeInAction   = SCNAction.FadeIn(1);
                var actionSequence = SCNAction.Sequence(new[] { waitAction, fadeInAction });

                // Not sure I can run actions before adding. May have to add, then run.
                informationPanelNode.RunAction(actionSequence);

                informationPanelNode.EulerAngles = new SCNVector3(angle, 0, 0);

                node.AddChildNode(informationPanelNode);
            }
        }
Пример #2
0
 void HandleCollideForCoconut(Coconut coconut)
 {
     // Remove coconut from the world after it has time to fall offscreen.
     coconut.RunAction(SCNAction.Wait(3.0), () => {
         coconut.RemoveFromParentNode();
         GameLevel.Coconuts.Remove(coconut);
     });
 }
Пример #3
0
        private void Close(bool flash = false)
        {
            if (!IsOpen || IsAnimating)
            {
                return;
            }

            IsAnimating = true;
            StopPulsing(FocusSquareNode());

            // Close animation
            SCNTransaction.Begin();
            SCNTransaction.AnimationTimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseOut);
            SCNTransaction.AnimationDuration       = AnimationDuration / 2f;
            FocusSquareNode().Opacity = 0.99f;
            SCNTransaction.SetCompletionBlock(() => {
                SCNTransaction.Begin();
                SCNTransaction.AnimationTimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseOut);
                SCNTransaction.AnimationDuration       = AnimationDuration / 4f;

                foreach (var segment in segments)
                {
                    segment.Close();
                }

                SCNTransaction.SetCompletionBlock(() => {
                    IsAnimating = false;
                });
                SCNTransaction.Commit();
            });
            SCNTransaction.Commit();

            // Scale/bounce animation
            FocusSquareNode().AddAnimation(ScaleAnimation("transform.scale.x"), "transform.scale.y");
            FocusSquareNode().AddAnimation(ScaleAnimation("transform.scale.y"), "transform.scale.y");
            FocusSquareNode().AddAnimation(ScaleAnimation("transform.scale.z"), "transform.scale.z");

            // Flash?
            if (flash)
            {
                var waitAction    = SCNAction.Wait(AnimationDuration * 0.75f);
                var fadeInAction  = SCNAction.FadeOpacityTo(0.25f, AnimationDuration * 0.125f);
                var fadeOutAction = SCNAction.FadeOpacityTo(0.0f, AnimationDuration * 0.125f);
                FillPlane.RunAction(SCNAction.Sequence(new SCNAction[] { waitAction, fadeInAction, fadeOutAction }));

                var flashSquareAction = FlashAnimation(AnimationDuration * 0.25f);
                foreach (var segment in Segments)
                {
                    segment.RunAction(SCNAction.Sequence(new[] { waitAction, flashSquareAction }));
                }
            }

            IsOpen = false;
        }
Пример #4
0
 public void DidHitEnemy()
 {
     this.model.RunAction(SCNAction.Group(new SCNAction[]
     {
         SCNAction.PlayAudioSource(this.hitEnemySound, false),
         SCNAction.Sequence(new SCNAction[]
         {
             SCNAction.Wait(0.5),
             SCNAction.PlayAudioSource(explodeEnemySound, false)
         })
     }));
 }
Пример #5
0
        public void CollideWithCoconut(SCNNode coconut, SCNVector3 contactPoint)
        {
            // No more collisions. Let it bounce away and fade out.
            coconut.PhysicsBody.CollisionBitMask = 0;
            coconut.RunAction(SCNAction.Sequence(new SCNAction[] {
                SCNAction.Wait(1.0),
                SCNAction.FadeOut(1.0),
                SCNAction.RemoveFromParentNode()
            }), () => {
                Coconuts.Remove((Coconut)coconut);
            });

            // Decrement score
            int amountToDrop = Score / 10;

            amountToDrop = Math.Max(1, amountToDrop);
            amountToDrop = Math.Min(10, amountToDrop);

            if (amountToDrop > Score)
            {
                amountToDrop = Score;
            }

            Score -= amountToDrop;

            // Throw bananas
            float spacing = 40f;

            for (int x = 0; x < amountToDrop; x++)
            {
                SCNNode banana = CreateBanana();
                RootNode.AddChildNode(banana);
                banana.Position = contactPoint;
                banana.PhysicsBody.CategoryBitMask  = GameCollisionCategory.NoCollide;
                banana.PhysicsBody.CollisionBitMask = GameCollisionCategory.Ground;
                SCNVector3 endPoint = SCNVector3.Zero;
                endPoint.X -= (spacing * x) + spacing;

                SCNAction flyoff = SCNAction.MoveBy(endPoint, MathUtils.RandomPercent() * 0.750f);
                flyoff.TimingMode = SCNActionTimingMode.EaseInEaseOut;

                banana.RunAction(flyoff, () => {
                    banana.PhysicsBody.CategoryBitMask  = GameCollisionCategory.Banana;
                    banana.PhysicsBody.CollisionBitMask = GameCollisionCategory.Ground | GameCollisionCategory.Player;
                });

                Bananas.Add(banana);
            }

            PlayerCharacter.InHitAnimation = true;
        }
Пример #6
0
        private void PerformCloseAnimation(bool flash = false)
        {
            if (this.isBorderOpen && !this.isAnimating)
            {
                this.isBorderOpen = false;
                this.isAnimating  = true;

                this.borderNode.RemoveAction("pulse");
                this.borderNode.Opacity = 1f;

                // Close animation
                SCNTransaction.Begin();
                SCNTransaction.AnimationDuration       = GameBoard.AnimationDuration / 2f;
                SCNTransaction.AnimationTimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseOut);

                this.borderNode.Opacity = 0.99f;
                SCNTransaction.SetCompletionBlock(() =>
                {
                    SCNTransaction.Begin();
                    SCNTransaction.AnimationDuration       = GameBoard.AnimationDuration / 2f;
                    SCNTransaction.AnimationTimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseOut);

                    foreach (var segment in this.borderSegments)
                    {
                        segment.Close();
                    }

                    SCNTransaction.SetCompletionBlock(() =>
                    {
                        this.isAnimating = false;
                    });

                    SCNTransaction.Commit();
                });

                SCNTransaction.Commit();

                if (flash)
                {
                    var waitAction    = SCNAction.Wait(GameBoard.AnimationDuration * 0.75f);
                    var fadeInAction  = SCNAction.FadeOpacityTo(0.6f, GameBoard.AnimationDuration * 0.125f);
                    var fadeOutAction = SCNAction.FadeOpacityTo(0f, GameBoard.AnimationDuration * 0.125f);
                    this.FillPlane.RunAction(SCNAction.Sequence(new SCNAction[] { waitAction, fadeOutAction, fadeInAction }));
                }
            }
        }
Пример #7
0
        void SetIngameFilters()
        {
            SCNTransaction.Begin();

            desaturationTechnique.SetValueForKey(new NSNumber(0.0), (NSString)"Saturation");
            SCNTransaction.AnimationDuration = 1.0;
            desaturationTechnique.SetValueForKey(new NSNumber(1.0), (NSString)"Saturation");

            SCNTransaction.Commit();

            SCNAction dropTechnique = SCNAction.Wait(1.0f);

            SharedAppDelegate appDelegate = SharedAppDelegate.AppDelegate;

            appDelegate.Scene.Scene.RootNode.RunAction(dropTechnique, () => {
                appDelegate.Scene.Technique = null;
            });
        }
Пример #8
0
        private void Explosion(SCNVector3 center, List <SCNNode> nodes)
        {
            foreach (var node in nodes)
            {
                var position = node.PresentationNode.Position;
                var dir      = SCNVector3.Subtract(position, center);

                var force    = (nfloat)25.0f;
                var distance = dir.Length;

                dir = SCNVector3.Multiply(dir, force / NMath.Max(0.01f, distance));

                node.PhysicsBody.ApplyForce(dir, new SCNVector3(RandFloat(-0.2, 0.2), RandFloat(-0.2, 0.2), RandFloat(-0.2, 0.2)), true);

                node.RunAction(SCNAction.Sequence(new SCNAction[] {
                    SCNAction.Wait(2),
                    SCNAction.FadeOut(0.5f),
                    SCNAction.RemoveFromParentNode()
                }));
            }
        }
Пример #9
0
        public void Update(CameraInfo cameraInfo)
        {
            if (this.Delegate == null)
            {
                throw new Exception("No delegate");
            }

            // Do not move the lever after it has been activated
            if (!(this.InteractionToActivate?.IsActivated ?? false))
            {
                if (this.activeSwitch != null)
                {
                    // Lever Pulling
                    var cameraOffset = this.activeSwitch.PullOffset(cameraInfo.Ray.Position - this.startLeverHoldCameraPosition);
                    var cameraMovedZ = cameraOffset.Z;

                    var targetEulerX = this.startLeverEulerX + LeverPullZtoLeverEulerRotation * cameraMovedZ;
                    targetEulerX            = DigitExtensions.Clamp(-LeverMaxEulerX, targetEulerX, LeverMaxEulerX);
                    this.activeSwitch.Angle = targetEulerX;

                    if (targetEulerX <= -LeverMaxEulerX)
                    {
                        // Interaction activation once the switch lever is turned all the way
                        this.InteractionToActivate?.Activate();

                        // Fade out the switches
                        var waitAction = SCNAction.Wait(3f);
                        var fadeAction = SCNAction.FadeOut(3d);

                        foreach (var resetSwitch in this.resetSwitches)
                        {
                            resetSwitch.Base.RunAction(SCNAction.Sequence(new SCNAction[] { waitAction, fadeAction }));
                        }
                        return;
                    }
                    else
                    {
                        // Inform peers of the movement
                        var leverId = this.resetSwitches.IndexOf(this.activeSwitch);
                        if (leverId == -1)
                        {
                            throw new Exception("No lever in array");
                        }

                        this.Delegate.DispatchActionToServer(new GameActionType {
                            LeverMove = new LeverMove(leverId, targetEulerX), Type = GameActionType.GActionType.LeverMove
                        });
                    }
                }
                else
                {
                    // Lever spring back
                    foreach (var lever in this.resetSwitches.Where(lever => lever.Angle < LeverMaxEulerX))
                    {
                        lever.Angle = Math.Min(LeverMaxEulerX, lever.Angle + LeverSpringBackSpeed * (float)GameTime.DeltaTime);
                    }
                }

                // Highlight lever when nearby, otherwise check if we should hide the highlight
                if (this.highlightedSwitch != null)
                {
                    if (!this.highlightedSwitch.ShouldHighlight(cameraInfo.Ray))
                    {
                        this.highlightedSwitch.DoHighlight(false, this.SfxCoordinator);
                        this.highlightedSwitch = null;
                    }
                }
                else
                {
                    foreach (var resetSwitch in this.resetSwitches)
                    {
                        if (resetSwitch.ShouldHighlight(cameraInfo.Ray))
                        {
                            resetSwitch.DoHighlight(true, this.SfxCoordinator);
                            this.highlightedSwitch = resetSwitch;
                        }
                    }
                }
            }
        }
Пример #10
0
        public override void PresentStep(int index, PresentationViewController presentationViewController)
        {
            Step = index;

            switch (index)
            {
            case 0:
                break;

            case 1:
                TextManager.FlipOutText(SlideTextManager.TextType.Bullet);

                TextManager.SetSubtitle("SCNPhysicsBody");

                TextManager.FlipInText(SlideTextManager.TextType.Subtitle);

                TextManager.AddBulletAtLevel("Dynamic Bodies", 0);

                // Add some code
                TextManager.AddCode("#// Make a node dynamic\n"
                                    + "aNode.#physicsBody# = [SCNPhysicsBody #dynamicBody#];#");

                TextManager.FlipInText(SlideTextManager.TextType.Bullet);
                TextManager.FlipInText(SlideTextManager.TextType.Code);
                break;

            case 2:
                //add a cube
                var worldPos = GroundNode.ConvertPositionToNode(new SCNVector3(0, 12, 2), null);
                var dice     = CreateBlock(worldPos, new SCNVector3(1.5f, 1.5f, 1.5f));
                dice.PhysicsBody = null;                 //wait!
                dice.Rotation    = new SCNVector4(0, 0, 1, (float)(Math.PI / 4) * 0.5f);
                dice.Scale       = new SCNVector3(0.001f, 0.001f, 0.001f);

                ((SCNView)presentationViewController.View).Scene.RootNode.AddChildNode(dice);
                SCNTransaction.Begin();
                SCNTransaction.AnimationDuration = 0.75f;
                dice.Scale = new SCNVector3(2, 2, 2);
                SCNTransaction.Commit();

                Dices.Add(dice);
                break;

            case 3:
                foreach (var node in Dices)
                {
                    node.PhysicsBody = SCNPhysicsBody.CreateDynamicBody();
                }
                break;

            case 4:
                PresentDices(presentationViewController);
                break;

            case 5:
                TextManager.FlipOutText(SlideTextManager.TextType.Bullet);
                TextManager.FlipOutText(SlideTextManager.TextType.Code);

                TextManager.AddBulletAtLevel("Manipulate with forces", 0);

                // Add some code
                TextManager.AddCode("#// Apply an impulse\n"
                                    + "[aNode.physicsBody #applyForce:#aVector3 #atPosition:#aVector3 #impulse:#YES];#");

                TextManager.FlipInText(SlideTextManager.TextType.Bullet);
                TextManager.FlipInText(SlideTextManager.TextType.Code);
                break;

            case 6:
                // remove dices
                var center = new SCNVector3(0, -5, 20);
                center = GroundNode.ConvertPositionToNode(center, null);

                Explosion(center, Dices);

                var popTime = new DispatchTime(DispatchTime.Now, (long)(1 * Utils.NSEC_PER_SEC));
                DispatchQueue.MainQueue.DispatchAfter(popTime, () => {
                    TextManager.FlipOutText(SlideTextManager.TextType.Code);
                    TextManager.FlipOutText(SlideTextManager.TextType.Bullet);

                    TextManager.AddBulletAtLevel("Static Bodies", 0);
                    TextManager.AddCode("#// Make a node static\n"
                                        + "aNode.#physicsBody# = [SCNPhysicsBody #staticBody#];#");
                    TextManager.FlipInText(SlideTextManager.TextType.Bullet);
                    TextManager.FlipInText(SlideTextManager.TextType.Code);
                });
                break;

            case 7:
                PresentWalls(presentationViewController);
                break;

            case 8:
                PresentBalls(presentationViewController);
                break;

            case 9:
                //remove walls
                var walls = new List <SCNNode> ();
                GroundNode.EnumerateChildNodes(delegate(SCNNode node, out bool stop) {
                    stop = false;
                    if (node.Name == "container-wall")
                    {
                        node.RunAction(SCNAction.Sequence(new SCNAction [] {
                            SCNAction.MoveBy(new SCNVector3(0, -2, 0), 0.5),
                            SCNAction.RemoveFromParentNode()
                        }));
                        walls.Add(node);
                    }
                    return(stop);
                });
                break;

            case 10:
                // remove balls
                center = new SCNVector3(0, -5, 5);
                center = GroundNode.ConvertPositionToNode(center, null);
                Explosion(center, Balls);

                popTime = new DispatchTime(DispatchTime.Now, (long)(0.5 * Utils.NSEC_PER_SEC));
                DispatchQueue.MainQueue.DispatchAfter(popTime, () => {
                    TextManager.FlipOutText(SlideTextManager.TextType.Code);
                    TextManager.FlipOutText(SlideTextManager.TextType.Bullet);

                    TextManager.AddBulletAtLevel("Kinematic Bodies", 0);
                    TextManager.AddCode("#// Make a node kinematic\n"
                                        + "aNode.#physicsBody# = [SCNPhysicsBody #kinematicBody#];#");
                    TextManager.FlipInText(SlideTextManager.TextType.Bullet);
                    TextManager.FlipInText(SlideTextManager.TextType.Code);
                });
                break;

            case 11:
                var boxNode = SCNNode.Create();
                boxNode.Geometry = SCNBox.Create(10, 0.2f, 10, 0);
                boxNode.Position = new SCNVector3(0, 5, MIDDLE_Z);
                boxNode.Geometry.FirstMaterial.Emission.Contents = NSColor.DarkGray;
                boxNode.PhysicsBody = SCNPhysicsBody.CreateKinematicBody();
                boxNode.RunAction(SCNAction.RepeatActionForever(SCNAction.RotateBy(0, 0, NMath.PI * 2, 2.0)));
                GroundNode.AddChildNode(boxNode);
                KinematicItems.Add(boxNode);

                var invisibleWall = SCNNode.Create();
                invisibleWall.Geometry = SCNBox.Create(4, 40, 10, 0);
                invisibleWall.Position = new SCNVector3(-7, 0, MIDDLE_Z);
                invisibleWall.Geometry.FirstMaterial.Transparency = 0;
                invisibleWall.PhysicsBody = SCNPhysicsBody.CreateStaticBody();
                GroundNode.AddChildNode(invisibleWall);
                KinematicItems.Add(invisibleWall);

                invisibleWall          = (SCNNode)invisibleWall.Copy();
                invisibleWall.Position = new SCNVector3(7, 0, MIDDLE_Z);
                GroundNode.AddChildNode(invisibleWall);
                KinematicItems.Add(invisibleWall);

                invisibleWall          = (SCNNode)invisibleWall.Copy();
                invisibleWall.Geometry = SCNBox.Create(10, 40, 4, 0);
                invisibleWall.Geometry.FirstMaterial.Transparency = 0;
                invisibleWall.Position    = new SCNVector3(0, 0, MIDDLE_Z - 7);
                invisibleWall.PhysicsBody = SCNPhysicsBody.CreateStaticBody();
                GroundNode.AddChildNode(invisibleWall);
                KinematicItems.Add(invisibleWall);

                invisibleWall          = (SCNNode)invisibleWall.Copy();
                invisibleWall.Position = new SCNVector3(0, 0, MIDDLE_Z + 7);
                GroundNode.AddChildNode(invisibleWall);
                KinematicItems.Add(invisibleWall);


                for (int i = 0; i < 100; i++)
                {
                    var ball = SCNNode.Create();
                    worldPos      = boxNode.ConvertPositionToNode(new SCNVector3(RandFloat(-4, 4), RandFloat(10, 30), RandFloat(-1, 4)), null);
                    ball.Position = worldPos;
                    ball.Geometry = SCNSphere.Create(0.5f);
                    ball.Geometry.FirstMaterial.Diffuse.Contents = NSColor.Cyan;
                    ball.PhysicsBody = SCNPhysicsBody.CreateDynamicBody();
                    ((SCNView)presentationViewController.View).Scene.RootNode.AddChildNode(ball);

                    KinematicItems.Add(ball);
                }
                break;

            case 12:
                TextManager.FlipOutText(SlideTextManager.TextType.Code);
                TextManager.FlipOutText(SlideTextManager.TextType.Bullet);
                TextManager.FlipOutText(SlideTextManager.TextType.Subtitle);
                TextManager.SetSubtitle("SCNPhysicsShape");
                TextManager.AddCode("#// Configure the physics shape\n\n"
                                    + "aNode.physicsBody.#physicsShape# = \n\t[#SCNPhysicsShape# shapeWithGeometry:aGeometry options:options];#");
                TextManager.FlipInText(SlideTextManager.TextType.Bullet);
                TextManager.FlipInText(SlideTextManager.TextType.Code);

                KinematicItems[0].RunAction(SCNAction.Sequence(new SCNAction[] {
                    SCNAction.FadeOut(0.5),
                    SCNAction.RemoveFromParentNode()
                }));
                for (int i = 1; i < 5; i++)
                {
                    KinematicItems[i].RemoveFromParentNode();
                }

                KinematicItems = null;
                break;

            case 13:
                //add meshes
                PresentMeshes(presentationViewController);
                break;

            case 14:
                // remove meshes
                center = new SCNVector3(0, -5, 20);
                center = GroundNode.ConvertPositionToNode(center, null);
                Explosion(center, Meshes);
                break;

            case 15:
                // add shapes
                PresentPrimitives(presentationViewController);
                break;

            case 16:
                // remove shapes
                center = new SCNVector3(0, -5, 20);
                center = GroundNode.ConvertPositionToNode(center, null);
                Explosion(center, Shapes);

                popTime = new DispatchTime(DispatchTime.Now, (long)(0.5 * Utils.NSEC_PER_SEC));
                DispatchQueue.MainQueue.DispatchAfter(popTime, () => {
                    TextManager.FlipOutText(SlideTextManager.TextType.Code);
                    TextManager.FlipOutText(SlideTextManager.TextType.Bullet);
                    TextManager.FlipOutText(SlideTextManager.TextType.Subtitle);

                    TextManager.SetSubtitle("SCNPhysicsBehavior");
                    TextManager.AddCode("#// setup a physics behavior\n\n"
                                        + "#SCNPhysicsHingeJoint# *joint = [SCNPhysicsHingeJoint\n\n"
                                        + "jointWithBodyA:#nodeA.physicsBody# axisA:[...] anchorA:[...]\n\n"
                                        + "bodyB:#nodeB.physicsBody# axisB:[...] anchorB:[...]];\n\n\n"
                                        + "[scene.#physicsWorld# addBehavior:joint];#");

                    TextManager.FlipInText(SlideTextManager.TextType.Bullet);
                    TextManager.FlipInText(SlideTextManager.TextType.Subtitle);
                    TextManager.FlipInText(SlideTextManager.TextType.Code);
                });
                break;

            case 17:
                //add meshes
                PresentHinge(presentationViewController);
                break;

            case 18:
                //remove constraints
                ((SCNView)presentationViewController.View).Scene.PhysicsWorld.RemoveAllBehaviors();

                foreach (var node in Hinges)
                {
                    node.RunAction(SCNAction.Sequence(new SCNAction[] { SCNAction.Wait(3.0), SCNAction.FadeOut(0.5), SCNAction.RemoveFromParentNode() }));
                }

                break;

            case 19:
                TextManager.FlipOutText(SlideTextManager.TextType.Bullet);
                TextManager.FlipOutText(SlideTextManager.TextType.Subtitle);
                TextManager.FlipOutText(SlideTextManager.TextType.Code);

                TextManager.SetSubtitle("More...");

                TextManager.FlipInText(SlideTextManager.TextType.Subtitle);

                TextManager.AddBulletAtLevel("SCNPhysicsField", 0);
                TextManager.AddBulletAtLevel("SCNPhysicsVehicle", 0);

                TextManager.FlipInText(SlideTextManager.TextType.Bullet);
                TextManager.FlipInText(SlideTextManager.TextType.Code);
                break;
            }
        }
Пример #11
0
        void StartGame()
        {
            if (!gameNodes.HasValue)
            {
                throw new InvalidProgramException("Nodes not set");
            }

            var startSequence = SCNAction.Sequence(new SCNAction [] {
                // Wait for 1 second.
                SCNAction.Wait(1),
                SCNAction.Group(new SCNAction[] {
                    SCNAction.FadeIn(0.3),

                    // Start the game.
                    SCNAction.Run(node => {
                        if (!gameNodes.HasValue)
                        {
                            return;
                        }
                        var gNodes = gameNodes.Value;

                        var rnd = new Random();

                        // Compute a random orientation for the object3D.
                        var theta = (float)(Math.PI * rnd.NextDouble());
                        var phi   = (float)(Math.Acos(2 * rnd.NextDouble() - 1) / NMath.PI);

                        var axis = new Vector3 {
                            X = (float)(Math.Cos(theta) * Math.Sin(phi)),
                            Y = (float)(Math.Sin(theta) * Math.Sin(phi)),
                            Z = (float)Math.Cos(theta)
                        };
                        var angle = (float)(2 * Math.PI * rnd.NextDouble());

                        SCNTransaction.Begin();
                        SCNTransaction.AnimationDuration = 0.3;
                        SCNTransaction.SetCompletionBlock(() => gameStarted = true);

                        gNodes.ObjectMaterial.Transparency = 1;
                        gNodes.Object.Transform            = SCNMatrix4.CreateFromAxisAngle(axis, angle);

                        SCNTransaction.Commit();
                    })
                })
            });

            gameNodes.Value.Object.RunAction(startSequence);

            // Load and set the background image.
            var backgroundImage = UIImage.FromBundle("art.scnassets/background.png");

            sceneInterface.Scene.Background.Contents = backgroundImage;

            // Hide particles, set camera projection to orthographic.
            particleRemovalTimer?.Invalidate();
            gameNodes.Value.CongratulationsLabel.RemoveFromParent();
            gameNodes.Value.Confetti.Hidden = true;
            gameNodes.Value.Camera.UsesOrthographicProjection = true;

            // Reset the countdown.
            countdown = 30;
            gameNodes.Value.CountdownLabel.Text      = countdown.ToString();
            gameNodes.Value.CountdownLabel.FontColor = GameColors.DefaultFont;
            gameNodes.Value.CountdownLabel.Position  = new CGPoint(ContentFrame.Width / 2, ContentFrame.Height - 30);

            textUpdateTimer?.Invalidate();
            textUpdateTimer = NSTimer.CreateRepeatingScheduledTimer(1, UpdateText);
        }