does anyone know how to improve this code?
I have this mechaniŃ : ball player creates a bullet ball in front of him and reduces his size, need to disable the reduction of the player's ball if the bullet is already created and flies forward.
I will be glad to any ideas
using UnityEngine;
public class DecreasePlayerBall : MonoBehaviour
{
[SerializeField] private float speedDecrease;
[SerializeField] private int healthPlayer;
public GameOver gameOver;
void Update()
{
if (GameObject.FindWithTag("ballBullet").transform.position.z <= 25.3481f)
{
if (Input.GetMouseButton(0))
{
Decrease();
healthPlayer--;
if (healthPlayer <= 0)
{
Destroy(gameObject);
gameOver.GameOverScreen();
}
}
}
}
void Decrease()
{
transform.localScale -= new Vector3(speedDecrease, speedDecrease, speedDecrease) * Time.deltaTime;
}
}
You can keep the position of the object in a variable and check if the position changes to check if it has moved or use Transform.hasChanged or a boolean of your choice to handle "hasMoved" state.
I'd try something like (not debuggued):
using UnityEngine;
public class DecreasePlayerBall : MonoBehaviour
{
[SerializeField] private float speedDecrease;
[SerializeField] private int healthPlayer;
public GameOver gameOver;
void Start() {
transform.hasChanged = false; // its false by default probably
}
void Update()
{
// if (GameObject.FindWithTag("ballBullet").transform.position.z <= 25.3481f)
if (!transform.hasChanged) {
if (Input.GetMouseButton(0))
{
Decrease();
healthPlayer--;
if (healthPlayer <= 0)
{
Destroy(gameObject);
gameOver.GameOverScreen();
}
}
}
}
void Decrease()
{
transform.localScale -= new Vector3(speedDecrease, speedDecrease, speedDecrease) * Time.deltaTime;
transform.hasChanged = true;
}
}
Related
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.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ShootBullets : MonoBehaviour
{
public float bulletDistance;
public bool automaticFire = false;
public float fireRate;
public Rigidbody bullet;
private float gunheat;
private bool shoot = false;
private GameObject bulletsParent;
private GameObject[] startpos;
// Start is called before the first frame update
void Start()
{
bulletsParent = GameObject.Find("Bullets");
startpos = GameObject.FindGameObjectsWithTag("Fire Point");
}
private void Fire()
{
for (int i = 0; i < startpos.Length; i++)
{
Rigidbody bulletClone = (Rigidbody)Instantiate(bullet, startpos[i].transform.position, startpos[i].transform.rotation, bulletsParent.transform);
bulletClone.velocity = transform.forward * bulletDistance;
Destroy(bulletClone.gameObject, 0.5f);
}
}
// Update is called once per frame
void FixedUpdate()
{
if (automaticFire == false)
{
if (Input.GetMouseButtonDown(0))
{
Fire();
}
}
else
{
if (shoot == true)
{
Fire();
shoot = false;
}
}
if (gunheat > 0) gunheat -= Time.deltaTime;
if (gunheat <= 0)
{
shoot = true;
gunheat = fireRate;
}
}
}
now the bullets firing up the air and i want the bullets to fire to a target with physics.
the main goal later is to make some kind of side mission where the player third person view should shoot on object and if and when hitting the object the object will fall down and then the player will be able to pick it up.
I am doing a school project in Unity. My team and I decided to make an endless runner 2D game. However, because it is the first time I use C#, I don't know how to make my player's speed accelerate when collide with a game object in Unity. I only know how to destroy the player's health when a collision happens. Please help me! Thank you!
it's quite hard to give answers without seeing any code you've written, but as it's 2D and you've already got the collision damage working, you've probably used an OnCollisionEnter(), well it's very similar: you test if you've collided (which you've already done), then you add force to your player using a rigidbody2d, probably something along the lines of:
public Rigidbody2D rb;
void OnCollisionEnter2D(Collision2D collision)
{
rb.AddForce(direction * force, ForceMode2D.Impulse); // the ForceMode2D is
// optional, it's just so that
// the velocity change is sudden.
}
This should work.
If you have a GameManager that stores the game speed you can also do that too:
private float gameSpeedMultiplier = 0.5f;
void OnCollisionEnter(Collision collision)
{
if(collision.gameObject.CompareTag("Tag of the collided object")
{
GameManager.Instance.gameSpeed += gameSpeedMultiplier;
}
}
public class PlayerContoler : MonoBehaviour
{
public static PlayerContoler instance;
public float moveSpeed;
public Rigidbody2D theRB;
public float jumpForce;
private bool isGrounded;
public Transform GroundCheckPoint;
public LayerMask whatIsGround;
private bool canDoubleJump;
private Animator anim;
private SpriteRenderer theSR;
public float KnockbackLength, KnockbackForce;
private float KnockbackCounter;
public float bonceForce;
public bool stopImput;
private void Awake()
{
instance = this;
}
// Start is called before the first frame update
void Start()
{
anim = GetComponent<Animator>();
theSR = GetComponent<SpriteRenderer>();
}
// Update is called once per frame
void Update()
{
if(!PauseMenu.instance.isPause && !stopImput)
{
if (KnockbackCounter <= 0)
{
theRB.velocity = new Vector2(moveSpeed * Input.GetAxis("Horizontal"), theRB.velocity.y);
isGrounded = Physics2D.OverlapCircle(GroundCheckPoint.position, .2f, whatIsGround);
if (isGrounded)
{
canDoubleJump = true;
}
if (Input.GetButtonDown("Jump"))
{
if (isGrounded)
{
theRB.velocity = new Vector2(theRB.velocity.x, jumpForce);
AudioManager.instance.PlaySFX(10);
}
else
{
if (canDoubleJump)
{
theRB.velocity = new Vector2(theRB.velocity.x, jumpForce);
canDoubleJump = false;
AudioManager.instance.PlaySFX(10);
}
}
}
if (theRB.velocity.x < 0)
{
theSR.flipX = true;
}
else if (theRB.velocity.x > 0)
{
theSR.flipX = false;
}
} else
{
KnockbackCounter -= Time.deltaTime;
if(!theSR.flipX)
{
theRB.velocity = new Vector2(-KnockbackForce, theRB.velocity.y);
} else
{
theRB.velocity = new Vector2(KnockbackForce, theRB.velocity.y);
}
}
}
anim.SetFloat("moveSpeed", Mathf.Abs(theRB.velocity.x));
anim.SetBool("isGrounded", isGrounded);
}
public void Knockback()
{
KnockbackCounter = KnockbackLength;
theRB.velocity = new Vector2(0f, KnockbackForce);
anim.SetTrigger("hurt");
}
}
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);
}
}
}
I'm programming in C# on Unity. When ever I need to reset a variable in a certain interval, I would tend to declare a lot of variables and use the Update() function to do what I want. For example, here is my code for resetting a skill's cooldown (Shoot() is called whenever player presses shoot key):
using UnityEngine;
using System.Collections;
public class Player : MonoBehavior
{
private bool cooldown = false;
private float shootTimer = 0f;
private const float ShootInterval = 3f;
void Update()
{
if (cooldown && Time.TimeSinceLevelLoad - shootTimer > ShootInterval)
{
cooldown = false;
}
}
void Shoot()
{
if (!cooldown)
{
cooldown = true;
shootTimer = Time.TimeSinceLevelLoad;
//and shoot bullet...
}
}
}
Is there any better ways to do the same thing? I think my current code is extremely messy with bad readability.
Thanks a lot.
Use Invoke this will save you a lot of variables.
public class Player : MonoBehavior
{
private bool cooldown = false;
private const float ShootInterval = 3f;
void Shoot()
{
if (!cooldown)
{
cooldown = true;
//and shoot bullet...
Invoke("CoolDown", ShootInterval);
}
}
void CoolDown()
{
cooldown = false;
}
}
A way without Invoke that is a bit easier to control:
public class Player : MonoBehavior
{
private float cooldown = 0;
private const float ShootInterval = 3f;
void Shoot()
{
if(cooldown > 0)
return;
// shoot bullet
cooldown = ShootInterval;
}
void Update()
{
cooldown -= Time.deltaTime;
}
}