Unity C# scripting - infinite loop without a loop...wait what? - c#

I have just started using Unity and I am trying to put together a simple C# script that places the Prefabs (some 2D sprites) on pre-defined positions. The problem is, that whenever I apply the script to the prefabs and try to play the scene, Unity freezes and apparently generates an infinite loop that uses up all the memory (sometimes even giving me a black screen) and I have to force-kill the process in the task manager.
The code, however, is very simple and contains no loops at all:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DungeonTest : MonoBehaviour {
public Transform DungeonBuilder;
// Use this for initialization
void Start ()
{
Instantiate (DungeonBuilder, new Vector2 (1, 1), DungeonBuilder.rotation);
Instantiate (DungeonBuilder, new Vector2 (2, 2), DungeonBuilder.rotation);
Instantiate (DungeonBuilder, new Vector2 (3, 3), DungeonBuilder.rotation);
}
void Update()
{
}
}
It can be seen from the Hierarchy Window, that the Start() method creates several instances and the memory usage also goes up to 85%(!):
Unity infinite loop(?)
Please advise on what could have possibly gone wrong here. I have already watched several tutorial videos and I have read the relevant passages from the documentation, but I can't seem to figure this one out.
Thanks!

Your DungeonTest script should not be on the prefab you are instantiating. What's happening is that Start() gets executed every time the prefab gets instantiated.
So the first tile creates 3 new tiles. Each of those tiles now also make 3 new tiles and so forth to infinity.
Make a new object in the scene with the DungeonTile script and remove all scripts from the prefab you are instantiating.
This does not mean you can't have scripts on your prefab. Simply keep in mind that whatever is in your Start() method will execute as soon as your new instance is added to the scene.

Related

Loading scene in Unity makes the references in that scene null

I am making my first game in Unity and I'm trying to load the first level of it when the cutscene at the start ends. I don't know if it's possible to make the script do something after a video clip ends, so I wrote my code like this:
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
public class CutsceneEnd : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
StartCoroutine("wait");
}
IEnumerator wait()
{
yield return new WaitForSeconds(36);
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}
But the problem is not with my method of waiting for the end of the video, it's with the scene it loads. I can't move my character because all the references in the scripts are null. I have no idea what I did wrong.
Unity references should be stored in the scene file. Are you using any source control like git? If someone else did not push the changes to the .unityscene file or the .meta files for the associated scripts/prefabs, it might break the references.
I think we need some more information here.
If you referring to references (public variables) to Assets/GameObjects in the loaded scene, then you may have simply never placed the references in the first place. Unity's public variables should be saved within the Scene file and should always load with the Scene.
Double check to see if the references are actually null. Are you using an animation when you load into the Scene? This can keep you from being able to manual move anything. For example, if you animate the character when loading the scene, it could get stuck in the animation clip and you won't be able to move it.
Lastly, if you are referring to references created within the code to other scripts, objects, or variables, then these may break between scenes if you don't utilize 'DontDestroyOnLoad'.
Just spit-balling here, I need more information to correctly find the solution.

Creating scripts for each objects behaviours instead of writing everything in the object script body

I have two game objects in my example scene. Both needs to rotate constantly with a specific speed on their local space. They are just simple coins which purpose is to be collected by the player. Instead of writing the rotation script inside each Update() method, I created a script called Rotator that makes the object which it's attached to actually rotate.
I have attached the component Rotator to each object and it works just fine.
Now I have two game objects which have a component called Rotator that will make them rotate.
This is the Rotator class:
using UnityEngine;
public class Rotator : MonoBehaviour
{
[SerializeField][Range(5, 10)]
private int _rotationSpeed = 5;
[SerializeField]
private float _xAxes = 0;
[SerializeField]
private float _yAxes = 0;
[SerializeField]
private float _zAxes = 0;
void Update()
{
transform.Rotate(new Vector3(_xAxes, _yAxes, _zAxes) * _rotationSpeed * Time.deltaTime);
}
}
I think that in this way the creation flow for my game objects is much easier and modular than writing the same code above inside each element that should rotate.
My question is: will this have an impact on performance? Is it a bad idea to split behaviors into small pieces or components and then adding them to each game object that require them? Or is better, in the case of just a simple rotation, to write that line of code directly inside one game object script?
For example, I have now another game object called Player that needs to rotate on himself as well. This game object as attached already another script called PlayerController that handle the player movements by keyboard inputs. I can write this part...
transform.Rotate(new Vector3(_xAxes, _yAxes, _zAxes) * _rotationSpeed * Time.deltaTime);
...inside the PlayerController's update() method, or, like I think is better, attach the Rotator script to it.
In the last case, I will have a game object with two components:
PlayerController
Rotator
Of course, this is just an example, I don't want my player to keep spinning on himself for real, is just to explain my point of view.
So, in the end, I need to understand if this is a good practice, even if is a small line of code like the rotation is worth to create a component? And I'm referring to behaviors that are needed by more than one object, of course, I know that if it's a one-only case, it makes more sense to write it directly in the script which is already attached to the game object in question.
Long post, hope I was clear enough. Thank you all!
The way you do it is the way it should be done. The idea of unity is to use small modular components which can be reused. However having a lot of update functions can have a performance impact, especially if you use Mono and not IL2CPP.
But there are multiple solutions to this.
Use IL2CPP to remove the "virtual" call layer between C# (your code) and c++ side (engine code).
Write a behaviour manager which updates all of your scripts from the c# side.
Look at the Entity Component System and iterate over everything which needs to be rotated in one "Update function".
We actually released a game where rotating crystals had a CPU performance impact. In the end we calculated the LocalRotation value only once per frame and distributed it to all crystals.
It really depends on the size of the games and number of scripts attached. If it is a big game with dozens of long scripts you have to balance between the organization of your game and performance, since both are important. For example, it is better to call a method 1000 times inside one Update method than 1000 separate scripts with one Update method
In your case since it is simple and the rotation is the same for both objects it is better to create a common separate script as you have. If you need any change in their rotation you can change it just once plus there's no significant cost on performance.

