|
昨天看到百度之星的趣味賽的帖子,很多代碼挺有意思的,于是想在控制臺下寫個簡潔點的小游戲,想想還是寫個最經典的游戲------俄羅斯方塊 吧。睡覺的時候構思了下大致的情況,今天早起開始寫,遇到最大的困難是鍵盤按鍵的問題,本來想開一個線程,然后讓系統讀getch(),但是getch和ReadConsoleInput函數一樣,是同步的,沒有輸入的狀態下會阻塞,所以不能用于鍵盤響應,找來找去,最后找到了GetAsyncKeyState函數,這個函數是異步的,可以在程序進行的同時獲取鍵盤消息,而且簡單易用,GetAsyncKeyState(VK_UP)表示向上鍵被按下。按鍵的問題解決后基本就只有顯示的問題了,因為控制臺下的顯示效率很低,所以不能每次重繪整個游戲界面(system("cls")),這樣會閃屏,于是我采用的機制是:每次擦掉上一次運行的結果,然后重繪當前狀態,這樣最多擦掉某一個區域,不會導致整個控制臺的閃爍,并且利用SetConsoleCursorPosition來設置當前光標的位置,以及利用SetConsoleCursorInfo來將光標隱藏以求達到更加真實的游戲畫面。 可執行文件下載地址:http://code.google.com/p/sguzty/downloads/detail?name=Tertrix.exe&can=2&q=#makechanges 附兩張簡單截圖:


 /**//*
Author : 周天涯
email : menjitianya2007@163.com
blog : http://www.shnenglu.com/menjitianya/
Description : 即興創作,《C控制臺 俄羅斯方塊》,歡迎交流與探討,直接將代碼粘貼到VC6.0的環境下即可運行。

← 左移
→ 右移
↓ 加速
↑ 旋轉

連續消去1行得1分、2行得3分、3行得5分、4行得7分。
積分達到一定程度,會有換命的活動,命最多6條。
難度會隨積分的上升逐漸上升,最多到6的難度。
*/

#include <iostream>
#include <windows.h>
#include <vector>
#include <mmsystem.h>

#pragma comment(lib, "winmm.lib")
using namespace std;

#define GameW 10
#define GameH 20
const int CtrlLeft = GameW*2+4 + 3;

 struct Point {
 Point() {}
 Point(int x, int y) {_x = x, _y = y;}
int _x, _y;
};

HANDLE g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE g_hInput = GetStdHandle(STD_INPUT_HANDLE);

Point g_ptCursor(0,0);
BOOL isChecking = FALSE;
BOOL g_bGameOver = FALSE;
int g_nGameBack[GameH][GameW], Case;
int nowKeyInfo = -1;
int g_nDiff = 1;
int g_nLife = 2;
int g_nScore = 0;

 void SetCursor(COORD cd) {
SetConsoleCursorPosition(g_hOutput, cd);
}
 void SetCursor(int x, int y) {
 COORD cd = {x, y};
SetCursor(cd);
}
 void SetBlockCursor(int x, int y) {
 COORD cd = {2*x + 2, y + 1};
SetCursor(cd);
}

 void SetBack(int x, int y, BOOL bk) {
SetBlockCursor(x, y);
if (bk)
printf("%s", "■");
else
printf(" ");
}

 bool Out(int x, int y) {
return x < 0 || y < 0 || x >= GameW || y >= GameH;
}

 struct xBlock {
public:
int len;
int nowRotateID;
BOOL mask[4][4][4];
static vector <xBlock> List;

 xBlock() { len = 0; }
 xBlock(int l, char *str) {
int i, j, k;
len = l;
memset(mask, FALSE, sizeof(mask));
 for(i = 0; i < l; i++) {
 for(j = 0; j < l; j++) {
mask[0][i][j] = str[i*l + j] - '0';
}
}
 for(k = 1; k < 4; k++) {
 for(i = 0; i < len; i++) {
 for(j = 0; j < len; j++) {
mask[k][i][j] = mask[k-1][j][len-1-i];
}
}
}
nowRotateID = rand() % 4;
}

 void rotate() {
nowRotateID ++;
if (nowRotateID >= 4)
nowRotateID = 0;
}

 BOOL getUnit(int x, int y, int roID) {
 if (roID == -1) {
roID = nowRotateID;
}
return mask[roID][y][x];
}
};

