1回顶部 提到 RPG (角色扮演游戏,Role Play Game),在座各位恐怕没有不熟悉的。从古老经典的 DOS 版《仙剑奇侠传》到新潮花哨的《轩辕剑》系列,无不以曲折优美的故事情节,美丽可人的主角,悦耳动情的背景音乐,震撼了每一个玩家的心灵。而说到 RPG,就不能不提 DirectX,因为 PC 上大部分的 RPG 都是用这个冬冬开发的。早在《轩辕剑叁外传——天之痕》推出的时候,我就曾想过用 DirectX 写一个自己的 RPG,自己来安排故事情节的发展,却总是因为这样或那样的事情,一直没有能够实现这个心愿。在耗费了宝贵的几年青春,搞定了诸如考试、恋爱、出国等琐碎杂事之后,我终于可以在这个 SARS 肆虐的时代,坐在陪伴了我整个大学生涯的电脑前,听着颓废而又声嘶力竭的不知名歌曲,写一些一直想写却没有写的东西。DirectX 简介 DirectX 对于大多数游戏爱好者来说都不陌生(当然,那些只在DOS下艰苦作战的朋友例外),在安装一个游戏前,系统总是会提示你是否需要同时升级 DirectX。简单地说,DirectX 就是一系列的 DLL (动态连接库),通过这些 DLL,开发者可以在无视于设备差异的情况下访问底层的硬件。DirectX 封装了一些 COM(Component Object Model)对象,这些 COM 对象为访问系统硬件提供了一个主要的接口。首先,我们先来看一下 DirectX 的结构:
2回顶部
3回顶部 图中的用黑色填充部分就是预先所定义的透明区域,在游戏中,我们按照白色线条可以将整个图片分别切开成9块,每块的大小均为80×50。我们先从左向右,然后在从上往下对起进行编号为1~9。如果我们将这九张图片依次循环显示出来,并且设置播放的速度为每秒24张,我们就可以得到一个飞船在旋转的动画,也就是一个飞船精灵。 开发过程 说了许多废话以后,下面我们脱离纸上谈兵,开始正式的开发。在本例中,我们主要实现用鼠标来控制精灵往八个方向行走。所有图片均来自大宇公司《轩辕剑叁——天之痕》,其中精灵采用故事中陈靖仇的形象特此致谢。同时,请各位读者勿把这些图片用于商业用途,否则后果自负。
4回顶部
5回顶部 打开 Delphi 并新建一个应用程序,依次选中 DelphiX 组件栏的 TDXDraw、TDXImageList、TDXInput、TDXTimer、TDXSpriteEngine 组件,添加到用户区,分别命名为 DXDraw、DXImageList、DXInput、DXTimer、DXSpriteEngine,按照下表设置其各项属性。对于 DXImageList,点击 Object Inspector 中的 Items,在其中加入两张位图(background.bmp和player.bmp),分别命名为 background 和 player,设置 player 的 PatternHeight 和 PatternWidth 均为120象素,设置其 transparentcolor 为粉红色(clFuchsia)。 控件 属性 值 DXDraw Align alClient Display.Width 800 Display.Height 600 Display.BitCount 24 Options [doAllowReboot, doWaitVBlank, doCenter, doFlip] AutoInitialize True DXTimer Enabled True Interval 0 DXInput Mouse.BindInputState True Mouse.Enabled True DXSpriteEngine DXDraw DXDraw 下面就是全部的源程序,请先在 Delphi 中产生相应事件然后填入代码,最后按下F9运行就可以运行程序了。用鼠标点击你的目的地,陈靖仇就会自动跑到指定地点。尝试一下开发一些简单的游戏吧,用 DelphiX 这把牛刀!所有程序在 Delphi 4.0 + DirectX 8.0 环境下测试通过。本文所需控件可以在 这里 下载。 Unit Main; Interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Menus, DXClass, DXSprite, DXInput, DXDraws; type TDirection = (DrUp, DrDown, DrLeft, DrRight, DrUpLeft, DrUpRight, DrDownLeft, DrDownRight); {自定义游戏中所用到的方向} TPlayerSprite = class (TImageSprite) CanMove: Boolean; protected procedure DoMove(MoveCount: Integer); override; procedure MoveTo(MoveCount:Integer; Direction: TDirection); procedure DoCollision(Sprite: TSprite; var Done: Boolean); override; end; TMainForm = class(TDXForm) {此处使用优化过的TDXForm来代替TForm} DXTimer: TDXTimer; DXDraw: TDXDraw; DXSpriteEngine: TDXSpriteEngine;
6回顶部 DXInput: TDXInput; ImageList: TDXImageList; procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); procedure DXDrawFinalize(Sender: TObject); procedure DXDrawInitialize(Sender: TObject); procedure FormCreate(Sender: TObject); procedure DXTimerTimer(Sender: TObject; LagCount: Integer); procedure DXDrawMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure DXDrawMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); procedure DXDrawMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); procedure FormClose(Sender: TObject; var Action: TCloseAction); private AnchorX: Integer; AnchorY: Integer; {鼠标点击发生的位置} MouseX: Integer; MouseY: Integer; {鼠标当前位置} PlayerSprite: TPlayerSprite; {游戏中我们所用鼠标控制的人物} BackSprite: TBackGroundSprite; {游戏的背景图} end; const speed=5; {游戏人物向各个方向运动时的动画播放速度} var MainForm: TMainForm; Steps: Integer; {用于控制切换精灵动画图片的参数} implementation {$R *.DFM} procedure TPlayerSprite.DoCollision(Sprite: TSprite; var Done: Boolean); begin Done:=False; {已经侦测到碰撞,不再重复检测碰撞} {检测游戏人物是否与其它精灵发生了碰撞,此处可以扩展为对话等情节} end; procedure TPlayerSprite.DoMove(MoveCount: Integer); var l,r,d,u: Boolean; absX,absY: Integer; {游戏人物的当前位置与目的地的绝对距离} begin inherited DoMove(MoveCount); MoveCount:=Trunc(MoveCount*1.5); l:=false; r:=false; u:=false; d:=false; if (Trunc(X)-MainForm.AnchorX>0) then l:=true else r:=true; if (Trunc(Y)-MainForm.AnchorY>0) then u:=true else d:=true;
7回顶部 absX:=abs(Trunc(X)-MainForm.AnchorX); absY:=abs(Trunc(Y)-MainForm.AnchorY); if absX<4 then begin l:=false; r:=false; end; if absY<4 then begin u:=false; d:=false; end; {如果绝对距离已经小于四个象素,则认为已经到达目的地} if u and l and not d and not r then MoveTo(MoveCount,DrUpLeft); if u and r and not l and not d then MoveTo(MoveCount,DrUpRight); if d and l and not r and not u then MoveTo(MoveCount,DrDownLeft); if d and r and not u and not l then MoveTo(MoveCount,DrDownRight); if d and not l and not r and not u then MoveTo(MoveCount,DrDown); if u and not l and not r and not d then MoveTo(MoveCount,DrUp); if l and not u and not r and not d then MoveTo(MoveCount,DrLeft); if r and not l and not u and not d then MoveTo(MoveCount,DrRight); {根据目的地来判断运动的方向,从而播放相应方向运动的动画} Collision; {检测碰撞} Engine.X := -X+Engine.Width div 2 - Width div 2; Engine.Y := -Y+Engine.Height div 2 - Height div 2; {移动引擎,从而是游戏人物处于舞台的正中央} end; procedure TMainForm.DXTimerTimer(Sender: TObject; LagCount: Integer); begin if not DXDraw.CanDraw then exit; {检测DXDraw是否可以画,否则退出} DXInput.Update; {捕捉各类设备输入,这里我们用来检测鼠标的输入} LagCount := 1000 div 60; {用来控制整个游戏运行速度的参数} DXSpriteEngine.Move(LagCount); DXSpriteEngine.Dead; DXDraw.Surface.Fill(0); {将整个屏幕填充为黑色} DXSpriteEngine.Draw; with DXDraw.Surface.Canvas do begin brush.style:=bsclear; pen.style:=psclear; pen.color:=clwhite; Font.Color:=clWhite; Font.Size:=10; textout(10,10,'Press ESC to Quit'); textout(100,100,'X: '+IntToStr(AnchorX)+'Y: '+IntToStr(AnchorY));
8回顶部 {鼠标点击的位置经转换后在游戏世界中的坐标} textout(100,200,'Sprit x:'+IntToStr(Trunc(PlayerSprite.x))+'Y: ' +IntToStr(Trunc(PlayerSprite.y))); {精灵在游戏世界中的坐标} textout(100,300,'Relative x:'+IntToStr(AnchorX-Trunc(PlayerSprite.x))+'Y: ' +IntToStr(AnchorY-Trunc (PlayerSprite.y))); {精灵当前位置与目的地之间的绝对距离} textout(200,100,'Mouse x:'+IntToStr(MainForm.MouseX)+'Y: ' +IntToStr(MainForm.MouseY)); {鼠标当前位置,相对于窗口左上角,未转换为游戏世界坐标} Release; end; {在字母上输出相应参数,用于程序调试} DXDraw.Flip; {将内存中的后台表面翻转到当前并且显示} end; procedure TMainForm.DXDrawFinalize(Sender: TObject); begin DXTimer.Enabled := False; {关闭定时器} end; procedure TMainForm.DXDrawInitialize(Sender: TObject); begin DXTimer.Enabled := True; {启动定时器} end; procedure TMainForm.FormCreate(Sender: TObject); begin Steps:=0; AnchorX:=0; AnchorY:=0; MouseX:=320; MouseY:=240; {默认使鼠标处于屏幕的中央} ImageList.Items.MakeColorTable; DXDraw.ColorTable := ImageList.Items.ColorTable; DXDraw.DefColorTable := ImageList.Items.ColorTable; DXDraw.UpdatePalette; {更新系统调色板} 9回顶部 BackSprite:=TBackgroundSprite.Create(DXSpriteEngine.Engine); with TBackgroundSprite(BackSprite) do begin SetMapSize(1, 1);{设定背景显示样式为1×1} Image := ImageList.Items.Find('background'); {载入背景图片} Z := -2; {设定背景层次} Tile := True; {设定背景填充样式为平铺} end; PlayerSprite := TPlayerSprite.Create(DXSpriteEngine.Engine); with TPlayerSprite(PlayerSprite) do begin Image := ImageList.Items.Find('player'); Z := 2; Width := Image.Width; Height := Image.Height; end; {载入游戏人物} end; procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin {如果按了Esc,则退出} if Key=VK_ESCAPE then Close; {全屏模式和窗口模式的切换} if (ssAlt in Shift) and (Key=VK_RETURN) then begin DXDraw.Finalize; if doFullScreen in DXDraw.Options then begin RestoreWindow; DXDraw.Cursor := crNone; BorderStyle := bsSizeable; DXDraw.Options := DXDraw.Options - [doFullScreen]; end else begin StoreWindow; DXDraw.Cursor := crNone; BorderStyle := bsNone; DXDraw.Options := DXDraw.Options + [doFullScreen]; end;
10回顶部 DXDraw.Initialize; end; end; procedure TMainForm.DXDrawMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin AnchorX := x + Trunc(PlayerSprite.x)-320; AnchorY := y + Trunc(PlayerSprite.y)-240; {将鼠标在屏幕上点击的位置转换到游戏世界中} PlayerSprite.CanMove:=True; {此参数允许鼠标拖动} end; procedure TMainForm.DXDrawMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin if PlayerSprite.CanMove then begin AnchorX := x + Trunc(PlayerSprite.x)-320; AnchorY := y + Trunc(PlayerSprite.y)-240; {在鼠标拖动过程中将鼠标在屏幕上点击的位置转换到游戏世界中} end; MouseX:=X; MouseY:=Y; {鼠标当前位置} end; procedure TMainForm.DXDrawMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin PlayerSprite.CanMove:=False; end; procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); begin DXSpriteEngine.Free; end; procedure TPlayerSprite.MoveTo(MoveCount: Integer; Direction: TDirection); begin {控制精灵往各个方向移动} case Direction of DrUp: begin Y := Y-(150/1000)*MoveCount; Inc(steps); AnimPos:=steps div speed+20+1; {当前动画中播放的图片序号}
11回顶部 if steps>4*speed-2 then steps:=0; end; DrDown: begin Y := Y+(150/1000)*MoveCount; Inc(steps); AnimPos:=steps div speed+1; if steps> 4*speed-2 then steps:=0; end; DrLeft: begin X := X-(150/1000)*MoveCount; Inc(steps); AnimPos:=steps div speed+10+1; if steps>4*speed-2 then steps:=0; end; DrRight: begin X := X+(150/1000)*MoveCount; Inc(steps); AnimPos:=steps div speed+30+1; if steps>4*speed-2 then steps:=0; end; DrUpLeft: begin X := X-(150/1000)*MoveCount; Y := Y-(150/1000)*MoveCount; Inc(steps); AnimPos:=steps div speed+15+1; if steps>4*speed-2 then steps:=0; end; DrUpRight: begin X := X+(150/1000)*MoveCount; Y := Y-(150/1000)*MoveCount; Inc(steps); AnimPos:=steps div speed+25+1; if steps>4*speed-2 then steps:=0; end; DrDownLeft: begin X := X-(150/1000)*MoveCount; Y := Y+(150/1000)*MoveCount; Inc(steps); AnimPos:=steps div speed+5+1; if steps>4*speed-2 then steps:=0; end; DrDownRight: begin X := X + (150/1000)*MoveCount; Y := Y + (150/1000)*MoveCount; Inc(steps); AnimPos:=steps div speed+35+1; if steps>4*speed-2 then steps:=0; end; end; end; end. 后记 通过以上的讲解和例子,相信大家已经对 Delphi 下的 DirectX 游戏开发有了初步的概念。国内讲解开发 DirectX 游戏的权威资料很少,如果这篇讲解可以起到抛砖引玉的效果的话,我将再高兴不过了。本牟糠帜谌萁杓斯庖恍┯蜗房⑼镜募际跷牡担诖艘徊⒅滦弧H绻魑挥惺裁次侍庑枰臀医涣鳎敕⑿胖羍agle_california@yahoo.com。 源程序下载地址:http://www.cfan.com.cn/11PROGRAM/200312/0312yde.zip。
|
正在阅读:用Delphi + DirectX开发简单RPG游戏用Delphi + DirectX开发简单RPG游戏
2004-02-14 09:34
出处:电脑爱好者
责任编辑:linjixiong