使用Managed DirectX编写游戏----理解sample framework 之事件处理

[原创]Managed DirectX 基础使用教程(一) hangtao_me@yahoo.com.cn 本次内容:如何用Managed  DirectX 显示图片(2D),以及一些变形.上回我们已经学习了MDX的一些基本知识,现在我们就要来使用它了.准备:创建一个Windows 窗体应用程序然后在引用中右键添加引用然后选择需要的.NET项目然后在代码中写上相应的using .......现在准备工作已经做好了,那么在 阅读详情

作者:clayman
clayman_joe@yahoo.com.cn
转载请注明作者,商业使用请联系我^_^

事件处理机制

      

         Framework类是sample framework中最重要的类,完成了创建窗体,初始化设备,创建命令行,事件处理(render loop)以及调节各种参数的任务。Framework类包含在dxmut.cs文件中。其中,比较特别的就是事件处理模型(或render loop)。

         为了获得高性能的渲染以及事件处理机制,framework类在初始化的方法中使用Device.IsUsingEventHandlers = false;关闭了事件处理模型。我们先来看看为什么默认的事件处理机制会导致性能的损失。默认情况下,Managed DirectX中的类在每创建一个资源时,都会为device订阅一些必然的事件。在最简单的情况下,每个资源(比如纹理或顶点缓冲)将会订阅Disposing事件,以及其他一些诸如DeviceLost和DeviceReset的事件。这个步骤在整个对象的生存期都会发生。但是为什么我们在程序中不需要这种行为呢?

         主要原因就是需要对这种行为付出一定代价,有些情况下,代价还会很大。我们举个例子来说明这一点。看看下面这段简单的伪代码:

SomeResource res = new SomeResource(device);

device.Render(res);

         这段代码看起来几乎是“无害”的。只是创建了一个资源,并且渲染它。当这个对象不再使用时,垃圾回收器应该会智能的清除它。但是,这个想法是完全错误的。当创建新资源时,至少需要对device订阅一个事件,允许device正确的清除它。这种订阅实际上是一把“双刃剑”。

首先,订阅事件时需要分配(allocation)EventHander对象完成实际的订阅工作。虽然这种分配的代价很小,但是,我们稍候就会看到,就算是很小的分配也会迅速膨胀。第二,订阅事件之后,资源和设备之间就有了一个硬连接(hard link)。因此,在垃圾回收器的眼里,这个对象在device的生存期里仍然处于使用状态,并且没有取消事件的订约。设想一下,这代码如果在渲染每帧的时候都运行一次;再设想一下,你的程序每分钟需要进行上千次渲染,并且程序已经运行了两分钟。结果,你创建了120000个在device生存期间不会被回收的对象,以及120000个事件句柄。所创建的这些对象不但会迅速消耗内存,而且会导致额外的垃圾回收,严重影响程序性能。如果你的资源都位于显存中,那么很快就会耗尽显存。

         这里,我们还没有考虑当最后释放设备时可能发生的情况。在前面的例子中,当释放device时,首先触发Disposing事件,此时,有120000个监听者订约了这个事件。你是否已经考虑到,调用这个巨大的事件句柄列表会将花费很多时间?实际上这将会花去几分钟时间,并且让用户认为程序已经处于死锁状态。

         因此,最好只在最简单的Direct3D程序中使用Managed Direct3D内建的事件处理机制。在任何需要考虑内存容量和性能的应用中(比如游戏),都必须避免这些处理过程。

         接下来,我们来看看framework中是如何实现事件处理模型的。实际上,SDK中的事件处理模型也是几经修改,现在使用的方法最早由Tom Mille 在他的bolg上贴出了具体的实现:

public void MainLoop()

{

        // Hook the application's idle event

        System.Windows.Forms.Application.Idle += new EventHandler(OnApplicationIdle);

        System.Windows.Forms.Application.Run(myForm);

}

 

private void OnApplicationIdle(object sender, EventArgs e)

