Multi-Touch Core Support

bluebooth

Veteran
Veteran
Joined
Nov 12, 2015
Messages
95
Reaction score
115
First Language
English
Primarily Uses
N/A

Multi-Touch 1.02



Author: Michael Morris @Blue Booth Studios



Introduction


Separates out multi-touch core support from Map Controls, to facilitate further growth of the plugin, and allows for potential incorporation into core upgrades.  Includes swipe gesture support.


Features


- Full (tested) multi-touch support!  WARNING: This required me to redo most of TouchInput core, which may introduce some incompatibilities.


- Swipe (up/down/left/right) gesture support.


- Input History tracking.


Screenshots


No visual effect by itself.


How to Use
- Copy script into your game js/plugins directory. 


- Import this script into your project and set all parameters.


- Set up events to use with the plugin.  See demo provided.


Demo


See Map Controls plugin demo.


Script

//=============================================================================
// Bluebooth Plugins - MultiTouch
// BBS_MultiTouch.js
//=============================================================================

//=============================================================================
/*:
* @title MultiTouch Plugin
* @author Michael Morris (https://www.*******.com/bluebooth)
* @date March 4, 2016
* @filename BBS_MultiTouch.js
* If you enjoy my work, consider supporting me on *******!
*
* https://www.*******.com/bluebooth
*
* @plugindesc v1.02 Overrides TouchInput core scripts to provide true multi-touch
* and gesture support for RPGMaker MV. Originally developed in conjunction with
* BBS_MapControls.
*
* Special Thanks to Tsukihime for all the help.
* Special Thanks to 'Ramza' Michael Sweeney and Lakaroth for helping so much with testing.
* Special Thanks to Jairaks89 for https://github.com/jairajs89/Touchy.js/blob/master/touchy.js
* on which the multi-touch structure was loosely based.
*
* ============================================================================
* Terms of Use
* ============================================================================
* - Free for use in non-commercial projects with credits
* - Contact me for commercial use
*
* ============================================================================
* Parameters
* ============================================================================
* @param Debug Mode
* @desc Enable to activate console variable logging. Use for debugging odd behaviour.
* true to enable console variable logging.
* @default false
*
* @param -----Input History Tracking-----
* @param Button History Max. Size
* @desc How many buttons to track at once. Keep small. Use value of 1 for single touch, 2+ for multi-touch.
* @default 2
*
* @param -----Gesture Handling-----
* @param Gesture Distance Threshold
* @desc Minimum distance touch travels (in pixels) to be considered a swipe gesture.
* @default 150
*
* @param Gesture Distance Restraint
* @desc Maximum distance touch can travel (in pixels) in perpendicular direction to swipe to be considered a swipe.
* @default 100
*
* @param Gesture Maximum Time
* @desc Maximum time before a touch times out, and ceases to be considered a gesture (in milliseconds).
* @default 300
*
* @help
* ============================================================================
* Description
* ============================================================================
*
* Provides full multi-touch and gesture support to RPGMaker MV, adds input history
* tracking of MRU input (from keyboard, gamepad, mouse, touch), to allow for custom
* input handling.
*
* ============================================================================
* Script Commands
* ============================================================================
* Script Commands:
*
* TouchInput.wasGestured('swipe left'); # Returns true if swipe left was gestured most recently.
*
* ============================================================================
* Change Log
* ============================================================================
* 1.02 - fixed issue where multi-touch being enabled on its own would fail to accept map touch input.
* 1.01 - multi-touch fixed to still work with mouse. Click and touch event handling separated.
* 1.00 - multi-touch seperated into own script to facilitate further growth.
*/
//=============================================================================

//=============================================================================
var Imported = Imported || {} ;
var BBS = BBS || {};
Imported.MultiTouch = 1;
BBS.MultiTouch = BBS.MultiTouch || {};

