2011/6/9
今天真是个诡异的日子~哈哈哈哈哈哈哈
经过前几天的小试牛刀,已经实现了游戏中的小人在跳来跳去的动画(键盘控制)。不过有个很碍眼的bug,画面闪烁得非常厉害。看到眼到晕了。![](/images/cppblog_com/arthaslee/201106091.png)
![](/images/cppblog_com/arthaslee/201106092.png)
此物会在绿色block和地面之间跳来跳去~
估计大概是我实现的原因,记得以前非礼MFC绘图的时候,想要实现类似动画的效果的话,一般做法是:
Set个Timer
然后在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的时间间隔:15ms到100ms不等,还是略闪。。。
最后得出一个结论:双缓冲只能改善闪烁问题,无法完全根除。更要命的是,其实大神的版本在某程度上来看的话,还是会略闪(大神别骂我哈!)。不过他比我聪明的地方在于他的镜头一直跟随者运动最频繁的那个物体(Player),因此Player相对于镜头几乎都是静止的,而玩家总是盯住Player看,管他周围的那些block是不是闪烁咩。。。
今晚写了程序,JJumper改进了,又解决了问题。心情好愉悦哇~
干掉整个JJumper之后估计写一系列关于设计这个C#版JJumper的文章。网上很多C++开发游戏的教程和资料什么的,但是C#略少,中文的,完整的,速成的更小。把我的经历分享出去应该能让看到的人少走很多歪路吧?(TM一开始看大神的代码的时候我晕了,n层类结构,各种继承,各种接口的实现,各种的设计。你不知道在设计World的时候要考虑添加一个WorldObject的List,也不知道以后的子类对象可能需要父类给他们提供一个可以跟窗口UI交互的接口,这样的例子,太多太多……)
做个好人应该是好的……
咦!?糟糕了!!!明天要考试~啊!!!去看PPT~
悲催,本来就是很讨厌背完即刻忘掉的模式的,为了考试……
估计即使让我背2h的ppt,也不如我这样收获大吧?不后悔,哈哈!
posted on 2011-06-09 00:44
ArthasLee 阅读(2428)
评论(7) 编辑 收藏 引用 所属分类:
无责任吐槽