public void ChainAnimation(string firstKey, string secondKey, float fadeTime)
        {
            CAAnimation firstAnim  = animationsDict [firstKey];
            CAAnimation secondAnim = animationsDict [secondKey];

            if (firstAnim == null || secondAnim == null)
            {
                return;
            }

            SCNAnimationEventHandler chainEventBlock = (CAAnimation animation, NSObject animatedObject, bool playingBackward) => {
                mainSkeleton.AddAnimation(secondAnim, secondKey);
            };

            if (firstAnim.AnimationEvents == null || firstAnim.AnimationEvents.Length == 0)
            {
                firstAnim.AnimationEvents = new SCNAnimationEvent[] { SCNAnimationEvent.Create(fadeTime, chainEventBlock) };
            }
            else
            {
                var pastEvents = new List <SCNAnimationEvent> (firstAnim.AnimationEvents);
                pastEvents.Add(SCNAnimationEvent.Create(fadeTime, chainEventBlock));
                firstAnim.AnimationEvents = pastEvents.ToArray();
            }
        }
        private void SetAnimation(CharacterAnimation index, string animationName, string sceneName)
        {
            // Load the DAE using SCNSceneSource in order to be able to retrieve the animation by its identifier
            var sceneURL    = NSUrl.FromFilename(NSBundle.MainBundle.PathForResource("Scenes.scnassets/boss/" + sceneName, "dae"));
            var sceneSource = SCNSceneSource.FromUrl(sceneURL, (NSDictionary)null);

            var bossAnimation = (CAAnimation)sceneSource.GetEntryWithIdentifier(animationName, new Class("CAAnimation"));

            Animations [(int)index] = bossAnimation;

            // Blend animations for smoother transitions
            bossAnimation.FadeInDuration  = 0.3f;
            bossAnimation.FadeOutDuration = 0.3f;

            if (index == CharacterAnimation.Attack)
            {
                // Create an animation event and set it to the animation
                var attackSoundEvent = new SCNAnimationEventHandler((CAAnimation animation, NSObject animatedObject, bool playingBackward) => {
                    InvokeOnMainThread(delegate {
                        var soundUrl = NSUrl.FromFilename(NSBundle.MainBundle.PathForResource("Sounds/attack4", "wav"));
                        new NSSound(soundUrl, false).Play();
                    });
                });
                bossAnimation.AnimationEvents = new SCNAnimationEvent[] { SCNAnimationEvent.Create(0.4f, attackSoundEvent) };
            }
        }
Exemple #3
0
        void SetupRunAnimation()
        {
            NSString runKey      = KeyForAnimationType(CharacterAnimation.Run);
            NSString runStartKey = KeyForAnimationType(CharacterAnimation.RunStart);
            NSString runStopKey  = KeyForAnimationType(CharacterAnimation.RunStop);

            CAAnimation runAnim      = LoadAndCacheAnimation("art.scnassets/characters/explorer/run", runKey);
            CAAnimation runStartAnim = LoadAndCacheAnimation("art.scnassets/characters/explorer/run_start", runStartKey);
            CAAnimation runStopAnim  = LoadAndCacheAnimation("art.scnassets/characters/explorer/run_stop", runStopKey);

            runAnim.RepeatCount      = float.MaxValue;
            runStartAnim.RepeatCount = 0;
            runStopAnim.RepeatCount  = 0;

            runAnim.FadeInDuration       = 0.05f;
            runAnim.FadeOutDuration      = 0.05f;
            runStartAnim.FadeInDuration  = 0.05f;
            runStartAnim.FadeOutDuration = 0.05f;
            runStopAnim.FadeInDuration   = 0.05f;
            runStopAnim.FadeOutDuration  = 0.05f;

            SCNAnimationEventHandler stepLeftBlock = (animation, animatedObject, playingBackward) => {
                GameSimulation.Sim.PlaySound("leftstep.caf");
            };

            SCNAnimationEventHandler stepRightBlock = (animation, animatedObject, playingBackward) => {
                GameSimulation.Sim.PlaySound("rightstep.caf");
            };

            SCNAnimationEventHandler startWalkStateBlock = (animation, animatedObject, playingBackward) => {
                if (InRunAnimation)
                {
                    IsWalking = true;
                }
                else
                {
                    mainSkeleton.RemoveAnimation(runKey, 0.15f);
                }
            };

            SCNAnimationEventHandler stopWalkStateBlock = (animation, animatedObject, playingBackward) => {
                IsWalking = false;
                TurnOffWalkingDust();
                if (changingDirection)
                {
                    inRunAnimation      = false;
                    InRunAnimation      = true;
                    changingDirection   = false;
                    playerWalkDirection = playerWalkDirection == WalkDirection.Left ? WalkDirection.Right : WalkDirection.Left;
                }
            };

            runStopAnim.AnimationEvents = new SCNAnimationEvent[] { SCNAnimationEvent.Create(1f, stopWalkStateBlock) };
            runAnim.AnimationEvents     = new SCNAnimationEvent[] {
                SCNAnimationEvent.Create(0.0f, startWalkStateBlock),
                SCNAnimationEvent.Create(0.25f, stepRightBlock),
                SCNAnimationEvent.Create(0.75f, stepLeftBlock)
            };
        }
