Sound not playing on last hit - c#

I have a problem which is that my hurt sound doesn't play on last hit. On other hits it works very well. I think that its because of the gameObject getting destroyed. I have tried it by putting the script to my bullet and changing the tag. Also the Debug.Log works perfectly on last hit. I hope that somebody could help me.
Anyway, here's my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HealthSystem : MonoBehaviour
{
public AudioSource source;
public AudioClip clip;
public Image healthBar;
public float healthAmount = 100;
public GameObject bullet;
void Start()
{
}
private void Update()
{
if(healthAmount <= 0)
{
Destroy(gameObject);
}
}
public void TakeDamage(float Damage)
{
source.PlayOneShot(clip);
healthAmount -= Damage;
healthBar.fillAmount = healthAmount / 100;
}
public void Healing(float healPoints)
{
healthAmount += healPoints;
healthAmount = Mathf.Clamp(healthAmount, 0, 100);
healthBar.fillAmount = healthAmount / 100;
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Bullet"))
{
if (!source.isPlaying)
{
Debug.Log("i have are played");
TakeDamage(20);
}
}
}
}

Exactly as you said: Because your game object is getting destroyed with last bullet, the AudioSource is getting destroyed with it and it cannot play whole sound. What is the workaround? There are 2, you can either:
Spawn new GameObject just to play the sound and destroy it right after
Hide/disable the object (as if it is destroyed), play the sound and once sample is played, destroy it

Related

Unity - Actions Being Called Twice - OnTriggerEnter, OnClick, EVERYTHING? [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 10 months ago.
Improve this question
So I'm creating a Sheep Counter Game and yesterday, when I went to bed everything was working great. Today, when I opened Unity up to do some finishing touches, everything I'm doing is being called twice...
So when I click the start button it calls the start game twice, then when my sheep hit obstacles it calls OnTriggerEnter twice. Idk what I did wrong or what happened and would prefer to not have to restart the whole project...
Console Log
Counter Script
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class Counter : MonoBehaviour
{
public TMP_Text savedText;
public TMP_Text deadText;
private int savedCount = 0;
private int deadCount = 0;
private void Start()
{
savedCount = 0;
deadCount = 0;
}
public void AddSavedSheep()
{
savedCount++;
savedText.text = "Saved: " + savedCount;
}
public void AddDeadSheep()
{
deadCount++;
deadText.text = "Dead: " + deadCount;
}
}
Sheep Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SheepControls : MonoBehaviour
{
private Rigidbody rb;
[Header("Movement")]
[Tooltip("How fast to move GameObject Forward.")]
public float forwardSpeed = 4.0f;
[Tooltip("Apply this much force to Rigidbody.")]
public float jumpForce = 5.0f;
private float groundY;
[Space]
[SerializeField] private bool jumping;
[SerializeField] private bool isDestroyed;
[SerializeField] private bool isSaved;
public ParticleSystem explosionParticles;
private Counter counter;
void Start()
{
rb = GetComponent<Rigidbody>();
groundY = (transform.position.y)+0.02f;
counter = FindObjectOfType<Counter>();
}
void Update()
{
transform.Translate(forwardSpeed * Time.deltaTime * Vector3.forward);
if (Input.GetKeyDown(KeyCode.Space) && !jumping)
{
if(transform.position.y < groundY)
{
Jump();
}
}
jumping = false;
}
private void Jump()
{
jumping = true;
rb.AddForce(Vector3.up * jumpForce);
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("ExplosiveWire"))
{
if (isDestroyed) return;
else
{
Debug.Log("Hit Wire");
isDestroyed = true;
Destroy(gameObject);
Instantiate(explosionParticles, transform.position,
explosionParticles.transform.rotation);
}
counter.AddDeadSheep();
}
if (other.CompareTag("Goal"))
{
if (isSaved) return;
else
{
Debug.Log("Reached Goal");
isSaved = true;
Destroy(gameObject);
}
counter.AddSavedSheep();
}
}
private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Sheep"))
{
Physics.IgnoreCollision(this.GetComponent<Collider>(),
collision.gameObject.GetComponent<Collider>());
}
}
}
GameManager Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour
{
public bool isGameActive;
public GameObject titleScreen;
public GameObject sheepControllerPrefab;
public void StartGame(int difficulty)
{
titleScreen.SetActive(false);
isGameActive = true;
InvokeRepeating(nameof(SpawnSheepWave), 2.0f, 2.0f);
}
public void Restart()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
public void SpawnSheepWave()
{
Instantiate(sheepControllerPrefab, transform.position,
transform.rotation);
}
}
Start Button Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class DifficultyButton : MonoBehaviour
{
private Button button;
private GameManager gameManager;
[Header("Difficulty Level")]
[Tooltip("The spawn rate will be divided by this number.")]
public int difficulty;
void Start()
{
gameManager = GameObject.Find("GameManager").GetComponent<GameManager>();
button = GetComponent<Button>();
button.onClick.AddListener(SetDifficulty);
}
public void SetDifficulty()
{
Debug.Log(gameObject.name + " was clicked.");
gameManager.StartGame(difficulty);
}
}
So, it's actually adding the score twice, so originally I thought maybe I need to do an isDestroyed or isSaved check and added that to the script but nothing seems to keep it from adding the score twice, or thinking I clicked the start button twice.
Double increment of the score
I feel like my code is fine, I just don't know what is going on with it and why it is calling everything twice. Do you think there is a solution, or that I might just have to recreate the game in a new project?
Would trying to disable the box collider when it enters the trigger maybe fix the problem?
There is only one BoxCollider on the sheep, and the one BoxCollider on the fence, and one BoxCollider on the goal. They all have rigid bodies and the calls are being made correctly, they just happen two times at once.
Thanks for any help you guys can provide!
I also tried moving the increment step before or after the if statements to see if it would do anything different but I get the same results.
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("ExplosiveWire"))
{
if (isDestroyed) return;
else
{
Debug.Log("Hit Wire");
isDestroyed = true;
Destroy(gameObject);
Instantiate(explosionParticles, transform.position,
explosionParticles.transform.rotation);
counter.AddDeadSheep();
}
}
if (other.CompareTag("Goal"))
{
if (isSaved) return;
else
{
Debug.Log("Reached Goal");
isSaved = true;
Destroy(gameObject);
counter.AddSavedSheep();
}
}
}
[Edit]
Added Image of scripts attached to game objects.
Scripts Attached to Game Objects
Each click you call start on your difficulty button. Which has already run. So you will get 2. See the choices in your start button inspector

