设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 1415|回复: 3
打印 上一主题 下一主题

[已经过期] 如何判断精灵与精灵触碰?

[复制链接]

Lv3.寻梦者

梦石
0
星屑
1939
在线时间
403 小时
注册时间
2015-8-30
帖子
395
跳转到指定楼层
1
发表于 2016-4-27 20:48:02 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有帐号?注册会员

x
就是说,判断一个Sprite对象和另一个Sprite对象的非透明部分的任何像素是否处在了同一坐标,如果判断为true就返回true,否则返回false。
小仙女一枚~
头像被屏蔽

Lv1.梦旅人 (禁止访问)

梦石
0
星屑
88
在线时间
631 小时
注册时间
2014-8-4
帖子
3600
2
发表于 2016-4-27 21:45:32 | 只看该作者
提示: 作者被禁止或删除 内容自动屏蔽
签名被屏蔽
回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
1939
在线时间
403 小时
注册时间
2015-8-30
帖子
395
3
 楼主| 发表于 2016-4-30 21:56:00 | 只看该作者
本帖最后由 有丘直方 于 2016-5-1 07:14 编辑
  1. #===============================================================================
  2. # 判断精灵触碰用的脚本
  3. # 目前没有自行测试过。
  4. # 注释有点乱……但是看着方便
  5. #-------------------------------------------------------------------------------
  6. # Sprite 精灵类
  7. #===============================================================================
  8. class Sprite
  9.   #-----------------------------------------------------------------------------
  10.   # 判断是否接触——按照矩形计算,也是最简单的算法。
  11.   # 这个方法是自己最能想到的。
  12.   # 返回值:true表示触碰,false表示没有触碰
  13.   # 参数:other:判断接触的另外一个精灵
  14.   #-----------------------------------------------------------------------------
  15.   def touch_rect(other)
  16.     self.ox, self.oy = 0 # 首先确保原点在左上角
  17.     if self.x.between?(other.x = self.bitmap.width,  # 判断self的x坐标是否过于靠右
  18.                        other.x + other.bitmap.width) # 判断self的x坐标是否过于靠左
  19.                                                      # 如果self的x坐标适中,那么继续判断
  20.       if self.y.between?(other.y = self.biemap.height,  # 与之前相似的办法判断y坐标
  21.                          other.y + other.bitmap.height, # 同上……
  22.                                                         # 如果y坐标适中,那么确定触碰
  23.         return true                                     # 返回true
  24.       else           # 如果y坐标的条件不满足触碰
  25.                      # 那么确定没有触碰
  26.         return false # 返回false
  27.       end            # y坐标判定结束
  28.     else           # 如果x坐标的条件不满足触碰
  29.                    # 同样,确定没有触碰
  30.       return false # 返回false
  31.     end            # x坐标判定结束
  32.   end # touch_rect方法定义结束
  33.   #-----------------------------------------------------------------------------
  34.   # 判断是否接触——按照圆形计算,略微有点复杂,因为涉及到四则运算之外。
  35.   # 这个方法是结合了taroxd和冷峻逸的方法想出来的……
  36.   # 返回值:true表示触碰,false表示没有触碰
  37.   # 参数:other:判断接触的另外一个精灵
  38.   #-----------------------------------------------------------------------------
  39.   def touch_circle(other)
  40.     self.ox, self.oy = 0 # 确保原点在左上角
  41.     bool = nil # 先创建一个临时变量,后期返回它
  42.     num1 = Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)     # 比较参数1:勾股平方和
  43.     num2 = self.bitmap.width / 2 + other.bitmap.width / 2 # 比较参数2:半径和平方
  44.     bool = (num1 <= num2) # 进行比较
  45.                           # 这里其实用到了勾股定理。
  46.                           # num1其实是在使用勾股定理通过两条直角边来计算斜边
  47.                           # num2则是利用圆半径永远相等的定理计算圆心的距离
  48.                           # 那么很显然,如果num1的长度不超过num2,两个精灵就相碰了
  49.     return bool # 返回运算结果。
  50.                 # 令我感到很奇怪的是这个方法的定义居然比touch_rect方法短
  51.   end # touch_circle方法定义结束
  52.   #-----------------------------------------------------------------------------
  53.   # 判断是否接触——按照非透明部分计算,很复杂,效率低。
  54.   # 这个方法是冷峻逸最先提出,也是被最先否定的,因为效率太低了
  55.   # 返回值:true表示触碰,false表示没有触碰
  56.   # 参数:other:判断接触的另外一个精灵
  57.   #-----------------------------------------------------------------------------
  58.   def touch_opacity(other)
  59.     return false unless self.touch_rect(other) # 如果没有矩形触碰则直接返回false
  60.     self.ox, self.oy = 0 # 惯例,让原点在左上角
  61.     tab1 = Table.new(self.bitmap.width, self.bitmap.height) # 这个二维表格是用来挨个判断不透明度用的
  62.     for x in 0...(self.bitmap.width)            # 用循环挨个获取像素点的Color
  63.       for y in 0...(self.bitmap.height)         # 分别用x、y两个临时变量接受像素坐标
  64.         tab1[x, y] = (self.bitmap.get_pixel(x, y).alpha > 0) # 在这里获取像素点是否透明
  65.       end                                       # 纵向获取结束
  66.     end                                         # 横向获取结束
  67.     tab2 = Table.new(other.bitmap.width, other.bitmap.height) # 这下轮到了判断触碰的精灵
  68.     for x in 0...(other.bitmap.width); for y in 0...(other.bitmap.height) # 循环
  69.       tab2[x, y] = (other.bitmap.get_pixel(x, y).alpha > 0) # 在这里获取像素点是否透明
  70.     end; end # 颜色获取结束
  71.     for x in 0...(self.bitmap.width); for y in 0...(self.bitmap.height) # 循环
  72.       if tab1[x, y] and tab2[x, y]                                 # 如果相对于精灵的位置相同的两点都不透明
  73.         if self.x + x == other.x + x and self.y + y == other.y + y # 如果相对于游戏程序窗口的位置也相同
  74.                                                                    # 确定两个精灵相碰
  75.           return true                                              # 返回true
  76.       end; end                                                     # 分歧结束
  77.     end # 循环结束
  78.     return false # 如果到现在还没有return,确定还没有触碰,返回false
  79.   end # touch_opacity方法定义结束
  80. end # Sprite类定义结束
  81. # 因为没有测试,所以能不能正常运行还不知道。不过应该可以了。
  82. # 第三种方法因为用到了很多循环,所以效率很低,不建议使用。
