-- 檢測碰撞物,如果發生碰撞則進行位移
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
只是提供思路,沒了