您的位置:首页 > 编程语言 > C#

使用C# + Managed DIrectX 制作 2D 游戏使用C# + Managed DIrectX 制作 2D 游戏

2008-06-05 23:52 483 查看
主旨:虽然目前Microsoft已经发布了XNA Game Studio,让游戏编程员能够更轻松的在Windows(XB360不在本文考虑范围内)制作游戏,不过这个XNAGS有个缺点,它要求系统必须有至少能够支援Shader Model 1.1的显示卡。这表示TNT,GeForce 1, 2, 和 GeForce 3 & 4 MX 系列的显示卡,以及较早期的 integrated display 都无法执行XNAGS本身,以及XNAGS所制作出来的游戏。在这些配备下,就算你打算制作的游戏根本不需要使用Shader (2D 游戏,或画面要求不高的 3D 游戏, 如一般的 online 游戏之类的), 你也无法使用XNAGS来开发你的游戏。无论如何,我相信在本地,不支援shader的电脑还大有市场,所以就开始研究如何在没有shader支援的环境下,使用C# + DirectX 来制作游戏,结果这篇分享文就这样诞生了。

何谓Managed DirectX? 在,NET盛行之前,想要编写DX游戏就得和臭名昭彰的COM打交道。COM(component object model)是一个颇为复杂的编程界面,它提倡所有物件都采用相同的interface,以便达到新版本都能对下兼容,却不必担心界面的改变而必须从新学习这些interface。COM带来的问题是,编程员需要记忆大量的interface的各种overloaded版本,令人头痛不已。

在.net framework之下的Managed DX 概念则是提供一个容易referance各种object,同时也提供了一些自动化的资源管理功能,以及以更简洁的指令执行原本很复杂的运作。在接下来的sample之中,有过使用COM版本DX经验的人会感到惊奇,原来使用 Managed DX 竟然变得如此简单!

文章对象:中等程度的 C# 或 C++ 经验(C++和C#的语言结构很接近,所以 C++ 编程员应该不难理解 C# 的 code),有过 Direct X 编程经验更佳。

软/硬件需求:
1)一台能够运行 Windows XP 的电脑,
2)Microsoft Visual C# 2005 Express Edition (2008 亦可)(可在Microsoft Developers Network 免费下载)(不需要XNAGS)(我使用的是 2005 版本)
3)Microsoft DirectX 9.0 SDK March 2007 或更新的版本(注意这不是一般安装游戏时发布的 Runtime 版本)(可在MSDN下载,免费)(我使用的是March 2008 版本)
4)一张至少能够运行DIrect X 9.0 版游戏的显示卡(我的开发电脑装备的是GeForce 2 MX 32MB)
5)一颗强壮的心,钢铁般的意志,对游戏开发和学习编程有强烈的兴趣。哦,还有超强的吸收力。。。:lol:

《待续》

geekman 14-5-2008 03:42 PM

安装好 Visual C# Express Edition 和 Direct X 9.0 SDK 后,在 VC# 开启一个新的 project:
File->New Project...
这时一个Dialog会弹出来,让你选择你想要的 project 类型。我们选择 Empty Project,project name 就用 My2DGame 吧(或者你自行选择)。决定之后,就会看到右手边,Solution Explorer 已经打开你的新 project。现在里面是空空的,只有一个References在你的Project 里面。现在,在References上Right Click, 然后选Add References... 然后就会有个Dialog, 显示你的电脑里已经安装了的,可以Reference的components。 我们要加入的是.NET的components:
System
System.Data
System.Drawing
System.Windows.Form
Microsoft.DirectX
Microsoft.DirectX.Direct3D
Microsoft.DirectX.Direct3DX
Microsoft.DirectX.DirectInput
Microsoft.DirectX.DIrectSound

暂时就这几个。

接下来,加入你的source code file。在Solution Explorer里面,right click你的Project name (例如 My2DGame)然后选择 Add->New Item... 在打开的Dialog里面选择 Code File, 命名为 My2DGame.cs。

接下来,左边就会显示一个空的 Code Editor。如果你关闭了这个Editor, 你只需 right click 那个新加入的 My2DGames.cs,选择 View Code,就可以打开这个 code file。

