PROCEDURAL Terrain Generation (with Unloading) in Godot!

แชร์
ฝัง
  • เผยแพร่เมื่อ 28 ธ.ค. 2024

ความคิดเห็น • 78

  • @JamesThomas-nr2og
    @JamesThomas-nr2og 11 หลายเดือนก่อน +13

    Hey man, thanks for this tutorial! It was super helpful! One issue I ran into is some major performance loss when moving far from the spawn/ origin, even with chunk unloading implemented. What I think was happening is that the cells in the unloaded chunks were still instantiated with an invisible/ uninteractable state. I fixed this by changing the .set_cell() method to .erase_cell(0, Vector2i(x,y)). This seems to fully remove the cells and allow chunks to properly unload.
    I will look into staggering the chunks being loaded and loaded also, as this seems to happen all within 1 frame causing slight stuttering.

    • @JamesThomas-nr2og
      @JamesThomas-nr2og 11 หลายเดือนก่อน +2

      Added a var time_since_chunk_loaded and var chunks_to_be_loaded = [ ] at the top of the script, then instead of drawing chunks immediately, they get appended to the chunks_to_be_loaded array. In _process, I added time_since_chunk_loaded += delta, and a conditional: "if chunks_to_be_loaded and time_since_chunk_loaded > 0.2: load_chunk(chunks_to_be_loaded[0]), chunks_to_be_loaded.erase(chunks_to_be_loaded[0]), time_since_chunk_loaded = 0"

    • @kobedev
      @kobedev  11 หลายเดือนก่อน +1

      Yeah at the time of recording I didn’t think of the fact that all the chunks are getting drawn and erased at once, having a queue would definitely help and I’m glad you found a solution, still not too sure if I’ll make another update video or not

    • @tannerroberts4140
      @tannerroberts4140 10 หลายเดือนก่อน

      @@kobedev Your idea is gold! I think a very successful follow-on to this would be adding the ability for the player to update the map (place/break a block like minecraft) and the saving of that tile state to memory.

    • @user-og6hl6lv7p
      @user-og6hl6lv7p 6 หลายเดือนก่อน

      It's probably also due to the fact that you're calling a square root function to calculate distance for every unloaded cell for *every* frame that passes. That is *massive* performance hit especially if you have hundreds or thousands of cells on screen. You could use godot's built in distance squared function or even create one using dot products e.g. Dot(v1, v1) * 4.0. This brings about a degree of inaccuracy, but you can always scale the distance value up/down so it reaches a better approximation.

  • @jaxterity
    @jaxterity ปีที่แล้ว +4

    Glad more and more people getting into GODOT and doing GODOT videos!
    I'm developing my game in GODOT.
    I would love to more and more on procedural generation and doing it in GODOT.
    Thanks for the video!

    • @kobedev
      @kobedev  ปีที่แล้ว +2

      Thanks for the kind comment mate, I’m really happy you liked the video and you will definitely see more tutorials, devlogs, and informative videos from me soon!

  • @MorseAmalgam
    @MorseAmalgam ปีที่แล้ว +3

    Really nice expansion on the original tutorial - More details about how particular things work, clearer audio, more easily readable text, new features. Improves on pretty much every facet of the information that was being conveyed in slothinahat's video.
    One thing I was surprised didn't make it into this is optimizations to reduce the amount of tiles being generated - With it being in the process function and having no checking, it's regenerating all the tiles in the space around the player every frame, when it only really needs to process them once.

    • @kobedev
      @kobedev  ปีที่แล้ว +1

      Really good point mate! With all the kind feedback this video got and constructive ideas, I’ll definitely post a short upload fixing some issues especially making this feature and the chunk unloading more optimised

    • @MorseAmalgam
      @MorseAmalgam ปีที่แล้ว +2

      @@kobedev Very cool! Procgen stuff is the kinda project you could probably make a whole series out of adding new features and optimizations for :P Keep up the good work, these videos are well put together!

  • @mincraft752
    @mincraft752 8 หลายเดือนก่อน +1

    Hey i love your videos u make everything so easy to understand . Can u please make a video about making your procedural terrain generation code optimized it gets very less frames and please make a save and load system so u can place and delete tiles.

    • @samuelhulme8347
      @samuelhulme8347 5 หลายเดือนก่อน +1

      If Godot supports threads the terrain generation code should be placed in a a thread.

  • @blarvinius
    @blarvinius ปีที่แล้ว +2

    Amazing! Perfect balance of design+code. More please!

    • @kobedev
      @kobedev  ปีที่แล้ว

      Don't worry, after the next two videos from me I'll work on a tutorial!

  • @dausboot3997
    @dausboot3997 ปีที่แล้ว +1

    Great stuff! Thanks for the help!

    • @kobedev
      @kobedev  11 หลายเดือนก่อน

      No worries!

  • @slothinahat
    @slothinahat ปีที่แล้ว +8

    YOO this is really good! I’m gonna have to take a look at that chunking code

    • @kobedev
      @kobedev  ปีที่แล้ว +1

      Yeah I think I just need to implement a chunk deletion queuing system so they don’t delete all in one frame and cause jumps. I’m glad you enjoyed!

  • @DanTMZ
    @DanTMZ ปีที่แล้ว +14

    Wow it has 4 views and 5 likes which shows how elite this is

    • @kobedev
      @kobedev  ปีที่แล้ว +1

      Someone must have used psychiatry or something

  • @mercantilistic
    @mercantilistic ปีที่แล้ว

    Impressive tutorial, this is much better than many other tutorials I've seen lately.

    • @shanefoster5305
      @shanefoster5305 9 หลายเดือนก่อน

      He literally duplicated sloth in hat's tutorial that was done a year ago...

    • @mercantilistic
      @mercantilistic 9 หลายเดือนก่อน

      @@shanefoster5305 I'm not an archivist, I didn't watch all the possible videos on youtube. I don't even know who sloth in hat is.

    • @shanefoster5305
      @shanefoster5305 9 หลายเดือนก่อน

      @@mercantilistic just letting you know. Im not a fan of people duplicating other peoples content and posting it as their own taking credit for it. At least could have credited the guy…

  • @mailmonkey
    @mailmonkey 11 หลายเดือนก่อน +2

    Is there any way to add collision shapes to the land?

    • @kobedev
      @kobedev  10 หลายเดือนก่อน +1

      There is! Click on your tilemap, go to physics layers and add a new layer, then go to tile set and select paint, then select from the property editor physics layer 0 and then start painting the hit box, if you are also using navigation you can do a similar thing with the navigation layers, with that, if you paint the whole thing it will count as a tile that the path finding will pathfind through, if not, it will act as a wall and the path finding will avoid it.
      Hope this helps!

    • @mailmonkey
      @mailmonkey 10 หลายเดือนก่อน

      @@kobedev Thanks

  • @starchsky
    @starchsky 10 หลายเดือนก่อน +1

    Without knowing much about what kind of compilation or optimization happens to the code/script in godot, there are some low hanging optimization that could be done. You should move and combine any math (especially divisions) out of the loop. You could predefine the "width/2" outside of the update. You could define the tile coordinates as out of the loop as you can (x outside of y loop, y before the noise lookup etc), instead of doing all the math again for each use inside the deepest loop. You could test using a dict/map for the loaded_chunks with coordinates as the key, so checking a value would be (hopefully) logn instead of n. And check the chunk before doing the noise lookup and cell setting, which might be unnecessary. Again, not sure what godot does with the script, but i assume there isn't much in the way of auto optimization happening.

  • @greighlinv
    @greighlinv ปีที่แล้ว +2

    This looks awesome! I’d like to see you add certain entities for specific biomes like cactus can only spawn in the desert. Or flowers in the plains etc. I’m working on something completely different but this definitely helped my understanding of referencing the built in tile map functions 👍

    • @kobedev
      @kobedev  ปีที่แล้ว +1

      Yeah absolutely you would just have to add another atlas and match that up to the moisture altitude and temperature levels of each tile, then run like a random number generator and only spawn that tile on top (you need to add another layer) if the random number = your predetermined number, set the range of random numbers between 1 and 10 if you want a 10% chance of that decoration tile appearing and you can adjust the range to make it more or less probable

  • @ollie7597
    @ollie7597 ปีที่แล้ว +1

    Please keep up! Thank you for the hard work, helpful and enjoyable.

    • @kobedev
      @kobedev  ปีที่แล้ว

      Thanks man! I really appreciate the kind comment and I am definitely planning to make more videos like this!

  • @archwhynot
    @archwhynot ปีที่แล้ว +5

    Imagine if you tried recording this and then you realised you didn’t record the audio.
    That would be pretty goofy.
    Luckily that didn’t happen…
    Another great video KobeDev! 👍

    • @kobedev
      @kobedev  ปีที่แล้ว +6

      You know what would be even goofier is if mid recording of the same video someone played earblasting metal pipe sound effect which was so jarring it made me jump and so loud it came through the microphone input.
      Luckily that didn’t happen…
      Another great comment ardy balls!

    • @archwhynot
      @archwhynot ปีที่แล้ว +2

      @@kobedev That would indeed be terrifying and it’s lucky it never occurred. If that happened to me, I’d hate the person who did that for the rest of my life. Who in their right mind would be that mean??
      Thankfully not me.
      Thanks KobeDev!

  • @remigouttefangeas
    @remigouttefangeas 10 หลายเดือนก่อน +1

    Hello, first of all thank you for this tutorial, it's exactly what I needed. I'm a beginner and I'd like to know if there's a way of preventing the player from spawn in the water, so limiting the appearance of water around the player during the first generation I imagine... ?

    • @kobedev
      @kobedev  10 หลายเดือนก่อน

      You could do that or do it the other way and position the player somewhere that has land terrain

  • @greighlinv
    @greighlinv ปีที่แล้ว +2

    This is awesome dude very easy to follow. If i wanted to implement terrain sets so all the tiles connected smoothly with auto tiling how would I go about doing that. When I use set_cell_terrain_connect to place terrain sets it doesnt load and it throws out no errors

    • @kobedev
      @kobedev  ปีที่แล้ว

      Yeah I was looking into the terrain method before hand briefly. To my knowledge you need a terrain set and I’m not sure how your project is set up so I can’t help you in this case but if you want to send me your project file and I can look into it just send it to my email in my about me or in the description and I’ll get back to you about that

  • @david18cruz1989
    @david18cruz1989 5 หลายเดือนก่อน

    Interesting, if you go away from a chunk untill it unloads and then cvomes back, will it reload as it was or as a different chunck?

  • @ТарасДзебчук-и1ы
    @ТарасДзебчук-и1ы 6 หลายเดือนก่อน

    Instead of rounding Vector2, wouldn't it be better to use Vector2i?

    • @kobedev
      @kobedev  6 หลายเดือนก่อน +1

      vector2i wont let us do some of the maths required and has some inconsistencies when you do this, plus the round function is not really that resource intensive

  • @ahmki52298
    @ahmki52298 9 หลายเดือนก่อน

    I cant even get it to let me use the tilemap after imported 😅

  • @stupidrainbo
    @stupidrainbo 11 หลายเดือนก่อน

    Once I finish a series of Godot tutorials I'm going to try to make my own Valheim :D
    I know it's probably too ambitious, but I'm not afraid to scale things back and keep it simple, lol.
    Good tutorial! My mission would be to figure out how to do this with a heightmap for a plane mesh. I assume it would be a similar process but instead of using 2d tiles, it would literally be printing pixels onto a PNG file or something? I suppose I'll cross that bridge when I come to it xD

    • @kobedev
      @kobedev  11 หลายเดือนก่อน

      Yeah it’s a lot more complicated than printing pixels to a png file, my honest opinion is for you to make a few simple games first, platformer, top down, maybe a 3d, then you’ll have the tools and skills to try and approach your dreams without getting stuck at every point and not even knowing what to search up…. Trust me, I made the same mistake

    • @stupidrainbo
      @stupidrainbo 11 หลายเดือนก่อน

      @@kobedev Thanks for the advice! Right now I'm doing tutorials and making a few small games in them. Maybe I'll just try and temper myself though once I finish them xD

  • @EnumDev1
    @EnumDev1 ปีที่แล้ว +2

    Thanks!!!!!!

    • @kobedev
      @kobedev  ปีที่แล้ว +1

      No worries mate, it means a lot to me that this video was helpful and useful to you!

  • @ArchaicVirusStudios
    @ArchaicVirusStudios ปีที่แล้ว +1

    This worked really well! I did notice however, there's quite a bit of lag once you get far enough from the origin to start unloading chunks. This causes a visual stutter due to the heavy operation of deleting a chunk within a single frame. Would you consider making a tutorial on deferring the chunk deletion to a threaded queue system? Then the chunk operations can be spread out over multiple frames, thus eliminating/reducing the stutter. I think this would make it a more viable system for practical use (no offense or anything). I have a similar worldgen system using noise, but I currently don't delete or unload chunks at all, and there is no lag or stutter, using a tilemap with 32x32 tile size @1920x1080, even after moving 500k+ tiles away, with 3 layers of tiles (fg tiles, bg tiles, and foliage). I guess Godot's tilemap system is very well optimized. Not to say that unloading isn't necessary, I just haven't coded up a system to handle it yet, which is what led me here :) Great tutorial! (And sorry if I sounded harsh, just trying to give constructive input)

    • @kobedev
      @kobedev  ปีที่แล้ว +1

      Yeah i should have thought of that system it makes much more sense, but damn, Godot’s tilemap must be super optimised for it to have no latency for loading that many chunks, maybe chunk unloading in godot (2D at least) is pretty overkill, thanks for the feedback as I totally missed this one!

    • @ArchaicVirusStudios
      @ArchaicVirusStudios ปีที่แล้ว

      @kobedev Right!? I'm not sure if it sleeps the far away quadrants, or toggles visibility of them automatically. I assume the ladder, but I do know that the tilemap will batch the draw calls for tiles within whatever quadrant size is configured on the tilemap, but other then that I'm not totally sure what optimizations they use. Your system is really great though, and I will need to use it in my game (infinite universe Starbound-esque) Especially when leaving planets, as it doesn't make sense to keep them loaded after. Perhaps you could expand on this design in the future with the optimizations, but either way, this way very helpful, and thanks for your reply!

  • @fabioehrt1073
    @fabioehrt1073 11 หลายเดือนก่อน

    @KobeDev Hey, i have a short question i tried it and it seemed that it does not choose the from the fourth row (x,3). Since I´m a newbie im not sure were my mistake is. Im using Godot 4.2. I havent seen any tiles from the fourth row in the end of the video. Can somebody explain to me what i would have to change?

    • @kobedev
      @kobedev  11 หลายเดือนก่อน

      Could you send me your code either to my email or as a reply and I’ll try to see if your error is there, if not you can copy the project files in the link in the description which has the working code there, I haven’t tested it in godot 4.2 though

  • @Trupen2
    @Trupen2 ปีที่แล้ว +3

    you are a bit too quite, especially when compared to that outro music, but overall great tutorial

    • @kobedev
      @kobedev  ปีที่แล้ว +1

      I couldn’t turn up my audio any more than that unfortunately because of my goofy mic. Definitely will turn down the meme outro music so it isn’t too jarring and thank you for the kind comment and great feedback mate! 😊

  • @jheartt
    @jheartt 11 หลายเดือนก่อน

    Is there a way to limit the overall size of the map, so it generates a shape and doesnt go on forever?

    • @kobedev
      @kobedev  11 หลายเดือนก่อน

      im not sure if this would work but you can try having 2 variables, maxwidth and maxheight and inside the function that draws the terrain just test first if the current position.x and position.y is inside this maxwidth and maxheight

  • @MangleParty9
    @MangleParty9 ปีที่แล้ว

    This is incredible

    • @kobedev
      @kobedev  ปีที่แล้ว +1

      Yea it’s pretty impressive, you can take it further by adding shaders too, you can also make it hexagonal tiles or have an offset like brick effect and heaps more just with the TileMap node!

  • @QwertyDev24
    @QwertyDev24 ปีที่แล้ว

    can you make a 3d version

    • @kobedev
      @kobedev  ปีที่แล้ว

      I’m actually working on transferring my 2D project to 3D so I can pick up some knowledge, but I will definitely add that to the list! Great idea!

  • @lilylee3990
    @lilylee3990 ปีที่แล้ว +3

    🥵Kobe dev

    • @kobedev
      @kobedev  ปีที่แล้ว +1

      Me too 🔥🔥🔥

  • @undefeated2555
    @undefeated2555 3 หลายเดือนก่อน

    I don't want to commit 18 minutes to a video, unless it is possible to use this to create chunks in 3d, I've been looking everywhere for tutorials, but I cannot find anything on 3d chunk generation.

  • @SoapGamingStudios
    @SoapGamingStudios 3 หลายเดือนก่อน

    It completly broke

  • @Жмелимператор
    @Жмелимператор 6 หลายเดือนก่อน

    Тяжело когда всего 4 гайда на ютубе по генерации в годо 3 из которых устаревшие а 1 англоязычный

  • @Professor_Sick
    @Professor_Sick 5 หลายเดือนก่อน

    3d please 3d gen

  • @shanefoster5305
    @shanefoster5305 9 หลายเดือนก่อน

    This is literally a copy of "sloth in a hat" video a year ago...

    • @kobedev
      @kobedev  8 หลายเดือนก่อน +2

      I understand where you're coming from, I actually asked him directly before the video if it would be fine if i make this video and he said yes, additionally, I added unloading logic and some fixes that I saw was wrong in the first video

  • @CodeBridge-v8c
    @CodeBridge-v8c 3 หลายเดือนก่อน

    im using icon svg(8x8) as my placeholder tile .
    So if i set width to 128
    i get 16 full icons or (16x(8x8))
    Code is pretty much same but if i keep numbers low and run on full screen i get some trailing like tiles because its drawing another tile above current one just after moveing 1 tile so unless i move to required distance it keeps on drawing those chunks above each time creating snake like pattern.
    And sometimes chunk doesnt disappear even after moving to other side of screen and sometime it trails few tiles after the chunk behind while moving(In just running program once not chaing any value).
    Tried keeping timer and everything help !!!!!
    extends Node2D
    @onready var player = get_node("player")
    @onready var tileMap =$TileMapLayer
    @export var width : int = 64
    var Map_size = Vector2(width,width)
    const LAND_CAP = 0.3
    const chunk_unload_distance : int = 48
    var noise = FastNoiseLite.new()
    var loaded_chunks = []
    func _ready():
    var player_tile_pos = tileMap.local_to_map(player.position)
    noise.seed = 1
    generate_chunk(player_tile_pos) #randi()
    #generateWorld()
    #
    func _process(delta: float) -> void:
    var player_tile_pos = tileMap.local_to_map(player.position)
    generate_chunk(player_tile_pos)
    unload_distance_chunk(player_tile_pos)
    #func generateWorld():
    #print("gen world")
    #var noise = FastNoiseLite.new()
    #noise.seed = 1 #randi()
    #noise.frequency = 0.01
    #
    #var cells : Array = []
    #for x in range(0,Map_size.x):
    #for y in range(0,Map_size.y):
    #var a = noise.get_noise_2d(x,y)
    #if a unload_dist:
    clear_chunk(chunk)
    loaded_chunks.erase(chunk)
    func clear_chunk(pos):
    for x in range(0,Map_size.x):
    for y in range(0,Map_size.y):
    #tileMap.set_cell(Vector2i(pos.x-(Map_size.x/2) + x,pos.y - (Map_size.y/2)+y),-1,Vector2i(-1,-1),0)
    tileMap.erase_cell(Vector2i(pos.x-(Map_size.x/2) + x,pos.y - (Map_size.y/2)+y))
    func dist(p1,p2):
    var r = p1-p2
    return sqrt(r.x ** 2+r.y ** 2)
    #func _on_timer_timeout() -> void:
    #var player_tile_pos = tileMap.local_to_map(player.position)
    #generate_chunk(player_tile_pos)
    #unload_distance_chunk(player_tile_pos)