Sending a Boolean as a field to another script - c#

I saw this question asked some times but no one correlates to mine. I see people using the GetComponent() function but this one should work too.
First I receive a Bool value from a toggle button and assign it as a field of the object 'hydro':
hydroControl.cs
using UnityEngine;
using System.Collections;
using Assets.Code.PowerPlants;
public class hydroProwerControlPanel : MonoBehaviour {
private HydroElectric hydro;
public bool t1;
void Start ()
{
t1 = true;
}
public hydroProwerControlPanel (){
hydro = new HydroElectric();
}
public void turbine1State (bool t1) {
hydro.t1Bool = t1;
}
The I have the object where this bool change should be recognized and sent as an output, but it doesn't:
using System;
using UnityEngine;
namespace Assets.Code.PowerPlants
{
public class HydroElectric
{
public bool t1Bool;
float turbina1;
public float prod;
public HydroElectric ()
{
t1Bool = true;
prod = 0f;
}
public float ControlPanel ()
{
turbina1 = t1Bool ? 1.5F : 0;
prod = turbina1 ;
Debug.Log (prod);
return prod;
}
}
}
As requested, this is where the function ControlPanel() is called:
using System;
using UnityEngine;
using UnityEngine.UI;
using Assets.Code.PowerPlants;
namespace Assets.Code.Interfaces
{
public class PlayLevel1 : MonoBehaviour
{
private HydroElectric hydro;
public Text producao;
public PlayLevel1 ()
{
hydro = new HydroElectric();
}
public void OnGUI()
{
producao.text = hydro.ControlPanel().ToString();
}
}
}
Do you have any idea why does this Bool does not get updated? Any help appreciated.

PlayLevel1's Hyrdo and hydroProwerControlPanel's hydro object are two different instances. Change in one instance will not reflect the change in second instance both are independent instances .
The thing you want to achieve is only possible if you make HydroElectric static class with static members.
Hope it will help.

Related

Save current Scrollrect position Unity3D

I am new with unity and C#, I have question about how I save current scrollrect position.
Example : I am scrolling the view , and move to another scene and then back to previous scene but the scroll shows the previous position before I moved the scene, not resetting the scroll to default.
Unfortunately, what you want to make is not available ready-made, you have to make it yourself
first use Recyclable-Scroll-Rect
When scrolling to the bottom of the scroll, you have to save the id you sent to DemoCall via PlayerPrefs, then when you go to another scene and back again to the selected scene, call the scroll info from the point it left off, which is the id you saved
EDIT
After adding the Recyclable-Scroll-Rect, you can use this code
using System.Collections.Generic;
using UnityEngine;
using PolyAndCode.UI;
using System.Collections;
public struct ContactTsnif
{
public string id;
}
public class Objx
{
public string id;
}
public class RecyclTsnif : MonoBehaviour, IRecyclableScrollRectDataSource
{
[SerializeField]
RecyclableScrollRect _recycHat;
public GameObject RecyScrHat;
[SerializeField]
public int _dataLenHat;
public int beginning;
private List<ContactTsnif> _contactList = new List<ContactTsnif>();
public List<string> id = new List<string>();
void Start()
{
beginning = PlayerPrefebs.GetInt("Start", 5)// start with 5
GetHat();
}
public void GetHat()
{
_dataLenHat = 0;
_recycHat.DataSource = this;
InitDataHat();
RecyScrHat.GetComponent<RecyclableScrollRect>().Initialize();
}
public void InitDataHat()
{
if (_contactList != null) _contactList.Clear();
for (int i = beginning; i < _dataLenHat;)
{
ContactTsnif obj = new ContactTsnif();
obj.id = id[i];
i++;
_contactList.Add(obj);
}
}
#region DATA-SOURCE
public int GetItemCount()
{
return _contactList.Count;
}
public void SetCell(ICell cell, int index)
{
var item1 = cell as DemoTsnif;
item1.ConfigureCellSor(_contactList[index], index);
}
#endregion
}
Demo
using UnityEngine;
using System;
using System.Collections;
public class DemoTsnif : MonoBehaviour, ICell
{
private ContactTsnif _ContactInfo;
private int _cellIndex;
public int id;
public void GetData()
{
}
public void ConfigureCellSor(ContactTsnif contactInfo,int cellIndex)
{
_cellIndex = cellIndex;
_ContactInfo = contactInfo;
id = contactInfo.id;
GetData();
}
}
Do you tried read / write normalizedPosition?
You basically need to do two things:
You need to attach this script to the GameObject which contains the ScrollRect in order to persist the position:
using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems; // Required when using event data
using UnityEngine.UI;
public class DragNotify : MonoBehaviour, IEndDragHandler // required interface when using the OnEndDrag method.
{
//Do this when the user stops dragging this UI Element.
public void OnEndDrag(PointerEventData data)
{
PlayerPrefs.SetFloat("scrollX", this.GetComponent<ScrollRect>().normalizedPosition.x);
}
}
You also need to apply the normalizedPosition once you initialized the ScrollRect and after you applied the desired content:
this.transform.Find("Scroll View").GetComponent<ScrollRect>().normalizedPosition = new Vector2(PlayerPrefs.GetFloat("scrollX"), 0F);

Unity C# creating observable variables with delegates

What I'm trying to do:
A ScriptableObject class to hold a single variable that can be subscribed to in an observer pattern to receive notifications when the value changes.
My intent is to have things like a UI display update when whatever they display changes, without having to manually trigger an event on every change.
Additionally, I want my class to have three features:
Use try/catch in order to really decouple things and not make all listeners fail just because one did
Have the option to log stuff for debugging
Show the list of currently active observers in the inspector
I thought that's a few lines of code with Delegates, but it turns out nope, that simply doesn't work.
My first naive iteration was this:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
[CreateAssetMenu(fileName = "New Observable Float", menuName = "Observables/Float")]
public class ObservableFloat : ScriptableObject {
public event Action<float> get;
[SerializeField] private float m_Value;
public float Value {
get {
return m_Value;
}
set {
m_Value = value;
get?.Invoke(value);
}
}
}
My second iteration, which works functionally, but doesn't show me the list of observers in the inspector, was this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
[CreateAssetMenu(fileName = "New Observable Float", menuName = "Observables/Float")]
public class ObservableFloat : ScriptableObject {
[SerializeField] List<UnityAction<float>> listeners = new List<UnityAction<float>>();
[SerializeField] private float m_Value;
public float Value {
get {
return m_Value;
}
set {
m_Value = value;
foreach (UnityAction<float> action in listeners) {
action.Invoke(value);
}
}
}
public void AddListener(UnityAction<float> func) => listeners.Add(func);
public void RemoveListener(UnityAction<float> func) => listeners.Remove(func);
}
My third iteration, replacing UnityAction with UnityEvents, appears to work at first glance (the list shows up in the Inspector), but it never updates the list and it's always shown as empty, even though again functionally it works:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using Sirenix.OdinInspector;
[CreateAssetMenu(fileName = "New Observable Float", menuName = "Observables/Float")]
public class ObservableFloat : ScriptableObject {
public UnityEvent<float> listeners = new UnityEvent<float>();
[SerializeField] private float m_Value;
public float Value {
get {
return m_Value;
}
set {
m_Value = value;
listeners?.Invoke(value);
}
}
}
In general I think what you are looking for would be UnityEvent
[SerializeField] private UnityEvent<float> listeners;
public void AddListener(Action<float> action) => listeners.AddListener(action);
public void RemoveListener(Action<float> action) => listeners.RemoveListener(action);
[SerializeField] private float m_Value;
public float Value {
get {
return m_Value;
}
set {
m_Value = value;
listeners?.Invoke(value);
}
}
Unfortunately these will always only show the persistent listeners in the Inspector. There is no simple built-in way to also display runtime callbacks, and if you want to do this I guess there is no way around Reflection and/or a very complex special Inspector implementation.
You could e.g. store something like
using System.Reflection;
using System.Linq;
...
[Serializable]
public class ListenerInfo
{
public Action<float> action;
public string MethodName;
public string TypeName;
}
[SerializeField] private List<string> listenerInfos;
public void AddListener(Action<float> action)
{
listeners.AddListener(action);
var info = action.GetMethodInfo();
listenerInfos.Add(new ListenerInfo { action = action, MethodName = info.Name, TypeName = info.DeclaringType.Name });
}
public void RemoveListener (Action<float> action)
{
listeners.RemoveListener(action);
var info = var info = action.GetMethodInfo();
listenerInfos.RemoveAll(l => l.action == action);
}
Also see e.g. Action delegate. How to get the instance that call the method
I guess that would kinda be the closest you can get without really diving deep into Unity Editor scripting and even more reflection ^^
I've come up with a solution that works, though I'm not perfectly sure about it, so I posted it in CodeReview - https://codereview.stackexchange.com/questions/272241/unity3d-observable-variable
Here's the code (but check the above link for possible fixes/improvements). A huge thanks to #derHugo who pointed me in the right direction:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEditor;
using Sirenix.OdinInspector;
[CreateAssetMenu(fileName = "New Observable Float", menuName = "Observables/Float")]
public class ObservableFloat : ScriptableObject {
[System.Serializable]
public class Listener {
[DisplayAsString, HideInInspector] public int ID;
[DisplayAsString, HideLabel, HorizontalGroup] public string Observer;
[DisplayAsString, HideLabel, HorizontalGroup] public string Method;
[HideInInspector] public UnityAction<float> Callback;
public Listener(UnityAction<float> cb) {
ID = cb.GetHashCode();
Observer = cb.Target.ToString();
Method = cb.Method.ToString();
Callback = cb;
}
}
[Delayed]
[OnValueChanged("NotifyListeners")]
[SerializeField] private float m_Value;
public float Value {
get {
return m_Value;
}
set {
m_Value = value;
NotifyListeners();
}
}
[Tooltip("Log Invoke() calls")]
[SerializeField] bool Trace;
[Tooltip("Use try/catch around Invoke() calls so events continue to other listeners even if one fails")]
[SerializeField] bool CatchExceptions;
[ListDrawerSettings(DraggableItems = false, Expanded = true, ShowIndexLabels = false, ShowPaging = false, ShowItemCount = true)]
[SerializeField] List<Listener> listeners = new List<Listener>();
void Awake() {
// clear out whenever we start - just in case some observer doesn't properly remove himself
// maybe later I'll also add persistent listeners, but for now I don't see the use case
listeners = new List<Listener>();
}
void NotifyListeners() {
foreach (Listener listener in listeners) {
if (Trace) {
Debug.Log("invoking "+listener.Observer+" / "+listener.Method+ " / value = "+m_Value);
}
if (CatchExceptions) {
try {
listener.Callback.Invoke(m_Value);
} catch (System.Exception exception) {
Debug.LogException(exception, this);
}
} else {
listener.Callback.Invoke(m_Value);
}
}
}
public void AddListener(UnityAction<float> func) { listeners.Add(new Listener(func)); }
public void RemoveListener(UnityAction<float> func) { listeners.RemoveAll(l => l.ID == func.GetHashCode()); }
}
This works and gives me the features I wanted, not sure if it's a great solution, so I'll leave the question open for better answers.

Dictionary not read when using GetComponent<>() in Unity

I have three scripts inside GameObjects strucured as following:
GameObjectA
└ Script1
GameObjectB
└ Script2
└ Script3
Script3 has a Dictionary I need to recall in Script2:
public void SomeMethod()
{
Dictionary<string, int> someDictionary;
someDictionary = gameObject.GetComponent<Script3>().myDictionary;
//other stuff
}
If I call the method in Script2 in its Start method, it gets called and normally, and the Dictionary is read without problems.
However, during code execution, I'll sometimes need to call Script2 from Script1:
Public GameObject GameObjectB;
GameObjectB.GetComponent<Script2>().SomeMethod();
and, if I do so, when stepping through code during execution I realize the Dictionary SomeDictionary is empty.
Is this normal? I can give more information or the real snippets if needed, I find this behavior weird and I don't know if it acts regularly.
Edit: real codes below.
Script1, PauseMenu:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PauseMenu : MonoBehaviour
{
public GameObject tracksMenuContent;
public void TracksList()
{
tracksMenuContent.GetComponent<PopulateGrid>().Populate();
}
}
Script2, PopulateGrid:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PopulateGrid : MonoBehaviour
{
[SerializeField] private TileDescr _prefab;
public static PopulateGrid instance;
private void Awake()
{
Populate();
}
public void Populate()
{
Dictionary<string, TileSupply> tilesList;
tilesList = gameObject.GetComponent<TileDB>().tiles30;
TileDescr newObj; // Create GameObject instance
foreach (KeyValuePair<string, TileSupply> tile in tilesList)
{
Sprite tileSprite = Resources.Load<Sprite>("Tiles/tile" + tile.Key);
string tileText = "[" + tile.Key + "] " + tile.Value.available + "/" + tile.Value.total;
newObj = Instantiate(_prefab, transform);
newObj.Initialize(tileSprite, tileText);
}
}
}
Script3, TileDB:
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TileSupply
{
public int available;
public int total;
}
public class TileDB : MonoBehaviour
{
public Dictionary<string, TileSupply> tiles30 = new Dictionary<string, TileSupply>();
public void Awake()
{
tiles30.Add("1", new TileSupply() { available = 1, total = 1 });
//many more elements, all pretty similar
}
}
a possible solution is to use static:
public class TileDB : MonoBehaviour
{
public static Dictionary<string, TileSupply> tiles30 = new Dictionary<string, TileSupply>();
public void Awake()
{
tiles30.Add("1", new TileSupply() { available = 1, total = 1 });
}
}
after to use the tiles30 in other script, just call the dictionary like that
TileDB.tiles30

How do I solve CS7036 error between classes in unity?

These two blocks of code I assume are involved with the following error:
Assets\Scripts\Weapons.cs(8,12): error CS7036: There is no argument given that corresponds to the required formal parameter 'name' of 'Item.Item(string, int)'
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Item : MonoBehaviour
{
private string name;
private int quantity;
public Item(string name, int quantity)
{
this.name = name;
this.quantity = quantity;
}
public string Name
{
get { return name; }
set { name = value; }
}
public int Quantity
{
get { return quantity; }
set { quantity = value; }
}
public virtual void UseItem()
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Weapons : Item
{
public GameObject bullet;
public GameObject shotgunSpawn;
public GameObject shotgunSpawn2;
//public bool useGun;
public Inventory iw;
GUI_2D m;
public float Speed;
GameObject patroller;
GameObject guard;
public bool pellet;
public bool shotGun;
public bool barGun;
public PlayerController player;
// Start is called before the first frame update
void Start()
{
Speed = 5f;
player = GameObject.FindGameObjectWithTag("Player");
patroller = GameObject.FindGameObjectWithTag("Patroller");
guard = GameObject.FindGameObjectWithTag("Guard");
guard = GameObject.FindGameObjectWithTag("Shotgun");
pellet = false;
shotGun = false;
barGun = false;
}
void DestroyEnemy()
{
if (patroller)
{
patroller.SetActive(false);
}
if (guard)
{
patroller.SetActive(false);
}
}
private void OnTriggerEnter(Collider other)
{
if (iw.invItems.Count < 12)
{
if (other.gameObject.CompareTag("Player"))
{
pellet = true;
}
}
}
public override void UseItem()
{
if (pellet)
{
player.pellet = true;
player.shotGun = false;
player.barGun = false;
}
if (shotGun)
{
player.pellet = false;
player.shotGun = true;
player.barGun = false;
}
if (barGun)
{
player.pellet = false;
player.shotGun = false;
player.barGun = true;
}
base.UseItem();
}
}
I am not trying to change the item class since doing so will affect another class that depends on the item classes constructor. Unless there is another way to resolve the error by changing the item class. The error given is also affecting another class in the same way. I am hoping to solve the issue in both the weapon class and the other class from an answer here. Thank you in advance.
You shouldn't add constructors to MonoBehaviours, as the Unity Engine is responsible for instantiating them and will never pass your constructors arguments (you shouldn't be creating instances of MonoBehaviours, either).
Just remove your Item constructor and assign the values manually whenever needed. Other construction code should be done in Awake() or Start().

Why it's not playing all the conversations and not in the right order from start to end?

For testing I have two conversations at index 0 and 1.
And I want it to play the first conversation index 0 and then when it finish playing it to start playing the next conversation at index 1.
This is the script with the playing methods. The first should play a list/array of conversations one by one the second should play only a single conversation :
PlayConversations and PlayConversation.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
public class ConversationTrigger : MonoBehaviour
{
public List<Conversation> conversations = new List<Conversation>();
public static List<int> conversationsToPlay = new List<int>();
public bool conversationEnd;
public GameObject canvas;
public static int conversationIndex;
private DialogueManager dialoguemanager;
private string jsonPath;
public void InitJsonPath()
{
jsonPath = Application.persistentDataPath + "/" + "Json.txt";
}
private void Start()
{
conversationIndex = 0;
dialoguemanager = FindObjectOfType<DialogueManager>();
}
public IEnumerator PlayConversations()
{
canvas.SetActive(true);
conversationEnd = false;
var conversations = conversationsToPlay.ToArray(); // Copy the list
conversationsToPlay.Clear(); // Immediately clear the original list
for (int i = 0; i < conversations.Length; i++) // iterate over the array
{
// Now you also don't need to remove items anymore,
// since you already cleared the list
yield return StartCoroutine(PlayConversation(conversations[i]));
}
}
public IEnumerator PlayConversation(int index)
{
if (conversations.Count > 0 &&
conversations[index].Dialogues.Count > 0)
{
for (int i = 0; i < conversations[index].Dialogues.Count; i++)
{
if (dialoguemanager != null)
{
dialoguemanager.StartDialogue(conversations[index].Dialogues[i]);
}
while (DialogueManager.dialogueEnded == false)
{
yield return null;
}
}
conversationEnd = true;
conversationIndex = index;
canvas.SetActive(false);
Debug.Log("Conversation Ended");
}
}
public void SaveConversations()
{
string jsonTransform = JsonHelper.ToJson(conversations.ToArray(), true);
File.WriteAllText(jsonPath, jsonTransform);
}
public void LoadConversations()
{
string jsonTransform = File.ReadAllText(jsonPath);
conversations.Clear();
conversations.AddRange(JsonHelper.FromJson<Conversation>(jsonTransform));
}
}
For that I made another helper script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayConversations : MonoBehaviour
{
private static ConversationTrigger conversationTrigger;
private static PlayConversations instance;
private void Awake()
{
conversationTrigger = GetComponent<ConversationTrigger>();
instance = this;
}
public static void ConversationToPlay(int index)
{
ConversationTrigger.conversationsToPlay.Add(index);
instance.StartCoroutine(conversationTrigger.PlayConversations());
}
}
And a script for testing :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BeginningCutscene : MonoBehaviour
{
public DepthOfField dephOfField;
// Update is called once per frame
void Update()
{
if (dephOfField.dephOfFieldFinished == true)
{
PlayConversations.ConversationToPlay(0);
PlayConversations.ConversationToPlay(1);
}
}
}
But it's start playing first the conversation at index 1 then only part of conversation at index 0 and then start over again then ending.
I think your issue has to do with the way you implemented the ConversationsToPlay method. At each call of the method you start a new coroutine which in turn will call the PlayConversation method. What this means is that each call of ConversationToPlay will play the conversation at the index you passed and that is why they are overlapping.
The simplest solution I can think of is to move the start of the coroutine outside of the ConversationsToPlay method.
Something like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayConversations : MonoBehaviour
{
private static ConversationTrigger conversationTrigger;
private static PlayConversations instance;
private void Awake()
{
conversationTrigger = GetComponent<ConversationTrigger>();
instance = this;
}
public static void AddConversationToPlay(int index)
{
ConversationTrigger.conversationsToPlay.Add(index);
}
public static void StartPlayConversationsCoroutine()
{
instance.StartCoroutine(conversationTrigger.PlayConversations());
}
}
and the test script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BeginningCutscene : MonoBehaviour
{
public DepthOfField dephOfField;
// Update is called once per frame
void Update()
{
if (dephOfField.dephOfFieldFinished == true)
{
PlayConversations.AddConversationToPlay(0);
PlayConversations.AddConversationToPlay(1);
PlayConversations.StartPlayConversationsCoroutine();
}
}
}

Categories

Resources