Activating a function from one script in another

Godot-4.3

Heya, I’m new here and am trying to learn Godot from scratch. I’ve watched and followed a couple of videos on GDScript but am still trying to wrap my head around some things. I’ve made a very rough and dry Dice Rolling script attached to a scene that I want to use across multiple other scenes of a game I am making that will use the dice for different mechanics. But that is where my first issue has come about. In a puzzle scene, I’m trying to reference a function in the dice script where the number is decided and use that number to determine whether the puzzle is solved or not. The rolling works fine for testing at least but I cant seem to get the puzzle script to receive that number. Here are the 2 scripts written so far:
Dice

extends Node2D

#Sets the number of dice rolled
@export var dice_amount: int = 1
@onready var dice_result: AnimatedSprite2D = $DiceResult

#Rolls the set number of dice
func _on_button_pressed() -> void:
	for n in dice_amount:
		var roll = randi_range(1, 6)
		print(roll) #For Dubbuging
		#Changes dice sprite to match number
		if roll == 1:
			$DiceResult.play("roll1")
		elif roll == 2:
			$DiceResult.play("roll2")
		elif roll == 3:
			$DiceResult.play("roll3")
		elif roll == 4:
			$DiceResult.play("roll4")
		elif roll == 5:
			$DiceResult.play("roll5")
		elif roll == 6:
			$DiceResult.play("Roll6")

Puzzle

extends Node2D

#Sets puzzle difficulty
@export var difficulty: int = 1
@onready var difficulty_label: Label = $DifficultyLabel
@onready var outcome: Label = $Outcome

#Sets the text to match the difficulty
func _ready() -> void:
	if difficulty == 1:
		difficulty_label.text = "You need " + str(difficulty) + " pass to succeed"
	else:
		difficulty_label.text = "You need " + str(difficulty) + " passes to succeed"

#Determines if player passed or failed
func _attempt():
	var Pass = false 
	if $Dice.roll() >= 5:
		Pass = true
		outcome.text = "You have passed"
	else:
		Pass = false
		outcome.text = "You have failed"

Welcome to the godot community, please consider using code banners (``` or click ‘Preformatted Text’ option when writing) to make code reading more pleasant.
Dice

extends Node2D

#Sets the number of dice rolled
@export var dice_amount: int = 1
@onready var dice_result: AnimatedSprite2D = $DiceResult

#Rolls the set number of dice
func _on_button_pressed() → void:
for n in dice_amount:
  var roll = randi_range(1, 6)
  print(roll) #For Dubbuging
  #Changes dice sprite to match number
  if roll == 1:
  $DiceResult.play(“roll1”)
  elif roll == 2:
  $DiceResult.play(“roll2”)
  elif roll == 3:
  $DiceResult.play(“roll3”)
  elif roll == 4:
  $DiceResult.play(“roll4”)
  elif roll == 5:
  $DiceResult.play(“roll5”)
  elif roll == 6:
  $DiceResult.play(“Roll6”)

Puzzle

extends Node2D

#Sets puzzle difficulty
@export var difficulty: int = 1
@onready var difficulty_label: Label = $DifficultyLabel
@onready var outcome: Label = $Outcome

#Sets the text to match the difficulty
func _ready() → void:
  if difficulty == 1:
    difficulty_label.text = “You need " + str(difficulty) + " pass to succeed”
    else:
    difficulty_label.text = “You need " + str(difficulty) + " passes to succeed”

#Determines if player passed or failed
func _attempt():
var Pass = false
  if $Dice.roll() >= 5:
  Pass = true
  outcome.text = “You have passed”
  else:
  Pass = false
  outcome.text = “You have failed”

Without re-writing all that code for you, I think you need to modify your approach. If your dice scene is named, say, Dice, you should have a function call roll_dice which receives the number of dice to roll. Something like this:

@onready var dice_node: Node2D = $Dice

# then when needed call the dice roll function (in this case for 4 dice):
var dice_score = dice_node.roll_dice(4)

The dice_node, your $Dice scene, should roll the dice and then return the total scored:

func roll_dice(4) -> int:
    # roll 4 dice and return the total scored
    return total

Your puzzle that is using the dice should decide if this is a winner or not or what to do with the returned dice total.

Alternatively you could have your dice_roll as an autoloaded script in globals to do the same thing. Or if you want animations and for the dice scene to do something visually, you could have your dice scene not return a value immediately, but emit a signal like ‘total_rolled’ signal.

signal total_dice_rolled

# when animations or whatever finished
total_dice_rolled.emit(dice_score)

Your puzzle script, on asking the dice scene to roll the dice listens for the signal before proceeding with whatever you want to do with the dice roll.

Perhaps something like this:

func _ready() -> void:
    dice_node.connect("dice_rolled", _on_dice_rolled)

func _on_dice_rolled(result: int) -> void:
    var passed = result >= 5
    outcome.text = "You have " + ("passed" if passed else "failed")

Your dice scene can then be added to any puzzle scene.

Hope that helps in some way.

1 Like

Sorry about that. Ill definitely keep that in mind for next time

I really appreciate your help. This is super informative and Ill definitely give some of these options a go. The only thing I see that could potentially be an issue is using the total dice score instead of using each individual score separately.
The goal I plan on aiming for is using the dice for different scenes and each scene will require a different number of dice. With the puzzle, say the difficulty is 3, then it will need 3 passes to get through the puzzle. If I then rolled 3 dice, wont the signal sending the total score cause only 1 pass instead of making the 3 checks?

1 Like

The beauty of signals is that you can send virtually any information you want with it. For instance suppose you call:

# roll 3 dice
dice_node.roll_dice(3)

You could return all the individual dice rolls in an array, as well as the total score like this:

signal dice_rolled(individual_results: Array, total: int)

# Then emit a results array and a total
emit_signal("dice_rolled", results, total)

You could even cater for different sided dice, something like this:

func roll_dice(num_dice: int, sides: int) -> void:
    var results = []
    var total = 0
    for i in range(num_dice):
        var roll = randi() % sides + 1
        results.append(roll)
        total += roll
    
    emit_signal("dice_rolled", results, total)

Your animations might get quite complicated, but perhaps in your puzzles you could let the user decide to use 3 * 20 sided dice or 10 * 6 sided dice.

PS If you meant you might want them to roll 3 dice three times, you could just call the roll_dice function three times in the puzzle. Or you could send the number of rolls and number of dice in the signal and get all the results back in the signal.

Or you might choose to send the passing mark to the dice roll and have it just return true or false depending on the result.

There are so many options here that it really depends on your requirements.

My advice would be to keep the puzzle elements separate from the actual dice rolling scene, all that should do is concentrate on rolling dice and returning the results. Your puzzle script should deal with the incoming results giving you as much flexibility in your puzzle design as possible.

You can then, whenever you want, improve or change the dice rolling scene without having to re-write any puzzles. This separation of concerns will mean you dice roll scene can be used wherever you need to use dice, independent of the puzzle structure.

Hope that helps.

1 Like

This helps alot. I think before I continue with my planned games, I might do some programming exercises to get used to some more complicated functions

1 Like