Sound is playing multiple times when look at an object - c#

I'm learning about raycasting in unity3d and facing a audio problem. What I'm doing is there is a Main Camera which attached with a script called NewCast. That Main Camera cast a ray to the cube which has a component called box collider. Ray casting is working fine, when looking at that cube. I'm playing a sound. It means when camera looking at that object ray is cast to the cube. But why audio is playing multiple times. I want to play that audio only one time, when i look at that object and repeat this procedure again and again. Package link.
Code:
public class NewCast : MonoBehaviour
{
private RaycastHit hit;
bool playAudio1;
[SerializeField]
private AudioSource source;
[SerializeField]
private AudioClip clip1;
private void Start()
{
source.clip = clip1;
playAudio1 = true;
}
private void Update()
{
if (Physics.Raycast(transform.position, transform.forward, out hit,9f))
{
if (hit.collider.gameObject.name == "Cube")
{
playAudio1 = false;
if (!playAudio1)
{
source.Play();
if (!source.isPlaying)
{
playAudio1 = true;
}
}
}
}
}
}

Everytime the collider is hitting, you're setting playAudio1 to false so the next condition is always true and so the sound will always play...
if (Physics.Raycast(transform.position, transform.forward, out hit,9f))
{
if (hit.collider.gameObject.name == "Cube"){
if (!source.isPlaying){
source.Play();
}
}
}
If you want to play only ONCE per look to object :
public class NewCast : MonoBehaviour
{
private RaycastHit hit;
bool soundPlayed = false;
[SerializeField]
private AudioSource source;
[SerializeField]
private AudioClip clip1;
private void Start()
{
source.clip = clip1;
}
private void Update(){
if(Physics.Raycast(transform.position, transform.forward, out hit,9f)){
if (!soundPlayed && hit.collider.gameObject.name == "Cube"){
if (!source.isPlaying){
source.Play();
soundPlayed = true;
}
}
}else{
if(soundPlayed) soundPlayed = false;
}
}
}

It's sometime since I used Unity, but AudioSource in unity is set to automatically loop. Check the script which has the AudioSource and make sure you uncheck "is looping".
See the Unity Documentation
So am guesing that if you add this to your Start() function you will be ok
private void Start()
{
source.clip = clip1;
source.loop=false;//only plays the once and stops
playAudio1 = true;
}

Related

Show and hide animation for a hand

So . I work at a 2d game in unity and I have some problems with some mechanics and i really need some help from you guys. All I want to do is to make my character hand to stop and grab a object. When I press E the animation of the hand start and have a collider for each frame, in case that the hand collide with a closer object, the animation to stop at that frame. I have a week since I try to figure it out. If you want to help me we can do it on discord. I will put the codes here, maybe the reason that I can't to what I want to do is very clear and I dont see it.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class showHide : MonoBehaviour
{
[SerializeField]
GameObject hand;
private Animator freeze;
public bool touch = false;
private bool ok = true;
// Start is called before the first frame update
void Start()
{
freeze = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.E) && ok == true)
{
freeze.Play("Follower");
StartCoroutine(show());
}
}
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "box")
{
StartCoroutine(colliderShow());
}
}
private IEnumerator show()
{
ok = false;
hand.SetActive(true);
yield return new WaitForSeconds(4f);
hand.SetActive(false);
ok = true;
}
private IEnumerator colliderShow()
{
touch = true;
print(touch);
yield return new WaitForSeconds(4f);
touch = false;
print(touch);
}
private void FreezeAniamtion()
{
freeze.speed = 0f;
StartCoroutine(waitTime());
}
private IEnumerator waitTime()
{
yield return new WaitForSeconds(0f);
freeze.speed = 1f;
}
}
This is the code that activate and deactivate my hand object , including the animation .
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class facingScript : MonoBehaviour
{
private SpriteRenderer render;
private Camera mainCam;
private Vector3 mousePos;
private Animator aniHand;
public bool atinge;
void Start()
{
mainCam = Camera.main;
render = GetComponent<SpriteRenderer>();
aniHand = GetComponent<Animator>();
gameObject.SetActive(false);
}
// Update is called once per frame
void Update()
{
mousePos = Input.mousePosition;
mousePos = mainCam.ScreenToWorldPoint(
new Vector3(mousePos.x, mousePos.y, mainCam.transform.position.z - transform.position.z)
);
Vector3 rotation = mousePos - transform.position;
if (rotation.x > 0)
render.flipY = true;
else
render.flipY = false;
if (atinge == false)
aniHand.speed = 1f;
Debug.Log(aniHand.speed);
}
// Collision with objects
void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "box")
{
atinge = true;
aniHand.speed = 0f;
StartCoroutine(freezingTime());
}
}
private IEnumerator freezingTime()
{
yield return new WaitForSeconds(4f);
atinge = false;
}
// No collision with any object
private void FreezeAnimation()
{
aniHand.speed = 0f;
StartCoroutine(waitTime());
}
private IEnumerator waitTime()
{
yield return new WaitForSeconds(0f);
aniHand.speed = 1f;
}
}
This is the hand component script . When the hand don't collide with anything the object is deactivated . The FreezeAnimation() is a event at the last frame. I tryed to do a collider animation for the showHide component that will be exact with the hand animation collider for each frame to check if the hand collide in the showHide script and if it collide to have a 4 seconds of waiting with the hand at that position.
I really tryed my best but I really can't figure it out.

