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
Related
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)
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! :)
In my GameManager script I have a delegate. In my Door script, I subscribe to this delegate. I've tried unsubscribing from it in both the OnDisable and OnDestroy methods in the Door script. In both cases, I get an error when I stop running the game in the editor:
Some objects were not cleaned up when closing the scene. (Did you spawn new GameObjects from OnDestroy?)
Using Debug.Log, I found out that this is because the GameManager will always be destroyed before the Door script. Even if I do a null check inside either the OnDisable or OnDestroy of the Door script to see if the GameManager is null, I get the same error
if (GameManager.Instance)
{
GameManager.Instance.OnAllEnemiesKilled -= OpenDoor;
}
Somebody told me that I don't need to unsubscribe from it, as the delegate will automatically become null when the Door object is destroyed, but that's not true. During runtime, after the Door is destroyed, my update loop inside of the GameManager is still printing that the delegate has one subscriber: the Door.
I suspect you're spawning a new singleton when you call GameManager.Instance after it's been destroyed? If so, do something like this instead:
public class GameManager : MonoBehaviour
{
public static GameManager Instance
{
get
{
if (isDestroyed) return null;
// Your spawn logic...
}
}
static bool isDestroyed;
void OnDestroy()
{
isDestroyed = true;
}
}
As Lece pointed out, I'm spawning a new singleton when I call GameManager.Instance. Rather than creating a static bool to remedy the problem however, I replaced if (_instance == null) with if ((object)_instance == null) inside my Instance getter and it solved the problem. This is because, while the object is destroyed in the native code, it still exists in the managed code. So I'm now referencing it in the managed world. Moreover, Unity makes it so that when an object is destroyed in native code, its value in managed code will still return null.
I know this has been covered in the past, but I believe this issue is being caused by the update to Unity 5, thus deprecating the older information. No errors come up, but the debug won't come up. The public variable of 'pushBlockEnter' does become true visibly on the player script, but the pushBlock class will not recognize it no matter what. I just want to be able to check if a variable is true in one script from another script in Unity 5, any way of doing it would be fine. I'm sure it is something simple but I just can't figure it out...I should also mention that both scripts are on a different object. Thanks in advance!:
public class pushBlock : MonoBehaviour {
public Player playerScript;
void Update () {
if (GameObject.Find("Player").GetComponent<Player>().pushBlockEnter)
{
Debug.Log ("do something blahh");
//Do Anything
}
}
}
Figured it out:
GameObject playerReference = GameObject.Find("Player");
void Update(){
if (GameObject.Find("pushBlockCollider").GetComponent<pushBlockCollider>().inPushBlock){
Debug.Log ("got to pushblockCollider True");
}
Is there a way I can prevent the designer from attaching my script to anything except a terrain type?
Normally when I create a script/Component/MonoBehavior, Unity allows a designer to add it to any game object. How do I limit that?
Two methods come to mind, but none are very elegant.
1) Implement OnValidate(). The downside is that's it's called only when modifying component's values, or entering/exiting game mode.
void OnValidate() {
if (GetComponent<Terrain>() == null) {
Debug.LogError("You can't attach this component without terrain!");
DestroyImmediate(this);
}
}
2) Make the script run in the editor, and implement OnAwake(). But be aware that methods like Update() will be called in the editor.
[ExecuteInEditMode]
class MyScript: MonoBehaviour {
void OnAwake() {
if (GetComponent<Terrain>() == null) {
Debug.LogError("You can't attach this component without terrain!");
DestroyImmediate(this);
}
}
}