Hit points bar wont fill completely, calculation only happens once - c#

I have a hit points bar which is supposed to fill up by 0.1 each time the ball object collides with the goodOrb object. However the bar fills up by 0.1 only on the first collision. It does not move when the ball collides again.
I tried Debug.Log the value of the variable hitPoints which stores the current amount the bar is filled by. This variable is initialized to 0, I have another variable called increase which is set to 0.1, each time the two objects collide variable increase is supposed to be added to hitpoints. But this is happening only once. Debug.Log(hitpoints) shows 0.1 only once.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class goodOrb : MonoBehaviour
{
public int maxHealth = 100;
public int currentHealth;
public Image barImage;
private Rigidbody2D rb;
public float increase = 0.1f; //amount to increase bar each time a collision happens
public float hitPoints = 0.0f; // current amount the bar is filled by
// public AudioSource collisionSound;
int scoreValue= 5 ;
// Start is called before the first frame update
public void Start()
{
GameObject Filler = GameObject.Find("Filler");
Image barImage = Filler.GetComponent<Image>();
}
void OnTriggerEnter2D(Collider2D other)
{
ParticleSystem ps = GetComponent<ParticleSystem>();
if(other.tag=="Ball")
{
ps.Play();
HitPoints();
scoreManager.score += scoreValue;
// barImage.fillAmount = (float)currentHealth / (float)maxHealth;
// collisionSound.Play();
}
}
void Update()
{
}
// Update is called once per frame
void HitPoints()
{
GameObject Filler = GameObject.Find("Filler");
Image barImage = Filler.GetComponent<Image>();
hitPoints = hitPoints + increase;
barImage.fillAmount = hitPoints;
//print(hitPoints);
Debug.Log(hitPoints);
}
}
I expect increase to be added to hitpoints each time the collision happens and the hitpoints bar to be filled.

1- make sure the ball collides with same orb object every time.
2- make sure one collider isTrigger is true.
3- make sure trigger function is called every time ball collides.
by doing this
void OnTriggerEnter2D(Collider2D other){
Debug.Log (other.tag);
}

Related

How to spawn prefabs within a certain period of time in Unity?

I am trying to create a ball hitting game in the baseball format. I create a ball as a prefab. I want to push the ball to the main scene within a certain period of time.
For example; when the first ball is in the scene, the second ball will spawn after 5-6 seconds, then the third, fourth etc. I am the beginner level of Unity and I am not good at C#. I am not sure whether I am using the true functions such as Instantiate. Here is my script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ball : MonoBehaviour {
public float RotateSpeed = 45; //The ball rotates around its own axis
public float BallSpeed = 0.2f;
public GameObject[] prefab;
public Rigidbody2D rb2D;
void Start() {
rb2D = GetComponent<Rigidbody2D>(); //Get component attached to gameobject
Spawn ();
}
void FixedUpdate() {
rb2D.MoveRotation(rb2D.rotation + RotateSpeed * Time.fixedDeltaTime); //The ball rotates around its own axis
rb2D.AddForce(Vector2.left * BallSpeed);
InvokeRepeating("Spawn", 2.0f, 2.0f);
}
public void Spawn ()
{
int prefab_num = Random.Range(0,3);
Instantiate(prefab[prefab_num]);
}
}
After I apply this script, the result is not what I want.
Add InvokeRepeating("Spawn", 2.0f, 2.0f); to the Start not the FixedUpdate.
InvokeRepeating invokes the method methodName in time seconds, then repeatedly every repeatRate seconds.
You can check the documentation here.
Use Coroutines
private IEnumerator SpawnBall() {
while(true) {
Instantiate(baseball);
yield return new WaitForSeconds(5);
}
}
Which can then be started with StartCoroutine() and terminated in one of three ways:
internally by breaking out of the while loop with break (the function would then have no more lines to execute and exit)
internally by yield break
externally by calling StopCoroutine() on a reference to the coroutine
Alternative to the other answers: Just use a countdown. This sometimes gives you more control
// Set your offset here (in seconds)
float timeoutDuration = 2;
float timeout = 2;
void Update()
{
if(timeout > 0)
{
// Reduces the timeout by the time passed since the last frame
timeout -= Time.deltaTime;
// return to not execute any code after that
return;
}
// this is reached when timeout gets <= 0
// Spawn object once
Spawn();
// Reset timer
timeout = timeoutDuration;
}
I updated my script by considering your feedbacks and it works like a charm. Thanks to all!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Threading;
public class Ball : MonoBehaviour {
public float RotateSpeed = 45; //The ball rotates around its own axis
public float BallSpeed = 0.2f;
public GameObject BaseBall;
public Transform BallLocation;
public Rigidbody2D Ball2D;
void Start() {
Ball2D = GetComponent<Rigidbody2D>(); //Get component attached to gameobject
InvokeRepeating("Spawn", 5.0f, 150f);
}
void FixedUpdate() {
Ball2D.MoveRotation(Ball2D.rotation + RotateSpeed * Time.fixedDeltaTime); //The ball rotates around its own axis
Ball2D.AddForce(Vector2.left * BallSpeed);
}
public void Spawn ()
{
Instantiate (BaseBall, BallLocation.position, BallLocation.rotation);
}
}