复制代码
  1. class Sprite
  2.   def touch_rect(other)
  3.     self.ox, self.oy, other.ox, other.oy = 0
  4.     return (self.x.between?(
  5.                             other.x - self.bitmap.width,
  6.                             other.x + other.bitmap.width
  7.                             ) and
  8.             self.y.between?(
  9.                             other.y - self.biemap.height,
  10.                             other.y + other.bitmap.height
  11.                             )
  12.             )
  13.   end
  14.   def touch_circle(other)
  15.     return false unless self.touch_rect(other)
  16.     self.ox, self.oy, other.ox, other.oy = 0
  17.     return (
  18.             Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) <=
  19.             self.bitmap.width / 2 + other.bitmap.width / 2
  20.             )
  21.   end
  22.   def touch_opacity(other)
  23.     return false unless self.touch_rect(other)
  24.     self.ox, self.oy, other.ox, other.oy = 0
  25.     tab_self = Table.new(self.bitmap.width, self.bitmap.height)
  26.     for x in 0...(self.bitmap.width)
  27.       for y in 0...(self.bitmap.height)
  28.         tab_self[x, y] = (self.bitmap.get_pixel(x, y).alpha > 0)
  29.       end
  30.     end
  31.     tab_other = Table.new(other.bitmap.width, other.bitmap.height)
  32.     for x in 0...(other.bitmap.width)
  33.       for y in 0...(other.bitmap.height)
  34.         tab_other[x, y] = (other.bitmap.get_pixel(x, y).alpha > 0)
  35.       end
  36.     end
  37.     ary = []
  38.     for self_x in 0...(self.bitmap.width)
  39.       for self_y in 0...(self.bitmap.height)
  40.         for other_x in 0...(other.bitmap.width)
  41.           for other_y in 0...(other.bitmap.height)
  42.             ary += (tab_self[self_x, self_y]             and
  43.                     tab_other[other_x, other_y]          and
  44.                     self.x + self_x == other.x + other_x and
  45.                     self.y + self_y == other.y + other_y
  46.                     )
  47.           end
  48.         end
  49.       end
  50.     end
  51.     return ary.include?(true)
  52.   end
  53. end
