2048最近似乎热度已过,但是并不影响我山寨一个它。周末闲来无事,终于付诸实现,包含winform和js版本。有图有真相
实现思路:
1、通过二维数组data=int[4][4]来存储游戏数据,初始全部为0
2、游戏初始在4X4布局中随机位置产生2个随机数(只随机2,4)
//初始化游戏数据 void InitGame() { ClearGame(); Random rand = new Random(); int pos1 = Convert.ToInt32(rand.Next(16)); SetData(pos1, rand.NextDouble() < 0.8 ? 2 : 4); int pos2 = GetRandPos(); SetData(pos2, rand.NextDouble() < 0.8 ? 2 : 4); }
分解:
a、随意位置产生一个随机数
b、在剩下的随机空白位置产生一个随机数,此方法在游戏移动后产生一个随机数时也要用到。随机空白位置的产生:遍历数据数组data,取得所有位置为空的集合pos,data中数据为0的即对应位置为空,可产生随机数;在pos中产生随机位置。
//随机产生空格处的坐标位置 int GetRandPos() { List<int> pos = new List<int>(); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (data[i,j] == 0) pos.Add(4 * i + j);//数组坐标刚好可以通过pos/4,pos%4得到 } } Random r = new Random(); int rand = r.Next(pos.Count); return pos[rand]; }
这里要注意data[i][j]和布局位置的对应关系:data[i][j]=>pos[4*i+j],pos[i]=>data[i/4][i%4];
3、移动操作:先合并再移动,以左移为例
a)、查找可合并的元素合并,以一次合并为例:
0 | 1 | 2 | 3 |
1) data1=从左到右第一个不为0的元素,假设是第j位置pos[j]
2) data2=pos[j+1]
3) a. data1==data2则进行合并操作
b. data2==0,data2下移一个
c. data2!=0&&data1!=data2,不能合并
//先合并元素 for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { int tb = data[i,j];//取一个元素值 if (tb != 0) { for (int k = j + 1; k < 4; k++) { int nexttb = data[i,k];//下一元素值 if (nexttb != 0) { if (tb == nexttb) {//相等合并,计算分数,设置移动状态 data[i,j] = tb * 2; data[i,k] = 0; totalscore += tb * 2; moveflag = true; } break; } } } } }
b)、合并完成后移动元素至最左端
前面的元素为空就依次把后面不为空的元素往前移,移动后后面元素置为空
//合并之后移动元素 for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { int tb = data[i,j];//取一个元素值 if (tb == 0) {//取得元素值为0表示当前位置为空 for (int k = j + 1; k < 4; k++) { int nexttb = data[i,k];//下一个元素值 if (nexttb != 0) {//下一元素值不为0移动,设置移动标志 data[i,j] = nexttb; data[i,k] = 0; moveflag = true; break; } } } } }
4、判断游戏是否结束,未结束则产生一个随机数
判断游戏是否结束:有元素为0或者有可合并的元素可继续,否则结束
//产生单个随机数 void GetRandNum() { if (IsGameOver()) { if (MessageBox.Show("失败,是否重新开始?", "GameOver", MessageBoxButtons.YesNo)== DialogResult.Yes) { InitGame(); } else return; } Random rand=new Random (); if(moveflag) SetData(GetRandPos(), rand.NextDouble() < 0.8 ? 2 : 4); moveflag = false;//重置是否移动标识 }
5、通过data数据更新界面
//根据data更新td内容,并设置样式 void InitData() { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { var td = btns.Find(m => m.Name == "btn" + (4 * i + j).ToString()); //btns[4 * i + j]; string tdval = td.Text.Length == 0 ? "0" : td.Text; if (data[i, j].ToString() != tdval) td.Text = data[i, j] == 0 ? "" : data[i, j].ToString(); //此处可设置数字样式 } } lblscore.Text = totalscore.ToString(); }
其他方向移动操作依葫芦画瓢,至此,一个简略山寨版2048完成。
需要注意的是Winform版本中要注意键盘上下左右事件方法要重写:
//重写上下左右键响应方法 protected override bool ProcessDialogKey(Keys keyData) { switch (keyData) { case Keys.Up: MoveUp(); break; case Keys.Down: MoveDown(); break; case Keys.Left: MoveLeft(); break; case Keys.Right: MoveRight(); break; default: break; } return base.ProcessDialogKey(keyData); }
通过下面的KeyDown事件监控没有效果,本人对winform不熟悉,期待大神解答原因
private void Form1_KeyDown(object sender, KeyEventArgs e) { if (e.KeyCode == Keys.Left) MoveLeft(); }
参考文章:http://blog.csdn.net/touchsnow/article/details/22985527