{"id":1818,"date":"2017-09-01T02:00:15","date_gmt":"2017-08-31T18:00:15","guid":{"rendered":"http:\/\/www.clonefactor.com\/wordpress\/?p=1818"},"modified":"2017-09-01T02:39:45","modified_gmt":"2017-08-31T18:39:45","slug":"unity3d-editorgui-custom-emoji-selection","status":"publish","type":"post","link":"https:\/\/www.clonefactor.com\/wordpress\/program\/unity3d\/1818\/","title":{"rendered":"Unity3d, EditorGUI, custom emoji selection"},"content":{"rendered":"<p>\u5728\u505a\u81ea\u5df1\u7684 Dialogue \u7cfb\u7d71\u7684\u6642\u5019\u7684\u9ede\u5b50. \u8868\u60c5\u7684\u72c0\u614b\u9084\u662f\u8207\u6a21\u578b\u672c\u8eab\u7684\u52d5\u756b\u5206\u958b\u8a2d\u5b9a\u624d\u66f4\u6709\u958b\u767c\u5f48\u6027.<br \/>\n\u7136\u5f8c\u5c31\u53bb\u505a Emotional \u7684 research, \u767c\u89ba\u5176\u5be6\u8868\u60c5\u9084\u771f\u7684\u6eff\u591a\u7684.<br \/>\n\u770b\u8457 Emotion Wheel \u5c31\u89ba\u5f97\u60f3\u76f4\u63a5\u7528\u6ed1\u9f20\u9ede\u4e00\u4e0b\u5c31\u6c7a\u5b9a\u597d\u7684\u8a71\u5c31\u66f4\u65b9\u4fbf\u4e86.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1819\" src=\"http:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2017\/09\/EmotionalData.gif\" alt=\"\" width=\"480\" height=\"359\" \/><\/p>\n<p>\u8868\u60c5\u5206\u5340\u662f\u53c3\u8003\u81ea:\u00a0<a href=\"http:\/\/www.6seconds.org\/2017\/04\/27\/plutchiks-model-of-emotions\/\">http:\/\/www.6seconds.org\/2017\/04\/27\/plutchiks-model-of-emotions\/<\/a><\/p>\n<p>\u5177\u9ad4\u7684\u8868\u60c5\u5716:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-1823\" src=\"http:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2017\/09\/EmotionWhell.png\" alt=\"\" width=\"1733\" height=\"1733\" srcset=\"https:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2017\/09\/EmotionWhell.png 1733w, https:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2017\/09\/EmotionWhell-150x150.png 150w, https:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2017\/09\/EmotionWhell-300x300.png 300w, https:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2017\/09\/EmotionWhell-768x768.png 768w, https:\/\/www.clonefactor.com\/wordpress\/wp-content\/uploads\/2017\/09\/EmotionWhell-1024x1024.png 1024w\" sizes=\"auto, (max-width: 1733px) 100vw, 1733px\" \/><\/p>\n<p>EmotionalData.cs<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">using UnityEngine;\r\n\r\nnamespace Kit.UI.Dialogue\r\n{\r\n\t\/\/\/ &lt;summary&gt;Emotional enum selector&lt;\/summary&gt;\r\n\t\/\/\/ &lt;see cref=\"http:\/\/www.6seconds.org\/2017\/04\/27\/plutchiks-model-of-emotions\/\"\/&gt;\r\n\t[System.Serializable]\r\n\tpublic struct EmotionalData\r\n\t{\r\n\t\t[SerializeField] float x, y;\r\n\r\n\t\tprivate const float ANGLE_OFFSET = -112.5f;\r\n\t\tpublic static readonly Vector2 BIAS_DIRECTION_ANCHOR = new Vector2(Mathf.Cos(ANGLE_OFFSET * Mathf.Deg2Rad), Mathf.Sin(ANGLE_OFFSET * Mathf.Deg2Rad)).normalized;\r\n\t\t\r\n\t\tpublic eEmotional GetEmotion()\r\n\t\t{\r\n\t\t\tVector2 vector = new Vector2(x, y);\r\n\t\t\tfloat distance = vector.magnitude;\r\n\r\n\t\t\tif (distance &lt; float.Epsilon)\r\n\t\t\t\treturn eEmotional.None;\r\n\r\n\t\t\tint level = (distance &gt;= 0f &amp;&amp; distance &lt; 0.5f) ? 0 :\r\n\t\t\t\t(distance &gt;= 0.5f &amp;&amp; distance &lt; 0.9f) ? 10 :\r\n\t\t\t\t20;\r\n\r\n\t\t\tVector2 lhs = vector.normalized;\r\n\t\t\tVector2 rhs = BIAS_DIRECTION_ANCHOR;\r\n\t\t\tvar sin = rhs.x * lhs.y - lhs.x * rhs.y;\r\n\t\t\tvar cos = lhs.x * rhs.x + lhs.y * rhs.y;\r\n\t\t\tfloat degree = Mathf.Atan2(sin, cos) * Mathf.Rad2Deg;\r\n\t\t\t\/\/ Debug.Log(\"Vector = \"+ vector.ToString(\"F2\") + \", Degree = \"+ degree + \", distance = \"+ distance + \", level =\"+ level);\r\n\t\t\tif (degree &lt; 0)\r\n\t\t\t\tdegree += 360f;\r\n\t\t\t\r\n\r\n\t\t\tint sector = 1;\r\n\t\t\tconst float session = 45f;\r\n\t\t\twhile (degree &gt; session)\r\n\t\t\t{\r\n\t\t\t\tdegree -= session;\r\n\t\t\t\tsector++;\r\n\t\t\t}\r\n\r\n\t\t\tint tmp = sector + level;\r\n\t\t\teEmotional rst = (eEmotional)tmp;\r\n\t\t\treturn rst;\r\n\t\t}\r\n\r\n\t\tpublic static string GetEmojiText(eEmotional emotional)\r\n\t\t{\r\n\t\t\tswitch (emotional)\r\n\t\t\t{\r\n\t\t\t\tcase eEmotional.None: return \"\uff65\u0e34_\uff65\u0e34\";\r\n\r\n\t\t\t\tcase eEmotional.ecstasy: return \"^\u25bd^\"; \/\/ \u72c2\u559c\r\n\t\t\t\tcase eEmotional.joy: return \"\u00b4\u2200`\"; \/\/ \u559c\u6085\r\n\t\t\t\tcase eEmotional.serenity: return \"'\u203f'\"; \/\/ \u5be7\u975c\r\n\r\n\t\t\t\tcase eEmotional.admiration: return \"\u2665\u203f\u2665\"; \/\/ \u6b3d\u4f69\r\n\t\t\t\tcase eEmotional.trust: return \"\u25e0\u203f\u25d5\"; \/\/ \u4fe1\u4efb\r\n\t\t\t\tcase eEmotional.acceptance: return \"\u2022\u03c9\u2022\"; \/\/ \u63a5\u53d7\r\n\r\n\t\t\t\tcase eEmotional.terror: return \"\u2609\u0434\u2299\"; \/\/ \u6050\u4f48\r\n\t\t\t\tcase eEmotional.fear: return \"\uff9f\u0434\uff9f\"; \/\/\"\u0ca0\u2583\u0ca0\"; \/\/ \u6050\u61fc\r\n\t\t\t\tcase eEmotional.apprehension: return \"\u00ba\u0394\u00ba\"; \/\/ \u9867\u616e\r\n\r\n\t\t\t\tcase eEmotional.amazement: return \"\u2299\u0303.o\"; \/\/ \u9a5a\u6115\r\n\t\t\t\tcase eEmotional.surprise: return \"\u0e4f_\u0e4f\"; \/\/ \u9a5a\u8a1d\r\n\t\t\t\tcase eEmotional.distraction: return \"\u02da\u2013\u02da\"; \/\/ \u5206\u795e\r\n\r\n\t\t\t\tcase eEmotional.grief: return \"\u2565\ufe4f\u2565\"; \/\/ \u54c0\u75db\r\n\t\t\t\tcase eEmotional.sadness: return \"\u260d\ufe4f\u2070\"; \/\/ \u60b2\r\n\t\t\t\tcase eEmotional.pensiveness: return \"\u2032\uff5e\u2035\"; \/\/ \u512a\u601d\r\n\r\n\t\t\t\tcase eEmotional.loathing: return \"\u0ca0\u76ca\u0ca0\"; \/\/ \u975e\u5e38\u8a0e\u58d3\r\n\t\t\t\tcase eEmotional.disgust: return \"\u0ca0\u256d\u256e\u0ca0\"; \/\/ \u53ad\u60e1\r\n\t\t\t\tcase eEmotional.boredom: return \"\u314d_\u314d\"; \/\/ \u7121\u804a\r\n\r\n\t\t\t\tcase eEmotional.rage: return \"\u25e3_\u25e2\"; \/\/ \u61a4\u6012\r\n\t\t\t\tcase eEmotional.anger: return \"\u22cb_\u22cc\"; \/\/ \u6012\r\n\t\t\t\tcase eEmotional.annoyance: return \"\u2256\ufe3f\u2256\"; \/\/ \u7169\u60f1\r\n\r\n\t\t\t\tcase eEmotional.vigilance: return \"\u272a\u03c9\u272a\"; \/\/ \u8b66\u89ba\r\n\t\t\t\tcase eEmotional.anticipation: return \"\u25d5\u203f\u25d5\"; \/\/ \u9810\u671f\r\n\t\t\t\tcase eEmotional.interest: return \"\u25c9\u203f\u25c9\"; \/\/ \u8208\u8da3\r\n\r\n\t\t\t\tdefault: return \"Err\";\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tpublic static explicit operator EmotionalData(Vector2 vector)\r\n\t\t{\r\n\t\t\treturn new EmotionalData() { x = vector.x, y = vector.y };\r\n\t\t}\r\n\r\n\t\tpublic static implicit operator Vector2(EmotionalData emotion)\r\n\t\t{\r\n\t\t\treturn new Vector2(emotion.x, emotion.y);\r\n\t\t}\r\n\r\n\t\tpublic static implicit operator eEmotional(EmotionalData data)\r\n\t\t{\r\n\t\t\treturn data.GetEmotion();\r\n\t\t}\r\n\t}\r\n\r\n\tpublic enum eEmotional\r\n\t{\r\n\t\tNone = 0,\r\n\r\n\t\tserenity = 1,   \/\/ 0 ~ 45\r\n\t\tacceptance,     \/\/ 45 ~ 90\r\n\t\tapprehension,   \/\/ 90 ~ 135\r\n\t\tdistraction,    \/\/ 135 ~ 180\r\n\t\tpensiveness,    \/\/ 180 ~ 225\r\n\t\tboredom,        \/\/ 225 ~ 270\r\n\t\tannoyance,      \/\/ 270 ~ 315\r\n\t\tinterest,       \/\/ 315 ~ 360\r\n\r\n\t\tjoy = 11,\r\n\t\ttrust,\r\n\t\tfear,\r\n\t\tsurprise,\r\n\t\tsadness,\r\n\t\tdisgust,\r\n\t\tanger,\r\n\t\tanticipation,\r\n\r\n\t\tecstasy = 21,\r\n\t\tadmiration,\r\n\t\tterror,\r\n\t\tamazement,\r\n\t\tgrief,\r\n\t\tloathing,\r\n\t\trage,\r\n\t\tvigilance,\r\n\t}\r\n\r\n}<\/pre>\n<p>EmotionalDataDrawer.cs<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"null\">using UnityEngine;\r\nusing UnityEditor;\r\nusing Kit.Extend;\r\n\r\nnamespace Kit.UI.Dialogue\r\n{\r\n\t[CustomPropertyDrawer(typeof(EmotionalData))]\r\n\tpublic class EmotionalDataDrawer : PropertyDrawer\r\n\t{\r\n\t\tconst float halfSize = 55f;\r\n\t\tconst float controlHalfSize = 5f;\r\n\t\tconst float radius = halfSize - controlHalfSize;\r\n\t\tconst float sqrRadius = radius * radius;\r\n\t\tstatic readonly Vector2 halfRange = Vector2.one * halfSize;\r\n\t\tstatic readonly Color thumbColor = new Color(95f \/ 255f, 131f \/ 255f, 221f \/ 255f, .3f);\r\n\t\tstatic readonly Color faceColor = new Color(211f \/ 255f, 188f \/ 255f, 152f \/ 255f, 1f);\r\n\t\tstatic readonly GUIStyle emojiStyle = new GUIStyle(GUI.skin.label)\r\n\t\t{\r\n\t\t\talignment = TextAnchor.MiddleCenter,\r\n\t\t\trichText = true,\r\n\t\t\tstretchWidth = true,\r\n\t\t\tstretchHeight = true,\r\n\t\t\tfontSize = 30,\r\n\t\t};\r\n\r\n\t\tprivate enum eState\r\n\t\t{\r\n\t\t\tIdle = 0,\r\n\t\t\tDrag,\r\n\t\t\tDragEnd,\r\n\t\t}\r\n\t\tprivate eState m_State = eState.Idle;\r\n\r\n\t\tpublic override void OnGUI(Rect position, SerializedProperty property, GUIContent label)\r\n\t\t{\r\n\t\t\tEditorGUI.BeginProperty(position, label, property);\r\n\t\t\tRect line = new Rect(position.x, position.y + 5f, halfSize * 2f, halfSize * 2f);\r\n\t\t\tSerializedProperty xProp = property.FindPropertyRelative(\"x\");\r\n\t\t\tSerializedProperty yProp = property.FindPropertyRelative(\"y\");\r\n\t\t\tEvent evt = Event.current;\r\n\r\n\t\t\t\/\/ identiry mouse event.\r\n\t\t\tif (evt.type == EventType.MouseDown &amp;&amp; line.Contains(evt.mousePosition, false))\r\n\t\t\t{\r\n\t\t\t\tm_State = eState.Drag;\r\n\t\t\t}\r\n\t\t\telse if (evt.type == EventType.MouseUp &amp;&amp; m_State == eState.Drag)\r\n\t\t\t{\r\n\t\t\t\tm_State = eState.DragEnd;\r\n\t\t\t}\r\n\r\n\t\t\t\/\/ data source location\r\n\t\t\tVector2 inputCircle;\r\n\t\t\tif (m_State != eState.Idle)\r\n\t\t\t{\r\n\t\t\t\tinputCircle = new Vector2(\r\n\t\t\t\t\tMathf.Clamp(evt.mousePosition.x - (line.x + halfSize), -halfSize, halfSize),\r\n\t\t\t\t\tMathf.Clamp(evt.mousePosition.y - (line.y + halfSize), -halfSize, halfSize)\r\n\t\t\t\t\t);\r\n\t\t\t\tif (inputCircle.sqrMagnitude &gt; sqrRadius)\r\n\t\t\t\t\tinputCircle = inputCircle.normalized * (halfSize - controlHalfSize);\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tinputCircle = new Vector2(xProp.floatValue, yProp.floatValue).ConvertSquareToCircle().Scale(-1f, 1f, -radius, radius);\r\n\t\t\t}\r\n\t\t\tVector2 square01 = inputCircle.Scale(-radius, radius, -1f, 1f).ConvertCircleToSquare();\r\n\t\t\tEmotionalData emoji = (EmotionalData)square01;\r\n\t\t\teEmotional emojiID = emoji.GetEmotion();\r\n\r\n\t\t\t\/\/ UI\r\n\t\t\t\/\/ Emoji face\r\n\t\t\tGUI.BeginClip(line);\r\n\t\t\tHandles.color = faceColor;\r\n\t\t\tHandles.DrawSolidDisc(halfRange, Vector3.forward, halfSize);\r\n\t\t\tGUI.EndClip();\r\n\r\n\t\t\tGUI.Label(line, EmotionalData.GetEmojiText(emojiID), emojiStyle);\r\n\t\t\t\r\n\t\t\t\/\/ UI Handle\r\n\t\t\tGUI.BeginClip(line);\r\n\t\t\tHandles.BeginGUI();\r\n\t\t\tHandles.color = Color.black;\r\n\t\t\tHandles.DrawWireDisc(halfRange, Vector3.forward, halfSize);\r\n\t\t\tHandles.color = thumbColor;\r\n\t\t\tHandles.DrawSolidDisc(halfRange + inputCircle, Vector3.forward, controlHalfSize);\r\n\t\t\tHandles.EndGUI();\r\n\t\t\tGUI.EndClip();\r\n\r\n\t\t\t\/\/ Vector2 field\r\n\t\t\tline.y += line.height;\r\n\t\t\tline.height = 20f;\r\n\t\t\tEditorGUI.LabelField(line, emojiID.ToString(), EditorStyles.helpBox);\r\n\r\n\t\t\tline.y += line.height;\r\n\t\t\tEditorGUI.BeginChangeCheck();\r\n\t\t\tVector2 tmp = EditorGUI.Vector2Field(line, GUIContent.none, square01);\r\n\t\t\tif (EditorGUI.EndChangeCheck())\r\n\t\t\t{\r\n\t\t\t\ttmp.x = Mathf.Clamp(tmp.x, -1f, 1f);\r\n\t\t\t\ttmp.y = Mathf.Clamp(tmp.y, -1f, 1f);\r\n\t\t\t\ttmp = tmp.ConvertSquareToCircle();\r\n\t\t\t\tinputCircle = tmp.Scale(-1f, 1f, -radius, radius);\r\n\t\t\t\tif (inputCircle.sqrMagnitude &gt; sqrRadius)\r\n\t\t\t\t\tinputCircle = inputCircle.normalized * radius;\r\n\t\t\t\tsquare01 = inputCircle.Scale(-radius, radius, -1f, 1f).ConvertCircleToSquare();\r\n\t\t\t\tm_State = eState.DragEnd;\r\n\t\t\t}\r\n\r\n\t\t\t\/\/ State &amp; apply change\r\n\t\t\tif (m_State == eState.DragEnd)\r\n\t\t\t{\r\n\t\t\t\tm_State = eState.Idle;\r\n\t\t\t\txProp.floatValue = square01.x;\r\n\t\t\t\tyProp.floatValue = square01.y;\r\n\t\t\t\tproperty.serializedObject.ApplyModifiedProperties();\r\n\t\t\t}\r\n\t\t\t\/\/else if (m_State == eState.Drag || !(evt.type == EventType.Repaint || evt.type == EventType.Layout))\r\n\t\t\t\/\/{\r\n\t\t\t\/\/\t\/\/ lower update rate.\r\n\t\t\t\/\/\tEditorUtility.SetDirty(property.serializedObject.targetObject);\r\n\t\t\t\/\/}\r\n\t\t\tEditorGUI.EndProperty();\r\n\r\n\t\t\tEditorUtility.SetDirty(property.serializedObject.targetObject);\r\n\t\t}\r\n\r\n\t\tpublic override float GetPropertyHeight(SerializedProperty property, GUIContent label)\r\n\t\t{\r\n\t\t\treturn 160f;\r\n\t\t}\r\n\t}\r\n}<\/pre>\n<p>Study result:<\/p>\n<ul>\n<li>\u76f4\u63a5\u7528 Event.current \u64cd\u4f5c\u4ecb\u9762, \u68c4\u7528\u50b3\u7d71 EditorGUI \u7b49\u7e6a\u756b\u65b9\u5f0f.<\/li>\n<li>Vector2 \u7684\u89d2\u5ea6\u5411\u91cf, \u7531\u6b63\u65b9\u5230\u5713\u5f62\u7684\u8f49\u63db<\/li>\n<li>Handle \u914d\u4e0a GUI.BeginClip \/ GUI.BeginGroup \u4e5f\u53ef\u4ee5\u5728 inspector \u8f15\u9b06\u4f7f\u7528.\n<ul>\n<li>GUI.BeginClip\u00a0\u5728 inspector \u4e0a\u5283\u51fa\u7e6a\u756b\u5340, \u88e1\u982d\u7684 Rect \u6703\u88ab\u91cd\u7f6e\u6b78\u96f6.<\/li>\n<\/ul>\n<\/li>\n<li>\u958b\u59cb\u4e82\u7528 implicit operator&#8230;. XD<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>\u5728\u505a\u81ea\u5df1\u7684 Dialogue \u7cfb\u7d71\u7684\u6642\u5019\u7684\u9ede\u5b50. \u8868\u60c5\u7684\u72c0\u614b\u9084\u662f\u8207\u6a21\u578b\u672c\u8eab\u7684\u52d5\u756b\u5206\u958b\u8a2d\u5b9a\u624d\u66f4\u6709\u958b\u767c\u5f48 &hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[20,43],"class_list":["post-1818","post","type-post","status-publish","format-standard","hentry","category-unity3d","tag-editor","tag-unity3d-2"],"_links":{"self":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/posts\/1818","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/comments?post=1818"}],"version-history":[{"count":0,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/posts\/1818\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/media?parent=1818"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/categories?post=1818"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.clonefactor.com\/wordpress\/wp-json\/wp\/v2\/tags?post=1818"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}