RTS/City Builder script structure.

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By FezMonki

Hi, noob alert. I’m trying to build the bones of a (2D) city builder, and have gotten as far as the map, the camera panning, and placing a building by instancing it as a scene, at the cursor’s location.

Now, I’m at the point where I need to set it up to:
[House] Button → House Sprite follows the mouse cursor → When clicking again it sticks to the cell the mouse was hovering.

The little guidance I’ve found online had a generic cursor, then popped a menu on click, and had a building choice, but I’d like to set it up for buildings of different cell sizes and location requirements, hence the GUI button.

What I almost got around to doing is have the button instance a scene of the building, and have its position tied to the mouse under _process, then check if left is clicked again to change it’s position from get_global_mouse_position() to the position of the nearest tile.

Actual Question:
Do I use a signal in the button, catch it in the World script, find out which building it corresponds to, instance that building, and do as I mentioned above? Is this a good way of going about it? Thank you.

:bust_in_silhouette: Reply From: Pomelo

It seems you got it quite right! wouldnt say noob at all. I have done things like this in 2 ways.

One way is, as you wrote, is to instance the selected building, make it follow the mouse and then leave it where you please. With this aproach the bulding scene shpuld have a var like is_placing that when true stops certain behaviour that you dont want when placing the building (and also lets you maybe change its alpha value or wahtever)

Another aproach would be to have a generic placing_building scene, that gets instacned when clicking the button and then gets data passed to it. It shows the correct sprite, and when placed you use its data to instance the actual building.

In both you need some data of what building you are placing when clicking the button, as you also said. The button holds the data, and when clicked (triggered by a onClick signal) it should send the data by a custom signal to whatever manages the “board”, “map”, “level” etc…

Feel free to ask me anything else!

Oh I am very new haha, I just have 3/4 of a course and a little php programming under my belt. Thank you for confirming the direction, I didn’t want to waste a ton of time getting it to work like that for nothing!

Nothing else for now, but I’m sure something will come up. Like how to detect coasts between water and land to place fishing shacks lol.

FezMonki | 2022-09-13 08:48

A state machine set can work excellent in your case:

# building.gd

enum STATES {
    IDLE, 
    MOVE, # while following the mouse
    PLACE, # place instance at mouse position
    BUILD, # play build animation
}

var state = STATES.IDLE

func _input(event):
        if event is InputEventMouseButton:
            if event.button_index == BUTTON_LEFT:
                if event.pressed:
                    match state:
                        STATES.MOVE:
                            # intent to place building
                            state = STATES.PLACE
                       STATES.PLACE:
                           # test map that building can be placed and change state
                           state = STATES.BUILD
                           # if it cant be placed
                           state = STATES.MOVE
                   
        if event is InputEventMouseMotion:
            match state:
                STATES.MOVE:
                    # position = mouse_position

And set the state on instance

# gui.gd

func on_sprite_click():
    var building = load("building.tscn").instance()
    building.position = get_global_mouse_position()
    building.state = building.STATES.MOVE

Wakatta | 2022-09-14 21:02