Yogoda / ZoneLoadingSystem

Dynamic zone loading system for Godot
Creative Commons Zero v1.0 Universal
226 stars 13 forks source link

Feedback & Requests #3

Open Yogoda opened 4 years ago

Yogoda commented 4 years ago

You can use this issue to discuss the plugin.

Gulbasaur commented 4 years ago

Firstly - thanks, this is exactly what I was looking for.

Secondly - would this work with a LOD? So you could have a low-poly version of a zone operating as scenery that's then swapped out on triggering the zone loading.

Thirdly - have you had any issues with saving and loading games?

Yogoda commented 4 years ago

Thank you, I'm happy that it is useful :)

For LOD version of zones, I think it should be possible without too many modifications if there is only one LOD level.

I've not had any issue with saving/loading games, but I only save very few data. For example, if I kill monsters in a zone and come back later, they will be back, but this is normal for my game. If you need to keep the zone state, you need to save each zone data when they are unloaded, I don't know if that is your issue.

rmvermeulen commented 4 years ago

Hi, just started playing with the template project. The character runs fast enough to launch itself out of all the zones and you can see them being unloaded and brought them back when you come down. It does exactly what I wanted to implement so far.

As to the workflow, I guess it comes down to

I'm thinking that maybe it would be nice for the zone to show the map-node it will load later while in the editor. In case you have to line things up again. But either way, very useful, thanks

Yogoda commented 4 years ago

You mean have all the zones attached in the editor to see the whole world? Unfortunately doing so Godot would load all the zones at the start of the game, defeating the system purpose (only load what is needed).

rmvermeulen commented 4 years ago

You can use the Engine.editor_hint in tool scripts to run some code only in-game, or in-editor.

tool extends Node
func _ready():
  if Engine.editor_hint:
    # in-editor behavior
    show_always() # or use VisibilityNotifier ?
  else:
    # regular behavior
    use_triggers()

I'll cook up a prototype, if it works I'll share it in a PR

EDIT you obviously know all that. I think the misunderstanding is the loading part; Godot only saves parts of the scene that are owned by the current_scene. If you add the map-part to the editor from scripts, but leave the owner blank, it will not be saved. Then when loading the level in-game only the triggers are there, as intended.

Yogoda commented 4 years ago

Wow, that is some seriously cool stuff, didn't know it was possible. It's a great addon to the system, I will merge it when I have time, thank you :)

Wrzlprnft commented 4 years ago

How about turning off the "monitorable" property for the ZoneX Area2D's? As far as i can see, they only need to detect the player entering and exiting, but do not need to be detected, as in any overlap between each other? (haven't looked much into the 3D variant, but from what i see i assume this works much the same in that case). I am barely starting to use this great system, but that seems like an easy performance gain for to me. I am just not sure wether this is needed when having a lot of zones, and wether i am missing something.

edit: Additional thought, would a visibilityEnabler2D also work to just disable trigger areas off screen, or that be unnecessary (too)?

Yogoda commented 4 years ago

Hello!

I use "zone.get_overlapping_areas()" to detect which zones are connected to each other, so "monitorable" is needed, and without "monitoring" the zone won't be able to detect the player entering it. Anyway, I don't think it is a problem performance-wise since there is only the player and the triggers on this physics layer.

I think it is possible to improve the system for 2D with VisibilityNotifier2D, I need to work on this.

Yogoda commented 4 years ago

Ok, I updated the 2D detection system and demo, there is actually no need to use VisibilityNotifiers or Enablers.

The 2D player now has an Area2D that covers the whole screen making zones attach as soon as they become visible.

Wrzlprnft commented 4 years ago

That was quick! Thanks! About my performance concerns: I am not sure wether the change adresses any performance concerns i had, but from the further reading i did, having them on a seperate layer to all other collisions is apparently enough for most cases. The thought behind the VisibiltyEnabler-Idea was to disable collision checking completely if an area is too far off screen/away from the player position. looks like this isn't necessary, and without a large enough project at hand that actually struggles just optimization for optimizations sake.

about the 2D specific change: One thing the change makes sure though, is that the view on the screen, not the player character causes the zones to load, which is a better default for the 2D example!

Yogoda commented 4 years ago

Yes, exactly my thoughts, also the physical engine is optimized for collision detection, so I don't think it is a big deal, unless we have hundreds of areas. In that case it is possible to have a culling process that disables the colliders according to the distance from the camera.

Wrzlprnft commented 4 years ago

A 2D game, where the perspective is not straight top down, but rather a 45 degree-ish angle (or an isometric view, now that i think about it), needs some Y-Sorting so things properly occlude each other. I guess there is a clever way of using nested YSort-Nodes, or brute force reparenting the player to the zone they are currently in, so the default YSort behavior works. Could this be something added to the demo project?

Edit: I think i figured it out, i overlooked Z-Index. Basically, in the demo2d Scene, change the types of World and ZoneLoader to YSort-Nodes, and change the root node of the Zone to YSort nodes, then change either the background stuff a Z-Index lower, or the Player and Obstacles/Enemys a Z-Indexhigher. image Now it is only a question wether that should be added to the demo.

Yogoda commented 4 years ago

Hello, I mainly use Godot for 3D stuff, so I don't know the issues with 2D sorting very well, if you think your solution can help most of the 2D games that will use this system, can you submit a pull request?

Much appreciated!

Wrzlprnft commented 4 years ago