Make Animation Play After Boolean is Set to True?

I am pretty fluent in using Unity, but regarding Mechanim and Animations I am not the too great at currently, so please don't give me too much of a hard time lol. So I have this boolean that is in my GameManager script:
public bool countDownDone = false;
This boolean gets set to true once my "3,2,1, GO!" Countdown Timer ends at the beginning of my game. Everything in my game starts after that boolean is set to true. Example:
using UnityEngine;
using System.Collections;
public class PlaneMover : MonoBehaviour {
private GameManagerScript GMS; // Reference to my GameManager Script
public float scrollSpeed;
public float tileSizeZAxis;
public float acceleration; //This has to be a negative number in the inspector. Since our plane is coming down.
private Vector3 startPosition;
void Start () {
GMS = GameObject.Find ("GameManager").GetComponent<GameManagerScript> (); //Finds the Script at the first frame
// Transform position of the quad
startPosition = transform.position;
}
void Update () {
if (GMS.countDownDone == true) //Everything starts once the countdown ends.
{
/* Every frame - time in the game times the assigned scroll speed
and never exceed the length of the tile that we assign */
float newPosition = Mathf.Repeat (Time.time * scrollSpeed, tileSizeZAxis);
// New position equal to the start position plus vector3forward times new position
transform.position = startPosition + Vector3.forward * newPosition; // was vector3.forward
scrollSpeed += Time.deltaTime * acceleration; // This makes the plane increase in speed over time with
// whatever our acceleration is set to.
}
}
}
I have this Crawling animation that plays at the very beginning of the game (Even before the Timer ends) and loops forever. My question is , how do I make that crawling animation also start once that boolean is set to "true"? Or do I just apply it to a CoRoutine and make it play after 3 seconds? I have done extensive research on whether to reference Animation or Animator and I got confused on that too. Any advice? If you need more pictures or details on my question, just let me know. Thank you! :)
NEW CODE BELOW
using UnityEngine;
using System.Collections;
public class Crawling : MonoBehaviour {
Animator animator;
private GameManagerScript GMS;
void Start ()
{
animator = GetComponent<Animator> ();
GMS = GameObject.Find ("GameManager").GetComponent<GameManagerScript> ();
}
void Update ()
{
if (GMS.countDownDone == true)
{
animator.Play("Main Character Crawling", 1);
}
}
}
I have this Crawling animation that plays at the very beginning of the
game (Even before the Timer ends) and loops forever. My question is ,
how do I make that crawling animation also start once that boolean is
set to "true"?
The solution is to play the animation from script.
Remove the currentAnimation.
Select the GameObject your PlaneMover script is attached to, attach Animation and Animator components to it. Make sure that the Animation's Play Automatically is unchecked.
public class PlaneMover : MonoBehaviour {
private GameManagerScript GMS; // Reference to my GameManager Script
public float scrollSpeed;
public float tileSizeZAxis;
public float acceleration; //This has to be a negative number in the inspector. Since our plane is coming down.
private Vector3 startPosition;
Animation animation;
public AnimationClip animationClip; //Assign from Editor
void Start () {
GMS = GameObject.Find ("GameManager").GetComponent<GameManagerScript> (); //Finds the Script at the first frame
// Transform position of the quad
startPosition = transform.position;
animation = GetComponent<Animation>();
//Add crawing Animation
animation.AddClip(animationClip, "Crawling");
//Add other animation clips here too if there are otheres
}
void Update ()
{
if (GMS.countDownDone) //Everything starts once the countdown ends.
{
/* Every frame - time in the game times the assigned scroll speed
and never exceed the length of the tile that we assign */
float newPosition = Mathf.Repeat (Time.time * scrollSpeed, tileSizeZAxis);
// New position equal to the start position plus vector3forward times new position
transform.position = startPosition + Vector3.forward * newPosition; // was vector3.forward
scrollSpeed += Time.deltaTime * acceleration; // This makes the plane increase in speed over time with
// whatever our acceleration is set to.
//Play Animation
animation.PlayQueued("Crawling", QueueMode.CompleteOthers);
}
}
} }
I solved the problem!!
This is my script:
using UnityEngine;
using System.Collections;
public class Crawling : MonoBehaviour {
public Animator animator;
private GameManagerScript GMS;
void Start ()
{
animator = GetComponent<Animator> ();
GMS = GameObject.Find ("GameManager").GetComponent<GameManagerScript> ();
}
void Update ()
{
if (GMS.countDownDone == true) {
animator.enabled = true;
}
else
{
animator.enabled = false;
}
}
}
All I do is just enable the Animator that I attach in the Inspector whenever "countDownDone" becomes "true", and for added safety, I added the "else" for it to be disabled. If someone notices a way for me to improve this script, please let me know. Thank you :)
Shorter version:
if (GMS.countDownDone)
animator.enabled = true;
else
animator.enabled = false;

