版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
游戏编程概述
游戏是什么?游戏程序员看游戏游戏”只是一个具有某种“逻辑”和某些“数据”的结合体游戏的组成游戏的基本部件游戏的运行流程游戏其实就是一个不断按某种逻辑更新各种数据(画面、声音等)的过程。游戏的基本流程只是一个连续的循环,它不断地按某种逻辑来绘制新的图像,并刷新画面DaveRoderick曾形象地把游戏类比为一个带有前置终端的实时数据库,该终端实时地接受用户(玩家)输入的各种交互指令,取出相应的数据,并“优雅”地将这些数据以各种形式(视觉、听觉等)展现给用户。
游戏开发的基本理念和方法(1)数据驱动理念“逻辑”方面是一款游戏的灵魂,主要由游戏设计人员来负责完成;“数据”方面主要起到描述性和修饰性的作用,主要由程序设计人员来处理。这样的主次关系决定了只有高效地、灵活地处理和对付这些“数据”部分,才能让游戏设计人员把更多的时间和精力花费在“逻辑”部分,游戏编程人员必须把这些“数据处理”工作变得简便和快捷,才能保证游戏开发的成功。游戏开发的基本理念和方法(2)数据驱动的基本策略预先开发专门的数据获取和管理工具。游戏程序设计人员常常需要针对当前游戏的开发,为游戏开发中的艺术创作人员专门设计和实现一系列的“数据”获取和管理工具,包括声音的处理程序、绘画工具等,以提高艺术创作人员的工作效率;游戏程序设计人员在编写代码时,要采用可以很容易修改游戏数据的方式,保证游戏数据的动态可调整性。游戏开发是一个不断修改完善的过程,游戏的设计人员常常需要访问那些影响游戏不同实体行为的数据,并且可以修改这些数据。游戏开发的基本理念和方法(2)保证游戏数据的动态可调整性的技巧和策略创建一个可以解析文本文件或者命令的子系统它能对游戏的初始化数据/参数进行动态地解释和配置,而不是简单地“启动”游戏的运行。把游戏程序中的一些常数放置在文本文件中,这样,不需要重新编译就可以很容易地对这些参数进行修改。所有的数据都不放在代码内。假设所有的数据内容都可以修改,或者将来可能被修改。使用脚本来控制游戏的流程。脚本是定义各种行为控制的最简单的方式,它特别擅长定义游戏中顺序执行的步骤,或者各种等待触发的游戏事件。在编码实现中,时刻牢记把“逻辑”和“数据”分开的核心准则即把复杂的游戏逻辑放在代码中,而把“数据”外置,这样可以有效地提高游戏的功能清晰性和可调试性。避免重复的数据。如果你需要在不同的游戏场景中表现相同的人物行为,那么,只需要在一个地方实现,而在另一个场景中直接调用就可以了。在大型的复杂游戏开发中,需要另外的编辑工具代替手工编辑方式,方便于产生游戏数据的文本文件,如关卡编辑器、脚本编辑器等。建构这些合适的编辑工具可谓是“磨刀不误砍柴功”,它们将十分高效地加快大型游戏的开发过程。游戏开发的基本理念和方法(3)软件工程中的原型法、中间件方法等都是开发游戏软件的有效方法可玩性测试的原型系统:专门用于检验游戏设计的合理性和用户的可接受程度;用户界面的原型系统:检查玩家如何与游戏进行交互;各个子系统的原型系统:测试该子系统的功能,并可检查各个子系统的交互关系和数据接口;算法测试的原型系统:可用来检查各种算法。尤其是特定领域的一些复杂算法需要通过原型系统进行不断地改进和提高。游戏的测试与调试(1)游戏的测试与调试目标游戏BUG的出现可能是由于程序员的技术问题,也可能是由于策划的设计问题,或者是因为美工的一时疏忽。不管是什么原因,在进行游戏的调试和测试工作时,要尽量把这些问题全部解决。游戏的调试是编程人员的任务,在这个阶段中要找出游戏程序的内在缺陷,并加以修正。采用一般的软件调试和测试方法就可以达到。游戏的测试相当于对游戏设计阶段的BUG的修正,找出游戏设计本身存在的缺陷。游戏的测试与调试(2)游戏的测试人员一般由以下几类人员组成:第一类游戏测试人员是开发队伍的成员。看到自己制作的美工、声效、代码和游戏中使用的关卡,更好地知道如何进行改进;第二类游戏测试人员是传统意义上的游戏测试人员,这些人在游戏进入“alpha”阶段就开始对游戏进行测试。这些测试人员会将一半的时间用来跟踪代码中的“BUG”,并且还会提供一些重要的反馈信息,比如,如何玩游戏、游戏的难度过于简单还是过于困难、游戏的交互控制是直观还是迟钝等;第三类游戏测试人员比较注重第一印象,他们短期参与到项目中,从事玩游戏的工作,然后提供关于游戏的最直接的感觉。关于游戏中不太直观的控制方式、不清晰的信息表示或者游戏难度设置不合理的部分,他们都可以提出建议;第四类游戏测试人员是没有从事当前游戏开发的游戏设计者或者开发人员,会以比其他的测试人员更为独到的方式提出自己对游戏优缺点的见解,他们在某种程度上理解游戏的设计思想,可以分析出项目为何存在缺点,并提出改进方案。游戏的测试与调试(3)游戏中的测试工作可以分为两种:有监督的测试和无监督的测试。有监督的测试一般在游戏开发的早期进行,让设计人员清楚地了解游戏有哪些部分不完整,需要从哪些方面获得反馈信息。在项目开发的后期,如果某个部分刚刚修改过或者返工过,设计人员只需要这一部分的反馈信息,以判断所做的修改是否解决了现有的问题,或者是否会在某个重要方面对游戏造成影响。无监督的测试也很重要,告诉他们开始玩游戏,观察他们做些什么,并且倾听他们的反馈意见,这样可以为设计人员提供关于游戏的全面的反馈意见,这些信息对于解决所有的问题至关重要。游戏的测试与调试(4)游戏在整体上完成后,在进入全面测试阶段的时候,就可以进行游戏参数的调整。参数的调整工作通常包括修改游戏中的某个设置,然后继续玩游戏,以判断修改是否达到了所要求的效果。在进行参数调整工作时,必须密切注意不同的值之间是如何相互作用和影响的。在决定如何对游戏进行参数调整时,没有绝对的规则可以遵循。你必须要全面地观察游戏,以理解比你玩游戏的经验少得多的玩家如何来玩这个游戏,并且还要了解哪些地方可以对他们提出挑战,而又不会不公平或者过于残酷。一旦决定对一个行为参数或效果参数进行调整,那么大刀阔斧地将其加倍或减半,这样在调试时才能清晰地感受到调整前后的变化,有利于快速地确定合适的参数值。游戏开发的基本准则所有的游戏开发都必须为今后的重用作好准备。尽量使用可重用的模块将会最大程度上缩短游戏的开发周期。开发文档不是可有可无的。好的开发文档不仅能为软件重用提供有力的技术保证,而且在游戏开发过程中,它能让其他的开发人员清楚地知道你在干什么。先设计,后编程。游戏的设计和编程实现是明显分开的,因此,游戏的开发一定要先进行设计,后进行编程实现。游戏开发是一个不断进行自我完善的过程,在开始编程的时候,大概只完成了80%的游戏设计工作量,其余的设计工作随着开发进程的推进而逐步地进行精致和完善。灵活有效地安排开发进程。在游戏开发过程中,一定要让每一个游戏编程人员知道他们的开发目标。虽然执著地实施既定的开发计划很重要,但是,更重要的是如何在开发进程落后的时候,重新校准新的开发计划。及时地发现错误。在开发过程中,及时地发现和纠正错误十分重要。时间越长,这些潜在的错误就越容易“发酵腐烂”,最终导致开发的失败。游戏开发小组包括艺术家动画师/造型人员编程人员音效/音乐设计产品开发人员游戏开发所需要的资源通常,游戏开发需要3-5个程序设计人员3-5个艺术设计各种支持(产品开发,市场推广,音效师,音乐家,测试,系统管理员,法律顾问等)将近18个月的开发时间游戏开发基本过程游戏开发的基本过程设计人员:描述游戏内容及其卖点开发人员:如何利用现有资源完成既定目标项目进度安排,充分调动积极性掩盖复杂的细节实现与游戏程序员相关的任务游戏资源开发编辑工具/数据游戏引擎开发考虑注册购买现有的产品具体实现(gameplay)定型
测试/修改发布。。。。。。游戏开发的辅助工具声音数字转换器音乐编辑器、编曲机图形工具:Photoshop等摄像头视频采集卡图形库:包括建模和绘制游戏引擎。。。。。。游戏引擎技术(1)游戏引擎技术的出现是游戏程序设计技术发展的里程碑之一,并已成为当前计算机游戏开发的关键技术和核心平台。它也是软件工程、专业化分工和游戏产品的独特文化性要求在游戏开发的综合体现,对游戏产业的发展起了巨大的推动作用游戏引擎的意义游戏编程人员就不需要从头做起,而是可以直接调用游戏引擎提供的强大功能,高质量地在很短的周期内开发出新游戏,适应游戏产业的激烈市场竞争游戏引擎促使游戏编程人员进行更为专业化的分工高水平的编程人员注重于性能要求很高的游戏引擎的开发,一般水平的游戏编程人员则利用游戏引擎进行具体的游戏产品的开发游戏引擎技术(2)游戏引擎相当于游戏的底层框架平台。框架平台搭好后,只要往里填充内容就可以了。如果把游戏引擎比拟为一个“游戏操作系统”,那么最终的游戏产品则可比拟为一个个具体地运行在“游戏操作系统”上的应用程序。游戏引擎已经发展为一套由多个子系统共同构成的复杂系统从建模、动画到光影和粒子特效,从物理系统、碰撞检测到文件管理、网络流量控制等,包括专业的编辑工具和插件,几乎涵盖了游戏程序设计过程中的所有重要环节游戏引擎技术(3)游戏引擎的终极目标游戏编程的透明化,让游戏的创意人员也能直接实现游戏根据创意,直接生成代码著名的商用游戏引擎DoomQuakeUnrealLithTech……游戏引擎技术(4)Ogre引擎开源/projects/ogre面向对象抽象底层图形库(D3D,OpenGL)灵活的渲染引擎(不是一个完整的游戏引擎)大量采用C++设计模式以插件的形式方便扩展OGRE的功能提供了抽象的渲染API,封装了底层的图形库。游戏引擎技术(5)Ogre引擎的学习(一个月的时间)读Ogre的源代码,学习Ogre的设计和编码方式解析Ogre的结构和功能熟悉API调用尝试改变Ogre的demo游戏程序员的基本素质要求软件设计的基本技能丰富的知识面
软件工程、数据结构、数据库、算法设计等2D/3D图形学、人工智能、音频/视频处理
、人机交互、计算机网络坚实的数学和物理基础:线性代数、欧氏几何、牛顿物理学经验和能力综合运用各片断技术的经验不断学习新技术的能力游戏的开发常常受限于运行时间或者存储空间等资源因素,但却处处追求高效率和高性能。其他游戏引擎,如DirectX,OpenGL,Ogre及其他商用游戏引擎一些数据处理和建模工具,如Photoshop,Maya,3DS等。。。。。。给游戏程序设计员的忠告游戏编程人员还需要不断地学习游戏开发特别强调创新,每次开发的游戏都会不一样,而且游戏编程环境和技术也在不断的发展变化中。只有善于从不同的方面(同事、网上的游戏编程资源、……)去学习,不断地提高自己的游戏编程能力,才有可能使自己在游戏业界立足争取获得一个大学文凭。要成为一个游戏编程人员,大学文凭并不是必须的,但是,一个大学文凭能在找工作时给你很多有益的帮助。。。。。。。Q&A??????OGRE
浅析教学中文资料OGRE_01a.doc:从使用者的角度将OGRE引擎最基本的概念和使用方法做一个较全面的介绍。隐藏了OGRE引擎内部的底层内容,力求做到简单、易懂,是OGRE引擎的入门教程。ByMarg小组Loneststarreading.docOGRE浅析:by邹林灿OGRE中文系列分析:by盛崇山OGRELOGO本次课程的主要内容OGRE简介OGRE的安装如何用OGRE来进行应用程序的开发OGRE简介
OGRE:Object-OrientedGraphicsRenderingEngine(面向对象的图形渲染引擎)C++开发面向对象且使用灵活的3D引擎。目的:让开发者方便和直接开发基于3D硬件设备的应用程序或游戏。引擎中的类库对更底层的系统库(如:Direct3D和OpenGL)的细节进行了抽象,并提供了基于现实世界对象的接口和其它类。
OGRE简介这部分主要介绍:OGRE特点OGRE中的模块OGRE特点
效率特性简单、易用的面向对象接口设计能更容易渲染3D场景,并使实现产品独立于渲染API(如Direct3D/OpenGL/Glide等)。可扩展的程序框架(framework)自动处理常见的需求,如渲染状态管理,hierarchical
culling,半透物体排序等。清晰、整洁的设计加上全面的文档支持。OGRE特点平台和3D
API支持支持Direct3D和OpenGL支持Windows平台,用VisualC++6(或VisualC++.Net)和STLport来编译。支持Linux平台,用gcc3+(或gcc2.9x)和STLport来编译。材质/Shader支持支持从PNG、JPEG或TGA这几种文件中加载纹理;自动产生MipMap;自动调整纹理大小以满足硬件需求。支持可程序控制的纹理坐标生成(如环境帖图)和转换(平移、扭曲、旋转)。材质可以拥有足够多的纹理层,每层纹理支持各种渲染特效,支持动画纹理。自动应用多通道渲染和多纹理,从而大幅度提高渲染质量。支持透明物体和其它场景级别的渲染特效。通过脚本语言可以不用重新编译就设置和更改高级的材质属性。OGRE特点网格Meshes高效的网格数据格式提供插件支持从Milkshape3D导出OGRE本身的.mesh和.skeleton文件格式。支持骨骼动画(可渲染多个动画的组合)支持用Bezier样条实现的曲面OGRE特点场景特性拥有高效率和高度可配置性的资源管理器,并支持多种场景类型。使用系统默认的场景组织方法,或通过亲自编写插件使用自己的场景组织方法。通过包围体(如包围盒)实现视域裁剪。提供的二叉树场景管理器插件可加速室内场景的渲染,支持加载Quake3关卡和shader脚本分析。优秀的场景组织体系;场景结点支持物体的附属(attach),并带动附属物体一起运动,实现了类似于关节的运动继承体系。OGRE特点特效粒子系统包括可以通过编写插件来扩展的粒子发射器(emitter)和粒子特效影响器(affector)。通过脚本语言可以不用重新编译就设置和更改粒子属性。支持并自动管理粒子池,从而提升粒子系统的性能。支持天空盒、天空面和天空圆顶,使用非常简单。支持Billboard,以实现特效。自动管理透明物体(系统自动帮你设置渲染顺序和深度缓冲)
OGRE特点其它特性资源管理和文档加载(ZIP、PK3)。支持高效的插件体系结构,它允许你不重新编译就扩展引擎的功能。运用'Controllers'你可以方便地改变一个数值。例如动态改变一个带防护罩的飞船的颜色值。调试用的内存管理器负责检查内存溢出。
OGRE中的模块
OGRE中由很多模块组成,每个模块互相配合,共同实现OGRE的强大功能和优秀特性。OGRE的模块大致可表现为如下结构,这也基本上是OGRE工程文件的结构:OgreMainPlatformManagersPluginsRenderSystemsTools
OgreMain模块
场景组织体系Material管理插件动态加载系统数学支持库渲染器和几何管道网格/几何实体管理资源管理天空/背景渲染公告板系统和粒子系统日志和异常处理事件监听器编解码器和图像加载器自定义内存管理器基本动画骨骼动画字体渲染/字体加载覆盖(Overlay)表面,二维元素Win32平台管理模块
实现了Windows平台的基本平台服务。特性输入管理配置系统BSP场景管理
该插件用BSP树和clusters提供了室内场景的管理。它可以导入Quake3的关卡。特性BSP树关卡导入Shader支持其它模块文件系统插件提供在文件系统的文件夹中定位资源的能力。GuiElement插件提供标准的二维表面元素,如文本输入区和边框。OctreeSceneManager插件用八叉树管理标准场景。可用它渲染地形。其它模块ParticleFX插件此插件提供了标准的粒子发射器和粒子特效影响器。Direct3D7渲染系统插件此插件提供了基于Direct3D7的渲染系统。Direct3D9渲染系统插件此插件提供了基于Direct3D9的渲染系统。其它模块SDL渲染系统插件此插件提供了基于OpenGL和SDL的渲染系统。3ds2oof工具这个工具可以将3DStudio的网络文件(.3ds)转换成.oof格式(OGRE以前的网络文件格式)。这个工具已经被抛弃了。3DstudioMax导出器这是一个3DStudioMAX(版本4或5)的插件,可以将3DStudio的模型数据转换成OGRE的.mesh或.skeleton格式。其它模块位图字体创建工具能过此工具你可以把二进制字体文件转换成OGRE的.fontdef文件。Milkshape3D导出器这个工具是Milkshape3D(一个建模工具)的插件,它允许你将模式导出成OGRE支持的.mesh和.skeleton文件格式。Python接口设计这个子工程的目的是以dll的形式提供一个接口,使Python(一种脚本语言)可以直接驱动OGRE,并且允许OGRE直接调用Python脚本以实现游戏相关的脚本语言。安装OGRE
这部分主要介绍获取OGRE支撑环境编译OGRE运行DEMOOGRE运行期结构获取OGREOGRE是一个开放源码项目,该项目的网址是。在这里可以获取到OGRE的最新版本和文档,此外还可以在论坛上与其它开发者交流。支撑环境
OGRE是一个比较大的项目,不可能每个功能都独立完成。OGRE的编译和使用需要一些其它库作为支撑环境。在Windows环境下编译和安装OGRE需要如下支撑环境:STLport4.5.3DirectX9SDK其它第三方库编译OGRE
在VC6环境里打开OGREOGRE的最新版本的ogrenew文件夹下的Ogre.dsw工作区文件,执行BatchBuild指令,该指令会自动处理OGRE中各个工程的依赖关系,正确完成全部的编译构建。
在VC7环境里打开OGREOGRE的最新版本的ogrenews文件夹下的Ogre.sln工作区文件,执行BatchBuild指令,该指令会自动处理OGRE中各个工程的依赖关系,正确完成全部的编译构建。运行DEMO在ogrenew\Samples\Common\bin\Debug下可以看到Debug方式编译的全部DEMO。OGRE运行期结构运行完DEMO之后,注意查看ogrenew\Samples\Common\bin\Debug文件夹中的内容,从这里可以看到OGRE程序的运行环境。除了DEMO的可执行文件外,该文件夹中还包括如下的动态链接库:OGRE运行期结构OgreMain.dllOgrePlatform.dllRenderSystem_Direct3D7.dllRenderSystem_Direct3D9.dllRenderSystem_SDL.dllPlugin_GuiElements.dllPlugin_BspSceneManager.dllPlugin_OctreeSceneManager.dllPlugin_FileSystem.dllPlugin_ParticleFX.dllDevil.dllSDL.dll其它:机器上还必须包括DirectX8.1和STLport的动态链接库,一般系统会将它们自动安装到Windows系统文件夹下。OGRE运行期结构OGRE的运行还需要如下的配置文件:ogre.cfg:OGRE的显示模式配置文件Plugins.cfg:插件配置文件,在这里指定插件的路径和插件文件名。上一个表中以Plugin_开头的dll文件都是插件,它们可以放在其它文件夹里,但必须在本文件里指定路径。resources.cfg:资源配置文件,设置资源搜索路径,Zip文件也作为搜索路径对待。quake3settings.cfg:quake3地图配置文件。terrain.cfg:室外地形场景配置文件。OGRE运行期结构OGRE程序的资源路径在resources.cfg里指定。OGREDEMO的资源都放在\ogrenew\Samples\Media及其下的Zip文件里。资源文件包括以下内容:.skeleton骨骼动画的骨骼定义文件.particle粒子模板定义文件.overlay二维及三维界面定义文件.mesh模型文件.material材质定义文件.fontdef字体定义文件.jpg图片文件.png图片文件准备用OGRE开发从FrameWork开始FrameWork与实际应用程序的关系ExampleApplication类ExampleFrameListener类第一个3D程序FrameWork
假设以Demo_EnvMapping工程为例,观察一下OGRE的Demo程序的代码结构,可以发现在本工程中其实只有两个文件:EnvMapping.h和EnvMapping.cpp。查看本工程的Settings,在C/C++选项卡中打开PreProcessor分类,可以看到有三个附加包含路径,其中..\include是本工程的头文件路径,..\..\Common\include是Demo程序公共的头文件路径,..\..\..\OgreMain\include是OGRE引擎的头文件路径。在..\..\Common\include中可以发现两个头文件:ExampleApplication.h和ExampleFrameListener.h。这两个文件定义了简单OGRE程序的应用框架,它们封装了简单OGRE程序的基本要素和运行过程。FrameWork与实际应用程序的关系
在创建我们自己的OGRE程序时,只需要继承OGREFrameWork中的类并做少量改动就可以了。OGREFrameWork与实际应用的关系如下图所示:ExampleApplication类
该类定义了如下数据成员://指向Root对象的指针
Root*mRoot;//指向程序中摄像机的指针
Camera*mCamera;//指向场景管理器的指针
SceneManager*mSceneMgr;//指向“帧监听器”的指针
FrameListener*mFrameListener;//指向渲染窗口的指针
RenderWindow*mWindow;Root:OGRE系统的入口点
Root对象在程序中必须最先创建和最后释放。OGRE引擎是通过Root将其它部分“串”起来的,通过Root对象可以调出配置对话框以配置渲染系统(RenderSystem);通过Root对象可以获取到引擎其它部分的指针,如SceneManager、RenderSystem、Resourcemanagers等;Root对象还提供一个startRendering方法来开始一个连续渲染过程,对该方法的调用在ExampleApplication类中就可以见到。Camera:摄象机
渲染结果实际上就是摄象机最后“看”到的结果。SceneManager:场景管理器
普通场景、室外封闭场景、室外无限场景和室内场景:
enumSceneType{ST_GENERIC,ST_EXTERIOR_CLOSE,ST_EXTERIOR_FAR,ST_INTERIOR};室内场景采用BSP场景管理方式,室外场景采用八叉树场景管理方式。初学者使用ST_GENERIC普通场景类型。对于复杂室内和室外场景,OGRE分两部分:基本上固定不变的“世界”,对于这部分采用BSP或Octree等特殊算法提高渲染效率;场景中的可移动物体,它们的创建和控制需要开发人员自己来完成。FrameListener:帧监听器
监听最终用户的控制信息(鼠标、键盘、遥控杆等),对摄象机、场景物体等进行控制.RenderWindow:渲染窗口
渲染结果所在的窗口,其中包括渲染真正的目的地:视口Viewport。启动OGRE
//继承自ExampleApplication类
EnvMapApplication
app;
try{
app.go(); }
catch(Exception&e) { …… }
启动OGRE
//启动引擎
virtual
void
go(void) {
if(!setup())
return;
mRoot->startRendering(); }
go函数
virtualvoidgo(void):该函数是ExampleApplication类除构造和析构函数以外唯一的public函数,它一调用,程序就正式开始运行了。代码如下:
virtualvoidgo(void){if(!setup())return;mRoot->startRendering();}从代码可以看出,先调用setup()函数完成渲染前的准备,如果setup成功,就由mRoot调用startRendering()开始渲染,如果不成功则退出。setup函数
Setup函数完成一个OGRE应用程序的开始渲染前的准备工作。实际步骤如下:首先创建Root类的对象。加载资源路径setupResources(void)弹出config对话框,配置RenderSystem选择场景管理器类型创建并初始化摄像机创建窗口中的视口创建场景创建帧监听器setup函数代码步骤的代码如下:virtualboolsetup(void){mRoot=newRoot();//首先创建Root类的对象。
setupResources();//加载资源路径
boolcarryOn=configure();//弹出config对话框,配置RenderSystemif(!carryOn)returnfalse;chooseSceneManager();//选择场景管理器类型
createCamera();//创建并初始化摄像机
createViewports();//创建窗口中的视口
//Setdefaultmipmaplevel(NBsomeAPIsignorethis)TextureManager::getSingleton().setDefaultNumMipMaps(5);createScene();//创建场景
createFrameListener();//创建帧监听器
returntrue;}setup函数至此,setup()函数结束,它已完成渲染之前的全部准备工作,接下来就由Root对象调用startRendering()函数指挥渲染系统开始连续的渲染过程。OGRE消息处理机制消息机制的设计一般总要设计到三个部分:消息的产生、消息的传递和消息的处理。OGRE中的消息处理者的抽象类主要是listener类,而listener必须是对应特定的target的,所以可以认为是由listener和target两个抽象类组成。而消息传递由Dispatcher和Processor组成。FrameListenerFrameListener类中有两个很重要的虚函数。frameStarted和frameEnded。
class_OgreExportFrameListener { public:
//帧渲染之前的事件处理方法
virtualboolframeStarted(constFrameEvent&evt){returntrue;} //帧渲染之后的事件处理方法
virtualboolframeEnded(constFrameEvent&evt){returntrue;} virtual~FrameListener(){} }
ExampleFrameListenerExampleFrameListener继承自FrameListener。它包装实现了一个帧监听器常用的功能。OGRE对事件的处理方法有两种模式,立即模式和缓冲模式。ExampleFrameListener中有一个mEventProcessor变量,它是用来处理缓冲模式的事件的。而在默认的非缓冲模式中,ExampleFrameListener实现了基本输入操作。如:键盘WASD控制视点前后左右移动,鼠标控制视点的旋转等等。EventProcessorEventProcessor类继承了FrameListener类,OGRE引擎自动在每帧前调用EventProcessor的frameStarted函数。EventProcessor类还继承了KeyTarget,MouseTarget,MouseMotionTarget。作为消息传送目标的EventProcessor,因此才有可能处理这些消息。键盘消息处理机制EventListenerisMulticasterKeyXX(KeyEvent)=0InputEventEventTarget*mSourceKeyEventintmKeyEventEventTargetvirtualprocessKeyEvent()=0KeyTargetprocessKeyEvent()add/removeKeyListenerKeyListenerTargetListenerEventProcessor的设置
//创建一个EventProcessor mEventProcessor=new
EventProcessor();
//初始化
mEventProcessor->initialise(win);
//开始处理消息
mEventProcessor->startProcessingEvents();
//将ExampleFrameListener作为按键消息的监听者注册
mEventProcessor->addKeyListener(this);
EventProcessor的运行
bool
EventProcessor::frameStarted(const
FrameEvent&evt) {
mInputDevice->capture();
while(mEventQueue->getSize()>0) {
InputEvent*e=mEventQueue->pop();
processEvent(e);
delete
e; }
return
true; }
第一个3D程序
OGRE的应用框架中定义好了ExampleApplication与ExampleFrameListener类,已封装了3D应用程序的全部要素,应用开发者所要做的工作主要有2点:派生出自己的应用程序类,重新实现createScene函数以创建场景。派生出自己的监听器类,如果需要的话,重新实现frameStarted函数以进行特殊的输入控制和动画控制。第一个3D程序打开Demo_EnvMapping工程,这是一个非常简单易懂的程序实例。这个程序只有两个文件EnvMapping.h和EnvMapping.cpp。EnvMapping.h文件定义了ExampleApplication类的派生类EnvMapApplication,并重新实现了createScene函数创建出一个亮闪闪的具有环境帖图的食人魔头像。classEnvMapApplication:publicExampleApplication{public:EnvMapApplication(){}protected://重新实现createScene函数,创建实际场景
voidcreateScene(void){//设置环境光
mSceneMgr->setAmbientLight(ColourValue(0.5,0.5,0.5));//创建点光源lLight*l=mSceneMgr->createLight("MainLight");//设置点光源l的位置,缺省颜色为白色
l->setPosition(20,80,50); //读入ogrehead.mesh模型文件,创建为一个Entity。
Entity*ent=mSceneMgr->createEntity("head","ogrehead.mesh");//设置食人魔Entity的材质为指定材质(环境贴图)
ent->setMaterialName("Examples/EnvMappedRustySteel");//将食人魔Entity连接到场景根节点上。
mSceneMgr->getRootSceneNode()->createChild()->attachObject(ent);}};第一个3D程序一个基本的场景包括光、摄象机和模型等。ExampleApplication类中创建了缺省摄象机(见FrameWork部分)。每个模型文件载入后被创建成Entity。为了便于对场景的管理,OGRE引入了场景节点的概念,所有场景节点组合成一棵节点树,全部Entity挂在这棵节点树中的不同节点上。场景管理器mSceneMgr通过对节点树的操控来完成对场景物体的操控。场景建立好之后,接下来处理应用程序的入口问题,这部分代码在EnvMapping.cpp里.EnvMapping程序课后工作安装OGRE熟悉OGRE的变换和光照部分熟悉OGRE的文件格式和场景管理模式预习OGRE中的纹理映射
二维图像游戏基础
1. 初始化 2. 进入游戏循环 3. 查询用户输入状态 4. 执行游戏逻辑和AI判断 5. 绘制图像 6. 循环 7. 退出游戏循环的基本步骤
1. 初始化 2. 进入游戏循环 3. 查询用户输入状态 4. 执行游戏逻辑和AI判断 5. 绘制图像 6. 循环 7. 退出游戏循环的基本步骤Demo:Airplane演示GameRunGameInitGameMenuGameRestartGameExitGameStarting//definesforgameloopstates#defineGAME_INIT 1 //thegameisinitializing#defineGAME_MENU 2 //thegameisinthemenu#defineGAME_STARTING 3 //thegameisabouttorun#defineGAME_RUN 4 //thegameisnowrunning#defineGAME_RESTART 5 //thegameisgoingtorestart#defineGAME_EXIT 6 //thegameisexiting//gameglobalsintgame_state =GAME_INIT; //startoffinthisstateInterror =0; //usedtosenderrorsbacktoOS//mainbeginshereVoidmain(){ //implementationofmaingameloopWhile(game_state!=GAME_EXIT){ //implementationofmaingameloop switch(game_state) { caseGAME_INIT: //thegameisinitializing { //allocateallmemoryandresources Init(); game_state=GAME_MENU; }break; caseGAME_MENU: //thegameisinthemenu { //callthemainmenufunctionandletitswitchstates game_state=Menu();
//note:wecouldforceaRUNstatehere }break;
caseGAME_STARTING: //thegameisabouttorun { //thisstateisoptional,butusuallyusedtosetthingsupright
//beforethegameisrunyoumightdoalittlemorehousekeeping Setup_For_Run();
//switchtorunstate
game_state=GAME_RUN; }break;
caseGAME_RUN:
//thegameisnowrunning
{
//thissectioncontainstheentiregamelogicloop Clear(); //clearthedisplay
Get_Input(); //gettheinput
Do_Logic(); //performlogicandAI
Render_Frame();//displaythenextframeofanimation
Wait(); //synchronizethedisplay
//theonlywaythatstatecanbechangedisthruuserinteraction //intheinputsectionorbymaybelosingthegame.
}break;
caseGAME_RESTART:
//thegameisrestarting
{
//thissectionisacleanupstateusedtofixupanylooseends //beforerunningagain
Fixup(); //switchstatesbacktothemenu
game_state=GAME_MENU;
}break;
caseGAME_EXIT: //thegameisexiting
{ //ifthegameisinthisstatethenit’stimetobail,killeverything //andcrossyourfingers
Release_And_Cleanup();
error=0; //settheerrorwordtowhatever //note:wedonothavetoswitchstatessincewearealreadyinthisstate //onthenextloopiterationthecodewillfalloutofthemainwhileand //exitbacktotheOS
}break; default: break;
}
//endswitch return(error); //returnerrorcodetooperatingsystem}//endmain二维游戏技术二维游戏概览地图的创建与显示颜色混合与半透明精灵动画碰撞检测游戏循环概念及实例解析二维游戏早期的游戏都是二维的如Diablo(暗黑破坏神)只有两个轴(上下,左右)很多RPG游戏是固定视角的二维半二维游戏(续)二维游戏对现在的编程仍然有意义(特别是现在的手机游戏):如内存、分辨率本质上视频游戏是一个连续的循环,执行逻辑指令,并将图像输出到屏幕。这和电影的播放非常类似,但是这个电影是用户指定的。地图的创建与显示为实现一个基本的二维游戏框架,首先要实现游戏地图的各种加载和编辑操作,为角色提供游戏环境。4种通用地图实现的方法:固定地图、滚屏地图、多层次地图、菱形地图固定地图使用固定的背景作为地图将屏幕切割成棋盘状的一系列小块在内存中保持一个二维数组,保存每个小块对应的编号绘制时根据数组提供的信息,在每个小块画上相应图块
固定地图拼接算法实现 for(yi=0;yi<y轴小地图个数;yi++){ for(xi=0;xi<x轴小地图个数;xi++){ intscreenx=xi*tile_wide; //乘以宽度得到最后屏幕上位置
intscreeny=yi*tile_high; inttileid=mapping_matrix[yi][xi]; //在这个数组中存放着对应位置的小地图编 //号,如1表示水,2表示石头,3表示砖等
blit(tillid,screenx,screeny); //自编函数,把相应地图贴到正确位置
} }该程序段中blit函数的实现,可以调用Windows系统的一个API函数BitBlt直接实现滚屏地图是固定地图的进一步扩展,可以显示远大于固定地图的图像根据玩家所在位置,确定显示的地图部分滚屏地图滚屏地图算法实现变量设置:playerx,playery为人物相对于完整地图左上角的坐标;screen_wide,screen_high为屏幕的宽和高;xtile为屏幕上x轴上可显示的小地图个数;ytile为屏幕上y轴上可显示的小地图个数;tileplayerx=playerx/tile_wide为人物所在格x轴下标;tileplayery=playery/tile_high为人物所在格y轴下标;应该绘制的地图范围是:x轴:由tileplayerx-xtile/2至tileplayerx+xtile/2;y轴:由tileplayery-ytile/2至tileplayery+ytile/2;当人物在屏幕正中央时,地图到屏幕的位置变化公式为:screenx=xi*tile_wide–playerx+0.5*screen_widescreeny=yi*tile_high–playery+0.5*screen_high滚屏地图算法实现(续)例程:
intbeginx=tileplayerx-xtile/2 intendx=tileplayerx+xtile/2 intbeginy=tileplayery-ytile/2 intendy=tileplayery+ytile/2 tileplayerx=playerx/tile_widetileplayery=playery/tile_high for(yi=beginy;yi<endy;yi++){ for(xi=beginx;xi<endx;xi++){ intscreenx=xi*tile_wide–playerx+0.5*screen_wide; //屏幕上位置
intscreeny=yi*tile_high–playery+0.5*screen_high;inttileid=mapping_matrix[yi][xi]; //地图数据数组
blit(tillid,screenx,screeny);//自编函数,将相应地图贴到正确位置
} }多层次地图以下列情况,可以考虑使用多层次地图。需要小地图能重叠或者有层次关系;在背景上有多个物体运动;需要模拟物体远近不同的透视关系;多层次地图的实现思想并不复杂,在滚屏地图的基础上设置多个层次的地图即可。不妨设从底往上分别为0层,1层,…把地图数据数组改为三维数组。可以使每个图层以不同的速度运动,模拟景物远近不同的层次感。这种技术,又称视差卷轴(ParallaxScrollers)。
菱形地图菱形地图是在二维画面上表现三维场景的常用技术拼接所使用的小地图是菱形,计算比较复杂菱形地图算法实现intMapDraw(HDChdc){ inti,j; intlim=MAXSCREENX/TILEWIDE;//所需绘制地图的范围
for(i=-lim;i<lim;i++){ for(j=-lim;j<lim;j++){ intsx=MAXSCREENX/2-(TILEWIDE/2)+(i*TILEWIDE/2)-(j*TILEWIDE/2); intsy=MAXSCREENY/2-(TILEHIGH/2)+(i*TILEHIGH/2)+(j*TILEHIGH/2); if((sx<MAXSCREENX)&&(sy<MAXSCREENY)&&(sx+TILEWIDE>0) &&(sy+TILEHIGH>0)&&(playerx+i<100&&playerx+i>=0) &&(playery+j<100&&playery+j>=0)){ //边界判断
TransparentBlt(hdcMem,sx,sy,TILEWIDE,TILEHIGH, hdcTiles[Data[playerx+i][playery+j]], 0,0,TILEWIDE,TILEHIGH,RGB(0,255,0));//贴图
} } } BitBlt(hdc,0,0,MAXSCREENX,MAXSCREENY,hdcMem,0,0,SRCCOPY);//画到窗口return0;}Demo:AOE演示空间的离散的数字化的描述二维区域什么是图像(r0,g0,b0)(r1,g1,b1)(r2,g2,b2)(r3,g3,b3)(r4,g4,b4)(r5,g5,b5)(r6,g6,b6)(r7,g7,b7)图像表示文件格式BMP,TGA,TIFF,GIF,JPEG等定义(Bitmap)位图图像是一块由彩色点集组成的矩形区域。DIB(DeviceIndependentbitmap) 设备无关位图DDB(Device-DependentBitmaps)设备有关位图:老的Windows系统。BMP结构BMP文件头比较简单的头信息位图信息关于数据尺寸的详细信息调色板(optional):相当于一个查找表RGB四元组位图数据RGB象素值索引值(如果有调色板)BMP文件头typedefstruct{ WORDbfType; //“BM”,表示该文件为位图
DWORDbfSize; //文件大小(以byte计) WORDbfReserved1; //保留位
WORDbfReserved2; DWORDbfOffbits; //实际位图起始位置相对于文件头的偏移量}BITMAPFILEHEADER位图信息typedefstruct{ DWORDbiSize; //=sizeof(BITMAPINFOHEADER) DWORDbiWidth; //位图宽度(以象素计) DWORDniHeight; //正数,左下角为起始点,从下向上
//负数,左上角为起点,从上向下
WORDbiplanes; //colorplane数,恒等于1 WORDbiBitCount; //1,4,8,24,32 DWORDbiCompression;//通常,以下域可以忽略,置为0即可
DWORDbiSizeImage; DWORDbiXPelsPerMeter; DWORDbiYPelsPerMeter; DWORDbiClrUsed; //位图使用的颜色
DWORDbiClrImportant;//重要颜色数}BITMAPINFOHEADERRGB四元组typedefstruct{ BYTErgbBlue; BYTErgbGreen; BYTErgbRed; BYTErgbReserved; }RGBQUAD
BMP图像显示从BMP文件中载入图像数据分配内存缓冲区基于文件结构提取图像数据在屏幕上显示图像:将图像数据发送至显卡Bitblt():块拷贝图像数据OpenGL/DirectXStretchBlt:将一个保存在DIB中的图像数据拷贝到另外一个矩形区域,带缩放。图像的半透明操作每种颜色都由红绿蓝3种基本色彩(三原色)组合而成;三原色中每一种颜色的亮度用一个8位的二进制数来表示半透明图色彩=源图像色彩×(100%-透明度)+背景图像色彩×透明度WindowsAPI函数:AlphaBlend+精灵动画(1)基于精灵的人物表现鬼怪ghosts,精灵sprites,骑士knights精灵:前景是图像,背景是透明的精灵动画:将上一帧中精灵出现的地方用背景填充,并在新的指定地点绘制精灵透明区域图像镂空(2)将掩码图和背景图案进行按位AND,使得原始图像的对应位置变空。将原始图像和上一步处理结果按位OR。这样,原始图像贴到背景上并遮盖背景,其余部分(掩码图中白色部分)没有贴到背景上。
掩码图原始图像精灵动画(3)英文为spriteanimation一幅背景图一组模板图(mask)人物的连续显示方式双缓冲机制不要在窗口中直接贴图,避免闪烁建立一个内存DC,然后把所有的贴图动作都在这个DC上进行,最后把结果显示到操作窗口中。演示VideoMatting精灵动画(4)对动画序列中的每一帧Load背景图确定sprite绘画的位置将某一掩码图与背景图作AND运算将对应的人物图与背景图作OR运算更新sprite绘画的位置演示D3DSprite程序碰撞检测对运动物体的碰撞判断是许多游戏程序中不可或缺的要素常见的碰撞检测方法区域检测碰撞点检测颜色检测:较为精确, 相对耗时区域检测碰撞点检测区域检测采用某种规则形状逼近物体物体之间的碰撞检测转化为规则形状之间的检测碰撞点检测本质是区域检测的一种一般在两个运动物体中的一个物体上设置碰撞点,在另一个物体上设置检测区域,运行时逐个判断碰撞点是否在检测区域中。颜色检测为树林做一张掩码图,将树林用黑色填充。要产生汽车驶入树林后面的效果,先在背景上贴上汽车的图像,然后在上面用镂空图技术画上树林。然后,判断汽车图像在树林图像上的相对位置,将汽车图像上的点和掩码图上相应位置的点做按位AND操作,检查结果中是否有黑色点(RGB值为0)存在。任何颜色的RGB值与黑色图形进行按位AND运算,将得到黑色。如果存在黑色点,表明有碰撞。图像操作全局操作对图像上的所有象素作同样的操作如:傅立叶变换、直方图统计、块拷贝、灰度转换、缩放等局部操作操作只与象素及其周围邻居的值有关如:滤波、边缘检测等直方图表示图像中象素颜色值的分布直方图上每一点横坐标:颜色(亮度)值纵坐标:图像中具有该颜色(亮度)值的象素的数目亮度增强亮度图像象素颜色平均值用直方图进行亮度增强将每一个象素灰度值加上一个常数得到的直方图是原始直方图向正轴方向的平移提高对比度每一象素点(x,y)上的图像对比度C定义为I(x,y)-该点象素值I-背景亮度平均值Imax-图像灰度最大值0–
图像灰度最小值通过直方图提高对比度将原始图像直方图的取值范围[Xmin,Xmax]通过线性变换扩大至[0,Ymax]算术运算加:减:除:与:或:异或:几何运算平移将图像沿坐标轴移动若干偏移量缩放整数倍放大整数倍缩小一般情况:缩放系数非整数的情况旋转旋转矩阵R及其逆矩阵R-1图像滤波算子垂直方向算子水平方向算子例如:边界增强算子本质上实现的时候都是将某个象素的新的值用邻域象素值的加权平均计算而得。形态算子一组空间滤波操作用于改变二值区域的形状腐蚀:减少物体边界的象素数膨胀:增加物体边界的象素数复合方法开:腐蚀,然后膨胀闭:膨胀,然后腐蚀OriginalImage膨胀与腐蚀(Dilation,Erosion)数学形态学里面最重要的操作腐蚀将图像的尺寸减少膨胀增加图像的尺寸可以用来消除图像上小的亮斑噪声和不规则的边腐蚀(续)定义:物体的颜色是白,背景是黑定义腐蚀模板为111111111将模板与图像进行加操作如果有,则结果为1,否则为0腐蚀(续)模板的效果相当于去掉物体边界处的单个象素4种情况:当前处理象素为1,邻域象素为1-》1当前处理象素为0,邻域象素为1-》0当前处理象素为0,邻域象素为1、0的混合-》0当前处理象素为1,邻域象素为1、0的混合-》1腐蚀(续)原始图像腐蚀后的图像腐蚀两次
膨胀膨胀是腐蚀的逆操作模板文件是000000000其效果相当于在物体的边界添加单个象素膨胀(续)4种情况当前处理象素为0,邻域象素为0-》0当前处理象素为1,邻域象素为1-》1当前处理象素为1,邻域象素为1、0的混合-》1当前处理象素为0,邻域象素为1、0的混合-》1逻辑操作算子是Or膨胀(续)原始图像膨胀多次后的图像膨胀图像开操作 开操作相当于先做腐蚀操作,再做膨胀操作效果相当于去掉单个象素,但是保留原来的形状何尺寸。原始图像腐蚀两次,然后膨胀两次(开操作)闭操作闭操作是开操作的相互操作先膨胀,然后腐蚀它可以用来填补一些小洞原始图像闭操作结果轮廓抽取 先做腐蚀操作,再将腐蚀结果图像减去原始图像图像特效工具例如:AdobePremiere或者Avid提供了丰富的特效非常方便和简单!电影广告中的特效经常以这种方式完成Hollywood由于资金不缺,经常是手工完成但是现在的趋势是编程实现淡入淡出最简单的图像特效图像从一个黑色背景中出现令透明度ALPHA从0变到1ALPHA=0图像为黑色ALPHA=1原始图像Alpha改变的速度决定了图像的溶解速度如果让ALPHA从1变到0,就得到淡出的效果Morphing(变形)图像处理中最有意思的效果某个物体伸展到另外一个物体通常利用网格辅助也涉及很多计算机视觉的知识图像溶解图像变形Morphing(变形)电影《黑与白》中应用的技术蜘蛛网格演示模糊(blur)本质上相当于一个信号处理中的反走样滤波将每个象素用其周围邻域象素值的加权平均值替代效果一般图像量化改变图像中颜色的数目或者灰度的层次效果非常有趣风格化图像保留尖锐的边缘其他部分用纹理来代替下图是Intel公司网页上的一个例子风格化图像(续)用多种合成滤波方法产生油画风格风格化图像(续)利用类比的方法产生更多风格各异的图像风格化图像(续)纹理合成+类比的方法二维潜艇游戏分析具备了最基本的游戏要素:目标性、计分系统、竞技性等。主要利用的技术:WindowsGDI、二维图像二维潜艇游戏分析(续)CMyObjectCSubmarineCMyShipCTorpedoCScoreCBombCExplosionCChildView二维潜艇游戏分析(续)CMyObjectCMyObject
所有物体的基类CPointGetPos();virtualCRectGetRect();虚函数,获得物体的矩形坐标virtualboolDraw(CDC*pDC,boolbPause);在pDC上绘制当前物体图像boolIsSubmarine();判断当前物体是否是潜艇
二维潜艇游戏分析(续)CMyShipCmyShip
我方战舰,由CmyObject继承
staticvoidDeleteImage();释放内存
staticBOOLLoadImage();图像初始化
boolDraw(CDC*pDC,boolbPause);绘制
voidSetMotion(intmotion)水平移动,motion是移动距离
intGetMotion() 获取战舰移动方面
CPointGetPos() 获取战舰绘制坐标
CRectGetRect()获得物体的矩形坐标
二维潜艇游戏分析(续)CSubmarineCsubmarine
敌方潜水艇
staticvoidDeleteImage();释放内存
staticBOOLLoadImage();图像初始化
CRectGetRect()获得物体的矩形坐标
intGetType()获得潜艇类型,(绿色、黄色)
boolDraw(CDC*pDC,boolbPause);绘制
voidSetFireFlag()设置已经开火标志,以防潜艇多次开火
boolGetFireFlag()判断潜艇是否开火
二维潜艇游戏分析(续)Cexplosion我方炸弹
CRectGetRect()获得物体的矩形坐标
intGetMulti() 获取m_nMulti(标记连锁爆炸的潜艇个数)
voidSetMulti(intMulti){m_nMulti=Multi;}设置m_nMulti boolDraw(CDC*pDC,boolbPause);绘制
二维潜艇游戏分析(续)CScoreCscore分数显示
boolDraw(CDC*pDC,boolbPause);绘制
CRectGetRect() (返回NULL,无效函数)
staticintGetTotalScore() {returnm_nTotalScore;}获得当前总得分
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年度春季江铜集团江铜国际贸易有限公司校园招聘2人备考题库附完整答案详解(各地真题)
- 意外自然灾害应对保障承诺函范文8篇
- 公益慈善制度
- 经营风险防控承诺书范文8篇
- 2026山东农业大学养分资源高效利用理论与技术创新团队博士后招聘备考题库含完整答案详解【历年真题】
- 2026内蒙古霍林河机场管理有限责任公司招聘工作人员3人备考题库(名校卷)附答案详解
- 2026贵州黔西南州政协办公室公益性岗位招聘4人备考题库附完整答案详解【夺冠系列】
- 公司采购办公用品制度
- 2026上海市消防救援局招聘500名政府专职消防员备考题库(历年真题)附答案详解
- 2026四川德阳市就业创业促进中心市本级公益性岗位招聘1人备考题库(夺冠系列)附答案详解
- 铣刨加罩道路工程施工组织设计方案
- 小学德育分年段
- GB/T 13202-2015摩托车轮辋系列
- windows系统安全机制1课件
- 细菌的革兰氏染色课件
- 人体解剖与组织胚胎学统考题库(含答案)
- 2022年广东粤财投资控股有限公司招聘笔试试题及答案解析
- 催收话述课件
- 家庭医生签约培训课件
- 公司冲压作业指导书SOP
- 大学《病理生理学》期末考试大题及答案汇总
评论
0/150
提交评论