So I have been trying to get a difficulty system to work in my game but I have encountered a problem, my float keeps on getting reset to 1, I have the larger script on a prefab that is in another prefab and the small script is one I have on the "DifficultyController". I have tried looking through the files but I cannot find any instance of the number 1 I have also tried making a new prefab to see if that was the problem but still the number 1!
The script in the prefab:
public float health;
public GameObject deathEffect;
public float difficulty;
private float destroy;
private void Update()
{
if (health <= 0)
{
//Instantiate(deathEffect, transform.position, Quaternion.identity);
Destroy(gameObject);
}
}
private void Start()
{
EnemyDifficulty(difficulty);
}
public void TakeDamage (float damage)
{
health -= damage;
}
public void EnemyDifficulty(float chance)
{
Debug.Log(chance);
destroy = Random.Range(0, 50);
if (destroy <= 3) { Destroy(gameObject); }
chance = Random.Range(chance / 2, chance * 2);
Debug.Log(chance);
if (chance <= 8 && chance >= 3)
{
BasicEnemy();
}
if (chance <= 20 && chance >= 8)
{
MediumEnemy();
}
if(chance <= 3)
{
Destroy(gameObject);
Debug.Log("Destroeyed");
}
}
public void BasicEnemy()
{
Debug.Log("basic");
}
public void MediumEnemy()
{
Debug.Log("medium");
}
}
Code in the "DifficultyController"
public Enemy enemy;
public float difficultye = 5;
// Update is called once per frame
void Update()
{
enemy.difficulty = difficultye;
}
You haven't specified which variable keeps getting set to one, but I assume it's either health, difficulty, or difficultye. This is because they are public, meaning that they can be edited in the inspector. If the inspector value differs from the code value, then the inspector value will override that of our code.
You can fix this by changing the variables in editor, marking them as [System.NonSerialized], or setting them to private, depending on which best fits your needs.
Related
So essentially I've:
Instantiated a new prefab of an object
Set all the variables in the transform of the prefab
Copied the script for the initial capsule (which works) into the prefab spawning script
And yet my prefabs for some reason really do not want to follow the path around, how do I make them follow the path?
public class PathFollower : MonoBehaviour
{
public float movementSpeed;
//private float pathProgress = 0;
public GameObject objectToSpawn;
public Transform[] positionPoint;
[Range(0,1)]
public float value;
void Start()
{
Debug.Log(iTween.PathLength(positionPoint));
}
void Update()
{
movementSpeed = 10f;
if (value < 1)
{
value += Time.deltaTime / movementSpeed;
}
iTween.PutOnPath(objectToSpawn, positionPoint, value);
}
private void OnDrawGizmos()
{
iTween.DrawPath(positionPoint,Color.green);
}
}
public class deployCapsule : MonoBehaviour
{
public float movementSpeed;
public Transform[] positionPoint;
public GameObject CapsulePrefab;
[Range(0, 1)]
public float value;
//movementspeed = 10f;
// Start is called before the first frame update
void Start()
{
}
void Update()
{
GameObject a = Instantiate(CapsulePrefab) as GameObject;
a.transform.position = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width - 20, Screen.height - 20, 10));
//if (Input.GetKeyDown(KeyCode.Space))
//{
a.GetComponent<PathFollower>().positionPoint = positionPoint;
//}
movementSpeed = 10f;
if (value < 1)
{
value += Time.deltaTime / movementSpeed;
}
iTween.PutOnPath(a, positionPoint, value);
}
}
Where the scripts are attached, also shows transform arrays:
Hard to say what is the exact problem, but from the image no 3 I see that value of variable value is 1 which may cause the problem.
It should be 0 so the object is at the beginning of the path.
You can use Start method to explicitly set the value of the value variable
Despite this, I wouldn't recommend you to instantiate game objects every frame (in Update method) because it's not efficient and probably will never have such use case.
I am trying to update the lives of my player in a brick breaker game, however I find myself unable to update the text UI responsible for displaying the score (it always displays 3 lives remaining).
Is there any way to fix this issue?
Here is the relevant code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
// Start is called before the first frame update
int lives;
public bool isDead;
public GameObject[] bricks;
int numberBricks;
public Text livesText;
private void Start()
{
isDead = false;
lives = 3;
livesText.text = "lives left: " + lives.ToString();
Debug.Log(lives);
}
// Update is called once per frame
void Update()
{
livesText.text = "lives left: " + lives.ToString();
numberBricks = GameObject.FindGameObjectsWithTag("brick").Length;
if (numberBricks == 0) {
passLevel();
}
}
private void passLevel() {
}
public void ReduceHealth() {
lives--;
Debug.Log(lives);
}
public void CheckDeath() {
if (lives == 0) {
isDead = true;
}
}
}
the ReduceHealth() function is called at
Ball.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ball : MonoBehaviour
{
// Start is called before the first frame update
Vector3 Direction;
int speedFactor;
float minX;
float maxX;
float additionalForce;
Rigidbody2D rb;
public GameObject paddle;
public AudioSource collisionSound;
public GameObject gameManager;
GameManager gm;
void Start()
{
float sign = Mathf.Sign(Random.Range(-1,1));
minX = -10f * sign;
maxX = -2f * sign;
Direction.x = Random.Range(minX,maxX) ;
Direction.y = -40f;
speedFactor = 1;
rb = GetComponent<Rigidbody2D>();
gm = gameManager.GetComponent<GameManager>();
}
// Update is called once per frame
void Update()
{
transform.position += Direction * speedFactor * Time.deltaTime;
}
void Reset() {
transform.position = new Vector3(0.14f,-4.58f,0f);
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.tag == "Player" || other.gameObject.tag == "border-vertical") {
Direction.y *=-1;
} else if(other.gameObject.tag=="border-bottom") {
gm.ReduceHealth();
Reset();
}else {
Direction.x *= -1;
}
}
}
and here is the Screenshot of my inspector (GameManager object)
any help would be appreciated.
It looks like the function ReduceHealth() has no references. I created a button to see if the lives are reduced or not, it's working. Here is an attached image of OnClick() in button inspector
A few tips
It is not a good practice to update the text in Update function, instead do it in the function where the value of lives is changing. Also check if the lives == 0 in the same function only, there is no need for the function CheckDeath().
public void ReduceHealth() {
lives--;
livesText.text = "lives left: " + lives.ToString();
if (lives == 0) {
isDead = true;
}
}
It is also not a good practice to keep checking the length of the number of bricks at every frame, instead have a check for it in whichever function the breaking of bricks is dealt with.
For eg when the health of the brick reaches zero, get a reference to the GameManager script and do the length check there.
im working on simple battle system in Unity, I have already added damage system but I want to upgrade it and push enemy back whenever he gets damage. I have made some tries but nothing worked correctly. Here is a litte bit of my code. I already appreciate for any suggestions.
public class EnemyTakeDmg : MonoBehaviour
{
public int health = 8;
private Rigidbody myBody;
public float knockBackForce = 5f;
public event System.Action OnDeath;
private void Start()
{
myBody = GetComponent<Rigidbody>();
}
public void TakeDamage(int damage)
{
health -= damage; // TAKING DAMAGE
// PUSHING ENEMY BACK ???
if(health <= 0)
{
Die();
}
}
public void Die()
{
if(OnDeath != null)
{
OnDeath();
}
Destroy(gameObject);
}
}
I'd suggest to somehow get a reference to the object that made the shot. This is necessary to calculate direction of the knockback.
After that, it is pretty easy!
public void TakeDamage(int damage, Transform shooter)
{
//Take damage
health -= damage;
//Knockback
Vector3 direction = (transform.position - shooter.position).normalized;
myBody.AddForce(direction * knockBackForce);
}
The direction might be the opposite, so you may need to switch shooter.position and transform.position.
I'm a beginner making my first game in Unity, following Unity's Create With Code course. I'm creating a shooter game that will use hand tracking. I haven't set up hand tracking yet so i created an OnTrigger input that explodes objects when I hit space. I created the spawn manager below to spawn waves of enemy attack, but they all the waves are spawning enemies too fast. It seems like they're spawning automatically instead of when the first wave has been destroyed.
Is there an easier way to spawn at a slower rate? Or spawn only when there are no more enemies alive?
EDIT: Added Attack script below
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnAttack : MonoBehaviour
{
public GameObject Trumps;
private float spawnRange = 9;
public int enemyCount;
public int waveNumber = 1;
void Start()
{
SpawnEnemyWave(waveNumber);
//InvokeRepeating("GenerateSpawnPosition", startDelay, randomInterval);
}
// Update is called once per frame
void Update()
{
enemyCount = FindObjectsOfType<Attack>().Length;
if(enemyCount == 0)
{
waveNumber++;
SpawnEnemyWave(waveNumber);
}
}
void SpawnEnemyWave(int enemiesToSpawn)
{
for (int i = 0; i < enemiesToSpawn; i++)
{
Instantiate(Trumps, GenerateSpawnPosition(), Trumps.transform.rotation);
}
}
private Vector3 GenerateSpawnPosition()
{
float spawnPosX = Random.Range(-spawnRange, spawnRange);
float spawnPosZ = Random.Range(-spawnRange, spawnRange);
Vector3 randomPos = new Vector3(spawnPosX, 0, spawnPosZ);
return randomPos;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Attack : MonoBehaviour
{
public float speed = 0.5f;
public GameObject Player;
private Rigidbody enemyRb;
// Start is called before the first frame update
void Start()
{
enemyRb = GetComponent<Rigidbody>();
Player = GameObject.Find("Player");
this.transform.LookAt(Player.transform);
}
// Update is called once per frame
void Update()
{
Vector3 lookDirection = (Player.transform.position - transform.position).normalized;
enemyRb.AddForce(lookDirection * speed);
transform.Translate(Vector3.forward * Time.deltaTime * speed);
}
private void OnTriggerEnter(Collider other)
{
Destroy(gameObject);
Debug.Log("Game Over");
}
}
I guess that you can use the invoke method:
Invoke("NameOfTheMethod", 1f)
What this method does is that it waits a certain amount of seconds before calling a method. You have to write the name of the method in quotation marks and then select how long you want to wait before the method is called (The "1f" represents the delay in seconds.) In your case, you can make the script wait before spawning enemies.
I don't know your Attack script but I would use something like
public class Attack : MonoBehaviour
{
public static readonly HashSet<Attack> AliveAttackInstances = new HashSet<Attack>();
private void Awake()
{
AliveAttackInstances.Add(this);
}
private void OnDestroy()
{
AliveAttackInstances.Remove(this);
}
}
So you can all the time in a cheaper way check how many and which enemies are alive like
public class SpawnAttack : MonoBehaviour
{
// I would change this type here to make sure your spawned prefab actually
// HAS an Attack attached .. otherwise your enemyCount will always be 0
public Attack Trumps;
...
void Update()
{
if(Attack.AliveAttackInstances.Count == 0)
{
waveNumber++;
SpawnEnemyWave(waveNumber);
}
}
Then in order to add a certain delay before spawning the next wave you could use a simple timer like
public class SpawnAttack : MonoBehaviour
{
public Attack Trumps;
[SerializeField] private float delay = 1f;
private float timer;
...
void Update()
{
if(Attack.AliveAttackInstances.Count == 0)
{
timer -= Time.deltaTime;
if(timer <= 0)
{
timer = delay;
waveNumber++;
SpawnEnemyWave(waveNumber);
}
}
}
Try to use Coroutine.
Here's a video about Coroutines: https://www.youtube.com/watch?v=qolMYyq0nX0
My example:
public class Spawn : MonoBehaviour {
private float TimeToWait = 3f;
public int enemyCount = 0;
public int waveNumber = 0;
public GameObject enemy;
void Start()
{
StartCoroutine(SpawnEnemyWave(waveNumber));
}
void Update()
{
if (enemyCount == 0)
{
waveNumber++;
StartCoroutine(SpawnEnemyWave(waveNumber));
}
if (Input.GetMouseButtonDown(0))
{
enemyCount--;
}
}
IEnumerator SpawnEnemyWave(int enemiesToSpawn)
{
//"Things to do before the seconds."
for (int i = 0; i < enemiesToSpawn; i++)
{
enemyCount++;
}
yield return new WaitForSeconds(TimeToWait); // at this example we wait 3 seconds (float TimeToWait = 3f;)
//"Things to do after the seconds."
for (int i = 0; i < enemiesToSpawn; i++)
{
Debug.Log("New Enemy!");
Instantiate(enemy, transform.position, Quaternion.identity);
}
}
}
still reasonably new to Unity and c# and so this may be a small and insignificant question to some but it has stumped me. I'm trying to add a number to my score when a game object is destroyed, I have a target script attached to a target prefab.
using UnityEngine;
public class Target : MonoBehaviour
{
public float health = 50f;
private float TotalCubesHit = 0;
public void TakeDamage (float amount){
health = health - (amount);
if (health <= 0f) {
TotalCubesHit += 1;
Die();
}
}
public void Die()
{
Destroy(gameObject);
Debug.Log(TotalCubesHit);
}
}
For some reason, it never gets above one and I'm unsure as to why? any help would be appreciated
Simplest way would be to use a static counter. This makes it a class field which is "shared" among all instances or better said is simply not bound to any instance:
public class Target : MonoBehaviour
{
public float health = 50f;
private static float totalCubesHit = 0;
// Optionally add a public ReadOnly property
// for allowing external access
public static float TotalCubesHit => totalCubesHit;
public void TakeDamage (float amount)
{
health -= amount;
if (health <= 0f)
{
totalCubesHit++;
Die();
}
}
public void Die()
{
Destroy(gameObject);
Debug.Log(totalCubesHit);
}
}
A useful side-effect (and therefore I added the public ReadOnly property): You can now also access it from any other script by simply using
Target.TotalCubeHits
while being sure it can not be changed by other scripts.
It is because it is incremented only once .. thats right before it dies.
public void TakeDamage (float amount){
health = health - (amount);
TotalCubesHit += 1; // <-- Move that outside of the if statement
if (health <= 0f) {
Die();
}
}
**EDIT: I have been re-reading your question and one thing i dont quite follow is: "I'm trying to add a number to my score when a game object is destroyed". If your intent is to keep track of the totalCubeHits from different Target objects that are destroyed, this wont work unless you are re-using the same Target object. If you create a new instance each time, TotalCubeHits will always start from 0.
Store the TotalCubeHits outside or initialize it with a number from previous object.