Godot + Arduino series: companion project code
Self-contained project code for the "Godot Isn't Just for Games" series: Arduino sketches, the Python serial<->UDP bridge, and the Godot 4 projects, one folder per article.
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
extends Control
|
||||
|
||||
# Part 1 — the minimal round trip: a Godot button that toggles an LED on the
|
||||
# Arduino. Only the OUTBOUND (command) half of the link is used here; Part 2
|
||||
# adds inbound telemetry from the joystick.
|
||||
|
||||
const COMMAND_ADDR := "127.0.0.1"
|
||||
const COMMAND_PORT := 4243
|
||||
|
||||
@onready var led_button: Button = $Panel/LedButton
|
||||
|
||||
var _udp_out := PacketPeerUDP.new()
|
||||
var _led_on := false
|
||||
|
||||
func _ready() -> void:
|
||||
# To SEND with PacketPeerUDP in Godot 4, use set_dest_address — NOT
|
||||
# connect_to_host, which is for receiving and silently drops outbound packets.
|
||||
_udp_out.set_dest_address(COMMAND_ADDR, COMMAND_PORT)
|
||||
led_button.pressed.connect(_on_led_pressed)
|
||||
|
||||
func _on_led_pressed() -> void:
|
||||
_led_on = not _led_on
|
||||
_udp_out.put_packet(("L1" if _led_on else "L0").to_utf8_buffer())
|
||||
led_button.text = "LED: ON" if _led_on else "LED: OFF"
|
||||
@@ -0,0 +1 @@
|
||||
uid://ctgyyk0it7dpd
|
||||
@@ -0,0 +1,91 @@
|
||||
[gd_scene load_steps=6 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://LedBasics.gd" id="1_led"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="bg"]
|
||||
bg_color = Color(0.043, 0.055, 0.078, 1)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="btn_normal"]
|
||||
bg_color = Color(0.11, 0.145, 0.204, 1)
|
||||
border_width_left = 1
|
||||
border_width_top = 1
|
||||
border_width_right = 1
|
||||
border_width_bottom = 1
|
||||
border_color = Color(0.2, 0.6, 1, 0.5)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="btn_hover"]
|
||||
bg_color = Color(0.145, 0.196, 0.278, 1)
|
||||
border_width_left = 1
|
||||
border_width_top = 1
|
||||
border_width_right = 1
|
||||
border_width_bottom = 1
|
||||
border_color = Color(0.3, 0.7, 1, 1)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="btn_pressed"]
|
||||
bg_color = Color(0.2, 0.6, 1, 1)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
|
||||
[node name="Main" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
script = ExtResource("1_led")
|
||||
|
||||
[node name="Panel" type="Panel" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
theme_override_styles/panel = SubResource("bg")
|
||||
|
||||
[node name="Title" type="Label" parent="Panel"]
|
||||
layout_mode = 0
|
||||
offset_left = 40.0
|
||||
offset_top = 96.0
|
||||
offset_right = 480.0
|
||||
offset_bottom = 130.0
|
||||
theme_override_colors/font_color = Color(0.85, 0.93, 1, 1)
|
||||
theme_override_font_sizes/font_size = 24
|
||||
text = "GODOT + ARDUINO: LED"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Subtitle" type="Label" parent="Panel"]
|
||||
layout_mode = 0
|
||||
offset_left = 40.0
|
||||
offset_top = 132.0
|
||||
offset_right = 480.0
|
||||
offset_bottom = 152.0
|
||||
theme_override_colors/font_color = Color(0.4, 0.55, 0.7, 1)
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "· click to reach down the wire ·"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="LedButton" type="Button" parent="Panel"]
|
||||
layout_mode = 0
|
||||
offset_left = 150.0
|
||||
offset_top = 220.0
|
||||
offset_right = 370.0
|
||||
offset_bottom = 284.0
|
||||
theme_override_colors/font_color = Color(0.85, 0.93, 1, 1)
|
||||
theme_override_colors/font_hover_color = Color(1, 1, 1, 1)
|
||||
theme_override_colors/font_pressed_color = Color(0.043, 0.055, 0.078, 1)
|
||||
theme_override_font_sizes/font_size = 18
|
||||
theme_override_styles/normal = SubResource("btn_normal")
|
||||
theme_override_styles/hover = SubResource("btn_hover")
|
||||
theme_override_styles/pressed = SubResource("btn_pressed")
|
||||
theme_override_styles/focus = SubResource("btn_normal")
|
||||
text = "LED: OFF"
|
||||
@@ -0,0 +1,63 @@
|
||||
extends Control
|
||||
|
||||
const TELEMETRY_PORT := 4242
|
||||
const COMMAND_ADDR := "127.0.0.1"
|
||||
const COMMAND_PORT := 4243
|
||||
|
||||
const ADC_MAX := 1023.0
|
||||
const SMOOTH_SPEED := 12.0 # higher = snappier, lower = floatier
|
||||
|
||||
@onready var led_button: Button = $Panel/LedButton
|
||||
@onready var pad: Panel = $Panel/Pad
|
||||
@onready var dot: ColorRect = $Panel/Pad/Dot
|
||||
|
||||
var _udp_in := PacketPeerUDP.new()
|
||||
var _udp_out := PacketPeerUDP.new()
|
||||
|
||||
var _target_x := 512.0 # rest near center so the dot starts in the middle
|
||||
var _target_y := 512.0
|
||||
var _smoothed_x := 512.0
|
||||
var _smoothed_y := 512.0
|
||||
var _btn_pressed := false
|
||||
var _led_on := false
|
||||
|
||||
const COLOR_DOT_IDLE := Color(0.3, 0.7, 1.0) # cyan
|
||||
const COLOR_DOT_PRESSED := Color(0.2, 1.0, 0.4) # green
|
||||
|
||||
func _ready() -> void:
|
||||
_udp_in.bind(TELEMETRY_PORT, "127.0.0.1")
|
||||
# set_dest_address is the reliable way to *send* with PacketPeerUDP in Godot 4;
|
||||
# connect_to_host is aimed at receiving and can silently drop outbound packets.
|
||||
_udp_out.set_dest_address(COMMAND_ADDR, COMMAND_PORT)
|
||||
led_button.pressed.connect(_on_led_pressed)
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
# Drain the queue; the newest packet wins. Format: "x,y,btn" e.g. "512,498,0"
|
||||
while _udp_in.get_available_packet_count() > 0:
|
||||
var parts := _udp_in.get_packet().get_string_from_utf8().split(",")
|
||||
if parts.size() >= 3 and parts[0].is_valid_int() and parts[1].is_valid_int():
|
||||
_target_x = float(parts[0])
|
||||
_target_y = float(parts[1])
|
||||
_btn_pressed = parts[2].strip_edges() == "1"
|
||||
|
||||
# Frame-rate-independent smoothing so the dot glides instead of twitching.
|
||||
var t := 1.0 - exp(-SMOOTH_SPEED * delta)
|
||||
_smoothed_x = lerpf(_smoothed_x, _target_x, t)
|
||||
_smoothed_y = lerpf(_smoothed_y, _target_y, t)
|
||||
|
||||
# Normalize raw ADC (0..1023) to 0..1.
|
||||
var nx := clampf(_smoothed_x / ADC_MAX, 0.0, 1.0)
|
||||
var ny := clampf(_smoothed_y / ADC_MAX, 0.0, 1.0)
|
||||
|
||||
# Move the dot within the pad. Invert Y so pushing the stick UP moves the dot UP.
|
||||
# (If it feels backwards, change "1.0 - ny" to "ny", or swap for X below.)
|
||||
var travel := pad.size - dot.size
|
||||
dot.position = Vector2(nx * travel.x, (1.0 - ny) * travel.y)
|
||||
|
||||
# Reflect the stick's push-button by recoloring the dot.
|
||||
dot.color = COLOR_DOT_PRESSED if _btn_pressed else COLOR_DOT_IDLE
|
||||
|
||||
func _on_led_pressed() -> void:
|
||||
_led_on = not _led_on
|
||||
_udp_out.put_packet(("L1" if _led_on else "L0").to_utf8_buffer())
|
||||
led_button.text = "LED: ON" if _led_on else "LED: OFF"
|
||||
@@ -0,0 +1 @@
|
||||
uid://d103i6ghi3ydl
|
||||
@@ -0,0 +1,137 @@
|
||||
[gd_scene format=3 uid="uid://b81ornfxnvxxo"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://d103i6ghi3ydl" path="res://Main.gd" id="1_main"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="bg"]
|
||||
bg_color = Color(0.043, 0.055, 0.078, 1)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="pad"]
|
||||
bg_color = Color(0.071, 0.09, 0.125, 1)
|
||||
border_width_left = 1
|
||||
border_width_top = 1
|
||||
border_width_right = 1
|
||||
border_width_bottom = 1
|
||||
border_color = Color(0.2, 0.6, 1, 0.55)
|
||||
corner_radius_top_left = 10
|
||||
corner_radius_top_right = 10
|
||||
corner_radius_bottom_right = 10
|
||||
corner_radius_bottom_left = 10
|
||||
shadow_color = Color(0.2, 0.6, 1, 0.15)
|
||||
shadow_size = 12
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="btn_normal"]
|
||||
bg_color = Color(0.11, 0.145, 0.204, 1)
|
||||
border_width_left = 1
|
||||
border_width_top = 1
|
||||
border_width_right = 1
|
||||
border_width_bottom = 1
|
||||
border_color = Color(0.2, 0.6, 1, 0.5)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="btn_pressed"]
|
||||
bg_color = Color(0.2, 0.6, 1, 1)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="btn_hover"]
|
||||
bg_color = Color(0.145, 0.196, 0.278, 1)
|
||||
border_width_left = 1
|
||||
border_width_top = 1
|
||||
border_width_right = 1
|
||||
border_width_bottom = 1
|
||||
border_color = Color(0.3, 0.7, 1, 1)
|
||||
corner_radius_top_left = 8
|
||||
corner_radius_top_right = 8
|
||||
corner_radius_bottom_right = 8
|
||||
corner_radius_bottom_left = 8
|
||||
|
||||
[node name="Main" type="Control" unique_id=2088197079]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_main")
|
||||
|
||||
[node name="Panel" type="Panel" parent="." unique_id=2059422118]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_styles/panel = SubResource("bg")
|
||||
|
||||
[node name="Title" type="Label" parent="Panel" unique_id=1798584764]
|
||||
layout_mode = 0
|
||||
offset_left = 40.0
|
||||
offset_top = 28.0
|
||||
offset_right = 480.0
|
||||
offset_bottom = 58.0
|
||||
theme_override_colors/font_color = Color(0.85, 0.93, 1, 1)
|
||||
theme_override_font_sizes/font_size = 22
|
||||
text = "GODOT + ARDUINO DEMO"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Subtitle" type="Label" parent="Panel" unique_id=462472763]
|
||||
layout_mode = 0
|
||||
offset_left = 40.0
|
||||
offset_top = 60.0
|
||||
offset_right = 480.0
|
||||
offset_bottom = 78.0
|
||||
theme_override_colors/font_color = Color(0.4, 0.55, 0.7, 1)
|
||||
theme_override_font_sizes/font_size = 12
|
||||
text = "· real-time serial link ·"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Pad" type="Panel" parent="Panel" unique_id=1034082861]
|
||||
layout_mode = 0
|
||||
offset_left = 70.0
|
||||
offset_top = 104.0
|
||||
offset_right = 450.0
|
||||
offset_bottom = 484.0
|
||||
theme_override_styles/panel = SubResource("pad")
|
||||
|
||||
[node name="CrosshairV" type="ColorRect" parent="Panel/Pad" unique_id=1237114789]
|
||||
layout_mode = 0
|
||||
offset_left = 189.0
|
||||
offset_right = 191.0
|
||||
offset_bottom = 380.0
|
||||
color = Color(0.2, 0.6, 1, 0.12)
|
||||
|
||||
[node name="CrosshairH" type="ColorRect" parent="Panel/Pad" unique_id=1265685762]
|
||||
layout_mode = 0
|
||||
offset_top = 189.0
|
||||
offset_right = 380.0
|
||||
offset_bottom = 191.0
|
||||
color = Color(0.2, 0.6, 1, 0.12)
|
||||
|
||||
[node name="Dot" type="ColorRect" parent="Panel/Pad" unique_id=2003621157]
|
||||
layout_mode = 0
|
||||
offset_right = 28.0
|
||||
offset_bottom = 28.0
|
||||
color = Color(0.3, 0.7, 1, 1)
|
||||
|
||||
[node name="LedButton" type="Button" parent="Panel" unique_id=1325564653]
|
||||
layout_mode = 0
|
||||
offset_left = 160.0
|
||||
offset_top = 516.0
|
||||
offset_right = 360.0
|
||||
offset_bottom = 566.0
|
||||
theme_override_colors/font_color = Color(0.85, 0.93, 1, 1)
|
||||
theme_override_colors/font_pressed_color = Color(0.043, 0.055, 0.078, 1)
|
||||
theme_override_colors/font_hover_color = Color(1, 1, 1, 1)
|
||||
theme_override_font_sizes/font_size = 18
|
||||
theme_override_styles/normal = SubResource("btn_normal")
|
||||
theme_override_styles/pressed = SubResource("btn_pressed")
|
||||
theme_override_styles/hover = SubResource("btn_hover")
|
||||
theme_override_styles/focus = SubResource("btn_normal")
|
||||
text = "LED: OFF"
|
||||
@@ -0,0 +1,24 @@
|
||||
; Engine configuration file.
|
||||
; It's best edited using the editor UI and not directly,
|
||||
; since the parameters that go here are not all obvious.
|
||||
;
|
||||
; Format:
|
||||
; [section] ; section goes between []
|
||||
; param=value ; assign values to parameters
|
||||
|
||||
config_version=5
|
||||
|
||||
[application]
|
||||
|
||||
config/name="ArduinoGodot"
|
||||
run/main_scene="res://Main.tscn"
|
||||
config/features=PackedStringArray("4.7")
|
||||
|
||||
[display]
|
||||
|
||||
window/size/viewport_width=520
|
||||
window/size/viewport_height=610
|
||||
|
||||
[dotnet]
|
||||
|
||||
project/assembly_name="ArduinoGodot"
|
||||
Reference in New Issue
Block a user