Instantiate creates multiple clones in Unity

I have the following code:
public Rigidbody2D ball;
Vector2 sp = new Vector2(0f, 2.1f);
void Update() {
if (Input.GetKeyDown("w")) {
SpawnBall();
DestroyBall();
}
}
void SpawnBall()
{
Instantiate(ball, sp, transform.rotation);
}
void DestroyBall()
{
if (ball.transform.position.y >= -5.7f)
{
Destroy(ball);
}
}
and the code is supposed to generate a new ball every time when "w" is pressed, but for some reason it creates multiple clones and it crashes the engine. How can I create a single clone only?
And also the destroy method doesn't do anything, although it should remove the clone when it passes -5.7 on the y-axis.
Thanks in advance
Create a new script named "SpawnRigidbody" and copy and paste the below code.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnRigidbody : MonoBehaviour
{
public Rigidbody2D ball;
Vector2 sp = new Vector2(0f, 2.1f);
void Update()
{
if (Input.GetKeyDown("w")) {
SpawnBall();
}
}
void SpawnBall()
{
Debug.Log ("spawn");
GameObject go = Instantiate(ball, sp, transform.rotation).gameObject;
go.AddComponent<DestroyAfterPosition> ();
}
}
Now create another script named "DestroyAfterPosition" and copy and paste the below code.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DestroyAfterPosition : MonoBehaviour
{
void Update ()
{
if (transform.position.y <= -5.7f)
{
Destroy(gameObject);
}
}
}
Create an empty game object and attach the SpawnRigidbody and then assign your ball in the inspector.
Hope this help you.
There are a couple problems with your Destroy() statement. First, it is destroying ball which is the Rigidbody2D. You will instead want to destroy the gameobject attached to the ball:
Destroy(ball.gameObject);
Second, you are trying to destroy the ball immediately after it is created, but only if y>=-5.7f. I think instead what you are looking to do is continuously check if the ball is above that point and destroy it if it is. Since you are creating multiple balls, they will all need their own check, which means you need to create a script for the ball prefab and in the Update() check its position and Destroy(gameObject) as necessary.
I think you may want to declare ball as a GameObject at the beginning of your script as well, so you are instantiating a gameobject and not just a Rigidbody2D:
public GameObject ball;
Object.Instantiate creates a clone of the provided object, so each time it is called a new object is created. MonoBehaviour.Update is called every frame, which means it's called at least 30 times per second (usually 60 or more on a PC).
So 30+ times per second, your code checks if the w key is held down, and, if so:
A clone of ball is created
Destroy the original ball if it is beyond -5.7 on the y-axis.
Thus, you're creating 30+ ball clones per second, and none will be deleted (because the DestroyBall method only looks at the original ball, not the clones).
If you want to work with the cloned object, assign the result of Instantiate to a field:
ballClone = Instantiate(ball, sp, transform.rotation);
Then you can check if ballClone exists and skip the SpawnBall call if the clone already exists. You can also use ballClone in the DestroyBall method.
ryemoss' answer seems like it's
also important. This answer is just based on looking at your code and the public Unity docs; I don't have a lot of experience with Unity.
Your destroy call is being called once, and only immediately after creating the object.
So it's currently doing this...
User clicks button...
Create ball...
Check to see if the ball is a certain height. It's not, so ignore...
Ball drops down based on gravity or whatever force compels it.
... and is never deleted.
If you want the ball to be destroyed when it reaches a certain point, either...
Have SpawnBall return the game object, which you store and check / delete later in Update,
...or...
Create a script that just checks the object's transform.position and blows up if it's where it needs to be. Attach that to the prefab of the ball you're creating a duplicate of.

