Calling function more than once causes gameplay issues in Unity - c#

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.

Related

Character attacks sometimes collide when collision isn't taking place. - Unity Collision

I'm working on a 3D top down game in Unity 2021.3.4f1 (HDRP). In the game I have a player character that's already placed in the game scene before it starts and enemy characters that are instantiated at random spawn points within a certain distance of the player. The enemies and player can use melee attacks to attack each other. Once in a blue moon the player attacks will hit an enemy that the attacks aren't colliding with. This also happens vice versa, where an enemy's attacks will hit the player when the colliders aren't colliding. Otherwise the attacks and collision work flawlessly. I'm 90% sure that this wasn't occurring before I upgraded the engine from a prior 2019 version. Any ideas to as why this could be happening?
I'm using an attack pooling system, where attacks are created with their set info (i.e. attack damage, how many characters can be hit with a single attack etc.) and then thrown into a pool of attack objects. After the attack is registered and has finished, it's deregistered and kept in the pool for further use. I'm using a bool return method called IsCollided that takes in the attacks information as a parameter to detect the collision:
private bool IsCollided(AttackInfo info)
{
foreach (KeyValuePair<TriggerDetector, List<Collider>> data in
control.animationProgress.CollidingBodyParts)
{
foreach (Collider collider in data.Value)
{
foreach (AttackPartType part in info.AttackParts)
{
if (info.Attacker.GetAttackingPart(part) == collider.gameObject)
{
//We want to get the attack scriptable object
control.animationProgress.Attack = info.AttackAbility;
//We also want to get the attacker
control.animationProgress.Attacker = info.Attacker;
//We also want to get the damaged trigger
control.animationProgress.DamagedTrigger = data.Key;
//And then we return true because the attack has collided
return true;
}
}
}
}
//Otherwise it hasn't collided
return false;
}
Basically what I'm trying to say is that attacks will sometimes trigger hit reactions when they're not actually colliding with another character. you could be 15 feet away from a character and you'll collide with them. Most of the time it works when it should. But on a rare occasion this issue occurs.
I'm using a "TriggerDetector.cs" script that has an OnTriggerEnter() method that stores all of the collisions that interact with each character's colliders in a list. Those are then removed from the list using an OnTriggerExit() method that removes the collided with objects when the collision is no longer occurring. Aside from that there's tons of checks which detect if the attack is allowed to collide. There's no issues with that though as the attacks always collide when they should be aside from this issue.
Any help would be greatly appreciated!
I speculate that your system for adding and removing colliders may not work in all cases.
Specifically, I believe that ontriggerexit is not always called correctly, or there is some other problem in the method which, however, I cannot see because you have not published it.
I can advise you to use a different system: instead of using collisions, you can check every frame or every short time with a coroutine, if there are enemies within your area.
To do this, you can study how circles work in mathematics and how you can use them to check if an object is within the area around you.
Since 2019 Unity has made many changes and has probably changed, perhaps making it worse, the collision system.
If you need help with the advice on using the circumference, I can help you, perhaps by opening another question.
If I helped you, you could thank me by marking my answer as accepted: I would be grateful :)
P.S. Instead of foreach use for - it's much better for performance.

How can I realise different actions (e.g. change scenery, interact with npc, pick up items) using the input system of Unity?

I am using the (new) input system of Unity and have the problem to realise an interaction with different objects. I am still quite new to Unity and hope to find some help.
I have created an event (OnInteract) that listens to whether e has been pressed.
Next I created an object with a collider into which my player can run. Whenever the player meets a certain collider, something should happen. In my case I want to change the scene. However, on my initial scene there are two doors that the player can open by pressing e. I have given both doors the same layer name because they are both exits.
Basically, it works that I can only press e when I hit this particular collider. However, I don't know how to do it instead of performing two different actions. Maybe there is a way to give the objects a script that I can trigger via the PlayerMovement script. Without taking it into the player.
this is my script that works so far:
void OnInteract(InputValue value)
{
if (myBoxCollider.IsTouchingLayers(LayerMask.GetMask("Entrance")))
{
Debug.Log("interact with the door");
}
}
or is there perhaps a way to listen to the "tag" instead of the layer?
I have also come across interfaces, but have not really understood how these help me. Or how I have to use them here to make them work.
In the end you're going to have to check some kind of tag, layer, or reference a component. it's just your preference. To make it a little simpler you can shoot a raycast in the direction you are looking to check for objects instead of having colliders. But in the end it's doing the same thing.
I don't really use interfaces as I'm pretty new myself but from what I can tell they are generally used for organization and global accessibility in similar objects, like doors lol.

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.

DontDestroyOnLoad is not Working on Scene?

I Need to apply DontDestroyOnLoad on Scene.is it possible?
I Need to Do not disturb the scene when going in to another scenes also.here i'm sending mail,when ever clicking send button its going to authentication in mail server in this time my scene idle means not responding anything until come back to the response from mail server,so on that time i show one loading bar in my scene.this is not do process.the entire scene is hang until came response from mail server,so how to solve this?
void Awake()
{
DontDestroyOnLoad(this.gameObject);
}
After reading so many non-answers I finally found the answer in a Unity forum. DontDestroyOnLoad ONLY works if the game object in question is at a "root level" meaning right under the scene, not nested under any other object. This is not mentioned anywhere in the documentation.
When loading a new level, scene, all Game Objects of the previous scene are destroyed by Unity.
If you want to protect a Game Object you can use the function.
DontDestroyOnLoad(gameObject);
It is important to note that when you say: this.gameObject you are pointing to pretty much the same thing, it just happens that this points directly to the script attached to that gameObject. So you don't need the this part, just gameObject will do.
Ideally you could protect that gameObject inside void Awake()
void Awake()
{
DontDestroyOnLoad(gameObject);
}
The above code will prevent Unity from destroying that gameObject unless your game closes completely or at a later point you call Destroy() on it. That means you can change from scene to scene and the gameObject will survive. However, if you make it back to the scene that creates that gameObject you are protecting you may run into problems if you do not have the logic implemented that prevents you from protecting a second, third, or many of that gameObject.
Your second question, if I understand it correctly:
You want to send mail when you change scenes, but your progress bar wont progress while changing scenes, it just stays there, static.
If that is the case then your problem is in Application.LoadLevel(sceneName); If you have the free version of Unity, then you need to come up with your own creative way of showing that progress bar, because Application.LoadLevel() will halt everything until it takes you to the new scene.
I don't completely understand what you are saying.
But because in your context, this in represents most probably a Monobehaviour, try the following:
void Awake() {
DontDestroyOnLoad(this.gameObject);
}
or
void Awake() {
DontDestroyOnLoad(gameObject);
}
See http://docs.unity3d.com/Documentation/ScriptReference/Object.DontDestroyOnLoad.html
I recommend you use a coroutine, with the 'yield' statement
check out this documentation of the WWW class, which likewise involves writing code to cope with waiting for a response from the web, without hanging your Unity3d program
coroutines are pretty powerful if you're working with tasks that take more than a frame or two. Richard Fine (AltDevBlog) has posted a really detailed description of what they are and how to use them, which I thoroughly recommend.

Categories