Save-Point
Learning GDScript - Printable Version

+- Save-Point (https://www.save-point.org)
+-- Forum: Games Development (https://www.save-point.org/forum-4.html)
+--- Forum: Tutorials (https://www.save-point.org/forum-19.html)
+--- Thread: Learning GDScript (/thread-8228.html)



Learning GDScript - MetalRenard - 02-09-2021

Hey! Going to use this as a place to keep track of my own progress learning GDScript (Godot). Some parts will be relevant to other scripting languages, thought it would be worth sharing and explaining it helps me learn, too.

Followed a few tutorials, watched and read a bunch of stuff. Today, trying to use a 'dictionary' to define level up stats and then call them from another script. First step: Learnt to create a dictionary that holds the data! Kind of a script database of sorts.


Code:
extends Node


var stats = {
    "hull": [6, 12, 18, 24],
    "shield": [10, 20, 30, 40],
    "shot": [8, 16, 24, 32],
    "speed": [150, 200, 250, 300],
}



RE: Learning GDScript - MetalRenard - 02-09-2021

Part 2!
Goal: Be able to change the character's rotation speed (they look at the mouse).
So the above script runs in the background and is used to store data. Now I want to access this data from another script called 'Player'.
It took me a little while to work this out because it requires specific formatting to tell the engine where to look.
Here's the updated GlobalPlayer script.

Code:
extends Node


var hp_lvl
var shield_lvl
var shot_lvl
var speed_lvl
var rot_lvl


var stats = {
    "hull": [6, 12, 18, 24],
    "shield": [10, 20, 30, 40],
    "shot": [8, 16, 24, 32],
    "speed": [150, 200, 250, 300],
    "rotation": [0.05, 0.07, 0.9, 0.11],
}


I'm checking to see what the rotation speed of the player is in position 0 of the dictionary's "rotation" array.
Code:
var rot_speed = GlobalPlayer.stats.rotation[0]
The thing that took me ages was learning how to look at data stored in an array which is stored in a dictionary! Finally arrived to this conclusion, very happy.

Next step: Use the _lvl variables to store the player's level and use that to compare with the position in the dictionary.

To accomplish this, I added these varibles above the Player script and compared them to the GlobalPlayer variables.
Code:
var cur_rot_lvl = GlobalPlayer.rot_lvl
And then updated the formula to compare the player's level to the position in the array.
Code:
var rot_speed = GlobalPlayer.stats.rotation[cur_rot_lvl]

It works!


RE: Learning GDScript - kyonides - 02-10-2021

My suggestion would be to tell newbies about GDScript's variable scopes or else they might think they might work everywhere...
Happy with a sweat Believe me, it has happened to many fellas a few times while working on or adapting or editing Ruby scripts.


RE: Learning GDScript - MetalRenard - 02-10-2021

I'll be honest, I don't really understand the term 'scope' in this setting. I am close to an absolute beginner (prior knowledge only being basic JS and decent html5).

Part 3!
Today's challenge was to create an object that, upon contact, "upgrades" your stats, for now, just the speed stat. You can connect a function in one script to a function in another, in Godot it's done with a 'signal'. You emit it when the two objects overlap and then tell the script to 'observe' it.
The game engine allows for the first part of this to be done automatically.
On the receiving end, you use this code:
Code:
func _on_SpeedUpgrade_body_entered(body):
        GlobalPlayer.speed_lvl += 1
        cur_speed_lvl = GlobalPlayer.speed_lvl
        max_speed = GlobalPlayer.stats.speed[cur_speed_lvl]

This updates the player's level on the GlobalPlayer script which holds the statistics dictionary, updates the current script's speed level so it can find that data in the GlobalPlayer script then updates max_speed accordingly. Max speed is used in another script to define the ships maximum speed like so:
Code:
func _physics_process(delta):
    #find out where the mouse is and create the vector from the ship towards it
    dir = (get_global_mouse_position() - position).normalized()    

    #define how the ship moves, max speed and also interpolate its slowdown speed based on the friction variable "fric".
    velocity.x = clamp(velocity.x, -max_speed, max_speed)
    velocity.y = clamp(velocity.y, -max_speed, max_speed)
    move_and_slide(velocity)

    velocity.x = lerp(velocity.x, 0, fric)
    velocity.y = lerp(velocity.y, 0, fric)
    
    #check for input and move if the up button is pressed (defined in my keymap as "w").
    if Input.is_action_pressed("ui_up"):
        velocity += dir * accel



RE: Learning GDScript - MetalRenard - 02-14-2021

Features so far:
Player ship can fly towards the mouse.
You can pick up an item which levels up your max speed, acceleration and rotation speed.

Todays challenge: Make a boost function and make upgrades for it.
New GlobalPlayer additions:
Code:
extends Node

# defines all player stats

var hp_lvl = 1
var shield_lvl = 1
var shot_lvl = 1
var speed_lvl = 1
var accel_lvl = 1
var rot_lvl = 1
var boost_lvl = 1

var money = 0


var stats = {
    "hull": [0, 6, 9, 12, 16],
    "shield": [0, 5, 7, 9, 12],
    "shot": [0, 6, 9, 12, 16],
    "speed": [0, 100, 140, 180, 220],
    "accel": [0, 3, 4, 6, 8],
    "rotation": [0, 0.02, 0.027, 0.034, 0.041],
    "boost": [0, 3.3, 1.4, 1.5, 1.6],
}
(Hint: later there will be a currency system, of course).

Before today, the game checked for input from the 'W' key, (if Input.is_action_pressed("ui_up"): ) and when it was pressed, you accelerated to your max speed (at level 1, that's 100 pixels per second, level 2 is 140).

Now it does that but also checks for the shift key "ui_boost". When pressed, it multiplies your current boost level's dictionary data by your current speed level's dictionary data. All the variables are set before this part.
Code:
if Input.is_action_pressed("ui_boost"):
            max_speed = GlobalPlayer.stats.speed[cur_speed_lvl] * boost

            velocity.x = clamp(velocity.x, -max_speed, max_speed)
            velocity.y = clamp(velocity.y, -max_speed, max_speed)

            velocity += dir * accel * boost
            $Camera2D.zoom.x = lerp($Camera2D.zoom.x, 1, 0.03)
            $Camera2D.zoom.y = lerp($Camera2D.zoom.y, 1, 0.03)

Thanks to the lerp (linear interpolation), the camera also zooms out smoothly while boosting. When you release the key, it zooms back in smoothly. Really helps sell the feeling of speed.

Did I say that I also made the ship have particle effects? Well it swaps between two versions, normal or one with more particles when boosting.

Code:
    if Input.is_action_just_pressed("ui_boost"):
        $Sprite/ShipTrail.emitting = false
        $Sprite/ShipTrailBoost.emitting = true


    if Input.is_action_just_released("ui_boost"):
        $Sprite/ShipTrail.emitting = true
        $Sprite/ShipTrailBoost.emitting = false

    # reset camera
    $Camera2D.zoom.x = lerp($Camera2D.zoom.x, 0.8, 0.01)
    $Camera2D.zoom.y = lerp($Camera2D.zoom.y, 0.8, 0.01)

So now I have a functional boost system that I can upgrade!


RE: Learning GDScript - MetalRenard - 02-19-2021

Today's challenge! GUI!
So I have set up the GUI visuals in the editor. You can see the attached file for what it looks like.

[Image: attachment.php?aid=1499]

.png   GUI.png (Size: 53.91 KB / Downloads: 16)

Now I have to make it update when the player does stuff.

I have variables for current HP (Hull Points), SP (Shield Points) and fuel for the booster.

First, I set up the bars and their labels to match the player's current stat levels. This part happens inside my 'interface' node which is a 'control' node.
Code:
extends Control


var hp
var sp
var fuel


# get and set stats from GlobalPlayer and bar ranges, reset player
func _ready():
    hp = GlobalPlayer.stats.hull[GlobalPlayer.hp_lvl]
    GlobalPlayer.cur_hp = hp
    sp = GlobalPlayer.stats.shield[GlobalPlayer.shield_lvl]
    GlobalPlayer.cur_sp = sp
    fuel = GlobalPlayer.stats.fuel[GlobalPlayer.fuel_lvl]
    GlobalPlayer.cur_fuel = fuel

    $MCon/VBCon/HBoxContainer/HBar/HBarCon/HullBar.max_value = hp
    $MCon/VBCon/SBar/SBarCon/ShieldBar.max_value = sp
    $MCon/VBCon/BBar/BBarContainer/BoostBar.max_value = fuel

I'm not a fan of using absolute paths (I've read it's very easy to break, for example if you change something in one of the folders) so in the future I'm going to look for a way to make it more flexible. For now though, it works. You can see in the picture, it writes how many HP/SP the player has (6 and 6 for now), and the bars are full.

Code:
# Change value of the bars and labels based on current stats
func _process(delta):
    # label changes
    $MCon/VBCon/HBoxContainer/HBar/Counter/MCon/Label.text = str(GlobalPlayer.cur_hp)
    $MCon/VBCon/SBar/Counter/MCon/Label.text = str(GlobalPlayer.cur_sp)
    $MCon/VBCon/HBoxContainer/MoneyUI/Counter/Split/MoneyLabel.text = str(GlobalPlayer.money)
    
    # bar progression
    $MCon/VBCon/SBar/SBarCon/ShieldBar.value = GlobalPlayer.cur_sp
    $MCon/VBCon/HBoxContainer/HBar/HBarCon/HullBar.value = GlobalPlayer.cur_hp
    

So, this part checks the player's CURRENT HP and SP, then sets the VALUE of the bars and their labels. It also updates the player's money display. Sorry, it's not very well aligned right now but it will be in the final version.

I haven't set this up for the fuel yet, but it works for the HP and SP! Check the 2nd attached image for the visuals.

[Image: attachment.php?aid=1500]

.png   GUI2.png (Size: 51.65 KB / Downloads: 17)

Oh and I made it check to see if the player has shield and if so, to take away from that before their HP.

Having so much fun doing this!


RE: Learning GDScript - MetalRenard - 02-23-2021

Today's challenge: Learn how to make menus
Task: Make a simple shop menu for the player to spend their cash on upgrades. Make the buttons clickable and, when clicked, check if they have enough cash then level up the appropriate stat.

[Image: attachment.php?aid=1501]

First, I set up the buttons, their tags and all that. Here's the code I wrote to make 2 of them function:

Code:
extends Control


var cost = 100
var cost_factor = 0.8
var bcost = 0.0
var tcost = 0.0

func _process(delta):
    # Set level display
    $MCon/VBCon/ThrustCon/TLevel.text = str(GlobalPlayer.speed_lvl)
    $MCon/VBCon/BoostCon/BLevel.text = str(GlobalPlayer.boost_lvl)
    $MCon/VBCon/MoneyCon/MALabel.text = str(GlobalPlayer.money)
    # Set price display
    bcost = GlobalPlayer.boost_lvl * cost * cost_factor
    tcost = GlobalPlayer.speed_lvl * cost * cost_factor
    $MCon/VBCon/ThrustCon/TCost.text = " Upgrade cost: " + str(tcost)
    $MCon/VBCon/BoostCon/BCost.text = " Upgrade cost: " + str(bcost)


# testing purposes only, to add 100 money to player's bank
func _on_DMoney_pressed():
    GlobalPlayer.money += 100


# Booster, check cost, if capable, upgrade level and take away money
func _on_BButton_pressed():
    if GlobalPlayer.money > bcost:
        GlobalPlayer.boost_lvl += 1
        GlobalPlayer.money -= bcost


#thruster
func _on_TButton_pressed():
    if GlobalPlayer.money > tcost:
        GlobalPlayer.speed_lvl += 1
        GlobalPlayer.accel_lvl += 1
        GlobalPlayer.money -= tcost



RE: Learning GDScript - MetalRenard - 03-03-2021

Had to take a break for 2 weeks, working on a music project. Plus I have sleep issues so I can't do anything stimulating after 9pm or I won't sleep until 3am. I finish work at 9:30pm 3 times a week so... Not much room for Godot at the moment!

Having said that, I have been thinking about my next step - enemy AI.
I'm planning on giving each enemy 2 collision areas, 1 for detection and 1 for receiving damage. The area will extend in front of the enemy more than behind. I also want them to follow you using pathfinding so... Hope that's not too difficult to do.