PUN 2 Audiosource plays multiple times - c#

I have animation events linked up to my characters that play a random footstep noise when they step. I'm trying to set it up for multiplayer, but I'm having some issues. With one person, the sounds only play once when they're supposed to. However, as tested with 2 people, it plays each footstep twice at the same time when one player steps. Each player has an audiosource component. Both footsteps sounds come from the audiosource of the player running, so it's not a case of both players playing the same sound. Any ideas as to why the sound is duped and played at the same time? The double sound comes from the same client, but only when that client is in multiplayer. And it's not when other people are walking, only the client. I must be setting up something wrong or putting something in the wrong place with my RPC.
1 player with 1 audiosource: sounds plays once
2 players with their own audiosource: sounds duplicates and plays at the same time
2 players with audiosource enabled for only the one walking: sounds still plays twice
From my player code
public void PlayFootstep()
{
int clipPick = Random.Range(0, footstepArray.Length);
GetComponent<AudioSource>().clip = footstepArray[clipPick];
photonView.RPC("PlayFootstepRPC", RpcTarget.All);
}
[PunRPC]
private void PlayFootstepRPC()
{
if (GetComponent<AudioSource>().isActiveAndEnabled && GetComponent<PlayerMovement>().ySpeed > 1.15)
{
GetComponent<AudioSource>().Play();
}
}

If PlayFootstep is called via an animation event, and you have animations synchronized via PhotonAnimatorView, then the PlayFootstepRPC() gets called several times, once per each connected client.
PhotonAnimatorView makes an object to play the same animations on every client. The PlayFootstep function gets called on every client, and every client sends RPC to itself and other clients, and that RPC plays the sound.
I suggest you should either not play footstep sounds via RPC, playing it locally instead (because animation event handles it for you), or add a check of PhotonView.IsMine before calling an RPC.

Related

How do I play an array of audio in turn in Unity?