If the 2D game is 100% top down view, then this will be not needed. If it isn't, and the viewing angle of the assets is more like 45 degree (e.g. Undertale), something like this will be necessary so the perspective makes sense(otherwise you will either always be in front or behind everything in every zone). I'll test around a little more to make sure I am not missing anything, and then I'll create a pull request. Glad I can contribute.

Yogoda commented 4 years ago

Ok, I converted the 3D demo with the new node structure, unified the scripts, and removed the basic 2D demo, the tilted demo is now the standard 2D demo.

@Wrzlprnft, can you check if everything looks fine?

Wrzlprnft commented 4 years ago

Oh, i didn't think of just calling connect from the zone on the trigger... whoopsie

Yeah, looks good to me 👍

Yogoda commented 4 years ago

I get the following error when quitting after a 2D zone have been unloaded:

ERROR: remove_shape: Index p_index = 0 is out of bounds (shapes.size() = 0). At: servers/physics_2d/collision_object_2d_sw.cpp:132

Any idea ?

Wrzlprnft commented 4 years ago

I traced it down to a zone having a tilemap with YSort enabled after being loaded and the unloaded at the point of quitting causing that error. i haven't found out why at this point, though.

I've yet to reproduce it on my personal project under the same circumstances, i do not get the error there.

Yogoda commented 4 years ago

Good news, I was able to fix the issue, so I submitted a new version to the Asset Library (waiting for approval) :)

The issue was when freeing a loaded zone that was not in the tree, with NPC. I changed free() to queue_free() and it seems to have fixed the issue.

Thanks for your patience!

Gamemap commented 3 years ago

If someone wants to have a larger viewing distance he can increase the area ZoneTrigger with .scale.x (also .scale.y and z) in the code.

I hope it helps you. Gamemap

Wrzlprnft commented 3 years ago

Would be a system to hand off NPC's traversing between zones within the scope of this project? I assume one would put objects that block pathfinding on the connecting points between the zones, and handle the logic of finding things seperatly from the actual pathfinding. Once an NPC decides to leave a loaded zone, it walks up to that connection area and is unloaded, while more custom logic handles the desired behaviour of it while unloaded/when to come back in.

Yogoda commented 3 years ago

I think it is a bit too specific to be included in the system. We would add a section to the readme on how to handle this problem if you have a good solution.

But I don't know much about pathfinding, I don't use it in my game. I know you have a navigation mesh and a navigation agent, but can you split the mesh across multiple zones and have it work?

Wrzlprnft commented 3 years ago

Ill dabble with it and see if i can come up with something. Might take a while, though, as i am just starting with the entire topic of pathfinding.

Wrzlprnft commented 3 years ago

It's been a while. Here are my conclusions on the npc stuff so far:

Would be a system to hand off npcs traversing between zones within the scope of this project?

Basically, it would need a "level of detail" like system for moving npcs:

Godot does offer addind/subtracting polygons from nav-meshes. Therefore zones could on loading and unloading add/subtract their navmesh polygon to/from a global "currently loaded" navmesh. Npcs could be reparented to the zone they are currently entering to prevent them going out with their starting zone. On loading and unlading zones could handle populating/depopulating the world areas they control, and hand over control of the remaining npc behaviours to their parent nodes/the world.

The real issue i encounter which always blows stuff out of proportion is the information npcs need to function. What if their goal got unloaded? What if they need to exit the loaded zones into a currently not loaded one?

There is a bottomless pit of system design here, because it is either super game specific or overkill for most games. It basically ranges from "well, we just want to have city guards be able to run across the whole city which consists of 2 zones" so therefore it would just need to be one navmesh for both zones to "well, we basically need a full economic simulation across all zones to have the traveling merchants be very believable in what they carry and where they go" which requires a whole separate set of logic.

I think it is a bit too specific to be included in the system. We would add a section to the readme on how to handle this problem if you have a good solution.

It appears that not the pathfinding is the problem, but rather information doesn't have the same visibility as the player, and therefore it isn't trivial to add this to a system that is based on player visibility. I do agree: adding a section with npc considerations to the readme is the preferred solution.

... unless someone comes around the corner with a finished system. (initial ideas on basically making a second layer of zones only consisting of information for npcs and have that load/unload based on npc behaviour instead of player behavious seemed promising, but i'd need way more time to make it actually work and useable)

blisterPrinkles commented 3 years ago

Can we make it while working on editor viewport?

Yogoda commented 3 years ago

@blisterPrinkles what do you mean?

blisterPrinkles commented 3 years ago

@blisterPrinkles what do you mean?

I was thinking in saving power while editing a 3d open-world... however I did not realized that there is other methods to join by script chunks and I bet that your system works with that too!

Yogoda commented 3 years ago

There is a "preview" flag that allows to show/hide zones in the editor.

HeroRobb commented 2 years ago

I kept having my game crash and I think I've found what's causing it. In background_loader.gd, line 237 or so there's a line that is

if resource_instance != null:

and there were some times when I freed the world to change scenes to a menu or whatever and then when I changed back to the world scene, it would crash. I changed the above line to

if is_instance_valid(resource_instance):

and it no longer crashes. Thanks.

Yogoda commented 2 years ago

Happy you were able to fix your issue :)

Normally, if you need to go to a main menu, you should stop the loading system by calling:

BackgroundLoader.request_stop()
yield(BackgroundLoader, "loading_process_stopped")

This will properly unload all used resources (might take a few seconds).

Hope it helps!