Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
ZombieApostate
Mar 13, 2011
Sorry, I didn't read your post.

I'm too busy replying to what I wish you said

:allears:
You should be able to do some relatively simple math to calculate what cell is under the cursor yourself, if you have to, based off the mouse position, camera offset, zoom level and tile size. I don't know anything about godot, so I can't tell you if something to do that already exists.

edit: or you could just figure it out while I'm typing :argh:

ZombieApostate fucked around with this message at 17:20 on Feb 8, 2023

Adbot
ADBOT LOVES YOU

Ihmemies
Oct 6, 2012

I spent days wondering why it is so hard. I used the mouse coordinates inside the window. So in a 1920x1080 window mouse coords could be 0,0 or 1920x1080 or something in between. Accidentally while browsing reddit I found about the global mouse coordinates. In a larger tilemap the coords can be like 8000,8000 so no wonder everything was always off.

Edit: another very interesting thing is to figure out how to use a variable texture from a TileSet in a 2d sprite. Or maybe I need to create a tilemap and use set_cell and uhh attach the tilemap to a mouse while the mouse is moving around..

Ihmemies fucked around with this message at 17:54 on Feb 8, 2023

Dr Jankenstein
Aug 6, 2009

Hold the newsreader's nose squarely, waiter, or friendly milk will countermand my trousers.
what tf kind of syntax does unity want me to use to pull a list from one class to the other?

I have a list in one class. i want to add/remove to it from a different class. its public.

every attempt to reference it either throws a bunch of errors about monobehaviors not being able to be called as new, or null reference exceptions, or some kind of error.

jizzy sillage
Aug 13, 2006

Raenir Salazar posted:

Sadly it's not that simple since at work we have like 50 modules and I've been tasked with refactoring some code to be less tightly couples and I think for the observer pattern ideally I should be passing a reference to the subject of all of the observers that will be watching it? And I'm trying to find a spot that would be persistent between levels.



Seems to indicate the GameInstance is created and initialised first.

Edit: though on closer inspection it mentions UWorld Start but not the UWorld init.

For observer you'd be using delegates and binding to them in every other relevant place right?

jizzy sillage fucked around with this message at 23:10 on Feb 8, 2023

TIP
Mar 21, 2006

Your move, creep.



AA is for Quitters posted:

what tf kind of syntax does unity want me to use to pull a list from one class to the other?

I have a list in one class. i want to add/remove to it from a different class. its public.

every attempt to reference it either throws a bunch of errors about monobehaviors not being able to be called as new, or null reference exceptions, or some kind of error.

there's nothing special going on in unity, it's standard c# and you do it like you would anywhere else

without code and only vague references to the errors you're receiving it's impossible to tell what you're doing wrong

maybe you're trying to use a non static variable without a reference? maybe you've loaded a new scene and your gameobject was destroyed so your reference is gone? there's basically endless ways to do it wrong

Dr Jankenstein
Aug 6, 2009

Hold the newsreader's nose squarely, waiter, or friendly milk will countermand my trousers.
Basically i'm doing ragdoll bowling.

so this is class A
code:
public class Pinsetter : MonoBehaviour
{

    public GameObject pinPrefab;
    public List<Transform> spawnPoints = new List<Transform>();
    private Vector3 offet = new Vector3(5.32f, -2.5f, -1.2f);


    public void spawnNewPins()
    {

        foreach(Transform spawnPoint in spawnPoints)
        GameObject.Instantiate(pinPrefab, (spawnPoint.position + offet), Quaternion.Euler(0,90,0));
       

    }


        
}
and then class B

code:
public class Joints : MonoBehaviour
{

    public ConfigurableJoint joint;
    private List<Transform> pinList;

    void Start()
    {
        var pinsetter = new Pinsetter();
    }


    void OnCollisionEnter(Collision col)
    {

        if(col.gameObject.CompareTag("ball"))
        {

            JointDrive jointDrive = joint.angularYZDrive;
            jointDrive.positionDamper = 70f;
            jointDrive.positionSpring = 5f;
            joint.angularYZDrive = jointDrive;
            
            //remove item from list from pinsetter here
            //so Remove(this.transform.root.gameObject) from the list here. 

        }

        if(col.gameObject.CompareTag("deahplane"))
        {

            Destroy(this.transform.root.gameObject);

        }

    }
    
}
This configuration throws the least errors, but it won't let me create a new monobehavior. trying to use getcomponent is returning null reference exceptions, or not liking the formatting.

while there is absolutely no *need* for Pinsetter to be a monobehavior, unity doesn't wanna let me attach it to a component without it being one. the private list in class B is a leftover from bashing my head against the wall trying to figure out how to pull this reference over.

the goal is so that when the ball hits part of the ragdoll, the ragdoll collapses (this part works just fine), and then gets removed from the list so that when i start the second ball those pins don't come back down.

Dr Jankenstein fucked around with this message at 04:22 on Feb 9, 2023

full point
Jan 21, 2006

You cannot create a MonoBehaviour using new!

