Is this script resource intensive? If so, how do one improve? - c#

This is a simple script that turns a saw blade in my game. The problem is there is approx 18 active blades on the scene, at a time. I am trying to eliminate any probability of lag. This made me wonder if using such a script in "Update", can cause lag?
public class SawBladesRotate : MonoBehaviour
{
public bool GameOver;
public GameObject Player;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
GameOver = Player.GetComponent<PlayerController>().GameOver;
if(GameOver == false)
{
transform.Rotate(new Vector3(0, 0, -45) * Time.deltaTime);
}
}
}

Put this on top of the Start method as class field
private PlayerController playerController;
and this into Start:
playeController = Player.GetComponent<PlayerController>()
Then re-use the reference:
private void Update()
{
if(playerController.GameOver) return;
//...
}
The rest is fine but ofcourse it always depends completely on your usecase.
Even more efficient it would be to directly reference the Component within unity:
[SerializeField] private PlayerController playerController;
Now you can simply drag&drop the Player GameObject into that field in the Inspector and can get rid of the GetComponent call.

Related

Player movement doesn't work after reloading the scene in Unity

Using Unity 2021.3.16f1.
I followed this tutorial from Brackeys to make the pause menu for my flappybird like game, my player movement stops working after I exit the main game to the main menu and return to the game.
Part of player code responsible for movement:
public class birdScript : MonoBehaviour
{
public Rigidbody2D myRigidbody;
public float flapStrength;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
myRigidbody.velocity = Vector2.up * flapStrength;
FindObjectOfType<AudioManager>().Play("jump");
}
}
}
Part of pause menu code responsible for loading the main menu scene:
using UnityEngine.SceneManagement;
public class pauseMenu : MonoBehaviour
{
public static bool gameIsPaused = false;
public GameObject pauseMenuUI;
public void loadMenu()
{
Time.timeScale = 1f;
SceneManager.LoadScene("Title");
}
}
Part of code responsible for loading the main game scene:
using UnityEngine.SceneManagement;
public class playButton : MonoBehaviour
{
public void loadLevel()
{
SceneManager.LoadScene("Main game");
}
}
I tried changing the play button code that loads the main game, in the scene hierarchy my main menu scene is 0 and my main game scene is 1. The play button is on the main menu.
I turned this
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
Into this
SceneManager.LoadScene("Main game");
But nothing happened, I tried searching on google but I have no idea how to search for the right answers.
Your problem has nothing to do with scene loading. That said your code for loading level is fine as it was:
using UnityEngine.SceneManagement;
public class playButton : MonoBehaviour
{
public void loadLevel()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}
Issue could be in your pauseMenu script but you only provided a part of the code. You need to make sure your Time.timeScale is set to 1f when the level scene is loaded.
I would suggest setting the default value for flapStrength. Also in void Start there is Time.timeScale set to 1f just to be sure. You could do this in the pauseMenu script.
public class birdScript : MonoBehaviour
{
public Rigidbody2D myRigidbody;
public float flapStrength = 10f;
void Start()
{
Time.timeScale = 1f;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
myRigidbody.velocity = Vector2.up * flapStrength;
FindObjectOfType<AudioManager>().Play("jump");
}
}
}
I hope the answer helped. The issue is that the problem could be anywhere. It is necessary to go through all the values and examine the behavior of the player. Didn't an error appear that would stop the game? If the problem persists, you would need to edit the question and add more information.

disabling gameObjects after seconds using object pooling and coroutines

