├── .gitignore ├── icon.png ├── line.png ├── demo └── demo.gif ├── tex ├── glow2.png ├── glow2.png.import ├── glow2.svg.import └── glow2.svg ├── fonts └── WireOne.ttf ├── sfx ├── 244195__cima__snare1.wav ├── 106290__pder__mein-kick-1.wav ├── 380509__jimthecab__hang-drum-9.wav ├── 73537__snipperbes__classiclaser.wav ├── 244195__cima__snare1.wav.import ├── 106290__pder__mein-kick-1.wav.import ├── 380509__jimthecab__hang-drum-9.wav.import └── 73537__snipperbes__classiclaser.wav.import ├── global_audioplayer.gd ├── default_env.tres ├── emitter.tscn ├── line.tscn ├── line.gd ├── icon.png.import ├── line.png.import ├── project.godot ├── global_audiocontainer.tscn ├── README.md ├── shockwave.tscn ├── LICENSE ├── main.gd ├── emitter.gd ├── ball.tscn ├── default_bus_layout.tres ├── ball.gd ├── main.tscn └── export_presets.cfg /.gitignore: -------------------------------------------------------------------------------- 1 | .import 2 | .import 3 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bauxitedev/balldrop/HEAD/icon.png -------------------------------------------------------------------------------- /line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bauxitedev/balldrop/HEAD/line.png -------------------------------------------------------------------------------- /demo/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bauxitedev/balldrop/HEAD/demo/demo.gif -------------------------------------------------------------------------------- /tex/glow2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bauxitedev/balldrop/HEAD/tex/glow2.png -------------------------------------------------------------------------------- /fonts/WireOne.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bauxitedev/balldrop/HEAD/fonts/WireOne.ttf -------------------------------------------------------------------------------- /sfx/244195__cima__snare1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bauxitedev/balldrop/HEAD/sfx/244195__cima__snare1.wav -------------------------------------------------------------------------------- /sfx/106290__pder__mein-kick-1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bauxitedev/balldrop/HEAD/sfx/106290__pder__mein-kick-1.wav -------------------------------------------------------------------------------- /sfx/380509__jimthecab__hang-drum-9.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bauxitedev/balldrop/HEAD/sfx/380509__jimthecab__hang-drum-9.wav -------------------------------------------------------------------------------- /sfx/73537__snipperbes__classiclaser.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bauxitedev/balldrop/HEAD/sfx/73537__snipperbes__classiclaser.wav -------------------------------------------------------------------------------- /global_audioplayer.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | var container 4 | func _ready(): 5 | 6 | container = preload("res://global_audiocontainer.tscn").instance() 7 | 8 | func play_at(sound_name, pos, pitch): 9 | 10 | var player = container.get_node("player_" + sound_name).duplicate() 11 | player.stream = player.stream.duplicate() 12 | player.stream.mix_rate = pitch * 44100.0 13 | player.position = pos 14 | player.play() 15 | $"/root".add_child(player) 16 | 17 | player.connect("finished", player, "queue_free") -------------------------------------------------------------------------------- /default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | sky_top_color = Color( 0.0470588, 0.454902, 0.976471, 1 ) 5 | sky_horizon_color = Color( 0.556863, 0.823529, 0.909804, 1 ) 6 | sky_curve = 0.25 7 | ground_bottom_color = Color( 0.101961, 0.145098, 0.188235, 1 ) 8 | ground_horizon_color = Color( 0.482353, 0.788235, 0.952941, 1 ) 9 | ground_curve = 0.01 10 | sun_energy = 16.0 11 | 12 | [resource] 13 | background_mode = 2 14 | background_sky = SubResource( 1 ) 15 | 16 | -------------------------------------------------------------------------------- /sfx/244195__cima__snare1.wav.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="wav" 4 | type="AudioStreamSample" 5 | path="res://.import/244195__cima__snare1.wav-47ffe1487ad16f0ed4c12dcb4a11ef7c.sample" 6 | 7 | [deps] 8 | 9 | source_file="res://sfx/244195__cima__snare1.wav" 10 | dest_files=[ "res://.import/244195__cima__snare1.wav-47ffe1487ad16f0ed4c12dcb4a11ef7c.sample" ] 11 | 12 | [params] 13 | 14 | force/8_bit=false 15 | force/mono=false 16 | force/max_rate=false 17 | force/max_rate_hz=44100 18 | edit/trim=true 19 | edit/normalize=true 20 | edit/loop=false 21 | compress/mode=0 22 | -------------------------------------------------------------------------------- /sfx/106290__pder__mein-kick-1.wav.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="wav" 4 | type="AudioStreamSample" 5 | path="res://.import/106290__pder__mein-kick-1.wav-678319b21193992aad14b655080ed64d.sample" 6 | 7 | [deps] 8 | 9 | source_file="res://sfx/106290__pder__mein-kick-1.wav" 10 | dest_files=[ "res://.import/106290__pder__mein-kick-1.wav-678319b21193992aad14b655080ed64d.sample" ] 11 | 12 | [params] 13 | 14 | force/8_bit=false 15 | force/mono=false 16 | force/max_rate=false 17 | force/max_rate_hz=44100 18 | edit/trim=true 19 | edit/normalize=true 20 | edit/loop=false 21 | compress/mode=0 22 | -------------------------------------------------------------------------------- /sfx/380509__jimthecab__hang-drum-9.wav.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="wav" 4 | type="AudioStreamSample" 5 | path="res://.import/380509__jimthecab__hang-drum-9.wav-eb6e5c2ecc13620db264f4ce960d35e1.sample" 6 | 7 | [deps] 8 | 9 | source_file="res://sfx/380509__jimthecab__hang-drum-9.wav" 10 | dest_files=[ "res://.import/380509__jimthecab__hang-drum-9.wav-eb6e5c2ecc13620db264f4ce960d35e1.sample" ] 11 | 12 | [params] 13 | 14 | force/8_bit=false 15 | force/mono=false 16 | force/max_rate=false 17 | force/max_rate_hz=44100 18 | edit/trim=true 19 | edit/normalize=true 20 | edit/loop=false 21 | compress/mode=0 22 | -------------------------------------------------------------------------------- /sfx/73537__snipperbes__classiclaser.wav.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="wav" 4 | type="AudioStreamSample" 5 | path="res://.import/73537__snipperbes__classiclaser.wav-8a04fca7ba6a386c810632aa2272b3e1.sample" 6 | 7 | [deps] 8 | 9 | source_file="res://sfx/73537__snipperbes__classiclaser.wav" 10 | dest_files=[ "res://.import/73537__snipperbes__classiclaser.wav-8a04fca7ba6a386c810632aa2272b3e1.sample" ] 11 | 12 | [params] 13 | 14 | force/8_bit=false 15 | force/mono=false 16 | force/max_rate=false 17 | force/max_rate_hz=44100 18 | edit/trim=true 19 | edit/normalize=true 20 | edit/loop=false 21 | compress/mode=0 22 | -------------------------------------------------------------------------------- /emitter.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=2] 2 | 3 | [ext_resource path="res://emitter.gd" type="Script" id=1] 4 | [ext_resource path="res://tex/glow2.png" type="Texture" id=2] 5 | 6 | [sub_resource type="CanvasItemMaterial" id=1] 7 | blend_mode = 1 8 | 9 | [node name="emitter" type="Node2D"] 10 | script = ExtResource( 1 ) 11 | sound = "bell" 12 | 13 | [node name="timer" type="Timer" parent="."] 14 | autostart = true 15 | 16 | [node name="spr" type="Sprite" parent="."] 17 | modulate = Color( 0.183594, 0.183594, 0.183594, 1 ) 18 | material = SubResource( 1 ) 19 | texture = ExtResource( 2 ) 20 | 21 | [connection signal="timeout" from="timer" to="." method="_on_timer_timeout"] 22 | -------------------------------------------------------------------------------- /line.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=2] 2 | 3 | [ext_resource path="res://line.gd" type="Script" id=1] 4 | [ext_resource path="res://line.png" type="Texture" id=2] 5 | 6 | [sub_resource type="CapsuleShape2D" id=1] 7 | resource_local_to_scene = true 8 | 9 | [node name="line" type="StaticBody2D" groups=[ 10 | "line", 11 | ]] 12 | input_pickable = true 13 | script = ExtResource( 1 ) 14 | 15 | [node name="shape" type="CollisionShape2D" parent="."] 16 | shape = SubResource( 1 ) 17 | 18 | [node name="spr" type="Sprite" parent="."] 19 | modulate = Color( 1.2, 1.2, 1.2, 1 ) 20 | texture = ExtResource( 2 ) 21 | 22 | [connection signal="input_event" from="." to="." method="_on_line_input_event"] 23 | -------------------------------------------------------------------------------- /line.gd: -------------------------------------------------------------------------------- 1 | extends StaticBody2D 2 | 3 | var a = Vector2() 4 | var b = Vector2() 5 | 6 | func set_ends(p1, p2): 7 | 8 | a = p1 9 | b = p2 10 | 11 | # update the capsule 12 | 13 | $shape.shape.height = a.distance_to(b) 14 | $shape.position = (a + b) / 2 15 | $shape.rotation = a.angle_to_point(b) + PI / 2 16 | 17 | # update the sprite 18 | # (using a sprite so we get anti aliasing) 19 | 20 | $spr.position = (a + b) / 2 21 | $spr.rotation = $shape.rotation + PI / 2 22 | $spr.scale = Vector2(a.distance_to(b), 1) 23 | 24 | 25 | 26 | 27 | func _on_line_input_event(viewport, event, shape_idx): 28 | 29 | # delete line when right clicked 30 | if event is InputEventMouseButton: 31 | if event.pressed and event.button_index == BUTTON_RIGHT: 32 | queue_free() 33 | -------------------------------------------------------------------------------- /icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://icon.png" 13 | dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /line.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/line.png-e86270f75d5b95d6fc8473dba8c1eec6.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://line.png" 13 | dest_files=[ "res://.import/line.png-e86270f75d5b95d6fc8473dba8c1eec6.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /tex/glow2.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/glow2.png-76735d130593fa13ae71d315b0855871.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://tex/glow2.png" 13 | dest_files=[ "res://.import/glow2.png-76735d130593fa13ae71d315b0855871.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /tex/glow2.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/glow2.svg-5d598b970613f059f6d3ffdc178062c8.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://tex/glow2.svg" 13 | dest_files=[ "res://.import/glow2.svg-5d598b970613f059f6d3ffdc178062c8.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 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=4 10 | 11 | _global_script_classes=[ ] 12 | _global_script_class_icons={ 13 | 14 | } 15 | 16 | [application] 17 | 18 | config/name="balldrop." 19 | run/main_scene="res://main.tscn" 20 | config/icon="res://icon.png" 21 | 22 | [autoload] 23 | 24 | AudioPlayer="*res://global_audioplayer.gd" 25 | 26 | [display] 27 | 28 | window/size/width=1280 29 | window/size/height=720 30 | window/size/fullscreen=true 31 | 32 | [rendering] 33 | 34 | quality/filters/msaa=3 35 | environment/default_environment="res://default_env.tres" 36 | -------------------------------------------------------------------------------- /global_audiocontainer.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=5 format=2] 2 | 3 | [ext_resource path="res://sfx/380509__jimthecab__hang-drum-9.wav" type="AudioStream" id=1] 4 | [ext_resource path="res://sfx/106290__pder__mein-kick-1.wav" type="AudioStream" id=2] 5 | [ext_resource path="res://sfx/244195__cima__snare1.wav" type="AudioStream" id=3] 6 | [ext_resource path="res://sfx/73537__snipperbes__classiclaser.wav" type="AudioStream" id=4] 7 | 8 | [node name="container" type="Node"] 9 | 10 | [node name="player_bell" type="AudioStreamPlayer2D" parent="."] 11 | stream = ExtResource( 1 ) 12 | bus = "Bell" 13 | 14 | [node name="player_kick" type="AudioStreamPlayer2D" parent="."] 15 | stream = ExtResource( 2 ) 16 | volume_db = 10.571 17 | bus = "Reverb" 18 | 19 | [node name="player_snare" type="AudioStreamPlayer2D" parent="."] 20 | stream = ExtResource( 3 ) 21 | volume_db = 5.0 22 | bus = "Reverb" 23 | 24 | [node name="player_laser" type="AudioStreamPlayer2D" parent="."] 25 | stream = ExtResource( 4 ) 26 | volume_db = -6.0 27 | bus = "Reverb" 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # balldrop. 2 | An experimental musical instrument, made with Godot 3.1. 3 | 4 | ![Demo](demo/demo.gif) 5 | 6 | Do you remember [BallDroppings](http://www.thaoh.net/BallDroppings/)? I decided to make something similar in Godot, adding some more features and more polished graphics. It turned out to be quite fun to play with, and given some patience you can actually make some cool music with it. 7 | 8 | The idea is that there are multiple emitters on the screen that periodically spawn balls. When the balls touch a line, a sound is emitted. The kind of sound depends on the color of the ball, and the pitch of the sound depends on the velocity of the ball. 9 | 10 | You can draw lines by holding the left mouse button and erase them again by pressing the right mouse button. 11 | 12 | You can get the executables [here](https://bauxite.itch.io/balldrop). 13 | 14 | [Here's a video of it in action.](https://www.youtube.com/watch?v=yhKnGPcZmE4) 15 | 16 | # License 17 | The code is MIT, the sound effects are CC0 (they were found on Freesound). 18 | -------------------------------------------------------------------------------- /shockwave.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=2] 2 | 3 | [ext_resource path="res://tex/glow2.png" type="Texture" id=1] 4 | 5 | [sub_resource type="CanvasItemMaterial" id=1] 6 | blend_mode = 1 7 | 8 | [sub_resource type="Animation" id=2] 9 | resource_name = "wave" 10 | tracks/0/type = "value" 11 | tracks/0/path = NodePath("spr:scale") 12 | tracks/0/interp = 1 13 | tracks/0/loop_wrap = true 14 | tracks/0/imported = false 15 | tracks/0/enabled = true 16 | tracks/0/keys = { 17 | "times": PoolRealArray( 0, 1 ), 18 | "transitions": PoolRealArray( 0.222165, 0.260803 ), 19 | "update": 0, 20 | "values": [ Vector2( 1, 1 ), Vector2( 1e-05, 1e-05 ) ] 21 | } 22 | 23 | [node name="shockwave" type="Node2D"] 24 | 25 | [node name="timer" type="Timer" parent="."] 26 | one_shot = true 27 | autostart = true 28 | 29 | [node name="spr" type="Sprite" parent="."] 30 | material = SubResource( 1 ) 31 | texture = ExtResource( 1 ) 32 | 33 | [node name="animation" type="AnimationPlayer" parent="."] 34 | autoplay = "wave" 35 | anims/wave = SubResource( 2 ) 36 | 37 | [connection signal="timeout" from="timer" to="." method="queue_free"] 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Bauxitedev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /main.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | var current_line = null 4 | var p1 = Vector2() 5 | var p2 = p1 6 | 7 | var last_mouse_pos = Vector2() 8 | 9 | func _ready(): 10 | 11 | yield(get_tree().create_timer(5), "timeout") 12 | 13 | spawn_emitter("bell", Vector2(0.3, 0.2)) 14 | spawn_emitter("kick", Vector2(0.3, 0.6)) 15 | spawn_emitter("snare", Vector2(0.7, 0.2)) 16 | spawn_emitter("laser", Vector2(0.7, 0.6)) 17 | 18 | 19 | func spawn_emitter(name, pos): 20 | var emitter = preload("res://emitter.tscn").instance() 21 | emitter.sound = name 22 | emitter.position = pos * OS.window_size 23 | add_child(emitter) 24 | 25 | func _process(delta): 26 | 27 | last_mouse_pos = $cam.get_global_mouse_position() 28 | 29 | p2 = last_mouse_pos 30 | 31 | if current_line != null: 32 | current_line.set_ends(p1, p2) 33 | 34 | func _input(event): 35 | 36 | if (event.is_action_released("ui_cancel")): 37 | get_tree().quit() 38 | 39 | if event is InputEventMouseButton: 40 | 41 | if event.pressed and event.button_index == BUTTON_LEFT: 42 | 43 | p1 = last_mouse_pos 44 | p2 = p1 45 | 46 | current_line = preload("res://line.tscn").instance() 47 | current_line.set_ends(p1, p2) 48 | add_child(current_line) 49 | 50 | else: 51 | 52 | current_line = null -------------------------------------------------------------------------------- /emitter.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | export(String, "bell", "kick", "snare", "laser") var sound 4 | 5 | var angle = 0 6 | 7 | func _on_timer_timeout(): 8 | 9 | var ball = preload("res://ball.tscn").instance() 10 | ball.position = position 11 | ball.sound = sound 12 | ball.vel = Vector2(4, 0).rotated(deg2rad(angle+45)) 13 | ball.notes = gen_notes() 14 | 15 | 16 | var ball_container = get_tree().get_nodes_in_group("ball_container")[0] 17 | ball_container.add_child(ball) 18 | 19 | angle += 90 20 | angle %= 360 21 | 22 | 23 | func gen_notes(): 24 | 25 | var notes = [] 26 | 27 | #seed(hash(angle+1)) 28 | seed(3) # seed = 2 is nice 29 | randi() 30 | 31 | var base_pitch = 0.06#rand_range(0.05, 0.2) 32 | # print(base_pitch) 33 | 34 | var note1 = randi() % 12 35 | var note2 = randi() % 12 36 | var note3 = randi() % 12 37 | 38 | notes.push_back(2 * base_pitch) 39 | notes.push_back(pow(2, 1 + note1/12.0) * base_pitch) 40 | notes.push_back(pow(2, 1 + note2/12.0) * base_pitch) 41 | notes.push_back(pow(2, 1 + note3/12.0) * base_pitch) 42 | 43 | for i in 100: 44 | var n1 = notes[-1] 45 | var n2 = notes[-2] 46 | var n3 = notes[-3] 47 | var n4 = notes[-4] 48 | 49 | notes.push_back(n1 * 2) 50 | notes.push_back(n2 * 2) 51 | notes.push_back(n3 * 2) 52 | notes.push_back(n4 * 2) 53 | 54 | randomize() 55 | 56 | return notes -------------------------------------------------------------------------------- /ball.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=8 format=2] 2 | 3 | [ext_resource path="res://ball.gd" type="Script" id=1] 4 | [ext_resource path="res://tex/glow2.png" type="Texture" id=2] 5 | 6 | [sub_resource type="CanvasItemMaterial" id=1] 7 | blend_mode = 1 8 | 9 | [sub_resource type="Gradient" id=2] 10 | offsets = PoolRealArray( 0, 0.00358423, 0.0860215, 1 ) 11 | colors = PoolColorArray( 0, 0, 0, 1, 0.0382166, 0.0382166, 0.0382166, 1, 1, 1, 1, 1, 0.044586, 0.044586, 0.044586, 1 ) 12 | 13 | [sub_resource type="GradientTexture" id=3] 14 | gradient = SubResource( 2 ) 15 | 16 | [sub_resource type="ParticlesMaterial" id=4] 17 | flag_disable_z = true 18 | spread = 180.0 19 | gravity = Vector3( 0, 0, 0 ) 20 | initial_velocity = 10.0 21 | initial_velocity_random = 1.0 22 | orbit_velocity = 0.0 23 | orbit_velocity_random = 0.0 24 | scale = 0.0 25 | scale_random = 1.0 26 | color_ramp = SubResource( 3 ) 27 | 28 | [sub_resource type="CanvasItemMaterial" id=5] 29 | blend_mode = 1 30 | 31 | [node name="ball" type="Node2D"] 32 | script = ExtResource( 1 ) 33 | 34 | [node name="parts" type="Particles2D" parent="."] 35 | modulate = Color( 0.21875, 0.21875, 0.21875, 0.21 ) 36 | material = SubResource( 1 ) 37 | amount = 80 38 | visibility_rect = Rect2( -1000, -1000, 2000, 2000 ) 39 | local_coords = false 40 | process_material = SubResource( 4 ) 41 | texture = ExtResource( 2 ) 42 | 43 | [node name="spr" type="Sprite" parent="."] 44 | modulate = Color( 1, 0.509363, 0.509363, 1 ) 45 | material = SubResource( 5 ) 46 | texture = ExtResource( 2 ) 47 | 48 | -------------------------------------------------------------------------------- /default_bus_layout.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="AudioBusLayout" load_steps=7 format=2] 2 | 3 | [sub_resource type="AudioEffectHighPassFilter" id=1] 4 | resource_name = "HighPassFilter" 5 | cutoff_hz = 20.0 6 | 7 | [sub_resource type="AudioEffectReverb" id=2] 8 | resource_name = "Reverb" 9 | room_size = 0.92 10 | damping = 1.0 11 | hipass = 0.01 12 | wet = 0.11 13 | 14 | [sub_resource type="AudioEffectChorus" id=3] 15 | resource_name = "Chorus" 16 | wet = 0.7 17 | voice/1/delay_ms = 5.0 18 | voice/1/rate_hz = 0.5 19 | voice/2/delay_ms = 10.0 20 | voice/2/rate_hz = 0.4 21 | voice/2/cutoff_hz = 7328.0 22 | 23 | [sub_resource type="AudioEffectCompressor" id=4] 24 | resource_name = "Compressor" 25 | ratio = 43.6 26 | attack_us = 80.0 27 | release_ms = 400.0 28 | 29 | [sub_resource type="AudioEffectHighPassFilter" id=5] 30 | resource_name = "HighPassFilter" 31 | cutoff_hz = 20.0 32 | 33 | [sub_resource type="AudioEffectLowShelfFilter" id=6] 34 | resource_name = "LowShelfFilter" 35 | 36 | [resource] 37 | bus/0/volume_db = -8.5 38 | bus/1/name = "Reverb" 39 | bus/1/solo = false 40 | bus/1/mute = false 41 | bus/1/bypass_fx = false 42 | bus/1/volume_db = 0.0 43 | bus/1/send = "Master" 44 | bus/1/effect/0/effect = SubResource( 1 ) 45 | bus/1/effect/0/enabled = true 46 | bus/1/effect/1/effect = SubResource( 2 ) 47 | bus/1/effect/1/enabled = true 48 | bus/2/name = "Bell" 49 | bus/2/solo = false 50 | bus/2/mute = false 51 | bus/2/bypass_fx = false 52 | bus/2/volume_db = 0.0 53 | bus/2/send = "Reverb" 54 | bus/2/effect/0/effect = SubResource( 3 ) 55 | bus/2/effect/0/enabled = true 56 | bus/2/effect/1/effect = SubResource( 4 ) 57 | bus/2/effect/1/enabled = true 58 | bus/2/effect/2/effect = SubResource( 5 ) 59 | bus/2/effect/2/enabled = true 60 | bus/2/effect/3/effect = SubResource( 6 ) 61 | bus/2/effect/3/enabled = true 62 | 63 | -------------------------------------------------------------------------------- /ball.gd: -------------------------------------------------------------------------------- 1 | extends Node2D 2 | 3 | var vel = Vector2() 4 | var prevpos = Vector2() 5 | var sound 6 | 7 | var notes = [] # TODO generate this globally 8 | 9 | func _ready(): 10 | 11 | prevpos = position 12 | 13 | var goalcol = Color(0,0,0) 14 | 15 | match sound: 16 | 17 | "bell": goalcol = Color(0,1,1) 18 | "kick": goalcol = Color(1,0,0) 19 | "snare": goalcol = Color(0,1,0) 20 | "laser": goalcol = Color(1,1,0) 21 | 22 | $spr.modulate = goalcol 23 | goalcol *= 0.3 24 | 25 | $parts.modulate = goalcol 26 | 27 | 28 | 29 | 30 | func _physics_process(delta): 31 | 32 | 33 | if position.y > 2000: 34 | queue_free() 35 | 36 | vel += Vector2(0, 20 * 1) * delta 37 | prevpos = position 38 | position += vel 39 | 40 | # manual collision for now 41 | 42 | var colliders = get_tree().get_nodes_in_group("line") 43 | 44 | for c in colliders: 45 | 46 | var p1 = c.a 47 | var p2 = c.b 48 | var norm = (p2-p1).tangent().normalized() 49 | var coll = Geometry.segment_intersects_segment_2d(prevpos, position, p1, p2) 50 | 51 | if coll != null: 52 | # print(norm) 53 | 54 | if vel.length() > 1.0: 55 | 56 | var pitch = vel.length() 57 | 58 | if sound == "bell": 59 | pass 60 | else: 61 | pitch *= 2 62 | 63 | pitch = notes[round(pitch)] #pow(2, round(log(pitch) / log(2)) / 3.0) 64 | 65 | #pitch *= round(rand_range(1,7)) 66 | pitch += rand_range(-1,1)*0.002 67 | AudioPlayer.play_at(sound, position, pitch) 68 | 69 | 70 | 71 | if vel.dot(norm) > 0: 72 | norm = -norm 73 | 74 | var restitution = 0.8 75 | vel = (-vel.reflect(norm)).linear_interpolate(vel.slide(norm), 1-restitution) 76 | 77 | position = coll + norm * 0.1 78 | 79 | var shockwave = preload("res://shockwave.tscn").instance() 80 | shockwave.position = position 81 | shockwave.modulate = $spr.modulate 82 | get_parent().add_child(shockwave) 83 | 84 | -------------------------------------------------------------------------------- /tex/glow2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 43 | 47 | 48 | 50 | 51 | 53 | image/svg+xml 54 | 56 | 57 | 58 | 59 | 60 | 64 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /main.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=11 format=2] 2 | 3 | [ext_resource path="res://main.gd" type="Script" id=1] 4 | [ext_resource path="res://fonts/WireOne.ttf" type="DynamicFontData" id=2] 5 | 6 | [sub_resource type="Environment" id=1] 7 | background_mode = 4 8 | glow_enabled = true 9 | glow_levels/1 = true 10 | glow_levels/2 = true 11 | glow_levels/4 = true 12 | glow_levels/6 = true 13 | glow_levels/7 = true 14 | glow_intensity = 0.4 15 | glow_blend_mode = 1 16 | glow_bicubic_upscale = true 17 | 18 | [sub_resource type="DynamicFont" id=2] 19 | size = 160 20 | font_data = ExtResource( 2 ) 21 | 22 | [sub_resource type="DynamicFont" id=3] 23 | size = 60 24 | font_data = ExtResource( 2 ) 25 | 26 | [sub_resource type="Animation" id=4] 27 | resource_name = "fade_in" 28 | length = 10.0 29 | tracks/0/type = "value" 30 | tracks/0/path = NodePath("vsplit/logo:modulate") 31 | tracks/0/interp = 2 32 | tracks/0/loop_wrap = true 33 | tracks/0/imported = false 34 | tracks/0/enabled = true 35 | tracks/0/keys = { 36 | "times": PoolRealArray( 0, 0.2, 1.4 ), 37 | "transitions": PoolRealArray( 1, 1, 1 ), 38 | "update": 0, 39 | "values": [ Color( 1, 1, 1, 0 ), Color( 1, 1, 1, 0 ), Color( 1, 1, 1, 1 ) ] 40 | } 41 | tracks/1/type = "value" 42 | tracks/1/path = NodePath("vsplit/controls:modulate") 43 | tracks/1/interp = 2 44 | tracks/1/loop_wrap = true 45 | tracks/1/imported = false 46 | tracks/1/enabled = true 47 | tracks/1/keys = { 48 | "times": PoolRealArray( 0, 2, 3.6 ), 49 | "transitions": PoolRealArray( 1, 1, 1 ), 50 | "update": 0, 51 | "values": [ Color( 1, 1, 1, 0 ), Color( 1, 1, 1, 0 ), Color( 1, 1, 1, 1 ) ] 52 | } 53 | tracks/2/type = "value" 54 | tracks/2/path = NodePath("vsplit:modulate") 55 | tracks/2/interp = 2 56 | tracks/2/loop_wrap = true 57 | tracks/2/imported = false 58 | tracks/2/enabled = true 59 | tracks/2/keys = { 60 | "times": PoolRealArray( 0, 8.2, 10 ), 61 | "transitions": PoolRealArray( 1, 1, 1 ), 62 | "update": 0, 63 | "values": [ Color( 1, 1, 1, 1 ), Color( 1, 1, 1, 1 ), Color( 1, 1, 1, 0 ) ] 64 | } 65 | 66 | [sub_resource type="Shader" id=5] 67 | code = "shader_type canvas_item; 68 | 69 | uniform sampler2D gradient; 70 | 71 | float rand(vec2 co){ 72 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); 73 | } 74 | 75 | void fragment() 76 | { 77 | //Adding a little noise here to reduce the banding 78 | COLOR = texture(gradient, vec2(UV.y + (rand(UV)-0.5) * 0.04, 0)); 79 | }" 80 | 81 | [sub_resource type="Gradient" id=6] 82 | colors = PoolColorArray( 0.371094, 0.371094, 0.371094, 1, 0.117188, 0.117188, 0.117188, 1 ) 83 | 84 | [sub_resource type="GradientTexture" id=7] 85 | gradient = SubResource( 6 ) 86 | 87 | [sub_resource type="ShaderMaterial" id=8] 88 | shader = SubResource( 5 ) 89 | shader_param/gradient = SubResource( 7 ) 90 | 91 | [node name="main" type="WorldEnvironment"] 92 | environment = SubResource( 1 ) 93 | script = ExtResource( 1 ) 94 | 95 | [node name="ball_container" type="Node" parent="." groups=[ 96 | "ball_container", 97 | ]] 98 | 99 | [node name="cam" type="Camera2D" parent="."] 100 | position = Vector2( 640, 360 ) 101 | 102 | [node name="center" type="CenterContainer" parent="."] 103 | anchor_right = 1.0 104 | anchor_bottom = 1.0 105 | mouse_filter = 2 106 | 107 | [node name="vsplit" type="VSplitContainer" parent="center"] 108 | modulate = Color( 1, 1, 1, 0 ) 109 | margin_left = 426.0 110 | margin_top = 168.0 111 | margin_right = 853.0 112 | margin_bottom = 552.0 113 | mouse_filter = 2 114 | dragger_visibility = 2 115 | 116 | [node name="logo" type="Label" parent="center/vsplit"] 117 | margin_right = 427.0 118 | margin_bottom = 177.0 119 | custom_fonts/font = SubResource( 2 ) 120 | custom_colors/font_color_shadow = Color( 0, 0, 0, 1 ) 121 | text = "balldrop." 122 | align = 1 123 | valign = 1 124 | 125 | [node name="controls" type="Label" parent="center/vsplit"] 126 | margin_top = 177.0 127 | margin_right = 427.0 128 | margin_bottom = 384.0 129 | custom_fonts/font = SubResource( 3 ) 130 | custom_colors/font_color_shadow = Color( 0, 0, 0, 1 ) 131 | text = " 132 | draw lines: left mouse button 133 | erase lines: right mouse button" 134 | align = 1 135 | valign = 1 136 | 137 | [node name="anim" type="AnimationPlayer" parent="center"] 138 | autoplay = "fade_in" 139 | anims/fade_in = SubResource( 4 ) 140 | 141 | [node name="layer_bg" type="CanvasLayer" parent="."] 142 | layer = -1 143 | 144 | [node name="gradient" type="ColorRect" parent="layer_bg"] 145 | material = SubResource( 8 ) 146 | anchor_right = 1.0 147 | anchor_bottom = 1.0 148 | mouse_filter = 2 149 | color = Color( 0.184314, 0.0901961, 0.0901961, 1 ) 150 | 151 | -------------------------------------------------------------------------------- /export_presets.cfg: -------------------------------------------------------------------------------- 1 | [preset.0] 2 | 3 | name="Android" 4 | platform="Android" 5 | runnable=true 6 | custom_features="" 7 | export_filter="all_resources" 8 | include_filter="" 9 | exclude_filter="" 10 | patch_list=PoolStringArray( ) 11 | 12 | [preset.0.options] 13 | 14 | graphics/32_bits_framebuffer=true 15 | one_click_deploy/clear_previous_install=true 16 | custom_package/debug="" 17 | custom_package/release="" 18 | command_line/extra_args="" 19 | version/code=1 20 | version/name="1.0" 21 | package/unique_name="org.godotengine.$genname" 22 | package/name="Balldrop" 23 | package/signed=true 24 | screen/immersive_mode=true 25 | screen/orientation=0 26 | screen/support_small=true 27 | screen/support_normal=true 28 | screen/support_large=true 29 | screen/support_xlarge=true 30 | launcher_icons/xxxhdpi_192x192="" 31 | launcher_icons/xxhdpi_144x144="" 32 | launcher_icons/xhdpi_96x96="" 33 | launcher_icons/hdpi_72x72="" 34 | launcher_icons/mdpi_48x48="" 35 | keystore/release="" 36 | keystore/release_user="" 37 | keystore/release_password="" 38 | apk_expansion/enable=false 39 | apk_expansion/SALT="" 40 | apk_expansion/public_key="" 41 | architectures/armeabi-v7a=true 42 | architectures/arm64-v8a=false 43 | architectures/x86=false 44 | architectures/x86_64=false 45 | permissions/access_checkin_properties=false 46 | permissions/access_coarse_location=false 47 | permissions/access_fine_location=false 48 | permissions/access_location_extra_commands=false 49 | permissions/access_mock_location=false 50 | permissions/access_network_state=false 51 | permissions/access_surface_flinger=false 52 | permissions/access_wifi_state=false 53 | permissions/account_manager=false 54 | permissions/add_voicemail=false 55 | permissions/authenticate_accounts=false 56 | permissions/battery_stats=false 57 | permissions/bind_accessibility_service=false 58 | permissions/bind_appwidget=false 59 | permissions/bind_device_admin=false 60 | permissions/bind_input_method=false 61 | permissions/bind_nfc_service=false 62 | permissions/bind_notification_listener_service=false 63 | permissions/bind_print_service=false 64 | permissions/bind_remoteviews=false 65 | permissions/bind_text_service=false 66 | permissions/bind_vpn_service=false 67 | permissions/bind_wallpaper=false 68 | permissions/bluetooth=false 69 | permissions/bluetooth_admin=false 70 | permissions/bluetooth_privileged=false 71 | permissions/brick=false 72 | permissions/broadcast_package_removed=false 73 | permissions/broadcast_sms=false 74 | permissions/broadcast_sticky=false 75 | permissions/broadcast_wap_push=false 76 | permissions/call_phone=false 77 | permissions/call_privileged=false 78 | permissions/camera=false 79 | permissions/capture_audio_output=false 80 | permissions/capture_secure_video_output=false 81 | permissions/capture_video_output=false 82 | permissions/change_component_enabled_state=false 83 | permissions/change_configuration=false 84 | permissions/change_network_state=false 85 | permissions/change_wifi_multicast_state=false 86 | permissions/change_wifi_state=false 87 | permissions/clear_app_cache=false 88 | permissions/clear_app_user_data=false 89 | permissions/control_location_updates=false 90 | permissions/delete_cache_files=false 91 | permissions/delete_packages=false 92 | permissions/device_power=false 93 | permissions/diagnostic=false 94 | permissions/disable_keyguard=false 95 | permissions/dump=false 96 | permissions/expand_status_bar=false 97 | permissions/factory_test=false 98 | permissions/flashlight=false 99 | permissions/force_back=false 100 | permissions/get_accounts=false 101 | permissions/get_package_size=false 102 | permissions/get_tasks=false 103 | permissions/get_top_activity_info=false 104 | permissions/global_search=false 105 | permissions/hardware_test=false 106 | permissions/inject_events=false 107 | permissions/install_location_provider=false 108 | permissions/install_packages=false 109 | permissions/install_shortcut=false 110 | permissions/internal_system_window=false 111 | permissions/internet=false 112 | permissions/kill_background_processes=false 113 | permissions/location_hardware=false 114 | permissions/manage_accounts=false 115 | permissions/manage_app_tokens=false 116 | permissions/manage_documents=false 117 | permissions/master_clear=false 118 | permissions/media_content_control=false 119 | permissions/modify_audio_settings=false 120 | permissions/modify_phone_state=false 121 | permissions/mount_format_filesystems=false 122 | permissions/mount_unmount_filesystems=false 123 | permissions/nfc=false 124 | permissions/persistent_activity=false 125 | permissions/process_outgoing_calls=false 126 | permissions/read_calendar=false 127 | permissions/read_call_log=false 128 | permissions/read_contacts=false 129 | permissions/read_external_storage=false 130 | permissions/read_frame_buffer=false 131 | permissions/read_history_bookmarks=false 132 | permissions/read_input_state=false 133 | permissions/read_logs=false 134 | permissions/read_phone_state=false 135 | permissions/read_profile=false 136 | permissions/read_sms=false 137 | permissions/read_social_stream=false 138 | permissions/read_sync_settings=false 139 | permissions/read_sync_stats=false 140 | permissions/read_user_dictionary=false 141 | permissions/reboot=false 142 | permissions/receive_boot_completed=false 143 | permissions/receive_mms=false 144 | permissions/receive_sms=false 145 | permissions/receive_wap_push=false 146 | permissions/record_audio=false 147 | permissions/reorder_tasks=false 148 | permissions/restart_packages=false 149 | permissions/send_respond_via_message=false 150 | permissions/send_sms=false 151 | permissions/set_activity_watcher=false 152 | permissions/set_alarm=false 153 | permissions/set_always_finish=false 154 | permissions/set_animation_scale=false 155 | permissions/set_debug_app=false 156 | permissions/set_orientation=false 157 | permissions/set_pointer_speed=false 158 | permissions/set_preferred_applications=false 159 | permissions/set_process_limit=false 160 | permissions/set_time=false 161 | permissions/set_time_zone=false 162 | permissions/set_wallpaper=false 163 | permissions/set_wallpaper_hints=false 164 | permissions/signal_persistent_processes=false 165 | permissions/status_bar=false 166 | permissions/subscribed_feeds_read=false 167 | permissions/subscribed_feeds_write=false 168 | permissions/system_alert_window=false 169 | permissions/transmit_ir=false 170 | permissions/uninstall_shortcut=false 171 | permissions/update_device_stats=false 172 | permissions/use_credentials=false 173 | permissions/use_sip=false 174 | permissions/vibrate=false 175 | permissions/wake_lock=false 176 | permissions/write_apn_settings=false 177 | permissions/write_calendar=false 178 | permissions/write_call_log=false 179 | permissions/write_contacts=false 180 | permissions/write_external_storage=false 181 | permissions/write_gservices=false 182 | permissions/write_history_bookmarks=false 183 | permissions/write_profile=false 184 | permissions/write_secure_settings=false 185 | permissions/write_settings=false 186 | permissions/write_sms=false 187 | permissions/write_social_stream=false 188 | permissions/write_sync_settings=false 189 | permissions/write_user_dictionary=false 190 | user_permissions/0=false 191 | user_permissions/1=false 192 | user_permissions/2=false 193 | user_permissions/3=false 194 | user_permissions/4=false 195 | user_permissions/5=false 196 | user_permissions/6=false 197 | user_permissions/7=false 198 | user_permissions/8=false 199 | user_permissions/9=false 200 | user_permissions/10=false 201 | user_permissions/11=false 202 | user_permissions/12=false 203 | user_permissions/13=false 204 | user_permissions/14=false 205 | user_permissions/15=false 206 | user_permissions/16=false 207 | user_permissions/17=false 208 | user_permissions/18=false 209 | user_permissions/19=false 210 | --------------------------------------------------------------------------------