vector <xBlock> xBlock::List;

 class Block {
public:
int x, y;
int ID;
xBlock bk;

 void reset(xBlock *pbk) {
bk = *pbk;

x = 4, y = 0;
ID = ++ Case;

 if(collide(0,0)) {
lifeDown();
}
draw();
*pbk = xBlock::List[rand() % xBlock::List.size()];
}
 void lifeDown() {
int i, j;
 for(i = 0; i < GameH; i++) {
 for(j = 0; j < GameW; j++) {
SetBack(j, i, TRUE);
Sleep(10);
}
}
 if(g_nLife) {
g_nLife --;
 for(i = g_nLife; i < 6; i++) {
SetCursor(CtrlLeft + i, 15);
printf("%c", ' ');
}
 for(i = GameH-1; i >= 0; i--) {
 for(j = GameW-1; j >= 0; j--) {
SetBack(j, i, FALSE);
Sleep(10);
g_nGameBack[i][j] = 0;
}
}
 }else {
g_bGameOver = TRUE;
}
}

 void erase() {
int i, j;
 for(i = 0; i < bk.len; i++) {
 for(j = 0; j < bk.len; j++) {
 if (bk.getUnit(j, i, -1)) {
 if(!Out(j+x, i+y) && g_nGameBack[i+y][j+x]) {
SetBack(j+x, i+y, FALSE);
g_nGameBack[i+y][j+x] = 0;
}
}
}
}
}
 void draw() {
int i, j;
 for(i = 0; i < bk.len; i++) {
 for(j = 0; j < bk.len; j++) {
 if (bk.getUnit(j, i, -1)) {
 if(!Out(j+x, i+y) && !g_nGameBack[i+y][j+x]) {
SetBack(j+x, i+y, TRUE);
g_nGameBack[i+y][j+x] = ID;
}
}
}
}
}
 void draw(int x, int y) {
int i, j;
 for(i = 0; i < 4; i++) {
 for(j = 0; j < 4; j++) {
SetCursor(x + 2*j, y + i);
 if (bk.getUnit(j, i, -1)) {
printf("%s", "■");
}else
printf(" ");
}
}
}
 bool collide(int dx, int dy, int roID = -1) {
int i, j;
 for(i = 0; i < bk.len; i++) {
 for(j = 0; j < bk.len; j++) {
 if (bk.getUnit(j, i, roID)) {
Point ptPos(j + x + dx, i + y + dy);
if(Out(ptPos._x, ptPos._y)
 || g_nGameBack[ptPos._y][ptPos._x] && ID != g_nGameBack[ptPos._y][ptPos._x]) {
return TRUE;
}
}
}
}
return FALSE;
}

 void rotate(int nTimes = 1) {
int nextro = (bk.nowRotateID + nTimes) % 4;
 if(collide(0, 0, nextro)) {
return ;
}
Beep(12000, 50);
erase();
bk.nowRotateID = nextro;
draw();
}

 BOOL changepos(int dx, int dy) {
 if(collide(dx, dy)) {
return FALSE;
}
erase();
x += dx;
y += dy;
draw();
return TRUE;
}
};

 void GameInit() {
CONSOLE_CURSOR_INFO cursor_info;
cursor_info.bVisible = FALSE;
cursor_info.dwSize = 100;
SetConsoleCursorInfo(g_hOutput, &cursor_info);
xBlock::List.push_back(xBlock(3, "010111000"));
xBlock::List.push_back(xBlock(3, "110110000"));
xBlock::List.push_back(xBlock(3, "111001000"));
xBlock::List.push_back(xBlock(3, "111100000"));
xBlock::List.push_back(xBlock(3, "110011000"));
xBlock::List.push_back(xBlock(3, "011110000"));
xBlock::List.push_back(xBlock(4, "1000100010001000"));
}

 void DrawFrame(int x, int y, int nWidth, int nHeight) {
int i;
 for(i = 0; i < nWidth; i++) {
SetCursor(x + 2*i + 2, y);
printf("%s", "一");
SetCursor(x + 2*i + 2, y + nHeight+1);
printf("%s", "┄");
}
 for(i = 0; i < nHeight; i++) {
SetCursor(x, y + i + 1);
printf("%s", "┆");
SetCursor(x + nWidth*2+2, y + i + 1);
printf("%s", "┆");
}
SetCursor(x, y);
printf("%s", "┌");
SetCursor(x, y + nHeight+1);
printf("%s", "└");
SetCursor(x + nWidth*2+2, y);
printf("%s", "┐");
SetCursor(x + nWidth*2+2, y + nHeight+1);
printf("%s", "┘");
}

 void MissionInit() {
memset(g_nGameBack, FALSE, sizeof(g_nGameBack));
Case = 1;
int i;
DrawFrame(0, 0, GameW, GameH);
DrawFrame(GameW*2+4, 0, 4, GameH);
SetCursor(CtrlLeft, 2);
printf("Next");
SetCursor(CtrlLeft, 8);
printf("Speed");
 for(i = 0; i < g_nDiff; i++) {
SetCursor(CtrlLeft + i, 9);
printf("%c", 1);
}

SetCursor(CtrlLeft, 11);
printf("Score");
SetCursor(CtrlLeft, 12);
printf("%d", g_nScore);

SetCursor(CtrlLeft, 14);
printf("Life");
 for(i = 0; i < g_nLife; i++) {
SetCursor(CtrlLeft + i, 15);
printf("%c", 3);
}

SetCursor(CtrlLeft-1, 19);
printf("by Zty");
SetCursor(CtrlLeft-1, 20);
printf("Baidu A*");
}

 void Check() {
isChecking = TRUE;
int i, j, k;
vector <int> line;
 for(i = 0; i < GameH; i++) {
 for(j = 0; j < GameW; j++) {
if(!g_nGameBack[i][j])
break;
}
 if(j == GameW) {
line.push_back(i);
}
}
 if(line.size()) {
int nCount = 7;
 while(nCount --) {
 for(i = 0; i < line.size(); i++) {
 for(j = 0; j < GameW; j++) {
SetBack(j, line[i], nCount&1);
}
}
Sleep(70);
}
 for(i = 0; i < line.size(); i++) {
 for(j = 0; j < GameW; j++) {
g_nGameBack[line[i]][j] = 0;
}
}

 for(i = 0; i < GameW; i++) {
int next = GameH-1;
 for(j = GameH-1; j >= 0; j--) {
 for(k = next; k >= 0; k--) {
if(g_nGameBack[k][i])
break;
}
next = k - 1;
BOOL is = (k >= 0);
SetBack(i, j, is);
g_nGameBack[j][i] = is;
}
}

g_nScore += 2*line.size()-1;
SetCursor(CtrlLeft, 12);
printf("%d", g_nScore);

 if( g_nScore >= g_nDiff * g_nDiff * 10) {
if(g_nDiff <= 6)
g_nDiff ++;
}
 if( g_nScore >= 50 * (g_nLife+1)) {
if(g_nLife <= 6)
g_nLife ++;
}
}

isChecking = FALSE;
}
 int main() {
Block* obj = new Block();
Block* buf = new Block();

BOOL bCreateNew = FALSE;
int nTimer = GetTickCount();
int LastKeyDownTime = GetTickCount();


GameInit();
MissionInit();
buf->bk = xBlock::List[rand() % xBlock::List.size()];
 while(1) {
 if(!bCreateNew) {
bCreateNew = TRUE;
obj->reset(&buf->bk);
if(g_bGameOver)
break;
buf->draw(CtrlLeft - 1, 4);
}
 if (GetTickCount() - nTimer >= 1000 / g_nDiff) {
nTimer = GetTickCount();
if (!obj->collide(0, 1))
obj->changepos(0, 1);
 else {
Check();
bCreateNew = FALSE;
}
}
 if (GetTickCount() - LastKeyDownTime >= 100) {
 if(FALSE == isChecking) {
LastKeyDownTime = GetTickCount();
 if (GetAsyncKeyState(VK_UP)) {
obj->rotate();
}
 if (GetAsyncKeyState(VK_LEFT)) {
obj->changepos(-1, 0);
}
 if (GetAsyncKeyState(VK_RIGHT)) {
obj->changepos(1, 0);
}
 if (GetAsyncKeyState(VK_DOWN)) {
if( FALSE == obj->changepos(0, 2) )
obj->changepos(0, 1);
}
}
}
}
SetCursor(8, 10);
printf("Game Over");

SetCursor(0, GameH+3);
printf("按ESC鍵退出游戲 ");

 while(1) {
if (GetAsyncKeyState(VK_ESCAPE))
break;
}
return 0;
}

|