(function() {

//=============================================================================
// Parameter Variables
//=============================================================================
var parameters = PluginManager.parameters('BBS_MultiTouch');
var pDebugging = eval(String(parameters['Debug Mode'] || 'false'));
var pBtnHistoryMaxSize= Number(parameters['Button History Max. Size'] || '2');
var pThreshold = Number(parameters['Gesture Distance Threshold'] || '150');
var pRestraint = Number(parameters['Gesture Distance Restraint'] || '100');
var pAllowedTime = Number(parameters['Gesture Maximum Time'] || '300');

//=============================================================================
// Input/Button History
//=============================================================================
function InputHistory() {
this.initialize.apply(this, arguments);
};

InputHistory.prototype = Object.create(InputHistory.prototype);
InputHistory.prototype.constructor = InputHistory;

InputHistory.prototype.initialize = function() {
this._btnHistory = Array();
this._swipeHistory = null;
};

InputHistory.prototype.wasPressed = function(btn) {
if (this._btnHistory.length < 1) return false;

var pressed = false;
for (var i = ; i < this._btnHistory.length; i++) {
if (this._btnHistory === btn) {
pressed = true;
break;
}
}

return pressed;
};

InputHistory.prototype.wasGestured = function(gesture) {
return (this._swipeHistory === gesture);
};

InputHistory.prototype.addBtn = function(btn) {
// If btn already stored, don't need to store it again.
if (!this.wasPressed(btn)) {
// Shift if history size exceeded.
if (this._btnHistory.length >= pBtnHistoryMaxSize) {
this._btnHistory.shift();
}

this._btnHistory.push(btn);

if (pDebugging) {
$gameScreen.showPicture(1, this._btnHistory[], , , , 100, 100, 255, );

if (this._btnHistory.length > 1) {
$gameScreen.showPicture(2, this._btnHistory[1], , 40, , 100, 100, 255, );
}
}

}
};

InputHistory.prototype.addGesture = function(gesture) {
this._swipeHistory = gesture;
};

InputHistory.prototype.removeBtn = function(btn) {
var index = this._btnHistory.indexOf(btn);
if (index >= ) {
this._btnHistory.splice(index, 1);
}
};

InputHistory.prototype.removeGesture = function(gesture) {
this._swipeHistory = null;
};

InputHistory.prototype._clear = function() {
if (pDebugging) {
$gameScreen.erasePicture(1);

if (this._btnHistory.length > 1) {
$gameScreen.erasePicture(2);
}
}

this._btnHistory = [];
this._swipeHistory = null;
};

//=============================================================================
// Finger - Object to manage single-finger touch interactions
//=============================================================================
function Finger(id) {
this.id = id;
this._x = null;
this._y = null;
this._startX = null;
this._startY = null;
this._date = ;

// Release not stored, because released fingers are deleted.
};

//=============================================================================
// Touch Input New Functions: https://github.com/jairajs89/Touchy.js/blob/master/touchy.js
//=============================================================================

/* Create a new hand based on the current touches on the screen */
TouchInput.fingersRestart = function(touches) {
if (touches.length <= ) {
return;
}

this.fingers = [];
for (var i = ; i < event.touches.length; i++)
{
var touch = event.touches;
var x = Graphics.pageToCanvasX(touch.pageX);
var y = Graphics.pageToCanvasY(touch.pageY);

if (Graphics.isInsideCanvas(x, y)) {
var finger = new Finger(touch.identifier);
this.fingers.push(finger);
this._screenPressed = true;

finger._x = x;
finger._startX = x;
finger._y = y;
finger._startY = y;
finger._date = Date.now();
finger._pressedTime = ;

if (pDebugging) {
console.log("Touch trigger activated");
console.log(this.fingers);
}

this._onTrigger(x, y);
}
}
};

TouchInput.fingersUpdate = function() {
if (!this.fingers) {
return;
}

for (var i = ; i < this.fingers.length; i++) {
var finger = this.fingers;
finger._pressedTime++;
}
};

/* Check a finger just prior to release to detect whether it was a swipe. */
TouchInput.checkForSwipe = function(finger) {
if (!finger) {
return;
}

var elapsedTime = new Date().getTime() - finger._date; // get time elapsed
if (elapsedTime > pAllowedTime) {
return;
}

var distX = finger._x - finger._startX; // get horizontal dist traveled by finger while in contact with surface
var distY = finger._y - finger._startY; // get vertical dist traveled by finger while in contact with surface

if (pDebugging) {
console.log(elapsedTime + " vs. " + pAllowedTime);
console.log("X Dist: " + distX + " Y Dist: " + distY);
console.log("Threshold: " + pThreshold + " Restraint: " + pRestraint);
}

if (Math.abs(distX) >= pThreshold && Math.abs(distY) <= pRestraint) { // 2nd condition for horizontal swipe met
// If dist traveled is negative, it indicates left swipe
if (distX < ) {
this._onSwipedLeft();
} else {
this._onSwipedRight();
}
}
else if (Math.abs(distY) >= pThreshold && Math.abs(distX) <= pRestraint) { // 2nd condition for vertical swipe met
// If dist traveled is negative, it indicates up swipe
if (distY < ) {
this._onSwipedUp();
} else {
this._onSwipedDown();
}
}

return;
};

/* Destroy the current hand regardless of fingers on the screen */
TouchInput.fingersDestroy = function() {
if (!this.fingers) {
return;
}

for (var i = ; i < this.fingers.length; i++) {
var finger = this.fingers;

// Gesture handling happens here.
this.checkForSwipe(finger);
this._onRelease(finger._x, finger._y);
}

this._screenPressed = false;
this.fingers = null;
};

/* Get finger by id */
TouchInput.getFinger = function(id) {
var foundFinger = null;

if (!this.fingers) return foundFinger;

// Direct iteration significantly faster than foreach.
for(var i = ; i < this.fingers.length; i++) {
if (this.fingers.id === id) {
foundFinger = this.fingers;
break;
}
}

return foundFinger;
};

/* last touch.x and .y will not work with multi-touch. Need to override. */
TouchInput.isTouchingArea = function(rect) {
if (this.fingers === null) return false;

for (var i = ; i < this.fingers.length; i++) {
var finger = this.fingers;
if (pDebugging) {
var strOut = "Finger at: " + finger._x + "; " + finger._y + " vs. touch area: " + rect.x + "; " + rect.y;
console.log(strOut);
}

if (rect.contains(finger._x, finger._y)) {
return true;
}
}

return false;
};

/* last touch.x and .y used only for mouse click. Required function, mouse input was non-functional.*/
TouchInput.isClicked = function(rect) {
if ( !this.isPressed() ) return false;

if (pDebugging) {
var strOut = "Click at: " + this._x + "; " + this._y + " vs. click area: " + rect.x + "; " + rect.y;
console.log(strOut);
}

if (rect.contains(this._x, this._y)) {
return true;
}

return false;
};

/* Input check for directional swipe */
TouchInput.swipedLeft = function() {
var swipedLeft = this._history.wasGestured('swipe left');
return swipedLeft;
};

/* Input check for directional swipe */
TouchInput.swipedRight = function() {
var swipedRight = this._history.wasGestured('swipe right');
return swipedRight;
};

/* Input check for directional swipe */
TouchInput.swipedUp = function() {
var swipedUp = this._history.wasGestured('swipe up');
return swipedUp;
};

/* Input check for directional swipe */
TouchInput.swipedDown = function() {
var swipedDown = this._history.wasGestured('swipe down');
return swipedDown;
};

/**
* @static
* @method _onSwipeLeft
* @private
*/
TouchInput._onSwipedLeft = function() {
this._history.addGesture('swipe left');
this._events.swipedLeft = true;
if (pDebugging) {
console.log("Left swipe detected!");
}
};

/**
* @static
* @method _onSwipeRight
* @private
*/
TouchInput._onSwipedRight = function() {
this._history.addGesture('swipe right');
this._events.swipedRight = true;
if (pDebugging) {
console.log("Right swipe detected!");
}
};

/**
* @static
* @method _onSwipeUp
* @private
*/
TouchInput._onSwipedUp = function() {
this._history.addGesture('swipe up');
this._events.swipedUp = true;
if (pDebugging) {
console.log("Up swipe detected!");
}
};

/**
* @static
* @method _onSwipeDown
* @private
*/
TouchInput._onSwipedDown = function() {
this._history.addGesture('swipe down');
this._events.swipedDown = true;
if (pDebugging) {
console.log("Down swipe detected!");
}
};

TouchInput.prototype.isGestured = function(gest) {
var gestSafe = gest.toLowerCase();
var pressed = this._history.wasGestured(gestSafe);
if (pressed) {
if (pDebugging) console.log(gestSafe);
this._history.removeGesture(gestSafe);
}

return pressed;
};

/* Adds button to input history if not already in input history. */
TouchInput.addLastButton = function(btn) {
this._history.addBtn(btn);
};

/* Clears input history. */
TouchInput._clearHistory = function() {
this._history._clear();
};

//=============================================================================
// Touch Input Core Overrides
//=============================================================================

/**
* Initializes the touch system.
*
* @static
* @method initialize
*/
var BBS_MC_TouchInputInitialize = TouchInput.initialize;
TouchInput.initialize = function() {
BBS_MC_TouchInputInitialize.call(this, arguments);
this._history = new InputHistory();
this.fingers = null;
};

/**
* @static
* @method _onTouchStart
* @param {TouchEvent} event
* @private
*/
// Replaces core functionality. Do not modify!
TouchInput._onTouchStart = function(event) {
this.fingersDestroy();
this.fingersRestart(event.touches);
if (window.cordova || window.navigator.standalone) {
event.preventDefault();
}
};

/**
* @static
* @method _onTouchMove
* @param {TouchEvent} event
* @private
*/
// Replaces core functionality. Do not modify!
TouchInput._onTouchMove = function(event) {
// Each changed touch for this event represents a moved touch.
// Direct iteration significantly faster than foreach.
for (var i = ; i < event.changedTouches.length; i++) {
var touch = event.changedTouches;
var finger = this.getFinger(touch.identifier);
if( !finger ) {
return;
}

var x = Graphics.pageToCanvasX(touch.pageX);
var y = Graphics.pageToCanvasY(touch.pageY);
finger._x = x;
finger._y = y;

this._onMove(x, y);
}
};

/**
* @static
* @method _onTouchEnd
* @param {TouchEvent} event
* @private
*/
// Replaces core functionality. Do not modify!
TouchInput._onTouchEnd = function(event) {
this.fingersDestroy();

var remainingTouches = [];
// Direct iteration significantly faster than foreach.
for (var i = ; i < event.touches.length; i++) {
var unChanged = true;

// Each changed touch for this event represents a released touch.
for (var j = ; j < event.changedTouches.length; j++) {
if (event.changedTouches[j].identifier === event.touches.identifier) {
unChanged = false;
break;
}
}

if (unChanged) {
remainingTouches.push(event.touches);
}
}

this.fingersRestart(remainingTouches);
event.preventDefault();
};

/**
* Clears all the touch data.
*
* @static
* @method clear
*/
var BBS_MC_TouchInputclear = TouchInput.clear;
TouchInput.clear = function() {
BBS_MC_TouchInputclear.call(this);
this._events.swipedLeft = false;
this._events.swipedRight = false;
this._events.swipedUp = false;
this._events.swipedDown = false;
this._swipedLeft = false;
this._swipedRight = false;
this._swipedUp = false;
this._swipedDown = false;
this.fingersDestroy();
};

/**
* Updates the touch data.
*
* @static
* @method update
*/
var BBS_MC_TouchInputupdate = TouchInput.update;
TouchInput.update = function() {
BBS_MC_TouchInputupdate.call(this);
this._swipedLeft = this._events.swipedLeft;
this._swipedRight = this._events.swipedRight;
this._swipedUp = this._events.swipedUp;
this._swipedDown = this._events.swipedDown;
this._events.swipedLeft = false;
this._events.swipedRight = false;
this._events.swipedUp = false;
this._events.swipedDown = false;
};

})(BBS.MultiTouch);
//=============================================================================
// End of File
//=============================================================================




 


