This commit is contained in:
Torsten 2024-12-22 14:02:54 +01:00
parent 1e22d28754
commit 6b926d3afe
126 changed files with 128562 additions and 104297 deletions

View file

@ -0,0 +1,3 @@
#godot-editor
Right upper corner in Menu selector window
![[Pasted image 20240204164006.png]]

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,16 @@
Sprite2D node
F => focus view on object
To get type information in code auto-completion, go to _Editor -> Editor Settings_, and in the _Text Editor -> Completion_ section, turn on the checkbox named _Add Type Hints_. You can also search for "type hints" in the search bar at the top of the window to find it quickly.
`Vector2` values have a function named `angle()` that calculates and returns the vector's angle in radians. If we assign that to the ship's rotation, it will turn to face its move direction.
```gdscript
var velocity = Vector2(480, -480)
func _process(delta: float) -> void:
position += velocity * delta
rotation = velocity.angle()
````

View file

@ -0,0 +1,21 @@
https://school.gdquest.com/courses/learn_2d_gamedev_godot_4/to_space_and_beyond/taking_control
```gdscript
extends Sprite2D
var direction := Vector2(0,0)
var max_speed := 600.0
var velocity = Vector2(0, 0)
func _process(delta: float) -> void:
direction.x = Input.get_axis("move_left", "move_right")
direction.y = Input.get_axis("move_up", "move_down")
if direction.length() > 1.0:
direction = direction.normalized()
velocity = direction * max_speed
position += velocity * delta
if direction.length() > 0.0:
rotation = velocity.angle()
```

View file

@ -0,0 +1,19 @@
```python
func _process(delta: float) -> void:
var direction := Vector2(0, 0)
direction.x = Input.get_axis("move_left", "move_right")
direction.y = Input.get_axis("move_up", "move_down")
if direction.length() > 1.0:
direction = direction.normalized()
if Input.is_action_just_pressed("boost"):
max_speed = boost_speed
get_node("Timer").start()
velocity = direction * max_speed
position += velocity * delta
if direction.length() > 0.0:
rotation = velocity.angle()
```

View file

@ -0,0 +1,35 @@
```python
extends Sprite2D
var direction := Vector2(0,0)
var max_speed := 300.0
var velocity = Vector2(0, 0)
var boost_speed := 1500.0
var normal_speed := 300.0
var desired_velocity := Vector2()
var steering_factor := 10
func _process(delta: float) -> void:
var direction := Vector2(0, 0)
direction.x = Input.get_axis("move_left", "move_right")
direction.y = Input.get_axis("move_up", "move_down")
if direction.length() > 1.0:
direction = direction.normalized()
if Input.is_action_just_pressed("boost"):
max_speed = boost_speed
get_node("Timer").start()
desired_velocity = max_speed * direction
var steering = desired_velocity - velocity
velocity += steering * steering_factor * delta
position += velocity * delta
if direction.length() > 0.0:
rotation = velocity.angle()
func _on_timer_timeout() -> void:
max_speed = normal_speed
```

View file

@ -0,0 +1,9 @@
```python
extends Area2D
func _ready() -> void:
area_entered.connect(_on_area_entered)
func _on_area_entered(area_that_entred: Area2D) -> void:
queue_free()
```

View file

@ -0,0 +1,43 @@
````python
extends Area2D
var max_speed := 1200.0
var velocity := Vector2(0, 0)
var steering_factor := 3.0
var health := 10
func _ready() -> void:
area_entered.connect(_on_area_entered)
# This call updates the health bar to match the health variable when the
# game starts.
set_health(health)
func _process(delta: float) -> void:
var direction := Vector2(0, 0)
direction.x = Input.get_axis("move_left", "move_right")
direction.y = Input.get_axis("move_up", "move_down")
if direction.length() > 1.0:
direction = direction.normalized()
var desired_velocity := max_speed * direction
var steering := desired_velocity - velocity
velocity += steering * steering_factor * delta
position += velocity * delta
if velocity.length() > 0.0:
get_node("Sprite2D").rotation = velocity.angle()
func set_health(new_health: int) -> void:
health = new_health
get_node("UI/HealthBar").value = health
func _on_area_entered(area_that_entered: Area2D) -> void:
set_health(health + 10)
```