You can instantiate a GameObject that already has the script attached to it:
code:
var go = GameObject.Instantiate(somePrefab);
var pinsetter = go.GetComponent<Pinsetter>();
You can add a MonoBehaviour to an existing GameObject:
code:
var pinsetter = go.AddComponent<Pinsetter>();
If the GameObject with the Pinsetter script attached to it is already in the scene, you can add:
code:
public Pinsetter pinsetter;
to the Joints class and set it in the inspector by dragging/dropping the script with Pinsetter onto that field...or you can get the reference using code:
code:
void Awake()
{
    pinsetter = GameObject.Find("NameOfSomeObjInTheScene").GetComponent<Pinsetter>();
}

full point fucked around with this message at 07:17 on Feb 9, 2023

TooMuchAbstraction
Oct 14, 2012

I spent four years making
Waves of Steel
Hell yes I'm going to turn my avatar into an ad for it.
Fun Shoe
If you (quite understandably) want to create and initialize a monobehavior in a single step, I recommend using a factory method to do it. e.g. if I have a Gun behavior and it needs to know about the AmmoTracker behavior so it can tell it when bullets are used, I would add this function to the Gun class:
code:
public static Gun Attach(GameObject g, AmmoTracker tracker) {
  var gun = g.AddComponent<Gun>();
  gun.tracker = tracker;
  return gun;
}
Note that the behavior's Awake() is invoked immediately on the monobehavior being created, so properties like `tracker` in this example would not be available yet. However, Start() is not invoked until later (IIRC right before Update is called on the behavior for the first time?), so stuff set in the factory method will be available.

Or you can just do the setup code you wanted to do in Awake or Start inside of the factory method instead.

Dr Jankenstein
Aug 6, 2009

Hold the newsreader's nose squarely, waiter, or friendly milk will countermand my trousers.
This is what i'm running into.

exposing the pinsetter game object to the inspector, and dragging it in to the joint script, so that i have

code:
public Pinsetter pinsetter;

void oncollisionenter()
{
	//yada yada ragdoll stuff. 
	Debug.Log(pinsetter.spawnPoints);
}
resulting in a null reference exception.

code:
var pinsetter = GameObject.AddComponent<Pinsetter>();
is telling me "an object reference is required for the non-static field, method, or property"

code:
void Awake()
{
    pinsetter = GameObject.Find("NameOfSomeObjInTheScene").GetComponent<Pinsetter>();
}
again results in null reference exceptions when i Debug.Log(pinsetter.spawnPoints)

that's why i'm saying i'm missing something about how c# wants the syntax to access that list. StackOverflow keeps saying the same thing you are that I should be able to get to it like that.

I'm at that weird stage of learning to code where its like, i understand *what* i need to do to make the program do what i want it to, but am still learning *how* to make it do that.Like i feel like I'm missing a *super* obvious step about how to call a reference at an earlier step in the process and just...have never had it be an issue until this point.

Raenir Salazar
Nov 5, 2010

College Slice

jizzy sillage posted:



Seems to indicate the GameInstance is created and initialised first.

Edit: though on closer inspection it mentions UWorld Start but not the UWorld init.

For observer you'd be using delegates and binding to them in every other relevant place right?

Yeah that last part is where I'm a little confused.

Problem:
We have a settings menu that is managing the internal state of various I suppose I'll call them "Models" so the Settings Menu is calling methods like ChangeCameraSpeed on our SettingsManager.

Example:
code:
void MySettingsMenu::HandleOnCameraSpeedSliderChanged(float InValueChanged) 
{
     MySettingsManager->ChangeCameraSpeed(InValueChanged);
}
My understanding is that this is an example of tight coupling; normally for smaller programs this isn't a big deal, but in larger projects the SettingsMenu directly changing the in game settings can introduce hard to trackdown bugs.

The solution as I understand it: Use the Observer pattern, which in Unreal is/can? implemented by using Delegate/Events. Instead of the IObserver/ISubject boilerplate which I see on C++ Websites?

However, while we do have an EventManager class that contains something like 95% of our games delegates, but it's also 1,000 lines and to use events for all the methods the Settings Menu currently has would probably add another 15 events; and if more parts of the game need similar refactoring I think it'd be better if we didn't continue to bloat this single "Superman" Event class?

So if I want to avoid using this global event class, I just create the delegates for the Settings Menu; and then broadcast; but then the Settings Manager, in order to subscribe to these events, would still need an include and thus needs to still "know about" the Settings Menu class right? Is that normal?

Am I overthinking this and is it fine for the "Observer" in this case to know about the thing it wants to Observe? In general when googling/researching design patterns I get that there's like a hierarchy of concerns of when it is or is not called for but I'm not sure how to judge/determine this.

Ihmemies
Oct 6, 2012

Since the building placement is hard to implement, I've been toying with worldgen class.

Now it can read an input image file and generate land and water based on pixel colors. First try did not go too well:



I adjusted the tile placement but then found out that the input map has features which are hard to render with the used tileset.. like water tiles surrounded by land in 3 sides:



I implemented a smoothing pass which goes over the 2D map array and recursively looks for bad spots and replaces them with ground. It now looks acceptable:



A 1000x1000 map causes 360000 recursions.

When zoomed out the FPS is under 20 when all the million tiles are visible. Some kind of LOD system would be handy, or something. Seems big tilemaps aren't meant to be looked from far away:



I made a bunch of methods which do their thing one by one. Next I'm interested in trying to generate some forests and bogs procedurally. No idea how to even start. I have implemented a flood fill algorithm once, but it's hard to imagine for it to produce nice results. Well, maybe I'll at least try it.



