diff options
-rw-r--r-- | core/input/input.compat.inc | 41 | ||||
-rw-r--r-- | core/input/input.cpp | 7 | ||||
-rw-r--r-- | core/input/input.h | 7 | ||||
-rw-r--r-- | core/os/os.h | 2 | ||||
-rw-r--r-- | doc/classes/Input.xml | 3 | ||||
-rw-r--r-- | misc/extension_api_validation/4.2-stable.expected | 7 | ||||
-rw-r--r-- | modules/navigation/2d/nav_mesh_generator_2d.cpp | 175 | ||||
-rw-r--r-- | modules/navigation/2d/nav_mesh_generator_2d.h | 2 | ||||
-rw-r--r-- | platform/android/java/lib/src/org/godotengine/godot/Godot.kt | 21 | ||||
-rw-r--r-- | platform/android/java_godot_wrapper.cpp | 13 | ||||
-rw-r--r-- | platform/android/java_godot_wrapper.h | 2 | ||||
-rw-r--r-- | platform/android/os_android.cpp | 4 | ||||
-rw-r--r-- | platform/android/os_android.h | 2 | ||||
-rw-r--r-- | platform/ios/ios.h | 2 | ||||
-rw-r--r-- | platform/ios/ios.mm | 42 | ||||
-rw-r--r-- | platform/ios/os_ios.h | 2 | ||||
-rw-r--r-- | platform/ios/os_ios.mm | 8 | ||||
-rw-r--r-- | platform/web/os_web.cpp | 2 | ||||
-rw-r--r-- | platform/web/os_web.h | 2 |
19 files changed, 205 insertions, 139 deletions
diff --git a/core/input/input.compat.inc b/core/input/input.compat.inc new file mode 100644 index 0000000000..cbc8b1df0f --- /dev/null +++ b/core/input/input.compat.inc @@ -0,0 +1,41 @@ +/**************************************************************************/ +/* input.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +void Input::_vibrate_handheld_bind_compat_91143(int p_duration_ms) { + vibrate_handheld(p_duration_ms, -1.0); +} + +void Input::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("vibrate_handheld", "duration_ms"), &Input::_vibrate_handheld_bind_compat_91143, DEFVAL(500)); +} + +#endif // DISABLE_DEPRECATED diff --git a/core/input/input.cpp b/core/input/input.cpp index c24a59203f..1eabfacd8e 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "input.h" +#include "input.compat.inc" #include "core/config/project_settings.h" #include "core/input/default_controller_mappings.h" @@ -120,7 +121,7 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("get_joy_vibration_duration", "device"), &Input::get_joy_vibration_duration); ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0)); ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &Input::stop_joy_vibration); - ClassDB::bind_method(D_METHOD("vibrate_handheld", "duration_ms"), &Input::vibrate_handheld, DEFVAL(500)); + ClassDB::bind_method(D_METHOD("vibrate_handheld", "duration_ms", "amplitude"), &Input::vibrate_handheld, DEFVAL(500), DEFVAL(-1.0)); ClassDB::bind_method(D_METHOD("get_gravity"), &Input::get_gravity); ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer); ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer); @@ -803,8 +804,8 @@ void Input::stop_joy_vibration(int p_device) { joy_vibration[p_device] = vibration; } -void Input::vibrate_handheld(int p_duration_ms) { - OS::get_singleton()->vibrate_handheld(p_duration_ms); +void Input::vibrate_handheld(int p_duration_ms, float p_amplitude) { + OS::get_singleton()->vibrate_handheld(p_duration_ms, p_amplitude); } void Input::set_gravity(const Vector3 &p_gravity) { diff --git a/core/input/input.h b/core/input/input.h index d1f284e8f7..93407da2d9 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -264,6 +264,11 @@ private: EventDispatchFunc event_dispatch_function = nullptr; +#ifndef DISABLE_DEPRECATED + void _vibrate_handheld_bind_compat_91143(int p_duration_ms = 500); + static void _bind_compatibility_methods(); +#endif // DISABLE_DEPRECATED + protected: static void _bind_methods(); @@ -323,7 +328,7 @@ public: void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0); void stop_joy_vibration(int p_device); - void vibrate_handheld(int p_duration_ms = 500); + void vibrate_handheld(int p_duration_ms = 500, float p_amplitude = -1.0); void set_mouse_position(const Point2 &p_posf); diff --git a/core/os/os.h b/core/os/os.h index d20f84b4ff..63cc6ed50e 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -185,7 +185,7 @@ public: virtual int get_process_id() const; virtual bool is_process_running(const ProcessID &p_pid) const = 0; virtual int get_process_exit_code(const ProcessID &p_pid) const = 0; - virtual void vibrate_handheld(int p_duration_ms = 500) {} + virtual void vibrate_handheld(int p_duration_ms = 500, float p_amplitude = -1.0) {} virtual Error shell_open(const String &p_uri); virtual Error shell_show_in_file_manager(String p_path, bool p_open_folder = true); diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index 119ecb7f0e..642bb76e75 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -397,11 +397,14 @@ <method name="vibrate_handheld"> <return type="void" /> <param index="0" name="duration_ms" type="int" default="500" /> + <param index="1" name="amplitude" type="float" default="-1.0" /> <description> + [b]Note:[/b] While [code skip-lint]amplitude[/code] expects a value between 0 and 1, -1 does the default amplitude for the device. Vibrate the handheld device for the specified duration in milliseconds. [b]Note:[/b] This method is implemented on Android, iOS, and Web. It has no effect on other platforms. [b]Note:[/b] For Android, [method vibrate_handheld] requires enabling the [code]VIBRATE[/code] permission in the export preset. Otherwise, [method vibrate_handheld] will have no effect. [b]Note:[/b] For iOS, specifying the duration is only supported in iOS 13 and later. + [b]Note:[/b] For Web, the amplitude cannot be changed. [b]Note:[/b] Some web browsers such as Safari and Firefox for Android do not support [method vibrate_handheld]. </description> </method> diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected index 5a9976dc71..55a4e0b18c 100644 --- a/misc/extension_api_validation/4.2-stable.expected +++ b/misc/extension_api_validation/4.2-stable.expected @@ -337,3 +337,10 @@ GH-91098 Validate extension JSON: Error: Field 'classes/RichTextLabel/methods/remove_paragraph/arguments': size changed value in new API, from 1 to 2. Added optional argument. Compatibility method registered. + + +GH-91143 +-------- +Validate extension JSON: Error: Field 'classes/Input/methods/vibrate_handheld/arguments': size changed value in new API, from 1 to 2. + +Added optional argument. Compatibility method registered.
\ No newline at end of file diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp index 15a645816c..2198158f9c 100644 --- a/modules/navigation/2d/nav_mesh_generator_2d.cpp +++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp @@ -243,7 +243,7 @@ void NavMeshGenerator2D::generator_parse_geometry_node(Ref<NavigationPolygon> p_ generator_parse_multimeshinstance2d_node(p_navigation_mesh, p_source_geometry_data, p_node); generator_parse_polygon2d_node(p_navigation_mesh, p_source_geometry_data, p_node); generator_parse_staticbody2d_node(p_navigation_mesh, p_source_geometry_data, p_node); - generator_parse_tilemap_node(p_navigation_mesh, p_source_geometry_data, p_node); + generator_parse_tile_map_layer_node(p_navigation_mesh, p_source_geometry_data, p_node); generator_parse_navigationobstacle_node(p_navigation_mesh, p_source_geometry_data, p_node); generator_rid_rwlock.read_lock(); @@ -259,6 +259,14 @@ void NavMeshGenerator2D::generator_parse_geometry_node(Ref<NavigationPolygon> p_ for (int i = 0; i < p_node->get_child_count(); i++) { generator_parse_geometry_node(p_navigation_mesh, p_source_geometry_data, p_node->get_child(i), p_recurse_children); } + } else if (Object::cast_to<TileMap>(p_node)) { + // Special case for TileMap, so that internal layer get parsed even if p_recurse_children is false. + for (int i = 0; i < p_node->get_child_count(); i++) { + TileMapLayer *tile_map_layer = Object::cast_to<TileMapLayer>(p_node->get_child(i)); + if (tile_map_layer->get_index_in_tile_map() >= 0) { + generator_parse_tile_map_layer_node(p_navigation_mesh, p_source_geometry_data, tile_map_layer); + } + } } } @@ -580,141 +588,102 @@ void NavMeshGenerator2D::generator_parse_staticbody2d_node(const Ref<NavigationP } } -void NavMeshGenerator2D::generator_parse_tilemap_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) { - TileMap *tilemap = Object::cast_to<TileMap>(p_node); - - if (tilemap == nullptr) { +void NavMeshGenerator2D::generator_parse_tile_map_layer_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) { + TileMapLayer *tile_map_layer = Object::cast_to<TileMapLayer>(p_node); + if (tile_map_layer == nullptr) { return; } - NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type(); - uint32_t parsed_collision_mask = p_navigation_mesh->get_parsed_collision_mask(); - - if (tilemap->get_layers_count() <= 0) { - return; - } - - Ref<TileSet> tile_set = tilemap->get_tileset(); + Ref<TileSet> tile_set = tile_map_layer->get_tile_set(); if (!tile_set.is_valid()) { return; } int physics_layers_count = tile_set->get_physics_layers_count(); int navigation_layers_count = tile_set->get_navigation_layers_count(); - if (physics_layers_count <= 0 && navigation_layers_count <= 0) { return; } - HashSet<Vector2i> cells_with_navigation_polygon; - HashSet<Vector2i> cells_with_collision_polygon; + NavigationPolygon::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type(); + uint32_t parsed_collision_mask = p_navigation_mesh->get_parsed_collision_mask(); + + const Transform2D tilemap_xform = p_source_geometry_data->root_node_transform * tile_map_layer->get_global_transform(); - const Transform2D tilemap_xform = p_source_geometry_data->root_node_transform * tilemap->get_global_transform(); + TypedArray<Vector2i> used_cells = tile_map_layer->get_used_cells(); + for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) { + const Vector2i &cell = used_cells[used_cell_index]; -#ifdef DEBUG_ENABLED - int error_print_counter = 0; - int error_print_max = 10; -#endif // DEBUG_ENABLED + const TileData *tile_data = tile_map_layer->get_cell_tile_data(cell); + if (tile_data == nullptr) { + continue; + } - for (int tilemap_layer = 0; tilemap_layer < tilemap->get_layers_count(); tilemap_layer++) { - TypedArray<Vector2i> used_cells = tilemap->get_used_cells(tilemap_layer); + // Transform flags. + const int alternative_id = tile_map_layer->get_cell_alternative_tile(cell); + bool flip_h = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H); + bool flip_v = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V); + bool transpose = (alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE); + + Transform2D tile_transform; + tile_transform.set_origin(tile_map_layer->map_to_local(cell)); + + const Transform2D tile_transform_offset = tilemap_xform * tile_transform; + + // Parse traversable polygons. + for (int navigation_layer = 0; navigation_layer < navigation_layers_count; navigation_layer++) { + Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(navigation_layer, flip_h, flip_v, transpose); + if (navigation_polygon.is_valid()) { + for (int outline_index = 0; outline_index < navigation_polygon->get_outline_count(); outline_index++) { + const Vector<Vector2> &navigation_polygon_outline = navigation_polygon->get_outline(outline_index); + if (navigation_polygon_outline.is_empty()) { + continue; + } - for (int used_cell_index = 0; used_cell_index < used_cells.size(); used_cell_index++) { - const Vector2i &cell = used_cells[used_cell_index]; + Vector<Vector2> traversable_outline; + traversable_outline.resize(navigation_polygon_outline.size()); - const TileData *tile_data = tilemap->get_cell_tile_data(tilemap_layer, cell, false); - if (tile_data == nullptr) { - continue; - } + const Vector2 *navigation_polygon_outline_ptr = navigation_polygon_outline.ptr(); + Vector2 *traversable_outline_ptrw = traversable_outline.ptrw(); - // Transform flags. - const int alternative_id = tilemap->get_cell_alternative_tile(tilemap_layer, cell, false); - bool flip_h = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_H); - bool flip_v = (alternative_id & TileSetAtlasSource::TRANSFORM_FLIP_V); - bool transpose = (alternative_id & TileSetAtlasSource::TRANSFORM_TRANSPOSE); - - Transform2D tile_transform; - tile_transform.set_origin(tilemap->map_to_local(cell)); - - const Transform2D tile_transform_offset = tilemap_xform * tile_transform; - - if (navigation_layers_count > 0) { - Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(tilemap_layer, flip_h, flip_v, transpose); - if (navigation_polygon.is_valid()) { - if (cells_with_navigation_polygon.has(cell)) { -#ifdef DEBUG_ENABLED - error_print_counter++; - if (error_print_counter <= error_print_max) { - WARN_PRINT(vformat("TileMap navigation mesh baking error. The TileMap cell key Vector2i(%s, %s) has navigation mesh from 2 or more different TileMap layers assigned. This can cause unexpected navigation mesh baking results. The duplicated cell data was ignored.", cell.x, cell.y)); - } -#endif // DEBUG_ENABLED - } else { - cells_with_navigation_polygon.insert(cell); - - for (int outline_index = 0; outline_index < navigation_polygon->get_outline_count(); outline_index++) { - const Vector<Vector2> &navigation_polygon_outline = navigation_polygon->get_outline(outline_index); - if (navigation_polygon_outline.size() == 0) { - continue; - } - - Vector<Vector2> traversable_outline; - traversable_outline.resize(navigation_polygon_outline.size()); - - const Vector2 *navigation_polygon_outline_ptr = navigation_polygon_outline.ptr(); - Vector2 *traversable_outline_ptrw = traversable_outline.ptrw(); - - for (int traversable_outline_index = 0; traversable_outline_index < traversable_outline.size(); traversable_outline_index++) { - traversable_outline_ptrw[traversable_outline_index] = tile_transform_offset.xform(navigation_polygon_outline_ptr[traversable_outline_index]); - } - - p_source_geometry_data->_add_traversable_outline(traversable_outline); - } + for (int traversable_outline_index = 0; traversable_outline_index < traversable_outline.size(); traversable_outline_index++) { + traversable_outline_ptrw[traversable_outline_index] = tile_transform_offset.xform(navigation_polygon_outline_ptr[traversable_outline_index]); } + + p_source_geometry_data->_add_traversable_outline(traversable_outline); } } + } - if (physics_layers_count > 0 && (parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH) && (tile_set->get_physics_layer_collision_layer(tilemap_layer) & parsed_collision_mask)) { - if (cells_with_collision_polygon.has(cell)) { -#ifdef DEBUG_ENABLED - error_print_counter++; - if (error_print_counter <= error_print_max) { - WARN_PRINT(vformat("TileMap navigation mesh baking error. The cell key Vector2i(%s, %s) has collision polygons from 2 or more different TileMap layers assigned that all match the parsed collision mask. This can cause unexpected navigation mesh baking results. The duplicated cell data was ignored.", cell.x, cell.y)); + // Parse obstacles. + for (int physics_layer = 0; physics_layer < physics_layers_count; physics_layer++) { + if ((parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationPolygon::PARSED_GEOMETRY_BOTH) && + (tile_set->get_physics_layer_collision_layer(physics_layer) & parsed_collision_mask)) { + for (int collision_polygon_index = 0; collision_polygon_index < tile_data->get_collision_polygons_count(physics_layer); collision_polygon_index++) { + PackedVector2Array collision_polygon_points = tile_data->get_collision_polygon_points(physics_layer, collision_polygon_index); + if (collision_polygon_points.is_empty()) { + continue; } -#endif // DEBUG_ENABLED - } else { - cells_with_collision_polygon.insert(cell); - - for (int collision_polygon_index = 0; collision_polygon_index < tile_data->get_collision_polygons_count(tilemap_layer); collision_polygon_index++) { - PackedVector2Array collision_polygon_points = tile_data->get_collision_polygon_points(tilemap_layer, collision_polygon_index); - if (collision_polygon_points.size() == 0) { - continue; - } - - if (flip_h || flip_v || transpose) { - collision_polygon_points = TileData::get_transformed_vertices(collision_polygon_points, flip_h, flip_v, transpose); - } - Vector<Vector2> obstruction_outline; - obstruction_outline.resize(collision_polygon_points.size()); + if (flip_h || flip_v || transpose) { + collision_polygon_points = TileData::get_transformed_vertices(collision_polygon_points, flip_h, flip_v, transpose); + } - const Vector2 *collision_polygon_points_ptr = collision_polygon_points.ptr(); - Vector2 *obstruction_outline_ptrw = obstruction_outline.ptrw(); + Vector<Vector2> obstruction_outline; + obstruction_outline.resize(collision_polygon_points.size()); - for (int obstruction_outline_index = 0; obstruction_outline_index < obstruction_outline.size(); obstruction_outline_index++) { - obstruction_outline_ptrw[obstruction_outline_index] = tile_transform_offset.xform(collision_polygon_points_ptr[obstruction_outline_index]); - } + const Vector2 *collision_polygon_points_ptr = collision_polygon_points.ptr(); + Vector2 *obstruction_outline_ptrw = obstruction_outline.ptrw(); - p_source_geometry_data->_add_obstruction_outline(obstruction_outline); + for (int obstruction_outline_index = 0; obstruction_outline_index < obstruction_outline.size(); obstruction_outline_index++) { + obstruction_outline_ptrw[obstruction_outline_index] = tile_transform_offset.xform(collision_polygon_points_ptr[obstruction_outline_index]); } + + p_source_geometry_data->_add_obstruction_outline(obstruction_outline); } } } } -#ifdef DEBUG_ENABLED - if (error_print_counter > error_print_max) { - ERR_PRINT(vformat("TileMap navigation mesh baking error. A total of %s cells with navigation or collision polygons from 2 or more different TileMap layers overlap. This can cause unexpected navigation mesh baking results. The duplicated cell data was ignored.", error_print_counter)); - } -#endif // DEBUG_ENABLED } void NavMeshGenerator2D::generator_parse_navigationobstacle_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node) { diff --git a/modules/navigation/2d/nav_mesh_generator_2d.h b/modules/navigation/2d/nav_mesh_generator_2d.h index 235a84d548..d5f9694242 100644 --- a/modules/navigation/2d/nav_mesh_generator_2d.h +++ b/modules/navigation/2d/nav_mesh_generator_2d.h @@ -89,7 +89,7 @@ class NavMeshGenerator2D : public Object { static void generator_parse_multimeshinstance2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node); static void generator_parse_polygon2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node); static void generator_parse_staticbody2d_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node); - static void generator_parse_tilemap_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node); + static void generator_parse_tile_map_layer_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node); static void generator_parse_navigationobstacle_node(const Ref<NavigationPolygon> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData2D> p_source_geometry_data, Node *p_node); static bool generator_emit_callback(const Callable &p_callback); diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt index ce53aeebcb..fbdf07e6c2 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -894,16 +894,25 @@ class Godot(private val context: Context) : SensorEventListener { */ @SuppressLint("MissingPermission") @Keep - private fun vibrate(durationMs: Int) { + private fun vibrate(durationMs: Int, amplitude: Int) { if (durationMs > 0 && requestPermission("VIBRATE")) { val vibratorService = getActivity()?.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator? ?: return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - vibratorService.vibrate( - VibrationEffect.createOneShot( - durationMs.toLong(), - VibrationEffect.DEFAULT_AMPLITUDE + if (amplitude <= -1) { + vibratorService.vibrate( + VibrationEffect.createOneShot( + durationMs.toLong(), + VibrationEffect.DEFAULT_AMPLITUDE + ) ) - ) + } else { + vibratorService.vibrate( + VibrationEffect.createOneShot( + durationMs.toLong(), + amplitude + ) + ) + } } else { // deprecated in API 26 vibratorService.vibrate(durationMs.toLong()) diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 61be6fc5db..0e766e7d56 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -72,7 +72,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_ _get_granted_permissions = p_env->GetMethodID(godot_class, "getGrantedPermissions", "()[Ljava/lang/String;"); _get_ca_certificates = p_env->GetMethodID(godot_class, "getCACertificates", "()Ljava/lang/String;"); _init_input_devices = p_env->GetMethodID(godot_class, "initInputDevices", "()V"); - _vibrate = p_env->GetMethodID(godot_class, "vibrate", "(I)V"); + _vibrate = p_env->GetMethodID(godot_class, "vibrate", "(II)V"); _get_input_fallback_mapping = p_env->GetMethodID(godot_class, "getInputFallbackMapping", "()Ljava/lang/String;"); _on_godot_setup_completed = p_env->GetMethodID(godot_class, "onGodotSetupCompleted", "()V"); _on_godot_main_loop_started = p_env->GetMethodID(godot_class, "onGodotMainLoopStarted", "()V"); @@ -331,11 +331,18 @@ void GodotJavaWrapper::init_input_devices() { } } -void GodotJavaWrapper::vibrate(int p_duration_ms) { +void GodotJavaWrapper::vibrate(int p_duration_ms, float p_amplitude) { if (_vibrate) { JNIEnv *env = get_jni_env(); ERR_FAIL_NULL(env); - env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms); + + int j_amplitude = -1.0; + + if (p_amplitude != -1.0) { + j_amplitude = CLAMP(int(p_amplitude * 255), 1, 255); + } + + env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms, j_amplitude); } } diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index 93998021a9..e86391d4e3 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -102,7 +102,7 @@ public: Vector<String> get_granted_permissions() const; String get_ca_certificates() const; void init_input_devices(); - void vibrate(int p_duration_ms); + void vibrate(int p_duration_ms, float p_amplitude = -1.0); String get_input_fallback_mapping(); int create_new_godot_instance(const List<String> &args); void begin_benchmark_measure(const String &p_context, const String &p_label); diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 463a307854..c60125c34e 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -746,8 +746,8 @@ ANativeWindow *OS_Android::get_native_window() const { #endif } -void OS_Android::vibrate_handheld(int p_duration_ms) { - godot_java->vibrate(p_duration_ms); +void OS_Android::vibrate_handheld(int p_duration_ms, float p_amplitude) { + godot_java->vibrate(p_duration_ms, p_amplitude); } String OS_Android::get_config_path() const { diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 7bdbeef77a..b150ef4f61 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -153,7 +153,7 @@ public: virtual Error move_to_trash(const String &p_path) override; - void vibrate_handheld(int p_duration_ms) override; + void vibrate_handheld(int p_duration_ms, float p_amplitude = -1.0) override; virtual String get_config_path() const override; diff --git a/platform/ios/ios.h b/platform/ios/ios.h index d488cde257..cb5be64cee 100644 --- a/platform/ios/ios.h +++ b/platform/ios/ios.h @@ -51,7 +51,7 @@ public: static void alert(const char *p_alert, const char *p_title); bool supports_haptic_engine(); - void vibrate_haptic_engine(float p_duration_seconds); + void vibrate_haptic_engine(float p_duration_seconds, float p_amplitude); String get_model() const; String get_rate_url(int p_app_id) const; diff --git a/platform/ios/ios.mm b/platform/ios/ios.mm index 0a2e1fd5cd..6943de5ac8 100644 --- a/platform/ios/ios.mm +++ b/platform/ios/ios.mm @@ -69,21 +69,41 @@ CHHapticEngine *iOS::get_haptic_engine_instance() API_AVAILABLE(ios(13)) { return haptic_engine; } -void iOS::vibrate_haptic_engine(float p_duration_seconds) API_AVAILABLE(ios(13)) { +void iOS::vibrate_haptic_engine(float p_duration_seconds, float p_amplitude) API_AVAILABLE(ios(13)) { if (@available(iOS 13, *)) { // We need the @available check every time to make the compiler happy... if (supports_haptic_engine()) { CHHapticEngine *cur_haptic_engine = get_haptic_engine_instance(); if (cur_haptic_engine) { - NSDictionary *hapticDict = @{ - CHHapticPatternKeyPattern : @[ - @{CHHapticPatternKeyEvent : @{ - CHHapticPatternKeyEventType : CHHapticEventTypeHapticContinuous, - CHHapticPatternKeyTime : @(CHHapticTimeImmediate), - CHHapticPatternKeyEventDuration : @(p_duration_seconds) - }, - }, - ], - }; + NSDictionary *hapticDict; + if (p_amplitude < 0) { + hapticDict = @{ + CHHapticPatternKeyPattern : @[ + @{CHHapticPatternKeyEvent : @{ + CHHapticPatternKeyEventType : CHHapticEventTypeHapticContinuous, + CHHapticPatternKeyTime : @(CHHapticTimeImmediate), + CHHapticPatternKeyEventDuration : @(p_duration_seconds), + }, + }, + ], + }; + } else { + hapticDict = @{ + CHHapticPatternKeyPattern : @[ + @{CHHapticPatternKeyEvent : @{ + CHHapticPatternKeyEventType : CHHapticEventTypeHapticContinuous, + CHHapticPatternKeyTime : @(CHHapticTimeImmediate), + CHHapticPatternKeyEventDuration : @(p_duration_seconds), + CHHapticPatternKeyEventParameters : @[ + @{ + CHHapticPatternKeyParameterID : @("HapticIntensity"), + CHHapticPatternKeyParameterValue : @(p_amplitude) + }, + ], + }, + }, + ], + }; + } NSError *error; CHHapticPattern *pattern = [[CHHapticPattern alloc] initWithDictionary:hapticDict error:&error]; diff --git a/platform/ios/os_ios.h b/platform/ios/os_ios.h index c4782a4768..b7c5a73065 100644 --- a/platform/ios/os_ios.h +++ b/platform/ios/os_ios.h @@ -123,7 +123,7 @@ public: virtual String get_unique_id() const override; virtual String get_processor_name() const override; - virtual void vibrate_handheld(int p_duration_ms = 500) override; + virtual void vibrate_handheld(int p_duration_ms = 500, float p_amplitude = -1.0) override; virtual bool _check_internal_feature_support(const String &p_feature) override; diff --git a/platform/ios/os_ios.mm b/platform/ios/os_ios.mm index 52d496d641..35b87ea647 100644 --- a/platform/ios/os_ios.mm +++ b/platform/ios/os_ios.mm @@ -571,9 +571,13 @@ String OS_IOS::get_system_font_path(const String &p_font_name, int p_weight, int return ret; } -void OS_IOS::vibrate_handheld(int p_duration_ms) { +void OS_IOS::vibrate_handheld(int p_duration_ms, float p_amplitude) { if (ios->supports_haptic_engine()) { - ios->vibrate_haptic_engine((float)p_duration_ms / 1000.f); + if (p_amplitude > 0.0) { + p_amplitude = CLAMP(p_amplitude, 0.0, 1.0); + } + + ios->vibrate_haptic_engine((float)p_duration_ms / 1000.f, p_amplitude); } else { // iOS <13 does not support duration for vibration AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); diff --git a/platform/web/os_web.cpp b/platform/web/os_web.cpp index ab4e7f8470..6b6c9ddd63 100644 --- a/platform/web/os_web.cpp +++ b/platform/web/os_web.cpp @@ -174,7 +174,7 @@ void OS_Web::add_frame_delay(bool p_can_draw) { #endif } -void OS_Web::vibrate_handheld(int p_duration_ms) { +void OS_Web::vibrate_handheld(int p_duration_ms, float p_amplitude) { godot_js_input_vibrate_handheld(p_duration_ms); } diff --git a/platform/web/os_web.h b/platform/web/os_web.h index a825938e96..55a5fcc6c6 100644 --- a/platform/web/os_web.h +++ b/platform/web/os_web.h @@ -98,7 +98,7 @@ public: // Implemented in web_main.cpp loop callback instead. void add_frame_delay(bool p_can_draw) override; - void vibrate_handheld(int p_duration_ms) override; + void vibrate_handheld(int p_duration_ms, float p_amplitude) override; String get_cache_path() const override; String get_config_path() const override; |