Search Unity

Need Help - Making A Sign Fade In And Out

Discussion in 'Scripting' started by Clicksurfer, Feb 1, 2015.

  1. Clicksurfer

    Clicksurfer

    Joined:
    Aug 17, 2014
    Posts:
    77
    Hey there. I've looked around a bit for help on this topic, but I couldn't find what I was looking for, so here I am.
    I have a game that in it, upon clicking on an object, a script triggers a menu (A bunch of Text Meshes and a Sprite behind them) to appear. When clicked again, the menu disappears. However, it appears more or less instantaneously.
    I want the menu to fade in and out upon clicking (Meaning that after being clicked when invisible, it will slowly become visible until looking solid, and the opposite of that when clicked again), but I can't find any way to do so. Care to help me out?

    I make the Game Objects appear and disappear by enabling and disabling their Mesh Renderer component, and my script uses C#.
    An example of making an object appear:

    Name.GetComponent<MeshRenderer> ().enabled = true;

    I got the whole, full code here, but most of it will not be relevant to the question at hand. This code is also responsible for triggering a sound upon click, and changing certain texts over time.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class PlanetClick : MonoBehaviour {
    5.  
    6.     public bool VisibleButtons=false;
    7.     public AudioClip Cursor_002;
    8.     public int AgeOfPlanet = 0;
    9.  
    10.     // Use this for initialization
    11.     void Start () {
    12.         InvokeRepeating ("AgeInc", 0f, 1.0f);
    13.     }
    14.  
    15.  
    16.  
    17.     void OnMouseDown()
    18.     {
    19.         audio.PlayOneShot (Cursor_002);
    20.         GameObject Age=GameObject.Find("PlanetTextAge");
    21.         GameObject Name=GameObject.Find("PlanetTextName");
    22.         GameObject Mass=GameObject.Find("PlanetTextMass");
    23.         GameObject Type=GameObject.Find("PlanetTextType");
    24.         GameObject Distance=GameObject.Find("PlanetTextDistance");
    25.         GameObject Frame=GameObject.Find("Frame");
    26.         if (VisibleButtons == false) {
    27.  
    28.             Name.GetComponent<MeshRenderer> ().enabled = true;
    29.             Mass.GetComponent<MeshRenderer> ().enabled = true;
    30.             Type.GetComponent<MeshRenderer> ().enabled = true;
    31.             Distance.GetComponent<MeshRenderer> ().enabled = true;
    32.             Age.GetComponent<MeshRenderer> ().enabled = true;
    33.             Frame.renderer.enabled=true;
    34.             VisibleButtons = true;
    35.         } else if (VisibleButtons == true) {
    36.             Name.GetComponent<MeshRenderer> ().enabled = false;
    37.             Mass.GetComponent<MeshRenderer> ().enabled = false;
    38.             Type.GetComponent<MeshRenderer> ().enabled = false;
    39.             Distance.GetComponent<MeshRenderer> ().enabled = false;
    40.             Age.GetComponent<MeshRenderer> ().enabled = false;
    41.             Frame.renderer.enabled = false;
    42.             VisibleButtons=false;
    43.  
    44.         }
    45.  
    46.         }
    47.  
    48.     void AgeInc()
    49.     {
    50.         AgeOfPlanet++;
    51.     }
    52.    
    53.     // Update is called once per frame
    54.     void Update () {
    55.         int Seconds = AgeOfPlanet;
    56.         int Minutes = Seconds / 60;
    57.         int Hours = Minutes / 60;
    58.         int Days = Hours / 24;
    59.         int Weeks = Days / 7;
    60.         int Months = Days / 30;
    61.         int Years = Months / 12;
    62.         int MillionYears = Years / 1000000;
    63.         int BillionYears = MillionYears / 1000;
    64.         if (Seconds < 60) {
    65.                         (GameObject.Find ("PlanetTextAge")).GetComponent<TextMesh> ().text = "Age - " + AgeOfPlanet + " Seconds";
    66.                 }
    67.         else if (0<Minutes&&Minutes < 60) {
    68.             (GameObject.Find ("PlanetTextAge")).GetComponent<TextMesh> ().text = "Age - " + Minutes + " Minutes";
    69.         }
    70.         else if (0<Hours &&Hours< 24) {
    71.             (GameObject.Find ("PlanetTextAge")).GetComponent<TextMesh> ().text = "Age - " + Hours + " Hours";
    72.         }
    73.         else if (0<Days&&Days < 7) {
    74.             (GameObject.Find ("PlanetTextAge")).GetComponent<TextMesh> ().text = "Age - " + Days + " Days";
    75.         }
    76.         else if (0<Days&&Days<30&&Weeks>0) {
    77.             (GameObject.Find ("PlanetTextAge")).GetComponent<TextMesh> ().text = "Age - " + Weeks + " Weeks";
    78.         }
    79.         else if (0<Months &&Months< 12) {
    80.             (GameObject.Find ("PlanetTextAge")).GetComponent<TextMesh> ().text = "Age - " + Months + " Months";
    81.         }
    82.         else if (0<Years &&Years< 1000000) {
    83.             (GameObject.Find ("PlanetTextAge")).GetComponent<TextMesh> ().text = "Age - " + Years + " Years";
    84.         }
    85.         else if (0<MillionYears &&MillionYears< 1000) {
    86.             (GameObject.Find ("PlanetTextAge")).GetComponent<TextMesh> ().text = "Age - " + MillionYears + " Million Years";
    87.         }
    88.         else if (0<BillionYears ) {
    89.             (GameObject.Find ("PlanetTextAge")).GetComponent<TextMesh> ().text = "Age - " + BillionYears + " Billion Years";
    90.         }
    91.     }
    92. }
    93.  
    I hope to get a reply soon. I've been struggling with this for quite a while!
    Thanks in advance,
    Clicksurfer
     
  2. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    Okay, so I see a couple of problems here.

    You really shouldn't be finding these game objects every time you use them. You should find them once and store the information. GameObect.Find is an expensive function.

    Another problem I see is that your editing the contents of the textMesh in every update... for each planet. I'm assuming there is only one set of text meshes as the ui, and I'm assuming there are mulitple planets to click on. If my assumptions are correct, your Update loop is no good, because every planet is going to update your single UI age textmesh every frame. You'd only want the planet in focus to update the UI.

    Before we get to the core of the question, are those assumptions correct? Because how we go about fixing it will depend on it.
     
  3. Clicksurfer

    Clicksurfer

    Joined:
    Aug 17, 2014
    Posts:
    77
    You're right about the first part. It's truly inefficient, the way I organize the game objects all the time, and find them. Truth is, they're the easiest alternative I found to this problem. If you have any suggestion as to how I can do this otherwise in a more efficient manner, I'd be glad to hear it.

    As for the Text Meshes, they're actually for the same planet. They all appear when I press on the planet. Each one is a different line, which relates to a different stat. I update them in every function because after I develop my game further I want them to be dynamic and evolve in some ways.

    Thanks for replying, by the way.
     
  4. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    Well, I'd do some more scripting tutorials.

    If each planet is supposed to have it's own set of text meshes, I would be doing it very differently, yes. The way you have it set up now, the game is trying to find one text mesh every frame. It doesn't matter if each planet has it's own, because you're only going to find one.

    Additionally, your age calculator is very cumbersome and shouldn't be using integers. Should be using floats or doubles.

    The age class could be an entire thing all on it's own.

    Is there a reason you're using text meshes instead of the 4.6 ui?

    I've given the planet class a rewrite. All it does is hold information about a planet, draw that planet, and show the UI when clicked:


    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3.  
    4. [System.Serializable]
    5. public class Age {
    6.  
    7.     public float startingTime;
    8.    
    9.     public Age (float startingAgeInSeconds) {
    10.         startingTime = startingAgeInSeconds;
    11.     }
    12.  
    13.     List<string> timeStrings;
    14.  
    15.     float LogType(float increment, float value, string name) {
    16.  
    17.         float next = 0f;
    18.         if (value > increment) {
    19.  
    20.             float remainder = value % increment;
    21.             next = (value - remainder) / increment;
    22.  
    23.             value = remainder;
    24.         }
    25.  
    26.         value = Mathf.Round (value * 100f) / 100f;
    27.  
    28.         if (value > 0f)
    29.             timeStrings.Add (value.ToString()+" "+name+" ");
    30.  
    31.         return next;
    32.     }
    33.  
    34.     public override string ToString ()
    35.     {
    36.         timeStrings = new List<string>();
    37.  
    38.         float currentAge = startingTime + Time.time;
    39.         float minutes = LogType(60f, currentAge, "seconds");
    40.         float hours = LogType(60f, minutes, "minutes");
    41.         float days = LogType(24f, hours, "hours");
    42.         float years = LogType(365f, days, "days");
    43.         float milennia = LogType(1000f, years, "years");
    44.         float mya = LogType(1000f, milennia, "mil");
    45.         LogType(1000f, mya, "mya");
    46.  
    47.  
    48.         string finalString = "";
    49.  
    50.         for (var i = 0; i < 3; i++) {
    51.             int nextIndex = timeStrings.Count - (1 + i);
    52.  
    53.             if (nextIndex < 0)
    54.                 break;
    55.  
    56.             finalString += timeStrings[nextIndex];
    57.  
    58.         }
    59.    
    60.         return finalString;
    61.     }
    62.  
    63. }
    64.  
    65.  
    66. public class Planet : MonoBehaviour {
    67.  
    68.     #region Types
    69.     //Enumartion for the planet type
    70.     public enum Type {
    71.         ClassA,
    72.         ClassM,
    73.         GasGiant,
    74.     }
    75.  
    76.     //Public struct, settable from the editor that holds the planets information
    77.     [System.Serializable]
    78.     public struct Info {
    79.         public Type type;
    80.         public string name;
    81.         public float mass;
    82.         public Age age;
    83.     }
    84.  
    85.  
    86.     //Private struct to hold all of our transforms and text meshes
    87.     private struct ChildObjects {
    88.  
    89.         public readonly Transform visualization;
    90.         public readonly Transform ui;
    91.  
    92.         public readonly TextMesh name;
    93.         public readonly TextMesh age;
    94.         public readonly TextMesh mass;
    95.         public readonly TextMesh type;
    96.         public readonly TextMesh dist;
    97.  
    98.         //We find all of our child objects once, when the struct is created, and we never have to look for them again.
    99.         public ChildObjects(Transform planetTransform) {
    100.  
    101.             visualization = planetTransform.Find ("Visualization");
    102.             ui = planetTransform.Find ("UI");
    103.  
    104.             name = ui.Find ("Name").GetComponent<TextMesh>();
    105.             age = ui.Find ("Age").GetComponent<TextMesh>();
    106.             mass = ui.Find ("Mass").GetComponent<TextMesh>();
    107.             type = ui.Find ("Type").GetComponent<TextMesh>();
    108.             dist = ui.Find ("Dist").GetComponent<TextMesh>();
    109.  
    110.         }
    111.     }
    112.  
    113.     #endregion
    114.  
    115.     #region Defines
    116.  
    117.     public Info info;
    118.     ChildObjects children;
    119.  
    120.     bool showUI = false;
    121.     Color uiColor = Color.white;
    122.     float uiUpdateInterval = 1f;
    123.     float uiUpdateTimeStamp;
    124.  
    125.     const float MassFactor = 2000f;
    126.     const float DistanceFactor = 1000f;
    127.     const float GasGiantVolumeFactor = 2f;
    128.     const float UiMargin = 0.1f;
    129.     const float FadeSpeed = 5f;
    130.  
    131.     #endregion
    132.  
    133.     #region Unity Callers
    134.  
    135.     void Start () {
    136.         children = new ChildObjects(transform);
    137.         uiUpdateTimeStamp = - uiUpdateInterval; // SO that it updates on the first frame
    138.         SetVolume();
    139.         SetMaterial();
    140.     }
    141.  
    142.     void Update () {
    143.    
    144.         FadeUI();
    145.         UpdateUI();
    146.  
    147.     }
    148.  
    149.     void OnMouseDown() {
    150.         //Toggles weather or not the UI should be shown.
    151.         showUI = !showUI;
    152.        
    153.     }
    154.  
    155.     #endregion
    156.  
    157.     #region Helper
    158.  
    159.     //Set the planets material based on it's type
    160.     void SetMaterial() {
    161.        
    162.         var material = Resources.Load<Material>("Materials/"+info.type.ToString()+"Planet");
    163.        
    164.         children.visualization.renderer.material = material;
    165.        
    166.     }
    167.  
    168.     //Set the scale of the object base on it's mass
    169.     void SetVolume() {
    170.        
    171.         //We'll make the volume be larger if it's a gas giant.
    172.         float volume = (info.mass / MassFactor) * ((info.type == Type.GasGiant) ? GasGiantVolumeFactor : 1f);
    173.        
    174.         children.visualization.localScale = new Vector3(volume, volume, volume);
    175.        
    176.         //To respond to click events, the collider has to be on the same object as this Planet component, rather than
    177.         //the visualization, so we have to set it's radius seperatly
    178.         (collider as SphereCollider).radius = volume * 0.5f;
    179.        
    180.         //We have to move the UI over depending on how big the planet is.
    181.         children.ui.localPosition = new Vector3(volume * 0.5f + UiMargin, 0f, 0f);
    182.        
    183.     }
    184.  
    185.     void FadeUI() {
    186.  
    187.         Color targetColor = (showUI) ? Color.white : Color.clear;
    188.        
    189.         uiColor = Color.Lerp (uiColor, targetColor, Time.deltaTime * FadeSpeed);
    190.        
    191.         children.name.renderer.material.color = uiColor;
    192.         children.age.renderer.material.color = uiColor;
    193.         children.mass.renderer.material.color = uiColor;
    194.         children.type.renderer.material.color = uiColor;
    195.         children.dist.renderer.material.color = uiColor;
    196.  
    197.     }
    198.  
    199.     void UpdateUI() {
    200.  
    201.         //if the ui isn't showing or if it's been updated since last interval
    202.         if (!showUI || Time.time - uiUpdateTimeStamp < uiUpdateInterval)
    203.             return;
    204.  
    205.         uiUpdateTimeStamp = Time.time;
    206.  
    207.         float distance = Vector3.Distance(transform.position, Camera.main.transform.position) * DistanceFactor;
    208.  
    209.         name = info.name; //For the heirarchy view
    210.         children.name.text = "Name: "+info.name;
    211.         children.mass.text = "Mass: "+info.mass.ToString()+" mTons";
    212.         children.type.text = "Type: "+info.type.ToString();
    213.         children.dist.text = "Dist: "+distance.ToString()+" mKm";
    214.         children.age.text = "Age: "+info.age.ToString();
    215.  
    216.     }
    217.  
    218.     #endregion;  
    219.  
    220. }
    221.  
    Working scene attached:
     

    Attached Files:

  5. Clicksurfer

    Clicksurfer

    Joined:
    Aug 17, 2014
    Posts:
    77
    Wow, your example is amazing! I've looked into it for a while now and it looked really great. Thank you very much.
    As for the age system, it looks good and all, but I am still unsure as to why you use doubles/floats. A practical time for a user should be in whole numbers, as far as I see. Still, the changes you've made to the age system work very good (Combining all the different time measurements to give an accurate number).
    As for the UI system, I haven't been using it for 2 main reasons. The first is that it is a relatively new topic, so I couldn't find a lot of helpful tutorials on its various uses. The second is that as far as I knew with my limited knowledge, you could've only used it for "Screen UI" (Like, when the UI covers the screen and moves along with the camera). I was not aware of the fact that you could use it in a manner such as your example has.

    In any case, I'll now work on implementing these things into my code. Thank you very much for your help.
     
  6. image28

    image28

    Joined:
    Jul 17, 2013
    Posts:
    457
    Didn't read all the answers, but you could assign the menu a shader with alpha and have a script smoothly fade the alpha value in/out when (dis/en)abling with
    Code (csharp):
    1. gameObject.renderer.material.SetFloat("_Alpha",currentAlphaValue);
    or something along those lines...
     
  7. BenZed

    BenZed

    Joined:
    May 29, 2014
    Posts:
    524
    I disagree. Time should always be measured as a float, because seconds are not whole things. Game time is measured in fractions of a second, and so should all time. The example uses the "start time" that we've given it plus the "game time" (which is Time.time) in order to calculate the updated age.

    It could probably be split up though. So that seconds are measured as floats, but minutes, hours, days, years and milennia are measured as whole numbers.

    Measuring time magnitudes of millions of years in seconds is pretty silly, but I did write it very quickly.

    Anyway, glad I could help!