c#俄罗斯方块游戏

一、设计思路

一个背景数组,一个方块数组。将方块数组放入背景数组时,需要一个坐标进行定位,即在两个坐标系之间存在一个映射关系。

二、form内代码

ErLuoSiClass elsClass = new ErLuoSiClass();

        /*游戏开始 按钮事件*/
        private void button1_Click(object sender, EventArgs e)
        {
            elsClass.CellWidthCount = 12;
            elsClass.CellHeightCount = 16;

            elsClass.InitErLuoSi();
            elsClass.winHandle = elsPanel.Handle;

            elsPanel.Width = elsClass.Width;
            elsPanel.Height = elsClass.Height;

            elsClass.ErLuoSiDraw();

            button1.Enabled = false;

            //随机生成当前 block
            elsClass.GetInitBlockData();

            //定时器开始
            timer1.Enabled = true;
        }

        /*俄罗斯方块重绘事件*/
        private void elsPanel_Paint(object sender, PaintEventArgs e)
        {
            if (elsClass.elsStartFlag)
                elsClass.ErLuoSiDraw();
        }

        /*键盘操作*/
        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Down)
            {
                //先判断是否可以下移
                if(elsClass.CanDown())
                    elsClass.currentX++;//下移
            }
            else if (e.KeyCode == Keys.Left)
            {
                //先判断是否可以左移
                if (elsClass.CanLeft())
                    elsClass.currentY--;//左移
            }
            else if (e.KeyCode == Keys.Right)
            {
                //先判断是否可以右移
                if (elsClass.CanRight())
                    elsClass.currentY++;//右移
            }
            else if (e.KeyCode == Keys.Up)
            {
                //先判断是否可以变换
                if(elsClass.CanChange())
                    elsClass.ChangeBlock();//变换
            }
            else if (e.KeyCode == Keys.Space)
            {
                //空格键暂停、开始
                if (timer1.Enabled)
                    timer1.Enabled = false;
                else
                    timer1.Enabled = true;
            }
            //重绘 此处也重绘 在界面上反映快点
            elsClass.ErLuoSiDraw();
            elsPanel.Focus();
        }

        /*定时器事件*/
        private void timer1_Tick(object sender, EventArgs e)
        {
            if (elsClass.CanDown())
            {
                elsClass.currentX++;
            }
            else  //假如不能再移动了
            {
                //将block中的值放入背景数组中
                elsClass.SetData();

                //检查并删除行
                elsClass.CheckAndDeleteLines();

                //检查是否到顶
                if (elsClass.CheckToTop())
                {
                    timer1.Enabled = false;
                    MessageBox.Show("到顶了!");
                }
                //重新设置起始状态
                elsClass.currentX = -4;
                elsClass.currentY = 5;

                //随机生成新的block
                elsClass.GetInitBlockData();
            }

            //重绘图
            elsClass.ErLuoSiDraw();

        }

三、核心控制类