Unity Add Component

I am having some trouble in unity with adding a script I wrote to a cube in runtime. I have seen lots of other people asking about this but they seem to have fixed it.
Code:
// Insert
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
// Appearance
cube.GetComponent<Renderer>().material.SetColor("_Color", Color.yellow);
Shader transparent;
transparent = Shader.Find("UI/Default");
cube.GetComponent<Renderer>().material.shader = transparent;
//
//
// Postioning
//cxcxcxcxc
cube.transform.position = new Vector3((xpos), (ypos), zpos);
cube.transform.localEulerAngles = new Vector3(90, 0, 0);
var myScript = cube.gameObject.AddComponent<CoinCollect>();
This code creates a cube, adds shaders and positions it. I would now like to add a script to it which I have created called "CoinCollect" which is done with the last line of code. This doesn't spring up any errors but when the game is run the script isn't added. Could someone help? I looked at the documentation but it mainly showed what I was doing.
Make sure that the script name and class name within the scripts are equal. Other than that, there is nothing wrong with your code. Even the official docs do it in the same way:
Unity Docs:
// Adds the sphere collider to the game object
SphereCollider sc = gameObject.AddComponent<SphereCollider>();
And this answer on Unity Answers suggest the same way to add scripts:
AddComponent and GetComponent use the name of the class in the script, not a path to the script.
AddComponent("DestroyOnTouch");
Or preferably:
AddComponent<DestroyOnTouch>();
As this will give you a compile time error if the class cannot be found, rather than a runtime error.
Also:
How do you know that the script doesn't work? Try a simple Debug.Log() and verify, it might be some error in the script itself.
Does the code you posted in the question ever get executed? Where have you placed it? In a script? Then check that it is attached to some gameObject or gets called. Also, you might consider checking whether the method in which you wrote the code gets called.
To check the above, place a Debug.Log("The main script ran"); in the same code block and run your game. Then, check the console in Unity.

Spawning Player at certain Point in Unity

I am making a small 2d click'n'point in Unity, and what I want to do is: I want to move towards the door and when my Player steps on a game Object with an attached SceneSwitcher Script he shall go through the door, into another scene. That works fine so far. Now I don't want him to appear in the middle of the room, but on the door, where he entered the room.
using UnityEngine;
using System.Collections;
using PixelCrushers.DialogueSystem;
public class ScenSwitcher : MonoBehaviour {
public string SceneName = "";
void OnTriggerEnter2D(Collider2D other) {
SwitchScene();
}
void SwitchScene(){
LevelManager levelManager = DialogueManager.Instance.GetComponent<LevelManager>();
levelManager.LoadLevel (SceneName);
changePosition ();
Debug.Log ("Scene Wechseln nach: " + SceneName);
}
void changePosition(){
GameObject player = GameObject.Find("Player");
player.transform.position = new Vector3(12,12,0);
}
}
That is my code, it does change Scenes, but not change the position. I would appreciate any help :)
On your ChangePosition() method you are passing hardcoded values to player position and it will assume always (12,12,0) on your scene space.
You need to define a spawn manager where you will get dynamically witch spawn point in your scene you want to use.
edited:
1: Try to create a singleton GameManager ( you can find singleton pattern examples here ) (IMPORTANT: Add DontDestroyOnLoad on your GameManager Awake).
2: In your GameManager define a Vector3 NextPosition property or something like this.
3: Declare a public Vector3 Destination on your "teleport" script to set it per teleport on inspector/editor.
4: Before this line levelManager.LoadLevel (SceneName) of code set GameManager.NextPosition = this.Destination;
5: If you are not persisting your character between scenes just call on one of hes behaviours Awake() or, if he persists create a method void OnLevelWasLoaded(int level) and chage players position setting GameManager.NextPosition ( wisely testing if it is valid for the current level before ;) ).
I cant try or do better coding now because I don't have access to unity editor so I hope it helps at last start a good research to solve your problem =/.
I would think the loadLevel function destroys the current script so changePosition does not get executed? I could be wrong.
Even if it is getting executed, there is a good chance it is executed before the level load and the properties for the next scene override where it got moved to.
I forget the exact syntax but look into getting GameObjects to not be destroyed on scene change.
EDIT
Object.DontDestroyOnLoad

Categories