Known Bugs / TODO


- If feature requested, extend plugin to work with all scenes.


- If feature requested, extend plugin to work with more gestures (one gesture per version).


- Checking with major script developers to ensure compatibility with their plugins.


- Make your suggestions or report bugs here!


 


History


 

v1.02


  - fixed issue where multi-touch being enabled on its own would fail to accept map touch input.


v1.01


  - multi-touch fixed to still work with mouse.  Click and touch event handling separated.
v1.00



  - multi-touch seperated into own script to facilitate further growth.

Suggestions, bug reports, and feature requests are welcomed!


 


Compatibility Issues


None known.  Compatible with Klaus Map Overlays, Terrax Lighting System, and most (if not all) of Yanfly's plugins.


 


FAQ


Q: The demo won't open, how to open it?
A: Download Winrar and try again.


 


Credit and Thanks
- Micheal Morris @Blue Booth Studios
- Credit to Tsukihime for continuing to be so helpful!


- Jairajs89 - Touchy.js, which serves as basis for some multi-touch functionality.


- Ramza, Lakaroth and friends for all their help testing multi-touch.


 


Author's Notes
Free for non-commercial usage as long as credit is given, contact me for commercial use.


View attachment BBS_MultiTouch.js
 
Last edited by a moderator:

Lidnel

