随笔 - 7  文章 - 57  trackbacks - 0
<2011年6月>
2930311234
567891011
12131415161718
19202122232425
262728293012
3456789

常用链接

留言簿(3)

随笔分类

随笔档案

文章分类

文章档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜

2011/6/9

今天真是个诡异的日子~哈哈哈哈哈哈哈

 

经过前几天的小试牛刀,已经实现了游戏中的小人在跳来跳去的动画(键盘控制)。不过有个很碍眼的bug,画面闪烁得非常厉害。看到眼到晕了。

此物会在绿色block和地面之间跳来跳去~

 

估计大概是我实现的原因,记得以前非礼MFC绘图的时候,想要实现类似动画的效果的话,一般做法是:

SetTimer

然后在OnTimer函数里绘制新的内容

然而绘制之前要注意把之前旧的内容删去

 

还非常清晰地记得看书的时候有这样类似的一句:“擦掉旧的,绘制新的,在很短的时间内,用户看起来就像图像真的动起来一样,也感觉不到擦除操作带来的副作用(残影)”

 

当时信以为真,实现了一下,还真是那样(也有可能是当时没有画些什么复杂的物体,只是demo)。

 

C++TM快啊……

当然,这是用了C#之后的感叹……

 

不是开发快,而是跑得快,快到完全可以无压力实现“让用户看起来就像图像真的动起来一样,也感觉不到擦除操作带来的副作用(残影)”的境界。

 

于是,我按照同样的思路在C#里这么干。

用背景色填充矩形区域——擦除

然后重新绘制这个矩形区域——重绘

 

结果,比照相机的闪光灯还闪烁得耀眼。

 

于是去google,搜索过包括什么“动画”“残影”“不平滑”“延迟”等关键字,最先是在CSDN上找到了一些解决方案的苗头,于是关键字在进化“drawImage”“闪烁”

 

对,“闪烁”很关键,有时在机器上看到有些非语言所能描述的现象,一下子还真找不出合适表达,没办法的,唯有慢慢搜索,逼近它。

 

不知道在AI领域,机器可不可以透过这种方式学习呢???

 

然后折腾了很久,终于理清了大概的解决方案思路:

使用双缓冲的方法绘制和显示图像:现在一个buffer上画好全图,然后一次显示

响应OnPaint方法,不擦除背景,直接重绘整个窗口

 

这样做有个好处:

不用填充矩形区域了,因为以后我的游戏背景不可能是纯色,之前用背景色填充来擦除纯粹是权宜之计,不过,这样做也有好处,因为在刚开始的时候根本无法预知之后发生的所有状况,完成当前阶段的任务,往后不断优化才是王道。

哎哟!怪不得瀑布开发模型要被迭代模型鄙视哈!敏捷开发,阶段性产出代码,文档量少,nice

 

具体操作和代码已经有很好的资料:

http://blog.csdn.net/Linux7985/archive/2010/11/03/5983832.aspx
 

于是在OnPaint方法中:

private void OnPaint(object sender, PaintEventArgs e)

        {

            //http://blog.csdn.net/Linux7985/archive/2010/11/03/5983832.aspx

            if (timer1.Enabled == false)

                return;

 

            //create a bitmap and a graphics to hold the temporary result of the new screen picture

            Bitmap bmpTemp = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);

            Graphics g = Graphics.FromImage(bmpTemp);

            //do sth I need

            g.Clear(this.BackColor);

            //update all the objects' position and draw them on the temporary bitmap

            this.PlayerUpdatePosition();

            g.DrawImage(bmpPlayer, rtPlayer);

            g.DrawImage(bmpStaticBlock, rtStaticBlock);

 

            //last, draw it on the screen!

            e.Graphics.DrawImage(bmpTemp, 0, 0);// = Graphics.FromImage(bmpTemp);

            //avoiding memory leak~

            g.Dispose();

            bmpTemp.Dispose();

 

            ////this also works, but I code myself after I understand the solution itself!haha!

            //Rectangle rect = e.ClipRectangle;

            //BufferedGraphicsContext currentContext = BufferedGraphicsManager.Current;

            //BufferedGraphics myBuffer = currentContext.Allocate(e.Graphics, e.ClipRectangle);

            //Graphics g = myBuffer.Graphics;

            //g.SmoothingMode = SmoothingMode.HighQuality;

            //g.PixelOffsetMode = PixelOffsetMode.HighSpeed;

            //g.Clear(this.BackColor);

            //this.PlayerUpdatePosition();

            //g.DrawImage(bmpStaticBlock, rtStaticBlock);

            //g.DrawImage(bmpPlayer, rtPlayer);

            //myBuffer.Render(e.Graphics);

            //g.Dispose();

            //myBuffer.Dispose();//º¨ª¤?Á¨º¡ä

        }

 

 

