Compare commits

..

No commits in common. "10552b377b058bf3b954e5ffdee6523e73547890" and "ae9ac2ac59b25329a26f38917c2ed7935f561108" have entirely different histories.

38 changed files with 2 additions and 609 deletions

View File

@ -1,16 +1,3 @@
# doodle game concept # gmtk-2025-loop
A game about using your finger/mouse to draw circles around moving game objects.
It works by instantiating an `Area2D` node with a polygon collision shape, and can probably
also be mildly modified to draw level geometry dynamically.
In either case, the challenge is taming an otherwise automatic system. GMTK game jam, 2025. Theme: Loop
Objects move on their own. You only control the pencil.
## Current questions
* Should it be about capturing only the correct game entities?
* Should it be about drawing level geometry to guide game entities to a goal?
## Current definites
* Doodles-in-the-margins lined paper aesthetic
* 2px outlines, simple colors
* Can likely use shaders to make it appear slightly sketchy and animated

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://54uy8wr13v7y"
path="res://.godot/imported/monster-programmer.png-ab25b81a62d3e60a3afa8336a427284f.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/monster-programmer.png"
dest_files=["res://.godot/imported/monster-programmer.png-ab25b81a62d3e60a3afa8336a427284f.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 781 B

View File

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ds5ybi21d52yw"
path="res://.godot/imported/pencil.png-1694d319d2e5ca8709382e64ef0add14.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/sprites/pencil.png"
dest_files=["res://.godot/imported/pencil.png-1694d319d2e5ca8709382e64ef0add14.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

View File

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bvn2kc5cb23ob"
path="res://.godot/imported/lined-paper-margin.png-2ae3d1d8409c38a2be5e57ed7a16368a.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/textures/lined-paper-margin.png"
dest_files=["res://.godot/imported/lined-paper-margin.png-2ae3d1d8409c38a2be5e57ed7a16368a.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

View File

@ -1,34 +0,0 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c7en2ehk2mvk0"
path="res://.godot/imported/lined-paper.png-89bb2c80a7f89cfdfb7c268868425ebe.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/textures/lined-paper.png"
dest_files=["res://.godot/imported/lined-paper.png-89bb2c80a7f89cfdfb7c268868425ebe.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

Binary file not shown.

View File

@ -11,27 +11,10 @@ config_version=5
[application] [application]
config/name="GMTK 2025 Loop" config/name="GMTK 2025 Loop"
run/main_scene="uid://by78isronsqtp"
config/features=PackedStringArray("4.4", "GL Compatibility") config/features=PackedStringArray("4.4", "GL Compatibility")
config/icon="res://icon.svg" config/icon="res://icon.svg"
[autoload]
SceneManager="*res://scripts/SceneManager.gd"
Globals="*res://scripts/Globals.gd"
[display]
window/size/viewport_width=960
window/size/viewport_height=540
window/stretch/mode="viewport"
[global_group]
collectable=""
[rendering] [rendering]
renderer/rendering_method="gl_compatibility" renderer/rendering_method="gl_compatibility"
renderer/rendering_method.mobile="gl_compatibility" renderer/rendering_method.mobile="gl_compatibility"
environment/defaults/default_clear_color=Color(0.12549, 0.12549, 0.12549, 1)

View File

@ -1,45 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://c1w3l5dyo0mrk"]
[sub_resource type="GDScript" id="GDScript_8neu0"]
script/source = "extends VBoxContainer
@onready var mouse_loop_button = $ScrollContainer/VBoxContainer/MouseLoop
@onready var mouse_fling_button = $ScrollContainer/VBoxContainer/MouseFling
const SCENE_MOUSE_LOOP = preload(\"res://scenes/debug/MouseLoop.tscn\")
const SCENE_MOUSE_FLING = preload(\"res://scenes/debug/MouseFling.tscn\")
func _on_mouse_loop_pressed() -> void:
SceneManager.change_scene(SCENE_MOUSE_LOOP)
func _on_mouse_fling_pressed() -> void:
SceneManager.change_scene(SCENE_MOUSE_FLING)
"
[node name="Debug" type="VBoxContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = SubResource("GDScript_8neu0")
[node name="CenterContainer" type="CenterContainer" parent="."]
layout_mode = 2
[node name="Label" type="Label" parent="CenterContainer"]
layout_mode = 2
text = "Debug Menu"
[node name="ScrollContainer" type="ScrollContainer" parent="."]
layout_mode = 2
size_flags_vertical = 3
[node name="VBoxContainer" type="VBoxContainer" parent="ScrollContainer"]
layout_mode = 2
[node name="MouseLoop" type="Button" parent="ScrollContainer/VBoxContainer"]
layout_mode = 2
text = "Mouse Loop"
[connection signal="pressed" from="ScrollContainer/VBoxContainer/MouseLoop" to="." method="_on_mouse_loop_pressed"]

View File

@ -1,3 +0,0 @@
[gd_scene format=3 uid="uid://cp2qdqu5ti42c"]
[node name="MouseFling" type="Node2D"]

View File

@ -1,70 +0,0 @@
extends Node2D
@onready var CURSOR = $Cursor
func _ready() -> void:
for x in range(64, get_viewport_rect().size.x, 96):
for y in range(64, get_viewport_rect().size.y, 96):
var c = Globals.SCENE_COLLECTABLE.instantiate()
c.global_position = Vector2(x, y)
if randf() < .2:
c.is_bad = true
add_child(c)
$Camera2D.position = get_viewport_rect().size / 2
func _draw() -> void:
var trail = CURSOR.get_tail(Globals.HISTORY_LENGTH)
if trail.size() <= 0:
return
for i in range(trail.size() - 1):
var color
if trail.size() > Globals.HISTORY_LENGTH * .66:
color = Color(.5,0,0,1)
elif trail.size() > Globals.HISTORY_LENGTH * .33:
color = Color(.5,.5,0,1)
else:
color = Color(0,.5,0,1)
draw_line(trail[i], trail[i + 1], color , Globals.LINE_THICC, false)
func _process(delta: float) -> void:
queue_redraw()
while CURSOR.loop_stack.size() > 0:
var loop = CURSOR.loop_stack.pop_back()
create_loop_object(loop)
func create_loop_object(loop:Array):
var scene = Globals.SCENE_LOOP.instantiate()
var polygon_collider = scene.get_node("PolygonCollision") as CollisionPolygon2D
polygon_collider.polygon = PackedVector2Array(loop)
add_child(scene)
await get_tree().create_timer(.2).timeout
var contents = scene.get_contained_nodes()
get_tree().queue_delete(scene)
print(contents)
var collectables = get_tree().get_nodes_in_group("collectable")
for i in contents:
if not (i in collectables):
continue
if i.is_bad:
print("ouch")
i.remove()
else:
i.remove()
func _on_cursor_started_drawing(pos:Vector2) -> void:
print("started drawing")
var cam = get_viewport().get_camera_2d()
var t = get_tree().create_tween()
t.tween_property(Engine, "time_scale", 0.2, .2)
$StartMarker.global_position = pos
$StartMarker.visible = true
func _on_cursor_stopped_drawing() -> void:
print("stopped drawing")
var cam = get_viewport().get_camera_2d()
var t = get_tree().create_tween()
t.tween_property(Engine, "time_scale", 1.0, .2)
$StartMarker.visible = false

View File

@ -1 +0,0 @@
uid://b1ddnopgn4scy

View File

@ -1,65 +0,0 @@
[gd_scene load_steps=9 format=3 uid="uid://d1bduqxqx10jb"]
[ext_resource type="Script" uid="uid://b1ddnopgn4scy" path="res://scenes/debug/MouseLoop.gd" id="1_x04i4"]
[ext_resource type="PackedScene" uid="uid://byipv4uqxdtfk" path="res://scenes/objects/Cursor.tscn" id="3_aaj44"]
[ext_resource type="Texture2D" uid="uid://c7en2ehk2mvk0" path="res://assets/textures/lined-paper.png" id="3_u4ca0"]
[ext_resource type="PackedScene" uid="uid://dnr505je252gd" path="res://scenes/objects/StartMarker.tscn" id="4_u4ca0"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_va2mg"]
size = Vector2(100, 591)
[sub_resource type="RectangleShape2D" id="RectangleShape2D_aaj44"]
size = Vector2(1025, 219.5)
[sub_resource type="RectangleShape2D" id="RectangleShape2D_u4ca0"]
size = Vector2(152.5, 717.75)
[sub_resource type="RectangleShape2D" id="RectangleShape2D_olkr6"]
size = Vector2(1263.25, 220.875)
[node name="MouseLoop" type="Node2D"]
z_index = 1
script = ExtResource("1_x04i4")
[node name="Cursor" parent="." instance=ExtResource("3_aaj44")]
z_index = 1
[node name="Walls" type="StaticBody2D" parent="."]
[node name="CollisionShape2D" type="CollisionShape2D" parent="Walls"]
position = Vector2(-51, 289)
shape = SubResource("RectangleShape2D_va2mg")
[node name="CollisionShape2D2" type="CollisionShape2D" parent="Walls"]
position = Vector2(449.5, 650.75)
shape = SubResource("RectangleShape2D_aaj44")
[node name="CollisionShape2D3" type="CollisionShape2D" parent="Walls"]
position = Vector2(1034.75, 338.125)
shape = SubResource("RectangleShape2D_u4ca0")
[node name="CollisionShape2D4" type="CollisionShape2D" parent="Walls"]
position = Vector2(500.375, -109.438)
shape = SubResource("RectangleShape2D_olkr6")
[node name="Camera2D" type="Camera2D" parent="."]
[node name="LinedPaper" type="TextureRect" parent="."]
z_index = -2
custom_minimum_size = Vector2(960, 540)
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_right = 960.0
offset_bottom = 540.0
grow_horizontal = 2
grow_vertical = 2
texture = ExtResource("3_u4ca0")
stretch_mode = 1
[node name="StartMarker" parent="." instance=ExtResource("4_u4ca0")]
visible = false
z_index = -1
[connection signal="started_drawing" from="Cursor" to="." method="_on_cursor_started_drawing"]
[connection signal="stopped_drawing" from="Cursor" to="." method="_on_cursor_stopped_drawing"]

View File

@ -1,7 +0,0 @@
extends Node2D
const PRIMARY_GAME = preload("res://scenes/debug/MouseLoop.tscn")
func _ready () -> void:
await get_tree().create_timer(.2).timeout
SceneManager.change_scene(PRIMARY_GAME)

View File

@ -1 +0,0 @@
uid://xv5ex4y3tndf

View File

@ -1,6 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://by78isronsqtp"]
[ext_resource type="Script" uid="uid://xv5ex4y3tndf" path="res://scenes/main.gd" id="1_o5qli"]
[node name="Main" type="Node2D"]
script = ExtResource("1_o5qli")

View File

@ -1,18 +0,0 @@
[gd_scene load_steps=4 format=3 uid="uid://c0srxosarykx7"]
[ext_resource type="Script" uid="uid://bmke2komimx40" path="res://scenes/objects/collectable.gd" id="1_dtuyt"]
[ext_resource type="Texture2D" uid="uid://54uy8wr13v7y" path="res://assets/sprites/monster-programmer.png" id="2_dtuyt"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_aaj44"]
size = Vector2(48, 46)
[node name="Collectable" type="CharacterBody2D" groups=["collectable"]]
z_index = -1
script = ExtResource("1_dtuyt")
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
position = Vector2(1, 0)
shape = SubResource("RectangleShape2D_aaj44")
[node name="Sprite2D" type="Sprite2D" parent="."]
texture = ExtResource("2_dtuyt")

View File

@ -1,12 +0,0 @@
[gd_scene load_steps=3 format=3 uid="uid://byipv4uqxdtfk"]
[ext_resource type="Script" uid="uid://b37llyj4cny7w" path="res://scenes/objects/cursor.gd" id="1_6xvnr"]
[ext_resource type="Texture2D" uid="uid://ds5ybi21d52yw" path="res://assets/sprites/pencil.png" id="1_erapf"]
[node name="Cursor" type="Node2D"]
script = ExtResource("1_6xvnr")
[node name="IconNormal" type="Sprite2D" parent="."]
texture = ExtResource("1_erapf")
centered = false
offset = Vector2(0, -32)

View File

@ -1,10 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://c3qo6pmowd5en"]
[ext_resource type="Script" uid="uid://bi2yo4l1m60iu" path="res://scenes/objects/loop.gd" id="1_vv7t2"]
[node name="Loop" type="Area2D"]
script = ExtResource("1_vv7t2")
[node name="PolygonCollision" type="CollisionPolygon2D" parent="."]
[connection signal="body_entered" from="." to="." method="_on_body_entered"]

View File

@ -1,14 +0,0 @@
shader_type canvas_item;
uniform float speed = 4.0;
uniform float scale = 8.0;
uniform vec4 color_a: source_color = vec4(1.,1.,1.,1.);
uniform vec4 color_b: source_color = vec4(0.,0.,0.,1.);
void fragment() {
vec2 uv = FRAGCOORD.xy;
float t = .5 + sin((uv.x + uv.y) / scale + TIME * speed) * .5;
t -= .5 + sin((uv.x - uv.y) / scale + TIME * speed) * .5;
vec3 tvec = vec3(t,t,t);
COLOR = tvec.x > 0.0 ? color_a : color_b;
}

View File

@ -1 +0,0 @@
uid://clj5gpccox0tb

View File

@ -1,18 +0,0 @@
[gd_scene load_steps=4 format=3 uid="uid://dnr505je252gd"]
[ext_resource type="Shader" uid="uid://clj5gpccox0tb" path="res://scenes/objects/StartMarker.gdshader" id="1_kpqbh"]
[ext_resource type="Script" uid="uid://bwut7fxknsnos" path="res://scenes/objects/start_marker.gd" id="1_u6erm"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_3ykre"]
shader = ExtResource("1_kpqbh")
shader_parameter/speed = 16.0
shader_parameter/scale = 8.0
shader_parameter/color_a = Color(0.158472, 0.158472, 0.158472, 1)
shader_parameter/color_b = Color(0.864675, 0.864675, 0.864675, 1)
[node name="StartMarker" type="Line2D"]
material = SubResource("ShaderMaterial_3ykre")
points = PackedVector2Array(-64, -64, 64, -64, 64, 64, -64, 64)
closed = true
width = 32.0
script = ExtResource("1_u6erm")

View File

@ -1,19 +0,0 @@
extends CharacterBody2D
@export var is_bad : bool = false
func _ready() -> void:
velocity = Vector2.from_angle(randf() * PI * 2) * 50
if is_bad:
modulate = Color.RED
func _process(delta: float) -> void:
var results = move_and_collide(velocity * delta, false)
if results != null:
velocity = velocity.reflect(Vector2.from_angle(results.get_angle()))
func remove() -> void:
if is_bad:
velocity = Vector2(0,0)
await get_tree().create_tween().tween_property(self, "scale", Vector2(2,2), 1.0).finished
get_tree().queue_delete(self)

View File

@ -1 +0,0 @@
uid://bmke2komimx40

View File

@ -1,98 +0,0 @@
extends Node2D
var positions_history : Array
var loop_stack : Array
signal started_drawing(pos:Vector2)
signal stopped_drawing
var is_drawing = false
func _ready() -> void:
init_history_stack()
loop_stack = []
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
global_position = get_viewport_rect().size / 2
func _process(delta: float) -> void:
if global_position.y < 32:
$IconNormal.scale.y = -1.0
else:
$IconNormal.scale.y = 1.0
if global_position.x > get_viewport_rect().size.x - 32:
$IconNormal.scale.x = -1.0
else:
$IconNormal.scale.x = 1.0
func _input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
var direction = event.relative.normalized()
# truthfully should calibrate by mouse
# need a max speed for fair gameplay
# var speed = clamp(event.relative.length(), 0.0, 128.0)
var speed = event.relative.length()
global_position += direction * speed
restrict_cursor()
if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
if not is_drawing:
emit_signal("started_drawing", global_position)
is_drawing = true
push_history_stack(global_position)
else:
if is_close_to_loop():
create_valid_loop()
init_history_stack()
if is_drawing:
emit_signal("stopped_drawing")
is_drawing = false
func get_tail(max_length: int = 32) -> Array:
var tail_length = min(positions_history.size(), max_length)
var output = positions_history.slice(positions_history.size() - tail_length, positions_history.size())
return output
func is_close_to_loop () -> bool:
if positions_history.size() < 4:
return false
var to = positions_history[positions_history.size() - 1]
var from = positions_history[0]
var distance = (to - from).length()
print(distance)
if distance < Globals.SNAP_DISTANCE:
print((to - from).length())
return true
return false
func init_history_stack() -> void:
positions_history = []
func create_valid_loop() -> void:
var smoothed_loop = positions_history
var last = 0
# simplify
for j in range(1):
var new_loop = []
for i in range(0, smoothed_loop.size(), 2):
new_loop.append(smoothed_loop[i])
smoothed_loop = new_loop
loop_stack.append(smoothed_loop)
func push_history_stack(value : Vector2) -> void:
if positions_history.size() < Globals.HISTORY_LENGTH:
positions_history.append(value)
# if positions_history.size() > Globals.HISTORY_LENGTH:
# positions_history.pop_front()
func restrict_cursor() -> void:
if global_position.x < 0:
global_position.x = 0
if global_position.y < 0:
global_position.y = 0
if global_position.x > get_viewport_rect().size.x:
global_position.x = get_viewport_rect().size.x
if global_position.y > get_viewport_rect().size.y:
global_position.y = get_viewport_rect().size.y

View File

@ -1 +0,0 @@
uid://b37llyj4cny7w

View File

@ -1,22 +0,0 @@
extends Area2D
var contents : Array = []
const LINE_THICC = 6.0
func _ready() -> void:
await get_tree().create_timer(.2).timeout
$PolygonCollision.disabled = true
func _draw() -> void:
draw_polyline(
$PolygonCollision.polygon,
Color.BLACK, LINE_THICC, false)
draw_line(
$PolygonCollision.polygon[0], $PolygonCollision.polygon[$PolygonCollision.polygon.size() - 1],
Color.BLACK, LINE_THICC, false)
func _on_body_entered(body: Node2D) -> void:
contents.append(body)
func get_contained_nodes() -> Array:
return contents

View File

@ -1 +0,0 @@
uid://bi2yo4l1m60iu

View File

@ -1,8 +0,0 @@
extends Line2D
func _ready() -> void:
var points = []
for i in range(36):
var a = deg_to_rad(i * 10)
points.append(Vector2(cos(a), -sin(a)) * ((self.width / 2.0) - Globals.SNAP_DISTANCE))
self.points = points

View File

@ -1 +0,0 @@
uid://bwut7fxknsnos

View File

@ -1,9 +0,0 @@
extends Node
const SCENE_LOOP = preload("res://scenes/objects/Loop.tscn")
const SCENE_COLLECTABLE = preload("res://scenes/objects/Collectable.tscn")
const SCENE_START_MARKER = preload("res://scenes/objects/StartMarker.tscn")
const LINE_THICC = 4.0
const HISTORY_LENGTH = 196
const SNAP_DISTANCE = 32

View File

@ -1 +0,0 @@
uid://ch2ayhum8jm28

View File

@ -1,8 +0,0 @@
extends Node
func change_scene(scene):
var current = get_tree().current_scene
get_tree().root.remove_child(current)
var next = scene.instantiate()
get_tree().root.add_child(next)
get_tree().current_scene = next

View File

@ -1 +0,0 @@
uid://dd13tvd0buvta