Start animation when player enters specific distance

I want to start an animation when my player enters a specific distance to an object, but my animation isn't starting. Any suggestion as to why it isn't starting?
Here is the code that I have so far:
using UnityEngine;
using System.Collections;
public class MoveTo : MonoBehaviour {
public Transform Player;
public Transform goal;
public Animator ani;
public Animator ani2;
// Use this for initialization
void Start ()
{
ani.Stop (); // this stop function is working accurate
ani2.Stop ();
}
void Update()
{
float dist = Vector3.Distance (Player.position, transform.position);
if (dist < 5)
{
ani.Play("Horse_Walk");// this is not working (Horse_Walk) is a state name
ani2.Play("Horse_Run");
pstart();
}
}
void pstart(){
NavMeshAgent agent=GetComponent<NavMeshAgent>();
agent.destination = goal.position;
}
}
There can be several reason including (1) wrong string (2) string name not matched and finally which seems more appropriate reason is that you playing animation again. you should need be call it with bool
bool isPlayAnim =true;
void Update()
{
float dist = Vector3.Distance (Player.position, transform.position);
if (dist < 5 && isPlayAnim)
{
isPlayAnim = false;//again true it on you specfic event
ani.Play("Horse_Walk");// this is not working (Horse_Walk) is a state name
ani2.Play("Horse_Run");
pstart();
}
}
isPlayAnim bool you can make and use to play animation only one time and again true it in specific event.
Or
Alternatively you need to use collider and its event to run this update code. I guess it is best way to do this job .

Unity: Pausing the game after player object gets destroyed