{

    while (AppStillIdle)

    {

         // Render a frame during idle time (no messages are waiting)

         UpdateEnvironment();

         Render3DEnvironment();

    }

}

 

private bool AppStillIdle

{

     get

    {

        NativeMethods.Message msg;

        return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);

     }

}

 

And the declarations for those two native methods members:

 

[StructLayout(LayoutKind.Sequential)]

public struct Message

{

    public IntPtr hWnd;

    public WindowMessage msg;

    public IntPtr wParam;

    public IntPtr lParam;

    public uint time;

    public System.Drawing.Point p;

}

 

[System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously

[DllImport("User32.dll", CharSet=CharSet.Auto)]

public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);

 

         这里,通过平台调用,使用了一些Win32 API。首先,在main方法中订阅了Application.Idle事件。在程序处理完了所有消息(如果不熟悉消息,那么可以把消息理解为系统定义的一个32位的值,他唯一的定义了一个事件,向Windows发出一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。)之后,将会触发Application.Idle事件。我们的目标是让程序尽可能快,尽可能多的处理消息,同时不打断wendows消息的输入。

         在OnApplicationIdle事件处理程序中,使用的了简单的Win32 API PeekMessage来检查程序是否有任何未处理的消息。这里使用while循环的原因是保证在处理完所有消息,同时消息队列还为空时,只触发一次Application.Idle事件。所以,我没一直循环,直到有新的消息,然后,跳出循环。普通的.Net WinForm窗体消息句柄将会选择未处理的消息。

         接下来,我们将使用框架,来显示一些物体了(源码请参考SDK中的empty project)。由于框架已经为我们完成了以上工作。我们只需要选择订阅那些事件就可以了。Sample framework通过这些事件通知应用程序关于改变设备、用户输入以及各种窗口消息。这些事件是可选的,但是,如果你没有设置,那么框架就不会为你处理相应的事件。在main方法中,创建了GameEngine对象之后,添加代码:

sampleFramework.Disposing += new EventHandler(blockerEngine.OnDestroyDevice);

sampleFramework.DeviceLost += new EventHandler(blockerEngine.OnLoseDevice);

sampleFramework.DeviceCreated += new DeviceEventHandler(blockerEngine.CreateDevice);

sampleFramework.DeviceReset += new DeviceEventHandler(blockerEngine.OnResetDevice);

sampleFramework.SetWndProcCallback(new WndProcCallback(blockerEngine.OnMsgProc));

sampleFramework.SetCallbackInterface(blockerEngine);

(注意,虽然在SDK October 2005的文档中还可以查到framework对象的SetKeyboardCallback方法,但实际上这个方法已经被删除了,老版本的SDK示例中使用了整个方法。)

         这一段代码作了很多工作,首先,为四个事件订阅了事件处理程序,分别是创建设备,失去设备,重置设备,销毁设备。我们将在后面实现这些事件处理程序。SetWndProcCallback方法订阅了处理windows消息的方法。随后,使用当前game engine实例作为参数,调用SetCallbackInterface方法。之后,编写事件处理程序

private void OnCreateDevice(object sender, DeviceEventArgs e)

{

    SurfaceDescription desc = e.BackBufferDescription;

}

private void OnResetDevice(object sender, DeviceEventArgs e)

{

    SurfaceDescription desc = e.BackBufferDescription;

}

private void OnLostDevice(object sender, EventArgs e)

{

}

private void OnDestroyDevice(object sender, EventArgs e)

{

}

public IntPtr OnMsgProc(IntPtr hWnd, NativeMethods.WindowMessage msg, IntPtr wParam, IntPtr lParam, ref bool noFurtherProcessing)

{

}

 

         由于之前的SetCallbackInterface需要接收一个IframeworkCallback的变量作为参数,但是我们的game engine类并没有实现这个类,所以添加以下代码:

public class EmptyProject : IFrameworkCallback, IdeviceCreation

         实现这个接口所定义的方法

public void OnFrameMove(Device device, double appTime, float elapsedTime)