View file

@ -0,0 +1 @@
https://school.gdquest.com/courses/learn_2d_gamedev_godot_4/telling_a_story/overview

View file

@ -0,0 +1,61 @@
```python
var inventory: Array[Dictionary] = [
{
"name": "sword",
"amount": 1,
},
{
"name": "potion",
"amount": 5,
},
{
"name": "potion",
"amount": 3,
},
{
"name": "coins",
"amount": 65,
},
]
```
[]
```python
var expressions := {
"happy" : preload("res://assets/emotion_happy.png"),
"regular" : preload("res://assets/emotion_regular.png"),
"sad" : preload("res://assets/emotion_sad.png"),
}
var dialogue_items: Array[Dictionary]= [
{
"expression": expressions["regular"],
"text": "I'm learning about Arrays..."
},
{
"expression": expressions["sad"],
"text": "... and it is a little bit complicated."
},
{
"expression": expressions["happy"],
"text": "Let's see if I got it right: an array is a list of values!",
},
{
"expression": expressions["regular"],
"text": "Did I get it right? Did I?",
},
{
"expression": expressions["happy"],
"text": "Hehe! Bye bye~!",
},
]
func show_text() -> void:
var current_item := dialogue_items[current_item_index]
rich_text_label.text = current_item["text"]
expression.texture = current_item["expression"]
```

View file

@ -0,0 +1 @@
A UI nodes' opacity is modified via the modulate property which controls the tinting of the node.

View file

