PDA

Volledige versie bekijken : MouseDetection LMB, MMB, RMB


Mediamonkey
%Europe/Berlin %737 %2005, 17:41
Dag lui,

Ik ben momenteel eindelijk weer druk bezig met m'n arcade-fly-shooter-game-ding na een beetje aandringen van Dauntless, en ik zat me te ergeren aan het feit dat ik te weinig muisknoppen kon gebruiken om m'n vliegtuigje aan te sturen. Ik wist wel hoe ik de up- & down-events kon faken, en kwam er achter dat de middelste muisknop ook prima te detecteren viel. A plan was born!

Zie hier de class en demo voor het detecteren van de LMB, MMB en RMB. Okee, de LMB was niet nodig geweest, aangezien die al een listener heeft in de Mouse-class, maar ik heb 'm toegevoegd voor het complete plaatje. De RMB is waarschijnlijk ook niet handig vanwege het oppoppende contectmenuutje, maar die MMB is mooi meegenomen! Kun je toch weer mooi een extra actie met je muis uitvoeren :)

Klik hier voor een online demo. (http://www.mediamonkey.nl/flashfiles/MouseDetection.html) | En hier voor de sources. (http://www.mediamonkey.nl/flashfiles/MouseDetection.zip)

Op- en aanmerkingen worden zoals gewoonlijk op prijs gesteld :)
Mediamonkey

** EDIT **
Tadaaa, ik heb een scroll en een doubleclick toegevoegd. Allebij als event te benaderen. Ik heb er niet voor gekozen om per button een apart event af te vuren, maar het buttontype als target (string) mee te geven. Dan mag je mooi in de scrollfunctie die je als listener verbindt die naam uit het argument-object vissen en er iets mee aansturen.

De swf en zip waar de linkjes naar verwijzen zijn ook aangepast.

/** MouseDetection.as
* @Author: Bart "Mediamonkey" Wttewaall
* @Date: 31-10-2005
* @Description:
* A class that tests throug an interval if the left, middle or right mousebutton is up or down.
* It returns an event that can be listened to through the EventDispatcher.
*
* Events LMBup, LMBdown, MMBup, MMBdown, RMBup, RMBdown all return buttonname and isDown boolean
* scroll returns direction as lines(number), doubleclick returns offsettime between clicks
*
*/

import mx.utils.Delegate;
import mx.events.EventDispatcher;

class MouseDetection {

private var intervalID:Number;
private var mouseListener:Object;

// Button states (true = down, false = up)
private var LMB:Boolean;
private var RMB:Boolean;
private var MMB:Boolean;

// stored time between two clicks
private var LMBtime:Number;
private var MMBtime:Number;
private var RMBtime:Number;

// after some tests, the fastests I could click was 30 ms
public var time:Number = 30;

// offset between two mouseclicks to count as a doubleclick
public var doubletime:Number = 250;

// mixin methods from the EventDispatcher
public var addEventListener:Function;
public var removeEventListener:Function;
public var dispatchEvent:Function;
public var dispatchQueue:Function;

function MouseDetection() {
EventDispatcher.initialize(this);
LMB = RMB = MMB = false;
LMBtime = MMBtime = RMBtime = 0;

mouseListener = new Object();
mouseListener.onMouseWheel = Delegate.create(this, mousewheel);
Mouse.addListener(mouseListener);

startTesting();
}

public function startTesting() {
intervalID = setInterval(this, "enterframe", time);
}

public function stopTesting() {
clearInterval(intervalID);
}

// -- the rest are private methods, don't bother with them

private function enterframe() {
LMBsetter = Key.isDown(1);
RMBsetter = Key.isDown(2);
MMBsetter = Key.isDown(4);

// this commented bit of code doesn't seem to work all the time, must be because of ALT
// Keycodes: 18 = ALT, 37 = LEFT, 39 = RIGHT
// backbutton = ALT+LEFT, forwardbutton = ALT+RIGHT

//if (Key.isDown(18) && Key.isDown(37)) trace("backbutton");
//if (Key.isDown(18) && Key.isDown(39)) trace("forwardbutton");
}

// -- LMBsetter property setter, without getter (no need for it).
// This catches the true/false value, checks for double entries (!!!) and dispatches the
// correct event before assigning the value to the LMB boolean, thus saving a few lines.

function set LMBsetter(b:Boolean) {
if (b && !LMB) {
dispatchEvent({type:"LMBdown", target:"LMB", isDown:true});
} else if (!b && LMB) {
dispatchEvent({type:"LMBup", target:"LMB", isDown:false});

// test doubleclick
var diff = getTimer()-LMBtime;
if (diff < doubletime) dispatchEvent({type:"doubleclick", target:"LMB", value:diff});
LMBtime = getTimer();
}
LMB = b;
}

function set MMBsetter(b:Boolean) {
if (b && !MMB) {
dispatchEvent({type:"MMBdown", target:"MMB", isDown:true});
} else if (!b && MMB) {
dispatchEvent({type:"MMBup", target:"MMB", isDown:false});

// test doubleclick
var diff = getTimer()-MMBtime;
if (diff < doubletime) dispatchEvent({type:"doubleclick", target:"MMB", value:diff});
MMBtime = getTimer();
}
MMB = b;
}

function set RMBsetter(b:Boolean) {
if (b && !RMB) {
dispatchEvent({type:"RMBdown", target:"RMB", isDown:true});
} else if (!b && RMB) {
dispatchEvent({type:"RMBup", target:"RMB", isDown:false});

// test doubleclick
var diff = getTimer()-RMBtime;
if (diff < doubletime) dispatchEvent({type:"doubleclick", target:"RMB", value:diff});
RMBtime = getTimer();
}
RMB = b;
}

private function mousewheel(direction:Number) {
if (direction > 0) dispatchEvent({type:"scroll", target:"scrollUp", value:direction});
else dispatchEvent({type:"scroll", target:"scrollDown", value:direction});
}
}