Also the mapgen now takes quite a few seconds even with only a 1000x1000 map, I need to probably implement saving&loading of map data so it doesn't have to do it every time I start up the game.

TIP
Mar 21, 2006

Your move, creep.



AA is for Quitters posted:

code:
var pinsetter = GameObject.AddComponent<Pinsetter>();
is telling me "an object reference is required for the non-static field, method, or property"

You can't add a component to GameObject, that's just a type of object. In a monobehavior you can refer to gameObject, which will refer to the instance of GameObject the script is attached to.

Dr Jankenstein
Aug 6, 2009

Hold the newsreader's nose squarely, waiter, or friendly milk will countermand my trousers.
GetComponent does the same thing... should there be a method that *returns* the list in the pinsetter class in order to call it? I havent seen a return in any of the similar questions to mine on stack overflow, but is that something that's just getting left out because its just something that is assumed dumbasses like me should know about? Like that's why i think i'm missing something really obvious with how to reference other classes in C# and just prior to this point I've not run into issues with it.

Peewi
Nov 8, 2012

AA is for Quitters posted:

GetComponent does the same thing... should there be a method that *returns* the list in the pinsetter class in order to call it? I havent seen a return in any of the similar questions to mine on stack overflow, but is that something that's just getting left out because its just something that is assumed dumbasses like me should know about? Like that's why i think i'm missing something really obvious with how to reference other classes in C# and just prior to this point I've not run into issues with it.

It seems to me like you don't understand the difference between types and objects that are instances of a type. You aren't calling a method on the GameObject class, but on an object that is GameObject.
Instead of
code:
var pinsetter = GameObject.AddComponent<Pinsetter>();
You need something like
code:
var pinsetter = myGameObjectVariable.AddComponent<Pinsetter>();
As TIP points out, if this is in a MonoBehaviour you might want to use gameObject (lowercase g).

There are also methods that are called directly from classes. These are called static methods. GameObject.Find, for example, is a static method in Unity.

12 rats tied together
Sep 7, 2006

Peewi posted:

You aren't calling a method on the GameObject class, but on an object that is GameObject.

The example I usually reach for is "You're trying to touch the ontological concept of Honda Civic, instead of say, 12 rats tied together's Honda Civic, year 2008, vin #12345, with 168,000 miles on it, and half a tank of gas"

Dr Jankenstein
Aug 6, 2009

Hold the newsreader's nose squarely, waiter, or friendly milk will countermand my trousers.

12 rats tied together posted:

The example I usually reach for is "You're trying to touch the ontological concept of Honda Civic, instead of say, 12 rats tied together's Honda Civic, year 2008, vin #12345, with 168,000 miles on it, and half a tank of gas"

This is..really helpful in understanding the difference, but like, is there a better primer for explaining it in terms of C#?

Like, i get that i need to reference *that specific list* and not just, the concept of lists, and i need to do it from *that specific object*... i need this Honda Civic that is sitting in *that* parking lot at 228 Spruce St...i dont need to create a new parking lot, nor do i need to create a new honda civic...

but I am not understanding how c# and unity reference that specific parking lot.

like with the example, I have no loving clue what variable that is supposed to be getting added to...

code:
var pinsetter = myGameObjectVariable.AddComponent<Pinsetter>();
like this is the first serious attempt i have made to learn coding where i am determined to actually *not give up* when i dont understand a concept like this, which is what i've done all 6000 times previously i have attempted to teach myself to code.

TIP
Mar 21, 2006

Your move, creep.



You're going to get frustrated trying to jump straight in without grasping the basics. I highly recommend getting a book designed for beginners and working your way through it.

I found the Head First series from O'Reilly to be a great starting point when I was learning Java, they have one for C# that I imagine is similarly good:
https://www.oreilly.com/library/view/head-first-c/9781491976692/

12 rats tied together
Sep 7, 2006

AA is for Quitters posted:

This is..really helpful in understanding the difference, but like, is there a better primer for explaining it in terms of C#?

I can't help but I wanted to commiserate with you, I find c# to be almost entirely unreadable even in the best scenarios (which game code is usually not).

I can do it in python for you though:
Python code:
# the ontological concept of "lists" are, in python, an object of type "type"
>>> list
<class 'list'>
>>> list.__class__
<class 'type'>

# in python you can instantiate a thing by ()ing it, which calls its constructor, which in python is __init__ / __new__
>>> list()
[]
# the result of initializing it is an instance of it
>>> list().__class__
<class 'list'>
The c# version of this seems to be --
C# code:
var pinsetter = GameObject.AddComponent<Pinsetter>(); // your code, GameObject here is "the concept  of GameObject"

