Es ist schon manchmal merkwürdig was Skripte die Jahrelang anstandslos funktioniert haben für Anstalten machen^^
Frohen 3ten Advent (darf man erst Morgen lesen... hehehe)
Es ist schon manchmal merkwürdig was Skripte die Jahrelang anstandslos funktioniert haben für Anstalten machen^^
Frohen 3ten Advent (darf man erst Morgen lesen... hehehe)
Hab gerade herausfinden müssen: Wenn man negative Doubles als Key verwenden will (z.B. $map[-1.234] = 5) schmiert AutoIt nach ab
Wenn man einen negativen Integer nutzt gibts die übliche "Variable subscript badly formatted" Meldung, bei negativem Double rechnet er erst eine halbe Sekunde und crasht dann.
#include <Array.au3>
Test()
Func Test()
Local $M[], $_ = [22, 33], $o = ObjCreate('Scripting.Dictionary')
Local $a = [12345, 'abcd', 123.456, Binary('abc'), DllStructCreate('int'), Ptr(23905872), $_, True, False, $o, Default, '0', '1']
For $i = 0 To UBound($a) - 1 Step 1
$M[$a[$i]] = $M[$a[$i]] & '[' & $a[$i] & '](' & VarGetType($a[$i]) & '), '
Next
_MapDisplay($M)
EndFunc
Func _MapDisplay(ByRef $M)
Local $aKeys = MapKeys($M), $xCurrent, $aRet[UBound($aKeys)][2]
For $i = 0 To UBound($aKeys) - 1 Step 1
$aRet[$i][0] = $aKeys[$i] & ' (' & VarGetType($aKeys[$i]) & ')'
$aRet[$i][1] = $M[$aKeys[$i]]
Next
_ArrayDisplay($aRet)
EndFunc
Alles anzeigen
Ich bin nicht sicher, ob ich die Aufgabenstellung verstanden habe. Hier ist meine Interpretation dazu: AutoIt bildet "alles" einfach auf Int64 ab, außer es ist ein String.
Also nach dem Motto:
Func Int64S($x)
Return IsString($x) ? $x : Int($x, 2)
EndFunc
;$Map[$something] ist äquivalent zu $Map[Int64S($something)]
lg
M
Moin,
seit es in AutoIt nun offiziell den Map-Datentyp gibt dachte ich mir: Warum keine UDF für doubly linked lists basteln?
Idee dahinter: Es gibt keinen nativen List-Datentyp, und (mangels Pointer und dynamischer Allokation) auch keine Möglichkeit selbst eine Liste aufzubauen (ja, es geht mit Structs die man vor dem automatischen Löschen schützt, aber die können keine "AutoIt-Variablen" speichern). Daher: Nutze eine Map als "dynamischen Speicher" und packe die [value, previousPointer, nextPointer] tripel einfach in eine Map.
Geschwindigkeitstests habe ich nur bei relevanten Funktionen gemacht:
Datengröße: 0 - 1000
- List schneller (ca. 2x - 10x) _ArrayAdd vs _ListPushBack, _ArrayPop vs _ListPopBack, (und ähnliche Funktionen) (Iteratorversion)
- List schneller (ca. 1x - 100x) _ArrayDelete vs _ListRemove, _ArrayInsert vs _ListInsert (Iteratorversion)
- Array schneller (100x+) Direktzugriff vs _ListRead & _ListWrite (Indexversion)
- Array schneller (ca. 2x) Direktzugriff vs (inlined) _ListRead & _ListWrite (Iteratorversion)
Von vielen List-Funktionen gibt es eine Indexversion und eine Iteratorversion. Die Indexversion hat immer Komplexität O(n/2). Die Iteratorversion ist (näherungsweise) O(1) und damit meistens halbwegs flott. Das allerschlimmste was man tun kann ist der Direktzugriff via Index (das ist 100x+ langsamer). Insgesamt kam das heraus was wir alle vorher schon wussten: Das was Lists in O(1) und Arrays in O(n) machen ist ab einer Größe von ca. 5 Elementen schon schneller, alles andere ist langsamer.
Manche List-typische Funktionalität fehlt leider:
- Es gibt keine Mergefunktion (hier würden Listen auch extrem gut abschneiden, da man nur ein paar Pointer ändert und nichts kopiert)
- Es gibt keine Splitfunktion (s.o.)
Grund dafür ist: Es ist (zumindest mit dem jetzigen Aufbau der UDF) nicht möglich von einer Liste auf eine andere zu verlinken. Ein "ListPool" der komplett innerhalb einer Map liegt könnte das Problem lösen, würde aber auch einiges an Overhead mitbringen.
- Insgesamt ist der Umfang/Errorhandling/etc. nicht mit Array.au3 vergleichbar, sondern nur ein sehr kleines Subset was mit der Zeit vielleicht noch etwas wächst
Wie immer gilt: Viel Spaß mit der UDF und meldet Fehler/Wünsche. Falls ich Zeit und Lust habe kümmere ich mich darum.
lg
Mars
Ich spiele das immer solo, bin eigentlich eher jemand der sich freut wenn Zahlen größer werden. Daher versuche ich viele Felder auf dem Planeten einzunehmen und viele Ressourcen zu sammeln^^
Mich hat das Spiel damals auch ziemlich überrumpelt. Hab sowas immer gesucht, und auf ein Mal war es da
Dokumentation (Work in Progress). Die soll mal in "Post 2" verschoben/kopiert werden (muss ein Mod machen)
Dokumentation (version 0.2)
Logik Komponenten:
Name (Englisch) | Beschreibung | Spezifikationen | |
Message Größe 1x1 | Zur Anzeige von Text. | Anzahl Zeichen: 400 | |
Switch Größe: 1x1 | Kann vom Spieler ein- und ausgeschaltet werden. | ||
Micro Processor Größe: 1x1 | Führt Instruktionen aus. | Instruktionen pro Sekunde: 120 (IPS/Größe: 120) Programmspeicher: 1000 Instruktionen (Speicher/Größe: 1000) | |
Logic Processor Größe: 2x2 | Führt Instruktionen aus. | Instruktionen pro Sekunde: 480 (IPS/Größe: 120) Programmspeicher: 1000 Instruktionen (Speicher/Größe: 250) | |
Hyper Processor Größe: 3x3 | Führt Instruktionen aus. | Instruktionen pro Sekunde: 1500 (IPS/Größe: 167) Programmspeicher: 1000 Instruktionen (Speicher/Größe: 111) | |
Memory Cell Größe: 1x1 | Bietet Random Access Memory. | Speicherkapazität: 64 (Speicher/Größe: 64) | |
Memory Bank Größe: 2x2 | Bietet Random Access Memory. | Speicherkapazität: 512 (Speicher/Größe: 128) | |
Logic Display Größe: 3x3 | Zur Anzeige von Pixelgrafik. | Auflösung: 80x80 Pixel | |
Large Logic Display Größe: 6x6 | Zur Anzeige von Pixelgrafik. | Auflösung: 176x176 Pixel |
Random Info:
Instruktionen:
Funktionen (Wrapped) | Mindustry Logic | Beschreibung |
result = Read(cell, index) | read result cell index | Liest den Wert einer Memory Cell oder Memory Bank an einem gewissen Index aus |
result = Sensor(block, @material) | sensor result block @material | Liest einen Wert aus einem Block aus. Auslesbare Werte beginnen mit @. Beispiele @scrap, @slag, @totalAmmo |
result = GetLink(index) | getlink result index | Gibt ein Handle zum mit dem Prozessor verbundenen Objekt an einem bestimmten Index zurück |
Mathe (Wrapped) | Mindustry Logic | Beschreibung |
result = And(a, b) | op land result a b | Logisches und |
result = Or(a, b) | op or result a b | Logisches oder (hier: Äquivalent zu BitOr) |
result = BitOr(a, b) | op or result a b | Bitwise OR |
result = BitAnd(a, b) | op and result a b | Bitwise AND |
result = BitXor(a, b) | op xor result a b | Bitwise XOR |
result = BitNot(a) | op not result a 0 | Bitwise NOT |
result = BitShl(a, b) | op shl result a b | BitShift, shifts "a" by "b" bits to the left |
result = BitShr(a, b) | op shr result a b | BitShift, shifts "a" by "b" bits to the right |
result = Sin(a) | op sin result a 0 | Sinus im Gradmaß |
result = Cos(a) | op cos result a 0 | Cosinus im Gradmaß |
result = Tan(a) | op tan result a 0 | Tangens im Gradmaß |
result = ASin(a) | op asin result a 0 | Arkussinus im Gradmaß |
result = ACos(a) | op acos result a 0 | Arkuscosinus im Gradmaß |
result = ATan(a) | op atan result a 0 | Arkustangens im Gradmaß |
result = ATan2(a, b) | op angle result a b | Arkustangens2 im Gradmaß (TODO: Check wo hier x und y ist, da ATan2(y, x) üblich ist) |
result = Max(a, b) | op max result a b | Gibt den größeren der beiden Werte zurück |
result = Min(a, b) | op min result a b | Gibt den kleineren der beiden Werte zurück |
result = Angle(a, b) | op angle result a b | Alias für ATan2 |
result = Len(a, b) | op len result a b | Gibt die Länge des Vektors [a, b] zurück. (result = Sqrt(a^2 + b^2)) |
result = Noise(a, b) | op noise result a b | Gibt den Werte eines 2D Noisegenerator mit 2 Koordinaten zurück |
result = Abs(a) | op abs result a 0 | Gibt den Absolutwert von a zurück |
result = Log(a) | op log result a 0 | Gibt den Logarithmus zur Basis e von a zurück |
result = Log10(a) | op log10 result a 0 | Gibt den Logarithmus zur Basis 10 von a zurück |
result = Floor(a) | op floor result a 0 | Rundet a ab |
result = Ceiling(a) | op ceil result a 0 | Rundet a auf |
result = Sqrt(a) | op sqrt result a 0 | Quadratwurzel aus a |
result = Random(a) | op rand result a 0 | Gibt eine float Zufallszahl zwischen 0 und a zurück. |
Calls (Wrapped) | Mindustry Logic | Beschreibung |
Lightcolor(block, col) | control color block col 0 0 0 | Setzt die Farbe eines Illuminierers |
Print(string) | print string | Schreibt eine Zeichenkette in den Printbuffer |
PrintFlush(message) | printflush message | Schreibt den Printbuffer in den Messageblock |
Write(value, cell, index) | write value block index | Schreibt den Wert value in die Speicherzelle an Position index. Gegenstück zu Read(). |
Sleep(seconds) | sleep seconds | Unterbricht das Programm für eine vorgegebene Zeit in Sekunden (nicht Millisekunden) |
Enabled(block, flag) | control enabled block flag 0 0 0 | Aktiviert (flag = 1) oder deaktiviert (flag = 0) einen Block |
DrawCalls (Wrapped) | Mindustry Logic | Beschreibung |
Stroke(width) | draw stroke width 0 0 0 0 0 | Setzt die Stiftbreite für Zeichnungen |
Color(R, G, B, A) | draw color R G B A 0 0 | Setzt die Füllfarbe |
Color(%RRGGBBAA) | draw col %RRGGBBAA 0 0 0 0 0 | Setzt die Füllfarbe (anderes Format... warum ein Prozentzeichen... wessen Idee war das?!) |
Clear(R, G, B) | draw clear R G B 0 0 0 | Füllt den Drawbuffer vollkommen mit einer Farbe |
Image(x, y, @image, size, rotation) | draw image x y @image size rotation 0 | Zeichnet das Bild @image (z.B. @copper) an die vorgegebene Stelle |
DrawFlush(display) | drawflush display | Zeichnet den Drawbuffer auf das Display |
LinePoly(mX, mY, n, radius, angle) | draw linepoly mX mY n radius angle 0 | Zeichnet ein gleichseitiges Polygon mit Mittelpunkt [mX, mY], n Seiten, radius (in Pixeln) und Drehwinkel |
Triangle(x1, y1, x2, y2, x3, y3) | draw triangle x1 y1 x2 y2 x3 y3 | Füllt ein Dreieck |
LineRect(x, y, w, h) | draw linerect x y w h 0 0 | Zeichnet ein achsenausgerichtetes Rechteck |
Line(x1, y1, x2, y2) | draw line x1 y1 x2 y2 0 0 | Zeichnet eine Linie |
Rect(x, y, w, h) | draw rect x y w h 0 0 | Füllt ein achsenausgerichtetes Rechteck |
Poly(mX, mY, n, radius, angle) | draw poly mX mY n radius angle 0 | Füllt ein gleichseitiges Polygon mit Mittelpunkt [mX, mY], n Seiten, radius (in Pixeln) und Drehwinkel |
Kontrollfluss (Wrapped) | Mindustry Logic | Beschreibung |
Goto(int) | jump int always 0 0 | Springt zu einer Programmzeile (Zeile im Mindustry Logic code, NICHT im Code den man schreibt) |
Goto(var) | set @counter var | Springt zu einer variablen Programmzeile (Zeile im Mindustry Logic code, NICHT im Code den man schreibt) |
If (a operation b) Goto(int) | jump int operation a b | Contitional Goto |
Exit | stop | Hält den Prozessor permanent an (gleichbedeutend mit "Beendet das Programm") |
Reset | end | Startet den Prozessor neu |
Sonstige (Wrapped) | Mindustry Logic | Beschreibung |
ShootP(block, unit, shoot) | control shootp block unit shoot 0 0 | ...? |
Config(block, flag) | control config block flag 0 0 0 | ...? |
Shoot(block, x, y, shoot) | control shoot block x y shoot 0 | ...? |
var = a | set var a | A kann Variable, Zahl oder String sein |
var = a operation b | op operation var a b | A und B können Variable, Zahl oder String sein. Operationen: [+ - * / ^ % = < > == != <> <= >= //] Manche Operationen sind Äquivalent wie z.B. = und ==, sowie != und <>. |
var operation= a | op operation var var a | A kann Variable, Zahl oder String sein. Operationen [+= -= *= /= ^= %= //=] |
Radar(...) | - | Nicht implementiert (Stand v0.2) |
Unit(...) | - | Nicht implementiert (Stand v0.2) |
Additional Functionality (Stand v0.1) | Substitution | Beschreibung |
#myLabel | - | Ein Label zu dem man springen kann |
Goto(#myLabel) | jump #mylabel always 0 0 | #myLabel wird zu int resolved |
If (Expression) ... EndIf | - | Erlaubt einen Codeblock konditional auszuführen |
If (var) ... EndIf | If (var != 0) ... EndIf | Verpackung für alleinstehende Variablen |
If (!var) ... EndIf | If (var = 0) ... EndIf | Verpackung für alleinstehende Variablen |
For (i = 0, i < 9, i++) ... Next | - | Führt einen Codeblock mit Zählervariable mehrfach aus. Obacht, keine Semikolons, sondern Kommata. |
var++ | op add var var 1 | Inkrement (kein Returnwert, also var1 = var2++ geht nicht) |
var-- | op sub var var 1 | Dekrement (kein Returnwert, also var1 = var2-- geht nicht) |
Enable(block) | Enabled(block, 1) | Aktiviert einen Block |
Disable(block) | Enabled(block, 0) | Deaktiviert einen Block |
ContinueLoop | - | Springt wieder zum Anfang des aktuellen Loops (z.B. nächste Runde einer For-Schleife) |
ExitLoop | - | Springt aus dem aktuellen Loop heraus (z.B. eine For-Schleife) |
Additional Functionality (Stand v0.2) | Substitution | Beschreibung |
result = Chance(percentage) | result = Random(1) result = result < percentage | Gibt mit einer gewissen Wahrscheinlichkeit 0 oder 1 aus |
result = Random() | result = Random(1) | Gibt einen Zufallswert (float) zwischen 0 und 1 aus |
result = Random(min, max) | result = max - min result = Random(result) result += min | Gibt einen Zufallswert (float) zwischen min und max aus |
Loop ... EndLoop | - | Führt einen Codeblock ohne Zählervariable endlos aus. |
Sub mySub() ... EndSub | - | Versieht einen Codeblock mit den nötigen Labels um ihn später von außerhalb anspringen zu können Der Raum innerhalb der Subroutine ist NICHT abgekapselt, es gibt keine Lokalen Variablen, keine Parameter, keine Returnwerte und sonst auch nichts (Stand v0.2). |
mySub() | - | Springt zum Codeblock "mySub" und führt ihn aus. Springt automatisch wieder zurück |
myArray = Array(cell) | ... | Verwendet "myArray" als Alias für den Parameter "cell" in zukünftigen Array-Funktionen |
result = myArray[index] | result = Read(cell, index) | Erfordert vorheriges verwenden von "myArray = Array(cell)" |
myArray[index] = value | Write(value, cell, index) | Erfordert vorheriges verwenden von "myArray = Array(cell)" |
If (...) ... Else ... EndIf | - | Unterstützung von Else |
Ggf. hau ich dich demnächst mal an, auf Steam gibts das Spiel ja für kleines Geld!
Das Spiel ist kostenlos, wenn du es von der Entwicklerseite (https://github.com/Anuken/MindustryBuilds/releases) als .jar herunterlädst. Mindustry war (glaube ich) schon immer ein "Pay what you want" Spiel. Niemand "muss" bezahlen, aber jeder "kann" bezahlen so viel oder so wenig er möchte.
Wie ist der aktuelle Stand?
Ich weiß garnicht wie man das sinnvoll auflistet, da ich nicht Buch führe was ich mache^^
Sachen die "jetzt" (bei mir, die Version im Startpost kann das nicht) gehen und vorher nicht gingen:
- If () ... (ohne then)
- If Else Endif (das else ist neu)
- Random(min, max)
- Chance(number between 0 and 1) (gibt true/false aus)
- Loop ... Endloop (einfach ein "unendlicher" loop ohne counter)
- Bugfixes (Das sind die wichtigsten, vorallem die andauerenden crashes wenn man die syntax falsch hat... aber da gibts noch hunderte, also vllt ein autosave feature einbauen^^)
- Vermutlich noch 10 Sachen die ich einfach vergessen habe.
TODO (danach gibt es hier ein Update)
- Array Datenstruktur (braucht eine Speicherzelle)
- Stack Datenstruktur (braucht eine Speicherzelle)
- Diese dämliche Syntax ändern die ich mir vorher ausgedacht habe in der "//" Kommentare einleutet und "/%" die Integerdivision ist. Außerdem ist "^" aktuell xor. Das wird alles geändert, wir sind hier nicht bei C.
- Subroutines (ausnahmsweise mal etwas "anderes" als Funktionen)
- Syntax ist:
- Erstmal ohne alles (ohne Parameter etc.) Das Problem ist der Overhead bei "Funktionen". Im Prinzip muss man ja einen Parameterstack & Returnstack (die brauchen eine Speicherzelle im Spiel, laufen also nicht zu 100% auf dem Prozessor) etc. haben. Alles davon braucht ziemlich viele Takte, gibt aber auch ziemlich viele Möglichkeiten (z.B. Rekursion). Daher wird es die Trennung zwischen "Subroutine" = "Funktion die keine Rekursion kann und zu 100% auf dem Prozessor läuft" und "Funktion" = "Funktion die eine Speicherzelle braucht und alle üblichen Freiheiten beinhaltet" geben. Subroutines will ich auch so gestalten, dass man sie inlinen kann.
Auch wichtig ist eine vollständige Dokumentation... Ich habe viele Logic Compiler ausprobiert (ich bin ja nicht der erste der einen gebastelt hat) und bei keinem einzigen gibt es eine Dokumentation. Also kann außer dem Ersteller des Compilers niemand damit etwas anfangen...
Im alten Code ist das in script_parser.cpp::Parser_Keyword_DIM (1920) mit bReDim = True. In den Kommentaren steht aber, dass ReDim eine eigene Funktion bekommen soll, also wird das nach 10+ Jahren wohl nicht mehr genau so sein wie es dort ist^^
Und so wie ich das dort sehe wird hier ebenfalls ein neues Array angelegt und eine komplette Kopie gemacht (lustigerweise "von Hand", vermutlich weil kein Standardcontainer verwendet wird, also das "Array" ist zusammengesetzt aus einem ->Data Feld (pointerliste auf Variants, also **Variant) und irgendwelchen Extras um die Indices zuzuordnen)...
Edit2: realloc? Wahre Meister verwenden "new"
Edit: In den Code schaue ich immer rein, wenn ich mich gut fühlen will
Ich bin mir eigentlich auch fast sicher, dass das nicht immer so war, also dass verkleinernde ReDims früher mal deutlich schneller waren. Aber vielleicht bilde ich mir das auch nur ein...
Alles anzeigenDoppel-Jein
_Arraydelete führt lediglich ein verkleinerndes ReDim durch.
Hier ist kein umkopieren nötig, so dass dies unerheblich für die Performance ist.
Das eigentliche "Problem" an dem mehrmaligen ArrayDelete() ist dass er alle Elemente die nach dem zu löschenden kommen eine Position nach vorn verschiebt.
Und das halt jedesmal.
Daher werden diese Elemente zig mal umkopiert anstatt gleich an seine Endposition.
Ich bin nicht sicher, ob sich das AutoIt-Intern geändert hat, aber kürzlich habe ich jede Menge Timings aufgenommen, und ReDim war eines davon. Und es sieht so aus, als würde ReDim grundsätzlich "alles was danach noch vorhanden ist" kopieren, egal ob man die Größe gleich lässt, vergrößert, oder verkleinert.
; ReDim timing
#include <Array.au3>
$a = Test()
_ArrayDisplay($a)
Func Test()
Local $aFunctions = [_0, ReDim_Equal_Empty , ReDim_Larger_Empty, ReDim_Smaller_Empty , ReDim_Equal_Full, ReDim_Larger_Full, ReDim_Smaller_Full], $aTimings[UBound($aFunctions)][2], $r
For $i = 0 To 999 Step 1
$r = Random(0, UBound($aFunctions) - 1, 1)
$aTimings[$r][0] += $aFunctions[$r]()
$aTimings[$r][1] += 1
Next
Local $aResult[UBound($aFunctions) - 1][2], $Null = $aTimings[0][0] / $aTimings[0][1]
For $i = 1 To UBound($aFunctions) - 1 Step 1
$aResult[$i - 1][0] = FuncName($aFunctions[$i])
$aResult[$i - 1][1] = ($aTimings[$i][0] / $aTimings[$i][1]) - $Null
Next
Return $aResult
EndFunc
; Default
Func _0()
Local $t = TimerInit()
$t = TimerDiff($t)
Return $t
EndFunc
; ReDim ohne Größenänderung (1000 -> 1000), Array ist gefüllt
Func ReDim_Equal_Full()
Local $a = RandomArray(1000)
Local $t = TimerInit()
ReDim $a[1000]
$t = TimerDiff($t)
Return $t
EndFunc
; ReDim Verkleinerung um 1, Array ist gefüllt
Func ReDim_Smaller_Full()
Local $a = RandomArray(1000)
Local $t = TimerInit()
ReDim $a[999]
$t = TimerDiff($t)
Return $t
EndFunc
; ReDim Vergrößerung um 1, Array ist gefüllt
Func ReDim_Larger_Full()
Local $a = RandomArray(1000)
Local $t = TimerInit()
ReDim $a[1001]
$t = TimerDiff($t)
Return $t
EndFunc
; ReDim ohne Größenänderung (1000 -> 1000), Array ist leer
Func ReDim_Equal_Empty()
Local $a[1000]
Local $t = TimerInit()
ReDim $a[1000]
$t = TimerDiff($t)
Return $t
EndFunc
; ReDim Verkleinerung um 1, Array ist leer
Func ReDim_Smaller_Empty()
Local $a[1000]
Local $t = TimerInit()
ReDim $a[999]
$t = TimerDiff($t)
Return $t
EndFunc
; ReDim Vergrößerung um 1, Array ist leer
Func ReDim_Larger_Empty()
Local $a[1000]
Local $t = TimerInit()
ReDim $a[1001]
$t = TimerDiff($t)
Return $t
EndFunc
Func RandomArray($n)
Local $_[$n]
For $i = 0 To $n - 1 Step 1
$_[$i] = Random()
Next
Return $_
EndFunc
Alles anzeigen
Das Ergebnis müsste sein, dass alle 3 Versionen (verkleinern, vergrößern, gleichlassen) genausoviel Zeit beanspruchen, obwohl z.B. verkleinern um 1 element intern eigentlich nur "eine einzige integer substraktion" sein sollte und damit im Nanosekundenbereich liegen sollte, genauso wie ein ReDim ohne Größenänderung...
lg
M
Ich dachte, dieses Thema schon einmal hier im Forum angesprochen zu haben, du hattest übrigens dazu auch ein Script gepostet...
Das ist der Beweis dafür, dass ich jetzt auch "zu den alten" gehöre. Ich habe selbst absolut keinen Überblick mehr über das was ich die letzten 10+ Jahre hier gepostet habe^^
lg
M
Keyboardstate ist quasi "IsPressed" für alle Tasten gleichzeitig. Um das zu nutzen müsste man extrem oft Abfragen machen und quasi (hier 256) alle Keys andauernd abfragen um zu schauen ob etwas gedrückt wurde. Das ist für ein "paar wenige" Keys ggf eine gute Idee, aber wenn ich ca. 150 mögliche Keys überwachen muss, nur damit ab und zu einer davon gedrückt und erkannt wird, dann sind 99.9% der Abfragen nutzlos^^
Damit auch schnelle Tastendrücke erfasst werden wäre man außerdem gezwungen mit hoher Frequenz zu prüfen (da die Events hier ungepuffert sind).
Ich lass die Idee noch ein bisschen sacken, und dann wirds wahrscheinlich die Version von Oscar (Eigenes Window + Proc) mit sämtlichen Sachen die ich vergessen habe (wovon BugFix einige aufgezeigt hat). Wobei ich noch nicht ganz sicher bin, die WMs (ohne ein eigenes Window) sind auch verlockend, vermutlich weniger Komplexität in der Implementierung, dafür mehr Overhead. Allerdings ist laut der Hilfe WM_CHAR nicht immer nutzbar, wo das vermutlich am einfachsten wäre (auch in Bezug auf das Gedrückthalten von Tasten und Kombinationen von Tasten)...
Da fällt mir direkt eine Frage ein: GUIRegisterMsg hat ja keinen Parameter für hWND. Hat ein Layered-Window dort einen eigenen Zugang (GuiSwitch und dann GUIRegisterMsg? Ich kenne mich damit leider nicht aus...), sodass ich z.B. WM_CHAR im Layered-Window registrieren kann (weil ich ja 100% sicher sein kann, dass ich kein Edit-Ctrl habe, also müsste WM_CHAR verfügbar sein)?
Edit: Ich glaube ich bin da auf dem mentalen Holzweg. Kann es sein, dass die von RegisterMsg registrierten Funktionen mit den via SetWindowLong(..., WNDPROC, ...) registrierten Funktionen auf ein und dasselbe verweisen und diese Methode quasi die oben angesprochene GUIRegisterMsg Version "mit" HWND ist? Falls das so ist glaube ich es jetzt ansatzweise verstanden zu haben...
Mensch, ein simples einfaches Texteingabefeld ist viel zu kompliziert...
lg
M
Das sieht schon ziemlich gut aus. WindowProc und die benötigten Zusatzfunktionen (Pfeiltasten, etc) hatte ich komplett übersehen.
Moin,
Um einem XY Problem vorzubeugen gleich vorweg das Ziel: Ich möchte ein GDI+ Ctrl basteln welches ähnlich zu einem Input-Ctrl arbeitet. (Texteingabe möglich). Außerdem soll es intern "keine" AutoIt-Ctrls verwenden, also kein "verstecktes" Ctrl das ausgelesen wird. Da das ganze angezeigt wird ist ein GUI vorhanden, RegisterMsg etc. funktioniert, GUIOnEventMode ist an. Die Tasten sollen nur gecaptured werden, wenn das GUI auch aktiv ist. Gedrückthalten von Tasten soll sich genauso verhalten wie man es vom InputCtrl gewohnt ist (also zunächst 1 Zeichen, dann kurze Pause, dann am laufenden Band neue Zeichen). Zu allem überfluss soll das ganze im Leerlauf möglichst wenig Performance brauchen, es wäre unschön, wenn ein Programm alleine 50% der Rechenzeit dafür aufwendet um ein InputCtrl zu verwalten.
Anätze
> IsPressed/Keyboardstate:
- Sehr viel Code muss ausgeführt werden um anständig festzustellen was gedrückt wurde.
- Kombinationen müssen von Hand ausgewertet werden (z.B. Shift + Taste)
- Wird immer ausgeführt egal ob GUI aktiv ist, oder nicht.
- Das Verhalten von "gedrückt halten einer Taste" muss manuell nachgebildet werden.
> Hotkeys:
- Unmengen Hotkeys müssen registriert werden.
+ Kombinationen wie Shift + Taste oder ähnliches ohne weitere Logik abrufbar.
- Wird immer ausgeführt egal ob GUI aktiv ist, oder nicht.
+ Das Verhalten von "gedrückt halten einer Taste" wird unterstützt.
> GUISetAccelerators:
+ Alle "Hotkeys" sind schnell und einfach initialisierbar (2D Array)
+ Kombinationen werden unterstützt.
? Belastet den GUIMessageLoop/EventLoop (wir sind im GUIOnEventMode, also werden die Accelerators -> Msgloop/Eventloop weitergeleitet) Keine ahnung "wie belastend" das ist, wenn da 150 Registrierungen drin sind.
+ Funktioniert nur wenn GUI aktiv.
- Braucht "versteckte Ctrls" (DummyCtrl).
? Das Verhalten von "gedrückt halten einer Taste" wird unterstützt?
> GUIRegisterMsg + Keymessages:
+ (glaube ich) Kombinationen werden unterstützt.
+ Belastet soweit ich weiß den Message/Eventloop nicht.
? Messages werden nur verschickt, wenn GUI aktiv?
+ (glaube ich) Man hat Zugriff auf zusätzliche Infos, weil lParam und wParam mehr als nur den Key der gedrückt wurde beinhaltet?
? Das Verhalten von "gedrückt halten einer Taste" wird unterstützt?
Jetzt kommt die Frage: Was habe ich übersehen (gibt es noch weitere Wege?), und gibt es einen "richtigen" Weg bei der Sache?
Einige Punkte habe ich mit Fragezeichen markiert, weil ich nicht genau weiß wie sich die Funktionen verhalten und weil ich nicht alles ausprobieren will.
Hier braucht niemand irgendwelchen Code zu posten, alles was ich möchte ist (falls ihr etwas in der Art schonmal gemacht habt) eine Info wie man sowas richtig angeht.
Meine Tendenz ist GUIRegisterMsg zu verwenden, aber vllt hat ja jemand hier mehr Erfahrung und kann mir einen Tipp geben.
lg
M
Klärt uns unwissende bitte auf. Ich bin mir fast sicher, dass ich Au3toCmd nicht verwenden werde, aber wenn jemand anderes mal ein Problem ähnlicher Art hat, wird er (sofern er weiß wie man die Suche benutzt) früher oder später hier landen (Keywords wurden in diesem Thread ja zu genüge verwendet). Dann wäre es schön, wenn hier ein Abschluss für offene Fragen steht.
Was war das Problem?
Was war der Bedienfehler?
Wie vermeidet man den Bedienfehler?
Ich glaube oh-ha meint damit, dass du aus der .png ein .ico machen sollst. Eigentlich willst du ja aber die .png so wie sie ist in einem Ctrl verwenden.
Ich vermute, dass UEZ dafür eine Funktion hat (wahrscheinlich sogar 5 verschiedene für jeden nur erdenklicken Ctrltyp + GUIs). Habe auch etwas gegoogelt, aber nicht die richtige gefunden (nur eine die das komplette Fenster benutzt um eine .png anzuzeigen). Ich bin leider nicht fit mit Winapi & Sendmessage, sonst würde ich die 3 Zeilen selbst aufschreiben...
lg
M
Vorab: Vielleicht habe ich einfach nicht gesehen, ob diese Funktionalität vorhanden ist. Dann ist dieser Beitrag obsolet.
Vorallem wenn man mit z.B. Kugelkoordinaten, etc. arbeitet braucht man oft Intervalle (Range) mit inklusiven, oder exklusiven Rändern.
z.B. Das Intervall [0, 2PI] ist NICHT dasselbe wie (0, 2PI). Dafür verwende ich gerne folgende Funktion:
#include-once
If @ScriptName = 'Range.au3' Then __Range_Example()
Func Range($sLeft = '[', $fMin = 0, $fMax = 1, $sRight = ']', $iSteps = 10)
Local $a[$iSteps], $_[2], $l = $sLeft = '[' ? 0 : 1, $s = ($fMax - $fMin) / ($iSteps - 1 + ($sRight = ']' ? 0 : 1) + $l)
For $i = 0 To $iSteps - 1 Step 1
$_[0] = $i
$_[1] = ($i + $l) * $s + $fMin
$a[$i] = $_
Next
Return $a
EndFunc
Func __Range_Example()
Local $2PI = 6.28318530717959
ConsoleWrite(@CRLF & 'Intervall = (0, 2PI), Schritte = 9' & @CRLF)
For $i In Range('(', 0, $2PI, ')', 9)
ConsoleWrite($i[0] & ' ' & $i[1] & @CRLF)
Next
ConsoleWrite(@CRLF & 'Intervall = (0, 2PI], Schritte = 9' & @CRLF)
For $i In Range('(', 0, $2PI, ']', 9)
ConsoleWrite($i[0] & ' ' & $i[1] & @CRLF)
Next
ConsoleWrite(@CRLF & 'Intervall = [0, 2PI), Schritte = 9' & @CRLF)
For $i In Range('[', 0, $2PI, ')', 9)
ConsoleWrite($i[0] & ' ' & $i[1] & @CRLF)
Next
ConsoleWrite(@CRLF & 'Intervall = [0, 2PI], Schritte = 9' & @CRLF)
For $i In Range('[', 0, $2PI, ']', 9)
ConsoleWrite($i[0] & ' ' & $i[1] & @CRLF)
Next
EndFunc
Alles anzeigen
Das ermöglicht es einen kontinuierlichen Raum disket zu sampeln, während man gleichzeitig auswählen kann welchen Rand die Intervalle haben (Abgeschlossen, oder Offen).
(Gilt natürlich in dieser Beispielfunktion nur für lineare Räume, wenn man hier eine Funktion anwendet wie x^2, dann stimmt das Sampling nicht mehr, sobald mindestens ein Rand offen ist)
Das wäre ein Vorschlag für etwas das man hier ergänzen könnte (ich weiß aber nicht wie man das hier geschickt einbaut ohne unnötig viele Parameter zu haben, vielleicht wirklich via String parsing (also "(0, 1)" steht für das beidseitig offene Intervall, "[0, 1]" steht für das beidseitig abgeschlossene Intervall).
lg
M
Ich kann hier nur über meine persönliche Meinung dazu sprechen (mögen andere mich korrigieren).
Ich halte AdlibRegister für weitestgehend nutzlos. Eine (meiner Meinung nach) bessere Methode sind Funktionen mit Static Timer, da diese an einem Ort ausgeführt werden den "ich" festlege, während AdlibRegister Funktionen "überall" aufgerufen werden können und den Programmablauf damit durcheinanderbringen (außer man hat seine Programmstruktur extra darauf ausgelegt).
Beispiel:
#include <Misc.au3>
FunktionNummerEins(Null, 1000)
FunktionNummerZwei(Null, 2345)
While Sleep(10) And Not _IsPressed('1B') ; ESC = exit
FunktionNummerEins(1, 2, 3)
FunktionNummerZwei(1, 2, 3)
WEnd
Func FunktionNummerEins($x = 0, $y = 0, $z = 0)
Local Static $_t = TimerInit(), $_i = 100 ; $_t ist der static timer, $_i ist das Zeitintervall in Millisekunden
If $x = Null Then $_i = $y ; Hier muss man sich irgendeine Konvention ausdenken um das Intervall einstellen zu können, z.B, wenn Param1 = Null
If TimerDiff($_t) < $_i Then Return
$_t = TimerInit()
; Hier beginnt die "eigentliche" Funktion
ConsoleWrite('FunktionNummerEins: ['& $_i &'ms] Len(x,y,z) = ' & StringFormat('%.2f', Sqrt($x^2 + $y^2 + $z^2)) & @CRLF)
EndFunc
Func FunktionNummerZwei($x = 0, $y = 0, $z = 0)
Local Static $_t = TimerInit(), $_i = 100 ; $_t ist der static timer, $_i ist das Zeitintervall in Millisekunden
If $x = Null Then $_i = $y ; Hier muss man sich irgendeine Konvention ausdenken um das Intervall einstellen zu können
If TimerDiff($_t) < $_i Then Return
$_t = TimerInit()
; Hier beginnt die "eigentliche" Funktion
ConsoleWrite('FunktionNummerZwei: ['& $_i &'ms] x + y + z = ' & ($x + $y + $z) & @CRLF)
EndFunc
Alles anzeigen
Man kann diese Funktionalität auch Wrappen, sodass man z.B. eine Funktion hat die "SetTimer($xFunc, $ms)" hat, usw.
Wenn du nicht willst, dass der Nutzer davon etwas mitbekommt musst du dafür sorgen, dass die Abfragen einzeln relativ schnell durchlaufen, dann kannst du im Mainloop z.B. immer ein paar Millisekunden zur Verfügung stellen um Abfragen abzuarbeiten. Sind diese Millisekunden aufgebraucht werden die restlichen Abfragen erst im nächsten Schleifendurchlauf angegangen. Sowas lässt sich mittels Queue + Timer regeln (z.B. Alle 5 Sekunden sollen 100 Abfragen gemacht werden, dann werden alle 5 Sekunden die 100 Abfragen in den Queue gesteckt (das geht sehr schnell), und in jedem Schleifendurchlauf werden ?? Millisekunden lang Abfragen bearbeitet bis der Queue leer ist. Da muss man aber aufpassen wenn Abfragen aus welchem Grund auch immer nicht abgearbeitet werden können, dann wird der Queue immer länger und das wollen wir ja nicht).
lg
M