當前位置:編程學習大全網 - 源碼下載 - (100分)策劃 俄羅斯方塊 完整的遊戲過程 (可以的話還加50分,謝謝)

(100分)策劃 俄羅斯方塊 完整的遊戲過程 (可以的話還加50分,謝謝)

網上下載的CSDN裏面的代碼^^^

分享…………

我用TC調試成功了

因為頭文件裏有<graphics.h> 所以不能在VC中運行

#include <stdio.h>

#include <stdlib.h>

#include <bios.h> /*這裏須要讀取系統運行時間來作為定時器*/

#include <graphics.h> /*很不幸,TC2的簡單圖形,讓我放棄了用*/

#include <conio.h> /*win32+openGL來講解.*/

#define MAX_X 14 /*可見最大X*/

#define MAX_Y 21 /*可見最大Y*/

/*我們定義了最大的可見X和Y,那麽即還有不

可見的部分,事實上地圖(大盒子)裏的左右

兩側和底部各兩行都被1填充,這樣大大簡化

出界的判斷,事實上,在本例中沒有這樣的

代碼,因為旁邊有壹圈1阻止小盒子越出大

盒子的按制範圍

*/

#define MAX_C 7 /*最大種類,這個無須解釋*/

#define KEY_UP 'w' /*定義上下左右按按鍵*/

#define KEY_DOWN 's'

#define KEY_LEFT 'a'

#define KEY_RIGHT 'd'

#define KEY_ESC 27 /*退出*/

typedef int BOOL;

#define FALSE 0

#define TRUE 1 /*這幾個TC中沒有...自己定義壹下吧:)*/

/*時鐘結構*/

typedef struct { /*時鐘結構*/

BOOL enabled; /*時鐘是否開啟*/

unsigned int intervel; /*定時間隔*/

unsigned int lasttime; /*這個屬於內部使用變量*/

} Timer;

/*

*現在進入了編程的初質階段

*在開始處我會寫出所有的函數原形,以及它們的作用

*main函數在程序的最後,妳可以在這裏看到整個遊戲的組織架構

*很好,它只有幾十行,並且非常容易理解,當然,還是先看壹下函數原形

*及解釋

*/

/******************************************************\

* 函數原形及說明 *

\******************************************************/

/*以下三個函數可以參照Timer結構體.在函數聲明後面*/

int GetTickCount(); /*返回電腦或操作系統運行逝去的時間*/

/*在win32環境下已包含在windows.h裏邊,返回的是4byte*/

/*在DOS(本代碼)環境下,要自己編寫,使用到BIOS.h內容*/

int setTimer(Timer *t, unsigned int intv, BOOL en);

/*設置時鐘t,參數分別為時鐘指針,時間間隔,是否活動*/

/*時間間隔,win32下為毫秒,DOS下為1/18秒(有點低)*/

BOOL testTimer(Timer *t); /*測試時鐘t是否到達定時時間*/

/*如下面這段代碼:*/

/*

setTimer(&t, 1, 1); 設置1個單位的間隔

while(1) {

if(testTimer(&t)) printf("Active!\n");

}

將會定時(1個單位)在屏幕上打印Active!

壹般來說testTimer必須放在循環中反復執行,激活時返回1

*/

void render(void); /*唯壹的繪圖函數*/

/*註意,此函數重畫整個地圖,根據地圖中的點陣,以及根據

小盒在地圖的中坐標在恰當位置畫出小盒子*/

/*DOS的圖形當然是很低的,但,全屏繪圖在這裏還是過得去

的,我用的是雙緩沖,交換繪圖,這樣感覺好點*/

void initMap(void); /*初始化地圖(大盒子)*/

/*之前提到過在這個兩維數組中有壹圈為1的東西來阻止

小盒子出界,這就是生成這壹圈的函數*/

void newGame(); /*新建壹個遊戲*/

/*這個函數初始化壹幾個時鐘和建造第壹個下落的小盒子*/

/*當然建造完後要生成壹個個的預覽*/

void rotateBox(int box1[5][5], int box2[5][5]);

/*核心函數成員,把box1逆時針旋轉90度,並保存到box2中*/