Unity 2D: How to add List of Collider2D inside of an if statement condition?

I have a list of Collider2D filled with a bunch of Collider2D's and I want to check to see if the player collides with any of the colliders added from the Unity editor in this list. Something like this snippet here:
using UnityEngine;
public class PlayerScript : MonoBehaviour
{
private Rigidbody PlayerRB;
public List<Collider2D> GroundCOlliders;
public bool IsGrounded;
private void Start()
{
PlayerRB = GetComponent<Rigidbody2D>();
GroundColliders = new List<Collider2D>();
}
private void Update()
{
if (PlayerRB.IsTouching(GroundColliders))
{
IsGrounded = true;
}
}
}
The typical solution is to cast a very short ray from the player's feet downwards and seeing if it hits one of the ground colliders. Consider adding a Ground tag or even layer to check against instead of holding a list of ground colliders. Something like:
// Note the use of FixedUpdate over regular Update for physics-related calculations.
private void FixedUpdate()
{
RaycastHit hit;
if (Physics.Raycast(playerFeetPosition, Vector3.down, out hit, 0.25f) && hit.collider.CompareTag("Ground"))
{
IsGrounded = true;
}
else
{
IsGrounded = false;
}
}
EDIT: I realize that my solution doesn't directly answer your question, but rather assumes that I know what you really want and how to do it better. For a more direct solution to your problem, look into the Physics.OverlapX family of functions.
if you are checking ground you use checkbox, and for the colliders you can use layerMask
Vector3 playerFeetPosition;
Vector3 playerBaseSize;
layermask groundmask;
private void FixedUpdate()
{
RaycastHit hit;
if ( Physics.CheckBox(playerFeetPosition, playerBaseSize,Quaternion.identity, groundmask)
{
IsGrounded = true;
}
else
{
IsGrounded = false;
}
}

Raycast hits 2 objects at the same time

I have recently developed in unity and I have a problem with using the raycast.
I created 3 scripts:
Interactor: Hooked to the player, it manages all the raycast features
InteractionObject: Hooked to the objects that need to animate
InteractionRaycast: Hooked to objects that need to be destroyed
Everything works, the only problem is that when the animation of the gameobject takes place (In my case the gameobject was a pillow), the other gameobject (It is under the pillow) is destroyed at the same time as the animation begins.
My goal is to first move the pillow and then click on the gameobject to be destroyed, what can I do?
Thank you in advance for your help
Interactor.cs
public class Interactor : MonoBehaviour
{
[SerializeField]
private float _interctRange;
private InteractionObject _interactObject;
private InteractionRaycast _interactionRaycast;
private Camera _camera;
private RaycastHit _hit;
// Start is called before the first frame update
void Start()
{
_camera = Camera.main;
}
// Update is called once per frame
void Update()
{
if (Input.GetButton("Fire1"))
{
Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out _hit, _interctRange);
if (_hit.transform)
{
_interactObject = _hit.transform.GetComponent<InteractionObject>();
}
if (_interactObject)
{
_interactObject.PerfomAction();
}
}
}
}
InteractionObject.cs
public class InteractionObject : MonoBehaviour
{
[SerializeField]
private Vector3 _openPosition, _closePosition;
[SerializeField]
private float _animationTime;
private Hashtable _iTweenArgs;
[SerializeField]
public bool _isOpen;
// Start is called before the first frame update
void Start()
{
_iTweenArgs = iTween.Hash();
_iTweenArgs.Add("position", _openPosition);
_iTweenArgs.Add("time", _animationTime);
_iTweenArgs.Add("islocal", true);
}
public void PerfomAction()
{
if (Input.GetButton("Fire1"))
{
if (_isOpen)
{
_iTweenArgs["position"] = _closePosition;
}
else
{
_iTweenArgs["position"] = _openPosition;
}
_isOpen = !_isOpen;
iTween.MoveTo(gameObject, _iTweenArgs);
}
}
}
InteractionRaycast.cs
public class InteractionRaycast : MonoBehaviour
{
[SerializeField]
private float _range;
Ray _myRay;
RaycastHit _hit;
// Update is called once per frame
void Update()
{
if (Input.GetButton("Fire1"))
{
Physics.Raycast(Camera.main.transform.position, Camera.main.transform.forward, out _hit, _range);
if (_hit.transform)
{
Destroy(gameObject);
}
}
}
}
Tip: Use RaycastAll() and filter out the objects you want based on conditions.
It might help you with your problem, although you first should pay attention to #derHugo answer. It points out many aspects that you will want to improve in your code.
Your InteractionRaycast will destroy this own gameObject it is attaced to completely regardless of what exactly you are hitting.
You either want to additionally check like e.g.
if (Input.GetButton("Fire1"))
{
var ray = _camera.ViewportPointToRay(new Vector3(0.5f, 0.5f));
if(Physics.Raycast(ray, out var hit, _interctRange))
{
// Is this actually the object that was hit?
if(hit.transform == transform)
{
Destroy(gameObject);
}
}
}
Or - and in general I would do that - instead of having such a component on each and every object you can interact with and shooting hundreds of redundant raycasts, I would rather have a component on your player object, shoot one single raycast and interact with whatever you hit.
Both your target objects can have a common interface
public interface IInteractionObject
{
void PerfomAction();
}
meaning both types need to implement a method called PerformAction without parameters.
And rather interact directly with that in
public class Interactor : MonoBehaviour
{
[SerializeField]
private float _interctRange;
private Camera _camera;
// Start is called before the first frame update
void Start()
{
_camera = Camera.main;
}
// Update is called once per frame
void Update()
{
if (Input.GetButton("Fire1"))
{
var ray = _camera.ViewportPointToRay(new Vector3(0.5f, 0.5f));
// was something hit at all? => Check the API and return values of methods!
if(Physics.Raycast(ray, out var hit, _interctRange))
{
// Did we hit an IInteractionObject
if(hit.transform.TryGetComponent<IInteractionObject>(out var interactable))
{
// This class has no idea what exactly it is interacting with and doesn't need to know
interactable.PerfomAction();
}
}
}
}
}
and then you have different implementations:
public class AnimatedInteractionObject : MonoBehaviour, IInteractionObject
{
[SerializeField] private Vector3 _openPosition;
[SerializeField] private Vector3 _closePosition;
[SerializeField] private float _animationTime;
[SerializeField] public bool _isOpen;
private Hashtable _iTweenArgs;
private void Start()
{
_iTweenArgs = iTween.Hash();
_iTweenArgs.Add("position", _openPosition);
_iTweenArgs.Add("time", _animationTime);
_iTweenArgs.Add("islocal", true);
}
public void PerfomAction()
{
_isOpen = !_isOpen;
// use ternary makes it easier to read
_iTweenArgs["position"] = _isOpen ? _openPosition : _closePosition;
iTween.MoveTo(gameObject, _iTweenArgs);
}
}
and
public class DestroyInteractionObject : MonoBehaviour, IInteractionObject
{
public void PerfomAction()
{
// This is only called by the Interactor
// it already does a key and raycast check so no need to do that here
Destroy(gameObject);
}
}

