russmatney

joined 1 year ago
[–] russmatney 2 points 1 year ago (4 children)

Looking pretty cool! I'm a sucker for beat em ups, so i'm already interested :D

[–] russmatney 5 points 1 year ago

Indeed! I pulled a few below (too many?) from my current project, Dino, which is on github if you want to dig into more.

Some of these make use of custom autoloads, but should still show the idea. Note the use of bind in places as well, which is making use of the same func/callable improvements.

  • Inlining a setup_fn to be called on the player after they're respawned:
func on_player_died(_player):
  Game.respawn_player.call_deferred({setup_fn=func(p):
    Hotel.check_in(p, {health=p.initial_health})})
  • Inlining a hide_fn predicate in a list of data (these dictionaries eventually get mapped into buttons)
var menu_scenes = [
  {label="Start Game", fn=Game.restart_game.bind(SuperElevatorLevel)},
  {label="Dino Menu", fn=Navi.nav_to_main_menu,
   hide_fn=func(): return not (OS.has_feature("dino") or OS.has_feature("editor")),
}]
  • Gathering a list of resource_paths from a list of levels:
var level_paths = levels.map(func(l): return l.resource_path)
  • A basic inline connect:
player.ready.connect(func(): player_ready.emit(player))
  • A more interesting connect:
func on_player_spawned(player):
  player.died.connect(on_player_died.bind(player), CONNECT_ONE_SHOT)
  • Creating a bunch of actions that use predicates heavily:

I have a general actions system, so it's nice to be able to implement small functions to describe some behavior rather than create a fully named function for every piece of logic.

var actions = [
  Action.mk({label="Open", fn=open_door,
    source_can_execute=func(): return state == door_state.CLOSED}),
  Action.mk({label="Close", fn=close_door,
    source_can_execute=func(): return state == door_state.OPEN}),
  Action.mk({label="Unlock", fn=unlock_door,
    source_can_execute=func(): return state == door_state.LOCKED,
    actor_can_execute=func(actor):
    if actor.has_method("can_unlock_door"):
      return actor.can_unlock_door()
    }),
  Action.mk({label="Jostle", fn=jostle_door,
    source_can_execute=func(): return state == door_state.LOCKED,
    actor_can_execute=func(actor):
    if actor.has_method("can_unlock_door"):
      return not actor.can_unlock_door()
    return true
    })
  ]
[–] russmatney 8 points 1 year ago

I was curious about writing one-off scripts in Godot, so here's a tiny example for doing that in Godot 4.

./hello-godot.gd (also in my dotfiles):

#!/usr/bin/env -S godot --headless --script
extends SceneTree

func _init():
  print("hello godot!")
  print("args", OS.get_cmdline_args())
  quit()

Note the first line, which invokes godot and includes the --headless and --script arguments. You can read more about these args via godot --help.

Once the script has executable permissions (typically via chmod +x hello-godot.gd), you can run it in a shell like:

./hello-godot.gd "some arg" "some other arg" 123

That should output something like:

hello godot!
args["-s", "/home/russ/.local/bin/hello-godot.gd", "some arg", "some other arg", "123"]

This kind of thing can be useful for running tests via something like GUT (which is where I started digging into this), or exporting games in scripts/CI.

Which makes me think I should look into how some of those godot CI docker images run - maybe there are other arguments/features/best-practices there to build on.

[–] russmatney 10 points 1 year ago (2 children)

Anonymous functions and callables have been a really nice step-up for GDscript.

Lots of formerly annoying things are much cleaner now, like getting away from connecting signals with strings, and using map and filter, etc.

view more: ‹ prev next ›