Usage:
import MouseDetection;
import mx.utils.Delegate;

var MD = new MouseDetection();
MD.addEventListener("LMBdown", Delegate.create(this, write));
MD.addEventListener("LMBup", Delegate.create(this, write));
MD.addEventListener("RMBdown", Delegate.create(this, write));
MD.addEventListener("RMBup", Delegate.create(this, write));
MD.addEventListener("MMBdown", Delegate.create(this, write));
MD.addEventListener("MMBup", Delegate.create(this, write));
MD.addEventListener("scroll", Delegate.create(this, scroll));
MD.addEventListener("doubleclick", Delegate.create(this, doubleclick));

function write(evt:Object) {
trace(evt.target+" = "+evt.isDown);
}

function scroll(evt:Object) {
trace(evt.target+" = "+evt.value);
}

function doubleclick(evt:Object) {
trace(evt.target+" doubleclick: "+evt.value+" ms");
}

Cowerd
%Europe/Berlin %760 %2005, 18:15
hmm.. als je je linker muis lang ingedrukt houdt komt er NaN ms
en daarna komen dan wel 2 uitslagen ofzo, maar als je dat zou maken is het vetter:P

Dauntless
%Europe/Berlin %761 %2005, 18:16
Leuk gedaan :). Dit is iets voor in de nl.flashfocus package :D

En zoals ik ook op msn zei: Als je'm compleet wil maken moet je ook 'scroll down', 'scroll up' en 'double click' toevoegen :).

Roenes
%Europe/Berlin %767 %2005, 18:24
Ik moet zeggen: ik heb er helaas weer niets op aan te merken. Het werk lekker en is duidelijk geschreven. En weer een voorbeeld voor het gebruik van EventDispatcher :P Ik zie die altijd alleen bij jouw :P Moet ik toch eens mee gaan testen :)

Ergens wist ik trouwens wel dat je met die Key.isDown(1), 2 en 4 de muisbuttons kon opvangen. Heeft Narie volgens mij eens verteld. Ik was er alleen niet zelf opgekomen. Ik herkende het toen ik het zag :P Blijft wel vreemd dat muis buttons zijn af te vangen met de Key class ;)

Flasher
%Europe/Berlin %783 %2005, 18:47
Leuk, lijkt me idd iets voor het nl.flashfocus package.

Ik zag trouwens dat je in de enterframe method de vars LMBdown, MMBdown en RMBdown gebruikt, en in de rest van het script LMB, MMB en RMB. Klopt dat??

Hartstikke handige class, ik heb nooit geweten dat dat kon!