Destroyed Bullet Won't Make Clones and Shoot After around 3 seconds

I made a script that shoots a bullet and destroys it after 3 seconds, however it destroys the original bullet after it is shot which makes unity unable to make copies of it to shoot another bullet, An okay solution might be to make a copy of the bullet and shoot the copy however I do not know how to do that.
This is the script for the gun
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunShootingScript : MonoBehaviour
{
public ParticleSystem MuzzleFlash;
public float damage = 10f;
public float range = 100f;
public GameObject bulletPrefab;
public float bulletLife = 3f;
public Camera playerCamera;
private void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0))
{
Destroy(bulletPrefab, bulletLife);
Shoot();
}
}
void Shoot()
{
MuzzleFlash.Play();
Instantiate(bulletPrefab, playerCamera.transform.position, playerCamera.transform.rotation);
RaycastHit hit;
if(Physics.Raycast(playerCamera.transform.position, playerCamera.transform.forward, out hit, range))
{
TakeDamage target = hit.transform.GetComponent<TakeDamage>();
if(target != null)
{
target.GiveDamage(damage);
}
}
}
}
This is the script for the bullet.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shot : MonoBehaviour
{
// Start is called before the first frame update
public float speed = 3000f;
void Update()
{
transform.position += transform.forward * speed * Time.deltaTime;
}
}
From what I see, you destroy the bulletPrefab but you never actually assign an object for it. You can have a separate GameObject and do that when instantiating inside the Shoot method. Try this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunShootingScript : MonoBehaviour
{
public ParticleSystem MuzzleFlash;
public float damage = 10f;
public float range = 100f;
public GameObject bulletPrefab;
public float bulletLife = 3f;
private GameObject bulletShot // This is what I added
public Camera playerCamera;
private void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0))
{
Destroy(bulletShot); // Removed the timer from here
Shoot();
}
}
void Shoot()
{
MuzzleFlash.Play();
bulletShot = Instantiate(bulletPrefab, playerCamera.transform.position, playerCamera.transform.rotation); // Assigned the bullet to the separate GameObject
RaycastHit hit;
if(Physics.Raycast(playerCamera.transform.position, playerCamera.transform.forward, out hit, range))
{
TakeDamage target = hit.transform.GetComponent<TakeDamage>();
if(target != null)
{
target.GiveDamage(damage);
}
}
}
}
That way, when you try to destroy it, there will actually be something to destroy. However, this will create a new issue. The previously instantiated object will be destroyed instantly when you shoot again. You can fix that by creating a list and adding the bullets there. However, that is NOT efficient. And since you are going to start all over, I suggest you go for an Object Pool. What is that? Glad you asked!
There is a very good video by Jason Weimann on YouTube about object pooling. You might want to give it a go, it is old but definitely not outdated.
https://www.youtube.com/watch?v=uxm4a0QnQ9E
You might need to keep a reference to the shot bullet, to be able to destroy with a coroutine after 3 seconds, it later on like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GunShootingScript : MonoBehaviour
{
public ParticleSystem MuzzleFlash;
public float damage = 10f;
public float range = 100f;
public GameObject bulletPrefab;
public float bulletLife = 3f;
private Queue<GameObject> myQ = new Queue<GameObject>(); //queue to keep track of the shot bullet and handle the delayed destroy
private IEnumerator coroutine;
void Start() {
coroutine = DelayedDestroy(3f);
}
public Camera playerCamera;
private void Update()
{
if(Input.GetKeyDown(KeyCode.Mouse0))
{
//Destroy(bulletPrefab, bulletLife); Commented to avoid immediate destruction
Shoot();
}
}
void Shoot()
{
MuzzleFlash.Play();
myQ.Enqueue(Instantiate(bulletPrefab, playerCamera.transform.position, playerCamera.transform.rotation));
RaycastHit hit;
if(Physics.Raycast(playerCamera.transform.position, playerCamera.transform.forward, out hit, range))
{
TakeDamage target = hit.transform.GetComponent<TakeDamage>();
if(target != null)
{
target.GiveDamage(damage);
}
}
StartCoroutine(coroutine);
}
private IEnumerator DelayedDestroy(float waitTime) {
yield return new WaitForSeconds(waitTime);
GameObject nullcheck = myQ.Dequeue();
if (nullcheck != null) { //in case the queue is empty
Destroy(nullcheck );
}
}
}
Did not debug that, it is to give you the idea. You can check the Queue data structure and coroutines.
Check pooling to achieve what you are making even better :)

