版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
-- 检测碰撞物,如果发生碰撞则进行位移
function LColliderBDY:BDYFixedUpdate(velocity)
local isGround = false
local isWall = false
-- 检测和什么碰,2d碰撞范围一般比实际要大,因为AABB要大一点,为了精确碰撞,需要自己实现
local contactColliders = CS.Tools.Instance:Collider2DOverlapCollider(self.collider, self.filter) -- 这个函数其实Collider2D.OverlapCollider,用来手动检测碰撞,这边因为lua的缘故封装了一下
-- 最终位移坐标
local finalOffset_x = 0
local finalOffset_y = 0
for p, k in pairs(contactColliders) do
if self.collider.bounds:Intersects(k.bounds) then
local up, down, left, right = false, false, false, false
local go = k.attachedRigidbody.gameObject
if go:GetComponent(typeof(CS.UnityEngine.Rigidbody2D)) ~= nil and go.name == "test" then -- 如果是地图块
local name = utils.split(k.name, ",")
local num = tonumber(name[#name]) -- 地图块最后一个数字作为bit
if num | 14 == 15 then --位操作,算出这个方块朝哪个方向进行碰撞,一个方块可以有多个碰撞方向,这部分随意设计,只需要能知道这个collider的判定方向,用layermask什么都行
up = true
end
if num | 13 == 15 then --位操作
down = true
end
if num | 11 == 15 then --位操作
left = true
end
if num | 7 == 15 then --位操作
right = true
end
elseif go:GetComponent(typeof(CS.XLuaTest.LuaBehaviour)) ~= nil then -- 是游戏object,则只允许左右进行碰撞,LuaBehaviour是用来调用lua的脚本,雨女无瓜
left = true
right = true
else
return false, false
end
local menseki = utils.getBoundsIntersectsArea(self.collider.bounds, k.bounds)
if menseki.magnitude > 0 then -- 无视多少面积设置,先不设
-- 算2个collider之间距离,主要是为了法线
local cd2d = self.collider:Distance(k)
local a = CS.UnityEngine.Vector3(cd2d.pointA.x, cd2d.pointA.y, 0)
local b = CS.UnityEngine.Vector3(cd2d.pointB.x, cd2d.pointB.y, 0)
local normal = -CS.UnityEngine.Vector3(cd2d.normal.x, cd2d.normal.y, 0)
CS.UnityEngine.Debug.DrawLine(a, a + normal, CS.UnityEngine.Color.red)
CS.UnityEngine.Debug.DrawLine(b, b + normal, CS.UnityEngine.Color.yellow)
-- 做碰撞法线与行进方向的点积
-- local projection = CS.UnityEngine.Vector2.Dot(velocity.normalized, normal) -- 没用到,有需要可以自己看情况加
local offset_x = 0
local offset_y = 0
-- 左移,右移
if self.collider.bounds.center.x < k.bounds.center.x then
if left and CS.UnityEngine.Vector2.Dot(velocity.normalized, CS.UnityEngine.Vector2(-1, 0)) <= 0 then -- 如果碰撞朝向与行进方向相反,则求出位移坐标
offset_x = -menseki.x
end
else
if right and CS.UnityEngine.Vector2.Dot(velocity.normalized, CS.UnityEngine.Vector2(1, 0)) <= 0 then
offset_x = menseki.x
end
end
-- 上移,下移
if self.collider.bounds.center.y > k.bounds.center.y then
if up and CS.UnityEngine.Vector2.Dot(velocity.normalized, CS.UnityEngine.Vector2(0, 1)) <= 0 then
offset_y = menseki.y
end
else
if down and CS.UnityEngine.Vector2.Dot(velocity.normalized, CS.UnityEngine.Vector2(0, -1)) <= 0 then
offset_y = -menseki.y
end
end
if (up or down) and (left or right) then -- 如果同时满足上下和左右方向同时存在的情况,则根据碰撞方向来筛选掉另一个轴的位移
offset_x = offset_x * math.abs(normal.x)
offset_y = offset_y * math.abs(normal.y)
end
-- 留下最小位移坐标
if velocity.x > 0 then
if offset_x < finalOffset_x then
finalOffset_x = offset_x
end
else
if offset_x > finalOffset_x then
finalOffset_x = offset_x
end
end
if velocity.y > 0 then
if offset_y < finalOffset_y then
finalOffset_y = offset_y
end
else
if offset_y > finalOffset_y then
finalOffset_y = offset_y
end
end
if go:GetComponent(typeof(CS.UnityEngine.Rigidbody2D)) ~= nil and go.name == "test" then -- 判断是不是撞到地面,这样写不好,以后再优化
if finalOffset_x ~= 0 then
isWall = true
end
if finalOffset_y > 0 then
isGround = true
end
end
end
end
end
-- 更新自身位置
self.collider.attachedRigidbody.position = self.collider.attachedRigidbody.position + CS.UnityEngine.Vector2(finalOffset_x, finalOffset_y)
return isGround, isWall
end
直接上代码,在fixedupdate里调用这个代码,代码是lua写的
记得rigidbody2d设为Kinematic,useFullKinematicContacts设为false,因为我们自己实现碰撞,不需要unity的碰撞oncolliderenter之类的,把这个关了可能可以提高性能8
讲一下思路
因为unity用的box2d的aabb碰撞触发范围比定义的范围要大,所以对于像素游戏来说,会有像素差,会影响实际画面,比如站在地上有时候会离地腾空1个像素高
况且像素游戏基本是方块判定,也用不太到什么摩擦力,反弹,几何形状碰撞等,简单的自己也能加,所以自己实现碰撞是一个不错的选择
用Collider2D.OverlapCollider详见官方API,来检测碰撞物,获得碰撞物允许碰撞的朝向(上,下,左,右),自己的collider和检测到的collider的bounds互相检测一下,相交的话
看自己在对象的位置,以及自己移动方向是否面朝对象允许碰撞的朝向(点积小于0)算出碰撞面积,也就是需要位移的x轴和y轴的偏移量,留下基于自己位置和方向的最小的偏移量,这些用移动方向法线来写可能代码会好看点,最后更新rigidbox的position
只是提供思路,没了