现在我们要正式开工了,在那个空荡荡的 My2DGame.cs 的 code file 里面输入你要使用的 component 的 using statement:
using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectInput;
using Microsoft.DirectX.DirectSound;

namespace My2DGame
{
}
接下来,我们要开启一个新的Windows Form:
namespace My2DGame
{
public class Game : Form
{
public Game()
{
this.ClientSize = new Size(800, 600);
this.Text = "The Game - Managed DirectX";
}
static void Main()
{
using (Game MainForm = new Game())
{
if (!MainForm.InitD3D())
{
MessageBox.Show("Failed to initialize Direct 3D Device");
return;
}
MainForm.Show();

while (MainForm.Created)
{
MainForm.UpdateGame();
Application.DoEvents();
}
}
}
}
}
以上就是你的 Form Class 和 Constructor。没错,在C#里面Create Windows Form 就是这么简单。后面接着来的就是这个 Program 的 Main() 以及初始化 Direct3D的Function call,和游戏循环(loop)。咦?我们不是要设计2D游戏吗?为何要启动Direct3D呢?其实,在DX8的时候就已经废除了2D 的 DirectDraw 了,现在的DX虽然还包含 DirectDraw 这个零件,但是那只是为了对下兼容以前的DD软件,事实上DD已经没有再更新了。现在,取代DD的就是 DirectX 9 Graphics 这个部件,它是以最简单的D3D功能模拟了2D环境,并且提供了更多样化的画面渲染方式,以让2D游戏能够以更华丽的方式呈现出来(DX9 Graphics 包含了一部分的3D功能,例如Scaling, rotating, tinting 等, 只是这些功能都是在X-Y轴上运算,忽略掉Z-轴运算),同时能够善用3D硬件的强大又快速的运算好处。

《待续》

[ 本帖最后由 geekman 于 14-5-2008 03:48 PM 编辑 ]

geekman 14-5-2008 05:33 PM

先别急着执行这个Program,你一定会遇到很多error的,因为还有很多东西没放进去。现在,在 class Game:Form{}里面,在顶端加入以下的object/variable declaration:
//--Graphics--
Microsoft.DirectX.Direct3D.Device d3dev = null;
Sprite sprite;
Texture myTexture;
Vector3 ctr;
Vector3 pos = new Vector3(400.0f, 300.0f, 0.0f);
float angle = 0.0f;

Matrix transformmatrix, rotatematrix, finalmatrix;
//--input----
Microsoft.DirectX.DirectInput.Device kb;
Direct3D.Device 代表了你的显示卡,也代表了你的绘图空间。Sprite 则是DX9 Graphics的灵魂所在,它提供了一系列的运算和绘画功能,让你可以轻松的把想呈现在荧幕上的画面画出来。Texture就是一般3D游戏中贴在3D模型上的材质,在这里,它成了你的物件的图像的寄身所在。在DX9 Graphics里面,每一个Sprite事实上是一个平面的正方形polygon,其normal永远和z-轴平行(就像以前Doom 2 时代的 Billboard object 一般永远对着镜头。只在这里,我们的镜头也是永远不变的跟z-轴平行着的),其画像就是贴在上面的Texture了。Vector3是一个从起源点(Origin, X=0, Y=0, Z=0),发射向某个点(X=nx, Y=ny, Z=nz)的假想线。在2D 环境下,我们可以把它当作一个点(point),因为我们只对其X和Y值有兴趣(Z值无视),我们有两个Vector3,pos和ctr。pos是用来代表我们的Sprite在这个2D空间的位置,ctr则是Sprite的中心点。为何要使用中心点呢?那是因为,如果你要使用Sprite的Origin(X=0, Y=0)来代表它的位置的话,你得同时keep track 它的宽和高,十分麻烦,而使用中心点的话,你只需注意一个point,方便很多。pos被设定为 X=400,Y=300,正处在画面中间(我们的画面,也就是Direct3D.Device.Viewport, 会被设定为 800x600)

Matrix 是用来进行transfrmation的,例如旋转(rotation),放大缩小(scaling),以及转移位置(translation)。Sprite 的Transformation功能有个Bug, 导致任何Sprite.Rotation 的运算都会以viewport的(0,0)作为中心点,而忽略了你所选择的中心点(例如 X=400, Y=300),所以我们不能使用Sprite的回旋功能。其解决方法就是使用两个transformation,先 rotate 再 translate 到你想要的位置。