Exemple #4
0
        void SetupJumpAnimation()
        {
            NSString jumpKey    = KeyForAnimationType(CharacterAnimation.Jump);
            NSString fallingKey = KeyForAnimationType(CharacterAnimation.JumpFalling);
            NSString landKey    = KeyForAnimationType(CharacterAnimation.JumpLand);
            NSString idleKey    = KeyForAnimationType(CharacterAnimation.Idle);

            CAAnimation jumpAnimation = LoadAndCacheAnimation("art.scnassets/characters/explorer/jump_start", jumpKey);
            CAAnimation fallAnimation = LoadAndCacheAnimation("art.scnassets/characters/explorer/jump_falling", fallingKey);
            CAAnimation landAnimation = LoadAndCacheAnimation("art.scnassets/characters/explorer/jump_land", landKey);

            jumpAnimation.FadeInDuration  = 0.15f;
            jumpAnimation.FadeOutDuration = 0.15f;
            fallAnimation.FadeInDuration  = 0.15f;
            landAnimation.FadeInDuration  = 0.15f;
            landAnimation.FadeOutDuration = 0.15f;

            jumpAnimation.RepeatCount = 0;
            fallAnimation.RepeatCount = 0;
            landAnimation.RepeatCount = 0;

            jumpForce = 7.0f;


            SCNAnimationEventHandler leaveGroundBlock = (animation, animatedObject, playingBackward) => {
                var jumpVelocity = new SCNVector3(0f, jumpForce * 2.1f, 0f);
                velocity        = SCNVector3.Add(velocity, jumpVelocity);
                Launching       = false;
                InJumpAnimation = false;
            };

            SCNAnimationEventHandler pause = (animation, animatedObject, playingBackward) => {
                mainSkeleton.PauseAnimation(fallingKey);
            };

            jumpAnimation.AnimationEvents = new SCNAnimationEvent[] { SCNAnimationEvent.Create(0.25f, leaveGroundBlock) };
            fallAnimation.AnimationEvents = new SCNAnimationEvent[] { SCNAnimationEvent.Create(0.5f, pause) };

            // Animation Sequence is to Jump -> Fall -> Land -> Idle.
            ChainAnimation(jumpKey, fallingKey);
            ChainAnimation(landKey, idleKey);
        }
        void SetupTauntAnimation()
        {
            string      path  = GameSimulation.PathForArtResource("characters/monkey/monkey_tree_hang_taunt");
            CAAnimation taunt = LoadAndCacheAnimation(path, "monkey_tree_hang_taunt-1");

            taunt.RepeatCount = 0;

            SCNAnimationEventHandler ackBlock = (CAAnimation animation, NSObject animatedObject, bool playingBackward) => {
                GameSimulation.Sim.PlaySound("ack.caf");
            };

            SCNAnimationEventHandler idleBlock = (CAAnimation animation, NSObject animatedObject, bool playingBackward) => {
                isIdle = true;
            };

            taunt.AnimationEvents = new SCNAnimationEvent[] {
                SCNAnimationEvent.Create(0.35f, ackBlock),
                SCNAnimationEvent.Create(1.0f, idleBlock)
            };
        }