// https://docs.unity3d.com/ScriptReference/GameObject-ctor.html
// you probably want to call "the GameObject constructor" which will return an instance of GameObject
GameObject yourThing = new GameObject();
yourThing.AddComponent<Pinsetter>();
The second part above reads as:
- Create a thing called 'yourThing' which is 'of type GameObject' (it's appropriate to refer to the ontological concept of GameObject here).
- The value of yourThing should be whatever happens when you call "GameObject()" (which we know from the documentation link is a constructor that returns an instance of a GameObject)
- Afterwards, take this thing and run its AddComponent method, with whatever "<Pinsetter>" means, which frankly I have no clue about because I can't read this language.

12 rats tied together fucked around with this message at 23:00 on Feb 9, 2023

Ihmemies
Oct 6, 2012

Do you guys have any ideas how to implement biome generation in a 2d map? To get some “natural looking” forests and bogs? What kind of algorithms would be useful, or must one go full dwarf fortress and simulate the world gen from beginning of time? Thanks :v:

https://gamedevacademy.org/procedural-2d-maps-unity-tutorial/

Edit: apparently by generating a noisemap and applhing biomes based on the output.

Ihmemies fucked around with this message at 23:08 on Feb 9, 2023

KillHour
Oct 28, 2007


AA is for Quitters posted:

like with the example, I have no loving clue what variable that is supposed to be getting added to...

code:
var pinsetter = myGameObjectVariable.AddComponent<Pinsetter>();

What's going on is your instance of myGameObjectVariable (which is presumably an instance of the GameObject class) has a method called AddComponent<T> that when called, creates a new component of type T (in this case, Pinsetter), and adds a reference to that component to the game object instance. So what has happened is the method mutated the instance myGameObjectVariable to now have an additional component attached to it.

That method also returns the reference to the new component it just created, so because you are assigning it to the variable pinsetter, you get the same reference that was added to the game object. In other words, that code does the same thing as

code:
myGameObjectVariable.AddComponent<Pinsetter>();
var pinsetter = myGameObjectVariable.GetComponent<Pinsetter>();
It's just shorthand, because you will often want to access the thing you just created.

jizzy sillage
Aug 13, 2006

Raenir Salazar posted:

Yeah that last part is where I'm a little confused.

Problem:
We have a settings menu that is managing the internal state of various I suppose I'll call them "Models" so the Settings Menu is calling methods like ChangeCameraSpeed on our SettingsManager.

Example:
code:
void MySettingsMenu::HandleOnCameraSpeedSliderChanged(float InValueChanged) 
{
     MySettingsManager->ChangeCameraSpeed(InValueChanged);
}
My understanding is that this is an example of tight coupling; normally for smaller programs this isn't a big deal, but in larger projects the SettingsMenu directly changing the in game settings can introduce hard to trackdown bugs.

The solution as I understand it: Use the Observer pattern, which in Unreal is/can? implemented by using Delegate/Events. Instead of the IObserver/ISubject boilerplate which I see on C++ Websites?

However, while we do have an EventManager class that contains something like 95% of our games delegates, but it's also 1,000 lines and to use events for all the methods the Settings Menu currently has would probably add another 15 events; and if more parts of the game need similar refactoring I think it'd be better if we didn't continue to bloat this single "Superman" Event class?

So if I want to avoid using this global event class, I just create the delegates for the Settings Menu; and then broadcast; but then the Settings Manager, in order to subscribe to these events, would still need an include and thus needs to still "know about" the Settings Menu class right? Is that normal?

Am I overthinking this and is it fine for the "Observer" in this case to know about the thing it wants to Observe? In general when googling/researching design patterns I get that there's like a hierarchy of concerns of when it is or is not called for but I'm not sure how to judge/determine this.

In the case of SettingsMenu making changes that only SettingsManager needs to know about, the coupling is okay - as long as the SetMan isn't watching for updates in its Tick. So if SetMenu only ever tells SetMan "Hey the player just updated their mouse sensitivity" then that's fine. You start to need Delegates (Unreal Engine Delegates) when you have a bunch of things all reliant on knowing certain things happened. Easy example is Player death.

Bad:

- Player dies
- EventManager watches on Tick for bIsPlayerDead = true, triggers something
- AudioManager watches on Tick for bIsPlayerDead = true, plays some audio
- Enemies nearby all check to see if player is dead, find new targets
- UI Widgets A, B, and C all watch and update.
- etc.

Good:

- Player dies, Broadcasts Multicast Delegate OnPlayerDeathDelegate
- EventManager is subbed to OnPlayerDeathDelegate, triggers OnPlayerDeath and records the stats, gives some achievements, whatever
- AudioManager is subbed, plays some audio
- Fifteen different enemies are nearby, subbed, get told the Player has died and find new targets.
- UI Widgets A and B are subbed, so they get the broadcast and update, but turns out C never needed to care so we just removed the binding in the UI Widget C.

The other aspect is that you're moving the code from one side of the equation to the other - instead of SettingsMenu calling SettingsManager->ChangeSensitivity(float newSense), now SettingsManager just binds itself to the delegate on construction and unbinds on death. Now in your SettingsMenu, you just blindly broadcast into the void "Hey a thing changed, anyone care? No? Cool." and it doesn't matter if nothing cared, or the SettingsManager hasn't finished Init, or there are five SettingsManagers of different types (one for Gamepad, one for KB+M) with different behaviours that happen when the broadcast is sent. By moving all the binding into the SettingsManager, it can bind to the Menu events, or it can bind to a system that Loads/Saves settings, or both, and you don't need the SettingsMenu and LoadSaveManager to have a reference to a SettingsManager.

Bit of a braindump but that might help a little?

Lucid Dream
Feb 4, 2003

That boy ain't right.

Ihmemies posted:

Do you guys have any ideas how to implement biome generation in a 2d map? To get some “natural looking” forests and bogs? What kind of algorithms would be useful, or must one go full dwarf fortress and simulate the world gen from beginning of time? Thanks :v:

https://gamedevacademy.org/procedural-2d-maps-unity-tutorial/

Edit: apparently by generating a noisemap and applhing biomes based on the output.

Yeah, one way or another you're going to be relying on noise. Perlin or simplex, you should be able to find libraries pretty easily. Find something that has a way to preview the different settings, because it takes awhile to get a handle on what the different variables do. Procedural content generation is all about iteratively trying poo poo and seeing what happens.

Ihmemies
Oct 6, 2012

Seems Godot has a built-in support for generating noise textures with multiple types of noise: https://docs.godotengine.org/en/latest/classes/class_fastnoiselite.html

I have to look at this. There was also a tutorial how to generate the tex with code and read the noise texture's values and generate different tiles based on it:

https://www.youtube.com/watch?v=m6mu4uPGrMk

I wonder if I could overlay the noise texture to my map image file/texture's land parts.. that way I could read the data from image only once, and generate different terrain straight from the data.

Alllso this mapgen is so very slow. I have to time it and see which operations take most of the time: https://www.gdquest.com/tutorial/godot/gdscript/optimization-measure/

Of course the profiler doesn't really work for me. Also the get_ticks_usec() isn't supported in godot 4 anymore. :sigh:

Edit: it's Time. instad of OS.
And results are:
pixel-data 0
smooth land 0.652519
generate biomes 0.000001
set tilemap tiles 2.179979

While the game takes at least 10-15 seconds to start. So I wonder what the the game is actually doing with the time. Well at least the mapgen is fast enough, I’ll just implement a loading screen.

Edit2: now I’m wondering how to generate textures based on TileMap data for zoomed out views. Displaying big TileMaps on screen tanks the fps to sub 20. Or maybe I need to do “mipmapped” tilemaps with 1 or 2 or 4px resolution and switch to a scene rendered with the low res tilemap after certain zoom level.

Edit3: or maybe I have to render only a portion of the tilemap based on the game state, and zoom, and switch to static textures generated based on game data.. or.. uh hmm.. drat. So many options, and no idea which of them are good.

Ihmemies fucked around with this message at 09:02 on Feb 10, 2023

Ihmemies
Oct 6, 2012

Implementing biomes based on noise data was extremely straightforward. Next I have to:
- refactor my forest edge/shoreline drawing code because it is buggy and bad.
- implement a bounds checking method which several other methods can use





Python code:
func generate_biomes() -> void:
    var fnl = FastNoiseLite.new()
    fnl.noise_type = FastNoiseLite.TYPE_PERLIN
    fnl.seed = randi()
    fnl.frequency = 0.1
    fnl.fractal_type = FastNoiseLite.FRACTAL_FBM
    fnl.fractal_octaves = 3
    fnl.fractal_lacunarity = 1
    fnl.fractal_gain = 1.746
  
    var water_next_to_tile:bool = false
  
    for y in map_tile_data.size():
        for x in map_tile_data[y].size():      
              
            # replace non-water with biomes          
            if map_tile_data[y][x] > 0:  
                water_next_to_tile = false
              
                # don't put forest next to water
                for dir in directions:
                    if (y+dir.y >= Globals.map_image_size.y) or (x+dir.x >= Globals.map_image_size.x):
                        continue
                    if map_tile_data[y+dir.y][x+dir.x] == Globals.TILE_WATER:
                        water_next_to_tile = true
              
                # if there's no water next to a land tile, it can be replaced with forest
                if !water_next_to_tile:
                    var noise_sample = fnl.get_noise_2d(x,y)
                    if noise_sample < 0.1:
                        count += 1
                        map_tile_data[y][x] = Globals.TILE_FOREST   

Ihmemies fucked around with this message at 16:33 on Feb 10, 2023

Chainclaw
Feb 14, 2009

Ihmemies posted:

Implementing biomes based on noise data was extremely straightforward. Next I have to:
- refactor my forest edge/shoreline drawing code because it is buggy and bad.
- implement a bounds checking method which several other methods can use





Python code:
func generate_biomes() -> void:
    var fnl = FastNoiseLite.new()
    fnl.noise_type = FastNoiseLite.TYPE_PERLIN
    fnl.seed = randi()
    fnl.frequency = 0.1
    fnl.fractal_type = FastNoiseLite.FRACTAL_FBM
    fnl.fractal_octaves = 3
    fnl.fractal_lacunarity = 1
    fnl.fractal_gain = 1.746
  
    var water_next_to_tile:bool = false
  
    for y in map_tile_data.size():
        for x in map_tile_data[y].size():      
              
            # replace non-water with biomes          
            if map_tile_data[y][x] > 0:  
                water_next_to_tile = false
              
                # don't put forest next to water
                for dir in directions:
                    if (y+dir.y >= Globals.map_image_size.y) or (x+dir.x >= Globals.map_image_size.x):
                        continue
                    if map_tile_data[y+dir.y][x+dir.x] == Globals.TILE_WATER:
                        water_next_to_tile = true
              
                # if there's no water next to a land tile, it can be replaced with forest
                if !water_next_to_tile:
                    var noise_sample = fnl.get_noise_2d(x,y)
                    if noise_sample < 0.1:
                        count += 1
                        map_tile_data[y][x] = Globals.TILE_FOREST   

I wanna fly an airship over that

Ihmemies
Oct 6, 2012

Chainclaw posted:

I wanna fly an airship over that



Well I could try to implement a player with an airship sprite, which can look/move in 8 different directions... It will probably be a good exercise :v: Altough my aim wasn't to make a player-driven game. Hmm, maybe I could implement cars/trains/walking citizens at least, I think simcity 4 had them?

It might be an interesting add-on, if I ever get the game part implemented. After building a city, just walk/fly around and chill. Hmm.

Ihmemies
Oct 6, 2012

Now I wish I understood tilemaps in Godot better.

If I create 16 new tilemaps with TileMap.new() and put them to a list, and use set_cell() to fill one of the tilemaps.

How do I actually display one of those generated tilemaps on screen with gdscript? :iiam:
Where does the tilemap go if I display it on screen? How I can tile tile tilemaps so they don't overlap?

For example if I have 16 tilemaps I want them positioned like so on screen:

[01] [02] [03] [04]
[05] [06] [07] [08]
[09] [10] [11] [12]
[13] [14] [15] [16]

Then I want to individually show and hide/clear the tilemaps when player/camera/whatever gets close enough that the tilemap will soon be on screen. I think I can manage that, but how to actually render even one tilemap on screen..

Do I need to create a canvaslayer? Add the tilemap array as a child? ? I really have no idea. All the examples are again GUI based which don't actually show how things are done.

Edit: maybe like that. Have a world node and add chunk scenes as a child to it... https://github.com/NesiAwesomeneess/ChunkLoader/blob/main/ChunkLoading/World.gd

Ihmemies fucked around with this message at 13:17 on Feb 12, 2023

RabbitWizard
Oct 21, 2008

Muldoon

roomforthetuna posted:

I guess your way avoids writing paths ever, but it's weird because you're having to make a variable name to substitute for the path, at which point you *could* just put the node in the root then it's got a short path so it's kinda the same but with less boilerplate code?
Not sure if I'm missing something here, but that would "destroy" the structure of my whole UI? I'm basically only using buttons within containers within containers. If those are all in root I'd have to write other code to tell them where they should be placed.

Ihmemies
Oct 6, 2012

I really wish I understood something about godot.

I have a Chunk class which extends Tilemap. It is visible in node tree with the is_visible_in_tree() command.

If I put a
func _draw():
draw_circle(Vector2(x,y), 350.0, Color(0,0,0))

to my Chunk class, it draws a black circle with 350px radius.

Then I have a chunk generation method like this:

Python code:
func generate_chunk() -> void:	
	for row in Globals.CHUNK_SIZE:
		for col in Globals.CHUNK_SIZE:
			var tile_data: Array = var tile_data: Array = Globals.map_tile_data[row+y*Globals.CHUNK_SIZE][col+x*Globals.CHUNK_SIZE]
			# layer | tile coords at tilemap | tilemap id | coords of the tile at tileset | alternative tile
			self.set_cell(
				Globals.LAYER_TERRAIN,
				Vector2i(col, row),
				0,
				tile_data[0],
				tile_data[1]
			)
But nothing appears on screen. Why the same Class can draw a circle, but when I set the tilemap full of tiles, it doesn't draw to screen? Goddamnnitt...

Edit:

Even the tiles look reasonable..

var str = "%s %s %s"
print(str % [tile_data, row, col])

code:
[(25, 0), 0] 0 0
[(20, 0), 0] 0 1
[(5, 0), 0] 0 2
[(1, 0), 2] 0 3
[(0, 0), 2] 1 0
[(18, 0), 0] 1 1
[(1, 0), 1] 1 2
[(1, 0), 2] 1 3
[(19, 0), 0] 2 0
[(1, 0), 3] 2 1
[(1, 0), 3] 2 2
[(1, 0), 1] 2 3
[(18, 0), 0] 3 0
[(1, 0), 0] 3 1
[(13, 0), 0] 3 2
[(1, 0), 2] 3 3
Hnngh.

Edit:
Finally I realized it is not using a tileset! ! ! ! ! ! :sigh:

code:
	var ts: TileSet = load("res://scenes/Chunk.tres")
	self.set_tileset(ts)
That to the Chunk class's init and uhh yes... now it works. Great. Time well spent. gently caress. :argh:

Edit2:


Edit3:
And now it loads chunks in n tile range (depending on vertical resolution) around the camera position, and frees chunks which are not needed anymore for display. Next I can tackle the save/load system. Probably best would be to save the generated maps, since 4096x4096 cell mapgen takes a minute. Then when a player starts a new game on a pre-generated map, just save the map data + changed cells + other new/changed game data to a new save file.. or something.

Then the player could resume the game quickly, when the game doesn't need to re-generate the map, and can draw chunks dynamically. That way even a large size map should load and be playable nearly instantly.

Ihmemies fucked around with this message at 22:23 on Feb 12, 2023

Dr Jankenstein
Aug 6, 2009

Hold the newsreader's nose squarely, waiter, or friendly milk will countermand my trousers.
I figured out my variable issue was due to never actually like, assigning variables at runtime, only declaring the defaults. So it was trying to, obviously, pull a null reference.

but now i have another *really* dumb question. Any way to hide cautions in unity? It's hard to go through the console because i'm using a font that doesn't have an underlined version for a UI thing.

Ihmemies
Oct 6, 2012

Any idea how I should use a thread to call chunk generation methods?

https://docs.godotengine.org/en/latest/tutorials/performance/using_multiple_threads.html

code:
var chunk_queue:Array = []
var mutex:Mutex
var thread:Thread

func _ready():
	mutex = Mutex.new()
	thread = Thread.new()
	thread.start(test_func(), Thread.PRIORITY_NORMAL)

func test_func():
	while true:
		if chunk_queue.size() > 0:
			mutex.lock()
			var vars = chunk_queue.pop_back()
			mutex.unlock()
			add_chunk(vars.x, vars.y)
then I have in my update_chunks()-method a way to add coords to the list:

Python code:
mutex.lock()
chunk_queue.push_front(Vector2i(x, y))
mutex.unlock()
And it does not work at all. I understood it would work like so:

1) create a new thread
2) new chunk coordinates are added to the chunk_queue list's front
3) thread keeps looking at the list, and if the list has items, it takes the last one and generates a chunk to the coords provided by the list item