之后就是Keyboard device, 在这个范例里面,我只打算使用keyboard, Mouse的使用方法和Keyboard相似,只是更简单(两轴+3个按钮+1个wheel)。

接下来是启动和初始化Direct3D。
public bool InitD3D()
{
try
{
PresentParameters pp = new PresentParameters();
pp.Windowed = false;
pp.BackBufferWidth = 800;
pp.BackBufferHeight = 600;
pp.SwapEffect = SwapEffect.Discard;
pp.BackBufferFormat = Format.A8R8G8B8;

d3dev = new Microsoft.DirectX.Direct3D.Device(0,
Microsoft.DirectX.Direct3D.DeviceType.Hardware,
this,
CreateFlags.SoftwareVertexProcessing,
pp);

sprite = new Sprite(d3dev);
myTexture = TextureLoader.FromFile(d3dev, "mypic.png");
System.Drawing.Image bmp = System.Drawing.Image.FromFile("mypic.png");
ctr = new Vector3(bmp.Width / 2, bmp.Height / 2, 0.0f);

kb = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
kb.SetCooperativeLevel(this,
CooperativeLevelFlags.NonExclusive |
CooperativeLevelFlags.Background);
kb.Acquire();

return true;
}
catch (DirectXException)
{
return false;
}
}
PresentParameters (发音 Pri-sent,“呈现”的意思)是用来决定Direct3D如何描绘画面的设定的structure。pp.Windowed决定你的游戏是Full Screen还是Windowed。要注意的是,如果你把Windowed设定为true,所有关于backbuffer的设定将会被无视,而viewport size和color depth将会跟随现有windows的Client Size和deskto color depth。BackBuffer Format采用Alpha8, Red8, Green8, Blue8,表示Sprite可以有256阶段的透明度(8-bit Alpha)。如果你的BackBuffer Format选择了错误的设定,会导致你的D3D初始化失败的,要注意。设定好PresentParameters 后就可以制造一个D3D Device了。其Argument如下:
Microsoft.DirectX.Direct3D.Device(
0, //adapter id, 0 for default, 也就是你的主显示卡
Microsoft.DirectX.Direct3D.DeviceType.Hardware, //如果你的显示卡友支援完整的DX9功能,你可以使用硬体加速,不然就只能使用软件模拟(很慢的)
this, //这个游戏的视窗本身
CreateFlags.SoftwareVertexProcessing, //不支援Shader的显示卡只能使用软件模拟的VP了
pp //PresentParameter
);
接下来是制造Sprite和Texture物件,由于它们都会用到D3D Device物件,所以必须在制造了D3D Device 才能制造这两个物件。在制造Texture物件前,你得先把一个图画加入在project里面,作为Texture的画像。Right click Solution Explorer里面的Project Name, 然后选择 Add->Existing Item...,选择一个你要当作图像的图画(BMP, JPG, PNG, GIF, WMF, EMF 等格式)我个人喜欢使用PNG格式,因为它支援透明背景,同时也不会像BMP般占大量空间,却也不会像JPG般压缩了之后会出现边缘模糊的情形。记着你的图形最好是正方形,而且尺寸必须是2的倍数(2,4,8,16,32,64,128,256。。。。),不然D3D会把你的图画缩小到最近似的2的倍数,例如你的图画是300x300的话,会被缩小成256x256。而在某些function中,不是2的倍数的texture尺寸会导致function fail。

然后我们通过一个Bitmap object来获取texture图像的尺寸(奇怪的是我无法通过Texture object来获得这些讯息, 有知道如何直接从Texture object获得图像来源的尺寸的高手请指教),一边计算出Texture的中心点。最后,我们制造一个键盘物件,并通过SetCooperativeLevel设定其运作个性(Behavior),以避免当玩家按 Alt-Tab 跳回桌面时,游戏仍然死抓着键盘控制权不放,造成操作上的不便。Acquire() 是确定键盘处在准备妥当的状态(没有别的program正在抓着键盘控制权)。如果这个初始化function出现问题,就会return false,反之,就会return true。

《待续》

