Tag Archives:小游戏
如何实现一款 1KB 大小的空战游戏?
经常看到知乎上有人在问:那些只有几 KB 的游戏是如何制作出来的?刚好这种代码我也写过,1KB 大小的空战游戏,很久以前读书时写的:

WINXP 下(或者 DOSBOX)在 DOS 窗口中运行 DEBUG,然后把横线下的内容复制、粘贴到 DEBUG 窗口中,回车就可以见到了。
(点击 Read more 展开)

MINI-FOOLTRIS 游戏代码
不到两百行的俄罗斯方块游戏,教学用:
/*=====================================================================// FOOLTRS.C: Fool tetris game v1.0 by skywind// 最简俄罗斯,游戏设计入门傻瓜版,林伟 1999年8月11日//// 游戏并无任何扩展,仅以最简单的方式向人说明俄罗斯方块的基本功能实现// 程序运行于MS-DOS,虽然现在写游戏的平台早已变化了许多,// 但是我尽量容入了一些不变理论,希望才入门的游戏设计者能得到一些帮助// 用方向键/空格游戏,代码不超过两百行,初学C者皆能读懂,请用TC20编译////=====================================================================*/#include <stdio.h>#include <stdlib.h>#include <time.h>#include <conio.h>#include <string.h>#include <dos.h>/*---------------------------------------------------------------------// 基本定义:地图和砖块//---------------------------------------------------------------------*/int map[26][10]; /* 游戏地图定义: 所有砖块将被描述在地图中 ............*/typedef struct { int d[4][4]; } Block; /* 砖块结构定义 ...............*/int BlockList[7][4][4] = { /* 七种传统的方块定义 .................*/ { { 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 0, 0 }, { 0, 1, 1, 0 } }, { { 0, 0, 0, 0 }, { 0, 0, 2, 0 }, { 0, 0, 2, 0 }, { 0, 2, 2, 0 } }, { { 0, 3, 0, 0 }, { 0, 3, 0, 0 }, { 0, 3, 0, 0 }, { 0, 3, 0, 0 } }, { { 0, 4, 0, 0 }, { 0, 4, 4, 0 }, { 0, 0, 4, 0 }, { 0, 0, 0, 0 } }, { { 0, 0, 5, 0 }, { 0, 5, 5, 0 }, { 0, 5, 0, 0 }, { 0, 0, 0, 0 } }, { { 0, 6, 6, 6 }, { 0, 0, 6, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }, { { 0, 0, 0, 0 }, { 0, 7, 7, 0 }, { 0, 7, 7, 0 }, { 0, 0, 0, 0 } }};unsigned char cmap[]={0,0x72,0x30,0x47,0x57,0x27,0x60,0x74}; /* 颜色表 */long GameMode = 0, GameSpeed = -1, GameScore = 0; /* 全局定义:各状态 .*//*---------------------------------------------------------------------// 视频相关数据定义//---------------------------------------------------------------------*/char far* TextBuf = (char far*)0xb8000000l; /* 文本状态的显存地址 .....*/char TextBck[20][10]; /* 文本状态的二级缓存 .....*/#define SCREEN(x,y) (TextBuf[((y) * 160) + ((x) * 2)]) /* 显存中字符 */#define SCOLOR(x,y) (TextBuf[((y) * 160) + ((x) * 2) + 1]) /* 屏幕颜色 *//*---------------------------------------------------------------------// 基本砖块操作函数//---------------------------------------------------------------------*/void Rotate(const Block* src, Block* dest) /* 旋转砖块src到dest ......*/{ int i, j; for (j = 0; j < 4; j++) for (i = 0; i < 4; i++) dest->d[i][j] = src->d[j][3 - i];}int MapCheck(int x, int y) /* 检测地图中某坐标是否有砖块 ..............*/{ if (x < 0 || x >=10 || y < 0 || y >= 26) return -1; return map[y][x];}int BlockCheck(int x, int y, const Block* src) /* 检测砖块位置合法性 ..*/{ int i, j; for (j = 0; j < 4; j++) for (i = 0; i < 4; i++) if (MapCheck(x + j, y - i) && src->d[i][j]) return 0; return 1;}void CreateBlock(Block* src) /* 随机创建一个砖块,然后保存到src .......*/{ int i,j,k = rand() % 7; Block t; for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) src->d[i][j] = BlockList[k][i][j]; for (i = rand() % 4; i > 0; i--, *src = t) Rotate(src, &t);}long TimePass = 0, TimeExit = -1, GameTime = -1, w;void GameMain(void);/*---------------------------------------------------------------------// 程序入口函数//---------------------------------------------------------------------*/void main(void){ clrscr(); /* 初始化:清屏 ...........*/ randomize(); /* 初始化:随机数种子 .....*/ for (w = 0; w < 26; w++) memset(map[w], 0, sizeof(int) * 10); for (w = 0; w < 22; w++) /* 初始化:绘制边框 .......*/ SCREEN(w,0)=254, SCOLOR(w,0)=8, SCREEN(w,21)=254, SCOLOR(w,21)=8, SCREEN(0,w)=254, SCOLOR(0,w)=8, SCREEN(21,w)=254, SCOLOR(21,w)=8; GameSpeed = 2; /* 初始化:设置速度 .......*/ GameMain(); /* 进入游戏主程序 .........*/}int readkey(void) /* 无等待的接收一个键盘消息 ...............*/{ if (!kbhit()) return 0; /* 如果当前没有按键消息则直接返回 .........*/ if ((w = getch()) == 0) return getch(); /* 返回非控制键 .........*/ return (int)w;}int SpeedX[10] = { 17, 15, 13, 11, 9, 7, 5, 4, 3, 2 }; /* 速度级别 ....*/int x = 3, y = 20, v = 0, level; /* 全局定义:砖块的坐标,速度 .....*/Block A, B, C; /* 全局定义:A, B, C三个活动砖块 ..*/void GameDriver(void);void GameView(void);/*---------------------------------------------------------------------// 游戏逻辑核心(重要): 每秒循环18.2次,处理砖块移动/下落/消除等逻辑//---------------------------------------------------------------------*/void GameMain(void){ long key, i, j, m, n, old_time = 0; /* 定义各变量 .........*/ CreateBlock(&A); CreateBlock(&C); /* 初始化砖块 .........*/ for (GameMode = 0, v = SpeedX[GameSpeed]; GameMode == 0; TimePass++) { while (clock() == old_time); /* 速度控制,clock()为 18.2Hz ..*/ old_time = clock(); nosound(); key = readkey(); /* 读入一个键盘消息 ............*/ if (key == 75) if (BlockCheck(x - 1, y, &A)) x--; /* 左移 ...*/ if (key == 77) if (BlockCheck(x + 1, y, &A)) x++; /* 右移 ...*/ if (key == 80) v = 0; /* 下落: SET v = 0 ....*/ if (key == ' ') { /* 直接下落 ...........*/ for (i = y; i >= 0; i--) if (!BlockCheck(x, i - 1, &A)) break; y = i; v = 0; } if (key == 27) GameMode = 1; /* ESC: 退出游戏 ......*/ if (key == 72) { /* UP: 转动砖块 .......*/ Rotate(&A, &B); if (BlockCheck(x, y, &B)) A = B; } if (--v <= 0) { /* 到达了下落的时间 .......*/ v = SpeedX[GameSpeed]; /* 重置砖块速度 ...........*/ if (BlockCheck(x, y - 1, &A) == 0) {/* 如果到达map底部 ....*/ for (i=0, m=y; i<4; i++,m--) for (j=0,n=x; j<4; j++,n++) if (MapCheck(n,m)==0 && A.d[i][j]) /* 将A复制到map */ map[m][n] = A.d[i][j]; A = C; /* 将C复制到A .........*/ CreateBlock(&C); /* 创建新的C砖块 ......*/ if (y >= 20) GameMode = -1; /* 判断死亡 ...........*/ else y = 20, x = 3; GameDriver(); /* 消除完成的行 .......*/ } else y--; /* 否则:下降 .........*/ } GameView(); /* 显示: 更新屏幕 .....*/ } while (kbhit()) getch(); /* 按任意键结束 .......*/ while (!kbhit()); nosound(); /* 关闭扬声器 .........*/}void GameDriver(void) /* 处理消除行 ..............................*/{ int inc[5]={10,100,300,600,1000}, count = 0, x, y, k, i, j; for (y = 0; y < 25; y++) for (; ; count++) { /* 扫描map测试消行 ...*/ for (x = 0, k = 0; x < 10; x++) k += (map[y][x])? 1 : 0; if (k < 10) break; /* 计算一行是否可以消去 .......*/ for (j = y; j < 25; j++) memcpy(map[j], map[j+1], 10 * sizeof(int)); } if ((GameScore / 10000) != ((GameScore + inc[count]) / 10000)) GameSpeed = (GameSpeed + 1) % 10; /* 计算速度升级 ...........*/ GameScore += inc[count]; /* 计算新的分数 ...........*/ sound((count == 0)? 75 : 900);}void GameView(void) /* 绘制:将map里面的砖块绘制到屏幕 .........*/{ int i, j, m, n; for (i=0; i<10; i++) for (j=0; j<20; j++) /* 更新TextBck二级缓存 ..*/ TextBck[j][i] = map[j][i]; for (i=0, n=x; i<4; i++, n++) for (j = 0, m=y; j < 4; j++, m--) if (m>=0&&m<20 && n>=0&&n<10 && A.d[j][i]) TextBck[m][n] = A.d[j][i]; for (i = 0; i < 10; i++) for (j = 0; j < 20; j++) { /* 显示地图 ...*/ m = TextBck[19 - j][i], n = i * 2; SCREEN(n+1,j+1) = (m)? '[':'.', SCOLOR(n+1,j+1) = (m)? cmap[m] : 8; SCREEN(n+2,j+1) = (m)? ']':'.', SCOLOR(n+2,j+1) = (m)? cmap[m] : 8; } for (j = 0; j < 4; j++) for (i = 0; i < 4; i++) { /* 显示下个砖块*/ m = C.d[j][i], n = 160 * (j + 8) + 4 * i + 58; TextBuf[n+0] = (m)? '[':'.', TextBuf[n+1] = (m)? cmap[m] : 8; TextBuf[n+2] = (m)? ']':'.', TextBuf[n+3] = (m)? cmap[m] : 8; } if (TimePass & 3) return; textcolor(0xa-8); gotoxy(30,1); printf("POSITION: [%2d,%2d]", x, y); /* 显示分数等 .*/ gotoxy(30,2); printf("SPEED: %d", GameSpeed); gotoxy(30,6); printf("SCORE: "); cprintf("%ld", GameScore); gotoxy(30,5); printf("TIME: "); /* 显示时间 ...*/ m = (int)(TimePass / 18.2) / 60, n = (int)(TimePass / 18.2) % 60; cprintf("%d%d:%d%d", (m/10), (m%10), (n/10), (n%10)); gotoxy(1, 23);}
MINI-FOOLFLY 游戏代码
此程序仅向人说明编写游戏程序的若干功能实现和常用处理方法,编译版本下载:
http://www.skywind.me/resource/oldworks/foolfly.zip
/* Fool fly game v1.0 by skywind * 空战摸拟源程序,林伟 1999年7月30日 * * 这是一个空战程序的模式例子,并没有做任何优化与扩充 * 仅仅向人说明编写游戏程序的若干功能实现或常用处理方法 * 程序是DOS版的,现在写游戏的平台早已变化了许多,但是 * 我尽量容入了一些不变理论,希望才入门的游戏设计者能受 * 到一些启发,通过它也可以看出当日DOS下设计的困难重重。 * * 整个程序不足300行,请用BC31或者TC20的Large模式编译 */#include <stdio.h>#include <dos.h>#include <stdlib.h>#include <conio.h>#include <mem.h>#include <time.h>#include <math.h>#include <alloc.h>#define MAX_STAR 80 /* 最多星星数目 */#define MAX_OBJ 30 /* 最多物体(子弹和敌人) */#define MAXY 169 /* y坐标的最大值 */typedef unsigned long ulong;char far *VideoBuf=(char far*)0xa0000000L, *MemBuf; /* MemBuf 图形二级缓存 */char keys[128]; /* keys[0..100] 键盘上各键的状态 */const KEY_UP=72,KEY_DOWN=80,KEY_LEFT=75,KEY_RIGHT=77,KEY_ESC=1,KEY_CTRL=29; /* 键盘扫描码 */void interrupt far (*OldInt9)(void); /* 老的键盘中断程序地址 */void interrupt NewInt9(void); /* 新的键盘中断服务程序 */char GameStart(void);struct TStars { int x,y,v; } Stars[MAX_STAR]; /* 星星的描述 */struct TObj { int mode,type,index,next,x,y; } Objs[MAX_OBJ]; /* 子弹和敌人的描述 */long GameScore=0; /* 游戏分数 */unsigned char fly_pic[260]={ /* 飞机的图形,我是用程序将PCX图形转换过来的 */16,0,16,0,0,0,0,29,25,0,0,0,0,25,29,196,0,0,0,0,0,0,0,25,25,0,0,0,0,25,0,196,0,0,0,0,112,112,0,25,196,40,40,29,112,112,40,112,0,112,112,112,40,40,112,112,40,40,25,25,112,40,40,112,112,112,112,112,112,40,112,25,40,40,112,112,112,40,40,25,40,112,112,0,0,112,112,25,40,40,40,112,40,40,40,25,40,112,0,0,0,0,112,25,40,112,40,112,112,112,40,25,112,0,0,0,0,0,0,29,112,0,40,0,112,0,112,29,0,0,0,0,0,0,0,29,112,0,40,54,112,0,112,29,0,0,0,0,0,0,0,29,0,0,40,54,112,0,0,29,0,0,0,0,0,0,0,0,0,40,112,25,112,112,0,0,0,0,0,0,0,0,0,0,0,40,112,25,112,112,0,0,0,0,0,0,0,0,0,0,0,40,112,112,112,112,0,0,0,0,0,0,0,0,0,0,0,0,40,112,112,0,0,0,0,0,0,0,0,0,0,0,0,0,40,25,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,112,0,0,0,0,0,0,0,0}; /* 数据头四个字节分别由两个16位数据表示图片的长和宽,后面是图片8bit的颜色数据 */void copyright(void){ printf("Game Over. your score %ld\n",GameScore); printf("Thank you for play it, if you have any question please call me\n"); printf("(0871)7167710 or lwwind@yeah.net, Lin Wei\n");}int game_init(void);int game_restore(void);void main(void){ if (!game_init()) return; /* 游戏初始化 */ printf(" - STAR WAR -\r"); /* 打印标题 */ GameStart(); /* 游戏主循环 */ game_restore(); /* 还原初始化 */ copyright(); /* 显示说明文 */}int game_init(void){ long i; union REGS regs; MemBuf=(char far*)farmalloc(320L*(MAXY+1)); if (!MemBuf) { printf("not enough memory\n"); return 0; } memset(MemBuf,0,320L*(MAXY+1)); randomize(); /* 初始化随机函数 */ for (i=0;i<128;i++) keys[i]=0; /* 键盘码初始化 */ for (i=0;i<MAX_STAR;i++) { /* 星空初始化 */ Stars[i].x=random(320); Stars[i].y=-20+random(250); Stars[i].v=1; if (i<MAX_STAR/3) Stars[i].v++; } for (i=0;i<MAX_OBJ;i++) Objs[i].mode=0; /* 物体初始化 */ /* 键盘中断初始化:设置新的中断 */ OldInt9=getvect(9); disable(); setvect(9,NewInt9); enable(); /* 设置 320x200x256c图形模式 */ regs.x.ax=0x13; int86(0x10,®s,®s); return 1;}int game_restore(void){ union REGS regs; /* 还原设置:还原老的中断 */ disable(); setvect(9,OldInt9); enable(); regs.x.ax=3; int86(0x10,®s,®s); farfree(MemBuf); return 0;}/* 键盘服务程序 */void interrupt NewInt9(void){ unsigned char key; key=inportb(0x60); /* 读键盘扫描码 */ if (key<0x80) keys[key]=1; /* 如果最高位是0,则为按下 */ else keys[key&0x7f]=0; /* 如果最高位是1,则为放开 */ key=inportb(0x61); key|=0x80; outportb(0x61,key); /* 告诉键盘已接收 */ outportb(0x61,key&0x7f); outportb(0x20,0x20); /* 发送中断结束信号 */}/*---------------------------- 游戏图形引擎 ---------------------------------*/void pixel(unsigned x,unsigned y,char c) /* 画点 */{ if (x>=320||y>MAXY) return; /* 判断范围 */ MemBuf[(y<<8)+(y<<6)+x]=c; /* MemBuf[y*320+x]=c */}/* 将二级缓存的内容显示出来 */void show(void){ int offset=(199-MAXY)*160; memcpy(VideoBuf+offset,MemBuf,320L*(MAXY+1));}/* 清屏 */void clear(void){ memset(MemBuf,0,320L*(MAXY+1));}/* 就是绘制图块,(x,y)是坐标b是内存地址mode表示是否上下颠倒 * 敌人和主人是同样的图片做颠倒 */void putimage(int x,int y,char *b,int mode){ int len, wid, i, j; len = b[0] + (int)b[1] * 256; wid = b[2] + (int)b[3] * 256; x-=len/2; y-=wid/2; /* 中心对称 */ for (j=0,b=b+4;j<wid;j++) for (i=0;i<len;i++,b++) { if (*b&&!mode) pixel(x+i,y+j,*b); /* 是否镜面翻转 */ if (*b&&mode) pixel(x+i,y+wid-j-1,*b); }}void drawfire(int x,int y) /* 画出激光 */{ int i,j; for (i=-6;i<6;i++) { pixel(x-5,y+i,9); pixel(x+3,y+i,9); }}/*---------------------------- 游戏控制引擎 ---------------------------------*/unsigned long timepass; /* 游戏进行的时间 */int GameOver, Sound=0;int fly_x=160,fly_y=MAXY*2/3,fly_flag=1,fire_flag=1;void drivers(void); /* 控制产生敌人的函数 */void control(void); /* 对象控制(事件处理) */int AllocObj(void) /* 分配空余对象 */{ int i=0; while (i<MAX_OBJ&&Objs[i].mode) i++; if (i>=MAX_OBJ) i=MAX_OBJ-1; return i;}int CheckHit(int x1,int y1,int x2,int y2,int r) /* 检查碰撞 */{ if (abs(x1-x2)<=r&&abs(y1-y2)<=r) return 1; return 0;}ulong fclock(void) /* 读取 1.19MHz的32位系统时钟 */{ ulong t; disable(); outportb(0x43,0); t=inportb(0x40); t+=(inportb(0x40)<<8); t=0xffff^t; enable(); /* (not t) + (clock()<<16) */ return (clock()<<16)+t;}char GameStart(void){ ulong start=0; timepass=0; while (!GameOver) { while (fclock()-start<=45000L); start=fclock(); /* 时间控制 */ clear(); /* 1.清屏. 以下5点为游戏的主循环 */ timepass++; /* 2.时间基数++ */ control(); /* 3.事件处理 */ drivers(); /* 4.事件引擎 */ show(); /* 5.显示 */ if (--Sound<=0) nosound(); /* 声音处理 */ if ((timepass&3)==0) printf("%d\r",GameScore); /* 到一定的时间更新显示分数 */ if (keys[KEY_ESC]) GameOver=1; } sound(105); delay(700); nosound(); /* Game Over发音 */ return 1;}/* 游戏的事件处理主程序分别处理星空和物体还有主角控制 * 通过扫描对象数组:Objs[MAX_OBJ]来完成,根据扫描到的对象属性Objs[i].mode来判断 * 到底是什么对象:敌机或者子弹。并进入相应的处理程序:分析对象的状态,然后作出 * 调整,并重新更改状态,这就是游戏编程同时处理众多物体的核心思想。 */void control(void){ int i,j,x,y, ok; /* 星星处理 */ for (i=0;i<MAX_STAR;i++) { Stars[i].y+=Stars[i].v; /* 向下移动 */ if (Stars[i].y>MAXY) { /* 如果移出屏幕就让它重新出现在屏幕上方某位置 */ Stars[i].x=random(320); Stars[i].y=-random(60); } if (Stars[i].v==1) pixel(Stars[i].x,Stars[i].y,23); /* 速度不一样颜色也不一样 */ else pixel(Stars[i].x,Stars[i].y,28); } /* 对象处理 */ for (i=0;i<MAX_OBJ;i++) /* 扫描对象数组 */ { x=Objs[i].x; y=Objs[i].y; ok=1; switch (Objs[i].mode) /* mode表示敌人,子弹等 */ { /* 敌人处理 */ case 1: if (Objs[i].index==0) { switch (Objs[i].type) /* 检测敌人的三种状态 */ { case 0: if (!random(30)) Objs[i].type=1; break; /* 3种动作状态 */ case 1: if (x<fly_x) x++; if (x>fly_x) x--; if (!random(40)) Objs[i].type=2; break; case 2: default:if (!random(3)) y+=3; break; } if (CheckHit(x,y,fly_x,fly_y,15)) fly_flag=0; /* 与主角相撞 */ putimage(x,y,fly_pic,0); } else { /* 如果被击中就闪烁地绘制飞机,然后消失 */ if ((Objs[i].index&3)==0) putimage(x,y,fly_pic,0); if (++Objs[i].index>50) Objs[i].mode=0; } y++; if (y>MAXY) Objs[i].mode=0; /* 移出屏幕就清除 */ break; /* 激光处理 */ case 2: y-=4; if (y<-20) Objs[i].mode=0; drawfire(x,y); for (j=0;j<MAX_OBJ;j++) /* 检查与敌机相碰 */ if (Objs[j].mode==1&&CheckHit(x,y,Objs[j].x,Objs[j].y,15)&& !Objs[j].index&&ok) /* 击中敌机 */ { Objs[j].index=1; ok=0; Objs[i].mode=0; sound(220); Sound=10; GameScore+=10; } break; } Objs[i].x=x; Objs[i].y=y; /* 更新坐标 */ } /* 主角控制 */ if (keys[KEY_UP]) if (--fly_y<0) fly_y=0; /* 如果上键被按下 */ if (keys[KEY_DOWN]) if (++fly_y>MAXY) fly_y=MAXY; /* 如果下键被按下 */ if (keys[KEY_LEFT]) if (--fly_x<0) fly_x=0; /* 如果左键被按下 */ if (keys[KEY_RIGHT]) if (++fly_x>319) fly_x=319; /* 如果右键被按下 */ if (keys[KEY_CTRL]&&fire_flag) { /* 如果按下CTRL */ i=AllocObj(); Objs[i].x=fly_x, Objs[i].y=fly_y-10, Objs[i].mode=2; fire_flag=0; } if (!keys[KEY_CTRL]) fire_flag=1; /* 如果放开CTRL */ putimage(fly_x,fly_y,fly_pic,1); /* 画出主角 */ if (!fly_flag) GameOver=1; /* 判断主角状态 */}/* 控制产生敌人的部分 */void drivers(void){ if (random(30)==0) { int i=AllocObj(); Objs[i].x=random(320); Objs[i].y=-random(20); Objs[i].mode=1; Objs[i].type=Objs[i].index=0; if (!random(20)) Objs[i].type=2; }}
