[源码下载]
作者: webabcd
介绍
使用 Silverlight 2.0(c#) 开发一个趣味钢琴
玩法
打开音箱,从左侧列表选择要挑战的乐谱,右侧会出现对应的乐谱提示动画,等按键提示移动到目标区后敲击键盘上对应的按键
在线DEMO
思路
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(this, new PianoKeyEventArgs() { Key = this.Key });
}
}

/// <summary>
/// 动画离开目标区后的事件
/// </summary>
public event EventHandler<PianoKeyEventArgs> Outside;
public void OnOutside()
{
if (Outside != null)
{
Outside(this, new 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(0, 1)).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(this, new EventArgs());
}
}

/// <summary>
/// 按键敲击错误或未及时敲击的事件
/// </summary>
public event EventHandler<EventArgs> Lost;
public void OnLost()
{
if (Lost != null)
{
Lost(this, new EventArgs());
}
}
}
}
OK
[源码下载]
游戏人生Silverlight(2) - 趣味钢琴[Silverlight 2.0(c#)]
作者: webabcd
介绍
使用 Silverlight 2.0(c#) 开发一个趣味钢琴
玩法
打开音箱,从左侧列表选择要挑战的乐谱,右侧会出现对应的乐谱提示动画,等按键提示移动到目标区后敲击键盘上对应的按键
在线DEMO
思路
1、添加多个MediaElement控件,循环使用,以达到同时播放多个音阶的效果
2、提示按键模块分3组动画,分别为提示部分、目标部分和离开部分,进入目标区和离开目标区都要触发对应的事件,以使外部判断用户是否按照提示正确地敲击了按键
关键代码
1、钢琴音阶播放器
ScalePlayer.xaml.cs



































































2、按键提示动画
AnimationKey.xaml

























































AnimationKey.xaml.cs













































































































































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
































































































































































OK
[源码下载]