Edit: I figured part of it out. The thread started before game loading was ready, so it gummed up the thing. Now the chunk handler won't do anything until the world gen is ready.

The thread works, kind of. Chunk queue fills up but the thread doesn't get any chunk generation done. Time to figure it out next... no children (new chunks) get added to the chunk handler. :argh:

Edit: now it maybe works. It is horrible, but it was the only example I understood something about :
https://pastebin.com/N3WNeRUJ

Ihmemies fucked around with this message at 20:38 on Feb 13, 2023

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!

AA is for Quitters posted:

I figured out my variable issue was due to never actually like, assigning variables at runtime, only declaring the defaults. So it was trying to, obviously, pull a null reference.

but now i have another *really* dumb question. Any way to hide cautions in unity? It's hard to go through the console because i'm using a font that doesn't have an underlined version for a UI thing.

I don't know if it's an extension I am using or not, but there's some icons I have towards to top right of the console that let me click warnings and errors on and off.

Raenir Salazar
Nov 5, 2010

College Slice

Ihmemies posted:

Any idea how I should use a thread to call chunk generation methods?

https://docs.godotengine.org/en/latest/tutorials/performance/using_multiple_threads.html

code:
var chunk_queue:Array = []
var mutex:Mutex
var thread:Thread

func _ready():
	mutex = Mutex.new()
	thread = Thread.new()
	thread.start(test_func(), Thread.PRIORITY_NORMAL)

