Using unity 2020.3 and the XR Plug-in (currently only oculus but will be moving to openxr i hope) and trying to start the microphone when secondary button is pushed. It works but starting the microphone causes lag. Coroutine doens't help, I tried threading which stops the lag but then can't do anything with the audioclip. This has been asked a few times over the years but no answer yet. Here's the code:
void Update()
{
foreach(var d in devices){
if (d.TryGetFeatureValue(CommonUsages.secondaryButton, out isPressed)){
if (isPressed && !wasTalking)
{
wasTalking = true;
asource.PlayOneShot(walkietalkie);
//start_recording = new Thread(startRecording);
//start_recording.Start();
startRecording();
}
else if (wasTalking && !isPressed){
finishRecording();
wasTalking = false;
}
}
private void startRecording(){
recording = Microphone.Start(null, false, 30, freq);
startRecordingTime = Time.time;
yield return null;
}
Edit: I've removed the useless coroutine. Why the -1 to my question?
The primary way I reckon that you could alleviate the stutter is moving this work to another thread, which is notoriously hard with Unity.
A solution to this could be using or adapting another method of recording audio like NAudio.
Use coroutines.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Threading;
public class TestMic : MonoBehaviour
{
[SerializeField] AudioSource self;
public int samplerate = 44100;
public int time = 10;
// Start is called before the first frame update
void Start()
{
//Debug.Log(Microphone.devices);
StartCoroutine("test");
}
IEnumerator test()
{
self.clip = Microphone.Start(null, true, time, samplerate);
self.loop = true;
self.Play();
yield return null;
}
// Update is called once per frame
void Update()
{
}
}
Related
When I useProfiler.BeginSample("Update Example") and Profiler.EndSample() in Update(), I can see "Update Example" in Update.ScriptRunBehaviorUpdate in the profiler window.
But when I use Profiler..."Start Example"... in Start(), I cannot see or find any mention in the profiler. Is there any way to track code in Start()?
using UnityEngine;
using UnityEngine.Profiling;
public class Example : MonoBehaviour
{
BoxParameter bParameter;
// Start is called before the first frame update
void Start()
{
Profiler.BeginSample("Start Example");
bParameter = GameObject.Find("Main Camera").GetComponent<ZombieInventory>().box;
Debug.Log(bParameter.width);
Profiler.EndSample();
}
// Update is called once per frame
void Update()
{
Profiler.BeginSample("Update Example");
bParameter = GameObject.Find("Main Camera").GetComponent<ZombieInventory>().box;
Debug.Log(bParameter.width);
Profiler.EndSample();
}
Play with Clear on Play and Deep Profile turned on.
Moves to 1Frame after playback. It is easier to step using a controller.
Then you can check the following.
To see if you get a value, try searching for the script name in the search window. It will not give you the details, but you can check if the value is taken.
The following test code was used.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
public class ProfilingTest : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
Profiler.BeginSample("Start Example");
var list = new List<int>();
for (var i = 0; i < 2000000; ++i)
{
list.Add(i);
}
Profiler.EndSample();
}
// Update is called once per frame
void Update()
{
Profiler.BeginSample("Update Example");
var list = new List<int>();
for (var i = 0; i < 2000000; ++i)
{
list.Add(i);
}
Profiler.EndSample();
}
}
BTW: Maybe it is because it is test code, but the Find function is heavy and should not be used. caching Camera.main is the fastest way. the same goes for GetComponent.
Camera _camera;
void Start()
{
_camera = Camera.main;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FirePistol : MonoBehaviour {
public GameObject TheGun;
public GameObject MuzzleFlash;
public AudioSource GunFire;
public bool IsFiring = false;
void Update () {
if (Input.GetButtonDown("Fire1"))
{
if (IsFiring == false)
{
StartCoroutine(FiringPistol());
}
}
}
IEnumerator FiringPistol ()
{
IsFiring = true;
TheGun.GetComponent<Animation>().Play("PistolShot");
MuzzleFlash.SetActive(true);
MuzzleFlash.GetComponent<Animation>().Play("MuzzleAnim");
GunFire.Play();
yield return new WaitForSeconds(0.5f);
IsFiring = false;
}
}
I am writing a gun mechanic And
I wonder why we need
yield return new WaitForSeconds(0.5f); .What is the difference without this command .It's really unnecessary to write this code coz the time is short .Afterall , will it be an error like scene crash after i deleting this code ?Any help is greatly appreciated !
In general: Every method returning IEnumerator has to contain at least one yield statement. In Unity you have to use StartCoroutine to run an IEnumerator as Coroutine.
In your specific case: So you can delay your code by 0.5 seconds!
It is short but 0.5 is about 30 frames!
Someone using e.g. something like AutoClicker could jam the fire key each frame so he would cause significantly more damage then someone playing "normal" (due to physical limitations of your keyboard and finger ;) )
You are just avoiding that and limit down firing to a maximum of 2x per second.
In general - as usual - there are multiple ways to achieve that and you could go without Coroutines entirely but it makes coding so much cleaner and easier to maintain then doing everything in Update!
As some alternative examples for simple delays as here you could also either do a simple timer in Update
private float timer;
void Update ()
{
if(timer > 0)
{
// reduce the timer by time passed since last frame
timer -= Time.deltaTime;
}
else
{
if(Input.GetButtonDown("Fire1"))
{
FiringPistol();
timer = 0.5f;
}
}
}
void FiringPistol()
{
TheGun.GetComponent<Animation>().Play("PistolShot");
MuzzleFlash.SetActive(true);
MuzzleFlash.GetComponent<Animation>().Play("MuzzleAnim");
GunFire.Play();
}
or you can also use Invoke with a given delay.
bool canFire;
void Update ()
{
if (Input.GetButtonDown("Fire1") && canFire)
{
FiringPistol();
}
}
void FiringPistol()
{
TheGun.GetComponent<Animation>().Play("PistolShot");
MuzzleFlash.SetActive(true);
MuzzleFlash.GetComponent<Animation>().Play("MuzzleAnim");
GunFire.Play();
Invoke(nameof(AfterCooldown), 0.5f);
}
void AfterCooldown()
{
canFire = true;
}
In general btw you should store the Animation references to not use GetComponent over and over again:
// if possible already reference these via the Inspector
[SerializeField] private Animation theGunAnimation;
[SerializeField] private Animation muzzleFlashAnimation;
private void Awake()
{
// as fallback get them on runtime
// since this is a fallback and in best case you already referenced these via the Inspector
// we can save a bit of resources and use GetComponent
// only in the case the fields are not already set
// otherwise we can skip using GetComponent as we already have a reference
if(!theGunAnimation) theGunAnimation = TheGun.GetComponent<Animation>();
if(!muzzleFlashAnimation) muzzleFlashAnimation = MuzzleFlash.GetComponent<Animation>();
}
then later you reuse them
theGunAnimation.Play("PistolShot");
MuzzleFlash.SetActive(true);
muzzleFlashAnimation.Play("MuzzleAnim");
GunFire.Play();
A little background: I am making a small bit of game that involves clicking and dragging a clock hand to add time to a bank. The issue I'm having is with the clicking and dragging process. After an amount of time or multiple clicks, clicking on the clock face no longer works to make the variable "isActive" true. I've confirmed that it stops working somewhere in the code below and not in the code that checks isActive. Thanks for the help.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ClockFace : MonoBehaviour
{
public bool isActive = false;
public bool isAble = false;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (isActive)
{
if (Input.GetButtonUp("Fire1"))
{
isActive = false;
}
}
if (Input.GetButtonDown("Fire1"))
{
if (isAble)
{
isActive = true;
}
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("Mouse"))
{
isAble = true;
}
}
private void OnTriggerExit2D(Collider2D collision)
{
if (collision.gameObject.CompareTag("Mouse"))
{
isAble = false;
}
}
}
Unitys old imput system is't very good now that the new imput system is out. It comes in the unity 2019.3 update, but you can install it from the package manager in earlier versions. I recomend to use that.
Hey so I'm building a baseball game and I'm using OnTriggerEnter to detect whether or not the baseball enters the strike zone. The strike zone is big enough so it can be detected and the ball isn't moving too fast. I want to display ("STRIKE!) once it hits the trigger, but nothing is happening. pls
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Strike : MonoBehaviour {
public AudioSource src;
public ParticleSystem stars;
public Text StrikeText;
private void Start()
{
StrikeText.text = " ";
}
public void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "ball")
{
src.Play();
Instantiate(stars, transform.position, transform.rotation);
StrikeText.text = "STRIKE!";
Destroy(other.gameObject);
SecondsWait();
StrikeText.text = " ";
}
}
IEnumerator SecondsWait()
{
yield return new WaitForSeconds(4);
}
That's not how coroutines work.
This is what your code is doing, line by line:
src.Play();
Instantiate(stars, transform.position, transform.rotation);
StrikeText.text = "STRIKE!";
Destroy(other.gameObject);
All of that runs as normal, just as you expect/want
SecondsWait();
This invokes a function that then invokes yield return new WaitForSeconds(4); As your code doesn't do anything with this return value, nothing happens, and the WaitForSeconds object gets garbage collected.
StrikeText.text = " ";
Then this line runs immediately, clearing out the text you'd previously set.
This is what you probably actually want:
src.Play();
Instantiate(stars, transform.position, transform.rotation);
Destroy(other.gameObject);
StartCoroutine(SecondsWait());
//...
IEnumerator SecondsWait()
{
StrikeText.text = "STRIKE!";
yield return new WaitForSeconds(4);
StrikeText.text = "";
}
By calling StartCoroutine() and passing the return of SecondsWait() to it, the SecondsWait() method itself (not OnTriggerEnter!) pauses when the yield occurs.
By the way this kind of code architecture is very poor. UI should never be related directly on your domain. You should consider using event-driven approach, even if you are just a very beginner.
Here are the two Error messages I am receiving.
This seems to only happen when the Run animation is playing. Any help would be appreciated.
I am building this game for android and I am using mouse clicks to move the character. Since mouse clicks translate to touch events this should have no barring on the game as far as I know.
I guess I should also note that the animations play fine while playing the game.
'defaultModelfbx' AnimationEvent 'FootL' has no receiver! Are you missing a component?
'defaultModelfbx' AnimationEvent 'FootR' has no receiver! Are you missing a component?
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PlayerController : MonoBehaviour
{
float speed = 10;
float rotSpeed = 5;
Vector3 targetPosition;
Vector3 lookAtTarget;
Quaternion playerRot;
bool moving = false;
Animator thisAnim;
void Update()
{
thisAnim = GetComponent<Animator>();
// Get movement of the finger since last frame
if (Input.GetMouseButton(0))
{
SetTargetPosition();
}
if (moving)
{
Move();
}
}
void SetTargetPosition()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if(Physics.Raycast(ray, out hit, 1000))
{
targetPosition = hit.point;
lookAtTarget = new Vector3(targetPosition.x - `transform.position.x, 0, targetPosition.z - transform.position.z);`
playerRot = Quaternion.LookRotation(lookAtTarget);
moving = true;
}
}
void Move()
{
thisAnim.SetFloat("speed", 1);
transform.rotation = Quaternion.Slerp(transform.rotation, playerRot, rotSpeed * Time.deltaTime);
transform.position = Vector3.MoveTowards(transform.position, targetPosition, speed * Time.deltaTime);
if (transform.position == targetPosition)
{
moving = false;
thisAnim.SetFloat("speed", 0);
}
}
}
I had the same error, but with a different cause and solution.
All my code was in a (grand)parent gameobject (e.g. WerewolfPlayer) and I wanted to keep it this way. The animator was in a (grand)child gameobject (e.g. WEREWOLF_PBR) and it couldn't find the animation events:
To fix this, I bubbled-up the event from the child gameobject to the parent:
I edited the PlayerController.cs in the parent gameobject (e.g. WerewolfPlayer) to find the new "hand off animation events" script and fire the animation events when they happen:
using UnityEngine;
using System;
public class PlayerController : MonoBehaviour
{
private HandOffAnimationEvent handOffAnimationEvent;
// called when animation event fires
public void Pickup()
{
Debug.Log("player picks up object here...");
}
void OnEnable()
{
handOffAnimationEvent = GetComponentInChildren<HandOffAnimationEvent>();
handOffAnimationEvent.OnPickup += Pickup;
}
void OnDisable()
{
handOffAnimationEvent.OnPickup -= Pickup;
}
}
The new HandOffAnimationEvents.cs was added to the child gameobject (e.g. WEREWOLF_PBR) and when the animation event fires, it fires its own event:
using UnityEngine;
using System;
public class HandOffAnimationEvent : MonoBehaviour
{
public event Action OnPickup;
// This is the animation event, defined/called by animation
public void Pickup()
{
OnPickup?.Invoke();
}
}
Okay thanks to Nathalia Soragge I have found the solution to the problem.
If you look closely at the animation window you can see two white dashes at the top of the time line. I have clicked on one of them in the picture, and sure enough it is one of the events that were throwing the error message.
I am assuming I can just delete these two events since I do not have them anywhere in code. In this case it is looking for the events in my PlayerController since it is attached to my Model defaultModelfbx
UPDATE: I have deleted both events and now everything is running smoothly. Thanks again Nathalia!!!!!!! ;)
If you don't want to listen to any events, you can set Animator.fireEvents to false.
public Animator animator;
void Start()
{
animator.fireEvents = false;
}
https://docs.unity3d.com/ScriptReference/Animator-fireEvents.html
I had the same problem.I solved this by deleting all animation events and recreating all of them again.