游戏人生Silverlight(2) - 趣味钢琴[Silverlight 2.0(c#)]

[索引页]
[源码下载]


游戏人生Silverlight(2) - 趣味钢琴[Silverlight 2.0(c#)]



作者:webabcd


介绍
使用 Silverlight 2.0(c#) 开发一个趣味钢琴


玩法
打开音箱,从左侧列表选择要挑战的乐谱,右侧会出现对应的乐谱提示动画,等按键提示移动到目标区后敲击键盘上对应的按键


在线DEMO
Get Microsoft Silverlight


思路
1、添加多个MediaElement控件,循环使用,以达到同时播放多个音阶的效果
2、提示按键模块分3组动画,分别为提示部分、目标部分和离开部分,进入目标区和离开目标区都要触发对应的事件,以使外部判断用户是否按照提示正确地敲击了按键


关键代码
1、钢琴音阶播放器
ScalePlayer.xaml.cs

using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Net;
using  System.Windows;
using  System.Windows.Controls;
using  System.Windows.Documents;
using  System.Windows.Input;
using  System.Windows.Media;
using  System.Windows.Media.Animation;
using  System.Windows.Shapes;

namespace  YYPiano.Controls.Parts
{
    
public partial class ScalePlayer : UserControl
    
{
        
// MediaElement 控件总数
        private int _count = 32;

        
// MediaElement 控件集合的索引
        private int _index = 0;

        
public ScalePlayer()
        
{
            InitializeComponent();

            
this.Loaded += new RoutedEventHandler(Player_Loaded);
        }


        
void Player_Loaded(object sender, RoutedEventArgs e)
        
{
            
// 在 Canvas 上添加指定数量的 MediaElement 控件
            for (int i = 0; i < _count; i++)
            
{
                var element 
= new MediaElement();
                element.Volume 
= 1d;

                root.Children.Add(element);
            }

        }


        
/// <summary>
        
/// 播放音阶
        
/// A 键对应 Scale 文件夹内的 A.mp3,以此类推
        
/// A 键对应 C 大调的低音 dou,以此类推
        
/// </summary>
        
/// <param name="key">键值</param>

        public void Play(Key key)
        
{
            
if (key >= Key.A && key <= Key.Z)
            
{
                
// 循环使用 MediaElement 控件集合中的控件
                if (_index > _count - 1)
                    _index 
= 0;

                
// 设置 MediaElement 的 Source 并播放
                var element = root.Children[_index] as MediaElement;
                element.Source 
= new Uri("/YYPiano;component/Scale/" + key.ToString() + ".mp3", UriKind.Relative);
                element.Stop();
                element.Play();

                _index
++;
            }

        }

    }

}



2、按键提示动画
AnimationKey.xaml

< UserControl  x:Class ="YYPiano.Controls.Parts.AnimationKey"
    xmlns
="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    xmlns:x
="http://schemas.microsoft.com/winfx/2006/xaml" >
    
< Canvas >
        
<!-- 3个椭圆,目标区,按键动画进入该区域后敲击则为有效敲击 -->
        
< Ellipse  x:Name ="target"  Width ="80"  Height ="80"  Stroke ="#F80"  Fill ="Transparent"  StrokeThickness ="1"  Canvas.Left ="0"  Canvas.Top ="250"   />
        
< Ellipse  x:Name ="target2"  Width ="80"  Height ="80"  Stroke ="#F80"  Fill ="Transparent"  StrokeThickness ="1"  Canvas.Left ="120"  Canvas.Top ="250"   />
        
< Ellipse  x:Name ="target3"  Width ="80"  Height ="80"  Stroke ="#F80"  Fill ="Transparent"  StrokeThickness ="1"  Canvas.Left ="240"  Canvas.Top ="250"   />

        
<!-- 提示按键 -->
        
< Border  x:Name ="container"  BorderBrush ="Gray"  BorderThickness ="1"  Width ="50"  Height ="50"  CornerRadius ="50"  Canvas.Left ="135"  RenderTransformOrigin ="0.5, 0.5" >
            
< TextBlock  x:Name ="key"  TextAlignment ="Center"  VerticalAlignment ="Center"  FontSize ="40"  FontWeight ="Bold" >
            
</ TextBlock >
            
< Border .RenderTransform >
                
< TransformGroup >
                    
< RotateTransform  x:Name ="rt"   />
                    
< TranslateTransform  x:Name ="tt"   />
                    
< ScaleTransform  x:Name ="st"  ScaleX ="0.3"  ScaleY ="0.3"   />
                
</ TransformGroup >
            
</ Border.RenderTransform >
        
</ Border >

        
< Canvas .Resources >
            
<!-- 主动画(缓冲提示) -->
            
< Storyboard  x:Name ="mainAni"  Completed ="mainAni_Completed" >
                
<!-- 坐标 -->
                
< DoubleAnimation  x:Name ="targetX"  From ="0"  To ="0"  Duration ="0:0:4"  Storyboard.TargetName ="tt"  Storyboard.TargetProperty ="X"   />
                
< DoubleAnimation  From ="0"  To ="250"  Duration ="0:0:4"  Storyboard.TargetName ="tt"  Storyboard.TargetProperty ="Y"   />

                
<!-- 旋转 -->
                
< DoubleAnimationUsingKeyFrames  Storyboard.TargetName ="rt"  Storyboard.TargetProperty ="Angle"  RepeatBehavior ="1x"   >
                    
< SplineDoubleKeyFrame  Value ="366"  KeySpline ="0.1,0 0.2,0.95"  KeyTime ="0:0:4"   />
                
</ DoubleAnimationUsingKeyFrames >

                
<!-- 缩放 -->
                
< DoubleAnimationUsingKeyFrames  Storyboard.TargetName ="st"  Storyboard.TargetProperty ="ScaleX" >
                    
< SplineDoubleKeyFrame  Value ="1"  KeySpline ="0.1,0 0.3,0.8"  KeyTime ="0:0:4"   />
                
</ DoubleAnimationUsingKeyFrames >
                
< DoubleAnimationUsingKeyFrames  Storyboard.TargetName ="st"  Storyboard.TargetProperty ="ScaleY" >
                    
< SplineDoubleKeyFrame  Value ="1"  KeySpline ="0.1,0 0.3,0.8"  KeyTime ="0:0:4"   />
                
</ DoubleAnimationUsingKeyFrames >
            
</ Storyboard >

            
<!-- 进入目标区后的动画 -->
            
< Storyboard  x:Name ="insideAni"  Completed ="insideAni_Completed"  Duration ="0:0:0.4" >
                
< DoubleAnimation  To ="310"  Storyboard.TargetName ="tt"  Storyboard.TargetProperty ="Y"   />
            
</ Storyboard >

            
<!-- 离开目标区后的动画 -->
            
< Storyboard  x:Name ="outsideAni" >
                
< DoubleAnimation  To ="400"  Storyboard.TargetName ="tt"  Storyboard.TargetProperty ="Y"   />
                
< DoubleAnimation  To ="0"  Storyboard.TargetName ="container"  Storyboard.TargetProperty ="Opacity"   />
            
</ Storyboard >
        
</ Canvas.Resources >
    
</ Canvas >
</ UserControl >


AnimationKey.xaml.cs

using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Net;
using  System.Windows;
using  System.Windows.Controls;
using  System.Windows.Documents;
using  System.Windows.Input;
using  System.Windows.Media;
using  System.Windows.Media.Animation;
using  System.Windows.Shapes;

namespace  YYPiano.Controls.Parts
{
    
public partial class AnimationKey : UserControl
    
{
        
public AnimationKey()
        
{
            InitializeComponent();
        }


        
/// <summary>
        
/// 键值 A - Z,动画显示,用于提示用户应该敲什么键
        
/// </summary>

        public Key Key
        
{
            
get return Convert.ToChar(key.Text).ToKey(); }
            
set { key.Text = value.ToChar().ToString(); }
        }


        
/// <summary>
        
/// 开始动画
        
/// </summary>

        public void Start()
        
{
            mainAni.Begin();
        }


        
/// <summary>
        
/// 停止动画
        
/// </summary>

        public void Stop()
        
{
            mainAni.Stop();
            insideAni.Stop();
            outsideAni.Stop();
        }


        
/// <summary>
        
/// 动画开始时间
        
/// </summary>

        public TimeSpan BeginTime
        
{
            
set { mainAni.BeginTime = value; }
        }


        
private int _targetIndex;
        
/// <summary>
        
/// UI 上设置了 3 个目标区,设置键的动画最终要落到哪个区上
        
/// </summary>

        public int TargetIndex
        
{
            
set
            
{
                
if (value == 0)
                    targetX.To 
= -120;
                
else if (value == 1)
                    targetX.To 
= 0;
                
else if (value == 2)
                    targetX.To 
= 120;
                
else
                    targetX.To 
= 0;

                _targetIndex 
= value;
            }

        }


        
/// <summary>
        
/// 主动画完成后
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>

        private void mainAni_Completed(object sender, EventArgs e)
        
{
            
if (_targetIndex == 0)
                target.Fill 
= new SolidColorBrush(Colors.Orange);
            
else if (_targetIndex == 1)
                target2.Fill 
= new SolidColorBrush(Colors.Orange);
            
else if (_targetIndex == 2)
                target3.Fill 
= new SolidColorBrush(Colors.Orange);

            insideAni.Begin();

            OnInside();
        }


        
/// <summary>
        
/// 目标区动画完成后
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>

        private void insideAni_Completed(object sender, EventArgs e)
        
{
            
if (_targetIndex == 0)
                target.Fill 
= new SolidColorBrush(Colors.Transparent);
            
else if (_targetIndex == 1)
                target2.Fill 
= new SolidColorBrush(Colors.Transparent);
            
else if (_targetIndex == 2)
                target3.Fill 
= new SolidColorBrush(Colors.Transparent);

            outsideAni.Begin();

            OnOutside();
        }


        
/// <summary>
        
/// 动画进入目标区后的事件
        
/// </summary>

        public event EventHandler<PianoKeyEventArgs> Inside;
        
public void OnInside()
        
{
            
if (Inside != null)
            
{
                Inside(
thisnew PianoKeyEventArgs() { Key = this.Key });
            }

        }


        
/// <summary>
        
/// 动画离开目标区后的事件
        
/// </summary>

        public event EventHandler<PianoKeyEventArgs> Outside;
        
public void OnOutside()
        
{
            
if (Outside != null)
            
{
                Outside(
thisnew PianoKeyEventArgs() { Key = this.Key });
            }

        }

    }

}



3、乐谱提示动画
AnimationMusicBook.xaml.cs

using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Net;
using  System.Windows;
using  System.Windows.Controls;
using  System.Windows.Documents;
using  System.Windows.Input;
using  System.Windows.Media;
using  System.Windows.Media.Animation;
using  System.Windows.Shapes;

using  YYPiano.Controls.Parts;
using  System.Threading;

namespace  YYPiano.Controls
{
    
/// <summary>
    
/// 乐谱动画
    
/// </summary>

    public partial class AnimationMusicBook : UserControl
    
{
        
/// <summary>
        
/// 当前进入到目标区域的按键集合(先进先出)
        
/// </summary>

        private List<KeyHitModel> _currentKeys = new List<KeyHitModel>();

        
public AnimationMusicBook()
        
{
            InitializeComponent();
        }


        
/// <summary>
        
/// 启动乐谱动画
        
/// </summary>
        
/// <param name="code">乐谱编码</param>
        
/// <returns>是否成功地启动了乐谱动画</returns>

        public bool Start(string code)
        
{
            code 
= code.ToUpper().Trim();

            
// 清除已有的 AnimationKey 控件
            foreach (var c in root.Children)
            
{
                var ak 
= c as AnimationKey;
                ak.Stop();
            }

            root.Children.Clear();
            _currentKeys.Clear();

            
// 把乐谱编码解析为乐谱实体类(用于描述乐谱的每一音阶)集合
            var musicBook = new List<MusicBookModel>();
            var countDelay 
= 0;
            
try
            
{
                
foreach (var s in code.Split(','))
                
{
                    var delay 
= int.Parse(s.Trim().Substring(1));
                    var key 
= Convert.ToChar(s.Trim().Substring(01)).ToKey();

                    musicBook.Add(
new MusicBookModel() { Length = countDelay, Key = key });

                    countDelay 
+= delay;
                }

            }

            
catch (Exception)
            
{
                
return false;
            }


            
// 在容器内放置相应的 AnimationKey 控件
            for (int i = 0; i < musicBook.Count; i++)
            
{
                AnimationKey key 
= new AnimationKey();
                key.TargetIndex 
= i % 3;
                key.Key 
= musicBook[i].Key;
                key.BeginTime 
= TimeSpan.FromMilliseconds(musicBook[i].Length);
                key.Inside 
+= new EventHandler<PianoKeyEventArgs>(key_Inside);
                key.Outside 
+= new EventHandler<PianoKeyEventArgs>(key_Outside);
                key.Start();

                root.Children.Add(key);
            }


            
return true;
        }


        
/// <summary>
        
/// 按键进入目标区
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>

        void key_Inside(object sender, PianoKeyEventArgs e)
        
{
            _currentKeys.Add(
new KeyHitModel { Key = e.Key, Hit = false });
        }


        
/// <summary>
        
/// 按键离开目标区
        
/// </summary>
        
/// <param name="sender"></param>
        
/// <param name="e"></param>

        void key_Outside(object sender, PianoKeyEventArgs e)
        
{
            
// 获取此次离开目标区的按键(进入到目标区域的按键集合的第一个成员)
            var key = _currentKeys.First();

            
if (!key.Hit)
                OnLost();

            _currentKeys.RemoveAt(
0);
        }


        
/// <summary>
        
/// 指定的键值被敲击后所执行的方法
        
/// </summary>
        
/// <param name="key">键值</param>

        public void Play(Key key)
        
{
            
if (key >= Key.A && key <= Key.Z && _currentKeys.Where(p => !p.Hit).Count() > 0)
            
{
                var validKey 
= _currentKeys.Where(p => !p.Hit && p.Key == key).FirstOrDefault();
                
if (validKey != null)
                
{
                    OnScore();
                    validKey.Hit 
= true;
                }

                
else
                
{
                    OnLost();
                }

            }

        }


        
/// <summary>
        
/// 按键敲击正确的事件
        
/// </summary>

        public event EventHandler<EventArgs> Score;
        
public void OnScore()
        
{
            
if (Score != null)
            
{
                Score(
thisnew EventArgs());
            }

        }


        
/// <summary>
        
/// 按键敲击错误或未及时敲击的事件
        
/// </summary>

        public event EventHandler<EventArgs> Lost;
        
public void OnLost()
        
{
            
if (Lost != null)
            
{
                Lost(
thisnew EventArgs());
            }

        }

    }

}



OK
[源码下载]

你可能感兴趣的:(silverlight)