unreal3的viewport和client
名字里带viewport/client的类不少,以及相关的类:
FViewportFrame、FViewport
FViewportClient/UScriptViewportClient/UGameViewportClient
UClient/UWindowsClient
UEngine/UGameEngine/UEditorEngine/UUnrealEdEngine
极易混淆,现整理如下:
首先从总体层级上看,UEngine最大,它包含了UClient和UGameViewportClient,
class UEngine : public USubsystem
{
public:
……
class UClient* Client;
class UGameViewportClient* GameViewport;
……
UEngine是己只是个抽象类,实际运行时根据是游戏还是编辑器模式,创建相应子类的实例,如是游戏则UGameEngine,如是编辑器则UUnrealEdEngine。
它的实例创建出来后就存在全局变量GEngine上。
在UGameEngine::Init里,先后创建了其它对象:
1、先创建了UClient,实际的子类通过“engine-ini:Engine.Engine.Client”配置指定,在windows上是【WinDrv.WindowsClient】,在mac上则是【MacDrv.MacClient】,可见各子类就是对应各平台相关的实现;
2、然后创建UGameViewportClient,实际的类型是通过Engine上的GameViewportClientClass这个globalconfig属性指定的,所以它会自动从配置中读取,配置的默认值是【Engine.GameViewportClient】
3、接着创建FViewportFrame:
ViewportFrame = Client->CreateViewportFrame(
ViewportClient,
*AppName,
GSystemSettings.ResX,
GSystemSettings.ResY,
GSystemSettings.bFullscreen
);
FViewportFrame* UWindowsClient::CreateViewportFrame(FViewportClient* ViewportClient,const TCHAR* InName,UINT SizeX,UINT SizeY,UBOOL Fullscreen)
{
return new FWindowsViewport(this,ViewportClient,InName,SizeX,SizeY,Fullscreen,NULL);
}
注意:创建函数(CreateViewportFrame)是UClient上的、并把UGameViewportClient做为参数,由此可略知其从属关系。
做为一般情形,这里创建的是FViewportFrame,所谓-Frame,也就是(操作系统层面的)外包窗口,因为通常程序总是要自己创建“主窗口”。但是在某些模式下(如内嵌于浏览器等),外包主窗口已经有了,就不需要再创建FViewportFrame,取而代之的是通过CreateWindowChildViewport接口来创建一个FViewport。
实际上FViewportFrame接口只有两个函数,一是获取其中的FViewport,二是Resize改变大小,这个Resize更能体现其做为外包窗口的特征。
此外,前面提到UClient的实际类型是UWindowsClient,它创建的FViewportFrame自然也是同一平台体系下的子类即FWindowsViewport。
FWindowsViewport是一个特定于Windows平台相关的实现类,它同时实现了FViewportFrame和FViewport两个接口,当然这只是实现的方便,并不能抹杀这两个接口的差异。
因为3中的创建方式,FViewportFrame在创建时传入了两个关键对象:UClient和UGameViewportClient,这当然都会被记在其成员变量上,以便后续使用。
下面细看UEngine的三大台柱子:(UClient、UGameViewportClient、FViewport)
台柱之一:UClient
看起来它像是对整个app的封装,其中的Tick函数每帧被调用,主要内容就是处理窗口和输入消息。
它的另外一个作用则是用来创建FViewport(Frame),这主要是给平台相关的子类机会,创建同一平台体系的FViewport子类。
UClient、UWindowsClient都是纯c++类(没有相关的脚本类),但是由于手动在声明中添加了DECLARE_ABSTRACT_CLASS_INTRINSIC,所以也遵照UObject/UClass系统规则,可以用StaticLoadClass/ConstructObject等规范函数统一创建。
台柱之二:UGameViewportClient
UGameViewportClient继承自->UScriptViewportClient->(UObject,FViewportClient),这里UScriptViewportClient没啥用,纯粹是打个酱油,把FViewportClient这种纯c++类转换到UClass体系链中而已。
先看FViewportClient基类接口的方法:
看起来它主要是用于消息处理。
但是在UEngine这种高层基类中声明的属性GameViewport,其类型并非FViewportClient基类,而是直接作为UGameViewportClient子类变量,这主要是因为脚本要用,GameViewport属性是在UEngine的脚本类里声明的,在脚本里只用当然只能用符合UObject/UClass体系的东西,而做为继承链中间过渡的UScriptViewportClient就是起了这个作用。
当然UGameViewportClient也添加了很多新方法和属性,如它也有一个Tick函数,也是每帧被调用。还有一个非要重要的成员GlobalInteractions,这个数组存的就是所有的输入消息处理器。
台柱之三:FViewport
这个,实在看不出有什么特点,只能说它是跟渲染器关联最紧密的一个类了,unreal3应该就是用FViewport来抽象一个可以渲染的区域吧。
三者之间彼此关联,纠缠万分。
首先,UEngine上存着由它创建的UClient和UGameViewportClient各一个,并且在Tick中又分别调用了它们的Tick
然后,UGameViewportClient上存着由UClient创建的FViewportFrame(以及其内的FViewport),(UGameViewportClient::SetViewportFrame)
还有,做为子类的UWindowsClient上存着所有由它创建的FWindowsViewport(FViewportFrame,FViewport),(static TArray<FWindowsViewport*> Viewports)
最后,作为最后被创建出来的FWindowsViewport,它当然也要记着创建它的UClient,以及用来初始化它的UGameViewportClient(这个是由基类FViewport记着的)。
在消息处理方面:
首先,系统窗口的消息处理函数是UWindowsClient::StaticWndProc
然后,大部份消息会交给该窗口(HWnd)对应的FWindowsViewport来处理:Viewports(i)->ViewportWndProc
然后,除了需要即时响应的消息被立即处理外,大部份输入类消息被缓存起来:Client->DeferMessage,这样一来消息处理流程又回到了UWindowsClient里
然后,在UWindowsClient::Tick里,会调用ProcessDeferredMessages和ProcessInput,来处理所有的输入消息,前者包括了键盘和鼠标点击,后者是鼠标移动,在Unreal3里比较特别的是,鼠标移动并不通过WM_XXX类的窗口消息来获取,而是用IDirectInputDevice8接口来获取。
ProcessDeferredMessages里面,每个消息又转给它的FWindowsViewport上的同名函数,最终都会交到它里面的FViewportClient(也就是UGameViewportClient)::InputKey/InputChar
ProcessInput里面,取当前活跃的FWindowsViewport(通常也就一个窗口),把鼠标事件都交给它里面的FViewportClient(也就是UGameViewportClient)::InputAxis
可见,键盘和鼠标操作,最终都是到了UGameViewportClient的InputXXX里,而它们里面的分派逻辑,也大致一样:
先是给自己身上的代理处理,UGameViewportClient上的HandleInput(Axis/Key/Char)
然后是给GlobalInteractions里的每一个去处理,这里的每个元素都是一个Interaction,Interaction是Unreal3里用来处理用户操作的基类,它身上也有一套Input(Axis/Key/Char)。
实际运行观察结果 ,GlobalInteractions共有4个元素,分别是:
Console,用来处理调试指令
GFxInteraction,给swf对象分派消息
UIInteraction,这个UI不知是啥了,可能是Unreal3自己的UI模块?
PlayerManagerInteraction:比较逻辑的一层,把输入映射给对应的Player处理,有点难以理解,但是ini里的[Engine.PlayerInput]节中,所有bindings绑定的消息处理函数,就是由它分派的
unreal3的viewport和client的更多相关文章
- (原)Unreal 渲染模块 渲染流程
@author:白袍小道 浏览分享随缘,评论不喷亦可. 扯淡部分: 在temp中,乱七八糟的说了下大致的UE过程.下面我们还是稍微别那么任性,一步步来吧. UE渲染模块牵扯到场景遍历. ...
- 从壹开始前后端分离 [ Vue2.0+.NetCore2.1] 二十六║Client渲染、Server渲染知多少{补充}
前言 书接上文,昨天简单的说到了 SSR 服务端渲染的相关内容<二十五║初探SSR服务端渲染>,主要说明了相关概念,以及为什么使用等,昨天的一个小栗子因为时间问题,没有好好的给大家铺开来讲 ...
- SignalR代理对象异常:Uncaught TypeError: Cannot read property 'client' of undefined 推出的结论 SignalR 简单示例 通过三个DEMO学会SignalR的三种实现方式 SignalR推送框架两个项目永久连接通讯使用 SignalR 集线器简单实例2 用SignalR创建实时永久长连接异步网络应用程序
SignalR代理对象异常:Uncaught TypeError: Cannot read property 'client' of undefined 推出的结论 异常汇总:http://www ...
- JS中client/offset/scroll等的宽高解析
原文地址:→传送门 window相关宽高属性 1. window.outerHeight (窗口的外层的高度) / window.outerWidth (窗口的外层的宽度) window.outerH ...
- 推断client手机类型,并跳转到对应的app下载页面
实现的原理,是检測浏览器的 USER-AGENT 这个header,然后依据正則表達式来确定client类型. 假设都不匹配,Fallback回退策略是显示相应的页面.让用户自己选择. 适合採用二维码 ...
- 关于client浏览器界面文字内容溢出用省略号表示方法
在实际的项目中,因为client浏览器文字内容的长度不确定性和页面布局的固定性,难免会出现文字内容超过div(或其它标签,下同)区域的情况.此时比較好的做法就是当文字超过限定的div宽度后自己主动以省 ...
- JavaScript位置:window&client&offset&scroll&MouseEvent&getBoundingClientRect&计算任意元素滚动条宽度
Window: window.innerWidth:浏览器viewport视口宽,包括垂直滚动条 window.innerHeight:浏览器视口高,包括水平滚动条 window.outerWidth ...
- offset系列、client系列、scroll系列
offset系列.client系列 <style> .testDOM { width: 200px; height: 200px; background-color: #2de; pa ...
- JavaScript offset、client、scroll家族
offsetParent <!DOCTYPE html> <html> <head> <meta charset="utf-8"> ...
随机推荐
- python的json模块
Python JSON 本章节我们将为大家介绍如何使用 Python 语言来编码和解码 JSON 对象. 环境配置 在使用 Python 编码或解码 JSON 数据前,我们需要先安装 JSON 模块. ...
- 两个二进制数多少个位(bit)不同
class Solution { public: /** * 获得两个整形二进制表达位数不同的数量 * * @param m 整数m * @param n 整数n * @return 整型 */ in ...
- 在Spring的bean中注入HttpServletRequest解密
我们可以在Spring的bean中轻松的注入HttpServletRequest,使用@Autowired HttpServletRequest request;就可以了. 但是,为什么我们可以直接这 ...
- 将插入的新行放入dataGridView的第一行
将插入的新行放入dataGridView的第一行 习惯这样用的: dataGridView1.Rows.Add(dataRow);改成:dataGridView1.Rows.Insert(0,data ...
- C# 时间类型
字符型转换为字符串// C 货币 2.5.ToString("C"); // ¥2.50 // D 10进制数 25.ToString("D5"); // 25 ...
- linux DMZ host 允许虚拟机以Host-only的方式上网
linux DMZ host 允许虚拟机以Host-only的方式上网. host ip 192.168.0.17 vboxnet0 ip 192.168.56.1 1.首先打开linux的转发功能: ...
- C++学习基础三——迭代器基础
迭代器分为两种:一种是iterator,另一种是const_iterator.两者都可进行访问容器中的元素,不同之处是:(1)const_iterator类型只能用于读取容器内的元素,不能更改其值:而 ...
- Python isdigit()方法
描述 Python isdigit() 方法检测字符串是否只由数字组成. 语法 isdigit()方法语法: str.isdigit() 参数 无. 返回值 如果字符串只包含数字则返回 True 否则 ...
- mongo 查找附近点
db.runCommand({geoNear:"demo", near: { type: "Point" , coordinates: [118.134535, ...
- nginx 启动,停止和重新加载配置
要启动nginx的,运行可执行文件.一旦nginx的启动时,它可以通过与-s参数调用可执行来控制.使用以下语法 nginx -s signal 其中,信号可以是下列之一: stop - fast sh ...