func test_func():
	while true:
		if chunk_queue.size() > 0:
			mutex.lock()
			var vars = chunk_queue.pop_back()
			mutex.unlock()
			add_chunk(vars.x, vars.y)
then I have in my update_chunks()-method a way to add coords to the list:

Python code:
mutex.lock()
chunk_queue.push_front(Vector2i(x, y))
mutex.unlock()
And it does not work at all. I understood it would work like so:

1) create a new thread
2) new chunk coordinates are added to the chunk_queue list's front
3) thread keeps looking at the list, and if the list has items, it takes the last one and generates a chunk to the coords provided by the list item

Edit: I figured part of it out. The thread started before game loading was ready, so it gummed up the thing. Now the chunk handler won't do anything until the world gen is ready.

The thread works, kind of. Chunk queue fills up but the thread doesn't get any chunk generation done. Time to figure it out next... no children (new chunks) get added to the chunk handler. :argh:

Edit: now it maybe works. It is horrible, but it was the only example I understood something about :
https://pastebin.com/N3WNeRUJ

:allears: I like that you're going through some similar pains I was. :buddy:

I believe Sebastian Lague has a tutorial series on procedural world generation using multithreaded chunks, its for Unity3D though so it might not be immediately useful to you though.

https://www.youtube.com/playlist?list=PLFt_AvWsXl0eBW2EiBtl_sxmDtSgZBxB3