Having problems with "OnCollisionEnter2D" in Unity2D

I'm using this code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class CollisionPlayer : MonoBehaviour
{
public bool alreadyDied = false;
public GameObject player;
public float timeDeath;
public ParticleSystem particles;
public GameObject explosionGO;
private SpriteRenderer sr;
private BoxCollider2D bc;
private PlayerController walkScript;
void Start()
{
sr = GetComponent<SpriteRenderer>();
bc = GetComponent<BoxCollider2D>();
walkScript = GetComponent<PlayerController>();
}
void OnCollisionEnter2D (Collision2D collide)
{
if (collide.gameObject.CompareTag("Dead"))
{
Instantiate(particles, player.transform.position, Quaternion.identity);
Instantiate(explosionGO, player.transform.position, Quaternion.identity);
CinemachineShake.Instance.ShakeCamera(30f, .1f);
alreadyDied = true;
}
}
void Update()
{
if(alreadyDied == true)
{
timeDeath -= Time.deltaTime;
sr.enabled = false;
bc.enabled = false;
walkScript.enabled = false;
}
if(timeDeath <= 0)
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
}
}
This is the bullet's code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LeftBulletScript : MonoBehaviour
{
// Start is called before the first frame update
public float speed;
public float destructionLeftTime;
public ParticleSystem particles;
private GameObject thisGameObject;
void Start()
{
thisGameObject = this.gameObject;
Destroy(gameObject, destructionLeftTime);
}
void Update()
{
transform.Translate(Vector2.left * speed * Time.deltaTime);
if(destructionLeftTime > 0.05f)
{
destructionLeftTime -= Time.deltaTime;
}
else
{
Instantiate(particles, thisGameObject.transform.position, Quaternion.identity);
}
}
}
This code should spawn some particles and a sound effect when the player gets hit by something with tag "Dead". But that does not happen. I have a box collider 2D on both the bullet (that should kill me) and the player. My Rigidbody2D is dynamic on the player with z freezed. The bullet does not have a rigidbody. I made sure that the bullet actually has the tag "Dead", spelled the exact same way as the way I wrote on the script. The weirdest thing is that I used this code on another game and nothing changed (just the name of a script). Both the player and the bullet are on the same layer. Anyone could tell me what could have happened?

