DotNet笔记

兴趣是最好的老师。
posts - 404, comments - 1430, trackbacks - 46, articles - 1
   :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

C#版俄罗斯方块

Posted on 2007-05-16 11:10 小y 阅读(14520) 评论(46) 编辑 收藏

C++是游戏编程的首选语言,但我相信C++能做到的C#也能做到。

本篇介绍用C#编写一个俄罗斯方块程序的原理,以及在C#里面播放声音,保存游戏设置的方法。

游戏界面预览:



菜单预览:



自定义每个小方块颜色功能界面:



游戏主要有四部分组成:Square类,Block类,gameField类,游戏引擎

Square类:
          这个类描述的对象是组成大方块中的每个小正方形实体。
     类设计:
    class Square
    {
        public Point location;   //小方块的坐标
        public Size size;          //小方块大小
        public Color foreColor;  //小方块前景色
        public Color backColor; //小方块背景色
        public Square(Size initSize,Color initForeColor,Color initBackColor) //构造函数
        {  ……}
         public void Draw(System.IntPtr winHandle) //在指定设备上画方块
        {  …… }
         public void Erase(System.IntPtr winHandle)//擦除方块
        { ……  }
     }

Block类:
          这个类描述的对象是某一个大方块的实体。每个大方块由四个小正方形组成,一共有7种组合方式。这个类需要实现一个大方块实体所有的属性和动作。包括:方块的形状,位置,方块左移,右移,下移,旋转等。
      类设计:
 class Block
    {
        public Square square1;  //组成block的四个小方块
        public Square square2;
        public Square square3;
        public Square square4;

        private const int squareSize = GameField.SquareSize; //小方块的边长
        public enum BlockTypes
        {
            undefined = 0,
            square = 1,
            line = 2,
            J = 3,
            L = 4,
            T = 5,
            Z = 6,
            S = 7
        };//一共有7种形状
        public BlockTypes blockType;  //方块的形状
        //七个小方块的颜色数组
        private Color foreColor;
        private Color backColor;
        //方块的方向
        public enum RotateDirections
        {
            North = 1,
            East = 2,
            South = 3,
            West = 4
        };
        public RotateDirections myRotation = RotateDirections.North;
       
        public Block(Point thisLocation,BlockTypes bType)
        { ……}
        //含有自定义颜色的重载
        public Block(Point thisLocation, BlockTypes bType,Color fc,Color bc)
        { ……}

        /*画方块*/
        public void Draw(System.IntPtr winHandle)
        {…… }
        /*擦方块*/
        public void Erase(System.IntPtr winHandle)
        {…… }

        /*移动*/
        public bool down()
        {……}
        public bool left()
        {……}
        public bool right()
        {……}
        /*旋转block*/
        public void Rotate()
        {……}
        /*检测是否到顶*/
        public int Top()
        {……}
    }

GameField类:
        这个类描述的对象是游戏场景实体,包括场景的背景色,大小,方块是否还可以移动,以及场景中填满一行的检测等。
        类设计:
     class GameField
    {
        public const int width = 20;           //场景的宽,以方块个数为单位
        public const int height = 30;
        public const int SquareSize = 15;      //每个四分之一小方块的边长
        public static Color BackColor;         //场景的背景色
        public static System.IntPtr winHandle; //场景的handle
        public static Color[] BlockForeColor ={ Color.Blue, Color.Beige, Color.DarkKhaki, Color.DarkMagenta, Color.DarkOliveGreen, Color.DarkOrange, Color.DarkRed };
        public static Color[] BlockBackColor ={ Color.LightCyan, Color.DarkSeaGreen, Color.Beige, Color.Beige, Color.Beige, Color.Beige, Color.Beige };
        public static bool isChanged=false; //设置是否被更改的标志位
        public static SoundPlayer sound = new SoundPlayer();  //播放声音

        public static Square[,] arriveBlock = new Square[width, height]; //保存已经不能再下落了的方块
        public static int[] arrBitBlock=new int[height];  //位数组:当某个位置有方块时,该行的该位为1
        private const int bitEmpty = 0x0;      //0000 0000 0000 0000 0000
        private const int bitFull = 0xFFFFF;   //1111 1111 1111 1111 1111

        /*检测某个位置是否为空*/
        public static bool isEmpty(int x, int y)
        {……}
        /*将方块停住*/
        public static void stopSquare(Square sq, int x, int y)
        {……}
        /*检测行是否满
         * 返回:成功消除的行数和  (方便统计分数)
         */

        public static int CheckLines()
        { ……}
        /*播放声音*/
        public static void PlaySound(string soundstr)
        {……}
        /*重画*/
        public static void Redraw()
        { …… }
         //结束
    }

