HGE做格斗游戏的热点图片碰撞检测法

碰撞检测始终是做2D游戏中的一个热点话题,我本人并没有做过这类游戏,所以一切只是理论而已,不过正打算做这么个小游戏练练手。
 
前几天在HGE的群里看到有人突然问到如何判断鼠标有没有点到人(点到纹理的透明区域不算),从而引申出了碰撞检测问题。
 
他的问题相对好实现,只要算出纹理所按的点是不是透明即可。
 
接下来我得做下碰撞检测的笔记:
 
碰撞检测最常用一个方法就是关节设置(当然我并没有做过),关节设置的话因为只是判断多边形的重叠状况,算法的复杂度低、效率高,虽然做工有点粗,但总体效果还是性价比比较高的一种方法。当然,这样的方法需要对每一帧的纹理都设置一个关节,对于人工的代价就稍微大了一些了,并且还要写个关节编辑器啊神马的,于是乎代码量又增加了。我这次是和同寝室木有一点基础的童鞋一起练手的,所以并没有打算引进这个方法。
 
于是我就用了另一种稍微“非主流”一些的方法了——逐像素判断。
 
但是逐像素判断还是有问题的——如果你的一个“效果”因为“温度过高”而不需要显示,直接隐藏,但又算伤害,这时纹理的逐像素就失去了意义。于是又有了个“臃肿”的办法,为需要“额外附加像素”的纹理另做一张图片,这张图片上有两种区域——热点区和非热点区。我们把需要“当做空气”的那些区域一律用某一种极其不常用的颜色覆盖,如ff00ff这种变态的粉色,然后其它区域的颜色就随你怎么搞了。我们载入的时候两张纹理一起载入,显示的时候显示正常的纹理,而在碰撞检测的时候用“热点图片”来进行逐像素检测。
 
与上面的关节设置法比较的话,人工的工作量我个人认为是大大地减少了,至于对于机器的执行能力来说,把时间复杂度提到了O(mn),平方级的复杂度了,即纹理相交区域的宽和高。
 
我们来看一下这种碰撞检测的大体流程吧:
 
1、获得两个精灵的矩形,并得到相交矩形。若无相交则直接返回false。
 
2、根据相交矩形,我们可以得到精灵1、2的纹理中需要检测的初始坐标。
 
3、将精灵1、精灵2的热点图片的相交区域的那一部分像素拷贝出来备用。(因为有可能两个纹理句柄是一样的,不好同时lock)
 
4、开始对于拷贝出来的像素信息逐一判断对应像素点是否都“不是空气”,若都“不是空气”则可以判断为碰撞。
 
当然以上的流程我们还可以优化一下,省去拷贝的那一段时间。我们可以直接hge->Texture_Lock()来进行得到两个纹理的像素信息的首指针,如果两个纹理其实只是一个纹理的话,则只需hge->Texture_Lock()一次,而另一个指针也只想hge->Texture_Lock即可,然后直接开始判断。
 
下面献上我这个函数的实现以及测试代码和素材:

 /**
 * @brief   Test the collision by the "hot" texture
 * @author  XadillaX
 * @email   admin@xcoder.in
 * @date    2011/10/18
 * @http://xcoder.in
 *
 * @param spr1 The first sprite to test the collision
 * @param x1 "x" of top-left corner of sprite 1
 * @param y1 "y" of top-left corner of sprite 1
 * @param spr2 The second sprite to test the collision
 * @param x2 "x" of top-left corner of sprite 2
 * @param y2 "y" of top-left corner of sprite 2
 * @param hot1 The "hot" texture for sprite 1. It will be the default texture of spr1 if it equal to 0
 * @param hot2 The "hot" texture for sprite 2. It will be the default texture of spr2 if it equal to 0
 * @param airColor The color which considered of "air"
 *
 * @return if they are collided, return true
 */