@ -0,0 +1,28 @@
Tweens are created using a nodes' create_tween() method
```python
func slide_in() -> void:
var tween := create_tween()
tween.set_trans(Tween.TRANS_QUART)
tween.set_ease(Tween.EASE_OUT)
````
Running tweens in parallel via parallel method.
Modulate controls tinting, subproperty a is for alpha, meaning a target visibilty of 100%
```python
tween.parallel().tween_property(body, "modulate:a", 1.0, 0.2)
```
Full tween method:
```python
func slide_in() -> void:
var tween = create_tween()
tween.set_trans(Tween.TRANS_QUART)
tween.set_ease(Tween.EASE_OUT)
body.position.x = 200.0
tween.tween_property(body, "position:x", 0.0, 1.0) # Move x from 200 to 0
body.modulate.a = 0.0 # UI node visibility = 0
tween.parallel().tween_property(body, "modulate:a", 1.0, 1.0) # Animate visibility towards 100% (a=alpha)
```

View file

@ -0,0 +1,127 @@
Nothing new here, just replacement of TextureRect images
```python
extends Control
var expressions := {
"happy": preload ("res://assets/emotion_happy.png"),
"regular": preload ("res://assets/emotion_regular.png"),
"sad": preload ("res://assets/emotion_sad.png"),
}
var bodies := {
"sophia": preload ("res://assets/sophia.png"),
"pink": preload ("res://assets/pink.png")
}
## Data to display in the dialogue. Each dictionary in this array has three properties:
##
## - expression: a [code]Texture[/code] containing an expression
## - text: a [code]String[/code] containing the text the character says
## - character: a [code]Texture[/code] representing the character
var dialogue_items: Array[Dictionary] = [
{
"expression": expressions["regular"],
"text": "I've been studying arrays and dictionaries lately.",
"character": bodies["sophia"],
},
{
"expression": expressions["regular"],
"text": "Oh, nice. How has it been going?",
"character": bodies["pink"],
},
{
"expression": expressions["sad"],
"text": "Well... it's a little complicated!",
"character": bodies["sophia"],
},
{
"expression": expressions["sad"],
"text": "Oh!",
"character": bodies["pink"],
},
{
"expression": expressions["regular"],
"text": "It sure takes time to click at first.",
"character": bodies["pink"],
},
{
"expression": expressions["happy"],
"text": "If you keep at it, eventually, you'll get the hang of it!",
"character": bodies["pink"],
},
{
"expression": expressions["regular"],
"text": "Mhhh... I see. I'll keep at it, then.",
"character": bodies["sophia"],
},
{
"expression": expressions["happy"],
"text": "Thanks for the encouragement. Time to LEARN!!!",
"character": bodies["sophia"],
},
]
var current_item_index := 0
@onready var rich_text_label: RichTextLabel = %RichTextLabel
@onready var next_button: Button = %NextButton
@onready var audio_stream_player: AudioStreamPlayer = %AudioStreamPlayer
@onready var body: TextureRect = %Body
@onready var expression_texture_rect: TextureRect = %Expression
func _ready() -> void:
show_text()
next_button.pressed.connect(advance)
func show_text() -> void:
# We first extract data for the current dialogue item and apply it to the UI
# elements.
var current_item := dialogue_items[current_item_index]
rich_text_label.text = current_item["text"]
expression_texture_rect.texture = current_item["expression"]
body.texture = current_item["character"]
# We then hide the text and animate it appearing.
rich_text_label.visible_ratio = 0.0
var tween := create_tween()
var text_appearing_duration: float = current_item["text"].length() / 30.0
tween.tween_property(rich_text_label, "visible_ratio", 1.0, text_appearing_duration)
# We randomize the audio playback's start time to make it sound different
# every time and play the sound until the text finishes displaying.
var sound_max_length := audio_stream_player.stream.get_length() - text_appearing_duration
var sound_start_position := randf() * sound_max_length
audio_stream_player.play(sound_start_position)
tween.finished.connect(audio_stream_player.stop)
# We animate the character sliding in.
slide_in()
## Advances the dialogue to the next item or quits the game if there are no more
## items.
func advance() -> void:
current_item_index += 1
if current_item_index == dialogue_items.size():
get_tree().quit()
else:
show_text()
## Animates the character sliding in.
func slide_in() -> void:
var slide_tween := create_tween()
slide_tween.set_ease(Tween.EASE_OUT)
body.position.x = get_viewport_rect().size.x / 7
slide_tween.tween_property(body, "position:x", 0, 0.3)
body.modulate.a = 0
slide_tween.parallel().tween_property(body, "modulate:a", 1, 0.2
```

View file

@ -0,0 +1,81 @@
ProjectProject Settings...Input Map
Define action + button there
![[Pasted image 20240713073756.png]]
Next, select a button, and open Shortcut section
![[Pasted image 20240713073933.png]]
Click the <empty> Shortcut field, then select New Shortcut
The `Shortcut` resource takes an array of inputs as its data because in case you'd want to trigger the same button with multiple different inputs or input actions.
sadfs
![[Pasted image 20240713074212.png]]
![[Pasted image 20240713074345.png]]
Click on the created InputEventAction to expand it.
![[Pasted image 20240713080012.png]]
Set action to advance_text ( The action we created in the very first screenshot of this page)
![[Pasted image 20240713080125.png]]
Now all input defined for advance_text action will trigger the button.
Disabling button until tween finished
```python
func show_text() -> void:
# We first extract data for the current dialogue item and apply it to the UI
# elements.
# ...
next_button.disabled = true
tween.finished.connect(func() -> void:
next_button.disabled = false
)
```
Note to self: It seems to be possible to connect an event to more than 1 function. Here the full function from the tutorial. Note the tween.connect re-use
```python
func show_text() -> void:
# We first extract data for the current dialogue item and apply it to the UI
# elements.
var current_item := dialogue_items[current_item_index]
rich_text_label.text = current_item["text"]
expression_texture_rect.texture = current_item["expression"]
body.texture = current_item["character"]
# We then hide the text and animate it appearing.
rich_text_label.visible_ratio = 0.0
var tween := create_tween()
var text_appearing_duration: float = current_item["text"].length() / 30.0
tween.tween_property(rich_text_label, "visible_ratio", 1.0, text_appearing_duration)
# We randomize the audio playback's start time to make it sound different
# every time and play the sound until the text finishes displaying.
var sound_max_length := audio_stream_player.stream.get_length() - text_appearing_duration
var sound_start_position := randf() * sound_max_length
audio_stream_player.play(sound_start_position)
tween.finished.connect(audio_stream_player.stop)
# We animate the character sliding in.
slide_in()
# Finally, we disable the next button until the text finishes displaying.
next_button.disabled = true
tween.finished.connect(func() -> void:
next_button.disabled = false
)
```

View file

@ -0,0 +1,25 @@
```bbcode
BBCode is a simple [b]markup language[/b] that is [i]supposed[/i] to be simpler than [b]HTML[/b].
```
Examples
```bbcode
[b]bold[/b]
[i]italics[/i]
[u]underline[/u]
[s]strikethrough[/s]
[url=https://godotengine.org]link[/url]
[shake]This text shakes[/shake] makes the text shake.
[wave]This text is wavy[/wave] makes the text animate in a wave pattern.
[tornado freq=1.0]This text is even more wavy[/tornado] makes the text rotate like leaves. The freq parameter controls the speed of the rotation.
[rainbow val=0.8]This text is colored[/rainbow] tints the text and animates it using rainbow colors. The val parameter controls the saturation, that is to say, how intense the colors are.
```
Works only with the `RichTextLabel` node, and needs to be activated for the node (performance reasons)
![[Pasted image 20240719083713.png]]

View file

@ -0,0 +1,10 @@
`HBoxContainer`
`VBoxContainer`
`GridContainer`
`Control`
Anchors define corner position of a control node
TextureRect => Like Sprite2D, but for UI
You can drag an asset file on top of the TextureRect
![[Pasted image 20240615074811.png]]

View file

@ -0,0 +1 @@
https://school.gdquest.com/courses/learn_2d_gamedev_godot_4/telling_a_story/first_ui_exploration

View file

@ -0,0 +1,5 @@
RichtextLabel
TextureRect
Button
ColorRect
AudioStreamPlayer

View file

@ -0,0 +1,11 @@
![[Pasted image 20240623095140.png]]![[Pasted image 20240623095146.png]]
![[Pasted image 20240623095156.png]]
.tres is text-based, recommnended and git-friendly
Theme Editor Overview: https://school.gdquest.com/courses/learn_2d_gamedev_godot_4/telling_a_story/all_theme_editor_areas
Beware of not working copy/paste in theme styles. Workaround is Make unique and saving / loding in new component
1.) Create Theme Resource
2.) Modifiy
3.) Assign to uppermost UI Control Node

View file

@ -0,0 +1,28 @@
https://school.gdquest.com/courses/learn_2d_gamedev_godot_4/telling_a_story/your_first_words
Scene Unique Nodes
`To mark a node as unique, right-click it in the _Scene_ dock and select _Access As Unique Name_ in the dropdown menu.`
![[Pasted image 20240625081421.png]]
Restriction: They can only be addressed from the scene their uniqueness is defined in.
`There's also an important limitation to this feature: nodes with a scene-unique name only exist in the scene they're marked in. For example, if we mark our `RichTextLabel` in the scene dialogue.tscn as unique, we can't reference it from another scene.
You also can't mark nodes instantiated at runtime as unique. That's why we learned the more general way of getting nodes first with `get_node()`.`
Script shortcut:
1.) Mark unique nodes
2.) Drag and hold over script editor
3.) Press and hold Ctrl
4.) Release and see created initialization code
![[Pasted image 20240625081825.png]]
Quitting the game
![[Pasted image 20240625083616.png]]
`get_tree().quit()`

View file

@ -0,0 +1,19 @@
![[Pasted image 20240626083938.png]]
`Do you remember how to find the name of the property in code? You can hover over the property in the editor, and a tooltip will show you the code name.`
````
## Draws the current text to the rich text element
func show_text() -> void:
var current_item := dialogue_items[current_item_index]
rich_text_label.text = current_item
rich_text_label.visible_ratio = 0.0 # <--rich
````
````
func show_text() -> void:
# ...
var tween := create_tween()
var text_appearing_duration := 1.2
tween.tween_property(rich_text_label, "visible_ratio", 1.0, text_appearing_duration)
````

View file

@ -0,0 +1,16 @@
Generic:
AudioStream
Positional:
AudioStream2D
AudioStream3D
`
```
var text_appearing_duration = 1.2 # value is in seconds
var sound_max_length := audio_stream_player.stream.get_length() - text_appearing_duration
var sound_start_position := randf() * sound_max_length
audio_stream_player.play(sound_start_position)
tween.finished.connect(audio_stream_player.stop)
```

View file

@ -0,0 +1,50 @@
```
extends Control
## An array of strings. Each string contains the text we want the character to
## say.
var dialogue_items: Array[String] = [
"I'm learning about Arrays...",
"...and it is a little bit complicated.",
"Let's see if I got it right: an array is a list of values!",
"Did I get it right? Did I?",
"Hehe! Bye bye~!",
]
## Holds the index of the currently displayed text
var current_item_index := 0
@onready var rich_text_label: RichTextLabel = %RichTextLabel
@onready var next_button: Button = %NextButton
@onready var audio_stream_player: AudioStreamPlayer = %AudioStreamPlayer
func _ready() -> void:
show_text()
next_button.pressed.connect(advance)
## Draws the current text to the rich text element
func show_text() -> void:
var current_item := dialogue_items[current_item_index]
rich_text_label.text = current_item
# We animate the text appearing letter by letter.
rich_text_label.visible_ratio = 0.0
var tween := create_tween()
var text_appearing_duration := 1.2
tween.tween_property(rich_text_label, "visible_ratio", 1.0, text_appearing_duration)
# This is where we play the audio. We randomize the audio playback's start
# time to make it sound different every time.
var sound_max_length := audio_stream_player.stream.get_length() - text_appearing_duration
var sound_start_position := randf() * sound_max_length
audio_stream_player.play(sound_start_position)
# We stop the audio when the text finishes appearing.
tween.finished.connect(audio_stream_player.stop)
func advance() -> void:
current_item_index += 1
# If we reached the end of the dialogue, we quit the game. Otherwise, we
# show the next dialogue line.
if current_item_index == dialogue_items.size():
get_tree().quit()
else:
show_text()
```

View file

@ -0,0 +1,3 @@
1. Arrange UI nodes in rows and columns using `Container` nodes.
2. Change the texture of a `TextureRect` node programmatically.
3. Map different image textures to different strings representing the expressions using [dictionaries](https://school.gdquest.com/glossary/dictionary).

View file

@ -0,0 +1,6 @@
```python
button_sophia.pressed.connect(func() -> void:
body.texture = bodies["sophia"]
)
```

View file

@ -0,0 +1,37 @@
In this lesson, you will write the code to change the expressions of the characters when clicking the buttons.
Loading image at compile time (advantage of seeing error if file doesnt exist)
```python
var image := preload("res:://image.png")
```
Loading at runtime on demand:
```python
load("res:://image.png")
```
Preloadingloading into a dictionary:
```python
var bodies := {
"sophia": preload("res://assets/sophia.png"),
"pink": preload("res://assets/pink.png")
}
```
Dont forget to multiselect + drag&drop assets into code editor, then multi-cursor correct using
either Alt+Ctrl+Mouseclicks, or Alt+Shift+Cursor Up/Down
*Setting TextureRect texture*
```python
func _ready() -> void:
body.texture = bodies["pink"]
expression.texture = expressions["happy"]
```
It's not mandatory to use square brackets to access dictionary values. You can also use the `.` operator. For example, writing `bodies["sophia"]` is equivalent to `bodies.sophia`.

View file

@ -0,0 +1,17 @@
```python
func create_button_pink() -> void:
var button := Button.new()
row_bodies.add_child(button)
```
```python
func create_buttons() -> void:
for current_body: String in bodies:
var button := Button.new()
row_bodies.add_child(button)
button.text = current_body.capitalize()
button.pressed.connect(func()-> void:
body.texture = bodies[current_body]
)
```

View file

@ -0,0 +1,10 @@
**Topics**:
Control Nodes
Label Node
AudioStreamPlayer Node
Arrays
Dictionaries
Dictionaries inside Arrays
Theming
Iterative Design

View file

@ -0,0 +1 @@
![[Pasted image 20240720081419.png]]

View file

@ -0,0 +1,12 @@
The goal: Going from linear dialogue display to integrating player choice
https://school.gdquest.com/courses/learn_2d_gamedev_godot_4/start_a_dialogue/refactoring_the_dialogue#preparing-to-update-the-dialogue-scene
Designing the interface to include choice buttons
![[Pasted image 20240720075840.png]]
![[Pasted image 20240720080105.png]]
Sizing examples:
![[Pasted image 20240720081104.png]]