Search code examples
buttongodotgdscript

Basic Calculator: How to script a function for deleting digits one by one? (GDScript)


How am I supposed to code a button that deletes digits in a basic calculator one by one?

Heres my script so far: https://pastebin.com/xLj2iCHU

func _on_BDeleteDigit_pressed():
    var numberAdded
    var numberBefore = $NumberStuffs.get_text()
    numberAdded = numberAdded.length() - 1
    numberAdded[numberBefore]=("")
    $Outputlabel.set_text(str(numberAdded))

This is also the error when the delete button is pressed: https://i.sstatic.net/8OJTI.png


Solution

  • Explaining the error

    I'll start with the error. It says

    Nonexistent function 'length' in base 'Nil'

    The error is telling you that you are trying call a function called length on something that is Nil, but Nil does not have a function called length.

    The problem is that you are trying to call something on Nil. What are you trying to call? length. So where is that? That is here:

    numberAdded = numberAdded.length() - 1
    

    So, numberAdded is Nil. The next question is why. So, let us see where numberAdded got its value…

    So you declare it here:

    var numberAdded
    

    Then we have this line that has nothing to do with it:

    var numberBefore = $NumberStuffs.get_text()
    

    And then the line with the error:

    numberAdded = numberAdded.length() - 1
    

    What did you expect numberAdded.length() to be, if numberAdded does not have a value? Of course that is an error.


    By the way, the code suggests that you expected numberAdded to be, well, a number. Which makes me wonder what you expected from this:

    numberAdded[numberBefore]=("")
    

    Yes you can do the inverted indexing thing in C or C++, because it is just adding pointers. But not GDScript, you don't.


    I suspect that using types would help you. For example declare numberAdded to be an integer like this: var numberAdded:int. Yes, GDScript has types, use them.


    How to delete the last character

    Let me take as reference how you add a digit:

    func _on_Button1_pressed():
        var currentNumber = $NumberStuffs.get_text()
        
        currentNumber = currentNumber + "1"
        $NumberStuffs.set_text(currentNumber)
    

    Alright, we are storing a string on the text property of the node $NumberStuffs. And we add a digit by concatenating to it.

    The above code should be is equivalent to this:

    func _on_Button1_pressed():
        $NumberStuffs.text += "1"
    

    If get_text and set_text are custom functions in GDScript, you may want to look into setget.


    Thus, the question is how to remove the last character of a string. There are several ways!

    One way would be using erase:

    func _on_BDeleteDigit_pressed():
        var text := $NumberStuffs.text
        var length := text.length()
        text.erase(length - 1, 1)
        $NumberStuffs.text = text
    

    Note that erase is modifying the string, but the string is a copy we got from reading $NumberStuffs.text (or calling $NumberStuffs.get_text), which is why we need to set to $NumberStuffs.text (or call $NumberStuffs.set_text) anyway.

    Or if you prefer:

    func _on_BDeleteDigit_pressed():
        var text := $NumberStuffs.get_text()
        var length := text.length()
        text.erase(length - 1, 1)
        $NumberStuffs.set_text(text)
    

    Alternatively, we might get a string which is like the one we have except lacking the last character. We can do that with left (which lets you get a portion of the string from the left, there is also a right function that does the same but from the right):

    func _on_BDeleteDigit_pressed():
        var length := $NumberStuffs.text.length()
        $NumberStuffs.text = $NumberStuffs.text.left(length - 1)
    

    Or, if you prefer:

    func _on_BDeleteDigit_pressed():
        var text := $NumberStuffs.get_text()
        var length := text.length()
        $NumberStuffs.set_text(text.left(length - 1))
    

    In general we can get a string containing a portion of another string with substr:

    func _on_BDeleteDigit_pressed():
        var length := $NumberStuffs.text.length()
        $NumberStuffs.text = $NumberStuffs.text.substr(0, length - 1)
    

    Or, if you prefer:

    func _on_BDeleteDigit_pressed():
        var text := $NumberStuffs.get_text()
        var length := text.length()
        $NumberStuffs.set_text(text.substr(0, length - 1))
    

    And yes, you can modify the string by indexing it, which I believe is what you were trying. For example:

    $NumberStuffs.text[-1] = "a"
    

    Which is equivalent to:

    var length := $NumberStuffs.text.length()
    $NumberStuffs.text[length - 1] = "a"
    

    Which is equivalent to:

    var text := $NumberStuffs.get_text()
    var length := text.length()
    text[length - 1] = "a"
    $NumberStuffs.set_text(text)
    

    Will replace the last character with an "a". And yes, you could remove by doing that. That would be:

    $NumberStuffs.text[-1] = ""
    

    Which is equivalent to:

    var length := $NumberStuffs.text.length()
    $NumberStuffs.text[length - 1] = ""
    

    Which is equivalent to:

    var text := $NumberStuffs.get_text()
    var length := text.length()
    text[length - 1] = ""
    $NumberStuffs.set_text(text)
    

    By the way, I have been using := when declaring the variables. That declares the variable of the type of whatever I'm using to initialize them.