A more efficient approach than multiple if elif elif elif elif elif etc?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Robster
:warning: Old Version Published before Godot 3 was released.

Hi all,

Have a look at this complete code. I want to select a random powerup to display. I have an array or powerup names, one is randomly chosen. Then, based on the random choice, the system needs to draw the sprite.

I look at all the if elif statements and wonder if there’s not a more efficient method?

extends Node2D

onready var labeloutput = get_node("RichTextLabel")
var powerUpSprite

func _ready():
	spawnRandomPowerup()


func spawnRandomPowerup():

	#list of power ups
	var powerUpArray = ["speedUpBat","slowDownBat","speedUpOtherBat","slowDownOtherBat","speedUpBall","slowDownBall","makeBatLarger","makeBatSmaller","freezeBat","doublePoints","randomObstacle","powerBall"]

	#get like... totally random dude	
	randomize()

	#pick randomly from the list of power ups
	var theChosenOne = powerUpArray[randi()%powerUpArray.size()]
	
	#print all the available choices
	for choice in powerUpArray:
		labeloutput.add_text(choice)
		labeloutput.add_text("\n")

	#print the chosen one
	labeloutput.add_text("\n")
	labeloutput.add_text(theChosenOne)
	
	#render the chose powerup on screen
	renderPowerup(theChosenOne)
	
	
func renderPowerup(powerUp):
	
	powerUpSprite = Sprite.new()	#create a new sprite
	add_child(powerUpSprite)		#add it as a child 
	
	var powerUpImage 				#the texture of the powerup will live here
	
	#I wonder, there must be a more efficient way to do this?
	if powerUp == "speedUpBat":
		powerUpImage = load("res://textures/speedUpBat.png")
		powerUpSprite.set_texture(powerUpImage)
		powerUpSprite.set_pos(Vector2(100,100))
	elif powerUp == "slowDownBat":
		powerUpImage = load("res://textures/slowDownBat.png")
		powerUpSprite.set_texture(powerUpImage)
		powerUpSprite.set_pos(Vector2(100,100))
	elif powerUp == "speedUpOtherBat":
		powerUpImage = load("res://textures/speedUpOtherBat.png")
		powerUpSprite.set_texture(powerUpImage)
		powerUpSprite.set_pos(Vector2(100,100))
	elif powerUp == "slowDownOtherBat":
		powerUpImage = load("res://textures/slowDownOtherBat.png")
		powerUpSprite.set_texture(powerUpImage)
		powerUpSprite.set_pos(Vector2(100,100))
	elif powerUp == "speedUpBall":
		powerUpImage = load("res://textures/speedUpBall.png")
		powerUpSprite.set_texture(powerUpImage)
		powerUpSprite.set_pos(Vector2(100,100))
	elif powerUp == "slowDownBall":
		powerUpImage = load("res://textures/slowDownBall.png")
		powerUpSprite.set_texture(powerUpImage)
		powerUpSprite.set_pos(Vector2(100,100))
	elif powerUp == "makeBatLarger":
		powerUpImage = load("res://textures/makeBatLarger.png")
		powerUpSprite.set_texture(powerUpImage)
		powerUpSprite.set_pos(Vector2(100,100))
	elif powerUp == "makeBatSmaller":
		powerUpImage = load("res://textures/makeBatSmaller.png")
		powerUpSprite.set_texture(powerUpImage)
		powerUpSprite.set_pos(Vector2(100,100))
	elif powerUp == "freezeBat":
		powerUpImage = load("res://textures/freezeBat.png")
		powerUpSprite.set_texture(powerUpImage)
		powerUpSprite.set_pos(Vector2(100,100))
	elif powerUp == "doublePoints":
		powerUpImage = load("res://textures/doublePoints.png")
		powerUpSprite.set_texture(powerUpImage)
		powerUpSprite.set_pos(Vector2(100,100))
	elif powerUp == "randomObstacle":
		powerUpImage = load("res://textures/randomObstacle.png")
		powerUpSprite.set_texture(powerUpImage)
		powerUpSprite.set_pos(Vector2(100,100))
	elif powerUp == "powerBall":
		powerUpImage = load("res://textures/powerBall.png")
		powerUpSprite.set_texture(powerUpImage)
		powerUpSprite.set_pos(Vector2(100,100))


func _on_Button_pressed():
	labeloutput.clear()
	powerUpSprite.hide()
	spawnRandomPowerup()

if you keep the image file names the same as the powerUp names you could skip the if statement and do something like the following.

powerUpImage = load("res://textures/"+powerUp+".png")
powerUpSprite.set_texture(powerUpImage)
powerUpSprite.set_pos(Vector2(100,100))

D | 2017-03-24 22:56

that… is so simple and so obvious (slaps face).
Thank you

Robster | 2017-03-24 23:11

It would be nice to turn the D comment into an answer :wink:

DriNeo | 2017-03-26 09:32

although that solution works for that piece of code, it won’t do much when you actually need to change something in the game besides the sprites. I assume those powerups will change variable values or something like that, so you will still need to check each case.

One solution for this could be to make a dictionary, each entry would be one powerup and contain the imagefile, etc, plus a callback for a function to activate that powerup.

Nuno Donato | 2017-03-27 13:42

This has been playing on my mind as I’ve been considering the exact issue. I haven’t reached the effect of each power up yet but that’s the perfect solution (dictionary). I guess I’ll still need a huge if ELIF statement then. I can’t seem to find anything about case statements in Godot being implemented.

Robster | 2017-03-27 21:47

no, with the dictinary you dont need any if statements, you just access the dictionary entry directly like

var selected_powerup = powerups[my_selection]
image = selected_powerup.image
hpchange += selected_powerup.hp

Nuno Donato | 2017-03-27 21:50

Thank you for this. I have to say I’m quite new at programming in general so please excuse my ignorance. I’ve been having a go at this but I can’t seem to find out how to add all that data into a single dictionary to pull it out again as you’ve shown.

Imagine I have a dictionary called powerups.

I can do this kind of thing:

var powerUpDict = {
	powerUpName = "speedUpBat",
	batSpeedChange = 10,
	ballSpeedChange = 0,
	freezeBat = false,
	doublePoints = false,
	spawnRandomPowerup = false
}

But how can I add ANOTHER powerup to that dictionary so I can have them all within it? My understanding is if I do something like:

var powerUpDict = {
	powerUpName = "speedUpBat",
	batSpeedChange = 10,
	ballSpeedChange = 0,
	freezeBat = false,
	doublePoints = false,
	spawnRandomPowerup = false,
	powerUpName = "slowDownBat",
	batSpeedChange = -10,
	ballSpeedChange = 0,
	freezeBat = false,
	doublePoints = false,
	spawnRandomPowerup = false
}

… then I will have overwritten the duplicate named entries. Is that correct? I wonder if someone wouldn’t mind pointing me in the right direction.

Anything to help, thank you :slight_smile:

Robster | 2017-03-31 03:21

You can’t duplicate entries in a dictionary, what you need is a dictionary(powerups) of dictionaries(attributes of each powerup)

so, like

var powerUpDict = {
  "speedUpBat" : {
    batSpeedChange = 10,
    ballSpeedChange = 0,
    freezeBat = false,
    doublePoints = false,
    spawnRandomPowerup = false,
  },
  "slowDownBat" : {
    batSpeedChange = -10,
    ballSpeedChange = 0,
    freezeBat = false,
    doublePoints = false,
    spawnRandomPowerup = false
  }

}

Nuno Donato | 2017-03-31 06:48