void rebuidNext();

/*核心函數成員,生成下壹個方塊*/

int drop();

/*核心函數成員,將下落的盒子向下移(實際上增加下落盒

子的Y值而已,當然要判斷是否與地圖點陣重疊*/

/*與地圖重疊,無法完成下落操作,返回0*/

void putBox();

/*在這之上,下落的盒子與地圖之前是獨立的兩個兩維數*/

/*當下落失敗後,小盒子要回到頂端再次重新執行下落,這*/

/*時原來的盒子內容當然就要變成地圖上的內容了,putBox

就是將下落盒子的內容根據XY寫到地圖上*/

void clear();

/*這個函數在下落失敗並putBox後執行,掃描整個地圖*/

/*清除掉滿行的點陣,具體細節在函數內講*/

int move(int dir);

/*左右移動下落盒子,dir指出向左還是向右,這個與drop

是壹樣的*/

int test(int mx, int my, int box[5][5]);

/*這個比較重點,判斷box在mx,my為坐標上,與地圖上的

非空點陣是否有重疊.很通用的壹個函數*/

int rotate();

/*旋轉下落的盒子,當然如果轉了之後與地圖有沖突,會

取消轉動,返回0,但返回的值好像沒什麽用~*/

int newfall();

/*創建下落元素,把"下壹個"預覽的內容復制到下落盒子*/

/*並將下落的盒子移動到地圖頂部,當然這個過程,如果頂

部有沖突,會返回0,這時說明已經滿了...gameOver*/

/******************************************************\

* 變量區 *

\******************************************************/

/*在上面的說明中,可能會有壹些蒙,因為可能對所用到的實際變量沒

*有了解

*/

int map[MAX_Y+4][MAX_X+4]; /*地圖\大盒子...MAX_X,Y是可見面積*/

/*我已說過需要在外面布兩圈"衛兵"*/

int curbox[5][5]; /*當前下落的盒子*/

int curx, cury; /*保存著當前活動盒子在地圖上的位置*/

int nextbox[5][5]; /*保存著下壹個形狀的盒子*/

/*以上就是這麽幾個盒子和坐標了*/

/*這裏列出了標準七種俄羅斯方塊圖形點陣,用到時它們會被復制到相*/

/*應的盒子...:)*/

int box[MAX_C][5][5] = { /*MAX_C(7)種預定義的盒子*/

{

{0,0,0,0,0},

{0,0,0,0,0},

{1,1,1,1,0},

{0,0,0,0,0},

{0,0,0,0,0}

},

{

{0,0,0,0,0},

{0,0,1,0,0},

{0,1,1,1,0},

{0,0,0,0,0},

{0,0,0,0,0}

},

{

{0,0,0,0,0},

{0,1,1,0,0},

{0,0,1,1,0},

{0,0,0,0,0},

{0,0,0,0,0}

},

{

{0,0,0,0,0},

{0,0,1,1,0},

{0,1,1,0,0},

{0,0,0,0,0},

{0,0,0,0,0}

},

{

{0,0,0,0,0},

{0,1,1,0,0},

{0,0,1,0,0},

{0,0,1,0,0},

{0,0,0,0,0}

},

{

{0,0,0,0,0},

{0,0,1,1,0},

{0,0,1,0,0},

{0,0,1,0,0},

{0,0,0,0,0}

},

{

{0,0,0,0,0},

{0,0,1,1,0},

{0,0,1,1,0},

{0,0,0,0,0},

{0,0,0,0,0}

}

};

/******************************************************\

* 時鐘 *

\******************************************************/

/*時鐘部分也非常理解的,壹個用到設置時鐘,壹個用來測試時鐘激活態*/

Timer tDown; /*正常下落定時時鐘intervel會比較大*/

Timer tFast; /*按KEY_DOWN時使用的快速下落*/

int speed = 13; /*控制下落時間間隔*/

#define FAST_INTV 1 /*快時鐘的間隔*/

/******************************************************\

* 時鐘 *

\******************************************************/

/*時鐘部分也非常理解的,壹個用到設置時鐘,壹個用來測試時鐘激活態*/