Exemple #6
0
        private void LoadAnimations()
        {
            var idleAnimation = Character.LoadAnimation("art.scnassets/character/max_idle.scn");

            this.model.AddAnimation(idleAnimation, new NSString("idle"));
            idleAnimation.Play();

            var walkAnimation = Character.LoadAnimation("art.scnassets/character/max_walk.scn");

            walkAnimation.Speed = Character.SpeedFactor;
            walkAnimation.Stop();

            if (Character.IsEnableFootStepSound)
            {
                walkAnimation.Animation.AnimationEvents = new SCNAnimationEvent[]
                {
                    SCNAnimationEvent.Create(0.1f, (animation, animatedObject, playingBackward) => { this.PlayFootStep(); }),
                    SCNAnimationEvent.Create(0.6f, (animation, animatedObject, playingBackward) => { this.PlayFootStep(); })
                };
            }

            this.model.AddAnimation(walkAnimation, new NSString("walk"));

            var jumpAnimation = Character.LoadAnimation("art.scnassets/character/max_jump.scn");

            jumpAnimation.Animation.RemovedOnCompletion = false;
            jumpAnimation.Stop();
            jumpAnimation.Animation.AnimationEvents = new SCNAnimationEvent[] { SCNAnimationEvent.Create(0f, (animation, animatedObject, playingBackward) => { this.PlayJumpSound(); }) };
            this.model.AddAnimation(jumpAnimation, new NSString("jump"));

            var spinAnimation = Character.LoadAnimation("art.scnassets/character/max_spin.scn");

            spinAnimation.Animation.RemovedOnCompletion = false;
            spinAnimation.Speed = 1.5f;
            spinAnimation.Stop();
            spinAnimation.Animation.AnimationEvents = new SCNAnimationEvent[] { SCNAnimationEvent.Create(0f, (animation, animatedObject, playingBackward) => { this.PlayAttackSound(); }) };
            this.model.AddAnimation(spinAnimation, new NSString("spin"));
        }
        void SetupGetCoconutAnimation()
        {
            SCNAnimationEventHandler pickupEventBlock = (CAAnimation animation, NSObject animatedObject, bool playingBackward) => {
                if (coconutInHand != null)
                {
                    coconutInHand.RemoveFromParentNode();
                }

                coconutInHand = Coconut.CoconutProtoObject;
                rightHand.AddChildNode(coconutInHand);
                hasCoconut = true;
            };

            CAAnimation getAnimation = LoadAndCacheAnimation(GameSimulation.PathForArtResource("characters/monkey/monkey_get_coconut"), "monkey_get_coconut-1");

            if (getAnimation.AnimationEvents == null)
            {
                getAnimation.AnimationEvents = new SCNAnimationEvent[] { SCNAnimationEvent.Create(0.4f, pickupEventBlock) }
            }
            ;

            getAnimation.RepeatCount = 1;
        }

        void SetupThrowAnimation()
        {
            CAAnimation throwAnimation = LoadAndCacheAnimation(GameSimulation.PathForArtResource("characters/monkey/monkey_throw_coconut"), "monkey_throw_coconut-1");

            throwAnimation.Speed = 1.5f;

            if (throwAnimation.AnimationEvents == null || throwAnimation.AnimationEvents.Length == 0)
            {
                SCNAnimationEventHandler throwEventBlock = ThrowCoconut;

                throwAnimation.AnimationEvents = new SCNAnimationEvent[] { SCNAnimationEvent.Create(0.35f, throwEventBlock) };
            }

            throwAnimation.RepeatCount = 0;
        }

        void ThrowCoconut(CAAnimation animation, NSObject animatedObject, bool playingBackward)
        {
            if (!hasCoconut)
            {
                return;
            }

            SCNMatrix4 worldMtx = coconutInHand.PresentationNode.WorldTransform;

            coconutInHand.RemoveFromParentNode();

            Coconut         node = Coconut.CoconutThrowProtoObject;
            SCNPhysicsShape coconutPhysicsShape = Coconut.CoconutPhysicsShape;

            node.PhysicsBody                  = SCNPhysicsBody.CreateBody(SCNPhysicsBodyType.Dynamic, coconutPhysicsShape);
            node.PhysicsBody.Restitution      = 0.9f;
            node.PhysicsBody.CollisionBitMask = GameCollisionCategory.Player | GameCollisionCategory.Ground;
            node.PhysicsBody.CategoryBitMask  = GameCollisionCategory.Coconut;

            node.Transform = worldMtx;
            GameSimulation.Sim.RootNode.AddChildNode(node);
            GameSimulation.Sim.GameLevel.Coconuts.Add(node);
            node.PhysicsBody.ApplyForce(new SCNVector3(-200, 500, 300), true);
            hasCoconut = false;
            isIdle     = true;
        }
    }
