How to debug why Unity freezes? - unity3d

I am doing a collaboration with my school mates for a school project, but recently when I run Unity it freezes and I can't get it to unfreeze. I have uninstalled and reimported all the assets and my group mates also said that their Unity does not have this problem. I can't find a solution.
The project is being done in 2d. I was testing some logic while this happened to me. I was making a health bar test where it automatically goes down and there's a text box while follows it containing the actual number. Then suddenly the next day Unity decided to quit on me and not run at all.
Update
Code:
public static float fillamount = 1f;
public float period = 1f;
public Image image;
private float time;
public float DoesItDecreaseOverTime = -0.1f;
private float tempnum;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
time += Time.deltaTime;
while (fillamount <100 || fillamount > 0)
{
if (time >= period)
{
fillamount += DoesItDecreaseOverTime;
time = 0;
}
}
tempnum = (fillamount * 100);
image.fillAmount = fillamount;
}
}

After long days of frustration myself, I have found how to debug while unity freezes. I was doing stuff with trees (not the vegetation, the data structure) and for me it was very hard to determine where and what causes stack overflow. The stack overflow freezes unity so I can't see any debug that would have helped me to solve the problem. But now, I found the solution.
SOLUTION
So, I tried using System.Diagnostics.Debug.WriteLine which you should see in the output window of Visual Studio. But that doesn't seem to work with a unity project. Anyways, I started to think a little bit and came up with this class:
using System;
using System.IO;
public class Logger
{
public Logger(string logFilePath)
{
if(!logFilePath.EndsWith(".log"))
logFilePath += ".log";
LogFilePath = logFilePath;
if(!File.Exists(LogFilePath))
File.Create(LogFilePath).Close();
WriteLine("New Session Started");
}
public string LogFilePath { get; private set; }
public void WriteLine(object message)
{
using(StreamWriter writer = new StreamWriter(LogFilePath, true))
writer.WriteLine(DateTime.Now.ToString() + ": " + message.ToString());
}
}
Usage is like this:
Logger logger = new Logger(#"C:\Users\Soogbad\Desktop\MyLog.log");
logger.WriteLine("Hello World");
This creates a log file in which you can see all the logs you made. It works perfect! Even while unity is freezing.

Try to avoid Loops in the update function (unless you fully control the loop), this function is called everyframe so if you perform long tasks in here you might experience framedrop and freezes.
In your case replace the while by a If and revert the conditions.
If you have unity pro you have the Profiler that will tell you what script/function is taking the most cpu time.

Related

I'm getting an UnassignedReferenceException when the reference is set. I've seen other questions similar but not exactly the same

As the title says i'm getting the UnassignedReferenceException error for a variable already set. Using ScriptableObjects im working on an inventory system(partially from scratch) and im trying to access the EquipmentUi in another class using a GameObject to hold the prefab containing the Character script. There is no issue with this working to access the Character script as shown in the picture because i can access the name. However, when i try and access the EquipmentUI of that character it gives the error. This isnt the last part i need access to but i have figured out that the UI is the part i cant access, i need the script held in it(which has worked before in another class).
The variable is already assigned, there is no other object in my scene with the same script attached, and the code can access other parts of the script i want access to which is why i made a new post after seeing the other posts and not seeing one that had quite the same issue.
using System.Collections;
using System.Collections.Generic;
using UnitEngine;
[CreateAssetMenu(fileName = "New Character Equipment", menuName = "Inventory/Character/CharacterEquipment)]
public class CharacterEquipmentObject : ScriptableObject
{
[SerializeField]
protected Player user;
[SerializeField]
private GameObject equipmentUser; // Only used to get the user because SOs cant getObject<>
[SerializeField]
public EquipmentObject[] equipment = new EquipmentObject[8];
[SerializeField]
EquipmentDisplay equipmentUI;
//private string[] SlotList = new string[8]{"Helmet", "Shoulders", "Chest", "MainHand", "OffHand", "Ring", "Legs", "Feet"}; may need later
public void start() // must be called in display
{
user = equipmentUser.GetComponent<Player>(); // set the user to be used in other classes\
Debug.Log("user in CEO: " + user.characterName); // this displays fine
Debug.Log("user " + user.characterName + "'s equipment: " + user.EquipmentUI.name); // This is what the editor says is empty when the slot has it assigned
equipmentUI = user.EquipmentUI.GetComponentInChildren<EquipmentDisplay>();
Debug.Log("equipmentUI: " + equipmentUI.name);
}
Thanks for any help in advance.
Player class:
public class Player : Character
{
[SerializeField]
protected Character[] companions = new Character[3];
[SerializeField]
GameObject InventoryUI;
public GameObject EquipmentUI;
bool isSelling = false;
public string characterName;
protected override void Start()
{
InventoryUI.SetActive(false);
EquipmentUI.SetActive(false);
Debug.Log("Player Equipment UI: " + EquipmentUI.GetComponentInChildren<EquipmentDisplay>().name);
base.Start();
}
private void OnTriggerEnter(Collider other)
{
var item = other.GetComponent<InteractItem>();
if(item)
{
characterInventory.addItem(item._item, 1);
useItem(item._item);
}
Destroy(other.gameObject);
}
private void Update()
{
if(Input.GetKeyDown(KeyCode.I))
{
InventoryUI.SetActive(!InventoryUI.activeSelf);
}
if(Input.GetKeyDown(KeyCode.C))
{
EquipmentUI.SetActive(!EquipmentUI.activeSelf);
if(EquipmentUI.activeSelf == true)
{
EquipmentUI.GetComponentInChildren<EquipmentDisplay>().updateEquipmentSlots();
}
}
}
Thanks for all the help on here but i figured out it is a ScriptableObjects issue as i went in and changed the way it is run, even putting the function to edit the users InventoryObject into the player itself it still gave the same issue.
In the player function Update() that does not have the InventoryObject(ScriptableObject) the update runs fine but as soon as the player function EmptySlot() is called from the InventoryObject it sends the same error.
Not entirely sure why this is but SOs are unable to access the GameObject that holds the script for the inventory display in them whatsoever. If anyone has a reason as to why this is I would love to know, but i am going to move on from this particular part and change how it works.
You might be experiencing a bug I have been facing where the element variable info does not update to show variable changes.
To force it to update, cause an error in your code. Switch to unity, when it shows you the error go back and fix it, and see if the variable value changes then.

How to generate a random number and make sure it is the same for everyone using Photon in Unity?

Hi Guys I am converting a single player Sudoko game into a multiplayer game (right now 2 players) using Photon in Unity.
The basic logic of the Sudoku game is that there are 300 puzzle data already loaded in it. A random number between 1 to 300 is picked up and the corresponding puzzle is loaded.
But the problem I am facing is even though I have made sure that the same number is getting picked up for both the client and the master server, different puzzles are getting loaded.
So basically I script called MultiManager attached to the MultiManager GameObject in the Sudoku screen.The script looks something like this.
void Start()
{
PV = GetComponent<PhotonView>();
if (PhotonNetwork.IsMasterClient)
{
puzzleIndex = Random.Range(0, 300);
PV.RPC("RPC_PuzzleIndex", RpcTarget.Others, puzzleIndex);
}
gameManager = FindObjectOfType(typeof(GameManager)) as GameManager;
gameManager.PlayNewGame("easy");
}
[PunRPC]
void RPC_PuzzleIndex(int puzzleIndexNUmber)
{
puzzleIndex = puzzleIndexNUmber;
}
So in the GameManager script you have these functions:
public void PlayNewGame(string groupId)
{
// Get the PuzzleGroupData for the given groupId
for (int i = 0; i < puzzleGroups.Count; i++)
{
PuzzleGroupData puzzleGroupData = puzzleGroups[i];
if (groupId == puzzleGroupData.groupId)
{
PlayNewGame(puzzleGroupData);
return;
}
}
}
private void PlayNewGame(PuzzleGroupData puzzleGroupData)
{
// Get a puzzle that has not yet been played by the user
PuzzleData puzzleData = puzzleGroupData.GetPuzzle();
// Play the game using the new puzzle data
PlayGame(puzzleData);
}
And in the PuzzleGroupData class you have this function :
public PuzzleData GetPuzzle()
{
return new PuzzleData(puzzleFiles[MultiManager.puzzleIndex], shiftAmount, groupId);
}
I don't quite get as to whats wrong which is happening. I tried to use other variations like keeping that random number outside of the condition inside of PhotonNetwork.isMasterClient and all, but doesn't work.
If anyone can help it would be great. By the way this Sudoku game was purchased and I am trying to convert it to a mutliPlayer game
Since the master is usually the first one in a room so also the first one getting Start called I think what happens is that your other clients are simply not connected yet when the RPC is called.
Further it might also happen (actually pretty likely) that Start is called before the RPC has the chance to be received.
I would rather actually wait until you have the value and do
void Start()
{
PV = GetComponent<PhotonView>();
if (PhotonNetwork.IsMasterClient)
{
PV.RPC(name of(RPC_PuzzleIndex), RpcTarget.AllBuffered, Random.Range(0, 300));
}
}
[PunRPC]
void RPC_PuzzleIndex(int puzzleIndexNUmber)
{
puzzleIndex = puzzleIndexNUmber;
gameManager = FindObjectOfType(typeof(GameManager)) as GameManager;
gameManager.PlayNewGame("easy");
}
This way
the RpcTarget.AllBuffered makes sure that also clients joining later will receive the call
there is no way you start a game without receiving the random value first

Can't load another level

I was following an online tutorial to make this little game in Unity and when I got to the end, I wound up with two different levels. The only problem is that I can only play one of them. I tried adding it in the build settings but for some reason, it just isn't working. Maybe there's a problem with the code? I don't know. This is my first-ever working with Unity so I'm still figuring things out.
public class LevelControllerScript : MonoBehaviour
{
private static int _nextLevelIndex = 1;
private NewBehaviourScript1[] _enemies;
private void OnEnable()
{
_enemies = FindObjectsOfType<NewBehaviourScript1>();
}
// Update is called once per frame
void Update()
{
foreach(NewBehaviourScript1 NewBehaviourScript1 in _enemies)
{
if (NewBehaviourScript1 != null)
return;
}
Debug.Log("You killed all enemies!");
_nextLevelIndex++;
string nextLevelName = "Level" + _nextLevelIndex;
SceneManager.LoadScene(nextLevelName);
}
}
string nextLevelName = "Level" + _nextLevelIndex;
This line of code makes the string nextLevelName something like Level1, or Level2, however, you are trying to match up to the string leveltwo.
To fix this you need to match up the strings, I believe you can just change the scene leveltwo to Level2 (and change all of the other scenes accordingly).

I'm using Unity and creating a text based game where your options directly affect the health and need assistance

I'm trying to add a healthbar which is affected by the choices made and link the two but am unable to do so. Any help would be appreciated.
I will recommend you that before you go any further with your game development process, you research a bit more about general OOP programming (no offense or anything, it will just make things waaay easier, trust me).
That being said, here's an example to steer you to the right direction:
You can create a script with a global variable called health and subtract it every time the player makes a decision that would "punish" them, here's an example of how that could work:
PlayerManager.cs (put this script on your player)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerManager : MonoBehaviour {
public float health = 100f;
private void Update() {
if (health < 0)
Die();
}
private void Die () {
//Fancy animations and text can be added on this method
Destroy(gameObject);
}
}
And on your dialogue script, once they choose a wrong answer, then you can just decrease health on the proper player manager instance, like this:
DialogueSystem.cs
public PlayerManager playerManager;
public float damage = 10f;
private void Start() {
//You can initialize other stuff here as well
//This line is assuming you have the dialogue system script attach to your player as well
playerManager = GetComponent<PlayerManager>();
}
private void Update () {
//This is obviously going to change depending on how your system is built
if (wrongChoice) {
playerManager.health -= damage;
wrongChoice = false;
}
}

Project Tango won't relocalize

I am trying out Area Recognition using Area Learning with predefined ADF files using Project Tango in Unity3d. I use the script from this tutorial as the basis, but for some reason it won't relocalize.
using UnityEngine;
using System.Collections;
using Tango;
public class TestADFFile : MonoBehaviour, ITangoLifecycle
{
private TangoApplication m_tangoApplication;
public UnityEngine.UI.Text statusText;
public string adfName;
public void Start()
{
m_tangoApplication = FindObjectOfType<TangoApplication>();
if (m_tangoApplication != null)
{
m_tangoApplication.Register(this);
m_tangoApplication.RequestPermissions();
}
}
public void OnTangoPermissions(bool permissionsGranted)
{
if (permissionsGranted)
{
if(AreaDescription.ImportFromFile(System.IO.Path.Combine(Application.streamingAssetsPath, "adfs/" + adfName))){
statusText.text = "success!";
}
else{
statusText.text = "fail!";
}
AreaDescription[] list = AreaDescription.GetList();
AreaDescription mostRecent = null;
AreaDescription.Metadata mostRecentMetadata = null;
if (list.Length > 0)
{
// Find and load the most recent Area Description
mostRecent = list[0];
mostRecentMetadata = mostRecent.GetMetadata();
foreach (AreaDescription areaDescription in list)
{
AreaDescription.Metadata metadata = areaDescription.GetMetadata();
if (metadata.m_dateTime > mostRecentMetadata.m_dateTime)
{
mostRecent = areaDescription;
mostRecentMetadata = metadata;
}
}
m_tangoApplication.Startup(mostRecent);
}
else
{
// No Area Descriptions available.
Debug.Log("No area descriptions available.");
}
}
}
public void OnTangoServiceConnected()
{
}
public void OnTangoServiceDisconnected()
{
}
}
The statusText is set to "success" so apparently the ADF is successfully loaded, right?
You're doing it right. Relocalization with area learning mode off works very quick. But relocalization with area learning mode on takes quite a while. You have to walk around up to 3-5 minutes until the relocalization works. Don't give up. I had the same problem. But in the end, it works!
Is not an answer I think, but just a checklist of things I would check.
Check that the ADF to relocalize is the right one, you may be loading an ADF from a different area.
On the TangoManager component check if Enable Area Descriptions is set to true.
Also check if the scene is set up correctly with all tango components required like: RelocalizingOverlay.cs, TangoApplication.cs
Also as a personal note I have noticed that ADF sometimes take quite a while to relocalized and sometimes they don't relocalize at all.
I think the reasons are that the room has change in someway or the lighting is very different, by different I mean maybe the day you recorded that ADF was a very sunny day and the day you are trying to relocalize is cloudy.(Again this are just theories of mine, I got from my testing)