游戏引擎:
      游戏引擎正如其名,就像一个发动机一样让游戏不间断运行。本游戏中就是让方块以一定的速度下落。并响应键盘事件,实行左右移动,和向下加速功能。(代码见源码)

声音播放:

     音效是游戏不可缺少的一部分。在.Net2.0中已经提供了一个类来播放声音。在using System.Media;命名空间。
     本游戏中播放声音的代码如下:(在 GameField类中)
     using System.Media;

     public static SoundPlayer sound = new SoundPlayer();
     
     /*播放声音*/
        public static void PlaySound(string soundstr)
        {
            switch (soundstr)
            {
                case "FinishOneLine": //消除一行的声音
                    if (!File.Exists("FinishOneLine.wav")) return;
                    sound.SoundLocation = "FinishOneLine.wav";
                    break;
                case "CanNotDo": //当无法操作时
                    if (!File.Exists("CanNotDo.wav")) return;
                    sound.SoundLocation = "CanNotDo.wav";
                    break;
            }
            sound.Play();
        }
        要播放的时候调用PlaySound()方法即可。
        其实步骤很简单,先引用System.Media空间,然后创建一个SoundPlayer 对象,用SoundLocation 属性设置声音文件的地址,然后调用Play()方法即可播放。不过注意,这个类可以播放的声音格式只有Wav文件。

保存游戏设置:
      在游戏中经常要保存用户自定义的设置。本游戏通过写进ini文件来保存。
      主要代码如:

       /*加载窗体时从配置文件Setting.ini中读取游戏设置*/
        private void getSettings()
        {
            if (!File.Exists("Setting.ini"))
                return;
            FileStream fs = new FileStream("Setting.ini", FileMode.OpenOrCreate, FileAccess.ReadWrite);
            StreamReader sr = new StreamReader(fs);
            string line1=sr.ReadLine();
            string line2=sr.ReadLine();
            string line3=sr.ReadLine();
            if (line1 != null && line1.Split('=').Length > 1)
            {
                GameField.BackColor = Color.FromArgb(int.Parse(line1.Split('=')[1]));
                picBackGround.BackColor = GameField.BackColor;
            }
            if (line2 != null && line2.Split('=').Length > 1)
                GameField.BlockForeColor = strToColor(line2.Split('=')[1]);
            if (line3 != null && line3.Split('=').Length > 1)
                GameField.BlockBackColor = strToColor(line3.Split('=')[1]);
            sr.Close();
            fs.Close();
        }
        /*如果游戏设置被更改,将新的设置保存到Setting.ini*/
        private void saveSettings()
        {
            FileStream fs = new FileStream("Setting.ini", FileMode.Create, FileAccess.ReadWrite);
            StreamWriter sw = new StreamWriter(fs);
            sw.WriteLine("GameFieldColor="+GameField.BackColor.ToArgb());
            sw.WriteLine("BlockFroeColor=" + colorToStr(GameField.BlockForeColor));
            sw.WriteLine("BlockBackColor=" + colorToStr(GameField.BlockBackColor));
            sw.Flush();
            sw.Close();
            fs.Close();
        }

麻雀虽小,五脏俱全,了解更多请看源码。
游戏源码下载:CODE

---------------------------------------------------------------------

Feedback

#1楼  回复 引用 查看   

2007-05-16 13:31 by 丁学      
有个小问题,游戏进行中修改背景色后,已经有的方块全看不见了,但游戏还在继续中,修改方块颜色后才再次出现,应该是修改背景色那个地方代码有问题

#2楼  回复 引用 查看   

2007-05-16 14:03 by jhtchina      
值得学习

#3楼  回复 引用 查看   

2007-05-16 15:43 by Anders Cui      
不错,学习!

#4楼  回复 引用 查看   

2007-05-17 08:06 by Solog      
顶。
支持

#5楼  回复 引用   

2007-05-23 14:56 by khler[未注册用户]
好东西,不能不顶

#6楼  回复 引用   

2007-05-24 17:01 by hyde100[未注册用户]
程序有个问题,就是键盘反应太慢,主要是键盘事件是写在KeyUp事件里的,如果写到KeyUp里就更好了

