我罗斯方块最终版
我罗斯方块最终篇
这个作业属于哪里 | 2020面向对象程序设计 |
---|---|
这个作业在哪里 | 我罗斯方块最终篇 |
这个作业目标 | 完成我罗斯方块并最终展示,项目发送至GitHub |
作业正文 | 我罗斯方块最终版 |
GitHub地址 | |
参考资料 | windows程序设计第五版 |
【国际歌×C++】编程写国际歌 建议改成:全世界的程序员联合起来! | |
C++俄罗斯方块 | |
小组成员学号 | 小组成员姓名 |
031902332 | 周浩东 |
031902302 | 蔡树峰 |
031902320 | 李霆政 |
一、游戏界面(周浩东)
1.本次项目使用windows渲染,所完成的实物与预期项目设计图高度符合,采用了经典怀旧风格搭配简单朴实的游戏界面以及具有年代感的背景音乐,具体项目实例截图如下:
(1).开始游戏界面
(2).进行游戏界面
(3).暂停游戏界面
(4).游戏结束界面
2.在游戏界面中实现的预期功能
预期功能
void GameStart(); //控制游戏开始
void Init(); //初始化游戏,重置设置
void Quit(); //退出游戏
void NewGame();//开始新游戏;
void GameOver();//结束游戏
void StopGame();//暂停游戏
void paintEvent();//绘制界面,加载背景、音频
void keypressEvent();//键盘事件处理
结果功能
void GameStart(); //控制游戏开始
void Init(); //初始化游戏,重置设置
void Quit(); //退出游戏
void NewGame();//开始新游戏;
void GameOver();//结束游戏
void StopGame();//暂停游戏
void paintEvent();//绘制界面,加载背景
void keypressEvent();//键盘事件处理
void song();//背景音乐
//大多都已经以原函数名实现,少部分以其他形式实现,总体全部实现
3.代码要点
//画线函数
MoveToEx();
LineTo();
//文字函数
wsprintf();
TextOut();
//创建画刷
HBRUSH hBrush=(HBRUSH)CreateSolidBrush();
//创建文字
HFONT hFont = CreateFont();
//矩形函数
HRGN hRgn = CreateRectRgn();
FrameRgn();//绘画矩形框
FillRgn();//填充矩形
//计时器函数
SetTimer();//创建计时器
KillTimer();//销毁计时器
//创造线程函数
VOID Thread(PVOID pvoid);//自定义线程
_beginthread();//开启线程
//停止游戏函数
exit(0);
//还有其他辅助设计的数据类型
大量使用的功能函数样例代码
//大量使用画线、文字函数
void GameStart(HDC hdc)
{
HRGN hRgn;
HFONT hFont,hFontOld;
COLORREF crColor=RGB(103,120,104);
HBRUSH hBrush=(HBRUSH)CreateSolidBrush(crColor),hBrush2=(HBRUSH)CreateSolidBrush(RGB(0,0,0));
hRgn = CreateRectRgn(155,150,450,500);
FillRgn(hdc,hRgn,hBrush);
MoveToEx(hdc,150,150,NULL);
DeleteObject(hRgn);
hRgn = CreateRectRgn(455,150,750,500);
FillRgn(hdc,hRgn,hBrush);
DeleteObject(hRgn);
MoveToEx(hdc,150,150,NULL);
LineTo(hdc,750,150);
MoveToEx(hdc,150,500,NULL);
LineTo(hdc,750,500);
hFont = CreateFont(35,15,0,0,FW_REGULAR,FALSE,FALSE,FALSE,GB2312_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,PROOF_QUALITY,FIXED_PITCH | FF_MODERN, "华文新魏");
hFontOld = (HFONT)SelectObject(hdc,hFont);
TextOut(hdc,375,160,"操作",4);
TextOut(hdc,460,160,"说明",4);
TextOut(hdc,160,200,"玩家一:",8);
TextOut(hdc,200,260,"W:旋转",8);
TextOut(hdc,200,310,"S:加速",8);
TextOut(hdc,200,360,"A:向左",8);
TextOut(hdc,200,410,"D:向右",8);
TextOut(hdc,200,460,"空格开始游戏",12);
TextOut(hdc,460,200,"玩家二:",8);
TextOut(hdc,520,260,"↑:旋转",8);
TextOut(hdc,520,310,"↓:加速",8);
TextOut(hdc,520,360,"←:向左",8);
TextOut(hdc,520,410,"→:向右",8);
TextOut(hdc,500,460,"回车键暂停游戏",14);
hRgn = CreateRectRgn(225,550,375,600);
FrameRgn(hdc,hRgn,hBrush2,5,5);
DeleteObject(hRgn);
hRgn = CreateRectRgn(220,545,370,595);
FillRgn(hdc,hRgn,hBrush);
DeleteObject(hRgn);
TextOut(hdc,225,550,"开始游戏",8);
hRgn = CreateRectRgn(520,550,670,600);
FrameRgn(hdc,hRgn,hBrush2,5,5);
DeleteObject(hRgn);
hRgn = CreateRectRgn(515,545,665,595);
FillRgn(hdc,hRgn,hBrush);
DeleteObject(hRgn);
TextOut(hdc,520,550,"退出游戏",8);
DeleteObject(hBrush);
DeleteObject(hBrush2);
DeleteObject(hFont);
DeleteObject(hFontOld);
}
//大量使用矩形以及画刷函数
void CreateR1(HDC hdc)
{
HRGN hRgn,hRgn1;
COLORREF crcolor;
int x=2,y=202;
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
if(x>=150) break;
if(board->block[0]->BLOCKS[board->block[0]->nextType][i][j]=='#') crcolor=RGB(94,108,94);
else if(board->block[0]->BLOCKS[board->block[0]->nextType][i][j]=='1') crcolor=RGB(0,0,0);
HBRUSH hBrush=(HBRUSH)CreateSolidBrush(crcolor);
hRgn = CreateRectRgn(x,y+35,x+35,y);
hRgn1 = CreateRectRgn(x+10,y+25,x+25,y+10);
FrameRgn(hdc,hRgn,hBrush,5,5);
FillRgn(hdc,hRgn1,hBrush);
x=x+37.5;
DeleteObject(hRgn);
DeleteObject(hRgn1);
DeleteObject(hBrush);
}
x=2;y=y+37.5;
}
}
void CreateR2(HDC hdc)
{
HRGN hRgn,hRgn1;
COLORREF crcolor;
int x=2+750,y=202;
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
if(x>=900) break;
if(board->block[1]->BLOCKS[board->block[1]->nextType][i][j]=='#') crcolor=RGB(94,108,94);
else if(board->block[1]->BLOCKS[board->block[1]->nextType][i][j]=='1') crcolor=RGB(0,0,0);
HBRUSH hBrush=(HBRUSH)CreateSolidBrush(crcolor);
hRgn = CreateRectRgn(x,y+35,x+35,y);
hRgn1 = CreateRectRgn(x+10,y+25,x+25,y+10);
FrameRgn(hdc,hRgn,hBrush,5,5);
FillRgn(hdc,hRgn1,hBrush);
x=x+37.5;
DeleteObject(hRgn);
DeleteObject(hRgn1);
DeleteObject(hBrush);
}
x=2+750;y=y+37.5;
}
}
4.存在的问题
本次学习的Windows程序设计是网上找的旧版书籍,没有很好的与时俱进结合新型技术。
5.游戏界面收获与心得
1.学习Windows并运用实践是本次的最大收获,能绘图以及建立键盘操作,找到了开发游戏的技术支持
2.寻找最新的知识,更新换代,这次的方法有点点繁琐
3.继续学习,搞一搞其他的功能
二、Block类(李霆政)
class Block
{
public:
char BLOCKS[21][5][5];//各个类型的方块
int NEXT[20];//模拟指向用于方块旋转
int x;//块重心起始坐标
int y;
int type;//块id
int nextType;//下一个块id
Board* board;
Block(Board*);//构造函数
void toNext();//更改块id
void moveUp();//变形
void moveRight();//加速
void moveLeft();//左移
void moveDown();//右移
bool detect(int,Player );//碰撞检测,int 为操作类型,Player 确定对map1或map2进行操作
};
定义字符数组直接画出各个类型的方块,便于后续生成与变形。
1.Block构造函数
用4*4的矩阵BLOCKS[21][5][5],存按变形的顺序,有序存储20种方块。x,y存方块矩阵左上角在map上的坐标,用于定位
例如:
#1## 1111
#1## ####
#1## ####
#1## #### //代表两种典型的方块。
其中
//NEXT数组模拟指向,用于方块旋转功能
for (int i = 0; i< 5; i++)
{
for (int j = 0; j < 3; j++)
NEXT[i * 4 + j] = i * 4 + j + 1;
NEXT[i * 4 + 3] = i * 4;
}//变化规律
//改变块的ID,并且随机更新下个块的类型
srand((unsigned)time(NULL));
type = rand() % 19;
nextType = rand() % 19;
if(type==nextType) nextType = rand() % 19;
2.碰撞检测函数bool detect(int,Player )
这是block类的核心代码,flag确定变形移动等操作,Player确定对哪张map进行检测。发生碰撞函数返回false。
bool Block::detect(int flag, Player player)
{
//在执行flag对应的操作前先在map图上将block消去
int nextX, nextY, nextType;
for (int i = 0; i< 4; i++)
for (int j = 0; j < 4; j++)
{
int tx = x + i;
int ty = y + j;
if (tx< 0 || tx> board->height + 1 || ty < 0 || ty > board->width + 1)
continue;
if (BLOCKS[type][i][j] != '#' && this->board->map[player.num][tx][ty] != '#')
this->board->map[player.num][tx][ty] = '#';
}
//确定操作类型,并检测是否碰撞
switch (flag)
{
case 0: nextX = x; nextY = y; nextType = NEXT[type]; break;
case 1: nextX = x + 1; nextY = y; nextType = type; break;
case 2: nextX = x; nextY = y - 1; nextType = type; break;
case 3: nextX = x; nextY = y + 1; nextType = type; break;
}
for (int i = 0; i< 4; i++)
for (int j = 0; j < 4; j++)
{
int tx = nextX + i;
int ty = nextY + j;
if (tx< 0 || tx> board->height + 1 || ty < 0 || ty > board->width + 1)
continue;
if (BLOCKS[nextType][i][j] != '#' && (ty == 0 || ty == board->width + 1))
return false;
if (BLOCKS[nextType][i][j] != '#' && this->board->map[player.num][tx][ty] != '#')
return false;
}
return true;
}
3.moveUP、moveRight、moveLetf、moveDown等操作函数
void Block::toNext()
{
x = 1;
y = 4;
type = nextType;
nextType = rand() % 19;
}//要落下新的block,block坐标重置,类型重置。
//旋转
void Block::moveUp()
{
type = NEXT[type];
}//利用NEXT变换规则变换block类型
//加速向下
void Block::moveDown()
{
x++;
}
//左移
void Block::moveLeft()
{
y--;
}//block在map上的定位坐标改变
三、Board类(蔡树峰)
class Board{
public:
int score_player1,score_player2;//玩家分数
int height,width;//地图长宽
int time;//下落间隔时间
Block *block[2];//下落的方块,两张图各自的方块
char map[2][50][50];//两张矩阵图保存地图信息,方块位置
Board();//地图初始化
void confirm(Player player);//方块下落后地图更新
bool is_end(Player player);//判断游戏是否结束
};
1.地图矩阵的构造Board()
构造出两个类似下图的矩阵,其中有023行,011列.其中要在游戏显示的为122行,110列。上下各多出一行便于此消彼长操作,左右各多出一列用于边界碰撞的判定。
1############1
1############1
1############1
…………………………….
1############1
1111111111111111
//初始化地图
for(int i=0; i<=height+1 ; i++)
for(int j=0; j<=width+1 ; j++){
map[0][i][j] = '#';
map[1][i][j] = '#';
}
for(int i=0; i<=height+1; i++){
map[0][i][0] = map[0][i][width+1] = '1';
map[1][i][0] = map[1][i][width+1] = '1';
}
for(int i=1 ; i<=width ; i++){
map[0][height+1][i] = '1';
map[1][height+1][i] = '1';
}
}//外围多一圈,便于消行时操作执行 1为有方块,#为空
2.地图更新函数void confirm(Player player)
这是Board类的核心代码,根据player.num对应的地图进行更新
void Board::confirm(Player player){
//将block更新到map
for(int i=0; i<4; i++)
for(int j=0; j<4; j++){
int tx = block[player.num]->x + i;//对应的方块在map图上的x坐标
int ty = block[player.num]->y + j;//对应的方块在map图上的x坐标
if(tx<1 || tx> height || ty < 1 || ty > width)
continue;
if(block[player.num]->BLOCKS[block[player.num]->type][i][j] != '#')
map[player.num][tx][ty] = block[player.num]->BLOCKS[block[player.num]->type][i][j];
}
//消去完整的行并计算行个数
int cnt = 0;
for(int i=height; i>=1; i--)
{
bool flag = false;
for(int j=1; j<=width; j++)
if(map[player.num][i][j] == '#')
{
flag = true;
break;
}
if(flag)
continue;
cnt++;
for(int j=i; j>=1; j--)
for(int k=1; k<=width; k++)
map[player.num][j][k] = map[player.num][j-1][k];//我方map消行用上一行取代下一行
for(int j=1; j<=height; j++)
for(int k=1; k<=width; k++)
map[1-player.num][j][k] = map[1-player.num][j+1][k];/对手map彼长用下一行取代上一行
this->block[1-player.num]->x--;//对手地图上的block的x坐标减一,block上移一格
int ran=rand() % 10+1;
map[1-player.num][height][ran]='#';
}
//计分
if(cnt&&player.num==0)
this->score_player1 += 10*(cnt);
if(cnt&&player.num==1)
this->score_player2 += 10*(cnt);
//每下落一个块,时间间隔减2
time -= 2;
if(time < 0)
time = 0;
//更新块
if(cnt)
block[player.num]->toNext();//如果发生消行,产生新的block
}
3.游戏结束的判断函数bool is_end(Player player)
bool Board::is_end(Player player){
for(int i=1; i<=width; i++){
int cnt=0;
for(int j=1;j<=5;j++){
if(this->map[player.num][j][i] == '1') cnt++;
}
if(cnt==5) return true;
}
return false;
}