6 外星人游戏(harib26e)

接下来,我们打算做一个跟OSASK上面的外星人游戏差不多的游戏。在看这本书的各位读者估计也不知道OSASK的外星人游戏到底是何方神圣,那么我们先从背景故事开始讲起吧(笑)。顺便说一句,这个故事不是之前就设定好的,而是笔者在这里现编的。

20XX年,从遥远的宇宙另一端飞来一群外星侵略者,它们不用穿太空服就能在宇宙中生存。它们也没有发达的文明,一直就这样在宇宙中四处漂泊。

虽然并没有恶意,但它们身上感染了一种凶恶的病毒,人类目前的科学水平还无法应付(它们自己虽然感染了病毒,但这种病毒对它们的健康无害),如果它们来到地球,包括人类在内的大多数地球生命都会遭到毁灭性的打击。

人类曾经尝试说服这些外星人,但是他们的语言能力不佳,根本就无法沟通。科学家们经过讨论,认为只好将它们击退。虽然做法有些残酷,但如果它们看到同伴受到攻击全都牺牲了,它们似乎就会做出“看来这里不适合我们居住”的判断。

时间紧迫,只争朝夕。就在人类商讨对策之时,它们已经来到地球了。没办法,只好抄起手边的等离子炮,将它们连同病毒一起消灭掉。

游戏的操作方法是这样的,按小键盘上的“4”和“6”键移动自机的位置,按空格键发射等离子炮。等离子炮每发射一次,必须进行一段时间的充电(或者是加热?)才能重新发射(其实这个时间也不是很长),因此不能连续发射。笔者也没见过真的等离子炮是什么样的(笑),不过我们的等离子炮炮弹飞行的速度慢到可以用肉眼看见,反正不是像光线那样飞快就是了,发射的时候还是要瞄准好哟。

消灭外星人会得到奖金(得分),命中率越高(打空的次数少)的话得到的奖金越多。因为这个等离子炮需要消费大量的电力,如果老是打不中而白白浪费的话,咱们国家的电力就会不够用啦。为了鼓励炮击手节约弹药,我们才设定了这样一个奖励的规定。

为了地球的和平,希望大家英勇奋战!

补充:当初估计它们只有30只来到了地球,但现在看来还有后续部队。另有不确定的情报表示,后续部队的移动速度很快……消灭它们吧!

我们来编写程序吧,这次比较长哦。

