unity3d实现像素游戏的精确碰撞判定

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq493453700/article/details/99397316
-- 检测碰撞物,如果发生碰撞则进行位移
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

只是提供思路,没了

猜你喜欢

转载自blog.csdn.net/qq493453700/article/details/99397316