Warper
Member
Joined
Mar 15, 2016
Messages
3
Reaction score
2
First Language
Ukrainian
Primarily Uses
Hello. Maybe I did something wrong but it just stuns movement. I tested in a pure project on android tablet by usb run. Really interested in your work, thanks
 

bluebooth

Veteran
Veteran
Joined
Nov 12, 2015
Messages
95
Reaction score
115
First Language
English
Primarily Uses
N/A
Hello. Maybe I did something wrong but it just stuns movement. I tested in a pure project on android tablet by usb run. Really interested in your work, thanks


Hey, Lidnel, sorry to hear you're having trouble.  Can you expand on what you mean by "stuns movement"?  How is your project setup?
 

Lidnel

Warper
Member
Joined
Mar 15, 2016
Messages
3
Reaction score
2
First Language
Ukrainian
Primarily Uses
When I touch screen on MAP scene nothing happens, menu works
 

bluebooth

Veteran
Veteran
Joined
Nov 12, 2015
Messages
95
Reaction score
115
First Language
English
Primarily Uses
N/A
Hello. Maybe I did something wrong but it just stuns movement. I tested in a pure project on android tablet by usb run. Really interested in your work, thanks


I'll take a look into that, thank you.  Does it cause any issues in combat, too?
 

bluebooth

