I’m making a continuous Metroidvania-style sidescroller, with a variety of different rooms and corridors, each saved as a different scene. I want the player to be able to transition seamlessly through these rooms - i.e. the run out on one side of the screen, and run back in on another.
However, whenever I change a scene through Application.LoadLevel(), my input get’s completely reset. I’ll be holding down the left key - GetAxis() returning a -1 - the next scene will load, and I’ll suddenly GetAxis will return 0. It won’t start returning normal values, until I release the key and press it again.
I’ve experimented around with this, and it looks like the only axis that Input retains between scenes is whatever the last key pressed was. So if I’m only running between scenes it works, fine, but if do a running jump between scenes, by Horizontal axis goes to 0 and all forward movement stops.
Has anyone seen this before? Does anyone know any work arounds for this?
I’m not sure about the solutions, but I have a couple ideas (though I have no idea if they’d work):
Try LoadLevelAsync instead, maybe it doesn’t reset the axis?
Try to understand the exact moment when the axis is reset. Is it when you start loading the level, or when it’s loaded? You might find a way to store the axis just a moment before, and the re-apply them the moment later (though this looks messy :-P)
I tested using LoadLevelAsync, it doesn’t change anything. Input buttons get reset anyways.
How can I set the axis of an Input button? The problem is that when you switch scenes, the Input doesn’t even return the GetKeyUp event. It’s like it doesn’t know anything about what keys are pressed.
The more robust way is to only use Input.GetKey(KeyCode k), and track up / down yourself. I typically wrap up Unity’s Input system in my own classes, entirely - for two reasons.
avoid funny behavior like this, or tabbing in and out and missing up / down events.
allow users to dynamically rebind keys.
That said, if they are internally calling ResetInputAxes - the documentation says “Resets all input. After ResetInputAxes all axes return to 0 and all buttons return to 0 for one frame.”
In which case, you might be out of luck. I’d file a bug on that.
Even using Input.GetKey the inputs gets reset… I’m making a game where the player can walk/run and enter and exit rooms, buildings and etc. Then everytime the player is teleported for another level (room, buildings, etc) he stops walking and running… I think the inputs are getting reset internally like you said… frustrating =/
Working on a game with Castlevania SOTN like level transitions and this is making my life hell. Any ideas on how to fix this? Feels like a bug to me, so I’m surprised there isn’t a patch or some sort of workaround for it.
Are you using a player object that have Unity - Scripting API: Object.DontDestroyOnLoad in them? I’d assume that if your motor functionality were all on the player object with this attached some where via codes that it shouldn’t affect it. But I’ve not really tried it as loading rooms as separate scenes feels/sounds pretty clunky to me.
Was able to solve the input issue using additive loading and it works great. Here is a test video I threw together of it in action. If anybody has questions on how to get this working just ask and I’ll gladly help.
Confirmed with the additive loading–it worked for my game too.
Instead of using Application.LoadLevel(“whatever”) I set up a function that destroys every game object in the scene and then additively loads the level again. Kind of a silly work-around but it allows my player to hold down the “Sprint” axis the whole time instead of having to let go of it every time they die and the level reloads.
Sample codez0rs:
public static class LevelManager
{
private static Transform[] allObjects;
public static void Reload()
{
//Find & destroy all objects in scene
allObjects = GameObject.FindObjectsOfType(typeof(Transform)) as Transform[];
foreach (Transform t in allObjects)
{
GameObject.Destroy (t.gameObject);
}
Application.LoadLevelAdditive (Application.loadedLevel);
}
}
Works great for my scenario. Surprisingly simple fix too.
Urgh. Don’t want to destroy all objects manually… I’m afraid I’ll forget something, and my whole hierarchy built with DontDestroyOnLoad doesn’t work any more. Any solution when not using Additive load?
Even worse, the DontDestroyOnLoad flag is not even exposed to the developer.
A lot of people having problems with this, and some even created their own scene management (from scratch!) just to overcome this problem:
I feel there is some pretty lazy development here, not having fixed this after 2 years.
Nothing to fix here actually. Think of a level load as a complete refresh of the entire application. It dumps everything in the system and restores it to the original state. You have to use additive level loading if you want to fix it. If you need some help I put together a live training video on how to write some code for this from scratch a few months back.
I understand that a level load is considered a complete refresh, but I see my peripheral Input as something outside of Unity that should not be apart of this refresh.
If someone is holding down the D key to move, then the next level loads and they keep holding it down, they should keep moving. Instead Unity is telling the Input system to make it believe the D key is not held down, when in fact it is. Seems like a bug to me.
If it stays this way they might want to reconsider changing descriptions in some of their documentation for instance Input.anyKey says: “Is any key or mouse button currently held down?” when it should say: “Is any key or mouse button currently held down since the scene has loaded?”
Even some kind of high level api call to turn this input refresh feature on/off, would be nice.
Additive level loading is a solution, but is there any way to do async additive level loading for large scenes? Not too mention Finding All Objects in a scene and then destroying them is a very expensive/resource heavy process.
I also don’t get what is the gain of resetting the input between scene loads? There seems to be tons of this kind of threads everywhere on the internet where people are struggling with this issue.
So people have to come up with workarounds like the additive load and manual destruction of the previous scene and stuff. Why? What is the reason to reset the input, what kind of scenario profits from this decision? I can’t think of any. If i call “Input.GetKey()” i want to know if the player is currently physically holding down that button, no matter if the application was restarted/tabbed out/reloaded whatever.
Edit. Also the Additive load “solution” seems to fill my hierarchy up with obsolete scene objects?
Thanks! This works like a charm.
Here’s my (simplified) code if anyone’s looking to see how that works:
using System.Runtime.InteropServices;
public class test : MonoBehavior {
[DllImport("user32.dll")]
public static extern short GetAsyncKeyState(int vkey);
public const int LEFT_ARROW_KEY = 0x25;
public const int RIGHT_ARROW_KEY = 0x27;
if ((GetAsyncKeyState(LEFT_ARROW_KEY) & 0x8000) > 0) {
// move left
} else if ((GetAsyncKeyState(RIGHT_ARROW_KEY) & 0x8000) > 0) {
// move right
}
}