using System; using UnityEditor.U2D.Layout; using UnityEngine; namespace UnityEditor.U2D.Animation { internal class SkeletonTool : BaseTool { [SerializeField] private SkeletonController m_SkeletonController; private SkeletonToolView m_SkeletonToolView; private RectBoneSelector m_RectBoneSelector = new RectBoneSelector(); private RectSelectionTool m_RectSelectionTool = new RectSelectionTool(); private UnselectTool m_UnselectTool = new UnselectTool(); public bool enableBoneInspector { get; set; } public SkeletonMode mode { get { return m_SkeletonController.view.mode; } set { m_SkeletonController.view.mode = value; } } public bool editBindPose { get { return m_SkeletonController.editBindPose; } set { m_SkeletonController.editBindPose = value; } } public ISkeletonStyle skeletonStyle { get { return m_SkeletonController.styleOverride; } set { m_SkeletonController.styleOverride = value; } } public override int defaultControlID { get { return 0; } } public BoneCache hoveredBone { get { return m_SkeletonController.hoveredBone; } } public SkeletonCache skeleton { get { return m_SkeletonController.skeleton; } private set { m_SkeletonController.skeleton = value; } } internal override void OnCreate() { m_SkeletonController = new SkeletonController(); m_SkeletonController.view = new SkeletonView(new GUIWrapper()); m_SkeletonController.view.InvalidID = 0; m_SkeletonController.selection = skinningCache.skeletonSelection; m_SkeletonToolView = new SkeletonToolView(); m_SkeletonToolView.onBoneNameChanged += BoneNameChanged; m_SkeletonToolView.onBoneDepthChanged += BoneDepthChanged; m_SkeletonToolView.onBonePositionChanged += BonePositionChanged; m_SkeletonToolView.onBoneRotationChanged += BoneRotationChanged; m_SkeletonToolView.onBoneColorChanged += BoneColorChanged; m_RectBoneSelector.selection = skinningCache.skeletonSelection; m_RectSelectionTool.rectSelector = m_RectBoneSelector; m_RectSelectionTool.cacheUndo = skinningCache; m_RectSelectionTool.onSelectionUpdate += () => { skinningCache.events.boneSelectionChanged.Invoke(); }; m_UnselectTool.cacheUndo = skinningCache; m_UnselectTool.selection = skinningCache.skeletonSelection; m_UnselectTool.onUnselect += () => { skinningCache.events.boneSelectionChanged.Invoke(); }; } public override void Initialize(LayoutOverlay layout) { m_SkeletonToolView.Initialize(layout); } protected override void OnActivate() { SetupSkeleton(skinningCache.GetEffectiveSkeleton(skinningCache.selectedSprite)); UpdateBoneInspector(); skinningCache.events.skeletonTopologyChanged.AddListener(SkeletonTopologyChanged); skinningCache.events.boneSelectionChanged.AddListener(BoneSelectionChanged); skinningCache.events.selectedSpriteChanged.AddListener(SelectedSpriteChanged); skinningCache.events.skinningModeChanged.AddListener(SkinningModeChanged); skinningCache.events.boneDepthChanged.AddListener(BoneDataChanged); skinningCache.events.boneNameChanged.AddListener(BoneDataChanged); skinningCache.events.boneColorChanged.AddListener(BoneDataChanged); skeletonStyle = null; } protected override void OnDeactivate() { m_SkeletonToolView.Hide(); m_SkeletonController.Reset(); skinningCache.events.skeletonTopologyChanged.RemoveListener(SkeletonTopologyChanged); skinningCache.events.boneSelectionChanged.RemoveListener(BoneSelectionChanged); skinningCache.events.selectedSpriteChanged.RemoveListener(SelectedSpriteChanged); skinningCache.events.skinningModeChanged.RemoveListener(SkinningModeChanged); skinningCache.events.boneDepthChanged.RemoveListener(BoneDataChanged); skinningCache.events.boneNameChanged.RemoveListener(BoneDataChanged); skinningCache.events.boneColorChanged.RemoveListener(BoneDataChanged); skeletonStyle = null; } void SkeletonTopologyChanged(SkeletonCache skeletonCache) { if (skeleton == skeletonCache && skeleton != null) m_RectBoneSelector.bones = skeleton.bones; } void BoneDataChanged(BoneCache bone) { if(m_SkeletonToolView.target == bone) m_SkeletonToolView.Update(bone.name, Mathf.RoundToInt(bone.depth), bone.position, bone.rotation.eulerAngles.z, bone.bindPoseColor); } private void SelectedSpriteChanged(SpriteCache sprite) { SetupSkeleton(skinningCache.GetEffectiveSkeleton(sprite)); } private void BoneSelectionChanged() { UpdateBoneInspector(); } private void UpdateBoneInspector() { var selectedBone = skinningCache.skeletonSelection.activeElement; var selectionCount = skinningCache.skeletonSelection.Count; m_SkeletonToolView.Hide(); if (enableBoneInspector && selectedBone != null && selectionCount == 1) { m_SkeletonToolView.Update(selectedBone.name, Mathf.RoundToInt(selectedBone.depth), selectedBone.position, selectedBone.rotation.eulerAngles.z, selectedBone.bindPoseColor); bool isReadOnly = skinningCache.bonesReadOnly; m_SkeletonToolView.Show(selectedBone, isReadOnly); } } private void SkinningModeChanged(SkinningMode mode) { SetupSkeleton(skinningCache.GetEffectiveSkeleton(skinningCache.selectedSprite)); } private void SetupSkeleton(SkeletonCache sk) { m_RectBoneSelector.bones = null; skeleton = sk; if (skeleton != null) m_RectBoneSelector.bones = skeleton.bones; } protected override void OnGUI() { m_SkeletonController.view.defaultControlID = 0; if (skeleton != null && mode != SkeletonMode.Disabled) { m_RectSelectionTool.OnGUI(); m_SkeletonController.view.defaultControlID = m_RectSelectionTool.controlID; } m_SkeletonController.OnGUI(); m_UnselectTool.OnGUI(); } private void BoneColorChanged(BoneCache selectedBone, Color32 color) { if (selectedBone != null) { skinningCache.BeginUndoOperation(TextContent.colorBoneChanged); selectedBone.bindPoseColor = color; skinningCache.events.boneColorChanged.Invoke(selectedBone); } } private void BonePositionChanged(BoneCache selectedBone, Vector2 position) { if (selectedBone != null) { skinningCache.BeginUndoOperation(TextContent.moveBone); selectedBone.position = position; HandleUtility.Repaint(); m_SkeletonController.InvokePoseChanged(); } } private void BoneRotationChanged(BoneCache selectedBone, float rotation) { if (selectedBone != null) { var euler = selectedBone.rotation.eulerAngles; euler.z = rotation; skinningCache.BeginUndoOperation(TextContent.rotateBone); selectedBone.rotation = Quaternion.Euler(euler); HandleUtility.Repaint(); m_SkeletonController.InvokePoseChanged(); } } private void BoneNameChanged(BoneCache selectedBone, string name) { if (selectedBone != null) { if (string.Compare(selectedBone.name, name) == 0) return; if(string.IsNullOrEmpty(name) || string.IsNullOrWhiteSpace(name)) m_SkeletonToolView.Update(selectedBone.name, Mathf.RoundToInt(selectedBone.depth), selectedBone.position, selectedBone.rotation.eulerAngles.z, selectedBone.bindPoseColor); else { using (skinningCache.UndoScope(TextContent.boneName)) { selectedBone.name = name; skinningCache.events.boneNameChanged.Invoke(selectedBone); } } } } private void BoneDepthChanged(BoneCache selectedBone, int depth) { if (selectedBone != null) { if (Mathf.RoundToInt(selectedBone.depth) == depth) return; using (skinningCache.UndoScope(TextContent.boneDepth)) { selectedBone.depth = depth; skinningCache.events.boneDepthChanged.Invoke(selectedBone); } } } } }