#7楼  回复 引用   

2007-05-24 17:02 by hyde100[未注册用户]
说错了,是:
程序有个问题,就是键盘反应太慢,主要是键盘事件是写在KeyUp事件里的,如果写到KeyDown里就更好了

#8楼[楼主]  回复 引用 查看   

2007-05-28 10:17 by 小y      
谢谢提醒,原本是想在keydown里面写,但测试的时候keydown事件好像没反应所以就写在keyup事件里了。
现在改到keydown里面。

#9楼  回复 引用   

2007-06-07 22:46 by crasysanta[未注册用户]
haodongxi

#10楼  回复 引用 查看   

2007-06-09 16:21 by 乐建军      
不能下载啊
能不能发给我一下?
11yuejianjun@163.com

#11楼  回复 引用   

2007-06-15 15:16 by liyangdan[未注册用户]
想学习写个ppc版的...但是下不下来,能不能给我email一份啊
lyd322@163.com,谢谢了

#12楼[楼主]  回复 引用 查看   

2007-06-15 15:34 by 小y      
能下吧,我试了

#13楼  回复 引用   

2007-06-15 16:42 by easy7857[未注册用户]
嘿嘿,很强呀,感谢中,不过发现个BUG:就是向下加速问题,在平常的游戏里,↓的时候,不应该是一次性就到底了吧?我这边测试,一按往下,马上到底了,而我的本意应该是快速下降一段距离,快到的时候在进行左右等操作,哈哈

#14楼[楼主]  回复 引用 查看   

2007-06-16 10:31 by 小y      
这个是游戏规则的问题,平常游戏里是那样
俺做的游戏自己定规则呵呵
其实这个游戏是模仿我的手机上那个俄罗斯方块的规则做的
它是一次性下落到底的。
你可以自己改改嘛,如果想实现你那样的下落方式的话。

#15楼  回复 引用   

2007-07-13 13:57 by Rocky[未注册用户]
对您做的这个东东很感兴趣。下载了源代码,不知道怎么加到我的asp。net里。
我用的是老版本的。2003年的1.0版本的。

#16楼[楼主]  回复 引用 查看   

2007-07-14 23:27 by 小y      
to Rocky

可能不行,因为我用到一个播放声音的类,只有vs2005中才有

#17楼  回复 引用   

2007-07-16 09:16 by Rocky[未注册用户]
to xiaoY:
thank you for your repley.

#18楼  回复 引用   

2007-07-17 12:06 by 侦探界小侠[未注册用户]
高手~~

#19楼  回复 引用   

2007-07-30 13:20 by 1[未注册用户]
顶!

#20楼  回复 引用   

2007-08-15 15:25 by yungangma[未注册用户]
打开项目运行时,出现"试图运行项目时出错:无法启动调试.绑定句柄无效."的错误,这是怎么回事呢?

#21楼  回复 引用   

2007-08-18 16:35 by f[未注册用户]
佩服 喜欢

#22楼  回复 引用   

2007-08-28 11:32 by tom[未注册用户]
不够完美,应该对自己要求高一点,做的足够好了再拿出来给别人看

#23楼  回复 引用   

2007-09-07 17:42 by TS小超[未注册用户]
不错啊,先谢谢了,我下了一下源代码,看了一下,GameField类里边的检测是否为空IsEmpty方法,停住方块StopSquare方法和检查行是否满CheckLines方法看不明白,能不能在说清楚一下啊,谢谢

#24楼  回复 引用   

2007-09-22 22:15 by qiafv[未注册用户]
顶,厉害呀.高手

#25楼  回复 引用   

2007-11-01 15:17 by 房东567[未注册用户]
睡觉睡到自然醒,数钱数到手抽筋,最后... 呵呵

#26楼  回复 引用   

2007-12-06 22:47 by ghost5018[未注册用户]
谢谢…… 我也来学习一下

#27楼  回复 引用   

2007-12-18 12:34 by 夏阳港[未注册用户]
不够完美,应该对自己要求高一点,做的足够好了再拿出来给别人看

确实是这样子,我也在做这个。确实可以做得更好,比如音效可以用axWindowsMediaPlayer,比如每一行的格子太多了,这样子要比较久才能消一行,人家玩起来不会很积极。

有一本书上有这个东西,我想你是加了音效与配色的那一块。

学习~~

#28楼  回复 引用   

2008-05-04 15:21 by hlm3312[未注册用户]
c#作业做这个,参详参详~(*^__^*) 嘻嘻……