Exemple #8
0
        public override void SetupSlide(PresentationViewController presentationViewController)
        {
            TextManager.SetTitle("Constraints");
            TextManager.SetSubtitle("SCNIKConstraint");

            TextManager.AddBulletAtLevel("Inverse Kinematics", 0);
            TextManager.AddBulletAtLevel("Node chain", 0);
            TextManager.AddBulletAtLevel("Target", 0);

            //load the hero
            Hero          = Utils.SCAddChildNode(GroundNode, "heroGroup", "Scenes.scnassets/hero/hero", 12);
            Hero.Position = new SCNVector3(0, 0, 5);
            Hero.Rotation = new SCNVector4(1, 0, 0, -(nfloat)Math.PI / 2);

            //hide the sword
            var sword = Hero.FindChildNode("sword", true);

            sword.Hidden = true;

            //load attack animation
            var path   = NSBundle.MainBundle.PathForResource("Scenes.scnassets/hero/attack", "dae");
            var source = SCNSceneSource.FromUrl(NSUrl.FromFilename(path), (NSDictionary)null);

            Attack                 = (CAAnimation)source.GetEntryWithIdentifier("attackID", new Class("CAAnimation"));
            Attack.RepeatCount     = 0;
            Attack.FadeInDuration  = 0.1f;
            Attack.FadeOutDuration = 0.3f;
            Attack.Speed           = 0.75f;

            Attack.AnimationEvents = new SCNAnimationEvent[] { SCNAnimationEvent.Create(0.55f, (CAAnimation animation, NSObject animatedObject, bool playingBackward) => {
                    if (IkActive)
                    {
                        DestroyTarget();
                    }
                }) };

            AnimationDuration = Attack.Duration;

            //setup IK
            var hand     = Hero.FindChildNode("Bip01_R_Hand", true);
            var clavicle = Hero.FindChildNode("Bip01_R_Clavicle", true);
            var head     = Hero.FindChildNode("Bip01_Head", true);

            Ik = SCNIKConstraint.Create(clavicle);
            hand.Constraints   = new SCNConstraint[] { Ik };
            Ik.InfluenceFactor = 0.0f;

            //add target
            Target          = SCNNode.Create();
            Target.Position = new SCNVector3(-4, 7, 10);
            Target.Opacity  = 0;
            Target.Geometry = SCNPlane.Create(2, 2);
            Target.Geometry.FirstMaterial.Diffuse.Contents = new NSImage(NSBundle.MainBundle.PathForResource("Images/target", "png"));
            GroundNode.AddChildNode(Target);

            //look at
            LookAt = SCNLookAtConstraint.Create(Target);
            LookAt.InfluenceFactor = 0;
            head.Constraints       = new SCNConstraint[] { LookAt };

            ((SCNView)presentationViewController.View).WeakSceneRendererDelegate = this;
        }
