I'm hoping this might be something simple I'm missing.
I have a ScriptableObject script that looks like this:
using UnityEngine;
using System.Collections;
[CreateAssetMenu]
public class Item: ScriptableObject
{
public string iname;
public int iindex;
public Sprite sprite;
public GameObject itemObject;
public int value;
public string description;
public itemType itemtype;
public enum itemType
{
Consumable,
Equippable,
}
}
This works great in the editor, but if I publish to Android or Windows any script that references the ScriptableObject, it does not work. What am I missing?
For example the following block of code does not seem to execute at all:
for (int i = 0; i < 3; i++)
{
int lootnum = Random.Range(0, 4);
slot1 = itemdb[lootnum];
tlist[i] = itemdb[lootnum];
slotlist[i].transform.GetChild(0).GetComponent<Image>().sprite = itemdb[lootnum].sprite;
slotlist[i].transform.GetChild(0).GetComponent<Image>().enabled = true;
}
Those lists in the code are of the type Item defined in the above script. I'm not sure how to debug this as I get no errors or warnings in the editor.
Here is the script which populates the inventory. There's a bit of junk in there but it definitley works fine pressing play in the editor. Just not on build.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
public class Inventory : MonoBehaviour {
public int invSize;
public Item slot1;
public Item tslot1;
public Item tslot2;
public Item tslot3;
public GameObject t1;
public GameObject t2;
public GameObject t3;
public Sprite itemsprite;
public List<Item> itemdb = new List<Item>();
public List<Item> items = new List<Item>();
public List<Item> tlist = new List<Item>();
public Text stext;
public Text description;
public Item selectItem;
public GameObject selectSlot;
public Object test2;
public List<GameObject> slotlist = new List<GameObject>();
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public void addItem(Item itemToAdd)
{
//items.Add(itemdb[0]);
for (int i = 0; i < 5; i++)
{
if (items[i] == null)
{
items[i] = itemToAdd;
itemsprite = itemToAdd.sprite;
return;
}
}
}
public void GenTreasure()
{
for (int i = 0; i < 3; i++)
{
int lootnum = Random.Range(0, 4);
slot1 = itemdb[lootnum];
tlist[i] = itemdb[lootnum];
slotlist[i].transform.GetChild(0).GetComponent<Image>().sprite = itemdb[lootnum].sprite;
slotlist[i].transform.GetChild(0).GetComponent<Image>().enabled = true;
}
}
public void Uptext(int indexx)
{
stext.text = tlist[indexx].iname;
selectItem = tlist[indexx];
selectSlot = slotlist[indexx];
description.text = selectItem.description;
}
public void Take(int index)
{
//items.Add(selectItem);
for (int i = 0; i < invSize; i++)
{
if (items[i] == null)
{
items[i] = selectItem;
// itemsprite = itemToAdd.sprite;
selectItem = null;
// tlist[i] = null;
// slotlist[i].transform.GetChild(0).GetComponent<Image>().sprite = null;
selectSlot.transform.GetChild(0).GetComponent<Image>().enabled = false;
return;
}
}
}
}
If the script is not in the scene, how does it get loaded? Is it using an asset bundle? If that is the case, then it's possible that the class is being stripped from the build. You can include the class in your link.xml to make sure it's included. Another option is to simply reference the script in an included scene anywhere.
Related
I want to create public Scene[] levels; and manually assign my levels to the array, then loop through it and generate level selection buttons, but it won't show up in the inspector.
Is there any workaround?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class True : MonoBehaviour
{
// Start is called before the first frame update
public static int money;
[SerializeField]
public SceneManager[] scenes;
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void nextscencefirst()
{
SceneManager.LoadScene("Level2");
money++;
}
}
SceneManager is a built-in static class to control scenes during runtime. We can go to any scene by calling LoadScene method from that class. Also SceneManager cannot be seen in Inspector since it cannot be serialized class.
A reminder: Your scenes should be listed on build settings for these to work.
There are three ways to do what you want to do:
Method 1. Create a string list that holds names of scenes
public List<string> sceneNameList = new List<string>;
Method 2. Create a list of indices of scenes that added in build settings.
public List<int> sceneBuildIndexList = new List<int>
If the names of scenes are long or somehow difficult or you want to use sceneBuildIndex value it would be good to create a class and reach scenes from it
[System.Serializable]
public class SceneData
{
public int sceneBuildIndex;
public string sceneKey;
//public void LoadScene() //Maybe
}
public List<SceneData> sceneDataList = new List<SceneData>();
public SceneData GetSceneData(string key)
{
for (int i = 0; i < sceneDataList.Count; i++)
{
if (sceneDataList[i].sceneKey == key)
{
return sceneDataList[i];
}
}
return null;
}
Method 3: In Unity most classes are created from UnityEngine.Object, so it may let you assign scene from the Inspector.
[System.Serializable]
public class SceneData
{
public UnityEngine.Object scene;
public string key;
public void LoadScene()
{
if (scene == null)
{
Debug.LogError("Scene is not assigned");
return;
}
string pathToScene = AssetDatabase.GetAssetPath(scene);
SceneManager.LoadScene(pathToScene); //If the scene is not set in BuildSettings it will throw an error.
}
}
public List<SceneData> sceneDataList = new List<SceneData>();
public SceneData GetSceneData(string key)
{
for (int i = 0; i < sceneDataList.Count; i++)
{
if (sceneDataList[i].key == key)
{
return sceneDataList[i];
}
}
return null;
}
private void Awake()
{
SceneData data = GetSceneData("Level1");
if (data != null)
{
data.LoadScene();
}
}
I am super new to C#, so apologies if this is a simple question or has already been answered. I've been looking at other SO questions, trying the exact methods they use, and am still having no luck.
In Unity, I have an Inventory Object class that I can use to create Inventories in my game:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "New Inventory", menuName = "Inventory System/Inventory")]
public class InventoryObject : ScriptableObject
{
public List<InventorySlot> Container = new List<InventorySlot>();
public void AddItem(ItemObject newItem, int itemAmount)
{
bool inventoryHasItem = false;
for (int i = 0; i < Container.Count; i++)
{
if (CurrentSlotHasItem(Container[i], newItem)) {
if (Container.FindAll((InventorySlot currentSlot) => CurrentSlotHasItem(currentSlot, newItem)).Count < newItem.maxStackSize)
{
Container[i].AddAmount(itemAmount);
inventoryHasItem = true;
break;
}
}
}
if (!inventoryHasItem)
{
Container.Add(new InventorySlot(newItem, itemAmount));
}
}
private bool CurrentSlotHasItem(InventorySlot currentSlot, ItemObject item)
{
return currentSlot.item == item;
}
}
[System.Serializable]
public class InventorySlot
{
public ItemObject item;
public int amount;
public InventorySlot(ItemObject _item, int _amount)
{
item = _item;
amount = _amount;
}
public void AddAmount(int value)
{
amount += value;
}
}
This works great, except for this line:
Container.FindAll((InventorySlot currentSlot) => CurrentSlotHasItem(currentSlot, newItem)).Count < newItem.maxStackSize
For some reason, no matter what I use for the findAll() predicate, I always get the same amount in my inspector - 1. Which means that .Count never goes above 1, and I can go way above my ItemObject.maxStackSize.
This is an example ItemObject class I have:
using UnityEngine;
[CreateAssetMenu(fileName = "New Food Object", menuName = "Inventory System/Items/Food")]
public class FoodObject : ItemObject
{
public int healthAmount;
private void Awake() {
type = ItemType.Food;
maxStackSize = 25;
}
}
This is also my Player script that adds the items to the inventory. It just adds them based off of OnTriggerEnter.
public class Player : MonoBehaviour
{
public InventoryObject inventory;
private void OnTriggerEnter(Collider other)
{
var item = other.GetComponent<Item>();
if (item)
{
inventory.AddItem(item.item, 1);
Destroy(other.gameObject);
}
}
}
Here's some screenshots of my Unity console/inspector, with these two lines added to my InventoryObject class. You can see that .Count never going above 1.
if (CurrentSlotHasItem(Container[i], newItem)) {
Debug.Log(Container.FindAll((InventorySlot currentSlot) => CurrentSlotHasItem(currentSlot, newItem)).Count);
Debug.Log(Container[i].amount);
// rest of class
I don't know what I was thinking here. All I needed to do to get maxStackSize to work was changing the logic inside of AddItem:
for (int i = 0; i < Container.Count; i++)
{
if (Container[i].item.id == newItem.id) {
if (Container[i].amount < newItem.maxStackSize)
{
Container[i].AddAmount(itemAmount);
inventoryHasItem = true;
break;
}
}
}
It just compares Container[i].amount to newItem.maxStackSize. If it's under, it will stack the item. Otherwise, it creates a new item in the inventory.
There might be similar questions to this one but I couldn't find any useful.
I have a simple scrips which has a list of classes that contains audio clips, id, and event system individually for each one of those elements.
I need to make that audio clip fields to list and make them play by order until the last one then stop and fire event.
Here's my code for only one audio clip slot:
(Also i tried audio clip array but its just playing the first one on the audio clip array)
public class MainAudioManager : MonoBehaviour {
public List<soundInfo> currentsoundinfoList = new List<soundInfo>();
AudioSource audioSource;
soundInfo curentSoundInfo;
float audioLenght;
void Start () {
audioSource = gameObject.GetComponent<AudioSource> ();
}
public void SetAudioToPlay (int ID) {
for (int i = 0; i < currentsoundinfoList.Count; i++) {
if (currentsoundinfoList [i].ID == ID) {
curentSoundInfo = currentsoundinfoList[i];
audioSource.clip = curentSoundInfo.clipToPlay;
audioSource.Play ();
audioLenght = audioSource.clip.length;
StartCoroutine ("waitnigTillEbd");
return;
}
}
}
IEnumerator waitnigTillEbd () {
yield return new WaitForSeconds (audioLenght + 1f);
curentSoundInfo.eventOnSound.Invoke ();
}
[System.Serializable]
public class soundInfo {
public string name;
public int ID;
public AudioClip clipToPlay;
public UnityEvent eventOnSound;
}
}
alright i found the solution by my self. here is the final result for creating list of sound with event onstop attached to each one of them individually and also playable by ID from another event and classes ,for anyone who need it
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
[RequireComponent(typeof(AudioSource))]
public class MainAudioManager : MonoBehaviour {
public List<soundInfo> soundList = new List<soundInfo>();
AudioSource audioSource;
soundInfo curentSoundInfo;
float audioLenght;
void Start () {
audioSource = gameObject.GetComponent<AudioSource> ();
}
public void SetAudioToPlay (int ID) {
for (int i = 0; i < soundList.Count; i++) {
if (soundList [i].ID == ID) {
curentSoundInfo = soundList [i];
StartCoroutine(playSequencely());
return;
}
}
}
IEnumerator playSequencely () {
yield return null;
for (int cnt = 0; cnt < curentSoundInfo.clipsToPlay.Length; cnt++) {
audioSource.clip = curentSoundInfo.clipsToPlay [cnt];
audioSource.Play ();
while (audioSource.isPlaying) {
yield return null;
}
}
//Debug.Log ("Last Audio Is Playing");
curentSoundInfo.onStop.Invoke ();
}
}
[System.Serializable]
public class soundInfo {
public string name;
public int ID;
[TextArea (2, 8)] public string About;
public AudioClip[] clipsToPlay;
//public float delayBetween;
public UnityEvent onStop;
}
In the SaveTransform method I'm saving all the changes to the hard disk:
PlayerPrefs.Save();
But then when using a break point in the LoadTransform method I see that the parent/s are null.
loadedTransforms[i].parent = savedTransforms[i].parent;
savedTransforms[i].parent is null.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine.SceneManagement;
public static class TransformSaver
{
[System.Serializable]
public class TransformInfo
{
public string sceneName;
public string name;
public Transform parent;
public Vector3 pos;
public Quaternion rot;
public Vector3 scale;
}
//Save Transform
public static void SaveTransform(Transform[] tranformToSave)
{
TransformInfo[] trnfrm = new TransformInfo[tranformToSave.Length];
for (int i = 0; i < trnfrm.Length; i++)
{
trnfrm[i] = new TransformInfo();
trnfrm[i].sceneName = tranformToSave[i].gameObject.scene.name;
trnfrm[i].name = tranformToSave[i].name;
trnfrm[i].parent = tranformToSave[i].parent;
trnfrm[i].pos = tranformToSave[i].localPosition;
trnfrm[i].rot = tranformToSave[i].localRotation;
trnfrm[i].scale = tranformToSave[i].localScale;
}
string jsonTransform = JsonHelper.ToJson(trnfrm, true);
PlayerPrefs.SetString("transform", jsonTransform);
PlayerPrefs.Save();
}
//Load Transform
public static Transform[] LoadTransform()
{
string jsonTransform = PlayerPrefs.GetString("transform");
if (jsonTransform == null)
{
return null;
}
TransformInfo[] savedTransforms = JsonHelper.FromJson<TransformInfo>(jsonTransform);
GameObject[] gameObjects = new GameObject[savedTransforms.Length];
Transform[] loadedTransforms = new Transform[savedTransforms.Length];
for (int i = 0; i < gameObjects.Length; i++)
{
SceneManager.SetActiveScene(SceneManager.GetSceneByName(savedTransforms[i].sceneName));
gameObjects[i] = new GameObject();
loadedTransforms[i] = gameObjects[i].transform;
loadedTransforms[i].name = savedTransforms[i].name;
loadedTransforms[i].parent = savedTransforms[i].parent;
loadedTransforms[i].localPosition = savedTransforms[i].pos;
loadedTransforms[i].localRotation = savedTransforms[i].rot;
loadedTransforms[i].localScale = savedTransforms[i].scale;
}
return loadedTransforms;
}
}
This is how I'm using the methods:
private void SaveLoad()
{
if (GUILayout.Button("Save"))
{
var selected = Selection.objects.OfType<GameObject>().ToList();
if (selected.Count > 0)
{
for (var i = selected.Count - 1; i >= 0; --i)
{
var select = selected[i];
transformSelection.Add(select.transform);
}
TransformSaver.SaveTransform(transformSelection.ToArray());
tempSelections = transformSelection;
transformSelection = new List<Transform>();
}
}
if (GUILayout.Button("Load"))
{
TransformSaver.LoadTransform();
}
}
Transforms can't be serialized
(But you knew that, which is why you created this class)
Yet:
public Transform parent;
...
trnfrm[i].parent = tranformToSave[i].parent;
You're still trying to serialize a Transform!
Rather than trying to serialize a transform in this manner (child-up) how about serializing a different way? If a transform has children create an array of TransformInfos and stuff the transform's children into it (parent-down).
It should also be noted that PlayerPrefs isn't meant for this kind of data. It's stored in plain text which the user can easily find and edit freely, and has a very limited size.
I have a dictionary with gameobjects as the keys, which is n amount and gameobjects as the values, which is amount of possible 4.
Dictionary<GameObject, ArrayList> polesAttachedToFloor = new Dictionary<GameObject, ArrayList>();
public void AddFloor(GameObject floor)
{
if(floor != null)
{
polesAttachedToFloor.Add(floor, new ArrayList { null,null,null,null });
}
}
public void AddPole(GameObject floor, GameObject pole)
{
for (int i = 0; i <= 4; i++)
{
}
}
How do I iterate through the "values?"... and is there a more 'appropriate' way of doing what Im aiming for?
ArrayList poles;
if (polesAttachedToFloor.ContainsKey(floor))
{
poles = dictionary[floor];
}
if(poles!=null)
{
for (int i = 0; i <= 4; i++)
{
poles.Add(pole);
}
}
Try that inside you AddPole function.
There is how you should encapsulate that...Dont have unity running so may be some typos.
public class FloorManager:MonoBehaviour
{
public gameObject floorPrefab;
private List<Floor> floors;
void Start()
{
floors = new List<Floor>();
}
public void AddFloor()
{
//instantiate prefab // gameObject floorGo = Instantiate....
Floor floorScript = floorGo.getComponent<Floor>();
floors.Add(floorScript);
floorScript.AddPoles();
}
public void RemoveFloor(Floor floor)
{
floors[floors.IndexOf(floor)].gameObject.Destory();
floors.Remove(floor);
}
}
public class Floor : MonoBehaviour
{
public gameObject polePrefab;
public Pole [] poles = new Pole[4];
public void AddPoles()
{
for (int i = 0; i < 4; i++)
{
//instantiate prefab // gameObject poleGo = Instantiate....
Pole poleScript = poleGo.getComponent<Pole>();
poles[i]=poleScript;
}
}
}
public class Pole : MonoBehaviour
{
//some logic here if needed...Destroy...Damage...
}
Play with Enumerator :D Have fun
public void AddPole(GameObject floor, GameObject pole)
{
var enumerator = polesAttachedToFloor.GetEnumerator ();
for (int i = 0; i <= 4; i++)
{
if (enumerator.MoveNext ()) {
var item = enumerator.Current;
}
}
}