WPF游戏 菜鸟版俄罗斯方块(转)
2010-09-26 11:05
246 查看
在以前的MFC、winform中实现一个游戏程序是一件相当困难的事情,自从WPF诞生后,富客户端设计工作变得相对简单。本人刚学WPF不久,写写劣质代码望大侠莫见笑。自于这个游戏本身的实现方式我并未去研究,这里只是根据自己的理解来做。
代码下载:http://files.cnblogs.com/lipan/WpfGame1.rar
主要思想:
一、提供一个容器类(Container),用来作为方块活动的网格状区域。由于WPF自带Grid控件支持网格,因此就直接利用了。
1.Container类需要关联到Grid,活动区域;和waitingGrid等候区域(下一个出现的方块)
2.在Container类中实现消层的逻辑
二、提供一个方块基类(Box),7中方块全部从其派生。
1.每个方块包含4个Rectangle(小方格)
2.通过一个工厂类随机产生某个方块的实例
3.基类实现方块移动、变形、等逻辑、子类定义方块颜色、初始化方式,确定每个方格相对坐标。
4.在方块下降到底部触发OnBottom事件,Container接受此事件触发消行逻辑。Container类中OnGameover事件被界面层接受处理。
运行效果:
代码部分:
Box方块基类
“Z”形方块子类
由于每种方块的变形方式都不一样,Z型有4种状态,I型有2中状态,而O型只有一种状态,现在需要描述方块形状状态,需要定义循环链表数据类型。
定义一个坐标点,描述位置和相对位置
在方块子类中Ready方法即为每种方块设置状态链表。
由于方块的生成为随机方式,定义简单工厂模式生成方块如下:
代码
到此为止,方块定义好了,也可以随机产生了,怎么让展示在Grid中?
1.方块子类ShowAt函数标示展示到指定Grid;
2.ShowWating表示展示在等候区域。
当方块展示到容器Grid中时,怎么自动下降呢?这里用到DispatcherTimer。
当方块到达底部时,事件通知容器类执行消层函数:
RemoveLine消层函数
OK,到此方块可以自动下降,可消层了,现在要统计消层得分和游戏级别(难度)。
定义一个新类Result
Result类
在该类中用单例模式控制产生单一实例,并通过实现接口绑定到界面分数展示台。
界面XAML如下:
这样,代码中对Result赋值,直接影响界面控件展示数值的变化:
/// <summary>
/// 活动方块到达底部时触发
/// </summary>
static public void ActivityBox_OnBottom(object sender, EventArgs e)
{
Result.GetInstance().CalculateScore(RemoveLine());
NewBoxReadyToDown();
}
这里根据消行函数返回值此处对result实例进行修改,界面数值(分数、级别)也同步变化。
最后,界面添加功能按钮,实现Window_KeyDown事件 OnGameover事件,游戏完成。
原文地址:http://www.cnblogs.com/lipan/archive/2010/09/20.html
代码下载:http://files.cnblogs.com/lipan/WpfGame1.rar
主要思想:
一、提供一个容器类(Container),用来作为方块活动的网格状区域。由于WPF自带Grid控件支持网格,因此就直接利用了。
1.Container类需要关联到Grid,活动区域;和waitingGrid等候区域(下一个出现的方块)
2.在Container类中实现消层的逻辑
二、提供一个方块基类(Box),7中方块全部从其派生。
1.每个方块包含4个Rectangle(小方格)
2.通过一个工厂类随机产生某个方块的实例
3.基类实现方块移动、变形、等逻辑、子类定义方块颜色、初始化方式,确定每个方格相对坐标。
4.在方块下降到底部触发OnBottom事件,Container接受此事件触发消行逻辑。Container类中OnGameover事件被界面层接受处理。
运行效果:
代码部分:
Box方块基类
abstract class Box { public Box() { for (int i = 0; i < 4; i++) { rectangles.Add(new Rectangle()); rectangles[i].Width = 24.0; rectangles[i].Height = 24.0; } dispatcherTimer = new DispatcherTimer(DispatcherPriority.Normal); dispatcherTimer.Tick += new EventHandler(Timer_Tick); dispatcherTimer.Interval = TimeSpan.FromMilliseconds(450 - Result.GetInstance().Level * 50); } protected Grid grid; /// <summary> /// 定义由四个方格组成的方块 /// </summary> protected IList<Rectangle> rectangles = new List<Rectangle>(4); DispatcherTimer dispatcherTimer; /// <summary> /// 当方块到达底部时触发的事件句柄 /// </summary> public event EventHandler OnBottom; /// <summary> /// 判断x行y列是否包含方格 /// </summary> protected bool Existence(int x, int y) { foreach (var r in grid.Children) { if (r is Rectangle) { if (this.rectangles.Contains(r as Rectangle)) return false; if (Convert.ToInt32((r as Rectangle).GetValue(Grid.ColumnProperty)) == x && Convert.ToInt32((r as Rectangle).GetValue(Grid.RowProperty)) == y) { return true; } } } return false; } /// <summary> /// 当前方块是否与其他方块重叠 /// </summary> public bool ISOverlapping() { foreach (var r in rectangles) { int x = Convert.ToInt32((r as Rectangle).GetValue(Grid.ColumnProperty)); int y = Convert.ToInt32((r as Rectangle).GetValue(Grid.RowProperty)); if (Existence(x, y)) return true; } return false; } /// <summary> /// 判断由数值 x,y标示的行列值是否在Grid范围内 /// </summary> protected bool InGrid(int x, int y) { if (x >= 12 || y >= 24 || x < 0 || y < 0) return false; return true; } /// <summary> /// 定义活动方块自动下降 /// </summary> public void AtuoDown() { dispatcherTimer.Start(); } private void Timer_Tick(object sender, EventArgs e) { if (!MoveDown()) { dispatcherTimer.Stop(); OnBottom(this, null); } } public abstract void Ready(); public abstract void ShowWating(ref Grid WaingGrid); protected bool Move(int _x, int _y) { if (IsPause) return false; for (int i = 0; i < 4; i++) { int x = Convert.ToInt32(rectangles[i].GetValue(Grid.ColumnProperty)) + _x; int y = Convert.ToInt32(rectangles[i].GetValue(Grid.RowProperty)) + _y; if (Existence(x, y)) return false; if (!InGrid(x, y)) return false; } for (int i = 0; i < 4; i++) { rectangles[i].SetValue(Grid.ColumnProperty, Convert.ToInt32(rectangles[i].GetValue(Grid.ColumnProperty)) + _x); rectangles[i].SetValue(Grid.RowProperty, Convert.ToInt32(rectangles[i].GetValue(Grid.RowProperty)) + _y); } return true; } public bool MoveUp() { return Move(0, -1); } public bool MoveDown() { return Move(0, 1); } public bool MoveLeft() { return Move(-1, 0); } public bool MoveRight() { return Move(1, 0); } /// <summary> /// 快速下降 /// </summary> public bool FastDown() { if (IsPause) return false; bool mark = false; int j = 0; while (!mark) { j++; for (int i = 0; i < 4; i++) { int x = Convert.ToInt32(rectangles[i].GetValue(Grid.ColumnProperty)); int y = Convert.ToInt32(rectangles[i].GetValue(Grid.RowProperty)) + j; if (Existence(x, y) || !InGrid(x, y)) { j--; mark = true; } } } for (int i = 0; i < 4; i++) { rectangles[i].SetValue(Grid.RowProperty, Convert.ToInt32(rectangles[i].GetValue(Grid.RowProperty)) + j); } //OnBottom(this, null); return mark; } /// <summary> /// 当前方块是否处于暂停状态 /// </summary> private bool IsPause = false; public void Pause() { dispatcherTimer.IsEnabled = false; IsPause = true; } public void UnPause() { dispatcherTimer.IsEnabled = true; IsPause = false; } public void StopAction() { dispatcherTimer.Stop(); } /// <summary> /// 当前变形状态 /// </summary> protected Status ActivityStatus; /// <summary> /// 变形 /// </summary> public bool ChangeShape() { if (IsPause) return false; IList<Position> P = new List<Position>(4); for (int i = 0; i < 4; i++) P.Add(new Position()); P[0].x = Convert.ToInt32(rectangles[0].GetValue(Grid.ColumnProperty)) + ActivityStatus.NextRelativeposition[0].x; P[0].y = Convert.ToInt32(rectangles[0].GetValue(Grid.RowProperty)) + ActivityStatus.NextRelativeposition[0].y; if (ActivityStatus.NeedCheck[0]) if (Existence(P[0].x, P[0].y) || !InGrid(P[0].x, P[0].y)) return false; P[1].x = Convert.ToInt32(rectangles[1].GetValue(Grid.ColumnProperty)) + ActivityStatus.NextRelativeposition[1].x; P[1].y = Convert.ToInt32(rectangles[1].GetValue(Grid.RowProperty)) + ActivityStatus.NextRelativeposition[1].y; if (ActivityStatus.NeedCheck[1]) if (Existence(P[1].x, P[1].y) || !InGrid(P[1].x, P[1].y)) return false; P[2].x = Convert.ToInt32(rectangles[2].GetValue(Grid.ColumnProperty)) + ActivityStatus.NextRelativeposition[2].x; P[2].y = Convert.ToInt32(rectangles[2].GetValue(Grid.RowProperty)) + ActivityStatus.NextRelativeposition[2].y; if (ActivityStatus.NeedCheck[2]) if (Existence(P[2].x, P[2].y) || !InGrid(P[2].x, P[2].y)) return false; P[3].x = Convert.ToInt32(rectangles[3].GetValue(Grid.ColumnProperty)) + ActivityStatus.NextRelativeposition[3].x; P[3].y = Convert.ToInt32(rectangles[3].GetValue(Grid.RowProperty)) + ActivityStatus.NextRelativeposition[3].y; if (ActivityStatus.NeedCheck[3]) if (Existence(P[3].x, P[3].y) || !InGrid(P[3].x, P[3].y)) return false; for (int i = 0; i < 4; i++) { rectangles[i].SetValue(Grid.ColumnProperty, P[i].x); rectangles[i].SetValue(Grid.RowProperty, P[i].y); } ActivityStatus = ActivityStatus.Next; return true; } }
“Z”形方块子类
class Box_Z : Box { public Box_Z(ref Grid grid) { this.grid = grid; for (int i = 0; i < 4; i++) rectangles[i].Fill = new SolidColorBrush(Colors.DarkOrange); } private void ShowAt(Position P, ref Grid grid) { rectangles[0].SetValue(Grid.ColumnProperty, P.x + 0); rectangles[0].SetValue(Grid.RowProperty, P.y + 0); rectangles[1].SetValue(Grid.ColumnProperty, P.x + 1); rectangles[1].SetValue(Grid.RowProperty, P.y + 0); rectangles[2].SetValue(Grid.ColumnProperty, P.x + 1); rectangles[2].SetValue(Grid.RowProperty, P.y + 1); rectangles[3].SetValue(Grid.ColumnProperty, P.x + 2); rectangles[3].SetValue(Grid.RowProperty, P.y + 1); for (int i = 0; i < 4; i++) grid.Children.Add(rectangles[i]); } public override void ShowWating(ref Grid WaingGrid) { ShowAt(new Position(1, 1), ref WaingGrid); } public override void Ready() { ShowAt(new Position(4, 0), ref grid); ActivityStatus = new Status(); ActivityStatus.NextRelativeposition.Add(new Position(1, 2)); ActivityStatus.NextRelativeposition.Add(new Position(0, 1)); ActivityStatus.NextRelativeposition.Add(new Position(1, 0)); ActivityStatus.NextRelativeposition.Add(new Position(0, -1)); ActivityStatus.NeedCheck.Add(true); ActivityStatus.NeedCheck.Add(false); ActivityStatus.NeedCheck.Add(false); ActivityStatus.NeedCheck.Add(true); ActivityStatus.Next = new Status(); ActivityStatus.Next.NextRelativeposition.Add(new Position(-1, -2)); ActivityStatus.Next.NextRelativeposition.Add(new Position(0, -1)); ActivityStatus.Next.NextRelativeposition.Add(new Position(-1, 0)); ActivityStatus.Next.NextRelativeposition.Add(new Position(0, 1)); ActivityStatus.Next.NeedCheck.Add(true); ActivityStatus.Next.NeedCheck.Add(true); ActivityStatus.Next.NeedCheck.Add(false); ActivityStatus.Next.NeedCheck.Add(false); ActivityStatus.Next.Next = ActivityStatus; } }
由于每种方块的变形方式都不一样,Z型有4种状态,I型有2中状态,而O型只有一种状态,现在需要描述方块形状状态,需要定义循环链表数据类型。
定义一个坐标点,描述位置和相对位置
/// <summary> /// 定义一个方格坐标点 /// </summary> class Position { public Position(int x, int y) { this.x = x; this.y = y; } public Position() { } public int x { get; set; } public int y { get; set; } }
/// <summary> /// 定义方块形状状态循环链表,标记变形状态 /// </summary> class Status { /// <summary> /// 方格[四个方块]下一次变形将要去的相对位置 /// </summary> public List<Position> NextRelativeposition = new List<Position>(4); /// <summary> /// 是否需要检查方块[每个方格]到这个位置的可行性 /// </summary> public List<bool> NeedCheck = new List<bool>(4); /// <summary> /// 指向下一状态 /// </summary> public Status Next; }
在方块子类中Ready方法即为每种方块设置状态链表。
由于方块的生成为随机方式,定义简单工厂模式生成方块如下:
代码
class BoxFactory { /// <summary> /// 随机方块工厂 /// </summary> static public Box GetRandomBox(ref Grid grid) { //return new Box_Z(ref grid); Random ran = new Random(); int index = ran.Next(7); switch (index) { case 0: return new Box_S(ref grid); case 1: return new Box_Z(ref grid); case 2: return new Box_J(ref grid); case 3: return new Box_L(ref grid); case 4: return new Box_I(ref grid); case 5: return new Box_O(ref grid); case 6: return new Box_T(ref grid); default: return null; } } }
到此为止,方块定义好了,也可以随机产生了,怎么让展示在Grid中?
1.方块子类ShowAt函数标示展示到指定Grid;
2.ShowWating表示展示在等候区域。
当方块展示到容器Grid中时,怎么自动下降呢?这里用到DispatcherTimer。
dispatcherTimer = new DispatcherTimer(DispatcherPriority.Normal); dispatcherTimer.Tick += new EventHandler(Timer_Tick); dispatcherTimer.Interval = TimeSpan.FromMilliseconds(450 - Result.GetInstance().Level * 50); /// <summary> /// 定义活动方块自动下降 /// </summary> public void AtuoDown() { dispatcherTimer.Start(); } private void Timer_Tick(object sender, EventArgs e) { if (!MoveDown()) { dispatcherTimer.Stop(); OnBottom(this, null); } }
当方块到达底部时,事件通知容器类执行消层函数:
在Box中: /// <summary> /// 当方块到达底部时触发的事件句柄 /// </summary> public event EventHandler OnBottom; private void Timer_Tick(object sender, EventArgs e) { if (!MoveDown()) { dispatcherTimer.Stop(); OnBottom(this, null); } } 在Container中: ActivityBox.OnBottom += ActivityBox_OnBottom; /// <summary> /// 活动方块到达底部时触发 /// </summary> static public void ActivityBox_OnBottom(object sender, EventArgs e) { Result.GetInstance().CalculateScore(RemoveLine()); NewBoxReadyToDown(); }
RemoveLine消层函数
/// <summary> /// 消层,并返回消除的层数 /// </summary> static int RemoveLine() { if (grid == null) new Exception("缺少活动区域,必须为容器指定grid值。"); int[] lineCount = new int[24]; for (int i = 0; i < 24; i++) lineCount[i] = 0; int RemoveLineCount = 0; //计算每一行方块总数 foreach (var r in grid.Children) { if (r is Rectangle) { int x = Convert.ToInt32((r as Rectangle).GetValue(Grid.RowProperty)); lineCount[x]++; } } for (int i = 23; i >= 0; i--) { if (lineCount[i] >= 12) { //移除一行小方格 for (int j = 0; j < grid.Children.Count; j++)// (var r in mygrid.Children) { if (grid.Children[j] is Rectangle) { if (Convert.ToInt32((grid.Children[j] as Rectangle).GetValue(Grid.RowProperty)) == i + RemoveLineCount) { grid.Children.Remove((grid.Children[j] as Rectangle)); j--; } } } //将上面的所有小方格下降一行 foreach (var r in grid.Children) { if (r is Rectangle) { if (Convert.ToInt32((r as Rectangle).GetValue(Grid.RowProperty)) < i + RemoveLineCount) { (r as Rectangle).SetValue(Grid.RowProperty, Convert.ToInt32((r as Rectangle).GetValue(Grid.RowProperty)) + 1); } } } //被移除行数加1 RemoveLineCount++; } } return RemoveLineCount; }
OK,到此方块可以自动下降,可消层了,现在要统计消层得分和游戏级别(难度)。
定义一个新类Result
Result类
/// <summary> /// 记录分数和级别 /// </summary> class Result : INotifyPropertyChanged { Result() { Score = 0; Level = 1; } //单例模式 private static Result instance; private static readonly object syncRoot = new object(); public static Result GetInstance() { if (instance == null) { lock (syncRoot) { if (instance == null) { instance = new Result(); } } } return instance; } int score; int level; public int Score { get { return score; } set { score = value; Notify("Score"); } } public int Level { get { return level; } set { level = value; Notify("Level"); } } public void CalculateScore(int Lines) { switch (Lines) { case 1: Score += 5; break; case 2: Score += 15; break; case 3: Score += 30; break; case 4: Score += 50; break; default: Score += 0; break; } if (Score < 20) Level = 1; else if (Score < 100) Level = 2; else if (Score < 300) Level = 3; else if (Score < 500) Level = 4; else if (Score < 1000) Level = 5; else if (Score < 3000) Level = 6; else if (Score < 5000) Level = 7; else Level = 8; } void Notify(string propName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propName)); } } public event PropertyChangedEventHandler PropertyChanged; }
在该类中用单例模式控制产生单一实例,并通过实现接口绑定到界面分数展示台。
界面XAML如下:
<Grid Grid.Column="1" Grid.Row="1" Height="65" HorizontalAlignment="Left" Margin="5,6,0,0" Name="grid4" VerticalAlignment="Top" Width="100"> <Label Content="积分:" Height="28" Name="label1" Margin="0,5,0,32" HorizontalAlignment="Left" Width="38" /> <Label Content="{Binding Path=Score}" Height="28" Name="label2" Margin="35,5,0,32" /> <Label Content="级别:" Height="28" Name="label3" Margin="0,34,0,3" HorizontalAlignment="Left" Width="38" /> <Label Content="{Binding Path=Level}" Height="28" Name="label4" Margin="0,0,0,4" HorizontalAlignment="Right" VerticalAlignment="Bottom" Width="65" /> </Grid>
这样,代码中对Result赋值,直接影响界面控件展示数值的变化:
/// <summary>
/// 活动方块到达底部时触发
/// </summary>
static public void ActivityBox_OnBottom(object sender, EventArgs e)
{
Result.GetInstance().CalculateScore(RemoveLine());
NewBoxReadyToDown();
}
这里根据消行函数返回值此处对result实例进行修改,界面数值(分数、级别)也同步变化。
最后,界面添加功能按钮,实现Window_KeyDown事件 OnGameover事件,游戏完成。
原文地址:http://www.cnblogs.com/lipan/archive/2010/09/20.html
相关文章推荐
- WPF游戏 菜鸟版俄罗斯方块
- 菜鸟做wpf绑定xml使遇到很多问题,跪求解
- 学习C#操纵Visio之五:做了个俄罗斯方块的游戏
- js写的打字游戏,功能非常简洁,菜鸟可以看看,高手就别来了
- C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(一)让物体动起来①
- C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):目录
- 通过俄罗斯方块浅谈游戏中的AI(二)AI策略
- C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三)让物体动起来③
- C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(十七) 完美精灵之八面玲珑(WPF Only)①
- C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(二十五)完美捕捉精灵之神器 -- HitTest
- C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(二十七)战斗前夜之构建动态障碍物系统
- C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(四)实现2D人物动画①
- C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(二十五)完美捕捉精灵之神器 -- HitTest
- C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(三十八)地图间的传送与切换
- C#开发WPF/Silverlight动画及游戏系列教程(Game Tutorial):(四十一)制作精美的Mini地图①
- Swift游戏开发之俄罗斯方块:No.3 二维数组
- Swift游戏开发之俄罗斯方块:No.5 Block Party
- C# WPF动画、游戏教程汇总
- WPF编游戏系列 之七 动画效果(2)
- WPF编游戏系列 之八 银行界面及金额校验