diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ce043f0a5..d3c772e382 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1139,6 +1139,7 @@ set(NativeAppSource UI/MiscScreens.cpp UI/GameScreen.cpp UI/GameSettingsScreen.cpp + UI/TiltAnalogSettingsScreen.cpp UI/TouchControlLayoutScreen.cpp UI/TouchControlVisibilityScreen.cpp UI/GamepadEmu.cpp diff --git a/Core/Config.cpp b/Core/Config.cpp index d06b03a89e..3cf1d2f3ca 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -219,7 +219,14 @@ void Config::Load(const char *iniFileName, const char *controllerIniFilename) { // control->Get("KeyMapping",iMappingMap); #ifdef USING_GLES2 control->Get("AccelerometerToAnalogHoriz", &bAccelerometerToAnalogHoriz, false); - control->Get("TiltSensitivity", &iTiltSensitivity, 100); + + control->Get("TiltBaseX", &fTiltBaseX, 0); + control->Get("TiltBaseY", &fTiltBaseY, 0); + control->Get("InvertTiltX", &bInvertTiltX, false); + control->Get("InvertTiltY", &bInvertTiltY, true); + control->Get("TiltSensitivityX", &iTiltSensitivityX, 100); + control->Get("TiltSensitivityY", &iTiltSensitivityY, 100); + #endif control->Get("TouchButtonOpacity", &iTouchButtonOpacity, 65); control->Get("ButtonScale", &fButtonScale, 1.15); @@ -411,7 +418,12 @@ void Config::Save() { // control->Set("KeyMapping",iMappingMap); #ifdef USING_GLES2 control->Set("AccelerometerToAnalogHoriz", bAccelerometerToAnalogHoriz); - control->Set("TiltSensitivity", iTiltSensitivity); + control->Set("TiltBaseX", fTiltBaseX); + control->Set("TiltBaseY", fTiltBaseY); + control->Set("InvertTiltX", bInvertTiltX); + control->Set("InvertTiltY", bInvertTiltY); + control->Set("TiltSensitivityX", iTiltSensitivityX); + control->Set("TiltSensitivityY", iTiltSensitivityY); #endif control->Set("TouchButtonOpacity", iTouchButtonOpacity); control->Set("ButtonScale", fButtonScale); diff --git a/Core/Config.h b/Core/Config.h index ec1dc33c12..13ddde2e05 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -108,7 +108,17 @@ public: int iShowFPSCounter; bool bShowDebugStats; bool bAccelerometerToAnalogHoriz; - int iTiltSensitivity; + + //Analog stick tilting + //the base x and y tilt. this inclination is treated as (0,0) and the tilt input + //considers this orientation to be equal to no movement of the analog stick. + float fTiltBaseX, fTiltBaseY; + //whether the x axes and y axes should invert directions (left becomes right, top becomes bottom.) + bool bInvertTiltX, bInvertTiltY; + //the sensitivity of the tilt in the x direction + int iTiltSensitivityX; + //the sensitivity of the tilt in the Y direction + int iTiltSensitivityY; // The three tabs. bool bGridView1; diff --git a/UI/EmuScreen.cpp b/UI/EmuScreen.cpp index eb77207076..65f57e4a0c 100644 --- a/UI/EmuScreen.cpp +++ b/UI/EmuScreen.cpp @@ -189,16 +189,19 @@ void EmuScreen::sendMessage(const char *message, const char *value) { } } +//curve1 implements a smooth deadzone as described here: +//http://www.gamasutra.com/blogs/JoshSutphin/20130416/190541/Doing_Thumbstick_Dead_Zones_Right.php inline float curve1(float x) { - const float deadzone = 0.15f; - const float factor = 1.0f / (1.0f - deadzone); - if (x > deadzone) { - return (x - deadzone) * (x - deadzone) * factor; - } else if (x < -0.1f) { - return -(x + deadzone) * (x + deadzone) * factor; - } else { - return 0.0f; - } + const float deadzone = 0.03f; + const float factor = 1.0f / (1.0f - deadzone); + if (x > deadzone) { + return (x - deadzone) * (x - deadzone) * factor; + } else if (x < -deadzone) { + return -(x + deadzone) * (x + deadzone) * factor; + } else { + return 0.0f; + } + } inline float clamp1(float x) { @@ -467,9 +470,38 @@ void EmuScreen::update(InputState &input) { // TODO: Make into an axis #ifdef USING_GLES2 if (g_Config.bAccelerometerToAnalogHoriz) { - // TODO: Deadzone, etc. - leftstick_x += clamp1(curve1(input.acc.y) * 2.0f) * g_Config.iTiltSensitivity / 100; + + //get the "base" coordinate system which is setup by the calibration system + float base_x = g_Config.fTiltBaseX; + float base_y = g_Config.fTiltBaseY; + + //convert the current input into base coordinates and normalize + //TODO: check if all phones give values between [-50, 50]. I'm not sure how iOS works. + float normalized_input_x = (input.acc.y - base_x) / 50.0 ; + float normalized_input_y = (input.acc.x - base_y) / 50.0 ; + + //TODO: need a better name for computed x and y. + float delta_x = curve1(normalized_input_x * 2.0 * (g_Config.iTiltSensitivityX)) ; + + //if the invert is enabled, invert the motion + if(g_Config.bInvertTiltX){ + delta_x *= -1; + } + + float delta_y = curve1(normalized_input_y * 2.0 * (g_Config.iTiltSensitivityY)) ; + + if(g_Config.bInvertTiltY){ + delta_y *= -1; + } + + //clamp the delta between [-1, 1] + leftstick_x += clamp1(delta_x); __CtrlSetAnalogX(clamp1(leftstick_x), CTRL_STICK_LEFT); + + + leftstick_y += clamp1(delta_y); + __CtrlSetAnalogY(clamp1(leftstick_y), CTRL_STICK_LEFT); + } #endif diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 2574f441ed..2cac66403c 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -31,6 +31,7 @@ #include "UI/DevScreens.h" #include "UI/TouchControlLayoutScreen.h" #include "UI/TouchControlVisibilityScreen.h" +#include "UI/TiltAnalogSettingsScreen.h" #include "Core/Config.h" #include "Core/Host.h" @@ -211,7 +212,9 @@ void GameSettingsScreen::CreateViews() { #ifdef USING_GLES2 controlsSettings->Add(new CheckBox(&g_Config.bHapticFeedback, c->T("HapticFeedback", "Haptic Feedback (vibration)"))); controlsSettings->Add(new CheckBox(&g_Config.bAccelerometerToAnalogHoriz, c->T("Tilt", "Tilt to Analog (horizontal)"))); - controlsSettings->Add(new PopupSliderChoice(&g_Config.iTiltSensitivity, 10, 200, c->T("Tilt Sensitivity"), screenManager())); + Choice *tiltAnalog = controlsSettings->Add(new Choice(c->T("Customize tilt"))); + tiltAnalog->OnClick.Handle(this, &GameSettingsScreen::OnTiltAnalogSettings); + tiltAnalog->SetEnabled(g_Config.bAccelerometerToAnalogHoriz); #endif controlsSettings->Add(new ItemHeader(c->T("OnScreen", "On-Screen Touch Controls"))); controlsSettings->Add(new CheckBox(&g_Config.bShowTouchControls, c->T("OnScreen", "On-Screen Touch Controls")))->OnClick.Handle(this, &GameSettingsScreen::OnToggleTouchControls); @@ -467,6 +470,11 @@ UI::EventReturn GameSettingsScreen::OnTouchControlLayout(UI::EventParams &e) { return UI::EVENT_DONE; }; +UI::EventReturn GameSettingsScreen::OnTiltAnalogSettings(UI::EventParams &e){ + screenManager()->push(new TiltAnalogSettingsScreen()); + return UI::EVENT_DONE; +}; + void DeveloperToolsScreen::CreateViews() { using namespace UI; root_ = new ScrollView(ORIENT_VERTICAL); diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index e4981e297c..89dbd35323 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -55,6 +55,8 @@ private: UI::EventReturn OnBack(UI::EventParams &e); UI::EventReturn OnReloadCheats(UI::EventParams &e); UI::EventReturn OnToggleTouchControls(UI::EventParams &e); + UI::EventReturn OnTiltAnalogSettings(UI::EventParams &e); + // Global settings handlers UI::EventReturn OnLanguage(UI::EventParams &e); diff --git a/UI/TiltAnalogSettingsScreen.cpp b/UI/TiltAnalogSettingsScreen.cpp new file mode 100644 index 0000000000..fd0ce0762e --- /dev/null +++ b/UI/TiltAnalogSettingsScreen.cpp @@ -0,0 +1,67 @@ +#include "TiltAnalogSettingsScreen.h" +#include "Core/Config.h" +#include "Core/System.h" +#include "i18n/i18n.h" + +TiltAnalogSettingsScreen::TiltAnalogSettingsScreen() : currentTiltX_(0), currentTiltY_(0) {}; + +void TiltAnalogSettingsScreen::CreateViews(){ + using namespace UI; + + I18NCategory *c = GetI18NCategory("Controls"); + + root_ = root_ = new ScrollView(ORIENT_VERTICAL); + + LinearLayout *settings = new LinearLayout(ORIENT_VERTICAL); + settings->SetSpacing(0); + + + settings->Add(new ItemHeader(c->T("Invert Axes"))); + settings->Add(new CheckBox(&g_Config.bInvertTiltX, c->T("Invert Tilt along X axis"))); + settings->Add(new CheckBox(&g_Config.bInvertTiltY, c->T("Invert Tilt along Y axis"))); + + settings->Add(new ItemHeader(c->T("Sensitivity"))); + //TODO: allow values greater than 100? I'm not sure if that's needed. + settings->Add(new PopupSliderChoice(&g_Config.iTiltSensitivityX, 0, 100, c->T("Tilt Sensitivity along X axis"), screenManager())); + settings->Add(new PopupSliderChoice(&g_Config.iTiltSensitivityY, 0, 100, c->T("Tilt Sensitivity along Y axis"), screenManager())); + + settings->Add(new ItemHeader(c->T("Calibration"))); + InfoItem *calibrationInfo = new InfoItem("To calibrate, keep device on a flat surface and press calibrate.", ""); + settings->Add(calibrationInfo); + + Choice *calibrate = new Choice(c->T("Calibrate D-Pad")); + calibrate->OnClick.Handle(this, &TiltAnalogSettingsScreen::OnCalibrate); + settings->Add(calibrate); + + root_->Add(settings); +}; + +void TiltAnalogSettingsScreen::update(InputState &input){ + UIScreen::update(input); + //I'm not sure why y is x and x is y. i's probably because of the orientation + //of the screen (the x and y are in portrait coordinates). once portrait and + //reverse-landscape is enabled, this will probably have to change. + //If needed, we can add a "swap x and y" option. + currentTiltX_ = input.acc.y; + currentTiltY_ = input.acc.x; +}; + + +UI::EventReturn TiltAnalogSettingsScreen::OnBack(UI::EventParams &e){ + if (PSP_IsInited()) { + screenManager()->finishDialog(this, DR_CANCEL); + } else { + screenManager()->finishDialog(this, DR_OK); + } + + return UI::EVENT_DONE; +}; + + +UI::EventReturn TiltAnalogSettingsScreen::OnCalibrate(UI::EventParams &e){ + g_Config.fTiltBaseX = currentTiltX_; + g_Config.fTiltBaseY = currentTiltY_; + + return UI::EVENT_DONE; +}; + diff --git a/UI/TiltAnalogSettingsScreen.h b/UI/TiltAnalogSettingsScreen.h new file mode 100644 index 0000000000..d8353d34d3 --- /dev/null +++ b/UI/TiltAnalogSettingsScreen.h @@ -0,0 +1,36 @@ +// Copyright (c) 2013- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "base/functional.h" +#include "ui/view.h" +#include "MiscScreens.h" + +class TiltAnalogSettingsScreen : public UIDialogScreenWithBackground { +public: + TiltAnalogSettingsScreen(); + + virtual void CreateViews(); + virtual void update(InputState &input); +protected: + virtual UI::EventReturn OnBack(UI::EventParams &e); +private: + UI::EventReturn OnCalibrate(UI::EventParams &e); + float currentTiltX_, currentTiltY_; +}; + diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 1da0d80ddd..62b2906aab 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -110,6 +110,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/UI/GameScreen.cpp \ $(SRC)/UI/ControlMappingScreen.cpp \ $(SRC)/UI/GameSettingsScreen.cpp \ + $(SRC)/UI/TiltAnalogSettingsScreen.cpp \ $(SRC)/UI/TouchControlLayoutScreen.cpp \ $(SRC)/UI/TouchControlVisibilityScreen.cpp \ $(SRC)/UI/CwCheatScreen.cpp \