衰落值一般功能/程序

时间:2014-11-24 21:27:08

标签: delphi timer delphi-xe3

我正在使用一般函数/程序,根据提供的数据计算淡入淡出时间和值,如下所示:

我在字节数组中保存了字节值:这些是起始值。然后我在其他一些数组中有一些记忆值:这些是新值。然后我有时间提供,这需要一个起始值来获得新值

我需要在每次更改时获得值的更新(准确度高达0.1秒)。我知道,如果值 A 更改为10而值 B 同时更改为100,那么让我们说1秒,我会获得价值 A 更新了10次,而值 B 将更新100次。 到目前为止,我一直在计划使用计时器,间隔时间为50毫秒,这将根据变化的值和所需的时间不断计算差异,例如:change step := (Difference between start and new value / {divided by} (fade time / timer interval) )。< / p>

但鉴于价值变化不同,衰落时间以及在第一次衰落结束之前我可以执行另一个值衰落的事实,这让我感到困惑和困难。

所以,我需要的是一个选项,让我们说,给定的索引1,2,3,4,5,6和7的值在30秒内改为新值,然后介于两者之间的某个地方,我可以将索引11,13和17的值排序为在9秒内更改为新值,等等......

此外,如果值 A 正在逐渐消失 B 正在进行中,而另一个淡出 A C 将被订购,我希望它被添加到队列列表中,在第一个淡入淡出完成后立即执行。此时,第一个命令的 B 值将成为第二个命令中的 A 值。这是由于以下事实:上面示例中的 A 应始终在淡入淡出开始时读取。这样,无论在淡入淡出之前做什么,还是在淡入淡出命令和淡入淡出执行之间,它都是起始值。因此,我可以将Fade1设置为 Current - &gt; B @ 10s 并为 Current - &gt;排队Fade2 C @ 10s ,而第二种情况下的 Current 实际上是值,否则保存为 B ,让我们假设 Current < fade1中的/ em>与保存为 C 的值相同。这样,该值将在环回中,每10秒更改一次。所以基本上,添加淡入淡出的命令应该只有 SetNewFade:Dest:= B;时间:= 10;

所以我可以添加 - &gt; B @ 10s - &gt; C @ 10s - &gt; B @ 10s ,< em> - &gt; C @ 10s ,它只是从 B 循环到 C 并向后循环,直到队列列表为空。我希望我能够明确地说明这一点。我真的无法更好地描述我需要实现的目标。

此外,由于所有淡入淡出都将在列表框中提供,我希望能够根据需要删除队列中的淡入淡出。但是,如果当前正在运行的淡入淡出被删除,则该值应该到一个新值,就像淡入淡出已经完成一样,然后通常然后启动新的淡入淡入队列列表,如果有的话。任何。

如何最容易创建?使用具有固定间隔的Timer是一个好主意吗?如果很多值将等待淡入淡出会导致任何延迟吗?是使用动态数组的值和时间(并在 StartFade 事件上填充它们并在淡入淡出完成后释放它们)在黑暗中拍摄或猜测好吗?

这里有一个我希望更清楚的例子:

A: array [0..20] of Byte;
B: array [0..20] of Byte;
C: array [0..20] of Byte;
Current: array [0..20] of Byte;

Button1将 A 值应用于当前值,Button2应用 B 值,Button3应用 C em>值,依此类推......

所以我在编辑框中设置时间,让我们说5秒,然后点击Button1。有了这个,我将 Current 的淡入淡出添加到数组 A 中的值,时间为5秒。由于它是队列中的第一个,因此它立即开始执行。在淡入淡出实际完成之前,我将时间设置为20秒并按下Button2。所以我只是在队列列表中添加了另一个淡入淡出:从 Current 到数组 B 中的值。由于我改变了相同的值(索引0..20),因此在第一个淡入淡出完成之后开始执行。注意:淡入淡出过程会不断更新 Current 数组,直到它与fade命令的数组具有相同的值!因此,第二个淡入淡出将再次从当前淡化为 B Current 实际上与 A 相同。< / p>

现在事情变得更加复杂的是当我实际设置从数组中索引0,1,2,3和4的值被褪色@ 5秒到 A ,然后我应用将索引为5,6,7,8和9的值在10秒内消隐为 B 值:在这种情况下,由于我正在衰落的索引是不同的,所以两个淡入淡出命令都应该执行右远

如果一个值都在两个渐变中(例如,如果我将第二个淡入淡出的值加上4),则只需要将此值添加到队列列表中。所以另一个消失了,而那个已经在第一个淡入淡出时消失的那个等待它完成,然后按照第二个命令开始消失。