Timer tDown; /*正常下落定時時鐘intervel會比較大*/

Timer tFast; /*按KEY_DOWN時使用的快速下落*/

#define FAST_INTV 1 /*快時鐘的間隔*/

int GetTickCount() { /*讀取BIOS時鐘*/

int ret;

ret = peek(0x0,0x46e); /*實際上讀取了內存0:046e處的內容*/

ret <<= 8; /*這個地方是$%#$^$%&^*/

ret += peek(0x0,0x46c); /*太多新的東西了,找點書看壹看吧*/

return (ret);

}

int setTimer(Timer *t, unsigned int intv, BOOL en) {

t -> enabled = en; /*設置壹個時鐘羅*/

t -> intervel = intv;

t -> lasttime = GetTickCount(); /*lasttime記錄的是上壹個*/

/*tickcount返回的東西*/

/*這樣當再壹次測試時間時新的tickcount產生了

它來減去上壹次的tickcount就得出了壹個時間

間隔,這個就可以和intervel比較從而得出是否

激活了

*/

return 0;

}

BOOL testTimer(Timer *t) { /*在上面6行的地方解釋了:)*/

unsigned int tmp, dt;

if (!(t -> enabled)) return FALSE;

tmp = GetTickCount();

dt = tmp - (t -> lasttime);

if(dt >= t -> intervel) {

t -> lasttime = tmp;

return TRUE;

}

return FALSE;

}

/******************************************************\

* 渲染部分 *

\******************************************************/

/*提供render更新整個屏幕*/

/*關於這個函數,要說的東西還是比較多,為了追求漂亮和編譯*/

/*時的靈活性,這個函數被寫得頗為冗長...*/

/*現在寫壹下本遊戲圖形的東西...使用TC2的Graphics我也不

太樂意,畢竟它本身已過時,但鑒於實在簡單實用,它用來教學

再合適不過,這也是教學總是用TC的原因,老師們不喜歡讓學

生問壹些讓他們掌握起來也困難的東西...*/

/*這裏我使用了VGAMED模式,而不是 VGAHI,因為VGAMED有兩個

頁(可以想像成緩沖),這樣可以用來做到不閃動畫.即:在後臺

頁繪制圖形,完成後再顯示出來.

這裏用到了兩個函數:

setactivepage(1 | 0) 參數只能是1或0,選擇繪圖頁,例如選

擇了1後,以後所有的繪圖動作將畫到頁1上.

setvisualpage(1 | 0) 這個叫做選擇可見頁,即選擇在屏幕上

顯示頁面1還是0

*/

void render(void) {

int x, y;

static int cPage = 0; /*當前頁,換頁用*/

#define STARTX 50 /*定義幾個常量*/

#define STARTY 0

#define LEN 18

setactivepage(cPage=(cPage == 0?1:0)); /*選擇頁*/

cleardevice(); /*清屏*/

setcolor(15);

rectangle( STARTX + LEN * 2 - 2,

STARTY + LEN * 3 - 2,

STARTX + LEN * (MAX_X - 2) + 2,

STARTY + LEN * (MAX_Y - 2) + 2);

/*用白色畫壹個外框*/

setfillstyle(SOLID_FILL, 5);

for(y = 3; y < MAX_Y - 2; y++) { /*畫地圖 */

for(x = 2; x < MAX_X - 2; x++) {

if(map[y][x]) {

rectangle( x * LEN + STARTX,

y * LEN + STARTY,

x * LEN + STARTX + LEN,

y * LEN + STARTY + LEN);

bar( x * LEN + STARTX + 1,

y * LEN + STARTY + 1,

x * LEN + STARTX + LEN - 2,

y * LEN + STARTY + LEN - 2);

}

}

}

/*繪圖操作就不要作太復雜的介紹了,這只寫作用*/

/*以上段,根據地圖上的點陣情況將地圖反映到屏幕上*/

for(y = 0; y < 5; y++) { /*畫下落物*/

for(x = 0; x < 5; x++) {

if(curbox[y][x]) {

if(y + cury > 2) {

rectangle( (x + curx) * LEN + STARTX,

(y + cury) * LEN + STARTY,

(x + curx) * LEN + STARTX + LEN,

(y + cury) * LEN + STARTY + LEN);

bar( (x + curx) * LEN + STARTX + 1,

(y + cury) * LEN + STARTY + 1,

(x + curx) * LEN + STARTX + LEN - 2,

(y + cury) * LEN + STARTY + LEN - 2);

}

}

}

}

/*以上將下落的盒子按昭它在地圖上的坐標,畫到對應的區域裏*/

for(y = 0; y < 5; y++) { /*畫下壹個*/

for(x = 0; x < 5; x++) {

if(nextbox[y][x]) {

rectangle( x * LEN + 320,

y * LEN + 10,

x * LEN + 338,

y * LEN + 28);

bar( x * LEN + 321,

y * LEN + 11,

x * LEN + 336,

y * LEN + 26);

}

}

}

/*這個畫出下壹個盒子的預覽*/

setvisualpage(cPage); /*確認在cPage頁裏畫好了*/

/*將它顯示出來*/

}

