加强版的俄罗斯,可以带回去给我妹妹玩了。
俄罗斯方块,大家很熟悉的一个游戏,你看到的全是控件组成的形状,预览窗口一看就知道不是控件了,加了点绘图上去。
首先就谈界面的设计,游戏预览和菜单三大部分,控件动态生成,方块肯定不能是同一个形状的,所以加上一个方块父类,编写子类,提高扩展性。
有了形状肯定是要会动的,不会动的游戏谁玩啊,形状可以移动位置,可以变换形状,还要注意很多情况,比如不能超过边界等,这些功能我归为辅助类了,帮助游戏功能的实现类。
这是大致的模块,通过界面游戏来添加事件,借助辅助类来完善游戏功能,提高可读性,不然代码全塞到界面上头晕,而且以后要加功能也很难找,当然,这三大模块还可以细分,大致的设计也都介绍了一遍,下面给出的代码注释的不能在详细了,所以功能方面我就不介绍了,相信下面会给出满意的介绍。
//游戏类
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Drawing.Drawing2D; 7 using System.IO; 8 using System.Linq; 9 using System.Media; 10 using System.Text; 11 using System.Threading.Tasks; 12 using System.Windows.Forms; 13 14 namespace ShapeUI 15 { 16 public partial class forShape : Form 17 { 18 public forShape() 19 { 20 InitializeComponent(); 21 } 22 private int index = 0; 23 private SoundPlayer music = null; 24 private static List<Control> conList = new List<Control>();//预览窗口 25 private Graphics g = null; 26 private void forShape_Load(object sender, EventArgs e) 27 { 28 music = new SoundPlayer();//.wav文件播放 29 } 30 private bool Stop(int key) //结束操作 31 { 32 bool b = true; 33 foreach (Control c in gpbGame.Controls) 34 { 35 if (c.Enabled) 36 { 37 int x = c.Location.X; 38 int y = c.Location.Y; 39 //判断是否撞墙 40 if (x >= gpbGame.Width - Helper.Over && key == 68) 41 { 42 b = false; 43 break; 44 } 45 else if (x <= 0 && key == 65) 46 { 47 b = false; 48 break; 49 } 50 else if (y >= gpbGame.Height - Helper.Over && key == 83) 51 { 52 b = false; 53 Enableds();//如果是下落操作碰到墙则固定形状 54 break; 55 } 56 } 57 } 58 return b; 59 } 60 public void Enableds()//固定 61 { 62 foreach (Control c in gpbGame.Controls) 63 { 64 if (c.Enabled) 65 { 66 c.KeyDown -= buttons_KeyDown;//消除按钮事件 67 c.Enabled = false; 68 int Red = new Random().Next(150, 250); 69 int Green = new Random().Next(0, 250); 70 int Blue = (Red + Green > 400)?0:400-Red - Green; 71 Blue = (Blue > 255) ? 255 : Blue; 72 //固定变换颜色 73 c.BackColor = Color.FromArgb(Red, Green, Blue); 74 } 75 //判断游戏结束 76 if (!c.Enabled && c.Location.Y <= 30) 77 { 78 tirShape.Stop(); 79 MessageBox.Show("Game Over");//游戏结束,初始化游戏窗口 80 lblGrade.Text = "0";//初始化等级 81 lblScore.Text = "0";//初始化分数 82 Helper.Speed = 550; 83 tirShape.Interval = Helper.Speed; 84 lblSpeed.Text = Helper.Speed.ToString();//初始化速度 85 this.gpbGame.Controls.Clear(); 86 return; 87 } 88 } 89 GetShape();//生成新的形状 90 } 91 private void GetShape() //生成形状 92 { 93 Helper.Score(gpbGame, lblScore);//加分判断 94 lblGrade.Text = Helper.Grade.ToString();//刷新等级 95 tirShape.Interval = Helper.Speed; 96 lblSpeed.Text = Helper.Speed.ToString();//刷新速度 97 IShapes s = null; 98 if(checkAdd.Checked)//得分模式 99 s = CreateShape.createShape(-1); 100 else 101 s = CreateShape.createShape(new Random().Next(1, 8));//获取形状 102 Bitmap bmp = new Bitmap(penGame.Width, penGame.Height);//创建一个位图 103 //先要查看list集合是否为空,不为空表示有形状,把上次结果,也就是下面保存的形状加入游戏窗口中去 104 if (conList.Count > 0) 105 { 106 foreach (Control b in conList)//在游戏窗口显示形状 107 { 108 b.KeyDown += buttons_KeyDown;//添加事件 109 gpbGame.Controls.Add(b); 110 b.Focus();//获取焦点 111 } 112 conList.Clear();//清空集合,保持只有一组形状 113 } 114 //预览形状,把要预览的形状保存到集合中去 115 foreach (Control c in s.Getbuttons()) 116 { 117 if (Helper.c.Length > 0) 118 { 119 c.Text = Helper.c[index].ToString();//添加字符 120 index++; 121 if (index == Helper.c.Length) 122 index = 0; 123 } 124 //调整形状出现在游戏窗口中的位置并存储到集合,此处更改的话会影响得分模式形状位置 125 c.Location = new Point(c.Location.X + Helper.Over,c.Location.Y); 126 conList.Add(c); 127 g = Graphics.FromImage(bmp);//在位图上创建画布 128 Brush brush = new SolidBrush(Color.Blue); //定义一个画刷 129 int x = c.Location.X; 130 int y = c.Location.Y; 131 //调整形状在预览窗口中出现的位置 132 if (Helper.Over == 20) 133 { 134 x = x - Helper.Over - 10; 135 y = y + Helper.Over * 4 + 20; 136 } 137 else 138 { 139 x = x - Helper.Over; 140 y = y + Helper.Over * 4 - 20; 141 } 142 g.DrawRectangle(new Pen(Brushes.Black, 1), x, y, Helper.Over, Helper.Over);//画矩形 143 g.FillRectangle(Brushes.Purple, x + 5, y + 5, Helper.Over - 10, Helper.Over - 10);//填充 144 g.Save(); 145 } 146 penGame.BackgroundImage = bmp;//把画好的图形作为预览窗口的背景图片显示出来 147 g.Dispose();//回收绘图资源 148 } 149 private void buttons_KeyDown(object sender, KeyEventArgs e)//键盘事件 150 { 151 MoveShape(e.KeyValue);//移动判断 152 } 153 private bool Impacts(int x, int y, int key) //向下碰撞到方块 154 { 155 bool b = true; 156 foreach (Control c in gpbGame.Controls) 157 { 158 if (c.Enabled) 159 { 160 //判断是否碰撞到方块 161 if (c.Location.X == x && c.Location.Y == y) 162 { 163 if (key == 83)//如果是向下降落的操作便固定 164 Enableds(); 165 b = false; 166 break; 167 } 168 } 169 } 170 return b; 171 } 172 private void MoveShape(int key)//移动位置 173 { 174 if (checkZuoSi.Checked)//急速模式 175 Helper.b = true; 176 else 177 Helper.b = false; 178 if (Stop(key))//是否碰撞 179 { 180 //预计下一步将要发生的事情 181 foreach (Control c in gpbGame.Controls) 182 { 183 if (!c.Enabled) 184 { 185 int x = c.Location.X; 186 int y = c.Location.Y; 187 if (key == 65) 188 x += Helper.Over; 189 else if (key == 68) 190 x -= Helper.Over; 191 else if (key == 87) 192 { 193 //ChangeShape(); 194 } 195 else if (key == 83) 196 { 197 y -= Helper.Over; 198 } 199 if (!Impacts(x, y, key))//判断是否与方块碰撞,是则结束 200 return; 201 } 202 } 203 //如果预计通过则实施操作 204 foreach (Control c in gpbGame.Controls) 205 { 206 if (c.Enabled) 207 { 208 int x = c.Location.X; 209 int y = c.Location.Y; 210 if (key == 65) 211 x -= Helper.Over; 212 else if (key == 68) 213 x += Helper.Over; 214 else if (key == 87) 215 { 216 Helper.ChangeShape(gpbGame);//变换形状操作 217 return;//变换形状后结束方法,否则会导致循环变换 218 } 219 else if (key == 83) 220 { 221 y += Helper.Over; 222 } 223 c.Focus();//防止失去焦点 224 c.Location = new Point(x, y);//移动操作 225 } 226 } 227 } 228 } 229 private void btnBegin_Click(object sender, EventArgs e)//开始游戏 230 { 231 conList.Clear();//清空预览形状 232 if (btnBegin.Text == "游戏开始") 233 { 234 btnBegin.Text = "重新开始"; 235 checkMax.Enabled = false; 236 } 237 if (checkMax.Checked) 238 { 239 this.gpbGame.Width = 520; 240 this.gpbGame.Height = 600; 241 this.penGame.Width = 400; 242 this.penGame.Height = 350; 243 Helper.Over = 40; 244 Helper.Font = 16; 245 } 246 else 247 { 248 this.gpbGame.Width = 260; 249 this.gpbGame.Height = 380; 250 this.penGame.Width = 190; 251 this.penGame.Height = 160; 252 Helper.Over = 20; 253 Helper.Font = 8; 254 } 255 if (File.Exists("file/c.wav")) 256 { 257 music = new SoundPlayer("file/c.wav");//附加指定的.wav文件 258 music.Play(); //开始播放 259 } 260 if (File.Exists("file/c.txt") && File.ReadAllText("file/c.txt").ToCharArray().Count() > 0) 261 Helper.c = File.ReadAllText("file/c.txt").ToCharArray(); 262 else 263 Helper.c = new Char[0]; 264 this.Width = gpbGame.Width + 480; 265 this.Height = gpbGame.Height + 110; 266 267 lblGrade.Text = "0";//初始化等级 268 lblScore.Text = "0";//初始化分数 269 Helper.Speed = 550; 270 tirShape.Interval = Helper.Speed; 271 lblSpeed.Text = Helper.Speed.ToString();//初始化速度 272 this.gpbGame.Controls.Clear();//初始化游戏窗口 273 GetShape();//生成形状 274 GetShape();//生成形状 275 Bitmap bmp = new Bitmap(gpbGame.Width, gpbGame.Height); 276 Graphics g = Graphics.FromImage(bmp); 277 //绘制边框 278 g.DrawRectangle(new Pen(Brushes.Black, 1), 1, 1, gpbGame.Width-3, gpbGame.Height-3); 279 //在控件上绘制网格 280 Pen p = null;//绘制面板 281 for (int i = Helper.Over; i <= gpbGame.Height; i += Helper.Over) 282 { 283 p = new Pen(Brushes.Gray, 1); 284 p.DashStyle = DashStyle.Dot; 285 g.DrawLine(p, 0, i, gpbGame.Width, i); 286 } 287 for (int i = Helper.Over; i <= gpbGame.Width; i += Helper.Over) 288 { 289 p = new Pen(Brushes.White, 1); 290 p.DashStyle = DashStyle.Dot; 291 g.DrawLine(p, i, Helper.Over, i, gpbGame.Height); 292 } 293 gpbGame.BackgroundImage = bmp; 294 g.Dispose(); 295 tirShape.Start(); 296 } 297 298 private void btnEnd_Click(object sender, EventArgs e)//游戏结束 299 { 300 tirShape.Stop(); 301 music.Stop(); 302 Application.Exit(); 303 } 304 305 private void tirShape_Tick(object sender, EventArgs e)//计时器 306 { 307 MoveShape(83);//默认自动下降操作 308 } 309 310 private void btnStop_Click(object sender, EventArgs e)//暂停游戏 311 { 312 if (tirShape.Enabled) 313 { 314 tirShape.Stop(); 315 music.Stop(); 316 forStop f = new forStop(); 317 f.ShowDialog();//暂停中 318 tirShape.Start();//暂停结束 319 music.Play(); 320 } 321 } 322 323 private void btnCue_Click(object sender, EventArgs e)//游戏操作提示 324 { 325 forCue f = new forCue();//提示 326 f.ShowDialog();//显示提示 327 } 328 329 private void checkMax_CheckedChanged(object sender, EventArgs e) 330 { 331 //if(checkMax.Checked) 332 //{ 333 // forShape f = new forShape(40); 334 // f.Show(); 335 //} 336 } 337 } 338 }
//辅助类
1 using System; 2 using System.Collections.Generic; 3 using System.Drawing; 4 using System.Linq; 5 using System.Text; 6 using System.Threading.Tasks; 7 using System.Windows.Forms; 8 9 namespace ShapeUI 10 { 11 public static class Helper 12 { 13 private static int s; 14 public static bool b = false; 15 public static int Speed { get { return s; } set { if (b)s = 100; else s = value; } }//降落速度 16 public static int Grade { get; set; }//等级 17 public static int Over { get; set; }//方块宽度 18 public static char[] c {get;set;}//形状文字字符集 19 public static int Font { get; set; }//形状字体大小 20 public static List<button> Getbuttons(int[,] points) //组成形状 21 { 22 List<button> bs = new List<button>(); 23 for (int i = 0; i < 4; i++) 24 { 25 button b = new button(); 26 b.Location = new Point(points[0, i] * (Helper.Over / 20), points[1, i] * (Helper.Over / 20) - 80);//初始化坐标 27 if (i == 1) 28 { 29 b.Tag = "point";//定义原点 30 } 31 else 32 b.Tag = ""; 33 bs.Add(b); 34 } 35 return bs; 36 } 37 public static void Score(GroupBox gpbGame, Label lblScore)//得分 38 { 39 int length = gpbGame.Width / Helper.Over; 40 for (int i = 0; i < length; i++)//没行都判断是否满足加分条件 41 { 42 AddScore(i,gpbGame,lblScore);//加分操作 43 } 44 } 45 public static void AddScore(int rowPage, GroupBox gpbGame,Label lblScore) 46 { 47 int count = 0; 48 int length = gpbGame.Width / Helper.Over;//列数 49 List<button> list = new List<button>(); 50 foreach (Control c in gpbGame.Controls) 51 { 52 if (c is button && !c.Enabled && c.Location.Y == gpbGame.Height - Helper.Over * rowPage)//判断是否为指定行的区域 53 { 54 count++; 55 list.Add(c as button);//要删除的控件 56 } 57 if (count >= length) 58 { 59 int score = Convert.ToInt32(lblScore.Text); 60 lblScore.Text = (score += 1).ToString();//刷新分数 61 if(score == 5)//分数等级 62 { 63 Grade = 1; 64 Speed = 550; 65 }else if(score == 15) 66 { 67 Grade = 2; 68 Speed = 450; 69 } 70 else if (score == 35) 71 { 72 Grade = 3; 73 Speed = 400; 74 } 75 else if (score == 80) 76 { 77 Grade = 4; 78 Speed = 350; 79 } 80 else if (score == 200) 81 { 82 Grade = 5; 83 Speed = 300; 84 MessageBox.Show("恭喜上仙修成正果天下无敌!"); 85 } 86 foreach (button b in list) 87 { 88 b.Dispose();//引用类型会同步所有的地址 89 } 90 91 Subtract(count, rowPage,gpbGame);//位置下降 92 foreach (Control b in gpbGame.Controls) 93 { 94 if (b is button && !b.Enabled && b.Location.Y == gpbGame.Height - Helper.Over * rowPage) 95 { 96 count--; 97 if (count == 0)//在次满足等分条件递归调用加分 98 Score(gpbGame,lblScore); 99 } 100 } 101 return; 102 } 103 } 104 } 105 public static void Subtract(int count, int rowPage, GroupBox gpbGame)//减行 106 { 107 foreach (Control bb in gpbGame.Controls) 108 { 109 if (bb is button && !bb.Enabled && bb.Location.Y == gpbGame.Height - Helper.Over * rowPage) 110 { 111 count--;//判断整行是否有控件 112 } 113 } 114 foreach (Control b in gpbGame.Controls) 115 { 116 //如果该行是空的则下降一行 117 if (count == 13 && !b.Enabled && b.Location.Y <= gpbGame.Height - Helper.Over * rowPage) 118 { 119 int y = b.Location.Y; 120 b.Location = new Point(b.Location.X, y += Helper.Over); 121 } 122 } 123 } 124 public static void ChangeShape(GroupBox gpbGame)//变换位置 125 { 126 int pointx = 0; 127 int pointy = 0; 128 //先取到原点坐标 129 foreach (Control c in gpbGame.Controls) 130 { 131 if (c.Enabled)//原点坐标为100,40 132 { 133 if (c.Tag.ToString() == "point") 134 { 135 pointx = c.Location.X;//得到原点坐标 136 pointy = c.Location.Y; 137 break; 138 } 139 } 140 } 141 //开始变换坐标 142 bool b = true;//是否可变换 143 foreach (Control c in gpbGame.Controls) 144 { 145 if (c.Enabled)//原点坐标为第二个方块坐标 146 { 147 int x = c.Location.X - pointx; 148 int y = -c.Location.Y + pointy;//得到相对坐标 149 int X = y + pointx; 150 int Y = x + pointy;//相对于原点的坐标 151 //判断方块是否超出边界 152 if (X >= gpbGame.Width || X < 0 || Y > gpbGame.Height || Y < 0) 153 { 154 b = false; 155 //c.BackColor = Color.Black; 156 break; 157 } 158 //判断方块是否碰到方块 159 foreach (Control cc in gpbGame.Controls) 160 { 161 if (!cc.Enabled) 162 { 163 if (X == cc.Location.X && Y == cc.Location.Y) 164 { 165 b = false; 166 break; 167 } 168 } 169 } 170 } 171 } 172 if (b)//判断操作可行 173 { 174 foreach (Control c in gpbGame.Controls) 175 { 176 if (c.Enabled)//原点坐标为第二个方块坐标 177 { 178 int x = c.Location.X - pointx; 179 int y = -c.Location.Y + pointy;//得到相对坐标 180 c.Location = new Point(y + pointx, x + pointy);//变换坐标 181 } 182 } 183 } 184 } 185 186 } 187 }
获取形状类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace ShapeUI 8 { 9 public class CreateShape 10 { 11 public static IShapes createShape(int shape)//获取形状坐标集合 12 { 13 IShapes s = null;//形状类 14 switch(shape) 15 { 16 case 1: 17 s = new Shape1(); 18 break; 19 case 2: 20 s = new Shape2(); 21 break; 22 case 3: 23 s = new Shape3(); 24 break; 25 case 4: 26 s = new Shape4(); 27 break; 28 case 5: 29 s = new Shape5(); 30 break; 31 case 6: 32 s = new Shape6(); 33 break; 34 case 7: 35 s = new Shape7(); 36 break; 37 default: 38 s = new ShapeCheat(); 39 break; 40 } 41 return s; 42 } 43 } 44 }
接口类button类与形状子类等
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ShapeUI { public interface IShapes { List<button> Getbuttons(); } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ShapeUI { public class Shape1 : IShapes { //山字型 private int[,] points = new int[,] { {80,100,120,100 }, {40,40,40,20 } };//初始化形状坐标 public List<button> Getbuttons()//返回一个形状 { return Helper.Getbuttons(points); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ShapeUI { public class Shape2 : IShapes { //一字型 private int[,] points = new int[,] { { 80, 100, 120, 140 }, { 40, 40, 40, 40 } };//初始化形状坐标 public List<button> Getbuttons() { return Helper.Getbuttons(points); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ShapeUI { public class Shape3 : IShapes { //田字型 private int[,] points = new int[,] { { 80, 100, 80, 100 }, { 40, 40, 60, 60 } };//初始化形状坐标 public List<button> Getbuttons() { return Helper.Getbuttons(points); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ShapeUI { public class Shape4 : IShapes { //t字型 private int[,] points = new int[,] { { 80, 80, 100, 120 }, { 40, 60, 60, 60 } };//初始化形状坐标 public List<button> Getbuttons() { return Helper.Getbuttons(points); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ShapeUI { public class Shape5 : IShapes { //n字型 private int[,] points = new int[,] { { 80, 80, 100, 100 }, { 40, 60, 60, 80 } };//初始化形状坐标 public List<button> Getbuttons() { return Helper.Getbuttons(points); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ShapeUI { public class Shape6 : IShapes { //L字型 private int[,] points = new int[,] { { 80, 100, 120, 80 }, { 40, 40, 40, 60 } };//初始化形状坐标 public List<button> Getbuttons()//返回一个形状 { return Helper.Getbuttons(points); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ShapeUI { public class Shape7 : IShapes { //z字型 private int[,] points = new int[,] { { 80, 80, 60, 60 }, { 40, 60, 60, 80 } };//初始化形状坐标 public List<button> Getbuttons()//返回一个形状 { return Helper.Getbuttons(points); } } } using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ShapeUI { public class ShapeCheat : IShapes { //得分型 private int[,] points1 = new int[,] { { -20,0, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240}, { 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 } };//初始化形状坐标 private int[,] points2 = new int[,] { { -20, 0, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240 }, { 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40 } };//初始化形状坐标 private int[,] points3 = new int[,] { { -20, 0, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 220, 240 }, { 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60 } };//初始化形状坐标 public List<button> Getbuttons() { List<button> bs = new List<button>(); for (int i = 0; i < 13; i++) { button b1 = new button(); button b2 = new button(); button b3 = new button(); b1.Location = new Point(points1[0, i] * (Helper.Over / 20), points1[1, i] * (Helper.Over / 20)-40);//初始化坐标 b2.Location = new Point(points2[0, i] * (Helper.Over / 20), points2[1, i] * (Helper.Over / 20) - 40);//初始化坐标 b3.Location = new Point(points3[0, i] * (Helper.Over / 20), points3[1, i] * (Helper.Over / 20) - 40);//初始化坐标 if (i == 1) { b1.Tag = "point";//定义原点 b2.Tag = "point"; b3.Tag = "point"; } else { b1.Tag = ""; b2.Tag = ""; b3.Tag = ""; } bs.Add(b1); bs.Add(b2); bs.Add(b3); } return bs; } } } using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace ShapeUI { public class button : Button { public button() //重写控件 { this.Width = Helper.Over; this.Height = Helper.Over; this.Font = new System.Drawing.Font("宋体", Helper.Font); BackColor = Color.YellowGreen; } } }