I am a beginer and have a problem with coding Unity. I want to show and hide 3D text meshes using two arrow keys. one mesh at once. but it doesn't work as I thought. I think you will easily find out what problem is.
and one more question. It will be worked at mobile device and dozens of meshes are going to be used. I'm not sure this is the best way, so please tell me if there is better. (especially about performance and optimization issue) thanks to read.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TextmeshSelection : MonoBehaviour{
public List <GameObject> Textmeshes = new List<GameObject> ();
public int Num;
void Update() {
if (Input.GetKeyDown(KeyCode.RightArrow)) {
Num++;
Renderer rend = Textmeshes[Num].GetComponent<Renderer> ();
if (rend.enabled)
rend.enabled = false;
else
rend.enabled = true;
}
if (Input.GetKeyDown(KeyCode.LeftArrow)) {
Num--;
Renderer rend = Textmeshes[Num].GetComponent<Renderer> ();
if (rend.enabled)
rend.enabled = false;
else
rend.enabled = true;
}
if (Num < 0) {
Num = 0;
Renderer rend = Textmeshes[Num].GetComponent<Renderer> ();
if (rend.enabled)
rend.enabled = false;
else
rend.enabled = true;
}
}
}
You shouldn't call GetComponent every time in Update but instead initialize all of them once e.g. in a dictionary
// Dictionary to store the references to the Renderer components
// Makes it easier to find them later
// You could as well only use a List instead and rely on the index
Dictionary<GameObject, Renderer> renderers = new Dictionary<GameObject, Renderer>();
private void Awake()
{
// Get all references already at app start
foreach(var textMesh in Textmeshes)
{
renderers[textMesh] = textMesh.GetComponent<Renderer>();
// And hide all from beginning
renderers[textMesh].enabled = false;
}
// Then enable only the first one depending what your Num is at start
SanatizeNum();
SetRendererEnabled(Num, true);
}
Then I would extract your code from Update into multiple methods to make it cleaner, remove redundancy and make it easier to maintain.
private void Update()
{
if (Input.GetKeyDown(KeyCode.RightArrow))
{
GoToNext();
}
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
GoToLast();
}
}
private void GoToNext()
{
// Hide the current renderer
SetRendererEnabled(Num, false);
Num++;
SanatizeNum();
// Show the new renderer
SetRendererEnabled(Num, true);
}
private void GoToLast()
{
SetRendererEnabled(Num, false);
Num--;
SanatizeNum();
SetRendererEnabled(Num, true);
}
private void SanatizeNum()
{
Num = Mathf.Clamp(Num, 0, Textmeshes.Count -1);
}
private void SetRendererEnabled(int index, bool enabled)
{
renderers[Textmeshes[index]].enabled = enabled;
}
Related
I have a scene with multiple triggers and a Player with collider. My idea is to have one script to deal with all interactions between triggers and Player collider. I thought that using the same script to different GameObjects would be enough but this thing doesn't work properly. While working with one trigger I need to change one GameObject attached to this trigger but the other GameObjects with this script are changing as well.
One more detail about script is that I have two types of triggers: one is for GameObjects that need some input from user, some doesn't need it. I check it by checking two fields firstChoiceText and secondChoiceText. And if they are empty, the interaction with GameObject doesn't need any choice from the player. Otherwise, I show a plane with two option and change the text of this options using firstChoiceTextfield and secondChoiceTextfield.
So the main idea of script is that when the Player enter the trigger the script activates E key of keyboard. And if we press the E button and if this interaction wasn't done yet, we go further. We check the type of trigger and if we need to make a choice, we activate so called choiceMode of PlayerScript. PlayerScript is just a script for movements of the player, and we need choiceMode just to use W and S keys to select the choice. We make our choice and return to usual movement of the player. If we don't need to make a choice we just do some action and mark the trigger as isDone.
Everything works great with one trigger but when I use several triggers and GameObjects with InteractionScript, all this triggers are mixed up. While working with one trigger I accidentally change the other script. And I don't quite understand how to separate this scripts from each other.
Can anybody give me some piece of advice?
using UnityEngine;
using UnityEngine.UI;
public class InteractionScript : MonoBehaviour
{
[SerializeField]
private GameObject buttonE;
[SerializeField]
private GameObject choiceMenu;
[SerializeField]
private PlayerScript ps;
private int choiceNum = 0;
[SerializeField]
private Transform choicePoint;
[SerializeField]
private Text firstChoiceTextfield;
[SerializeField]
private Text secondChoiceTextfield;
[SerializeField]
private string firstChoiceText;
[SerializeField]
private string secondChoiceText;
[SerializeField]
private bool enteredMe;
[SerializeField]
private bool isDone;
void OnTriggerEnter2D() {
if (!isDone) {
enteredMe = true;
buttonE.SetActive(true);
Debug.Log("++");
}
}
void OnTriggerExit2D() {
if (!isDone) {
buttonE.SetActive(false);
enteredMe = false;
}
}
void Update() {
if (!isDone) {
if (Input.GetKeyDown(KeyCode.E) && buttonE.activeSelf && enteredMe) {
if (firstChoiceText != "" && secondChoiceText != "") {
choiceMenu.SetActive(true);
buttonE.SetActive(false);
ps.choiceMode = true;
Debug.Log("with choice");
} else {
Debug.Log("without choice");
//Action
buttonE.SetActive(false);
isDone = true;
}
}
}
if (ps.choiceMode) {
firstChoiceTextfield.text = firstChoiceText;
secondChoiceTextfield.text = secondChoiceText;
if (Input.GetKeyDown(KeyCode.W)) {
choiceNum += 1;
}
if (Input.GetKeyDown(KeyCode.S)) {
choiceNum -= 1;
}
if (choiceNum > 1) {
choiceNum = 0;
} else if (choiceNum < 0) {
choiceNum = 1;
}
if (choiceNum == 0) {
choicePoint.localPosition = new Vector3(0f, 24f, 0f);
if (Input.GetKeyDown(KeyCode.Return)) {
Debug.Log("choice1");
choiceMenu.SetActive(false);
isDone = true;
enteredMe = false;
ps.choiceMode = false;
}
} else if (choiceNum == 1) {
choicePoint.localPosition = new Vector3(0f, -21.5f, 0f);
if (Input.GetKeyDown(KeyCode.Return)) {
Debug.Log("choice2");
choiceMenu.SetActive(false);
isDone = true;
enteredMe = false;
ps.choiceMode = false;
}
}
}
}
}
I'm probably not doing this properly at all. I'm trying to make a script that will allow dropping and throwing of a weapon, and I'm trying to keep the script on the weapon for prefab.
void PrepareObject() {
Rigidbody gameObjectsRigidBody = Stick.AddComponent<Rigidbody>();
gameObjectsRigidBody.mass = 0.1f;
Debug.Log("added rigidbody to weapon");
}
public void OnThrow() {
anims.SetTrigger("isThrowing");
PrepareObject();
StartCoroutine(LetGo());
}
private IEnumerator LetGo() {
Debug.Log("let go of weapon");
yield return new WaitForSeconds(1f);
Hand.transform.DetachChildren();
beingThrown = true;
}
public void OnInteract() {
playerInRange = Physics.CheckSphere(Stick.transform.position, interactRange, layerPlayer);
Debug.Log("Pressed interact");
if (playerInRange && beingThrown) {
Debug.Log("attempted pickup");
Stick.transform.SetParent(HandTransform);
} else return;
beingThrown = false;
}
When using this code, I get OnInteract() while I'm holding the weapon, but as soon as I LetGo(), I no longer get OnInteract(). I am using the Unity Input System BroadcastMessage() on the parent.. I'm kind of lost on what to do, any suggestions?
Im working on an 2d local multiplayer platformer Game. In the game are obstacles (Spikes), when the player collides with them the player will die. I want the Players of the game to decide if they would like to enable or disable the Spikes (the link leads to an image that will help understanding my problem)by pressing a Key. I already wrote a script for that and added it to my dontdestroyOnLoad GameManager. So all my spikes I built are the same Prefabs. My idea was to disable the main Prefab from the Project folder to disable all the spikes in every scene, until you press a Key to reactivate them again. The Problem is, that only the texture itself in the Project Panel disables and not the Spikes Prefabs in the Hierarchy, because the prefabs turn into regular gameObjects. How can I fix this?
My Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//using System.Threading;
public class DisableSpikes : MonoBehaviour
{
[Header("Spikes")]
public KeyCode disableSpikes;
public float time;
public GameObject prefabSpikes;
public bool toggleSpikes = true;
[Header("Player Green")]
public KeyCode disableGreen;
public GameObject prefabGreen;
public bool toggleGreen = true;
[Header("Reset Score")]
public KeyCode resetScore;
// Start is called before the first frame update
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(disableSpikes) && toggleSpikes == true)
{
prefabSpikes.SetActive(false);
Debug.Log("Disable");
//Thread.Sleep(1000);
Invoke("SetFalse", time);
}
if (Input.GetKeyDown(disableSpikes) && toggleSpikes == false)
{
prefabSpikes.SetActive(true);
Debug.Log("Anable");
//Thread.Sleep(1000);
Invoke("SetTrue", time);
}
if (Input.GetKeyDown(disableGreen) && toggleGreen == true)
{
prefabGreen.SetActive(false);
Debug.Log("Disable");
//Thread.Sleep(1000);
Invoke("SetFalse", time);
}
if (Input.GetKeyDown(disableGreen) && toggleGreen == false)
{
prefabGreen.SetActive(true);
Debug.Log("Anable");
//Thread.Sleep(1000);
Invoke("SetTrue", time);
}
if (Input.GetKeyDown(resetScore))
{
ScoreScriptBlue.scoreValueBlue = 0;
ScoreScriptRed.scoreValueRed = 0;
ScoreScriptGreen.scoreValueGreen = 0;
RoundScript.scoreValueRound = 0;
TimeScript.scoreValueTime = 0;
}
}
public void SetFalse()
{
toggleGreen = false;
toggleSpikes = false;
}
public void SetTrue()
{
toggleGreen = true;
toggleSpikes = true;
}
}
void Update() {
if (Input.GetKeyDown(disableSpikes) && toggleSpikes == true){
// show
// renderer.enabled = true;
gameObject.GetComponent<Renderer>().enabled = true;
}
if (Input.GetKeyDown(disableSpikes) && toggleSpikes == false) {
// hide
// renderer.enabled = false;
gameObject.GetComponent<Renderer>().enabled = false;
}
}
I am very new to programming and Unity so please have patience. I am trying to disable a collider for a specified amount of time upon pressing a button (In this case, the button "s"). I have been trying to do this by using the "WaitForSeconds" command but I have never used it before and I don't know it works, but I tried anyway and as I expected it didn't work. I have to mention that the simulation does run with no errors but when I press "s" the collider doesn't disable at all. As I have mentioned I am very new to programming and Unity so I apologize if I don't understand some solutions you may suggest. Anyways here is the code
{
public bool IsFacingR = true;
public bool IsFacingL = false;
public Rigidbody2D RB;
public BoxCollider2D m_col;
void Update()
{
if (Input.GetKeyDown(KeyCode.D))
{
IsFacingR = true;
IsFacingL = false;
}
if (Input.GetKeyDown(KeyCode.A))
{
IsFacingR = false;
IsFacingL = true;
}
if (Input.GetKeyDown(KeyCode.S))
{
Slider();
StartCoroutine(SlideCol());
m_col.enabled = false;
}
}
void Slider()
{
if (IsFacingR == true)
{
RB.AddForce(Vector2.right * 9999f);
}
if (IsFacingL == true)
{
RB.AddForce(-Vector2.right * 9999f);
}
}
IEnumerator SlideCol()
{
yield return new WaitForSeconds(1);
m_col.enabled = true;
}
}
Well i tried your code in my project and it works fine. I can see collider disable for one second. Maybe there is someting that makes your collider always enable.
I have 26 images (GameObjects) where each image has a button.
If a button is clicked, it plays a sound and animation.
However, I have a problem. I have tried to prevent this, but if I perform a multi touch / stress test, some buttons cannot be clicked anymore. If I click slowly it works fine, but if I perform multiple clicks on a button then it will fail/can't be clicked anymore. Any idea why?
My source code :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using DG.Tweening;
[RequireComponent(typeof(AudioSource))]
public class z_abcLetter_Main : MonoBehaviour {
public GameObject InputData;
public List<GameObject> List_uGUI;
private List<Sprite> List_Sprite;
private List<AudioClip> List_AudioClip;
private int currentIndex;
private GameObject currentGameObject;
private GameObject parentGameObject;
// Use this for initialization
void Start () {
List_Sprite = InputData.GetComponent<z_abcLetter_InputData>().Daftar_element;
List_AudioClip = InputData.GetComponent<z_abcLetter_InputData>().Daftar_Suara;
int total_element = List_Sprite.Count;
for (int i = 0; i < total_element; i++)
{
List_uGUI[i].GetComponent<UnityEngine.UI.Image>().sprite = List_Sprite[i];
List_uGUI[i].GetComponent<UnityEngine.UI.Image>().preserveAspect = true;
}
}
public void onButtonClick()
{
currentIndex = int.Parse(EventSystem.current.currentSelectedGameObject.transform.parent.name.ToString());
currentGameObject = EventSystem.current.currentSelectedGameObject;
parentGameObject = currentGameObject.transform.parent.gameObject;
StartCoroutine(PlaySoundWithAnimation());
}
IEnumerator PlaySoundWithAnimation()
{
currentGameObject.GetComponent<UnityEngine.UI.Button>().enabled = false;
currentGameObject.GetComponent<UnityEngine.UI.Button>().interactable = false;
AudioSource audio = GetComponent<AudioSource>();
audio.clip = List_AudioClip[currentIndex];
audio.Play();
parentGameObject.transform.DOPunchScale(new Vector3(2, 2, 0), 1, 1, 1);
yield return new WaitForSeconds(1.5f);
currentGameObject.GetComponent<UnityEngine.UI.Button>().enabled = true;
currentGameObject.GetComponent<UnityEngine.UI.Button>().interactable = true;
}
// Update is called once per frame
void Update () {
}
}
Your code seems to behave as expected. But for fast multiple clicks, you need to reset your coroutine.
To do that, you need a reference to PlaySoundWithAnimation.
private IEnumerator coroutine;
void Start()
{
coroutine = PlaySoundWithAnimation();
}
public void onButtonClick()
{
StopCoroutine(coroutine);
// you also need to stop audio here
//...
StartCoroutine(coroutine);
}
see: https://docs.unity3d.com/ScriptReference/MonoBehaviour.StopCoroutine.html
The reason some buttons are not responding is because the coroutines are stacked and they're all waiting for yield return new WaitForSeconds(1.5f);
see: https://answers.unity.com/questions/309613/calling-startcoroutine-multiple-times-seems-to-sta.html
Note:
If you don't want to stop the clip, consider audio.clip.length before enabling the buttons.