Transparente Bilder und GDI+

  • Hey Leute :)

    Ich hab heute angefangen nen Spiel zu programmieren, hatte irgendwie mal wieder Lust darauf :D
    Leider hab ich da gleich am Anfang schon ein Problem..

    Also:
    Es gibt ein Männchen, das heißt Jack^^ Der sollte halt rumlaufen und hüpfen und so :) Funktioniert auch alles super - zumindest mit weißem Hintergrund. Wenn ich mir aber vorstelle dass da mal ein Hintergrund hin soll, muss ich den Hintergrund von Jack transparent machen --> Da tauchen dann die Probleme auf.
    - GuiCtrlCreatePic() unterstützt keine Transparenz..
    - Mit GDI+ kenn ich mich nicht soo gut aus. So wie ich es nun gemacht habe mit Löschen und neu Zeichnen funktioniert es zwar, aber es flackert ziemlich..

    Habe nur die Animation von Jack's Körper während er steht als Beispiel genommen:


    Erste Variante mit Gui-Funktionen (weißes Viereck rund um Jack)

    Spoiler anzeigen
    [autoit]

    #include <GuiConstants.au3>

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

    Opt("GUIOnEventMode",1)

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

    $GUI=GUICreate("Fenster",800,500)
    GUISetBkColor(0xFFFFFF)
    GUISetState(@SW_SHOW)
    GUICtrlCreatePic("skin/Hintergrund.jpg",0,0,800,500)
    GUISetOnEvent($GUI_EVENT_CLOSE,"_close")

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

    Local $left=350, $top=250

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

    $Jack = GUICtrlCreatePic("skin/Jack/Breath0.jpg",$left,$top,75,100)
    GUICtrlSetState(-1,@SW_DISABLE)

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

    AdlibRegister("_JackBreath",650)

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

    Func _JackBreath()
    For $x=0 To 3 Step 1
    GUICtrlSetImage($Jack,"skin/Jack/Breath"&$x&".jpg")
    Sleep(150)
    Next
    EndFunc

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

    Func _close()
    Exit
    EndFunc
    While 1
    Sleep(1)
    WEnd

    [/autoit]


    Zweite Variante mit GDI+ (flackert, durch das Löschen und wieder erstellen der Pics)

    Spoiler anzeigen
    [autoit]

    #include <GuiConstants.au3>
    #include <GDIPlus.au3>

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

    Opt("GUIOnEventMode",1)

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

    $GUI=GUICreate("Fenster",800,500)
    GUISetBkColor(0xFFFFFF)
    GUISetState(@SW_SHOW)
    GUICtrlCreatePic("skin/Hintergrund.jpg",0,0,800,500)
    GUISetOnEvent($GUI_EVENT_CLOSE,"_close")

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

    Local $left=350, $top=250

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

    $Jack=GUICtrlCreatePic("",$left,$top,75,110)
    _GDIPlus_Startup()
    $hGraphic=_GDIPlus_GraphicsCreateFromHWND($GUI)
    $image = _GDIPlus_ImageLoadFromFile("skin/Jack/Breath0.png")
    _GDIPlus_GraphicsDrawImage($hGraphic, $image, $left, $top)
    AdlibRegister("_JackBreath",650)

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

    Func _JackBreath()
    For $x=0 To 3 Step 1
    GUICtrlDelete($Jack)
    $Jack=GUICtrlCreatePic("",$left,$top,75,110)
    $image = _GDIPlus_ImageLoadFromFile("skin/Jack/Breath"&$x&".png")
    _GDIPlus_GraphicsDrawImage($hGraphic, $image, $left, $top)
    Sleep(150)
    Next
    EndFunc

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

    Func _close()
    Exit
    _GDIPlus_Shutdown()
    EndFunc

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

    While 1
    Sleep(1)
    WEnd

    [/autoit]

    Vlt hat jemand eine Idee wie es besser funktioniert :) Eventuell geht es auch dass man mit GDI+ einfach nur das Bild ändert anstatt es neu zu Zeichnen?
    Wäre sehr dankbar darüber.

    Mfg
    Ijens

  • Schau dir mal das GDI+ Tut. von Ubuntu an, da wird erklärt wie man einen Backbuffer erstellt damit es nicht flackert. GUICtrlCreatePic würde ich NIEMALS für Spiele benutzen, das sind GUIControls und keine Game-Objekte ^^

  • Vielen Dank das sieht wirklich gut aus, ist ein sehr gutes Tutorial.
    Also man muss dann einfach ALLES - auch den Hintergrund - mit GDI+ zeichnen. Und das mit den Buffern ist echt cool :D

    Hab zwar schon ganz gute Sachen gebastelt mit GUIControls, aber ist halt im Vergleich "unflexibel".
    Dann werd ich mir jetzt mal GDI+ aneignen ;)

    Danke nochmal :)
    Mfg

  • Ich hab das bei meinem Spiel ( s. Signatur ) so gemacht, dass ich ein Bild hatte ( transparent ) und darauf dann das Männchen gezeichnet habe ( also mit IcoFX das Bild erstellt ).
    Bild im Anhang
    Das ganze musst du als *.png oder *.gif oder so abspeichern ( micht als *.bmp, weil bmp keine transparenz kann ). Dann gas ganze mit GDIPlus ins Spiel einbringen.

    mfg
    Hauke

  • Ich hab das bei meinem Spiel ( s. Signatur ) so gemacht, dass ich ein Bild hatte ( transparent ) und darauf dann das Männchen gezeichnet habe ( also mit IcoFX das Bild erstellt ).
    Bild im Anhang
    Das ganze musst du als *.png oder *.gif oder so abspeichern ( micht als *.bmp, weil bmp keine transparenz kann ). Dann gas ganze mit GDIPlus ins Spiel einbringen.

    mfg
    Hauke

    Danke
    ja das hab ich schon so, ich mach meine Bilder mit Inkscape.

    Hab mir jetzt GDI+ genauer angesehen, ist ja ganz cool :D
    Aber muss man wirklich immer das komplette Bild neu Zeichnen? Das dauert ja dann doch recht lange wenn der den Hintergrund und alle Objekte darauf erst in den Buffer zeichnen und dann darstellen muss.
    Wie kommt man da auf die erforderlichen FPS?

    Mfg Ijens

  • Viele machen das so, dass sie das, was sie zeichnen möchten ( z.B. 10x10 Bilder oder so ) in Fragmente unterteilen, sodass sie jedes Fragment ( jedes Bild oder jeden Teil halt ) nur zeichnen müssne und nciht die ganze GUI neu zeichnen.

    ich hoffe du hast das verstanden :D

    mfg
    Hauke

  • Viele machen das so, dass sie das, was sie zeichnen möchten ( z.B. 10x10 Bilder oder so ) in Fragmente unterteilen, sodass sie jedes Fragment ( jedes Bild oder jeden Teil halt ) nur zeichnen müssne und nciht die ganze GUI neu zeichnen.

    ich hoffe du hast das verstanden :D

    mfg
    Hauke

    Ok danke aber du meinst jetzt nicht so wie blacky94 das in dem Thread heute wollte oder? https://autoit.de/index.php?page=Thread&amp;threadID=30039

    Und so wie BadBunny das gemeint hat mit DC:

    Benutz ein DC der GUI. Dann erstellt du eine Bitmap mit dem Bild drauf was du als Hintergrund malst. Dann kannst du die Bitmap immer auf das DC Blitten.


    Wie war das gemeint?^^

    Wie hast du es denn bei deinem Spiel gemacht Hauke?

    Danke :) Ijens

  • @Ijens:

    Nein nciht ganz. Also ich mache das immer wie folgt, wenn ich irgendwas in der richtung mache:

    |
    | Blickrichtung
    V
    __________ <-- GDI+ Bild, etc.
    __________ <-- Label mit datehn: Position oder Name, etc.
    __________ <-- oft ein weiteres Label mit anderen Infos
    __________ <-- GUI

    Das ganze auf verschiedenen Bereichen erleichtert das neuzeichnen. Bei meinem Spiel kann man das Prinzip ganz gut sehen ( lads dir mal runter und schaus dir an ;) ).
    Wenn ich also etwas neues Zeichnen will, mal ich das einfach über und änder die Label. Meistens beschrifte ich die Label mit der Position, sodass ich dann einfach die Position auslesen muss um das ganz einfach neu zu zeichnen.
    Eine andere Möglichkeit ist das Label genau so groß zu machen wie das Bild ( oder die GDI+ Sache halt ) und die Position des Labels herausfinden, dann kann man ein Label für 2 Infos nehmen.

    Ich hoffe du hast das so ein wenig Verstanden.

    mfg
    Hauke

  • Mit GDI+ beim Double Buffering wie weiter oben beschrieben
    läst sich auch der Hintergrund für deine Spielfigur
    auf das Rechteck begrenzen.

    Die Details musst du dir aber selbst raussuchen.

    Das Prinzip ist das nur der Bereich Hinter der Figur (Kleines Rechteck) Sowie die Figur
    an der Stelle wo es gezeichnet werden soll neu gezeichnet wird.

    Der Befehl müsste _GDIPlus_GraphicsDrawImageRectRect lauten mit dem du das machen kannst.

    Ich hab wie immer leider keine Zeit. Denn Rest must du dir Heraus Rechachieren.
    Aber ich sage dir es lohnt sich!

    Alles Gute.
    Mr.Script (Timeless) 8)

  • Vielen Dank ja das hat sehr geholfen :)

    Also Hauke du hast ja die kleinen Kästchen -> musst also immer nur 2 davon Bearbeiten (neu Zeichnen). Und eventuell noch die Labels.

    Und Mr. Script, das ist mir heute gerade eingefallen - bin dann auch auf _GDIPlus_GraphicsDrawImageRectRect gestoßen ;D
    Mal überlegen ob ich das so mache wie du sagst (Wär vom Design her einfacher),
    oder den Background so wie Hauke aus mehreren Teilen (Boden, Wände, Bäume, ...) zusammensetze - dahinter irgend ne Farbe.
    Schaun was schneller geht bzw ob es überhaupt nen großen Unterschied macht :)

    Also eig kommts dann nur drauf an wieviele Sachen ich pro Durchlauf neu Zeichnen muss -> Je kleiner die Dinger desto besser.
    Zurzeit bin ich wenn ich alles neu zeichne also auch Hintergrund auf 25 FPS.. ohne auf 600^^

    Gut dann werd ich mich mal in die Grafik Szene einleben ;) Vielen Dank
    Mfg

  • Ich kann dir nur nochmal raten die WinAPI zu benutzen, damit geht es bis zu 5 x schneller!!! Und wenn mans geschickt macht sogar auch mit Hintergrund!

    So erstellst du die nötigen DCs und Backbuffers von WinAPI

    [autoit]

    $hDC = _WinAPI_GetDC($hWnd)
    $hDC_Buffer = _WinAPI_CreateCompatibleDC($hDC)
    $hGDI_Bitmap = _WinAPI_CreateCompatibleBitmap($hDC, $nWidth, $nHeight)
    _WinAPI_SelectObject($hDC_Buffer, $hGDI_Bitmap)

    [/autoit]

    Und dann einfach eine Grafik aus dem Backbuffer DC erstellen

    [autoit]

    _GDIPlus_Startup()
    $hGraphics = _GDIPlus_GraphicsCreateFromHDC($hDC_Buffer)

    [/autoit]

    Jetzt kannst du mit

    [autoit]

    _WinAPI_BitBlt($hDC, 0, 0, $nWidth, $nHeight, $hDC_Buffer, 0, 0, 0x00CC0020)

    [/autoit]

    Die Grafik auf das DC übertragen. Alles wird ansonsten auf die Grafik gedrawt, wie normal (Brauchst auch keinen Backbuffer der Grafik, der is schon drin)

    Um jetzt einen Hintergrund schnell drauf zu malen (Hab ein Spiel geschrieben mit 1000x800 Hintergrund und haufen Elementen, läuft bei 60-80 FPS!)
    muss man erstmal ein Bild laden, (Kann auch eine Bitmap einer selbstgemalten Grafik sein)

    [autoit]

    ;; background laden
    $hImage_BG = _GDIPlus_ImageLoadFromFile(PATH)

    [/autoit]

    Danach wird wieder ein Buffer und eine Bitmap auf der DC genommen

    [autoit]

    ;; background auf DC kopieren
    $hDC_Buffer_BG = _WinAPI_CreateCompatibleDC($hDC)
    $hGDI_Bitmap_BG = _WinAPI_CreateCompatibleBitmap($hDC, $nWidth, $nHeight)
    _WinAPI_SelectObject($hDC_Buffer_BG, $hGDI_Bitmap_BG)

    [/autoit]


    Und anschließend brauch man am Anfang der Draw-Schleife den Buffer des neuen DCs mit dem Image/BG auf den Backbuffer des Haupt-DCs blitten

    [autoit]

    ; Blit BG
    _WinAPI_BitBlt($hDC_Buffer, 0, 0, $nWidth, $nHeight, $hDC_Buffer_BG, 0, 0, 0x00CC0020)

    [/autoit]

    Hoffe konnte dir helfen, PS: Nicht vergessen alles nochmal im Speicher freizugeben ;)

  • Hi,
    irgendwo hier im Forum geistert ein Script von mir rum, ich habs aber selbst nicht mehr gefunden^^
    Hier nochmal eine Demo, was man ohne GDI machen kann....
    autoit.de/wcf/attachment/14753/


  • Hoffe konnte dir helfen, PS: Nicht vergessen alles nochmal im Speicher freizugeben

    Gut dass du mich erinnerst ;D
    Und danke für die genaue Erklärung, jetzt versteh ich das mit den DC's und diese WinAPI Funktionen :D
    Das Blitten eines 2. Buffers ist ja wirklich ne tolle Sache! Thx!


    Danke, bei dem Beispiel werden ja sozusagen die Pixel für die Kreise und Spiralen gezeichnet - da nehm ich wohl doch lieber GDI+.
    Aber das mit den DC's und dem Blitten von WinAPI ist genial, werd ich bestimmt verwenden.
    Wirklich toll was man alles machen kann -> wenn mans kann ;)

    Danke euch beiden ;)

    Ijens

  • Zitat

    da nehm ich wohl doch lieber GDI+.

    mit was du die "Sprites" machst, ist dir überlassen^^
    Ich würde die Sprites als X*Y Pixel große Bitmaps zeichnen (mit irgendeinem Zeichenprogramm, zur Not geht Paint) und dann in den Backbuffer blitten.

    UEZ, Marsi und viele andere haben hier im Forum schon gezeigt, daß sich auch in Autoit schnelle grafische Spiele realisieren lassen.
    An der reinen "Darstellung", egal ob durch GDI oder per Win-API, hapert es eher selten^^, siehe Beispiel! 50 transparente Sprites bewegen sich auf einem rotierenden Hintergrund mit 50-60FPS@2.5Ghz.

    Meistens liegt es an den mehr oder weniger aufwendigen Berechnungen, die durch Autoit extrem ausgebremst werden, Kollisionsberechnung, Positionsvergleiche usw.
    Aber die Berechnungen im "inner Loop" lassen sich auch in eine einfache C- oder Assembler-dll auslagern und dann einfach callen.

  • Hallo,

    Ich habe mir den Beitrag von BadBunny nochmals durchgelesen (bin dir auch sehr dankbar dafür) doch ich verstehe es leider nicht. Könnte mir jemand vielleicht zeigen wie ich bei diesen Simplen Beispiel mittels WinApi einen Hintergrund(-bild) hinzufüge ohne es (den Hintergrund) immer neumalen zu müssen?

    Spoiler anzeigen
    [autoit]

    #include <WinApi.au3>
    #include <GDIPlus.au3>
    #include <Misc.au3>
    #include <GuiConstants.au3>

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

    $posX = 0
    $posY = 0
    $iWidth = 700
    $iHeight = 500

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

    $hWnd = GUICreate("Test WinApi",700,500)
    GUISetState()

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

    _GDIPlus_Startup()

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

    $hDC = _WinAPI_GetDC($hWnd)
    $hDC_Buffer = _WinAPI_CreateCompatibleDC($hDC)
    $hDC_Bitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iWidth, $iHeight)
    _WinAPI_SelectObject($hDC_Buffer, $hDC_Bitmap)

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

    $hGraphics = _GDIPlus_GraphicsCreateFromHDC($hDC_Buffer)

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

    _WinAPI_BitBlt($hDC, 0, 0, $iWidth, $iHeight, $hDC_Buffer, 0, 0, 0x00FFFFFF)

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

    $hBrush = _GDIPlus_BrushCreateSolid(0xFFFFFFFF)

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

    Do
    Select
    Case _IsPressed(27)
    _Walk(16,0)
    Case _IsPressed(25)
    _Walk(-16,0)
    Case _IsPressed(26)
    _Walk(0,-16)
    Case _IsPressed(28)
    _Walk(0,16)
    EndSelect
    Until GUIGetMsg() = -3

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

    _Exit()

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

    Func _Walk($x=1,$y=0)
    _ReDraw()
    $posX = $posX + $x
    $posY = $posY + $y
    _GDIPlus_GraphicsFillEllipse($hGraphics,$posX,$posY,16,16,$hBrush)
    Sleep(50)
    EndFunc

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

    Func _ReDraw()
    _WinAPI_BitBlt($hDC, 0, 0, $iWidth, $iHeight, $hDC_Buffer, 0, 0, 0x00CC0020)
    _GDIPlus_GraphicsClear($hGraphics, 0xFFCC0020)
    EndFunc

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

    Func _Exit()
    _WinAPI_ReleaseDC($hWnd, $hDC_Buffer)
    _WinAPI_ReleaseDC($hWnd, $hDC)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_Shutdown()
    EndFunc

    [/autoit]
  • Du benutzt leider kein Bild deswegen hab ich es mit einem Rechteck in der Farbe des BG gemacht (PS: Würde noch WM_Paint reinmachen, damit es beim überdecken neugemalt wird)

    [autoit]

    #include <WinApi.au3>
    #include <GDIPlus.au3>
    #include <Misc.au3>
    #include <GuiConstants.au3>

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

    $posX = 0
    $posY = 0
    $iWidth = 700
    $iHeight = 500

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

    $hWnd = GUICreate("Test WinApi", 700, 500)
    GUISetState()

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

    _GDIPlus_Startup()

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

    $hDC = _WinAPI_GetDC($hWnd)
    $hDC_Buffer = _WinAPI_CreateCompatibleDC($hDC)
    $hDC_Bitmap = _WinAPI_CreateCompatibleBitmap($hDC, $iWidth, $iHeight)
    _WinAPI_SelectObject($hDC_Buffer, $hDC_Bitmap)

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

    $hGraphics = _GDIPlus_GraphicsCreateFromHDC($hDC_Buffer)

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

    $hBrush = _GDIPlus_BrushCreateSolid(0xFFFFFFFF)
    $hBrush_BG = _GDIPlus_BrushCreateSolid(0xFFCC0020)

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

    _GDIPlus_GraphicsClear($hGraphics, 0xFFCC0020)
    _GDIPlus_GraphicsFillEllipse($hGraphics, $posX, $posY, 16, 16, $hBrush)
    _WinAPI_BitBlt($hDC, 0, 0, $iWidth, $iHeight, $hDC_Buffer, 0, 0, 0x00CC0020)

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

    Do
    Select
    Case _IsPressed(27)
    _Walk(16, 0)
    Case _IsPressed(25)
    _Walk(-16, 0)
    Case _IsPressed(26)
    _Walk(0, -16)
    Case _IsPressed(28)
    _Walk(0, 16)
    EndSelect

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

    Until GUIGetMsg() = -3

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

    _Exit()

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

    Func _Walk($x = 1, $y = 0)
    _ReDraw()
    $posX = $posX + $x
    $posY = $posY + $y
    _GDIPlus_GraphicsFillEllipse($hGraphics, $posX, $posY, 16, 16, $hBrush)
    Sleep(50)
    EndFunc ;==>_Walk

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

    Func _ReDraw()
    _WinAPI_BitBlt($hDC, 0, 0, $iWidth, $iHeight, $hDC_Buffer, 0, 0, 0x00CC0020)
    _GDIPlus_GraphicsFillRect($hGraphics, $posX, $posY, 16, 16, $hBrush_BG)
    EndFunc ;==>_ReDraw

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

    Func _Exit()
    _WinAPI_ReleaseDC($hWnd, $hDC_Buffer)
    _WinAPI_ReleaseDC($hWnd, $hDC)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_BrushDispose($hBrush_BG)
    _GDIPlus_GraphicsDispose($hGraphics)
    _GDIPlus_Shutdown()
    EndFunc ;==>_Exit

    [/autoit]

    Das Blitten muss aber trotzdem gemacht werden

  • Ich würde echt empfehlen wenn man die

    _WinAPI_BitBlt

    wie weiter oben beschrieben nutzen möchte, diese Funktion
    selbst in eigener Art zu Implementieren.
    Wenn man sich die "WinAPI" Include mal ansieht lädt die Function
    mit DllCall jedesmal die Dll neu, was unnötig ist!!!!!

    Wenn man statt dessen mit DllOpen vorher läd und mit DllClose
    später wieder entlädt und DllCall mit dem bzgl. Handle nutzt ist alles viel Schneller.

    Alles Gute.
    Mr.Script
    8)