/******************************************************\

* 初始化部分 *

\******************************************************/

/*提供newGame()初始化新遊戲*/

void initMap(void) { /*初始化地圖*/

int x, y; /*我們須要壹圈衛兵...*/

for(y = 0; y < MAX_Y; y++) {

for(x = 0; x < MAX_X; x++) {

if(x < 2 || x > MAX_X - 3 || y > MAX_Y - 3)

map[y][x] = 1;

else map[y][x] = 0;

}

} /*這裏初始化出這個形狀*/

} /*當然是無蓋的...*/

void newGame() { /*新建遊戲*/

int x, y;

initMap(); /*初始化地圖*/

srand(GetTickCount()); /*初始化隨機發生器*/

rebuidNext(); /*建立下壹個*/

setTimer(&tDown, speed, 1); /*啟動時鐘(快慢兩個)*/

setTimer(&tFast, FAST_INTV, 1);

newfall(); /*對下落的盒子操作壹下*/

/*這樣第壹個下落的方塊

就在地圖頂部準備好了*/

}

/******************************************************\

* 核心函數 *

\******************************************************/

void rotateBox(int box1[5][5], int box2[5][5]) {

/*旋轉box1輸出到box2*/

int x, y;

for(x = 0; x < 5; x++) /*這個函數可以須要實際*/

for(y = 4; y >= 0; y--) /*編寫壹下才能印像深刻*/

box2[y][x] = box1[x][4 - y];

}

void rebuidNext() { /*新建下壹個形狀並放到nextbox中*/

int i, x, y;

i = random(MAX_C); /*從幾種方塊裏面選壹種*/

for(y = 0; y < 5; y++) /*並復制過來*/

for(x = 0; x < 5; x++)

nextbox[y][x] = box[i][y][x]; /*復制*/

}

int drop() { /*下落,返回成功與否*/

int newy; /*盒子要下落的新位置*/

newy = cury + 1; /*為當前Y位置+1*/

if(test(curx, newy, curbox)) {

cury = newy; /*測試下落盒在這個位置*/

return 1; /*上是否有沖突,沒有的話*/

} /*直接設置cury*/

return 0;

}

void putBox() { /*將curbox填充到地圖上*/

int x, y;

for(y = 0; y < 5; y++) /*這個也簡單,主要是要根*/

for(x = 0; x < 5; x++) /*據curx,cury指出位置 */

if(curbox[y][x])

map[y + cury][x + curx] = curbox[y][x];

}

void clear() { /*清除掉滿行*/

/*這個函數實際上效率也很低的,為了簡便

它從頭到尾作了測試*/

/*具體的算法為:

從第0行開始到最後壹行,測試地圖點陣是否為滿,如果是的話

從當前行算起,之上的地圖向下掉壹行*/

int x, y;

int dx, dy;

int fullflag;

for(y = 0; y < MAX_Y - 2; y++) { /*最後兩行保留行*/

fullflag = 1; /*假設為滿*/

for(x = 2; x < MAX_X - 2; x++) { /*保留列~*/

if(!map[y][x]) {

fullflag = 0;

break;

}

}

if(fullflag) { /*向下移動壹行*/

for(dy = y; dy > 0; dy--)

for(dx = 2; dx < MAX_X - 2; dx++)

map[dy][dx] = map[dy - 1][dx];

for(dx = 2; dx < MAX_X - 2; dx++)

map[0][dx] = 0;

/*並清除掉第壹行*/

}

}

}