Exemple #9
0
        private void SetAnimation(CharacterAnimation index, string animationName, string sceneName)
        {
            // Load the DAE using SCNSceneSource in order to be able to retrieve the animation by its identifier
            var path        = NSBundle.MainBundle.PathForResource("Scenes/hero/" + sceneName, "dae");
            var sceneURL    = NSUrl.FromFilename(path);
            var sceneSource = SCNSceneSource.FromUrl(sceneURL, (NSDictionary)null);

            var animation = (CAAnimation)sceneSource.GetEntryWithIdentifier(animationName, new Class("CAAnimation"));

            Animations [(int)index] = animation;

            // Blend animations for smoother transitions
            animation.FadeInDuration  = 0.3f;
            animation.FadeOutDuration = 0.3f;

            if (index == CharacterAnimation.Die)
            {
                // We want the "death" animation to remain at its final state at the end of the animation
                animation.RemovedOnCompletion = false;
                animation.FillMode            = CAFillMode.Both;

                // Create animation events and set them to the animation
                var swipeSoundEventHandler = new SCNAnimationEventHandler((CAAnimation handlerAnimation, NSObject animatedObject, bool playingBackward) => {
                    InvokeOnMainThread(delegate {
                        var soundUrl = NSUrl.FromFilename(NSBundle.MainBundle.PathForResource("Sounds/swipe", "wav"));
                        new NSSound(soundUrl, false).Play();
                    });
                });

                var deathSoundEventBlock = new SCNAnimationEventHandler((CAAnimation handlerAnimation, NSObject animatedObject, bool playingBackward) => {
                    InvokeOnMainThread(delegate {
                        var soundUrl = NSUrl.FromFilename(NSBundle.MainBundle.PathForResource("Sounds/death", "wav"));
                        new NSSound(soundUrl, false).Play();
                    });
                });

                animation.AnimationEvents = new SCNAnimationEvent[] {
                    SCNAnimationEvent.Create(0.0f, swipeSoundEventHandler),
                    SCNAnimationEvent.Create(0.3f, deathSoundEventBlock)
                };
            }

            if (index == CharacterAnimation.Attack)
            {
                // Create an animation event and set it to the animation
                var swordSoundEventHandler = new SCNAnimationEventHandler((CAAnimation handlerAnimation, NSObject animatedObject, bool playingBackward) => {
                    InvokeOnMainThread(delegate {
                        var soundUrl    = NSUrl.FromFilename(NSBundle.MainBundle.PathForResource("Sounds/sword", "wav"));
                        var attackSound = new NSSound(soundUrl, false);
                        attackSound.Play();
                    });
                });

                animation.AnimationEvents = new SCNAnimationEvent[] { SCNAnimationEvent.Create(0.4f, swordSoundEventHandler) };
            }

            if (index == CharacterAnimation.Walk)
            {
                // Repeat the walk animation 3 times
                animation.RepeatCount = 3;

                // Create an animation event and set it to the animation
                var stepSoundEventHandler = new SCNAnimationEventHandler((CAAnimation handlerAnimation, NSObject animatedObject, bool playingBackward) => {
                    InvokeOnMainThread(delegate {
                        var soundUrl = NSUrl.FromFilename(NSBundle.MainBundle.PathForResource("Sounds/walk", "wav"));
                        new NSSound(soundUrl, false).Play();
                    });
                });

                animation.AnimationEvents = new SCNAnimationEvent[] {
                    SCNAnimationEvent.Create(0.2f, stepSoundEventHandler),
                    SCNAnimationEvent.Create(0.7f, stepSoundEventHandler)
                };
            }
        }
