Guten Morgen Forum!
Ich möchte hier einen der Codes präsentieren, die ich in letzter Zeit so produziert habe: InputHandler.
Was sind InputHandler?
InputHandler sind eine Möglichkeit, Nutzereingaben zu verarbeiten. Und zwar Nutzereingaben jeder Art. Sie funktionieren mit den Funktionen aus BRL.PolledInput und - wenn ihr ähnliche Funktionen habt (für bspw. Geräte anderer Art) - auch mit anderem.
Wie nutze ich sie?
InputHandler werden genutzt, indem eine Instanz des Types TUserInputHandler (ein Beispiel gibt es weiter unten) über den Konstruktor erstellt wird. Dabei müssen einige Informationen übergeben werden (diese werden im Beispielcode erläutert).
Was für Vorteile bieten sie mir gegenüber den Standardfunktionen?
InputHandler bieten zwei Modi: Den Request Mode und den Event Mode. Der Request Mode ist der Modus, der aus dem Standardmodul (BRL.PolledInput) bekannt ist: Man fragt die Tastendrücke über bestimmte Funktionen ab. Zusätzlich zu den beiden bekannten Funktionen existiert noch die Möglichkeit, abzufragen, ob eine Taste losgelassen wurde.
Der Event Mode hingegen bietet alle Möglichkeiten, die man auch im Request Mode hat, und noch mehr: Man kann einem Tastendruck (oder dem Halten (für eine bestimmte Zeit)/Loslassen einer Taste) eine bestimmte Funktion zuweisen, die dann aufgerufen wird, wenn die Taste gedrückt/(eine bestimmte Zeit lang)gehalten/losgelassen wird. Dann gibt es noch die Listenerfunktion, welche für jeden von einer anderen Funktion unbearbeiteten (oder weitergereichten) Tastendruck aufgerufen wird. Als letztes gibt es noch SequenceHandler. Diese bieten die Möglichkeit, eine Funktion aufrufen zu lassen, wenn der Nutzer eine bestimmte Tastenfolge gedrückt hat (wer mal GTA gespielt hat: Das sieht dann so aus wie Cheats in den Spielen).
Wenn diese Features nicht für InputHandler sprechen, weiß ich auch nicht mehr weiter.
Und nun: Die Codes!
TUserInputHandler.bmx BlitzMax: [AUSKLAPPEN] [EINKLAPPEN] SuperStrict
Import BRL.Blitz
Const MODE_REQUEST:Byte=$0 Const MODE_EVENT:Byte=$1
Const TRIGGER_HIT:Int=0 Const TRIGGER_DOWN:Int=1 Const TRIGGER_RELEASE:Int=2 Const TRIGGER_HOLD:Int=32 Const TIME_S:Int=4 Const TIME_MS:Int=8 Const RESET_AFTER:Int=16
Type TUserInputHandler Const C_cHoldFlags:Int=4 Const C_iHoldFlags:Int=0 Const C_iHoldTime:Int=1 Const C_iHoldStarttime:Int=2 Const C_iHoldfActive:Int=3 Field _sMode:Byte Field _ListenerFnc:Int(Event:TInputEvent) Field _rgEventHandler:Int(Event:TInputEvent)[] Field _rgDownEventHandler:Int(Event:TInputEvent)[] Field _rgReleaseEventHandler:Int(Event:TInputEvent)[] Field _rgHoldHandler:Int(Event:TInputEvent)[] Field _rgHoldHandlerFlag:Int[,] Field _LstEvents:TInputEvent Field _LstSequences:TInputSequence Field _LstQueuedEvents:TInputEvent Field _QueuedLstTail:TInputEvent Field _cQueuedEvents:Int Field _cStates:Int Field _StateFnc:Int(i:Int) Field _rgState:Int[] Field _cModifiers:Int Field _ModifierFnc:Byte Ptr[] Field _rgcModifierIndex:Int[] Field _rgModifier:Int[][] Field _StateDownFnc:Int(i:Int) Field _rgStateDown:Int[] Field _rgLastStateDown:Int[] Method New() Self._sMode=MODE_REQUEST End Method
Function Create:TUserInputHandler(cStates:Int,cModifierIndex:Int[],StateFnc:Int(i:Int),StateDownFnc:Int(i:Int),ModifierFnc:Byte Ptr[]) Return New TUserInputHandler.Init(cStates,cModifierIndex,StateFnc,StateDownFnc,ModifierFnc) End Function Method Init:TUserInputHandler(cStates:Int,cModifierIndex:Int[],StateFnc:Int(i:Int),StateDownFnc:Int(i:Int),ModifierFnc:Byte Ptr[]) Self._cStates=cStates Self._StateFnc=StateFnc Self._StateDownFnc=StateDownFnc Self._rgState=New Int[cStates] Self._rgStateDown=New Int[cStates] Self._rgLastStateDown=New Int[cStates] Self._rgEventHandler=Self._rgEventHandler[..cStates] Self._rgDownEventHandler=Self._rgDownEventHandler[..cStates] Self._rgReleaseEventHandler=Self._rgReleaseEventHandler[..cStates] Self._rgHoldHandler=Self._rgHoldHandler[..cStates] Self._rgHoldHandlerFlag=New Int[cStates,C_cHoldFlags] If cModifierIndex<>Null Self._cModifiers=cModifierIndex.length Self._rgcModifierIndex=cModifierIndex Self._ModifierFnc=ModifierFnc Self._rgModifier=Self._rgModifier[..Self._cModifiers] For Local i:Int=0 Until Self._cModifiers Local c:Int=Max(1,Self._rgcModifierIndex[i]) Self._rgModifier[i]=New Int[c] Next Else Self._cModifiers=0 End If Return Self End Method
Method SetMode(sMode:Int) Self._sMode=sMode End Method
Method AddEventHandler(iState:Int,HandlerFnc:Int(Event:TInputEvent),Flag:Int=TRIGGER_HIT,Info:Int=0) If iState<Self._rgState.length If Flag=TRIGGER_HIT Self._rgEventHandler[iState]=HandlerFnc Else If Flag=TRIGGER_DOWN Self._rgDownEventHandler[iState]=HandlerFnc Else If Flag=TRIGGER_RELEASE Self._rgReleaseEventHandler[iState]=HandlerFnc Else If Flag&TRIGGER_HOLD<>0 Local Time:Int=Info*(((Flag&TIME_S)<>0)*1000)+Info*((Flag&TIME_MS)<>0) If Time<>0 Self._rgHoldHandler[iState]=HandlerFnc Self._rgHoldHandlerFlag[iState,C_iHoldTime]=Time Self._rgHoldHandlerFlag[iState,C_iHoldFlags]=Flag~TRIGGER_HOLD End If End If End If End If End Method
Method RemoveEventHandler(iState:Int,TriggerAction:Int=TRIGGER_HIT) If iState<Self._rgState.length Select TriggerAction Case TRIGGER_HIT Self._rgEventHandler[iState]=Null Case TRIGGER_DOWN Self._rgDownEventHandler[iState]=Null Case TRIGGER_RELEASE Self._rgReleaseEventHandler[iState]=Null Case TRIGGER_HOLD Self._rgHoldHandler[iState]=Null End Select End If End Method
Method SetListenerFunction(ListenerFnc:Int(Event:TInputEvent)) If Self._sMode=MODE_EVENT Self._ListenerFnc=ListenerFnc End If End Method
Method AddSequenceHandler(ID:Int,rgState:Int[],Fnc:Int()) Local Sequence:TInputSequence=TInputSequence.Create(ID,rgState,Fnc) Sequence._Succ=Self._LstSequences Self._LstSequences=Sequence End Method
Method RemoveSequenceHandler(ID:Int) Local Sequence:TInputSequence=Self._LstSequences Local LastSequence:TInputSequence While Sequence<>Null If Sequence._ID=ID If LastSequence=Null Self._LstSequences=Self._LstSequences._Succ Else LastSequence._Succ=Sequence._Succ End If Return End If LastSequence=Sequence Sequence=Sequence._Succ Wend End Method
Method IsPressed:Int(i:Int) Return Self._rgState[i] End Method
Method IsDown:Int(i:Int) Return Self._rgStateDown[i] End Method
Method IsReleased:Int(i:Int) Return (Self._rgLastStateDown[i]-Self._rgStateDown[i])=1 End Method
Method GetModifier:Int(i:Int,ii:Int=0) If i<Self._cModifiers If ii<Self._rgModifier[i].length Return Self._rgModifier[i][ii] End If End If End Method
Method Update() Local c:Int=Self._cModifiers For Local i:Int=0 Until c If Self._rgcModifierIndex[i]=0 Local Fnc:Int()=Self._ModifierFnc[i] Self._rgModifier[i][0]=Fnc() Else Local Fnc:Int(i:Int)=Self._ModifierFnc[0] For Local ii:Int=0 Until Self._rgcModifierIndex[i] Self._rgModifier[i][ii]=Fnc(ii) Next End If Next c=Self._cStates For Local i:Int=0 Until c Self._rgState[i]=Self._StateFnc(i) Self._rgLastStateDown[i]=Self._rgStateDown[i] Self._rgStateDown[i]=Self._StateDownFnc(i) If Self._sMode=MODE_EVENT Local Trigger:Int,fThrow:Int If Self.IsPressed(i)=True fThrow=True Trigger=TRIGGER_HIT Else If Self.IsDown(i)=True If Self._rgHoldHandlerFlag[i,C_iHoldfActive]=False Self._rgHoldHandlerFlag[i,C_iHoldStarttime]=MilliSecs() Self._rgHoldHandlerFlag[i,C_iHoldfActive]=True End If fThrow=True Trigger=TRIGGER_DOWN Else If Self.IsReleased(i)=True Self._rgHoldHandlerFlag[i,C_iHoldfActive]=False fThrow=True Trigger=TRIGGER_RELEASE End If If fThrow If Self._rgHoldHandlerFlag[i,C_iHoldfActive]=True If MilliSecs()>=(Self._rgHoldHandlerFlag[i,C_iHoldStarttime]+Self._rgHoldHandlerFlag[i,C_iHoldTime]) Trigger=TRIGGER_HOLD If (Self._rgHoldHandlerFlag[i,C_iHoldFlags]&RESET_AFTER)=RESET_AFTER Self._rgHoldHandlerFlag[i,C_iHoldStarttime]=MilliSecs() Else Self._rgHoldHandlerFlag[i,C_iHoldfActive]=-1 End If Else fThrow=False End If End If If fThrow Local rgModifier:Int[][] rgModifier=rgModifier[..Self._cModifiers] For Local ii:Int=0 Until Self._cModifiers Local c:Int=Self._rgcModifierIndex[ii] rgModifier[ii]=New Int[c] For Local iii:Int=0 Until c rgModifier[iii]=Self._rgModifier[iii] Next Next Local Event:TInputEvent=Self._LstEvents Self._LstEvents=TInputEvent.Create(i,rgModifier,Trigger) Self._LstEvents._Succ=Event End If End If End If Next If Self._sMode=MODE_EVENT While Self._LstEvents<>Null Local Event:TInputEvent=Self._LstEvents If Event.Trigger()=TRIGGER_HIT Local fQueue:Int=False Local Sequence:TInputSequence=Self._LstSequences While Sequence If Self._cQueuedEvents<Sequence._cStates If Sequence._rgState[Self._cQueuedEvents]=Event.State() fQueue=True Exit End If End If Sequence=Sequence._Succ Wend If fQueue=True Local fDeleteQueue:Int=False Self._cQueuedEvents:+1 If Self._LstQueuedEvents=Null Self._LstQueuedEvents=Event Else Self._QueuedLstTail._Succ=Event End If Self._QueuedLstTail=Event Local Sequence:TInputSequence=Self._LstSequences While Sequence If Self._cQueuedEvents=Sequence._cStates Local fMatch:Int=True Local Event2:TInputEvent=Self._LstQueuedEvents For Local i:Int=0 Until Self._cQueuedEvents If Sequence._rgState[i]<>Event2.State() fMatch=False Exit End If Event2=Event2._Succ Next If fMatch=True Sequence._Fnc() fDeleteQueue=True Exit End If End If Sequence=Sequence._Succ Wend If fDeleteQueue Self._cQueuedEvents=0 Self._QueuedLstTail=Null Self._LstQueuedEvents=Null End If Else Self._cQueuedEvents=0 Self._LstQueuedEvents=Null Self._QueuedLstTail=Null End If End If Local rg:Int(Event:TInputEvent)[] Select Event.Trigger() Case TRIGGER_HIT rg=Self._rgEventHandler Case TRIGGER_DOWN rg=Self._rgDownEventHandler Case TRIGGER_RELEASE rg=Self._rgReleaseEventHandler Case TRIGGER_HOLD rg=Self._rgHoldHandler End Select If rg[Event._State]<>Null rg[Event._State](Event) Else Event.Pass() End If If Event._fThrow=True And Self._ListenerFnc<>Null Self._ListenerFnc(Event) End If Self._LstEvents=Self._LstEvents._Succ Wend End If End Method End Type
Type TInputEvent Field _fThrow:Int Field _Succ:TInputEvent Field _State:Int Field _rgModifiers:Int[][] Field _Trigger:Int Method New() Self._fThrow=False End Method Function Create:TInputEvent(State:Int,rgModifiers:Int[][],Trigger:Int=TRIGGER_HIT) Local InputEvent:TInputEvent=New TInputEvent InputEvent._State=State InputEvent._rgModifiers=rgModifiers InputEvent._Trigger=Trigger Return InputEvent End Function
Method Pass() Self._fThrow=True End Method
Method State:Int() Return Self._State End Method
Method Modifier:Int(i:Int,ii:Int=0) If i<Self._rgModifiers.length If ii<Self._rgModifiers[i].length Return Self._rgModifiers[i][ii] End If End If End Method
Method Trigger:Int() Return Self._Trigger End Method End Type
Private
Type TInputSequence Field _rgState:Int[] Field _cStates:Int Field _ID:Int Field _Succ:TInputSequence Field _Fnc:Int() Function Create:TInputSequence(ID:Int,rgState:Int[],Fnc:Int()) Local Sequence:TInputSequence=New TInputSequence Sequence._ID=ID Sequence._rgState=rgState Sequence._cStates=rgState.length Sequence._Fnc=Fnc Return Sequence End Function End Type
Und ein Beispielcode, kommentiert, damit die ganze Sache verständlich wird. BlitzMax: [AUSKLAPPEN] [EINKLAPPEN] SuperStrict
Import "TUserInputHandler.bmx"
Graphics(800,600,0,0) Global Timer:TTimer=CreateTimer(60)
Global KeyboardInput:TUserInputHandler=TUserInputHandler.Create(255,Null,KeyHit,KeyDown,Null) KeyboardInput.SetMode(MODE_EVENT)
KeyboardInput.AddEventHandler(KEY_SPACE,SpaceKeyHit) KeyboardInput.AddEventHandler(KEY_LEFT,LeftKeyRelease,TRIGGER_RELEASE)
KeyboardInput.AddEventHandler(KEY_RIGHT,RightKeyDown,TRIGGER_DOWN) KeyboardInput.SetListenerFunction(KeyboardListener)
KeyboardInput.AddEventHandler(KEY_D,KeyDHold,TRIGGER_HOLD|TIME_S,2)
KeyboardInput.AddEventHandler(KEY_S,KeySHold,TRIGGER_HOLD|TIME_MS|RESET_AFTER,1500)
KeyboardInput.AddSequenceHandler(0,[KEY_C,KEY_H,KEY_E,KEY_A,KEY_T],TypedCHEAT)
KeyboardInput.AddSequenceHandler(1,[KEY_H,KEY_A,KEY_L,KEY_T],TypedHALT)
Global MouseInput:TUserInputHandler=TUserInputHandler.Create(4,[0,0],MouseHit,MouseDown,[Byte Ptr(MouseX),Byte Ptr(MouseY)])
Repeat DrawText("Maus bei: "+MouseInput.GetModifier(0)+","+MouseInput.GetModifier(1),10,10) If MouseInput.IsPressed(MOUSE_LEFT) DebugLog "Linke Maustaste gedrueckt." Else If MouseInput.IsDown(MOUSE_MIDDLE) DebugLog "Mittlere Maustaste gehalten." Else If MouseInput.IsReleased(MOUSE_RIGHT) DebugLog "Rechte Maustaste losgelassen." End If KeyboardInput.Update() MouseInput.Update() WaitTimer(Timer) Flip 0 Cls Forever End
Function TypedHALT() DebugLog "Tastenfolge H A L T wurde gedrueckt." End Function Function TypedCHEAT() DebugLog "Tastenfolge C H E A T wurde gedrueckt." End Function
Function KeyboardListener:Int(Event:TInputEvent) DebugLog "Taste gedrueckt (Code): "+Event.State()+", Trigger: "+Event.Trigger() If Event.State()=KEY_ESCAPE Then End End Function
Function SpaceKeyHit:Int(Event:TInputEvent) DebugLog "Space gedrueckt." Event.Pass() End Function Function LeftKeyRelease:Int(Event:TInputEvent) DebugLog "Linke Richtungstaste losgelassen." End Function Function RightKeyDown:Int(Event:TInputEvent) DebugLog "Rechte Richtungstaste gehalten." End Function Function KeyDHold:Int(Event:TInputEvent) DebugLog "Taste D fuer 2 Sekunden gehalten." End Function Function KeySHold:Int(Event:TInputEvent) DebugLog "Taste S fuer 1,5 Sekunden gehalten." End Function
Ich hoffe, diese Sache hilft jemandem. Über Feedback würde ich mich freuen.
EDIT (19.06.2011): Kleine Erweiterung, die es ermöglicht, Eventbehandelnde Funktionen nach einer bestimmten Zeit des Haltens einer Taste aufrufen zu lassen.
|