




免费预览已结束,剩余10页可下载查看
下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
游戏中汉字显示的实现与技巧作者:炎龙工作室 千里马肝版本:v1.0最后更新日期:2002-3-30绪言在游戏中,因为我们是中国人麻,通常都需要显示汉字,比方说交待剧情。而对于文字的显示,英文的显示要较其简单得多,因为只有26个字母,就算再加一些标点、符号什么的,用一张位图,就可以足以显示所有的单词了,而相关实现技巧,也比较轻松。而中文的显示方法,要复杂得许多。记得原来在DOS下,汉字的显示都是读的UCDOS的点阵字库,而点阵字库的读取方法,在UCDOS SDK中都有源代码可以参考。但是自从Windows操作系统开始,我们开始了解到一种更好的字库,它就是TTF。注:以下我所指的开发环境,除非明确说明,默认的平台是VC6.0+DirectX8.1,使用D3D来加速2D。然后使用的STL是用的SGI实现的那一套STL。点阵字库包括现在,有很多游戏都还是使用的点阵字库。因为操作起来比较方便,加上这方面的经验已经积累了好几年了。通常如果只是一种字体就可以满足需要的话,它会是一个比较好、快的解决办法。但是它有3个缺点:1. 如果放大显示,不做处理的话,显示出来的汉字,是很难看的。2. 像是UCDOS所提供的点阵字库,只有24点阵的有几种字体,如:宋体、黑体、揩体,而16点阵的好象就只有宋体一种。3. 点阵字库,通常是有版权的,尤其是第三方制作的汉字库(如:方正)。在这样的情况下,当我们写好这样的一个显示函数,就算是解决了如:放大、快速显示等问题的话,可供选择的字体还是太过于局限了。所以,在字体的要求比较强的情况下,点阵字库并不是一个好的解决方法,他不够灵活。尽管我们对于它的操作是如此得熟练,可以写出优美的代码来展示我们的编程技巧。TTFTTF是True Type Font的简称。在WindowsFonts目录下面,我们可以看到许多后缀为ttf的文件,它就是接下来我们接下来所要谈到的。TTF是一种矢量字库。我们经常可以听到矢量这个词,像是FLASH中的矢量图形,在100*100分辨率下制作的flash,就算它放大为全屏,显示出的画面也不会出现马赛克。所谓矢量,其实说白了就是用点和线来描述图形,这样,在图形需要放大的时候,只要把所有这个图形的点和线放大相应的倍数就可以了。而且,在网站上有很多的TTF字库可以下载,或者你可以去买一些专门的字库光盘。然后在你发行你精心制作的游戏时,可以顺便捎上这些后缀为.ttf的文件就行了。包括Quake这样的惊世之作,也都是用的TTF字库。这样,我们就可以解决点阵汉字的一些问题。通过TTF,我们在字体的质量和字库的数量上获得了暂时性的胜利。字库的读取和显示先前谈到点阵字库,只需要很简单的一些操作,就可以显示出想要的汉字。下面我给出一个读取hzk16的函数,它需要一个Surface以供显示用:#include #include #include / 读取16x16void DispHZ16(int x, int y, BYTE *Str, LPDIRECTDRAWSURFACE surf)const int Mask = 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 ;FILE *HzkFp;WORD i, j, k=0, m;WORD HzNum;WORD QuHao;WORD WeiHao;long offset;BYTE dotBuffer32;HzkFp = fopen(HZK16, rb);HzNum = strlen(const char *)Str)/2;DDSURFACEDESC ddsd;LPWORD lpSurface;HRESULT ddrval; ddsd.dwSize = sizeof(ddsd);while(ddrval=surf-Lock(NULL, &ddsd, 0, NULL)=DDERR_WASSTILLDRAWING);if(ddrval = DD_OK)lpSurface = (LPWORD)ddsd.lpSurface;for(i = 0; iHzNum; i+)QuHao = Stri*2-160;WeiHao = Stri*2+1-160;offset = (QuHao - 1) * 94 + (WeiHao-1)*32;fseek(HzkFp, offset, SEEK_SET);fread(dotBuffer, 32, 1, HzkFp);for(j=0;j16;j+) for(k=0;k2;k+) for(m=0;mUnlock(NULL);fclose(HzkFp);其实原理很简单:1. 打开字库2. 计算字符串长度(这个函数只支持中文),并且Lock Surface3. 依次计算出每个汉字所对应的区码和位码(汉字的第1个字节是区码,第2个字节就是位码),然后通过公式计算出这个汉字在字库中的偏移量:offset = (QuHao - 1) * 94 + (WeiHao-1)*32;4. 读出一个32个字节的点阵5. 绘制到Surface上以上只是1616点阵字库的显示方法,2424的读取方法与之类似,大家可以参照相关资料来书写出自己的代码。如何显示TTF字库呢,有很多种手段,下面我按从简单到复杂的的顺序依次介绍:1. 使用Windows API,也就是大家所熟悉的TextOut。通过它,还需要一个HDC(设备句柄),我们就可以随意地在屏幕任何地方显示出文字了。2. 在,有一个FreeType的免费库,而且是OpenSource的。它目前有2个版本:1.0和2.0。其区别在于,1.0只能读取TTF格式的,而2.0支持更多的文件格式,在使用它之前请详细阅读所要遵循的Licence,以下是摘自FreeType2.0对字库的支持列表:o TrueType fonts (and collections) o Type 1 fonts o CID-keyed Type 1 fonts o CFF fonts o OpenType fonts (both TrueType and CFF variants) o SFNT-based bitmap fonts o X11 PCF fonts o Windows FNT fonts 3. 自己研究TTF的格式,然后自己来操作。晕. /倒! 虽然我们想要把每一件事情都做好,但是也不是每一件事情都要亲历亲为。如果你非要这样,也行_,但是过不了多久,你就会陷入泥沼,到时候你会发现自己的热情正在慢慢被磨灭,什么叫做抓狂,相信你很快就会知道_。在有多种选择可以取舍的情况下,我们需要考虑一下,对比一下各种解决方法的优劣。在DirectDraw时代,我们都不自觉地喜欢上了GetDC,因为多方便啊。可是现在已经到了DirectX8.1时代了(我要使劲地摇那些还沉醉于DirectX7中,为如何在使用alpha时提升那可怜的1、2个fps的朋友们:醒醒,该起床了!),HDC已经被M$列为禁用品。怎么办呢?是的,你可能已经想到了,我们还一直保存着窗口的hWnd呢,可以通过它来得到hdc,从而调用那些需要hdc的API,可是,这样做是更为愚蠢的,这样对你是没有一点好处的,不信,你就试试吧。有一句话,请牢记:要想你的游戏有更快的速度的话,请不要再去碰HDC了。我们非常清楚hdc是一个超慢的解决办法,它无法在我们的高速游戏中满60分及格。下面来看看FreeType,它更像是一个Service。它的解决方法是,先通过一系列的初始化和设置,告诉FreeType字体的名字和大小等,然后它会动态地申请一个Graphic,再把我们要显示的字画到这个Graphic上,你还可以把它保存为tga格式。不过我们最终所想要的不是这个,所以可能我们还需要从这个Graphic上逐点读取或者用CopyRect,然后再画到我们的画面上。其实它已经是很方便的了,可是需要你去学习如何配置和使用它,这是很花时间的一件事情,而且它最大的优点是可以跨平台,我们需要它吗?如果有一个更为简单的办法,像是如果Textout不是那么慢的话,就好了在这里,顺便谈一下另2个字体显示类:ID3DXFont和CD3DFONT。可能早就有人会说怎么在上面的列表中没有它们?原因我会在下面慢慢地说明:ID3DXFont,它存在于D3DX库中,一个现成的字体类,不过对于它的处理方法我实在不敢恭维,就引用一位大师所说的话来表达我的看法吧: 在内部实现中, ID3DXFont:DrawText()函数确实做了我上面讨论的工作,先建立一张GDI兼容的位图,把文本绘制到位图上,而后把位图拷贝到纹理贴图上去,最后把纹理渲染到屏幕上。这样你就聚齐了所有的龟速的原始GDI函数,还包括了一大堆的额外开销 最终,这个函数比原来GDI的DrawTextEx()函数要慢上超过六倍CD3DFONT,是由M$在D3D的框架代码中提供。不过它只能显示英文,有很多朋友通过自己定制和修改这个类,来实现自己的中文显示。不过效果都不是很好。其实原理,跟ID3DXFont的方法差不多,不过处理方法要聪明了一点。分析与思考那么我们应该怎么办呢?通常我们会幻想,如果可以像处理英文那样,把所有的汉字都保存在一张位图里,该有多好。这样,显示的速度就不是问题了,直接可以CopyRect上去。可是,这样可能吗?首先,必须每一种字体都要生成这样的一个巨型位图。而且据说在GB2312中,一共有6000多个汉字,就算是用16*16,oh my god,这个位图该有多大啊(据说会有2.5M_)!而且在DirectX8.1中,对于Texture(显示的最小单位,就好象是原来DirectSurface的概念一样。说过多少遍了,不要再用DirectX8以前的东西了。不要试着去回忆那些美好的过去,我很明白,要你一下子放弃原来多年所获得的成就,是一件很痛苦的事情,但是包袱太重,是会影响进步的。就像是我们的国家扯远了),不同的显卡,支持的最大容量也是不同的。比方说早期的Voodoo,只支持256*256大小的Texture。而在我的显卡(Geforce2 MX 200)上测试,支持最大2048*2048大小的Texture。对于这样的硬件不确定性,我们只能取其最小值,也就是256*256。汉字虽然很多,但是常用的汉字,其实也就只有那么几百个。像这样的字:鬯、鞴,你一辈子会看到多少次呢?如果可以做一个类似于Cache的东西,保存着常用的那些个汉字,在需要显示的的时候,先在Cache中查找,如果有的话,就马上画上去;如果没有,就从字库中提取到Cache中。这样的话,在使用Texture来保存汉字的位图信息的同时,对于每个汉字,我们还要定义一个结构,然后用一个东西把它串起来,综合它们2个,也就实现了我们所要的Cache了。刚开始,我所定义的结构是这样的:struct Char char hz3; / 保存汉字 int frequency;/ 使用频率 RECT rect; / 这个字对应位图的区域 Bool isUsing; / 是否使用对于汉字和英文,我在这里大概地讲一下原理:汉字是由2个字节保存,而英文只需要1个。而判断一个字是否是汉字,只需判断第1个byte是否128(在原来的GB2312中,汉字的2个字节都是128的。而新的GBK字库,汉字的第2个字节不一定128,我想这是扩大了字库容量的原因。我的意思是说,如果给一个字符串你,随机给其中一个位置,然后我问你这个位置是什么?你的回答只能是:1 英文 2 汉字的首字节 3 汉字的尾字节。而这个问题的解法,为了稳妥起见,你必须从字符串的开始判断起)。也就是说在char3中,如果保存的是汉字,则char0保存汉字第1个字节,char1保存汉字第2个字节,第3个存放0;如果是英文的话,则只用到char0,其它的全部为0。接下来,对于使用char3来保存汉字,是否真的很合适呢?因为如果把它当作一个字符串来看的话,在查找时就需要使用 strcmp 来比较字符串了,这样一定是会影响速度的。如果不把它看作字符串(字符串的最后一个字节需要以0结尾),只用char2的话,我们可以只是简单地调用宏MAKEWORD,把2个byte压成1个WORD。当把文字作为一个WORD来看的时候,这样查找比较时可以用WORD内建的=操作,这样要比调用strcmp函数要快得多。int frequency用来标志每个WORD的使用频率。设想,如果一个字已经存在于Cache中,以后每对它调用一次,就让frequency+。这样做还有一个用意是,是否可以在一个合适的时候,以frequency为参照来对这整个Cache排个序,把常用的字放在前面。那么在显示时,可以先在Cache中查找所要显示的字是否已经存在于Cache中,如果有则直接显示,没有的话才需要采取某种手段将字加入到Cache中。一些常用的字(像:我、的、着、了、过),使得显示的速度将会大大提高。其实上面说了半天的Cache,它具体是什么呢?其实就是指的最小绘制单位,在DirectX7里是Surface,而在DirectX8中就是Texture。使用它来存放显示过的汉字,这样,就不用每次都从字库中读取或是调用如TextOut这类GDI超慢的函数了。因为每次在绘制一个文字之前,都会先在这个Cache中找,有的话就直接画上去,没有才会调用TextOut操作。而这样做的原因,我们先设想一下:游戏一般会控制为30fps或是60fps的速度不停地刷新,如果在GameLoop中有任何的代码是龟速级的话,这样就会导致fps的最大数的降低,也就意味着在保证30fps或60fps的同时,能绘制到屏幕上的物体的数量减少了。这就是我们为什么要使用Texture来作为Cache的实现的原因。再一个,文字在屏幕上显示时一般会保持一段时间,这个时间可能是1秒-3秒,我们的游戏也就会相应地更新60fps或180fps,这是因为人们需要阅读它们。或者是一些如标题这样的文字,它们总是不会更新的,或是更新得很慢。我们完全可以在第一时间,比方说我们的画面有60fps,在第1个fps时,我们得知要显示文字”唐”,然后先在Cache中找,结果很糟:没有找到!这时马上用TextOut写到Texture上(现在还是属于第1个fps的时间范围内),而接下来的59个fps(甚至更多),都不用再调TextOut了,而是直接从我们的Cache:Texture上Copy到屏幕上,速度得到了保障。谈到GDI的函数,为了实现设备无关性,它们的速度都很慢。其实它们也不像说得那么慢,如果不是每一帧都要调用它们,也算是蛮快的_。那么这个RECT rect,就代表着这个文字所对应在Texture上的区域位置。使用什么东西来把这n个Char串起来呢,一般会想到的是链表,原因无非有2个:1 随时有新的字加进来,而内存是不连续的 2 它几乎没有容量的限制(除非是内存用完了)。不过链表的访问速度是很慢的,如果使用像数组这样的东西就好了。仔细想想,在这里,我们用来存储的Cache,最大也就是256*256(理由上面说了),所以大小应该会是固定的。我们只需要在数组中的给每一个汉字加上一个标志,说明这个位置的使用情况。那么就使用数组吧,这样的话,访问的速度要更快一些,直接首地址+偏移量就够了,不必像链表,在查找时需要逐node访问。当然,我绝不会想到用new Char来申请这个数组。因为这样做实在没有必要,请不要过于迷信自己的能力,在STL中已经有vector了,为什么还要自己写呢?_最后的一个bool成员变量isUsing,也就是上面所说,用来标志使用情况的。实际的操作上面考虑了那么多,我认为都是实际操作之前所应该有的。先谈谈如何显示吧,因为在DirectX8.1中已经将DirectDraw和Direct3D融合为DirectGraphics了。所以无法像原来那样了哦,实在有太多东西要讲了,我还是推荐几篇文章给你吧_:/mays/develop/directx/200201/Geczy3Din2D.htm/mays/develop/directx/200201/GESurface.htm/mays/develop/directx/200112/2DGtoDX8.htm/mays/develop/directx/200201/DX8adv2D.htm接下来,我会假设你已经具备了在DirectX8.1中绘图的基本概念了,所以在你继续往下阅读之前,请务必先仔细阅读以上推荐的文章。前面提到,需要一个vector来对应Texture上各个位置文字的信息,上面已经创建了一个结构Char,则这个vector的定义为:vector _vBuf;/ 记录缓冲中现有的文字情况首先,由于可以利用硬件的放大缩小机能,所以字体的大小精度要求不是很高,只需要支持16*16和24*24大小的字体就可以了。我们需要一个这样的初始化函数:bool CFont: /*-LPDIRECT3DDEVICE8 pd3dDevice - D3DDevice设备char szFontName - 字体名(如: 宋体)int nSize - 字体大小, 只支持16和24int nLevel - 纹理的大小级别-*/Init( LPDIRECT3DDEVICE8 pd3dDevice, char szFontName, int nSize, int nLevel )。在DirectX8.1中,由SetTexture()所贴的图的大小,也就是Texture的大小,是有大小限制的,长和宽都必须是2n,而且位图越大,所花费的显存越大,这样留给其他显示用的显存就少了。所以,必须根据需求的不同,来自定Texture(也就是Cache)的大小。因为汉字点阵大小的原因,所以从实用角度而言(比方说只是显示fps或是短小的标题),开辟一个64*64大小的Texture,才能满足最低情况下的需要(这时如果选择16点阵的话可以存放16个汉字,24点阵可以存放7个,依次类推)。根据设置,创建Texture:_TextureSize = 32 nLevel;/ 纹理大小_TextSize = nSize;/ 文字大小_TextureSize = 32 CreateVertexBuffer( _Max * 6 * sizeof(FONT2DVERTEX),D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0,D3DPOOL_DEFAULT, &_pVB ) ) )DeleteObject( _hFont );DeleteObject( _hBmp );DeleteDC( _hDc );return false;创建Textureif ( FAILED( _pd3dDevice-CreateTexture( _TextureSize, _TextureSize, 1, 0, D3DFMT_A4R4G4B4, D3DPOOL_MANAGED, &_pTexture ) ) )DeleteObject( _hFont );DeleteObject( _hBmp );DeleteDC( _hDc );SAFE_RELEASE(_pVB); return false;设置渲染设备的渲染属性:_pd3dDevice-SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );_pd3dDevice-SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );_pd3dDevice-SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );_pd3dDevice-SetRenderState( D3DRS_ALPHATESTENABLE,TRUE );_pd3dDevice-SetRenderState( D3DRS_ALPHAREF,0x08 );_pd3dDevice-SetRenderState( D3DRS_ALPHAFUNC,D3DCMP_GREATEREQUAL );_pd3dDevice-SetTextureStageState( 0, D3DTSS_ALPHAOP,D3DTOP_MODULATE );_pd3dDevice-SetTexture( 0, _pTexture );_pd3dDevice-SetVertexShader( D3DFVF_FONT2DVERTEX );_pd3dDevice-SetStreamSource( 0, _pVB, sizeof(FONT2DVERTEX) );设置缓冲的最大容量_vBuf.resize( _Max );这样,初始化完成了。接下来是如何把一个汉字写到Texture中,以及如何进行管理。定义函数:/ 得到文字在纹理中的位置void CFont:/*-char c1 - 文字的第1个字节char c2 - 文字的第2个字节int & tX - 写入纹理中的坐标xint & tY - 写入纹理中的坐标y-*/Char2Texture( char c1, char c2, int & tX, int & tY )WORD w = MAKEWORD(c1, c2);/ 把此字变为WORDvector:iterator it = find( _vBuf.begin(), _vBuf.end(), w );if ( it = _vBuf.end() )/ 如果没找到it = find( _vBuf.begin(), _vBuf.end(), 0 ); / 查找空闲位置if ( it = _vBuf.end() )/ 缓冲已满for(; it!=_vBuf.begin(); it- )it-hz = 0;/Log.Output( 字体缓冲已满, 清空! );/ 计算当前空闲的Char在缓冲中是第几个int at = it-_vBuf.begin();/ 得到空闲位置的坐标tX = (at % _RowNum) * _TextSize;tY = (at / _RowNum) * _TextSize;/ 设置这个Char为使用中(*it).hz = w;RECT rect = 0, 0, _TextSize, _TextSize;char sz3 = c1, c2, 0;/ 填充背景为黑色(透明色)FillRect( _hDc, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH) );/ 往hBitmap上写字:TextOut( _hDc, 0, 0, sz, c1 & 0x80 ? 2 : 1 );/ 锁定表面, 把汉字写入纹理, 白色的是字(可见), 黑色为背景(透明)D3DLOCKED_RECT d3dlr;_pTexture-LockRect(0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK);BYTE * pDstRow = (BYTE*)( (WORD *)d3dlr.pBits + tY * _TextureSize + tX );for (DWORD y=0; y_TextSize; y+)WORD * pDst16 = (WORD*)pDstRow;for (DWORD x=0; x 4);if (bAlpha 0)*pDst16+ = (bAlpha UnlockRect( NULL );else/ 计算当前空闲的Char在缓冲中是第几个int at = it-_vBuf.begin();/ 得到这个字的坐标tX = (at % _RowNum) * _TextSize;tY = (at / _RowNum) * _TextSize;以上代码中的注释已经很清楚了,相信无须我多言。这里唯一需要声明的是:原来所定义的Char结构是这样的struct Char char hz3; / 保存汉字 int frequency;/ 使用频率 RECT rect; / 这个字对应位图的区域 Bool isUsing; / 是否使用后来因为将char hz3合成为WORD,所以改为WORD hz。然后对于int frequency,这个词频应该如何表现,我一直没有想到很好的方法。frequency应该在何时+呢?是在每次被使用的时候吗?但是这样的话,上面说过,游戏是以60fps的速度在刷新,如果停上1分钟的话,变量很快就会溢出了。就算是使用像是DWORD或_int64这样的巨型变量保存,也是不安全的。除非能在某个合适的时候将frequency清零,但是这个“时候”是什么时候呢?或者设置一个最大值,如65535,但是这样也基本上没什么用途,很快,所有在vector中的Char中的frequency都会+成65535的。回忆一下最初,是因为想把常用字放到vector的前面,以便每次find操作可以最快返回结果的。而经过我的测试,即使不做这样的优化操作,速度也是很快的,毕竟Cache不是很大,加上vector是连续内存空间。所以可以放弃使用int frequency。然后对于RECT rect,因为没有了int frequency,意味着一旦将汉字写入到Texture,其位置就不会变动了。所以,很容易根据find函数操作后的iterator,直接计算出这个汉字所在Texture的位置。这样,RECT rect也不再必须。而bool isUsing,它本身就是个鸡肋,要也可以,这样结构更加清晰。不过,直接通过观察WORD hz为0或非0,即可实现isUsing的作用了。为什么要对结构Char这么精雕细琢呢?1. 既然没有必要的东西,就应该删除2. Char结构的大小越大,vector所要求的内存越大3. 小的结构,find可以更快地查找出所结果为什么find会正常工作呢?这里我要大概地讲一下find是如何查找出所需的位置的:它只是简单地使用while从vector的begin一直遍历到end,逐个判断,直到找到为止。find要求必须实现自己的operator =(),进一步跟踪到find的源码中,发现也是这样。于是前面的结构Char变成了现在这样:struct CharWORDhz;/ 文字Char() : hz(0) / 用作查找文字inline bool operator = ( WORD h ) constreturn hz=h ? true : false;是不是很简单?_终于到了显示的函数了:/ 得到文字在纹理中的位置bool CFont:/*-char szText - 显示的字符串int x - 屏幕坐标xint y - 屏幕坐标yD3DCOLOR - 颜色
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 教师招聘之《幼儿教师招聘》通关检测卷含答案详解(突破训练)
- 2025内蒙古呼伦贝尔扎兰屯市综合类岗位“校园引才”37人笔试备考及答案详解(全优)
- 2025年兵团第十师北屯市引进和事业编工作人员考试笔试试卷【附解析】
- 2025内蒙古霍林河机场管理有限责任公司拟录用人员笔试历年参考题库附带答案详解
- 100MW300MWh构网型电化学独立储能项目可行性研究报告模板-拿地立项申报
- 2025年多式联运信息平台协同物流与智慧物流产业政策解读报告
- 合肥市S社区“三社联动”:运行机制、困境与突破路径研究
- 教师招聘之《小学教师招聘》预测复习附答案详解(预热题)
- 2025年教师招聘之《小学教师招聘》题库附答案详解【突破训练】
- 教师招聘之《小学教师招聘》模拟卷包带答案详解(突破训练)
- 《煤矿安全规程》2025
- 燃气综合考试题及答案
- 临建人员安全教育
- 年产50万件巴枪以及快递包装袋生产项目报告表
- 柴油使用安全管理办法
- 安全生产的主体责任
- 安全副总经理岗位职责
- 中国移民史与典型移民事件
- 患者发生病情变化应急预案
- 质量改进培训课件
- 中国写意花鸟课件
评论
0/150
提交评论