Exemple #10
0
        public Character()
        {
            for (int i = 0; i < StepsSoundCount; i++)
            {
                steps [i, (int)FloorMaterial.Grass]        = SCNAudioSource.FromFile(string.Format("game.scnassets/sounds/Step_grass_0{0}.mp3", i));
                steps [i, (int)FloorMaterial.Grass].Volume = 0.5f;
                steps [i, (int)FloorMaterial.Rock]         = SCNAudioSource.FromFile(string.Format("game.scnassets/sounds/Step_rock_0{0}.mp3", i));
                if (i < StepsInWaterSoundCount)
                {
                    steps [i, (int)FloorMaterial.Water] = SCNAudioSource.FromFile(string.Format("game.scnassets/sounds/Step_splash_0{0}.mp3", i));
                    steps [i, (int)FloorMaterial.Water].Load();
                }
                else
                {
                    steps [i, (int)FloorMaterial.Water] = steps [i % StepsInWaterSoundCount, (int)FloorMaterial.Water];
                }

                steps [i, (int)FloorMaterial.Rock].Load();
                steps [i, (int)FloorMaterial.Grass].Load();

                // Load the character.
                SCNScene characterScene        = SCNScene.FromFile("game.scnassets/panda.scn");
                SCNNode  characterTopLevelNode = characterScene.RootNode.ChildNodes [0];

                Node = SCNNode.Create();
                Node.AddChildNode(characterTopLevelNode);

                // Configure the "idle" animation to repeat forever
                foreach (var childNode in characterTopLevelNode.ChildNodes)
                {
                    foreach (var key in childNode.GetAnimationKeys())
                    {
                        CAAnimation animation = childNode.GetAnimation(key);
                        animation.UsesSceneTimeBase = false;
                        animation.RepeatCount       = float.PositiveInfinity;
                        childNode.AddAnimation(animation, key);
                    }
                }

                // retrieve some particle systems and save their birth rate
                fireEmitter   = characterTopLevelNode.FindChildNode("fire", true);
                fireBirthRate = fireEmitter.ParticleSystems [0].BirthRate;
                fireEmitter.ParticleSystems [0].BirthRate = 0;
                fireEmitter.Hidden = false;

                smokeEmitter   = characterTopLevelNode.FindChildNode("smoke", true);
                smokeBirthRate = smokeEmitter.ParticleSystems [0].BirthRate;
                smokeEmitter.ParticleSystems [0].BirthRate = 0;
                smokeEmitter.Hidden = false;

                whiteSmokeEmitter   = characterTopLevelNode.FindChildNode("whiteSmoke", true);
                whiteSmokeBirthRate = whiteSmokeEmitter.ParticleSystems [0].BirthRate;
                whiteSmokeEmitter.ParticleSystems [0].BirthRate = 0;
                whiteSmokeEmitter.Hidden = false;

                SCNVector3 min = SCNVector3.Zero;
                SCNVector3 max = SCNVector3.Zero;

                Node.GetBoundingBox(ref min, ref max);

                float radius = (max.X - min.X) * .4f;
                float height = (max.Y - min.Y);

                // Create a kinematic with capsule.
                SCNNode colliderNode = SCNNode.Create();
                colliderNode.Name        = "collider";
                colliderNode.Position    = new SCNVector3(0f, height * .51f, 0f);
                colliderNode.PhysicsBody = SCNPhysicsBody.CreateBody(
                    SCNPhysicsBodyType.Kinematic,
                    SCNPhysicsShape.Create(SCNCapsule.Create(radius, height))
                    );

                // We want contact notifications with the collectables, enemies and walls.
                colliderNode.PhysicsBody.ContactTestBitMask = (nuint)(int)(Bitmask.SuperCollectable | Bitmask.Collectable | Bitmask.Collision | Bitmask.Enemy);
                Node.AddChildNode(colliderNode);

                walkAnimation = LoadAnimationFromSceneNamed("game.scnassets/walk.scn");
                walkAnimation.UsesSceneTimeBase = false;
                walkAnimation.FadeInDuration    = .3f;
                walkAnimation.FadeOutDuration   = .3f;
                walkAnimation.RepeatCount       = float.PositiveInfinity;
                walkAnimation.Speed             = CharacterSpeedFactor;

                // Play foot steps at specific times in the animation
                walkAnimation.AnimationEvents = new [] {
                    SCNAnimationEvent.Create(.1f, (animation, animatedObject, playingBackward) => PlayFootStep()),
                    SCNAnimationEvent.Create(.6f, (animation, animatedObject, playingBackward) => PlayFootStep())
                };
            }
        }
