1、工具方法

深拷贝clone与类方法class

-- 深拷贝 保护原数据
function clone(object)local lookup_table = {}local function _copy(object)if type(object) ~= "table" thenreturn objectelseif lookup_table[object] thenreturn lookup_table[object]endlocal newObject = {}lookup_table[object] = newObjectfor key, value in pairs(object) donewObject[_copy(key)] = _copy(value)endreturn setmetatable(newObject, getmetatable(object))endreturn _copy(object)
end--定义一个类对象
--@param   #string     className   类名
--@param   #table      super       父类
--@return  #table  类
function class(className, super, isUserData)local cls = {name = className,ctor = false,      init = false,__cccreator = false,__cccreatorSelf = false,instanceIndexT = {},   --存储需要继承的方法跟属性}local superType = type(super)if "table" == superType thencls.super = supercls.__cccreator = super.__cccreatorcls.__cccreatorSelf = super.__cccreatorSelfend--该类所生成实例用于索引的元表local instanceIndexT = cls.instanceIndexT    if cls.super thenfor k, v in pairs(cls.super.instanceIndexT) doinstanceIndexT[k] = vendendfunction cls.new(...)local instance = { __cname = cls.name }local mt = { __index = instanceIndexT,}setmetatable(instance, mt)cls.runCtor(instance, ...)cls.runInit(instance, ...)--限制只能在构造函数执行完之前定义类变量mt.__newindex = errorNewIndexreturn instanceend--执行构造函数function cls.runCtor(this, ...)local function ctor(c, ...)--递归调用父类的构造函数if c.super thenctor(c.super, ...)endif c.ctor thenc.ctor(this, ...)endendctor(cls, ...)end--执行构造后的初始化函数function cls.runInit(this, ...)local function init(c, ...)--递归调用父类的构造函数if c.super theninit(c.super, ...)endif c.init thenc.init(this, ...)endendinit(cls, ...)end--将类方法copy到指定对象上,主要给ccclass用function cls.copyFuncs(this)for k, v in pairs(instanceIndexT) dothis[k] = vendend--用在有时候想要调用某个类的方法,但又不需要创建类的实例--被调用方法里面的self不能是cls以及instanceIndexT, 因为这两个是会被继承的function cls.staticCall(funcName, ...)return instanceIndexT[funcName](nil, ...)endsetmetatable(cls, {     __newindex = function(_, k, v)instanceIndexT[k] = vend})if super thenreturn cls,super.instanceIndexTelsereturn clsend
end

2、四叉树算法

1、简介

通过四叉树算法 解决2D游戏物体的碰撞检测
算法核心:
    添加游戏物体
    1、一定范围内存在超过规定数量的物体时,对此范围进行四等分切割
    2、对各物体进行新的象限所属计算
    3、添加物体重复1、2步骤
    4、直至所有物体添加完毕

碰撞检测
    1、遍历所有物体
    2、对同一深度象限及其递归子象限的物体进行碰撞计算

2、创建并初始化类

此处需要对分割进行条件判断,应用中根据实际情况来设置。

local MAX_DEPTH = 99    -- 四叉树的最大深度
local MAX_OBJ_NUM = 9  -- 每个节点(象限)所能包含物体的最大数量
local MIN_WIDTH = 50   -- 象限最小宽度
local MIN_HEIGHT = 50  -- 象限最小高度-- 四叉树节点包含:
-- - objects: 用于存储物体对象
-- - nodes: 存储四个子节点
-- - depth: 该节点的深度,根节点的默认深度为0
-- - bounds: 该节点对应的象限在屏幕上的范围,bounds是一个矩形
-- x1:左下横坐标 y1:左下纵坐标 cx:中心点横坐标 cy:中心点纵坐标local QuadTree = class("QuadTree")
-- 初始化构造
function QuadTree:init(bounds, depth, index)if not bounds or not next(bounds) or not depth or type(depth) ~= "number" thenprint("Error:参数错误!!!")returnendself.index = index or 1self.objects = {}self.nodes = {}self.depth = depth or 0if bounds and next(bounds) thenself.bounds = {x1 = bounds[1], y1 = bounds[2], cx = bounds[3], cy = bounds[4]}else self.bounds = {x1 = 0, y1 = 0, cx = 600, cy = 600}end
end

3、获取所在象限方法

--[[获取物体对应的象限序号,以屏幕中心为界限,切割屏幕:- 右上:象限一 index:1- 左上:象限二   index:2- 左下:象限三  index:3- 右下:象限四  index:4- 物体表示rect = {x, y, width, height}
]]function QuadTree:getIndex(rect)local bounds = self.boundslocal onTop = rect.y >= bounds.cylocal onBottom = rect.y + rect.height <= bounds.cylocal onRight = rect.x >= bounds.cxlocal onLeft = rect.x + rect.width <= bounds.cxlocal index = -1if onTop thenif onRight thenindex = 1elseif onLeft thenindex = 2endelseif onBottom thenif onLeft thenindex = 3elseif onRight thenindex = 4endendreturn index
end

