public void SetupLetterMesh(ref CustomCharacterInfo char_info) { m_offset_width = char_info.width; m_width = char_info.vert.width; m_height = char_info.vert.height; m_flipped = char_info.flipped; // Setup base vertices m_x_offset = char_info.vert.x; m_y_offset = char_info.vert.y; if (!m_flipped) { // TR, TL, BL, BR m_base_vertices = new Vector3[] { new Vector3(m_width, (m_effect_manager_handle.FontBaseLine + m_y_offset), 0), new Vector3(0, (m_effect_manager_handle.FontBaseLine + m_y_offset), 0), new Vector3(0, m_height + m_effect_manager_handle.FontBaseLine + m_y_offset, 0), new Vector3(m_width, m_height + m_effect_manager_handle.FontBaseLine + m_y_offset, 0) }; } else { // rotate order of vertices by one. // TL, BL, BR, TR m_base_vertices = new Vector3[] { new Vector3(0, (m_effect_manager_handle.FontBaseLine + m_y_offset), 0), new Vector3(0, m_height + m_effect_manager_handle.FontBaseLine + m_y_offset, 0), new Vector3(m_width, m_height + m_effect_manager_handle.FontBaseLine + m_y_offset, 0), new Vector3(m_width, (m_effect_manager_handle.FontBaseLine + m_y_offset), 0) }; } }
public void ScaleClone(float scale, ref CustomCharacterInfo char_info) { char_info.flipped = flipped; char_info.uv = new Rect(uv); char_info.vert = new Rect(vert); char_info.width = width; // Scale char_info values char_info.vert.x /= scale; char_info.vert.y /= scale; char_info.vert.width /= scale; char_info.vert.height /= scale; char_info.width /= scale; }
public void SetupLetterMesh(ref CustomCharacterInfo char_info) { m_offset_width = char_info.width; m_width = char_info.vert.width; m_height = char_info.vert.height; m_flipped = char_info.flipped; // Setup base vertices m_x_offset = char_info.vert.x; m_y_offset = char_info.vert.y; if(!m_flipped) { // TR, TL, BL, BR m_base_vertices = new Vector3[] { new Vector3(m_width, (m_effect_manager_handle.FontBaseLine + m_y_offset), 0), new Vector3(0, (m_effect_manager_handle.FontBaseLine + m_y_offset), 0), new Vector3(0, m_height + m_effect_manager_handle.FontBaseLine + m_y_offset, 0), new Vector3(m_width, m_height + m_effect_manager_handle.FontBaseLine + m_y_offset, 0)}; } else { // rotate order of vertices by one. // TL, BL, BR, TR m_base_vertices = new Vector3[] {new Vector3(0, (m_effect_manager_handle.FontBaseLine + m_y_offset), 0), new Vector3(0, m_height + m_effect_manager_handle.FontBaseLine + m_y_offset, 0), new Vector3( m_width, m_height + m_effect_manager_handle.FontBaseLine + m_y_offset, 0), new Vector3(m_width, (m_effect_manager_handle.FontBaseLine + m_y_offset), 0)}; } }
public void Recycle(string character, int letter_idx, Mesh mesh, Vector3 base_offset, ref CustomCharacterInfo char_info, int line_num, int word_idx, EffectManager effect_manager) { m_character = character; m_mesh = mesh; m_base_offset = base_offset; m_effect_manager_handle = effect_manager; m_progression_variables = new AnimationProgressionVariables(letter_idx, word_idx, line_num); SetupLetterMesh(ref char_info); if(m_flipped) { // flip UV coords in x axis. m_mesh.uv = new Vector2[] {mesh.uv[3], mesh.uv[2], mesh.uv[1], mesh.uv[0]}; } m_current_letter_action = null; }
public LetterSetup(string character, int letter_idx, Mesh mesh, Vector3 base_offset, ref CustomCharacterInfo char_info, int line_num, int word_idx, EffectManager effect_manager) { m_character = character; m_mesh = mesh; m_base_offset = base_offset; m_effect_manager_handle = effect_manager; m_progression_variables = new AnimationProgressionVariables(letter_idx, word_idx, line_num); m_anim_state_vars = new AnimationStateVariables(); m_anim_state_vars.m_active_loop_cycles = new List<ActionLoopCycle>(); SetupLetterMesh(ref char_info); if(m_flipped) { // flip UV coords in x axis. m_mesh.uv = new Vector2[] {mesh.uv[3], mesh.uv[2], mesh.uv[1], mesh.uv[0]}; } }
public void Recycle(string character, int letter_idx, Mesh mesh, Vector3 base_offset, ref CustomCharacterInfo char_info, int line_num, int word_idx, EffectManager effect_manager) { m_character = character; m_mesh = mesh; m_base_offset = base_offset; m_effect_manager_handle = effect_manager; m_progression_variables = new AnimationProgressionVariables(letter_idx, word_idx, line_num); SetupLetterMesh(ref char_info); if (m_flipped) { // flip UV coords in x axis. m_mesh.uv = new Vector2[] { mesh.uv[3], mesh.uv[2], mesh.uv[1], mesh.uv[0] }; } m_current_letter_action = null; }
public LetterSetup(string character, int letter_idx, Mesh mesh, Vector3 base_offset, ref CustomCharacterInfo char_info, int line_num, int word_idx, EffectManager effect_manager) { m_character = character; m_mesh = mesh; m_base_offset = base_offset; m_effect_manager_handle = effect_manager; m_progression_variables = new AnimationProgressionVariables(letter_idx, word_idx, line_num); m_anim_state_vars = new AnimationStateVariables(); m_anim_state_vars.m_active_loop_cycles = new List <ActionLoopCycle>(); SetupLetterMesh(ref char_info); if (m_flipped) { // flip UV coords in x axis. m_mesh.uv = new Vector2[] { mesh.uv[3], mesh.uv[2], mesh.uv[1], mesh.uv[0] }; } }
public void SetText(string new_text, bool force_all_new = false) { if(m_renderer == null) { m_renderer = this.GetComponent<Renderer>(); } bool setup_correctly = false; // Automatically assign the font material to the renderer if its not already set if((m_renderer.sharedMaterial == null || m_renderer.sharedMaterial != m_font_material) && m_font_material != null) { m_renderer.sharedMaterial = m_font_material; } else if(m_font != null) { if(m_renderer.sharedMaterial == null || m_renderer.sharedMaterial != m_font_material) { m_font_material = m_font.material; m_renderer.sharedMaterial = m_font_material; } if(m_renderer.sharedMaterial != null) { setup_correctly = true; } } if(!setup_correctly && (m_renderer.sharedMaterial == null || m_font_data_file == null)) { // Incorrectly setup font information m_font = Resources.GetBuiltinResource(typeof(Font), "Arial.ttf") as Font; m_font_material = m_font.material; m_renderer.sharedMaterial = m_font_material; m_font_data_file = null; } m_text = new_text; // Remove all carriage return char's from new_text new_text = new_text.Replace("\r", ""); string raw_chars = m_text.Replace(" ", ""); raw_chars = raw_chars.Replace("\n", ""); raw_chars = raw_chars.Replace("\r", ""); raw_chars = raw_chars.Replace("\t", ""); int text_length = new_text.Length; LetterSetup[] prev_letters = m_letters; m_letters= new LetterSetup[raw_chars.Length]; CustomCharacterInfo char_info = new CustomCharacterInfo(); CustomCharacterInfo last_char_info = null; m_text_datas = new List<TextSizeData>(); if(m_font != null) { // Make sure font contains all characters required m_font.RequestCharactersInTexture(m_text); if(m_font_material.mainTexture.width != m_font_texture_width || m_font_material.mainTexture.height != m_font_texture_height) { // Font texture size has changed m_font_texture_width = m_font_material.mainTexture.width; m_font_texture_height = m_font_material.mainTexture.height; SetText(m_text, true); return; } } // Calculate bounds of text mesh char character; float y_max=0, y_min=0, x_max=0, x_min=0; float text_width = 0, text_height = 0; int line_letter_idx = 0; float line_height_offset = 0; float total_text_width = 0, total_text_height = 0; float line_width_at_last_space = 0; float space_char_offset = 0; int last_letter_setup_idx = -1; float last_space_y_max = 0; float last_space_y_min = 0; Rect uv_data; LetterSetup last_letter = null; float letter_offset = 0; int letter_count = 0; int line_idx = 0; int word_idx = 0; m_line_height = 0; Action AddNewLineData = new Action( () => { if(m_display_axis == TextDisplayAxis.HORIZONTAL) { float height = Mathf.Abs(y_max - y_min ) * LineHeightFactor; // Check if line is the tallest so far if(height > m_line_height) m_line_height = height; if(last_char_info != null) { // Re-adjust width of last letter since its the end of the text line text_width += - last_char_info.width + last_char_info.vert.width + last_char_info.vert.x; } m_text_datas.Add( new TextSizeData(text_width, height, line_height_offset, y_max)); line_height_offset += height; if(text_width > total_text_width) { total_text_width = text_width; } total_text_height += height; } else { float width = Mathf.Abs( x_max - x_min ) * LineHeightFactor; // Check if line is the tallest so far if(width > m_line_height) m_line_height = width; m_text_datas.Add( new TextSizeData( width, text_height * -1, line_height_offset, 0)); line_height_offset += width; total_text_width += width; if(text_height < total_text_height) { total_text_height = text_height; } } line_letter_idx = 0; text_width = 0; line_width_at_last_space = 0; space_char_offset = 0; last_space_y_max = 0; last_space_y_min = 0; last_letter_setup_idx = -1; text_height = 0; last_char_info = null; }); for(int letter_idx=0; letter_idx < text_length; letter_idx++) { character = new_text[letter_idx]; if(GetCharacterInfo(character, ref char_info)) { if(character.Equals('\t')) { continue; } else if(character.Equals(' ')) { if(m_display_axis == TextDisplayAxis.HORIZONTAL) { // Record the state of the line dims at this point incase the next word is forced onto next line by bound box line_width_at_last_space = text_width; space_char_offset = char_info.width; last_space_y_max = y_max; last_space_y_min = y_min; last_letter_setup_idx = letter_count; text_width += char_info.width; } else { char_info.vert.height = -char_info.width; } // Add space width to offset value letter_offset += m_display_axis == TextDisplayAxis.HORIZONTAL ? char_info.width : -char_info.width; //Increment word count word_idx++; } else if(character.Equals('\n')) { AddNewLineData.Invoke(); letter_offset = 0; line_idx++; //Increment word count word_idx++; } else { if(m_display_axis == TextDisplayAxis.HORIZONTAL) { if(line_letter_idx == 0 || char_info.vert.y > y_max) { y_max = char_info.vert.y; } if(line_letter_idx == 0 || char_info.vert.y + char_info.vert.height < y_min) { y_min = char_info.vert.y + char_info.vert.height; } // increment the text width by the letter progress width, and then full mesh width for last letter or end of line. text_width += (letter_idx == text_length - 1) ? char_info.vert.width + char_info.vert.x : char_info.width; // Handle bounding box if setup if(m_max_width > 0 && last_letter_setup_idx >= 0) { float actual_line_width = (letter_idx == text_length - 1) ? text_width : text_width - char_info.width + char_info.vert.width + char_info.vert.x; if(actual_line_width > m_max_width) { // Line exceeds bounding box width float new_line_text_width = text_width - line_width_at_last_space - space_char_offset; float new_line_y_min = last_space_y_min; float new_line_y_max = last_space_y_max; // Set line width to what it was at the last space (which is now the end of this line) text_width = line_width_at_last_space; y_max = last_space_y_max; y_min = last_space_y_min; letter_offset = 0; line_idx++; // Need to change the associated line number and positional offset of the letters now on a new line for(int past_letter_idx=last_letter_setup_idx; past_letter_idx < letter_count; past_letter_idx++) { m_letters[past_letter_idx].m_progression_variables.m_line_value = line_idx; m_letters[past_letter_idx].m_base_offset = m_display_axis == TextDisplayAxis.HORIZONTAL ? new Vector3(letter_offset, 0, 0) : new Vector3(0, letter_offset, 0); letter_offset += m_display_axis == TextDisplayAxis.HORIZONTAL ? m_letters[past_letter_idx].m_offset_width + (m_px_offset.x / FontScale) : m_letters[past_letter_idx].m_height + (-m_px_offset.y / FontScale); } AddNewLineData.Invoke(); // Setup current values text_width = new_line_text_width; y_min = new_line_y_min; y_max = new_line_y_max; } } } else { if(line_letter_idx == 0 || char_info.vert.x + char_info.vert.width > x_max) { x_max = char_info.vert.x + char_info.vert.width; } if(line_letter_idx == 0 || char_info.vert.x < x_min) { x_min = char_info.vert.x; } text_height += char_info.vert.height; } // Get letterSetup reference if(letter_count < prev_letters.Length && !force_all_new) { last_letter = prev_letters[letter_count]; } // Either reuse the same previous instance of LetterSetup or create a new one for this character. if( !force_all_new && prev_letters != null && letter_count < prev_letters.Length && last_letter.m_character.Equals(new_text[letter_idx].ToString()) && last_letter.m_progression_variables.m_letter_value == letter_idx && last_letter.m_mesh != null) { // Use same LetterSetup from previous configuration m_letters[letter_count] = last_letter; // Remove instance from previous letters list prev_letters[letter_count] = null; // position the letter offset again, incase it has changed from previous letters changing. last_letter.m_base_offset = m_display_axis == TextDisplayAxis.HORIZONTAL ? new Vector3(letter_offset, 0, 0) : new Vector3(0, letter_offset, 0); last_letter.SetupLetterMesh(ref char_info); last_letter.m_progression_variables.m_line_value = line_idx; last_letter.m_progression_variables.m_word_value = word_idx; last_letter.m_base_offsets_setup = false; } else { uv_data = char_info.uv; if(letter_count < prev_letters.Length && !force_all_new) { // Recycle last letter instance. m_letters[letter_count] = last_letter; // Setup Mesh UV co-ords and triangles (and fill in placeholder vertices) last_letter.m_mesh.vertices = new Vector3[]{Vector3.zero, Vector3.zero, Vector3.zero, Vector3.zero}; last_letter.m_mesh.uv = new Vector2[]{ new Vector2(uv_data.x + uv_data.width, uv_data.y + uv_data.height), new Vector2(uv_data.x, uv_data.y + uv_data.height), new Vector2(uv_data.x, uv_data.y), new Vector2(uv_data.x + uv_data.width, uv_data.y)}; last_letter.m_mesh.triangles = new int[]{2,1,0, 3,2,0}; last_letter.m_mesh.normals = new Vector3[]{ Vector3.back, Vector3.back, Vector3.back, Vector3.back}; last_letter.Recycle( "" + character, letter_count, last_letter.m_mesh, m_display_axis == TextDisplayAxis.HORIZONTAL ? new Vector3(letter_offset, 0, 0) : new Vector3(0, letter_offset, 0), // base_offset ref char_info, line_idx, word_idx, this); last_letter.m_base_offsets_setup = false; // Remove instance from previous letters list prev_letters[letter_count] = null; } else { Mesh mesh = new Mesh(); // Setup Mesh UV co-ords and triangles (and fill in placeholder vertices) mesh.vertices = new Vector3[]{Vector3.zero, Vector3.zero, Vector3.zero, Vector3.zero}; mesh.uv = new Vector2[]{ new Vector2(uv_data.x + uv_data.width, uv_data.y + uv_data.height), new Vector2(uv_data.x, uv_data.y + uv_data.height), new Vector2(uv_data.x, uv_data.y), new Vector2(uv_data.x + uv_data.width, uv_data.y)}; mesh.triangles = new int[]{2,1,0, 3,2,0}; mesh.normals = new Vector3[]{ Vector3.back, Vector3.back, Vector3.back, Vector3.back}; m_letters[letter_count] = new LetterSetup( "" + character, letter_count, mesh, m_display_axis == TextDisplayAxis.HORIZONTAL ? new Vector3(letter_offset, 0, 0) : new Vector3(0, letter_offset, 0), // base_offset ref char_info, line_idx, word_idx, this); if(last_letter != null) { m_letters[letter_count].SetAnimationVars(last_letter); } } } letter_count ++; letter_offset += m_display_axis == TextDisplayAxis.HORIZONTAL ? char_info.width + (m_px_offset.x / FontScale) : char_info.vert.height + (-m_px_offset.y / FontScale); last_char_info = char_info; } } line_letter_idx++; } // Save line and word info for later m_number_of_words = word_idx + 1; m_number_of_lines = line_idx + 1; if(m_display_axis == TextDisplayAxis.HORIZONTAL) { float height = Mathf.Abs(y_max - y_min ); m_text_datas.Add( new TextSizeData(text_width, height, line_height_offset, y_max)); if(text_width > total_text_width) { total_text_width = text_width; } total_text_height += height; } else { float width = Mathf.Abs( x_max - x_min ); m_text_datas.Add( new TextSizeData( width, text_height * -1, line_height_offset, 0)); total_text_width += width; if(text_height < total_text_height) { total_text_height = text_height; } } #if UNITY_EDITOR m_total_text_width = total_text_width; m_total_text_height = total_text_height; #endif for(int idx=0; idx < m_text_datas.Count; idx++) { m_text_datas[idx].m_total_text_height = total_text_height * (m_display_axis == TextDisplayAxis.HORIZONTAL ? 1 : -1); if(m_max_width > 0) { m_text_datas[idx].m_total_text_width = m_max_width; } else { m_text_datas[idx].m_total_text_width = total_text_width; } } // Destroy any left over unused meshes if(prev_letters != null) { foreach(LetterSetup old_letter in prev_letters) { if(old_letter != null) { // Letter wasn't used in new text setup; delete it's mesh instance. if(Application.isPlaying) { Destroy(old_letter.m_mesh); } else { DestroyImmediate(old_letter.m_mesh); } } } } // Set letter base offsets where needed bool all_offsets_set = true; do { all_offsets_set = true; foreach(LetterSetup letter in m_letters) { if(!letter.m_base_offsets_setup) { if(m_text_datas.Count == 0) { all_offsets_set = false; break; } letter.SetBaseOffset(m_text_anchor, m_display_axis, m_text_alignment, m_text_datas); } } if(!all_offsets_set) { // If text_datas has been lost or if legacy effect and hasn't been created, re-set text to recalculate it. Debug.LogError("If text_datas has been lost or if legacy effect and hasn't been created, reset text."); SetText(m_text); } } while(!all_offsets_set); // Calculate action progression values PrepareAnimationData(); // Render state of newly set text UpdateMesh(true, true, 0, 0); }
bool GetCharacterInfo(char m_character, ref CustomCharacterInfo char_info) { if(m_character.Equals('\n') || m_character.Equals('\r')) { return true; } if(m_font != null) { if(!m_current_font_name.Equals(m_font.name)) { // Recalculate font's baseline value // Checks through all available alpha characters and uses the most common bottom y_axis value as the baseline for the font. #pragma warning disable Dictionary<float, int> baseline_values = new Dictionary<float, int>(); float baseline; foreach(CharacterInfo character in m_font.characterInfo) { // only check alpha characters (a-z, A-Z) if((character.index >= 97 && character.index < 123) || (character.index >= 65 && character.index < 91)) { baseline = -character.vert.y - character.vert.height; if(baseline_values.ContainsKey(baseline)) baseline_values[baseline] ++; else baseline_values[baseline] = 1; } } #pragma warning restore // Find most common baseline value used by the letters int idx=0; int highest_num=0, highest_idx=-1; float most_common_baseline = -1; foreach(int num in baseline_values.Values) { if(highest_idx == -1 || num > highest_num) { highest_idx = idx; highest_num = num; } idx++; } // Retrieve the most common value and use as baseline value idx=0; foreach(float baseline_key in baseline_values.Keys) { if(idx == highest_idx) { most_common_baseline = baseline_key; break; } idx++; } m_font_baseline = most_common_baseline; // Set font name to current, to ensure this check doesn't happen each time m_current_font_name = m_font.name; } CharacterInfo font_char_info = new CharacterInfo(); m_font.GetCharacterInfo(m_character, out font_char_info); #pragma warning disable char_info.flipped = font_char_info.flipped; char_info.uv = font_char_info.uv; char_info.vert = font_char_info.vert; char_info.width = font_char_info.width; // Scale char_info values char_info.vert.x /= FontScale; char_info.vert.y /= FontScale; char_info.vert.width /= FontScale; char_info.vert.height /= FontScale; char_info.width /= FontScale; if(font_char_info.width == 0) { // Invisible character info returned because character is not contained within the font Debug.LogWarning("Character '" + GetHumanReadableCharacterString(m_character) + "' not found. Check that font '" + m_font.name + "' supports this character."); } #pragma warning restore return true; } if(m_font_data_file != null) { if(m_custom_font_data == null || !m_font_data_file.name.Equals(m_current_font_data_file_name)) { // Setup m_custom_font_data for the custom font. #if !UNITY_WINRT if(m_font_data_file.text.Substring(0,5).Equals("<?xml")) { // Text file is in xml format m_current_font_data_file_name = m_font_data_file.name; m_custom_font_data = new CustomFontCharacterData(); XmlTextReader reader = new XmlTextReader(new StringReader(m_font_data_file.text)); int texture_width = 0; int texture_height = 0; int uv_x, uv_y; float width, height, xoffset, yoffset, xadvance; CustomCharacterInfo character_info; while(reader.Read()) { if(reader.IsStartElement()) { if(reader.Name.Equals("common")) { texture_width = int.Parse(reader.GetAttribute("scaleW")); texture_height = int.Parse(reader.GetAttribute("scaleH")); m_font_baseline = int.Parse(reader.GetAttribute("base")); } else if(reader.Name.Equals("char")) { uv_x = int.Parse(reader.GetAttribute("x")); uv_y = int.Parse(reader.GetAttribute("y")); width = float.Parse(reader.GetAttribute("width")); height = float.Parse(reader.GetAttribute("height")); xoffset = float.Parse(reader.GetAttribute("xoffset")); yoffset = float.Parse(reader.GetAttribute("yoffset")); xadvance = float.Parse(reader.GetAttribute("xadvance")); character_info = new CustomCharacterInfo(); character_info.flipped = false; character_info.uv = new Rect((float) uv_x / (float) texture_width, 1 - ((float)uv_y / (float)texture_height) - (float)height/(float)texture_height, (float)width/(float)texture_width, (float)height/(float)texture_height); character_info.vert = new Rect(xoffset,-yoffset,width, -height); character_info.width = xadvance; m_custom_font_data.m_character_infos.Add( int.Parse(reader.GetAttribute("id")), character_info); } } } } else #endif if(m_font_data_file.text.Substring(0,4).Equals("info")) { // Plain txt format m_current_font_data_file_name = m_font_data_file.name; m_custom_font_data = new CustomFontCharacterData(); int texture_width = 0; int texture_height = 0; int uv_x, uv_y; float width, height, xoffset, yoffset, xadvance; CustomCharacterInfo character_info; string[] data_fields; string[] text_lines = m_font_data_file.text.Split(new char[]{'\n'}); foreach(string font_data in text_lines) { if(font_data.Length >= 5 && font_data.Substring(0,5).Equals("char ")) { // character data line data_fields = ParseFieldData(font_data, new string[]{"id=", "x=", "y=", "width=", "height=", "xoffset=", "yoffset=", "xadvance="}); uv_x = int.Parse(data_fields[1]); uv_y = int.Parse(data_fields[2]); width = float.Parse(data_fields[3]); height = float.Parse(data_fields[4]); xoffset = float.Parse(data_fields[5]); yoffset = float.Parse(data_fields[6]); xadvance = float.Parse(data_fields[7]); character_info = new CustomCharacterInfo(); character_info.flipped = false; character_info.uv = new Rect((float) uv_x / (float) texture_width, 1 - ((float)uv_y / (float)texture_height) - (float)height/(float)texture_height, (float)width/(float)texture_width, (float)height/(float)texture_height); character_info.vert = new Rect(xoffset,-yoffset +1,width, -height); character_info.width = xadvance; m_custom_font_data.m_character_infos.Add( int.Parse(data_fields[0]), character_info); } else if(font_data.Length >= 6 && font_data.Substring(0,6).Equals("common")) { data_fields = ParseFieldData(font_data, new string[]{"scaleW=", "scaleH=", "base="}); texture_width = int.Parse(data_fields[0]); texture_height = int.Parse(data_fields[1]); m_font_baseline = int.Parse(data_fields[2]); } } } } if(m_custom_font_data.m_character_infos.ContainsKey((int) m_character)) { ((CustomCharacterInfo) m_custom_font_data.m_character_infos[(int)m_character]).ScaleClone(FontScale, ref char_info); return true; } } return false; }