Adds graphics for snake that updates according to movement (mostly)

master
TechieDamien 2025-07-31 19:40:11 +01:00
parent 8f6bc2c9a6
commit dfee5ebc1e
Signed by: TechieDamien
GPG Key ID: 2ACE3574E164B780
34 changed files with 333 additions and 19 deletions

View File

@ -49,5 +49,4 @@ offset_top = 173.0
offset_right = 812.0
offset_bottom = 475.0
[connection signal="area_exited" from="Level" to="SnakePart" method="_on_level_area_exited"]
[connection signal="timeout" from="RestartTimer" to="Timer" method="start" binds= [0.3]]

View File

@ -1,6 +1,6 @@
extends VBoxContainer
var level : PackedScene = load("res://Scenes/main.tscn")
#var level : PackedScene = load("res://Scenes/main.tscn")
var main_menu : PackedScene = load("res://Scenes/main_menu.tscn")
@onready
var score_label : Label = $FinalScore
@ -10,7 +10,7 @@ func update_score() -> void:
score_label.text = "Final Score\n{score}".format(GameManager)
func restart():
get_tree().change_scene_to_packed(level)
get_tree().change_scene_to_packed(load("res://Scenes/main.tscn"))
func back():
get_tree().change_scene_to_packed(main_menu)

View File

@ -8,13 +8,15 @@ enum States {ALIVE, DEAD, OUROBOROS, OLD_OUROBOROS}
var part_type : PartTypes = PartTypes.HEAD
var state : States = States.ALIVE
var current_direction : Vector2 = Vector2.RIGHT
var old_direction : Vector2 = Vector2.RIGHT
var inputs : Dictionary[String, Vector2] = {"right": Vector2.RIGHT,
"left": Vector2.LEFT,
"up": Vector2.UP,
"down": Vector2.DOWN}
var possible_x : Array
var possible_y : Array
@onready
var raycast_right : RayCast2D = $RightRayCast2D
@ -34,11 +36,23 @@ var timer_ref : Timer = $"../Timer"
@onready
var game_over_menu : VBoxContainer = get_tree().get_first_node_in_group("GameOverMenu")
@onready
var sprite : Sprite2D = $Sprite2D
var image_head : CompressedTexture2D = preload("res://Sprites/snake_head.png")
var image_head_dead : CompressedTexture2D = preload("res://Sprites/snake_head_dead.png")
var image_head_ouroboros : CompressedTexture2D = preload("res://Sprites/snake_head_ouroboros.png")
var image_left : CompressedTexture2D = preload("res://Sprites/snake_left.png")
var image_right : CompressedTexture2D = preload("res://Sprites/snake_right.png")
var image_straight : CompressedTexture2D = preload("res://Sprites/snake_straight.png")
var image_tail : CompressedTexture2D = preload("res://Sprites/snake_tail.png")
var next_part : SnakePart = null
var commanding_head : SnakePart = null
var skip_next_move_propagation : bool = false
var queued_growth : int = 0
signal on_movement(new_dir)
signal on_movement(new_dir, prevent_move)
signal on_death
signal on_ouroboros
@ -48,10 +62,21 @@ func _ready() -> void:
# Add to group for easy access from other nodes
add_to_group("Head")
# Work out allowed positions
# Determine the level limits
var level : Area2D = get_tree().get_first_node_in_group("Level")
var x_bounds : Vector2 = level.position.x * Vector2.ONE + Vector2(-TILE_SIZE, TILE_SIZE) * level.scale.x / 2
var y_bounds : Vector2 = level.position.y * Vector2.ONE + Vector2(-TILE_SIZE, TILE_SIZE) * level.scale.y / 2
# Determine the possible x and y coordinates
possible_x = range(x_bounds.x, x_bounds.y, TILE_SIZE)
possible_y = range(y_bounds.x, y_bounds.y, TILE_SIZE)
# Attach clock
var timer : Node = get_tree().get_first_node_in_group("GameClock")
if timer is Timer:
timer.timeout.connect(process_movement.bind(Vector2.ZERO))
timer.timeout.connect(process_movement.bind(Vector2.ZERO, false))
# Set up a body part
var body = snake_part_obj.instantiate()
@ -72,6 +97,7 @@ func _ready() -> void:
body.on_movement.connect(tail.process_movement)
on_death.connect(tail.lose_game)
on_ouroboros.connect(tail.ouroboros)
tail.commanding_head = self
else:
# Free raycasts if we don't need them (consider only loading them in for the head for performance if needed)
@ -133,7 +159,7 @@ func check_position_for_snake(query_position : Vector2) -> bool:
return true
return false
func process_movement(new_direction : Vector2) -> void:
func process_movement(new_direction : Vector2, prevent_movment : bool = false) -> void:
# Only alive snakes can move
if state != States.ALIVE:
@ -142,27 +168,75 @@ func process_movement(new_direction : Vector2) -> void:
# The head needs to check if we are about to collide with something
if part_type == PartTypes.HEAD:
check_movement()
if queued_growth:
if queued_growth and state == States.ALIVE:
extend()
# Only alive snakes can move
update_sprite()
if state != States.ALIVE:
return
# Update the position of this part
position = (position + current_direction * TILE_SIZE).snapped(Vector2.ONE * TILE_SIZE)
if !prevent_movment:
position = (position + current_direction * TILE_SIZE).snapped(Vector2.ONE * TILE_SIZE)
# Tell the next part to move and give it the direction we are moving
if !skip_next_move_propagation:
on_movement.emit(current_direction)
on_movement.emit(current_direction, prevent_movment or skip_next_move_propagation)
skip_next_move_propagation = false
# Unless we are the head, change our direction to the direction given
if new_direction != Vector2.ZERO:
if !prevent_movment:
old_direction = current_direction
if new_direction != Vector2.ZERO and !prevent_movment:
current_direction = new_direction
# Update the sprite
update_sprite()
func update_sprite() -> void:
# Set the sprite based on state and part type
match part_type:
PartTypes.HEAD:
match state:
States.ALIVE:
sprite.texture = image_head
States.DEAD:
sprite.texture = image_head_dead
_:
sprite.texture = image_head_ouroboros
PartTypes.TAIL:
if state == States.OUROBOROS or state == States.OLD_OUROBOROS:
var tail_rel_direction : float = current_direction.angle_to(position - commanding_head.position)
if abs(tail_rel_direction) < 0.1:
sprite.texture = image_straight
elif tail_rel_direction > 0:
sprite.texture = image_left
elif tail_rel_direction < 0:
sprite.texture = image_right
else:
sprite.texture = image_tail
PartTypes.BODY:
var tail_rel_direction : float = current_direction.angle_to(old_direction)
if abs(tail_rel_direction) < 0.1:
sprite.texture = image_straight
elif tail_rel_direction > 0:
sprite.texture = image_left
elif tail_rel_direction < 0:
sprite.texture = image_right
if current_direction != Vector2.ZERO:
sprite.rotation = current_direction.angle() + PI / 2
else:
sprite.rotation = old_direction.angle() + PI / 2
func check_movement() -> void:
# Make sure we are going to stay in the arena
var new_position : Vector2 = (position + current_direction * TILE_SIZE).snapped(Vector2.ONE * TILE_SIZE)
if not int(new_position.x) in possible_x or not int(new_position.y) in possible_y:
lose_game()
# Get the correct raycast for the direction we are moving
var raycast_to_use : RayCast2D
if current_direction == Vector2.RIGHT:
@ -213,6 +287,7 @@ func extend() -> void:
# Set direction of new part
new_body.current_direction = current_direction
new_body.old_direction = old_direction
# Update reference to next part
next_part = new_body
@ -224,20 +299,22 @@ func extend() -> void:
queued_growth -= 1
func lose_game() -> void:
func lose_game(from_lack_of_space : bool = false) -> void:
# You lose!
print("Game lost")
state = States.DEAD
on_death.emit()
if !from_lack_of_space:
state = States.DEAD
on_death.emit()
game_over_menu.visible = true
func ouroboros() -> void:
print("You have achieved the ouroboros!")
if state == States.OUROBOROS:
state = States.OLD_OUROBOROS
elif state == States.ALIVE:
state = States.OUROBOROS
if part_type == PartTypes.TAIL:
update_sprite()
on_ouroboros.emit()
# The head can spawn a new snake
@ -282,7 +359,7 @@ func spawn_new_snake() -> void:
# If there is no room to spawn a snake, we lose
if spawn_positions.is_empty():
lose_game()
lose_game(true)
return
# Pick a position
@ -304,5 +381,5 @@ func _unhandled_input(event: InputEvent) -> void:
# If we leave the arena
func _on_level_area_exited(_area: Area2D) -> void:
lose_game()
#func _on_level_area_exited(_area: Area2D) -> void:
#lose_game()

BIN
Sprites/snake_head.kra Normal file

Binary file not shown.

BIN
Sprites/snake_head.kra~ Normal file

Binary file not shown.

BIN
Sprites/snake_head.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 B

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://djqg2n4n81gqj"
path="res://.godot/imported/snake_head.png-d9b790ba6d57ab17f6536c547f091446.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Sprites/snake_head.png"
dest_files=["res://.godot/imported/snake_head.png-d9b790ba6d57ab17f6536c547f091446.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

BIN
Sprites/snake_head_dead.kra Normal file

Binary file not shown.

Binary file not shown.

BIN
Sprites/snake_head_dead.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bm1cbd5uf6tlq"
path="res://.godot/imported/snake_head_dead.png-e3c3b818ce7ed644b9a37e714866ea73.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Sprites/snake_head_dead.png"
dest_files=["res://.godot/imported/snake_head_dead.png-e3c3b818ce7ed644b9a37e714866ea73.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.

After

Width:  |  Height:  |  Size: 497 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://f31d1vxf6kdv"
path="res://.godot/imported/snake_head_ouroboros.png-880dd7bcf916adb92d621a92cede4944.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Sprites/snake_head_ouroboros.png"
dest_files=["res://.godot/imported/snake_head_ouroboros.png-880dd7bcf916adb92d621a92cede4944.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

BIN
Sprites/snake_left.kra Normal file

Binary file not shown.

BIN
Sprites/snake_left.kra~ Normal file

Binary file not shown.

BIN
Sprites/snake_left.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 B

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b5lwghfk2spid"
path="res://.godot/imported/snake_left.png-badd0667030f5f0cf0ee9e32fe23f909.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Sprites/snake_left.png"
dest_files=["res://.godot/imported/snake_left.png-badd0667030f5f0cf0ee9e32fe23f909.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

BIN
Sprites/snake_left.png~ Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 B

BIN
Sprites/snake_right.kra Normal file

Binary file not shown.

BIN
Sprites/snake_right.kra~ Normal file

Binary file not shown.

BIN
Sprites/snake_right.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bvg583s86j3ca"
path="res://.godot/imported/snake_right.png-06790ccfafebd89ea6cb77e5510b3ad6.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Sprites/snake_right.png"
dest_files=["res://.godot/imported/snake_right.png-06790ccfafebd89ea6cb77e5510b3ad6.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

BIN
Sprites/snake_right.png~ Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B

BIN
Sprites/snake_straight.kra Normal file

Binary file not shown.

BIN
Sprites/snake_straight.kra~ Normal file

Binary file not shown.

BIN
Sprites/snake_straight.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 B

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b0can5myhke8s"
path="res://.godot/imported/snake_straight.png-afe959d90748ce7a63c1d2cc542b0007.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Sprites/snake_straight.png"
dest_files=["res://.godot/imported/snake_straight.png-afe959d90748ce7a63c1d2cc542b0007.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

BIN
Sprites/snake_straight.png~ Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

BIN
Sprites/snake_tail.kra Normal file

Binary file not shown.

BIN
Sprites/snake_tail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

View File

@ -0,0 +1,34 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dkbr22xkxksaf"
path="res://.godot/imported/snake_tail.png-86597c4ec7b604cdaa9ee2880ddc84b9.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Sprites/snake_tail.png"
dest_files=["res://.godot/imported/snake_tail.png-86597c4ec7b604cdaa9ee2880ddc84b9.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