[ 本帖最后由 geekman 于 15-5-2008 11:13 AM 编辑 ]

tensaix2j 14-5-2008 05:49 PM

yay 我第一个来支持你了 :loveliness:

geekman 14-5-2008 06:17 PM

下一步,就是游戏的内容更新,这个Function的作用是读取玩家的输入(键盘,滑鼠,gamepad, joystick, 驾驶盘,诸如此类),根据输入进行计算(移动玩家角色,计算AI,刷新NPC状态(活着,移动中,死亡),刷新游戏世界状态,描绘HUD等等),然后将计算后的结果描绘成下一个Frame,最后呈现在荧幕上。
public void UpdateGame()
{
KeyboardState ks = kb.GetCurrentKeyboardState();
if (ks[Key.Escape])
{
this.Close();
return;
}
if (ks[Key.A])
{
pos.X -= 5;
if (pos.X < 0)
pos.X = 0;
}
if (ks[Key.D])
{
pos.X += 5;
if (pos.X > d3dev.Viewport.Width)
pos.X = d3dev.Viewport.Width;

}
if (ks[Key.W])
{
pos.Y -= 5;
if (pos.Y < 0)
pos.Y = 0;
}
if (ks[Key.S])
{
pos.Y += 5;
if (pos.Y > d3dev.Viewport.Height)
pos.Y = d3dev.Viewport.Height;
}
angle+=0.1f;
if (angle >= 360)
{
angle = 0;
}
Render();
}
以上Function我只检测键盘的5个键:WASD 移动物件(Sprite)的 pos,Escape键结束游戏。要注意的是,UpdateGame 里面就只纯粹更新状态,别把描绘的工作掺杂在里面,以免你无法迅速的完成工作而导致游戏出现控制反应迟缓的现象。

最后,把游戏状态描绘成画面,然后呈现在荧幕:
public void Render()
{
d3dev.Clear(ClearFlags.Target, Color.SkyBlue, 1.0f, 0);
d3dev.BeginScene();
sprite.Begin(SpriteFlags.AlphaBlend);
rotatematrix.RotateZ(angle);
transformmatrix.Translate(pos);
finalmatrix = Matrix.Multiply(rotatematrix, transformmatrix);
sprite.Transform = finalmatrix;
sprite.Draw(myTexture, Rectangle.Empty, ctr, Vector3.Empty, Color.White);
sprite.End();
d3dev.EndScene();
d3dev.Present();
}
首先清空画面,填上Color.xxxx 的颜色。然后就启动D3D Device的描绘机制,当你呼叫Device.Begin()时,系统就会自动的锁定你的Frame Buffer(显示卡里面属于你的游戏的记忆空间),以免被其它程式,或你的游戏里的其他Thread (如果你采用 Multi Threaded 设计)干扰。Sprite本身也采用类似的locking来锁定自己的Texture Buffer。 SpriteFlags.AlphBlend是告诉显示卡,我们要使用Alph channel, 来制造出透明效果。由于Sprite的Bug,我们不能使用它内建的Rotation功能,所以我们使用了两个Matrix,把它们Multiply起来,再直接把结果set在Sprite.Transformation, Sprite 就会乖乖的完成你指定的变形(以 X=400,Y=300 为中心点,绕着自己的中心缓缓的旋转)。End()指令告诉显示卡,你已经完成工作,可以解开Frame Buffer的锁定。Present()就是Flip Buffer, 因为这游戏是 Double-buffered 的,也是说它有两个Frame Buffer,一个在前也就是显示在荧幕上的Front Buffer, 另一个是在荧幕显示之外的 Back Buffer。你的一切更新都是在 Back Buffer 里进行的,当完成更新之后,Present() 就会把两个Buffer调换,荧幕上就会显示出最新的画面,而你就继续将已经过时了的画面清掉,描绘下一个画面。这么做得好处是你不必等待荧幕刷新时才能进行描绘,而且也能避免画面撕扯(tearing)的现象。

好了,现在你已经有了一个2D游戏的雏形,接下来就发挥你的创作力,写一个游戏出来吧。
以下是整个Program的原始码:
using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectInput;
using Microsoft.DirectX.DirectSound;