I have a fairly large array of 60 tracks. I need them to play one by one. When the scene changes, the track is also interrupted and the next track is played in turn. That is, in one scene can play 0, 1, 2 items. After switching scenes, 3 should play, and so on. On the Internet I found a function that seems to work the way I want. But I do not quite understand how to call function correctly.
public AudioClip[] clipArray;
public AudioSource effectSource;
private int clipIndex;
void PlayRoundRobin() {
if (clipIndex < clipArray.Length)
{
effectSource.PlayOneShot(clipArray[clipIndex]);
clipIndex++;
}
else
{
clipIndex = 0;
effectSource.PlayOneShot(clipArray[clipIndex]);
clipIndex++;
}
Put your method into your scene manager.
If you load the scene, call the method.
After that, call the method whenever you want to play the next track.

Calling function more than once causes gameplay issues in Unity

I am currently working on translating a VR game to the Oculus Quest 2 from a PC standalone version. In this game, the game menu cannot be accessed by the player wearing the headset, as it is not visible to the player; it is instead accessed by another party at the computer itself. When the person at the computer clicks, start game, a number of processes begin, including a coroutine to spawn multiple instances of a game object in a non-player enemy's hands.
Part of the translation process includes allowing the player to start the game from the Oculus Touch controllers, I am attempting to implement a feature where either of the four face buttons will start the game.
if (OVRInput.GetDown(OVRInput.Button.One) || OVRInput.GetDown(OVRInput.Button.Two) || OVRInput.GetDown(OVRInput.Button.Three) || OVRInput.GetDown(OVRInput.Button.Four))
{
startGameClick();
}
However, it seems like calling startGameClick(); more than once, whether in the same script or otherwise, causes the game to not run certain processes, chief among them the ball spawn coroutine. This causes the NPC enemies to activate their throwing animations without having a ball to throw, and they do not return to their idle animations afterwards. I am unsure why this is, however it has been a major roadblock in attempting the platform translation.
Additionally, this is the startGameClick(); function:
// If the start game button is clicked
public void startGameClick() {
StandaloneServer.startgame = true;
if (Master.usingMM && ServerController.IsServerReady())
Master.ready = true;
else if (!Master.usingMM)
Master.ready = true;
roundController.startInput();
beginGameButton.GetComponentInChildren<Text>().text = "In Progress";
beginGameButton.interactable = false;
}
My assumption is that one of the references in this function is the source of the issue, but I cannot pinpoint which one.
Base Information
Unfortunately, to answer your question, we would need to see all of the related scripts for this function which just isn't feasible as that would also require us to be kind enough to sift through your code to find your error.
A Cheap Solution
However, there is a cheap solution to your issue. You can simply reload the scene (or reset the script if it's DNDOL) and it should work again.
It seems I've found a solution at long last.
Instead of calling the startGame() function again, I decided to invoke onClick() to simulate the effect of clicking the button with a mouse, although the player is actually touching the button in the VR space. This worked, and the game is running as it should.

Unity StateMachineBehaviour : Start function called once?

i'm making my first enemy AI in unity. I'm trying to make a finished state machine with the animator controller to do it.
I just discovered the StateMachineBehaviour script which is called when the AI is in a state. It has multiple methods, including the OnStateEnter. It is called everytime the AI enter the state.
My problem is only about optimization, my AI need to get the GameObject "Player" in order to attack it. So i'm getting it in my OnStateEnter method for the moment, which i feel is bad, because i'm getting it every time the animation is called, i would like to get it only once, at the start.
I basicly need a start function but it's not working, i have made research and found nothing. I tried to watch video about people making a finished state machine but they are just getting the same GameObject multiple time ( example here : https://youtu.be/dYi-i83sq5g?t=409 ).
So, is there a way to have a start function or to get an element only once ?
I could make a bool that is called only the first time and that get the GameObject, but again that would be an "useless" if running in my function.
Any suggestions ? Thanks
No, unlike a MonoBehaviour a StateMachineBehaviour has no Start message only OnStateEnter, OnStateExit, OnStateIK, OnStateMove and OnStateUpdate.
There are also Awake and OnEnable but I'm pretty sure they are not used in the StateMachine and might not behave as expected.
You can however either use
OnStateMachineEnter
Called on the first Update frame when making a transition to a StateMachine. This is not called when making a transition into a StateMachine sub-state.
Or use a simple bool flag like
bool alreadyExecuted = false;
OnStateEnter()
{
if(alreadyExecuted) return;
// Do your stuff
alreadyExecuted = true;
}
(Just a guess)
In the Inspector you actually can enable and disable StateMachineBehaviours like components. So it might be able to do this also in script maybe the same way using
enabled = false;
but I didn't find anything about it in the API and since I'm currently on my Smartphone I can't test it.

Multiplayer Game with players on separate versions of the same map

I'm making/want to make a bullet-hell game where there are essentially two players, each own their own identical copy of a map competition for the high score. And the game ends whenever a player dies.
I have all of this working in multiplayer, but my approach seems a bit hacky, so I'm wondering if there's a better way for me to accomplish this.
At first I was using just unity's network manager, and in my player Start() function I checked if(!isLocal), and if so I set the gameObject enabled to false. This worked great, but like I said, felt a little hacky.
Example of the code:
if (!isLocalPlayer) {
gameObject.SetActive(false);
return;
}
Next I moved to unity's LobbyManager. This is where things got really sticky. Now on the Host, the game loads fine, but on the client, only one game object is created, and it's set as disabled which leads me to believe that it is the 'enemy' or not local player object.
I slowly figured out that the cause of this was setting the enemy game object active to false. If I left it true, both players would spawn on both screens. My solution now is to not disable the enemy player object, but every component on it so it doesn't get in the way.
Again this feels very hacky, and like it could lead to problems down the road. Is this really the best option, or am I missing something obvious?
Example of the Code:
if (!isLocalPlayer) {
gameObject.GetComponent<MeshRenderer>().enabled = false;
gameObject.GetComponent<BoxCollider>().enabled = false;
gameObject.GetComponent<CharacterController>().enabled = false;
gameObject.GetComponent<PlayerController>().enabled = false;
gameObject.GetComponent<Rigidbody>().detectCollisions = false;
guns = gameObject.GetComponentsInChildren<MeshRenderer>();
foreach (MeshRenderer gun in guns) {
gun.enabled = false;
}
}
Thanks in advance! Sorry this is so long, hopefully it isn't a chore to read.
Make an empty scene with nothing but a score manager and what else you need to transfer.
Have another scene as an asset in your project folder, this is where the map is.
When a player joins, have them join the scene with the score manager.
If they also are the local player, they should load the map scene.
That way, both players will be in the same scene while also having their own instance of the map.
You can load scenes in asynchronously and additively, which would be ideal for your situation.

AudioClip only plays one time on Trigger

In my game I am creating in unity I am trying to play an audio clip every time that a collision happens on one of my game objects. For some reason the audio will play the first time there is a collisiom, but from then on it no longer plaus the sound.
The game object that is collided with has a component for the AudioSource and I have the audio clip selected in that component.
Here is the code where I start the audio:
if (PlayerPrefs.GetInt ("Sound Playing", 1) == 1)
{
audio = GetComponent <AudioSource>();
audio.Play ();
}
I have also tried using the PlayOneShot () method but it does the same thing.
EDIT
Here is a small representation what of my game is doing here:
public class Ship : MonoBehaviour
{
void OnTriggerEnter2D(Collider2D collider)
{
Laser laser = collider.gameObject.GetComponent<Laser> ();
if (laser)
{
if (!immune)
{
//update healthbar
healthbar.hit();
//reset health boost
forcefield.resetHealthBoost();
health -= laser.getDamage(); //remove health from ship
//create explosion
GameObject explosion = Instantiate(explode, gameObject.transform.position, Quaternion.identity) as GameObject;
explosion.GetComponent<ParticleSystem>().startColor = this.GetComponent<SpriteRenderer>().color;
if (SoundEffects.soundOn && PlayerPrefs.GetInt("Sound Playing", 1) == 1)
{
audio = GetComponent<AudioSource>();
audio.Play();
}
}
laser.hit();//destroy the laser that hit the ship
}
}
}
Here is to show that I have the AudioSource and the AudioClip setup in the inspector (the AudioSource is attached to the ship):
The issue with my game was that I didn't realize that using gameobject.SetActive(false) later in my code was stopping the second audio clip from being able to play. I set it to false when the ship was destroyed and this is what the problem was all along. It works fine now.
It's looks with audio it's all right. May be problem is in collisions or something else. Add code Debug.Log("Play sound."); before code line audio.Play();, and test.
Make sure your audio listener is near the explosion too. It could be far away and may not be heard. Also do a check to see if audio.isPlaying, then play audio. Audio may be still playing and you have to stop it properly.
if (audio.isPlaying) {
audio.Stop();
audio.Play();
}
If all else fails you can always do PlayOneShot(), which will essentially do as it says. audio.PlayOneShot(); This could be problematic though since if you are putting it OnTriggerEnter() it may run more than once in a short duration. You may hear it stutter because it's queued up however many explosions.
Finally, don't use PlayerPrefs for those specific things. You should reserve it honestly for Game Specific data storage. Why can't you simply use isPlaying? PlayerPrefs will only make the task very convoluted. I learned it the hard way when I used to use PlayerPrefs to transfer data from C# to UnityScript. In the end it's almost best to use Static Variables, or create a Singleton class and commit values to various instances. Plus I learned that I can use methods and variables cross languages when I adjust the compilation order(s). A little bit off topic, but still substantially important.

Categories