I'm developing a simple VR shooter in unity, i use object pooling for the bullets (Lasers here)
this is the ObjectPool script, here i istantiate a list of bullets in the start() method and disable them.
public class ObjectPool : MonoBehaviour
{
public static ObjectPool instance;
private List<GameObject> pooledObject = new List<GameObject>();
private int amountToPool = 20;
[SerializeField] private GameObject laserPrefab;
private void Awake()
{
if (instance == null)
{
instance = this;
}
}
// Start is called before the first frame update
void Start()
{
for (int i = 0; i < amountToPool; i++)
{
GameObject obj = Instantiate(laserPrefab);
obj.SetActive(false);
pooledObject.Add(obj);
}
}
public GameObject GetPooledObject()
{
for (int i = 0; i < pooledObject.Count; i++)
{
if (!pooledObject[i].activeInHierarchy)
{
return pooledObject[i];
}
}
return null;
}
}
This is the Script attached to the gun where i get the pooled bullet and set it to active
public class FireLaserGun : MonoBehaviour
{
public GameObject laserBeamModel;
public Transform laserSpawnPoint;
// Start is called before the first frame update
public void FireGun()
{
GameObject laser = ObjectPool.instance.GetPooledObject();
if (laser != null)
{
laser.transform.position = laserSpawnPoint.position;
laser.transform.rotation = laserSpawnPoint.rotation;
laser.SetActive(true);
Debug.Log("BOOM");
}
else
{
Debug.Log("Laser is null");
}
}
}
I'm trying to disable the bullet after two seconds was fired using a coroutine in the script that moves the bullets:
public class LaserBeamMove : MonoBehaviour
{
private Rigidbody rb;
public float thrust = 10.0f;
float waitTime = 2.0f;
private IEnumerator coroutine;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
coroutine = WaitToDisable(waitTime);
StartCoroutine(coroutine);
}
void FixedUpdate()
{
rb.velocity = transform.forward * thrust;
}
private IEnumerator WaitToDisable(float waitTime)
{
yield return new WaitForSeconds(waitTime);
gameObject.SetActive(false);
Debug.Log("bullet disabled after " + waitTime + "seconds");
}
}
Strangely first seconds of the game everything seems fine, all the bullets start as inactive and every bullet becomes active when fired and inactive after two seconds.
After some seconds bullets dont become inactive anymore (actually only some of them do).
this is a screenshot of the console log, when i fire i print "BOOM" and when a bullet becomes inactive i print "bullet disabled after 2 seconds"
As you can see this down't work for every bullet and i don't understand why.
Am i doing something wrong with the courutine?
https://i.stack.imgur.com/rMJgg.png
Take the code you have in void Start() and move it to a new void OnEnable. The reason is that Start is only called once, the first time the object holding the script is enabled (e.g when the scene loads, when you instantiate a bullet etc), OnEnable is called every time the object is enabled, which is what you want here. You could also use Awake but IMHO that's best kept separately for initialisation stuff only so you can keep the code tidy and separate out game logic from init logic.

FixedUpdate slightly fired up even if scene is paused with Time.timeScale = 0f

I'm a newbie in unity & c# . The FixedUpdate function does some Rigidbody action (here pushing the cube over the z axis)
However the scene works fine in development (cube starts at z = 0 )
The problem is in build , the cube starts at certain distance means z = 5 or 6
According to my understanding , I believe this is caused due to FixedUpdate being fired for some milli seconds before it notice Time.timeScale = 0f in PauseGame function
And when it notice this it behave as expected.
But then on Restart function when being called up (called by a button) using SceneManager.LoadScene function cube starts with z= 0 in build.
clip at development and clip with bug at build
where did I go wrong, thanks in advance.
image of bug at build, cube starts at certain position
image of development which working fine as expected cube starts at z = 0
public class GameController : MonoBehaviour
{
public GameObject tapToStart;
private void Start()
{
tapToStart.SetActive(true);
PauseGame();
}
private void Update()
{
StartGame();
}
public void Restart()
{
SceneManager.LoadScene("Game");
}
public void PauseGame()
{
Time.timeScale = 0f;
}
public void StartGame()
{
tapToStart.SetActive(false);
Time.timeScale = 1f;
}
}
public class PlayerScript : MonoBehaviour
{
public new Rigidbody rigidbody;
public float force;
private void Update(){}
private void FixedUpdate()
{
rigidbody.AddForce(0, 0, force * Time.deltaTime);
}
}
You are correct! FixedUpdate is called at relatively'fixed' intervals. It doesn't matter if something is done in your other code and you need it to get updated immediately. FixedUpdate will just keep pressing on at a a regular interval.
You can verify your suspicions by placing the line:
rigidbody.AddForce(0, 0, force * Time.deltaTime);
Inside your Update() function.
Now, onto the solution. I suggest that create place the following code in your currently empty Update() function:
if(Time.timeScale = 0f){
rigidbody.velocity = Vector3.zero;
rigidbody.angularVelocity = Vector3.zero;
}
Now, as soon as you press the pause button, it will be detected on the next frame of Update() and the object will be frozen. This will likely occur even before FixedUpdate is called. Of course, if you want to 'restore' the velocity and angular velocity when you unpause, you will need to store that data, and then set it back up when you unpause the game.
The issue has been solved .. I can't pause on start,
need to create a Boolean in PlayerScript.
public class PlayerScript : MonoBehaviour
{
public new Rigidbody rigidbody;
public float force;
public bool isGameStart; // this Boolean
private void Update(){}
private void FixedUpdate()
{
if (isGameStart)
{
rigidbody.AddForce(0, 0, force * Time.deltaTime);
}
}
}
and using that bool value , we need to use rigidbody component.
and in StartGame function in GameController we must assign true to that bool value
public class GameController : MonoBehaviour
{
public GameObject tapToStart;
public PlayerScript playerScript;
private void Start()
{
tapToStart.SetActive(true);
PauseGame();
}
private void Update()
{
StartGame();
}
public void Restart()
{
SceneManager.LoadScene("Game");
}
public void PauseGame()
{
Time.timeScale = 0f;
}
public void StartGame()
{
tapToStart.SetActive(false);
Time.timeScale = 1f;
playerScript.isGameStart = true;
}
}
this worked for me , this solution was given by a Youtuber "Unity city".. credits to him.

