title | author | date | CreateTime | categories |
---|---|---|---|---|
俄罗斯方块
|
lindexi
|
2018-2-13 17:23:3 +0800
|
2018-2-13 17:23:3 +0800
|
|
俄罗斯方块是一个很经典的游戏,做一个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
我们需要做一个游戏,我们有了之前的算法,我拿到了一位大神
看不懂他写的,问了一位大神,他帮我改了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); }
运行,我们可以开始玩了,然后你会发现好像我们还少个没有写
我把全部代码放下面,等到你不知道怎么写才去,我的变量命名其实有问题,如果可以去改下
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); } private void tick(object sender, object e) { DispatcherTimer time = sender as DispatcherTimer; time?.Stop(); down(); time?.Start(); } //Rectangle private Rectangle[,] _rectangle; //private SolidColorBrush[,] _solid; public ObservableCollection<solid> solid_collection { set; get; } //private void draw() //{ // foreach (grid temp in _model.grid_observable) // { // } //} public int col { set { value = 0; } get { return _model.col; } } public int row { set { value = 0; } get { return _model.row; } } //public SolidColorBrush[,] solid //{ // set // { // _solid = value; // OnPropertyChanged(); // } // get // { // return _solid; // } //} public Visibility[,] rectangle_visibility { set { _rectangle_visibility = value; OnPropertyChanged(); } get { return _rectangle_visibility; } } private Visibility[,] _rectangle_visibility; private Visibility[] _visibility; public Visibility[] visibility { set { _visibility = value; OnPropertyChanged(); } get { return _visibility; } } public void down() { //visibility = visibility == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed; _model.down(); foreach (grid temp in _model.grid_observable) { //SolidColorBrush _sld=new SolidColorBrush(); if (temp.rectangle == 0) { //solid[temp.row, temp.col] = new SolidColorBrush(Colors.Gray); solid_collection[temp.row * col + temp.col].solids = new SolidColorBrush(Colors.Gray); //rectangle_visibility[temp.row, temp.col] = Visibility.Collapsed; //visibility[temp.row * col + temp.col] = Visibility.Collapsed; } else //if(temp.rectangle == 1) { //solid[temp.row, temp.col] = new SolidColorBrush(Colors.White); solid_collection[temp.row * col + temp.col].solids = new SolidColorBrush(Colors.White); //rectangle_visibility[temp.row, temp.col] = Visibility.Visible; //visibility[temp.row * col + temp.col] = Visibility.Visible; } } 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); } } 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); } } private model _model = new model(); } public class solid : notify_property { public solid(SolidColorBrush solid) { _solid = solid; } public SolidColorBrush solids { set { _solid = value; OnPropertyChanged(); } get { return _solid; } } private SolidColorBrush _solid; } }
model
// lindexi // 10:41 # region using System; using System.Collections.ObjectModel; # endregion namespace tetris { public class model { public model() { _grid_observable = new ObservableCollection<grid>(); for (int i = 0; i < _row; i++) { for (int j = 0; j < _col; j++) { _grid_observable.Add(new grid(j, i)); } } } public square _square { set; get; } public int col { set { _col = value; } get { return _col; } } public ObservableCollection<grid> grid_observable { set { _grid_observable = value; } get { return _grid_observable; } } public int row { set { _row = value; } get { return _row; } } public void translate() { square rotate = _square.clone(); rotate.rotate(); if (rectangle(rotate)) { _square = rotate; } } 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; } } 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; } } private Random ran { set { _ran = value; } get { return _ran; } } private int _col = 10; private ObservableCollection<grid> _grid_observable; private Random _ran = new Random(); private int _row = 20; private void new_block() { block _block = (block) ran.Next(4); int center = _col/2; _square = new square(_block, center); //_square = new square(block.straight, center); } 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 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; } 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; } } } } public class grid : notify_property { public grid() { _rectangle = 1; } public grid(int col, int row) { _col = col; _row = row; _rectangle = 0; } public int col { set { _col = value; OnPropertyChanged(); } get { return _col; } } public int rectangle { set { _rectangle = value; } get { return _rectangle; } } public int row { set { _row = value; OnPropertyChanged(); } get { return _row; } } public grid clone() { return new grid(col, row); } private int _col; private int _rectangle; private int _row; } public enum block { straight, //直 square, t, bent } public class square { public square(block block, int center) { _block = block; int n = 4; //col = new int[n]; //row = new int[n]; _grid = new grid[n]; for (int i = 0; i < n; i++) { _grid[i] = new grid(); switch (block) { case block.straight: //col[i] = center; //row[i] = -i; _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) { //col[i] = center + i; //row[i] = 0; _grid[i].col = center + i; _grid[i].row = 0; } else { //col[i] = center + i - 2; //row[i] = -1; _grid[i].col = center + i - 2; _grid[i].row = -1; } break; case block.bent: if (i <= 1) { //col[i] = center + 1; //row[i] = 0; _grid[i].col = center + i; _grid[i].row = 0; } else { //col[i] = center + i - 3; //row[i] = -1; _grid[i].col = center + i - 3; _grid[i].row = -1; } break; default: throw new ArgumentOutOfRangeException(nameof(block), block, null); } } } public void rotate() { switch (_block) { case block.straight: if (rotate_count == 0 || rotate_count == 2) { int row = _grid[2].row; int col = _grid[2].col; _grid[0].row = row; _grid[0].col = col + 2; _grid[1].row = row; _grid[1].col = col + 1; _grid[3].row = row; _grid[3].col = col - 1; } else if (rotate_count == 1 || rotate_count == 3) { int row = _grid[2].row; int col = _grid[2].col; _grid[0].row = row - 2; _grid[0].col = col; _grid[1].row = row - 1; _grid[1].col = col; _grid[3].row = row + 1; _grid[3].col = col; } break; case block.square: break; case block.t: if (rotate_count == 0) { int row = _grid[2].row; int col = _grid[2].col; _grid[0].row = row + 1; _grid[0].col = col - 1; _grid[1].row--; _grid[1].col++; _grid[3].row++; _grid[3].col--; } else if (rotate_count == 1) { int row = _grid[3].row; int col = _grid[3].col; _grid[0].row--; _grid[1].row = row; _grid[1].col = col + 1; _grid[2].row = row; _grid[2].col = col - 1; } else if (rotate_count == 2) { int row = _grid[2].row; int col = _grid[2].col; _grid[0].col++; _grid[1].row = row - 1; _grid[1].col = col; _grid[3].row = row + 1; _grid[3].col = col; } else if (rotate_count == 3) { int row = _grid[2].row; int col = _grid[2].col; _grid[0].row = row + 1; _grid[0].col = col + 2; _grid[1].row = row; _grid[1].col = col; _grid[2].row = row; _grid[2].col = col + 1; _grid[3].row = row; _grid[3].col = col + 2; } break; case block.bent: if (rotate_count == 0 || rotate_count == 2) { _grid[1].row = _grid[2].row - 1; _grid[1].col = _grid[2].col; } else if (rotate_count == 1 || rotate_count == 3) { _grid[1].row = _grid[0].row; _grid[1].col = _grid[0].col + 1; } break; default: throw new ArgumentOutOfRangeException(); } rotate_count++; } 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; } private int rotate_count { set { if (value >= 4) { value = value%4; } _rotate_count = value; } get { if (_rotate_count > 4) { _rotate_count = 0; } return _rotate_count; } } //public int[] col //{ // set // { // _col = value; // } // get // { // return _col; // } //} //public int[] row //{ // set // { // _row = value; // } // get // { // return _row; // } //} private block _block; private int _rotate_count; public grid[] _grid; } }