Destroying a game object that's part of a prefab once an action key is pressed inside a trigger

so I'm making a 2d platform. My level is made up of a multiple platforms that are all part of a prefab. I want to make it so when my player presses a key (in this case 'E') inside of a collider2d the platform above the player is destroyed and the box resting on the platform falls down.
I've got the detection working for when 'E' is pressed inside of the trigger but can't figure out how to destroy just the single platform in the prefab.
Any help would be appreciated!
public class SwitchController : MonoBehaviour
{
public Collider2D switchCollider;
public Rigidbody2D player;
void Start()
{
switchCollider = GetComponent<Collider2D>();
}
private void OnTriggerStay2D(Collider2D col)
{
// var player = col.GetComponent<PlayerController>();
var actionBtn = PlayerController.action;
if (player)
{
Debug.Log("Collided");
if (Input.GetKeyDown(KeyCode.E))
{
actionBtn = true;
Debug.Log("Action Pressed");
}
}
}
}
If possible, the simplest solution would be to store the platform via the inspector (of the prefab).
Then you would destroy the game-object when needed, like so:
public class SwitchController : MonoBehaviour {
// ...
public GameObject targetPlatform;
private void OnTriggerStay2D(Collider2D col) {
// ...
Destroy(targetPlatform); // Destroy the platform.
}
}
Or, you can raycast upwards from the player
public class SwitchController : MonoBehaviour {
public Collider2D switchCollider;
public Rigidbody2D player;
[SerializeField, Tooltip("The layer mask of the platforms.")]
public LayerMask platformLayerMask;
void Start() {
switchCollider = GetComponent<Collider2D>();
}
private void OnTriggerStay2D(Collider2D col) {
var actionBtn = PlayerController.action;
if (player) {
if (Input.GetKeyDown(KeyCode.E)) {
actionBtn = true;
// Raycast upwards from the player's location.
// (Raycast will ignore everything but those with the same layermask as 'platformPlayerMask')
RaycastHit2D hit = Physics2D.Raycast(player.transform.position, Vector2.up, Mathf.Infinity, platformLayerMask);
if (hit.collider != null) {
// Destroy what it hits.
Destroy(hit.transform.gameObject);
}
}
}
}
}
Compared to the first solution, this solution is more dynamic.
You just have to set the Layers of the platforms in the inspector.

