C++俄罗斯方块
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++俄罗斯方块相关的知识,希望对你有一定的参考价值。
C++的课题设计选择了写俄罗期方块,想了一下却不知道怎么下手去做,无从下手。。求高人指点一下~~
//类用到的函数所需的头文件#include <windows.h>
#include <time.h>
//类用到的基本宏定义
#define SHAPENUM 7 //方块形状的数目
#define MAXBLOCKNUM 4 //不同形状方块的小方块的最大数
//类用到的类型定义
typedef struct
POINT pt[MAXBLOCKNUM];//各小方块在网格中的索引
int ncount;//小方块数
RUSSIAN;
class Russian
public:
//类的公有函数
Russian();
void init(COLORREF blockcolor,COLORREF blocklinecolor,COLORREF removecolor,int blocksize,int xindex,int yindex);
int randomblock(int shapeindex);//随机产生一个方块
int drawblock(HDC hdc,int left,int top,COLORREF brushcolor,COLORREF pencolor);
int virtualmoveblock(int *falled,int m,int n,int incre,int *adjust,int hv);
int rotate(HDC hdc,int left,int top,int *falled,int m,int n);
int coordinaterotate(int *falled,int m,int n);
void trans(POINT *pt);
int linefinished(int *falled,int m,int n);
int minmax(int xy,int flag);
void mov(int incre,int flag);
int setfalled(int *falled,int m,int n);
int getblock(HDC hdc,int left,int top,int shapeindex);
~Russian();
private:
//类的私有属性
POINT block[MAXBLOCKNUM];//方块中各小方块的位置索引
int ncount;//方块可分成几个小方块
static RUSSIAN russianshape[SHAPENUM];//方块形状描述数组
COLORREF m_blocklinecolor;//方块边框颜色
COLORREF m_blockcolor;//方块颜色
COLORREF m_removecolor;//擦除方块所用颜色
int m_blocksize;//方块尺寸
int m_xindex;//方块x坐标偏移m_xindex个m_blocksize单位
int m_yindex;//方块y坐标偏移yindex个m_blocksize单位
;
void Russian::mov(int incre,int flag)
//功能:调整方块incre个基本单位
//参数:flag为1,水平方向上调整,为2垂直方向
POINT *p;
for(p=block;p<block+ncount;p++)
if(flag==1)
p->x+=incre;
else if(flag==2)
p->y+=incre;
else
int Russian::minmax(int xy,int flag)
//功能:返回当前方块(block ncount属性指定)的某些最值
//参数:
//1.xy 偶数表示返回X坐标 奇数表示Y坐标
//2.flag 1返回最大值,0返回最小值
int *p,*pint;
int temp;
pint=(int *)block+xy%2;
temp=*pint;
for(p=pint;p<pint+sizeof(POINT)/sizeof(int)*ncount;p+=2)
if(flag==1)
if(temp<*p)
temp=*p;
else
if(temp>*p)
temp=*p;
return temp;
int Russian::randomblock(int shapeindex)
//根据方块形状数组,随机产生一个方块,对产生的方块顺时针随机旋转0~270度
//返回值:下一个方块的索引
POINT *p1,*p2;
int i;
if(shapeindex<0||shapeindex>=SHAPENUM)//如果传入的方块索引不正确,产生当前方块的索引
srand(time(NULL));//随机数序列种子
shapeindex=rand()%SHAPENUM;//随机数除方块形状数目的余数,shapeindex范围为0~SHAPENUM-1
ncount=russianshape[shapeindex].ncount;//初始化全局变量加速倍数为1
//初始化block为各个小方块的网格索引
for(p1=block,p2=russianshape[shapeindex].pt;p1<block+ncount;p1++,p2++)
p1->x=p2->x+m_xindex;
p1->y=p2->y+m_yindex;
srand(time(NULL));//随机数序列种子
shapeindex=rand()%4;//随机数除4的余数,顺时针旋转90度0~3次度,实现方块的方向随机
for(i=0;i<shapeindex;i++)
trans(block);//旋转坐标变换
//产生下一个方块的索引
srand(time(NULL));
shapeindex=rand()%SHAPENUM;
return shapeindex;
int Russian::drawblock(HDC hdc,int left,int top,COLORREF brushcolor,COLORREF pencolor)
//在hdc,屏幕坐标为x,y处用brushcolor、pencolor绘制方块
//如果color参数为NULL,则用默认的方块画刷、方块边框实心笔
POINT *p;
int x,y;
HBRUSH hbrush;
HPEN hpen;
if(pencolor==NULL)
hpen=(HPEN)CreatePen(PS_SOLID,1,m_blocklinecolor);
else
hpen=(HPEN)CreatePen(PS_SOLID,1,pencolor);
if(brushcolor==NULL)
hbrush=(HBRUSH)CreateSolidBrush(m_blockcolor);
else
hbrush=(HBRUSH)CreateSolidBrush(brushcolor);
SelectObject(hdc,hbrush);
SelectObject(hdc,hpen);
for(p=block;p<block+ncount;p++)
x=p->x*m_blocksize+left;
y=p->y*m_blocksize+top;
Rectangle(hdc,x,y,x+m_blocksize,y+m_blocksize);//有边框
DeleteObject(hbrush);
DeleteObject(hpen);//释放对象内存
return 1;
int Russian::virtualmoveblock(int *falled,int m,int n,int incre,int *adjust,int hv)
//功能:假设水平或垂直方向移动方块incre增量,是否会遇到障碍(边界或者falled)
//参数:
//1.pt为POINT数组的起始地址
//判断n个POINT范围内是否有边界或者障碍(falled)
//3.incre 坐标增量
//4.adjust存储新的增量(单位为BLOCKSIZE),即实际可行的移动的网格数
//5.hv 0或1,分别表示水平方向、垂直方向
//返回值:-1表示参数错误,0表示可以移动,1表示水平有边界或者障碍,2表示垂直有边界或障碍(randomblock)
POINT *p;
int index;
int i;
int resflag=0;//返回值变量,初始为0
int temp;//临时存储变量
int direction;//移动方向,1或者 -1
incre=incre/m_blocksize;
if(adjust==NULL)
return -1;//参数错误
else
*adjust=incre;//默认值等于incre/BLOCKSIZE
if(incre==0)
return 0;
if(falled==NULL)
return -1;//参数错误
direction=abs(incre)/incre;//方向:1为向右向下 -1为向左向上
for(p=block;p<block+ncount;p++)
if(hv==0)//判断水平方向是否有边界或障碍
index=p->x+incre;
for(i=p->x;(i-index)*direction<=0;i+=direction)
if(*(falled+p->y*n+i)==1)//遇到障碍
resflag=3;
temp=(i-direction)-p->x;
if(direction*(*adjust-temp)>0)//取绝对值较小的偏移量
*adjust=temp;
break;
if(resflag!=3)
if(index<0)//遇到左边界
*adjust=-minmax(0,0);
resflag=1;
break;
if(p->x+incre>n-1)//遇到右边界
*adjust=(n-1)-minmax(0,1);//返回(NLINE-1)*BLOCKSIZE-x坐标最大值
resflag=2;
break;
else//判断垂直方向是否有边界或障碍
index=p->y+incre;
for(i=p->y;(i-index)*direction<=0;i+=abs(incre)/incre)
if(i>=0&&*(falled+i*n+p->x)==1)//遇到障碍
resflag=5;
temp=(i-direction)-p->y;
if(direction*(*adjust-temp)>0)//取ncount个小方块中绝对值最小的偏移量
*adjust=temp;
break;
if(resflag!=5)//没有遇到障碍才进行边界的判断,否则会出现加速下降穿过障碍物的情况
if(p->y+incre>m-1)//
*adjust=m-1-minmax(1,1);
resflag=4;
break;
return resflag;
int Russian::rotate(HDC hdc,int left,int top,int *falled,int m,int n)
//功能:将方块绕点(pt->x,pt->y)顺时针旋转90度,坐标变换(网格坐标系中)
//参数:
//1.hdc 设备上下文,用于绘图
//2.pt为方块各小方块网格索引数组
//3.n为小方块数目
//返回值:如果pt为NULL,则返回0,否则返回1
//注意:在函数中,进行坐标变换,覆盖pt所指向的数组
drawblock(hdc,left,top,m_removecolor,0);//擦除旧方块,无边框
coordinaterotate(falled,m,n);//坐标转换
drawblock(hdc,left,top,m_blockcolor,0);//新方块,无边框
return 1;
int Russian::coordinaterotate(int *falled,int m,int n)
//原理:(x1,y1)绕(x0,y0)顺时针旋转90度,新坐标为:(x0+y0-y1,y0-x0+x1)
//功能:将方块绕点(0+pt->x,0+pt->y)顺时针旋转90度,坐标变换(网格坐标系中)
//返回值,1 已转换,0不能转换
int resflag=1;
POINT *p1,*p2;
POINT pt[MAXBLOCKNUM];//临时存储旋转后的坐标
trans(pt);//转换坐标,存储到pt中
for(p2=pt;p2<pt+ncount;p2++)//旋转后坐标有效性判断
if(!(!(p2->x>n-1||p2->y>m||p2->x<0)&&*(falled+p2->y*n+p2->x)!=1))
resflag=0;//坐标不合法
break;
if(resflag!=0)//坐标合法,把临时存储的坐标复制到block
for(p1=block,p2=pt;p1<block+ncount;p1++,p2++)
p1->x=p2->x;
p1->y=p2->y;
return resflag;
void Russian::trans(POINT *pt)
//功能:无条件将方块坐标转换(网格坐标系中),存储在pt所指向的数组中
//pt的size不小于block
int temp;
POINT *p1,*p2;
int xindex,yindex;
xindex=block->x;
yindex=block->y;
for(p1=block,p2=pt;p1<block+ncount;p1++,p2++)
p2->x=p1->x-xindex;
p2->y=p1->y-yindex;
temp=p2->x;
p2->x=-p2->y;
p2->y=temp;
p2->x+=xindex;
p2->y+=yindex;
int Russian::setfalled(int *falled,int m,int n)
//功能:设置障碍
//返回值,-1为参数错误,0为设置障碍失败(方块y坐标<0,游戏结束),1为成功设置障碍
POINT *p;
if(falled==NULL)
return -1;
for(p=block;p<block+ncount;p++)
if(p->y>=0)
*(falled+p->y*n+p->x)=1;//设置障碍
else
return 0;
return 1;
int Russian::linefinished(int *falled,int m,int n)
//方块停止落下时判断,是否有行满,并进行处理
//返回值:填满的行数
int *p;
int *pline;//指向NLINE个元素的指针
int i=0;//满行计数器
// int res[MAXBLOCKNUM];//存放满行的index,最多消去的行数等于各种形状方块有的小方块的数目
for(pline=falled+(m-1)*n;pline>=falled;pline-=n)//从最底下一行开始
for(p=pline;p<pline+n;p++)//从最左边一列开始
if(*p!=1)
break;
if(p>=pline+n)//说明pline行的所有元素都是1,行满
i++;
// res[i++]=pline-falled;
else//行不满,复制到底层
if(i!=0)
memcpy(pline+i*n,pline,sizeof(int)*n);//不满的行下面有i个满的行,满的行不用理会
memset(falled,0,sizeof(int)*n*i);//清空最上面i行
return i;
void Russian::init(COLORREF blockcolor,COLORREF blocklinecolor,COLORREF removecolor,int blocksize,int xindex,int yindex)
//初始化方块的各项属性
m_blockcolor=blockcolor;
m_blocklinecolor=blocklinecolor;
m_removecolor=removecolor;
m_blocksize=blocksize;
m_xindex=xindex;
m_yindex=yindex;
int Russian::getblock(HDC hdc,int left,int top,int shapeindex)
POINT *p1,*p2;
POINT pt[MAXBLOCKNUM];
HPEN hpen;
HBRUSH hbrush;
int x,y;
if(shapeindex<0||shapeindex>=SHAPENUM)//如果传入的方块索引不正确
return 0;
for(p1=pt,p2=russianshape[shapeindex].pt;p1<pt+ncount;p1++,p2++)
p1->x=p2->x;
p1->y=p2->y;
hpen=CreatePen(PS_SOLID,1,m_blocklinecolor);
SelectObject(hdc,hpen);
hbrush=CreateSolidBrush(m_blockcolor);
SelectObject(hdc,hbrush);//创建DC对象,选入DC,并保存旧的DC对象
for(p1=pt;p1<pt+russianshape[shapeindex].ncount;p1++)
x=p1->x*m_blocksize+left;
y=p1->y*m_blocksize+top;
Rectangle(hdc,x,y,x+m_blocksize,y+m_blocksize);//有边框,若hpen为0,则边框与背景色一致
DeleteObject(hbrush);
DeleteObject(hpen);//释放内存
return 1;
RUSSIAN Russian::russianshape[SHAPENUM]=
//在类外初始化静态成员
/*
方块的各种形状描述,以中心点开始描述
△■■ ■■△ ■ △△■ △■△ ■■△ △■△
△■△ ■■△ △■△ △■■ △■■ △■△ △■■
△■△ △△△ △■△ △■△ △■△ △■△ △△■
△■△
*/
0,0,//以0,0开始
0,-1,
1,-1,
0,1
,
4
,
0,0,
-1,-1,
-1,0,
0,-1
,
4
,
0,0,
0,-1,
0,1,
0,-2
,
4
,
0,0,
0,1,
1,-1,
1,0
,
4
,
0,0,
1,0,
0,-1,
0,1
,
4
,
0,0,
0,-1,
-1,-1,
0,1
,
4
,
0,0,
0,-1,
1,0,
1,1
,
4
;
Russian::Russian()
Russian::~Russian()
参考技术A 有一本书叫C语言经典案例,有个俄罗斯方块,你可以参考一下 参考技术B 我有完整代码,LZ要吗
win10 uwp 俄罗斯方块
俄罗斯方块是一个很经典的游戏,做一个UWP俄罗斯方块没有什么用,我想说的是移植,把经典游戏移植到UWP。
我的所有博客都是抄别人的,这个才是我自己写的。后台很多代码还是抄别人的,我想说的是我们之前有很多游戏,很多软件使用C或者C++之类的来写,或者C#,其实我们可以把之前的算法拿出来,转换为UWP的C#,这时大家会说,界面。对,界面我们没法直接移植,但是用XAML做一个界面很快的,那么现在的问题就是,我们如何使用之前的算法来用现在的界面。
简单的一个,我们可以使用绑定。
MVVM的知识,我觉得看到一篇文章,忘了出处,希望知道的小伙伴提醒。他说,MVVM的ViewModel作用是界面的抽象。我们不用理界面,因为界面总是改,所以我们需要一个抽象的界面,就是我们做的ViewModel,那么model做的就是算法,数据。Model不知道界面怎样,他需要知道数据。ViewModel不知道界面怎样,他知道界面需要什么。
MVVM的知识我说的不算对,也不算错,但从这个看也是可以。
为什么要分开view?
其实可以看下面的:
假设我们需要做一个软件,这个软件是举报恶意转载的功能,他能够在网上搜,找到那些恶意转载的网站。
先吐槽下中国学网那些垃圾网站,全部东西都是转载的。吐槽下百度,搜索到的转载的都是前,找不到我的博客。
还是360好,能找到
我们软件开始界面
发现我们需要改
接着发现还是需要改
如果我们和界面有联系,一改界面就需要改,那么这样我们开发将会很慢。
如果我们能使用抽象,那么界面怎么改,我们修改的也就界面。
上面图片来自:http://my.oschina.net/Android1989/blog/296850
我们需要做一个游戏,我们有了之前的算法,我拿到了一位大神:http://www.cnblogs.com/china_x01/p/5253556.html
看不懂他写的,问了一位大神,他帮我改了UWP,最后我也看不懂,他写的没有注释。
做一个俄罗斯方块算法简单,我们放在后面,现在先和大家说界面。
后面说的有些小白。
我们程序:
- view:MainPage.xaml
- viewModel.cs
- model.cs
我们在界面
放一个Canvas
里面就是游戏
因为我们需要游戏按键,所以我们需要一个TextBox
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox Margin="10,10,10,10" Width="1" Height="1" KeyDown="keydown"></TextBox>
<Canvas x:Name="canvas" Margin="10,10,10,10">
</Canvas>
</Grid>
每个方块
Rectangle[,] _rectangle
我们需要设计高度宽度 size = 10;
,其实这里可以根据你自己需要,每个方块的size可以设置大,可以看到上面的图,我的方块是有些小。
现在就是我们重要的绑定,我们有200个Rectangle,如果每个在xaml,写我觉得我没有这么时间,也容易错
所以我们在
for (int i = 0; i < view.row; i++)
{
for (int j = 0; j < view.col; j++)
{
_rectangle[i, j] = new Rectangle()
{
Width = size,
Height = size,
Fill = new SolidColorBrush(Colors.Gray),
Stroke = new SolidColorBrush(Colors.LightCoral),
AllowDrop = false,
CanDrag = false,
Margin = new Thickness(j * size, i * size, 0, 0)
};
canvas.Children.Add(_rectangle[i, j]);
}
}
后台写了200个方块,就几句。我们给宽度高度、显示的颜色。显示颜色是没有方块显示的颜色,这里说的没有方块是说没有俄罗斯方块。
然后我们给每个方块边框,Stroke,他们的位置。
这样我们的屏幕就有了200个方块,但是放进去我们会发现和我们上面的图不同,因为宽度和高度不同
canvas.Width = size * view.col;
canvas.Height = size * view.row;
这样就好了。
界面大概就需要做的就这样,算法很简单,放在最后。
我们有的model,有俄罗斯方块的初始方块、移动、变形、向下
他把所有的数据保存在一个数组grid_observable
,类型grid里面有个rectangle
,如果为0表示这个地方没有方块,如果为1表示有方块。
类型grid
- 长
- 宽
- 是否有方块
我们界面根据rectangle显示,如果有,那么显示灰色,没有显示白色。
因为我们view是不知道后台,所以这个显示需要viewModel把后台的rectangle变为颜色。
我们ViewModel把颜色放ObservableCollection<solid> solid_collection
需要把rectangle变为颜色
foreach (grid temp in _model.grid_observable)
{
if (temp.rectangle == 0)
{
solid_collection[temp.row * col + temp.col].solids = new SolidColorBrush(Colors.Gray);
}
else
{
solid_collection[temp.row * col + temp.col].solids = new SolidColorBrush(Colors.White);
}
}
为了让solid一修改就可以告诉view
public class solid : notify_property
{
public solid(SolidColorBrush solid)
{
_solid = solid;
}
public SolidColorBrush solids
{
set
{
_solid = value;
OnPropertyChanged();
}
get
{
return _solid;
}
}
private SolidColorBrush _solid;
}
因为每次写INotifyPropertyChanged要写很多,我们需要通知有很多 ,所以写notify_property
ViewModel能把后台的rectangle变颜色,那么我们view把颜色显示
我们刚才new 了200个Rectangle
我们把他的颜色绑定ViewModel
如果使用xaml,我觉得我没法
那么我们在代码
_rectangle[i, j] = new Rectangle()
{
Width = size,
Height = size,
Fill = new SolidColorBrush(Colors.Gray),
Stroke = new SolidColorBrush(Colors.LightCoral),
AllowDrop = false,
CanDrag = false,
Margin = new Thickness(j * size, i * size, 0, 0)
};
Binding bind = new Binding()
{
Path = new PropertyPath("solid_collection[" + (i * view.col + j) + "].solids"),
Mode = BindingMode.OneWay
};
_rectangle[i, j].DataContext = view;
_rectangle[i, j].SetBinding(Shape.FillProperty, bind);
绑定可以Binding bind = new Binding()
里面写路径,可以数组中Path = new PropertyPath("solid_collection[" + (i * view.col + j) + "].solids"),
其实Path写在new Binding(string Path)
我们可以设置Source = view
Binding bind = new Binding()
{
Path = new PropertyPath("solid_collection[" + (i * view.col + j) + "].solids"),
Mode = BindingMode.OneWay,
Source = view
};
也可以_rectangle[i, j].DataContext = view;
写完我们需要
_rectangle[i, j].SetBinding(Shape.FillProperty, bind);
如果我们后台是可以的,那么我们就能看到
我想说的不是写俄罗斯,而是把之前的软件移植,我们可以把二维表,bool,表示为颜色,把颜色显示,我们有很多游戏都是可以这样,那么移植UWP简单,需要使用绑定,一个转换。
大神:可以直接绑定转换。
其实我是不喜欢直接绑定就转换,因为这样类很多,我们需要文件夹
Convert里面是转换类
我想说的不是做一个俄罗斯方块,而是把之前数据保存二进制矩阵的游戏移植到UWP思路。很简单不用多修改就可以使用,界面我们可以自己来写,只要绑定写了,那么就可以使用。
写到这,后面都是小白
俄罗斯方块
我们先打开vs神器,之前下载vs安装,需要sdk,这个在安装自己弄。
新建项目,我叫tetris
新建一个类叫viewModel,一个model
再新建一个类notify_property,接口INotifyPropertyChanged
/// <summary>
/// 提供继承通知UI改变值
/// </summary>
public class notify_property : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void UpdateProper<T>(ref T properValue , T newValue , [System.Runtime.CompilerServices.CallerMemberName] string properName = "")
{
if (object.Equals(properValue , newValue))
return;
properValue = newValue;
OnPropertyChanged(properName);
}
public void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string name="")
{
PropertyChangedEventHandler handler = PropertyChanged;
handler?.Invoke(this , new PropertyChangedEventArgs(name));
}
}
这个类是我们每次需要INotifyPropertyChanged都需要写PropertyChanged,觉得还是放成类,让需要的继承
俄罗斯方块有
- 新建方块
- 方块移动
- 方块向下
- 碰到下面方块
- 清除
我们把算法写model
方块有
- straight,
- square,
- t,
- bent
我们需要做一个来保存
public enum block
{
straight,
square,
t,
bent
}
那么我们需要一个来放我们的方块
public class grid : notify_property
{
public grid()
{
_rectangle = 1;
}
public grid(int col, int row)
{
_col = col;
_row = row;
_rectangle = 0;
}
public grid clone()
{
return new grid(col, row);
}
public int row
{
set
{
_row = value;
OnPropertyChanged();
}
get
{
return _row;
}
}
public int col
{
set
{
_col = value;
OnPropertyChanged();
}
get
{
return _col;
}
}
public int rectangle
{
set
{
_rectangle = value;
}
get
{
return _rectangle;
}
}
private int _col;
private int _rectangle;
private int _row;
}
行列,是否有方块
我们发现这个只能放一个方块,所以我们写
放着grid[] _grid;
新建方块:
square(block block, int center)
我们需要方块是什么,中心
我们先做直线
public square(block block, int center)
{
_block = block;
int n = 4;
_grid = new grid[n];
for (int i = 0; i < n; i++)
{
_grid[i] = new grid();
switch (block)
{
case block.straight:
_grid[i].col = center;
_grid[i].row = -i;
break;
default:
throw new ArgumentOutOfRangeException(nameof(block), block, null);
}
}
}
我们来做t
case block.t:
_grid[0].col = center;
_grid[0].row = 0;
if (i > 0)
{
_grid[i].col = center + i - 3;
_grid[i].row = -1;
}
square
case block.square:
if (i <= 1)
{
_grid[i].col = center + i;
_grid[i].row = 0;
}
else
{
_grid[i].col = center + i - 2;
_grid[i].row = -1;
}
bent
case block.bent:
if (i <= 1)
{
_grid[i].col = center + i;
_grid[i].row = 0;
}
else
{
_grid[i].col = center + i - 3;
_grid[i].row = -1;
}
public square(block block, int center)
{
_block = block;
int n = 4;
_grid = new grid[n];
for (int i = 0; i < n; i++)
{
_grid[i] = new grid();
switch (block)
{
case block.straight:
_grid[i].col = center;
_grid[i].row = -i;
break;
case block.t:
_grid[0].col = center;
_grid[0].row = 0;
if (i > 0)
{
_grid[i].col = center + i - 3;
_grid[i].row = -1;
}
break;
case block.square:
if (i <= 1)
{
_grid[i].col = center + i;
_grid[i].row = 0;
}
else
{
_grid[i].col = center + i - 2;
_grid[i].row = -1;
}
break;
case block.bent:
if (i <= 1)
{
_grid[i].col = center + i;
_grid[i].row = 0;
}
else
{
_grid[i].col = center + i - 3;
_grid[i].row = -1;
}
break;
default:
throw new ArgumentOutOfRangeException(nameof(block), block, null);
}
}
}
这样看起来代码很多,这样不好,我们需要把每个放在一个函数
public square(block block, int center)
{
_block = block;
int n = 4;
_grid = new grid[n];
for (int i = 0; i < n; i++)
{
_grid[i] = new grid();
switch (block)
{
case block.straight:
block_straight(center, i);
break;
case block.t:
block_t(center, i);
break;
case block.square:
block_square(center, i);
break;
case block.bent:
block_bent(center, i);
break;
default:
throw new ArgumentOutOfRangeException(nameof(block), block, null);
}
}
}
private void block_straight(int center, int i)
{
_grid[i].col = center;
_grid[i].row = -i;
}
private void block_t(int center, int i)
{
_grid[0].col = center;
_grid[0].row = 0;
if (i > 0)
{
_grid[i].col = center + i - 3;
_grid[i].row = -1;
}
}
private void block_bent(int center, int i)
{
if (i <= 1)
{
_grid[i].col = center + i;
_grid[i].row = 0;
}
else
{
_grid[i].col = center + i - 3;
_grid[i].row = -1;
}
}
我们model还没写东西
我们先做新建方块
我们需要最大值
public int row
{
set
{
_row = value;
}
get
{
return _row;
}
}
public int col
{
set
{
_col = value;
}
get
{
return _col;
}
}
private int _col = 10;
private int _row = 20;
当前方块
public square _square
{
set;
get;
}
新建方块
private void new_block()
{
block _block = (block)ran.Next(4);
int center = _col / 2;
_square = new square(_block, center);
}
我们现在没有想着去什么,我们需要显示
每次下降
public void down()
{
if (_square == null)
{
new_block();
}
}
我们在ViewModel
public viewModel()
{
solid_collection = new ObservableCollection<solid>();
for (int i = 0; i < col * row; i++)
{
solid_collection.Add(new solid(new SolidColorBrush(Colors.Gray)));
}
DispatcherTimer time = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(0.5)
};
time.Tick += tick;
time.Start();
}
public int col
{
set
{
value = 0;
}
get
{
return _model.col;
}
}
public int row
{
set
{
value = 0;
}
get
{
return _model.row;
}
}
public ObservableCollection<solid> solid_collection
{
set;
get;
}
private void tick(object sender, object e)
{
DispatcherTimer time = sender as DispatcherTimer;
time?.Stop();
down();
time?.Start();
}
public void down()
{
_model.down();
}
我们需要DispatcherTimer,给他时间Interval = TimeSpan.FromSeconds(0.5)
就向下
如果model
if (_square == null)
{
new_block();
}
我们现在新建出来,还没有显示
我们需要把_square
显示
viewModel
public void down()
{
_model.down();
foreach (grid temp in _model._square._grid.Where(temp => temp.col >= 0 && temp.row >= 0))
{
solid_collection[temp.row * col + temp.col].solids = new SolidColorBrush(Colors.White);
}
}
我们现在除了界面,都做好了。
打开MainPage
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox Margin="10,10,10,10" Width="1" Height="1" KeyDown="keydown"></TextBox>
<Canvas x:Name="canvas" Margin="10,10,10,10">
</Canvas>
</Grid>
TextBox 我们需要按键
private viewModel view;
public MainPage()
{
InitializeComponent();
rectangle();
}
rectangle绑定我们的界面
绑定在上面有写了
private void rectangle()
{
view = new viewModel();
Rectangle[,] _rectangle = new Rectangle[view.row, view.col];
double height = 600;
double size = height / view.row;
if (size < 10)
{
size = 10;
}
canvas.Width = size * view.col;
canvas.Height = size * view.row;
canvas.Background = new SolidColorBrush(Colors.BurlyWood);
for (int i = 0; i < view.row; i++)
{
for (int j = 0; j < view.col; j++)
{
_rectangle[i, j] = new Rectangle()
{
Width = size,
Height = size,
Fill = new SolidColorBrush(Colors.Gray),
Stroke = new SolidColorBrush(Colors.LightCoral),
AllowDrop = false,
CanDrag = false,
Margin = new Thickness(j * size, i * size, 0, 0)
};
Binding bind = new Binding()
{
Path = new PropertyPath("solid_collection[" + (i * view.col + j) + "].solids"),
Mode = BindingMode.OneWay
};
_rectangle[i, j].DataContext = view;
_rectangle[i, j].SetBinding(Shape.FillProperty, bind);
canvas.Children.Add(_rectangle[i, j]);
}
}
}
我们可以开始运行,就可以看到,只下来一块
我们要让整个方块向下
public void down()
{
if (_square == null)
{
new_block();
}
foreach (grid temp in _square._grid)
{
temp.row++;
}
}
每个都向下
我们会发现我们的方块下到最下还是没有停下来
我们需要判断方块是不是到最下
判断方块是不是要到的位置不可以,这句话是说判断是不是在grid里,位置超过最大可以的位置,或者小于0,位置是不是有方块。
private bool rectangle(square _square)
{
bool _rectangle = true;
foreach (grid temp in _square._grid)
{
if (temp.col >= 0 && temp.row >= 0)
{
int n = (temp.row ) * col + temp.col;
if (_grid_observable.Count > n && _grid_observable[n].rectangle != 0)
{
_rectangle = false;
}
}
else if (temp.col < 0)
{
return false;
}
if (temp.row >= _row || temp.col >= _col)
{
return false;
}
}
return _rectangle;
}
我们先复制方块,然后让方块向下,判断是个方块是不是可以在他的位置,如果可以,复制回去。
public void down()
{
if (_square == null)
{
new_block();
}
square temp = _square.clone();
foreach (grid t in temp._grid)
{
t.row++;
}
if (rectangle(temp))
{
_square = temp;
}
}
复制
public square clone()
{
square temp = new square(_block, 0);
for (int i = 0; i < _grid.Length; i++)
{
temp._grid[i] = _grid[i].clone();
}
temp.rotate_count = rotate_count;
return temp;
}
运行我们可以看到方块能向下,在最下停下,但是不会出新方块,我们需要判断向下,如果没有可以位置,那么新方块
public void down()
{
if (_square == null)
{
new_block();
}
square temp = _square.clone();
foreach (grid t in temp._grid)
{
t.row++;
}
if (!rectangle(temp))//修改
{
draw(); //画出来
new_block();
}
else
{
_square = temp; //为什么不clone
}
}
上面代码留有一个问题,就是最后的把可以的位置复制回去怎么不需要写复制,当然这个简单。
我们现在还需要看画出来
我们需要把方块画在grid,那些我们无法移动的方块需要画在grid
private void draw()
{
int n = 0;
foreach (grid temp in _square._grid)
{
if (temp.col >= 0 && temp.row >= 0)
{
n = temp.row * col + temp.col;
if (_grid_observable.Count > n)
{
_grid_observable[n].rectangle = 1;
}
}
}
clean();
}
画完我们判断是不是可以删掉,判断一行是不是有方块,有就删掉
private void clean()
{
bool _rectangle = true;
int n = 1;
foreach (grid temp in _grid_observable)
{
if (temp.col < _col - 1)
{
_rectangle = _rectangle && temp.rectangle != 0;
}
else if (temp.col == _col - 1)
{
_rectangle = _rectangle && temp.rectangle != 0;
if (_rectangle)
{
for (int i = n*_col - 1; i > _col; i--)
{
_grid_observable[i].rectangle = _grid_observable[i - _col].rectangle;
}
n--;
}
n++;
_rectangle = true;
}
}
}
现在我们需要写移动
左移col–,判断是否可以存在,可以,复制回去
public void move(bool left)
{
square temp = _square.clone();
foreach (grid t in temp._grid)
{
if (left)
{
t.col--;
}
else
{
t.col++;
}
}
if (rectangle(temp))
{
_square = temp;
}
}
我们基本做好了俄罗斯方块算法
我们去mainpage写
private void keydown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Up)
{
view.translate();
}
else if (e.Key == VirtualKey.Left)
{
view.move(true);
}
else if(e.Key==VirtualKey.Right)
{
view.move(false);
}
else if (e.Key == VirtualKey.Down)
{
view.down();
}
}
viewmode只是使用model
public void move(bool left)
{
_model.move(left);
}
运行,我们可以开始玩了,然后你会发现好像我们还少个没有写
我把全部代码放下面,我希望等到你不知道怎么写才去,我的变量命名其实有问题,如果可以去改下,因为按shift然后大写实在不会,所以都是_
。如果觉得_
需要两个键,就让我卖关子,其实我只需要使用-
,怎么做我会在之后告诉大家
https://code.msdn.microsoft.com/win10-uwp-def86f86
MainPage.xaml
<Page
x:Class="tetris.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:tetris"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBox Margin="10,10,10,10" Width="1" Height="1" KeyDown="keydown"></TextBox>
<Canvas x:Name="canvas" Margin="10,10,10,10">
</Canvas>
</Grid>
</Page>
// lindexi
// 11:22
#region
using System.Runtime.CompilerServices;
using Windows.System;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Shapes;
#endregion
//“空白页”项模板在 http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409 上有介绍
namespace tetris
{
/// <summary>
/// 可用于自身或导航至 Frame 内部的空白页。
/// </summary>
public sealed partial class MainPage : Page
{
private viewModel view;
public MainPage()
{
InitializeComponent();
rectangle();
}
private void keydown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Up)
{
view.translate();
}
else if (e.Key == VirtualKey.Left)
{
view.move(true);
}
else if(e.Key==VirtualKey.Right)
{
view.move(false);
}
else if (e.Key == VirtualKey.Down)
{
view.down();
}
}
private void rectangle()
{
view = new viewModel();
Rectangle[,] _rectangle = new Rectangle[view.row, view.col];
double height = 600;
double size = height / view.row;
if (size < 10)
{
size = 10;
}
canvas.Width = size * view.col;
canvas.Height = size * view.row;
canvas.Background = new SolidColorBrush(Colors.BurlyWood);
for (int i = 0; i < view.row; i++)
{
for (int j = 0; j < view.col; j++)
{
_rectangle[i, j] = new Rectangle()
{
Width = size,
Height = size,
Fill = new SolidColorBrush(Colors.Gray),
Stroke = new SolidColorBrush(Colors.LightCoral),
AllowDrop = false,
CanDrag = false,
Margin = new Thickness(j * size, i * size, 0, 0)
};
Binding bind = new Binding()
{
Path = new PropertyPath("solid_collection[" + (i * view.col + j) + "].solids"),
Mode = BindingMode.OneWay
};
_rectangle[i, j].DataContext = view;
_rectangle[i, j].SetBinding(Shape.FillProperty, bind);
canvas.Children.Add(_rectangle[i, j]);
}
}
}
}
}
viewModel
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Shapes;
namespace tetris
{
public class viewModel : notify_property
{
public viewModel(Rectangle[,] rectangle)
{
_rectangle = rectangle;
}
public viewModel()
{
//_solid = new SolidColorBrush[row, col];
solid_collection = new ObservableCollection<solid>();
for (int i = 0; i < col * row; i++)
{
solid_collection.Add(new solid(new SolidColorBrush(Colors.Gray)));
}
visibility = new Visibility[row * col];
rectangle_visibility = new Visibility[row, col];
DispatcherTimer time = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(0.5)
};
time.Tick += tick;
time.Start();
}
public void translate()
{
_model.translate();
}
public void move(bool left)
{
_model.move(left);
}
以上是关于C++俄罗斯方块的主要内容,如果未能解决你的问题,请参考以下文章