├── .gitattributes ├── .gitignore ├── README.md ├── assets ├── MaldiniBold.ttf ├── MaldiniBold.ttf.import ├── apple.png ├── apple.png.import ├── bg.png └── bg.png.import ├── project.godot └── scenes ├── game_over_menu.gd ├── game_over_menu.tscn ├── hud.tscn ├── main.gd ├── main.tscn └── snake_segment.tscn /.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize EOL for all files that Git considers text files. 2 | * text=auto eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Godot 4+ specific ignores 2 | .godot/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # snake_tutorial 2 | Snake game made in Godot 3 | -------------------------------------------------------------------------------- /assets/MaldiniBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/russs123/snake_tutorial/ef48461bd67b63689fe72eaaaae7dd385137aa01/assets/MaldiniBold.ttf -------------------------------------------------------------------------------- /assets/MaldiniBold.ttf.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="font_data_dynamic" 4 | type="FontFile" 5 | uid="uid://r5ttqaxnuvgk" 6 | path="res://.godot/imported/MaldiniBold.ttf-cce0e3cae12b8508d03cb12dba036872.fontdata" 7 | 8 | [deps] 9 | 10 | source_file="res://assets/MaldiniBold.ttf" 11 | dest_files=["res://.godot/imported/MaldiniBold.ttf-cce0e3cae12b8508d03cb12dba036872.fontdata"] 12 | 13 | [params] 14 | 15 | Rendering=null 16 | antialiasing=1 17 | generate_mipmaps=false 18 | multichannel_signed_distance_field=false 19 | msdf_pixel_range=8 20 | msdf_size=48 21 | allow_system_fallback=true 22 | force_autohinter=false 23 | hinting=1 24 | subpixel_positioning=1 25 | oversampling=0.0 26 | Fallbacks=null 27 | fallbacks=[] 28 | Compress=null 29 | compress=true 30 | preload=[] 31 | language_support={} 32 | script_support={} 33 | opentype_features={} 34 | -------------------------------------------------------------------------------- /assets/apple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/russs123/snake_tutorial/ef48461bd67b63689fe72eaaaae7dd385137aa01/assets/apple.png -------------------------------------------------------------------------------- /assets/apple.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://dxyvfago0gd5f" 6 | path="res://.godot/imported/apple.png-d0c5a0e024efc439723035186c67a74a.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://assets/apple.png" 14 | dest_files=["res://.godot/imported/apple.png-d0c5a0e024efc439723035186c67a74a.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /assets/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/russs123/snake_tutorial/ef48461bd67b63689fe72eaaaae7dd385137aa01/assets/bg.png -------------------------------------------------------------------------------- /assets/bg.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="CompressedTexture2D" 5 | uid="uid://bj2ambwei5ogi" 6 | path="res://.godot/imported/bg.png-23a59c2e9cba2223a50fa3fe41b70b25.ctex" 7 | metadata={ 8 | "vram_texture": false 9 | } 10 | 11 | [deps] 12 | 13 | source_file="res://assets/bg.png" 14 | dest_files=["res://.godot/imported/bg.png-23a59c2e9cba2223a50fa3fe41b70b25.ctex"] 15 | 16 | [params] 17 | 18 | compress/mode=0 19 | compress/high_quality=false 20 | compress/lossy_quality=0.7 21 | compress/hdr_compression=1 22 | compress/normal_map=0 23 | compress/channel_pack=0 24 | mipmaps/generate=false 25 | mipmaps/limit=-1 26 | roughness/mode=0 27 | roughness/src_normal="" 28 | process/fix_alpha_border=true 29 | process/premult_alpha=false 30 | process/normal_map_invert_y=false 31 | process/hdr_as_srgb=false 32 | process/hdr_clamp_exposure=false 33 | process/size_limit=0 34 | detect_3d/compress_to=1 35 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=5 10 | 11 | [application] 12 | 13 | config/name="snake_tutorial" 14 | run/main_scene="res://scenes/main.tscn" 15 | config/features=PackedStringArray("4.1", "Forward Plus") 16 | config/icon="res://icon.svg" 17 | 18 | [display] 19 | 20 | window/size/viewport_width=1000 21 | window/size/viewport_height=1050 22 | 23 | [input] 24 | 25 | move_left={ 26 | "deadzone": 0.5, 27 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"echo":false,"script":null) 28 | ] 29 | } 30 | move_right={ 31 | "deadzone": 0.5, 32 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"echo":false,"script":null) 33 | ] 34 | } 35 | move_up={ 36 | "deadzone": 0.5, 37 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"echo":false,"script":null) 38 | ] 39 | } 40 | move_down={ 41 | "deadzone": 0.5, 42 | "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"echo":false,"script":null) 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /scenes/game_over_menu.gd: -------------------------------------------------------------------------------- 1 | extends CanvasLayer 2 | 3 | signal restart 4 | 5 | func _on_restart_button_pressed(): 6 | restart.emit() 7 | -------------------------------------------------------------------------------- /scenes/game_over_menu.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=3 uid="uid://cad25ojl47q8"] 2 | 3 | [ext_resource type="FontFile" uid="uid://ctijp6avftrtc" path="res://assets/MaldiniBold.ttf" id="1_4cjqx"] 4 | [ext_resource type="Script" path="res://scenes/game_over_menu.gd" id="1_k7vgk"] 5 | 6 | [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_hy06m"] 7 | bg_color = Color(0, 0.239216, 0.305882, 1) 8 | shadow_size = 5 9 | shadow_offset = Vector2(10, 10) 10 | 11 | [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8be6j"] 12 | bg_color = Color(0, 0.611765, 0.172549, 1) 13 | 14 | [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_l65v3"] 15 | bg_color = Color(0, 0.737255, 0.168627, 1) 16 | 17 | [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_u4ktg"] 18 | bg_color = Color(0, 0.572549, 0.631373, 1) 19 | 20 | [node name="GameOverMenu" type="CanvasLayer"] 21 | script = ExtResource("1_k7vgk") 22 | 23 | [node name="GameOverPanel" type="Panel" parent="."] 24 | anchors_preset = 8 25 | anchor_left = 0.5 26 | anchor_top = 0.5 27 | anchor_right = 0.5 28 | anchor_bottom = 0.5 29 | offset_left = -200.0 30 | offset_top = -125.0 31 | offset_right = 200.0 32 | offset_bottom = 125.0 33 | grow_horizontal = 2 34 | grow_vertical = 2 35 | theme_override_styles/panel = SubResource("StyleBoxFlat_hy06m") 36 | 37 | [node name="ResultLabel" type="Label" parent="."] 38 | anchors_preset = 8 39 | anchor_left = 0.5 40 | anchor_top = 0.5 41 | anchor_right = 0.5 42 | anchor_bottom = 0.5 43 | offset_left = -133.5 44 | offset_top = -86.0 45 | offset_right = 133.5 46 | offset_bottom = -16.0 47 | grow_horizontal = 2 48 | grow_vertical = 2 49 | theme_override_fonts/font = ExtResource("1_4cjqx") 50 | theme_override_font_sizes/font_size = 50 51 | text = "GAME OVER!" 52 | horizontal_alignment = 1 53 | vertical_alignment = 1 54 | 55 | [node name="RestartButton" type="Button" parent="."] 56 | offset_left = 375.0 57 | offset_top = 545.0 58 | offset_right = 625.0 59 | offset_bottom = 608.0 60 | theme_override_fonts/font = ExtResource("1_4cjqx") 61 | theme_override_font_sizes/font_size = 50 62 | theme_override_styles/normal = SubResource("StyleBoxFlat_8be6j") 63 | theme_override_styles/hover = SubResource("StyleBoxFlat_l65v3") 64 | theme_override_styles/pressed = SubResource("StyleBoxFlat_u4ktg") 65 | text = "Play Again" 66 | 67 | [connection signal="pressed" from="RestartButton" to="." method="_on_restart_button_pressed"] 68 | -------------------------------------------------------------------------------- /scenes/hud.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=3 uid="uid://tmcq5gpw66gu"] 2 | 3 | [ext_resource type="FontFile" uid="uid://ctijp6avftrtc" path="res://assets/MaldiniBold.ttf" id="1_jt2fd"] 4 | 5 | [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_36f6m"] 6 | bg_color = Color(0, 0.392157, 0.878431, 1) 7 | 8 | [node name="Hud" type="CanvasLayer"] 9 | 10 | [node name="ScorePanel" type="Panel" parent="."] 11 | anchors_preset = 10 12 | anchor_right = 1.0 13 | offset_bottom = 50.0 14 | grow_horizontal = 2 15 | theme_override_styles/panel = SubResource("StyleBoxFlat_36f6m") 16 | 17 | [node name="ScoreLabel" type="Label" parent="."] 18 | anchors_preset = 5 19 | anchor_left = 0.5 20 | anchor_right = 0.5 21 | offset_left = -65.0 22 | offset_top = -6.0 23 | offset_right = 65.0 24 | offset_bottom = 52.0 25 | grow_horizontal = 2 26 | theme_override_colors/font_color = Color(1, 1, 1, 1) 27 | theme_override_colors/font_shadow_color = Color(0, 0, 0, 1) 28 | theme_override_fonts/font = ExtResource("1_jt2fd") 29 | theme_override_font_sizes/font_size = 40 30 | text = "SCORE: 0" 31 | horizontal_alignment = 1 32 | vertical_alignment = 1 33 | -------------------------------------------------------------------------------- /scenes/main.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | @export var snake_scene : PackedScene 4 | 5 | #game variables 6 | var score : int 7 | var game_started : bool = false 8 | 9 | #grid variables 10 | var cells : int = 20 11 | var cell_size : int = 50 12 | 13 | #food variables 14 | var food_pos : Vector2 15 | var regen_food : bool = true 16 | 17 | #snake variables 18 | var old_data : Array 19 | var snake_data : Array 20 | var snake : Array 21 | 22 | #movement variables 23 | var start_pos = Vector2(9, 9) 24 | var up = Vector2(0, -1) 25 | var down = Vector2(0, 1) 26 | var left = Vector2(-1, 0) 27 | var right = Vector2(1, 0) 28 | var move_direction : Vector2 29 | var can_move: bool 30 | 31 | # Called when the node enters the scene tree for the first time. 32 | func _ready(): 33 | new_game() 34 | 35 | func new_game(): 36 | get_tree().paused = false 37 | get_tree().call_group("segments", "queue_free") 38 | $GameOverMenu.hide() 39 | score = 0 40 | $Hud.get_node("ScoreLabel").text = "SCORE: " + str(score) 41 | move_direction = up 42 | can_move = true 43 | generate_snake() 44 | move_food() 45 | 46 | func generate_snake(): 47 | old_data.clear() 48 | snake_data.clear() 49 | snake.clear() 50 | #starting with the start_pos, create tail segments vertically down 51 | for i in range(3): 52 | add_segment(start_pos + Vector2(0, i)) 53 | 54 | func add_segment(pos): 55 | snake_data.append(pos) 56 | var SnakeSegment = snake_scene.instantiate() 57 | SnakeSegment.position = (pos * cell_size) + Vector2(0, cell_size) 58 | add_child(SnakeSegment) 59 | snake.append(SnakeSegment) 60 | 61 | # Called every frame. 'delta' is the elapsed time since the previous frame. 62 | func _process(delta): 63 | move_snake() 64 | 65 | func move_snake(): 66 | if can_move: 67 | #update movement from keypresses 68 | if Input.is_action_just_pressed("move_down") and move_direction != up: 69 | move_direction = down 70 | can_move = false 71 | if not game_started: 72 | start_game() 73 | if Input.is_action_just_pressed("move_up") and move_direction != down: 74 | move_direction = up 75 | can_move = false 76 | if not game_started: 77 | start_game() 78 | if Input.is_action_just_pressed("move_left") and move_direction != right: 79 | move_direction = left 80 | can_move = false 81 | if not game_started: 82 | start_game() 83 | if Input.is_action_just_pressed("move_right") and move_direction != left: 84 | move_direction = right 85 | can_move = false 86 | if not game_started: 87 | start_game() 88 | 89 | func start_game(): 90 | game_started = true 91 | $MoveTimer.start() 92 | 93 | 94 | func _on_move_timer_timeout(): 95 | #allow snake movement 96 | can_move = true 97 | #use the snake's previous position to move the segments 98 | old_data = [] + snake_data 99 | snake_data[0] += move_direction 100 | for i in range(len(snake_data)): 101 | #move all the segments along by one 102 | if i > 0: 103 | snake_data[i] = old_data[i - 1] 104 | snake[i].position = (snake_data[i] * cell_size) + Vector2(0, cell_size) 105 | check_out_of_bounds() 106 | check_self_eaten() 107 | check_food_eaten() 108 | 109 | func check_out_of_bounds(): 110 | if snake_data[0].x < 0 or snake_data[0].x > cells - 1 or snake_data[0].y < 0 or snake_data[0].y > cells - 1: 111 | end_game() 112 | 113 | func check_self_eaten(): 114 | for i in range(1, len(snake_data)): 115 | if snake_data[0] == snake_data[i]: 116 | end_game() 117 | 118 | func check_food_eaten(): 119 | #if snake eats the food, add a segment and move the food 120 | if snake_data[0] == food_pos: 121 | score += 1 122 | $Hud.get_node("ScoreLabel").text = "SCORE: " + str(score) 123 | add_segment(old_data[-1]) 124 | move_food() 125 | 126 | func move_food(): 127 | while regen_food: 128 | regen_food = false 129 | food_pos = Vector2(randi_range(0, cells - 1), randi_range(0, cells - 1)) 130 | for i in snake_data: 131 | if food_pos == i: 132 | regen_food = true 133 | $Food.position = (food_pos * cell_size)+ Vector2(0, cell_size) 134 | regen_food = true 135 | 136 | func end_game(): 137 | $GameOverMenu.show() 138 | $MoveTimer.stop() 139 | game_started = false 140 | get_tree().paused = true 141 | 142 | 143 | func _on_game_over_menu_restart(): 144 | new_game() 145 | -------------------------------------------------------------------------------- /scenes/main.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=7 format=3 uid="uid://esywf0ii8i6u"] 2 | 3 | [ext_resource type="Script" path="res://scenes/main.gd" id="1_fmw02"] 4 | [ext_resource type="Texture2D" uid="uid://ckds54seb4bfe" path="res://assets/bg.png" id="2_fps3c"] 5 | [ext_resource type="PackedScene" uid="uid://beaa828xfn3nx" path="res://scenes/snake_segment.tscn" id="2_io2sj"] 6 | [ext_resource type="PackedScene" uid="uid://tmcq5gpw66gu" path="res://scenes/hud.tscn" id="3_s77pf"] 7 | [ext_resource type="Texture2D" uid="uid://bs828gnkhbpqx" path="res://assets/apple.png" id="5_sxedt"] 8 | [ext_resource type="PackedScene" uid="uid://cad25ojl47q8" path="res://scenes/game_over_menu.tscn" id="6_xkat8"] 9 | 10 | [node name="Main" type="Node"] 11 | script = ExtResource("1_fmw02") 12 | snake_scene = ExtResource("2_io2sj") 13 | 14 | [node name="Background" type="Sprite2D" parent="."] 15 | position = Vector2(500, 550) 16 | texture = ExtResource("2_fps3c") 17 | 18 | [node name="Hud" parent="." instance=ExtResource("3_s77pf")] 19 | 20 | [node name="MoveTimer" type="Timer" parent="."] 21 | wait_time = 0.1 22 | 23 | [node name="Food" type="Sprite2D" parent="."] 24 | texture = ExtResource("5_sxedt") 25 | offset = Vector2(25, 25) 26 | 27 | [node name="GameOverMenu" parent="." instance=ExtResource("6_xkat8")] 28 | process_mode = 2 29 | 30 | [connection signal="timeout" from="MoveTimer" to="." method="_on_move_timer_timeout"] 31 | [connection signal="restart" from="GameOverMenu" to="." method="_on_game_over_menu_restart"] 32 | -------------------------------------------------------------------------------- /scenes/snake_segment.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=3 uid="uid://beaa828xfn3nx"] 2 | 3 | [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mb3th"] 4 | bg_color = Color(0.192157, 0.654902, 0, 0.784314) 5 | border_width_left = 1 6 | border_width_top = 1 7 | border_width_right = 1 8 | border_width_bottom = 1 9 | border_color = Color(0.270588, 0.580392, 0, 1) 10 | corner_radius_top_left = 5 11 | corner_radius_top_right = 5 12 | corner_radius_bottom_right = 5 13 | corner_radius_bottom_left = 5 14 | 15 | [node name="SnakeSegment" type="Panel" groups=["segments"]] 16 | offset_right = 50.0 17 | offset_bottom = 50.0 18 | theme_override_styles/panel = SubResource("StyleBoxFlat_mb3th") 19 | --------------------------------------------------------------------------------