From a6b8e896ac4e1225dc25e443d62ea47aa368c909 Mon Sep 17 00:00:00 2001 From: RhapsodyInGeek <44485952+RhapsodyInGeek@users.noreply.github.com> Date: Mon, 29 Jul 2024 14:54:41 -0400 Subject: [PATCH] More FuncGodot examples --- .../GD Extension/prop_dynamic.tres | 32 +++ .../GD Extension/prop_multi_mesh.tres | 39 ++++ FuncGodot Entities/GD Script/mesh_face.gd | 44 ++++ .../GD Script/prop_multi_mesh.gd | 172 ++++++++++++++ .../GD Script/prop_multi_mesh.tres | 39 ++++ ...ntrolsManager.cpp => controls_manager.cpp} | 212 ++++++++---------- .../{ControlsManager.h => controls_manager.h} | 69 +++--- .../{SpriteText.cpp => sprite_text.cpp} | 2 +- .../{SpriteText.h => sprite_text.h} | 0 Godot General/GD Script/ControlsManager.gd | 81 +++---- 10 files changed, 489 insertions(+), 201 deletions(-) create mode 100644 FuncGodot Entities/GD Extension/prop_dynamic.tres create mode 100644 FuncGodot Entities/GD Extension/prop_multi_mesh.tres create mode 100644 FuncGodot Entities/GD Script/mesh_face.gd create mode 100644 FuncGodot Entities/GD Script/prop_multi_mesh.gd create mode 100644 FuncGodot Entities/GD Script/prop_multi_mesh.tres rename Godot General/GD Extension/{ControlsManager.cpp => controls_manager.cpp} (77%) rename Godot General/GD Extension/{ControlsManager.h => controls_manager.h} (66%) rename Godot General/GD Extension/{SpriteText.cpp => sprite_text.cpp} (99%) rename Godot General/GD Extension/{SpriteText.h => sprite_text.h} (100%) diff --git a/FuncGodot Entities/GD Extension/prop_dynamic.tres b/FuncGodot Entities/GD Extension/prop_dynamic.tres new file mode 100644 index 0000000..b65708c --- /dev/null +++ b/FuncGodot Entities/GD Extension/prop_dynamic.tres @@ -0,0 +1,32 @@ +[gd_resource type="Resource" script_class="FuncGodotFGDPointClass" load_steps=3 format=3 uid="uid://rorln1pcp1bq"] + +[ext_resource type="Resource" uid="uid://dnjlim0ik6hty" path="res://tb/fgd/base/prop_base.tres" id="1_lvmib"] +[ext_resource type="Script" path="res://addons/func_godot/src/fgd/func_godot_fgd_point_class.gd" id="3_xvxts"] + +[resource] +script = ExtResource("3_xvxts") +apply_rotation_on_map_build = true +classname = "prop_dynamic" +description = "Dynamic prop entity." +func_godot_internal = false +base_classes = Array[Resource]([ExtResource("1_lvmib")]) +class_properties = { +"anim": "", +"editor_display_path": "", +"next_anim": "", +"prop_path": "", +"scale": Vector3(1, 1, 1) +} +class_property_descriptions = { +"anim": "Starting animation override.", +"editor_display_path": "Display path for editor model. Not used in Godot.", +"next_anim": "Animation to be played when this entity is triggered.", +"prop_path": "Local Resource or User path to GLTF or GLB file in Godot." +} +meta_properties = { +"color": Color(0.8, 0, 0.8, 1), +"model": "editor_display_path", +"size": AABB(-8, -8, 0, 8, 8, 16) +} +node_class = "PropDynamic" +name_property = "" diff --git a/FuncGodot Entities/GD Extension/prop_multi_mesh.tres b/FuncGodot Entities/GD Extension/prop_multi_mesh.tres new file mode 100644 index 0000000..d942a59 --- /dev/null +++ b/FuncGodot Entities/GD Extension/prop_multi_mesh.tres @@ -0,0 +1,39 @@ +[gd_resource type="Resource" script_class="FuncGodotFGDPointClass" load_steps=4 format=3 uid="uid://dg5i0lvmh0dye"] + +[ext_resource type="Resource" uid="uid://3nrgyhofbhnq" path="res://tb/fgd/base/globalname_base.tres" id="1_5yybl"] +[ext_resource type="Script" path="res://addons/func_godot/src/fgd/func_godot_fgd_point_class.gd" id="1_vr34g"] +[ext_resource type="Resource" uid="uid://dqeio0pq2js4m" path="res://tb/fgd/base/shadowcast_base.tres" id="2_hbd8m"] + +[resource] +script = ExtResource("1_vr34g") +apply_rotation_on_map_build = true +classname = "prop_multi_mesh" +description = "Multi mesh prop that generates mesh instances across target geometry. Surface targets can be picked by either proximity or matching the target property with an entity's globalname property." +func_godot_internal = false +base_classes = Array[Resource]([ExtResource("1_5yybl"), ExtResource("2_hbd8m")]) +class_properties = { +"amount": 128, +"base_scale": 1.0, +"mesh_res_path": "", +"random_rotation": 0.0, +"random_scale": 0.0, +"random_tilt": 0.0, +"target": "", +"target_mesh": "" +} +class_property_descriptions = { +"amount": "Number of mesh instances to generate.", +"base_scale": "Base scale factor for mesh instances.", +"mesh_res_path": "Resource filepath of a mesh to instance across the target geometry. Overridden by target_mesh, if target_mesh is valid.", +"random_rotation": "Range of rotation for mesh instances on the Y axis.", +"random_scale": "Scale factor offset range for mesh instances.", +"random_tilt": "Range of tilt for mesh instances on the X and Z axes.", +"target": "Globalname of the target geometry object to place the multi mesh instances across.", +"target_mesh": "Globalname of the geometry to copy across target. This can be used to instance brush entities. This overrides mesh_res_path, if target_mesh is valid." +} +meta_properties = { +"color": Color(0.8, 0, 0.8, 1), +"size": AABB(-16, -16, -16, 16, 16, 16) +} +node_class = "PropMultiMesh" +name_property = "" diff --git a/FuncGodot Entities/GD Script/mesh_face.gd b/FuncGodot Entities/GD Script/mesh_face.gd new file mode 100644 index 0000000..567c3db --- /dev/null +++ b/FuncGodot Entities/GD Script/mesh_face.gd @@ -0,0 +1,44 @@ +class_name MeshFace +extends RefCounted + +enum Side { + SIDE_OVER, + SIDE_UNDER, + SIDE_SPAWNING, + SIDE_COPLANAR +} + +var vertex: Array[Vector3] = [Vector3.ZERO,Vector3.ZERO,Vector3.ZERO] + +enum ClockDirection { CLOCKWISE, COUNTER_CLOCKWISE} + +func get_plane() -> Plane: + return Plane(vertex[0], vertex[1], vertex[2]) + +func get_random_point_inside() -> Vector3: + var a: float = randf_range(0, 1) + var b: float = randf_range(0, 1) + if a > b: + var c: float = a + a = b + b = c + return vertex[0] * a + vertex[1] * (b - a) + vertex[2] * (1 - b) + +func get_area() -> float: + return (vertex[0] - vertex[1]).cross(vertex[0] - vertex[2]).length() * 0.5 + +func print_face_info(face_index: int = -1) -> void: + var s: String = "Face" + if face_index > -1: + s += " " + str(face_index) + s += ":\n" + for v in vertex: + s += str(v) + "\n" + s += str(get_area()) + s += "\n" + print(s) + +func _init(v1: Vector3, v2: Vector3, v3: Vector3) -> void: + vertex[0] = v1 + vertex[1] = v2 + vertex[2] = v3 diff --git a/FuncGodot Entities/GD Script/prop_multi_mesh.gd b/FuncGodot Entities/GD Script/prop_multi_mesh.gd new file mode 100644 index 0000000..04a1f7e --- /dev/null +++ b/FuncGodot Entities/GD Script/prop_multi_mesh.gd @@ -0,0 +1,172 @@ +@tool +class_name PropMultiMesh +extends MultiMeshInstance3D + +@export var populate_multi_mesh: bool : + set(val): + _populate_multi_mesh() + +@export var target: String = "" + +@export var targetname: String = "" + +@export var target_mesh: String = "" + +@export var mesh_res_path: String = "" + +@export var amount: int = 128 : + set(val): + amount = max(val, 0) + +@export var random_rotation: float = 0.0 : + set(val): + random_rotation = abs(val) + +@export var random_tilt: float = 0.0 : + set(val): + random_tilt = abs(val) + +@export var base_scale: float = 1.0 : + set(val): + base_scale = max(abs(val), 0.01) + +@export var random_scale: float = 0.0 : + set(val): + random_scale = abs(val) + +func _populate_multi_mesh() -> void: + # Find surface target + var mesh_targ: Array[MeshInstance3D] = [null, null] + + for i in 2: + var targ_path: NodePath + if i == 0: + targ_path = "entity_" + target + else: + targ_path = "entity_" + target_mesh + + if get_parent().has_node(targ_path): + var temp: Node = get_parent().get_node(targ_path) + if temp is MeshInstance3D: + mesh_targ[i] = temp as MeshInstance3D + elif temp.get_child_count() > 0: + for child in temp.get_children(): + if child is MeshInstance3D: + mesh_targ[i] = child as MeshInstance3D + break + + # No valid surface target! Find one within range + if !mesh_targ[0] or !mesh_targ[0].mesh: + var world_ents: Array[Node] = get_tree().get_nodes_in_group("WORLD") + for ent in world_ents: + for child in ent.get_children(): + if child is MeshInstance3D and child.mesh: + if child.global_position.distance_to(global_position) < 8.0: + mesh_targ[0] = child + break + if mesh_targ[0]: + break + if !mesh_targ[0]: + printerr("No target found for PropMultiMesh " + name) + return + + # Cache the mesh + var mesh: Mesh = null + if mesh_targ[1] != null: + mesh = mesh_targ[1].mesh + if !mesh and FileAccess.file_exists(mesh_res_path): + mesh = load(mesh_res_path) + if !mesh: + printerr("No valid mesh set for PropMultiMesh " + name) + return + + # Prepare target surface + var geo_xform: Transform3D = global_transform.affine_inverse() * mesh_targ[0].global_transform + var verts: PackedVector3Array = mesh_targ[0].mesh.get_faces() + if verts.size() == 0 or verts.size() % 3 != 0: + printerr("PropMultiMesh " + name + " target has invalid mesh") + return + + var faces: Array[MeshFace] = [] + faces.resize(verts.size() / 3) + var facecount: int = faces.size() + for i in facecount: + faces[i] = MeshFace.new(verts[i*3], verts[i*3+1], verts[i*3+2]) + for j in 3: + faces[i].vertex[j] = geo_xform * faces[i].vertex[j] + + var area_accum: float = 0.0 + var triangle_area_map: Dictionary = {} + for i in facecount: + var area: float = faces[i].get_area() + if area < 0.00001: + continue + triangle_area_map[area_accum] = i + area_accum += area + + if triangle_area_map.is_empty() or area_accum == 0: + printerr("PropMultiMesh " + name + " couldn't map area") + return + + # Generate the MultiMesh resource + multimesh = MultiMesh.new() + multimesh.transform_format = MultiMesh.TRANSFORM_3D + multimesh.instance_count = amount + multimesh.mesh = mesh + + # Place instances + for i in amount: + var area_pos: float = randf_range(0, area_accum) + var area_key: float = 0 + for key in triangle_area_map.keys(): + if abs(key - area_pos) < abs(area_key - area_pos): + area_key = key + var index: int = wrapi(triangle_area_map[area_key], 0, facecount) + + var face: MeshFace = faces[index] + + var pos: Vector3 = face.get_random_point_inside() + var normal: Vector3 = face.get_plane().normal + var op_axis: Vector3 = (face.vertex[0] - face.vertex[1]).normalized() + + var xform: Transform3D + xform.origin = pos + xform = xform.looking_at(pos + op_axis, normal) + + var rot: float = deg_to_rad(random_rotation) + var tilt: float = deg_to_rad(random_tilt) + + var post_xform: Basis + post_xform = post_xform.rotated(post_xform.y, randf_range(-rot, rot)) + post_xform = post_xform.rotated(post_xform.z, randf_range(-tilt, tilt)) + post_xform = post_xform.rotated(post_xform.x, randf_range(-tilt, tilt)) + xform.basis = post_xform * xform.basis + xform.basis *= max(abs(base_scale + randf_range(-random_scale, random_scale)), 0.01) + + multimesh.set_instance_transform(i, xform) + +func _func_godot_apply_properties(props: Dictionary) -> void: + if props.has("target"): + target = props["target"] as String + if props.has("target_mesh"): + target_mesh = props["target_mesh"] as String + if props.has("targetname"): + targetname = props["targetname"] as String + if props.has("mesh_res_path"): + mesh_res_path = props["mesh_res_path"] as String + if props.has("amount"): + amount = max((props["amount"] as int), 0) + if props.has("random_rotation"): + random_rotation = abs(props["random_rotation"] as float) + if props.has("random_tilt"): + random_tilt = props["random_tilt"] as float + if props.has("base_scale"): + base_scale = maxf(abs(props["base_scale"] as float), 0.01) + if props.has("random_scale"): + random_scale = abs(props["random_scale"] as float) + + _populate_multi_mesh() + +func _init() -> void: + add_to_group("prop_multi_mesh") + diff --git a/FuncGodot Entities/GD Script/prop_multi_mesh.tres b/FuncGodot Entities/GD Script/prop_multi_mesh.tres new file mode 100644 index 0000000..4164b10 --- /dev/null +++ b/FuncGodot Entities/GD Script/prop_multi_mesh.tres @@ -0,0 +1,39 @@ +[gd_resource type="Resource" script_class="FuncGodotFGDPointClass" load_steps=3 format=3 uid="uid://dmmnqnundewsi"] + +[ext_resource type="Script" path="res://addons/func_godot/src/fgd/func_godot_fgd_point_class.gd" id="1_vr34g"] +[ext_resource type="Script" path="res://entities/props/prop_multi_mesh.gd" id="2_no2cd"] + +[resource] +script = ExtResource("1_vr34g") +script_class = ExtResource("2_no2cd") +apply_rotation_on_map_build = true +classname = "prop_multi_mesh" +description = "Multi mesh prop that generates mesh instances across surface target geometry. Surface targets can be picked by either proximity or matching the target property with an entity's globalname property." +func_godot_internal = false +base_classes = Array[Resource]([]) +class_properties = { +"amount": 128, +"base_scale": 1.0, +"mesh_res_path": "", +"random_rotation": 0.0, +"random_scale": 0.0, +"random_tilt": 0.0, +"target": "", +"target_mesh": "" +} +class_property_descriptions = { +"amount": "Number of mesh instances to generate.", +"base_scale": "Base scale factor for mesh instances.", +"mesh_res_path": "Resource filepath of a mesh to instance across the target geometry. Overridden by target_mesh, if target_mesh is valid.", +"random_rotation": "Range of rotation for mesh instances on the Y axis.", +"random_scale": "Scale factor offset range for mesh instances.", +"random_tilt": "Range of tilt for mesh instances on the X and Z axes.", +"target": "Globalname of the target geometry object to place the multi mesh instances across.", +"target_mesh": "Globalname of the geometry to copy across target. This can be used to instance brush entities. This overrides mesh_res_path, if target_mesh is valid." +} +meta_properties = { +"color": Color(0.8, 0, 0.8, 1), +"size": AABB(-16, -16, -16, 16, 16, 16) +} +node_class = "MultiMeshInstance3D" +name_property = "" diff --git a/Godot General/GD Extension/ControlsManager.cpp b/Godot General/GD Extension/controls_manager.cpp similarity index 77% rename from Godot General/GD Extension/ControlsManager.cpp rename to Godot General/GD Extension/controls_manager.cpp index de9ffcb..ebaa4aa 100644 --- a/Godot General/GD Extension/ControlsManager.cpp +++ b/Godot General/GD Extension/controls_manager.cpp @@ -6,7 +6,7 @@ Autoload singleton interface for user input. All input checks are requested through this node. ***************************************************************************/ -#include "ControlsMgr.h" +#include "controls_manager.h" #include #include #include @@ -62,6 +62,7 @@ void ControlsManager::_bind_methods() { ClassDB::bind_method(D_METHOD("released", "action"), &ControlsManager::released); ClassDB::bind_method(D_METHOD("held", "action"), &ControlsManager::held); ClassDB::bind_method(D_METHOD("get_held_time", "action"), &ControlsManager::get_held_time); + ClassDB::bind_method(D_METHOD("axis", "negative_action", "positive_action"), &ControlsManager::axis); ClassDB::bind_method(D_METHOD("release_all"), &ControlsManager::release_all); // LOCKOUT @@ -72,7 +73,7 @@ void ControlsManager::_bind_methods() { ADD_SIGNAL(MethodInfo("console_input")); ADD_SIGNAL(MethodInfo("action_pressed", PropertyInfo(Variant::STRING, "action"))); ADD_SIGNAL(MethodInfo("action_released", PropertyInfo(Variant::STRING, "action"))); - ADD_SIGNAL(MethodInfo("action_remapped", PropertyInfo(Variant::BOOL, "remap_successful"))); + ADD_SIGNAL(MethodInfo("action_remapped", PropertyInfo(Variant::STRING, "action"), PropertyInfo(Variant::BOOL, "remap_successful"))); // BASE PROCESSING ClassDB::bind_method(D_METHOD("input", "event"), &ControlsManager::input); @@ -165,7 +166,7 @@ MAPPING void ControlsManager::set_action_map() { InputMap* IM = InputMap::get_singleton(); String action; - int map_input[4]; + int map_input[2]; for (std::unordered_map>::iterator itr = action_map.begin(); itr != action_map.end(); itr++) { action = String(itr->first.c_str()); @@ -173,40 +174,49 @@ void ControlsManager::set_action_map() { for (int j = 0; j < 2; j++) { if (j == InputMode::Keyboard) { - map_input[0] = itr->second[0]; // input event type - map_input[1] = itr->second[1]; // input global constant - switch (map_input[0]) { + map_input[ACTION_TYPE] = itr->second[0]; // input event type + map_input[ACTION_INPUT] = itr->second[1]; // input global constant + + if (map_input[ACTION_INPUT] == KEY_NONE) + continue; + + switch (map_input[ACTION_TYPE]) { case InputType::KEY: { Ref event; event.instantiate(); - event->set_keycode(Key(map_input[1])); + event->set_keycode(Key(map_input[ACTION_INPUT])); IM->action_add_event(action, event); break; } case InputType::MOUSEBUTTON: { Ref event; event.instantiate(); - event->set_button_index(MouseButton(map_input[1])); + event->set_button_index(MouseButton(map_input[ACTION_INPUT])); IM->action_add_event(action, event); break; } } } else { // Gamepad - map_input[2] = itr->second[2]; // input event type - map_input[3] = itr->second[3]; // input global constant - switch (map_input[0]) { + map_input[ACTION_TYPE] = itr->second[2]; // input event type + map_input[ACTION_INPUT] = itr->second[3]; // input global constant + + if (map_input[ACTION_INPUT] == -1) + continue; + + switch (map_input[ACTION_TYPE]) { case InputType::JOYBUTTON: { Ref event; event.instantiate(); - event->set_button_index(JoyButton(map_input[1])); + event->set_button_index(JoyButton(map_input[ACTION_INPUT])); IM->action_add_event(action, event); break; } case InputType::JOYAXIS: { Ref event; event.instantiate(); - event->set_axis(JoyAxis(map_input[1])); + event->set_axis(JoyAxis(map_input[ACTION_INPUT])); + event->set_axis_value((float)itr->second[ACTION_AXIS_VALUE]); IM->action_add_event(action, event); IM->action_set_deadzone(action, DEADZONE); } @@ -216,13 +226,35 @@ void ControlsManager::set_action_map() { } } -void ControlsManager::reset_to_defaults() { +void ControlsManager::reset_action_map_to_default() { action_map = DEFAULT_ACTION_MAP; + set_action_map(); +} + +void ControlsManager::reset_keyboard_map_to_default() { + std::unordered_map> defmap = DEFAULT_ACTION_MAP; + for (std::unordered_map>::iterator itr = defmap.begin(); itr != defmap.end(); itr++) { + action_map[itr->first][0] = itr->second[0]; + action_map[itr->first][1] = itr->second[1]; + } + set_action_map(); +} + +void ControlsManager::reset_gamepad_map_to_default() { + std::unordered_map> defmap = DEFAULT_ACTION_MAP; + for (std::unordered_map>::iterator itr = defmap.begin(); itr != defmap.end(); itr++) { + action_map[itr->first][2] = itr->second[2]; + action_map[itr->first][3] = itr->second[3]; + } + set_action_map(); +} + +void ControlsManager::reset_to_defaults() { + reset_action_map_to_default(); set_mouse_sensitivity(); set_mouse_invert(); set_gamepad_sensitivity(); set_gamepad_invert(); - set_action_map(); } void ControlsManager::set_remap_mode(String new_remap_action, float new_remap_wait, bool new_remap_mode) { @@ -238,37 +270,34 @@ bool ControlsManager::remap(InputEvent* event, String action) { if (_class == "InputEventMouseMotion") return false; - int new_input[2] = { -1, -1 }, input_check[2] = { -1, -1 }; + int new_input[3] = { -1, -1, 0 }, input_check[3] = { -1, -1, 0 }; if (_class == "InputEventKey") { - input_check[1] = event->call("get_keycode"); - if (input_check[1] != KEY_QUOTELEFT) - if (input_check[1] < KEY_F1 || input_check[1] > KEY_F12) { - new_input[0] = InputType::KEY; - new_input[1] = input_check[1]; + input_check[ACTION_INPUT] = event->call("get_keycode"); + if (input_check[ACTION_INPUT] != KEY_QUOTELEFT) + if (input_check[ACTION_INPUT] < KEY_F1 || input_check[ACTION_INPUT] > KEY_F12) { + new_input[ACTION_TYPE] = InputType::KEY; + new_input[ACTION_INPUT] = input_check[ACTION_INPUT]; } } else if (_class == "InputEventMouseButton") { - new_input[0] = InputType::MOUSEBUTTON; - new_input[1] = event->call("get_button_index"); + new_input[ACTION_TYPE] = InputType::MOUSEBUTTON; + new_input[ACTION_INPUT] = event->call("get_button_index"); } else if (_class == "InputEventJoypadMotion") { - input_check[1] = event->call("get_axis"); - // Joysticks are reserved for movement and aim, but the triggers are fair game - if (input_check[1] > JOY_AXIS_RIGHT_Y) { - new_input[0] = InputType::JOYAXIS; - new_input[1] = input_check[1]; - } + new_input[ACTION_TYPE] = InputType::JOYAXIS; + new_input[ACTION_INPUT] = event->call("get_axis"); + new_input[REMAP_AXIS_VALUE] = (int)Math::sign((float)event->call("get_axis_value")); } else if (_class == "InputEventJoypadButton") { - input_check[1] = event->call("get_button_index"); - if (input_check[1] != JOY_BUTTON_GUIDE && input_check[1] != JOY_BUTTON_MISC1) { - new_input[0] = InputType::JOYBUTTON; - new_input[1] = input_check[1]; + input_check[ACTION_INPUT] = event->call("get_button_index"); + if (input_check[ACTION_INPUT] != JOY_BUTTON_GUIDE && input_check[1] != JOY_BUTTON_MISC1) { + new_input[ACTION_TYPE] = InputType::JOYBUTTON; + new_input[ACTION_INPUT] = input_check[ACTION_INPUT]; } } - if (new_input[1] >= 0) { + if (new_input[ACTION_INPUT] >= 0) { // Grab the right map int in_ofs = 0; if (input_mode != InputMode::Keyboard) @@ -277,33 +306,43 @@ bool ControlsManager::remap(InputEvent* event, String action) { std::string _action = action.utf8().get_data(); // We need to make sure we don't have 2 actions assigned to the same input - input_check[0] = action_map[_action][in_ofs]; - input_check[1] = action_map[_action][in_ofs + 1]; + input_check[ACTION_TYPE] = action_map[_action][in_ofs]; + input_check[ACTION_INPUT] = action_map[_action][in_ofs + 1]; + input_check[REMAP_AXIS_VALUE] = action_map[_action][ACTION_AXIS_VALUE]; for (std::unordered_map>::iterator itr = action_map.begin(); itr != action_map.end(); itr++) { - if (itr->second[in_ofs + 1] == new_input[1] && itr->second[in_ofs] == new_input[0]) { - action_map[itr->first][in_ofs] = input_check[0]; - action_map[itr->first][in_ofs + 1] = input_check[1]; - break; + if (itr->second[in_ofs + 1] == new_input[ACTION_INPUT] && itr->second[in_ofs] == new_input[ACTION_TYPE]) { + if (input_check[ACTION_TYPE] != InputType::JOYAXIS || itr->second[ACTION_AXIS_VALUE] == new_input[REMAP_AXIS_VALUE]) { + action_map[itr->first][ACTION_TYPE + in_ofs] = input_check[0]; + action_map[itr->first][ACTION_INPUT + in_ofs] = input_check[1]; + if (input_check[ACTION_TYPE] == InputType::JOYAXIS) + action_map[itr->first][ACTION_AXIS_VALUE] = input_check[REMAP_AXIS_VALUE]; + break; + } } } // Setting up the new control - action_map[_action][in_ofs] = new_input[0]; - action_map[_action][in_ofs + 1] = new_input[1]; + action_map[_action][ACTION_TYPE + in_ofs] = new_input[ACTION_TYPE]; + action_map[_action][ACTION_INPUT + in_ofs] = new_input[ACTION_INPUT]; + if (new_input[ACTION_TYPE] == InputType::JOYAXIS) + action_map[_action][ACTION_AXIS_VALUE] = new_input[REMAP_AXIS_VALUE]; + else + action_map[_action][ACTION_AXIS_VALUE] = 0; // Remap Controls set_action_map(); release_all(); - emit_signal("action_remapped", true); + emit_signal("action_remapped", action, true); return true; } + // We couldn't remap it? release_all(); - emit_signal("action_remapped", false); + emit_signal("action_remapped", action, false); return false; } -void ControlsManager::dict_to_map(int mode, Dictionary new_map) { +void ControlsManager::dict_to_map(Dictionary new_map) { Array keys = new_map.keys(), v; for (int i = 0; i < keys.size(); i++) { v = new_map[keys[i]]; @@ -315,10 +354,10 @@ void ControlsManager::dict_to_map(int mode, Dictionary new_map) { } } -Dictionary ControlsManager::map_to_dict(int mode) { +Dictionary ControlsManager::map_to_dict() { Dictionary dict_map = {}; for (std::unordered_map>::iterator itr = action_map.begin(); itr != action_map.end(); itr++) - dict_map[String(itr->first.c_str())] = Array::make(itr->second[0], itr->second[1]); + dict_map[String(itr->first.c_str())] = Array::make(itr->second[0], itr->second[1], itr->second[2], itr->second[3]); return dict_map; } @@ -338,34 +377,7 @@ String ControlsManager::action_to_ui(String action, int mode) { if (ui_input[0] == InputType::KEY) return OS::get_singleton()->get_keycode_string(Key(ui_input[1])); - if (ui_input[0] == InputType::MOUSEBUTTON) { - switch (ui_input[1]) { - case MOUSE_BUTTON_LEFT: - return "Mouse Left"; - case MOUSE_BUTTON_RIGHT: - return "Mouse Right"; - case MOUSE_BUTTON_MIDDLE: - return "Mouse Middle"; - case MOUSE_BUTTON_WHEEL_UP: - return "Mouse Wheel Up"; - case MOUSE_BUTTON_WHEEL_DOWN: - return "Mouse Wheel Down"; - case MOUSE_BUTTON_WHEEL_RIGHT: - return "Mouse Wheel Right"; - case MOUSE_BUTTON_WHEEL_LEFT: - return "Mouse Wheel Left"; - case MOUSE_BUTTON_XBUTTON1: - return "Mouse X Button 1"; - case MOUSE_BUTTON_XBUTTON2: - return "Mouse X Button 2"; - } - } - - if (ui_input[0] == InputType::JOYBUTTON) - return ""; - - if (ui_input[0] == InputType::JOYAXIS) - return ""; + return String::num(ui_input[0]) + ";" + String::num(ui_input[1]); } /************************************************* @@ -392,6 +404,12 @@ float ControlsManager::get_held_time(String action) { return held_time[action.utf8().get_data()]; } +float ControlsManager::axis(String negative_action, String positive_action) { + if (lockout > 0.0f) + return 0.0f; + return INPUT->get_axis(negative_action, positive_action); +} + void ControlsManager::release_all() { for (int i = 0; i < action_list.size(); i++) INPUT->action_release(action_list[i]); @@ -441,7 +459,9 @@ void ControlsManager::process(float delta) { if (input_mode == InputMode::Keyboard) set_deferred("mouse_motion", Vector2()); + update_held_time(delta); + if (lockout > 0) { release_all(); lockout -= delta; @@ -475,47 +495,7 @@ void ControlsManager::input(InputEvent* event) { // Mouselook if (event->get_class() == "InputEventMouseMotion" && input_mode == InputMode::Keyboard) - mouse_motion = Vector2(event->get("relative")) * mouse_sensitivity; - - // Gamepad movement - else if (event->get_class() == "InputEventJoypadMotion" && input_mode != InputMode::Keyboard) { - int axis = event->get("axis"); - float av = event->get("axis_value"); - //av = (abs(av) - DEADZONE) / (1.0 - DEADZONE) * Math::sign(av); - - switch (axis) { - // Movement - case (JoyAxis::JOY_AXIS_LEFT_X): { - if (abs(av) > 0.0f) - move_motion.x = av; - else - move_motion.x = 0.0f; - break; - } - case (JoyAxis::JOY_AXIS_LEFT_Y): { - if (abs(av) > 0.0f) - move_motion.y = av; - else - move_motion.y = 0.0f; - break; - } - // Aiming - case (JoyAxis::JOY_AXIS_RIGHT_X): { - if (abs(av) > 0.0f) - mouse_motion.x = av * mouse_sensitivity.x; - else - mouse_motion.x = 0.0f; - break; - } - case (JoyAxis::JOY_AXIS_RIGHT_Y): { - if (abs(av) > 0.0f) - mouse_motion.y = av * mouse_sensitivity.y; - else - mouse_motion.y = 0.0f; - break; - } - } - } + mouse_motion = Vector2(event->get("relative")) * 0.1f * mouse_sensitivity; // Input states for (int i = 0; i < action_list.size(); i++) { diff --git a/Godot General/GD Extension/ControlsManager.h b/Godot General/GD Extension/controls_manager.h similarity index 66% rename from Godot General/GD Extension/ControlsManager.h rename to Godot General/GD Extension/controls_manager.h index 0f8d68b..3353111 100644 --- a/Godot General/GD Extension/ControlsManager.h +++ b/Godot General/GD Extension/controls_manager.h @@ -13,6 +13,7 @@ CONTROLS MANAGER #include #define DEADZONE 0.333f +#define MAX_INPUT_TYPES 2 namespace godot { @@ -27,35 +28,41 @@ namespace godot Input* INPUT = nullptr; enum InputType { KEY, MOUSEBUTTON, MOUSEAXIS, JOYBUTTON, JOYAXIS }; + enum ActionMapElement { ACTION_TYPE, ACTION_INPUT, REMAP_AXIS_VALUE, ACTION_AXIS_VALUE = 4 }; const std::unordered_map> DEFAULT_ACTION_MAP = { - {"move_forward", { InputType::KEY, KEY_W, InputType::JOYAXIS, JOY_AXIS_LEFT_Y }}, - {"move_backward", { InputType::KEY, KEY_S, InputType::JOYAXIS, JOY_AXIS_LEFT_Y }}, - {"move_right", { InputType::KEY, KEY_D, InputType::JOYAXIS, JOY_AXIS_LEFT_X }}, - {"move_left", { InputType::KEY, KEY_A, InputType::JOYAXIS, JOY_AXIS_LEFT_X }}, - {"jump", { InputType::KEY, KEY_SPACE, InputType::JOYBUTTON, JOY_BUTTON_A }}, - {"walk", { InputType::KEY, KEY_SHIFT, InputType::KEY, KEY_SHIFT }}, - {"crouch", { InputType::KEY, KEY_CTRL, InputType::JOYBUTTON, JOY_BUTTON_B }}, - {"use", { InputType::KEY, KEY_E, InputType::JOYBUTTON, JOY_BUTTON_X }}, - {"attack", { InputType::MOUSEBUTTON, MOUSE_BUTTON_LEFT, InputType::JOYAXIS, JOY_AXIS_TRIGGER_RIGHT }}, - {"alt_attack", { InputType::MOUSEBUTTON, MOUSE_BUTTON_RIGHT, InputType::JOYAXIS, JOY_AXIS_TRIGGER_LEFT }}, - {"torch", { InputType::KEY, KEY_F, InputType::JOYBUTTON, JOY_BUTTON_Y }}, - {"menu", { InputType::KEY, KEY_ESCAPE, InputType::JOYBUTTON, JOY_BUTTON_BACK }}, - {"objective", { InputType::KEY, KEY_TAB, InputType::JOYBUTTON, JOY_BUTTON_LEFT_STICK }}, - {"console", { InputType::KEY, KEY_QUOTELEFT, InputType::KEY, KEY_QUOTELEFT }}, - {"weapon_wheel", { InputType::KEY, KEY_Q, InputType::JOYBUTTON, JOY_BUTTON_RIGHT_SHOULDER }}, - {"weapon_1", { InputType::KEY, KEY_1, InputType::KEY, KEY_1}}, - {"weapon_2", { InputType::KEY, KEY_2, InputType::KEY, KEY_2}}, - {"weapon_3", { InputType::KEY, KEY_3, InputType::KEY, KEY_3}}, - {"weapon_4", { InputType::KEY, KEY_4, InputType::KEY, KEY_4}}, - {"weapon_5", { InputType::KEY, KEY_5, InputType::KEY, KEY_5}}, - {"weapon_6", { InputType::KEY, KEY_6, InputType::KEY, KEY_6}}, - {"weapon_7", { InputType::KEY, KEY_7, InputType::KEY, KEY_7}}, - {"weapon_8", { InputType::KEY, KEY_8, InputType::KEY, KEY_8}}, - {"weapon_9", { InputType::KEY, KEY_9, InputType::KEY, KEY_9}}, - {"weapon_10", { InputType::KEY, KEY_0, InputType::KEY, KEY_0 }}, - {"quicksave", { InputType::KEY, KEY_F5, InputType::KEY, KEY_F5 }}, - {"quickload", { InputType::KEY, KEY_F9, InputType::KEY, KEY_F9 }} + {"move_forward", { InputType::KEY, KEY_W, InputType::JOYAXIS, JOY_AXIS_LEFT_Y, -1 }}, + {"move_backward", { InputType::KEY, KEY_S, InputType::JOYAXIS, JOY_AXIS_LEFT_Y, 1 }}, + {"move_right", { InputType::KEY, KEY_D, InputType::JOYAXIS, JOY_AXIS_LEFT_X, 1 }}, + {"move_left", { InputType::KEY, KEY_A, InputType::JOYAXIS, JOY_AXIS_LEFT_X, -1 }}, + {"aim_up", { InputType::KEY, KEY_NONE, InputType::JOYAXIS, JOY_AXIS_RIGHT_Y, -1 }}, + {"aim_down", { InputType::KEY, KEY_NONE, InputType::JOYAXIS, JOY_AXIS_RIGHT_Y, 1 }}, + {"aim_right", { InputType::KEY, KEY_NONE, InputType::JOYAXIS, JOY_AXIS_RIGHT_X, 1 }}, + {"aim_left", { InputType::KEY, KEY_NONE, InputType::JOYAXIS, JOY_AXIS_RIGHT_X, -1 }}, + {"jump", { InputType::KEY, KEY_SPACE, InputType::JOYBUTTON, JOY_BUTTON_A, 0 }}, + {"walk", { InputType::KEY, KEY_SHIFT, InputType::KEY, KEY_SHIFT, 0 }}, + {"crouch", { InputType::KEY, KEY_CTRL, InputType::JOYBUTTON, JOY_BUTTON_B, 0 }}, + {"use", { InputType::KEY, KEY_E, InputType::JOYBUTTON, JOY_BUTTON_X, 0 }}, + {"attack", { InputType::MOUSEBUTTON, MOUSE_BUTTON_LEFT, InputType::JOYAXIS, JOY_AXIS_TRIGGER_RIGHT, 0 }}, + {"alt_attack", { InputType::MOUSEBUTTON, MOUSE_BUTTON_RIGHT, InputType::JOYAXIS, JOY_AXIS_TRIGGER_LEFT, 0 }}, + {"torch", { InputType::KEY, KEY_F, InputType::JOYBUTTON, JOY_BUTTON_Y, 0 }}, + {"menu", { InputType::KEY, KEY_ESCAPE, InputType::JOYBUTTON, JOY_BUTTON_START, 0 }}, + {"objective", { InputType::KEY, KEY_TAB, InputType::JOYBUTTON, JOY_BUTTON_BACK, 0 }}, + {"console", { InputType::KEY, KEY_QUOTELEFT, InputType::KEY, KEY_QUOTELEFT, 0 }}, + {"command_enter", { InputType::KEY, KEY_ENTER, InputType::KEY, KEY_ENTER, 0 }}, + {"weapon_wheel", { InputType::KEY, KEY_Q, InputType::JOYBUTTON, JOY_BUTTON_RIGHT_SHOULDER, 0 }}, + {"weapon_1", { InputType::KEY, KEY_1, InputType::KEY, KEY_1, 0}}, + {"weapon_2", { InputType::KEY, KEY_2, InputType::KEY, KEY_2, 0}}, + {"weapon_3", { InputType::KEY, KEY_3, InputType::KEY, KEY_3, 0}}, + {"weapon_4", { InputType::KEY, KEY_4, InputType::KEY, KEY_4, 0}}, + {"weapon_5", { InputType::KEY, KEY_5, InputType::KEY, KEY_5, 0}}, + {"weapon_6", { InputType::KEY, KEY_6, InputType::KEY, KEY_6, 0}}, + {"weapon_7", { InputType::KEY, KEY_7, InputType::KEY, KEY_7, 0}}, + {"weapon_8", { InputType::KEY, KEY_8, InputType::KEY, KEY_8, 0}}, + {"weapon_9", { InputType::KEY, KEY_9, InputType::KEY, KEY_9, 0}}, + {"weapon_10", { InputType::KEY, KEY_0, InputType::KEY, KEY_0, 0 }}, + {"quicksave", { InputType::KEY, KEY_F5, InputType::KEY, KEY_F5, 0 }}, + {"quickload", { InputType::KEY, KEY_F9, InputType::KEY, KEY_F9, 0 }} }; std::unordered_map> action_map = {}; @@ -93,11 +100,14 @@ namespace godot // MAPPING void set_action_map(); + void reset_action_map_to_default(); + void reset_keyboard_map_to_default(); + void reset_gamepad_map_to_default(); void reset_to_defaults(); void set_remap_mode(String new_remap_action, float new_remap_wait = 0.1f, bool new_remap_mode = true); bool remap(InputEvent* event, String action); - void dict_to_map(int mode, Dictionary new_map); - Dictionary map_to_dict(int mode); + void dict_to_map(Dictionary new_map); + Dictionary map_to_dict(); String action_to_ui(String action, int mode = ControlsManager::Keyboard); // INPUT STATES @@ -106,6 +116,7 @@ namespace godot bool held(String action); void update_held_time(float delta); float get_held_time(String action); + float axis(String negative_action, String positive_action); // LOCKOUT void release_all(); diff --git a/Godot General/GD Extension/SpriteText.cpp b/Godot General/GD Extension/sprite_text.cpp similarity index 99% rename from Godot General/GD Extension/SpriteText.cpp rename to Godot General/GD Extension/sprite_text.cpp index f0b1004..670a586 100644 --- a/Godot General/GD Extension/SpriteText.cpp +++ b/Godot General/GD Extension/sprite_text.cpp @@ -1,4 +1,4 @@ -#include "SpriteText.h" +#include "sprite_text.h" #include #include #include diff --git a/Godot General/GD Extension/SpriteText.h b/Godot General/GD Extension/sprite_text.h similarity index 100% rename from Godot General/GD Extension/SpriteText.h rename to Godot General/GD Extension/sprite_text.h diff --git a/Godot General/GD Script/ControlsManager.gd b/Godot General/GD Script/ControlsManager.gd index 22ada25..f9bd487 100644 --- a/Godot General/GD Script/ControlsManager.gd +++ b/Godot General/GD Script/ControlsManager.gd @@ -30,37 +30,30 @@ var action_list: Array[String] = [] var map: Dictionary = {} var input_mode: InputMode = InputMode.Keyboard -var mouse_motion: Vector2 = Vector2.ZERO -var mouse_sensitivity: Vector2 = Vector2(3.0, 3.0): - set(value): - mouse_sensitivity = value - mouse_sensitivity.x = clampf(value.x, 0.01, 20.0) - mouse_sensitivity.y = clampf(value.y, 0.01, 20.0) var mouse_invert: Vector2 = Vector2.ONE: set(value): if value.length() != 0: mouse_invert = value.sign() -var gamepad_sensitivity: Vector2 = Vector2(3.0, 3.0): - set(value): - gamepad_sensitivity = value - gamepad_sensitivity.x = clampf(value.x, 0.01, 20.0) - gamepad_sensitivity.y = clampf(value.y, 0.01, 20.0) var gamepad_invert: Vector2 = Vector2.ONE: set(value): if value.length() != 0: - gamepad_invert = value.sign() -var move_motion: Vector2 = Vector2.ZERO + mouse_invert = value.sign() var lockout: float = 0.0 -var console_mode: bool = false var remap_mode: bool = false -var remap_wait: float = 0.0 +var console_mode: bool = false var remap_action: String +var mouse_motion: Vector2 = Vector2.ZERO +var mouse_sensitivity: Vector2 = Vector2(0.5, 0.5): + set(value): + mouse_sensitivity.x = clampf(value.x, 0.01, 1.0) + mouse_sensitivity.y = clampf(value.y, 0.01, 1.0) +var move_motion: Vector2 = Vector2.ZERO var held_time: Dictionary = {} signal console_input signal action_pressed(action: String) signal action_released(action: String) -signal action_remapped(action: String, remap_successful: bool) +signal action_remapped #************************************************************* # PROPERTIES @@ -109,7 +102,7 @@ func input_mode_swap(event: InputEvent) -> void: # MAPPING #************************************************************* func set_action_map() -> void: - var map_input: Array[int] = [0,0,0,0] + var map_input: Array[int] = [0,0] for action in action_map.keys(): InputMap.action_erase_events(action) @@ -117,6 +110,8 @@ func set_action_map() -> void: if j == InputMode.Keyboard: map_input[0] = action_map[action][0] # input event type map_input[1] = action_map[action][1] # input global constant + if map_input[1] == KEY_NONE: + continue match map_input[0]: InputType.KEY: var event:= InputEventKey.new() @@ -127,8 +122,10 @@ func set_action_map() -> void: event.button_index = map_input[1] as MouseButton InputMap.action_add_event(action, event) else: # Gamepad - map_input[2] = action_map[action][2] # input event type - map_input[3] = action_map[action][3] # input global constant + map_input[0] = action_map[action][2] # input event type + map_input[1] = action_map[action][3] # input global constant + if map_input[1] == -1: + continue match map_input[0]: InputType.JOYBUTTON: var event:= InputEventJoypadButton.new() @@ -137,6 +134,8 @@ func set_action_map() -> void: InputType.JOYAXIS: var event:= InputEventJoypadMotion.new() event.axis = map_input[1] as JoyAxis + if (action_map[action] as Array).size() > 4: + event.axis_value = action_map[action][4] as float InputMap.action_add_event(action, event) InputMap.action_set_deadzone(action, DEADZONE) @@ -147,9 +146,8 @@ func reset_to_defaults() -> void: gamepad_invert = Vector2.ONE set_action_map() -func set_remap_mode(new_remap_action: String, new_remap_wait: float = 0.1, new_remap_mode: bool = true) -> void: +func set_remap_mode(new_remap_action: String, new_remap_mode: bool) -> void: remap_action = new_remap_action - remap_wait = new_remap_wait remap_mode = new_remap_mode func action_remap(event: InputEvent, action: String) -> bool: @@ -203,12 +201,10 @@ func action_remap(event: InputEvent, action: String) -> bool: # Remap Controls set_action_map() release_all() - emit_signal("action_remapped", remap_action, true) return true # We couldn't remap it? release_all() - emit_signal("action_remapped", remap_action, false) return false func action_to_ui(action: String, mode: int): @@ -269,6 +265,11 @@ func update_held_time(delta: float) -> void: else: held_time[action] = 0.0 +func axis(negative_action: String, positive_action: String) -> float: + if lockout > 0.0: + return 0.0 + return Input.get_axis(negative_action, positive_action) + func release_all() -> void: for action in action_list: Input.action_release(action) @@ -313,10 +314,9 @@ func _input(event): # Input remapping eats inputs if remap_mode: - if remap_wait > 0.0: - remap_wait -= get_process_delta_time() - elif action_remap(event, remap_action): + if action_remap(event, remap_action): remap_mode = false + emit_signal("action_remapped") get_viewport().set_input_as_handled() return @@ -326,35 +326,6 @@ func _input(event): if event.get_class() == "InputEventMouseMotion" and input_mode == InputMode.Keyboard: mouse_motion = Vector2((event as InputEventMouseMotion).relative) * mouse_sensitivity - # Gamepad movement - elif event.get_class() == "InputEventJoypadMotion" and input_mode != InputMode.Keyboard: - var axis: int = (event as InputEventJoypadMotion).axis - var av: float = (event as InputEventJoypadMotion).axis_value - - match axis: - # Movement - JOY_AXIS_LEFT_X: - if abs(av) > 0.0: - move_motion.x = av - else: - move_motion.x = 0.0 - JOY_AXIS_LEFT_Y: - if abs(av) > 0.0: - move_motion.y = av - else: - move_motion.y = 0.0 - # Aiming - JOY_AXIS_RIGHT_X: - if abs(av) > 0.0: - mouse_motion.x = av * mouse_sensitivity.x - else: - mouse_motion.x = 0.0 - JOY_AXIS_RIGHT_Y: - if abs(av) > 0.0: - mouse_motion.y = av * mouse_sensitivity.y - else: - mouse_motion.y = 0.0 - # Input states for action in action_list: if event.is_action_pressed(action):