1.利用位移计算乘法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45


int count_offest(int _width,int x,int y) //移位乘法计算显示偏移值


{


 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);


}

2.高速绘制点(不用乘法)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69


void point(int x,int y,unsigned char color) //高速画点


{


 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


 }


}

3.镂空算法

镂空算法很多,用的都是AND MASK+OR方式,不要去理它,每一个点要处理两遍,包含三次

读内存,两次逻辑运算,一次写内存,太慢了,在你的颜色中规定一种透明色,画点时不去管

它就行了(上例)。核心思想:每个点上少处理一次。

4.双缓冲

双缓冲是必要的,但也不全是,很多教课书上把双缓冲作为消除屏幕闪烁的唯一方法,这不

对,因为只要跟踪了屏幕刷新周期,就不会闪,双缓冲直接带来的就是你的程序画点必须画两次

,一次向buffer,另一次重buffer搬到屏幕。我在做优化时,往往首先把双缓冲优化掉。没必要

浪费时间,就算有点闪,游戏是可以牺牲效果,换取时间的。这里给一个跟踪屏幕刷新周期的函

数,只要在你的刷屏程序前加上,效果基本上就可以了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15


void wait (void) //VGA屏幕刷新周期的测试


{


 while (inp(0x3DA)&0x08);


 while (!(inp(0x3DA)&0x08));


}

5.减少循环

减少循环,循环中多开销了一次累加(读写内存),一次比较(读内存+1次逻辑),如下例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12


for (i=0;i<10000;i++)


 {


  *(p+i)=0;


 }

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39


for (i=0;i<10000;i+=10)  //循环次数减少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;


 }

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42


for (i=0;i<10000;i+=10)  //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;


 }

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15


for (i=0;i<10000;i+=10)  //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要少一次逻辑比较。

6.图块处理

具体的说,处理一个图块时,很多人采用x,y两重循环,这是很值得研究的,根据屏幕特点,应该

只保留y循环,x方向直接线性累加处理即可。

7.不要解决判断语句

不要节约判断语句,它可能给你带来多一条语句的开销,但是却可能减少几百条语句的开销

8.良好的判断语句程序书写习惯

养成良好的书写习惯,让编译程序为你检查错误,如下例:

1
2
3


if (i==1)

写成 if (i=1) 编译不出错,但意思错了

写成 if (1=i) 编译就出错,可以检查出来

9.要程序流畅,主循环只做屏幕刷新的事情

主循环往往只是包含刷屏的一个死循环,更多的东东放在时钟里头,

要熟练拦截时钟,改变它的频率,你的画面就会动得流畅、自然。

下面是一个拦截时钟的例子,因为采用时钟循环,所以必须大量使用switch/case结构,要有思想准备。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141


#define TIME_KEEPER_INT 0x1c


long timer_counter;


void (_interrupt far *Old_Time_Isr)();


void timer_program(void);


////////////////////////////////////////////////////////////////


//注意:中断函数中不能调用系统输入输出函数,应尽量使用自己的程序


void _interrupt Timer(void)


{


 timer_program(); //调用用户程序


 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


//改变定时器频率函数


//注意:超过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));


}


////////////////////////////////////////////////////////////////


//安装时钟


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);


}


////////////////////////////////////////////////////////////////


//卸载时钟


void uninstall_timer()


{


 Change_Timer(TIME_18HZ);


 _dos_setvect(TIME_KEEPER_INT,Old_Time_Isr);


}