使用 DirectDraw Wrapper for C# 旋转 Sprite 对象





3.00/5 (4投票s)
评估 C# 在全屏 1280x1024 分辨率下渲染超过 1000 个动画精灵的能力。
引言
该项目的主要目标是观察 C# 在通过托管代码的 DirectDraw 包装器 (DDW) 渲染数百个视觉对象时的行为。
如果主要目标得以实现,并且 C#/DDW 被证明是一个坚实的开发环境,那么项目的方向将是创建一个类似游戏的瓦片世界,其中包含数百个可见的静态和移动对象。
使用代码
根据设计,所有方法在成功时应返回 null,在失败时,返回内部生成的异常。
以下是类描述列表。
System.Windows.Forms.Form Form1
承载用户窗口,将用户输入翻译给引擎。
CWorld
一个与屏幕分辨率 1280x1024 相等的世界坐标系统。
CEngine
维护用户请求的状态、单元初始化、主循环,同时更新和绘制单元和文本。
CDevice
封装 DDW 初始化。显示模式为 1280x1024x16 (宽度 x 高度 x 深度)。
CSprite
精灵描述,x 和 y 方向的总帧数以及 respective 大小,或者对于位图中的单个图像,显然是 1x1。
CSurface
为前缓冲区和后缓冲区以及位图 (
CSprite
) 保存空间。Respective 描述和颜色键控。CFrameRate
计算每秒渲染的帧数。
- CUnit
基本单元信息,包括类指针
CSprite
、CUnitManager
和状态 (ACTION_BASIC
enum
)。
由于状态可能会累积(未来使用),单元可能想要 STAND 但也 ROTATE_LEFT
。我决定最好的方法是使用一个 BitArray
,其大小等于 ACTION_BASIC
enum
中的状态数。
public enum ACTION_BASIC
{
STOPPED=0,
STOPPING,
ROTATE_LEFT,
ROTATE_RIGTH,
MOVE_FORWARD,
MOVE_BACKWARD,
RUN_FORWARD,
RUN_BACKWARD,
UNABLE_TO_MOVE,
SLIDE_LEFT,
SLIDE_RIGTH,
}
public BitArray m_ACTION_BASIC = new BitArray(new bool[11]
{true,false,false,false,false,false,false,false,false,false,false});
调用通过非常简单的 get/set 方法完成。
//
if (this.m_ACTION_BASIC.Get((int)ACTION_BASIC.ROTATE_LEFT))
this.Angle += this.m_TurnVelocity / m_FPS;
//
m_ACTION_BASIC.Set((int)ACTION_BASIC.STOPPED,true);
对 DDW 最重要的调用是 Draw
,下面将展示一个带有完整解释的示例。
s.Draw( // Surface to write to
new Rectangle( // writeto: In this case the screen coordinates rectangle
(int)m_X,
(int)m_Y,
m_CSprite.m_FrameWidth,
m_CSprite.m_FrameHeigth
),
m_CSprite.m_Surface.m_Surface, // Surface to read from
new Rectangle( // readfrom: In this case the sprite coordinates rectangle
(m_Frame % m_CSprite.m_NFrameX)*m_CSprite.m_FrameWidth,
(m_Frame / m_CSprite.m_NFrameX)*m_CSprite.m_FrameHeigth,
m_CSprite.m_FrameWidth,
m_CSprite.m_FrameHeigth
),
DrawFlags.KeySource|DrawFlags.Wait // drawing flags
);
读者说易于阅读。实际上非常非常简单,第一个矩形保存要写入的表面 s
,以及坐标值(记住屏幕大小等于世界大小,所以 m_X
代表屏幕上单元的 x 坐标,y 也一样)。宽度和高度来自加载时精灵的声明。
第二个矩形保存要读取的表面,即正在使用的 CSprite
的表面,以及坐标值。因此,如本版本所示,仅使用蓝色帧(如下所示)。该类必须保存确切的帧 (m_Frame
),这取决于它的角度。所以,基于帧的宽度,整个位图的精确 x(如下所示)是使用读者在最后一个代码片段中看到的公式计算的。y 也一样。
位图文件已加载并存储为 m_CSprite
。
CUnitManager
负责初始化单元。将所有已初始化的单元保存在一个名为 UNITLIST
的链表中(声明如下所示)。将 CEngine
的命令传递给单元。
public class UNITLIST
{
private object pPrev;
private object pNext;
private CUnit pItem;
public UNITLIST _PREV {get{return (UNITLIST)pPrev;}
set{pPrev = (UNITLIST)value;}}
public UNITLIST _NEXT {get{return (UNITLIST)pNext;}
set{pNext = (UNITLIST)value;}}
public CUnit _UNIT {get{return pItem;}set{pItem = value;}}
}
在下面的示例方法中,CEngine
可以使用 CUnitManager
在表面 s
中绘制每个 CUnit
。循环从头部元素 pHead
开始,向下移动到列表的最后一个插入元素,对每个单元执行 Draw
方法。
public System.Exception Draw(Microsoft.DirectX.DirectDraw.Surface s)
{
try
{
UNITLIST ul = pHead;
for (int i = 0; i < m_Count; i++)
{
ul._UNIT.Draw(s);
ul = ul._NEXT;
}
return null;
}
catch(Exception e)
{
this.m_Trace.T("CUnitManager::Draw>"+e);
return e;
}
}
CUtils
此类实际上包含 2D 数学。每个单元的位置都由一个 x、y、角度的 VECTOR 结构表示。这些方法在当前版本中的使用很少,但当下一版本中,例如:单元尝试从点 p1 移动到鼠标点击的点 p2 时,它们将非常重要。在本版本中包含它们是为了表明对这些计算精度的关注,并给读者时间进行“批评”。尽情发挥吧 // 谢谢。
DistancePercentageFromOrigin(VECTOR vInicial,VECTOR vFinal,VECTOR vCurrent)
public double DistancePercentageFromOrigin(VECTOR vInicial,
VECTOR vFinal, VECTOR vCurrent)
{
double dInicial=Math.Sqrt(Math.Pow((vFinal.x-vInicial.x),2) +
Math.Pow((vFinal.y-vInicial.y),2));
double dCurrent=Math.Sqrt(Math.Pow((vFinal.x-vCurrent.x),2) +
Math.Pow((vFinal.y-vCurrent.y),2));
if (dInicial==0)
return 0;
return dCurrent*100/dInicial;
}
InternalProduct(VECTOR v1, VECTOR v2)
public double InternalProduct(VECTOR v1, VECTOR v2)
{
return v1.x*v2.x+v1.y*v2.y;
}
Modulus(VECTOR v1)
public double Modulus(VECTOR v1)
{
return Math.Sqrt(Math.Pow(v1.x,2)+Math.Pow(v1.y,2));
}
AngleBetweenTwoVectorsWithSameOrigin(VECTOR v1, VECTOR v2)
public double AngleBetweenTwoVectorsWithSameOrigin(VECTOR v1, VECTOR v2)
{
double m1=Modulus(v1)*Modulus(v2);
if (m1==0)
return 0;
double m2=InternalProduct(v1,v2)/m1;
return Math.Acos(m2);
}
DecideLeftRigthOnAngle(VECTOR v1, double dAngle)
public int DecideLeftRigthOnAngle(VECTOR v1, double dAngle)
{
if (v1.x*-1*Math.Sin(dAngle)<=Math.Cos(dAngle)*v1.y)
return -1;
return 1;
}
CTrace
由于这是一个全屏应用程序,没有 IDE,因此没有调试;该项目包含一个文本文件调试引擎。
关注点
- (--) DDW 中的透明模型无法处理白色背景的位图 - 因此项目必须使用读者在名称下方看到的黑色背景:spr_blackdrop.png。
- (--)
CUnitManager
有点麻烦,因为 C# 编译器不接受自引用的结构。CUnitManager
类必须保存该结构,因为它需要将操作传递给屏幕上的每个可见CUnit
。第一个解决方法是为结构中的每个变量创建访问器。这不起作用。在第二个解决方法中,这个问题是将结构声明为一个类。这工作正常。 - (++) Microsoft 曾承诺 C++ DirectDraw 实现 98% 的正常 blitting。本项目体验到了更轻松的编码方式。C# 真正地提升了代码学习体验,并兑现了承诺。高达约 1000 个单元,以非常快的速度左右旋转 - 正常的画面效果,使用 Radeon 9600 和 2.8MHz P4。由于应用程序/屏幕尺寸的限制(创建时新精灵不能重叠),并且在此版本中,世界窗口等于屏幕。在下一版本中,将屏幕上超过约 1000 个单元将是一个测试。
- (++) 读者此时应该明白该项目的方向,即一个实时策略应用程序。
历史
- 2004/12 0.0 初始版本,固定世界大小。无人工智能 (AI) 单元。单元用户命令包括简单的对象操作(添加、旋转)。
- 2005/01 0.1 创建世界文件加载器,并使屏幕窗口大小不同于世界大小。新世界对象(建筑物/树木)。单元 AI 级别 1(更多基本身体动作)和单元交互(我看到谁?)。