bool IsCollision(hgeSprite* spr1, float x1, float y1, hgeSprite* spr2, float x2, float y2, HTEXTURE hot1 = 0, HTEXTURE hot2 = 0, DWORD airColor = 0xffff00ff)
{
    /** Set the rect */
    hgeRect r1, r2;
    r1.Set(x1, y1, x1 + spr1->GetWidth(), y1 + spr1->GetHeight());
    r2.Set(x2, y2, x2 + spr2->GetWidth(), y2 + spr2->GetHeight());

    /** Test for the intersect of rectangles */
    if(r1.Intersect(&r2))
    {
        int x[] = { x1, x2, x1 + spr1->GetWidth(), x2 + spr2->GetWidth() };
        int y[] = { y1, y2, y1 + spr1->GetHeight(), y2 + spr2->GetHeight() };
        std::sort(x, x + 4);
        std::sort(y, y + 4);
        hgeRect r;

        /** Set the rectangle area where the two rectangles intersected. */
        r.Set(x[1], y[1], x[2], y[2]);

        /** The start point of sprite1 and sprite2. (From the intersected area) */
        int sx1, sy1, sx2, sy2;
        sx1 = x[1] - x1;
        sy1 = y[1] - y1;
        sx2 = x[1] - x2;
        sy2 = y[1] - y2;

        /** Get the "hotspot" of texture */
        HTEXTURE hTex1 = hot1;
        HTEXTURE hTex2 = hot2;
        if(hTex1 == 0) hTex1 = spr1->GetTexture();
        if(hTex2 == 0) hTex2 = spr2->GetTexture();

        float tx1, ty1, tw1, th1, tx2, ty2, tw2, th2;
        int w1 = hge->Texture_GetWidth(hTex1), w2 = hge->Texture_GetWidth(hTex2);
        spr1->GetTextureRect(&tx1, &ty1, &tw1, &th1);
        spr2->GetTextureRect(&tx2, &ty2, &tw2, &th2);

        DWORD* color1 = new DWORD[(x[2] - x[1]) * (y[2] - y[1])];
        DWORD* color2 = new DWORD[(x[2] - x[1]) * (y[2] - y[1])];
        DWORD* color;

        /** Copy the effectivearea of texture 1 */
        color = hge->Texture_Lock(hTex1, true);
        for(int i = 0; i < y[2] - y[1]; i++)
        {
            for(int j = 0; j < x[2] - x[1]; j++)
            { 
                color1[i * (x[2] - x[1]) + j] = color[((int)ty1 + sy1) * w1 + (int)tx1 + sx1 + i * w1 + j];
            } 
        } 
        hge->Texture_Unlock(hTex1);

        /** Copy the effectivearea of texture 2 */
        color = hge->Texture_Lock(hTex2, true);
        for(int i = 0; i < y[2] - y[1]; i++)
        {
            for(int j = 0; j < x[2] - x[1]; j++) 
            { 
                color2[i * (x[2] - x[1]) + j] = color[((int)ty2 + sy2) * w2 + (int)tx2 + sx2 + i * w1 + j]; 
            } 
        } 
        hge->Texture_Unlock(hTex2);

        /** Test for the collision */
        for(int i = 0; i < y[2] - y[1]; i++)
        {
            for(int j = 0; j < x[2] - x[1]; j++)
            {
                if(color1[i * (x[2] - x[1]) + j] != airColor && color2[i * (x[2] - x[1]) + j] != airColor)
                {
                    delete []color1;
                    delete []color2;

                    return true;
                }
            }
        }

        delete []color1;
        delete []color2;
        return false;
    }
    else return false;
}


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
基本功能和翎风的一样。 ------------------------------------------------------------------ [2016.09.30] 更新M2功能 ------------------------------------------------------------------ 1.增加脚本命令:InSafeZone //检测人物是否在安全区 ------------------------------------------------------------------ 2.修改程序防止最新僵尸攻击 3.增加脚本命令:SETRANDOMNO //产生一个随机数字 [@main] #IF #ACT SETRANDOMNO //产生一个随机数字 #SAY : //将显示出产生的随机数 [@@CHECKNO] #IF CHECKRANDOMNO //检测输入的验证码是否正确 #ELSEACT MESSAGEBOX 验证码错误! CLOSE BREAK 4.增加脚本命令:ISONMAP MAP //检测当前人是否在MAP地图上 5.增加脚本命令:DELAYCALL 作用:延时执行同一个NPC脚本中指定的标签内容 格式:DELAYCALL S LABEL 其中S是延时秒数(毫秒),Label是执行的脚本标签 示例:DelayCall 3000 @DELAYCALLTEST --- 3秒后自动跳到@DELAYCALLTEST节执行 6.增加脚本命令:KILLBYMON //是否被怪杀 7.增加脚本命令:KILLBYHUM //是否被人杀 地图增加一参数:KILLFUNC(X) //X可以随意数字 说明: 人物在该地图杀人,将触发QFunction-0.txt的[@KillPlay数字]节 人物在该地图杀怪,将触发QFunction-0.txt的[@KillPlayMon数字]节 注:宝宝杀人杀怪有效(秒杀除外) 8. //杀人的怪物名字 9. //杀人的人物名字 #IF KILLBYMON SENDMSG 5 在:%m(%x:%y)把干掉了! #IF KILLBYHUM SENDMSG 5 在:%m(%x:%y)把干掉了! 10.增加人物S变量:(0~99个) 11.解决地图标记 NoHUMNoMon 有人刷怪不能用的问题! 12.增加NPC命令:REPAIRALL //特修身上所有装备 13.增加数据库(修复神水)设置:3 14 //特修身上所有装备 14.增加脚本命令:ISGROUPMASTER //检测是否组长,加入编组后组长会触发QFunction-0.txt 15.增加挂机泡点功能。 ===================================== OFFLINE S EXP S是时间,单位秒 EXP为每S秒得到的经验值 默认必须在安全区域有效 脚本示意: (******************************************************************) (@@offlinemsg) [@main] 一、点击开始脱机泡点后,可输入一段留言信息给你的朋友。\ \ \ \ [@@offlinemsg] #IF CHECKLEVEL > 0 #ACT OFFLINE 5 500 //每5秒增加500经验 --------------------------------------------------------------------------- 16.;检测是否组长 [@MAIN] #IF ISGROUPMASTER #ACT GroupMoveMap 3 330 330 #elsesay 你不是组长. 加入编组后组长会触发QFunction-0.txt [@GroupCreate] #if #say 加入编组 --------------------------------------------------------------------------- 17.增加脱机泡点功能 18.增加地图标记有人刷怪 [0 比奇] NoHUMNoMon ;有人才开始刷怪 19.增加地图标记禁止仍背包物品 [1 沃玛森林] NOTHROWITEM ;禁止仍背包物品 20.增加地图标记死后不爆背包物品 [2 毒蛇山谷] NODR

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值