子弹反弹示意图如下:
1.墙体与子弹均为碰撞体
2.前提条件:需要知道子弹前进方向向量,墙体方向向量
3.计算简易过程,黄色为子弹方向向量,计算出与墙的夹角,根据入射角等于反射角算出橙色方向向量。此时只是子弹方向改变了,接下来还要算出位置的偏移量,根据与墙夹角与子弹本身的大小由三角函数sin/cos算出偏移量,最终效果如红色箭头。
代码:
-- 子弹反弹,ColliderObj:碰撞墙体共4面.
-- self.bulletObjLength为子弹长度估算值, self.Dir为子弹当前的方向向量, self.o_Transform为子弹实例
function BCAmmoBase:ReflectBullet(ColliderObj)
-- inNormal: 墙体方向向量,用于计算反射角,为法线
-- XOrZ: 判断子弹碰撞为水平墙体还是垂直墙体
-- iden: 子弹所碰撞墙体的标识,用于记录上次碰撞的墙体
local inNormal, XOrZ, iden = nil
if tostring(ColliderObj.transform.name) == "ForwardWall" then
inNormal = Vector3(0, 0, -1)
XOrZ = true
iden = "F"
elseif tostring(ColliderObj.transform.name) == "BackWall" then
inNormal = Vector3(0, 0, 1)
XOrZ = true
iden = "B"
elseif tostring(ColliderObj.transform.name) == "LeftWall" then
inNormal = Vector3(1, 0, 0.1)
XOrZ = false
iden = "L"
elseif tostring(ColliderObj.transform.name) == "RightWall" then
inNormal = Vector3(-1, 0, 0.1)
XOrZ = false
iden = "R"
end
if inNormal == nil or iden == nil then
-- log("inNormal == nil or iden == nil ")
return
end
-- 为了避免子弹飞出墙体,同一墙体禁止反弹多次
if iden == self.collideLastIden then
-- log("iden == self.collideLastIden ")
return
end
self.collideLastIden = iden
self.collideInNormal = inNormal
self.collideXOrZ = XOrZ
local reflexDir = Vector3.Reflect(self.Dir, self.collideInNormal)
local dirAngle = 90 - Vector3.Angle(reflexDir, self.collideInNormal)
local dic = math.sin(math.rad(dirAngle)) * self.bulletObjLength
-- log(tostring(reflexDir) .. " 反射向量")
-- log(tostring(dirAngle) .. " 反射向量与位置矫正的夹角")
-- log(tostring(dic) .. " 位置矫正")
-- 位置矫正
local pos = self.o_Transform.position
if self.collideXOrZ then
self.o_Transform.position = reflexDir.x >= 0 and pos + Vector3(dic, 0, 0) or pos - Vector3(dic, 0, 0)
else
self.o_Transform.position = reflexDir.z >= 0 and pos + Vector3(0, 0, dic) or pos - Vector3(0, 0, dic)
end
-- 方向矫正
local dir = Vector3.Normalize(reflexDir)
self.Dir = Vector3(dir.x, 0, dir.z) -- Y轴固定为0,避免子弹上仰或者下倾
self.o_Transform.rotation = Quaternion.FromToRotation(Vector3.forward, self.Dir)
end