I am some What a beginner in the C# Buisness, and even more so a beginner in the Unity Business. I need to have a script that tells how many enemies there are (21 at the beginning), and when that number goes down, it adds 100 points. Here is what I have in my code right now:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameController : MonoBehaviour {
public float shootingInterval = 3f;
public float shootingSpeed = 2f;
public GameObject enemyMissilePrefab;
public GameObject enemyContainer;
public Player player;
public float maximumMovingInterval = 0.4f;
public float minimumMovingInterval = 0.05f;
public float movingDistance = 0.1f;
public float horizontalLimit = 2.5f;
public int ScoreRaw;
public int ScoreNew;
private int score;
private float movingInterval;
private float movingDirection = 1;
private float movingTimer;
private float shootingTimer;
private int enemyCount;
// Use this for initialization
void Start () {
movingInterval = maximumMovingInterval;
shootingTimer = shootingInterval;
enemyCount = GetComponentsInChildren<Enemy> ().Length;
}
// Update is called once per frame
void Update () {
int currentEnemyCount = GetComponentsInChildren<Enemy> ().Length;
// Shooting logic.
shootingTimer -= Time.deltaTime;
if (currentEnemyCount > 0 && shootingTimer <= 0f) {
shootingTimer = shootingInterval;
Enemy[] enemies = GetComponentsInChildren<Enemy> ();
Enemy randomEnemy = enemies[Random.Range(0, enemies.Length)];
GameObject missileInstance = Instantiate (enemyMissilePrefab);
missileInstance.transform.SetParent (transform);
missileInstance.transform.position = randomEnemy.transform.position;
missileInstance.GetComponent<Rigidbody2D> ().velocity = new Vector2 (0, -shootingSpeed);
Destroy (missileInstance, 5f);
}
//Score Logic.
ScoreRaw = (currentEnemyCount * 100);
ScoreNew = (((((ScoreRaw / 2100) - 1) * 10) * -1) * 100);
Debug.Log(ScoreNew);
// Movement logic.
movingTimer -= Time.deltaTime;
if (movingTimer <= 0f) {
float difficulty = 1f - (float) currentEnemyCount / enemyCount;
movingInterval = maximumMovingInterval - (maximumMovingInterval - minimumMovingInterval) * difficulty;
movingTimer = movingInterval;
enemyContainer.transform.position = new Vector2 (
enemyContainer.transform.position.x + (movingDistance * movingDirection),
enemyContainer.transform.position.y
);
if (movingDirection > 0) {
float rightmostPosition = 0f;
foreach (Enemy enemy in GetComponentsInChildren<Enemy>()) {
if (enemy.transform.position.x > rightmostPosition) {
rightmostPosition = enemy.transform.position.x;
}
}
if (rightmostPosition > horizontalLimit) {
movingDirection *= -1;
enemyContainer.transform.position = new Vector2 (
enemyContainer.transform.position.x,
enemyContainer.transform.position.y - movingDistance
);
}
} else {
float leftmostPosition = 0f;
foreach (Enemy enemy in GetComponentsInChildren<Enemy>()) {
if (enemy.transform.position.x < leftmostPosition) {
leftmostPosition = enemy.transform.position.x;
}
}
if (leftmostPosition < -horizontalLimit) {
movingDirection *= -1;
enemyContainer.transform.position = new Vector2 (
enemyContainer.transform.position.x,
enemyContainer.transform.position.y - movingDistance
);
}
}
}
if (currentEnemyCount == 0 || player == null) {
SceneManager.LoadScene ("Game");
}
}
}
This seems to work in the calculator, But in the game it goes to 1000 when the enemy is destroyed, and does not go up anymore after that.
yes, its because GetComponent is a very costly operation that searches through your project without using references, due to that you should use it only once in start and save it in a variable(reference) so you can then use that variable without having to call getcomponent constantly.
you then should only use enemyCount, and instead of checking over and over again how many enemies are in the scene you should make your enemies register their deaths and reduce the enemy count when they die.
Also, if GetComponent in update() is already bad, you are doing even worse by using GetComponents which if you have 21 enemies is as bad as calling get component 21 times. And you are doing that multiple times per frame in your code, its just very bad.
Of course if you had this only in this 1 script it will be alright, but if you keep coding like that one day your game will run at 10 fps and you will have to recode everything.
Ok, thank you, is there any way to edit a UI with this code, because I have tried the things in other forums, and they dont work, Here is my Hierarchy:
I need the highlighted GameObject, Text, To be updated, (Btw the script is on the GameController Empty) with the score. I tried doing it, and it goes back and forth 0, score, 0, score so fast that it shows 0
Is there any way to update the UI without it doing the back and forth?
I made a new script for the UI, also on the game controller empty:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIController : MonoBehaviour
{
[SerializeField]
private UnityEngine.UI.Text scoreUI;
public GameController gameController;
// Start is called before the first frame update
void Start()
{
gameController = FindObjectOfType<GameController>();
}
public void Update()
{
scoreUI.text = gameController.ScoreNew.ToString()
}
}
But that doesn’t seem to work either. What is the error in my code?
its not efficient to constantly update the score, instead update it only when it changes. Go back to your gamecontroller script and add this:
using UnityEngine.UI;// add this together with the others on the top of the script
// then in the variables
public Text scoreUI;
//then when you do
ScoreNew = (21 - currentEnemyCount )*100;
//add also
scoreUI.text = ScoreNew.ToString();
}
Sadly, this code does not work either, It fixed the back and forth problem, but is now just doing nothing, the UI wont update. Here is the current script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class GameController : MonoBehaviour {
public float shootingInterval = 3f;
public float shootingSpeed = 2f;
public GameObject enemyMissilePrefab;
public GameObject enemyContainer;
public Player player;
public float maximumMovingInterval = 0.4f;
public float minimumMovingInterval = 0.05f;
public float movingDistance = 0.1f;
public float horizontalLimit = 2.5f;
public int ScoreRaw;
public int ScoreNew;
public Text scoreUI;
private int score;
private float movingInterval;
private float movingDirection = 1;
private float movingTimer;
private float shootingTimer;
private int enemyCount;
// Use this for initialization
void Start () {
movingInterval = maximumMovingInterval;
shootingTimer = shootingInterval;
enemyCount = GetComponentsInChildren<Enemy> ().Length;
}
// Update is called once per frame
void Update () {
int currentEnemyCount = GetComponentsInChildren<Enemy> ().Length;
// Shooting logic.
shootingTimer -= Time.deltaTime;
if (currentEnemyCount > 0 && shootingTimer <= 0f) {
shootingTimer = shootingInterval;
Enemy[] enemies = GetComponentsInChildren<Enemy> ();
Enemy randomEnemy = enemies[Random.Range(0, enemies.Length)];
GameObject missileInstance = Instantiate (enemyMissilePrefab);
missileInstance.transform.SetParent (transform);
missileInstance.transform.position = randomEnemy.transform.position;
missileInstance.GetComponent<Rigidbody2D> ().velocity = new Vector2 (0, -shootingSpeed);
Destroy (missileInstance, 5f);
}
//Score Logic.
ScoreRaw = (currentEnemyCount * 100);
ScoreNew = (21 - currentEnemyCount) * 100;
scoreUI.text = ScoreNew.ToString();
Debug.Log(ScoreNew);
// Movement logic.
movingTimer -= Time.deltaTime;
if (movingTimer <= 0f) {
float difficulty = 1f - (float) currentEnemyCount / enemyCount;
movingInterval = maximumMovingInterval - (maximumMovingInterval - minimumMovingInterval) * difficulty;
movingTimer = movingInterval;
enemyContainer.transform.position = new Vector2 (
enemyContainer.transform.position.x + (movingDistance * movingDirection),
enemyContainer.transform.position.y
);
if (movingDirection > 0) {
float rightmostPosition = 0f;
foreach (Enemy enemy in GetComponentsInChildren<Enemy>()) {
if (enemy.transform.position.x > rightmostPosition) {
rightmostPosition = enemy.transform.position.x;
}
}
if (rightmostPosition > horizontalLimit) {
movingDirection *= -1;
enemyContainer.transform.position = new Vector2 (
enemyContainer.transform.position.x,
enemyContainer.transform.position.y - movingDistance
);
}
} else {
float leftmostPosition = 0f;
foreach (Enemy enemy in GetComponentsInChildren<Enemy>()) {
if (enemy.transform.position.x < leftmostPosition) {
leftmostPosition = enemy.transform.position.x;
}
}
if (leftmostPosition < -horizontalLimit) {
movingDirection *= -1;
enemyContainer.transform.position = new Vector2 (
enemyContainer.transform.position.x,
enemyContainer.transform.position.y - movingDistance
);
}
}
}
if (currentEnemyCount == 0 || player == null) {
SceneManager.LoadScene ("Game");
}
}
}
Thanks for the help, that seemed to have fixed that!
I have another game that I am making, a breakout clone, and I am having a problem where the script is detecting a collision, but only once, and never again, here is the script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
Rigidbody _rigidbody;
public GameObject thisGameObject;
public GameObject laserPrefab;
public GameObject[] powerUpSPrefabs;
public int currentPowerupNumber;
public string currentPowerupName = "none";
void Start()
{
_rigidbody = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
_rigidbody.MovePosition(new Vector3(Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, 0, 50)).x, -17, 0));
if (currentPowerupName == "Sticky")
{
Ball.Instance._rigidbody.velocity = Vector3.zero;
Ball.Instance._rigidbody.transform.position = new Vector3(thisGameObject.transform.position.x, -19, 0);
Invoke("Launch", 10f);
// Return to previous mesh
ReturnToPreviousMesh();
//reset current powerup name
currentPowerupName = "none";
}
else if (currentPowerupName == "Laser")
{
//shoot lasers
for (int i = 0; i < 100; i++)
{
//shoot lasers
Instantiate(laserPrefab);
}
//return to previous mesh.
ReturnToPreviousMesh();
//reset current powerup name
currentPowerupName = "none";
}
else if (currentPowerupName == "none")
{
Debug.Log("No current powerup");
}
}
private void OnTriggerEnter(Collider other)
{
currentPowerupNumber = Random.Range(0, powerUpSPrefabs.Length - 1);
currentPowerupName = powerUpSPrefabs[currentPowerupNumber].name;
thisGameObject.GetComponent<MeshFilter>().sharedMesh = powerUpSPrefabs[currentPowerupNumber].GetComponent<MeshFilter>().sharedMesh;
thisGameObject.GetComponent<MeshCollider>().sharedMesh = powerUpSPrefabs[currentPowerupNumber].GetComponent<MeshCollider>().sharedMesh;
}
void Launch()
{
Ball.Instance._rigidbody.velocity = Vector3.up * 20f;
}
void ReturnToPreviousMesh()
{
thisGameObject.GetComponent<MeshFilter>().sharedMesh = powerUpSPrefabs[4].GetComponent<MeshFilter>().sharedMesh;
thisGameObject.GetComponent<MeshCollider>().sharedMesh = powerUpSPrefabs[4].GetComponent<MeshCollider>().sharedMesh;
}
}
the problem is the OnTriggerEnter function, it seems to work the first time, but stop working after this. Is there a solution to this?