瀏覽代碼

Merge pull request #2301 from Laserlicht/haptic_feedback

Haptic feedback
Ivan Savenko 2 年之前
父節點
當前提交
b30db5fc1f

+ 2 - 0
Mods/vcmi/config/vcmi/english.json

@@ -74,6 +74,8 @@
 	"vcmi.systemOptions.longTouchMenu.entry"     : "%d milliseconds",
 	"vcmi.systemOptions.framerateButton.hover"  : "Show FPS",
 	"vcmi.systemOptions.framerateButton.help"   : "{Show FPS}\n\nToggle the visibility of the Frames Per Second counter in the corner of the game window",
+	"vcmi.systemOptions.hapticFeedbackButton.hover"  : "Haptic feedback",
+	"vcmi.systemOptions.hapticFeedbackButton.help"   : "{Haptic feedback}\n\nToggle the haptic feedback on touch inputs",
 
 	"vcmi.adventureOptions.infoBarPick.hover" : "Show Messages in Info Panel",
 	"vcmi.adventureOptions.infoBarPick.help" : "{Show Messages in Info Panel}\n\nWhenever possible, game messages from visiting map objects will be shown in the info panel, instead of popping up in a separate window.",

+ 2 - 0
Mods/vcmi/config/vcmi/german.json

@@ -74,6 +74,8 @@
 	"vcmi.systemOptions.longTouchMenu.entry"     : "%d Millisekunden",
 	"vcmi.systemOptions.framerateButton.hover"  : "FPS anzeigen",
 	"vcmi.systemOptions.framerateButton.help"   : "{FPS anzeigen}\n\n Schaltet die Sichtbarkeit des Zählers für die Bilder pro Sekunde in der Ecke des Spielfensters um.",
+	"vcmi.systemOptions.hapticFeedbackButton.hover"  : "Haptisches Feedback",
+	"vcmi.systemOptions.hapticFeedbackButton.help"   : "{Haptisches Feedback}\n\nHaptisches Feedback bei Touch-Eingaben.",
 
 	"vcmi.adventureOptions.infoBarPick.hover" : "Meldungen im Infobereich anzeigen",
 	"vcmi.adventureOptions.infoBarPick.help" : "{Meldungen im Infobereich anzeigen}\n\nWann immer möglich, werden Spielnachrichten von besuchten Kartenobjekten in der Infoleiste angezeigt, anstatt als Popup-Fenster zu erscheinen",

+ 2 - 1
android/vcmi-app/src/main/AndroidManifest.xml

@@ -3,6 +3,7 @@
     package="eu.vcmi.vcmi">
 
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.VIBRATE" />
 
     <application
         android:extractNativeLibs="true"
@@ -50,4 +51,4 @@
             android:exported="false"/>
     </application>
 
-</manifest>
+</manifest>

+ 14 - 0
android/vcmi-app/src/main/java/eu/vcmi/vcmi/NativeMethods.java

@@ -3,11 +3,14 @@ package eu.vcmi.vcmi;
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Build;
 import android.os.Environment;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
 
 import org.libsdl.app.SDL;
 import org.libsdl.app.SDLActivity;
@@ -138,6 +141,17 @@ public class NativeMethods
     {
         internalProgressDisplay(false);
     }
+    
+    @SuppressWarnings(Const.JNI_METHOD_SUPPRESS)
+    public static void hapticFeedback()
+    {
+        final Context ctx = SDL.getContext();
+        if (Build.VERSION.SDK_INT >= 26) {
+            ((Vibrator) ctx.getSystemService(ctx.VIBRATOR_SERVICE)).vibrate(VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK));
+        } else {
+            ((Vibrator) ctx.getSystemService(ctx.VIBRATOR_SERVICE)).vibrate(30);
+        }
+    }
 
     private static void internalProgressDisplay(final boolean show)
     {

+ 20 - 0
client/eventsSDL/InputSourceTouch.cpp

@@ -22,6 +22,12 @@
 #include "../gui/MouseButton.h"
 #include "../gui/WindowHandler.h"
 
+#if defined(VCMI_ANDROID)
+#include "../../lib/CAndroidVMHelper.h"
+#elif defined(VCMI_IOS)
+#include "../ios/utils.h"
+#endif
+
 #include <SDL_events.h>
 #include <SDL_hints.h>
 #include <SDL_timer.h>
@@ -32,6 +38,7 @@ InputSourceTouch::InputSourceTouch()
 	params.useRelativeMode = settings["general"]["userRelativePointer"].Bool();
 	params.relativeModeSpeedFactor = settings["general"]["relativePointerSpeedMultiplier"].Float();
 	params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float();
+	params.hapticFeedbackEnabled = settings["general"]["hapticFeedback"].Bool();
 
 	if (params.useRelativeMode)
 		state = TouchState::RELATIVE_MODE;
@@ -100,6 +107,7 @@ void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinge
 {
 	// FIXME: better place to update potentially changed settings?
 	params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float();
+	params.hapticFeedbackEnabled = settings["general"]["hapticFeedback"].Bool();
 
 	lastTapTimeTicks = tfinger.timestamp;
 
@@ -215,6 +223,7 @@ void InputSourceTouch::handleUpdate()
 		if (currentTime > lastTapTimeTicks + params.longTouchTimeMilliseconds)
 		{
 			GH.events().dispatchShowPopup(GH.getCursorPosition());
+			hapticFeedback();
 
 			if (GH.windows().isTopWindowPopup())
 				state = TouchState::TAP_DOWN_LONG;
@@ -287,3 +296,14 @@ void InputSourceTouch::emitPinchEvent(const SDL_TouchFingerEvent & tfinger)
 	if (distanceOld > params.pinchSensitivityThreshold)
 		GH.events().dispatchGesturePinch(lastTapPosition, distanceNew / distanceOld);
 }
+
+void InputSourceTouch::hapticFeedback() {
+	if(params.hapticFeedbackEnabled) {
+#if defined(VCMI_ANDROID)
+        CAndroidVMHelper vmHelper;
+        vmHelper.callStaticVoidMethod(CAndroidVMHelper::NATIVE_METHODS_DEFAULT_CLASS, "hapticFeedback");
+#elif defined(VCMI_IOS)
+    	iOS_utils::hapticFeedback();
+#endif
+	}
+}

+ 4 - 0
client/eventsSDL/InputSourceTouch.h

@@ -79,6 +79,8 @@ struct TouchInputParameters
 	uint32_t pinchSensitivityThreshold = 10;
 
 	bool useRelativeMode = false;
+
+	bool hapticFeedbackEnabled = false;
 };
 
 /// Class that handles touchscreen input from SDL events
@@ -94,6 +96,8 @@ class InputSourceTouch
 
 	void emitPanningEvent(const SDL_TouchFingerEvent & tfinger);
 	void emitPinchEvent(const SDL_TouchFingerEvent & tfinger);
+	
+	void hapticFeedback();
 
 public:
 	InputSourceTouch();

+ 2 - 0
client/ios/utils.h

@@ -15,4 +15,6 @@ double screenScale();
 
 void showLoadingIndicator();
 void hideLoadingIndicator();
+
+void hapticFeedback();
 }