OnCollisionEnter not working Unity3D

I'm trying to build a game where u need to dodge falling objects. I've made a hazard but it seems as if the hazard 'clone' is behaving diffrently.
I've made a collision script when the hazard hits the platform it needs to disappear. This works for the hazard object, but not the hazard clone objects that fall.
As u can see in the first screenshot, the red circled block behaves
like it use to. But the blue circled once (clones) fall right through
objects.
As u can see in the second screenshot, the red circled one is gone,
because it hit the platform. But still the blue once fall right
through.
Thanks in advance!
Below u will find the Collision script, below that is the Hazard Spawn script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HazardCollisionFunctions : MonoBehaviour {
#region Variables
//Public
//Private
#endregion
#region UnityFunctions
void Start()
{
}
void Update()
{
}
#endregion
private void OnCollisionEnter(Collision collision)
{
if(collision.gameObject.tag == "platform")
{
this.gameObject.SetActive(false);
}
if(collision.gameObject.tag == "Player")
{
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnHazards : MonoBehaviour {
#region Variables
//Public
//Private
[SerializeField]
public float minX = 0.0f;
[SerializeField]
public float maxX = 0.0f;
[SerializeField]
private GameObject[] hazards; //potential array of hazards
[SerializeField]
private float timeBetweenSpawns = 0.0f;
private bool canSpawn = false;
private int amountOfHazardsToSpawn = 0;
private int hazardToSpawn = 0;
#endregion
#region UnityFunctions
public void Start()
{
canSpawn = true; //Temp start
}
public void Update()
{
if(canSpawn == true)
{
StartCoroutine("GenerateHazard");
}
}
#endregion
private IEnumerator GenerateHazard()
{
canSpawn = false;
timeBetweenSpawns = Random.Range(0.5f, 2.0f); //Testing values
amountOfHazardsToSpawn = Random.Range(1, 5); //Testing values
for(int i = 0; i < amountOfHazardsToSpawn; i ++)
{
Vector3 spawnPos = new Vector3(Random.Range(minX, maxX), 8.0f, 0.0f); //Gen spawnpoint for the hazard
Instantiate(hazards[hazardToSpawn], spawnPos, Quaternion.identity); //Spawn the hazard
}
yield return new WaitForSeconds(timeBetweenSpawns);
canSpawn = true;
}
}
OnCollisionEnter
OnCollisionEnter takes Collision object as a parameter and it requires the isTrigger property of the attached Collider component to be FALSE.
void OnCollisionEnter(Collision collision)
{
foreach (ContactPoint contact in collision.contacts)
{
Debug.DrawRay(contact.point, contact.normal, Color.white);
}
}
OnTriggerEnter
OnTriggerEnter takes Collider object as a parameter and it requires the isTrigger property of the attached Collider component to be TRUE.
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("CheckPoint"))
{
Destroy(other.gameObject);
}
}
If you are instantiating the object from prefab, make sure that
prefab have required components (rigidbody/collider) and
properties to achieve the desired behaviour.
To detect Collision/Trigger, at least one of the object must have a
physics component (Rigidbody)
Rigidbody MUST be attached to the moving object.
Hope this helps :)

Categories