源战役客户端
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 

748 行
21 KiB

SapManager = SapManager or BaseClass()
local SapManager = SapManager
local Status = Status
local Time = Time
local ScreenWidth = ScreenWidth
local ScreenHeight = ScreenHeight
local table_insert = table.insert
local table_sort = table.sort
SapManager.CONST_X_SIZE = 3000
SapManager.CONST_Y_SIZE = 3000
SapManager.XSize = 3000
SapManager.YSize = 3000
SapManager.viewDistanceMin = -1000 --视野可见范围最小值 真实值*100
SapManager.viewDistanceMax = 6000 --视野可见范围最大值 真实值*100
SapManager.ObjType =
{
JumpPoint = 1,
NPC = 2,
Door = 3,
Tile = 4,
Effect = 5,
Model = 6,
MoveObj = 7,
SpecialModel = 8,--特殊模型,只摆放在场景装饰用
CacheEffect = 9,
}
SapManager.OperateType =
{
CreateOBJ = 1,
DestoryOBJ = 2,
}
function SapManager:__init()
SapManager.Instance = self
self.callback_func_list = {}
self.objs_list = {}
self.obj_state = {}
self.last_update_time = 0
self.scene_mgr = SceneManager:getInstance()
self.main_camera = MainCamera:getInstance()
self.scene = Scene:getInstance()
self.screen_delta = co.TableXY(100,100)
self.point_list = {}
self.update_frame = 0
self.check_create_far_frame = 0
self.check_create_obj_frame = 0
self.first_check = true
self.main_role_pos = CoVector2.New(0,0)
self.cur_model_pos = CoVector2.New(0,0)
self.is_use_higher_check = false
self.is_enabled = true
self.last_create_effect_time = 0
self.open_model_height_check_mode = false
self.ignore_list = {}
self.create_list = {}
self.deleta_list = {}
self.camera_rect = {}
self.show_bound_max_size = 0
-- if ApplicationPlatform ~= RuntimePlatform.IPhonePlayer then
-- -- 检测模式 true为四叉树检测
-- self.change_check_mode = true
-- end
self.change_check_mode = false
for i=1,9 do
self.point_list[i] = Vector3(0,0,0)
end
--加载完NPC传送点后强制检测一次区域对象
local func = function()
self.lastCamPos = nil
end
GlobalEventSystem:Bind(EventName.NPC_LOAD_FINISH,func)
--小飞鞋传送上天后强制检测一次远景模型加载
local fly_func = function()
self.force_check_far_model_state = true
end
GlobalEventSystem:Bind(SceneEventType.DO_FLYSHOE_EFFECT_FINISH_UP,fly_func)
--小飞鞋落地后强制检测一次SAP清理旧位置对象
local fly_end_func = function()
self.check_create_obj_frame = 1000
self.check_create_far_frame = 1000
self.force_check_far_model_state = true
end
GlobalEventSystem:Bind(SceneEventType.DO_FLYSHOE_EFFECT_FINISH_DOWN,fly_end_func)
local scene_change_func = function()
self.initTree = false
end
GlobalEventSystem:Bind(SceneEventType.SCENE_CHANGED, scene_change_func)
end
function SapManager:getInstance()
if SapManager.Instance == nil then
SapManager.New()
end
return SapManager.Instance
end
--[[ func结构
operate:操作类型
type:对象类型
data:自定义数据
]]
function SapManager:RegisterCallbackFunc( func )
table_insert(self.callback_func_list,func)
end
--[[ obj结构
id:对象索引
obj_type:对象类型 SapManager.ObjType
pos:坐标 co.TableXY
size:大小 co.TableXY
]]
function SapManager:RegisterObj(obj)
local id = obj.type .. "-" .. obj.id
if not self.objs_list[id] then
obj.size.z = obj.size.z or 1
obj.bound_offset = obj.bound_offset or co.TableXYZ(0,0,0)
obj.bound_offset.z = obj.bound_offset.z or 0
obj.pos.z = obj.pos.z or 0
obj.rect = {center = obj.pos,
x1 = obj.pos.x - obj.size.x/2 + obj.bound_offset.x, y1 = obj.pos.y - obj.size.y/2 + obj.bound_offset.y, z1 = obj.pos.z - obj.size.z/2 + obj.bound_offset.z,
x2 = obj.pos.x + obj.size.x/2 + obj.bound_offset.x, y2 = obj.pos.y + obj.size.y/2 + obj.bound_offset.y, z2 = obj.pos.z + obj.size.z/2 + obj.bound_offset.z,}
self.objs_list[id] = obj
self.obj_state[id] = false
if self.change_check_mode then
self:InsertTree(obj)
end
else
logWarn("SapManager:RegisterObj id is registered",id)
end
end
function SapManager:UnRegisterObj(obj_type,obj_index)
local id = obj_type .. "-" .. obj_index
if self.objs_list[id] then
if self.obj_state[id] then
local obj = self.objs_list[id]
for k,func in pairs(self.callback_func_list) do
func(SapManager.OperateType.DestoryOBJ,obj.type,obj.id)
end
end
self.objs_list[id] = nil
self.obj_state[id] = nil
if self.change_check_mode then
self:ClearTreeObj(id)
end
end
end
function SapManager:IsInScreen(screen_pos)
if screen_pos.x > 0 and screen_pos.x < ScreenWidth + self.screen_delta.x and screen_pos.y > 0 and screen_pos.y < ScreenHeight + self.screen_delta.y then
return true
else
return false
end
end
function SapManager:IsOutSide(val1)
if val1 > SapManager.viewDistanceMax or val1 < SapManager.viewDistanceMin then
return true
else
return false
end
end
--使用更加精细的检测模式
function SapManager:SetUseCarefulCheckMode(bool, close_dis_check)
self.is_use_higher_check = bool
self.is_close_dis_check = close_dis_check
end
--[[
判断条件:如果两个“包围盒的中心点的距离”大于“两个包围盒长度的总和的一半”,就是超出边界(画图更能理解)
]]
function SapManager:InRect( r1, obj)
local r2 = obj.rect
local is_far = obj.is_far
if self.is_use_higher_check then
if obj.is_far == false and obj.type == SapManager.ObjType.Model then
--if SystemMemoryLevel.Cur == SystemMemoryLevel.Hight then --高端机采用更精确算法
r2 = {x1 = obj.rect.x1,y1 = obj.rect.z1 , z1 = obj.rect.y1,
x2 = obj.rect.x2,y2 = obj.rect.z2 , z2 = obj.rect.y2,
center = obj.rect.center}
is_far = true
--end
end
end
if is_far then
if not obj.is_far then
if self:InSmallRect(r1,r2) then
return true
end
end
self.cur_model_pos.x = obj.rect.center.x
self.cur_model_pos.y = obj.rect.center.z
local dis = CoVector2.distance(self.main_role_pos,self.cur_model_pos)
if not self.is_close_dis_check and self:IsOutSide(dis)then
return false
end
--判断条件:包围盒的任意一个顶点在屏幕内,就认为能看到
self.point_list[1].x,self.point_list[1].y,self.point_list[1].z = r2.x1,r2.y1,r2.z1
self.point_list[2].x,self.point_list[2].y,self.point_list[2].z = r2.x2,r2.y1,r2.z1
self.point_list[3].x,self.point_list[3].y,self.point_list[3].z = r2.x2,r2.y1,r2.z2
self.point_list[4].x,self.point_list[4].y,self.point_list[4].z = r2.x1,r2.y1,r2.z2
self.point_list[5].x,self.point_list[5].y,self.point_list[5].z = r2.x1,r2.y2,r2.z1
self.point_list[6].x,self.point_list[6].y,self.point_list[6].z = r2.x1,r2.y2,r2.z2
self.point_list[7].x,self.point_list[7].y,self.point_list[7].z = r2.x2,r2.y2,r2.z1
self.point_list[8].x,self.point_list[8].y,self.point_list[8].z = r2.x2,r2.y2,r2.z2
self.point_list[9].x,self.point_list[9].y,self.point_list[9].z = r2.center.x,r2.center.y,r2.center.z
local screen_pos = 1
for i,v in ipairs(self.point_list) do
screen_pos = self.main_camera:WorldToScreenPoint(v/MainCamera.PixelsToUnits)
if self:IsInScreen(screen_pos) then
return true
end
end
return false
end
local state = self:InSmallRect(r1,r2)
if state and self.open_model_height_check_mode and obj.type == SapManager.ObjType.Model then
local offset = obj.pos.z - self.role_height
if offset > 3000 or offset < -3000 then
state = false
end
end
return state
end
function SapManager:InSmallRect(r1,r2)
local l1 = (r1.x1+r1.x2)*0.5-(r2.x1+r2.x2)*0.5 -- 2个包围盒的中心点的距离
local l2 = (r1.x2+r2.x2-r1.x1-r2.x1)*0.5 -- [(r1.x2 - r1.x1) + (r2.x2 - r2.x1)] * 0.5 两个包围盒长的总和的一半
local s1 = (r1.y1+r1.y2)*0.5-(r2.y1+r2.y2)*0.5
local s2 = (r1.y2+r2.y2-r1.y1-r2.y1)*0.5
if l1 < 0 then
l1 = -l1
end
if l2 < 0 then
l2 = -l2
end
if s1 < 0 then
s1 = -s1
end
if s2 < 0 then
s2 = -s2
end
if l1 < l2 and s1 < s2 then
return true
end
return false
end
function SapManager:Update( cam_pos )
if not self.is_enabled then
return
end
if self.show_bound_max_size > 0 then
self:DebugDrawBound(cam_pos)
end
self.update_frame = self.update_frame + 1
self.check_create_far_frame = self.check_create_far_frame + 1
self.check_create_obj_frame = self.check_create_obj_frame + 1
if not self.scene:IsSceneLoaded() then
self.first_check = true
self.last_create_effect_time = 0
return
end
if cam_pos and self.lastCamPos then
local x_offset = cam_pos.x - self.lastCamPos.x
local y_offset = cam_pos.y - self.lastCamPos.y
local small_offset = 100
if ( x_offset < small_offset and x_offset > -small_offset ) and ( y_offset < small_offset and y_offset > -small_offset ) then
if not self.force_check_far_model_state and not self.wait_load_terrain_finish and not self.wait_load_particle_finish then
return
end
end
end
if Time.time - self.last_update_time < 0.1 and not self.wait_load_terrain_finish and not self.wait_load_particle_finish then
return
end
local main_role = self.scene:GetMainRole()
if not main_role then
return
end
self.main_role_pos.x = main_role.real_pos.x
self.main_role_pos.y = main_role.real_pos.y
self.last_update_time = Time.time
if self.lastCamPos == nil then
self.lastCamPos = {x = cam_pos.x, y = cam_pos.y}
else
self.lastCamPos.x = cam_pos.x
self.lastCamPos.y = cam_pos.y
end
local scale = self.main_camera:GetCameraRatio()
self.role_height = main_role.obj_pos_height * MainCamera.PixelsToUnits
local low_offset = 1
if SystemMemoryLevel.Cur == SystemMemoryLevel.Low then
low_offset = 0.8
if SystemRuntimePlatform.IsIphone() then
low_offset = 0.6
end
end
--跳跃的时候摄像机变高了,会影响视野范围,这里增加一个参数做偏移
--0.1 这个参数如果最大跳跃高度改变了,可能需要调整
local jump_offset = 0
if main_role then
jump_offset = (main_role.jump_height)/MainCamera.PixelsToUnits*0.15
end
scale = scale + jump_offset
local now_width = SapManager.XSize * scale * low_offset
local now_height = SapManager.YSize * scale * low_offset
self.camera_rect.x1 = cam_pos.x - now_width * 0.7
self.camera_rect.y1 = cam_pos.y - now_height * 0.3
self.camera_rect.x2 = cam_pos.x + now_width * 0.7
self.camera_rect.y2 = cam_pos.y + now_height * 1
if MapView.OpenSceneOperate then
self.camera_rect.y1 = cam_pos.y - now_height * 1
end
--远景对象一秒检测1次
local check_far = false
if self.update_frame > 60 then
check_far = true
self.update_frame = 0
self.force_check_far_model_state = false
end
--已创建的远景对象5秒才检测是否要销毁
local check_create_far = false
if self.check_create_far_frame > 300 then
check_create_far = true
self.check_create_far_frame = 0
end
--已创建的场景对象3秒才检测是否要销毁
local check_create_obj = false
if self.check_create_obj_frame > 180 or SystemRuntimePlatform.IsIphone() then
check_create_obj = true
self.check_create_obj_frame = 0
end
--local cur_time = os.clock()
if self.change_check_mode then
if self.initTree then -- 初始化判断
self:CheckChild(self.objTree,check_far,check_create_far,check_create_obj)
end
else
self:CheckObj(check_far,check_create_far,check_create_obj)
end
if self.deleta_list then
for key,id in pairs(self.deleta_list) do
local obj = self.objs_list[id]
for k,func in pairs(self.callback_func_list) do
if obj.type == SapManager.ObjType.CacheEffect then
func(SapManager.OperateType.DestoryOBJ,obj.type,obj.data.obj.obj_name)
else
func(SapManager.OperateType.DestoryOBJ,obj.type,obj.id)
end
end
self.deleta_list[key] = nil
end
end
if self.create_list then
local sort_func = function(a,b)
local i = self.objs_list[a]
local j = self.objs_list[b]
local di = co.PointDistance(i.pos,main_role.real_pos)
local dj = co.PointDistance(j.pos,main_role.real_pos)
if di < dj then
return true
end
end
table_sort(self.create_list,sort_func)
for key,id in ipairs(self.create_list) do
local obj = self.objs_list[id]
for k,func in pairs(self.callback_func_list) do
func(SapManager.OperateType.CreateOBJ,obj.type,obj.data)
end
self.create_list[key] = nil
end
end
self.first_check = false
end
-- 递归遍历树节点
function SapManager:CheckChild(parent,check_far,check_create_far,check_create_obj)
for id,v in pairs(parent.obj) do
local obj = self.objs_list[v]
if not self.ignore_list[id] and (not obj.is_far or self.first_check or check_far) then
if obj.is_far and self.obj_state[id] and not check_create_far then
--已经加载的远景对象未到检测周期
else
if self.obj_state[id] and not check_create_obj then
--已经加载的场景对象未到检测周期
else
if self:InRect(self.camera_rect,obj) then
if not self.obj_state[id] then
self.obj_state[id] = true
table_insert(self.create_list,id)
end
else
if self.obj_state[id] then
self.obj_state[id] = false
table_insert(self.deleta_list,id)
end
end
end
end
end
end
for i,v in pairs(parent.childs) do
if self:InSmallRect(self.camera_rect,v.rect) or parent.has_far==1 then
self:CheckChild(v,check_far,check_create_far,check_create_obj)
end
end
end
-- 全遍历场景物体
function SapManager:CheckObj(check_far,check_create_far,check_create_obj)
for id,obj in pairs(self.objs_list) do
if not self.ignore_list[id] and (not obj.is_far or self.first_check or check_far) then
if obj.is_far and self.obj_state[id] and not check_create_far then
--已经加载的远景对象未到检测周期
else
if self.obj_state[id] and not check_create_obj then
--已经加载的场景对象未到检测周期
else
if self:InRect(self.camera_rect,obj) then
if not self.obj_state[id] then
--限制场景特效创建间隔
local delay_state = false
--[[
if obj.type == SapManager.ObjType.Effect then
if cur_time - self.last_create_effect_time <= 0.5 then
delay_state = true
else
self.last_create_effect_time = cur_time
end
end
]]
if not delay_state then
self.obj_state[id] = true
if self.create_list == nil then
self.create_list = {}
end
table_insert(self.create_list,id)
end
end
else
if self.obj_state[id] then
self.obj_state[id] = false
if self.deleta_list == nil then
self.deleta_list = {}
end
table_insert(self.deleta_list,id)
end
end
end
end
end
end
end
function SapManager:SetEnabled(bool)
self.is_enabled = bool
end
function SapManager:AddIgnoreObj(id)
self.ignore_list[id]=true
self.obj_state[id] = false
local obj = self.objs_list[id]
if obj then
for k,func in pairs(self.callback_func_list) do
func(SapManager.OperateType.DestoryOBJ,obj.type,obj.id)
end
end
end
function SapManager:RemoveIgnoreObj(id)
self.ignore_list[id]=nil
end
function SapManager:ForceCheckFarModelLoad()
self.force_check_far_model_state = true
end
function SapManager:WaitLoadTerrainFinish(state)
self.wait_load_terrain_finish = state
end
function SapManager:WaitLoadParticleFinish(state)
self.wait_load_particle_finish = state
end
function SapManager:OpenModelHeightCheckMode( state )
self.open_model_height_check_mode = state
end
---------使用四叉树管理物体-----------
function SapManager:InitTree()
local map_size = MapView:getInstance():GetMapSize()
local max_size = math.max(map_size.x, map_size.y)
self.maxDepth = max_size > 22000 and 4 or 3
self.objTree = {
depth = 0,
obj = {},
childs= {},
rect = {
x1 = 0, y1 = 0,
x2 = max_size, y2 = max_size,
},
}
local scene_id = SceneManager.Instance:GetSceneId()
-- 全加载地图
if scene_id and MapView.AllLoadScene[scene_id] then
self.maxDepth = 0
end
end
-- 创建树节点
function SapManager:CreatTreeNode(parent, i, obj_rect)
local rect = parent.rect
local pos_x = (rect.x2 + rect.x1) * 0.5
local pos_y = (rect.y2 + rect.y1) * 0.5
local rect_size = (rect.x2 - pos_x) * 0.5
if i == 1 then
pos = {x = pos_x + rect_size, y = pos_y + rect_size}
elseif i == 2 then
pos = {x = pos_x + rect_size, y = pos_y - rect_size}
elseif i == 3 then
pos = {x = pos_x - rect_size, y = pos_y - rect_size}
else
pos = {x = pos_x - rect_size, y = pos_y + rect_size}
end
local child_rect = {
x1 = pos.x - rect_size, y1 = pos.y - rect_size,
x2 = pos.x + rect_size, y2 = pos.y + rect_size,
}
-- 包含obj才创建节点
if not self:InBound(child_rect,obj_rect) then return end
parent.childs[i] = {
depth = parent.depth + 1,
obj = {},
childs = {},
rect = child_rect,
}
return parent.childs[i]
end
-- obj插入树,obj被子节点包含时,添加到子节点,否则添加到父节点
function SapManager:InsertTree(obj)
if not self.initTree then
self:InitTree()
self.initTree = true
end
local rect = obj.rect
local id = obj.type .. "-" .. obj.id
local is_far = obj.is_far and 1 or 0
local Insert
Insert = function ( parent)
-- 标志有无远物
parent.has_far = parent.has_far == 1 and 1 or is_far
local inBound = false
for i = 1,4 do
local is_creat = false
local child = parent.childs[i]
if not child then
-- 动态添加子节点
child = self:CreatTreeNode(parent, i,rect)
is_creat = true
end
if child and (is_creat or self:InBound(child.rect,rect)) then
if child.depth < self.maxDepth then
Insert(child)
else
if child.obj[id] == nil then
-- 只存id,减少消耗
child.obj[id] = id
end
end
inBound = true
break
end
end
if not inBound and parent.obj[id] == nil then
parent.obj[id] = id
end
end
Insert(self.objTree)
end
-- r1 包含 r2
function SapManager:InBound(r1,r2)
if r1.x1 <= r2.x1 and r1.y1 <= r2.y1 and r1.x2 >= r2.x2 and r1.y2 >= r2.y2 then
return true
end
return false
end
function SapManager:ClearTreeObj( id )
local clearTree
clearTree = function(parent)
if parent.obj[id] then
parent.obj[id] = nil
return
end
parent.has_far = 0
for i,v in pairs(parent.childs) do
clearTree(v)
end
end
clearTree(self.objTree)
end
-- 编辑器窗口下显示检测区域
function SapManager:DebugDrawBound(cam_pos)
if self.initTree and self.camera_rect.x1 and self.change_check_mode then
self:DebugDrawBoundByRect(self.camera_rect,Color.red)
local showChild
showChild = function(parent)
local len = TableSize(parent.obj)
if len > 0 then
for id,v in pairs(parent.obj) do
local obj = self.objs_list[id]
local r = obj.rect
if obj and obj.size and obj.size.x < self.show_bound_max_size then
if self:InRect(self.camera_rect,obj) then
self:DebugDrawBoundByRect(r,Color.green)
else
self:DebugDrawBoundByRect(r,Color(1, 1, 1, 0.6))
end
end
end
end
--local color = self:GetColorByDepth(parent.depth)
self:DebugDrawBoundByRect(parent.rect,color)
if TableSize(parent.childs) == 0 then return end
for i,v in pairs(parent.childs) do
-- 进入区域才检测
if self:InSmallRect(self.camera_rect,v.rect) or parent.has_far==1 then
showChild(v)
end
end
end
showChild(self.objTree)
end
-- 兼容遍历模式的区域显示
if not self.change_check_mode and self.camera_rect.x1 then
self:DebugDrawBoundByRect(self.camera_rect,Color.red)
for id,obj in pairs(self.objs_list) do
if obj.size and obj.size.x < self.show_bound_max_size then
local r = obj.rect
if self:InRect(self.camera_rect,obj) then
self:DebugDrawBoundByRect(r,Color.green)
else
self:DebugDrawBoundByRect(r,Color(1, 1, 1, 0.6))
end
end
end
end
end
function SapManager:GetColorByDepth( depth )
--最底层蓝色
if depth == self.maxDepth then
return Color.blue
end
end
function SapManager:DebugDrawBoundByRect(r,color)
color = color or Color.yellow
r.z1 = r.z1 or -3000
r.z2 = r.z2 or 5000
if not self.point then
self.point = {}
for i=1,9 do
self.point[i] = Vector3(0,0,0)
end
end
self.point[1].x,self.point[1].z,self.point[1].y = r.x1/100,r.y1/100,r.z1/100
self.point[2].x,self.point[2].z,self.point[2].y = r.x2/100,r.y1/100,r.z1/100
self.point[3].x,self.point[3].z,self.point[3].y = r.x2/100,r.y1/100,r.z2/100
self.point[4].x,self.point[4].z,self.point[4].y = r.x1/100,r.y1/100,r.z2/100
self.point[5].x,self.point[5].z,self.point[5].y = r.x1/100,r.y2/100,r.z1/100
self.point[6].x,self.point[6].z,self.point[6].y = r.x1/100,r.y2/100,r.z2/100
self.point[7].x,self.point[7].z,self.point[7].y = r.x2/100,r.y2/100,r.z1/100
self.point[8].x,self.point[8].z,self.point[8].y = r.x2/100,r.y2/100,r.z2/100
UnityEngine.Debug.DrawLine(self.point[1], self.point[2],color);
UnityEngine.Debug.DrawLine(self.point[2], self.point[3],color);
UnityEngine.Debug.DrawLine(self.point[3], self.point[4],color);
UnityEngine.Debug.DrawLine(self.point[4], self.point[1],color);
UnityEngine.Debug.DrawLine(self.point[4], self.point[6],color);
UnityEngine.Debug.DrawLine(self.point[3], self.point[8],color);
UnityEngine.Debug.DrawLine(self.point[2], self.point[7],color);
UnityEngine.Debug.DrawLine(self.point[1], self.point[5],color);
UnityEngine.Debug.DrawLine(self.point[5], self.point[6],color);
UnityEngine.Debug.DrawLine(self.point[6], self.point[8],color);
UnityEngine.Debug.DrawLine(self.point[8], self.point[7],color);
UnityEngine.Debug.DrawLine(self.point[7], self.point[5],color);
end
function SapManager:ChangeSize( size)
self.show_bound_max_size = size or self.show_bound_max_size
end