当然,由于在玩游戏的时候,窗口大小一般不会发生改变,窗口也不会四处移动(窗震!?),因此还是在timer里面强迫它重绘吧!哈哈哈哈!

private void timer1_Tick(object sender, EventArgs e)

        {

            this.Refresh();

        }

 

这样干完之后,已经耗费了1.5h的时间,主要是搜索和筛选资料,还有反复调试代码,debug神马的。

然后在实际运行的时候还是不是很流畅。

 

还是略闪~

 

印象中,大神的版本是不肿么闪烁的喔!!!

结果纠缠了很久,就像分手不遂死缠烂打一样(囧)。

 

结果反复调试,设置了无数timer的时间间隔:15ms100ms不等,还是略闪。。。

 

最后得出一个结论:双缓冲只能改善闪烁问题,无法完全根除。更要命的是,其实大神的版本在某程度上来看的话,还是会略闪(大神别骂我哈!)。不过他比我聪明的地方在于他的镜头一直跟随者运动最频繁的那个物体(Player),因此Player相对于镜头几乎都是静止的,而玩家总是盯住Player看,管他周围的那些block是不是闪烁咩。。。

 

今晚写了程序,JJumper改进了,又解决了问题。心情好愉悦哇~

 

干掉整个JJumper之后估计写一系列关于设计这个C#JJumper的文章。网上很多C++开发游戏的教程和资料什么的,但是C#略少,中文的,完整的,速成的更小。把我的经历分享出去应该能让看到的人少走很多歪路吧?(TM一开始看大神的代码的时候我晕了,n层类结构,各种继承,各种接口的实现,各种的设计。你不知道在设计World的时候要考虑添加一个WorldObjectList,也不知道以后的子类对象可能需要父类给他们提供一个可以跟窗口UI交互的接口,这样的例子,太多太多……)

 

做个好人应该是好的……

 

咦!?糟糕了!!!明天要考试~啊!!!去看PPT~

悲催,本来就是很讨厌背完即刻忘掉的模式的,为了考试……

估计即使让我背2hppt,也不如我这样收获大吧?不后悔,哈哈!

posted on 2011-06-09 00:44 ArthasLee 阅读(1793) 评论(7)  编辑 收藏 引用 所属分类: 无责任吐槽

FeedBack:
# re: 双缓冲只能改善图像闪烁问题,而不能彻底消除它 2011-06-09 10:53 陈梓瀚(vczh)
恭喜,你必须在打开了DoubleBuffered的同时,自己再创建一个Bitmap,把东西全部画进去,再贴到控件上。这样才不会闪。

而且windows有WM_ERASEBGN还是类似的一个什么破消息忘记了,要在C#里面屏蔽掉。  回复  更多评论
  
# re: 双缓冲只能改善图像闪烁问题,而不能彻底消除它 2011-07-16 10:31 egmkang
闪烁的原因很简单,就是前面一帧和后面一帧的对比度差别过大了,眼睛才看的见
有可能是你无意中刷新的窗口的背景,这样你即便双缓冲,也无济于事
  回复  更多评论
  
# re: 双缓冲只能改善图像闪烁问题,而不能彻底消除它 2011-07-16 10:33 egmkang
# re: 双缓冲只能改善图像闪烁问题,而不能彻底消除它 2011-07-16 10:34 egmkang
另外,你用timer去让window重绘,以后肯定有问题
最好的办法就是发消息给窗口  回复  更多评论
  
# re: 双缓冲只能改善图像闪烁问题,而不能彻底消除它 2011-07-16 19:00 ArthasLee
@egmkang
非常感谢你的回复,我是个新手,一切都在探索中进行。
请问,发消息给窗口不也是发重绘的消息么?我不是很理解你的意思,不知道有没有相关方面的资料可以参考呢?  回复  更多评论
  
# re: 双缓冲只能改善图像闪烁问题,而不能彻底消除它 2011-11-27 03:18 タコです
^-^僕たちのタコさん~

早く見てしてほしいですれ~  回复  更多评论
  
# re: 双缓冲只能改善图像闪烁问题,而不能彻底消除它 2011-12-08 18:28 asuper
在窗体类的构造函数尾部加上
this.SetStyle(ControlStyles.AllPaintingInWmPaint
| ControlStyles.OptimizedDoubleBuffer
| ControlStyles.UserPaint
, true);
this.UpdateStyles();
效果很明显的  回复  更多评论
  

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理