I have an Inventory system working and when I picked up an item it would go into it but I'm not sure what I changed because the item won't be picked up anymore. I'm trying to everything I can but now the only time I can add an item to my inventory is through the start function where it was initialized.
Here is the code attached to the player
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class BasePlayer : MonoBehaviour {
private List<BaseStat> _playerStats = new List<BaseStat> ();
private List<BaseItem> _playerInventory = new List<BaseItem>();
void Start () {
BaseItem _item = new BaseItem ();
BaseItem _weapon = new BaseItem ();
_weapon.ItemType = BaseItem.ItemTypes.WEAPON;
BaseItem _potion = new BaseItem();
_potion.WeaponType = BaseItem.WeaponTypes.BOW;
_potion.ItemType = BaseItem.ItemTypes.POTION;
_playerInventory.Add (_item);
_playerInventory.Add (_weapon);
_playerInventory.Add (_potion);
}
public List<BaseItem> ReturnPlayerInventory() {
return _playerInventory;
}
}
Here is the code attached to the item
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Avalon : MonoBehaviour {
private List<BaseItem> playerInventory = new List<BaseItem>();
public GameObject Sword;
void Start () {
BasePlayer basePlayerScript = GameObject.FindGameObjectWithTag ("Player").GetComponent<BasePlayer> ();
playerInventory = basePlayerScript.ReturnPlayerInventory ();
}
void OnTriggerEnter2D(Collider2D col) {
if (col.CompareTag ("Player")) {
GreatSwordAvalon ();
Destroy (Sword.gameObject);
}
}
public void GreatSwordAvalon() {
BaseItem _avalon = new BaseItem ();
_avalon.ItemName = "GreatSword of Avalon";
_avalon.ItemDescription = "Strongest sword in the game";
_avalon.ItemType = BaseItem.ItemTypes.WEAPON;
_avalon.ItemValue = 999999;
_avalon.ItemStats.Add (new BaseStrength ());
_avalon.WeaponType = BaseItem.WeaponTypes.SWORD;
playerInventory.Add (_avalon);
}
}
Any help is greatly appreciated
I ended up solving the problem myself. In my Inventory Window script I have a function called AddItemsFromInventory that checks everything in the player inventory and adds it into the Inventory window.
The problem was that I had the call to the function in the start instead of the update so it constantly checks instead of just in the beginning.
Related
The question is very simple, I'm a beginner in unity and I'm having trouble creating two toggles for music and fx audio. At first I succeded with a simple script but the toggles encountered a NullReferenceException once the scene changed.
This is the AudioManager script:
using UnityEngine.Audio;
using System;
using UnityEngine;
using UnityEngine.UI;
[RequireComponent(typeof(Toggle))]
public class AudioManager : MonoBehaviour
{
public Sound[] sounds;
public static AudioManager instance;
private void Awake()
{
if (instance == null)
instance = this;
else
{
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
foreach (Sound s in sounds)
{
s.source = gameObject.AddComponent<AudioSource>();
s.source.clip = s.clip;
s.source.volume = s.volume;
s.source.pitch = s.pitch;
s.source.loop = s.loop;
}
}
private void Start()
{
Play("Music");
}
public void Play(string name)
{
Sound s = Array.Find(sounds, sound => sound.name == name);
s.source.Play();
}
}
do you have any suggestions about it?
Thanks in advance :)
What line is the NullReferenceException on? I don’t see anything wrong with your code as it is.
Awake doesnt work for second time.
Because it is singleton.
So you can create OnEnable method, and put that foreach to OnEnable method.
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.
I have been working on a dialogue system for my game and I was wondering if anyone knows how to keep the system between different scenes. I know you can use things such as Player Prefs but for one, I do not understand it and upon research, people do not generally recommend it for storing large complicated things. I managed to get close to doing so by using dontDestroy just as you would with a character, however, it did not work completely as the button to switch to the next line of text, of course, broke along with the singleton I created for my system. What would be the best way for me to go about this?
Here is all of my code just in case it is needed:
Making the scriptable object:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "New Dialogue", menuName = "Dialogues")]
public class Dialogue : ScriptableObject
{
[System.Serializable]
public class Info
{
public string myName;
public Sprite portrait;
[TextArea(4, 8)]
public string mytext;
}
[Header("Insert Dialogue Info Below")]
public Info[] dialogueInfoSection;
}
Main code for system (sigleton breaks here while switching scenes):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class MainDialogueManager : MonoBehaviour
{
public static MainDialogueManager instance;
private void Awake()
{
if(instance != null)
{
Debug.LogWarning("FIX THIS" + gameObject.name);
}
else
{
instance = this;
}
}
public GameObject DialogueBoX;
public Text dialogueNameofChar;
public Text characterSays;
public Image characterPortrait;
private float textDelay = 0.005f;
public Queue<Dialogue.Info> dialogueInfoSection = new Queue<Dialogue.Info>();
public void EnqueueDialogue(Dialogue db)
{
DialogueBoX.SetActive(true);
dialogueInfoSection.Clear();
foreach(Dialogue.Info info in db.dialogueInfoSection)
{
dialogueInfoSection.Enqueue(info);
}
DequeueDialogue();
}
public void DequeueDialogue()
{
if (dialogueInfoSection.Count==0)
{
ReachedEndOfDialogue();
return; /////
}
Dialogue.Info info = dialogueInfoSection.Dequeue();
dialogueNameofChar.text = info.myName;
characterSays.text = info.mytext;
characterPortrait.sprite = info.portrait;
StartCoroutine(TypeText(info));
}
IEnumerator TypeText(Dialogue.Info info)
{
characterSays.text= "";
foreach(char c in info.mytext.ToCharArray())
{
yield return new WaitForSeconds(textDelay);
characterSays.text += c;
yield return null;
}
}
public void ReachedEndOfDialogue()
{
DialogueBoX.SetActive(false);
}
}
Dialogue Activation:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MainDialogueActivation : MonoBehaviour
{
public Dialogue dialogue;
public void startActivationofDialogue()
{
MainDialogueManager.instance.EnqueueDialogue(dialogue);
}
private void Start()
{
startActivationofDialogue();
}
}
Go to next dialogue line:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MainDialogueButtons : MonoBehaviour
{
public void GoToNextDialogueLine()
{
MainDialogueManager.instance.DequeueDialogue();
}
}
How about something like this?
The idea is pretty similar to what you're doing, with a few tweaks:
I'm storing the active dialog in a scriptable object (DialogueSystem) so that it can persist between scenes. Each time I load a new scene, I check if there's an active dialog, and if I so I show the dialog popup in Start().
Whereas you remove the dialog section that you're currently showing to the player from the current dialog, I don't remove the current section until the player clicks to the next section. That's necessary because you may need to re-show the same section if you move to a new scene.
Make sure to create an instance of the DialogueSystem scriptable object and assign it to MainDialogueActivation and MainDialogManager
MainDialogActiviation has some testing code in it so you can hit a key to start a new dialog or switch between scenes.
MainDialogueActiviation.cs
using UnityEngine;
using UnityEngine.SceneManagement;
public class MainDialogueActivation : MonoBehaviour
{
public Dialogue dialogue;
// This scriptable object stores the active dialog so that you
// can persist it between scenes
public DialogueSystem dialogSystem;
private void Start()
{
// If we had an active dialog from the previous scene, resume that dialog
if (dialogSystem?.dialogInfoSections.Count > 0)
{
GetComponent<MainDialogueManager>().ShowDialog();
}
}
private void Update()
{
// Pressing D queues and shows a new dialog
if (Input.GetKeyDown(KeyCode.D))
{
GetComponent<MainDialogueManager>().EnqueueDialogue(this.dialogue);
}
// Pressing C ends the current dialog
if (Input.GetKeyDown(KeyCode.C))
{
this.dialogSystem.dialogInfoSections.Clear();
GetComponent<MainDialogueManager>().ReachedEndOfDialogue();
}
// Pressing S swaps between two scenes so you can see the dialog
// persisting
if (Input.GetKeyDown(KeyCode.S))
{
if (SceneManager.GetActiveScene().name == "Scene 1")
{
SceneManager.LoadScene("Scene 2");
}
else if (SceneManager.GetActiveScene().name == "Scene 2")
{
SceneManager.LoadScene("Scene 1");
}
}
}
}
MainDialogueManager.cs
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
public class MainDialogueManager : MonoBehaviour
{
// This scriptable object stores the active dialog
public DialogueSystem dialogSystem;
public GameObject DialogueBox;
public Text dialogueNameofChar;
public Text characterSays;
public Image characterPortrait;
private float textDelay = 0.005f;
// The game object for the dialog box that is instantiated in this
// scene
private GameObject dialogBoxGameObject;
/// <summary>
/// Shows the dialog window for the dialog that is in this object's
/// dialogSystem property.
/// </summary>
public void ShowDialog()
{
// Instantiate the dialog box prefab
this.dialogBoxGameObject = Instantiate(this.DialogueBox);
// I'd recommend putting a script on your "dialog box" prefab to
// handle this stuff, so that this script doesn't need to get a
// reference to each text element within the dialog prefab. But
// this is just a quick and dirty example for this answer
this.dialogueNameofChar = GameObject.Find("Character Name").GetComponent<Text>();
this.characterSays = GameObject.Find("Character Text").GetComponent<Text>();
this.characterPortrait = GameObject.Find("Character Image").GetComponent<Image>();
// If you have multiple response options, you'd wire them up here.
// Again; I recommend putting this into a script on your dialog box
GameObject.Find("Response Button 1").GetComponent<Button>().onClick.AddListener(ShowNextDialogSection);
GameObject.Find("Response Button 2").GetComponent<Button>().onClick.AddListener(ShowNextDialogSection);
ShowDialogSection(this.dialogSystem.dialogInfoSections.Peek());
}
/// <summary>
/// Puts a dialog into this object's dialogSystem property and
/// opens a dialog window that will show that dialog.
/// </summary>
public void EnqueueDialogue(Dialogue db)
{
foreach (Dialogue.Info info in db.dialogueInfoSection)
{
this.dialogSystem.dialogInfoSections.Enqueue(info);
}
ShowDialog();
}
/// <summary>
/// Removes the dialog section at the head of the dialog queue,
/// and shows the following dialog statement to the player. This
/// is a difference in the overall logic, because now the dialog
/// section at the head of the queue is the dialog that's currently
/// being show, rather than the previous one that was shown
/// </summary>
public void ShowNextDialogSection()
{
this.dialogSystem.dialogInfoSections.Dequeue();
if (this.dialogSystem.dialogInfoSections.Count == 0)
{
ReachedEndOfDialogue();
return;
}
Dialogue.Info dialogSection = this.dialogSystem.dialogInfoSections.Peek();
ShowDialogSection(dialogSection);
}
/// <summary>
/// Shows the specified dialog statement to the player.
/// </summary>
public void ShowDialogSection(Dialogue.Info dialogSection)
{
dialogueNameofChar.text = dialogSection.myName;
characterSays.text = dialogSection.mytext;
characterPortrait.sprite = dialogSection.portrait;
StartCoroutine(TypeText(dialogSection));
}
IEnumerator TypeText(Dialogue.Info info)
{
characterSays.text = "";
foreach (char c in info.mytext.ToCharArray())
{
yield return new WaitForSeconds(textDelay);
characterSays.text += c;
yield return null;
}
}
public void ReachedEndOfDialogue()
{
// Destroy the dialog box
Destroy(this.dialogBoxGameObject);
}
}
DialogSystem.cs
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "Dialogues/Dialog System")]
public class DialogueSystem : ScriptableObject
{
public Queue<Dialogue.Info> dialogInfoSections = new Queue<Dialogue.Info>();
}
Here's what my dialog box prefab looks like
Every scene needs an object (presumably a prefab to make it easy to add to every scene) that has MainDialogActiviation and MainDialogManager on it. Mine looks like this:
This might be a bit of an unpopular opinion but using Singleton's are fine. It's just that MonoBehaviour singletons are tricky, you can use Object.DontDestroyOnLoad(instance). But things get ugly because it doesn't get destroyed when the scene changes (good) but if you go back to the scene it will load another one (bad). There's a few ways to get around that like having the object destroy itself if there's already an instance or having a subscene.
I would suggest not using MonoBehaviour singletons and use ScriptableObject singletons. You can lazy instantiate by putting the asset in a resource folder and use Resource.Load like this.
public class ScriptableSingleton<T> : ScriptableObject where T : ScriptableSingleton<T> {
private static string ResourcePath {
get {
return typeof(T).Name;
}
}
public static T Instance {
get {
if (instance == null) {
instance = Resources.Load(ResourcePath) as T;
}
return instance;
}
}
private static T instance;
}
With this code you create a Singleton class say DialogueManager you create a DialogueManager.asset for it and put it in a "Resources" folder.
I have been following this great tutorial:
https://www.youtube.com/watch?v=uxrKE2VKvmc
and at some point it does this for each target, it creates a gameObject ?
If you look at the code here:
private void SetupTargets(List<TrackableBehaviour> allTargets)
{
Debug.Log("Listing all Targets names:");
foreach (TrackableBehaviour target in allTargets)
{
Debug.Log("Target's name:" + target.TrackableName);
target.gameObject.transform.parent = transform;
target.gameObject.name = target.TrackableName;
target.gameObject.AddComponent<PlaneManager>();
Debug.Log(target.TrackableName + " created!");
}
}
why this line of code ?
target.gameObject.transform.parent = transform;
If I comment it out it still works fine..
The full class code is below:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Vuforia;
using System.Linq;
public class TargetManager : MonoBehaviour
{
public String mStartingMarkerDatabaseName = "";
private List<TrackableBehaviour> mAllTargets = new List<TrackableBehaviour>();
private void Awake()
{
VuforiaARController.Instance.RegisterVuforiaStartedCallback(onVuforiaStarted);
}
private void onDestroy()
{
VuforiaARController.Instance.UnregisterVuforiaStartedCallback(onVuforiaStarted);
}
private void onVuforiaStarted()
{
LoadDatabase(mStartingMarkerDatabaseName);
mAllTargets = GetTargets();
SetupTargets(mAllTargets);
}
private void LoadDatabase(string name)
{
ObjectTracker objectTracker = TrackerManager.Instance.GetTracker<ObjectTracker>();
objectTracker.Stop();
if (DataSet.Exists(name))
{
DataSet dataSet = objectTracker.CreateDataSet();
dataSet.Load(name);
objectTracker.ActivateDataSet(dataSet);
}
objectTracker.Start();
}
private List<TrackableBehaviour> GetTargets()
{
List<TrackableBehaviour> allTrackables = new List<TrackableBehaviour>();
allTrackables = TrackerManager.Instance.GetStateManager().GetTrackableBehaviours().ToList();
return allTrackables;
}
private void SetupTargets(List<TrackableBehaviour> allTargets)
{
Debug.Log("Listing all Targets names:");
foreach (TrackableBehaviour target in allTargets)
{
Debug.Log("Target's name:" + target.TrackableName);
target.gameObject.transform.parent = transform;
target.gameObject.name = target.TrackableName;
target.gameObject.AddComponent<PlaneManager>();
Debug.Log(target.TrackableName + " created!");
}
}
}
target.gameObject.transform.parent = transform;
That code means that the target gameObject will be a child of whatever gameObject the TargetManager script is attached to.
As derHugo pointed out in the comments, it's redundant and can just be written as
target.transform.parent = transform;
I want focus input fields using tab button. I have found some code for focusing input fields but that one not satisfied my requirement. In my application one page design like number of input field in both horizontal and vertical formate. In my page have 3 rows of input field and each row have 3 input fields. my requirement is when user click on tab button focus next input field it's working but when reach end of the row, how can I focus next row contain input field. Please suggest any idea. Thank you. You can find my sample page design in below.
Here is the code I have tried.
Selectable next = system.currentSelectedGameObject.GetComponent<Selectable().FindSelectableOnRight();
if (next != null)
{
InputField inputfield = next.GetComponent<InputField>();
if (inputfield != null)
{
inputfield.OnPointerClick(new PointerEventData(system));
system.SetSelectedGameObject(next.gameObject, new BaseEventData(system));
}
}
I had implemented the a similar function by NGUI, you can make some modify if using UGUI. The idea is set the nextInput manually by public variable
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIInputTab : MonoBehaviour {
UIInput thisInput;
public GameObject nextInput;
void Start () {
thisInput = transform.GetComponent<UIInput>();
}
void Update () {
if (thisInput.isSelected)
{
if (nextInput != null && Input.GetKeyDown(KeyCode.Tab))
{
UICamera.selectedObject = nextInput;
}
}
}
}
Update
: Dynamic generate random number InputField and assign the next InputField.
InputfieldTest.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class InputfieldTest : MonoBehaviour {
public GameObject inputFieldPrefab;
public GameObject panel;
private GameObject lastInput;
EventSystem m_EventSystem;
void Start () {
m_EventSystem = EventSystem.current;
for(int i = 0; i < Random.Range(5,10);i++){
GameObject column = new GameObject();
column.transform.parent = panel.transform;
column.name = "Column" + i;
for (int j = 0; j < Random.Range(2, 8);j++){
GameObject input = Instantiate(inputFieldPrefab);
input.transform.parent = column.transform;
input.GetComponent<RectTransform>().position = new Vector3(300+ 200 * j, 300+ 200 * i, 0);
input.name = "InputField" + i + "-" + j;
// set nextInput
if(lastInput != null) {
lastInput.GetComponent<InputTabControl>().nextInput = input;
}
lastInput = input;
}
}
}
void Update () {
GameObject currentSelect = m_EventSystem.currentSelectedGameObject;
if (currentSelect != null)
{
print(currentSelect.name);
GameObject nextInput = currentSelect.GetComponent<InputTabControl>().nextInput;
if (nextInput != null && Input.GetKeyDown(KeyCode.Tab))
{
InputField inputfield = nextInput.GetComponent<InputField>();
if (inputfield != null)
{
inputfield.OnPointerClick(new PointerEventData(m_EventSystem));
m_EventSystem.SetSelectedGameObject(nextInput.gameObject, new BaseEventData(m_EventSystem));
}
}
}
}
}
InputTabControl.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class InputTabControl : MonoBehaviour {
public GameObject nextInput;
}
Result:
Here's my solution, pretty similar.
public class TabToNextController : MonoBehaviour {
public InputField nextField;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (GetComponent<InputField> ().isFocused && Input.GetKeyDown (KeyCode.Tab)) {
nextField.ActivateInputField ();
}
}
Just add this script to the item you want to be tabbed from. Then in the GUI drop in the inputfield you want to be tabbed to as the "nextField".
I recently ran into this problem and it drove me crazy to think there was no tabs feature. I had never noticed it before. A simple solution that seems to work so far is to use the Selectable class that the UI elements inherit from.
I tested this with the most common types (for me) - InputField, Buttons and drop down lists.
It tabs to each of them as expected
use spacebar to click the button
use spacebar to open the drop down
If you want full web style controls (eg arrow keys) to invoke the drop down, you need to write that code.
public class TabToNextController : MonoBehaviour, IUpdateSelectedHandler
{
public Selectable nextField;
public void OnUpdateSelected(BaseEventData data)
{
if (Input.GetKeyDown(KeyCode.Tab))
nextField.Select();
}
}
Cheers
This is what worked for me:
public Selectable next; //if you want the cursor to start at a specific field
//you can set this in the inspector to any input field
private EventSystem system;
private int tabIndex; //to keep track of cursor
void Start()
{
system = EventSystem.current;// EventSystemManager.currentSystem;
//if you don't want to set next in the inspector, you can do it here
//next = Selectable.allSelectables[0];
tabIndex = 0;
next.Select(); //make sure to do this!
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Tab))
{
if (tabIndex >= Selectable.allSelectables.Count)
{
next = Selectable.allSelectables[0];
tabIndex = 0;
}
else
{
//if nothing is selected then system.currentSelectedGameObject
//will throw a nullPointer Exception
next = system.currentSelectedGameObject.GetComponent<Selectable>()
.FindSelectableOnDown();
tabIndex++;
}
Debug.Log(next);
if (next != null)
{
InputField inputfield = next.GetComponent<InputField>();
if (inputfield != null)
inputfield.OnPointerClick(new PointerEventData(system)); //if it's an input field, also set the text caret
system.SetSelectedGameObject(next.gameObject, new BaseEventData(system));
}
}
}