as the title says, I want to make it so that when the player game object gets destroyed, the game will then pause and bring up a screen (I've made one called GameOverScreen), however, I cannot seem to get my head around it. The screen displays however the game doesn't pause. Any idea what could be causing this? Here is my code:
using UnityEngine;
using UnityEngine.UI;
public class PlayerHealth : MonoBehaviour
{
public float m_StartingHealth = 100f; // The amount of health each tank starts with.
public Slider m_Slider; // The slider to represent how much health the tank currently has.
public Image m_FillImage; // The image component of the slider.
public Color m_FullHealthColor = Color.green; // The color the health bar will be when on full health.
public Color m_ZeroHealthColor = Color.red; // The color the health bar will be when on no health.
public GameObject m_ExplosionPrefab; // A prefab that will be instantiated in Awake, then used whenever the tank dies.
public Camera mainCamera;
public Camera gameOverCamera;
public GameObject GameOverMenu;
private bool gameover = false;
private AudioSource m_ExplosionAudio; // The audio source to play when the tank explodes.
private ParticleSystem m_ExplosionParticles; // The particle system the will play when the tank is destroyed.
private float m_CurrentHealth; // How much health the tank currently has.
private bool m_Dead; // Has the tank been reduced beyond zero health yet?
void Start()
{
GameOverMenu.SetActive(false);
}
private void Awake()
{
// Instantiate the explosion prefab and get a reference to the particle system on it.
m_ExplosionParticles = Instantiate(m_ExplosionPrefab).GetComponent<ParticleSystem>();
// Get a reference to the audio source on the instantiated prefab.
m_ExplosionAudio = m_ExplosionParticles.GetComponent<AudioSource>();
// Disable the prefab so it can be activated when it's required.
m_ExplosionParticles.gameObject.SetActive(false);
}
private void OnEnable()
{
// When the tank is enabled, reset the tank's health and whether or not it's dead.
m_CurrentHealth = m_StartingHealth;
m_Dead = false;
// Update the health slider's value and color.
SetHealthUI();
}
public void TakeDamage(float amount)
{
// Reduce current health by the amount of damage done.
m_CurrentHealth -= amount;
// Change the UI elements appropriately.
SetHealthUI();
// If the current health is at or below zero and it has not yet been registered, call OnDeath.
if (m_CurrentHealth <= 0f && !m_Dead)
{
{
gameover = !gameover;
}
if (gameover)
{
GameOverMenu.SetActive(true);
Time.timeScale = 0;
}
if (!gameover)
{
GameOverMenu.SetActive(false);
Time.timeScale = 1;
}
OnDeath();
mainCamera.transform.parent = null;
mainCamera.enabled = true;
gameOverCamera.enabled = false;
}
}
private void SetHealthUI()
{
// Set the slider's value appropriately.
m_Slider.value = m_CurrentHealth;
// Interpolate the color of the bar between the choosen colours based on the current percentage of the starting health.
m_FillImage.color = Color.Lerp(m_ZeroHealthColor, m_FullHealthColor, m_CurrentHealth / m_StartingHealth);
}
private void OnDeath()
{
// Set the flag so that this function is only called once.
m_Dead = true;
// Move the instantiated explosion prefab to the tank's position and turn it on.
m_ExplosionParticles.transform.position = transform.position;
m_ExplosionParticles.gameObject.SetActive(true);
// Play the particle system of the tank exploding.
m_ExplosionParticles.Play();
// Play the tank explosion sound effect.
m_ExplosionAudio.Play();
// Turn the tank off.
gameObject.SetActive(false);
}
}
set a precise float value like this ,
Time.timeScale = 0.0f;
Firstly, you don't need two if's, just do:
if (gameover) {
Time.timeScale = 0;
}else{
Time.timeScale = 1;
}
Next, add this:
void OnPauseGame ()
{
GameOverMenu.SetActive(true);
}
If still doesn't work, then add:
void Update(){
if(Time.timeScale == 0)return;
}
Easiest way to my opinion to pause the game upon an event is go use the 'Error Pause' button on the Console.
Of course you will need to generate an error but this is easy.
Just use the
Debug.LogError("Game Paused");
Good luck

Unity C# check if player (Rigidbody2D) stopped moving for x seconds

I'm working on a script, so I could detect when player is not moving for x seconds and load another scene accordingly.
If player started moving again after x seconds, then loading another scene should't be called.
I've tried using isSleeping function and delay it by including Coroutine with WaitForSeconds but it is still checking Rigidbody2D every frame. Is there any other way to check if Rigidbody2D hasn't moved for x seconds and only then load game over level, otherwise continue moving as before?
using UnityEngine;
using System.Collections;
public class PlayerStop : MonoBehaviour {
void Update() {
if (GetComponent<Rigidbody2D>().IsSleeping()) {
Application.LoadLevel(2);
}
}
}
In addition I have a script, which enables me to draw lines (with mouse) and stop player's movement, however lines disappear after x seconds. So for example if I'd set lines to disappear after 1 second, I would like to check if Rigidbody2D stopped moving for 2 seconds and only then load game over scene. Otherwise do nothing as Rigidbody2D will continue moving again after line disappears.
Try this
using UnityEngine;
using System.Collections;
public class PlayerStop : MonoBehaviour {
float delay = 3f;
float threshold = .01f;
void Update() {
if (GetComponent<Rigidbody2D>().velocity.magnitude < threshold * threshold)
StartCoRoutine("LoadTheLevel");
}
IEnumerator LoadTheLevel()
{
float elapsed = 0f;
while (GetComponent<Rigidbody2D>().velocity.magnitude < threshold * threshold)
{
elapsed += Time.deltaTime;
if(elapsed >= delay)
{
Application.LoadLevel(2);
yield break;
}
yield return null;
}
yield break;
}
}
You could try this...I'm not able to test this right now, so may need a little tweaking...
First some private variables:
private float _loadSceneTime;
private Vector3 _lastPlayerPosition;
private float _playerIdleDelay;
And in the Update method check if the player has moved:
private void Update()
{
// Is it time to load the next scene?
if(Time.time >= _loadSceneTime)
{
// Load Scene
}
else
{
// NOTE: GET PLAYERS POSITION...THIS ASSUMES THIS
// SCRIPT IS ON THE GAME OBJECT REPRESENTING THE PLAYER
Vector3 playerPosition = this.transform.position;
// Has the player moved?
if(playerPosition != _lastPlayerPosition)
{
// If the player has moved we will attempt to load
// the scene in x-seconds
_loadSceneTime = Time.time + _playerIdleDelay;
}
_lastPlayerPosition = playerPosition;
}
}

Categories

Resources