+ 6 - 0
client/ios/utils.mm

@@ -43,4 +43,10 @@ void hideLoadingIndicator()
 	[indicator removeFromSuperview];
 	indicator = nil;
 }
+
+void hapticFeedback()
+{
+    auto hapticGen = [[UIImpactFeedbackGenerator alloc] initWithStyle:UIImpactFeedbackStyleLight];
+    [hapticGen impactOccurred];
+}
 }

+ 8 - 0
client/windows/settings/GeneralOptionsTab.cpp

@@ -153,6 +153,10 @@ GeneralOptionsTab::GeneralOptionsTab()
 	{
 		setBoolSetting("video", "showfps", value);
 	});
+	addCallback("hapticFeedbackChanged", [](bool value)
+	{
+		setBoolSetting("general", "hapticFeedback", value);
+	});
 
 	//moved from "other" tab that is disabled for now to avoid excessible tabs with barely any content
 	addCallback("availableCreaturesAsDwellingChanged", [=](int value)
@@ -190,6 +194,10 @@ GeneralOptionsTab::GeneralOptionsTab()
 	std::shared_ptr<CToggleButton> framerateCheckbox = widget<CToggleButton>("framerateCheckbox");
 	framerateCheckbox->setSelected(settings["video"]["showfps"].Bool());
 
+	std::shared_ptr<CToggleButton> hapticFeedbackCheckbox = widget<CToggleButton>("hapticFeedbackCheckbox");
+	if (hapticFeedbackCheckbox)
+		hapticFeedbackCheckbox->setSelected(settings["general"]["hapticFeedback"].Bool());
+
 	std::shared_ptr<CSlider> musicSlider = widget<CSlider>("musicSlider");
 	musicSlider->scrollTo(CCS->musich->getVolume());
 

+ 6 - 1
config/schemas/settings.json

@@ -33,7 +33,8 @@
 				"extraDump",
 				"userRelativePointer",
 				"relativePointerSpeedMultiplier",
-				"longTouchTimeMilliseconds"
+				"longTouchTimeMilliseconds",
+				"hapticFeedback"
 			],
 			"properties" : {
 				"playerName" : {
@@ -101,6 +102,10 @@
 				"longTouchTimeMilliseconds" : {
 					"type" : "number",
 					"default" : 1000
+				},
+				"hapticFeedback" : {
+					"type" : "boolean",
+					"default" : false
 				}
 			}
 		},

+ 11 - 1
config/widgets/settings/generalOptionsTab.json

@@ -57,6 +57,10 @@
 					"name": "longTouchLabel",
 					"text": "vcmi.systemOptions.longTouchButton.hover",
 					"created" : "touchscreen"
+				},
+				{
+					"text": "vcmi.systemOptions.hapticFeedbackButton.hover",
+					"created" : "mobile"
 				}
 			]
 		},
@@ -76,7 +80,7 @@
 					"name": "scalingButton",
 					"type": "buttonGear",
 					"help": "vcmi.systemOptions.scalingButton",
-					"callback": "setGameScaling",
+					"callback": "setGameScaling"
 				},
 				{
 					"name": "fullscreenBorderlessCheckbox",
@@ -106,6 +110,12 @@
 					"help": "vcmi.systemOptions.longTouchButton",
 					"callback": "setLongTouchDuration",
 					"created" : "touchscreen"
+				},
+				{
+					"name": "hapticFeedbackCheckbox",
+					"help": "vcmi.systemOptions.hapticFeedbackButton",
+					"callback": "hapticFeedbackChanged",
+					"created" : "mobile"
 				}
 			]
 		},