How can I create a list with images and change which one is displayed in Unity - unity3d

I'm trying to make a ui that has a video shown and after that video is done, it recommends 3 different videos, these videos are dependent on the video that was just watched.
Now did I manage to get the buttons to display different text with a list system.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using UnityEngine.UI;
// using UnityEngine.Video;
public class Recommendations : MonoBehaviour
{
public Button Rec1;
public Button Rec2;
public Button Rec3;
private int[] recommendedButtonValues;
private float[] recommendedSliderValues;
public List<DescriptionContainer> descriptionContainer = new List<DescriptionContainer>();
public List<ImageContainer> ImageContainer = new List<ImageContainer>();
public VideoplayerScript VideoPlayer;
public void SetRecommendationButtons(int buttonValue, float sliderValue) {
CheckRecommendations(buttonValue, sliderValue);
Rec1.GetComponentInChildren<TextMeshProUGUI>().text = descriptionContainer[recommendedButtonValues[0]].descriptions[(int)recommendedSliderValues[0]];
Rec2.GetComponentInChildren<TextMeshProUGUI>().text = descriptionContainer[recommendedButtonValues[1]].descriptions[(int)recommendedSliderValues[1]];
Rec3.GetComponentInChildren<TextMeshProUGUI>().text = descriptionContainer[recommendedButtonValues[2]].descriptions[(int)recommendedSliderValues[2]];
private void CheckRecommendations(int buttonValue, float sliderValue) {
if (buttonValue == 0) {
if (sliderValue == 0) {
recommendedButtonValues = new[] { 2, 0, 0 };
recommendedSliderValues = new[] { 0f, 2f, 4f };
//currentInterviewPhoto = ImageContainer[recommendedButtonValues].images[(int)recommendedSliderValues];
}
else if (sliderValue == 1) {
recommendedButtonValues = new[] { 1, 0, 3 };
recommendedSliderValues = new[] { 1f, 4f, 0f };
}
}
//this is repeated multiple times but I dont think its needed to show to get the point across, both for multiple button as slider values.
public void OnClickButton1() {
VideoPlayer.buttonValue = recommendedButtonValues[0];
VideoPlayer.sliderValue = recommendedSliderValues[0];
Debug.Log("Recommendation pressed for the following video, Button value: " + recommendedButtonValues[0] + ". Slider value: " + recommendedSliderValues[0]);
//repeated for all three buttons
[System.Serializable]
public class DescriptionContainer {
[TextArea(10, 10)]
public List<string> descriptions;
}
All this works but I'm now trying to do something similar with an images but I cant really get a list to work with an images. The thing I want to do is have 3 different images, depending on the
recommendedButtonValues[0,1 or 2] and the recommendedSliderValues[0,1 or 2] it decides which one to show. I've tried creating a list with textures like this:
[System.Serializable]
public class ImageContainer
{
public List<Texture> images;
}
public void WhatImag1e()
{
Image1 = ImageContainer[recommendedButtonValues[0]].images[(int)recommendedSliderValues[0]];
}
But I got the error that Unity cant convert UnityEngine.Texture to UnityEngine.UI.Image.
So my questions is what kind of list can I make which does work? Or is this not possible with a list and does I need to do.
I havent copied my entire code since that seems like an overkill but if I missed something I can show it. I'm using Unity version 2019.4.18f1 for this.

A UI.Image is a sprite container, it has a property 'sprite' where you can set sprites and it will change the texture of the widget to the sprite passed.
In your case, if you have textures, you need to change your UI.Image to UI.RawImage (change component in the editor), which accepts textures in its property 'texture'.

Related

Unity3D custom inspector not saving values correctly

I try to achieve a tiny custom editor in Unity3D which offers buttons to modify a value in two components of the same game object. So in the "PlanetGameObject" there is the MonoBehaviour "Planet" where the member "phase" should change (to one of the matching enum values) and also a SpriteRenderer where the member "color" changes to the matching value of the phase.
Basically it works as intended. When I click the buttons in the editor both values get assigned correctly but when the game starts the phase switches back to the initial value while the color of the sprite renderer stays with the new value. When I close the running game the incorrect value stays.
The setup:
Planet script:
using UnityEngine;
public class Planet : MonoBehaviour
{
public enum Phase { NEUTRAL, RED, GREEN, BLUE }
public Phase phase = Phase.NEUTRAL;
public static Color PhaseColor(Phase phase)
{
switch (phase)
{
case Phase.RED: return Color.red;
case Phase.GREEN: return Color.green;
case Phase.BLUE: return Color.blue;
default: return Color.white;
}
}
}
The custom editor script:
[![using UnityEngine;
using UnityEditor;
\[CustomEditor(typeof(Planet))\]
\[CanEditMultipleObjects\]
public class PlanetEditor : Editor
{
public override void OnInspectorGUI()
{
GUILayout.BeginHorizontal();
if (GUILayout.Button("neutral"))
{
foreach(Object t in targets)
{
Planet planet = (Planet)t;
planet.phase = Planet.Phase.NEUTRAL;
planet.gameObject.GetComponent<SpriteRenderer>().color = Planet.PhaseColor(planet.phase);
}
};
if (GUILayout.Button("red"))
{
foreach (Object t in targets)
{
Planet planet = (Planet)t;
planet.phase = Planet.Phase.RED;
planet.gameObject.GetComponent<SpriteRenderer>().color = Planet.PhaseColor(planet.phase);
}
};
if (GUILayout.Button("green"))
{
foreach (Object t in targets)
{
Planet planet = (Planet)t;
planet.phase = Planet.Phase.GREEN;
planet.gameObject.GetComponent<SpriteRenderer>().color = Planet.PhaseColor(planet.phase);
}
};
if (GUILayout.Button("blue"))
{
foreach (Object t in targets)
{
Planet planet = (Planet)t;
planet.phase = Planet.Phase.BLUE;
planet.gameObject.GetComponent<SpriteRenderer>().color = Planet.PhaseColor(planet.phase);
}
};
GUILayout.EndHorizontal();
DrawDefaultInspector();
}
}][1]][1]
Never go through the actual component references in Editor scripts.
This won't work with saving and undo/redo etc. because it doesn't mark the changes as dirty automatically.
Rather go through the SerializedPropertys which automatically handles marking stuff as dirty => saving. And Undo/Redo etc.
Could look somewhat like
using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(Planet))]
[CanEditMultipleObjects]
public class PlanetEditor : Editor
{
SerializedProperty _phase;
private void OnEnable()
{
// This links the _phase SerializedProperty to the according actual field
_phase = serializedObject.FindProperty("phase");
}
public override void OnInspectorGUI()
{
// This gets the current values from all serialized fields into the serialized "clone"
serilaizedObject.Update();
GUILayout.BeginHorizontal();
if (GUILayout.Button("neutral"))
{
// It is enough to do this without a loop as this will already affect all selected instances
phase.intValue = (int)Planet.Phase.NEUTRAL;
};
if (GUILayout.Button("red"))
{
phase.intValue = (int)Planet.Phase.RED;
};
if (GUILayout.Button("green"))
{
phase.intValue = (int)Planet.Phase.GREEN;
};
if (GUILayout.Button("blue"))
{
phase.intValue = (int)Planet.Phase.RED;
};
GUILayout.EndHorizontal();
// This writes the changed properties back into the actual instance(s)
serializedObject.ApplyModifiedProperties();
DrawDefaultInspector();
}
}
So far for saving the enum of this component itself.
Now the simpliest way for the renderer would be to let your class itself react to that change:
[RequireComponent(typeof(SpriteRenderer))]
public class Planet : MonoBehaviour
{
public enum Phase { NEUTRAL, RED, GREEN, BLUE }
public Phase phase = Phase.NEUTRAL;
// Will be called everytime a field in the Inspector is changed
private void OnValidate()
{
GetComponent<SpriteRenderer>().color = PhaseColor(phase);
}
public static Color PhaseColor(Phase phase)
{
switch (phase)
{
case Phase.RED: return Color.red;
case Phase.GREEN: return Color.green;
case Phase.BLUE: return Color.blue;
default: return Color.white;
}
}
}
As this also should handle the marking dirty and thereby saving correctly afaik.
Actually your editor becomes a bit redundant with this since this also already reacts if you change the enum field manually ;)
Note: Typed on smartphone but I hope the idea gets clear

How do i implement TMP in an unity script that normally supports unity text

I'm following a skillshare tutorial on how to make a card game, and in the tutorial normal unity text is used, but in for me the text turns out blurry. So i turned to using TextMeshPro to doing it because the text supports auto-resizing and is crisp.
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
// holds the refs to all the Text, Images on the card
public class OneCardManager : MonoBehaviour {
public CardAsset cardAsset;
public OneCardManager PreviewManager;
[Header("Text Component References")]
public Text ManaCostText;
public Text PowerText;
[Header("Image References")]
public Image CardBodyImage;
public Image CardFaceGlowImage;
public Image CardBackGlowImage;
void Awake()
{
if (cardAsset != null)
ReadCardFromAsset();
}
private bool canBePlayedNow = false;
public bool CanBePlayedNow
{
get
{
return canBePlayedNow;
}
set
{
canBePlayedNow = value;
CardFaceGlowImage.enabled = value;
}
}
public void ReadCardFromAsset()
{
// universal actions for any Card
// add mana cost
ManaCostText.text = cardAsset.ManaCost.ToString();
if (cardAsset.Power != 0)
{
// this is a creature
PowerText.text = cardAsset.Power.ToString();
}
if (PreviewManager != null)
{
// this is a card and not a preview
// Preview GameObject will have OneCardManager as well, but PreviewManager should be null there
PreviewManager.cardAsset = cardAsset;
PreviewManager.ReadCardFromAsset();
}
}
}
the mana text should be able to take in a TMP object but i don't know how to do that. help please!
Use the TMPro.TMP_Text instead of UnityEngine.UI.Text.

How to instantiate objects in a specified place in unity?

I have an AR app that displays objects when it detects a QR code. The way I do it is with an empty object called model caller that has an script that instantiates a model, this is the script:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Android;
using UnityEngine.Networking;
using UnityEngine.UI;
public class Load_Models: MonoBehaviour
{
// Start is called before the first frame update
[Serializable]
public class PlaceModel
{
public string Clave = "";
}
public GameObject[] model;
public string[] clave_modelos;
public string URL;
public GameObject ModeloUI;
public PlaceModel placeModel;
Dictionary<string, GameObject> strGO = new Dictionary<string, GameObject>();
public void Start()
{
StartCoroutine(GetRequest(URL));
for (int i = 0; i < model.Length; i++)
{
strGO.Add(clave_modelos[i], model[i]);
}
}
IEnumerator GetRequest(string uri)
{
using (UnityWebRequest webRequest = UnityWebRequest.Get(uri))
{
// Request and wait for the desired page.
yield return webRequest.SendWebRequest();
string jsonForm = uri;
if (webRequest.isNetworkError)
{
Debug.Log("Error loading");
}
else
{
try
{
PlaceModel model_1 = JsonUtility.FromJson<PlaceModel>(webRequest.downloadHandler.text);
Instantiate(strGO[model_1.Clave], new Vector3(0, 0, 0), Quaternion.identity, transform); //instantiates the model
Debug.Log("Loaded");
}
catch
{
Debug.Log("Error in connection");
}
}
}
}
}
and this is what happens when I detect more than 1 QR (it also happens with only one, just without the models "fusing" with each other):
Explanation: It should display 3 models, 1 simple street (the white block) and 2 "no model" 3d texts, but, the idea is for the models to appear "attached" (I don't know how to word it) to the QR code. And I tried to do it by having model caller as a child of the ImageTarget and with model caller being in the dead center of the imagetarget, also with new Vector3(0, 0, 0).
Is there a way to do this?
I know that I can do it by simply using the prefab by itself instead of a script, but I need for the models to change depending on a website (which I already did).
I'm using EasyAR 3 for this
If I got you right, try to change Instantiate line this way:
Instantiate(strGO[model_1.Clave], transform.position, Quaternion.identity, transform);
Also, maybe, you dont need to set parent transform, it depends from your implementation.

Is there any way to hide the "Object picker" of an EditorGUILayout.ObjectField in Unity Isnpector?

I'm just asking if there is any possibility to hide the "Object Picker" (The little knob/menu next to an ObjectField) in a custom Inspector. I have some cases where changes are disabled (DisableGroup) and I would like to also hide the knob while the content can not be changed anyway.
Also to make things easier for users I think about making the field higher (EditorGUIUtility.SingleLineHeight * 2) -> the picker gets stretched as well what looks kind of shitty ^^
example:
using UnityEditor;
using UnityEngine;
public class Bla : MonoBehaviour {
[CustomEditor(typeof(Bla))]
public class BlaEditor : Editor
{
private AudioClip _clip;
public override void OnInspectorGUI()
{
EditorGUI.BeginDisabledGroup(true);
// do some magic to hide the object picker
_clip = (AudioClip) EditorGUILayout.ObjectField("some label", _clip, typeof(AudioClip), false);
EditorGUI.EndDisabledGroup();
}
}
}
I want to stick with an ObjectField rather than a simple Label for two reasons:
Even on a disabled `ObjectField| the "ping" functionality is still working. (If you click on it, the according asset gets highlighted in the Hierarchy.) This is not the case obviously with a label.
The user should not get confused with completely different looking controls but I rather only want to remove some unnecessary clutter.
You might find a solution to hide the object picker by usage of stylesheets.
If all you want is just to display some reference, you can use a simple button basically styled as text field, adding an image and ping the object from code yourself.
using UnityEngine;
namespace Test
{
public class TestBehaviour : MonoBehaviour
{
[SerializeField] private bool _audioEnabled;
[SerializeField] private AudioClip _audioClip;
}
}
editor:
using System.Reflection;
using UnityEditor;
using UnityEditor.Experimental.UIElements;
using UnityEngine;
namespace Test
{
[CustomEditor(typeof(TestBehaviour))]
public class TestBehaviourEditor : Editor
{
private SerializedProperty _clipProp;
private SerializedProperty _audioEnabledProp;
private ObjectField m_ObjectField;
private const BindingFlags FIELD_BINDING_FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
private void OnEnable()
{
_clipProp = serializedObject.FindProperty("_audioClip");
_audioEnabledProp = serializedObject.FindProperty("_audioEnabled");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(_audioEnabledProp);
if(_audioEnabledProp.boolValue)
EditorGUILayout.PropertyField(_clipProp);
else
{
//TODO: calculate proper layout
var type = target.GetType().GetField(_clipProp.propertyPath, FIELD_BINDING_FLAGS).FieldType;
var clip = _clipProp.objectReferenceValue;
var guiContent = EditorGUIUtility.ObjectContent(clip, type);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Fake ObjectField Button");
var style = new GUIStyle("TextField");
style.fixedHeight = 16;
style.imagePosition = clip ? ImagePosition.ImageLeft : ImagePosition.TextOnly;
if (GUILayout.Button(guiContent, style ) && clip)
EditorGUIUtility.PingObject(clip);
EditorGUILayout.EndHorizontal();
}
serializedObject.ApplyModifiedProperties();
}
}
}
I've got another solution: Ignore the pick result of this object picker, and although the picker is still here and can show the picker window, pick up will not work.
(I still don't know how to hide this button and the pickerwindow, > <), and this answer was posted at unity answers as well.
Here is the code:
// Register another callback of this object field
myObjectField.RegisterValueChangedCallback(DefaultObjectFieldCallback);
// In this callback, is a trick
private void DefaultAssetFieldCallback(ChangeEvent<UnityEngine.Object> evt) {
// unregister the callback first
myObjectField.UnregisterValueChangedCallback(DefaultAssetFieldCallback);
// trick: set back to the old value
m_ConfigAssetField.value = evt.previousValue;
// register the callback again
myObjectField.RegisterValueChangedCallback(DefaultObjectFieldCallback);
}
I needed to do something similar and found a way to do this by stepping through the ObjectField in the UIToolkit Debugger. The type of the little object selector button is hidden, so we cant really work with the class itself.
This solution is using UIToolkit, so unfortunately it won't work with Unity Editor IMGUI, but hopefully it will be helpful to someone.
The Solution in easy steps:
Find out what the uss style class of the ObjectFieldSelector is.
Recursively search the children of the ObjectField for a VisualElement containing the uss style class.
Set visibility to false.
All done!
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
[CustomPropertyDrawer(typeof(MyClass))]
public class MyClassDrawer: PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
var field = new ObjectField(property.displayName);
field.objectType = typeof(MyClass);
var button = FindChild(field, "unity-object-field__selector");
button.visible = false;
return field;
}
private VisualElement FindChild(VisualElement parent, string ussClass)
{
foreach(var child in parent.Children())
{
if (child.ClassListContains(ussClass))
return child;
var subChild = FindChild(child, ussClass);
if (subChild != null)
return subChild;
}
return null;
}
}

How to display a name and score list for players using happyfuntimes and unity3d

This is related to happyfuntimes plugin if you have used it .
I am trying to make a game on it and stuck at a point of displaying score along with name to display on large screen while user is playing on his mobile.(i have already tried to display name and score on mobile screens have seen in sample do not need that ). Please suggest how can this be done if you have used happyfuntimes plugin.
I could see the HFTgamepad input having public GameObject player name which I am trying to access ,do I have to make array ?
public string playerName;
I am trying to put these name on array.
Displaying anything in unity is really normal unity issue and not special to happyfuntimes. Games display highscore lists, item lists, inventory lists, etc... A list of players is no different
Use the GUI system display text.
According to the docs if you want to generate UI dynamically you should make a prefab
So basically you'd make a UI hierarchy in the scene. When a player is added to the game Instantiate your name-score prefab, search for the UI hierarchy gameObject (do that at init time), then set the parent of the preafab you just instantiated so it's in the UI hierarchy.
Look up the UI.Text components that represent name and score and set their text properties.
There's various auto layout components to help layout your scores
When a player disconnects Destroy the prefab you instantiated above.
OR ...
Conversely you can use the old immediate mode GUI. You'd make a GameObject, give it a component that has a OnGUI function. Somewhere keep a list of players. Add players to the list as they connect and remove them from the list as they disconnect. In your OnGUI function loop over the list of players and call the GUI.Label function or similar to draw each name and score
Here's a hacked up example.
Assuming you have a PlayerScript script component that's on each player that has a public accessor PlayerName for getting the player's name then you can make a GameObject and put a script like this on it.
using UnityEngine;
using System.Collections.Generic;
public class ExamplePlayerListGUI : MonoBehaviour
{
static ExamplePlayerListGUI s_instance;
List<PlayerScript> m_players = new List<PlayerScript>();
static public ExamplePlayerListGUI GetInstance()
{
return s_instance;
}
void Awake()
{
if (s_instance != null)
{
Debug.LogError("there should only be one ExamplePlayerListGUI");
}
s_instance = this;
}
public void AddPlayer(PlayerScript bs)
{
m_players.Add(bs);
}
public void RemovePlayer(PlayerScript bs)
{
m_players.Remove(bs);
}
public void OnGUI()
{
Rect r = new Rect(0, 0, 100, m_players.Count * 20);
GUI.Box(r, "");
r.height = 20;
foreach(var player in m_players)
{
GUI.Label(r, player.PlayerName);
r.y += 20;
}
}
}
PlayerScript can then call ExamplePlayerListGUI.GetInstance().AddPlayer in its Start function and ExamplePlayerListGUI.GetInstance().RemovePlayer in it's OnDestroy function
public PlayerScript : MonoBehaviour
{
private string m_playerName;
...
public string PlayerName
{
get
{
return m_playerName;
}
}
void Start()
{
...
ExamplePlayerListGUI playerListGUI = ExamplePlayerListGUI.GetInstance();
// Check if it exists so we can use it with or without the list
if (playerListGUI != null)
{
playerListGUI.AddPlayer(this);
}
}
void OnDestroy()
{
...
ExamplePlayerListGUI playerListGUI = ExamplePlayerListGUI.GetInstance();
if (playerListGUI != null)
{
playerListGUI.RemovePlayer(this);
}
}
}