{

}

public void OnFrameRender(Device device, double appTime, float elapsedTime)

{

}

         哦,框架性的东西总算是弄的差不多了。在SetCallbackInterface之后加上以下代码

try

{

 

         sampleFramework.SetCursorSettings(true, true);

         sampleFramework.Initialize( false, false, true );

         sampleFramework.CreateWindow("haha");

         sampleFramework.Window.KeyDown += new System.Windows.Forms.KeyEventHandler(blockerEngine.OnKeyEvent);

         sampleFramework.CreateDevice( 0, true, Framework.DefaultSizeWidth, Framework.DefaultSizeHeight, blockerEngine);

         sampleFramework.MainLoop();

 

}

#if(DEBUG)

         catch (Exception e)

         {

                   sampleFramework.DisplayErrorMessage(e);

#else

     catch

     {

          // In release mode fail silently

#endif

                   // Ignore any exceptions here, they would have been handled by other areas

                   return (sampleFramework.ExitCode == 0) ? 1 : sampleFramework.ExitCode; // Return an error code here

                   }

 

                   return sampleFramework.ExitCode;

         }

}

         现在运行程序看看,虽然只是一个蓝色的窗口,但是我们背后所搭建的框架已经可以实际应用到一个游戏之中了。为了让程序开起来有一点点交互,我们还订阅了键盘事件,通过空格键可以改变程序的颜色。

 

 源码:http://bbs.gameres.com/upload/sf_2005103102041.rar

sample-framework 示例流框架 一个简单的流框架,带有用于字数统计的组件 立即下载

相关推荐

Qwen2.5-Omni实战:5分钟搞定多模态AI模型本地部署(含Thinker-Talker架构解析)

本文详细介绍了Qwen2.5-Omni多模态AI模型的本地部署流程,包括硬件配置、环境准备和实战应用开发。特别解析了其创新的Thinker-Talker架构,该架构通过TMRoPE位置编码实现跨模态同步处理,大幅提升多模态任务效率。5分钟即可完成部署,适合开发者快速实现文本、图像和音频的联合处理。

weixin_29085865的博客 171

【翻译】managed DirectX (第三章)

使用简单的渲染技术翻译:clayman    至今为止,我们的渲染工作效率都很低。每次渲染场景时,都要分配新的顶点列表,并且所有东西存储在系统内存里。现代显卡集成了足够显存,把顶点数据存放在显存可以获得大幅的新能提升:存放在系统内存里的数据,渲染每一帧时都要拷贝到显卡,这会带来极大的损失。只有移除每帧时的这种分配才能帮助我们提高性能。  使用顶点缓冲(Using Vertex Buffers)Di

Clayman的专栏 5027

基于FPGA的心电信号实时处理与智能诊断系统实现【附源码解析】

本文详细介绍了基于FPGA的心电信号实时处理与智能诊断系统的设计与实现。系统通过高精度采集电路和FPGA数字滤波技术处理微弱心电信号,采用自适应算法准确检测QRS波群,实现心率计算和心律异常诊断。附源码解析展示了FIR滤波器、中值滤波器和诊断逻辑的Verilog实现,为心电监护系统开发提供实用参考。

g8f9d0s1a2的博客 770

SampleFramework学习

这是在追新使用 VS 2005后又不得不回来使用VC6,VC6在98/Me XP 2k 2003上工作的都良好,但是也有个Bug,就是在使用Dx时并不能列出组件的成员。//-------------------------------------------------//假设您已经安装好了DirectX SDK Summer 2003以上版本到D:\下。Sample Framework 例子框架...

weixin_30408739的博客 141

SAP 资产主数据屏幕增强:对比字段追加与子屏幕/页签 2 种方案优劣

本文深入对比了SAP资产主数据(AS01/AS03)屏幕增强的两种方案:字段追加与子屏幕/页签。从技术实现、业务适配、系统维护等维度分析各自优劣,提供方案选型决策树和混合方案实践建议,帮助企业在SAP实施中做出最优选择。

