tastaturzeichen als positionierung+klick

  • ich schreibe diesen script da racing rivals über fb nicht mehrspielbar ist und nur noch über mobilgeräte funktioniert.
    die lösung hierzu wäre ja bluestacks, jedoch ist es unmöglich mit der maus zwischen den jeweiligen buttons zu springen, das man ein saueres rennen fahren könnte.


    dies ist mein erstes projekt in der programierung und vermutlich sind in diesen quellcode zuviele fehler das er funktioniert,
    verurteilt mich bitte nich bin noch ein absoluter Noob auf dem gebiet.

    das sind die funktionen die das fertige programm können soll.

    Taste P: script pausieren
    Taste ESC: script ende
    pfeiltaste unten: gas geben (solange taste gehalten wird)
    pfeiltaste oben: Nos aktivieren
    pfeiltaste links: gang runtersschalten
    pfeiltaste rechts: gang hochschalten.

    derzeitiger Status im spoiler

    kordinaten sind derzeit nur volständigkeitshalber eingefügt, da ich die jeweiligen kordinaten noch ermitteln muss.
    für den abschluss des fertigen scripts fehlt mir dann auch noch die info wie ich daraus ne laufende .exe datei machen kann zum testen.

    Spoiler anzeigen


    HotKeySet("p", "TogglePause")
    HotKeySet("{ESC}", "Terminate")


    Func gas_geben()
    "down"
    MouseMove (100, 100)
    MouseClick
    EndFunc


    func Rennstart()
    "q"
    mousemove (200, 200)
    MouseClick
    EndFunc


    func Nos_aktivieren()
    "up"
    MouseMove (300, 300)
    MouseClick
    EndFunc


    func hochschalten()
    "right"
    MouseMove (400, 400)
    MouseClick
    EndFunc


    func runterchalten()
    "left"
    MouseMove (500, 500)
    MouseClick
    EndFunc


    Func TogglePause()
    $Paused = NOT $Paused
    While $Paused
    sleep(100)
    ToolTip('Script ist pausiert',0,0)


    WEnd
    ToolTip("")
    EndFunc


    Func Terminate()
    Exit 0
    EndFunc

    2 Mal editiert, zuletzt von MX3ver (11. April 2015 um 21:11)

    • Offizieller Beitrag
    Zitat von MX3ver


    für den abschluss des fertigen scripts fehlt mir dann auch noch die info wie ich daraus ne laufende .exe datei machen kann zum testen.

    In SciTE Ctrl+F7 oder im Menü > Tools > Compile

  • danke Raupi.

    irgendwie, ist mein quellcode inordung da ich exit code:0 bekomme das freut mich ja fürs erste
    aber er läuft nur maximal 0,03 sekunden irgendwas muss ich also noch falsch sein :(
    hab ma testlauf mit F5 gemacht

    einen fehler hab ich schon gefunden
    MouseClick
    zu
    MouseClick ("left")
    geändert

  • 1. Bitte in zukunft um deinen Quelltext ein [.autoit][./autoit] schreiben (ohne den Punkt), dann ist es schöner zu lesen ;)
    2. Du hast Funktionen definiert... Da sie nirgendwo aufgerufen werden sind sie im Prinzip nichts. Das Programm läuft einfach darüberhinweg. Damit dein Programm weiterläuft brauchst du eine Hauptschleife, die dafür sorgt, dass sich das Programm nicht beendet. Diese schreibt man normal vor den Beginn der Funktionen. Die sollte so aussehen:

    [autoit]


    while sleep(10)
    WEnd

    [/autoit]


    Das Sleep(10) wartet 10 ms, das dient nur dazu, die CPU-Last zu verringern.
    MouseMove und MouseClick, etc. gehört auch eher zum "unschönen" gebrauch. Damit die Mausdrücke nur an ein bestimmtes Fenster gehen wird normal ControlClick genutzt.
    Desweiteren ist MouseClick ebenfalls eine Funktion. Funktionen bestehen immer aus dem Namen und den beiden Klammern. Gegebenenfalls auch aus Parametern. Dein MouseClick sollte also nicht funktionieren.
    Einfach irgendwo "down" hinzuschreiben ist auch so nicht richtig. wenn du einen Kommentar schreiben willst solltest du das mit ; oder mehrzeilig zwischen #cs und #ce schreiben.

  • mit "left", "up", "down", "right" und "q" wollte ich eigentlich die jeweilige funktion per tastertur anwählen
    wodurch dann die jeweils zugeordnete position angeklickt wird.

    Spoiler anzeigen
    [autoit]


    while sleep(10)
    WEnd

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    HotKeySet("p", "TogglePause")
    HotKeySet("{ESC}", "Terminate")

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func gas_geben()
    "down"
    ControlClick (button = "left"[100, 100])
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    func Rennstart()
    "q"
    ControlClick (button = "left"[200, 100])
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    func Nos_aktivieren()
    "up"
    ControlClick (button = "left"[300, 100])
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    func hochschalten()
    "right"
    ControlClick (button = "left"[400, 100])
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    func runterschalten()
    "left"
    ControlClick (button = "left"[500, 100])
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func TogglePause()
    $Paused = NOT $Paused
    While $Paused
    sleep(100)
    ToolTip('Script ist pausiert',0,0)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    WEnd
    ToolTip("")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func Terminate()
    Exit 0
    EndFunc

    [/autoit]

    spoiler autoit funktion check

    nun muss ich nur noch die jeweiligen funktionen anwählen.
    mal gleich wieder in die F1 verschwinde

    3 Mal editiert, zuletzt von MX3ver (11. April 2015 um 23:45)

    • Offizieller Beitrag

    Keine Ahnung warum, aber deine Quellcodeformatierung ist im Eimer, keine LineFeeds.
    Was aber sofort auffält ist, das du die While WEnd Schleife am Scriptanfang hast, somit wird der darauffolgende Code niemals ausgeführt.

  • hatte was editiert danach war der line feed im eimer. nach erneuten copy paste war der line feed wieder da.
    vermutlich war das gerade in der zwischenzeit als du den spoiler geöffnet hattest

    Spoiler anzeigen
    [autoit]


    Global $Paused, $vab, $countdown = 0
    HotKeySet("p", "TogglePause")
    HotKeySet("{ESC}", "Terminate")
    HotKeySet("down", "gas_geben")
    HotKeySet("up", "Nos")
    HotKeySet("left", "runterschalten")
    HotKeySet("right", "hochschalten")
    HotKeySet("q", "Rennstart")

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func gas_geben()
    ControlClick (button = "left"[100, 100])
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    func Rennstart()
    ControlClick (button = "left"[200, 100])
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    func Nos()
    ControlClick (button = "left"[300, 100])
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    func hochschalten()
    ControlClick (button = "left"[400, 100])
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    func runterschalten()
    ControlClick (button = "left"[500, 100])
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func TogglePause()
    $Paused = NOT $Paused
    While $Paused
    sleep(100)
    ToolTip('Script ist pausiert',0,0)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    WEnd
    ToolTip("")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func Terminate()
    Exit 0
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    while sleep(1000)
    WEnd

    [/autoit]

    im probelauf funktioniert nun schon mal die beendenfunktion
    und die Pausenfunktion :)
    den controlclick muss ich dann morgen mal genauer ansehen damit ich die jeweils richtigen positonen anwählen kann.

    für heute ist dann mal feierabend.
    werde dann morgen weitermachen.

    2 Mal editiert, zuletzt von MX3ver (12. April 2015 um 00:49)

    • Offizieller Beitrag

    Die Func TogglePause brauchst du nicht. Die ist absolut unnötig für dein Script.

  • die pausen funktion ist beabsichtigt, da ich die tastatur auch für den chat nutze ;)

    hab contolklick mal wieder rausgenommen und staddessen mousemove und mouseklickgenommen
    und das funktiniert soweit wenn ich den hotkey ändere
    jedoch würde ich die pfeiltasten so wie ich es gewohnt war gerne wiederverwenden für die jeweiligen funktionen
    bzw mit welcher eingabe kann ich mit die pfeiltasten als hotkey anwählen.

    teilauszug aus script

    [autoit]

    HotKeySet("up", "Nos")

    [/autoit][autoit][/autoit][autoit]

    Func Nos()
    MouseMove (300, 100)
    MouseClick ("left")
    EndFunc

    [/autoit]

    das funktionierst so nicht gedoch mit

    [autoit]

    HotKeySet("i", "Nos")

    [/autoit]

    funktioniert es


    ganzer script:

    Spoiler anzeigen
    [autoit]


    Global $Paused, $vab, $countdown = 0
    HotKeySet("ü", "TogglePause")
    HotKeySet("{ESC}", "Terminate")
    HotKeySet("k", "gas_geben")
    HotKeySet("i", "Nos")
    HotKeySet("j", "runterschalten")
    HotKeySet("l", "hochschalten")
    HotKeySet("q", "Rennstart")

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func gas_geben()
    MouseMove (1400,450, 0 )
    MouseClick ("left")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    func Rennstart()
    MouseMove (180,510, 0 )
    MouseClick ("left")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    func Nos()
    MouseMove (1400,180, 0 )
    MouseClick ("left")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    func hochschalten()
    MouseMove (1360,700, 0 )
    MouseClick ("left")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    func runterschalten()
    MouseMove (240,700, 0 )
    MouseClick ("left")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func TogglePause()
    $Paused = NOT $Paused
    While $Paused
    sleep(100)
    ToolTip('Script ist pausiert',0,0)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    WEnd
    ToolTip("")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func Terminate()
    Exit 0
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    while sleep(1000)
    WEnd

    [/autoit]

    was ich aber noch ändern muss ist die funktion gas geben da der mausklick gehalten werden muss um diese funktion sauber ausführen zu können.
    position wird angefahren und angeklickt wie gewünscht nur muss die klickfunktion gehalten bleiben solange der hotkey betätigt ist.

    achja mousemove und mouseklick ist derzeit zu testzwecken im script und das wird dann später noch verschönert.

    6 Mal editiert, zuletzt von MX3ver (12. April 2015 um 13:26)

    • Offizieller Beitrag

    Schau mal in der Hilfe von Send, da kannst du entnehmen wie die UP,DOWN,LEFT und RIGHT Taste definiert sind.
    Die Bezeichnug der Tasten gehört in { }

    [autoit]

    HotKeySet("{UP}", "Nos")

    [/autoit]
  • hmm mousemove rausgenommen und kordinaten bei mouseclick hinzugefügt
    von

    [autoit]

    func Rennstart()
    MouseMove (180,510, 0 )
    MouseClick ("left")
    EndFunc

    [/autoit]

    geändert zu

    [autoit]

    func Rennstart()
    MouseClick ("left"[180,510])
    EndFunc

    [/autoit]

    fehlermeldung:
    ==> Subscript used on non-accessible variable.:
    MouseClick ("left"[180,510])
    MouseClick ("left"^ ERROR

    mit der Ausführung im spoiler klappt es soweit ganz gut

    Spoiler anzeigen
    [autoit]


    Global $Paused, $vab, $countdown = 0
    HotKeySet("ü", "TogglePause")
    HotKeySet("{ESC}", "Terminate")
    HotKeySet("{DOWN}", "gas_geben")
    HotKeySet("{UP}", "Nos")
    HotKeySet("{LEFT}", "runterschalten")
    HotKeySet("{RIGHT}", "hochschalten")
    HotKeySet("q", "Rennstart")

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func gas_geben()
    MouseMove (1400,450, 0 )
    mousedown ("left")
    Sleep (13)
    mouseup ("left")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    func Rennstart()
    MouseMove (180,510, 0 )
    MouseClick ("left")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    func Nos()
    MouseMove (1400,180, 0 )
    MouseClick ("left")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    func hochschalten()
    MouseMove (1360,700, 0 )
    MouseClick ("left")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    func runterschalten()
    MouseMove (240,700, 0 )
    MouseClick ("left")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func TogglePause()
    $Paused = NOT $Paused
    While $Paused
    sleep(100)
    ToolTip('Script ist pausiert',0,0)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    WEnd
    ToolTip("")
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func Terminate()
    Exit 0
    EndFunc

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    while sleep(1000)
    WEnd

    [/autoit]

    einzig und allein an einen problem hab ich noch zu knabbern.
    das gas geben funktioniert nicht flüssig.
    verschiedene Sleepwerte wurden versucht unter 13ms funktionierte es kaum bis garnicht
    mit höheren werten wie zb 25ms ist es sehr schwer handzuhaben.
    beim start hatte ich über facebook in der regel reaktionszeiten zwischen 0,001 und 0,150sec
    nun liege bei jedemstart mit dem script bei etwa 0,3-0,4 sec
    möglicher performancefehler im script oder es liegt beim bluestacks

    Einmal editiert, zuletzt von MX3ver (12. April 2015 um 23:44)

  • mit

    [autoit]


    HotKeySet("{DOWN}", "gas_geben")

    [/autoit][autoit][/autoit][autoit]

    Func gas_geben()
    MouseMove (1400,450, 0 )
    mousedown ("left")
    Sleep (13)
    mouseup ("left")
    EndFunc

    [/autoit]

    lief es ja nicht flüssig

    mit _ispressed habe ich nun die verschiedensten sachen ausprobiert
    nur kam ich da auf schlechtere resultate
    vermutlich habe ich da irgendwie einen fehler in meiner logik.

    [autoit]


    #include <Misc.au3>

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Local $hDLL = DllOpen("user32.dll")

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    While 1
    If _IsPressed("51", $hDLL) Then
    ; Wait until key is released.
    MouseMove (1400,450, 0 )
    Mousedown ("left")
    Sleep (100);veränderung des parmaters zeitge keine auswirkung
    While _IsPressed("51", $hDLL)
    MouseUp ("left")
    Sleep(100)
    WEnd
    ConsoleWrite("_IsPressed - Shift Key was released." & @CRLF)

    [/autoit][autoit][/autoit][autoit][/autoit][autoit][/autoit][autoit]

    ExitLoop
    EndIf
    Sleep(250)
    WEnd

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    DllClose($hDLL)

    [/autoit]

    ich denke mal ich mus hier für taste Q einen loop erstellen
    welche dann den mausklick auf meiner gewünschten position solange hält wie ich die q taste drücke
    bin hierbei auf mouseclickdelay gestosen wobei diese option ja lediglich sagt wieviele ms der mausklick gehalten wird,
    also müste ich auf mouseclickdelay eine vaiable legen wobei der dann ja vermutlich dann im ganzeen script dann diesen paramter benutzt

  • An sich richtig, nur ein kleiner Denkfehler. Wenn _isPressed losgelassen wird soll die maus losgelassen werden, nicht, solange gedrückt wird ;)
    So wäre es richtig:

    [autoit]

    ;...
    If _IsPressed("51", $hDLL) Then
    ; Wait until key is released.
    MouseMove (1400,450, 0 )
    Mousedown ("left")
    While _IsPressed("51", $hDLL)
    Sleep(10)
    WEnd
    MouseUp ("left")
    ConsoleWrite("_IsPressed - Shift Key was released." & @CRLF)
    ;...

    [/autoit]
  • ahh, desswegen konnte es also nicht funktionieren (irgendwie logisch).
    nun aber mal zur arbeit fahren und danach den script neuschreiben damit ich das gleich mal ein bischen festigen kann.

    wenn ich das dann habe. werde ich wieder in die hilfefunktion schaun obs da auch was gibt zum exe export (aut2exe)
    es ist ja auch machbar das man dort die Dateidetails hinzufügt.
    quasi Dateiversion, produktname,.....usw.

    ne deutsche hilfedatei gibts für autoit vermutlich nicht nehme ich mal an.
    hab bisher bei den sachen die mir im englishen nicht ganz klar waren immer wieder den google übersetzer zurhand genommen auch wenn die übersetztung zum teil dann mühselig zu lesen ist.

  • Auch, wenn das hier jetzt nicht sonderlich hilfreich ist, ich würde an deiner Stelle erstmal mit etwas simpleren beginnen.
    Beispielsweise einfach mit einem Taschenrechner ohne GUI. (von wegen Inputboxen usw.)
    Dann kommst du schneller an den Syntax ran, und sowas hier:

    Zitat von MX3ver
    [autoit]

    MouseClick ("left"[180,510])

    [/autoit]

    passiert dann nicht mehr so schnell.
    PS: Das richtige wäre:

    [autoit]


    ;Nach deiner MouseClick(..., ...) Interpretation:
    ;Local $aCoords[2] = [180, 510]
    ;MouseClick("left", $aCoords)

    [/autoit][autoit][/autoit][autoit]

    ;Richtige Lösung:
    MouseClick("left", 180, 510)

    [/autoit]

    Hoffe, ich konnte helfen :)

    Spoiler anzeigen

    Überraschung!


    MfG Donkey

  • nun läuft es flüssig

    ps.: den mousemove hab ich absichtlich auf ; gesetzt
    und die optionalen parameter beim Mouseclick sind auch gewollt.

    da ich das Script in unterschiedlichen ausführungen durchlaufen lassen möchte
    um zu sehen welche ausführung die beste Performence hat.
    auch wenn ich dafür ein anderes script schreiben muss.

    [autoit]


    MsgBox (48, "RacingRivals Hotkeytool V 0.9.2","beta enjoi it",5)
    #include <Misc.au3>
    Local $hDLL = DllOpen("user32.dll")

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Global $Paused, $vab, $countdown = 0
    HotKeySet("ü", "TogglePause")
    HotKeySet("{ESC}", "End")
    ;HotKeySet("k", "gas")
    HotKeySet("l", "gear_up")
    HotKeySet("j", "gear_down")
    HotKeySet("a", "start")
    HotKeySet("i", "Nos")

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func TogglePause()
    $Paused = NOT $Paused
    While $Paused
    sleep(10)
    ToolTip('Pause',0,0)
    WEnd
    ToolTip("")
    EndFunc

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func End()
    Exit 0
    EndFunc

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    While 1 ;gas
    If _IsPressed("4B", $hDLL) Then ; 4b=k
    MouseMove (1400,450, 0 )
    MouseDown ("left")
    While _IsPressed("4B", $hDLL)
    Sleep(10)
    WEnd
    MouseUp ("left")
    ElseIf _IsPressed("1B", $hDLL) Then
    ExitLoop
    EndIf
    Sleep(10)
    WEnd

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    func gear_up()
    ;MouseMove (1360,700, 0 )
    MouseClick ("left",1360,700,1,0)
    EndFunc

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    func gear_down()
    ;MouseMove (240,700, 0 )
    MouseClick ("left",240,700,1,0)
    EndFunc

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    func start()
    ;MouseMove (180,510, 0 )
    MouseClick ("left",180,510,1,0)
    EndFunc

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    func Nos()
    ;MouseMove (1400,180, 0 )
    MouseClick ("left",1400,180,1,0)
    EndFunc

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    while sleep (10)
    WEnd

    [/autoit]

    @donkey einen Taschenrechner schreiben klingt für mich schwerer als das da oben.
    wobei übung macht den meister.

    inpunkto mit was simpleren beginnen. ich habe ja eine umschulung zum Zerspanungstechniker (CNC) gemacht.
    als wir in der schule mit den ersten cnc-fräs-grundlagen begonnen haben.
    hatte ich zugleich begonnen mein erstes eigenes fräsprogramm zu schreiben.
    war zwar nur eine kleine gravur mit rund 400 zeilen gewesen aber als ich das programm meinen ausbildner nach nicht mal einer woche gezeigt habe.
    hat der nur mal komisch geschaut, und gefragt was das ist, da es im normalen windoes editor geschrieben war.
    nachdem er es in den simulator eingespielt hatte, konnte er es nicht glauben das ich es geschrieben hatte.
    ein foto und ein paar detailbilder später glaubte er es mir dann erst.

    selbst beim autodesk inventor projekt hab ich gleich mit was großen begonne. mein projekt bestand da auch gleich mal aus über 1500 teilen.
    okay bei dem habe ich aber fast die ganze freizeit reingesteckt

  • @MX3ver >> Schick, schick ^^
    Mit Taschenrechner meine ich keinen mit Fenster, sondern eher solch etwas:

    Spoiler anzeigen
    [autoit]


    $sRechnung = ""
    $sRechung &= InputBox("Nummer 1:")
    $sRechenzeichen = InputBox("Welches Rechenzeichen:"=
    If $sRechenzeichen = "plus" Then $sRechnung&="+"
    ...
    MsgBox(0,0,Execute($sRechnung))

    [/autoit]


    Solch simple Sachen bringen einem den Syntax mit If, While etc. bei :thumbup:

    Spoiler anzeigen

    Überraschung!


    MfG Donkey