local rawget = rawget local rawset = rawset local getmetatable = getmetatable local setmetatable = setmetatable XBindTool = XBindTool or {} local oldPairs = pairs local pairs = function(arr) local meta_t = getmetatable(arr) if meta_t and meta_t.__pairs then return meta_t.__pairs(arr) end return oldPairs(arr) end local function InitBind(obj) if rawget(obj, "___isBinded") then return end local store = {} for key, _ in pairs(obj) do local v = rawget(obj, key) if v ~= nil then store[key] = v obj[key] = nil end end local meta_t = getmetatable(obj) if meta_t then setmetatable(store, meta_t) end setmetatable(obj, { __index = function(_, index) local ret = rawget(obj, index) if ret ~= nil then return ret end return store[index] end, __newindex = function(_, index, v) local event = rawget(obj, "___bind_event") local old_v = store[index] store[index] = v if old_v ~= v then if event and event[index] then event[index].running = true for key, func in pairs(event[index].callList) do if not event[index].removeList[key] then func(v, old_v) end end event[index].running = nil if next(event[index].removeList) then for removeIndex, _ in pairs(event[index].removeList) do event[index].callList[removeIndex] = nil end event[index].removeList = {} end end end end, __pairs = function(_) return oldPairs(store) end }) rawset(obj, "___isBinded", true) rawset(obj, "___bind_store", store) rawset(obj, "___bind_event", {}) rawset(obj, "___bind_id", 0) end function XBindTool.BindAttr(obj, attr, callback) InitBind(obj) local event = rawget(obj, "___bind_event") local id = rawget(obj, "___bind_id") event[attr] = event[attr] or { callList = {}, removeList = {} } id = id + 1 rawset(obj, "___bind_id", id) event[attr].callList[id] = callback local value = obj[attr] if value ~= nil then callback(value) end return { obj = obj, attr = attr, id = id } end function XBindTool.GetBindInfo(val) if type(val) ~= "table" then return val, false end if not rawget(val, "___isBinded") then return val, false end return rawget(val, "___bind_store"), true end function XBindTool.UnBind(handle) local event = rawget(handle.obj, "___bind_event") if event and event[handle.attr] then if event[handle.attr].running then event[handle.attr].removeList[handle.id] = true else event[handle.attr].callList[handle.id] = nil end end end function XBindTool.UnBindObj(obj) local event = rawget(obj, "___bind_event") if event then for _, attrListener in pairs(event) do if attrListener.running then for key, _ in pairs(attrListener.callList) do attrListener.removeList[key] = true end end end rawset(obj, "___bind_event", {}) end end local NodeBindInfoRecord = {} function XBindTool.BindNode(node, obj, attr, func, unbindFunc) if not NodeBindInfoRecord[node] then NodeBindInfoRecord[node] = {} end local bindInfo = NodeBindInfoRecord[node] bindInfo[obj] = bindInfo[obj] or { length = 0, record = {} } local checkExist if node.Exist then checkExist = function() return node:Exist() end else local gameObject = node.GameObject or node.gameObject or node.Transform or node.transform if gameObject and gameObject.Exist then checkExist = function() return gameObject:Exist() end end end local handle if checkExist then handle = XBindTool.BindAttr(obj, attr, function(...) if not checkExist() then XBindTool.UnBindNode(node) if unbindFunc then unbindFunc() end else func(...) end end) else handle = XBindTool.BindAttr(obj, attr, func) end bindInfo[obj].record[handle.id] = handle bindInfo[obj].length = bindInfo[obj].length + 1 return handle end function XBindTool.UnBindNode(node) local bindInfo = NodeBindInfoRecord[node] if bindInfo then for key, val in pairs(bindInfo) do for _, item in pairs(val.record) do XBindTool.UnBind(item) end bindInfo[key] = nil end NodeBindInfoRecord[node] = nil end end