/// <inheritdoc /> /// <summary> /// Ctor /// </summary> /// <param name="type"></param> /// <param name="processor"></param> internal HealthBar(HealthBarType type, ScoreProcessor processor) : base(SkinManager.Skin.HealthBarBackground) { Type = type; Processor = processor; Size = new ScalableVector2(Frames.First().Width, Frames.First().Height); // Start animation StartLoop(Direction.Forward, 60); // Create the foreground bar (the one that'll serve as the gauge progress). ForegroundBar = new AnimatableSprite(SkinManager.Skin.HealthBarForeground) { Parent = this, SpriteBatchOptions = new SpriteBatchOptions() { SortMode = SpriteSortMode.Deferred, BlendState = BlendState.NonPremultiplied, SamplerState = SamplerState.PointClamp, DepthStencilState = DepthStencilState.Default, RasterizerState = RasterizerState.CullNone, Shader = new Shader(GameBase.Game.Resources.Get("Quaver.Resources/Shaders/semi-transparent.mgfxo"), new Dictionary <string, object>() { { "p_position", new Vector2() }, { "p_rectangle", new Vector2() }, { "p_dimensions", new Vector2() }, { "p_alpha", 0f } }) } }; ForegroundBar.Size = new ScalableVector2(ForegroundBar.Frames.First().Width, ForegroundBar.Frames.First().Height); // Start animation. ForegroundBar.StartLoop(Direction.Forward, 60); switch (Type) { case HealthBarType.Horizontal: Alignment = Alignment.TopLeft; ForegroundBar.Alignment = Alignment.TopLeft; ForegroundBar.SpriteBatchOptions.Shader.SetParameter("p_position", new Vector2(ForegroundBar.Width, 0f), true); break; case HealthBarType.Vertical: Alignment = Alignment.BotLeft; ForegroundBar.Alignment = Alignment.TopLeft; ForegroundBar.SpriteBatchOptions.Shader.SetParameter("p_position", new Vector2(0, 0), true); break; default: throw new NotImplementedException(); } // Set default shader params. ForegroundBar.SpriteBatchOptions.Shader.SetParameter("p_rectangle", new Vector2(Width, Height), true); ForegroundBar.SpriteBatchOptions.Shader.SetParameter("p_dimensions", new Vector2(Width, Height), true); }
public override void onAddedToScene() { // TODO - this should happen automatically scale = new Vector2(5, 5); var gameScene = (HandyScene)scene; var animationDefinition = gameScene.AnimationDefinitions[AsepriteFiles.SPACEMAN_SHIELD]; var sprite = new AnimatableSprite(animationDefinition.SpriteDefinition.Subtextures); sprite.renderLayer = RenderLayers.PRIMARY; var animation = new AnimationComponent(sprite, animationDefinition, SpacemanAnimations.SHIELD); var collider = new BoxCollider(5 * 1.5f, 5 * 5.5f); collider.localOffset = new Vector2(5, 0); Flags.setFlagExclusive(ref collider.physicsLayer, PhysicsLayers.BACK_WALLS); Flags.setFlag(ref collider.collidesWithLayers, PhysicsLayers.BALL); var events = new EventComponent(); events.SetTriggers(_triggers); addComponent(sprite); addComponent(animation); addComponent(events); addComponent(collider); position = new Vector2(position.X + 5 * 15, position.Y); }
/// <summary> /// Initialize HitObject Sprite used for Object Pooling. Only gets initialized once upon object creation. /// </summary> /// <param name="info"></param> /// <param name="ruleset"></param> private void InitializeSprites(GameplayRulesetKeys ruleset, int lane) { // Reference variables var playfield = (GameplayPlayfieldKeys)ruleset.Playfield; var posX = playfield.Stage.Receptors[lane].X; // Create the base HitObjectSprite HitObjectSprite = new Sprite() { Alignment = Alignment.TopLeft, Position = new ScalableVector2(posX, 0), SpriteEffect = !GameplayRulesetKeys.IsDownscroll && SkinManager.Skin.Keys[MapManager.Selected.Value.Mode].FlipNoteImagesOnUpscroll ? SpriteEffects.FlipVertically : SpriteEffects.None, Image = UserInterface.BlankBox, }; // Update hit body's size to match image ratio HitObjectSprite.Size = new ScalableVector2(playfield.LaneSize, playfield.LaneSize * HitObjectSprite.Image.Height / HitObjectSprite.Image.Width); LongNoteBodyOffset = HitObjectSprite.Height / 2; // Create Hold Body var bodies = SkinManager.Skin.Keys[ruleset.Mode].NoteHoldBodies[lane]; LongNoteBodySprite = new AnimatableSprite(bodies) { Alignment = Alignment.TopLeft, Size = new ScalableVector2(playfield.LaneSize, 0), Position = new ScalableVector2(posX, 0), Parent = playfield.Stage.HitObjectContainer }; // Create the Hold End LongNoteEndSprite = new Sprite() { Alignment = Alignment.TopLeft, Position = new ScalableVector2(posX, 0), Size = new ScalableVector2(playfield.LaneSize, 0), Parent = playfield.Stage.HitObjectContainer, SpriteEffect = !GameplayRulesetKeys.IsDownscroll && SkinManager.Skin.Keys[MapManager.Selected.Value.Mode].FlipNoteEndImagesOnUpscroll ? SpriteEffects.FlipVertically : SpriteEffects.None, }; // Set long note end properties. LongNoteEndSprite.Image = SkinManager.Skin.Keys[ruleset.Mode].NoteHoldEnds[lane]; LongNoteEndSprite.Height = playfield.LaneSize * LongNoteEndSprite.Image.Height / LongNoteEndSprite.Image.Width; LongNoteEndOffset = LongNoteEndSprite.Height / 2f; // We set the parent of the HitObjectSprite **AFTER** we create the long note // so that the body of the long note isn't drawn over the object. HitObjectSprite.Parent = playfield.Stage.HitObjectContainer; }
public override void onAddedToScene() { var gameScene = (HandyScene)scene; foreach (var character in _stateComponent.State.CharacterOrder) { // not moving this to shared code // as we may decide we want different sprites/animations for the choice menu // e.g. "them posing and looking dope" or whatever AnimationDefinition animationDefinition; AnimatableSprite sprite; switch (character) { case Gameplay.Character.KNIGHT: animationDefinition = gameScene.AnimationDefinitions[AsepriteFiles.KNIGHT]; sprite = new AnimatableSprite(animationDefinition.SpriteDefinition.Subtextures); CharacterChoiceSprites.Add(character, sprite); CharacterChoiceAnimations.Add(character, new AnimationComponent(sprite, animationDefinition, KnightAnimations.WALK_FORWARD)); break; case Gameplay.Character.WIZARD: animationDefinition = gameScene.AnimationDefinitions[AsepriteFiles.WIZARD]; sprite = new AnimatableSprite(animationDefinition.SpriteDefinition.Subtextures); CharacterChoiceSprites.Add(character, sprite); CharacterChoiceAnimations.Add(character, new AnimationComponent(sprite, animationDefinition, WizardAnimations.WALK_DOWN)); break; case Gameplay.Character.SPACEMAN: animationDefinition = gameScene.AnimationDefinitions[AsepriteFiles.SPACEMAN]; sprite = new AnimatableSprite(animationDefinition.SpriteDefinition.Subtextures); CharacterChoiceSprites.Add(character, sprite); CharacterChoiceAnimations.Add(character, new AnimationComponent(sprite, animationDefinition, SpacemanAnimations.WALK_DOWN)); break; case Gameplay.Character.ALIEN: throw new NotImplementedException(); case Gameplay.Character.PIRATE: throw new NotImplementedException(); case Gameplay.Character.SKELETON: throw new NotImplementedException(); default: throw new ArgumentOutOfRangeException(); } } CharacterChoiceSprites.Values.ToList().ForEach(s => addComponent(s)); CharacterChoiceSprites.Values.ToList().ForEach(s => s.renderLayer = RenderLayers.PRIMARY); CharacterChoiceAnimations.Values.ToList().ForEach(a => addComponent(a)); }
/// <summary> /// Initialize HitObject Sprite used for Object Pooling. Only gets initialized once upon object creation. /// </summary> /// <param name="info"></param> /// <param name="ruleset"></param> private void InitializeSprites(GameplayRulesetKeys ruleset, int lane, ScrollDirection direction) { // Reference variables var playfield = (GameplayPlayfieldKeys)ruleset.Playfield; var posX = playfield.Stage.Receptors[lane].X; var flipNoteBody = direction.Equals(ScrollDirection.Up) && SkinManager.Skin.Keys[MapManager.Selected.Value.Mode].FlipNoteImagesOnUpscroll; var flipNoteEnd = direction.Equals(ScrollDirection.Up) && SkinManager.Skin.Keys[MapManager.Selected.Value.Mode].FlipNoteEndImagesOnUpscroll; ScrollDirection = direction; // Create the base HitObjectSprite HitObjectSprite = new Sprite() { Alignment = Alignment.TopLeft, Position = new ScalableVector2(posX, 0), SpriteEffect = flipNoteBody ? SpriteEffects.FlipVertically : SpriteEffects.None }; // Create Hold Body var bodies = SkinManager.Skin.Keys[ruleset.Mode].NoteHoldBodies[lane]; LongNoteBodySprite = new AnimatableSprite(bodies) { Alignment = Alignment.TopLeft, Size = new ScalableVector2(playfield.LaneSize, 0), Position = new ScalableVector2(posX, 0), Parent = playfield.Stage.HitObjectContainer }; // Create the Hold End LongNoteEndSprite = new Sprite() { Alignment = Alignment.TopLeft, Position = new ScalableVector2(posX, 0), Size = new ScalableVector2(playfield.LaneSize, 0), Parent = playfield.Stage.HitObjectContainer, SpriteEffect = flipNoteEnd ? SpriteEffects.FlipVertically : SpriteEffects.None }; // Set long note end properties. LongNoteEndSprite.Image = SkinManager.Skin.Keys[ruleset.Mode].NoteHoldEnds[lane]; LongNoteEndSprite.Height = playfield.LaneSize * LongNoteEndSprite.Image.Height / LongNoteEndSprite.Image.Width; LongNoteEndOffset = LongNoteEndSprite.Height / 2f; // We set the parent of the HitObjectSprite **AFTER** we create the long note // so that the body of the long note isn't drawn over the object. HitObjectSprite.Parent = playfield.Stage.HitObjectContainer; }
public override void onAddedToScene() { var gameScene = (Handy.Scene)scene; var animationDefinition = gameScene.AnimationDefinitions[AsepriteFiles.HIT_EFFECT]; var sprite = new AnimatableSprite(animationDefinition.SpriteDefinition.Subtextures); sprite.renderLayer = RenderLayers.FOREGROUND; var animationComponent = new AnimationComponent(sprite, animationDefinition, "DEFAULT"); var eventComponent = new EventComponent(); eventComponent.AddEvent("DEFAULT", 3, "END"); addComponent(sprite); addComponent(animationComponent); addComponent(eventComponent); addComponent(new HitEffectComponent()); }
public override void onAddedToScene() { var gameScene = (HandyScene)scene; /* * SPRITE AND ANIMATION */ var subtextures = Util.ExtractSubtextures(gameScene.Textures[Constants.BallSprites.DEFAULT], 1, 1); _sprite = new AnimatableSprite(subtextures); _sprite.renderLayer = RenderLayers.PRIMARY; /* * COLLISIONS AND KINEMATICS */ // must generate collider after we create the sprite, // otherwise the collider doesn't know how big it is (that's how it default works) _collider = new CircleCollider(9); Flags.setFlagExclusive(ref _collider.collidesWithLayers, PhysicsLayers.BACK_WALLS); Flags.setFlag(ref _collider.collidesWithLayers, PhysicsLayers.SIDE_WALLS); _kinematic.CollisionType = KinematicComponent.ECollisionType.Bounce; /* * STATE */ _ballStateComponent = new BallStateComponent(); /* * PARTICLES */ _particleEmitter = new ParticleEmitterComponent(generateParticleEmitterConfig(gameScene.Textures[BallSprites.DEFAULT])); _particleEmitter._active = true; _particleEmitter.renderLayer = RenderLayers.PRIMARY; _particleEmitter.renderOffset = -1; addComponent(_kinematic); addComponent(_sprite); addComponent(_collider); addComponent(_ballStateComponent); addComponent(_particleEmitter); scale = new Vector2(3, 3); }
public override void onAddedToScene() { var gameScene = (HandyScene)scene; AnimationDefinition animationDefinition; Vector2[] lightPoints; Vector2[] heavyPoints; switch (Settings.Character) { case Gameplay.Character.KNIGHT: _state = new KnightStateMachineComponent(new KnightState()); _events.SetTriggers(_knightEventTriggers); animationDefinition = gameScene.AnimationDefinitions[AsepriteFiles.KNIGHT]; lightPoints = scene.content.Load <Polygon>(Hitboxes.KNIGHT_HITBOX_LIGHT).points; heavyPoints = scene.content.Load <Polygon>(Hitboxes.KNIGHT_HITBOX_HEAVY).points; break; case Gameplay.Character.WIZARD: _state = new WizardStateMachineComponent(new WizardState()); _events.SetTriggers(_wizardEventTriggers); animationDefinition = gameScene.AnimationDefinitions[AsepriteFiles.WIZARD]; lightPoints = scene.content.Load <Polygon>(Hitboxes.WIZARD_HITBOX_LIGHT).points; heavyPoints = scene.content.Load <Polygon>(Hitboxes.WIZARD_HITBOX_HEAVY).points; break; case Gameplay.Character.SPACEMAN: _state = new SpacemanStateMachineComponent(new SpacemanState()); _events.SetTriggers(_spacemanEventTriggers); animationDefinition = gameScene.AnimationDefinitions[AsepriteFiles.SPACEMAN]; lightPoints = scene.content.Load <Polygon>(Hitboxes.KNIGHT_HITBOX_LIGHT).points; heavyPoints = scene.content.Load <Polygon>(Hitboxes.KNIGHT_HITBOX_HEAVY).points; break; case Gameplay.Character.ALIEN: throw new NotImplementedException(); case Gameplay.Character.PIRATE: throw new NotImplementedException(); case Gameplay.Character.SKELETON: throw new NotImplementedException(); default: throw new ArgumentOutOfRangeException(); } _mainPlayerBodySprite = new AnimatableSprite(animationDefinition.SpriteDefinition.Subtextures); _mainPlayerBodySprite.renderLayer = RenderLayers.PRIMARY; _mainBodyAnimation = new AnimationComponent(_mainPlayerBodySprite, animationDefinition, KnightAnimations.IDLE_HORIZONTAL); // must generate collider after we create the sprite, // otherwise the collider doesn't know how big it is (that's how it default works) _collider = new BoxCollider(15, 10); _collider.name = ComponentNames.PLAYER_COLLIDER; Flags.setFlagExclusive(ref _collider.physicsLayer, PhysicsLayers.PLAYER); _collider.collidesWithLayers = 0; Flags.setFlag(ref _collider.collidesWithLayers, PhysicsLayers.BACK_WALLS); Flags.setFlag(ref _collider.collidesWithLayers, PhysicsLayers.SIDE_WALLS); Flags.setFlag(ref _collider.collidesWithLayers, PhysicsLayers.ENVIRONMENT); Flags.setFlag(ref _collider.collidesWithLayers, PhysicsLayers.NET); // Aim Indicator var aimIndicator = new AimIndicator(this); scene.addEntity(aimIndicator); addComponent(_state); // _hitbox = new PolygonCollider([]); addComponent(_mainPlayerBodySprite); addComponent(_mainBodyAnimation); addComponent(_collider); if (_stateComponent.side == Gameplay.Side.RIGHT) { _mainPlayerBodySprite.flipX = true; // TODO - this is horrible but it's the only solution that worked Vector2 lightOffset = new Vector2(); Vector2 heavyOffset = new Vector2(); switch (Settings.Character) { case Gameplay.Character.KNIGHT: lightOffset = new Vector2(-8.5f, -1.5f); heavyOffset = new Vector2(-25, 2.5f); break; case Gameplay.Character.WIZARD: lightOffset = new Vector2(0.333f, 1f); heavyOffset = new Vector2(-.286f, 0.429f); break; case Gameplay.Character.SPACEMAN: break; case Gameplay.Character.ALIEN: break; case Gameplay.Character.PIRATE: break; case Gameplay.Character.SKELETON: break; default: throw new ArgumentOutOfRangeException(); } lightPoints = lightPoints.Reverse().Select(point => new Vector2(-point.X + lightOffset.X, point.Y + lightOffset.Y)).ToArray(); heavyPoints = heavyPoints.Reverse().Select(point => new Vector2(-point.X + heavyOffset.X, point.Y + heavyOffset.Y)).ToArray(); } _lightHitbox = new PolygonCollider(lightPoints); _lightHitbox.isTrigger = true; _lightHitbox.name = ComponentNames.HITBOX_COLLIDER_LIGHT; addComponent(_lightHitbox); _heavyHitbox = new PolygonCollider(heavyPoints); _heavyHitbox.isTrigger = true; _heavyHitbox.name = ComponentNames.HITBOX_COLLIDER_HEAVY; addComponent(_heavyHitbox); // Circle and cooldowns var subtextures = Util.ExtractSubtextures(gameScene.Textures[Sprites.CHARACTER_CIRCLE], 1, 1); var circle = new SpritesheetComponent(subtextures); circle.name = ComponentNames.CHARACTER_CIRCLE; circle.renderLayer = RenderLayers.BACKGROUND; circle.localOffset = new Vector2(0, 15); circle.renderOffset = 1; addComponent(circle); subtextures = Util.ExtractSubtextures(gameScene.Textures[Sprites.LEFT_COOLDOWN], 1, 15); var left = new SpritesheetComponent(subtextures); left.name = ComponentNames.LEFT_COOLDOWN; left.renderLayer = RenderLayers.BACKGROUND; left.localOffset = new Vector2(0, 15); left.renderOffset = 1; addComponent(left); subtextures = Util.ExtractSubtextures(gameScene.Textures[Sprites.RIGHT_COOLDOWN], 1, 15); var right = new SpritesheetComponent(subtextures); right.name = ComponentNames.RIGHT_COOLDOWN; right.renderLayer = RenderLayers.BACKGROUND; right.localOffset = new Vector2(0, 15); right.renderOffset = 1; addComponent(right); addComponent(new SpriteDepthComponent { BaseRenderLayer = RenderLayers.PRIMARY }); SetupSound(); }
/// <inheritdoc /> /// <summary> /// </summary> /// <param name="screen"></param> public TestDrawingSpritesScreenView(Screen screen) : base(screen) { #region GREEN_BOX GreenBox = new Sprite() { Parent = Container, Size = new ScalableVector2(50, 50), Tint = Color.Green, Position = new ScalableVector2(0, 10), Alignment = Alignment.TopCenter }; GreenBox.AddBorder(Color.White, 2); #endregion #region HELLO_WORLD_TEXT HelloWorldText = new SpriteText("exo2-bold", "Hello, World!", 18) { Parent = Container, Alignment = Alignment.TopCenter, Y = GreenBox.Height + GreenBox.Y + 40 }; #endregion #region CLICK_ME_BUTTON ClickMeButton = new TextButton(WobbleAssets.WhiteBox, "exo2-bold", "Click me!", 18, (sender, args) => { // Click event handler method goes here. // Choose a random background color! BackgroundColor = new Color(Rng.Next(0, 255), Rng.Next(0, 255), Rng.Next(0, 255)); }) { Parent = Container, Size = new ScalableVector2(150, 50), Tint = Color.White, Text = { Tint = Color.Black }, Alignment = Alignment.TopCenter, Y = HelloWorldText.Y + 40 }; #endregion #region ANIMATING_LIGHTING AnimatingLighting = new AnimatableSprite(Textures.TestSpritesheet) { Parent = Container, Size = new ScalableVector2(200, 200), Alignment = Alignment.MidRight, X = -20, // Here we will create new custom SpriteBatchOptions for this sprite. // This overrides SpriteBatch.Begin() to include these new SpriteBatchOptions ONLY for this sprite and any // sprites drawn after it that specify `UsePreviousSpriteBatchOptions` // // IMPORTANT! // Any sprites you want to have these same SpriteBatch.Begin options for WITHOUT creating a new SpriteBatch, // you must set `UsePreviousSpriteBatchOptions` to true. SpriteBatchOptions = new SpriteBatchOptions { BlendState = BlendState.Additive }, }; // Start the animation loop forwards at 60FPS infinitely. AnimatingLighting.StartLoop(Direction.Forward, 60); #endregion #region SPRITE_WITH_SHADER SpriteWithShader = new Sprite { Image = WobbleAssets.WhiteBox, Size = new ScalableVector2(200, 200), Alignment = Alignment.TopCenter, Parent = Container, Tint = Color.Orange, Y = ClickMeButton.Y + ClickMeButton.Height + 40, SpriteBatchOptions = new SpriteBatchOptions { SortMode = SpriteSortMode.Deferred, BlendState = BlendState.NonPremultiplied, SamplerState = SamplerState.PointClamp, DepthStencilState = DepthStencilState.Default, RasterizerState = RasterizerState.CullNone, // The shader attached is to make the sprite semi transparent // Shader created by "Vortex-" (https://github.com/VortexCoyote) Shader = new Shader(GameBase.Game.Resources.Get("Wobble.Tests.Resources/Shaders/semi-transparent.mgfxo"), new Dictionary <string, object> { { "p_dimensions", new Vector2(200, 200) }, { "p_position", new Vector2(0, 0) }, { "p_rectangle", new Vector2(100, 100) }, { "p_alpha", 0f } }) } }; #endregion }
public AnimatableObjectSample(Microsoft.Xna.Framework.Game game) : base(game) { Rectangle bounds = GraphicsService.GraphicsDevice.Viewport.TitleSafeArea; // ----- Create three named AnimatableSprite instances. _animatableSpriteA = new AnimatableSprite("SpriteA", SpriteBatch, Logo) { Position = new Vector2(bounds.Center.X, bounds.Center.Y / 2.0f), Color = Color.Red, }; _animatableSpriteB = new AnimatableSprite("SpriteB", SpriteBatch, Logo) { Position = new Vector2(bounds.Center.X, bounds.Center.Y), Color = Color.Green, }; _animatableSpriteC = new AnimatableSprite("SpriteC", SpriteBatch, Logo) { Position = new Vector2(bounds.Center.X, 3.0f * bounds.Center.Y / 2.0f), Color = Color.Blue, }; // Create a looping color key-frame animation. ColorKeyFrameAnimation colorAnimation = new ColorKeyFrameAnimation { TargetProperty = "Color", EnableInterpolation = true, }; colorAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(0.0), Color.Red)); colorAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(0.5), Color.Orange)); colorAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(1.0), Color.Yellow)); colorAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(1.5), Color.Green)); colorAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(2.0), Color.Blue)); colorAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(2.5), Color.Indigo)); colorAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(3.0), Color.Violet)); colorAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(3.5), Color.Red)); AnimationClip<Color> loopedColorAnimation = new AnimationClip<Color>(colorAnimation) { TargetObject = "SpriteB", LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue, }; // Create a oscillating from/to animation for the position. Vector2FromToByAnimation vector2Animation = new Vector2FromToByAnimation { TargetProperty = "Position", From = new Vector2(bounds.Left + 100, _animatableSpriteC.Position.Y), To = new Vector2(bounds.Right - 100, _animatableSpriteC.Position.Y), Duration = TimeSpan.FromSeconds(2), EasingFunction = new HermiteEase { Mode = EasingMode.EaseInOut }, }; AnimationClip<Vector2> oscillatingVector2Animation = new AnimationClip<Vector2>(vector2Animation) { LoopBehavior = LoopBehavior.Oscillate, Duration = TimeSpan.MaxValue, }; // Create a timeline group that contains both animations. TimelineGroup spriteCAnimation = new TimelineGroup { TargetObject = "SpriteC", }; spriteCAnimation.Add(loopedColorAnimation); spriteCAnimation.Add(oscillatingVector2Animation); // Create a timeline group that contains the animations for SpriteB and SpriteC. TimelineGroup allSpriteAnimations = new TimelineGroup(); allSpriteAnimations.Add(loopedColorAnimation); allSpriteAnimations.Add(spriteCAnimation); // There are several ways to apply animations to animatable objects and properties: // // Method 1: Apply to an IAnimatableProperty directly. // We either have direct access to a IAnimatableProperty or we can ask the IAnimatableObject // to give us a named IAnimatableProperty. // For example: // AnimationService.StartAnimation(loopedColorAnimation, _animatableSpriteB.GetAnimatableProperty<Color>("Color")); // AnimationService.StartAnimation(loopedColorAnimation, _animatableSpriteC.GetAnimatableProperty<Color>("Color")); // AnimationService.StartAnimation(oscillatingLeftRightAnimation, _animatableSpriteC.GetAnimatableProperty<Vector2>("Position")); // In this case, the "TargetObject" and the "TargetProperty" values of the timelines/animations do not matter. // // Method 2: Apply to an IAnimatableObject directly. // For example: // AnimationService.StartAnimation(loopedColorAnimation, _animatableSpriteB); // AnimationService.StartAnimation(spriteCAnimation, _animatableSpriteC); // The animation service checks the TargetProperty value of the animations to see which // IAnimatableProperty of the IAnimatableObjects should be animated by the animation. // The "TargetObject" values of the timelines are ignored. // // Method 3: Apply a timeline to a collection of IAnimatableObjects. // For example: var animationController = AnimationService.StartAnimation(allSpriteAnimations, new[] { _animatableSpriteA, _animatableSpriteB, _animatableSpriteC }); // The "TargetObject" and "TargetProperty" values are used to check which objects and // properties must be animated by which animation. // combinedSpriteAnimation is a "tree" of timelines: // // Type TargetObject TargetProperty // -------------------------------------------------------------------------------------------------- // allSpriteAnimations TimelineGroup - - // loopedColorAnimation AnimationClip<Color> "SpriteB" - // colorAnimation ColorKeyFrameAnimation - "Color" // spriteCAnimation TimelineGroup "SpriteC" - // loopedColorAnimation AnimationClip<Color> "SpriteB" - // colorAnimation ColorKeyFrameAnimation - "Color" // oscillatingVector2Animation AnimationClip<Vector2> - - // vector2Animation FromToByAnimation - "Position" // // No animation specifies "SpriteA" as its TargetObject, therefore no animation // are applied to SpriteA. // // The first loopedColorAnimation is applied to "SpriteB". And the contained // colorAnimation is applied to the Color property of SpriteB. // // The spriteCAnimation is applied to "SpriteC". Therefore, all "children" of // spriteCAnimation are also applied to this object! The TargetObject property of // the second loopedColorAnimation is ignored! The second loopedColorAnimation is // applied to SpriteC! animationController.UpdateAndApply(); }
public BlendedAnimationSample(Microsoft.Xna.Framework.Game game) : base(game) { Rectangle bounds = GraphicsService.GraphicsDevice.Viewport.TitleSafeArea; // Create the animatable object. _animatableSprite = new AnimatableSprite("SpriteA", SpriteBatch, Logo) { Position = new Vector2(bounds.Center.X, bounds.Center.Y / 2.0f), Color = Color.Red, }; Vector2FromToByAnimation slowLeftRightAnimation = new Vector2FromToByAnimation { TargetProperty = "Position", From = new Vector2(bounds.Left + 100, bounds.Top + 200), To = new Vector2(bounds.Right - 100, bounds.Top + 200), Duration = TimeSpan.FromSeconds(4), EasingFunction = new HermiteEase { Mode = EasingMode.EaseInOut }, }; ColorKeyFrameAnimation redGreenAnimation = new ColorKeyFrameAnimation { TargetProperty = "Color", EnableInterpolation = true, }; redGreenAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(0), Color.Red)); redGreenAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(4), Color.Green)); TimelineGroup animationA = new TimelineGroup { slowLeftRightAnimation, redGreenAnimation, }; Vector2FromToByAnimation fastLeftRightAnimation = new Vector2FromToByAnimation { TargetProperty = "Position", From = new Vector2(bounds.Left + 100, bounds.Bottom - 200), To = new Vector2(bounds.Right - 100, bounds.Bottom - 200), Duration = TimeSpan.FromSeconds(1), EasingFunction = new HermiteEase { Mode = EasingMode.EaseInOut }, }; ColorKeyFrameAnimation blackWhiteAnimation = new ColorKeyFrameAnimation { TargetProperty = "Color", EnableInterpolation = true, }; blackWhiteAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(0), Color.Black)); blackWhiteAnimation.KeyFrames.Add(new KeyFrame<Color>(TimeSpan.FromSeconds(1), Color.White)); TimelineGroup animationB = new TimelineGroup { fastLeftRightAnimation, blackWhiteAnimation, }; // Create a BlendGroup that blends animationA and animationB. // The BlendGroup uses the TargetProperty values of the contained animations to // to match the animations that should be blended: // slowLeftRightAnimation with fastLeftRightAnimation // redGreenAnimation with blackWhiteAnimation _blendedAnimation = new BlendGroup { LoopBehavior = LoopBehavior.Oscillate, Duration = TimeSpan.MaxValue, }; _blendedAnimation.Add(animationA, 1); _blendedAnimation.Add(animationB, 0); _blendedAnimation.SynchronizeDurations(); // Start blended animation. AnimationService.StartAnimation(_blendedAnimation, _animatableSprite).UpdateAndApply(); }