void MoveNode(int id, Vector2 newOffset, bool snapToPixel = true) { if (m_playing) { return; } PowerAnimFrame frame = GetCurrentFrame(); if (frame == null) { return; } Sprite sprite = GetSpriteAtTime(m_animTime); if (sprite != null && snapToPixel) { // Snap offset to sprite pixels newOffset = new Vector2(Utils.Snap(newOffset.x, 0.5f), Utils.Snap(newOffset.y, 0.5f)); } SetNodeOffsetAtFrame(frame, id, newOffset); m_nodePositions[id] = newOffset; // NB: Don't wanna apply changes while dragging, just apply on lift. So no ApplyChanges() here. }
// Gets node at frame, creating it if it doesn't exist Node FindOrCreateNodeAtFrame(PowerAnimFrame frame, int nodeId) { if (frame == null) { return(null); } Node node = null; if (frame.m_nodes == null) { frame.m_nodes = new List <Node>(1); } else { node = frame.m_nodes.Find(item => item.m_nodeId == nodeId); } if (node == null) { // Adding node first time, set it's id, and set offset/angle to whatever the cached one is (if it's set on a previous frame it'll use that). node = new Node() { m_nodeId = nodeId }; frame.m_nodes.Add(node); // Set defaults for frame (based on previous frames) InitNodeValues(node, frame); } return(node); }
// Sets the angle of a node on a particular anim frame, adding it if it didn't exist. void SetNodeAngleAtFrame(PowerAnimFrame frame, int nodeId, float angle) { if (frame != null) { FindOrCreateNodeAtFrame(frame, nodeId).m_angle = Mathf.Repeat(angle, 360f); } }
// Updates the current frame's node pos/rot, if node isn't overriden on current frame it's set to last set position void UpdateCurrentFrameNodePositions() { // NB: Could cashe stuff to do this more efficiently than looping through every node every frame. int currFrame = GetCurrentFrameId(); if (currFrame < 0) { return; } // Work backwards through nodes to find the latest node with a value for (int nodeId = 0; nodeId < PowerSpriteAnimNodes.NUM_NODES; ++nodeId) { m_nodePositions[nodeId] = Vector2.zero; m_nodeAngles[nodeId] = 0; for (int frameId = currFrame; frameId >= 0; --frameId) { Node node = null; PowerAnimFrame frame = m_frames[frameId]; if (frame.m_nodes != null) { node = frame.m_nodes.Find(item => item.m_nodeId == nodeId); } if (node != null) { m_nodePositions[nodeId] = node.m_offset; m_nodeAngles[nodeId] = Mathf.Repeat(node.m_angle, 360f); break; // brake from inner loop } } } }
void LayoutAnimationNodesPanel(Rect rect) { UpdateCurrentFrameNodePositions(); Event e = Event.current; PowerAnimFrame currentFrame = GetCurrentFrame(); // // Draw Nodes - back to front (selected at front) // for (int i = 0; i < PowerSpriteAnimNodes.NUM_NODES; ++i) { if (i != m_selectedNode && Utils.BitMask.IsSet(m_visibleNodes, i)) { LayoutNode(rect, i, currentFrame); } } if (m_selectedNode >= 0) { LayoutNode(rect, m_selectedNode, currentFrame); } // // Handle node events - front to back (selected last) // if (m_selectedNode >= 0) { HandleNodeEvents(rect, m_selectedNode, e); } for (int i = PowerSpriteAnimNodes.NUM_NODES - 1; i >= 0; --i) { if (i != m_selectedNode && Utils.BitMask.IsSet(m_visibleNodes, i)) { HandleNodeEvents(rect, i, e); } } // // Check for double click to add node // if (m_playing == false && e.button == 0 && e.type == EventType.MouseDown && rect.Contains(e.mousePosition) && m_dragState == eDragState.None) { if (m_selectedNode >= 0) { ClearSelection(); GUI.FocusControl("none"); e.Use(); } if (e.clickCount == 2 && GetUnusedNodeId() >= 0) { // Double clicked, add new node at position, with the cached angle from the last set frame AddNode((e.mousePosition - rect.center - m_previewOffset) / m_previewScale); GUI.FocusControl("none"); e.Use(); } } }
void LayoutFrameListFrame(Rect rect, int index, bool isActive, bool isFocused) { if (m_frames == null || index < 0 || index >= m_frames.Count) { return; } PowerAnimFrame frame = m_frames[index]; EditorGUI.BeginChangeCheck(); rect = new Rect(rect) { height = rect.height - 4, y = rect.y + 2 }; // frame ID float xOffset = rect.x; float width = Styles.INFOPANEL_LABEL_RIGHTALIGN.CalcSize(new GUIContent(index.ToString())).x; EditorGUI.LabelField(new Rect(rect) { x = xOffset, width = width }, index.ToString(), Styles.INFOPANEL_LABEL_RIGHTALIGN); // Frame Sprite xOffset += width + 5; width = (rect.xMax - 5 - 28) - xOffset; // Sprite thingy Rect spriteFieldRect = new Rect(rect) { x = xOffset, width = width, height = 16 }; frame.m_sprite = EditorGUI.ObjectField(spriteFieldRect, frame.m_sprite, typeof(Sprite), false) as Sprite; // Frame length (in samples) xOffset += width + 5; width = 28; GUI.SetNextControlName("FrameLen"); int frameLen = frame.m_length; frameLen = EditorGUI.IntField(new Rect(rect) { x = xOffset, width = width }, frameLen); SetFrameLength(frame, frameLen); if (EditorGUI.EndChangeCheck()) { // Apply events ApplyChanges(); } }
// Sets the y offset of a node on a particular anim frame, adding it if it didn't exist. void SetNodeYAtFrame(PowerAnimFrame frame, int nodeId, float value) { Node node = FindOrCreateNodeAtFrame(frame, nodeId); // if (value == 0 && onLoad == false ) - This function is only called on load // value = 0.0001f; // Zero nodes are stripped, so when setting to zero, actually set to a real low value if (node != null) { node.m_offset = new Vector2(node.m_offset.x, value); } }
// Sets the offset of a node on a particular anim frame, adding it if it didn't exist. void SetNodeOffsetAtFrame(PowerAnimFrame frame, int nodeId, Vector2 offset) { Node node = FindOrCreateNodeAtFrame(frame, nodeId); if (offset == Vector2.zero) { offset = new Vector2(0.0001f, 0.0001f); // Zero nodes are stripped, so when setting to zero, actually set to a real low value } if (node != null) { node.m_offset = offset; } }
void RotateNode(int id, float newAngle) { if (m_playing) { return; } PowerAnimFrame frame = GetCurrentFrame(); if (frame == null) { return; } SetNodeAngleAtFrame(frame, id, newAngle); m_nodeAngles[id] = Mathf.Repeat(newAngle, 360); // NB: Don't wanna apply changes while dragging, just apply on lift. So no ApplyChanges() here. }
// Sets node values based on what nodes have on previous frames void InitNodeValues(Node node, PowerAnimFrame frame) { // Work backwards to find last node for (int frameId = m_frames.FindIndex(item => item == frame) - 1; frameId >= 0; --frameId) { Node prevFrameNode = null; PowerAnimFrame prevFrame = m_frames[frameId]; if (prevFrame.m_nodes != null) { prevFrameNode = prevFrame.m_nodes.Find(item => item.m_nodeId == node.m_nodeId); } if (prevFrameNode != null) { node.m_offset = prevFrameNode.m_offset; node.m_angle = prevFrameNode.m_angle; break; } } }
// Delete the node form the current frame, if there are no frames left, deletes the node void DeleteNode(int id) { if (m_playing) { return; } // check if on other frames bool onOtherFrames = false; PowerAnimFrame currentFrame = GetCurrentFrame(); if (currentFrame == null) { return; } foreach (PowerAnimFrame frame in m_frames) { if (frame != currentFrame && frame.m_nodes != null && frame.m_nodes.Exists(item => item.m_nodeId == id && (item.m_offset != Vector2.zero || item.m_angle != 0))) { onOtherFrames = true; break; } } if (onOtherFrames == false) { m_nodePositions[id] = Vector2.zero; m_nodeAngles[id] = 0; m_nodeMask = Utils.BitMask.UnsetAt(m_nodeMask, id); } if (currentFrame.m_nodes != null) { currentFrame.m_nodes.RemoveAll(item => item.m_nodeId == id); } ApplyChanges(); }
// Node data editor void LayoutBottomBarNodeData(Rect rect) { PowerAnimFrame currentFrame = GetCurrentFrame(); bool frameHasNode = (currentFrame != null && currentFrame.m_nodes != null && currentFrame.m_nodes.Exists(item => item.m_nodeId == m_selectedNode)); GUIContent labelContent = null; float xOffset = 10; float width = 60; GUI.Label(new Rect(xOffset, 1, width, rect.height), "Node: " + m_selectedNode, EditorStyles.boldLabel); Vector2 pos = m_nodePositions[m_selectedNode]; float angle = m_nodeAngles[m_selectedNode]; // Position EditorGUI.BeginChangeCheck(); xOffset += width; width = 10; labelContent = new GUIContent("X"); width = EditorStyles.miniLabel.CalcSize(labelContent).x; GUI.Label(new Rect(xOffset, 1, width, rect.height), labelContent, EditorStyles.miniLabel); xOffset += width; width = 40; GUI.SetNextControlName("OffsetX"); pos.x = EditorGUI.FloatField(new Rect(xOffset, 2, width, rect.height - 3), Mathf.Abs(pos.x) <= 0.00011f ? 0 : pos.x, EditorStyles.miniTextField); xOffset += width; width = 10; labelContent = new GUIContent("Y"); width = EditorStyles.miniLabel.CalcSize(labelContent).x; GUI.Label(new Rect(xOffset, 0, width, rect.height), labelContent, EditorStyles.miniLabel); xOffset += width; width = 40; GUI.SetNextControlName("OffsetY"); pos.y = EditorGUI.FloatField(new Rect(xOffset, 2, width, rect.height - 3), Mathf.Abs(pos.y) <= 0.00011f ? 0 : pos.y, EditorStyles.miniTextField); if (EditorGUI.EndChangeCheck()) { MoveNode(m_selectedNode, pos); ApplyChanges(); } // Angle EditorGUI.BeginChangeCheck(); xOffset += width + 1; labelContent = new GUIContent("Angle:"); width = EditorStyles.miniLabel.CalcSize(labelContent).x; GUI.Label(new Rect(xOffset, 1, width, rect.height), labelContent, EditorStyles.miniLabel); xOffset += width; GUI.SetNextControlName("Angle"); width = Mathf.Max(25, EditorStyles.miniTextField.CalcSize(new GUIContent(angle.ToString())).x + 2); angle = EditorGUI.FloatField(new Rect(xOffset, 2, width, rect.height - 3), angle, EditorStyles.miniTextField); if (EditorGUI.EndChangeCheck()) { RotateNode(m_selectedNode, angle); ApplyChanges(); } // Display Name xOffset += width + 15; labelContent = new GUIContent("Preview name:"); width = EditorStyles.miniLabel.CalcSize(labelContent).x; EditorGUI.LabelField(new Rect(xOffset, 1, width, rect.height - 3), labelContent, EditorStyles.miniLabel); xOffset += width; width = 60; GUI.SetNextControlName("NodeName"); width = Mathf.Max(25, EditorStyles.miniTextField.CalcSize(new GUIContent(m_defaultNodeNames[m_selectedNode])).x + 2); m_defaultNodeNames[m_selectedNode] = EditorGUI.TextField(new Rect(xOffset, 2, width, rect.height - 3), m_defaultNodeNames[m_selectedNode], EditorStyles.miniTextField); xOffset += width; labelContent = new GUIContent("/ sprite"); width = EditorStyles.miniLabel.CalcSize(labelContent).x; EditorGUI.LabelField(new Rect(xOffset, 1, width, rect.height - 3), labelContent, EditorStyles.miniLabel); xOffset += width; width = 70; labelContent = new GUIContent(m_defaultNodeSprites[m_selectedNode] == null ? "None" : m_defaultNodeSprites[m_selectedNode].name); width = Mathf.Min(120, EditorStyles.miniTextField.CalcSize(labelContent).x + 38); //width = Mathf.Max( 25, EditorStyles.miniTextField.CalcSize(new GUIContent(m_defaultNodeSprites[m_selectedNode])).x + 2 ); m_defaultNodeSprites[m_selectedNode] = EditorGUI.ObjectField(new Rect(xOffset, 2, width, rect.height - 3), m_defaultNodeSprites[m_selectedNode], typeof(Sprite), false) as Sprite; xOffset += width; //width = 120; //GUI.Label( new Rect(xOffset,1, width,rect.height), "(editor only)", EditorStyles.miniLabel ); // Align to right xOffset = rect.width; // Reset button width = 50; xOffset -= width + 5; if (GUI.Button(new Rect(xOffset, 2, width, rect.height - 4), "Delete", EditorStyles.miniButton)) { DeleteNodeAllFrames(m_selectedNode); } #if UNITY_2019_1_OR_NEWER width = 125; #else width = 105; #endif xOffset -= width + 5; GUI.enabled = frameHasNode; if (GUI.Button(new Rect(xOffset, 2, width, rect.height - 4), "Clear Frame Offset", EditorStyles.miniButton)) { DeleteNode(m_selectedNode); } GUI.enabled = true; }
void LayoutNode(Rect rect, int nodeId, PowerAnimFrame currentFrame) { if (Utils.BitMask.IsSet(m_nodeMask, nodeId) == false) { return; } // // Translate node position to preview window // Vector2 nodePos = m_nodePositions[nodeId]; nodePos = nodePos * m_previewScale; nodePos = nodePos + m_previewOffset; Rect nodeRect = new Rect(rect.center.x + nodePos.x - 8, rect.center.y + nodePos.y - 8, 16, 16); // Check rect is visible if (rect.Contains(nodeRect.center) == false) { return; } // // Draw // Color savedColor = Handles.color; Handles.color = Color.white; // NODE_COLORS[i%NODE_COLORS.Length]; bool hasSprite = m_defaultNodeSprites[nodeId] != null; if (hasSprite) { // Render node sprite if it has one LayoutFrameSprite(new Rect(rect) { center = nodeRect.center }, m_defaultNodeSprites[nodeId], m_previewScale, Vector2.zero /*m_previewOffset*/, false, true, m_nodeAngles[nodeId]); } string nodeText = nodeId.ToString(); if (string.IsNullOrEmpty(m_defaultNodeNames[nodeId]) == false && hasSprite == false) { nodeText = string.Concat(nodeText, ':', m_defaultNodeNames[nodeId]); } float textWidth = Styles.PREVIEW_LABEL_BOLD.CalcSize(new GUIContent(nodeText)).x + 2; Rect labelRectName = new Rect(nodeRect) { x = nodeRect.x + 12f, y = nodeRect.y - 1, width = textWidth }; // Offset while dragging or if angle arrow would be overlapping text if (m_selectedNode == nodeId && (m_dragState == eDragState.MoveNode || (m_nodeAngles[nodeId] < 10 || m_nodeAngles[nodeId] > 340))) { labelRectName.y = labelRectName.y - 10f; } GUI.Label(new Rect(labelRectName) { x = labelRectName.x + 1f, y = labelRectName.y + 1f }, nodeText, new GUIStyle(Styles.PREVIEW_LABEL_BOLD_SHADOW) { fontSize = 10 }); GUI.Label(new Rect(labelRectName) { x = labelRectName.x + 1f, y = labelRectName.y }, nodeText, new GUIStyle(Styles.PREVIEW_LABEL_BOLD_SHADOW) { fontSize = 10 }); GUI.Label(new Rect(labelRectName) { x = labelRectName.x - 1f, y = labelRectName.y - 1f }, nodeText, new GUIStyle(Styles.PREVIEW_LABEL_BOLD_SHADOW) { fontSize = 10 }); GUI.Label(labelRectName, nodeText, new GUIStyle(Styles.PREVIEW_LABEL_BOLD) { fontSize = 10 }); // Alpha lower when the frame doesn't have the node overriden bool frameHasNode = (currentFrame != null && currentFrame.m_nodes != null && currentFrame.m_nodes.Exists(item => item.m_nodeId == nodeId)); float alpha = frameHasNode ? 1.0f : 0.25f; float sizeInner = frameHasNode ? 4 : 3; Color colorSelectedOutline = Color.white.WithAlpha(alpha); Color colorUnselectedOutline = Color.black.WithAlpha(0.5f * alpha); Quaternion nodeRotation = Quaternion.Euler(0, 0, -m_nodeAngles[nodeId]); if (m_selectedNode == nodeId) { if (m_dragState == eDragState.MoveNode) { // Draw crosshair float crosshairLength = 256; Handles.color = Color.white.WithAlpha(0.4f); DrawLine(new Vector2(nodeRect.center.x + 1 - crosshairLength, nodeRect.center.y + 1), new Vector2(nodeRect.center.x + 1 + crosshairLength, nodeRect.center.y + 1), Handles.color, 0); DrawLine(new Vector2(nodeRect.center.x + 1, nodeRect.center.y + 1 - crosshairLength), new Vector2(nodeRect.center.x + 1, nodeRect.center.y + 1 + crosshairLength), Handles.color, 0); Handles.DrawWireDisc(nodeRect.center, Vector3.back, 128); Handles.DrawWireDisc(nodeRect.center, Vector3.back, 96); Handles.DrawWireDisc(nodeRect.center, Vector3.back, 64); Handles.DrawWireDisc(nodeRect.center, Vector3.back, 32); crosshairLength = 20; DrawLine(new Vector2(nodeRect.center.x + 1 - crosshairLength, nodeRect.center.y + 1), new Vector2(nodeRect.center.x + 1 + crosshairLength, nodeRect.center.y + 1), Color.black, 0); DrawLine(new Vector2(nodeRect.center.x + 1, nodeRect.center.y + 1 - crosshairLength), new Vector2(nodeRect.center.x + 1, nodeRect.center.y + 1 + crosshairLength), Color.black, 0); DrawLine(new Vector2(nodeRect.center.x - crosshairLength, nodeRect.center.y), new Vector2(nodeRect.center.x + crosshairLength, nodeRect.center.y), NODE_COLORS[nodeId % NODE_COLORS.Length], 0); DrawLine(new Vector2(nodeRect.center.x, nodeRect.center.y - crosshairLength), new Vector2(nodeRect.center.x, nodeRect.center.y + crosshairLength), NODE_COLORS[nodeId % NODE_COLORS.Length], 0); // Draw position string string positionString = string.Format("{0:0.0}, {1:0.0}", m_nodePositions[nodeId].x, m_nodePositions[nodeId].y); Vector2 labelSize = Styles.PREVIEW_LABEL_BOLD.CalcSize(new GUIContent(positionString)); Rect labelRect = new Rect(nodeRect.x + 30, nodeRect.y + 5, labelSize.x, labelSize.y); GUI.Label(new Rect(labelRect) { x = labelRect.x + 1f, y = labelRect.y + 1f }, positionString, Styles.PREVIEW_LABEL_BOLD_SHADOW); GUI.Label(new Rect(labelRect) { x = labelRect.x - 1f, y = labelRect.y - 1f }, positionString, Styles.PREVIEW_LABEL_BOLD_SHADOW); GUI.Label(labelRect, positionString, Styles.PREVIEW_LABEL_BOLD); } else { Handles.color = NODE_COLORS[nodeId % NODE_COLORS.Length].WithAlpha(alpha); Vector2 angleOffset = nodeRotation * Vector2.right * ROTATE_ARROW_LENGTH; Vector2 lineOffset = nodeRotation * Vector2.up; DrawLine(nodeRect.center + lineOffset, nodeRect.center + angleOffset + lineOffset, Handles.color); lineOffset = nodeRotation * Vector2.down; DrawLine(nodeRect.center + lineOffset, nodeRect.center + angleOffset + lineOffset, Handles.color); Handles.DrawSolidArc((Vector3)nodeRect.center + (nodeRotation * Vector2.right * (ROTATE_ARROW_LENGTH + 3)), Vector3.back, Quaternion.Euler(0, 0, -m_nodeAngles[nodeId] + 20f) * Vector2.left, 40, 10); DrawLine(nodeRect.center, nodeRect.center + angleOffset, Color.white); Handles.DrawSolidDisc(nodeRect.center, Vector3.back, sizeInner + 3); Handles.color = colorSelectedOutline; Handles.DrawSolidDisc(nodeRect.center, Vector3.back, sizeInner + 2); Handles.color = NODE_COLORS[nodeId % NODE_COLORS.Length].WithAlpha(alpha); Handles.DrawSolidDisc(nodeRect.center, Vector3.back, sizeInner); if (m_dragState == eDragState.RotateNode) { // Draw angle string string angleString = string.Format("{0:0}\u00B0", m_nodeAngles[nodeId]); Vector2 labelSize = Styles.PREVIEW_LABEL_BOLD.CalcSize(new GUIContent(angleString)); Rect labelRect = new Rect(nodeRect.x + 15, nodeRect.y + 15, labelSize.x, labelSize.y); GUI.Label(new Rect(labelRect) { x = labelRect.x + 1f, y = labelRect.y + 1f }, angleString, Styles.PREVIEW_LABEL_BOLD_SHADOW); GUI.Label(new Rect(labelRect) { x = labelRect.x - 1f, y = labelRect.y - 1f }, angleString, Styles.PREVIEW_LABEL_BOLD_SHADOW); GUI.Label(labelRect, angleString, Styles.PREVIEW_LABEL_BOLD); } } } else { Handles.color = colorUnselectedOutline; Handles.DrawSolidDisc(new Vector2(nodeRect.center.x, nodeRect.center.y), Vector3.back, sizeInner + 1); // Draw angle arrow float arrowSize = sizeInner * 3.0f; const float arrowAngle = 40; if (m_nodeAngles[nodeId] != 0) { Handles.DrawSolidArc((Vector3)nodeRect.center + (nodeRotation * Vector2.right * (arrowSize + 3)), Vector3.back, Quaternion.Euler(0, 0, -m_nodeAngles[nodeId] + (arrowAngle * 0.5f)) * Vector2.left, arrowAngle, arrowSize); } Handles.color = NODE_COLORS[nodeId % NODE_COLORS.Length].WithAlpha(alpha); if (m_nodeAngles[nodeId] != 0) { Handles.DrawSolidArc((Vector3)nodeRect.center + (nodeRotation * Vector2.right * arrowSize), Vector3.back, Quaternion.Euler(0, 0, -m_nodeAngles[nodeId] + (arrowAngle * 0.5f)) * Vector2.left, arrowAngle, arrowSize); } // Draw foreground dot Handles.DrawSolidDisc(new Vector2(nodeRect.center.x, nodeRect.center.y), Vector3.back, sizeInner); } Handles.color = savedColor; }