每像素碰撞检测

一旦你知道如何检查两个矩形之间的碰撞,你可以检查任何两个图像之间的碰撞,因为所有的图像都是由矩形构成的。

在电子游戏中,所有的东西都可以用矩形来做,甚至这个点:

没看到?我们把它放大:

还没看到?那现在呢?:

图像由正方形的像素组成,正方形的像素为矩形。 要进行每个像素的碰撞检测,我们要做的就是让每个对象都有一组碰撞框,并检查一组碰撞框与另一组碰撞框的碰撞,如下所示:

//在屏幕上移动的点
class Dot
{public://The dimensions of the dotstatic const int DOT_WIDTH = 20;static const int DOT_HEIGHT = 20;//Maximum axis velocity of the dotstatic const int DOT_VEL = 1;//Initializes the variablesDot( int x, int y );//Takes key presses and adjusts the dot's velocityvoid handleEvent( SDL_Event& e );//Moves the dot and checks collisionvoid move( std::vector<SDL_Rect>& otherColliders );//Shows the dot on the screenvoid render();//获取碰撞框std::vector<SDL_Rect>& getColliders();private://The X and Y offsets of the dotint mPosX, mPosY;//The velocity of the dotint mVelX, mVelY;//点的碰撞框std::vector<SDL_Rect> mColliders;//移动碰撞框相对于点的偏移void shiftColliders();
};

这是我们的点,现在有了每像素碰撞检测。它的速度减少到每帧1像素,使碰撞更容易看到。move函数现在接受了一个碰撞框的向量,所以我们可以对照检查两组碰撞。由于我们将有两个点碰撞,我们需要能够得到碰撞器,所以我们有一个函数来处理。

我们没有一个单一的碰撞框,而是有一个碰撞器的向量。我们也有一个内部函数来移动碰撞器以匹配点的位置。

//Starts up SDL and creates window
bool init();//Loads media
bool loadMedia();//Frees media and shuts down SDL
void close();//Box set collision detector
bool checkCollision( std::vector<SDL_Rect>& a, std::vector<SDL_Rect>& b );

在这里,我们有了新的碰撞检测器,它可以相互检查碰撞框的集合。

Dot::Dot( int x, int y )
{//初始化偏移量mPosX = x;mPosY = y;//创建必要的SDL_RectsmColliders.resize( 11 );//初始化速度mVelX = 0;mVelY = 0;//初始化碰撞框的宽度和高度。mColliders[ 0 ].w = 6;mColliders[ 0 ].h = 1;mColliders[ 1 ].w = 10;mColliders[ 1 ].h = 1;mColliders[ 2 ].w = 14;mColliders[ 2 ].h = 1;mColliders[ 3 ].w = 16;mColliders[ 3 ].h = 2;mColliders[ 4 ].w = 18;mColliders[ 4 ].h = 2;mColliders[ 5 ].w = 20;mColliders[ 5 ].h = 6;mColliders[ 6 ].w = 18;mColliders[ 6 ].h = 2;mColliders[ 7 ].w = 16;mColliders[ 7 ].h = 2;mColliders[ 8 ].w = 14;mColliders[ 8 ].h = 1;mColliders[ 9 ].w = 10;mColliders[ 9 ].h = 1;mColliders[ 10 ].w = 6;mColliders[ 10 ].h = 1;//初始化相对于位置的碰撞器shiftColliders();
}

就像之前一样,我们必须在构造函数中设置碰撞器的尺寸。这里唯一不同的是,我们有多个碰撞框需要设置。

void Dot::move( std::vector<SDL_Rect>& otherColliders ){//Move the dot left or rightmPosX += mVelX;shiftColliders();//If the dot collided or went too far to the left or rightif( ( mPosX < 0 ) || ( mPosX + DOT_WIDTH > SCREEN_WIDTH ) || checkCollision( mColliders, otherColliders ) ){//Move backmPosX -= mVelX;shiftColliders();}//Move the dot up or downmPosY += mVelY;shiftColliders();//If the dot collided or went too far up or downif( ( mPosY < 0 ) || ( mPosY + DOT_HEIGHT > SCREEN_HEIGHT ) || checkCollision( mColliders, otherColliders ) ){//Move backmPosY -= mVelY;shiftColliders();}
}

这个功能和之前的差不多。每当我们移动点,我们就移动碰撞器。在我们移动点之后,我们检查它是否离开了屏幕或撞到了什么东西。如果是这样,我们就把点移回来,并把它的碰撞器也一起移动。

