public override EffectLayer Render(IGameState gamestate) { _previousAnimationTime = _currentAnimationTime; _currentAnimationTime = GetAnimationTime() % Properties.AnimationDuration; EffectLayer gradient_layer = new EffectLayer(); if (Properties.AnimationRepeat > 0) { if (_playTimes >= Properties.AnimationRepeat) { return(gradient_layer); } if (_currentAnimationTime < _previousAnimationTime) { _playTimes++; } } else { _playTimes = 0; } EffectLayer gradient_layer_temp = new EffectLayer(); using (Graphics g = gradient_layer_temp.GetGraphics()) { Properties.AnimationMix.Draw(g, _currentAnimationTime); } Rectangle rect = new Rectangle(0, 0, Effects.canvas_width, Effects.canvas_height); if (Properties.ScaleToKeySequenceBounds) { var region = Properties.Sequence.GetAffectedRegion(); rect = new Rectangle((int)region.X, (int)region.Y, (int)region.Width, (int)region.Height); } using (Graphics g = gradient_layer.GetGraphics()) { g.DrawImage(gradient_layer_temp.GetBitmap(), rect, new Rectangle(0, 0, Effects.canvas_width, Effects.canvas_height), GraphicsUnit.Pixel); } gradient_layer_temp.Dispose(); if (Properties.ForceKeySequence) { gradient_layer.OnlyInclude(Properties.Sequence); } return(gradient_layer); }
public override EffectLayer Render(IGameState gamestate) { EffectLayer animationLayer = new EffectLayer(); // Calculate elapsed time since last Render call long dt = _animTimeStopwatch.ElapsedMilliseconds; _animTimeStopwatch.Restart(); // Update all running animations. We have to call "ToList()" to prevent "Collection was modified; enumeration operation may not execute" runningAnimations.ToList().ForEach(anim => { anim.currentTime += dt / 1000f; if (Properties.AnimationRepeat > 0) { anim.playTimes += (int)(anim.currentTime / Properties.AnimationDuration); } anim.currentTime %= Properties.AnimationDuration; }); // Remove any animations that have completed their play times if (Properties.AnimationRepeat > 0) { runningAnimations.RemoveAll(ra => ra.playTimes >= Properties.AnimationRepeat); } // Check to see if the gamestate will cause any animations to trigger CheckTriggers(gamestate); // Render each playing animation. We have to call "ToList()" to prevent "Collection was modified; enumeration operation may not execute" runningAnimations.ToList().ForEach(anim => { EffectLayer temp = new EffectLayer(); // Default values for the destination rect (the area that the canvas is drawn to) and animation offset Rectangle destRect = new Rectangle(0, 0, Effects.canvas_width, Effects.canvas_height); PointF offset = Properties.KeyTriggerTranslate ? anim.offset : PointF.Empty; // When ScaleToKeySequenceBounds is true, additional calculations are needed on the destRect and offset: if (Properties.ScaleToKeySequenceBounds) { // The dest rect should simply be the bounding region of the affected keys RectangleF affectedRegion = Properties.Sequence.GetAffectedRegion(); destRect = Rectangle.Truncate(affectedRegion); // If we are scaling to key sequence bounds, we need to adapt the offset of the pressed key so that it // remains where it is after the bound - scaling operation. // Let's consider only 1 dimension (X) for now since it makes it easier to think about. The scaling process // is: the whole canvas width is scaled down to the width of the affected region, and then it offset by the // X of the affected region. To have a point that remains the same, we need to reposition it when it's being // used on the canvas, therefore this process needs to be inverted: 1.take the original offset of X and // subtract the affected region's X, thereby giving us the distance from the edge of the affected region to // the offset; 2. scale this up to counter-act the down-scaling done, so we calculate the change in scale off // the canvas by dividing canvas width by the affected region's width; 3.multiply these two numbers together // and that's our new X offset. // This probably makes no sense and I'll forget how it works immediately, but hopefully it helps a little in // future if this code ever needs to be revised. It's embarassing how long it took to work this equation out. offset.X = (offset.X - affectedRegion.X) * (Effects.canvas_width / affectedRegion.Width); offset.Y = (offset.Y - affectedRegion.Y) * (Effects.canvas_height / affectedRegion.Height); } // Draw the animation to a temporary canvas using (Graphics g = temp.GetGraphics()) Properties.AnimationMix.Draw(g, anim.currentTime, 1f, offset); // Draw from this temp canvas to the actual layer, performing the scale down if it's needed. using (Graphics g = animationLayer.GetGraphics()) g.DrawImage(temp.GetBitmap(), destRect, new Rectangle(0, 0, Effects.canvas_width, Effects.canvas_height), GraphicsUnit.Pixel); temp.Dispose(); }); if (Properties.ForceKeySequence) { animationLayer.OnlyInclude(Properties.Sequence); } return(animationLayer); }