diff --git a/Bezel.gd b/Bezel.gd index 814e794..f046488 100644 --- a/Bezel.gd +++ b/Bezel.gd @@ -1,15 +1,29 @@ -extends "res://main.gd" +tool +extends Node2D # Draw the bezel for radial gamemode var center := Vector2(0.0, 0.0) -func _draw(): - var bezel_colors := PoolColorArray([GameTheme.bezel_color]) - var bezel_points: PoolVector2Array - var screen_height2 := screen_height/2.0 +func arc_point_list(center: Vector2, radius: float, angle_from:=0.0, angle_to:=360.0, points:=20) -> PoolVector2Array: + var point_list = PoolVector2Array() + for i in range(points): + var angle = deg2rad(angle_from + i * (angle_to - angle_from) / (points-1)) +# point_list.push_back(center + Vector2(cos(angle), sin(angle)) * radius) + point_list.push_back(center + polar2cartesian(radius, angle)) + return point_list - draw_rect(Rect2(-screen_height2, -screen_height2, -x_margin, screen_height), GameTheme.bezel_color) - draw_rect(Rect2(screen_height2, -screen_height2, x_margin, screen_height), GameTheme.bezel_color) +func _draw(): +# var bezel_colors := PoolColorArray([GameTheme.bezel_color]) + var bezel_colors := PoolColorArray([Color.red]) + var bezel_points: PoolVector2Array + + var screen_size = $"/root".get_visible_rect().size + var screen_height = 1080 # min(screen_size.x, screen_size.y) + + var screen_height2 = screen_height/2.0 + +# draw_rect(Rect2(-screen_height2, -screen_height2, -x_margin, screen_height), GameTheme.bezel_color) +# draw_rect(Rect2(screen_height2, -screen_height2, x_margin, screen_height), GameTheme.bezel_color) bezel_points = arc_point_list(center, screen_height2, 0, -90) bezel_points.push_back(Vector2(screen_height2, -screen_height2)) @@ -26,3 +40,6 @@ func _draw(): bezel_points = arc_point_list(center, screen_height2, -270, -360) bezel_points.push_back(Vector2(screen_height2, screen_height2)) draw_polygon(bezel_points, bezel_colors) + +func _ready(): + $"/root".connect("size_changed", self, "update") \ No newline at end of file diff --git a/GameTheme.gd b/GameTheme.gd index c05eb53..5f28209 100644 --- a/GameTheme.gd +++ b/GameTheme.gd @@ -1,3 +1,4 @@ +tool extends Node var receptor_ring_radius := 460.0 diff --git a/InputHandler.gd b/InputHandler.gd index d281393..d487add 100644 --- a/InputHandler.gd +++ b/InputHandler.gd @@ -17,6 +17,11 @@ const TOUCHBUTTON_MAX_DIST := 1.05 const BUTTON_MIN_DIST := TOUCHBUTTON_MAX_DIST const BUTTON_MAX_DIST := 1.25 +func resize(): + var screen_size = $"/root".get_visible_rect().size + rect_position = -screen_size*0.5 + rect_size = screen_size + func _init(): buttons_pressed.resize(Rules.COLS) touchbuttons_pressed.resize(Rules.COLS) @@ -28,6 +33,8 @@ func _ready(): set_process_unhandled_input(true) # process user input set_fingers(0) # connect("button_pressed", self, "print_pressed") + $"/root".connect("size_changed", self, "resize") + resize() func print_pressed(col: int): diff --git a/Menu.gd b/Menu.gd index e408922..8230e10 100644 --- a/Menu.gd +++ b/Menu.gd @@ -1,3 +1,4 @@ +tool extends Node2D var song_defs = {} @@ -289,7 +290,7 @@ func _draw_score_screen(center: Vector2) -> Array: notecount_total += score # Kinda redundant, will probably refactor eventually note_count += score note_score += score * judge_scores[j] - draw_string_centered(TitleFont, Vector2(x2+x_spacing*(len(judgestrs)+1), y_row), "%2.2f%%"%(note_score/note_count*100.0), Color(0.95, 0.95, 1.0)) + draw_string_centered(TitleFont, Vector2(x2+x_spacing*(len(judgestrs)+1), y_row), "%2.2f%%"%(note_score/max(note_count, 1)*100.0), Color(0.95, 0.95, 1.0)) total_score += note_score * notetype_weights[i] total_scoremax += note_count * notetype_weights[i] @@ -325,6 +326,17 @@ func _draw_score_screen(center: Vector2) -> Array: draw_string_centered(TitleFont, Vector2(x-210, 320), "Saved", Color(0.95, 0.95, 1.0)) return touchrects +func _draw_gameplay(center: Vector2) -> Array: + var touchrects = [] + var x = center.x + var y = center.y + + var rect_songselect := Rect2(-960.0, 440.0, 100.0, 100.0) + draw_rect(rect_songselect, Color.red) + draw_string_centered(TitleFont, Vector2(-910, 470), "Stop", Color(0.95, 0.95, 1.0)) + touchrects.append({rect=rect_songselect, action="stop"}) + return touchrects + func _draw(): var songs = len(song_defs) @@ -373,6 +385,7 @@ func _draw(): pass MenuMode.GAMEPLAY: GameTheme.set_screen_filter_alpha(0.0) + touch_rects[menu_mode] = _draw_gameplay(center) MenuMode.SCORE_SCREEN: GameTheme.set_screen_filter_alpha(1.0) touch_rects[menu_mode] = _draw_score_screen(center) @@ -402,6 +415,12 @@ func touch_select_chart(touchdict): self.selected_difficulty = touchdict.chart_idx SFXPlayer.play(SFXPlayer.Type.NON_POSITIONAL, self, snd_interact, -4.5) +func touch_gameplay(touchdict): + if touchdict.has("action"): + SFXPlayer.play(SFXPlayer.Type.NON_POSITIONAL, self, snd_interact, 0.0) + if touchdict.action == "stop": + $"/root/main/NoteHandler".stop() + func touch_score_screen(touchdict): if touchdict.has("next_menu"): SFXPlayer.play(SFXPlayer.Type.NON_POSITIONAL, self, snd_interact, 0.0) @@ -436,6 +455,10 @@ func _input(event): for d in touch_rects[MenuMode.CHART_SELECT]: if d.rect.has_point(pos): touch_select_chart(d) + MenuMode.GAMEPLAY: + for d in touch_rects[MenuMode.GAMEPLAY]: + if d.rect.has_point(pos): + touch_gameplay(d) MenuMode.SCORE_SCREEN: for d in touch_rects[MenuMode.SCORE_SCREEN]: if d.rect.has_point(pos): diff --git a/NoteHandler.gd b/NoteHandler.gd index c9521a2..93407a8 100644 --- a/NoteHandler.gd +++ b/NoteHandler.gd @@ -1,4 +1,6 @@ -extends "res://main.gd" +extends Node2D + +var screen_height := 1080 # This script will draw all note events. signal finished_song(song_key, score_data) @@ -537,6 +539,11 @@ func load_track(data: Dictionary, difficulty_idx: int): $meshinstance.material.set_shader_param("screen_size", get_viewport().get_size()) $meshinstance.set_texture(tex) +func stop(): + $"/root/main/music".stop() + $"/root/main/video".stop() +# running = false + next_note_to_load = 1000000 # Hacky but whatever func intro_click(): SFXPlayer.play(SFXPlayer.Type.NON_POSITIONAL, self, snd_count_in) @@ -656,7 +663,7 @@ func _process(delta): # if (len(active_notes) < 1) and (next_note_to_load >= len(all_notes)) and (time > 10.0) and not get_node("/root/main/video").is_playing(): # time = -10.0 # next_note_to_load = 0 - if (len(active_notes) < 1) and (next_note_to_load >= len(all_notes)) and not get_node("/root/main/music").is_playing(): + if (len(active_notes) < 1) and (next_note_to_load >= len(all_notes)) and not get_node("/root/main/music").is_playing() and (len(active_judgement_texts) < 1): self.running = false self.timers_set = false emit_signal("finished_song", song_key, scores) diff --git a/Receptors.gd b/Receptors.gd index 81f1eac..407ce5a 100644 --- a/Receptors.gd +++ b/Receptors.gd @@ -1,4 +1,5 @@ -extends "res://main.gd" +tool +extends Node2D var ring_px := 4 var receptor_px := 24 @@ -6,25 +7,83 @@ var shadow_px := 5 var shadow_color := Color.black var center := Vector2(0.0, 0.0) -func _draw(): - # Screen filter - draw_rect(Rect2(-screen_height/2, -screen_height/2, screen_height, screen_height), GameTheme.screen_filter) +func make_ring_mesh(inner_vertices: int, thickness: float, radius: float, skew=0.5): + # This makes a trianglestrip around the ring, consisting of chords on the inside and tangents on the outside. + # The goal is to exchange some fragment and vertex processing load: + # - a full quad of the ring would be the maximum fragment load and minimum vertex load + # - a complex mesh closely following the outline of the ring would minimize discarded fragments at the cost of increased vertex processing + # - the ideal workload ratio is probably different for each GPU and also depends on other things our program is doing + assert(inner_vertices >= 3) + assert(thickness > 0.0) + assert(radius > 0.0) + # While values of 3 and 4 are mathematically possible, they result in half of the trianglestrip being degenerate for thin lines. + # Only values of 5 and above should be used practically. + var vertices = inner_vertices * 2 + # For simplicity's sake, the width of the ring will be the full thickness at the receptor points. + # For high vertex counts a slightly more optimal mesh could be constructed based on the thickness at each arc of the ring and where it would be intersected by the outer tangent. + # Essentially, we will be making an inner polygon and an outer polygon. + var angle_increment = TAU/float(inner_vertices) + var angle_outer_offset = skew*angle_increment + var r1 = radius - thickness*0.5 + # Outer polygon side-length = inner side-length / sin(inside angle/2) + # inside angle for a polygon is pi-tau/n. We already precalculated tau/n for other purposes. + var r2 = (radius + thickness*0.5)/sin((PI-angle_increment)/2) + + var vertex_list = PoolVector2Array() + for i in inner_vertices: + var angle_i = i * angle_increment + var angle_o = angle_i + angle_outer_offset + vertex_list.push_back(polar2cartesian(r1, angle_i)) + vertex_list.push_back(polar2cartesian(r2, angle_o)) + return vertex_list + +func triangle_area(a: Vector2, b: Vector2, c: Vector2) -> float: + return 0.5 * abs((a.x-c.x)*(b.y-a.y) - (a.x-b.x)*(c.y-a.y)) + +func arc_point_list(center: Vector2, radius: float, angle_from:=0.0, angle_to:=360.0, points:=90) -> PoolVector2Array: + var point_list = PoolVector2Array() + for i in range(points): + var angle = deg2rad(angle_from + i * (angle_to - angle_from) / (points-1)) +# point_list.push_back(center + Vector2(cos(angle), sin(angle)) * radius) + point_list.push_back(center + polar2cartesian(radius, angle)) + return point_list +func _draw(): # Receptor ring var receptor_circle := arc_point_list(center, GameTheme.receptor_ring_radius, 0.0, 360.0, 360) var receptor_centers := arc_point_list(center, GameTheme.receptor_ring_radius, Rules.FIRST_COLUMN_ANGLE_DEG, Rules.FIRST_COLUMN_ANGLE_DEG+360.0-Rules.COLS_ANGLE_DEG, Rules.COLS) - # Shadows - for i in range(len(receptor_circle)-1): - draw_line(receptor_circle[i], receptor_circle[i+1], shadow_color, ring_px + shadow_px, true) - for i in range(len(receptor_centers)): - draw_circle(receptor_centers[i], (receptor_px + shadow_px)/2, shadow_color) - - # Foregrounds +# # Shadows +# for i in range(len(receptor_circle)-1): +# draw_line(receptor_circle[i], receptor_circle[i+1], shadow_color, ring_px + shadow_px, true) +## draw_line(receptor_circle[i], receptor_circle[i+1], shadow_color) +# for i in range(len(receptor_centers)): +# draw_circle(receptor_centers[i], (receptor_px + shadow_px)/2, shadow_color) +# +# # Foregrounds for i in range(len(receptor_circle)-1): draw_line(receptor_circle[i], receptor_circle[i+1], GameTheme.receptor_color, ring_px, true) +# draw_line(receptor_circle[i], receptor_circle[i+1], GameTheme.receptor_color) for i in range(len(receptor_centers)): draw_circle(receptor_centers[i], receptor_px/2, GameTheme.receptor_color) + + var mesh_v = $VerticesSlider.value + var skew = $SkewSlider.value + var dbg_color = Color.red + var ring_vertices = make_ring_mesh(mesh_v, receptor_px + shadow_px, GameTheme.receptor_ring_radius, skew) + var estimated_area = 0.0 + var ideal_ring_area = PI * (pow(GameTheme.receptor_ring_radius+(receptor_px+shadow_px)/2, 2) - pow(GameTheme.receptor_ring_radius-(receptor_px+shadow_px)/2, 2)) + var l = len(ring_vertices) + for i in l: + draw_line(ring_vertices[i], ring_vertices[(i+1)%l], dbg_color) + draw_line(ring_vertices[i], ring_vertices[(i+2)%l], dbg_color, 2.0) + estimated_area += triangle_area(ring_vertices[i], ring_vertices[(i+1)%l], ring_vertices[(i+2)%l]) + var quad_area = 4*pow(GameTheme.receptor_ring_radius+(receptor_px+shadow_px)/2, 2) + $"/root/main/InputHandler".text = "Vertices: %d*2 Skew: %.3f\nArea: %.0f\n(%.0f%% ideal ring)\n(%.0f%% quad)"%[mesh_v, skew, estimated_area, 100.0*estimated_area/ideal_ring_area, 100.0*estimated_area/quad_area] func _ready(): - GameTheme.connect("screen_filter_changed", self, "update") \ No newline at end of file + $"/root".connect("size_changed", self, "update") + +func _process(delta): + if not Engine.editor_hint: + update() \ No newline at end of file diff --git a/ScreenFilter.gd b/ScreenFilter.gd new file mode 100644 index 0000000..885c22f --- /dev/null +++ b/ScreenFilter.gd @@ -0,0 +1,9 @@ +extends Node2D + +func _draw(): + var screen_size = $"/root".get_visible_rect().size + var screen_height = max(screen_size.x, screen_size.y) + draw_rect(Rect2(-screen_height/2, -screen_height/2, screen_height, screen_height), GameTheme.screen_filter) + +func _ready(): + GameTheme.connect("screen_filter_changed", self, "update") \ No newline at end of file diff --git a/main.gd b/main.gd index c2d0f68..e45ede9 100644 --- a/main.gd +++ b/main.gd @@ -2,24 +2,20 @@ extends Node2D # member variables var screen_height := 1080 -var x_margin := (1920 - screen_height)/2 +var x_margin := 0.0 +var y_margin := 0.0 var screen_center := Vector2(1920/2, screen_height/2) -func arc_point_list(center: Vector2, radius: float, angle_from:=0.0, angle_to:=360.0, points:=90) -> PoolVector2Array: - var point_list = PoolVector2Array() - for i in range(points): - var angle = deg2rad(angle_from + i * (angle_to - angle_from) / (points-1)) -# point_list.push_back(center + Vector2(cos(angle), sin(angle)) * radius) - point_list.push_back(center + polar2cartesian(radius, angle)) - return point_list - -# Called when the node enters the scene tree for the first time. -#func _ready(): -# pass # Replace with function body. - -# Called every frame. 'delta' is the elapsed time since the previous frame. -#func _process(delta): -# pass - +func resize(): + var screen_size = $"/root".get_visible_rect().size + screen_center = screen_size*0.5 + position = screen_center + + screen_height = screen_size.y + x_margin = max((screen_size.x - screen_size.y)/2.0, 0.0) + y_margin = max((screen_size.y - screen_size.x)/2.0, 0.0) +func _ready(): + $"/root".connect("size_changed", self, "resize") + resize() diff --git a/main.tscn b/main.tscn index edd3886..b74c629 100644 --- a/main.tscn +++ b/main.tscn @@ -1,21 +1,22 @@ -[gd_scene load_steps=16 format=2] +[gd_scene load_steps=17 format=2] [ext_resource path="res://main.gd" type="Script" id=1] [ext_resource path="res://video.gd" type="Script" id=2] -[ext_resource path="res://Receptors.gd" type="Script" id=3] -[ext_resource path="res://NoteHandler.gd" type="Script" id=4] -[ext_resource path="res://assets/text-4k.png" type="Texture" id=5] -[ext_resource path="res://shaders/notelines.shader" type="Shader" id=6] -[ext_resource path="res://shaders/notemesh.shader" type="Shader" id=7] -[ext_resource path="res://Menu.gd" type="Script" id=8] -[ext_resource path="res://shaders/scoretext.tres" type="Material" id=9] -[ext_resource path="res://ScoreText.gd" type="Script" id=10] -[ext_resource path="res://Bezel.gd" type="Script" id=11] -[ext_resource path="res://assets/NotoSans.tres" type="DynamicFont" id=12] -[ext_resource path="res://InputHandler.gd" type="Script" id=13] +[ext_resource path="res://ScreenFilter.gd" type="Script" id=3] +[ext_resource path="res://Receptors.gd" type="Script" id=4] +[ext_resource path="res://NoteHandler.gd" type="Script" id=5] +[ext_resource path="res://assets/text-4k.png" type="Texture" id=6] +[ext_resource path="res://shaders/notelines.shader" type="Shader" id=7] +[ext_resource path="res://shaders/notemesh.shader" type="Shader" id=8] +[ext_resource path="res://Menu.gd" type="Script" id=9] +[ext_resource path="res://shaders/scoretext.tres" type="Material" id=10] +[ext_resource path="res://ScoreText.gd" type="Script" id=11] +[ext_resource path="res://Bezel.gd" type="Script" id=12] +[ext_resource path="res://assets/NotoSans.tres" type="DynamicFont" id=13] +[ext_resource path="res://InputHandler.gd" type="Script" id=14] [sub_resource type="ShaderMaterial" id=1] -shader = ExtResource( 6 ) +shader = ExtResource( 7 ) shader_param/line_color = Plane( 0.8, 0.8, 1, 0.8 ) shader_param/line_color_double = Plane( 1, 1, 0.6, 0.9 ) shader_param/dot_color = Plane( 1, 1, 1, 0.8 ) @@ -27,18 +28,18 @@ shader_param/dot_fullbright_thickness = 0.013 shader_param/max_angle = 1.0708 [sub_resource type="ShaderMaterial" id=2] -shader = ExtResource( 7 ) +shader = ExtResource( 8 ) shader_param/bps = null shader_param/star_color = null shader_param/held_color = null shader_param/screen_size = null [node name="main" type="Node2D"] -position = Vector2( 960, 540 ) +position = Vector2( 540, 540 ) script = ExtResource( 1 ) __meta__ = { "_edit_horizontal_guides_": [ ], -"_edit_vertical_guides_": [ 420.85, 1500.91 ] +"_edit_vertical_guides_": [ ] } [node name="music" type="AudioStreamPlayer" parent="."] @@ -58,16 +59,44 @@ __meta__ = { "_edit_use_anchors_": false } -[node name="Receptors" type="Node2D" parent="."] +[node name="ScreenFilter" type="Node2D" parent="."] script = ExtResource( 3 ) -[node name="NoteHandler" type="Node2D" parent="."] +[node name="Receptors" type="Node2D" parent="."] script = ExtResource( 4 ) +[node name="SkewSlider" type="HSlider" parent="Receptors"] +margin_left = -200.0 +margin_top = -30.0 +margin_right = 200.0 +margin_bottom = 30.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +max_value = 1.0 +step = 0.001 +tick_count = 3 +ticks_on_borders = true + +[node name="VerticesSlider" type="HSlider" parent="Receptors"] +margin_left = -200.0 +margin_top = -100.0 +margin_right = 200.0 +margin_bottom = -84.0 +min_value = 3.0 +max_value = 72.0 +value = 8.0 +tick_count = 2 +ticks_on_borders = false + +[node name="NoteHandler" type="Node2D" parent="."] +script = ExtResource( 5 ) + [node name="SlideTrailHandler" type="Node2D" parent="NoteHandler"] [node name="JudgeText" type="MeshInstance2D" parent="NoteHandler"] -texture = ExtResource( 5 ) +texture = ExtResource( 6 ) [node name="notelines" type="MeshInstance2D" parent="NoteHandler"] material = SubResource( 1 ) @@ -76,20 +105,23 @@ material = SubResource( 1 ) material = SubResource( 2 ) [node name="Menu" type="Node2D" parent="."] -script = ExtResource( 8 ) +script = ExtResource( 9 ) [node name="ScoreText" type="Node2D" parent="Menu"] -material = ExtResource( 9 ) -script = ExtResource( 10 ) - -[node name="Bezel" type="Node2D" parent="."] +material = ExtResource( 10 ) script = ExtResource( 11 ) +[node name="Bezel" type="Node2D" parent="."] +script = ExtResource( 12 ) + [node name="InputHandler" type="Label" parent="."] -margin_left = -960.0 +margin_left = -540.0 margin_top = -540.0 -margin_right = 960.0 +margin_right = 540.0 margin_bottom = 540.0 -custom_fonts/font = ExtResource( 12 ) +custom_fonts/font = ExtResource( 13 ) text = "Fingers on the screen:" -script = ExtResource( 13 ) +script = ExtResource( 14 ) +__meta__ = { +"_edit_lock_": true +} diff --git a/project.godot b/project.godot index c12c98c..f4c741a 100644 --- a/project.godot +++ b/project.godot @@ -44,12 +44,12 @@ gdscript/warnings/integer_division=false [display] -window/size/width=1920 +window/size/width=1080 window/size/height=1080 window/size/fullscreen=true window/handheld/orientation="sensor" window/stretch/mode="2d" -window/stretch/aspect="keep_height" +window/stretch/aspect="expand" [gdnative]