Exemple #11
0
        public override void PresentStep(int index, PresentationViewController presentationViewController)
        {
            // Animate by default
            SCNTransaction.Begin();
            SCNTransaction.AnimationDuration = 0;

            switch (index)
            {
            case 0:
                TextManager.FlipInText(SlideTextManager.TextType.Bullet);
                break;

            case 1:
                TextManager.FlipOutText(SlideTextManager.TextType.Bullet);
                TextManager.AddEmptyLine();
                TextManager.AddCode("#// Rotate forever\n"
                                    + "[aNode #runAction:#\n"
                                    + "  [SCNAction repeatActionForever:\n"
                                    + "  [SCNAction rotateByX:0 y:M_PI*2 z:0 duration:5.0]]];#");

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

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

                TextManager.AddBulletAtLevel("Move", 0);
                TextManager.AddBulletAtLevel("Rotate", 0);
                TextManager.AddBulletAtLevel("Scale", 0);
                TextManager.AddBulletAtLevel("Opacity", 0);
                TextManager.AddBulletAtLevel("Remove", 0);
                TextManager.AddBulletAtLevel("Wait", 0);
                TextManager.AddBulletAtLevel("Custom block", 0);

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

            case 3:
                TextManager.FlipOutText(SlideTextManager.TextType.Bullet);
                TextManager.AddEmptyLine();
                TextManager.AddBulletAtLevel("Directly targets the render tree", 0);
                TextManager.FlipInText(SlideTextManager.TextType.Bullet);
                break;

            case 4:
                TextManager.AddBulletAtLevel("node.position / node.presentationNode.position", 0);

                //labels
                var label1 = TextManager.AddTextAtLevel("Action", 0);
                label1.Position = new SCNVector3(-15, 3, 0);
                var label2 = TextManager.AddTextAtLevel("Animation", 0);
                label2.Position = new SCNVector3(-15, -2, 0);

                //animation
                var animNode = SCNNode.Create();
                var cubeSize = 4.0f;
                animNode.Position = new SCNVector3(-5, cubeSize / 2, 0);

                var cube = SCNBox.Create(cubeSize, cubeSize, cubeSize, 0.05f * cubeSize);

                cube.FirstMaterial.Diffuse.Contents  = new NSImage(NSBundle.MainBundle.PathForResource("SharedTextures/texture", "png"));
                cube.FirstMaterial.Diffuse.MipFilter = SCNFilterMode.Linear;
                cube.FirstMaterial.Diffuse.WrapS     = SCNWrapMode.Repeat;
                cube.FirstMaterial.Diffuse.WrapT     = SCNWrapMode.Repeat;
                animNode.Geometry = cube;
                ContentNode.AddChildNode(animNode);

                SCNTransaction.Begin();


                SCNNode           animPosIndicator = null;
                SCNAnimationEvent startEvt         = SCNAnimationEvent.Create(0, (CAAnimation animation, NSObject animatedObject, bool playingBackward) => {
                    SCNTransaction.Begin();
                    SCNTransaction.AnimationDuration = 0;
                    animPosIndicator.Position        = new SCNVector3(10, animPosIndicator.Position.Y, animPosIndicator.Position.Z);
                    SCNTransaction.Commit();
                });
                SCNAnimationEvent endEvt = SCNAnimationEvent.Create(1, (CAAnimation animation, NSObject animatedObject, bool playingBackward) => {
                    SCNTransaction.Begin();
                    SCNTransaction.AnimationDuration = 0;
                    animPosIndicator.Position        = new SCNVector3(-5, animPosIndicator.Position.Y, animPosIndicator.Position.Z);
                    SCNTransaction.Commit();
                });

                var anim = CABasicAnimation.FromKeyPath("position.x");
                anim.Duration        = 3;
                anim.From            = new NSNumber(0.0);
                anim.To              = new NSNumber(15.0);
                anim.Additive        = true;
                anim.AutoReverses    = true;
                anim.AnimationEvents = new SCNAnimationEvent[] { startEvt, endEvt };
                anim.RepeatCount     = float.MaxValue;
                animNode.AddAnimation(anim, new NSString("cubeAnimation"));

                //action
                var actionNode = SCNNode.Create();
                actionNode.Position = new SCNVector3(-5, cubeSize * 1.5f + 1, 0);
                actionNode.Geometry = cube;

                ContentNode.AddChildNode(actionNode);

                var mv = SCNAction.MoveBy(15, 0, 0, 3);

                actionNode.RunAction(SCNAction.RepeatActionForever(SCNAction.Sequence(new SCNAction[] {
                    mv,
                    mv.ReversedAction()
                })));

                //position indicator
                var positionIndicator = SCNNode.Create();
                positionIndicator.Geometry = SCNCylinder.Create(0.5f, 0.01f);
                positionIndicator.Geometry.FirstMaterial.Diffuse.Contents  = NSColor.Red;
                positionIndicator.Geometry.FirstMaterial.LightingModelName = SCNLightingModel.Constant;
                positionIndicator.EulerAngles = new SCNVector3(NMath.PI / 2, 0, 0);
                positionIndicator.Position    = new SCNVector3(0, 0, cubeSize * 0.5f);
                actionNode.AddChildNode(positionIndicator);

                //anim pos indicator
                animPosIndicator          = positionIndicator.Clone();
                animPosIndicator.Position = new SCNVector3(5, cubeSize / 2, cubeSize * 0.5f);
                ContentNode.AddChildNode(animPosIndicator);

                SCNTransaction.Commit();

                break;
            }
            SCNTransaction.Commit();
        }