4、拆分方法

-- 拆分为四个象限
function QuadTree:split()local depth = self.depthlocal bounds = self.boundslocal x = bounds.x1local y = bounds.y1local cx = bounds.cxlocal cy = bounds.cylocal width = (cx - x)/2local height = (cy - y)/2-- print("==拆分为四个象限=split==")self.nodes = {QuadTree.new({cx, cy, cx+width, cy+height}, depth+1, 1),QuadTree.new({x, cy, x+width, cy+height}, depth+1, 2),QuadTree.new({x, y, x+width, y+height}, depth+1, 3),QuadTree.new({cx, y, cx+width, y+height}, depth+1, 4),}-- 待优化-- 剔除多余的节点 ###
end

5、辅助方法

-- 将两个table合并为数组形式
local function tableMerge(tb1, tb2)local ret = {}tb1 = tb1 or {}tb2 = tb2 or {}for _,v in pairs(tb1) doret[#ret+1] = vendfor _,v in pairs(tb2) doret[#ret+1] = vendreturn ret
end-- 象限分割矩形
local function rectCarve(rect, bounds)local arr = {}local x = rect.xlocal y = rect.ylocal index = rect.indexlocal width = rect.widthlocal height = rect.heightlocal cx = bounds.cxlocal cy = bounds.cylocal spxlocal spyif cx > x and cx < x + width thenspx = cxendif cy > y and cy < y + height thenspy = cyendif spx thenif spy thenarr = {{index = index, x = x, y = y, width = spx - x, height = spy - y},{index = index, x = cx, y = y, width = width - spx + x, height = spy - y},{index = index, x = x, y = cy, width = spx - x, height = height - spy + y},{index = index, x = cx, y = cy, width = width - spx + x, height = height - spy + y},}elsearr = {{index = index, x = x, y = y, width = spx - x, height = height},{index = index, x = cx, y = y, width = width - spx + x, height = height},}endelseif spy thenarr = {{index = index, x = x, y = y, width = width, height = spy - y},{index = index, x = x, y = cy, width = width, height = height - spy + y},}endendreturn arr
end-- 获取table长度
local function getTableLen(tb)local count = 0for _,_ in pairs(tb) docount = count + 1endreturn count
end

6、插入方法

-- 插入节点
--[[插入功能:- 如果当前节点[ 存在 ]子节点,则检查物体到底属于哪个子节点,如果能匹配到子节点,则将该物体插入到该子节点中- 如果当前节点[ 不存在 ]子节点,将该物体存储在当前节点。随后,检查当前节点的存储数量,如果超过了最大存储数量,则对当前节点进行划分,划分完成后,将当前节点存储的物体重新分配到四个子节点中。
]]
function QuadTree:insert(rect)local indexself.objects[rect.index] = rectlocal function _insert()-- print("======insert:" .. self.depth .. "  " .. self.index)for i,_rect in pairs(self.objects) doindex = self:getIndex(_rect)if index ~= -1 thenself.nodes[index]:insert(_rect)self.objects[i] = nilelse-- 同时处于多个象限 切割矩形 仅用于计算  local subArr = rectCarve(_rect, self.bounds)for _,__rect in pairs(subArr) doindex = self:getIndex(__rect)self.nodes[index]:insert(__rect)endself.objects[i] = nilendendend-- 若存在子节点if next(self.nodes) then_insert()returnend-- 如果当前节点存储的数量超过了MAX_OBJECTS 划分为四象限if getTableLen(self.objects) > MAX_OBJ_NUM and self.depth < MAX_DEPTH and self.bounds.cx - self.bounds.x1 > MIN_WIDTHand self.bounds.cy - self.bounds.y1 > MIN_HEIGHT thenself:split()_insert()end
end

7、检索方法

.

--[[检索功能:给出一个物体对象,该函数负责将该物体可能发生碰撞的所有物体选取出来。该函数先查找物体所属的象限,该象限下的物体都是有可能发生碰撞的,然后再递归地查找子象限...
]]function QuadTree:retrieve(rect)local result = {}local nodes = self.nodeslocal indexlocal indexL = {}-- 若存在子节点if next(nodes) thenindex = self:getIndex(rect)-- print("getIndex", index)if index ~= -1 thentable.insert(indexL, index)result = tableMerge(result, nodes[index]:retrieve(rect))else-- 同时处于多个象限 切割矩形 仅用于计算local subArr = rectCarve(rect, self.bounds)for _,_rect in pairs(subArr) doindex = self:getIndex(_rect)table.insert(indexL, index)result = tableMerge(result, nodes[index]:retrieve(_rect))endendend -- 可优化部分,对于在中间节点上,横跨多个象限的节点可以进行具体的条件local objs = clone(self.objects)local cx = self.bounds.cxlocal cy = self.bounds.cyfor _, _index in ipairs(indexL) doif _index == 1 thenfor k,v in pairs(objs) doif v.x + v.width < cx or v.y + v.height < cy thenobjs[k] = nilendendelseif _index == 2 thenfor k,v in pairs(objs) doif v.x > cx or v.y + v.height < cy thenobjs[k] = nilendendelseif _index == 3 thenfor k,v in pairs(objs) doif v.x > cx or v.y > cy thenobjs[k] = nilendendelseif _index == 4 thenfor k,v in pairs(objs) doif v.x + v.width < cx or v.y > cy thenobjs[k] = nilendendendendresult = tableMerge(result, objs)return result
end

3、测试应用

1、矩形相交判断方法

-- 判断两个矩形是否相交
-- true为相交,false不相交
-- 顶点或边重合视为 不相交 -- 1、不影响物理计算-- 2、方便四叉树象限划分 -- 1 节省内存空间-- 2 减少象限划分时的计算量
local count_times = 0
local function isCollision(rect1, rect2)count_times = count_times + 1return not(rect1.x >= rect2.x + rect2.widthor rect1.y >= rect2.y + rect2.heightor rect2.x >= rect1.x + rect1.widthor rect2.y >= rect1.y + rect1.height)
end

2、应用

-- 屏幕size
local SCREEN_WIDTH = 1200
local SCREEN_HEIGHT = 1200-- 1、初始化根节点
local quadTree = QuadTree.new({0, 0, SCREEN_WIDTH/2, SCREEN_HEIGHT/2}, 0)
local rect_list = {}-- 随机初始化
math.randomseed(os.time())
for i=1,500 dolocal rect = {index = i,width = math.random(10, i>10 and i or 15),height = math.random(15, i>15 and i or 20),x = math.random(1, SCREEN_WIDTH),y = math.random(1, SCREEN_HEIGHT),}table.insert(rect_list, rect)
end-- 2、添加游戏物体
for i,v in ipairs(rect_list) doquadTree:insert(v)
end-- 3、循环检测所有可能的碰撞
print("------全部遍历----")local function calCollsition(answer)local collisionCount = 0count_times = 0print("\n=========方法" .. answer)if answer == 2 then-- 2) 遍历四叉树象限-- 思路一 插入矩形划分象限时,对跨多个象限的矩形特殊处理-- 优点:节省内存空间-- 缺点:花费较多的时间-- 思路二 插入矩形划分象限时,考虑直接将跨多个象限的矩形同时划分至其子象限  2020.07.14 采用此方案-- 优点:易理解,查询碰撞省时间-- 缺点:花费更多内存空间local recordDict = {}local function chechCollision(quadTree)if next(quadTree.nodes) thenfor i,v in pairs(quadTree.nodes) do-- print("递归循环\ndepth:" .. v.depth)-- print("index:" .. i)chechCollision(v, quadTree.objects)endelselocal ret = {}for k,v in pairs(quadTree.objects) doret[#ret+1] = vendfor _index=1,#ret dolocal rect = ret[_index]for __index=_index+1,#ret dolocal otherRect = ret[__index]local index1 = rect.indexlocal index2 = otherRect.indexif (recordDict[index1] and recordDict[index1][index2]) or (recordDict[index2] and recordDict[index2][index1]) thenelseif isCollision(rect, otherRect) thenif isPrint thenprint("\t" .. rect.index .. "与" .. otherRect.index .. "相交")endcollisionCount = collisionCount + 1if recordDict[index1] thenrecordDict[index1][index2] = trueelserecordDict[index1] = {}recordDict[index1][index2] = trueendendendendendendendchechCollision(quadTree)elseif answer == 3 then-- 3) 全部遍历for index=1,#rect_list dolocal rect = rect_list[index]for _index=index+1,#rect_list do     local otherRect = rect_list[_index]if isCollision(rect, otherRect) thenif isPrint thenprint("\t" .. rect.index .. "与" .. otherRect.index .. "相交")endcollisionCount = collisionCount + 1endendendendprint("计算次数:" .. count_times)print("相交次数:" .. collisionCount)
endcalCollsition(2)
calCollsition(3)

3、结果比较

------全部遍历----=========方法2
计算次数:10123
相交次数:5453=========方法3
计算次数:124750
相交次数:5453

4、待写

1、复杂度比较

2、复杂度优化

3、游戏中每帧计算与动态更新

……

四叉树 - 碰撞检测算法 Lua版相关推荐

  1. Java ME游戏开发中,碰撞检测算法在Java?ME中的实现(

    2019独角兽企业重金招聘Python工程师标准>>> 在Java ME游戏开发中,碰撞检测算法在Java?ME中的实现(百搜技术) 在Java ME游戏开发中,经常需要进行碰撞检测 ...

  2. mooc数据结构与算法python版期末考试_数据结构与算法Python版-中国大学mooc-试题题目及答案...

    数据结构与算法Python版-中国大学mooc-试题题目及答案 更多相关问题 婴儿出生一两天后就有笑的反应,这种笑的反应属于(). [判断题]填制原始凭证,汉字大写金额数字一律用正楷或草书书写,汉字大 ...

  3. python数据结构算法 北京大学_北京大学公开课《数据结构与算法Python版》

    之前我分享过一个数据结构与算法的课程,很多小伙伴私信我问有没有Python版. 看了一些公开课后,今天特向大家推荐北京大学的这门课程:<数据结构与算法Python版>. 课程概述 很多同学 ...

  4. java计算一个多边形的重心_2D凸多边形碰撞检测算法(二) - GJK(上)

    2D凸多边形碰撞检测算法(二) - GJK(上) 原理 在 Narrow Phase 精细碰撞检测中,除了 SAT ,另外一个就是 GJK(Gilbert–Johnson–Keerthi)算法.它足够 ...

  5. 把数字随机分成 php,php随机数 微信随机生成红包金额算法php版

    最近在研究发红包的功能,于是写了个红包的生成算法. 红包生成算法的需求 预先生成所有的红包还是一个请求随机生成一个红包 简单来说,就是把一个大整数m分解(直接以"分为单位,如1元即100)分 ...

  6. access中判断回文的代码_前端也来点算法(TypeScript版) | 2 - 回文数和回文链表

    算法采用 TS 进行编写.  回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数.回文链表是链表节点的值和回文数有相同规律的链表. " 回文数 这个数字可以看成是以中心对称分布的 ...

  7. 数据结构与算法 python版 之 递归三定律

    #数据结构与算法 python版 之 谢尔宾斯基三角形 , 树叉 1.为了向阿西莫夫的"机器人三定律"直径,递归算法也总结出"三定律" 1递归算法必须有一个基本 ...

  8. 微信手气红包算法 php,微信随机生成红包金额算法php版

    最近在研究发红包的功能,于是写了个红包的生成算法. 红包生成算法的需求预先生成所有的红包还是一个请求随机生成一个红包 简单来说,就是把一个大整数m分解(直接以"分为单位,如1元即100)分解 ...

  9. 字符串全排列算法_C#版_剑指OFFER

    字符串全排列算法_C#版_剑指OFFER 题目描述 ​题目描述 输入一个长度为 n 字符串,打印出该字符串中字符的所有排列,你可以以任意顺序返回这个字符串数组. 例如输入字符串ABC,则输出由字符A, ...

最新文章

  1. hypervisor简介
  2. 信息系统项目管理师优秀论文:项目整体管理
  3. R语言观察日志(part3)--repeat循环
  4. 八个使前端工程师惊艳的效果设计,码否?
  5. linux服务器配置端口,Linux服务器配置-新增端口
  6. STM32——库函数开发小结
  7. python shape函数_Python中的多态及抽象类
  8. freeswitch 用户配置详解_FreeSwitch安装配置记录-阿里云开发者社区
  9. 深度学习----现今主流GAN原理总结及对比
  10. Python 画数学函数图像 matplotlib.pyplot
  11. ddl是什么意思(ddl是什么意思(网络语ddl是什么梗))
  12. 最新中国省市区SQL
  13. 什么是交互设计,为什么要进行交互设计
  14. 达梦dm8可视化工具_DM8(达梦8)数据库安装和使用
  15. 基于51单片机的智能大棚光温控制系统
  16. #649 (Div. 2)D. Ehab‘s Last Corollary
  17. android 对话框 美化,Android修改Dialog样式
  18. java文档注释用什么开头,极其重要
  19. java实现第一个数字
  20. java-net-php-python-06白茶销售系统计算机毕业设计程序

热门文章

  1. php题库app_在线模拟考试系统 php
  2. 汽车卤素改氙气大灯,再也不用忍受烛光了,给远光狗晃回去
  3. Spring Security OAuth2 入门
  4. 分享下最近写的开源电子书《我的职业是前端工程师》
  5. 数学知识——求组合数
  6. android 9 手机硬件性能,安卓手机性能排行:小米11垫底,一加9 Pro第四,第一名意料之外...
  7. linux 怎么调试 js,JavaScript程序设计之JS调试_javascript技巧
  8. Chrome中的JS调试
  9. python计算两个日期相差的天数的代码
  10. VGGNet tensorflow实战(CIFAR10数据集)