A Few Pointers

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

Hi All,

I’m hoping someone can give me a few pointers to help me get started with my first game. A bit of background to myself - I have about 5 years coding experience but it’s pretty much exclusively statistical (I use R and Python a lot, but also dabble in some WebDev). Anyway, since finishing my PhD last year I’ve slowly been getting round to starting the projects that I’ve been meaning to do for years, but just never had the time/motivation to do before because of my ridiculous workload - one of them is to make my first game! I’m really into management sim games so I thought I’d try to clone the classic Lemonade Tycoon as my first try and to get stuck into a project. In my naivety I feel that it’s a relatively straightforward game to code and requires little artistic skill as it’s a mostly UI-driven game (this second point is important as I can’t draw!).

I settled on Godot as my engine because (1) it was recommended here and elsewhere that the engine is good for 2D UI-driven games and (2) coming from a statistical background, the Godot syntax feels very comfortable and natural (given my experience with R and Python). While I feel like I have a good handle on the mechanics of the game (user buying lemons, sugar, stall upgrade modifying various aspects of the simulatiuon etc), and how the user will interact with it, I am stuck on thinking through two key areas of the game. I’m hoping someone could give me some pointers on the below:

  1. In the classic Lemonade Tycoon, the game “starts” when the player clicks “Start Day”. I was wondering what the best way to handle time would be in Godot? Would I be best to use a Timer with a countdown? On clicking ‘Start Day’ I could take the user imputs, spawn some customers and then run the simulation during the countdown. I guess I would need some sort of time-step within the countdown, for the code to run sales, making new lemonade when you run out etc. If this is indeed the best way of going about it, could someone point me in the direction of any tutorials that cover this mechanic in Godot? I have followed this tutorial on creating an Idle Tycoon game and, while it discusses timers, it doesn’t address them in the context of my needs (i.e. I can’t figure out whether I could modify this timer mechanic to step through the sales part of the simulation, or whether I need another solution).
  2. Are there any good sources for spawning NPCs in Godot that can act as “customers”? e.g. spawn a random number of “customers” with various properties (money, tastes, time available etc.), have them follow a pre-defined path, set a percentage probability of them stopping at your stall when they pass etc.

I think with some pointers in these two key areas I will have a solid foundation on which to start coding. It’s possible that both questions might be too big and ill-defined to be answered properly, but any pointers in the right direction would be gratefully received.

Thanks in advance for your help!

I’m not 100% sure how you want your timer to work, but I can suggest a method of setting up a time system.

Create an autoload/singleton that acts as your timer that uses the following code:

#when we create this auoload, we'll call it "timer"
signal time_changed_to(time)
signal day_ended()

var active = true
var curr_time = 0
var day_time = x #total day time (in frames). 
func _physics_process(_delta):
    if active:
        curr_time += 1
        if curr_time < day_time:
            emit_signal("time_changed_to", curr_time)
        elif curr_time >= day_time:
            emit_signal("day_ended")
            active = false

Then you can connect nodes to the timer signals in their code. Lets say you have a node that has an event initiated that, in 10 seconds, you want to have it print “surprise!”. You could do it via a function on that node like:

var queued_funcs = [
    ]

func _ready():
    timer.connect("time_changed_to", self, "time_is_now")

func print_text(text):
    print(text)

func queue_func(func_name, func_args, execute_time):
    new_queue_func = {
        "func_name" : func_name, #This is a string
        "func_args" : func_args, #This is an array of arguments
        "execute_time" : execute_time, #This is an int
        }
    queued_funcs.append(new_queue_func)

func time_is_now(time):
    for func in queued_funcs:

    #If func's stored time has passed
    if func["execute_time"] >= time:

        #Run the function using any stored arguments, 
        callv(func["func_name"], func["func_args"])

        #Then remove the func from the queue
        queued_funcs.erase(func)
        
func _process(_delta):
    if Input.is_action_just_pressed("ui_accept"):
        queue_func("print_text", ["surprise!"], timer.curr_time + 600)

Pressing ui_accept (by default it’s the enter key) will run the queue_func function and make an entry with the specific details of what func to run, any arguments to run it with, and when to run it. A better methodology would probably be to move this whole queue process onto the timer node itself, and have other nodes tell the timer node a ref to themselves along with the details of the func execution. I used _physics_process fopr the timer because it’s going to run at a constant 60 frames per second. There’s probably other ways of accomplishing this, but I like this method because it means you have a single, centralized timer to reference. Let me know if I should clarify anything.

You can read more about autoloads here: Singletons (Autoload) — Godot Engine (stable) documentation in English

As for the second question, that’s what object oriented is for. You can make a scene/class for a customer, then instance it and set up their details after creation. You can read more about instancing nodes through code here: Resources — Godot Engine (latest) documentation in English

But a simple summary is: https://forum.godotengine.org/36046/how-can-i-instance-by-code

denxi | 2020-04-30 16:33

Hey @denxi, thanks so much for your time and your suggestions.

This is honestly a massive help and will really go a long way to fleshing out some important aspects of the game and will help me plan things better before I jump in. In particular, the timing code will be very useful - I guess there are a number of different ways to go about this and having your input will really help me expand my research on this topic. I was actually surprised at how little information on this I could find from (pretty extensive) Googling; I searched for all sorts of things such as ‘global timing system’, ‘count event timers’, ‘business simulation timing events’, ‘timing management’ etc. and couldn’t anything that appeared useful. I did come across _delta a few times but the examples I found lacked enough context for me to piece together exactly what was happening. Anyway, I shall certainly use your code as my starting point and start experimenting so I can fully understand what’s happening.

I have some time over the next few days so I’m looking forward to getting stuck in. i’ll likely get hopelessly stuck so you may want to rescind your offer of clarification :wink:

Thanks again for your suggestions.

starr | 2020-04-30 18:35

I’m glad you found my post useful.

Part of the reason I think your search efforts weren’t helpful was because your question isn’t really a single question, it’s a group of multiple question. I’ve found it’s easier to find the mechanistic answers for the individual questions if you separate them. In this case, you’re really looking at finding answers for:

  1. How can I keep track of time?
  2. How can I communicate that variable to other nodes?
  3. How can I have those nodes act in specific ways, depending on the communicated variable?

Question 3 can really be split up into sub-questions even further, depending on what exactly you’re looking for.

As for delta, the reason its prefaced with an underscore is because it’s an argument the function is run with that I never actually wrote code to use. If you run it without the underscore it’ll throw a warning, but it’ll still work. Delta is the time since the last frame occurred. This is more useful IMO in working with _process, because unlike _physics_process, it does not have a fixed frame rate. This allows you to code more consistent behaviors over a potentially erratic environment. In _physics_process it’s less useful, because it runs at a constant frame rate (default is 60fps, you can change this in the project settings though). If you print(delta) in _physics_process, you should always get the same number. In_process the number that gets spits out will vary depending on your frame rate on a frame by frame basis.

denxi | 2020-04-30 20:20