6 外星人游戏(harib26e)
接下来,我们打算做一个跟OSASK上面的外星人游戏差不多的游戏。在看这本书的各位读者估计也不知道OSASK的外星人游戏到底是何方神圣,那么我们先从背景故事开始讲起吧(笑)。顺便说一句,这个故事不是之前就设定好的,而是笔者在这里现编的。
20XX年,从遥远的宇宙另一端飞来一群外星侵略者,它们不用穿太空服就能在宇宙中生存。它们也没有发达的文明,一直就这样在宇宙中四处漂泊。
虽然并没有恶意,但它们身上感染了一种凶恶的病毒,人类目前的科学水平还无法应付(它们自己虽然感染了病毒,但这种病毒对它们的健康无害),如果它们来到地球,包括人类在内的大多数地球生命都会遭到毁灭性的打击。
人类曾经尝试说服这些外星人,但是他们的语言能力不佳,根本就无法沟通。科学家们经过讨论,认为只好将它们击退。虽然做法有些残酷,但如果它们看到同伴受到攻击全都牺牲了,它们似乎就会做出“看来这里不适合我们居住”的判断。
时间紧迫,只争朝夕。就在人类商讨对策之时,它们已经来到地球了。没办法,只好抄起手边的等离子炮,将它们连同病毒一起消灭掉。
游戏的操作方法是这样的,按小键盘上的“4”和“6”键移动自机的位置,按空格键发射等离子炮。等离子炮每发射一次,必须进行一段时间的充电(或者是加热?)才能重新发射(其实这个时间也不是很长),因此不能连续发射。笔者也没见过真的等离子炮是什么样的(笑),不过我们的等离子炮炮弹飞行的速度慢到可以用肉眼看见,反正不是像光线那样飞快就是了,发射的时候还是要瞄准好哟。
消灭外星人会得到奖金(得分),命中率越高(打空的次数少)的话得到的奖金越多。因为这个等离子炮需要消费大量的电力,如果老是打不中而白白浪费的话,咱们国家的电力就会不够用啦。为了鼓励炮击手节约弹药,我们才设定了这样一个奖励的规定。
为了地球的和平,希望大家英勇奋战!
补充:当初估计它们只有30只来到了地球,但现在看来还有后续部队。另有不确定的情报表示,后续部队的移动速度很快……消灭它们吧!
我们来编写程序吧,这次比较长哦。
invader.c
#include <stdio.h> /* sprintf */
#include <string.h> /* strlen */
#include "apilib.h"
void putstr(int win, char *winbuf, int x, int y, int col, unsigned char *s);
void wait(int i, int timer, char *keyflag);
static unsigned char charset[16 * 8] = {
/* invader(0) */
0x00, 0x00, 0x00, 0x43, 0x5f, 0x5f, 0x5f, 0x7f,
0x1f, 0x1f, 0x1f, 0x1f, 0x00, 0x20, 0x3f, 0x00,
/* invader(1) */
0x00, 0x0f, 0x7f, 0xff, 0xcf, 0xcf, 0xcf, 0xff,
0xff, 0xe0, 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0x00,
/* invader(2) */
0x00, 0xf0, 0xfe, 0xff, 0xf3, 0xf3, 0xf3, 0xff,
0xff, 0x07, 0xff, 0xff, 0x03, 0x03, 0x03, 0x00,
/* invader(3) */
0x00, 0x00, 0x00, 0xc2, 0xfa, 0xfa, 0xfa, 0xfe,
0xf8, 0xf8, 0xf8, 0xf8, 0x00, 0x04, 0xfc, 0x00,
/* fighter(0) */
0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x43, 0x47, 0x4f, 0x5f, 0x7f, 0x7f, 0x00,
/* fighter(1) */
0x18, 0x7e, 0xff, 0xc3, 0xc3, 0xc3, 0xc3, 0xff,
0xff, 0xff, 0xe7, 0xe7, 0xe7, 0xe7, 0xff, 0x00,
/* fighter(2) */
0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0xc2, 0xe2, 0xf2, 0xfa, 0xfe, 0xfe, 0x00,
/* laser */
0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00
};
/* invader:"abcd", fighter:"efg", laser:"h" */
void HariMain(void)
{
int win, timer, i, j, fx, laserwait, lx = 0, ly;
int ix, iy, movewait0, movewait, idir;
int invline, score, high, point;
char winbuf[336 * 261], invstr[32 * 6], s[12], keyflag[4], *p;
static char invstr0[32] = " abcd abcd abcd abcd abcd ";
win = api_openwin(winbuf, 336, 261, -1, "invader");
api_boxfilwin(win, 6, 27, 329, 254, 0);
timer = api_alloctimer();
api_inittimer(timer, 128);
high = 0;
putstr(win, winbuf, 22, 0, 7, "HIGH:00000000");
restart:
score = 0;
point = 1;
putstr(win, winbuf, 4, 0, 7, "SCORE:00000000");
movewait0 = 20;
fx = 18;
putstr(win, winbuf, fx, 13, 6, "efg");
wait(100, timer, keyflag);
next_group:
wait(100, timer, keyflag);
ix = 7;
iy = 1;
invline = 6;
for (i = 0; i < 6; i++) {
for (j = 0; j < 27; j++) {
invstr[i * 32 + j] = invstr0[j];
}
putstr(win, winbuf, ix, iy + i, 2, invstr + i * 32);
}
keyflag[0] = 0;
keyflag[1] = 0;
keyflag[2] = 0;
ly = 0; /*不显示*/
laserwait = 0;
movewait = movewait0;
idir = +1;
wait(100, timer, keyflag);
for (;;) {
if (laserwait != 0) {
laserwait--;
keyflag[2 /* space */] = 0;
}
wait(4, timer, keyflag);
/*自机的处理*/
if (keyflag[0 /* left */] != 0 && fx > 0) {
fx--;
putstr(win, winbuf, fx, 13, 6, "efg ");
keyflag[0 /* left */] = 0;
}
if (keyflag[1 /* right */] != 0 && fx < 37) {
putstr(win, winbuf, fx, 13, 6, " efg");
fx++;
keyflag[1 /* right */] = 0;
}
if (keyflag[2 /* space */] != 0 && laserwait == 0) {
laserwait = 15;
lx = fx + 1;
ly = 13;
}
/*外星人移动*/
if (movewait != 0) {
movewait--;
} else {
movewait = movewait0;
if (ix + idir > 14 || ix + idir < 0) {
if (iy + invline == 13) {
break; /* GAME OVER */
}
idir = - idir;
putstr(win, winbuf, ix + 1, iy, 0, " ");
iy++;
} else {
ix += idir;
}
for (i = 0; i < invline; i++) {
putstr(win, winbuf, ix, iy + i, 2, invstr + i * 32);
}
}
/*炮弹处理*/
if (ly > 0) {
if (ly < 13) {
if (ix < lx && lx < ix + 25 && iy <= ly && ly < iy + invline) {
putstr(win, winbuf, ix, ly, 2, invstr + (ly - iy) * 32);
} else {
putstr(win, winbuf, lx, ly, 0, " ");
}
}
ly--;
if (ly > 0) {
putstr(win, winbuf, lx, ly, 3, "h");
} else {
point -= 10;
if (point <= 0) {
point = 1;
}
}
if (ix < lx && lx < ix + 25 && iy <= ly && ly < iy + invline) {
p = invstr + (ly - iy) * 32 + (lx - ix);
if (*p != ' ') {
/* hit ! */
score += point;
point++;
sprintf(s, "%08d", score);
putstr(win, winbuf, 10, 0, 7, s);
if (high < score) {
high = score;
putstr(win, winbuf, 27, 0, 7, s);
}
for (p--; *p != ' '; p--) { }
for (i = 1; i < 5; i++) {
p[i] = ' ';
}
putstr(win, winbuf, ix, ly, 2, invstr + (ly - iy) * 32);
for (; invline > 0; invline--) {
for (p = invstr + (invline - 1) * 32; *p != 0; p++) {
if (*p != ' ') {
goto alive;
}
}
}
/*全部消灭*/
movewait0 -= movewait0 / 3;
goto next_group;
alive:
ly = 0;
}
}
}
}
/* GAME OVER */
putstr(win, winbuf, 15, 6, 1, "GAME OVER");
wait(0, timer, keyflag);
for (i = 1; i < 14; i++) {
putstr(win, winbuf, 0, i, 0, " ");
}
goto restart;
}
void putstr(int win, char *winbuf, int x, int y, int col, unsigned char *s)
{
int c, x0, i;
char *p, *q, t[2];
x = x * 8 + 8;
y = y * 16 + 29;
x0 = x;
i = strlen(s); /*计算s的字符数*/
api_boxfilwin(win + 1, x, y, x + i * 8 - 1, y + 15, 0);
q = winbuf + y * 336;
t[1] = 0;
for (;;) {
c = *s;
if (c == 0) {
break;
}
if (c != ' ') {
if ('a' <= c && c <= 'h') {
p = charset + 16 * (c - 'a');
q += x;
for (i = 0; i < 16; i++) {
if ((p[i] & 0x80) != 0) { q[0] = col; }
if ((p[i] & 0x40) != 0) { q[1] = col; }
if ((p[i] & 0x20) != 0) { q[2] = col; }
if ((p[i] & 0x10) != 0) { q[3] = col; }
if ((p[i] & 0x08) != 0) { q[4] = col; }
if ((p[i] & 0x04) != 0) { q[5] = col; }
if ((p[i] & 0x02) != 0) { q[6] = col; }
if ((p[i] & 0x01) != 0) { q[7] = col; }
q += 336;
}
q -= 336 * 16 + x;
} else {
t[0] = *s;
api_putstrwin(win + 1, x, y, col, 1, t);
}
}
s++;
x += 8;
}
api_refreshwin(win, x0, y, x, y + 16);
return;
}
void wait(int i, int timer, char *keyflag)
{
int j;
if (i > 0) {
/*等待一段时间*/
api_settimer(timer, i);
i = 128;
} else {
i = 0x0a; /* Enter */
}
for (;;) {
j = api_getkey(1);
if (i == j) {
break;
}
if (j == '4') {
keyflag[0 /* left */] = 1;
}
if (j == '6') {
keyflag[1 /* right */] = 1;
}
if (j == ' ') {
keyflag[2 /* space */] = 1;
}
}
return;
}
由于这不是操作系统,而只是一个应用程序。这本书也不是讲如何编写外星人游戏的,而是讲如何编写操作系统的(笑),因此对于程序的讲解我们就速战速决吧。
putstr函数用来显示字符串,不过a~h的字符不是直接显示,而是用charset的字库来显示的,为提高显示速度,使用了api_refreshwin。
wait函数用来延时并等待按键输入。当i指定为0时等待回车键的输入,否则按照“指定的时间×0.01秒”为基准进行延时等待,在等待期间如果有按键输入则反映到keyflag[0~2]中。
HariMain则是处理游戏的主体,里面有很多变量,这里介绍一些比较难懂的。
fx
:自机的x坐标(fighter_x
)
lx, ly
:等离子炮弹的坐标(laser_x, laser_y
)1
ix, iy
:外星人群的坐标(invaders_x, invaders_y
)
idir
:外星人群的移动方向(invaders_direction
)
laserwait
:等离子炮弹的剩余充电时间
movewait
:当这个变量变为0时外星人群前进一步
movewait0
:movewait
的初始值(消灭30只敌人后减少)
invline
:外星人群的行数(invaders_line
)
score
:当前得分
high
:最高得分
point
:得分的增加量(奖金的单价?)
invstr
:将外星人群的状态显示为字符串的变量
1 一开始本来管自机发射的炮弹叫“激光”,不过激光居然飞得这么慢实在太诡异了(笑),于是在编故事的时候改成等离子炮弹了。
对了,在这个游戏中,当外星人到达最底下一行时就Game Over了,这时按下回车键可以重新开始。对于这个程序我们没有设定正常的结束方法,大家可以按Shift+F1或者点击“×”按钮强制结束。
■■■■■
好了,我们来“make run”。静下心来,为了保卫地球的明天,加油!
![]() | ![]() | |
战斗开始 | 呜哇! |
漫长的战斗之后(话说也就是几分钟吧)……终于挂了!
地球完蛋了!……这样可不行,要是人生能重新来过就好了(太夸张了)。其实按下回车键就可以了哦,胜败乃兵家常事,大侠请重新来过(笑)。
我们再来重新看看程序吧。make之后生成的invader.hrb大小为2335字节,也就是说,“纸娃娃系统”所具备的API使得这样一个游戏仅用2.28KB就可以写出来。和其他操作系统相比,这并不能算是非常好,不过笔者还是感到挺自豪的。
今天就到这里吧,明天我们继续来编写应用程序哦!