namespace My2DGame
{
public class Game : Form
{
public Game()
{
this.ClientSize = new Size(800, 600);
this.Text = "The Game - Managed DirectX";
}
//--Graphics---
Microsoft.DirectX.Direct3D.Device d3dev = null;
Sprite sprite;
Texture myTexture;
Vector3 ctr;
Vector3 pos = new Vector3(400.0f, 300.0f, 0.0f);
float angle = 0.0f;

Matrix transformmatrix, rotatematrix, finalmatrix;
//--input---
Microsoft.DirectX.DirectInput.Device kb;

public bool InitD3D()
{
try
{
PresentParameters pp = new PresentParameters();
pp.Windowed = false;
pp.BackBufferWidth = 800;
pp.BackBufferHeight = 600;
pp.SwapEffect = SwapEffect.Discard;
pp.BackBufferFormat = Format.A8R8G8B8;

d3dev = new Microsoft.DirectX.Direct3D.Device(0,
Microsoft.DirectX.Direct3D.DeviceType.Hardware, this,
CreateFlags.SoftwareVertexProcessing, pp);

sprite = new Sprite(d3dev);
myTexture = TextureLoader.FromFile(d3dev, "mypic.png");
System.Drawing.Image bmp = System.Drawing.Image.FromFile("mypic.png");
ctr = new Vector3(bmp.Width / 2, bmp.Height / 2, 0.0f);

kb = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
kb.SetCooperativeLevel(this,
CooperativeLevelFlags.NonExclusive |
CooperativeLevelFlags.Background);
kb.Acquire();

return true;
}
catch (DirectXException)
{
return false;
}

}

static void Main()
{
using (Game MainForm = new Game())
{
if (!MainForm.InitD3D())
{
MessageBox.Show("Failed to initialize Direct 3D Device");
return;
}
MainForm.Show();

while (MainForm.Created)
{
MainForm.UpdateGame();
Application.DoEvents();
}
}
}
public void UpdateGame() { KeyboardState ks = kb.GetCurrentKeyboardState(); if (ks[Key.Escape]) { this.Close(); return; } if (ks[Key.A]) { pos.X -= 5; if (pos.X < 0) pos.X = 0; } if (ks[Key.D]) { pos.X += 5; if (pos.X > d3dev.Viewport.Width) pos.X = d3dev.Viewport.Width; } if (ks[Key.W]) { pos.Y -= 5; if (pos.Y < 0) pos.Y = 0; } if (ks[Key.S]) { pos.Y += 5; if (pos.Y > d3dev.Viewport.Height) pos.Y = d3dev.Viewport.Height; } angle+=0.1f; if (angle >= 360) { angle = 0; } Render(); }
public void Render() { d3dev.Clear(ClearFlags.Target, Color.SkyBlue, 1.0f, 0); d3dev.BeginScene(); sprite.Begin(SpriteFlags.AlphaBlend); rotatematrix.RotateZ(angle); transformmatrix.Translate(pos); finalmatrix = Matrix.Multiply(rotatematrix, transformmatrix); sprite.Transform = finalmatrix; sprite.Draw(myTexture, Rectangle.Empty, ctr, Vector3.Empty, Color.White); sprite.End(); d3dev.EndScene(); d3dev.Present(); }
}
}


geekman 14-5-2008 06:22 PM

原帖由 tensaix2j 于 14-5-2008 05:49 PM 发表
yay 我第一个来支持你了 :loveliness:


呵呵呵,谢谢。对于 C# 我还在摸索阶段,很多东西都还没弄清楚(例如using, namespace 等是什么意思我也不懂 ;P 我只是把它当作新款式的C++来用。。。),所以各位高手看到有什么不对的请多多指教~:$

Squall_Chua 14-5-2008 07:32 PM

microsoft的directx最令人討厭的地方是
每次新version的都會phase out掉一些舊version的東西
結果讓舊的東西不能夠compile了

言歸主題
managed directx的code看起來干凈多了
我最討厭win32所用的hungarian notation
看到我昏
可以考慮學C# + Managed DirectX
新一代DirectX 10聽說滿不錯下
microsoft重金聘請了些很出名的programmer來打造的

tensaix2j 14-5-2008 09:52 PM

原帖由 geekman 于 14-5-2008 06:22 PM 发表

