local CharacterFSMManager = require "app/module/character_fsm/character_fsm_manager" local BaseObject = require "app/bf/unity/base_object" local CharacterObject = class("CharacterObject", BaseObject) local BF_CHARACTER_HELPER = GConst.TYPEOF_UNITY_CLASS.BF_CHARACTER_HELPER local ANIMATOR = GConst.TYPEOF_UNITY_CLASS.ANIMATOR local ANIMATION_FRAME_PER_SECOND = 30 local SUPPORT_SHADER = { ["BF/Models/Character Battle"] = true, } local hash = GFunc.hash local HashName = {} local function getHashName(str) local intName = HashName[str] if intName == nil then intName = hash(str) HashName[str] = intName end return intName end local CHILDMAP_METATABLE = { __index = function(t, k) if rawget(t, k) == nil then local v = rawget(t, hash(k)) if v then rawset(t, k, v) end return v end return rawget(t, k) end } local Vector3 = BF.Vector3(0, 0, 0) local abs = math.abs function CharacterObject:ctor() self.timeScale = 1 end function CharacterObject:initWithPrefab(assetPath, prefab, modelId) BaseObject.initWithPrefab(self, assetPath, prefab) self.characterHelper = self:getComponent(BF_CHARACTER_HELPER) if self.characterHelper == nil then self.characterHelper = self:addComponent(BF_CHARACTER_HELPER) end self.mainModel = self.characterHelper:GetModelObject() self.objectIndex = -1 self.modelId = modelId self:_genAllBones() end function CharacterObject:initWithCharacterHelper(characterHelper, index, gameObject, name, admin) self.characterHelper = characterHelper self.objectIndex = index self.gameObject = gameObject self.name = name self.admin = admin end function CharacterObject:_genAllBones() local childMap = {} if self.characterHelper then local childNum = self.characterHelper:GetListCount() for i = 0, childNum do local gameObject = self.characterHelper:GetGameObjectByIndex(i) local hashName = self.characterHelper:GetHashNameByIndex(i) local child = CharacterObject:create() child:initWithCharacterHelper(self.characterHelper, i, gameObject, hashName, self) if childMap[hashName] == nil then childMap[hashName] = child end table.insert(self.childList, child) end end setmetatable(childMap, CHILDMAP_METATABLE) self.childMap = childMap end function CharacterObject:getBoneByName(name) return self.childMap[name] end function CharacterObject:fastGetPosition() if self.characterHelper then self.characterHelper:CachePosition(self.objectIndex) return self.characterHelper.PositionX, self.characterHelper.PositionY, self.characterHelper.PositionZ else local position = self:getTransform().position return position.x, position.y, position.z end end function CharacterObject:getBonePositionByName(name) return self.childMap[name]:fastGetPosition() end function CharacterObject:getHashCode() local code = -1 if self.characterHelper then code = self.characterHelper:GetModelHashCode() end return code end function CharacterObject:play(name, layer, normalizedTime) layer = layer or -1 normalizedTime = normalizedTime or 0 if self.weaponObj and not self.weaponObj:isDestroyed() then if self.weaponAniName then -- local weaponAniName = self:getWeaponSpecialAni(name, self.weaponAniName, true) -- if self.weaponObj:getStateTime(weaponAniName) > 0 then -- self.weaponObj:play(weaponAniName, layer, normalizedTime) -- end local newName = self:getWeaponSpecialAni(name, self.weaponAniName) or name if self:getStateTime(newName) > 0 then name = newName end end else self:setWeaponInfo() end if self.mainAnimator == nil then if self.mainModel then self.mainAnimator = self.mainModel:GetComponent(ANIMATOR) self.mainAnimator:Play(name, layer, normalizedTime) end else self.mainAnimator:Play(name, layer, normalizedTime) end end function CharacterObject:playHashName(hashName, layer, normalizedTime) layer = layer or -1 normalizedTime = normalizedTime or 0 if self.weaponObj and not self.weaponObj:isDestroyed() then if self.weaponAniName then -- local weaponHashName = self:getWeaponSpecialAniHash(hashName, self.weaponAniName, true) -- if self.weaponObj:getStateTimeHash(weaponHashName) > 0 then -- self.weaponObj:playHashName(weaponHashName, layer, normalizedTime) -- end local newHashName = self:getWeaponSpecialAniHash(hashName, self.weaponAniName, true) or hashName if self:getStateTimeHash(newHashName) > 0 then hashName = newHashName end end else self:setWeaponInfo() end if self.mainAnimator == nil then if self.mainModel then self.mainAnimator = self.mainModel:GetComponent(ANIMATOR) self.mainAnimator:Play(hashName, layer, normalizedTime) end else self.mainAnimator:Play(hashName, layer, normalizedTime) end end function CharacterObject:getWeaponSpecialAni(name, specialAniName, isWeapon) if not self.weaponSpecialAniMap then self.weaponSpecialAniMap = {} end if not self.weaponSpecialAniMap[name] then self.weaponSpecialAniMap[name] = {} end if not self.weaponSpecialAniMap[name][specialAniName] then self.weaponSpecialAniMap[name][specialAniName] = {} end if not isWeapon then if not self.weaponSpecialAniMap[name][specialAniName].heroAniName then self.weaponSpecialAniMap[name][specialAniName].heroAniName = name .. "_" .. specialAniName end return self.weaponSpecialAniMap[name][specialAniName].heroAniName else if not self.weaponSpecialAniMap[name][specialAniName].weaponAniName and self.modelId then self.weaponSpecialAniMap[name][specialAniName].weaponAniName = self.modelId .. "_" .. name .. "_" .. specialAniName end return self.weaponSpecialAniMap[name][specialAniName].weaponAniName end end function CharacterObject:getWeaponSpecialAniHash(hashName, specialAniName, isWeapon) if not self.weaponSpecialAniHashMap then self.weaponSpecialAniHashMap = {} end if not self.weaponSpecialAniHashMap[hashName] then self.weaponSpecialAniHashMap[hashName] = {} end if not self.weaponSpecialAniHashMap[hashName][specialAniName] then self.weaponSpecialAniHashMap[hashName][specialAniName] = {} end if not isWeapon then if not self.weaponSpecialAniHashMap[hashName][specialAniName].heroAniName then local name = GConst.HeroConst.HERO_ANI_HASH_MAP[hashName] if name then self.weaponSpecialAniHashMap[hashName][specialAniName].heroAniName = CS.UnityEngine.Animator.StringToHash(name .. "_" .. specialAniName) end end return self.weaponSpecialAniHashMap[hashName][specialAniName].heroAniName else if not self.weaponSpecialAniHashMap[hashName][specialAniName].weaponAniName and self.modelId then local name = GConst.HeroConst.HERO_ANI_HASH_MAP[hashName] if name then self.weaponSpecialAniHashMap[hashName][specialAniName].weaponAniName = CS.UnityEngine.Animator.StringToHash(self.modelId .. "_" .. name .. "_" .. specialAniName) end end return self.weaponSpecialAniHashMap[hashName][specialAniName].weaponAniName end end function CharacterObject:setWeaponInfo(weaponAniName, weaponObj) if not weaponObj then self.weaponAniName = nil self.weaponObj = nil end self.weaponAniName = weaponAniName self.weaponObj = weaponObj end function CharacterObject:setAnimatorBool(key, value) if self.mainAnimator == nil then if self.mainModel then self.mainAnimator = self.mainModel:GetComponent(ANIMATOR) self.mainAnimator:SetBool(key, value) end else self.mainAnimator:SetBool(key, value) end end function CharacterObject:getMainAnimator() if self.mainAnimator == nil then if self.mainModel then self.mainAnimator = self.mainModel:GetComponent(ANIMATOR) end end return self.mainAnimator end function CharacterObject:getMainModelTransform() if self.mainModelTransform == nil then self.mainModelTransform = self.mainModel and self.mainModel.transform or nil end return self.mainModelTransform end function CharacterObject:getMainModel() return self.mainModel end function CharacterObject:getCurrentAnimationHash() return CS.BF.Utils.GetCurrentAnimationHash(self:getMainAnimator()) end function CharacterObject:setMainModelLocalScale(x, y, z) local transform = self:getMainModelTransform() if transform then Vector3.x = x Vector3.y = y Vector3.z = z transform.localScale = Vector3 end end function CharacterObject:setMainModelLocalPosition(x, y, z) if self.characterHelper then self.characterHelper:SetMainModelLocalPosition(x, y, z) else local transform = self:getMainModelTransform() if transform then Vector3.x = x Vector3.y = y Vector3.z = z transform.localPosition = Vector3 end end end function CharacterObject:getStateTime(name) local hashName = getHashName(name) return self.characterHelper:GetStateTime(hashName) end function CharacterObject:getStateTimeHash(hashName) return self.characterHelper:GetStateTime(hashName) end function CharacterObject:getStateKeyTime(name, index) local hashName = getHashName(name) local keyTime = self.characterHelper:GetStateKeyFrame(hashName, index) or 0 return keyTime/ANIMATION_FRAME_PER_SECOND end function CharacterObject:containsState(name) return self:getStateTime(name) > 0.00001 end function CharacterObject:setTimeScale(timeScale) if abs(self.timeScale - timeScale) < 0.00001 then return end self.timeScale = timeScale local mainAnimator = self:getMainAnimator() mainAnimator.speed = timeScale*(self.timeScaleAddition or 1) end function CharacterObject:setTimeScaleAddition(addition) self.timeScaleAddition = addition local timeScale = self.timeScale*addition local mainAnimator = self:getMainAnimator() mainAnimator.speed = timeScale end function CharacterObject:setCullingMode(cullingMode) local mainAnimator = self:getMainAnimator() if mainAnimator then mainAnimator.cullingMode = cullingMode end end function CharacterObject:getCharacterMaterials() if not self.characterMaterials then self.characterMaterials = {} for k, prefabObj in pairs(self.childMap) do local render = prefabObj:getComponent(GConst.TYPEOF_UNITY_CLASS.SKINNED_MESH_RENDERER) if not CS.BF.Utils.IsNull(render) and not CS.BF.Utils.IsNull(render.material) and SUPPORT_SHADER[render.material.shader.name] then table.insert(self.characterMaterials, render.material) end end end return self.characterMaterials end function CharacterObject:getCharacterMeshRenderer() if not self.characterMeshRenders then self.characterMeshRenders = {} for k, prefabObj in pairs(self.childMap) do local render = prefabObj:getComponent(GConst.TYPEOF_UNITY_CLASS.SKINNED_MESH_RENDERER) if not CS.BF.Utils.IsNull(render) and not CS.BF.Utils.IsNull(render.material) and SUPPORT_SHADER[render.material.shader.name] then table.insert(self.characterMeshRenders, render) end end end return self.characterMeshRenders end function CharacterObject:getMaterialPropertyBlock() if not self.mpb then self.mpb = CS.UnityEngine.MaterialPropertyBlock() end return self.mpb end function CharacterObject:setCustomShadowEnable(enabled) local materials = self:getCharacterMaterials() for _, material in ipairs(materials) do material:SetShaderPassEnabled("Always", enabled) end end function CharacterObject:setCharacterFSM(fsm) self.fsm = fsm end ---- 默认 function CharacterObject:setDefault() if self.fsm:getCurStateKey() == CharacterFSMManager.STATE_TYPE.DEFAULT then return end self.fsm:changeState(CharacterFSMManager.STATE_TYPE.DEFAULT) end ---- 石化 function CharacterObject:setStone(callback) if self.fsm:getCurStateKey() == CharacterFSMManager.STATE_TYPE.STONE then return end self.fsm:changeState(CharacterFSMManager.STATE_TYPE.STONE, {callback = callback}) end ---- 石化效果2 function CharacterObject:setStone2(callback) if self.fsm:getCurStateKey() == CharacterFSMManager.STATE_TYPE.STONE_2 then return end self.fsm:changeState(CharacterFSMManager.STATE_TYPE.STONE_2, {callback = callback}) end ---- 冰化 function CharacterObject:setIce() if self.fsm:getCurStateKey() == CharacterFSMManager.STATE_TYPE.ICE then return end self.fsm:changeState(CharacterFSMManager.STATE_TYPE.ICE) end ---- 闪红 function CharacterObject:setGlow(time, callback) local state = self.fsm:getCurStateKey() if state == CharacterFSMManager.STATE_TYPE.DISSOLVE or state == CharacterFSMManager.STATE_TYPE.GLOW then return end if state == CharacterFSMManager.STATE_TYPE.ICE then self.fsm:changeState(CharacterFSMManager.STATE_TYPE.DEFAULT) end self.fsm:changeState(CharacterFSMManager.STATE_TYPE.GLOW, {time = time or 0.7, callback = function() if callback then callback() end self.fsm:changeState(state) end}) end ---- 消融 function CharacterObject:setDissolve(time, callback, needCallback) self.fsm:changeState(CharacterFSMManager.STATE_TYPE.DISSOLVE, {time = time, callback = callback, needCallback = needCallback}) end function CharacterObject:setShadowColor(color) local renderers = self:getCharacterMeshRenderer() local mpBlock = self:getMaterialPropertyBlock() for i, renderer in ipairs(renderers) do renderer:GetPropertyBlock(mpBlock) mpBlock:SetColor("_shadow_color", color) renderer:SetPropertyBlock(mpBlock) end end function CharacterObject:setLocalEulerAngles(x, y, z) if self.characterHelper then self.characterHelper:SetLocalEulerAngles(self.objectIndex, x, y, z) else BaseObject.setLocalEulerAngles(self, x, y, z) end end function CharacterObject:setLocalPosition(x, y, z) if self.characterHelper then self.characterHelper:SetLocalPosition(self.objectIndex, x, y, z) else BaseObject.setLocalPosition(self, x, y, z) end end function CharacterObject:setPosition(x, y, z) if self.characterHelper then self.characterHelper:SetPosition(self.objectIndex, x, y, z) else BaseObject.setPosition(self, x, y, z) end end function CharacterObject:setLocalScale(x, y, z) if self.characterHelper then self.characterHelper:SetLocalScale(self.objectIndex, x, y, z) else BaseObject.setLocalScale(self, x, y, z) end end function CharacterObject:onDestroy() if self.fsm then CharacterFSMManager:stopFSM(self.fsm) self.fsm = nil end BaseObject.onDestroy(self) self.characterHelper = nil self.mainModel = nil self.mainAnimator = nil self.childMap = nil self.characterMaterials = nil self.characterMeshRenders = nil self.mpb = nil end return CharacterObject