I'm using the example on this thread as a reference: viewtopic.php?f=3&t=55338
Basically what I'm trying to do is make a progression based RandomSpawner for spawning enemies.
The problem I'm having is the global variable isn't saved when you save and load the game, but everything else works.
I'm not too sure what's going on here, plus I'm not super well versed in ZScript but I understand most of the basic stuff
Attached is an example pk3 with a map to easily test the effects of the spawners.
[ZScript] Thinker based Global Variable not saving
Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.
Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.
Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)
- DustedPandemonic
- Posts: 82
- Joined: Tue Nov 28, 2017 12:17 pm
[ZScript] Thinker based Global Variable not saving
- Attachments
-
randomspawner_test.pk3- (32.28 KiB) Downloaded 16 times
- Player701
-

- Posts: 1710
- Joined: Wed May 13, 2009 3:15 am
- Graphics Processor: nVidia with Vulkan support
- Contact:
Re: [ZScript] Thinker based Global Variable not saving
Now this is very interesting.
The problem is that RandomSpawner chooses an actor to spawn during its BeginPlay call.
What happens is that even before the save file is loaded, the map gets initialized first. Therefore, all RandomSpawners get initialized as well and call BeginPlay. Note that at this point, your thinker hasn't been deserialized yet! So a new instance of it gets created, and then the save file is deserialized, and your previous instance is added on top of that. But since your ThinkerIterator makes only one pass, it returns the wrong instance (the one that got created when the map was loaded).
To fix this, you could modify the code of the Get method so that the iterator finds all instances of your thinker and destroys them all except the last one:
I'm not sure if this is the "true way" to solve this problem because I haven't worked with thinkers a lot. I might have overlooked something, but at least in my tests this approach seems to work.
Another way is to use event handlers instead of thinkers. These are created by the engine and always exist as singletons, i.e. you can be certain there is no more than one instance of each event handler. If you want your variable to be level-scoped, simply use an EventHandler instead of a Thinker. For variables that persist across maps, you will need both an EventHandler and a StaticEventHandler. This is because EventHandler works within a single level but is serialized in save files, while StaticEventHandler can be used to transfer data between different levels but is never serialized. A combination of both should be used to achieve the desired effect.
The problem is that RandomSpawner chooses an actor to spawn during its BeginPlay call.
What happens is that even before the save file is loaded, the map gets initialized first. Therefore, all RandomSpawners get initialized as well and call BeginPlay. Note that at this point, your thinker hasn't been deserialized yet! So a new instance of it gets created, and then the save file is deserialized, and your previous instance is added on top of that. But since your ThinkerIterator makes only one pass, it returns the wrong instance (the one that got created when the map was loaded).
To fix this, you could modify the code of the Get method so that the iterator finds all instances of your thinker and destroys them all except the last one:
Code: Select all
static MyGlobalVariables Get()
{
let it = ThinkerIterator.Create('MyGlobalVariables', STAT_STATIC);
MyGlobalVariables cur, prev = null;
while ((cur = MyGlobalVariables(it.Next())) != null)
{
if (prev != null)
{
prev.Destroy();
}
prev = cur;
}
if (prev == null)
{
prev = new('MyGlobalVariables').Init();
}
return prev;
}Another way is to use event handlers instead of thinkers. These are created by the engine and always exist as singletons, i.e. you can be certain there is no more than one instance of each event handler. If you want your variable to be level-scoped, simply use an EventHandler instead of a Thinker. For variables that persist across maps, you will need both an EventHandler and a StaticEventHandler. This is because EventHandler works within a single level but is serialized in save files, while StaticEventHandler can be used to transfer data between different levels but is never serialized. A combination of both should be used to achieve the desired effect.
- DustedPandemonic
- Posts: 82
- Joined: Tue Nov 28, 2017 12:17 pm
Re: [ZScript] Thinker based Global Variable not saving
Interesting, I'll try the fix you posted but I'll also keep the event handler method in consideration. Thanks!