Veteran
Veteran
Joined
Nov 12, 2015
Messages
95
Reaction score
115
First Language
English
Primarily Uses
N/A
Sorry, I don't use combat in my game. I can test it later.


Standard new project without plugins etc.


Sounds good.  Did you change any of the default plugin values for multi-touch and if so, to what?


The issue is likely a conflict between the way default RMMV stores map touch as a single x, y value, whereas multi-touch uses an array of values (it must by nature).  I'm in the middle of busy week at university, but I'll take a look at the plugin to correct this really soon.


UPDATE: Still crazy busy, but I've been able to replicate your issue.  Will have a fix within the next two weeks (mainly dependent on University, I have had a major deadline every 24 hours there for the past two weeks, and I still have to survive until the end of the month before projects are all over).


EDIT: That moment when it turns out you forgot to set InputTouch.isTouched to true when a finger gesture occurs.  I've fixed the issue, and will be posting an update now.  Sorry it took a while, @Lidnel!
 
Last edited by a moderator:

TenTranVN

Veteran
Veteran
Joined
Apr 13, 2017
Messages
165
Reaction score
17
First Language
Vietnam
Primarily Uses
RMMV
Support Webbrowser ?, MV 1.5.1 ?
 

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Posts

Latest Profile Posts

Playtesting for Battle Castle is done, demo time for me was roughly 6 hours and 20 minutes. I'm going to pass it on to a few friends first, and after getting some feedback will be posting an official topic.
There's another Humble Bundle available now for RPG developer assets, this time focused somewhat more towards pixel art style games.
Ive got a long lost half uncle. Thats real cool.
I forget what is bumping rule in this forum. Is anyone know? Thank you.
BCj
Ew, why does my blogpost on the rpgmaker.net site look like a garbled mess? Are html codes disabled or something?

Forum statistics

Threads
97,909
Messages
947,780
Members
129,148
Latest member
lemminace
Top