[SDL] Transparente Farbe eines Sprites setzten

Für Probleme bezüglich Grafik, Audio, GL, ACS, ...
Antworten
bembulak
Beiträge: 370
Registriert: Di 6. Feb 2007, 09:29
OS, Lazarus, FPC: L0.9.29 SVN:24607 FPC 2.4.0-32 bit @ Win XP SP3
CPU-Target: 32bit i386, ARM
Wohnort: Oberösterreich

[SDL] Transparente Farbe eines Sprites setzten

Beitrag von bembulak »

Guten Morgen euch allen!

Ich spiele mich gerade ein wenig mit JEDI-SDL und bin da nun auf ein Problem gestoßen, welches ich bisher nicht lösen konnte.
Daher hoffe ich nun auf eure Unterstützung.

Laut Dokumentation ist es so, dass man für ein "Surface" (in diesem Fall ist das Surface ein Bild) eine Farbe definieren kann, welche auf dem "Haupt-Surface" (also der "Screen") nicht erscheint. Es wird also Transparent dargestellt.
Das Definieren dieses Bereiches soll mit SDL_SetColorKey() funktionieren, wobei folgende Parameter notwendig sind:
  • Surface/Bild welches dargestellt werden soll.
  • ein Flag (dessen Sinn sich mir noch nicht erschließt). Laut Dokumentation kann der Wert 0 sein. Damit werden bestehende "Colorkeys" gelöscht - was auch immer damit gemeint ist.
  • Die Farbe, welche transparent sein soll im Format key : UInt32
Damit man auf die Farbe kommt, die man ja nicht als RGB(r:byte, g:byte, b:byte) angeben kann, muss man diese vorher mit SDL_MapRGB() definieren.
Dazu muss man das Format (also die Auflösung) einer Quelle heranziehen und dann die Farbe in RGB (ähnlich wie oben beschrieben) definieren. Als Format empfiehlt sich der Screen selbst (so ist es zumindest in anderen Wrappern für SDL die ich kenne).

Was ich also habe:

Code: Alles auswählen

var
   // SCREEN
   screen : PSDL_Surface;
 
   // HINTERGRUNDBILD:
   backIMG : PSDL_Surface;
   i, j : Integer; // Counter für HintergrundTile
 
   // FIGUR
   gbImage : PSDL_Surface;
   gbRect : PSDL_Rect;
 
begin
begin
     {
     SDL Initialisieren (sofern möglich),
     Fenster erzeugen und konfigurieren.
     }

     if (SDL_Init(SDL_INIT_VIDEO) < 0) then
     begin
          SDL_Quit;
          exit;
     end;
     SDL_WM_SetCaption('GearBoy Walks on Tiles 01', 'Icon Name');
     screen := SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE or SDL_HWACCEL);
     if screen = NIL then
     begin
          halt;
          SDL_Quit;
     end;
 
     {
     Laden des Hintergrundbildes (Tile) und verteilen über den Screen.
     Dazu wird ein "RECT" benötigt, welches über eine For-Schleife
     auf den genügend oft (in Zusammenhang mit der Screen-Größe)
     auf den Bildschirm gemalt wird.
     }

     backIMG := SDL_LoadBMP('GRASS32x32.bmp');
     new(screen_RECT);
     screen_RECT^.x:= 0;
     screen_RECT^.y:= 0;
 
     for i := 0 to 20 do
     begin
          for j := 0 to 15 do
          begin
               screen_RECT^.x := (i * 32);
               screen_RECT^.y := (j * 32);
               SDL_BlitSurface(backIMG, nil, screen, screen_RECT);
          end;
     end;
 
     {
     GEARBOY LADEN:
     }

     gbImage:= SDL_LoadBMP('GB_WALK.bmp');
     SDL_SetColorKey(gbImage, 0, SDL_MapRGB(screen^.format, 255,0,255));
     new(gbRECT);
     gbRECT^.x := 0;
     gbRect^.y := 0;
     gbRect^.w := 32;
     gbRect^.h := 48;
 
 
     {
     MAINLOOP
 
     Der Mainloop startet hier:
     Erst wird ein Event erzeugt und dieser dann in einer Schleife immer wieder
     abgefragt.
 
     Eventverarbeitung:
     }

     New(mEvent);
     While loopstop = False do
     begin
       if SDL_PollEvent(mEvent) = 1 then // wenn etwas in der Queue ist...
       begin
           case mEvent^.type_ of
                { Unterscheiden der diversen Events:}
 
                // QUIT (Schließen des Fensters durch klicken auf 'x'
                SDL_QUITEV:
                begin
                     loopstop := True;
                end;
 
                // Eine Taste wurde gedrückt...
                SDL_KEYDOWN:
                begin
                     // welche Taste:
                     case mEvent^.key.keysym.sym of
                          SDLK_ESCAPE: loopstop:= True;
                          SDLK_UP: ;
                          SDLK_DOWN: ;
                          SDLK_LEFT: ;
                          SDLK_RIGHT: ;
                     end;
                end;
           end;
       end;
      {
      Zeichnoperationen:
      }

       SDL_BlitSurface(gbImage, gbRECT, screen, nil);
       SDL_Flip(screen);
     end;
 
     {
     Mainloop endet hier.
     }

 
     {
     AUFRÄUMEN:
     Freigeben der Ressourcen:
     }

     Dispose(mEvent);
     Dispose(screen_RECT);
     Dispose(gbRECT);
     SDL_FreeSurface(gbImage);
     SDL_FreeSurface(backIMG);
     SDL_FreeSurface(screen);
     SDL_Quit;