public class ErLuoSiClass
    {
        #region 参数

        public System.IntPtr winHandle;

        //游戏开关标志
        public bool elsStartFlag = false;

        //游戏画板对应数组 默认全是0
        public int[,] RushData;

        #region 小图案的状态,暂且7种,每种都用四个变化 (7*4)*4*4
        public int[, ,] Blocks = 
        {
            //逆时针旋转
            #region 横杆的4种状态
            {
                {1,0,0,0},
                {1,0,0,0},
                {1,0,0,0},
                {1,0,0,0}
            },
            {
                {1,1,1,1},
                {0,0,0,0},
                {0,0,0,0},
                {0,0,0,0}
            },
            {
                {1,0,0,0},
                {1,0,0,0},
                {1,0,0,0},
                {1,0,0,0}
            },
            {
                {1,1,1,1},
                {0,0,0,0},
                {0,0,0,0},
                {0,0,0,0}
            },
            #endregion
            
            #region 方框的4种状态
            {
                {1,1,0,0},
                {1,1,0,0},
                {0,0,0,0},
                {0,0,0,0}
            },
            {
                {1,1,0,0},
                {1,1,0,0},
                {0,0,0,0},
                {0,0,0,0}
            },
            {
                {1,1,0,0},
                {1,1,0,0},
                {0,0,0,0},
                {0,0,0,0}
            },
            {
                {1,1,0,0},
                {1,1,0,0},
                {0,0,0,0},
                {0,0,0,0}
            },
            #endregion

            #region T字的4种状态
            {
                {1,1,1,0},
                {0,1,0,0},
                {0,0,0,0},
                {0,0,0,0}
            },
            {
                {1,0,0,0},
                {1,1,0,0},
                {1,0,0,0},
                {0,0,0,0}
            },
            {
                {0,1,0,0},
                {1,1,1,0},
                {0,0,0,0},
                {0,0,0,0}
            },
            {
                {0,1,0,0},
                {1,1,0,0},
                {0,1,0,0},
                {0,0,0,0}
            },
            #endregion

            #region 7字的4种状态
            {
                {1,1,0,0},
                {0,1,0,0},
                {0,1,0,0},
                {0,0,0,0}
            },
            {
                {1,1,1,0},
                {1,0,0,0},
                {0,0,0,0},
                {0,0,0,0}
            },
            {
                {1,0,0,0},
                {1,0,0,0},
                {1,1,0,0},
                {0,0,0,0}
            },
            {
                {0,0,1,0},
                {1,1,1,0},
                {0,0,0,0},
                {0,0,0,0}
            },
            #endregion
            
            #region 反7字的4种状态
            {
                {1,1,0,0},
                {1,0,0,0},
                {1,0,0,0},
                {0,0,0,0}
            },
            {
                {1,0,0,0},
                {1,1,1,0},
                {0,0,0,0},
                {0,0,0,0}
            },
            {
                {0,1,0,0},
                {0,1,0,0},
                {1,1,0,0},
                {0,0,0,0}
            },
            {
                {1,1,1,0},
                {0,0,1,0},
                {0,0,0,0},
                {0,0,0,0}
            },
            #endregion
            
            #region Z字的4种状态
            {
                {1,1,0,0},
                {0,1,1,0},
                {0,0,0,0},
                {0,0,0,0}
            },
            {
                {0,1,0,0},
                {1,1,0,0},
                {1,0,0,0},
                {0,0,0,0}
            },
            {
                {1,1,0,0},
                {0,1,1,0},
                {0,0,0,0},
                {0,0,0,0}
            },
            {
                {0,1,0,0},
                {1,1,0,0},
                {1,0,0,0},
                {0,0,0,0}
            },
            #endregion

            #region 反Z字的4种状态
            {
                {0,1,1,0},
                {1,1,0,0},
                {0,0,0,0},
                {0,0,0,0}
            },
            {
                {1,0,0,0},
                {1,1,0,0},
                {0,1,0,0},
                {0,0,0,0}
            },
            {
                {0,1,1,0},
                {1,1,0,0},
                {0,0,0,0},
                {0,0,0,0}
            },
            {
                {1,0,0,0},
                {1,1,0,0},
                {0,1,0,0},
                {0,0,0,0}
            },
            #endregion
        };
        #endregion

        /*当前操作的block块的内部组成*/
        public int[,] CurrentBlock = new int[4, 4];

        //当前操作的block起点在整个数组中的位置,起点在左上角
        public int currentX = -4;
        public int currentY = 5;

        //当前方格数、方格状态数
        public int CellNum = 0;
        public int CellState = 0;

        //一个格子的大小
        public int CellSize = 15;

        //在宽度、高度方向上格子数目
        public int CellWidthCount;
        public int CellHeightCount;

        //区域宽度、高度
        public int Width;
        public int Height;

        #endregion

        /*初始化*/
        public void InitErLuoSi()
        {
            RushData = new int[CellHeightCount, CellWidthCount];

            Width = CellWidthCount * (CellSize + 1) + 1;
            Height = CellHeightCount * (CellSize + 1) + 1;
            elsStartFlag = true;    //游戏状态设置为开始
        }

        /*画游戏图*/
        public void ErLuoSiDraw()
        {
            Image myImage = new Bitmap(Width, Height);
            Graphics g = Graphics.FromImage(myImage);
            g.FillRectangle(new SolidBrush(Color.Gray), 0, 0, Width, Height);
            

            //宽度方向上走,画竖线
            for (int i = 0; i <= CellWidthCount; i++)
            {
                g.DrawLine(new Pen(Color.Black), (CellSize+1) * i, 0, (CellSize+1) * i, Height);
            }
            //高度方向上走,画横线
            for (int i = 0; i <= CellHeightCount; i++)
            {
                g.DrawLine(new Pen(Color.Black), 0, (CellSize + 1) * i, Width, (CellSize + 1) * i);
            }

            SolidBrush brush = new SolidBrush(Color.Gray);  //默认灰色背景画刷
            FontFamily fontFamily = new FontFamily("Arial");
            Font font = new Font(fontFamily, 12, FontStyle.Regular, GraphicsUnit.Pixel);
            string strTemp = "";  //默认需要写的字
            int fontLeft = CellSize/5;

            //画所有方格
            for (int i = 0; i < CellHeightCount; i++)
            {
                for (int j = 0; j < CellWidthCount; j++)
                {
                    brush = new SolidBrush(Color.Gray);  //灰色背景画刷
                    if (RushData[i, j]  == 1)
                        brush = new SolidBrush(Color.Gold);  //金色背景画刷

                    strTemp = RushData[i, j].ToString();

                    g.FillRectangle(brush, (CellSize + 1) * j + 1,
                        (CellSize + 1) * i + 1, CellSize, CellSize);
                    g.DrawString(strTemp, font, new SolidBrush(Color.Black),
                        (CellSize + 1) * j + fontLeft, (CellSize + 1) * i + fontLeft);
                }
            }

            //画当前移动的方格
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    if (currentX + i < 0)
                        continue;

                    brush = new SolidBrush(Color.Gray);  //默认灰色背景画刷
                    strTemp = CurrentBlock[i, j].ToString();

                    if (CurrentBlock[i, j] == 1)
                    {
                        brush = new SolidBrush(Color.Gold);  //金色背景画刷
                    }
                    if (currentX + i >= 0 && currentX + i <= CellHeightCount - 1
                        && currentY+j >= 0 && currentY + j <= CellWidthCount - 1
                        && RushData[currentX+i,currentY+j]==1)
                    {
                        brush = new SolidBrush(Color.Gold);  //金色背景画刷
                        strTemp = "1";
                    }                   

                    g.FillRectangle(brush, (CellSize + 1) * (currentY + j) + 1,
                        (CellSize + 1) * (currentX + i) + 1, CellSize, CellSize);
                    g.DrawString(strTemp, font, new SolidBrush(Color.Black),
                        (CellSize + 1) * (currentY + j) + fontLeft, (CellSize + 1) * (currentX + i) + fontLeft);
                }
            }

            Graphics gg = Graphics.FromHwnd(winHandle);
            gg.DrawImage(myImage, 0, 0);  

        }
        
        /*随机生成当前的block*/
        public void GetInitBlockData()
        {
            Random ran = new Random();
            CellNum = ran.Next(0, 6);
            CellState = ran.Next(0, 4);

            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    CurrentBlock[i, j] = Blocks[CellNum * 4 + CellState, i, j];
                }
            }
        }

        /*是否可以向下移动*/
        public bool CanDown()
        {
            for(int i=3; i>=0; i--)
            {
                for (int j = 0; j <4 ; j++)
                {
                    //小方格内为1 则需要判断是否可以向下
                    if (CurrentBlock[i, j] == 1)
                    {
                        if (currentX + i + 1 >= CellHeightCount)
                        {
                            return false;  //不能向下移动了
                        }
                        else if (currentX + i + 1 >= 0 && currentX + i + 1 < CellHeightCount
                            && currentY + j >= 0 && currentY + j < CellWidthCount
                            && RushData[currentX + i + 1, currentY + j] == 1)
                        {
                            return false;  //不能向下移动了
                        }
                    }
                }
            }

            return true;
        }

        /*是否可以向左移动*/
        public bool CanLeft()
        {
            for (int i = 0; i <4 ; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    if (CurrentBlock[ i,  j] == 1)
                    {
                        if( currentY + j - 1 < 0)
                        {
                            return false;  //不能向下移动了
                        }
                        else if (currentX + i >= 0 && currentX + i < CellHeightCount
                            && currentY + j - 1 >= 0 && currentY + j - 1 < CellWidthCount
                            && RushData[currentX + i , currentY + j- 1] == 1)
                        {
                            return false;  //不能向下移动了
                        }
                    }
                }
            }
            return true;
        }

        /*是否可以向右移动*/
        public bool CanRight()
        {
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    if (CurrentBlock[i, j] == 1)
                    {
                        if (currentY + j + 1 >= CellWidthCount)
                        {
                            return false;  //不能向下移动了
                        }
                        else if (currentX + i >= 0 && currentX + i < CellHeightCount
                            && currentY + j + 1 >= 0 && currentY + j + 1 < CellWidthCount
                            && RushData[currentX + i , currentY + j + 1] == 1)
                        {
                            return false;  //不能向下移动了
                        }
                    }
                }
            }
            return true;
        }

        /*是否可以旋转*/
        public bool CanChange()
        {
            CellState = (CellState + 1) % 4;
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    //主要看为1的方格旋转后的位置是否冲突
                    if (Blocks[CellNum * 4 + CellState, i, j] == 1)
                    {
                        //如果在范围内
                        if (currentX + i >= 0 && currentX + i < CellWidthCount
                            && currentY + j >= 0 && currentY + j < CellHeightCount)
                        {
                            if (RushData[i, j] == 1)
                                return false;
                        }
                        //如果不在范围内
                        else
                        {
                            return false;    
                        }
                    }
                }
            }                    

            return true;
        }

        /*旋转当前的方格*/
        public void ChangeBlock()
        {
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    CurrentBlock[i, j] = Blocks[CellNum * 4 + CellState, i, j];
                }
            }
        }

        /*置数组:将已经不能再移动的block中的值放入背景数组中 */
        public void SetData()
        {
            for (int i = 0; i < 4; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    if (CurrentBlock[i, j] == 1)
                    {
                        if (currentX + i >= 0 && currentX + i < CellHeightCount
                            && currentY + j >= 0 && currentY + j < CellWidthCount )
                            RushData[currentX + i,currentY + j] = 1;
                    }
                }
            }

        }
        
        /*检查行满,并销毁行、计分*/
        public void CheckAndDeleteLines()
        {
            int lines = 0;  //行数 用于计分
            int[] linesFlag = new int[CellHeightCount]; //记录每行是否填满

            //查找满的行
            for (int i = CellHeightCount-1; i >=0 ; i--)
            {
                int count = 0;  //本行满的数目
                for (int j = CellWidthCount-1; j >=0 ; j--)
                {
                    if (RushData[i, j] == 1)
                    {
                        count++;
                    }
                }
                if (count == CellWidthCount)
                {
                    linesFlag[i] = 1;
                    lines++;
                }
            }

            //移动行
            for (int i = 0; i < CellHeightCount; i++)
            {
                //如果本行满
                if (linesFlag[i] == 1)
                {
                    //从最大序号的行开始移动
                    for (int j = i; j >0 ; j--)
                    {
                        for (int k = 0; k < CellWidthCount; k++)
                        {
                            RushData[j, k] = RushData[j - 1, k]; 
                        }
                    }
                    for (int k = 0; k < CellWidthCount; k++)
                    {
                        RushData[0, k] = 0;
                    }
                }
            }


        }

        //检查是否到顶
        public bool CheckToTop()
        {
            //列循环
            for (int j = 0; j < CellWidthCount; j++)
            {
                if (RushData[0, j] == 1 && currentX < 0)
                    return true;  //到顶   
            }
            return false;

        }

    }
posted on 2011-06-22 17:31  windfree  阅读(822)  评论(1编辑  收藏  举报