invader.c

  1. #include <stdio.h> /* sprintf */
  2. #include <string.h> /* strlen */
  3. #include "apilib.h"
  4. void putstr(int win, char *winbuf, int x, int y, int col, unsigned char *s);
  5. void wait(int i, int timer, char *keyflag);
  6. static unsigned char charset[16 * 8] = {
  7. /* invader(0) */
  8. 0x00, 0x00, 0x00, 0x43, 0x5f, 0x5f, 0x5f, 0x7f,
  9. 0x1f, 0x1f, 0x1f, 0x1f, 0x00, 0x20, 0x3f, 0x00,
  10. /* invader(1) */
  11. 0x00, 0x0f, 0x7f, 0xff, 0xcf, 0xcf, 0xcf, 0xff,
  12. 0xff, 0xe0, 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0x00,
  13. /* invader(2) */
  14. 0x00, 0xf0, 0xfe, 0xff, 0xf3, 0xf3, 0xf3, 0xff,
  15. 0xff, 0x07, 0xff, 0xff, 0x03, 0x03, 0x03, 0x00,
  16. /* invader(3) */
  17. 0x00, 0x00, 0x00, 0xc2, 0xfa, 0xfa, 0xfa, 0xfe,
  18. 0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x04, 0xfc, 0x00,
  19. /* fighter(0) */
  20. 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  21. 0x01, 0x43, 0x47, 0x4f, 0x5f, 0x7f, 0x7f, 0x00,
  22. /* fighter(1) */
  23. 0x18, 0x7e, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0xff,
  24. 0xff, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0x00,
  25. /* fighter(2) */
  26. 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
  27. 0x80, 0xc2, 0xe2, 0xf2, 0xfa, 0xfe, 0xfe, 0x00,
  28. /* laser */
  29. 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
  30. 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00
  31. };
  32. /* invader:"abcd", fighter:"efg", laser:"h" */
  33. void HariMain(void)
  34. {
  35. int win, timer, i, j, fx, laserwait, lx = 0, ly;
  36. int ix, iy, movewait0, movewait, idir;
  37. int invline, score, high, point;
  38. char winbuf[336 * 261], invstr[32 * 6], s[12], keyflag[4], *p;
  39. static char invstr0[32] = " abcd abcd abcd abcd abcd ";
  40. win = api_openwin(winbuf, 336, 261, -1, "invader");
  41. api_boxfilwin(win, 6, 27, 329, 254, 0);
  42. timer = api_alloctimer();
  43. api_inittimer(timer, 128);
  44. high = 0;
  45. putstr(win, winbuf, 22, 0, 7, "HIGH:00000000");
  46. restart:
  47. score = 0;
  48. point = 1;
  49. putstr(win, winbuf, 4, 0, 7, "SCORE:00000000");
  50. movewait0 = 20;
  51. fx = 18;
  52. putstr(win, winbuf, fx, 13, 6, "efg");
  53. wait(100, timer, keyflag);
  54. next_group:
  55. wait(100, timer, keyflag);
  56. ix = 7;
  57. iy = 1;
  58. invline = 6;
  59. for (i = 0; i < 6; i++) {
  60. for (j = 0; j < 27; j++) {
  61. invstr[i * 32 + j] = invstr0[j];
  62. }
  63. putstr(win, winbuf, ix, iy + i, 2, invstr + i * 32);
  64. }
  65. keyflag[0] = 0;
  66. keyflag[1] = 0;
  67. keyflag[2] = 0;
  68. ly = 0; /*不显示*/
  69. laserwait = 0;
  70. movewait = movewait0;
  71. idir = +1;
  72. wait(100, timer, keyflag);
  73. for (;;) {
  74. if (laserwait != 0) {
  75. laserwait--;
  76. keyflag[2 /* space */] = 0;
  77. }
  78. wait(4, timer, keyflag);
  79. /*自机的处理*/
  80. if (keyflag[0 /* left */] != 0 && fx > 0) {
  81. fx--;
  82. putstr(win, winbuf, fx, 13, 6, "efg ");
  83. keyflag[0 /* left */] = 0;
  84. }
  85. if (keyflag[1 /* right */] != 0 && fx < 37) {
  86. putstr(win, winbuf, fx, 13, 6, " efg");
  87. fx++;
  88. keyflag[1 /* right */] = 0;
  89. }
  90. if (keyflag[2 /* space */] != 0 && laserwait == 0) {
  91. laserwait = 15;
  92. lx = fx + 1;
  93. ly = 13;
  94. }
  95. /*外星人移动*/
  96. if (movewait != 0) {
  97. movewait--;
  98. } else {
  99. movewait = movewait0;
  100. if (ix + idir > 14 || ix + idir < 0) {
  101. if (iy + invline == 13) {
  102. break; /* GAME OVER */
  103. }
  104. idir = - idir;
  105. putstr(win, winbuf, ix + 1, iy, 0, " ");
  106. iy++;
  107. } else {
  108. ix += idir;
  109. }
  110. for (i = 0; i < invline; i++) {
  111. putstr(win, winbuf, ix, iy + i, 2, invstr + i * 32);
  112. }
  113. }
  114. /*炮弹处理*/
  115. if (ly > 0) {
  116. if (ly < 13) {
  117. if (ix < lx && lx < ix + 25 && iy <= ly && ly < iy + invline) {
  118. putstr(win, winbuf, ix, ly, 2, invstr + (ly - iy) * 32);
  119. } else {
  120. putstr(win, winbuf, lx, ly, 0, " ");
  121. }
  122. }
  123. ly--;
  124. if (ly > 0) {
  125. putstr(win, winbuf, lx, ly, 3, "h");
  126. } else {
  127. point -= 10;
  128. if (point <= 0) {
  129. point = 1;
  130. }
  131. }
  132. if (ix < lx && lx < ix + 25 && iy <= ly && ly < iy + invline) {
  133. p = invstr + (ly - iy) * 32 + (lx - ix);
  134. if (*p != ' ') {
  135. /* hit ! */
  136. score += point;
  137. point++;
  138. sprintf(s, "%08d", score);
  139. putstr(win, winbuf, 10, 0, 7, s);
  140. if (high < score) {
  141. high = score;
  142. putstr(win, winbuf, 27, 0, 7, s);
  143. }
  144. for (p--; *p != ' '; p--) { }
  145. for (i = 1; i < 5; i++) {
  146. p[i] = ' ';
  147. }
  148. putstr(win, winbuf, ix, ly, 2, invstr + (ly - iy) * 32);
  149. for (; invline > 0; invline--) {
  150. for (p = invstr + (invline - 1) * 32; *p != 0; p++) {
  151. if (*p != ' ') {
  152. goto alive;
  153. }
  154. }
  155. }
  156. /*全部消灭*/
  157. movewait0 -= movewait0 / 3;
  158. goto next_group;
  159. alive:
  160. ly = 0;
  161. }
  162. }
  163. }
  164. }
  165. /* GAME OVER */
  166. putstr(win, winbuf, 15, 6, 1, "GAME OVER");
  167. wait(0, timer, keyflag);
  168. for (i = 1; i < 14; i++) {
  169. putstr(win, winbuf, 0, i, 0, " ");
  170. }
  171. goto restart;
  172. }
  173. void putstr(int win, char *winbuf, int x, int y, int col, unsigned char *s)
  174. {
  175. int c, x0, i;
  176. char *p, *q, t[2];
  177. x = x * 8 + 8;
  178. y = y * 16 + 29;
  179. x0 = x;
  180. i = strlen(s); /*计算s的字符数*/
  181. api_boxfilwin(win + 1, x, y, x + i * 8 - 1, y + 15, 0);
  182. q = winbuf + y * 336;
  183. t[1] = 0;
  184. for (;;) {
  185. c = *s;
  186. if (c == 0) {
  187. break;
  188. }
  189. if (c != ' ') {
  190. if ('a' <= c && c <= 'h') {
  191. p = charset + 16 * (c - 'a');
  192. q += x;
  193. for (i = 0; i < 16; i++) {
  194. if ((p[i] & 0x80) != 0) { q[0] = col; }
  195. if ((p[i] & 0x40) != 0) { q[1] = col; }
  196. if ((p[i] & 0x20) != 0) { q[2] = col; }
  197. if ((p[i] & 0x10) != 0) { q[3] = col; }
  198. if ((p[i] & 0x08) != 0) { q[4] = col; }
  199. if ((p[i] & 0x04) != 0) { q[5] = col; }
  200. if ((p[i] & 0x02) != 0) { q[6] = col; }
  201. if ((p[i] & 0x01) != 0) { q[7] = col; }
  202. q += 336;
  203. }
  204. q -= 336 * 16 + x;
  205. } else {
  206. t[0] = *s;
  207. api_putstrwin(win + 1, x, y, col, 1, t);
  208. }
  209. }
  210. s++;
  211. x += 8;
  212. }
  213. api_refreshwin(win, x0, y, x, y + 16);
  214. return;
  215. }
  216. void wait(int i, int timer, char *keyflag)
  217. {
  218. int j;
  219. if (i > 0) {
  220. /*等待一段时间*/
  221. api_settimer(timer, i);
  222. i = 128;
  223. } else {
  224. i = 0x0a; /* Enter */
  225. }
  226. for (;;) {
  227. j = api_getkey(1);
  228. if (i == j) {
  229. break;
  230. }
  231. if (j == '4') {
  232. keyflag[0 /* left */] = 1;
  233. }
  234. if (j == '6') {
  235. keyflag[1 /* right */] = 1;
  236. }
  237. if (j == ' ') {
  238. keyflag[2 /* space */] = 1;
  239. }
  240. }
  241. return;
  242. }