weixin_28559803的博客 284

基于ZYNQ的HP总线与DDR交互:实现PL与PS高效数据传输的完整指南

本文将深入探讨如何通过AXI HP总线实现PL与PS的DDR交互,结合代码示例、硬件配置与创新优化,助力开发者掌握这一关键技术。ZYNQ内部通过AXI HP接口直接连接PL与DDR控制器,PL通过AXI协议发送读写请求,绕过PS的CPU直接操作DDR。通过AXI HP总线实现PL与PS的DDR交互,开发者可充分发挥ZYNQ异构架构的优势。在Xilinx ZYNQ系列芯片中,PS(Processing System)与PL(Programmable Logic)的协同设计是其核心优势。

QQ_778132974的博客 1223

DirectX Input 键盘实现

// DInputKeyboard.h: interface for the CDInputKeyboard class.////////////////////////////////////////////////////////////////////////#pragma once#define DIRECTINPUT_VERSION 0x800#include

zgl7903的专栏 4205

使用强化学习快速让AI学会玩贪食蛇游戏(轻量级二十分钟训练+代码)

如何让AI玩会贪食蛇,甚至比你厉害概述构建问题(强化学习求解的一般步骤)环境动作定义状态定义奖励设计训练奖励值收敛图效果代码 概述 所用技术:强化学习(Deep Reinforcement Learning),属于一种无监督学习,利用奖励rewardrewardreward教会智能体AgentAgentAgent在合适的场景做合适的决策。 采用算法:试过两种算法D3QN和离散版本的PPO算法,最终采用离散版本PPO算法+GAE(PPO是我用过的性能最好的算法之一) 编程语言与深度学习框架:Python3.

ZYunfei的博客 2万+

Windows桌面实现之八(DirectX HOOK 方式截取特殊的全屏程序之二)

by fanxiushu 2019-04-18 转载或引用请注明原始作者。 接上文。 WIN7以上系统WDDM虚拟显卡开发(WDDM Filter/Hook Driver 显卡过滤驱动开发之一) 这篇文章,曾经提到过: windows的应用程序中,绘图的基础图形库包括...

fanxiushu的专栏 1万+

华为OD机考真题 新系统 9月23号 【世界杯徽章换购】多语言题解

华为OD机试真题 新系统【世界杯徽章换购】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题 【世界杯徽章换购】多语言题解。

qq_45776114的博客 686

Android 埋点信息分析——statsd框架

StatsPullAtomService 用以采集数据,并将数据填充到参数中,在StatsManager.PullAtomCallbackInternal 中会转换成 Parcel 格式传入到 native。 StatsCallbackPuller::PullInternal 中会回调StatsManager.PullAtomCallbackInternal 的onPullAtom,该函数中会回调StatsCallbackPuller::PullInternal 中定义的PullResultRecei

私房菜 2628

使用Managed DirectX编写游戏----理解sample framework 之枚举设备

作者:claymanclayman_joe@yahoo.com.cn转载请注明作者,商业使用请联系我^_^理解框架          首先创建工程,添加对DirectX程序集的引用。接下来,把sample framework添加到工程中。我们把这些文件放到一个单独的文件夹中,在解决方案管理器中点击右键---添加---新建文件夹,并把它命名为framework。右键点击新创建

Clayman的专栏 2816

【人工智能 Open AI】程序员怎样提升研发效率?从技术、业务、项目管理等等多个方面来阐述。

程序员要想提升研发效率,需要从技术、业务、项目管理等多方面着手,多靠工具配合实践实现,从而提升研发效率。

AI天才研究院 5526

打造属于自己的sample framework(一)

将sample framework 做成用户控件        可能本文比较适合对 sample framework 有稍微了解的人。        前段时间研究了一下directx sdk里的sample framework。s

kkme88的专栏 1029

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Managed Direct3D开发经验浅析

