I have a script that I have connected to a button in Unity that switches the scene, but I also have an animation I want to play after the button is pressed. The button starts that animation, but you can't see the animation because the scene switches too quickly.
I've tried using coroutine and timers, but I don't have the technical know how to fully implement them.
I would prefer using a coroutine if that's possible.
My code is:
public void LoadNextScene()
{
int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
SceneManager.LoadScene(currentSceneIndex + 1);
}
public void LoadMenuScene()
{
SceneManager.LoadScene(0);
}
}
I want the button to be pressed, then the animation played, and finally the scene switches. Could you please recommend the required code changes to achieve my desire?
There are several different ways to do what you are describing. The three most popular are Co-routines, invoke, and animation events.
The easiest is invoke, here is an example of your code implemented in an invoke. Just call 'CallLoadNextScene'.
public void CallLoadNextScene()
{
Invoke("LoadNextScene",yourDelay);
}
public void LoadNextScene()
{
int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;
SceneManager.LoadScene(currentSceneIndex + 1);
}
public void CallLoadMenu(int id)
{
Invoke("LoadMenuScene",yourDelay);
}
public void LoadMenuScene()
{
SceneManager.LoadScene(0);
}
Animation events would probably be the most "professional" way to do it because then your have better delegation of responsibilities. Here is a link to Unity's website on those.
A solution using Co-Routines is going to look a lot like the invoke, but since you don't need a lot of advanced functionality Invoke is a little bit faster and easier to use.
Best of luck on your project! :)
Related
I know I can inherit from MonoBehaviour and add some handler functions to SceneManager.sceneLoaded, as described in official document https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager-sceneLoaded.html.
My question is, do I need to create an empty GameObject in the scene, and attach a script to that GameObject to do the stuff? Or there is some other better practice?
This is good practice. Create a SceneManager empty GameObject. Attach a script inheriting from MonoBehaviour and handle all scene related actions in there.
Very lucid, straight to the point.
It is one way to go, yes.
You do not necessarily require a GameObject for this.
You can also e.g. use [RuntimeInitializeOnLoadMethod] and attach the listener within there and then further process it.
public static class SomeClass
{
[RuntimeInitializeOnLoadMethod]
private static void Init()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
private static void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
Debug.Log("OnSceneLoaded: " + scene.name);
Debug.Log(mode);
}
}
What is "better" depends a lot on your use case and requirements of course
I managed to get some kind of multiplayer working with Unity Netcode which I understand is very new, but I'm running into a pretty big issue. I can't seem to be able to disconnect from the server instance. This causes an issue in which I load the game scene and the NetworkManager instance is spawned. Then I return to the main menu scene and load the game scene again, and now there are 2 NetworkManager instances which obviously causes errors.
I read in the docs that you can use this:
public void Disconnect()
{
if (IsHost)
{
NetworkManager.Singleton.StopHost();
}
else if (IsClient)
{
NetworkManager.Singleton.StopClient();
}
else if (IsServer)
{
NetworkManager.Singleton.StopServer();
}
UnityEngine.SceneManagement.SceneManager.LoadScene("MainMenu");
}
However all these stop functions don't seem to exist at all under the singleton. Am I missing a library or something?
For now I am using a workaround which is just disabling "Don't destroy" and then the NetworkManager is destroyed after switching scenes, but what if I wanted to keep it after switching a scene? Is there no way to control when the Server/Client is stopped? I'm assuming this is a bug but I just want to make sure.
void Disconnect()
{
NetworkManager.Singleton.Shutdown();
}
Disconnects clients if connected and stops server if running.
To answer your full question, you also need a Cleanup() function in your non-networked scene that checks for astray NetworkManager singletons and destroys them. This will avoid unnecessary duplication of network managers.
void Cleanup()
{
if (NetworkManager.Singleton != null)
{
Destroy(NetworkManager.Singleton.gameObject);
}
}
I'm implementing a system in which a player can pickup an item from the game world. After picking the item up, I want to destroy the game object. To do this, I figured I'd make a ServerRPC that deletes the object, but the ServerRPC is not getting called. I tried making a ClientRPC as well, but that one isn't being called either. I noticed that it only gets called when the host of the game tries to pick up the item -- any ideas on how to fix this? The documentation is pretty bare but please let me know if I missed anything. Here is my code:
public override void Interact(GameObject player)
{
player.GetComponent<PlayerInventory>().AddItem(this.item);
Debug.Log("before");
TestClientRpc();
TestServerRpc();
}
[ServerRpc]
public void TestServerRpc()
{
Debug.Log("server");
// Destroy(gameObject);
}
[ClientRpc]
public void TestClientRpc()
{
Debug.Log("client");
// Destroy(gameObject);
}
EDIT: image of the components on the weapon
EDIT: This class extends a class that extends the networkbehaviour class. The objects are just sitting in the scene at start -- they are not spawned/instantiated (not sure if that matters or not).
The most plausible solution to this problem , either it is running into a error in some previous lines or as some other person pointed out , requireOwnership is not set to false .
For in depth analysis of MLAPI , https://youtube.com/channel/UCBStHvqSDEF751f0CWd3-Pg
I found the tutorial series on this channel best
I needed to add the following attribute to my ServerRpc:
[ServerRpc(RequireOwnership = false)]
since the player was not the owner of the item.
I have the same issue. Been looking at it for 2 days but I am inclined to think it is a bug to be honest.
I just spent 4+ hours banging my head against the wall because they didn't write this important info in the documentation. When calling ServerRpc from a client that doesn't own the Network Object, RPC attribute should be set to RequireOwnership = false.
For me the issue was that the GameObject(with NetwrokObject component) was sitting in the scene before starting the game, I couldn't use NetworkVariables nor ServerRpc. I think those are only usable on spawned object, but not sure. So I would say : either instantiate the object and add it to the NetworkManager or use normal variable on those objects.
Had the same issue but later realized I just forgot to inherit NetworkBehaviour
For Example:
using Unity.Netcode;
using UnityEngine;
class myObject : NetworkBehaviour {
public override void Interact(GameObject player)
{
player.GetComponent<PlayerInventory>().AddItem(this.item);
Debug.Log("before");
TestClientRpc();
TestServerRpc();
}
[ServerRpc]
public void TestServerRpc()
{
Debug.Log("server");
//Destroy(gameObject);
}
[ClientRpc]
public void TestClientRpc()
{
Debug.Log("client");
// Destroy(gameObject);
}
}
I had the same problem, where the ServerRpc wouldn't even be called. For me it worked after I made the GameObject that my script was attached to a prefab and added it to the NetworkManager's list of NetworkPrefabs.
Note: (I am using Netcode for GameObjects, I don't think it will be the same for other Multiplayer solutions)
This is a pretty simple question, but it seems something has changed on Unity in the last versions answers I've found on the internet are no longer valid, so here goes nothing:
I got some UI elements, and a "InputController" class which is intended to handle the user input during the game (Input on the controllers are handled through the onclick events).
What I'm looking is for a way to being able to know if the mouse is clicking a UI element to block the execution of my input handling (and avoid the user clicking on "pause" while also the game executes "left button clicked."
Now, most solutions I've fond were a bit messy or used EventSystem.current.IsPointerOverGameObject() (like this one, which was shown when writing this question), which in 2019.4 does not longer appear. So, there's any new way to do this, do I have to make some hacky solution to receive the event from the UI, then block the execution of my code or am I missing something here?
You should look into interfaces like IPointerEnterHandler and IPointerExitHandler. If you implement these interfaces for your UI elements, you can add the necessary code to the OnPointerEnter and OnPointerExit methods that those interfaces require.
It should be as simple as adding a bool to your InputController such as isInputEnabled and only handling input when that is true. Set it to false OnPointerEnter and true OnPointerExit.
I spent a good amount of time trying to figure this out as well. I am on Unity 2022.1 using the Input System and UI Toolkit (UI Elements)
The following should help anyone else who is struggling with this to get the behavior they need.
Examine your UI Documents
You don't want your UI Document to always report clicks. So you need to set the picking mode in your UXML documents accordingly. Have a look at the images below,
In the image on the left, I have a wrapping element that allows me to position the panel at the bottom of the document. By default this element will receive all pointer events. What I actually want is to only receive pointer events inside of the orange area seen in the image on the right.
I can fix this by setting the picking mode of all parent elements to ignore:
Set up the input system
Setting up the new input system is a topic in itself. Refer to the documentation for more information, but in the image you can see I am using a simple button event as a click/tap action:
Set up your script
Next you need to respond to the input action and check if your input is seen by the UI
public class SimpleInput : MonoBehaviour
{
public Camera ViewCamera;
private PlayerControls _controls;
private void OnEnable()
{
Assert.IsNotNull(ViewCamera, "ViewCamera cannot be null");
// This class will vary depending on the name of your Input Action Asset
_controls = new PlayerControls();
_controls.Enable();
_controls.Gameplay.TapAction.performed += OnInputTapAction;
}
private void OnInputTapAction(InputAction.CallbackContext obj)
{
Vector2 position = Pointer.current.position.ReadValue();
Ray ray = ViewCamera.ScreenPointToRay(position);
if (PointerIsUIHit(position))
{
Debug.Log("Ui event received");
}
else
{
// Perform game-world events here
}
}
private bool PointerIsUIHit(Vector2 position)
{
PointerEventData pointer = new PointerEventData(EventSystem.current);
pointer.position = position;
List<RaycastResult> raycastResults = new List<RaycastResult>();
// UI Elements must have `picking mode` set to `position` to be hit
EventSystem.current.RaycastAll(pointer, raycastResults);
if (raycastResults.Count > 0)
{
foreach (RaycastResult result in raycastResults)
{
if (result.distance == 0 && result.isValid)
{
return true;
}
}
}
return false;
}
}
The helper method PointerIsUIHit was inspired by a conversation found in the Unity Forums which had some insight into how to get this done. It also shed some insight into the frustrating experience of being a Unity Dev.
Hopefully this helps other people struggling to find a proper guide.
I'm trying to write a drawing library. In this drawing library there is an update function that should update every frame. I do this by using a do while loop See code below:
private void UpdateCanvas()
{
do
{
Canvas.PumpEvents();
if(UserUpdateVoid != null) UserUpdateVoid();
} while (Canvas.Exists);
}
I also have a function in which the user can set their own update function. This function is part of the SharpDraw class, see code below:
public void SetCustomUpdateFunction(Action function)
{
Console.WriteLine("updated the user function");
UserUpdateVoid = function;
Console.WriteLine(UserUpdateVoid);
}
all this is called in the following way:
public class SharpCanvas
{
private Sdl2Window Canvas;
private GraphicsDevice GraphicsManager;
private Action UserUpdateVoid = null;
public SharpCanvas()
{
WindowCreateInfo WindowInfo = new WindowCreateInfo(
200,
200,
100,
100,
WindowState.Normal,
"SharpWindow"
);
CreateCanvas(WindowInfo);
UpdateCanvas();
}
}
And the SharpDraw instance is made in the following way:
namespace test
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
SharpCanvas Canvas = new SharpCanvas(200,200);
Canvas.SetCustomUpdateFunction(Update);
}
private static void Update(){
Console.WriteLine("update");
}
}
}
But the problem is that the Console.Writelines in the SetCustomUpdateFunction() are never executed. I guess this has to do with the fact that the while loop keeps the program from further execution. So my question is how do i keep the while loop running while still being able to execute different pieces of code? In unity they are able to do it :P
If there is something unclear let me know so i can clarify!
That is entirely normal. It does not mater if you are running a console application, a Windows Form or WPF/UWP application*: Only one piece of code can be executing. While one piece of code does not return, not other code can run.
You need to add some form of Multitasking into the mix. Now that looks extremely like a Console Application and those are the last place I would advise learning Multithreading in. My personal advise is to start under Windows Forms using the BackgroundWorker. It is dated and rarely used in practice, but it can help you get up to speed with the rules and conventions. But this is one area where you can ask 10 people and get 11 Opinions.
*Web Applciations are semi special. As they are pleasingly parallel and it helps with isolation usually each request is given their own Thread. But at least for each singular request, it still holds true.
When you call UpdateCanvas, you enter a loop and code never goes further. To prevent this, you should use threads, async-await or something similar else (see this answer for async-await).
You have to use Multithreading programming. Look for it on google, there are plenty of examples.