C#吉他英雄克隆,注意动画时机

本文关键字:动画 时机 吉他英雄 | 更新日期: 2023-09-27 18:26:59

我正在使用C#创建一个吉他英雄克隆,在同步动画和声音时遇到了一些问题。

我现在做的事情的概要:

  • A读取文件并获得注释高速公路、注释时间而非长度(这都是正确的)
  • 音符被放入一个数组中
  • 在一首歌开始之前,我生成一个故事板,读入整个音符数组,然后创建DoubleAnimations,使矩形对象(音符)在屏幕上向下滚动
  • 我将动画的BeginTime设置为预期播放时间之前的1秒(持续时间接近尾声),并将动画持续时间设置为1秒。然而,我期望发生的事情(音符同步播放)并没有发生

以下是创建动画的代码:

private void StartNote(MidiNoteEvent evnt)
    {
        const double length = 0;
        int[] highwayArray = evnt.Highway;
        for (int i = 1; i <= highwayArray.Length; i++ )
        {
            var highway = highwayArray[i-1];
            if (highway > 0)
            {
                string name = evnt.Time.ToString() + "|" + highway.ToString(); 
                var rect = new Rectangle {Tag=name, Height = length+5, Width = 50 };
                Canvas.SetTop(rect,-6);
                int offset;
                if (i == 1)
                {
                    offset = 20;
                    rect.Fill = new SolidColorBrush(Colors.Green);
                }
                else if (i == 2)
                {
                    offset = 120;
                    rect.Fill = new SolidColorBrush(Colors.Red);
                }
                else if (i == 3)
                {
                    offset = 220;
                    rect.Fill = new SolidColorBrush(Colors.Yellow);
                }
                else if (i == 4)
                {
                    offset = 320;
                    rect.Fill = new SolidColorBrush(Colors.Blue);
                }
                else
                {
                    offset = 420;
                    rect.Fill = new SolidColorBrush(Colors.Orange);
                }
                rect.Margin = new Thickness(offset, 0, 0, 0);
                guitarCanvas.Children.Add(rect);
                var duration = new Duration(TimeSpan.FromSeconds(1));
                var myDoubleAnimation2 = new DoubleAnimation
                                             {Duration = duration, BeginTime = TimeSpan.FromSeconds(evnt.Time-1)};
                _notes.Children.Add(myDoubleAnimation2);
                Storyboard.SetTarget(myDoubleAnimation2, rect);
                // Set the attached properties of Canvas.Left and Canvas.Top
                // to be the target properties of the two respective DoubleAnimations
                Storyboard.SetTargetProperty(myDoubleAnimation2, new PropertyPath("(Canvas.Top)"));
                myDoubleAnimation2.To = guitarCanvas.Height;
                myDoubleAnimation2.Completed += new EventHandler(myDoubleAnimation2_Completed);
                // Begin the animation.
                //sb.Begin();
            }
        }
    }

我不确定如何解决这个问题,我是不是误解了动画的工作原理?使用错误的单位?还是完全是别的东西?任何提示都非常感谢,即使这是对我如何显示笔记的完全重写。

C#吉他英雄克隆,注意动画时机

您是如何播放声音的?我在视频上也做了类似的事情。我在放其他动画的同一个故事板中开始了视频,所以它们都在同一个计时器上。所以我用MediaTimeline制作了我的故事板,然后将我的其他动画添加到同一个故事板中。

工作得很好。只要确保在情节提要中将SlipBehavior设置为"滑动"即可。

在点击按钮的操作中,我有:

<BeginStoryboard Name="playVideoSB">
    <Storyboard SlipBehavior="Slip" Completed="Storyboard_Completed" FillBehavior="Stop">
        <MediaTimeline Source="{Binding Filename}" Storyboard.TargetName="video" Name="mediaTime"/>
    </Storyboard>
</BeginStoryboard>

我在代码中添加了一些类似的东西:

BooleanAnimationUsingKeyFrames bukf = new BooleanAnimationUsingKeyFrames();
Storyboard.SetTargetProperty(bukf, new PropertyPath(PropertyFoo));
Storyboard.SetTarget(bukf, myObject);
bukf.KeyFrames.Add(new DiscreteBooleanKeyFrame(true, KeyTime.FromTimeSpan(start)));
bukf.KeyFrames.Add(new DiscreteBooleanKeyFrame(false, KeyTime.FromTimeSpan(end)));
playVideoSB.Storyboard.Children.Add(bukf);

我碰巧使用了布尔动画,但它应该可以使用替身。

我建议您使用UserControl并使用绘制事件绘制FillRectangle,然后更改控件的位置以使用Timer-Tick事件制作矩形动画。

编辑1:要提高用户控件的性能并避免闪烁效果,请查看此处。

编辑2:计时器在后台工作,要启用和禁用它,您可以编写一个公共字段,如下所示:

private Timer _timer;
public bool TimerEnabled
{
    get { return _timer.Enabled; }
    set { _timer.Enabled = value; }
}
void InitializeTimer()
{
    _timer = new Timer();
    _timer.Interval = 1; //One millisecond
    _timer.Enabled = false;
    _timer.Tick += new EventHandler(_timer_Tick);
}
void _timer_Tick(object sender, EventArgs e)
{
    //Example
    Location = new Point(Location.X, Location.Y + 1);
}

我的答案并不是针对C#本身,而是针对一般的音频/视频同步。它还取决于用于播放声音的音响系统。对我来说,听起来你遇到的问题是你的动画时钟与音频时钟不同步

通常,当您编写一些必须使音符流与音轨同步的代码时,您可以将动画更新直接与声源上的时钟联系起来,而不是依赖于渲染器帧事件。

对于许多动画系统,当你说"播放此动画"时,更新循环很可能会在内部执行可变的时间步长动画,以在T上逐渐将对象从a移动到B。这一切都很好,直到你希望事情像音频计时一样精确(这是节奏动作游戏的必要条件)。我不会详细介绍它背后的细节,但关键是为你的帧计时的时钟和为你的音频计时的时钟可能不会给出完全相同的时间。

解决方案相对简单(尽管我的解释可能不太好):在每帧的基础上更新笔记位置,而不依赖动画系统来完成。

您知道,由于它们在通道上向下滚动,每个音符在时间t时都有一个Y位置p。使用t=(音频时钟的时间值),您可以计算在任何给定时间应该在哪里绘制p,类似于:p=laneLength->t//strong>,其中laneLength是您的车道的顶部(假设1.0)。然后音符将随着音频时钟准确地向下滚动超过0。当他们击中或传球0时会发生什么,取决于显示器。

我希望这是有意义的,但我不相信:)