复制代码
  1. # 测试代码如下(此代码经过测试,没有BUG):
  2. class Sprite
  3.   def touch_rect(other)
  4.     return (self.x.between?(
  5.                             other.x - self.bitmap.width,
  6.                             other.x + other.bitmap.width
  7.                             ) and
  8.             self.y.between?(
  9.                             other.y - self.bitmap.height,
  10.                             other.y + other.bitmap.height
  11.                             )
  12.             )
  13.   end
  14. end
  15. module SceneManager
  16.   def self.first_scene_class
  17.     return Scene_Test
  18.   end
  19. end
  20. class Scene_Test < Scene_Base
  21.   def start
  22.     super
  23.     @test = Sprite.new
  24.     @test.bitmap = Bitmap.new(32, 32)
  25.     @test.bitmap.fill_rect(0, 0, 32, 32, Color.new(255, 0, 0))
  26.     @test.y = (640 - 32) / 2
  27.     @sprite = Sprite.new
  28.     @sprite.bitmap = Bitmap.new(12, 416)
  29.     @sprite.bitmap.fill_rect(0, 0, 12, 416, Color.new(255, 255, 255))
  30.     @sprite.x = 544 - 12 - 12
  31.   end
  32.   def update
  33.     super
  34.     @test.update
  35.     @sprite.update
  36.     @test.x += 1
  37.     if @test.touch_rect(@sprite)
  38.       p 'OK.'
  39.       SceneManager.goto(Scene_Title)
  40.     end
  41.   end
  42.   def terminate
  43.     super
  44.     @test.bitmap.dispose
  45.     @sprite.bitmap.dispose
  46.     @test.dispose
  47.     @sprite.dispose
  48.   end
  49. end
复制代码

点评

我大体瞄了一眼,第一个代码框的21行少一个有括号。第二个代码框的09行的self.biemap错误不解释。语法错误一大堆,不知道你的代码怎么跑起来的  发表于 2016-5-2 22:01
我用相似的办法测试了touch_opacity。这个方法卡得啊……执行一次要花N长时间。不过测试结果令人满意。  发表于 2016-5-1 07:26
测试好了,没有问题  发表于 2016-5-1 07:14
我测试一下  发表于 2016-5-1 06:59
根据我目测,3段代码应该都不能正常运行  发表于 2016-5-1 06:56
小仙女一枚~
回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
4598
在线时间
1206 小时
注册时间
2016-4-7
帖子
982

开拓者

4
发表于 2016-5-3 05:43:20 | 只看该作者
这槽点满满的代码我都不知从何吐槽起。。。ORZ
先不说楼主的整体思路就有问题了,代码中的各种语法错误就有不少。。。贴代码前能自己先跑一边么,这连解释器都过不了的代码竟然还能测试,还能测试成功ORZ

我就只说说楼主的 第三个代码框里的touch_rect这个方法吧。。。

我们把问题稍微简化一下,假定sprite的width和sprite.bitmap.width相等,height等同。
然后把sprite什么的都扔掉不管,整个问题可以简化为两个rect的碰撞检测的问题,换算到数学上就是两个齐轴矩形是否相交。
再进一步,我们只考虑单个轴方向上碰撞(这里看X轴)的情况(x和y轴的判别方法同理)。

根据楼主的代码,得出如下的简化代码:
RUBY 代码复制
  1. # 一个rect差不多是如下这样的结构体
  2. rect = Struct.new(:x, :y, :w, :h)
  3.  
  4. # 转换过后的touch_rect方法
  5. def touch_rect(rect1, rect2)
  6.     rect1.x.between?(rect2.x - rect1.w, rect2.x + rect1.w)
  7. end


可以看出楼主的判别方法是判断rect1的x坐标是否在rect2.x±rect1.w这个区间内。

那么上动图:


在这里,点A、B所构成的区间即为rect1,点C、D所构成的区间为rect2,而虚线E、F则表示的是rect2.x±rect1.w这个判别区间。
从动图来看,前半部份并没问题,但是当rect1进入灰色区域时,问题就出现了,这时点A早就出了判别区间,这时会认为rect1和rect2并没发生碰撞,但是实际上rect1的一大半部分还在rect2内!真正的碰撞结束应该是当点A离开C、D区间!

而楼主在第三个代码框内的所谓的test只能说是没考虑全部情况的错误测试。(很明显代码中@sprite.width小于@test.width)

最简单的矩形碰撞都有问题了,其余的我就不说了。


实际上楼主所描述的问题是游戏开发中很重要的一大块——物理引擎的核心部分包围盒碰撞检测问题。
而一般包围盒碰撞检测的代码也不会直接放到sprite这样的基础渲染对象里(降低代码耦合)。

点评

width等同于src_rect.width,而src_rect是位图的矩形,所以width永远是和bitmap一致  发表于 2016-5-7 07:10
miaowm5.github.io/RMVA-F1/RPGVXAcecn/rgss/gc_sprite.html width方法让你吃了?  发表于 2016-5-5 05:06
rect1.x.between?(rect2.x-rect1.w,rect2.x+rect2.w) 还有就是纠正一下,sprite没有width,只有sprite的bitmap有width。  发表于 2016-5-4 22:17
诶不过用修改的测试过了,还是成功的。你介绍的方法让我好好研究一下  发表于 2016-5-4 22:10
附庸的附庸不是我的附庸,女儿的女儿还是我的女儿。CK2沉迷ing
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-11-16 09:27

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表