How can I make my jump and land animations play reliably?

I can't make my jump and land animations play reliably Currently the jump animation doesn't play and the land animation seems to play randomly.
I've been working with unity 3d for several months but I am new to using animations. I may just be missing some basic information.
At one point the jump animation would play, but only after the player was in the air. How do I make animations play at the right time reliably?
This is my player movement script where I'm currently calling the jump and land animations from.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMvmt : MonoBehaviour
{
public Rigidbody player;
public float sideForce = 5;
public float jumpForce = 5;
public float fallingForce = 10;
public SphereCollider col;
public LayerMask groundLayers;
public Animator jump;
public Animator land;
private void Start()
{
jump = GetComponent<Animator>();
land = GetComponent<Animator>();
}
private void Update()
{
if ( Input.GetKey("a"))
{
player.AddForce(-sideForce, 0, 0);
}
if ( Input.GetKey("d"))
{
player.AddForce(sideForce, 0, 0);
}
if (IsGrounded() && Input.GetKey("w"))
{
jump.SetBool("playJump",true);
Invoke("StopJumpAnimation", .4f);
player.AddForce(0, jumpForce, 0);
}
OncollisionEnter();
if (IsGrounded() == false)
{
player.AddForce(0, -fallingForce, 0);
}
}
private bool IsGrounded()
{
return Physics.CheckCapsule(col.bounds.center,
new Vector3(col.bounds.center.x,
col.bounds.min.y, col.bounds.center.z), col.radius * .9f, groundLayers);
}
void StopJumpAnimation()
{
jump.SetBool("playJump", false);
}
void StopLandAnimation()
{
land.SetBool("playLand", false);
}
void OncollisionEnter()
{
land.SetBool("playLand", true);
Invoke("StopLandAnimation", .3f);
}
}
I can show you pictures of the animator if that helps. I might just need a basic explanation of how to use the animator since I'm new to animation.

Sound is super distorted when played

When ever the enemy dies, he plays the sound but the sound ends up being super distorted. I think it's because its playing in the Update method, but I'm not sure how to overcome that.
From other forums I've read they say to use a boolean but how would I go about implementing a boolean in this situation?
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.AI;
public class aiscript : MonoBehaviour
{
NavMeshAgent agent;
public GameObject bloodExplosion;
private AudioSource audioSource;
public GameObject render;
public float Health;
public GameObject bloodSpawn;
private Transform bloodSpawned;
public GameObject player;
void Start()
{
agent = GetComponent<NavMeshAgent>();
}
void Update()
{
agent.SetDestination(player.transform.position);
if(Health <= 0)
{
render.SetActive(true);
audioSource = gameObject.GetComponent<AudioSource>();
audioSource.PlayOneShot(audioSource.clip);
Instantiate(bloodExplosion, bloodSpawn.transform.position,
bloodSpawn.transform.rotation);
Die();
}
}
public void Die()
{
Destroy(this.gameObject, audioSource.clip.length);
}
}
Found out how, if anyone was wondering I added an if statement:
if(Health <= 0)
{
render.SetActive(true);
audioSource = gameObject.GetComponent<AudioSource>();
if(!audioSource.isPlaying)
{
audioSource.PlayOneShot(audioSource.clip);
}
Instantiate(bloodExplosion, bloodSpawn.transform.position,
bloodSpawn.transform.rotation);
Die();
}

Categories