void Dot::shiftColliders(){//行偏移量int r = 0;//通过点的碰撞框for( int set = 0; set < mColliders.size(); ++set ){//将碰撞框居中mColliders[ set ].x = mPosX + ( DOT_WIDTH - mColliders[ set ].w ) / 2;//在它的行偏移处设置碰撞框mColliders[ set ].y = mPosY + r;//将行的偏移量向下移动到碰撞框的高度。r += mColliders[ set ].h;}
}std::vector<SDL_Rect>& Dot::getColliders(){return mColliders;
}

不要太担心shiftColliders的工作原理。它是mColliders[ 0 ].x = …,mColliders[ 1 ].x = …等的简便方法,它适用于这种特定情况。对于自己的每一个像素对象,你会有自己的放置函数。

而在shiftColliders之后,要有一个获取colliders的访问函数。

bool checkCollision( std::vector<SDL_Rect>& a, std::vector<SDL_Rect>& b ){//矩形的边框int leftA, leftB;int rightA, rightB;int topA, topB;int bottomA, bottomB;//通过A框for( int Abox = 0; Abox < a.size(); Abox++ ){//计算矩形A的边长leftA = a[ Abox ].x;rightA = a[ Abox ].x + a[ Abox ].w;topA = a[ Abox ].y;bottomA = a[ Abox ].y + a[ Abox ].h;//通过B框for( int Bbox = 0; Bbox < b.size(); Bbox++ ){//计算矩形B的边长leftB = b[ Bbox ].x;rightB = b[ Bbox ].x + b[ Bbox ].w;topB = b[ Bbox ].y;bottomB = b[ Bbox ].y + b[ Bbox ].h;//如果A的任何边都不在B的外面if( ( ( bottomA <= topB ) || ( topA >= bottomB ) || ( rightA <= leftB ) || ( leftA >= rightB ) ) == false ){//检测到碰撞return true;}}}//如果两组碰撞框都没有接触return false;
}

在我们的碰撞检测函数中,我们有一个for循环,计算对象a中每个碰撞框的顶部/底部/左侧/右侧。

然后我们计算对象b中每个碰撞框的上/下/左/右,然后检查是否没有分离轴。如果没有分离轴,我们返回true。如果我们通过这两个集合而没有碰撞,我们返回false。

            //Main loop flagbool quit = false;//Event handlerSDL_Event e;//将在屏幕上移动的点Dot dot( 0, 0 );//将要碰撞的点Dot otherDot( SCREEN_WIDTH / 4, SCREEN_HEIGHT / 4 );

在进入主循环之前,我们先声明我们的点和我们要碰撞的另一个点。

             //While application is runningwhile( !quit ){//Handle events on queuewhile( SDL_PollEvent( &e ) != 0 ){//User requests quitif( e.type == SDL_QUIT ){quit = true;}//Handle input for the dotdot.handleEvent( e );}//移动点并检查碰撞dot.move( otherDot.getColliders() );//Clear screenSDL_SetRenderDrawColor( gRenderer, 0xFF, 0xFF, 0xFF, 0xFF );SDL_RenderClear( gRenderer );//Render dotsdot.render();otherDot.render();//Update screenSDL_RenderPresent( gRenderer );}

再次在主循环中,为点处理事件,对点进行碰撞检查,然后最后我们渲染我们的对象。

我经常被问到的一个问题是,如何制作一个加载图像并自动生成每像素碰撞检测的碰撞框集的函数。答案很简单。

别这样做

在大多数游戏中,你不希望100%的准确性。碰撞框越多,你的碰撞检查就越多,速度也越慢。大多数游戏追求的是足够接近,比如在《街头霸王》中:

结果虽然不是像素完美,但已经很接近了。

另外,我们还可以在这里做一个优化。我们可以为点设置一个边界框,封装所有其他的碰撞框,然后在进入每个像素的碰撞框之前先检查这一个。这样做确实会多增加一次碰撞检测,但由于两个物体不碰撞的可能性更大,所以更可能为我们节省额外的碰撞检测。在游戏中,通常使用具有不同细节级别的树结构来完成此操作,以便尽早使用,以防止在每个像素级别进行不必要的检查。和之前的教程一样,树形结构不在这些教程的范围内。

在 这里下载本教程的媒体和源代码。

原文链接

关注我的公众号:编程之路从0到1

