gpt4 book ai didi

delphi - 衰落值常规功能/过程

转载 作者:行者123 更新时间:2023-12-03 15:56:20 27 4
gpt4 key购买 nike

我需要一个通用函数/过程,该函数/过程将根据提供的数据计算出我的淡入淡出时间和值,如下所示:

我将字节值保存在字节数组中:这些是起始值。然后,我在其他数组中存储了一些值:这些将是新值。然后我有时间要提供,这是开始值达到新值所需的时间。

每当值更改时,我都需要获取值的更新(精确到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) )

但是考虑到值变化是不同的,淡入淡出时间也是如此,而且我可以在第一次淡入淡出之前执行另一次淡入淡出的事实,这一切使我感到困惑和困难。

因此,我需要的是一个选项,假设给定索引1、2、3、4、5、6和7的值,并在30秒内将其更改为新值,然后在可以命令索引11、13和17的值在9秒内更改为新值,依此类推...

另外,如果值A会逐渐向值B褪色,并且会命令从A到C的另一次淡入,我希望将其添加到队列列表中,以便在第一次淡入完成后立即执行。那时,第一个命令的B值将成为第二个命令的A值。这是由于以下事实造成的:上面示例中的A应该始终在淡入淡出开始时就被读取。这样,无论在淡入淡出之前还是在淡入淡出命令和淡入淡出执行之间进行了什么操作,它都是一个初始值。因此,我可以将Fade1设置为Current-> B @ 10s,然后将Fade2排队等待Current-> C @ 10s,而第二种情况下的Current实际上是值,否则另存为B,并假设Fade1中的Current与值保存为C。这样,该值将处于回送状态,每10秒更改一次。因此,基本上,添加淡入淡出的命令应仅具有SetNewFade之类的内容:Dest:= B;时间:= 10 ;。

因此,我可以添加-> B @ 10s,-> C @ 10s,-> B @ 10s,-> 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值,依此类推...

因此,我在“编辑”框中设置时间,例如5秒钟,然后单击“ Button1”。这样,我在5秒的时间内添加了从Current到数组A中值的渐变。由于它是队列中的第一个队列,因此它将立即开始执行。在淡入淡出实际完成之前,我将时间设置为20秒,然后按Button2。因此,我只是在队列列表中添加了另一个淡入淡出:从Current到数组B中的值。由于我要更改相同的值(索引0..20),因此在第一个淡入淡出完成后立即开始执行此操作。注意:淡入淡出过程会不断更新Current数组,直到其值与衰落命令的数组相同为止!因此,第二次淡入将再次从电流淡入到B,而电流实际上与A相同。

现在,事情变得更加复杂的是,当我实际上仅将要从数组中索引为0、1、2、3和4的值@ 5sec设置为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]]。Current保留所有值的数据,同时A,B,C等的记录仅保留在该记录内被索引的那些值。
根据上面的描述,数组的长度并不总是相等的,因为它们并不总是更改相同数量的值。
初始化时从数据库填充阵列。由于可以在运行时创建它们,因此将它们从“当前”值中填充并也存储为新数组。但这也总是写在数据库中。它们是一种记忆的值,因此我可以使用按钮来调用它们。为此,我希望有一个选项可以立即调用这些值(就像我现在已经做的那样)或通过衰落选项调用。现在,为了避免队列中的值出现问题,我正在考虑也通过衰落过程仅在0秒的时间内发送该立即更改。这样,不在队列中的值将被立即应用,而如果当前正在逐渐消失的值将在淡入完成后被应用。就是说,这种淡入淡出的过程将一直存在于命令流中。


如果还有其他需要说明的地方,请随时询问!

我知道这真的很复杂,这就是为什么我在寻求您的帮助。任何部分的帮助或建议,将不胜感激。

最佳答案

我正在执行一般功能/程序...


实际上,您似乎在追求一个完整的程序。您正在考虑从整体上解决它,而这却是一片白云,这就是为什么您有这么多问题的原因。您需要学习将这项任务分解为较小的部分,并更清楚地总结需求。当前形式的问题已接近主题,可能更适合SE Programmers。但是,由于这正好适合我的小巷,所以我想逐步引导您。

要求


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


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

转场

您建议使用计时器在每个间隔上执行总过渡的一部分是合理的。现在,有两种方法可以计算这些片段:


将总转换划分为固定数量的小转换,将定时器的间隔设置为总持续时间除以该数字,然后处理每个间隔上所有已处理的小转换的总和。这是您在更改步骤的计算中提出的建议。这种方法的缺点是双重的:


计时器的间隔是一个近似值,由于各种原因,它不是精确的,其中一个原因取决于Windows消息传递模型,该模型的计时受许多过程(包括您的,
因此,可能会出现粗糙或不平稳的进度。

在每个间隔重新计算已处理过渡的一部分。这样,无论下一个间隔是否花费两倍以上的时间,进度始终将保持平稳。


首选第二种解决方案,它可以转换为您要寻找的以下常规例程。让我们从假设一个项目开始简单:

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的结果都是正确的。计时器唯一需要的是驱动进度。如果要67 Hz刷新率,则将其间隔设置为15毫秒。如果您希望刷新频率为20 Hz,则将间隔设置为50毫秒。

性能


  如果许多值要等待淡入淡出,是否会导致任何延迟?


不,不是出于暗示的原因。所有计算所需的时间可能取决于队列的大小,但这肯定不是一个重要因素。 (如果是这样,那么您将面临更多麻烦的问题)。由于丢失或合并了Windows Timer消息,可能的“延迟”将以较低的刷新率体现出来,具体取决于计算机处理工作的繁忙程度。

数据存储


  使用动态数组来获取值和时间(并在“ StartFade”事件中填充它们并在褪色完成后释放它们)是在黑暗中拍摄还是很好的猜测?


首先,让我们分析一下需要处理哪些数据。有一组任意长度的中间值,每个值都有自己的四个属性:开始值,结束值,过渡持续时间和过渡开始时间。因此,您可以选择:


存储5组:一组当前值和四组属性,或者
存储1组:一组当前值,其中每个值都有四个属性成员。


第一种解决方案要求保持所有五组同步。第二个要求另一个维度。我希望后者。

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

但是由于仍然需要动态解决方案,因此我建议您跳出框框思考。例如,RTL没有为阵列提供默认的内置管理工具,但是它确实具有集合类,例如 TList

对于此答案的其余部分,我将假定使用将Object用于元素以及将List用于跟踪它们的决定。

设计

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

有一个包含项目的列表,每个项目都有其当前值和四个属性:开始,结束,持续时间和开始时间。每个项目都必须能够获取新的属性值。有一个基于属性的公式来计算当前值。并且有一个计时器可以自动执行这些计算的倍数。

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

缺少什么?不,走吧

实作

我们需要:


具有两个成员的过渡类型:最终值和持续时间,
这些转换的多个类型,最好具有队列特征,
具有六个成员的项的类型:开始值,结束值,持续时间,开始时间,当前值和转换,
此类项目列表的类型,
计算项目当前值的例程,
当当前值达到最终值时,用于弹出新过渡的例程,
进行此计算并弹出所有项目的例程,
一个计时器来驱动这个整体例程,
用于更新项目属性的例程。概括。我们是否需要设置所有属性的能力?过渡是否没有所需的所有设置?
这是将所有内容组合在一起的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.


还有一个很小的即插即用演示程序(请注意,标签仅显示最后一个请求):



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.


成功。

关于delphi - 衰落值常规功能/过程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27114481/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com