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
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)
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 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.
This is the most unusual error ever!
I am using XNA(Monogame) and using the following code to get the mouse state:
Mousestate ms = Mouse.GetState();
Then I could check for clicks with the following:
if(ms.RightButton == ButtonState.Pressed)
{
}
Or check for scroll by setting a previous scroll variable and compare it to the current one.
All was working well, until I was working on my system today, and I tested it and all mouse interaction stopped working. But keyboard state did work.
I thought it could be because it was not getting called or was not being checked.
So I placed this is my working update method.
if (ms.LeftButton == ButtonState.Pressed)
{
throw new NullRefrenceException();
}
I tried Left clicking and nothing happened. Made sure it was not something else by removing the if-statement and sure enough it did throw it.
So after being desperate I created a Windows Mono-game Proj and put the same code into the update method.
I left-clicked and the error was thrown sure enough.
I have tried commenting out every line of code which has the word ms/mouseState/Mouse. And only leaving one, but to no avail.
I would create a Minimal, Complete, and Verifiable Example but my project is very large, and I have declared Mouse-state in over 30 classes.
I have tried restarting computer, restarting visual-studio, ending all vs/vs-host processes, using a different mouse and lots of code tweaking.
No errors are thrown, when I try and use break-point near Mouse-state it is not set to null.
If any further information is needed for this question, please say so.
Well. If you initialize Game1.
Game1 g1 = new Game1();
It will break mouseState.
If a class extends : Microsoft.Xna.Framework.Game
And uses its methods. If you initialize it again it will break.
Game1 should not be initialized.
I have a Windows Form with a status bar which shows the current state of application.
I have a class named AppState with update the Label in the status bar and in dispose it changes the state back to "Ready".
In code when I do an operation like:
using (AppState state = new AppState("Processing..."))
{
//Do some work that take some seconds
}
But the label remains the same. I am not getting any exceptions. The label text is updated but on UI it keeps on showing previous value. Am I missing anything here?
santosc you are right, thats the only thing I am doing. Here is the AppState code
public class AppState : IDisposable
{
static string Default = "Ready";
public AppState(string status)
{
Form.StatusLabel.Text = status;
}
public void Dispose()
{
Form.StatusLabel.Text = Default;
}
}
It's always the same thing...
If you want to start something that takes a while, don't do it within your GUI thread or your GUI will freeze (no updates of label, no resizing, no moving, no whatever).
Filling your code on thousand places with Application.DoEvents() is also a bad practice.
If you have some long running task (long means > 1 sec) you should probably use a BackgroundWorker. Maybe it's a little bit harder at the beginning, but you will love it if your program gets more complex. Due to the fact, that this has already being discussed several time, here is a link with some sample code.
Now that you know the right tool (BackgroundWorker) to solve your problem, you should get it to work (or ask another question about your new specific problem).
Looks like you want to put Application.DoEvents() after setting the StatusLabel text field value. This tells Windows Forms to process the Windows event queue for your form, causing changes to be repainted.
in order to be "thread safe" use Invoke, and test with the InvokeRequired in the form like:
// code outside the myForm:-----------------------
if (myForm.InvokeRequired)
myForm.Invoke(new ChangeLabelEventHandler(ChangeLabel), "teeeest");
else
myForm.ChangeLabel("teeeest");
// code in the myForm:-----------------------------
public delegate void ChangeLabelEventHandler(string newText);
private void ChangeLabel(string newLabelText)
{
this.label1.Text = newLabelText;
}
I'm new to C# stuff, but why can't you just do something like:
private void updateStatusBar(string status)
{
if (StatusLabel.InvokeRequired)
{
StatusLabel.Invoke((MethodInvoker)(() =>
{
StatusLabel.Text = status;
}));
}
else
{
StatusLabel.Text = status;
}
}
When you want to update the status?
Maybe multiple threads could solve your problem.
The easiest way is using a BackgroundWorker.
The reason is that the UI is only able to redraw when the UI thread has nothing else to do. And you are blocking it with your calculation.
use Label.Refresh(); it saves a lot of time.This should work for u