stop a function from being called every frame c# - c#

I am trying to create a list of buttons using information collected from www forms in my program. The problem I am having is that when I create the buttons, it infinitely creates them, and thusly unity crashes. I have two theories as to why this is happening, the first being that the function is being called each frame and is therefore creating too many buttons and the second is that for some reason when I check the length of a multidimensional array it has a length of 33 when it should only have a length of 3 (it should have 3 arrays of arrays). The whole script is below.
using Boo.Lang;
using System.Collections;
using SCG = System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using System;
public class MeetingRequestViewer : MonoBehaviour
{
//This script contains the code on how to display information about the students to the staff
//it displays each student in a different row
private Rect windowRect = new Rect(0, 0, Screen.width, Screen.height);
public Vector2 scrollPosition = Vector2.zero;
private int BSpace;
public string[,] SortedStudentArray;
private int j;
private string[][] MeetRequests;
private string[] temp;
private SCG.List<string> H_Priority = new SCG.List<string>();
private SCG.List<string> M_Priority = new SCG.List<string>();
private SCG.List<string> L_Priority = new SCG.List<string>();
private SCG.List<string> FullList = new SCG.List<string>();
private SCG.List<string> UAList;
private SCG.List<object> StudentButtonList = new SCG.List<object>();
private string[] Holding = new string[5];
private string[] SearchTerms;
public int ButtonSpacing = 10;
public int ButtonWidth = 80;
public int ButtonHeight = 30;
public void OnGUI()
{
//create a window
GUI.Window(0, windowRect, WindowFunction, "Meeting Request Viewer");
}
public void WindowFunction(int windowID)
{
//Fetches all user Data
string[][] userArray = GetComponent<Userdata>().CallDetail();
string[][] StudentArray = GetComponent<Userdata>().UserSorting(userArray);
//Calls the SortStudentArray method
string[,] SortedStudentArray = SortStudentList();
//Creates a box with a scrolling bar to taverse the y axis
scrollPosition = GUI.BeginScrollView(new Rect(Screen.width / 6, Screen.height / 6, 350, 250), scrollPosition, new Rect(0, 0, 300, 40 * SortedStudentArray.Length));
//for each row in the sorted student array
for (int x = 0; x < SortedStudentArray.Length - 1; x++)
{
//This keeps the gap between each button consistent
var y = ButtonSpacing + ButtonHeight;
//Regular Meeting Request
if (SortedStudentArray[x, 7] == "1")
{
//Urgent Meeting Request
if (SortedStudentArray[x, 8] == "1")
{
Debug.Log("Even Here");
//Create Button coloured red
GUI.backgroundColor = Color.red;
}
//Regular Meeting Request
else
{
//Create Button coloured yellow
GUI.backgroundColor = Color.yellow;
}
}
//No Meeting Request
else
{
//Create Button coloured green
GUI.backgroundColor = Color.green;
}
GUI.Button(new Rect(ButtonSpacing, ButtonSpacing + x * y, ButtonWidth, ButtonHeight), SortedStudentArray[x, 6]);
}
GUI.EndScrollView();
ButtonsCreated = true;
}
private string[,] SortStudentList()
{
//This method is used to fetch the meeting request data, split it into a jagged array and then again so that each
//row is a new user and each item in the row is a different peice of information relating to the user
//The Jagged array is then sorted using the bubble sort algorithm so that the students that require urgent meetings
//appear at the topo of the request table
//call website with the data store
WWW MeetReqData = new WWW("http://localhost/Wellbeing/meetingrequest.php");
//until WWW is finished do nothing
while (MeetReqData.isDone == false)
{
}
//convert the returned value into a string
string MeetReqString = MeetReqData.text;
//split the text into a string list
string[] mrq = MeetReqString.Split(';');
//convert the string list into a jagged array
MeetRequests = new string[mrq.Length][];
int i = 0;
//for each item in the list
foreach (string s in mrq)
{
//split it into its individual peice of information
string[] g = s.Split('|');
//store the information about a user on a new line
MeetRequests[i] = g;
++i;
}
for (int n = 0; n < MeetRequests.Length - 1; n++)
{
if (MeetRequests[n][1] == "1" && MeetRequests[n][2] == "1")
{
H_Priority.Add(MeetRequests[n][0]);
}
else if (MeetRequests[n][1] == "1" && MeetRequests[n][2] == "0")
{
M_Priority.Add(MeetRequests[n][0]);
}
else
{
L_Priority.Add(MeetRequests[n][0]);
}
}
//Combines all lists into a larger list of priorities
FullList.AddRange(H_Priority);
FullList.AddRange(M_Priority);
FullList.AddRange(L_Priority);
//convertFullList into an array for easier mainpulation and comparisons
string[][] feelingsArray = GetComponent<Userdata>().CallFeelings();
//FullList only contains 3 values
Debug.Log(FullList.Count);
//Info array about each user
string[,] SortedStudentArray = new string[FullList.Count, 11];
//SortedStudentArray contains 33 rows
Debug.Log("This thing Here");
Debug.Log(SortedStudentArray.Length);
//Line Counter
int SSAPos = 0;
// For every element in FullList
foreach (var item in FullList)
{
Debug.Log(FullList.Count);
//For each user in userArray
for (int y = 0; y < Userdata.userArray.Length; y++)
{
if (Userdata.userArray[y][0] == item)
{
for (int n = 0; n < Userdata.userArray; n++)
SortedStudentArray[SSAPos, n] = Userdata.userArray[y][n];
break;
}
}
Debug.Log(SortedStudentArray.Length);
//For each user in userArray
for (int y = 0; y < MeetRequests.Length; y++)
{
if (MeetRequests[y][0] == item)
{
SortedStudentArray[SSAPos, 7] = MeetRequests[y][1];
SortedStudentArray[SSAPos, 8] = MeetRequests[y][2];
break;
}
}
Debug.Log(SortedStudentArray.Length);
SSAPos += 1;
}
return SortedStudentArray;
}
// Update is called once per frame
void Update ()
{
//if (GUI.Button(new Rect(Screen.width / 4, Screen.height / 7, Screen.width / 2, Screen.height / 8), "Log Out"))
//{
// Debug.Log("Logged Out");
// SceneManager.LoadScene("Login");
//}
}
I did try to use a while loop around the section of code however it made no difference to the creation of buttons, they were still created more times than were needed

FullList is continually being populated which will eventually lead to a crash.
Call SortStudentList in Awake, such as;
string[,] SortedStudentArray;
void Awake ()
{
//Calls the SortStudentArray method
SortedStudentArray = SortStudentList();
}
Then;
public void WindowFunction(int windowID)
{
// Prevent further execution until SortedStudentArray is ready
if (SortedStudentArray == null) return;
...
}

From the documentation:
OnGUI is called for rendering and handling GUI events.
This means that your OnGUI implementation might be called several
times per frame (one call per event). For more information on GUI
events see the Event reference. If the MonoBehaviour's enabled
property is set to false, OnGUI() will not be called.
You need to reevaluate how you're doing your GUI. As mentioned in their GUI scripting guide you should probably use their UI system.
You especially don't want to be making one or more WWW calls per frame, or doing a bunch of sorting every frame.

https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnGUI.html
OnGUI is called for rendering and handling GUI events.
This means that your OnGUI implementation might be called several times per frame (one call per event). For more information on GUI events see the Event reference. If the MonoBehaviour's enabled property is set to false, OnGUI() will not be called.
You should probably use another way to call your method, or use a boolean value to check whether or not it has already been called.
Please refer to the image below to better understand the Unity lifecycle methods.
More information about Unity's Lifecycle can be found at: https://docs.unity3d.com/Manual/ExecutionOrder.html

Related

How delete clone GameObjects in Unity

So I didn't find something useful, to solve my problem. My script is downloading an JSON Array from my Server and fills some text with that informations.
Thats the part of code:
void DrawUI()
{
GameObject buttonObj = transform.GetChild (0).gameObject; //Gets button to clone it
GameObject g;
int N = allCars.Length;
for (int i = 0; i < N; i++)
{
g = Instantiate(buttonObj, transform);
g.transform.Find("name").GetComponent<Text>().text = allCars[i].carName;
g.transform.Find("type").GetComponent<Text>().text = allCars[i].type;
g.transform.Find("price").GetComponent<Text>().text = allCars[i].price+ "€";
if(balance < int.Parse(allCars[i].price))
{
g.transform.Find("price").GetComponent<Text>().color = Color.red;
} else if (balance >= int.Parse(allCars[i].price))
{
g.transform.Find("price").GetComponent<Text>().color = new Color32(53, 140, 3, 255);
}
g.GetComponent<Button>().AddEventListener(i, OpenBuyDialog);
itemIndex = i;
}
Destroy(prefab);
}
This code loops and creates clones of my buttons, all fine. When user confirms the Buy Dialog, it should reload/refresh the list, but for that I have to delete the old clones. I can't find how to do that properly.
Assume the transform has only one child (buttonObj) at the beginning, then you can delete the clones with this code.
var count = transform.childCount;
for (var i = 1; i != count; ++i)
Destroy(transform.GetChild(i).gameObject);
Add your instantiates objects to a member:
private List<GameObject> buttons;
On refresh, iterate through buttons and call Destroy on each GameObject.
Since you said:
g = Instantiate(buttonObj, transform);
Your clone object is = g.
So you can basically say:
Destroy(g.gameObject);

How can I compare two lists inside Editor script to make a for loop only one time each time the lists are not the same size?

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
[CustomEditor(typeof(DialogueTrigger))]
public class DialogueTriggerEditor : Editor
{
private SerializedProperty _dialogues;
private SerializedProperty _conversations;
private SerializedProperty old_Conversations;
private void OnEnable()
{
_conversations = serializedObject.FindProperty("conversations");
old_Conversations.arraySize = _conversations.arraySize;
}
public override void OnInspectorGUI()
{
//base.OnInspectorGUI();
serializedObject.Update();
_conversations.arraySize = EditorGUILayout.IntField("Conversations Size", _conversations.arraySize);
if (old_Conversations.arraySize != _conversations.arraySize)
{
for (int x = 0; x < _conversations.arraySize; x++)
{
var conversation = _conversations.GetArrayElementAtIndex(x);
var Id = conversation.FindPropertyRelative("conversationName");
EditorGUILayout.PropertyField(Id);
_dialogues = conversation.FindPropertyRelative("Dialogues");
_dialogues.arraySize = EditorGUILayout.IntField("Dialogues size", _dialogues.arraySize);
for (int i = 0; i < _dialogues.arraySize; i++)
{
var dialogue = _dialogues.GetArrayElementAtIndex(i);
EditorGUILayout.PropertyField(dialogue, new GUIContent("Dialogue " + i), true);
}
}
}
old_Conversations.arraySize = _conversations.arraySize;
serializedObject.ApplyModifiedProperties();
}
}
Everything inside the OnInspectorGUI is call over and over again very fast.
So it's doing the loops nonstop.
I added a old copy of the _conversations:
private SerializedProperty old_Conversations;
I tried first time to assign it to the original _conversations inside the OnEnable:
old_Conversations.arraySize = _conversations.arraySize;
But I used a break point and the OnEnable never called.
Then before the loops I'm doing the check:
if (old_Conversations.arraySize != _conversations.arraySize)
In the bottom I'm making both equal again.
But it's not working. When changing the size of the _conversations it's all the time change it back to 1 and does nothing after it.
My main goal is somehow to call the loops only once each time the size changed.
It's working good if the size is 1 or 5 or 10 but if I change the size to 100 everything get slow almost freeze.
You can use BeginChangeCheck / EndChangeCheck to detect the modification.
EditorGUI.BeginChangeCheck();
_conversations.arraySize = EditorGUILayout.IntField("Conversations Size", _conversations.arraySize);
if(EditorGUI.EndChangeCheck()) {
for ...
}
Page view
page = EditorGUILayout.IntField("Page", page);
int index = page * countPerPage;
for(int x = index; i < Mathf.Min(_conversations.arraySize, index + countPerPage); ++i)
{
...
}
You can try to save just the array size of the old conversations in an integer and not saving a second array. But there is a chance, that this wont work because i think the onInspectorGui code has to be executed in every call(But not sure about that).

Deleting all PictureBox in C#

i am creating a classic snake game where i need to shrink the board from the original size to smaller one as the level increases, what i got currently was that i managed to resize the board but the unused PictureBox are still there and unable to delete them. i am having trouble on how to delete and recall the method to recreate the PictureBox with new variable.
Really appreciate if there is someone who can help me solve my issue regarding PictureBox deletion/removal in C#. Here are some parts of the source code for my project. For Viewing the whole source code, can request from me directly.
Here where placed my code for the board to recreate/refresh.
private void gotoNextLevel(int nextLevel)
{
mode = "REST";
mySnake = new Snake(mainBoard); //Brand new snake with length 1
apples = new Rewards(nextLevel, mainBoard); //<--- Generate 5 apples
}
Here is how i create my board for the game.
int maxRow = 10, maxCol = 20; //Max 10 rows, and 20 columns in the board
int squareSize = 30; //Each square is 30px by 30px
PictureBox[,] squares;
public Board(Form mainForm)
{
squares = new PictureBox[maxRow, maxCol];
for (int row = 0; row < maxRow; row++)
{
for (int col = 0; col < maxCol; col++)
{
squares[row, col] = new PictureBox();
squares[row, col].Location = new Point(col * squareSize, row * squareSize);
squares[row, col].Height = squareSize;
squares[row, col].Width = squareSize;
squares[row, col].SizeMode = PictureBoxSizeMode.StretchImage;
squares[row, col].BackColor = Color.DarkGray;
squares[row, col].BorderStyle = BorderStyle.FixedSingle;
mainForm.Controls["boardPanel"].Controls.Add(squares[row, col]);
}
}
mainForm.Controls["controlPanel"].Location = new Point(mainForm.Controls["boardPanel"].Location.X, mainForm.Controls["boardPanel"].Location.Y + mainForm.Controls["boardPanel"].Height + 20);
}
Here is the refresh method.
private void refresh(Object myObject, EventArgs myEventArgs)
{
mySnake.move(mode); //Move the snake based on mode
modeLBL.Text = mode;
mainBoard.draw();
apples.draw(); //<----- draw apples
mySnake.draw();
//increment the duration by amount of time that has passed
//this method is called every speed millisecond
duration += speed;
timerLBL.Text = Convert.ToString(duration / 1000); //Show time passed
//Check if snke is biting itself. If so, call GameOver.
if (mySnake.checkEatItself() == true)
{
GameOver();
}
else if (apples.checkIFSnakeHeadEatApple( mySnake.getHeadPosition()) == true)
{
score += apples.eatAppleAtPostion(mySnake.getHeadPosition());
scoreLBL.Text = Convert.ToString(score);
if (apples.noMoreApples() == true)
{
clock.Stop();
level++;
levelLBL.Text = Convert.ToString(level);
gotoNextLevel(level);
MessageBox.Show("Press the start button to go to Level " + level, "Congrats");
}
else
{
//Length the snake and continue with the Game
mySnake.extendBody();
}
}
}
First of all: is the signature of refresh() generated by Visual Studio itself? If so, why don't you copy the body and put it in a new method without parameters because apparently you don't even use the ones in your current refresh() method.
Secondly: if you create that new method, you can create a method where you first clear the controls and then you call the redraw. I think it doesn't get redrawn because it was an event from a control which never gets called again because you just cleared all the controls.
And last but not least: if you have other controls than PictureBox but you only want to remove PictureBox you could use this:
foreach (Control control in this.Controls)
{
if (control is PictureBox)
{
this.Controls.Remove(control);
}
}

Movement Loop in Unity

I have 4 objects each located in a corner of a square. I wish to move these objects clockwise, 1 position per method call.
With the code I have atm, they all just complete the entire loop on 1 method call instead of moving only one position...
My code so far:
void SwitchPositions()
{
tempPosition1 = parent.transform.GetChild(0).GetComponent<Transform>().position;
tempPosition2 = parent.transform.GetChild(1).GetComponent<Transform>().position;
tempPosition3 = parent.transform.GetChild(2).GetComponent<Transform>().position;
tempPosition4 = parent.transform.GetChild(3).GetComponent<Transform>().position;
parent.transform.GetChild (0).GetComponent<Transform> ().position = tempPosition2;
parent.transform.GetChild (1).GetComponent<Transform> ().position = tempPosition3;
parent.transform.GetChild (2).GetComponent<Transform> ().position = tempPosition4;
parent.transform.GetChild (3).GetComponent<Transform> ().position = tempPosition1;
Debug.Log (tempPosition1);
}
If anyone has any ideas how to fix this or at least explain to me why it-s completing the entire loop in 1 method call...
Thank you!
I am really not sure how your timer works or for that matter anything wrong with your code. But I have used the coroutine where after every two seconds the blocks get switched and it happens continuously. I think this should be somewhere close to what you need.
//Predefined positions where objects to place
public Transform[] Position;
//The objects that will will be swapped in coroutines
public Transform[] ObjectsToMove;
private int ObjectIndex = 0;
private bool startupdate = true;
void Update () {
if(startupdate)
StartCoroutine(SwitchBlocks());
}
IEnumerator SwitchBlocks() {
startupdate = false;
int tempIndex = ObjectIndex;
for(int i = 0; i < ObjectsToMove.Length; i++) {
tempIndex = ObjectIndex + i;
if(tempIndex > ObjectsToMove.Length - 1)
tempIndex -= ObjectsToMove.Length;
ObjectsToMove[i].position = Position[tempIndex].position;
}
ObjectIndex++;
if(ObjectIndex > ObjectsToMove.Length - 1) {
ObjectIndex = 0;
}
yield return new WaitForSeconds(2.0f);
startupdate = true;
yield return null;
}
Hope this helps.

Cardboard Magnet Detection

I got some trouble with my unity cardboard app. May some of you guys can help me.
I have build a little Island with Animations and A second island as a main menu.
So when the apps starts, you see the Island from above and the Logo of the App.
When the user pull down the magnet button on side the app will starts another level.
I used this scripts:
http://www.andrewnoske.com/wiki/Unity_-_Detecting_Google_Cardboard_Click
Detecting Google Cardboard Magnetic Button Click - Singleton Implementation
CardboardMagnetSensor.cs and CardboardTriggerControlMono.cs
I created a script in my asset folder(CardboardMagnetSensor.cs) like in the description from Link. Than I created a second script(CardboardTriggerControlMono.cs) like in the discription an dragged it onto my CardboardMain in may Projekt.
The CardboardTriggerControlMono.cs looks like:
using UnityEngine;
using System.Collections;
public class CardboardTriggerControlMono : MonoBehaviour {
public bool magnetDetectionEnabled = true;
void Start() {
CardboardMagnetSensor.SetEnabled(magnetDetectionEnabled);
// Disable screen dimming:
Screen.sleepTimeout = SleepTimeout.NeverSleep;
}
void Update () {
if (!magnetDetectionEnabled) return;
if (CardboardMagnetSensor.CheckIfWasClicked()) {
Debug.Log("Cardboard trigger was just clicked");
Application.LoadLevel(1);
CardboardMagnetSensor.ResetClick();
}
}
}
The CarboardMagnetSensor:
using UnityEngine;
using System.Collections.Generic;
public class CardboardMagnetSensor {
// Constants:
private const int WINDOW_SIZE = 40;
private const int NUM_SEGMENTS = 2;
private const int SEGMENT_SIZE = WINDOW_SIZE / NUM_SEGMENTS;
private const int T1 = 30, T2 = 130;
// Variables:
private static bool wasClicked; // Flips to true once set off.
private static bool sensorEnabled; // Is sensor active.
private static List<Vector3> sensorData; // Keeps magnetic sensor data.
private static float[] offsets; // Offsets used to detect click.
// Call this once at beginning to enable detection.
public static void SetEnabled(bool enabled) {
Reset();
sensorEnabled = enabled;
Input.compass.enabled = sensorEnabled;
}
// Reset variables.
public static void Reset() {
sensorData = new List<Vector3>(WINDOW_SIZE);
offsets = new float[SEGMENT_SIZE];
wasClicked = false;
sensorEnabled = false;
}
// Poll this once every frame to detect when the magnet button was clicked
// and if it was clicked make sure to call "ResetClick()"
// after you've dealt with the action, or it will continue to return true.
public static bool CheckIfWasClicked() {
UpdateData();
return wasClicked;
}
// Call this after you've dealt with a click operation.
public static void ResetClick() {
wasClicked = false;
}
// Updates 'sensorData' and determines if magnet was clicked.
private static void UpdateData() {
Vector3 currentVector = Input.compass.rawVector;
if (currentVector.x == 0 && currentVector.y == 0 && currentVector.z == 0) {
return;
}
if(sensorData.Count >= WINDOW_SIZE) sensorData.RemoveAt(0);
sensorData.Add(currentVector);
// Evaluate model:
if(sensorData.Count < WINDOW_SIZE) return;
float[] means = new float[2];
float[] maximums = new float[2];
float[] minimums = new float[2];
Vector3 baseline = sensorData[sensorData.Count - 1];
for(int i = 0; i < NUM_SEGMENTS; i++) {
int segmentStart = 20 * i;
offsets = ComputeOffsets(segmentStart, baseline);
means[i] = ComputeMean(offsets);
maximums[i] = ComputeMaximum(offsets);
minimums[i] = ComputeMinimum(offsets);
}
float min1 = minimums[0];
float max2 = maximums[1];
// Determine if button was clicked.
if(min1 < T1 && max2 > T2) {
sensorData.Clear();
wasClicked = true; // Set button clicked to true.
// NOTE: 'wasClicked' will now remain true until "ResetClick()" is called.
}
}
private static float[] ComputeOffsets(int start, Vector3 baseline) {
for(int i = 0; i < SEGMENT_SIZE; i++) {
Vector3 point = sensorData[start + i];
Vector3 o = new Vector3(point.x - baseline.x, point.y - baseline.y, point.z - baseline.z);
offsets[i] = o.magnitude;
}
return offsets;
}
private static float ComputeMean(float[] offsets) {
float sum = 0;
foreach(float o in offsets) {
sum += o;
}
return sum / offsets.Length;
}
private static float ComputeMaximum(float[] offsets) {
float max = float.MinValue;
foreach(float o in offsets) {
max = Mathf.Max(o, max);
}
return max;
}
private static float ComputeMinimum(float[] offsets) {
float min = float.MaxValue;
foreach(float o in offsets) {
min = Mathf.Min(o, min);
}
return min;
}
}
And my steps:
http://www.directupload.net/file/d/3887/mtjygjan_jpg.htm
(sorry I´m not able to upload pictures here)
How ever, it wont work. When I start the app and pull down the magnet, nothing happens. May I did something wrong with switching the level over level index?
I use a nexus 4 and 5 for testing the app
Thanks allot and greetz to you!
Phillip
If you are using the Google Cardboard SDK for Unity, it currently has a bug that prevents Unity from seeing the magnet (and gyro, and accelerometer). That is probably why your script is not working. Until the bug is fixed, there is no good workaround, but you can instead use the property Cardboard.CardboardTriggered to detect if the magnet was pulled.
Update for Unity 5: The sensor bug is gone. Cardboard SDK does not block the sensors.

Categories

Resources