My game ended up having similar controls and I had a very difficult time detecting loops/deciding what to do with them. I'm curious how your loop detection works under the hood cause it seems more robust than what mine ended up being.
Viewing post in LOOPsnake jam comments
ill send you the code (disclaimer this is in GD script and there are lots of removed and broken features) - this is practicly my enitre game code besides the snake movement
extends Node2D
@onready var snake_head = $SnakeHead
@onready var snake_trail = $SnakeTrail
@onready var trail_label: Label = $"CanvasLayer/Panel/Trail Length UI"
var comboactive = false
var path: Array[Vector2] = []
var path_length := 0.0
var combomult = 1
signal dashupgot
var dashup = false
var combolife = 1
signal permspeed
var doubleupgot = false
@export var spacing := 5.0
@export var trail_length := 500.0
var apple := 0
signal speady
var more_apples_upgrade = false
@export var berry_scene: PackedScene
@export var big_apple_scene: PackedScene
@export var golden_apple_scene: PackedScene
@export var bomb_apple_scene: PackedScene
@export var poison_apple_scene: PackedScene
@export var spawn_area: Rect2 = Rect2(Vector2(-1000, -1000), Vector2(1800, 1800))
@export var berry_count := 5
signal moreapplesgot
signal betterpallesgot
signal combogot
var better_apples = false
var big_apple: Node2D = null
var golden_apple: Node2D = null
var bomb_apple: Node2D = null
var poison_apple: Node2D = null
var upgrade_big_apple := false
var upgrade_golden_apple := false
var upgrade_bomb_apple := false
var upgrade_poison_apple := false
var golden_multiplier_active := false
var combo = 0
var extralife = false
var loop_cooldown := false
var loop_check_distance := 15.0
func _ready():
for i in berry_count:
spawn_random_berry()
if upgrade_big_apple:
spawn_big_apple()
if upgrade_golden_apple:
spawn_golden_apple()
if upgrade_bomb_apple:
spawn_bomb_apple()
if upgrade_poison_apple:
spawn_poison_apple()
path.append(snake_head.global_position)
snake_trail.add_point(snake_head.global_position)
func _process(delta):
var current_pos = snake_head.global_position
var last_point = path[-1] if not path.is_empty() else current_pos
var distance = current_pos.distance_to(last_point)
trail_label.text = "Apples: " + str(apple)
if better_apples == true:
emit_signal("betterpallesgot")
if distance >= spacing:
path.append(current_pos)
snake_trail.add_point(current_pos)
path_length += distance
while path.size() > 1 and path_length > trail_length:
var removed = path.pop_front()
var next = path[0]
var seg_length = removed.distance_to(next)
snake_trail.remove_point(0)
path_length -= seg_length
if not loop_cooldown:
_check_for_loop()
func _check_for_loop():
if path.size() < 6:
return
var head = path[-1]
for i in range(path.size() - 5):
if head.distance_to(path[i]) < loop_check_distance:
_process_loop()
break
func _process_loop():
var polygon = path.duplicate()
var collected := 0
var new_points := 0
var golden_multiplier = golden_multiplier_active
golden_multiplier_active = false
for berry in get_tree().get_nodes_in_group("berries"):
var pos = to_local(berry.global_position)
if Geometry2D.is_point_in_polygon(pos, polygon):
_consume_berry(berry)
collected += 1
if better_apples == false:
new_points += 1
else:
new_points += 2
for big in get_tree().get_nodes_in_group("big_apples"):
var pos = to_local(big.global_position)
if Geometry2D.is_point_in_polygon(pos, polygon):
big.queue_free()
spawn_big_apple()
collected += 1
new_points += 3
for gold in get_tree().get_nodes_in_group("golden_apples"):
var pos = to_local(gold.global_position)
if Geometry2D.is_point_in_polygon(pos, polygon):
gold.queue_free()
spawn_golden_apple()
collected += 1
new_points += 5
golden_multiplier_active = true
for bomb in get_tree().get_nodes_in_group("bomb_apples"):
var pos = to_local(bomb.global_position)
if Geometry2D.is_point_in_polygon(pos, polygon):
bomb.queue_free()
spawn_bomb_apple()
_explode_nearby_apples(bomb.global_position)
collected += 1
for poison in get_tree().get_nodes_in_group("poison_apples"):
var pos = to_local(poison.global_position)
if Geometry2D.is_point_in_polygon(pos, polygon):
poison.queue_free()
spawn_poison_apple()
collected += 1
new_points -= 2
if collected >= 2 and doubleupgot== true:
emit_signal("speady")
if new_points > 0 and golden_multiplier:
new_points *= 2
if collected > 0:
$AudioStreamPlayer2D.play()
_start_loop_cooldown()
if combolife == 1 and extralife == true:
combolife =2
if comboactive== true:
apple += new_points + combo
combo+=combomult
print (combo)
else:
apple += new_points
func _consume_berry(berry: Node2D):
berry.queue_free()
spawn_random_berry()
func _random_spawn_pos() -> Vector2:
return Vector2(
randf_range(spawn_area.position.x, spawn_area.end.x),
randf_range(spawn_area.position.y, spawn_area.end.y)
)
func spawn_random_berry():
var berry = berry_scene.instantiate()
berry.global_position = _random_spawn_pos()
berry.add_to_group("berries")
add_child(berry)
func spawn_big_apple():
if big_apple:
big_apple.queue_free()
big_apple = big_apple_scene.instantiate()
big_apple.global_position = _random_spawn_pos()
big_apple.add_to_group("big_apples")
add_child(big_apple)
func spawn_golden_apple():
if golden_apple:
golden_apple.queue_free()
golden_apple = golden_apple_scene.instantiate()
golden_apple.global_position = _random_spawn_pos()
golden_apple.add_to_group("golden_apples")
add_child(golden_apple)
func spawn_bomb_apple():
if bomb_apple:
bomb_apple.queue_free()
bomb_apple = bomb_apple_scene.instantiate()
bomb_apple.global_position = _random_spawn_pos()
bomb_apple.add_to_group("bomb_apples")
add_child(bomb_apple)
func spawn_poison_apple():
if poison_apple:
poison_apple.queue_free()
poison_apple = poison_apple_scene.instantiate()
poison_apple.global_position = _random_spawn_pos()
poison_apple.add_to_group("poison_apples")
add_child(poison_apple)
func _explode_nearby_apples(origin: Vector2):
var radius = 100.0
var to_free := []
for berry in get_tree().get_nodes_in_group("berries"):
if berry.global_position.distance_to(origin) <= radius:
to_free.append(berry)
for big in get_tree().get_nodes_in_group("big_apples"):
if big.global_position.distance_to(origin) <= radius:
to_free.append(big)
for gold in get_tree().get_nodes_in_group("golden_apples"):
if gold.global_position.distance_to(origin) <= radius:
to_free.append(gold)
for poison in get_tree().get_nodes_in_group("poison_apples"):
if poison.global_position.distance_to(origin) <= radius:
to_free.append(poison)
for apple in to_free:
# Determine type and respawn
if apple.is_in_group("berries"):
_consume_berry(apple)
elif apple.is_in_group("big_apples"):
apple.queue_free()
spawn_big_apple()
elif apple.is_in_group("golden_apples"):
apple.queue_free()
spawn_golden_apple()
elif apple.is_in_group("poison_apples"):
apple.queue_free()
spawn_poison_apple()
func _start_loop_cooldown():
loop_cooldown = true
await get_tree().create_timer(0.5).timeout
loop_cooldown = false
func _on_size_increase_pressed() -> void:
if apple >= 1:
trail_length += 50
apple -= 1
func _on_texture_button_pressed() -> void:
if apple >= 3:
berry_count += 2
apple -= 3
spawn_random_berry()
spawn_random_berry()
more_apples_upgrade = true
emit_signal("moreapplesgot")
func _on_texture_button_2_pressed() -> void:
if apple >= 5 and upgrade_big_apple == false and more_apples_upgrade == true:
upgrade_big_apple = true
apple -= 5
spawn_big_apple()
func _on_golden_apples_pressed() -> void:
if apple >= 20 and upgrade_golden_apple == false:
upgrade_golden_apple = true
apple -= 20
spawn_golden_apple()
func _on_better_apples_pressed() -> void:
if apple >= 5 and better_apples == false:
better_apples = true
apple -=5
func _on_snake_head_bang() -> void:
combolife -= 1
if combolife <= 0:
combo = 0
combolife = 1 if extralife else 0
func _on_combo_pressed() -> void:
if comboactive == false and apple>= 5:
comboactive= true
apple-= 5
emit_signal("combogot")
func _on_x_combo_pressed() -> void:
if apple >= 10 and combomult==1:
apple-= 10
combomult = 2
func _on_extra_life_streak_pressed() -> void:
if apple>=10 and combolife==1 and extralife == false :
apple-=10
combolife=2
extralife = true
func _on_double_pressed() -> void:
if apple >= 5 and doubleupgot == false:
doubleupgot = true
apple -= 5
func _on_speed_pressed() -> void:
if apple >= 1:
emit_signal("permspeed")
apple-=1
func _on_dash_pressed() -> void:
if apple >= 10 and dashup == false:
dashup = true
apple -= 10
emit_signal("dashupgot")
sorry if its quite long i didnt remove anything in case of breaking it
also - the formating is broken there are normaly tabs