roomforthetuna
Mar 22, 2005

I don't need to know anything about virii! My CUSTOM PROGRAM keeps me protected! It's not like they'll try to come in through the Internet or something!

Ihmemies posted:

code:
func test_func():
	while true:
		if chunk_queue.size() > 0:
			mutex.lock()
			var vars = chunk_queue.pop_back()
			mutex.unlock()
			add_chunk(vars.x, vars.y)
In general you don't want to be doing a "while true" loop because now you have a thread just *constantly* checking if chunk_queue.size() > 0.
It's also not doing it with any mutex locking, which means potentially if the update to chunk_queue.size() happens on a different core, it won't even see that anything changed.
The common ways to resolve this are:
1. Some sort of mutex.await construct that makes it only check the value when the mutex is released elsewhere, and only take the mutex if the condition is met. This fixes the busy-looping, but still leaves the thread blocked until something happens, so you wouldn't want to do it on a main thread, but it's a good solution when a worker thread needs to wait for something. You might want to use this form for your worker thread that performs load operations.
2. A callback dispatcher. The worker thread pushes a callback onto the dispatcher when a piece of work completes, and the main thread (or a worker, anyone who listens to that dispatcher) does the work in that callback when it gets around to it. This usually works well for games and similar "mostly busy" tasks. The thing you ended up with resembles a callback dispatcher. You can either wait for the dispatcher to receive work (again with something like mutex.await), or peek at it once per frame or whatever to see if anything needs doing.

Ihmemies
Oct 6, 2012

roomforthetuna posted:

In general you don't want to be doing a "while true" loop because now you have a thread just *constantly* checking if chunk_queue.size() > 0.
It's also not doing it with any mutex locking, which means potentially if the update to chunk_queue.size() happens on a different core, it won't even see that anything changed.
The common ways to resolve this are:
1. Some sort of mutex.await construct that makes it only check the value when the mutex is released elsewhere, and only take the mutex if the condition is met. This fixes the busy-looping, but still leaves the thread blocked until something happens, so you wouldn't want to do it on a main thread, but it's a good solution when a worker thread needs to wait for something. You might want to use this form for your worker thread that performs load operations.
2. A callback dispatcher. The worker thread pushes a callback onto the dispatcher when a piece of work completes, and the main thread (or a worker, anyone who listens to that dispatcher) does the work in that callback when it gets around to it. This usually works well for games and similar "mostly busy" tasks. The thing you ended up with resembles a callback dispatcher. You can either wait for the dispatcher to receive work (again with something like mutex.await), or peek at it once per frame or whatever to see if anything needs doing.

Thanks. I switched to a semaphore.wait() inside the while true: -loop. I understand the loop execution stops, until something posts semapore.post() command elsewhere. Then the loop proceeds. Maybe that is enough.

I did not achieve results with a 2nd thread which generates chunks, I mean the game runs a bit choppily while moving the camera, frametimes are not smooth.

I will try other things next, like making a minimap.

One weird thing is that when I set image pixels to brown, they display as white. If I set them as pink, they display as pink. I don't understand :-F At least drawing a minimap was easy, now I need to figure out the colors... also the game has a billion different image formats, I guessed a RGB8 would be good, maybe not then. Maybe it needs to be RGBF...

Toplowtech
Aug 31, 2004

Raenir Salazar posted:

:allears: I like that you're going through some similar pains I was. :buddy:

I believe Sebastian Lague has a tutorial series on procedural world generation using multithreaded chunks, its for Unity3D though so it might not be immediately useful to you though.

https://www.youtube.com/playlist?list=PLFt_AvWsXl0eBW2EiBtl_sxmDtSgZBxB3
There are a few series of video on youtube that port Lague's works to Godot but the last one i can think of is centered on his procedural generation of planets using rounded squares, not the multithreading part.

Ihmemies
Oct 6, 2012

Really I don't understand how Godot's images work either. I have a new image and I set each pixel with colors like

image = Image.create(Globals.map_size, Globals.map_size, false, Image.FORMAT_RGBAF)
color = Color(148, 113, 71)
image.set_pixel(x, y, color)

Then I turn it to texture so I can use it on sprite:
minimap_texture = ImageTexture.create_from_image(image)
sprite.texture = minimap_texture

Result-> WHITE. HOW CAN IT BE WHITE. It does not make any sense at all to set a pixel's color as brown, and get white!!!! :argh:

commando in tophat
Sep 5, 2019

Ihmemies posted:

Really I don't understand how Godot's images work either. I have a new image and I set each pixel with colors like

image = Image.create(Globals.map_size, Globals.map_size, false, Image.FORMAT_RGBAF)
color = Color(148, 113, 71)
image.set_pixel(x, y, color)

Then I turn it to texture so I can use it on sprite:
minimap_texture = ImageTexture.create_from_image(image)
sprite.texture = minimap_texture

Result-> WHITE. HOW CAN IT BE WHITE. It does not make any sense at all to set a pixel's color as brown, and get white!!!! :argh:

I don't know anything about godot, but usually when you have image format float (I assume RGBAF is either 32 of 16 floats), white color will be (1.0, 1.0, 1.0), so if you set all three colors above 1 it will be white (if your green is anything other than (0,???,0), you can disregard this garbage theory)

Ihmemies
Oct 6, 2012

commando in tophat posted:

I don't know anything about godot, but usually when you have image format float (I assume RGBAF is either 32 of 16 floats), white color will be (1.0, 1.0, 1.0), so if you set all three colors above 1 it will be white (if your green is anything other than (0,???,0), you can disregard this garbage theory)

Well thanks. I have never ever seen colors being defined as floats between 0.0...1.0 anywhere before :D But I guess it makes sense in some way. Seems I have to use Color8(200, 150, 45) if I want to use human-readable formats without breaking out the pocket calculator or doing stupid poo poo like 1/255*200 when defining colors.

Edit: also learning C# would help to get rid of this annoying editor in Godot which is extremely barebones. With C# I could just use VSC or something modern instead. This doesn't even have splitscreen?!

Ihmemies fucked around with this message at 09:48 on Feb 14, 2023

Adbot
ADBOT LOVES YOU

Cory Parsnipson
Nov 15, 2015

Ihmemies posted:

Well thanks. I have never ever seen colors being defined as floats between 0.0...1.0 anywhere before :D But I guess it makes sense in some way. Seems I have to use Color8(200, 150, 45) if I want to use human-readable formats without breaking out the pocket calculator or doing stupid poo poo like 1/255*200 when defining colors.

Edit: also learning C# would help to get rid of this annoying editor in Godot which is extremely barebones. With C# I could just use VSC or something modern instead. This doesn't even have splitscreen?!

Yeah no splitscreen. I miss my vim keybindings too. :( The good news is that they made sure not to architect Godot 4 such that implementing splitscreen would be an ordeal. I don't know if it's already in or not, but the plan is to have that in 4.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply