鸿 网 互 联 www.68idc.cn

当前位置 : 服务器租用 > 编程语言开发 > delphi > >

【Asphyre引擎】学习笔记(二)

来源:互联网 作者:佚名 时间:2017-09-20 09:59
转一篇火人论坛那边的一份学习文档,我简单排一下版,希望对入门者有帮助。 感谢China Yang,这份文档也帮助我快速入了门。 和我一起学 Asphyre Sphinx Framework v 1.0.0 China Yang Http://www.huosoft.com/bbs/ : ID:Installxp 前言少序 当我准备用业余

转一篇火人论坛那边的一份学习文档,我简单排一下版,希望对入门者有帮助。

感谢China Yang,这份文档也帮助我快速入了门。

 

和我一起学

Asphyre Sphinx Framework v1.0.0

China Yang

Http://www.huosoft.com/bbs/ : ID:Installxp

 

  • 前言少序

当我准备用业余时间开始写这段文字,当你准备在Library Path里添上Asphyre的Source文件夹。那么我们一起学习的旅程就开始了,我会把我学习的过程写在这里,希望有一天,你也把你的学习过程形成文字,和其他人一起分享。

这些文字是面向初学者的,所以文字拖沓是必然的,请各路Delphi大仙们绕行。

我们的运行环境是:Delphi 2006。请将你的Asp Sphinx FrameWord v1.0.0 目录下的 Source文件夹添加到:

菜单 ->  Tools  ->  Options  ->  library-Win32 –>  Library Path。

 

  •  第一个目标

我们要在屏幕上用Asphyre 画点。

在此之前你需要了解的基础知识:基本控件的“过程,方法和事件”。这些是必备的知识,如果你不清楚这些,请找一本DELPHI 基础书参照学习。

下面我们开始正式进入ASPHYRE学习,从现在开始我和你一样,一步一步探索。

在程序的开始,我们需要一个事件过程。

请使用Design,我们可以看到设计期的窗口。

我们在空窗口上鼠标左键连续点击两次,DELPHI将智能的帮我们建立下面这个事件:

procedure TForm1.FormCreate(Sender: TObject);

FormCreate 是窗口建立时必然运行的一个过程,你在这个过程所写的代码,将随着窗口的建立而被执行。我们将在这里Create我们程序需要的类和其他一些相关的东西。

FormCreate内的第一句代码:

ReportMemoryLeaksOnShutdown:= DebugHook <> 0;

{ 以下关于第一句代码的相关知识:                                     } 

DELPHI引入了FastMM替换掉早期的内存管理器。FastMM是一个开源的项目,你可以在互联网上找到它,在很长一段时间里有经验的程序员使用它来检测程序的内存泄漏。它可以帮助DELPHI IDE更快,更稳定的运行。当然,即使你不将ReportMemoryLeaksOnShutdown开关打开,FastMM也是在工作的,只是当你的程序出现内存问题时,DELPHI 将不会提示你。所以一个好的习惯就是将它打开,它只会帮助你更好的工作。也可以像下面这样:

ReportMemoryLeaksOnShutdown := True;

当程序是在IDE里运行时 DebugHook = 1 ;当程序在IDE外独立运行时 DebugHook = 0;

如果你是一个刚刚开始学习编程的爱好者,下面的方式可能会让你迷惑,不过不要紧。

ReportMemoryLeaksOnShutdown:= DebugHook <> 0;

( DebugHook <> 0 ) 是一个表达式,当DebugHook是1的时候。( DebugHook <> 0 )将返回一个False (假值)给ReportMemoryLeaksOnShutdown。如果DebugHook 的值是0的时候,( DebugHook <> 0 )将返回一个真值。

言归正传,我们要开始在FormCreate里定义一个DisplaySize看起来像下面这样

Procedure TForm1.FormCreate (Sender: TObject);
Var
  DisplaySize: TPoint2px;        { 我们定义的地方 }
Begin

为了我们能正常的使用TPoint2px,我们要引入Vectors2px单元。所以我们要在Implementation 关键字下面添加第一个引入单元,看起来像下面这样

… …

Implementation

Uses

  Vectors2px;

… …

当我们按住Ctrl键用鼠标去点击Vectors2px的时候,我们就会追踪到某个类和某个类型定义的起始处。

DisplaySize被定义成TPoint2px记录(Record);我们可以追踪到 Vectors2px单元第四十五行。

PPoint2px = ^TPoint2px;
TPoint2px = record
     x, y: Integer;

关于 Record 请参看你手头的工具书,记录(Record);

当然DisplaySize:= Point2px(ClientWidth, ClientHeight);也可以换一个写法,例如下面这样:

  DisplaySize.x := ClientWidth;  { ClientWidth 窗口的宽 }
  DisplaySize.y := ClientHeight;  { ClientWidth 窗口的高 }

上面的句子似乎更容易理解一点,DisplaySize里将保存我们窗口的大小,我们后面会用到它。

接下来,我们还要再引入一个单元AsphyreFactory,该单元保存着接口信息.

因为我们的第一个目标并不复杂,所以DirectX7接口足够我们使用,当然我们也可以使用DirectX8, DirectX9;

… …

Implementation
Uses
  Vectors2px,  AsphyreFactory;

… …

添加完单元,我们的代码看起来是上面这个样子。接下来,我们要告诉Asphyre,我们要使用的DirectX版本。

Factory.UseProvider(idDirectX7);

在我们的第一个目标里,我们不会去涉及Asphyre的细节问题,那不便于我们学习,我们暂时只要知道,Factory.UseProvider可以让Asphyre在某个DirectX版本下工作。其实我暂时也不知道Factory.UseProvider的细节,完全是靠猜的,呵呵。

 

当我们将 Factory.UseProvider(idDirectX7);添加到DisplaySize.y := ClientHeight;的后面,我们会发现idDirectX7是错误的。所以我们要找到idDirectX7的定义。在DX7Providers单元的43行,idDirectX7被定义成常量。idDirectX7 = $10000700; $代表16进制操作符,当然你要原意的话,你也可以直接在括号内使用$10000700,不过当你再阅读的时候,不会容易读懂,假设你记不住的话。

在这里我们使用AsphyreFactory的定义。具体为什么要使用$10000700,你可以查找MSDN里面的详细说明,当我们在Windows下编写程序的时候,我们会经常使用msdn来查找一些windows特有的声明。

为了使用idDirectX7,我们又要引入一个新单元,DX7Providers单元。单元引用现在看起来应该是这样:

uses
  Vectors2px, AsphyreFactory, DX7Providers;

接下来我们要引入两个重要的设备:

  GameDevice : TAsphyreDevice = nil;
  GameCanvas : TAsphyreCanvas = nil;

TAsphyreDevice和TAsphyreCanvas,它们所在的单元和Asphyre旧版本不同。分别在AbstractDevices, AbstractCanvas单元。

我们将在 Var Form1:TForm1下面声明它们,当然你可以在其它允许声明的位置声明它们。我们的声明是下面这个样子。

var
  Form1: TForm1;
  GameDevice : TAsphyreDevice = nil;
  GameCanvas : TAsphyreCanvas = nil;

请注意,在Form1:TForm1下面。这样,我们就在要程序的最开始位置引入单元,看起来像下面这个样子。

 

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, AbstractDevices, AbstractCanvas;

 

如果你是一个刚要进入Delphi的入门者,你可能会很可怪,为什么单元的引入位子在上面也有,在下面也有。很烦的,这个问题,你先不要考虑,讨论这个问题会偏离我们第一个目标的主题很远。所以你先试着接受它,在以后很长一段学习DELPHI的日子里,总有一天,我们会搞明白它们的每一个细节,只要你有足够的耐心学习下去。

 

言归正传,我假设你有一定的计算机基础知识,那么我们要说明上面两个设备的关系。

TAsphyreDevice,你可以把它想象成驱动设备,它是我们跟DirectX沟通的桥,所有我们要使用的DirectX都要在TAsphyreDevice里实现,它是我们使用DirectX的基础。

TAsphyreCanvas,画布,我们要在它上面涂鸦,当然不是用笔,而是用我们的代码。我们使用TAsphyreCanvas涂鸦的作品,最终会在窗口上显示出来。

没有TAsphyreDevice,我们的TAsphyreCanvas将不知道怎样将你的作品通过哪里才能让你看到。

 

由于我们是以类(Class)的方式使用TAsphyreDevice,TAsphyreCanvas,而不是我们常用的控件方式,所以我们在使用前一定要Create(建立)它们。

也许你还在使用控件搭积木的方式使用Delphi,也许你对类(Class)完全不了解。这些都没关系,从现在开始我们将一点一点学习和使用它们。随着越来越熟悉,你会发现潜藏在控件图标下的世界更精彩,从现在开始,你需要去补充类(Class)的知识,你需要找一本基础书去了解,什么是类、抽象类、类的实例化。它们可以帮助你在Delphi的世界里走的更远。

当然这完全不影响你随着我继续学习,你完全可以找一本Delphi基础书先偷偷的充电。

又说远了,继续回到我们的主题上来。我们将在下面的代码里Create  TAsphyreDevice和TAsphyreCanvas。

procedure TForm1.FormCreate(Sender: TObject);

Var
  DisplaySize :TPoint2px;
begin
  ReportMemoryLeaksOnShutdown:= DebugHook <> 0;

  { DisplaySize := Point2px(ClientWidth, ClientHeight); 也可以换一种写法 }

  DisplaySize.x := ClientWidth;
  DisplaySize.y := ClientHeight;

  Factory.UseProvider(idDirectX7);

  GameDevice:= Factory.CreateDevice();
  GameCanvas:= Factory.CreateCanvas();

上面两句GameDevice和GameCanvas,跟据前面的声明,我们将Create它们。当然你也可以将GameDevice和GameCanvas,换成你习惯的名子,但是同时需要修改var Form1: TForm1; 下面的关于GameDevice和GameCanvas的声明。

是不是有些累了,可以试当的休息一下。在Main的单元里有完整的代码,供对照着阅读。

我们下面要做的工作(Work)是要设置GameDevice,也就是TAsphyreDevice。在下面,我们是这样设置的:

  GameDevice.WindowHandle:= Self.Handle;
  GameDevice.Size    := DisplaySize;
  GameDevice.Windowed:= True;
  GameDevice.VSync   := False;

Handle句柄,句柄这个东西不是Delphi生产出来的,而是Windows生产出来的。每个一控件,窗口,设备,都有一个句柄。除了使用名子访问外,还可以用句柄访问。你可以把Handle理解成数字化的名子,这个名子是唯一的,不重复的。

Self :自己。在这里 Self就是我们程序的窗口。我们希望所有的绘画都显示在我们的窗口上。所以我们把自己窗口的句柄告诉TAsphyreDevice。

就是这个样子,GameDevice.WindowHandle:= Self.Handle;

告诉TAsphyreDevice,我们窗口的大小  GameDevice.Size := DisplaySize;

告诉TAsphyreDevice,我们使用窗口模式,而不是全屏幕模式。

告诉TAsphyreDevice,是否打开垂直同步。啥是垂直同步?这个问题很高深。我就不多说了,请参看http://bbs.mydrivers.com/archiver/tid-265698.html,如果还是不同,请在Http://www.huosoft.com/bbs/论坛上提出,我将详细的回答你。

EventDeviceCreate.Subscribe(OnDeviceCreate, 0);

是我们在设置完成后需要写下的句子。这个句子和旧版本不同。

这里EventDeviceCreate.Subscribe将激活一个过程,你在这个过程里可以读取你的资源,然后确定是否让初始成功。

OnDeviceCreate ,我以经用红字标出,在上面的句子里。我们可以看到,EventDeviceCreate.Subscribe将通过OnDeviceCreate调用我们的过程。

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private

    { Private declarations }
    procedure OnDeviceCreate(Sender: TObject; Param: Pointer; var Handled: Boolean);

从上面我们可以看到我们声明的过程是这个样子:

procedure OnDeviceCreate(Sender: TObject; Param: Pointer; var Handled: Boolean);

你可能会想到,为什么,一定要是这种参数方式,我们是不是可以换一种参数方式?在EventProviders单元的22行,Asphyre定义了这个过程的形参方式,如果你需要换一种方式,就要对Asphyre本身进行修改,如果你现在还没有那个能力,那么还是老老实实的跟着我断续学习。

 

单元 EventProviders.pas 第22行;

TEventCallback = procedure(Sender: TObject; Param: Pointer;  var Handled: Boolean) of object;

由于我们的第一个目标是在屏幕上画一个点,所以我们不需要装载任何资料,所以我们的过程实现,看起来是下面这个样子。

procedure TForm1.OnDeviceCreate(Sender: TObject; Param: Pointer; var Handled: Boolean);
var
   Success: Boolean;
begin
   Success:= PBoolean(Param)^;
   PBoolean(Param)^:= Success;
end;

我们把Param原原本本的再传回去,不做修改,如果我们有资源在这里装载失败的话,修改Param指向的地址。在我们第一目标中,我们不需要它。

回到 Form1.Create 过程 我们要在EventDeviceCreate.Subscribe(OnDeviceCreate, 0);下面正式初始化TAsphyreDevice

if (not GameDevice.Initialize()) then
begin
   ShowMessage('Failed to initialize Asphyre device.');
   Application.Terminate();
   Exit;
end;

细节先不要去思考,你先把它当成固定格式,以后我们会讨论,当然是在深入以后,才会讨论TAsphyreDevice的细节。

 

接下来是建立我们的计时器:

  Timer.OnTimer  := TimerEvent;
  Timer.OnProcess:= ProcessEvent;
  Timer.Speed    := 60.0;
  Timer.MaxFPS   := 4000;
  Timer.Enabled  := True;

Timer的声明是在AsphyreTimer单元里,所以我们要引入AsphyreTimer单元。引入后是下面这个样子:

implementation
uses
  Vectors2px, AsphyreFactory, DX7Providers, AsphyreTimer;

 

我们先简单的看一下计时器的设置。

Timer.OnTimer  := TimerEvent;

 

我们要在TimerEvent事件过程里执行我们的绘画代码。后面我们要定义TimerEvent过程和实现这个过程。

Timer.OnProcess:= ProcessEvent;

 

我们要在ProcessEvent事件过程里放一个记数器。在我们第一目标中,这个记数器没什么作用。在以后的以后,我们会用到它。

Timer.Speed    := 60.0;

 

计时器的速度,或者叫响应间隔也行,取决于你将来的理解,以后会慢慢理解的,别着急。

Timer.MaxFPS   := 4000;

 

最大刷新,当真正执行一个大绘画时,基本达不到。

Timer.Enabled  := True;

 

当这一句执行的时候,计时器将正式启动。

如果你不设置Timer.Speed,MaxFPS,那么它们的默认值将是:

Speed := 60.0;
MaxFPS:= 100;

 

接下来我们要做的工作是 Timer.OnTimer  := TimerEvent;我们要声明一个TimerEvent事件供计时器调用的时候使用。

  private
    { Private declarations }
    GameTicks: Integer;
    procedure OnDeviceCreate(Sender: TObject; Param: Pointer; var Handled: Boolean);
    procedure TimerEvent(Sender: TObject);

看起来是上面这个样子,你需要在private里声明TimerEvent。然后在过程后面的分号处按键盘上的 Ctrl + Shift +C 转到过程的实现部分。

procedure TForm1.TimerEvent(Sender: TObject);
begin
  GameDevice.Render(RenderEvent, $000000);
  Timer.Process();
end;

说明:

GameDevice.Render(绘画事件过程,背影色);

RenderEvent 我们要绘画的事件。

$000000 为黑色。

Timer.Process(); 调用  Timer.OnProcess:= ProcessEvent; 我们需要定义一个ProcessEvent事件过程,在里面实现一个简单的计数器,虽然在我们第一目标里没什么用,但以后的以后总会用到。

所以现在要声明ProcessEvent事件,声明看起来和下面差不多:

 

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    GameTicks: Integer;
    procedure OnDeviceCreate(Sender: TObject; Param: Pointer; var Handled: Boolean);
    procedure TimerEvent(Sender: TObject);
    procedure ProcessEvent(Sender: TObject);

上面最后一行就是我们对ProcessEvent的声明,你别忘了,顺手在{ Private declarations }下面,将GameTicks: Integer;写上,这个变量就是一个简单的记数器了。

 

在ProcessEvent过程后面的分号处Ctrl + Shift +C转到实现部分,要写成下面这个样子:

procedure TForm1.ProcessEvent(Sender: TObject);
begin
  Inc(GameTicks);
end;

 

GameDevice.Render画完一次就会调用Timer.Process(); 在一定的条件下,Timer.Process(),会在内部调用ProcessEvent事件过程,进行计数器累加操作。

Inc 等同于 GameTicks = GameTicks +1;但是inc更快。

最后,我们要把RenderEvent事件声明完成,并实现它。

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    GameTicks: Integer;
    procedure OnDeviceCreate(Sender: TObject; Param: Pointer; var Handled: Boolean);
    procedure TimerEvent(Sender: TObject);
    procedure ProcessEvent(Sender: TObject);
    procedure RenderEvent(Sender: TObject);

看上面最后一句,这是我们最终要绘画过程的声明,我们在分号部分Ctrl + Shift +C 转到过程的实现部分。

然后完成实现部分:

procedure TForm1.RenderEvent(Sender: TObject);
begin
  GameCanvas.PutPixel(Point(10,10),$FFFFFFFF);
end;

 

忙活了半天的时间,就为了中间的一句话,

GameCanvas.PutPixel(Point(10,10),$FFFFFF);

我们终于用到GameCanvas了,画布,还记得我们前面声明和建立的画布了么??

PutPixel画点,我们要在画布上画点,Point(x,y)在哪个位置画呢?$FFFFFF,我们要画的点是白色的。$FFFFFF 白色。

 

这篇文字希望会对一个入门者有帮助,China Yang,本次就到这里,休息,休息~~

 

demo下载

上一篇:我常用的delphi 第三方控件
下一篇:RO05
网友评论
<