Roenes
%Europe/Berlin %840 %2005, 20:10
LMB, RMB, MMB zijn vars die bijhouden of de corresponderende knop is ingedrukt. Die LMBdown en dergelijke zijn de events die getriggerd worden bij de juiste knoppen. Dus beide dingen betekenen iets anders. Dus dat klopt wel :)

Flasher
%Europe/Berlin %884 %2005, 21:13
ooww nou snap ik het
die LMBdown verwijst naar die setter functie:D

Mediamonkey
%Europe/Berlin %913 %2005, 21:55
Jaaaaa.. er zijn wat dingetjes toegevoegd.
Ik heb nog even zitten keyfilteren op de back- & forwardbutton op de muis, maar aangezien die knoppen een combinatie afvuren van ALT+links of rechts en de ALT-toets nogal eens problemen geeft met het vuren van zijn event kan ik die muisknoppen helaas niet ondersteunen. Soms werkt het namelijk wel, maar meestal niet. Van de 10x klikken komen er misschien 2 door.. buh.

Oh en over dat gebruik van die setters, dat leek me even een grappige manier om de code niet in de enterframe-method te plaatsen, maar bij het veranderen van de boolean variabelen. Meer een "hack" om de code schoon te houden eigenlijk.. ik denk dat de echte programmeurs hier blind van raken :D

Mediamonkey
%Europe/Berlin %966 %2005, 23:11
En voor wie nu al met AS3 speelt, zoals Narie, hier een kleine aanpassing.
In plaats van het aanroepen van een event dmv een string wordt er gebruik gemaakt van een static public variable. Het voordeel is dat een spelfout wordt opgemerkt, wat niet het geval is met een string waarbij je maar moet hopen dat er geen fout in zit. Je komt er natuurlijk wel snel achter als je gedelegeerde functie niet aanslaat, maar het is nu ook "good practice" die je met AS3 wordt geacht aan te houden.

Oh en als je je afvraagt wat (num = 1 << 0) betekent: da's een snelle manier om een getal van decimaal naar binair om te zetten. 0 t/m 7 naar binair omzetten heeft als voordeel dat ze uniek zijn en bovendien erg bruikbaar in boolean condities (in dit voorbeeld niet voor gebruikt).

bv:
var netteKleren = 1 << 0;
var tandenGepoetst = 1 << 1;
etc..

if (netteKleren+tandenGepoetst+luchtjeOp-puistjes-ranzigeNagels) goDate();

/** MouseDetection.as
* @Author: Bart "Mediamonkey" Wttewaall
* @Date: 31-10-2005
* @Description:
* A class that tests throug an interval if the left, middle or right mousebutton is up or down.
* It returns an event that can be listened to through the EventDispatcher.
*
* Events LMBup, LMBdown, MMBup, MMBdown, RMBup, RMBdown all return buttonname and isDown boolean
* scroll returns direction as lines(number), doubleclick returns offsettime between clicks
*
*/

import mx.utils.Delegate;
import mx.events.EventDispatcher;