reference from various scripts causing problems [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 1 year ago.
For some reason when I'm in the normal view in-game I am able to link the scripts that I need to call in order to make an animation like so
however, whenever I start the game it automatically removes them from the slots, and it doesn't work
I am completely clueless as to why this may be happening and unity keep giving me errors that say that I'm not setting an instance to my script I really have no clue why this may be happening.
I have 3 scripts that I'm working with that is giving me the problem
one is the main script for the enemy vision (I am referencing the other scripts in this one)
the second is the enemy animation script which makes him go from idle to attack and the third is an animation for the gun model since I had to make it follow the hands of the enemy
scripts attached bellow
1st script(enemyAI):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyAi : MonoBehaviour
{
public bool detected;
GameObject target;
public Transform enemy;
public GameObject Bullet;
public Transform shootPoint;
public float shootSpeed = 10f;
public float timeToShoot = 1f;
public EnemyAni Animation;
public GunAni GunAnimation;
void Start()
{
Animation = GetComponent<EnemyAni>();
GunAnimation = GetComponent<GunAni>();
}
public void Update()
{
//makes the enemy rotate on one axis
Vector3 lookDir = target.transform.position - transform.position;
lookDir.y = 0f;
//makes enemy look at the players position
if (detected)
{
enemy.LookAt(target.transform.position, Vector3.up);
enemy.rotation = Quaternion.LookRotation(lookDir, Vector3.up);
}
if (detected == true)
{
Animation.LookPlayer = true;
GunAnimation.ShootPlayer = true;
}
if (detected == false)
{
Animation.LookPlayer = false;
GunAnimation.ShootPlayer = false;
}
}
//detects the player
void OnTriggerEnter(Collider other)
{
if (other.tag == "Player")
{
detected = true;
target = other.gameObject;
}
}
void OnTriggerExit(Collider other)
{
if (other.tag == "Player")
{
detected = false;
}
}
}
2nd Script (EnemyAnimation):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyAni : MonoBehaviour
{
public Animator animator;
public bool LookPlayer;
public void Start()
{
animator = GetComponent<Animator>();
}
public void Update()
{
if (LookPlayer == true)
{
animator.SetBool("IsShootingStill", true);
}
else
{
animator.SetBool("IsShootingStill", false);
}
}
}
3rd script (GunAnimation):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunAni : MonoBehaviour
{
public Animator animator;
public bool ShootPlayer;
public void Start()
{
animator = GetComponent<Animator>();
}
public void Update()
{
if (ShootPlayer == true)
{
animator.SetBool("IsShooting", true);
}
else
{
animator.SetBool("IsShooting", false);
}
}
}
Your code:
void Start()
{
Animation = GetComponent<EnemyAni>();
GunAnimation = GetComponent<GunAni>();
}
Searches the GameObject that holds the EnemyAI script for EnemyAni and GunAni. If the GameObject doesn't have those it will return null.
Sounds like you want to remove that code.
Note that if the scripts exist on a child of that GameObject you will need to use GetComponentInChildren
There are some things to review, good practices:
GameObject target is private for C#, but it is better to put private before.
variable names like Bullet Animation GunAnimation should always begin with lowercase, because they might be confused with a Class Name (search in internet for CamelCase and PascalCase).
Name should be clear. EnemyAni Animation is not clear enough, because Animation maybe any animation (player, enemy, cube, car, etc.).
It is better enemyAnimation to be clear, as you did with GunAnimation (only just with lower case at beginning)
As slaw said, the Animation must be in the GO attached.
about last item
Let's say you have an empty GO (GameObject) called GameObject1 and you attach EnemyAi script to it.
In Runtime (when game mode begins), it will try to find Animation = GetComponent<EnemyAni>();, but it won't find it
Why?
Because GetComponent<> searches for the Component(Class) that is in the <> (in this case EnemyAni) in its GO (in this case, GameObject1), but the unique script attached to GameObject1 is EnemyAI.
So, you have 3 options:
Attach EnemyAni to GameObject1
Create another GO (for example, GameObjectEnemyAni), attach the script EnemyAni and drag and drop GameObjectEnemyAni to GameObject1 and delete Animation = GetComponent<EnemyAni>(); in Start
Keep in mind: if you leave that line of code, instead of getting the script EnemyAni from GameObjectEnemyAni, it will run the code Animation = GetComponent<EnemyAni>(); in Start, and obviously, it won't find it
Create events. It's a really good practice for avoiding code tight coupling, but that is more advanced stuff.

Instances of prefab aren't being controlled separately

I have multiple enemies set up in a level, all using the same behaviour and animator scripts. When I hit or kill one of them, all of them get hit or killed. I need them to function as separate instances.
I have tried referencing only an instance of the script:
private GoblinBehaviour goblin;
goblin = GetComponent<GoblinBehaviour>();
goblin.IncrementHits(1);
But that doesn't work. An error arises that says that script can't be accessed with an instance and needs a type instead.
code for hit detection script:
public class RangedDetection : MonoBehaviour
{
private GoblinBehaviour goblin;
void OnTriggerEnter(Collider other)
{
//on colliding destroy rocks after its life time
Destroy(gameObject, rockLifeTime);
if (other.gameObject.tag.Equals("Enemy"))
//if (other.tag == "Enemy")
{
Destroy(other.gameObject);
}
else if (other.gameObject.tag.Equals("Goblin"))//goblin should play
death animation
{
goblin = GetComponent<GoblinBehaviour>();
goblin.IncrementHits(1);
GetComponent<BoxCollider>().enabled = false; //Removing hit
collider so it only hits target once.
}
}
}
Simplified Code for goblin script:
public class GoblinBehaviour : MonoBehaviour
{
Transform player;
public static bool isDead;
public static bool isPunching;
public static bool isHit;
public GameObject particles;
public Animator anim;
public void IncrementHits(int hitCount)
{
isHit = true;
hits += hitCount;
if (hits >= 2) isDead = true;
}
void Die()
{
Instantiate(particles, transform.position, transform.rotation);
Destroy(gameObject);
}
void updateAnim()
{
anim.SetBool("Punch_b", isPunching);
anim.SetBool("hit", isHit);
}
}
Things should animate and act separately, I'm not sure how to reference only the current instance of the script.
While your code is incomplete and the problem cannot be said for sure, it looks like you are using statics improperly.
Static properties are instance analagous. In other words, all of your goblin instances share any static properties (isPunching, isHit, isDead, etc.). Making these values static allows you to reference them directly without having to obtain the instance you're affecting, but results in you updating all goblins at once.
Your solution will involve removing the static modifier from your GoblinBehaviour properties unless the properties are meant to be shared across all instances.

Categories

Resources