int move(int dir) { /*返回成功與否*/

int newx;

if(dir) newx = curx + 1;

/*與drop壹樣,準備移動後的坐標*/

else newx = curx - 1;

if(test(newx, cury, curbox)) { /*測試是否沖突*/

curx = newx; /*可以的話切換curx*/

return 1;

}

return 0;

}

int test(int mx, int my, int box[5][5]) {

/*測試box在map裏mx,my位置上是否能著陸*/

/*這個是最關鍵的壹個函數,它判斷是否產生非空沖突*/

/*但算法還是很簡單的*/

int x, y;

for(y = 0; y < 5; y++)

for(x = 0; x < 5; x++)

if(map[y + my][x + mx] && box[y][x])

return 0;

return 1;

}

int rotate() {

int x, y;

int newbox[5][5]; /*我們必須將當前盒子轉動到新的盒子*/

/*再對這個新的盒子的沖突作測試*/

rotateBox(curbox, newbox); /*轉動到新的盒子*/

if(test(curx, cury, newbox)) {

/*並且新的盒子能放到地圖上而不沖突*/

for(y = 0; y < 5; y++)

for(x = 0; x < 5; x++)

curbox[y][x] = newbox[y][x]; /*復制進來*/

return 1;

}

else return 0;

}

int newfall() { /*創建下落元素失敗返回0*/

int x, y;

curx = MAX_X / 2 - 2; /*重新指定小盒位置*/

cury = 0;

for(y = 0; y < 5; y++)

for(x = 0; x < 5; x++)

curbox[y][x] = nextbox[y][x];/*將nextBox復制過來*/

rebuidNext(); /*重建nextBox*/

return test(curx, cury, curbox);

}

/************************************************************\

* 主函數 -- 整個遊戲架構 *

\************************************************************/

int main() {

char key; /*記錄當前按鍵*/

int i;

int gd = VGA, gm = VGAMED; /*初始化的圖形模式*/

Timer *ptDown; /*下落所指向的時鐘(有快慢)*/

Timer trender; /*為了避免渲染給程序造成過大的負擔*/

/*用壹個時鐘來控制渲染速度*/

/*把它設置interval = 1,*/

/*這樣就是18 FPS了,當然無法達到標*/

/*準的60 FPS...畢竟這是DOS...*/

setTimer(&trender, 1, 1);

initgraph(&gd, &gm, ""); /*初始化圖形*/

newGame(); /*新遊戲...*/

while(1) { /*主遊戲循環*/

if(kbhit()) { /*如果鍵盤有按下*/

key = getch(); /*讀取壹個按鍵值到key*/

}

else key = 0;

switch(key) { /*對讀到的key進行判斷*/

case KEY_UP:

rotate(); /*上,旋轉下落盒子*/

break;

case KEY_DOWN:

ptDown = &tFast; /*使用tFast時鐘 */

break;

case KEY_LEFT:

move(0); /*左移*/

break;

case KEY_RIGHT:

move(1); /*右移*/

break;

case KEY_ESC:

closegraph(); /*結束遊戲*/

exit(0);

default:

ptDown = &tDown; /*使用原來速度 */

}

if(testTimer(ptDown)) { /*在上面已設置了下落要

使用的時鐘在ptDown裏*/

if(!drop()) { /*下落,失敗返回0*/

putBox(); /*寫到地圖裏*/

clear(); /*清除滿行*/

if(!newfall()) { /*新建下落,失敗則遊戲結束*/

closegraph();

exit(0);

}

}

}

if(testTimer(&trender)) /*最後...渲染...*/

render();

}

}

  • 上一篇:微信公眾平臺裏的廣告主與流量主是什麽意思
  • 下一篇:國家政務服務平臺
  • copyright 2024編程學習大全網