C#初学:2048小游戏的改进

这是中国大学mooc上C#网课的作业,原文件下载地址:Game2048.zip,里面已经实现了2048游戏,要做的改进如下:

  • 给程序加上显示分数的功能(score这个变量程序中已有了)。
  • 给程序加上选择模式的功能(gameMode这个变量已有了,想办法用上)。
  • 给程序加上记录最高分的功能(可以写到一个文本文件中)。如果用户得到的分数比记录高,则更新这个记录,并给用户以祝贺。
  • 其他方面的修改,如美化界面,加上下左右4个按钮(让用户可以使用鼠标)来完成,或者是其他方面的修改(就要发挥你的想像了)。

成品展示

  • 原程序运行后界面:在这里插入图片描述
  • 改进的程序运行后的界面:在这里插入图片描述
  • 改进后的完整代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace Game2048
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            Add_Handler();  //为容器中的按钮的click事件注册并绑定,发生时统一处理
            this.BackgroundImage = Image.FromFile("../../../pic.jpg");  //添加窗体背景图
        }

        const int N = 4;  //方块在纵向及横向上的个数
        private int[,] board; //记录每个方块上的数
        Button[] btns;
        private int score;  //当前分数
        private int highest;    //最高分
        bool renew = false; //记录是否更新了最高分

        int gameMode;
        string[] str1 = { "夏", "商", "周", "秦", "汉", "隋", "唐", "宋", "元", "明", "清" };
        string[] str2 = { "贱民", "良民", "九品", "八品", "七品", "六品", "五品", "四品", "三品", "二品", "一品"};
        string[] str3 = { "贱民", "良民", "士兵", "排长", "连长", "营长", "旅长", "师长", "军长", "司令", "大帅" };

        private void Add_Handler()
        {
            this.btnMode0.Click += new System.EventHandler(btn_ModeClick);
            this.btnMode1.Click += new System.EventHandler(btn_ModeClick);
            this.btnMode2.Click += new System.EventHandler(btn_ModeClick);
            this.btnMode3.Click += new System.EventHandler(btn_ModeClick);
            this.btnUp.Click += new System.EventHandler(btn_DirClick);
            this.btnDown.Click += new System.EventHandler(btn_DirClick);
            this.btnLeft.Click += new System.EventHandler(btn_DirClick);
            this.btnRight.Click += new System.EventHandler(btn_DirClick);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            board = new int[N, N];
            score = 0;
            gameMode = 0;

            this.Text = "Game2048";
            this.DoubleBuffered = true;

            ReadFile();  //读入文件,读取最高分数
            if (highest < 0)    //若读出的最高分为负数,则置最高分为0并写入文件
            {
                highest = 0;
                WriteFile();
            }
            lblScore.Text = "当前分数:" + score;
            lblHighest.Text = "最高分:" + highest;

            StartGame();
        }
        private void StartGame()
        {
            //设置两个不同的块为随机的2或4
            Random rnd = new Random();
            int n1 = rnd.Next(N * N);
            int n2;
            do
            {
                n2 = rnd.Next(N * N);
            }
            while (n1 == n2);
            board[n1 / N, n1 % N] = rnd.Next(2) * 2 + 2; //设为2或4
            board[n2 / N, n2 % N] = rnd.Next(2) * 2 + 2;
            
            InitialUI();
        }

        //初始化界面
        private void InitialUI()
        {
            //将所有容器的背景色均设为透明,但会使程序加载变慢
            gbxDirection.BackColor = Color.Transparent;
            gbxMode.BackColor = Color.Transparent;
            lblHighest.BackColor = Color.Transparent;
            lblScore.BackColor = Color.Transparent;
            //pnlBoard.BackColor = Color.Transparent;这个不建议使用,会让按钮加载、刷新都变慢
            //生成按钮
            GenerateAllButtons();
        }


        //产生所有的按钮
        private void GenerateAllButtons()
        {
            btns = new Button[N * N];

            int x0 = 5, y0 = 5, w = 60, d = w+5;

            for (int i = 0; i < btns.Length; i++)
            {
                Button btn = new Button();

                int r = i / N;  //行
                int c = i % N;  //列

                btn.Left = x0 + c * d;
                btn.Top = y0 + r * d;
                btn.Width = w;
                btn.Height = w;
                btn.Font = new Font("楷体", 16);

                btn.Text = GetTextOfButton(board[r, c]);
                btn.BackColor = GetColorOfButton(board[r, c]);

                btn.Visible = true;
                btns[i] = btn;
                this.pnlBoard.Controls.Add(btn);
            }
        }

        //更新界面
        private void RefreshUI()
        {
            RefreshAllButtons();
            lblScore.Text = "当前分数:" + score;    //更新当前分数
            if (score > highest)    //更新最高分并写入文件,置renew=true
            {
                renew = true;
                highest = score;
                lblHighest.ForeColor = Color.Red;   //改变最高分的字体颜色
                lblHighest.Text = "最高分:" + highest;
                WriteFile();
            }
        }
        private void RefreshAllButtons()
        {
            for (int i = 0; i < btns.Length; i++)
            {
                int r = i / N;  //行
                int c = i % N;  //列
                btns[i].Text = GetTextOfButton(board[r, c]);
                btns[i].BackColor = GetColorOfButton(board[r, c]);
            }
        }

        //得到方块上应有的文字
        string GetTextOfButton(int n)
        {
            if (n < 2) return "";

            int k = (int)Math.Log(n, 2) - 1; //若n==2,则k==1;若n==4,则k==2

            if (gameMode == 0)
            {
                return n.ToString();
            }
            else if (gameMode == 1)
            {
                return str1[k];
            }
            else if (gameMode == 2)
            {
                return str2[k];
            }
            else if (gameMode == 3)
            {
                return str3[k];
            }
            return "";
        }

        //得到方块上应有的颜色

        Color GetColorOfButton(int n)
        {
            if (n == 0) return Color.FromArgb(100, 200, 200, 200);

            int tmp = 230-(int)Math.Log(n, 2) * 20;
            return Color.FromArgb(250, tmp, tmp, 0);
        }


        //处理键盘消息
        //要注意的是:由于按钮等元素的存在,窗体得不到KeyDown事件,所以在覆盖ProcessCmdKey
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            bool bMoved = false;

            if (keyData == Keys.Right)
            {
                bMoved = rightMove();
            }
            else if (keyData == Keys.Left)
            {
                bMoved = leftMove();
            }
            else if (keyData == Keys.Down)
            {
                bMoved = downMove();
            }
            else if (keyData == Keys.Up)
            {
                bMoved = upMove();
            }

            if (bMoved)
            {
                generateNewData();
                RefreshUI();
                if (IsGameOver() == true)
                {
                    string mesg = "Game Over!\n你的得分为:" + score;
                    mesg = renew ? mesg += "\n祝贺你刷新了最高分!太棒了!" : mesg;  //若刷新了最高分则给出祝贺
                    MessageBox.Show(mesg);
                }
                return true;
            }
            
            return base.ProcessCmdKey(ref msg, keyData);
        }

        //产生新的随机数据
        private void generateNewData()
        {
            Random rnd = new Random();
            //随机找到一个空的块
            int nCount;
            do
            {
                nCount = rnd.Next(N * N);
            }
            while (board[nCount / N, nCount % N] != 0);

            board[nCount / N, nCount % N] = rnd.Next(2) * 2 + 2; //其值设为2或者4
        }

        #region 关于四个方向的业务逻辑
        private bool rightMove()
        {
            bool isMoved = false;
            for (int j = 0; j < board.GetLength(0); j++) //针对所有的行
            {
                int x1 = -1, y1 = -1, value1 = -1;// x2=-1, y2=-1,value2=-1;
                for (int i = board.GetLength(1) - 1; i > -1; i--) //从右边起,针对每个块进行处理
                {
                    if (board[j, i] == 0) continue; //空的块不处理
                    if (board[j, i] == value1)//如果与上一次找到的相等,则合并
                    {
                        board[x1, y1] = board[x1, y1] * 2;
                        board[j, i] = 0;
                        value1 = -1;
                        score += board[x1, y1];
                        isMoved = true;
                    }
                    else
                    {
                        int k;
                        for (k = i + 1; k < board.GetLength(1); k++) //向右找到一个非空的块
                        {
                            if (board[j, k] > 0)
                                break;
                        }
                        if (k - 1 != i)//如果这个非空的块左边有空位置
                        {
                            isMoved = true;
                            board[j, k - 1] = board[j, i]; //“移动”到这里(将其上的数字显示到这里)
                            board[j, i] = 0;
                        }
                        value1 = board[j, k - 1]; //记下这个值(非空的块)
                        x1 = j;
                        y1 = k - 1;
                    }
                }
            }
            return isMoved;
        }
        private bool upMove()
        {
            bool isMoved = false;
            for (int i = 0; i < board.GetLength(1); i++)
            {
                int x1 = -1, y1 = -1, value1 = -1;// x2=-1, y2=-1,value2=-1;
                for (int j = 0; j < board.GetLength(0); j++)
                {
                    if (board[j, i] != 0)
                    {
                        if (value1 < 0)
                        {
                            int k;
                            for (k = j - 1; k > -1; k--)
                            {
                                if (board[k, i] > 0)
                                    break;
                            }
                            board[k + 1, i] = board[j, i];
                            if (k + 1 != j)
                            {
                                board[j, i] = 0;
                                isMoved = true;
                            }
                            value1 = board[k + 1, i];
                            x1 = k + 1;
                            y1 = i;
                        }
                        else
                        {
                            if (board[j, i] == value1)//合并
                            {
                                board[x1, y1] = board[x1, y1] * 2;
                                board[j, i] = 0;
                                value1 = -1;
                                score += board[x1, y1];
                                isMoved = true;
                            }
                            else
                            {
                                int k;
                                for (k = j - 1; k > -1; k--)
                                {
                                    if (board[k, i] > 0)
                                        break;
                                }
                                board[k + 1, i] = board[j, i];
                                if (k + 1 != j)
                                {
                                    isMoved = true;
                                    board[j, i] = 0;
                                }
                                value1 = board[k + 1, i];
                                x1 = k + 1;
                                y1 = i;
                            }
                        }
                    }
                }

            }
            return isMoved;
        }

        private bool leftMove()
        {
            bool isMoved = false;
            for (int j = 0; j < board.GetLength(0); j++)
            {
                int x1 = -1, y1 = -1, value1 = -1;// x2=-1, y2=-1,value2=-1;
                for (int i = 0; i < board.GetLength(1); i++)
                {
                    if (board[j, i] != 0)
                    {
                        if (value1 < 0)
                        {
                            int k;
                            for (k = i - 1; k > -1; k--)
                            {
                                if (board[j, k] > 0)
                                    break;
                            }
                            board[j, k + 1] = board[j, i];
                            if (k + 1 != i)
                            {
                                isMoved = true;
                                board[j, i] = 0;
                            }
                            value1 = board[j, k + 1];
                            x1 = j;
                            y1 = k + 1;
                        }
                        else
                        {
                            if (board[j, i] == value1)//合并
                            {
                                board[x1, y1] = board[x1, y1] * 2;
                                board[j, i] = 0;
                                value1 = -1;
                                score += board[x1, y1];
                                isMoved = true;
                            }
                            else
                            {
                                int k;
                                for (k = i - 1; k > -1; k--)
                                {
                                    if (board[j, k] > 0)
                                        break;
                                }
                                board[j, k + 1] = board[j, i];
                                if (k + 1 != i)
                                {
                                    isMoved = true;
                                    board[j, i] = 0;
                                }
                                value1 = board[j, k + 1];
                                x1 = j;
                                y1 = k + 1;
                            }
                        }
                    }
                }

            }
            return isMoved;
        }

 

        private bool downMove()
        {
            bool isMoved = false;
            for (int i = 0; i < board.GetLength(1); i++)
            {
                int x1 = -1, y1 = -1, value1 = -1;// x2=-1, y2=-1,value2=-1;
                for (int j = board.GetLength(0) - 1; j > -1; j--)
                {
                    if (board[j, i] != 0)
                    {
                        if (value1 < 0)
                        {
                            int k;
                            for (k = j + 1; k < board.GetLength(0); k++)
                            {
                                if (board[k, i] > 0)
                                    break;
                            }
                            board[k - 1, i] = board[j, i];
                            if (k - 1 != j)
                            {
                                board[j, i] = 0;
                                isMoved = true;
                            }
                            value1 = board[k - 1, i];
                            x1 = k - 1;
                            y1 = i;
                        }
                        else
                        {
                            if (board[j, i] == value1)//合并
                            {
                                board[x1, y1] = board[x1, y1] * 2;
                                board[j, i] = 0;
                                value1 = -1;
                                score += board[x1, y1];
                                isMoved = true;
                            }
                            else
                            {
                                int k;
                                for (k = j + 1; k < board.GetLength(0); k++)
                                {
                                    if (board[k, i] > 0)
                                        break;
                                }
                                board[k - 1, i] = board[j, i];
                                if (k - 1 != j)
                                {
                                    board[j, i] = 0;
                                    isMoved = true;
                                }
                                value1 = board[k - 1, i];
                                x1 = k - 1;
                                y1 = i;
                            }
                        }
                    }
                }

            }
            return isMoved;
        }

        #endregion

        private bool IsGameOver()
        {
            int nCount = 0; //计算非空的格子的个数
            for (int i = 0; i < board.GetLength(0); i++)
            {
                for (int j = 0; j < board.GetLength(1); j++)
                {
                    if (board[i, j] > 0) nCount++;                        
                }
            }
            if (nCount != N * N) return false;

            //如果满了,并且没有可以相邻相同(可合并),则GameOver
            for (int i = 0; i < board.GetLength(0) - 1; i++)
            {
                for (int j = 0; j < board.GetLength(1) - 1; j++)
                {
                    if (board[i, j] == 0) continue;
                    if (board[i, j] == board[i + 1, j] || board[i, j] == board[i, j + 1])
                        return false;
                }
            }
            return true;
        }
        
        private void btn_ModeClick(object sender, EventArgs e)
        {
            Button btn = (Button)sender;
            gameMode = btn.TabIndex;
            RefreshAllButtons();
        }

        private void btn_DirClick(object sender, EventArgs e)
        {
            Button btn = (Button)sender;
            bool bMoved = false;
            
            if (btn.TabIndex == 0)
            {
                bMoved = upMove();
            }
            else if (btn.TabIndex == 1)
            {
                bMoved = downMove();
            }
            else if (btn.TabIndex == 2)
            {
                bMoved = leftMove();
            }
            else if (btn.TabIndex == 3)
            {
                bMoved = rightMove();
            }

            if (bMoved)
            {
                generateNewData();
                RefreshUI();
                if (IsGameOver() == true)
                {
                    string msg = "Game Over!\n你的得分为:" + score;
                    msg = renew ? msg += "\n祝贺你刷新了最高分!太棒了!" : msg;  //若刷新了最高分则给出祝贺
                    MessageBox.Show(msg);
                }
                    
            }
        }

        private void ReadFile()   //读取最高分文件,没有则创建
        {
            string path = @"..\..\..\Highest Score.txt";
            
            FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite);
            StreamReader sr = new StreamReader(fs, Encoding.UTF8);

            if (sr.Peek() == -1)    //若文件中没有内容,则写入0(即最高分为0)
            {
                StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
                sw.Write(0);
                sw.Flush(); //一定要用,不然流将无法读取
                sw.Close();
                sr = new StreamReader(fs, Encoding.UTF8);   //需要重新创建读取流
            }
            highest = int.Parse(sr.ReadToEnd());        //不能用int,读出来不对
            
            sr.Close();
            fs.Close();
        }

        private void WriteFile()    //写入最高分到文件中
        {
            string path = @"..\..\..\Highest Score.txt";
            StreamWriter sw = new StreamWriter(path,false,Encoding.UTF8);
            sw.Write(highest);
            sw.Flush();
            sw.Close();
        }
    }

}