注意:本文主要针对Managed DirectX(MDX) 1.1,而不是MDX2.0或XNA Question 1. 如何正确地安装Managed DirectX(MDX)运行库? 对于开发者,先装好vs2005,然后再安装最新的DirectX SDK即可,SDK会自动安装MDX运行库; 对于使用者,并没有必要安装SDK,仅需正确地安装.Net Framework 2.0 Redis...

weixin_34146410的博客 155

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

hook dx然后绘制出fps

   也不是啥新鲜东西了,只是搞着玩玩罢了。   首先是Hook dx device,然后可以hook内部的函数,这里只是hook d3drender的结束标记函数endScene。内部记录fps,然后创建一个D3DXFont对象来讲fps绘制在屏幕的指定位置。   hook进d3d 还是老方法,设置钩子,将自己代码注入到远程进程地址空间,在dll代码中进行hook api。这里是修改

doudouHuY的专栏 4671

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

纯C#实现Hook功能详解

发布一个自己写的用于Hook .Net方法的类库,代码量不大,完全的C#代码实现,是一个比较有趣的功能,分享出来希望能和大家共同探讨安装:Install-Package DotNetDetour源码:http://xiazai.jb51.net/201701/yuanma/DotNetDetour_jb51.rar1.为何想做这个说到hook大家都应该不陌生,就是改变函数的执行流程,让本应该执行的...

bruce135lee的专栏 1万+

【Unity知识速递】第7期 Unity 6.6 深度解析:WebGPU 正式版与 WebAssembly64,Web 游戏的内存革命

如何启用 Wasm64?需要注意的是,Wasm64 需要浏览器支持 Memory64 提案,目前主流浏览器的最新版本都已支持。一句话总结: Unity 6.6 用 WebGPU 转正、Wasm64 16GB 内存和渐进式加载三连击,将 Web 游戏的体验天花板又往上推了一大截——Web 平台不再是"妥协版",正在成为真正的一等公民。Unity 6.6 新增了 WebAssembly64(Wasm64) 构建选项,这可能是本次更新中对 Web 游戏开发者影响最大的一项升级。

crystal_WJT的博客 416

我给 DeepSeek 大肥鱼做了一款马里奥式闯关游戏

当全网都在把大肥鱼做成直播虚拟主播的时候,我决定给她做一款横版闯关游戏。

2301_79028807的博客 888

节日小游戏活动快速落地与高效转化技巧

比如,把平时验证过的消消乐、大转盘、接红包等经典玩法作为底子,只需要针对特定的节日(像春节、中秋)或者门店庆典,快速替换一套应景的UI素材和文案,再植入专属的节日福利。这种“旧瓶装新酒”的方式,能把开发周期压缩到极致,确保活动能踩着流量的爆发点准时上线。特别是对于实体门店,游戏通关后的奖励最好能直接引导到线下核销,比如“满100减20”的优惠券,这样能把线上的流量精准地拽进店里,形成实实在在的成交。这种基于熟人关系的推荐,比硬广有效得多,能迅速在朋友圈和社群里铺开,实现低成本的流量获取。

Hxzyxkf2016的博客 181

Claude Code做Unity项目移植(三): 使用SKILL进行自动化Git提交

本文介绍基于ClaudeCodeSkill的Unity自动化Git提交助手——unity-git-commit,通过四大模块实现提交前的智能检查与安全修复:1)校验Git仓库状态;2)验证Unity项目文件(如排除Library/Temp,确保.meta文件完整);3)依据可配置的C#命名规范自动识别并安全修复命名问题(避免序列化、反射等高危场景误改);4)生成精准提交注释,但强制人工确认。全程仅本地commit,不自动推送或破坏性操作,保障安全。核心思想为“机械化工作交AI,关键决策留人工”,显著提升提交

Aman1984的博客 641
上一篇: 【翻译】Managed DirectX(第十二章上 )
下一篇: 使用Managed DirectX创建三维地形
soilwork
博客等级 码龄23年 290粉丝 116原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值