imbajin / HitBricks

Hit-bricks game implements by C++ , core algorithm --> O(1)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

简介

  1. 打砖块游戏C++实现. 思路非传统遍历. 代码少
  2. 桌面记事贴,采用list.带常见crud功能. UI没有管..思路已经优化过.(通过构造方法子调父,解决线程死锁问题)

C++打砖块游戏中的算法思路优化

摘要:

一般小游戏设计采用C++/C#为开发语言为主,然后开发环境一般在VCL跟MFC中选择一个,这里是采用了C++Builder的VCL做的一个打砖块小游戏,游戏本身很普通,这里主要针对游戏设计的思路,跟碰撞算法的分析优化,改进时间复杂度为主要目的.本文主要从这几个方面来谈游戏:一是砖块跟挡板的初始化,二是分析小球运动的物理模型,三是判断小球碰撞单个砖块的算法.四是如何真正判断是否同时碰到多个

关键字: C++ Builder,打砖块,碰撞算法

1.背景介绍

说到C++ Builder的编程,可能很多人对它的印象已经比较淡了,不过提起Delphi估计还有印象,C++ Builder跟Delphi除了使用语言不一样,其他几乎完全相同,所以直接说一下VC与C++ Builder就行了.

首先,不管是VCL还是MFC都是一个类似人们习惯理解的类库包一样的东西,

2.开发流程

2.1开发环境

Window10Preview下,采用C++ Builder 6.0版开发,可以跨环境(比如Linux下)运行.UI方面由于CB限制...所以没有多花费时间优化调整.

2.2界面设计

clip_image002

如图所示,初始化之后是一个带有简单图形的基本游戏框体,颜色采用随机生成.有HP就是生命值.分数以及剩余时间等额外的参数.空格开始暂停.

2.3物理模型分析

小球的物理模型在VCL中有点不一样,原本在物理模型中,一个规则形状体的中心应该是它的重心也就是球心处.那么计算的时候需要判断一个半径r的数学模型进行计算,会加大不少计算的复杂度,不过VCL中默认取得球心,而是模拟为一个矩形,取的矩形的左上顶点处坐标.砖块/挡板也是同理.所以分析起来方便不少,把小球抽象为一个点,去判断它是否进入越过x/y边界,或者是否进入了砖块的线性规划区域就可以了.

其二,小球的运动轨迹也是一个关键,这里我的思路是从物理运动学角度矢量分解为Vx跟 Vy,然后他们的合速度就是小球实际运行的轨迹了.这样简化了很多复杂的判断,可以针对Vx 跟 Vy单独判断.然后可以给一个角度θ,通过sinθ跟cosθ就可以算出合速度(这里为了简单实现效果,采用的Vx = Vy,即θ=45°).

其三, 小球的反弹其三理论上要考虑一些物理上动量的公式的,特别是如果是多个小球碰撞,或者小球碰到可以移动的砖块位移,应该要满足动量守恒定理,这里因为砖块跟墙壁都是固定的,所以就没有考虑,反弹也分为刚性碰撞跟非刚性,这里一律视为刚性,然后反弹的原理为了通用,直接让小球打到x轴的时候Vy= -Vy,

打到y轴的时候, Vx = -Vx 这样可以打到通配的反弹算法,包括挡板,砖块也是一样的道理.

2.4砖块初始化分析

砖块的初始化经过几次思路的优化,从最开始存放一个二维int数组[m][n],存放砖块的下标,到后来觉得这样太浪费,完全没有利用到二维数组的特性,改成一个int [n]3这是一种简单的对象替代,最后我发现TShape,也就是VCL自带的这个类是可以直接作为对象数组的,那么最简单的用法就是直接用一个List或者Array存TShape对象,然后将它初始化循环就行,这样只需要一个TShape [n]brickList就可以了,而且可以把数组最后一个存放挡板,达到一个init初始所有图形的效果.

clip_image004

2.5单个砖块碰撞算法

砖块最关键的地方就是单个砖块的碰撞算法的设计实现,这里简单来说,从最开始的双层for循环去遍历砖块,到后面想到可以不从头开始遍历,从尾部遍历可以减少很多比必要的判断,到之后又想到,小球每次飞行到砖块跟反弹回挡板的时间其实占据了整个飞行时间很大的一块,这一部分是不可能碰到砖块或者挡板的,不需要hitBricks方法开始,于是又设置了一个范围,让小球只在接近挡板或者砖块的时候才开始进入判断碰撞的函数,这样又可以减少了70%的不必要的判断,然而后来觉得这样做只是把算法复杂度,从O(8n2) 降到了O(n2),并没有根本解决当砖块行列如果非常大的话,小球速度也足够快的时候,根本来不及判断,出现可能直接飞出去的bug,然后这段时间一直在想如何根本改变小球碰撞的时间复杂度.

