新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 大家累了可以在这里休息一下谈天说地:)
    [返回] 中文XML论坛 - 专业的XML技术讨论区休息区『 灌水天堂 』 → DOS游戏编程二十一条 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 9676 个阅读者浏览上一篇主题  刷新本主题   平板显示贴子 浏览下一篇主题
     * 贴子主题: DOS游戏编程二十一条 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 灌水天堂 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客楼主
    发贴心情 DOS游戏编程二十一条


    本文中所有例子均在WATCOM C/C++ 10.6下调试通过。

    1、找一种好的编程语言:
    当然,游戏可以用任何语言编写,这是可以肯定的,我就使用过Turbo Basic编写过跑马机游戏,还用VB写过一个半成品的网络拱猪游戏,但是,一个好的编程语言能够达到好的效果,这是毋庸置疑的。一个游戏程序员,梦寐以求的就是一个方便、完美、高速的语言。
    汇编是一种高速语言,但不够方便,如果要方便,就必须大量使用宏,笔者就曾经在6502汇编语言中大量使用FOR,NEXT,PRINT等语句,全部是宏,但是相应的内存开销,时间开销都加大了,不划算。
    WATCOM C/C++是一种好语言,可以访问大内存,速度快,也够方便,但是调试不够方便,只能用自己写的调试函数解决问题。还有每次运行必须调用DOS/4GW这个32位环境程序,既累赘又不方便,还占地方。
    MSC7.0也不错,通过它的虚拟内存机制也可以访问大内存,但可惜是16位仿真的,速度太慢。
    DJGPP也是很不错,关键它是共享的,同时还带有一个Alleg的共享游戏库,非常好用,推荐使用,但它生成的程序代码太大,不够优化。
    作为游戏程序员,我们追求的就是快一点、快一点、再快一点,如果还有更快的语言,希望大家介绍给我。

    2、要写专有程序,不要写通用的,通用,意味着慢,哪怕下次重新来过,也不能为了下次耽误这次。同理,凡是系统给你的函数,调用,要有坚决不用的思想准备,要自己写一套。

    3、写出来的程序,每秒钟必须刷屏70次以上,再通过时钟限制在30次(不抖),剩下的时间,就是运行你的游戏程序内容的时间,算一算,不多。

    4、要有引擎的概念,引擎包含系统底层的程序,数据结构,调用方法等,这些直接限制你以后的游戏好不好编,一般说来,我们做一个游戏半年时间,其中两个月编引擎,两个月编游戏,剩下两个月调试,可见引擎的重要。永远记着,你写的程序,就计算机而言,就是在搬数,把一堆数据提出来,处理一下搬到另外一块地方,就这么简单,那么,搬数的方法有多重要,你知道了吧。 5、尽量少用乘除法、浮点数,必要时使用移位乘法。这里给一个移位乘法的例子,大家可以参考:
    /*-------------------------------------------------------------------------------*/
    file://XiaoGe Made under WATCOM C/C++ 10.6
    /*-------------------------------------------------------------------------------*/
    int count_offest(int _width,int x,int y) file://移位乘法计算显示偏移值
    {
    int mode[17]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536};
    int i,offest=0;
    for (i=16;i>=0;i--)
    {
    if (_width>=mode[i])
    {
    offest+=(y<<i);
    _width-=mode[i];
    if (_width<1) break;
    }
    }
    return(offest+x);
    }
    /*-------------------------------------------------------------------------------*/
    结果=屏幕宽度*y+x。(这仅仅是一个移位乘法的例子,真正用时,要根据具体情况选择使用)

    6、没人会用数学描述去写一幅游戏图形,所有的图形都来自于美工画的PCX,BMP图形,去找一个美工,或者偷一批图形吧。读写PCX,BMP的函数是必须的。

    7、游戏程序员语录:给我一个画点函数吧,我能描绘出整个世界。任何时候下,一个最高速的画点程序都是必要的,这里给出一个例子(没有乘法的):
    /*-------------------------------------------------------------------------------*/
    file://XiaoGe Made under WATCOM C/C++ 10.6
    /*-------------------------------------------------------------------------------*/
    void point(int x,int y,unsigned char color) file://高速画点
    {
    if ((color!=NO_COLOR)&&
    (x>=0)&&(x<SCR_H)&&
    (y>=0)&&(y<SCR_V))
    {
    #ifdef VESA_320_200
    *(buffer+(y<<8)+(y<<6)+x)=color;
    #endif
    #ifdef VESA_640_480
    *(buffer+(y<<9)+(y<<7)+x)=color;
    #endif
    #ifdef VESA_800_600
    *(buffer+(y<<9)+(y<<8)+(y<<5)+x)=color;
    #endif
    #ifdef VESA_1024_768
    *(buffer+(y<<10)+x)=color;
    #endif
    #ifdef VESA_1280_1024
    *(buffer+(y<<10)+(y<<8)+x)=color;
    #endif
    }
    }
    /*-------------------------------------------------------------------------------*/

    8、镂空算法很多,用的都是AND MASK+OR方式,不要去理它,每一个点要处理两遍,包含三次读内存,两次逻辑运算,一次写内存,太慢了,在你的颜色中规定一种透明色,画点时不去管它就行了(上例)。记住,每个点上少处理一次,你至少可以多跳一圈舞。

    9、双缓冲是必要的,但也不全是,很多教课书上把双缓冲作为消除屏幕闪烁的唯一方法,这不对,因为只要跟踪了屏幕刷新周期,就不会闪,双缓冲直接带来的就是你的程序画点必须画两次,一次向buffer,另一次重buffer搬到屏幕。我在做优化时,往往首先把双缓冲优化掉。没必要浪费时间,就算有点闪,游戏是可以牺牲效果,换取时间的。这里给一个跟踪屏幕刷新周期的函数,只要在你的刷屏程序前加上,效果基本上就可以了。
    /*-------------------------------------------------------------------------------*/
    file://XiaoGe Made under WATCOM C/C++ 10.6
    /*-------------------------------------------------------------------------------*/
    void wait (void) file://VGA屏幕刷新周期的测试
    {
    while (inp(0x3DA)&0x08);
    while (!(inp(0x3DA)&0x08));
    }
    /*-------------------------------------------------------------------------------*/


    10、刷屏程序应该包含:背景屏幕刷新、精灵动画刷新、鼠标处理、键盘处理等,并且,每秒钟必须能运行70次以上,如果做不到,优化你的程序。

    11、优化是必须的,一个游戏引擎,至少应该优化7-10次,我的一个引擎,就优化了14次,速度从每秒钟12.1屏到70屏。还有,不要使用编译器的优化,除非你想你的用户无法使用你的程序。 12、计算你的每一步使用了几步操作,这一点在C中尤其重要,因为C太方便了,隐瞒了很多细节,如下例:
    从 *(Video+k)=*(p[1]+j);
    k++;
    到 *(Video+(k++))=*(p[1]+j); file://减少了一次k读内存操作
    到 *(Video+(k++))=*(*(p+1)+j); file://减少了把p转化成数组操作

    13、减少循环,循环中多开销了一次累加(读写内存),一次比较(读内存+1次逻辑),如下例:
    从 for (i=0;i<10000;i++)
    {
    *(p+i)=0;
    }
    到 for (i=0;i<10000;i+=10) file://循环次数减少9000次
    {
    *(p+i+0)=0;
    *(p+i+1)=0;
    *(p+i+2)=0;
    *(p+i+3)=0;
    *(p+i+4)=0;
    *(p+i+5)=0;
    *(p+i+6)=0;
    *(p+i+7)=0;
    *(p+i+8)=0;
    *(p+i+9)=0;
    }
    到 for (i=0;i<10000;i+=10) file://20次读变量内存减少为12次读,1次写
    {
    j=p+i;
    *(j+0)=0;
    *(j+1)=0;
    *(j+2)=0;
    *(j+3)=0;
    *(j+4)=0;
    *(j+5)=0;
    *(j+6)=0;
    *(j+7)=0;
    *(j+8)=0;
    *(j+9)=0;
    }
    到 for (i=0;i<10000;i+=10) file://10次读值内存减少为1次,其余为寄存器变量
    {
    j=p+i;
    *(j+0)=*(j+1)=*(j+2)=*(j+3)=*(j+4)=*(j+5)=*(j+6)=*(j+7)=*(j+8)=*(j+9)=0;
    }
    当然,如果允许,可以写10000个,不过也没必要,减掉一多半就行了。关键在速度和程序容量上达成平衡。另外,DO...WHILE比FOR和WHILE要少一次逻辑比较。

    14、具体的说,处理一个图块时,很多人采用x,y两重循环,这是很值得研究的,根据屏幕特点,应该只保留y循环,x方向直接线性累加处理即可。

    15、不要节约判断语句,它可能给你带来多一条语句的开销,但是却可能减少几百条语句的开销,1赔100,赌了。

    16、别给自己找病,养成良好的书写习惯,让编译程序为你检查错误,如下例:
    if (i==1)
    写成 if (i=1) 编译不出错,但意思错了
    写成 if (1=i) 编译就出错,可以检查出来

    17、游戏程序没有主循环,主循环往往只是包含刷屏的一个死循环,更多的东东放在时钟里头,要熟练拦截时钟,改变它的频率,你的画面就会动得流畅、自然。下面是一个拦截时钟的例子,因为采用时钟循环,所以必须大量使用switch/case结构,要有思想准备。

    /*-------------------------------------------------------------------------------*/
    file://XiaoGe Made under WATCOM C/C++ 10.6
    /*-------------------------------------------------------------------------------*/
    #define TIME_KEEPER_INT 0x1c
    long timer_counter;
    void (_interrupt far *Old_Time_Isr)();
    void timer_program(void);
    ////////////////////////////////////////////////////////////////
    file://注意:中断函数中不能调用系统输入输出函数,应尽量使用自己的程序
    void _interrupt Timer(void)
    {
    timer_program(); file://调用用户程序
    timer_counter++;
    Old_Time_Isr();
    }
    ////////////////////////////////////////////////////////////////
    #define CTRL_8253 0x43
    #define CTRL_WORD 0x3c
    #define COUNTER_0 0x40
    #define COUNTER_1 0x41
    #define COUNTER_2 0x42
    #define LOW_BYTE(n) (n&0x00ff)
    #define HI_BYTE(n) ((n>>8)&0x00ff)
    #define TIME_18HZ 0xFFFF
    file://改变定时器频率函数
    file://注意:超过1000Hz,与Windows将发生冲突
    void Change_Timer(unsigned short new_count)
    {
    outp(CTRL_8253,CTRL_WORD);
    outp(COUNTER_0,LOW_BYTE(new_count));
    outp(COUNTER_0,HI_BYTE(new_count));
    }
    ////////////////////////////////////////////////////////////////
    file://安装时钟
    void install_timer(int Hz)
    {
    short time_hz;
    time_hz=short(1193180/Hz);
    timer_counter=0;
    Change_Timer(time_hz);
    Old_Time_Isr=_dos_getvect(TIME_KEEPER_INT);
    _dos_setvect(TIME_KEEPER_INT,Timer);
    }
    ////////////////////////////////////////////////////////////////
    file://卸载时钟
    void uninstall_timer()
    {
    Change_Timer(TIME_18HZ);
    _dos_setvect(TIME_KEEPER_INT,Old_Time_Isr);
    }
    ////////////////////////////////////////////////////////////////
    /*-------------------------------------------------------------------------------*/

    18、不要去相信mouse程序会为你做到一切,去读0x33的状态,光标由自己显示,否则,哼哼......
    例子:
    /*-------------------------------------------------------------------------------*/
    file://XiaoGe Made under WATCOM C/C++ 10.6
    /*-------------------------------------------------------------------------------*/
    unsigned short cursor[] =
    {
    0x0000, /*0000000000000000*/ /* 16 words of cursor mask */
    0x4000, /*0100000000000000*/
    0x6000, /*0110000000000000*/
    0x7000, /*0111000000000000*/
    0x7800, /*0111100000000000*/
    0x7c00, /*0111110000000000*/
    0x7e00, /*0111111000000000*/
    0x7f00, /*0111111100000000*/
    0x7c00, /*0111110000000000*/
    0x4600, /*0100011000000000*/
    0x0600, /*0000011000000000*/
    0x0300, /*0000001100000000*/
    0x0300, /*0000001100000000*/
    0x0180, /*0000000110000000*/
    0x0180, /*0000000110000000*/
    0x00c0, /*0000000011000000*/
    };
    struct Mouse
    {
    char show; file://mouse 光标显示/不显示
    char left; file://mouse左键
    char right file://mouse右键
    char middle; file://mouse中键
    int x; file://mouseX坐标
    int y; file://mouseY坐标
    unsigned char color; file://mouse光标颜色
    }mouse;
    int mouse_page;
    /*-------------------------------------------------------------------------------*/
    void set_mouse_xy(int x_min,int x_max,int y_min,int y_max)
    {
    REGS regs;
    if (x_min<0) x_min=0;
    if (x_max>SCR_H) x_max=SCR_H;
    if (y_min<0) y_min=0;
    if (y_max>SCR_V) y_max=SCR_V;
    file://Define H min-max
    regs.w.ax=0x07;
    regs.w.cx=x_min;
    regs.w.dx=x_max;
    int386(0x33,&regs,&regs);
    file://Define V min-max
    regs.w.ax=0x08;
    regs.w.cx=y_min;
    regs.w.dx=y_max;
    int386(0x33,&regs,&regs);
    file://POSITION MOUSE CURSOR
    regs.w.ax=0x04;
    regs.w.cx=(x_max-x_min)>>1;
    regs.w.dx=(y_max-y_min)>>1;
    int386(0x33,&regs,&regs);
    }
    /*-------------------------------------------------------------------------------*/
    void init_mouse(void)
    {
    REGS regs;
    mouse.x=SCR_H/2;
    mouse.y=SCR_V/2;
    mouse.left=0;
    mouse.right=0;
    mouse.middle=0;
    mouse.color=255;
    mouse.show=0;
    file://mouse reset
    regs.w.ax=0x00;
    int386(0x33,&regs,&regs);
    file://old mouse hidden
    regs.w.ax=0x01;
    int386(0x33,&regs,&regs);
    set_mouse_xy(0,SCR_H,0,SCR_V);
    file://Define Mic/Piexl
    regs.w.ax=0x0F;
    regs.w.cx=4;
    regs.w.dx=4;
    int386(0x33,&regs,&regs);
    }
    /*-------------------------------------------------------------------------------*/
    void hard_disp_mouse(void)
    {
    int i,j,x,y;
    long addr,addr1,page;
    unsigned short temp;
    unsigned char color;
    unsigned int b[]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};
    unsigned char *video=(unsigned char *)0xA0000;


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2006/2/6 17:00:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 灌水天堂 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/3/28 21:00:25

    本主题贴数4,分页: [1]

     *树形目录 (最近20个回帖) 顶端 
    主题:  DOS游戏编程二十一条(10751字) - 卷积内核,2006年2月6日
        回复:  (2字) - doomguard,2006年3月30日
        回复:  谢谢哦!!!!!!!(20字) - hjx_221,2006年2月7日
        回复:  color=mouse.color;y=mouse.y-1;addr=count_offest..(7523字) - 卷积内核,2006年2月6日

    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    78.125ms