你已经得到了一个很好的答案,为什么你当前的代码不起作用,在你的评论中你提到你想从玩家的角度进行射线投射和绘图,所以我假设某种游戏背景。
我不确定VCL是游戏的最佳基础。不同的哲学和需求。作为Arioch'解释说Delphi的VCL是事件驱动的。事情发生在窗户消息的响应中,甚至是绘画。如果没有任何东西需要重新绘制,则不会重新绘制任何内容。
这与我理解游戏引擎的方式非常不同(我绝不是专家)。即使没有任何反复发生,它们也会不断地逐帧绘制,以尽可能呈现流畅。每个帧可能包括基于游戏规则,物理,玩家输入,动画的基础结构的更新,但即使它们保持不变,也将绘制新的帧。基本上在简化的“游戏循环”中会发生三个步骤
所有这一切都发生在每一帧。可能没有输入,没有游戏结构的更新,甚至不需要演示。但是所有三个步骤都属于一起,导致稍后呈现的更新的输入发生在与结果图形完全相同的帧中。
这是我觉得很难融入VCL的东西。作为解决方案必须基于现有的VCL循环和Windows消息。你基本上试图在VCL中创建这样的游戏循环。
解决您的直接问题的方法 - 您希望基于计算呈现某些内容 - 可能只是使用VCL的原则。你想要绘制一些东西。 VCL控制通常表达他们的愿望 Invalidate ,造成他们的 BoundsRect 失效。完成计算后,您可以这样做。在下面的例子中,我将使用一个计时器来模拟你的计算完成。请注意 Invalidate 会引发 WM_PAINT 要为控件生成的消息,但不会立即重新绘制。可能有消息在排队之前排队 WM_PAINT 已处理。 我正在使用 TPaintBox 的 OnPaint 为了实际进行绘画工作,您可能希望将来在项目进展时拥有自己的控制权。
Invalidate
BoundsRect
WM_PAINT
TPaintBox
OnPaint
unit Unit2; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls; type TFormMain = class(TForm) procedure FormCreate(Sender: TObject); private Timer1: TTimer; PaintBox1: TPaintBox; { Private declarations } procedure PaintBox1Paint(Sender: TObject); procedure Timer1Timer(Sender: TObject); public { Public declarations } end; implementation {$R *.dfm} procedure TFormMain.FormCreate(Sender: TObject); begin PaintBox1 := TPaintBox.Create(Self); PaintBox1.Parent := Self; PaintBox1.Align := alClient; PaintBox1.OnPaint := PaintBox1Paint; Timer1 := TTimer.Create(Self); Timer1.Interval := 100; Timer1.OnTimer := Timer1Timer; Randomize; end; procedure TFormMain.PaintBox1Paint(Sender: TObject); var AColor: TColor; I: Integer; begin for I := 0 to PaintBox1.ClientWidth - 1 do begin AColor := RGB(Random(256), Random(256), Random(256)); PaintBox1.Canvas.Pen.Color := AColor; PaintBox1.Canvas.MoveTo(I, 0); PaintBox1.Canvas.LineTo(I, PaintBox1.ClientHeight); end; end; procedure TFormMain.Timer1Timer(Sender: TObject); begin PaintBox1.Invalidate; end; end.
procedure TForm1.OnCreate(Sender: TObject); begin IsDone := False; end; procedure TForm1.Button1Click(Sender: TObject); begin Image1OnCreate(); Button1.Visible := False; While(IsDone = False) do begin ScreenRender(); end; end;
假设 IsDone 总是 False (因为否则我们不会进入循环),这个循环不能终止。这是无限的。
IsDone
False
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin IsDone := True; end;
你不要从里面调用这个程序 TForm1.Button1Click 循环,因此在进入该循环后永远不会调用它。既然你永远不会退出 TForm1.Button1Click 程序你不允许任何外部代理(如VCL中的消息调度循环)执行并调用该过程。为了在进入循环后进行总结,没有任何可以更改的可执行代码 IsDone 值。所以,它没有改变。
TForm1.Button1Click
事件处理程序应该是非常短的程序,几乎立即执行,并放弃“执行流程控制”回到VCL内部。每一次漫长(更无限)的处理都会导致程序变得无法响应。无论Windows可能想要告诉程序多少新闻 - 程序都不会要求它们。
曾经有人告诉他们,Windows窗口(GDI对象)生活在“消息风暴”的中心,他们必须及时解决问题。每秒都有数百条消息传入,并且一个Window Procedure(在Delphi 7表单的VCL类中构建)应该在它为时已晚之前接收,发送和处理它们中的每一个。
一旦你通过使一个事件处理程序长期甚至无休止地阻止了这个过程 - 你就破坏了操作系统和应用程序之间的基本合同。
您必须执行“控制反转”,将您的连续工作分解为小的短块,并在适当的时候让Windows调用这些块。 尝试使用 TTimer 例如。
TTimer
PS。你可以看到一个非常遥远的问题:
跳过那里的所有多线程的东西,对于你的情况,其他线程创建那些我们必须在我们的表单上绘制的“工作块”是很重要的。 当Windows要求我们这样做时 以一些合理的帧速率(不太快也不太慢)。你的工作块根本不同,所以所有的线程都与你无关。
渲染是在里面进行的 TTimer 事件。因此,设置计时器,打开和关闭计时器的“框架”可能会引起您的兴趣。然而,你将要做的工作 .OnTimer 事件会有很大的不同(只是画一些东西,甚至只是画画 invalidating 表单的某些部分并等待Windows触发 OnPaint 事件。)。
.OnTimer
invalidating