最后突破在于想到砖块个数是无穷的,而小球是限定的,不管是一个还是多个小球同时在走,他们都是有限的,我们不应该用无限的对象跟有限的对比,而应该反过来,用有限的去比对,思路简单来说就是以小球为中心,每次判断它周围存在的”9方格”正方形,代表了小球可能运动的所有方向,然后判断这个9方格是否与砖块们重合,重合了马上跳出判断,这样,不管砖块有多少个,运动轨迹如何,算法始终只围绕小球所在位置附近的9个可能的砖块判断,最少判断一次,最多判断7次,平均判断4次.实现了真正的算法思路突破改进.

2.6多个砖块判断碰撞

单个砖块的算法思路完成之后,马上开始着手了多个砖块的真正碰撞思路,看了很多的思路个人感觉不是真正的碰撞,只是因为小球在定时器极短的时候内快速的碰撞了两次或者多次,看起来仿佛像是一次,并不是真正的同时打到,而且如何小球是不规则的呢,是一个五角星形状,可以同时碰到很多个,那这样就明显是不对的了.

那么如何判断同时打到的确是一个难题,我的思路是换条路试试,想到一个计算小球碰撞砖块的时间t,当且仅当t1=t2=…tn 的时候,视为小球同时碰到了多个砖块,这样有多少个相同时间的t,就有多少个同时碰到的砖块,是一个比较通用的算法,但是这里关键在于如何计算小球运动的时间并快速比对.暂时没有实现,有待具体化.

3.游戏核心代码

/*1.找到小球所在砖块(虚拟)是第几个
2.计算出周围的8个砖块bricks[i]值.(即第几个砖块,从而砖块的x(Left),y(Top),visible全部获得了) */

void__fastcall TForm1::hitBrick(void){
        int ballX = Shape1->Left;
        int ballY = Shape1->Top;
        int ballRow = ballY/60;
        int ballColumn = ballX/100;
        int ballIndex=ballRow*10+ballColumn+1;//算出来的是小球所在砖块(虚拟)的index
        int brickIndex;
        inttempBricks[9]={ballIndex-11,ballIndex-10,ballIndex-9,ballIndex-1,
        ballIndex,ballIndex+1,ballIndex+9,ballIndex+10,ballIndex+11};//周围的8个砖块index
       if(ballX<bricks[30]->Left+200&ballX>bricks[30]->Left&ballY>bricks[30]->Top-15) dy=1; //打到挡板
        for(int i=0;i<9;i++){
        brickIndex = tempBricks[i];
       if(brickIndex>-1&&brickIndex<30&&bricks[brickIndex]->Visible==true){//只在边界内跟当前砖块visible为true判断
       if(ballX>bricks[brickIndex]->Left&&ballX<(bricks[brickIndex]->Left)+100
       &&ballY>bricks[brickIndex]->Top&&ballY<(bricks[brickIndex]->Top)+60){//砖块的矩形区域
               bricks[brickIndex]->Visible=false;
               Label1->Caption=AnsiString("Score:")+(++score);//计分
               dy=-dy;
               break; //hit one then jump outloop
        }
     }
   }
} 

参考文献:

[1] https://github.com/wangyucode/OpenBrick

[2] https://github.com/chendotjs/Hit-The-Bricks

[2] http://bbs.csdn.net/topics/350255299

[4] http://www.docin.com/p-763956556.html(打砖块的算法分析)赵高

结语:

图形化的,看起来很不起眼的传统小游戏,在程序世界自己设计的时候都可能遇到很多的问题,小球如何飞行,在现实世界只是一道弧线,然而在计算机世界中却需要让他在一个循环中不断的位移达到效果,包括如何判断碰撞,从最开始O(n2)算法复杂度,到最后优化到O(1),平均判断4次的常数级复杂度,离不开探讨,研究.

在游戏世界开发中,一个好的思路结构,一个优化的算法,可以避免非常多的问题,让你的程序显得清晰可读,代码复用性也会很强,这个游戏就算需要新增功能也会比较容易,并且性能上的差距也是极大的,打砖块的算法我查阅了不少blog跟github上的资料,但是网上大多都是传统的O(n2)遍历,甚至也没有从尾部开始,所以并不是没有研究跟自己改进的地方.相反还有挺大空间.

ps:打砖块的小游戏虽然代码少,思路精简,且查找算法复杂度为O(1),但是四个边界处没想到统一 的办法反弹,只管了上下边界.有待完善改进,希望完成的人能提交一下~

About

Hit-bricks game implements by C++ , core algorithm --> O(1)


Languages

Language:Pascal 83.5%Language:C++ 16.5%