#29楼  回复 引用   

2008-09-09 21:09 by chen_dian_dian[未注册用户]
启动Terminal Service服务再试 或者直接Ctrl+F5运行
--引用--------------------------------------------------
yungangma: 打开项目运行时,出现"试图运行项目时出错:无法启动调试.绑定句柄无效."的错误,这是怎么回事呢?
--------------------------------------------------------

#30楼  回复 引用   

2008-09-23 16:38 by wenqing[未注册用户]
你写的好吗?拿出来让我们看看,没本事先别评论别人!

#31楼  回复 引用   

2008-10-26 13:48 by dadadeyezi[未注册用户]
谢谢,借来参考一下^^

如果楼主可以写下做游戏的心得就更好了,比如关键的,重要的,难理解的,不容易想到的:P

#32楼  回复 引用   

2008-11-19 22:07 by 爱在两腿间[未注册用户]
看了一个下午,终于一句句的读完了,还是有几处不太懂的地方。极为佩服楼主的水平。
提点小建议哦,旋转方块的时候,应该可以直接用一个方法来确定坐标的变化,因为每个方块每次都是顺时针绕中心点转的嘛,转动时直接调用方法就OK了。还有,俄罗斯方块的规则应该是每次消去的行数越多分数就越高的,一次消去一行只能得100,一次性干掉四行可以得1000的哦。分数达到了某一个值时(比如10000),应该自动加速。最后就是,在给方块配色的时候,希望鼠标悬停或单击在调色版上的某个格子时,方块应该自动显示选色的效果,而不应该到按了确定后才能显示选色的效果。
呵呵,我会按照以上建议去完善你的程序。
最后,再一次表示对你的水平的佩服,特别是记录某个地方有方格存在的办法,简直帅呆啦!!

#33楼  回复 引用   

2008-12-26 10:29 by yinko[未注册用户]
我现在也要做这个游戏,谢谢你了

#34楼  回复 引用 查看   

2009-07-10 16:49 by 快乐八哥      
我根据陈广老师的视频,也做了一个。但是没有你的功能多,把你的源代码下载了,好好学习一下。Thank you!

#35楼  回复 引用   

2009-07-19 12:47 by lglmike[未注册用户]
我是新手,把你的源代码下载下来用VS2005打开看不到代码,是怎么回事?隐藏起来了吗?怎么显示出来呢?谢了。

#36楼  回复 引用   

2009-08-04 16:36 by tp[未注册用户]
太好了,非常感谢,慢慢研究

#37楼  回复 引用   

2009-08-14 01:45 by shirley1989[未注册用户]
博主,我是新手,想请教下,isEmpty里面的1<<x是怎么回事,为什么要左移呢。。。

#38楼  回复 引用 查看   

2009-12-08 09:46 by DreamZero      
我也想问下,判断是否为空那里的 左移 的原因。谢谢。

#39楼  回复 引用 查看   

2010-05-04 00:10 by Daybreak      
thanks 收了

#40楼  回复 引用 查看   

2010-06-19 00:45 by zanewin      
感谢提供!

#41楼  回复 引用 查看   

2010-12-16 09:32 by 龙行天下      
我看了你的代码,你的小方块坐标的X的存位置的SQUARE里的X好像是反着不知道是怎么运行起来的,怎么能用arrBitBlock[y] & (1 << x)) != 0来判断为是否为空,照你这样想保存位置的坐标是从右上角开始的,而画图坐标是从左上角开始的这个不能改变,也就是这两个映射的X轴是相反,在你程序中怎么没看到转换,方块的X除方块边长就是小方块在SQUARE中的坐标这样好像不对吧》??

#42楼  回复 引用 查看   

2010-12-25 16:39 by 雪之妖      
不错,

#43楼  回复 引用 查看   

2011-07-06 16:55 by 路小牌      
很强大 我是刚刚接触.net的 能不能发一份源代码到Email 非常感谢
chengzhi0001@126.com

#44楼  回复 引用 查看   

2011-09-08 00:32 by xdsUp      
楼主:
代码中判断是否为空 (arrBitBlock[y] & (1 << x)) != 0 是什么意思啊?

#45楼  回复 引用 查看   

2011-11-23 14:02 by 不闷      
谢了~收下研究研究~!很强大~

#46楼  回复 引用 查看   

2011-12-12 15:51 by 陈玉鸣      
大哥,真的不错,继续关注并学习着