呵呵呵,谢谢。对于 C# 我还在摸索阶段,很多东西都还没弄清楚(例如using, namespace 等是什么意思我也不懂 ;P 我只是把它当作新款式的C++来用。。。),所以各位高手看到有什么不对的请多多指教~:$


namespace 就是把一群related的 东西 grp 在一起, 这样就可以避免 class name,或 variable name clashing 的问题
using 就是 帮助 略省 写那么长的namespace。。。

Squall_Chua 14-5-2008 10:17 PM

回复 8# tensaix2j 的帖子

簡單來說就跟java的package和import statement是一樣的東西來的
;P ;P

geekman 15-5-2008 11:24 AM

我现在正在考虑是否要继续扩展这个 2D Framework 成为一个完整的游戏范例。不知道自己是否有足够的时间和热情继续下去。。。C# + MDX 真的是越用越爱不释手 :lol: 。

其实 MDX 也已经算是被 MS 所遗弃的了,MS 现在将精力集中在 XNAGS,MDX 只是他们开发 XNA 的过渡产品,不过 MDX 现在的状况已经足够自给自足,要用它来开发游戏也不是问题的。:)

xFreaKx 22-5-2008 12:35 PM

一定要有Graphic Card才能运行吗??
我的只是32bit color 的而已。。
我运行时显示Failed to initialized DirectX 3D Device!:I :I :I
很想学咧~:'(

geekman 22-5-2008 04:21 PM

我用来写这些 code 的电脑装备的是 GeForce 2 MX (现在 GeForce 已经去到第九代了,十代已经公布规格了),已经可以使用 Hardware Processing (只要有支援 Transform & Lighting 的卡就能使用了,也就是说第一代的 GeForce 理论上也可以执行 DX9 Graphics)。如果你的显示卡是 built in 的显示卡就有很大可能性不能运行。你可以尝试运行 Direct X 的测试工具 (在XP的电脑上点 Start -> Run...,然后在指令栏输入 dxdiag 就可以启动 Direct X 测试工具)来判定你的显示卡是什么款式。现在一张普通的 3D 显示卡(可以支援 DX9 的)也不过 RM100上下,我看过一张AGP 8x 的 Radeon 9250 也不过 RM85,这张显示卡已经足够应付 DX9 Graphics,甚至在我的下一个文章(正在研究中):Managed DirectX 3D graphics 也足够应付了。

geekman 22-5-2008 04:41 PM

