Scripting Help desperately needed.
Hello everyone!
I have this script on a system made for gems.
using UnityEngine;
using UnityEngine.UI;
using System.Linq;
using System.Collections;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace GemSystem {
public class GemManager : MonoBehaviour {
public static GemManager singleton { get; private set; }
public int rank { get { return _rank; } }
[Header("Hover over properties to get a tooltip.")]
[Tooltip("In here you should drag the game object that has all the inventory slots under it.")]
[SerializeField] RectTransform inventoryParent;
[Tooltip("In here you should drag the game object that has all the crafting slots under it.")]
[SerializeField] RectTransform craftingParent;
[Tooltip("This text component is used for counting down time after crafting process begins.")]
[SerializeField] Text craftingTimer;
[Tooltip("A reference to the orb button is needed, in order to disable it when you use an orb.")]
[SerializeField] Selectable useOrbButton;
[Tooltip("This is used to display how many orbs you have.")]
[SerializeField] Text orbAmountText;
[Tooltip("The limit of how many orbs you can have.")]
[SerializeField] int maxOrbNumber = 1;
[Tooltip("An audio source is needed for playing audio clips in different situations.")]
[SerializeField] AudioSource interfaceSource;
[Tooltip("The clip is played when player begins dragging a gem.")]
[SerializeField] AudioClip beginDraggingSFX;
[Tooltip("The clip is played when crafting worked.")]
[SerializeField] AudioClip craftingSuccessSFX;
[Tooltip("The clip is played when crafting fails.")]
[SerializeField] AudioClip craftingFailedSFX;
[Tooltip("This slot is used for previewing the result of combining gems.")]
[SerializeField] Slot resultSlot;
[Tooltip("This slot is used to determine the category of the new crafted gem.")]
[SerializeField] Slot lastCraftingSlot;
[Tooltip("Drag the 'Text' component that will be used for displaying the rank in here.")]
[SerializeField] Text rankText;
[Tooltip("Drag the gem prefab in here.")]
[SerializeField] GameObject gemPrefab;
[Tooltip("Define different types of gems, preferably, each category means a different color.")]
[SerializeField] GemCategory[] gemCategories;
public Text GemBonus;
public Image flare;
public Image flare1;
public Image flare2;
public int remainedTime;
Slot[] inventorySlots;
Slot[] craftingSlots;
int _rank;
float timeWhenCraftingIsDone;
int _orbAmount;
bool orbUsed;
bool orbAddedDueToSameCategory;
Coroutine craftingTimerCoroutine;
void Awake() {
if(!singleton) {
singleton = this;
}
else if(singleton != this) {
Destroy(gameObject);
}
inventorySlots = inventoryParent.GetComponentsInChildren<Slot>();
craftingSlots = (from slot in craftingParent.GetComponentsInChildren<Slot>()
where slot != resultSlot
select slot).ToArray();
StartCoroutine(UpdateCraftingPeriodically());
}
public void PlayBeginDragClip() {
interfaceSource.PlayOneShot(beginDraggingSFX);
}
public void UseOrb() {
orbAmountText.text = statics.orb1.ToString();
if (Time.time > timeWhenCraftingIsDone) return;
if(statics.orb1 > 0) {
useOrbButton.interactable = false;
statics.orb1--;
orbUsed = true;
}
orbAmountText.text = statics.orb1.ToString();
orbAddedDueToSameCategory = false;
}
public void AddOrb() {
_orbAmount = Mathf.Clamp(_orbAmount + 1, 0, maxOrbNumber);
orbAmountText.text = _orbAmount.ToString();
}
public void RefreshRank() {
int newRank = 0;
foreach(var slot in inventorySlots) {
Gem gem = slot.currentHeldGem;
if(gem) newRank += gem.gemValue;
}
foreach(var slot in craftingSlots) {
Gem gem = slot.currentHeldGem;
if(gem) newRank += gem.gemValue;
}
if(Slot.draggedGemRect) {
newRank += Slot.draggedGemRect.GetComponent<Gem>().gemValue;
}
UpdateRank(newRank);
}
public void AddRandomGem(int minValue) {
GemCategory category = gemCategories[Random.Range(0, gemCategories.Length)];
int maxValue = category.categoryLevels[category.categoryLevels.Length - 1].maxValue;
AddGem(category.categoryName, Random.Range(minValue, maxValue / 2));
}
public void AddRandomGem(int minValue, int maxValue) {
GemCategory category = gemCategories[Random.Range(0, gemCategories.Length)];
AddGem(category.categoryName, Random.Range(minValue, maxValue));
}
public bool AddGem(string categoryName, int gemValue) {
int lowestGemIndex = 0;
for(int slotIndex = 0;slotIndex < inventorySlots.Length;slotIndex ++) {
Slot slot = inventorySlots[slotIndex];
// Keeping track of the lowest gem value.
if(slotIndex > 0 && inventorySlots[slotIndex - 1].currentHeldGem && slot.currentHeldGem) {
if(inventorySlots[lowestGemIndex].currentHeldGem.gemValue > slot.currentHeldGem.gemValue) {
lowestGemIndex = slotIndex;
}
}
// Add a gem only if a slot is not populated or the inventory is full and the new gem has a higher value
if(!slot.currentHeldGem) {
Gem newGem = CreateGem(gemValue, categoryName);
slot.DestroyCurrentGem();
slot.HoldGem(newGem);
RefreshRank();
return true;
}
else if(slotIndex == inventorySlots.Length - 1 && inventorySlots[lowestGemIndex].currentHeldGem.gemValue < gemValue) {
Gem newGem = CreateGem(gemValue, categoryName);
slot = inventorySlots[lowestGemIndex];
slot.DestroyCurrentGem();
slot.HoldGem(newGem);
RefreshRank();
return true;
}
}
return false;
}
// --------------------- SAVE / LOAD ------------------------
public void Save() {
BinaryFormatter bf = new BinaryFormatter ();
FileStream file = File.Create (Application.persistentDataPath + "/playerInventory.dat");
InventoryState inventoryState = new InventoryState();
inventoryState.inventorySlots = new InventoryState.SlotState[inventorySlots.Length];
inventoryState.craftingSlots = new InventoryState.SlotState[craftingSlots.Length];
// Saving inventory slots
for(int i = 0;i < inventorySlots.Length;i ++) {
Gem gem = inventorySlots[i].currentHeldGem;
string catName = gem ? gem.category : "none";
int value = gem ? gem.gemValue : -1;
inventoryState.inventorySlots[i] = new InventoryState.SlotState() {
populated = gem,
categoryName = catName,
gemValue = value
};
}
// Saving crafting slots
for(int i = 0;i < craftingSlots.Length;i ++) {
Gem gem = craftingSlots[i].currentHeldGem;
string catName = gem ? gem.category : "none";
int value = gem ? gem.gemValue : -1;
inventoryState.craftingSlots[i] = new InventoryState.SlotState() {
populated = gem,
categoryName = catName,
gemValue = value
};
}
bf.Serialize(file, inventoryState);
file.Close();
}
public void Load() {
if (File.Exists (Application.persistentDataPath + "/playerInventory.dat")) {
BinaryFormatter bf = new BinaryFormatter ();
FileStream file = File.Open (Application.persistentDataPath + "/playerInventory.dat", FileMode.Open);
InventoryState data = (InventoryState)bf.Deserialize (file);
file.Close ();
// Cleaning the preview slot
resultSlot.DestroyCurrentGem();
IntrreruptCraftingProcess();
// Loading inventory slots
for(int i = 0;i < inventorySlots.Length;i ++) {
InventoryState.SlotState slotState = data.inventorySlots[i];
inventorySlots[i].DestroyCurrentGem();
if(slotState.populated) {
Gem gem = CreateGem(slotState.gemValue, slotState.categoryName);
inventorySlots[i].HoldGem(gem);
}
}
// Loading crafting slots
for(int i = 0;i < craftingSlots.Length;i ++) {
InventoryState.SlotState slotState = data.craftingSlots[i];
craftingSlots[i].DestroyCurrentGem();
if(slotState.populated) {
Gem gem = CreateGem(slotState.gemValue, slotState.categoryName);
craftingSlots[i].HoldGem(gem);
}
}
UpdateCrafting();
}
}
// ----------------------- CRAFTING SECTION --------------------------
public void UpdateCrafting() {
if(craftingSlots.Length < 2) return;
foreach(var slot in craftingSlots) {
if(!slot.currentHeldGem) {
IntrreruptCraftingProcess();
return;
}
}
// If the crafting slots are populated with the same category, then show a preview in the result slot and start the crafting process
if(Time.time > timeWhenCraftingIsDone) {
StartCraftingProcess();
}
}
public void IntrreruptCraftingProcess() {
if(craftingTimerCoroutine != null) {
StopCoroutine(craftingTimerCoroutine);
craftingTimerCoroutine = null;
}
flare.enabled = false;
flare1.enabled = false;
flare2.enabled = false;
GemBonus.enabled = false;
craftingTimer.enabled = false;
resultSlot.DestroyCurrentGem();
useOrbButton.interactable = true;
orbUsed = false;
timeWhenCraftingIsDone = 0f;
if(orbAddedDueToSameCategory) {
_orbAmount --;
_orbAmount = (int)Mathf.Clamp(_orbAmount, 0, Mathf.Infinity);
orbAmountText.text = _orbAmount.ToString();
orbAddedDueToSameCategory = false;
}
}
void StartCraftingProcess() {
flare.enabled = true;
flare1.enabled = true;
flare2.enabled = true;
GemBonus.enabled = true;
craftingTimer.enabled = true;
Gem craftedGem = null;
int combinedValue = 0;
bool sameCategory = true;
string firstCategoryName = craftingSlots[0].currentHeldGem.category;
foreach(var slot in craftingSlots) {
combinedValue += slot.currentHeldGem.gemValue;
if(firstCategoryName != slot.currentHeldGem.category) {
sameCategory = false;
}
}
if(sameCategory) _orbAmount ++;
orbAmountText.text = _orbAmount.ToString();
orbAddedDueToSameCategory = true;
foreach(var category in gemCategories) {
if(category.categoryName == lastCraftingSlot.currentHeldGem.category) {
craftedGem = CreateGem((combinedValue + statics.added), category.categoryName);
}
}
resultSlot.HoldGem(craftedGem);
if(craftingTimerCoroutine != null) StopCoroutine(craftingTimerCoroutine);
craftingTimerCoroutine = StartCoroutine(DoCraftingTimer());
}
IEnumerator DoCraftingTimer() {
int successChance = 0;
foreach(var category in gemCategories) {
if(category.categoryName == resultSlot.currentHeldGem.category) {
timeWhenCraftingIsDone = Time.time + category.categoryLevels[resultSlot.currentHeldGem.level - 1].craftingTime;
successChance = category.categoryLevels[resultSlot.currentHeldGem.level - 1].successChance;
}
}
while(Time.time < timeWhenCraftingIsDone) {
remainedTime = Mathf.RoundToInt(timeWhenCraftingIsDone - Time.time);
int minutes = remainedTime / 60;
int seconds = remainedTime - minutes * 60;
string addon = seconds < 10 ? "0" : "";
craftingTimer.text = minutes.ToString() + ":" + addon + seconds.ToString();
yield return null;
}
foreach(var slot in craftingSlots) slot.DestroyCurrentGem();
if(orbUsed || Random.Range(0, 101) <= successChance) {
AddGem(resultSlot.currentHeldGem.category, resultSlot.currentHeldGem.gemValue);
interfaceSource.PlayOneShot(craftingSuccessSFX);
}
else {
// Crafting was not a success
interfaceSource.PlayOneShot(craftingFailedSFX);
}
IntrreruptCraftingProcess();
}
IEnumerator UpdateCraftingPeriodically() {
while(true) {
UpdateCrafting();
RefreshRank();
yield return new WaitForSeconds(0.3f);
}
}
// This function will create a Gem game object and return it for further using.
Gem CreateGem(int gemValue, string categoryName) {
Gem newGem = Instantiate<GameObject>(gemPrefab).GetComponent<Gem>();
Sprite gemIcon = null;
int levelTemp = 0;
// Find gem icon based on the gem value
foreach(var category in gemCategories) {
if(category.categoryName == categoryName) {
for(int levelIndex = 0;levelIndex < category.categoryLevels.Length;levelIndex ++) {
GemLevel level = category.categoryLevels[levelIndex];
if(level.maxValue > gemValue) {
gemIcon = level.icon;
levelTemp = levelIndex + 1;
break;
}
else if(levelIndex == category.categoryLevels.Length - 1) {
// Assign the highest possible value, if the 'gemValue' goes over the max value of this category
int maxValue = category.categoryLevels[category.categoryLevels.Length - 1].maxValue;
gemValue = maxValue;
gemIcon = category.categoryLevels[category.categoryLevels.Length - 1].icon;
levelTemp = levelIndex + 1;
}
}
}
}
newGem.InitializeGem(gemValue, gemIcon, categoryName, levelTemp);
return newGem;
}
void UpdateRank(int newRank) {
_rank = newRank;
rankText.text = _rank.ToString();
}
bool InventoryIsFull() {
foreach(var slot in inventorySlots) {
if(!slot.currentHeldGem) {
return false;
}
}
return true;
}
}
// ------------- SOME HELPER CLASSES ------------------
[System.Serializable]
public class GemCategory {
public string categoryName;
public GemLevel[] categoryLevels;
}
[System.Serializable]
public class GemLevel {
[Tooltip("Crafting gems with the combined value greater than this, will result in increasing the gem's level.")]
public int maxValue;
public int craftingTime = 15;
[Range(0, 100)] public int successChance = 70;
public Sprite icon;
}
[System.Serializable]
public class InventoryState {
public SlotState[] inventorySlots;
public SlotState[] craftingSlots;
[System.Serializable]
public class SlotState {
public bool populated;
public string categoryName;
public int gemValue;
}
}
}
The IEnumerator DoCraftingTimer() holds a timer which counts down from a timer variable on the gem called craftingTime. This time is currently set to 30 seconds. The timer works well and counts down from 30 seconds every time gems are crafted. The problem is that whenever the player changes the scene, the countdown timer stops, and whenever they re-enter the scene, the timer reloads back at 30 seconds. I have a persistent data script in the game, and I think I can get the countdown timer to continue by running the timer through the persistent data script, which will be kept active in every scene, that way when the player plays the game or switches scenes the counter will continue. Can anyone help me with doing this? Here is the persistent data script: using UnityEngine; using System.Collections; using System; using System.Runtime.Serialization.Formatters.Binary; using System.IO; public class stay : MonoBehaviour { public static stay gamecontrol; public static int gemtime = 20; public static bool gemcrafting = true; public static int remainingtime; public int check; void Awake () { if (gamecontrol == null){ DontDestroyOnLoad(gameObject); gamecontrol = this; } else if (gamecontrol != this){ Destroy (gameObject); }
}
void Update () {
}
}
You should have posted this in the Help Room as you don't have an error/problem with Unity functions.
Don't post 400 lines of code, just explain your problem. ("I have a timer and want it to continue to count down even when switching scenes").
Format your code properly (the bottom part of the question, which is unreadable)
If we set all the above aside, why don't you just count down in the first scene, and then store the remaining time and subtract it from the timer in the new scene?
That would be the most quick and easy way I guess.
The other problem you seem to have (I didn't bother reading the script, I just read your actual text) is the one most people seem to have with persistent objects, switching more than once between scenes. You need to make sure your persistent object only exists once in any scene you load. It needs to check for another copy of itself when it starts and if it finds a copy of itself, delete that copy. Then it can resume executing.
For that matter, there are loads of questions out already and multiple tutorials either on youtube or coding forums.
Unity Live Training Video about Saving and Loading and Data Persistence
Watching this should give you all the information you need. Hope it helps you.
I never knew there was a such long video for saving and loading! thank you
Answer by Pratap-Dafedar · Feb 25, 2016 at 06:31 AM
using UnityEngine;
using System.Collections;
using System;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public class stay : MonoBehaviour
{
public static stay gamecontrol;
public static int gemtime = 20;
public static bool gemcrafting = true;
public static int remainingtime;
public int check;
void Awake ()
{
if (gamecontrol == null)
{
DontDestroyOnLoad(gameObject);
gamecontrol = this;
}
else if (gamecontrol != this)
{
Destroy (gameObject);
}
}
void Start()
{
craftingTimerCoroutine = this.StartCoroutine(DoCraftingTimer());
}
IEnumerator DoCraftingTimer()
{
int successChance = 0;
foreach(var category in gemCategories) {
if(category.categoryName == resultSlot.currentHeldGem.category) {
timeWhenCraftingIsDone = Time.time + category.categoryLevels[resultSlot.currentHeldGem.level - 1].craftingTime;
successChance = category.categoryLevels[resultSlot.currentHeldGem.level - 1].successChance;
}
}
while(Time.time < timeWhenCraftingIsDone) {
remainedTime = Mathf.RoundToInt(timeWhenCraftingIsDone - Time.time);
int minutes = remainedTime / 60;
int seconds = remainedTime - minutes * 60;
string addon = seconds < 10 ? "0" : "";
craftingTimer.text = minutes.ToString() + ":" + addon + seconds.ToString();
yield return null;
}
foreach(var slot in craftingSlots) slot.DestroyCurrentGem();
if(orbUsed || Random.Range(0, 101) <= successChance) {
AddGem(resultSlot.currentHeldGem.category, resultSlot.currentHeldGem.gemValue);
interfaceSource.PlayOneShot(craftingSuccessSFX);
}
else {
// Crafting was not a success
interfaceSource.PlayOneShot(craftingFailedSFX);
}
IntrreruptCraftingProcess();
}
}
This is the max I can help with your huge question.
I tried what you suggested, but it didn't work. The timer basically does the same thing which is restart every time i enter the scene, and it stops whenever i leave the scene. Is there any way at all to get this timer to continue through the persistent data script and not restart when I reenter the scene? I desperately need to figure this out and I have been at this for weeks now.
Your answer
Follow this Question
Related Questions
The laser problems 0 Answers
How can i make sure the script will work only on the attached object ? 1 Answer
How can I check if a object in the hierarchy have been destroyed using EditorWindow type script ? 1 Answer
How do i put "Wasted!" after respawn? 0 Answers
need help with simple timing code 1 Answer