class MouseDetection {

// static event variables. call these as listenernames.
static public var LEFT_DOWN:Number = 1 << 0;
static public var LEFT_UP:Number = 1 << 1;
static public var RIGHT_DOWN:Number = 1 << 2;
static public var RIGHT_UP:Number = 1 << 3;
static public var MIDDLE_DOWN:Number = 1 << 4;
static public var MIDDLE_UP:Number = 1 << 5;
static public var DOUBLE:Number = 1 << 6;
static public var SCROLL:Number = 1 << 7;

private var intervalID:Number;
private var mouseListener:Object;

// Button states (true = down, false = up)
private var LMB:Boolean;
private var RMB:Boolean;
private var MMB:Boolean;

// stored time between two clicks
private var LMBtime:Number;
private var MMBtime:Number;
private var RMBtime:Number;

// after some tests, the fastests I could click was 30 ms
public var time:Number = 30;

// offset between two mouseclicks to count as a doubleclick
public var doubletime:Number = 250;

// mixin methods from the EventDispatcher
public var addEventListener:Function;
public var removeEventListener:Function;
public var dispatchEvent:Function;
public var dispatchQueue:Function;

function MouseDetection() {
EventDispatcher.initialize(this);
LMB = RMB = MMB = false;
LMBtime = MMBtime = RMBtime = 0;

mouseListener = new Object();
mouseListener.onMouseWheel = Delegate.create(this, mousewheel);
Mouse.addListener(mouseListener);

startTesting();
}

public function startTesting() {
intervalID = setInterval(this, "enterframe", time);
}

public function stopTesting() {
clearInterval(intervalID);
}

// -- the rest are private methods, don't bother with them

private function enterframe() {
LMBsetter = Key.isDown(1);
RMBsetter = Key.isDown(2);
MMBsetter = Key.isDown(4);

// this commented bit of code doesn't seem to work all the time, must be because of ALT
// Keycodes: 18 = ALT, 37 = LEFT, 39 = RIGHT
// backbutton = ALT+LEFT, forwardbutton = ALT+RIGHT

//if (Key.isDown(18) && Key.isDown(37)) trace("backbutton");
//if (Key.isDown(18) && Key.isDown(39)) trace("forwardbutton");
}

// -- LMB property setter, without getter (no need for it).
// This catches the true/false value, checks for double entries (!!!) and dispatches the
// correct event before assigning the value to the LMB boolean, thus saving a few lines.

function set LMBsetter(b:Boolean) {
if (b && !LMB) {
dispatchEvent({type:LEFT_DOWN, target:"LMB", isDown:true});
} else if (!b && LMB) {
dispatchEvent({type:LEFT_UP, target:"LMB", isDown:false});

// test doubleclick
var diff = getTimer()-LMBtime;
if (diff < doubletime) dispatchEvent({type:DOUBLE, target:"LMB", value:diff});
LMBtime = getTimer();
}
LMB = b;
}

function set MMBsetter(b:Boolean) {
if (b && !MMB) {
dispatchEvent({type:MIDDLE_DOWN, target:"MMB", isDown:true});
} else if (!b && MMB) {
dispatchEvent({type:MIDDLE_UP, target:"MMB", isDown:false});

// test doubleclick
var diff = getTimer()-MMBtime;
if (diff < doubletime) dispatchEvent({type:DOUBLE, target:"MMB", value:diff});
MMBtime = getTimer();
}
MMB = b;
}

function set RMBsetter(b:Boolean) {
if (b && !RMB) {
dispatchEvent({type:RIGHT_DOWN, target:"RMB", isDown:true});
} else if (!b && RMB) {
dispatchEvent({type:RIGHT_UP, target:"RMB", isDown:false});

// test doubleclick
var diff = getTimer()-RMBtime;
if (diff < doubletime) dispatchEvent({type:DOUBLE, target:"RMB", value:diff});
RMBtime = getTimer();
}
RMB = b;
}

private function mousewheel(direction:Number) {
if (direction > 0) dispatchEvent({type:SCROLL, target:"scrollUp", value:direction});
else dispatchEvent({type:SCROLL, target:"scrollDown", value:direction});
}
}import MouseDetection;
import mx.utils.Delegate;

var MD = new MouseDetection();
MD.addEventListener(MouseDetection.LEFT_DOWN, Delegate.create(this, write));
MD.addEventListener(MouseDetection.LEFT_UP, Delegate.create(this, write));
MD.addEventListener(MouseDetection.RIGHT_DOWN, Delegate.create(this, write));
MD.addEventListener(MouseDetection.RIGHT_UP, Delegate.create(this, write));
MD.addEventListener(MouseDetection.MIDDLE_DOWN, Delegate.create(this, write));
MD.addEventListener(MouseDetection.MIDDLE_UP, Delegate.create(this, write));
MD.addEventListener(MouseDetection.DOUBLE, Delegate.create(this, doubleclick));
MD.addEventListener(MouseDetection.SCROLL, Delegate.create(this, scroll));

function write(evt:Object) {
trace(evt.target+" = "+evt.isDown);
}

function doubleclick(evt:Object) {
trace(evt.target+" doubleclick: "+evt.value+" ms");
}

function scroll(evt:Object) {
trace(evt.target+" = "+evt.value);
}

Mediamonkey
%Europe/Berlin %580 %2005, 13:55
note to self: voeg mouseMove toe!
note to self 2 : LEFT_UP & LEFT_DOWN in 1 event: LEFT met als argument isDown:Boolean

Tha Narie
%Europe/Berlin %645 %2005, 15:29
note to yourself: en mouseDrag, en een geëvalde _droptarget :D

Mediamonkey
%Europe/Berlin %653 %2005, 15:40
Je wil 't wel weer allemaal he.. en nog gratis ook zeker?