I'm working on a save system and everything is working perfectly except one thing. When game is loading scene with actual level saved position of player won't set. I've tried to load it in additive mode and then unload the previous scene, but console only shows me that player couldn't be found. I checked multiple times for typo.
It worked one time when i putted function for checking if game was loaded. It looked like this
is_loaded = true;
SceneManager.LoadScene("scene_to_load");
And in another script linked with object on level
void Start()
{
if (is_loaded)
{
load();
}
}
void load()
{
//actual loading code
}
It's working but i don't like how this work. There must be a better way to do that.
Related
I have been creating a multiplayer game using Mirror. I have coded an entire Fight function, and I turned it to a command function since i want it to be run on a server (because I think it's easier to calculate who you shot on server then on a client), but I get an error:
Command Function System.Void CharacterMovement::CmdFight() called on NetworkCharacterContainer(Clone) without authority
(The error on the title appears on host and I don't know why).
Here is the code:
public void HandleFighting()
{
if(Input.GetMouseButtonDown(0))
{
Debug.Log(this);
CmdFight(); //! We want to fight!
}
}
[Command]
private void CmdFight()
{
if (!isLocalPlayer)
{
Debug.LogWarning("Command Function CmdFight() called on CharacterMovement without authority.");
return;
}
//! If we clicked on left mouse button
if (WeaponData != null)
{
RaycastHit raycastHit;
if (Physics.Raycast(CharacterPosition, Input.mousePosition, out raycastHit, WeaponData.Range))
{
//!We hit a player
GameObject ho = raycastHit.collider.gameObject;
Health health;
if (ho.TryGetComponent<Health>(out health))
{
//! We hit a player
health.SubstactHealth(WeaponData.damage);
health.AddEffects(WeaponData.effects);
}
}
}
}
I am spawning player normally and on network server (like this):
public override void OnServerAddPlayer(NetworkConnectionToClient conn)
{
//TODO Read the code and fix it
GameObject Player = Instantiate(playerPrefab,transform.position,transform.rotation); //! create a player
NetworkServer.Spawn(Player);
If anybody can help, please write it down below.
I have asked ChatGP and it told me like 7 steps, i did them all and nothing worked.
Some of these steps were:
Make sure that the object that is calling the CmdFight() function has a NetworkIdentity component. You can add it by clicking on the object in the Unity editor, and then in the "Add Component" menu, searching for "Network Identity".
or
Make sure that the NetworkIdentity component is correctly set up. In the inspector, check that the "Local Player Authority" option is enabled. This option allows the object to execute command functions as a local player object.
or even
Make sure that the object was instantiated using the NetworkServer.Spawn() method or a NetworkIdentity component with the Local Player Authority option selected.
I'm trying to access a value of a local variable inside a lambda according to this post, but I get this excepton:
MissingReferenceException: The object of type 'Transform' has been destroyed but you are still trying to access it.
Here is my code:
private static void ValidateComponent<T>(Transform transform, Delegate validationMethod, object[] args) where T : Component
{
#if UNITY_EDITOR // EditorApplication is only available when running in the editor
if (EditorApplication.isPlaying) return;
if (transform == null) return; // in case inspector field is not assigned
var t = transform;
EditorApplication.delayCall += () =>
{
var component = t.GetComponent<T>();
if (component == null)
{
LogAddingComponent<T>(t);
var addedComponent = t.gameObject.AddComponent<T>();
validationMethod.DynamicInvoke(args.Prepend(addedComponent));
}
else
{
validationMethod.DynamicInvoke(args.Prepend(component));
}
};
#endif
}
What am I doing wrong?
You are attempting to pin transform via the variable t only to use it later during your delayCall callback. That's dangerous considering many types in Unity contain unmanaged resources hence the error. The effect is the same if you created a Font in WinForms, Disposed it, then attempted to use the font.
OP:
Is there a better way to approach this then?
From what I understand, Transforms are always attached to the GameObject and it is the GameObject that is being destroyed taking the Transform along with it.
The only way I know to prevent game object destruction is viaDontDestroyOnLoad but I'm not sure if its applicable here since the latter is really designed for protection whilst transitioning to a new scene, but you never know perhaps it prevents things from being destroyed in other scenarios.
Try calling DontDestroyOnLoad at the top of ValidateComponent like so:
private static void ValidateComponent<T>(Transform transform,
Delegate validationMethod,
object[] args)
where T : Component
{
#if UNITY_EDITOR // EditorApplication is only available when running in the editor
if (EditorApplication.isPlaying) return;
if (transform == null) return; // in case inspector field is not assigned
DontDestroyOnLoad (transform.gameObject); // <---- new
var t = transform;
.
.
.
Otherwise, there is this Unity Answer, that suggests simply disabling and re-enabling the object rather than whatever is destroying the object in the first place. Following that approach you shouldn't require any changes to your code above.
Editor-time-only scripts
BTW, regarding the #if UNITY_EDITOR bit, if you want to avoid having to decorate your code with #ifs everywhere (as an ex c/c++ programmer I certainly don't miss having to do that all the time), consider simply moving your script into a child folder called Editor. Unity automatically treats anything placed in such folders as Editor-time-only and won't be incorporated into your final game build. Better yet, no need for the #if stuff.
The best reason for it though is to prevent build errors when Unity is producing your final game due to forgetting to either use #if or Editor folders. I typically run into this one with 3rd party scripts obtained from the Unity store. Instead of having to fix the code I simply move it to a child Editor folder.
See also
Special folder names, Unity
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 have a problem where I'm changing the Image of a SpriteRenderer as a onClick method is called.
However I had a problem of duplicates in the Scene after reloading it (i.e going out of the scene and then back again).
I have tried solving this by destroying the correct duplicate when reentering the scene. However, the old (changed) sprite (the one I would like to keep) keeps getting destroyed while the new one is still there.
This is part of my code for saving and destroying the sprite:
private void saveSprite(Clue c){
markedObjects.Add(c);
Debug.Log ("Save");
DontDestroyOnLoad (c.getSprite());
create = true;
}
private void destroySprite(Clue c){
foreach(Clue g in markedObjects){
print (g);
if (!markedObjects.Contains(c)){
Debug.Log("Destroy");
Destroy (c.getSprite());
}
}
}
I really don't understand how the destroySprite condition !markedObjects.Contains(c) can be passed while still destroying the incorrect sprite.
Any help would be greatly appreciated.
private void destroySprite(Clue c){ <-- I assume Clue c is the sprite you want to destroy
foreach(Clue g in markedObjects){ <-- get each Clue in markedObjects
print (g);
if (!markedObjects.Contains(c)){ <-- if c is not within all of markedObjects then destroy c? Perhaps you meant to destroy g.
Debug.Log("Destroy");
Destroy (c.getSprite());
}
}
}
I am currently working on a save system in my XNA game that fires off an event when it's finished saving.
My game has multiple screens which are managed by a screen manager, each screen has its own Update and Draw methods.
In the constructor, my _newSave boolean is flagged as true, this boolean starts off the game save process (which is managed by helper), and once it starts the process, it is flagged to false to prevent constant looping (since i only want to save once per save screen).
When helper is finished saving, it fires off the event, back inside the SaveScreen class, which sets _newSave back to true, and tells the ScreenManager to change screens.
Playing the game everything seems to work correctly, until the second time saving, when this save is attempted the game freezes. It is worth noting that this bool is private and only used in this method the 4 times shown (found usage by visual studio to confirm)
class SaveScreen : LoadingScreen
{
private bool _newSave;
private Player _player;
public SaveScreen(EventHandler screenEvent, ContentManager Content, GraphicsDevice GraphicsDevice, Player player)
: base(screenEvent, Content, GraphicsDevice)
{
screenTexture = Content.Load<Texture2D>("saving");
_newSave = true;
this._player = player;
helper.FinishedSaving = new EventHandler(FinishedSaving);
}
public override void Update(GameTime gameTime)
{
if (_newSave)
{
helper.RequestSave(_player);
_newSave = false;
}
helper.UpdateSaving();
base.Update(gameTime);
}
private void FinishedSaving(object o, EventArgs e)
{
_newSave = true;
screenEvent.Invoke(this, new EventArgs());
}
}
With a breakpoint inside the FinishedSaving event, i confirmed that the bool is changing to true, which is correct, however, a following breakpoint on the next save cycle on the if statement, shows that bool is false.
I am so completely confused by this, as the only _newSave = false statement is inside the if statement that needs to be true to access, the breakpoint inside the event shows that it is true, yet on the first "new" loop of the update code, it is false.
I am really pulling my hair out with this and cannot understand what's happening.
Any help is hugely appreciated, and i'm sorry if it's something silly, i'm a relatively new programmer :)
Thank You! <3
EDIT: Pastebin of entire Helper.cs class: http://pastebin.com/uJ0g6e00
Loading Screen Class: pastebin.com/8W8HxBnq
Screen Class: pastebin.com/qr29gzuq
Are you using multiple threads? If so, I wonder whether it's a race condition.
For example:
Update is called
Let's suppose that _newSave is true
RequestSave is called
Before RequestSave returns, Update is called again, but _newSave is still true...
I figured out the problem, apologies to everyone for wasting their time, and thank you for trying to help, turns out that simply switching the _newSave; = false and helper.RequestSave(_player); statements around fixed the issue, I thought that the problem was isolated into this class alone due it being the only place where the changes happened, in fact the issue was that i invoked the delegate inside the RequestSave method when the player score was not greater than the high score, this caused the code to then return to the if block and since the "_newSave = false;" statement was directly after the method call, it was reverting the statement back to false!
So silly of me but sometimes it's really hard to see the simple problems :P Thank you again!
<3