Kindern etwas spielerisch beibringen - Schritt für Schritt Anleitungen

  • Hallo,

    alle gut im Jahr 2017 angekommen?, dann auf in gutes 2017 :)

    Ich bin auf der Suche nach Anregungen, wie man Schritt für Schritt Anleitungen mit AutoIt auf eine Oberfläche bringen kann,
    die Kinder spielerisch mit der Maus in die richtige Reihenfolge bringen können.

    Kurz gefasst: Objekte (Button, Icons, Label, Bilder) per Drag / Drop in die richtige Reihenfolge verschieben + Auswertung der Reihenfolge.

    Beispiel

    1 Spielsachen wegräumen
    2 Händewaschen
    3 Teller holen
    4 an den Tisch setzen
    5 essen

    Diese Punkte sind auf einer Oberfläche durcheinandergewürfelt zu lesen oder als Bild dargestellt.
    (sowie zusätzlich zwei Begriffe, die nicht dazugehören)


    essen
    Traumlandreise
    an den Tisch setzen
    Händewaschen
    Spielsachen wegräumen
    Teller holen
    Hund ausführen


    Die "Begriffe" sind wie Spielkarten frei auf der Oberfläche ausgebreitet.


    Mit der Maus sollen die Elemente in die richtige Reihenfolge verschoben werden.


    Wenn die richtige Reihenfolge erreicht wurde, erscheint eine Meldung.

    Auf der Oberfläche gibt es einen "Papierkorb", so dass die Elemente, die nicht zum Thema gehören, vom Kind erkannt, mit der Maus in den
    „Papierkorb später“ geschoben werden können.
    (Aus obigen Beispiel wären das die Begriffe: „Traumlandreise“ und „Hund ausführen“.)

    Das ist eine erste Idee.

    Wie würdet ihr so eine Schritt für Schritt Anleitung für Kinder aufbauen?
    (inklusive Laden unterschiedlicher Themen, vielleich ini-Datei?)

    Gibt es hier im Forum bereits vergleichbare Beispiele, bei denen es darum geht, Elemente auf einer Oberfläche mit der Maus und Drag and Drop zu sortieren und die Reihenfolge zu prüfen?

    Danke :)

  • Hi,

    ich würde das ganze auf Basis von Child Windows realisieren. Dann nimmt dir Windows das Verschieben und Neuzeichnen direkt ab. Ich hab mal ein kleines (fehlerhaftes) Minimalbeispiel geschrieben. Bei mir gibt es da den ein oder anderen Darstellungsfehler, ich bin mir aber nicht sicher, was davon am Betriebssystem liegt (Debian 8 mit Wine). Daher, sieh es einfach mal als Proof-Of-Concept.

    [autoit]


    #include <StaticConstants.au3>
    #include <GUIConstants.au3>

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

    $hMainWnd = GUICreate("Child Window Sort", 600, 400)
    $cSlotA = GUICtrlCreateLabel("Example Slot", 250, 150, 100, 100, $SS_CENTER + $SS_CENTERIMAGE)
    GUICtrlSetBkColor(-1, 0x00ff00)
    GUISetBkColor(0xffffff)

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

    Global $ahChildWnd[3]
    For $i = 0 To UBound($ahChildWnd) - 1
    $ahChildWnd[$i] = GUICreate("", 100, 100, 0, 0, $WS_CHILD, Default, $hMainWnd)
    GUICtrlCreateLabel("Drag Me " & ($i + 1), 0, 0, 100, 100, $SS_CENTER + $SS_CENTERIMAGE, $GUI_WS_EX_PARENTDRAG)
    GUISetBkColor(0xff00ff)
    GUISetState()
    Next

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

    GUISetState(@SW_SHOW, $hMainWnd) ;parent window auf sichtbar stellen

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

    While True
    Switch GUIGetMsg()
    Case $GUI_EVENT_CLOSE
    Exit
    Case $GUI_EVENT_PRIMARYUP
    ;Linke Maustaste losgelassen
    CheckPositions()
    EndSwitch
    WEnd

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

    Func CheckPositions()
    ;Alle Child-Windows durchgehen und schauen,
    ;ob eines teilweise auf dem Slot liegt
    ;(Stichwort: Kollisionsabfrage)
    $aSlotPos = WinGetPos(GUICtrlGetHandle($cSlotA))
    $aMainPos = WinGetPos($hMainWnd)
    For $i = 0 To UBound($ahChildWnd) - 1
    $aChildPos = WinGetPos($ahChildWnd[$i])
    If IsRectCollision($aChildPos, $aSlotPos) Then
    ConsoleWrite("Kollision erkannt. Richte ChildWnd " & ($i + 1) & " neu aus..." & @CRLF)
    WinMove($ahChildWnd[$i], "", $aSlotPos[0] - $aMainPos[0], $aSlotPos[1] - $aMainPos[1])
    EndIf
    Next
    EndFunc

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

    Func IsRectCollision($aPosA, $aPosB)
    Local Enum $X, $Y, $W, $H
    If $aPosA[$X] > ($aPosB[$X] + $aPosB[$W]) Then Return False
    If ($aPosA[$X] + $aPosA[$W]) < $aPosB[$X] Then Return False
    If $aPosA[$Y] > ($aPosB[$Y] + $aPosB[$H]) Then Return False
    If ($aPosA[$Y] + $aPosA[$H]) < $aPosB[$Y] Then Return False
    Return True
    EndFunc

    [/autoit]
  • Danke @chesstiger

    Unter Windows 8 funktioniert das hier ohne Grafikfehler - das ist die gute Nachricht :)

    "Child Windows" in AutoIt sind Neuland für mich.

    (1)

    Wenn ich „ISN Autoit Studio“ nutze, müsste ich den Controls folgende Parameter mitgeben:
    $WS_CHILD
    $GUI_WS_EX_PARENTDRAG

    Habe ich das soweit richtig verstanden?
    Kann ich so jedes Control (Button, Image, Icon) zu einem "Child Window" machen?

    (2)

    Verschiebe ich ein violettes "Drag-Me-Feld" auf das grüne "Ziel", so erscheint das violette Feld hinter dem grünen Feld.

    Ich habe noch nicht die "Schraube" im Script finden können, an der ich "drehen" muss, um das "Drag-Me-Feld" über das "Ziel" zu legen - welche "Schraube" ist es?


    @all

    Drag an Drop scheint ein sehr interessantes Feld. Wer dazu weitere Script-Beispiele beitragen kann, die für obiges Thema geeignet sind, bitte hier einfügen.

  • Wenn ich „ISN Autoit Studio“ nutze, müsste ich den Controls folgende Parameter mitgeben:

    $WS_CHILD
    $GUI_WS_EX_PARENTDRAG

    Habe ich das soweit richtig verstanden?

    $WS_CHILD musst du als ChildWindow Style setzen, $GUI_WS_EX_PARENTDRAG beim Label.

    Kann ich so jedes Control (Button, Image, Icon) zu einem "Child Window" machen?

    Du erstellst kleine Fenster wo du die Controls platzierst, du machst die Controls selbst nicht zu einem Child Window!
    Du kannst nur Elemente verschieben die den Extended Style $GUI_WS_EX_PARENTDRAG unterstützten.

    Schau einfach mal im Form Designer welche Controls das sind.

  • Zu zwei: Man platziere die Slots ebenfalls auf Child-Windows und erstelle sie nach den verschiebbaren Feldern (im Skript Tiles). Dann passt das.

    PARENTDRAG können nur Label und Pic, soweit ich mich erinnere. Ich habe das ganze mal ein wenig ausgebaut, mit Positionsüberprüfung. Die richtige Reihenfolge ist 1, 2, 3. Dann wird der Hintergrund grün. :D

    [autoit]

    #include <StaticConstants.au3>
    #include <GUIConstants.au3>

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

    ;Main Window
    $hMainWnd = GUICreate("Child Window Sort", 600, 400)
    GUISetBkColor(0xffffff)

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

    ;Tile Windows
    Global $ahTileWnd[3]
    For $i = 0 To UBound($ahTileWnd) - 1
    $ahTileWnd[$i] = GUICreate("", 100, 100, 0, 0, $WS_CHILD, $WS_EX_TOPMOST, $hMainWnd)
    GUICtrlCreateLabel("Drag Me " & ($i + 1), 0, 0, 100, 100, $SS_CENTER + $SS_CENTERIMAGE, $GUI_WS_EX_PARENTDRAG)
    GUISetBkColor(0xff00ff)
    GUISetState()
    Next

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

    ;Slot Windows
    Global $ahSlotWnd[3]
    For $i = 0 To UBound($ahSlotWnd) - 1
    $ahSlotWnd[$i] = GUICreate("", 100, 100, 100 * ((1.5 * $i) + 1), 150, $WS_CHILD, Default, $hMainWnd)
    GUICtrlCreateLabel("Slot " & ($i + 1), 0, 0, 100, 100, $SS_CENTER + $SS_CENTERIMAGE)
    GUICtrlSetBkColor(-1, 0x0000ff)
    GUISetState()
    Next

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

    GUISetState(@SW_SHOW, $hMainWnd) ;parent window auf sichtbar stellen

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

    While True
    Switch GUIGetMsg()
    Case $GUI_EVENT_CLOSE
    Exit
    Case $GUI_EVENT_PRIMARYUP
    ;Linke Maustaste losgelassen
    $aTilePos = AlignTiles($ahTileWnd, $ahSlotWnd)
    CheckPositions($aTilePos)
    EndSwitch
    WEnd

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

    Func CheckPositions($aTilePos)
    ;Diese Funktion entscheidet,
    ;ob ein Tile auf dem richtigen Slot
    ;liegt (bzw. ob alle richtig liegen)

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

    ;Das folgende kann man auch in eine
    ;Schleife packen, so ist der Sinn
    ;aber deutlicher
    If ($aTilePos[0] = 0) And ($aTilePos[1] = 1) And ($aTilePos[2] = 2) Then
    GUISetBkColor(0x00ff00, $hMainWnd)
    Else
    GUISetBkColor(0xff0000, $hMainWnd)
    EndIf
    EndFunc

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

    Func AlignTiles($ahTileWnd, $ahSlotWnd)
    ;Alle Child-Windows durchgehen und schauen,
    ;ob eines teilweise auf dem Slot liegt
    ;(Stichwort: Kollisionsabfrage
    Local $aReturn[UBound($ahSlotWnd)]
    For $i = 0 To UBound($aReturn) - 1
    $aReturn[$i] = -1
    Next
    ;Außerdem gibt diese Funktion ein
    ;Array zur Bestimmung der Positionen
    ;zurück. Aufbau: $aReturn[n] = m
    ; n: Tile-Nummer
    ; m: Slot-Nummer
    $aMainPos = WinGetPos($hMainWnd)
    $aMainClientSize = WinGetClientSize($hMainWnd)
    $aMainPos[0] += $aMainPos[2] - $aMainClientSize[0] - 3
    $aMainPos[1] += $aMainPos[3] - $aMainClientSize[1] - 3
    For $iSlot = 0 To UBound($ahSlotWnd) - 1
    $aSlotPos = WinGetPos($ahSlotWnd[$iSlot])
    For $iTile = 0 To UBound($ahTileWnd) - 1
    $aTilePos = WinGetPos($ahTileWnd[$iTile])
    If IsRectCollision($aTilePos, $aSlotPos) Then
    ConsoleWrite("+DEBUG> Kollision erkannt. Richte ChildWnd " & ($iTile + 1) & " neu aus..." & @CRLF)
    WinMove($ahTileWnd[$iTile], "", $aSlotPos[0] - $aMainPos[0], $aSlotPos[1] - $aMainPos[1])
    $aReturn[$iTile] = $iSlot
    EndIf
    Next
    Next
    Return $aReturn
    EndFunc ;==>AlignTiles

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

    Func IsRectCollision($aPosA, $aPosB)
    Local Enum $X, $Y, $W, $H
    If $aPosA[$X] > ($aPosB[$X] + $aPosB[$W]) Then Return False
    If ($aPosA[$X] + $aPosA[$W]) < $aPosB[$X] Then Return False
    If $aPosA[$Y] > ($aPosB[$Y] + $aPosB[$H]) Then Return False
    If ($aPosA[$Y] + $aPosA[$H]) < $aPosB[$Y] Then Return False
    Return True
    EndFunc ;==>IsRectCollision

    [/autoit]

    P.S.: Das ist nicht violett, das ist pink!

    Edit: Hatte vergessen, das Return-Array von AlignTiles() vorher auf unplausible Werte zu setzen.

  • P.S.: Das ist nicht violett, das ist pink!

    Ich gebe zu, ich habe die Farbe von pink in violett geändert ... und das, obowhl ein pinker Tiger in der Nähe ist :)

    Deine neue Umsetzung sieht interessant aus. Ich zerlege das gerade, um es zu verstehen. Danke Euch beide!

  • Die richtige Reihenfolge ist 1, 2, 3. Dann wird der Hintergrund grün.


    Wirf bitte noch mal einen Blick drauf. Als Codeknacker funktioniert auch die Lösung
    leer - 2 - 3
    (also wenn Feld 1 frei bleibt, schaltet der Hintergrund auf grün um)

    (edit)

    ... schmunzel, Du hast es ist der Zwischenzeit schon selbst gemerkt. Du bist um einiges schneller als ich.
    Lösung 1 - 2 - 3

    Danke :)

    • Offizieller Beitrag

    Statt mit mehreren Fenstern, würde ich einfach nur die Control-Elemente verschieben. Das läßt sich IMHO einfacher handhaben:

    • Offizieller Beitrag

    Hier mal ein erster Versuch mit Text-Label:

  • Hier mal ein erster Versuch mit Text-Label:

    Vielen Dank Oscar. Dein Entwurf in Post 9 sieht optisch einfach (und) gut aus!

    Kannst Du bitte an zwei Stellen noch ewtas ergänzen (sind 2 kleine optische Sachen)?

    (a)
    Derzeit kann man pro Ziel-Feld mehrere Text-Labels raufsetzen.
    Was muss im Code ergänzt werden, dass Text-Labels im Ziel nicht übereinander gestapelt werden können? Bzw. wenn ein neues Text-Label reingezogen wird, dass ein bisheriges dort vorhandenes wieder auf die Oberfläche gelegt wird?

    (b)
    Wenn man ein Text-Label richtig in ein Ziel gezogen hat, wird auf grün umschaltet.
    Zieht man es wieder heraus, also wenn das Ziel wieder leer ist, bleibt der Status auf grün und schaltet nicht auf rot um.


    Übrigens - Deine Idee, eine Teillösung als richtig (grün) zu makieren, ist für Kinder eine gute Motivation, um schneller Erfolge zu erzielen.


    Als zusätzliche Motivation werde ich versuchen, einen Counter zu ingegrieren, wieviele Versuche notwendig waren, um alle Text-Labels richtig zu plazieren.
    Einmal für das Einzel-Feld rechts neben dem Ziel.
    Und einmal als Summe für alle Felder.

    Gute Nacht - und vielen Dank an alle helfenden Hände :)

    • Offizieller Beitrag

    Hier mal die korrigierte Version:

    Das ist jetzt aber ziemlich kompliziert geworden.
    Ich denke, dass man das auch einfacher hinbekommt, aber dazu muss man das ganze Konzept nochmal überdenken.
    Aber das sollte ja auch nur als Grundgerüst dienen, damit Du siehst, wie man die Control-Elemente verschieben/auswerten kann.

  • So, wie man mich mittlerweile kennt mag ich Flexibilität ;)
    Also hier das ganze flexibler:

    Es kann einfach die Funktion _loadTask mit den beiden Arrays aufgerufen werden und schon kanns losgehen.
    Die Felder und Slots werden automatisch ausgerichtet. Ist nicht genug Platz kommt eine Fehlermeldung :)
    Ebenfalls, wenn die Parameter nicht stimmen.
    Man kann ne Funktion angeben, die aufgerufen wird, wenn ein Item gesetzt wurde und eine Funktion, die prüft ob alles richtig ist :)

    Viel Spaß, Kanashius.

    PS: Weniger Komplex ist es dadurch nicht geworden. Eher im Gegenteil :P

    • Offizieller Beitrag

    Ich habe mein Script mal noch etwas erweitert (Kommentare, Spielende plus Auswertung, Neustart):

  • Ich danke Euch allen für die sehr gute Hilfe!

    Auf in ein erfolgreiches 2017 :)

    Am Wochenende schaue ich mir in Ruhe die Codezeilen an, um etwas daraus zu zaubern. Ich hoffe, meine kleinen Geister lassen mir die Zeit :)

    Grünen Haken gesetzt :)