diff --git a/Boids/Assets/OpenHyperlinks.cs b/Boids/Assets/OpenHyperlinks.cs new file mode 100644 index 0000000..769a375 --- /dev/null +++ b/Boids/Assets/OpenHyperlinks.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.EventSystems; +using TMPro; + +// somewhat based upon the TextMesh Pro example script: TMP_TextSelector_B +[RequireComponent(typeof(TextMeshProUGUI))] +public class OpenHyperlinks : MonoBehaviour, IPointerClickHandler { + public bool doesColorChangeOnHover = true; + public Color hoverColor = new Color(60f / 255f, 120f / 255f, 1f); + + private TextMeshProUGUI _pTextMeshPro; + private Canvas _pCanvas; + private Camera _pCamera; + + public bool IsLinkHighlighted => _pCurrentLink != -1; + + private int _pCurrentLink = -1; + private List _pOriginalVertexColors = new List(); + + protected virtual void Awake() { + _pTextMeshPro = GetComponent(); + _pCanvas = GetComponentInParent(); + + // Get a reference to the camera if Canvas Render Mode is not ScreenSpace Overlay. + if (_pCanvas.renderMode == RenderMode.ScreenSpaceOverlay) + _pCamera = null; + else + _pCamera = _pCanvas.worldCamera; + } + + void LateUpdate() { + // is the cursor in the correct region (above the text area) and furthermore, in the link region? + var isHoveringOver = + TMP_TextUtilities.IsIntersectingRectTransform(_pTextMeshPro.rectTransform, Input.mousePosition, _pCamera); + int linkIndex = isHoveringOver + ? TMP_TextUtilities.FindIntersectingLink(_pTextMeshPro, Input.mousePosition, _pCamera) + : -1; + + // Clear previous link selection if one existed. + if (_pCurrentLink != -1 && linkIndex != _pCurrentLink) { + // Debug.Log("Clear old selection"); + SetLinkToColor(_pCurrentLink, (linkIdx, vertIdx) => _pOriginalVertexColors[linkIdx][vertIdx]); + _pOriginalVertexColors.Clear(); + _pCurrentLink = -1; + } + + // Handle new link selection. + if (linkIndex != -1 && linkIndex != _pCurrentLink) { + // Debug.Log("New selection"); + _pCurrentLink = linkIndex; + if (doesColorChangeOnHover) + _pOriginalVertexColors = SetLinkToColor(linkIndex, (_linkIdx, _vertIdx) => hoverColor); + } + + // Debug.Log(string.Format("isHovering: {0}, link: {1}", isHoveringOver, linkIndex)); + } + + public void OnPointerClick(PointerEventData eventData) { + // Debug.Log("Click at POS: " + eventData.position + " World POS: " + eventData.worldPosition); + + int linkIndex = TMP_TextUtilities.FindIntersectingLink(_pTextMeshPro, Input.mousePosition, _pCamera); + if (linkIndex != -1) { + // was a link clicked? + TMP_LinkInfo linkInfo = _pTextMeshPro.textInfo.linkInfo[linkIndex]; + + // Debug.Log(string.Format("id: {0}, text: {1}", linkInfo.GetLinkID(), linkInfo.GetLinkText())); + // open the link id as a url, which is the metadata we added in the text field + Application.OpenURL(linkInfo.GetLinkID()); + } + } + + List SetLinkToColor(int linkIndex, Func colorForLinkAndVert) { + TMP_LinkInfo linkInfo = _pTextMeshPro.textInfo.linkInfo[linkIndex]; + + var oldVertColors = new List(); // store the old character colors + + for (int i = 0; i < linkInfo.linkTextLength; i++) { + // for each character in the link string + int characterIndex = linkInfo.linkTextfirstCharacterIndex + i; // the character index into the entire text + TMP_CharacterInfo charInfo = _pTextMeshPro.textInfo.characterInfo[characterIndex]; + int meshIndex = + charInfo + .materialReferenceIndex; // Get the index of the material / sub text object used by this character. + int vertexIndex = charInfo.vertexIndex; // Get the index of the first vertex of this character. + + Color32[] vertexColors = + _pTextMeshPro.textInfo.meshInfo[meshIndex].colors32; // the colors for this character + oldVertColors.Add(vertexColors.ToArray()); + + if (charInfo.isVisible) { + vertexColors[vertexIndex + 0] = colorForLinkAndVert(i, vertexIndex + 0); + vertexColors[vertexIndex + 1] = colorForLinkAndVert(i, vertexIndex + 1); + vertexColors[vertexIndex + 2] = colorForLinkAndVert(i, vertexIndex + 2); + vertexColors[vertexIndex + 3] = colorForLinkAndVert(i, vertexIndex + 3); + } + } + + // Update Geometry + _pTextMeshPro.UpdateVertexData(TMP_VertexDataUpdateFlags.All); + + return oldVertColors; + } +} \ No newline at end of file diff --git a/Boids/Assets/OpenHyperlinks.cs.meta b/Boids/Assets/OpenHyperlinks.cs.meta new file mode 100644 index 0000000..671611f --- /dev/null +++ b/Boids/Assets/OpenHyperlinks.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0f3a0b33c50264b44b8f7584c79656f4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Boids/Assets/Resources/Fonts/EncodeSans-Bold SDF.asset b/Boids/Assets/Resources/Fonts/EncodeSans-Bold SDF.asset index bf1a7ed..c795766 100644 --- a/Boids/Assets/Resources/Fonts/EncodeSans-Bold SDF.asset +++ b/Boids/Assets/Resources/Fonts/EncodeSans-Bold SDF.asset @@ -52,9 +52,9 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 71c1514a6bd24e1e882cebbe1904ce04, type: 3} m_Name: EncodeSans-Bold SDF m_EditorClassIdentifier: - hashCode: 0 + hashCode: -53336336 material: {fileID: 2738316607261864018} - materialHashCode: 0 + materialHashCode: 525044091 m_Version: 1.1.0 m_SourceFontFileGUID: 43d6a52fcfa8f28419b2edb41d50e5c0 m_SourceFontFile_EditorRef: {fileID: 0} @@ -2700,9 +2700,9 @@ Material: - _OutlineWidth: 0 - _PerspectiveFilter: 0.875 - _Reflectivity: 10 - - _ScaleRatioA: 1 - - _ScaleRatioB: 1 - - _ScaleRatioC: 1 + - _ScaleRatioA: 0.8333333 + - _ScaleRatioB: 0.6770833 + - _ScaleRatioC: 0.6770833 - _ScaleX: 1 - _ScaleY: 1 - _ShaderFlags: 0 diff --git a/Boids/Assets/Scenes/Project.unity b/Boids/Assets/Scenes/Project.unity index 6ca72a0..b6fe8bb 100644 --- a/Boids/Assets/Scenes/Project.unity +++ b/Boids/Assets/Scenes/Project.unity @@ -120,85 +120,6 @@ NavMeshSettings: debug: m_Flags: 0 m_NavMeshData: {fileID: 0} ---- !u!1 &238998675 -GameObject: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - serializedVersion: 6 - m_Component: - - component: {fileID: 238998676} - - component: {fileID: 238998678} - - component: {fileID: 238998677} - m_Layer: 5 - m_Name: AboutTitle - m_TagString: Untagged - m_Icon: {fileID: 0} - m_NavMeshLayer: 0 - m_StaticEditorFlags: 0 - m_IsActive: 1 ---- !u!224 &238998676 -RectTransform: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 238998675} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} - m_LocalPosition: {x: 0, y: 0, z: 0} - m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: [] - m_Father: {fileID: 882182194} - m_RootOrder: 0 - m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0.5, y: 0.5} - m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: -0.000027587, y: 143.5} - m_SizeDelta: {x: 44.94, y: 17.95} - m_Pivot: {x: 0.5, y: 0.5} ---- !u!114 &238998677 -MonoBehaviour: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 238998675} - m_Enabled: 1 - m_EditorHideFlags: 0 - m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} - m_Name: - m_EditorClassIdentifier: - m_Material: {fileID: 0} - m_Color: {r: 0.6509434, g: 0.6509434, b: 0.6509434, a: 1} - m_RaycastTarget: 1 - m_OnCullStateChanged: - m_PersistentCalls: - m_Calls: [] - m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, - Version=1.0.0.0, Culture=neutral, PublicKeyToken=null - m_FontData: - m_Font: {fileID: 12800000, guid: 43d6a52fcfa8f28419b2edb41d50e5c0, type: 3} - m_FontSize: 14 - m_FontStyle: 0 - m_BestFit: 0 - m_MinSize: 10 - m_MaxSize: 40 - m_Alignment: 0 - m_AlignByGeometry: 0 - m_RichText: 1 - m_HorizontalOverflow: 0 - m_VerticalOverflow: 0 - m_LineSpacing: 1 - m_Text: About ---- !u!222 &238998678 -CanvasRenderer: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 238998675} - m_CullTransparentMesh: 0 --- !u!1 &342684657 GameObject: m_ObjectHideFlags: 0 @@ -823,7 +744,7 @@ GameObject: - component: {fileID: 882182196} - component: {fileID: 882182195} m_Layer: 5 - m_Name: Panel + m_Name: AboutPanel m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 @@ -840,7 +761,7 @@ RectTransform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_Children: - - {fileID: 238998676} + - {fileID: 2098048398} - {fileID: 1872191079} m_Father: {fileID: 1384661769} m_RootOrder: 5 @@ -1583,6 +1504,7 @@ GameObject: - component: {fileID: 1872191082} - component: {fileID: 1872191081} - component: {fileID: 1872191080} + - component: {fileID: 1872191083} m_Layer: 5 m_Name: AboutDescription m_TagString: Untagged @@ -1666,16 +1588,18 @@ MonoBehaviour: - Project Source - - My GitHub Profile + - My GitHub Profile Thanks to - Sebastian Lague for the inspiration. + Sebastian Lague + for the inspiration. - Conrad Parker for the detailed pseudocode. + Conrad Parker for the + detailed pseudocode. - Craig Reynold for the Boids algorithm.' + Craig Reynold for the Boids algorithm.' m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: d6ea291f4e788d84dbca1ad85398677b, type: 2} m_sharedMaterial: {fileID: -4557755317545283406, guid: d6ea291f4e788d84dbca1ad85398677b, @@ -1708,7 +1632,7 @@ MonoBehaviour: m_fontSizeBase: 14 m_fontWeight: 400 m_enableAutoSizing: 0 - m_fontSizeMin: 18 + m_fontSizeMin: 14 m_fontSizeMax: 72 m_fontStyle: 0 m_textAlignment: 257 @@ -1776,6 +1700,20 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1872191078} m_CullTransparentMesh: 0 +--- !u!114 &1872191083 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1872191078} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0f3a0b33c50264b44b8f7584c79656f4, type: 3} + m_Name: + m_EditorClassIdentifier: + doesColorChangeOnHover: 1 + hoverColor: {r: 0.23529412, g: 0.47058824, b: 1, a: 1} --- !u!1 &2044694037 GameObject: m_ObjectHideFlags: 0 @@ -1897,3 +1835,161 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2044694037} m_CullTransparentMesh: 0 +--- !u!1 &2098048397 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2098048398} + - component: {fileID: 2098048400} + - component: {fileID: 2098048399} + m_Layer: 5 + m_Name: AboutTitle + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2098048398 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2098048397} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 882182194} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 143.5} + m_SizeDelta: {x: 70, y: 19} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2098048399 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2098048397} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_text: About + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 177b68b1752f0584e8204a8d6693c602, type: 2} + m_sharedMaterial: {fileID: 2738316607261864018, guid: 177b68b1752f0584e8204a8d6693c602, + type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4291940817 + m_fontColor: {r: 0.8207547, g: 0.8207547, b: 0.8207547, a: 1} + m_enableVertexGradient: 0 + m_colorMode: 3 + m_fontColorGradient: + topLeft: {r: 1, g: 1, b: 1, a: 1} + topRight: {r: 1, g: 1, b: 1, a: 1} + bottomLeft: {r: 1, g: 1, b: 1, a: 1} + bottomRight: {r: 1, g: 1, b: 1, a: 1} + m_fontColorGradientPreset: {fileID: 0} + m_spriteAsset: {fileID: 0} + m_tintAllSprites: 0 + m_overrideHtmlColors: 0 + m_faceColor: + serializedVersion: 2 + rgba: 4294967295 + m_outlineColor: + serializedVersion: 2 + rgba: 4278190080 + m_fontSize: 17 + m_fontSizeBase: 17 + m_fontWeight: 400 + m_enableAutoSizing: 0 + m_fontSizeMin: 18 + m_fontSizeMax: 72 + m_fontStyle: 0 + m_textAlignment: 514 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_enableWordWrapping: 1 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_firstOverflowCharacterIndex: 0 + m_linkedTextComponent: {fileID: 0} + m_isLinkedTextComponent: 0 + m_isTextTruncated: 0 + m_enableKerning: 1 + m_enableExtraPadding: 0 + checkPaddingRequired: 0 + m_isRichText: 1 + m_parseCtrlCharacters: 1 + m_isOrthographic: 1 + m_isCullingEnabled: 0 + m_ignoreRectMaskCulling: 0 + m_ignoreCulling: 1 + m_horizontalMapping: 0 + m_verticalMapping: 0 + m_uvLineOffset: 0 + m_geometrySortingOrder: 0 + m_VertexBufferAutoSizeReduction: 1 + m_firstVisibleCharacter: 0 + m_useMaxVisibleDescender: 1 + m_pageToDisplay: 1 + m_margin: {x: 0, y: 0, z: 0, w: 0} + m_textInfo: + textComponent: {fileID: 2098048399} + characterCount: 5 + spriteCount: 0 + spaceCount: 0 + wordCount: 1 + linkCount: 0 + lineCount: 1 + pageCount: 1 + materialCount: 1 + m_isUsingLegacyAnimationComponent: 0 + m_isVolumetricText: 0 + m_spriteAnimator: {fileID: 0} + m_hasFontAssetChanged: 0 + m_subTextObjects: + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + - {fileID: 0} + m_baseMaterial: {fileID: 0} + m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!222 &2098048400 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2098048397} + m_CullTransparentMesh: 0