SDL编程入门(28)每像素碰撞检测相关推荐

  1. SDL编程入门(27)碰撞检测

    碰撞检测 在游戏中,你经常需要判断两个物体是否相互撞击.对于简单的游戏来说,通常用边界框碰撞检测来完成. 碰撞框是检查两个对象之间碰撞的标准方法.当两个多边形没有分开时,它们就会发生碰撞. 这里我们有 ...

  2. SDL编程入门(8)几何图形渲染

    几何图形渲染 除了新的纹理API,SDL还有新的基元渲染调用作为其渲染API的一部分.因此,如果你需要渲染一些基本的形状,而你又不想为它们创建额外的图形,SDL可以为你省力. bool loadMed ...

  3. SDL编程入门(20)触力反馈

    触力反馈 我们知道了如何用SDL使用操纵杆,现在我们可以使用新的触觉API来使控制器震动. //带有触力反馈的游戏控制器1处理程序 SDL_Joystick* gGameController = NU ...

  4. SDL编程入门(19)游戏手柄和操纵杆

    游戏手柄和操纵杆 就像鼠标输入和键盘输入一样,SDL也有能力读取来自操纵杆/游戏手柄/游戏控制器的输入.在本教程中,我们将根据操纵杆的输入使箭头旋转. //模拟手柄死区 const int JOYST ...

  5. Opencv3编程入门学习笔记(三)之访问图像像素的三种方法

    访问图像像素的三种方法:指针访问,迭代器访问,动态地址访问.访问最快的为指针访问,以下算法在几毫秒,但指针访问容易造成内存泄漏:其次为迭代器访问:最后为动态地址访问. 以下程序是根据<OpenC ...

  6. 【SDL游戏编程入门第一卷】配置SDL环境

    一.前言 这段时间正好使用 SDL,所以打算开一个新的系列,作为经验分享 -- SDL 游戏编程入门. 这也是 AnnihilateSword 在此站的第一个系列,文中可能有许多不足,还请多多包涵,如 ...

  7. Pygame游戏编程入门笔记

    目录 1. Python游戏编程入门 2. 建立开发环境 3. 简单示例 4. 绘制不同形状 5. 事件监听举例 6. 用pygame打印文本 7. 键盘事件 8. 鼠标事件 9. 轮询键盘 10. ...

  8. 【浅墨著作】 OpenCV3编程入门 内容简介 勘误 配套源代码下载

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 经过近一 ...

  9. Android 4游戏编程入门经典

    <Android 4游戏编程入门经典> 基本信息 原书名:Beginning Android 4 Games evelopment 作者: (美)Mario Zechner Robert ...

最新文章

  1. 阿帕奇搭建文件服务器,关于LINUX文件服务器简单搭建---NFS与APACHE服务
  2. python常用函数-Python小白必备的8个最常用的内置函数(推荐)
  3. ATO,MTO和ETO
  4. u-boot移植重要问题说明
  5. tracert路由检测命令使用方法
  6. 综述ASP.NET下的AJAX模式
  7. 中国如何引进CMM评估,促进软件产业发展
  8. 解决C# 7.2中的结构体性能问题
  9. java中bpmn流程图_Java学习之BPMN知识以及Activiti的流程部署
  10. mysql 出现 quot_MYSQL 新版出现 Client does_mysql _ 搞代码
  11. mysql80连接不上本地服务器_小白教程:ubuntu服务器安装jupyter notebook, 并能够实现本地远程连接
  12. mysql查询操作的5种子句
  13. BZOJ3881 Coci2015 Divljak fail树+差分
  14. Java伪装csrss_结束伪装成系统进程名的进程的批处理
  15. SolarWinds盛邀技术专业人士参加第八届年度THWACKcamp虚拟IT活动
  16. html5在线聊天插件,纯js网页在线聊天对话插件(原创)
  17. VBA解压压缩文件。
  18. pycharm切换python环境_在PyCharm的 Terminal(终端)切换Python版本的方法
  19. 通过Akka学习指数退避(Exponential Backoff)
  20. 物联网驱动数字化转型

热门文章

  1. python课程介绍-Python简介
  2. 互联网公司招聘黑话大全
  3. 15款js编辑器大全 收藏
  4. YTU软件工程白盒测试
  5. 使用js格式化json
  6. 转自Avan_Lau
  7. 简单脱壳教程笔记(2)---手脱UPX壳(1)
  8. docker介绍说明,docker配置代理加速器、docker使用国内镜像仓库
  9. 计算机pm,PM、PMP、PMO分别都是什么 以及三者的关系
  10. photoshop时钟制作过程