其他一些信息:

  • 此刻,数组的长度不固定,但如果这很重要,可以设置固定。它肯定是乘数512,最大值为2047。

  • 数组的数量未知,需要在运行时进行修改。它们可能会被存储为记录(例如StoredVals: array of record;Index: array of Integer(值的索引;这是为了告诉哪些值存储在此记录中),以及Value: array of Byte;(这些是例如,基于Current[StoredVals[0].Index[0]]的淡出实际值。当前保留所有值的数据,同时记录 A B C 等...仅保留在该记录中索引的值的值。

  • 根据上面的描述,数组的长度并不总是相等,因为它们并不总是改变相同数量的值。

  • 在初始化时从数据库填充数组。由于它们可以在运行时创建,因此它们可以从 Current 值填充并存储为新数组。但是这也总是写在数据库中。它们是一种记忆值,所以我可以用按钮回忆它们。就此而言,我希望有一个选项可以立即回忆这些值(正如我现在所做的那样)或通过淡入淡出选项。现在,为了避免队列中的值出现问题,我还考虑通过淡入淡出过程发送立即更改,仅使用时间0秒。这样,将不会立即应用不在队列中的值,而如果某个值当前正在衰落,则将在该淡入淡出完成后应用它。也就是说,这个淡入淡出过程一直都在命令流程中。

如果需要进一步澄清,请不要犹豫!

我知道这真的很复杂,这就是我寻找你帮助的原因。任何部分帮助或建议将不胜感激。

1 个答案:

答案 0 :(得分:2)

  

我是在一般职能/程序之后...

实际上,你似乎完成了一个完整的程序。你正在考虑整体解决这个问题,这就是为什么你有这么多问题。您需要学习在较小的部分中完成此任务,并更清楚地总结要求。目前形式的问题接近于偏离主题,它可能更适合SE Programmers。但由于这适合我的小巷,我想引导你。

要求

  • 有一组长度为N的值X.
  • 可以为此集合中的一个或多个值分配新值。
  • 应在特定的持续时间内逐步执行从旧值到新值的修改。
  • 这会在此过渡期间产生中间值。
  • 此转换是值/索引特定的,即X [0]的持续时间可能与X [ 1]的持续时间不同。
  • 必须先完成转换才能分配另一个新值。
  • 转换正在进行时,可能会要求分配新值。
  • 结论是新请求应存储在队列中,这样当转换完成后,可以从队列中提取新的值请求,从而导致新的转换。

我很确定这是您问题的正确摘要。

转换

您建议使用计时器在每个间隔执行总转换的是合理的。现在,有两种方法可以计算

  1. 将总转换分为固定数量的小转换,将Timer的间隔设置为总持续时间除以该数字,并在每个时间间隔处理所有已处理的较小转换的总和 。这是您在计算更改步骤时提出的建议。这种方法的缺点是双重的:
    • 计时器的间隔是一个近似值,由于各种原因而不准确,其中一个因素依赖于Windows消息传递模型。时间受到许多流程的影响,包括你的流程,
    • 因此而可能出现粗糙或不顺利的进展。
  2. 每隔一段时间重新计算已处理过渡的部分。这样,无论下一个间隔是否需要两倍,进展总是顺利进行。
  3. 第二种解决方案是首选,这转化为您正在寻找的以下一般程序。让我们通过假设一个项目开始简单:

    function InBetweenValue(BeginValue, EndValue: Byte; Duration, 
      StartTick: Cardinal): Byte
    var
      Done: Single;
    begin
      Done := (GetTickCount - StartTick) / Duration;
      if Done >= 1.0 then
        Result := EndValue
      else
        Result := Round(BeginValue + (EndValue - BeginValue) * Done);
    end;
    
      

    使用固定间隔的定时器是个好主意吗?

    使用这种方法,计时器的间隔会影响计算:在任何给定时间InBetweenValue的结果都是正确的。需要Timer的唯一事物正在推动进步。如果需要67 Hz刷新率,则将其间隔设置为15毫秒。如果要刷新率为20 Hz,则将间隔设置为50毫秒。

    性能

      

    如果很多值会等待淡入淡出会导致任何延迟吗?

    不,不是因为隐含的原因。所有计算所需的时间可能取决于队列的大小,但这肯定不是一个重要因素。 (如果是这样,那么你就会遇到更令人不安的问题)。可能&#34;延迟&#34;由于错过或合并的Windows计时器消息,刷新率会降低,具体取决于计算机与其执行的所有操作的繁忙程度。

    数据存储

      

    正在使用动态数组来表示值和时间(并在#34; StartFade&#34;事件中填充它们并在淡入淡出完成后释放它们)在黑暗中拍摄或猜测好吗?

    让我们首先分析需要处理哪些数据。存在一组任意长度的中间当前值,每个值都有自己的四个属性:开始值,结束值,转换持续时间和转换开始时间。所以你可以选择:

    1. 存储5组:一组当前值和四组属性,或
    2. 存储1套:一组当前值,其中每个值都有四个属性成员
    3. 第一种解决方案需要保持所有五组同步的麻烦。第二个需要另一个维度。我更喜欢后者。

      您是否使用数组或其他内容取决于您。选择最适合您的,最适合的目的或最佳匹配输入或所需输出的内容。是否选择静态动态数组取决于输入的可变性,并且在性能上没有任何可测量的差异。动态数组需要运行时长度管理,而静态数组则不需要。

      但是既然你需要一个动态的解决方案,那么我建议不要在盒子外思考。例如,RTL没有为数组提供默认的内置管理工具,但它确实有集合类,例如, TList

      对于本答案的其余部分,我将假设使用Object作为元素和List来决定跟踪它们。

      设计

      既然已经解决了两个最紧迫的问题,那么设计就可以解决了。

      列表中包含项目,每个项目都有其当前值和四个属性:开始,结束,持续时间和开始时间。每个项目都必须能够获取新的属性值。根据属性,有一个计算当前值的公式。并且有一个Timer应该自动执行这些计算的多个。

      此外,应为项目存储多个转换命令。由于我们已经有一个包含成员的项目,因此我们也将这些命令添加为项目的成员。

      缺少什么?不,我们走了。

      实施

      我们需要:

      • 具有两个成员的过渡类型:结束值和持续时间,
      • 多个这些转换的类型,最好具有队列特征
      • 具有六个成员的项目的类型:开始值,结束值,持续时间,开始时间,当前值和转换,
      • 此类项目列表的类型,
      • 用于计算项目当前值的例程,
      • 当前值达到最终值时弹出新转换的例程,
      • 执行此计算并弹出所有项目的例程,
      • 用于驱动此全面例程的计时器,
      • 更新项目属性的例程。概括。我们是否需要设置所有属性的能力?转换不具备所需的所有设置吗?
      • 持有此内容的Object的类型。

      这应该可以帮助您设置代码的接口部分。灵儿,并且渴望开始编写实现代码。

      我的试用版,如上所述:

      unit Modulation;
      
      interface
      
      uses
        System.SysUtils, System.Classes, System.Generics.Collections, WinAPI.Windows,
        VCL.ExtCtrls;
      
      type
        TTransition = record
          EndValue: Byte;
          Duration: Cardinal;
        end;
      
        TTransitions = class(TQueue<TTransition>);
      
        TByte = class(TObject)
        private
          FBeginValue: Byte;
          FCurrentValue: Byte;
          FEndValue: Byte;
          FDuration: Cardinal;
          FStartTick: Cardinal;
          FTransitions: TTransitions;
          procedure PopTransition;
        public
          procedure AddTransition(ATransition: TTransition);
          constructor Create;
          destructor Destroy; override;
          function HasTransition: Boolean;
          function InTransition: Boolean;
          procedure Recalculate;
          property CurrentValue: Byte read FCurrentValue;
        end;
      
        TBytes = class(TObjectList<TByte>);
      
        TByteModulator = class(TObject)
        private
          FItems: TBytes;
          FOnProgress: TNotifyEvent;
          FTimer: TTimer;
          function Finished: Boolean;
          function GetCurrentValue(Index: Integer): Byte;
          function GetItemCount: Integer;
          procedure SetItemCount(Value: Integer);
          procedure Proceed(Sender: TObject);
        protected
          procedure DoProgress;
        public
          procedure AddTransition(Index: Integer; ATransition: TTransition);
          constructor Create;
          destructor Destroy; override;
          property CurrentValues[Index: Integer]: Byte read GetCurrentValue; default;
          property ItemCount: Integer read GetItemCount write SetItemCount;
          property OnProgress: TNotifyEvent read FOnProgress write FOnProgress;
        end;
      
      implementation
      
      { TByte }
      
      procedure TByte.AddTransition(ATransition: TTransition);
      begin
        if ATransition.Duration < 1 then
          ATransition.Duration := 1;
        FTransitions.Enqueue(ATransition);
        Recalculate;
      end;
      
      constructor TByte.Create;
      begin
        inherited Create;
        FTransitions := TTransitions.Create;
        FDuration := 1;
      end;
      
      destructor TByte.Destroy;
      begin
        FTransitions.Free;
        inherited Destroy;
      end;
      
      function TByte.HasTransition: Boolean;
      begin
        Result := FTransitions.Count > 0;
      end;
      
      function TByte.InTransition: Boolean;
      begin
        Result := FCurrentValue <> FEndValue;
      end;
      
      procedure TByte.PopTransition;
      var
        Transition: TTransition;
      begin
        Transition := FTransitions.Dequeue;
        FBeginValue := FCurrentValue;
        FEndValue := Transition.EndValue;
        FDuration := Transition.Duration;
        FStartTick := GetTickCount;
      end;
      
      procedure TByte.Recalculate;
      var
        Done: Single;
      begin
        Done := (GetTickCount - FStartTick) / FDuration;
        if Done >= 1.0 then
        begin
          FCurrentValue := FEndValue;
          if HasTransition then
            PopTransition;
        end
        else
          FCurrentValue := Round(FBeginValue + (FEndValue - FBeginValue) * Done);
      end;
      
      { TByteModulator }
      
      const
        RefreshFrequency = 25;
      
      procedure TByteModulator.AddTransition(Index: Integer;
        ATransition: TTransition);
      begin
        FItems[Index].AddTransition(ATransition);
        FTimer.Enabled := True;
      end;
      
      constructor TByteModulator.Create;
      begin
        inherited Create;
        FItems := TBytes.Create(True);
        FTimer := TTimer.Create(nil);
        FTimer.Enabled := False;
        FTimer.Interval := MSecsPerSec div RefreshFrequency;
        FTimer.OnTimer := Proceed;
      end;
      
      destructor TByteModulator.Destroy;
      begin
        FTimer.Free;
        FItems.Free;
        inherited Destroy;
      end;
      
      procedure TByteModulator.DoProgress;
      begin
        if Assigned(FOnProgress) then
          FOnProgress(Self);
      end;
      
      function TByteModulator.Finished: Boolean;
      var
        Item: TByte;
      begin
        Result := True;
        for Item in FItems do
          if Item.InTransition or Item.HasTransition then
          begin
            Result := False;
            Break;
          end;
      end;
      
      function TByteModulator.GetCurrentValue(Index: Integer): Byte;
      begin
        Result := FItems[Index].CurrentValue;
      end;
      
      function TByteModulator.GetItemCount: Integer;
      begin
        Result := FItems.Count;
      end;
      
      procedure TByteModulator.Proceed(Sender: TObject);
      var
        Item: TByte;
      begin
        for Item in FItems do
          Item.Recalculate;
        DoProgress;
        FTimer.Enabled := not Finished;
      end;
      
      procedure TByteModulator.SetItemCount(Value: Integer);
      var
        I: Integer;
      begin
        for I := FItems.Count to Value - 1 do
          FItems.Add(TByte.Create);
        FItems.DeleteRange(Value, FItems.Count - Value);
      end;
      
      end.
      

      一个小小的即插即用演示程序(注意标签只显示最后一个请求):

      enter image description here

      unit Unit2;
      
      interface
      
      uses
        System.SysUtils, System.Classes, Vcl.Controls, Vcl.Forms,
        VCL.ComCtrls, VCL.StdCtrls, Modulation;
      
      type
        TForm2 = class(TForm)
        private
          FBars: array of TProgressBar;
          FLabels: array of TLabel;
          FByteModulator: TByteModulator;
          procedure FormClick(Sender: TObject);
          procedure Progress(Sender: TObject);
        public
          constructor Create(AOwner: TComponent); override;
          destructor Destroy; override;
        end;
      
      var
        Form2: TForm2;
      
      implementation
      
      {$R *.dfm}
      
      { TForm2 }
      
      const
        Count = 10;
      
      constructor TForm2.Create(AOwner: TComponent);
      var
        I: Integer;
      begin
        inherited Create(AOwner);
        FByteModulator := TByteModulator.Create;
        FByteModulator.ItemCount := Count;
        FByteModulator.OnProgress := Progress;
        SetLength(FBars, Count);
        SetLength(FLabels, Count);
        for I := 0 to Count - 1 do
        begin
          FBars[I] := TProgressBar.Create(Self);
          FBars[I].SetBounds(10, 10 + 30 * I, 250, 25);
          FBars[I].Smooth := True;
          FBars[I].Max := High(Byte);
          FBars[I].Parent := Self;
          FLabels[I] := TLabel.Create(Self);
          FLabels[I].SetBounds(270, 15 + 30 * I, 50, 25);
          FLabels[I].Parent := Self;
        end;
        OnClick := FormClick;
      end;
      
      destructor TForm2.Destroy;
      begin
        FByteModulator.Free;
        inherited Destroy;
      end;
      
      procedure TForm2.FormClick(Sender: TObject);
      var
        Transition: TTransition;
        Index: Integer;
      begin
        Transition.EndValue := Random(High(Byte) + 1);
        Transition.Duration := Random(3000);
        Index := Random(Count);
        FLabels[Index].Caption := Format('%d > %d @ %f',
          [FByteModulator.CurrentValues[Index], Transition.EndValue,
          Transition.Duration / MSecsPerSec]);
        FByteModulator.AddTransition(Index, Transition);
      end;
      
      procedure TForm2.Progress(Sender: TObject);
      var
        I: Integer;
      begin
        for I := 0 to Count - 1 do
          FBars[I].Position := FByteModulator.CurrentValues[I];
      end;
      
      initialization
        Randomize;
      
      end.
      

      更迭。