end.


So, das ist nun ein wenig viel, aber wenn es jemand testen will, hat er schon was lauffähiges (sofern SDL installiert ist).

Das "Problem", bzw. den Grund, warum es nicht funktioniert vermute ich in diesem Codeabschnitt:

Code: Alles auswählen

gbImage:= SDL_LoadBMP('GB_WALK.bmp');
SDL_SetColorKey(gbImage, 0, SDL_MapRGB(screen^.format, 255,0,255));


Die Transparente Farbe RGB(255,0,255) ist ein hässliches Pink, welches in Sprites für 2D-Spiele üblich ist. Wenn ich das Bild in anderen Sprachen und Game-Libs benutze, funktioniert es einwandfrei.
Ein Fehler des Bildes kann also ausgeschlossen werden.
Ich hatte ein paar mal WriteLn() eingebaut, um zu sehen, was die Funktionen so zurückgeben, und konnte auch dort nirgends einen Fehler meinerseits ausmachen.

Kann mir von euch jemand auf die Sprünge helfen, oder kennt jemand eine Lösung?
Was mache ich falsch?

Hier noch die verwendeten Bilder:
GRASS32x32.bmp
GRASS32x32.bmp (3.05 KiB) 1133 mal betrachtet
Spriteset
Spriteset
54.bmp (72.05 KiB) 1133 mal betrachtet
Hier die Unterschiede zwischen FPC und PyGame, dort funzt es mit den gleichen Bildern.
Hier die Unterschiede zwischen FPC und PyGame, dort funzt es mit den gleichen Bildern.


Wie ihr am letzten Bild sehen könnt, habe ich das Problem nur mit Lazarus, was bestätigt, dass es an meinem Code liegen muss und die Bilder fehlerfrei sind.

bembulak
Beiträge: 370
Registriert: Di 6. Feb 2007, 09:29
OS, Lazarus, FPC: L0.9.29 SVN:24607 FPC 2.4.0-32 bit @ Win XP SP3
CPU-Target: 32bit i386, ARM
Wohnort: Oberösterreich

Re: [SDL] Transparente Farbe eines Sprites setzten

Beitrag von bembulak »

Hallo!

Auch wenn es scheinbar nicht so interessant ist, könnte es doch sein, dass mal wieder jemand etwas mit SDL und Multimedia/Grafik machen möchte.
Ich habe heute eine Lösung gefunden.
Es ärgert mich, dass ich nicht schon eher drauf gekommen bin, aber was soll's?

Hier der Lösungsweg:

Code: Alles auswählen

var
// Im Variablenbereich eine zusätzlich Farbe anlegen:
gbTransp = TSDL_Color;
 
//
// Und in der Implentierung habe ich nun folgendes gemacht:
gbTransp.r := 255;
     gbTransp.g := 0;
     gbTransp.b := 255;
     SDL_SetColorKey(gbImage,
                             SDL_SRCCOLORKEY or SDL_RLEACCEL,
                             SDL_MapRGB(gbImage^.format,
                             gbTransp.r,
                             gbTransp.g,
                             gbTransp.b));

Es wirkt zwar noch etwas aufgeläht (wenn alles in Units und Klassen verpackt ist, wird es nicht mehr so sein), aber es funktioniert!

Bild

Vielleicht hilft es ja jemanden, oder es bekommt noch jemand Bock, mehr Spiele, etc. mit Lazarus zu machen!

Macht's gut!

Jay
Beiträge: 65
Registriert: Sa 29. Dez 2007, 02:21
OS, Lazarus, FPC: Linux, 0.9.30.4 Beta, FPC 2.6.0
CPU-Target: 32Bit
Kontaktdaten:

Re: [SDL] Transparente Farbe eines Sprites setzten

Beitrag von Jay »

Ich habe einfach PNG-Dateien genommen, die bekanntlicherweise Transparenz unterstützen. Da hab ich einfach das, was transparent sein soll, transparent gemacht :lol:
Die Datei lade ich dann entsprechend in ein Surface und kann sie beliebig verwenden.

Cybermonkey342
Beiträge: 109
Registriert: Sa 1. Mär 2008, 15:19
OS, Lazarus, FPC: Kubuntu 22.04 (FPC 3.2.2) / Win11 Pro (FPC 3.2.2) / macOS Ventura (FPC 3.2.2)
CPU-Target: x86/x64
Kontaktdaten:

Re: [SDL] Transparente Farbe eines Sprites setzten

Beitrag von Cybermonkey342 »

Also in der EGSL engine habe ich das so gelöst (reiner Freepascal code und
ckred, ckblue und ckgreen sind Variablen des typ byte und colourkey ist ein integer):

Code: Alles auswählen

colorkey := SDL_MapRGB(screen.format, ckred, ckgreen, ckblue);
  SDL_SetColorKey(image,SDL_RLEACCEL or SDL_SRCCOLORKEY , colorkey);
Mit besten Grüßen,
Cybermonkey

Antworten