由于这不是操作系统,而只是一个应用程序。这本书也不是讲如何编写外星人游戏的,而是讲如何编写操作系统的(笑),因此对于程序的讲解我们就速战速决吧。

putstr函数用来显示字符串,不过a~h的字符不是直接显示,而是用charset的字库来显示的,为提高显示速度,使用了api_refreshwin。

wait函数用来延时并等待按键输入。当i指定为0时等待回车键的输入,否则按照“指定的时间×0.01秒”为基准进行延时等待,在等待期间如果有按键输入则反映到keyflag[0~2]中。

HariMain则是处理游戏的主体,里面有很多变量,这里介绍一些比较难懂的。

fx:自机的x坐标(fighter_x

lx, ly:等离子炮弹的坐标(laser_x, laser_y1

ix, iy:外星人群的坐标(invaders_x, invaders_y

idir:外星人群的移动方向(invaders_direction

laserwait:等离子炮弹的剩余充电时间

movewait:当这个变量变为0时外星人群前进一步

movewait0movewait的初始值(消灭30只敌人后减少)

invline:外星人群的行数(invaders_line

score:当前得分

high:最高得分

point:得分的增加量(奖金的单价?)

invstr:将外星人群的状态显示为字符串的变量

1 一开始本来管自机发射的炮弹叫“激光”,不过激光居然飞得这么慢实在太诡异了(笑),于是在编故事的时候改成等离子炮弹了。

对了,在这个游戏中,当外星人到达最底下一行时就Game Over了,这时按下回车键可以重新开始。对于这个程序我们没有设定正常的结束方法,大家可以按Shift+F1或者点击“×”按钮强制结束。

■■■■■

好了,我们来“make run”。静下心来,为了保卫地球的明天,加油!

6 外星人游戏(harib26e) - 图1 6 外星人游戏(harib26e) - 图2
战斗开始 呜哇!

漫长的战斗之后(话说也就是几分钟吧)……终于挂了!

地球完蛋了!……这样可不行,要是人生能重新来过就好了(太夸张了)。其实按下回车键就可以了哦,胜败乃兵家常事,大侠请重新来过(笑)。

我们再来重新看看程序吧。make之后生成的invader.hrb大小为2335字节,也就是说,“纸娃娃系统”所具备的API使得这样一个游戏仅用2.28KB就可以写出来。和其他操作系统相比,这并不能算是非常好,不过笔者还是感到挺自豪的。

今天就到这里吧,明天我们继续来编写应用程序哦!