我决定了要编写一个实际的游戏作为范例,游戏的类型是射击游戏(STG,又称 Shoot 'em up,或者Shmup),是一个直向射击游戏,像经典的1943那种的。

下面是我改写了之后的程式码,主要是把 Game Initialization 分成 Graphics Device 和 Keyboard,Mouse 以及 DirectSound 将会在以后用到时再加进去。然后加入了一个Struct, 作为游戏里的 “角色” ,包括玩家的飞机和敌人的雏形(目前 Actor 的设计比较偏向作为玩家的战机的设定,将来要套用在敌人身上时会做一些更改)。这个 Actor 的功能会在需要用到时再增加、扩充。目前的功能有:回转(承续自前一个demo的功能,目前没有实际用途,所以处在半废置状态),染色,透明化,自我移动范围规制,根据键盘输入而移动等。

接下来的讨论将会趋向于游戏设计,和解决各种设计游戏时将面对的问题。
using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectInput;
using Microsoft.DirectX.DirectSound;

namespace My2DGame
{
public class Game : Form
{
Microsoft.DirectX.Direct3D.Device d3dev = null;
Microsoft.DirectX.DirectInput.Device kb = null;

Actor player;
Sprite sprite;

int errorNo;

public struct Actor
{
Vector3 pos;
Vector3 ctr;
Texture texture;
Matrix rotMatrix, transMatrix, finalMatrix;
int speed;
uint alpha, tint, spcolor;
Rectangle boundingRect;
int powerLevel;

public Actor(Microsoft.DirectX.Direct3D.Device device)
{
pos = new Vector3(device.Viewport.Width / 2, device.Viewport.Height / 1.2f, 0.0f);
texture = TextureLoader.FromFile(device, "plane.dds");

ctr = new Vector3(32.0f, 32.0f, 0.0f);
speed = 5;
rotMatrix = new Matrix();
transMatrix = new Matrix();
finalMatrix = new Matrix();

boundingRect = new Rectangle(32, 32, (device.Viewport.Width - 64), (device.Viewport.Height - 64));
alpha = 0xff000000;
tint = 0xffffff;
spcolor = alpha | tint;
powerLevel = 1;
}

public void Rotate(float angle)
{
rotMatrix.RotateZ(angle);
transMatrix.Translate(pos);
finalMatrix = Matrix.Multiply(rotMatrix, transMatrix);
}

public void Move(int hDir, int vDir)
{
if (hDir > 0)
{
pos.X += speed;
if (pos.X > boundingRect.Right)
pos.X = boundingRect.Right;
}
else if (hDir < 0)
{
pos.X -= speed;
if (pos.X < boundingRect.Left)
pos.X = boundingRect.Left;
}

if (vDir > 0)
{
pos.Y += speed;
if (pos.Y > boundingRect.Bottom)
pos.Y = boundingRect.Bottom;
}
else if (vDir < 0)
{
pos.Y -= speed;
if (pos.Y < boundingRect.Top)
pos.Y = boundingRect.Top;
}
}

public void DrawSprite(Sprite sp)
{
sp.Draw(texture, ctr, pos, unchecked((int)spcolor));
}

public void setAlpha(uint newAlpha)
{
alpha = newAlpha;
spcolor = alpha | tint;
}

public void setTint(uint newTint)
{
tint = newTint;
spcolor = alpha | tint;
}

public void fire()
{
switch (powerLevel)
{
case 1:
break;
case 2:
break;
}
}
}

public Game()
{
this.ClientSize = new Size(800, 600);
this.Text = "My 2D Game with Managed DirectX";
}

public bool InitD3D()
{
try
{
PresentParameters pp = new PresentParameters();
pp.Windowed = false;
pp.BackBufferWidth = 800;
pp.BackBufferHeight = 600;
pp.BackBufferFormat = Format.A8R8G8B8;
pp.SwapEffect = SwapEffect.Discard;

d3dev = new Microsoft.DirectX.Direct3D.Device(0,
Microsoft.DirectX.Direct3D.DeviceType.Hardware,
this,
CreateFlags.HardwareVertexProcessing,
pp);

return true;
}
catch (DirectXException)
{
errorNo = DirectXException.LastError;
return false;
}
}

public bool InitKeyboard()
{
try
{
kb = new Microsoft.DirectX.DirectInput.Device(SystemGuid.Keyboard);
kb.SetCooperativeLevel(this, CooperativeLevelFlags.NonExclusive | CooperativeLevelFlags.Background);

kb.Acquire();
return true;
}
catch(DirectXException)
{
errorNo = DirectXException.LastError;
return false;
}
}

public void UpdateGame()
{
KeyboardState ks = kb.GetCurrentKeyboardState();

if (ks[Key.Escape])
{
this.Close();
return;
}
if (ks[Key.Space])
{
player.fire();
}
if (ks[Key.A])
{
player.Move(-1, 0);
}
if (ks[Key.D])
{
player.Move(1, 0);
}
if (ks[Key.W])
{
player.Move(0, -1);
}
if (ks[Key.S])
{
player.Move(0, 1);
}
RenderGame();
}

public void RenderGame()
{
d3dev.Clear(ClearFlags.Target, Color.Blue, 1.0f, 0);

d3dev.BeginScene();
sprite.Begin(SpriteFlags.AlphaBlend);
player.DrawSprite(sprite);
sprite.End();
d3dev.EndScene();
d3dev.Present();

}

public bool InitGame()
{
if (!InitD3D())
{
return false;
}

if (!InitKeyboard())
{
return false;
}

sprite = new Sprite(d3dev);
player = new Actor(d3dev);
return true;
}

static void Main()
{
using (Game MainForm = new Game())
{
if (!MainForm.InitGame())
{

MessageBox.Show("Failed to initialize D3D: " + DirectXException.GetDirectXErrorString(MainForm.errorNo));
return;
}
MainForm.Show();

while (MainForm.Created)
{
MainForm.UpdateGame();
Application.DoEvents();
}
}
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: