348 lines
8.0 KiB
GDScript
348 lines
8.0 KiB
GDScript
class_name GameKey extends Node3D
|
|
|
|
#region variables
|
|
static var _scene := preload("res://scenes/game_key.tscn")
|
|
|
|
@export_group("Node references")
|
|
@export var _skeleton_primary: Skeleton3D
|
|
@export var _skeleton_secondary: Skeleton3D
|
|
@export var _upper_left_label: Label3D
|
|
@export var _upper_right_label: Label3D
|
|
@export var _lower_left_label: Label3D
|
|
@export var _lower_right_label: Label3D
|
|
@export var _center_label: Label3D
|
|
@export var _press_light: OmniLight3D
|
|
@export var _sfx_player: AudioStreamPlayer3D
|
|
|
|
@export_group("Bones")
|
|
@export var _bone_top_left: String = "TopLeft"
|
|
@export var _bone_top_right: String = "TopRight"
|
|
@export var _bone_bottom_left: String = "BottomLeft"
|
|
@export var _bone_bottom_right: String = "BottomRight"
|
|
|
|
@export_group("Light")
|
|
@export var _light_energy: float = 3
|
|
@export var _light_fade_time: float = 0.25
|
|
|
|
@export_group("Animation")
|
|
@export var _pos_sod_fzr: Vector3 = Vector3(5, 0.15, 0)
|
|
|
|
@export_subgroup("Idle")
|
|
@export var _idle_amplitude: float = 0.1
|
|
@export var _idle_frequency_x: float = 1
|
|
@export var _idle_frequency_z: float = -1
|
|
|
|
@export_group("Pressing")
|
|
@export var _press_offset: float = 0.1
|
|
|
|
@export_group("SFX")
|
|
@export var _press_sfx: AudioStream
|
|
@export var _release_sfx: AudioStream
|
|
|
|
var props: KeyProps
|
|
var keyboard: GameKeyboard
|
|
var input_event_init: InputEventKey
|
|
|
|
var _is_pressed: bool
|
|
|
|
var _input_event_main: InputEventKey
|
|
var _input_event_shift: InputEventKey
|
|
var _input_event_alt: InputEventKey
|
|
var _input_event_alt_shift: InputEventKey
|
|
|
|
var _light_timer: float
|
|
var _default_position: Vector3
|
|
|
|
@onready
|
|
var _polyphonic := _sfx_player.get_stream_playback() as AudioStreamPlaybackPolyphonic
|
|
@onready var _pos_sod := SecondOrderDynamics.new(_pos_sod_fzr, _default_position)
|
|
|
|
#endregion
|
|
|
|
|
|
#region static methods
|
|
static func instantiate_with_props(
|
|
_props: KeyProps, default_position: Vector3, _keyboard: GameKeyboard
|
|
) -> GameKey:
|
|
var node := _scene.instantiate() as GameKey
|
|
node.keyboard = _keyboard
|
|
|
|
var input_event := InputEventKey.new()
|
|
input_event.physical_keycode = _props.physical_keycode
|
|
input_event.location = _props.location
|
|
node.input_event_init = input_event
|
|
|
|
node.name = node.name + " " + input_event.as_text_physical_keycode()
|
|
if _props.location != KEY_LOCATION_UNSPECIFIED:
|
|
node.name += " " + input_event.as_text_location()
|
|
|
|
node.load_props(_props, default_position)
|
|
|
|
return node
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region builtins
|
|
func _ready() -> void:
|
|
_set_labels()
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
_animate(delta)
|
|
_animate_light(delta)
|
|
|
|
|
|
func _unhandled_input(event: InputEvent) -> void:
|
|
if event.is_action_pressed("reset_animations"):
|
|
_pos_sod = SecondOrderDynamics.new(_pos_sod_fzr, _default_position)
|
|
return
|
|
|
|
if event is not InputEventKey:
|
|
return
|
|
|
|
var event_key := event as InputEventKey
|
|
|
|
if (
|
|
event_key.physical_keycode != props.physical_keycode
|
|
or (
|
|
event_key.location != KEY_LOCATION_UNSPECIFIED
|
|
and event_key.location != props.location
|
|
)
|
|
or event_key.echo
|
|
):
|
|
return
|
|
|
|
_set_pressing(event_key.is_pressed())
|
|
|
|
if LayoutConfig.is_configuring and event_key.is_pressed():
|
|
if not keyboard.alt_layout:
|
|
if not event_key.shift_pressed:
|
|
_input_event_main = event_key
|
|
else:
|
|
_input_event_shift = event_key
|
|
else:
|
|
if not event_key.shift_pressed:
|
|
_input_event_alt = event_key
|
|
else:
|
|
_input_event_alt_shift = event_key
|
|
|
|
_set_labels()
|
|
|
|
|
|
func _exit_tree() -> void:
|
|
_set_keyboard_pressed_position(false)
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region public
|
|
func load_props(_props: KeyProps, default_position: Vector3) -> void:
|
|
_set_keyboard_pressed_position(false)
|
|
_default_position = default_position
|
|
if _is_pressed:
|
|
_set_keyboard_pressed_position(true)
|
|
|
|
if props:
|
|
_props.chars_from_dict(props.chars_to_dict())
|
|
props = _props
|
|
|
|
_skeleton_primary.visible = true
|
|
_skeleton_secondary.visible = props.secondary_rect
|
|
_set_bones()
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region pressing
|
|
func _set_pressing(new_is_pressed: bool) -> void:
|
|
if _is_pressed == new_is_pressed:
|
|
return
|
|
|
|
_is_pressed = new_is_pressed
|
|
|
|
_set_keyboard_pressed_position(_is_pressed)
|
|
|
|
if _is_pressed:
|
|
_play_sfx(_press_sfx)
|
|
else:
|
|
_play_sfx(_release_sfx)
|
|
|
|
|
|
func _set_keyboard_pressed_position(pressed: bool) -> void:
|
|
if pressed:
|
|
keyboard.pressed_positions[_default_position] = pressed
|
|
else:
|
|
keyboard.pressed_positions.erase(_default_position)
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region labels
|
|
func _set_labels() -> void:
|
|
_upper_left_label.text = ""
|
|
_upper_right_label.text = ""
|
|
_lower_left_label.text = ""
|
|
_lower_right_label.text = ""
|
|
_center_label.text = ""
|
|
|
|
_set_labels_text()
|
|
|
|
_upper_left_label.visible = _upper_left_label.text != ""
|
|
_upper_right_label.visible = _upper_right_label.text != ""
|
|
_lower_left_label.visible = _lower_left_label.text != ""
|
|
_lower_right_label.visible = _lower_right_label.text != ""
|
|
_center_label.visible = _center_label.text != ""
|
|
|
|
|
|
func _set_labels_text() -> void:
|
|
if not props.is_char():
|
|
_center_label.text = input_event_init.as_text_physical_keycode()
|
|
return
|
|
|
|
var chars_dict: Dictionary[String, String] = {
|
|
KeyProps.MAIN_CHAR:
|
|
(
|
|
char(_input_event_main.unicode).to_upper()
|
|
if _input_event_main
|
|
else props.main_char
|
|
),
|
|
KeyProps.SHIFT_CHAR:
|
|
(
|
|
char(_input_event_shift.unicode).to_upper()
|
|
if _input_event_shift
|
|
else props.shift_char
|
|
),
|
|
KeyProps.ALT_CHAR:
|
|
(
|
|
char(_input_event_alt.unicode).to_upper()
|
|
if _input_event_alt
|
|
else props.alt_char
|
|
),
|
|
KeyProps.ALT_SHIFT_CHAR:
|
|
(
|
|
char(_input_event_alt_shift.unicode).to_upper()
|
|
if _input_event_alt_shift
|
|
else props.alt_shift_char
|
|
),
|
|
}
|
|
|
|
props.chars_from_dict(chars_dict)
|
|
|
|
if props.main_char and not props.shift_char:
|
|
_upper_left_label.text = props.main_char
|
|
|
|
if props.main_char and props.shift_char:
|
|
_upper_left_label.text = props.shift_char
|
|
_lower_left_label.text = props.main_char
|
|
|
|
if not props.main_char and props.shift_char:
|
|
_upper_left_label.text = props.shift_char
|
|
|
|
if props.alt_char:
|
|
_lower_right_label.text = props.alt_char
|
|
|
|
if props.alt_shift_char:
|
|
_upper_right_label.text = props.alt_shift_char
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region bones
|
|
func _set_bones() -> void:
|
|
var rect := Rect2(
|
|
-(keyboard.key_size * props.width) / 2,
|
|
-keyboard.key_size / 2 + props.y * keyboard.key_size,
|
|
keyboard.key_size * props.width,
|
|
keyboard.key_size * props.height
|
|
)
|
|
|
|
_set_bones_from_rect(rect, _skeleton_primary)
|
|
|
|
if props.secondary_rect:
|
|
var rect_secondary := Rect2(
|
|
rect.position.x + props.x2 * keyboard.key_size,
|
|
rect.position.y + props.y2 * keyboard.key_size,
|
|
keyboard.key_size * props.width2,
|
|
keyboard.key_size * props.height2
|
|
)
|
|
|
|
_set_bones_from_rect(rect_secondary, _skeleton_secondary)
|
|
|
|
|
|
func _set_bones_from_rect(rect: Rect2, skeleton: Skeleton3D) -> void:
|
|
var top_left_idx := skeleton.find_bone(_bone_top_left)
|
|
skeleton.set_bone_pose_position(
|
|
top_left_idx, Vector3(rect.position.x, 0, rect.position.y)
|
|
)
|
|
|
|
var top_right_idx := skeleton.find_bone(_bone_top_right)
|
|
skeleton.set_bone_pose_position(
|
|
top_right_idx, Vector3(rect.end.x, 0, rect.position.y)
|
|
)
|
|
|
|
var bottom_left_idx := skeleton.find_bone(_bone_bottom_left)
|
|
skeleton.set_bone_pose_position(
|
|
bottom_left_idx, Vector3(rect.position.x, 0, rect.end.y)
|
|
)
|
|
|
|
var bottom_right_idx := skeleton.find_bone(_bone_bottom_right)
|
|
skeleton.set_bone_pose_position(
|
|
bottom_right_idx, Vector3(rect.end.x, 0, rect.end.y)
|
|
)
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region animation
|
|
func _animate(delta: float) -> void:
|
|
position = _pos_sod.process(delta, _animate_position())
|
|
|
|
|
|
func _animate_position() -> Vector3:
|
|
var new_position := _default_position
|
|
|
|
new_position.y += (
|
|
sin(
|
|
(
|
|
keyboard.anim_time
|
|
+ position.x * _idle_frequency_x
|
|
+ position.z * _idle_frequency_z
|
|
)
|
|
)
|
|
* _idle_amplitude
|
|
)
|
|
|
|
new_position.y -= _press_offset if _is_pressed else 0.0
|
|
|
|
return new_position
|
|
|
|
|
|
func _animate_light(delta: float) -> void:
|
|
if _is_pressed:
|
|
_light_timer = _light_fade_time
|
|
|
|
if _light_timer <= 0:
|
|
_press_light.visible = false
|
|
return
|
|
|
|
_press_light.visible = true
|
|
_press_light.light_energy = (_light_timer / _light_fade_time) * _light_energy
|
|
_light_timer -= delta
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
#region sounds
|
|
func _play_sfx(stream: AudioStream) -> void:
|
|
_polyphonic.play_stream(stream)
|
|
|
|
#endregion
|