改进过程中犯过的傻事

  1. 模式的选择控件应该用listbox来做的,只用按钮的话当模式多了的时候占位置就太多了。而且写更改模式的代码也会更简单。
  2. 统一处理几个按钮的事件可以按照如下操作:
        private void Add_Handler()
        {
            this.btnMode0.Click += new System.EventHandler(btn_ModeClick);
            this.btnMode1.Click += new System.EventHandler(btn_ModeClick);
            this.btnMode2.Click += new System.EventHandler(btn_ModeClick);
            this.btnMode3.Click += new System.EventHandler(btn_ModeClick);
            this.btnUp.Click += new System.EventHandler(btn_DirClick);
            this.btnDown.Click += new System.EventHandler(btn_DirClick);
            this.btnLeft.Click += new System.EventHandler(btn_DirClick);
            this.btnRight.Click += new System.EventHandler(btn_DirClick);
        }
        private void btn_ModeClick(object sender, EventArgs e)
        {
            Button btn = (Button)sender;
            gameMode = btn.TabIndex;
            RefreshAllButtons();
        }

        private void btn_DirClick(object sender, EventArgs e)
        {
            Button btn = (Button)sender;
            bool bMoved = false;
            
            if (btn.TabIndex == 0)
            {
                bMoved = upMove();
            }
            else if (btn.TabIndex == 1)
            {
                bMoved = downMove();
            }
            else if (btn.TabIndex == 2)
            {
                bMoved = leftMove();
            }
            else if (btn.TabIndex == 3)
            {
                bMoved = rightMove();
            }

            if (bMoved)
            {
                generateNewData();
                RefreshUI();
                if (IsGameOver() == true)
                {
                    string msg = "Game Over!\n你的得分为:" + score;
                    msg = renew ? msg += "\n祝贺你刷新了最高分!太棒了!" : msg;  //若刷新了最高分则给出祝贺
                    MessageBox.Show(msg);
                }
                    
            }
        }
  • 读取最高分文件
        private void ReadFile()   //读取最高分文件,没有则创建
        {
            string path = @"..\..\..\Highest Score.txt";
            
            FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite);
            StreamReader sr = new StreamReader(fs, Encoding.UTF8);

            if (sr.Peek() == -1)    //若文件中没有内容,则写入0(即最高分为0)
            {
                StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
                sw.Write(0);
                sw.Flush(); //一定要用,不然流将无法读取
                sw.Close();
                sr = new StreamReader(fs, Encoding.UTF8);   //需要重新创建读取流
            }
            highest = int.Parse(sr.ReadToEnd());        //不能用sr.Read(),读出来不对
            
            sr.Close();
            fs.Close();
        }
  • 其中sw.Flush();一定要用,不然后面读取文件的时候会显示“流无法读取”。

  • 执行if语句的最后要用sr = new StreamReader(fs, Encoding.UTF8);不然就会显示“不能读取已经关闭的文件”。

  • 文件的编码方式要指明为utf-8,用default或其他编码都不行。

  • highest = int.Parse(sr.ReadToEnd()); 这条语句不能写成 highest = sr.Read(),读出来的数字不对。

  • 不建议用FileStream读写,byte[ ]麻烦。

  • 吐槽一下notepad3,用它看最高分文本文件的时候,即使是指定了正确的编码也显示的是乱码,害得我以为自己代码写错了,找了好久的原因。后来发现用记事本或者notepad++就正常显示,就把notepad3卸了换成notepad++了。

  • 切换模式后执行的语句应该是RefreshAllButtons();来刷新按钮,而不是StartGame();来重新开始游戏,否则无法正常运行(按键盘或者点方向button都不执行相应的代码)。一开始我写的是StartGame(); 然后找了好久好久的原因,才发现自己用错函数了。(我玩游戏的时间都没了啊!)

Tips: 让容器的背景色变透明可以用如lblScore.BackColor = Color.Transparent;的语句。

  • 4
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值