3Danke
ERLEDIGT
JA
JA
ANTWORTEN
14
14
ZUGRIFFE
2040
2040
EMPFEHLEN
-
Ich habe einen neuen Typen deklariert:
Code :1 2 3 4
type TSpielfigur = class(TImage) private pos: array[1..819,1..396] of integer; end;
Jetzt gibt es zwei Probleme:
1. pos ist ein zweidimensionales Array, ich möchte einer Speicherposition zwei Werte gleichzeitig zuweisen (z.B. pos[5,8] := 32,36 o.ä.). Wie könnte man das machen?
2.
Code :1 2 3 4 5 6
Figur := TSpielfigur.Create(Spielfeld); Figur.Picture.Icon.LoadFromFile('C:\Delphi-Programme\Icons\Smiley2.ico'); Figur.Picture.Icon.Height := 33; Figur.Picture.Icon.Width := 33; Figur.Left := 32; Figur.Top := 176; Figur.Visible := true;
Ich erstelle eine Komponente vom Typ TSpielfigur und lade ein Bild vom Typ TIcon. Höhe, Breite und Position auf dem Formular werden festgelegt. Trotzdem wird das Icon auf dem Formular nicht sichtbar. Was ist da los und wie kann man es beheben?
-
19.02.09 17:53 #2
- Registriert seit
- Mar 2004
- Beiträge
- 441
Gar nicht. Denn dein Array ist ein Array (wenn auch zweidimensional) von Integern und ein Integer kann per definitionem nur einen (Ganzzahl-) Wert aufnehmen. Ich habe keine Ahnung, was du mit deinem Array bezweckst, aber eventuell kann dir TPoint bei dem helfen, was du vorhast.
Wow. Also mit TIcon innerhalb von TPictures (innerhalb von Timages) habe ich noch nicht gearbeitet, aber ich hatte schon ziemlich oft den Fall, daß das "Image" nicht angezeigt wird. Der Grund lag zu 99% darin, daß die TBitmap des TPicture des TImage entweder nil war oder Width, Height auf 0,0 standen.
Von daher habe ich mir die direkte Arbeit mit TBitmaps angewöhnt und verwende TImages normalerweise nur noch als Viewports (insbesondere für schnelle Skalierungen). Seitdem hatte ich keinen Ärger mehr mit nicht angezeigten Bildern.
Als schnelle Lösung kannst du mal folgendes probieren:
Ist allerdings ungetestet. Bin nicht mehr am Arbeitsrechner.Code :1 2 3
Figur.Picture.Bitmap.Width := Figur.Picture.Icon.Width; Figur.Picture.Bitmap.Height := Figur.Picture.Icon.Height; Figur.Picture.Bitmap.Canvas.Draw(0, 0, Figur.Picture.Icon);
-
Also, ich habe jetzt auf Bitmap umgestellt (auch das Bildchen ist eine Bitmap).
Code :1 2 3 4 5 6
Figur.Picture.Bitmap.LoadFromFile('C:\Delphi-Programme\Icons\Smiley2.bmp'); Figur.Picture.Bitmap.Height := 33; Figur.Picture.Bitmap.Width := 33; if Figur.Picture.Bitmap.Empty = true then ShowMessage('Bitmap leer') else Figur.Picture.Bitmap.Canvas.Draw(32, 176, Figur.Picture.Bitmap);
Laut if-Abfrage ist die Bitmap nicht leer, aber es wird trotzdem nichts gezeichnet. Ich dachte erst, es könnte an der Zeichenfläche liegen, deshalb habe ich das Formular die Bitmap zeichnen lassen:
Jetzt sagt der Compiler, das wäre eine Adressverletzung. Warum?Code :1
Spielfeld.Canvas.Draw(32,176,Figur.Picture.Bitmap);
-
20.02.09 09:16 #4
- Registriert seit
- Mar 2004
- Beiträge
- 441
Keine Ahnung.
Zeichnest du im OnCreate?
Referenzierst du ein self im OnCreate?
Ist Spielfeld nicht nur definiert sondern auch instantiiert?
Ist Figur nicht nur definiert sondern auch instantiiert?
Ist Figur.Picture nicht nur definiert sondern auch instantiiert?
Ist Figur.Picture.Bitmap nicht nur definiert sondern auch instantiiert?
All dies kann dir der Debugger beantworten.
-
habe jetzt den Typ TSpielfigur von TBitmap abgeleitet
Code :1 2 3 4
type TSpielfigur = class(TBitmap) private pos: TPoint; end;
Dann habe ich eine Prozedur geschrieben, die das Bild erstellen soll (damit FormCreate nicht zu voll wird).
Code :1 2 3 4 5 6 7 8 9 10
procedure TSpielfeld.bild_einrichtung; begin Figur := TSpielfigur.Create; Figur.pos.X := 32; Figur.pos.Y := 176; Figur.LoadFromFile('C:\Delphi-Programme\Icons\Smiley2.bmp'); Figur.Height := 33; Figur.Width := 33; Figur.Canvas.Draw(32, 176, Figur); end;
Sieht nach Augenwischerei aus, ist es vielleicht auch, aber selbst mit Spielfigur als direkte Bitmap funktioniert es nicht. CSANecromancer, ich weiß, du gibst ungern Sourcecodes, aber wie machst du es generell, wenn du ein Bild von dem Programm erstellen und anzeigen lassen willst?
-
21.02.09 03:27 #6
- Registriert seit
- Mar 2004
- Beiträge
- 441
Das liegt in 9 von 10 Fällen daran, daß derjenige, der nach dem Source fragt, mit an Sicherheit grenzender Wahrscheinlichkeit nichts damit anfangen kann, weil eine 1-Zeilen-Lösung für komplexe Probleme erwartet wird.
Ich denke, an der Stelle sollte ich mal ein paar Takte darüber ablassen, wie ich die graphischen Komponenten von Delphi betrachte und kennen gelernt habe.
Der unterste Level ist der TCanvas. So ziemlich jede visuelle und/oder graphische Komponente hat einen TCanvas. Egal ob TImage, TPicture, TBitmap, TShape, TPaintBox, TForm, TButton etc.pp., überall gibt es einen TCanvas, mittels dem gezeichnet werden kann.
Einige Leute zeichnen direkt auf den TCanvas des Hauptformulars, doch ich war noch nie ein Fan davon. Denn zwischen Zeichnen und Anzeigen liegt ein deutlicher Unterschied, wie du ja auch schon festgestellt hast.
Ok, wozu sind dann die anderen graphischen Komponenten gut?
TBitmap ermöglicht die einfachsten Lade- und Speicheroptionen für die einfachste Bildart überhaupt, eine unkomprimierte Bitmap. D.h. jeder Pixel der Bitmap wird im Idealfall (24 Bit Farbtiefe) durch 3 Byte repräsentiert: Rot, Grün, Blau. Klar gibt's da noch Feinheiten, aber um die nachzulesen gibt's ja die Onlinehilfe.
TPicture kapselt die TBitmap und ermöglicht erweiterte Lade- und Speicheroptionen. Schau dir auch das mal in der Onlinehilfe an, da ist sehr schön beschrieben, welche Eigenschaften und Methoden TPicture selbst mitbringt und welche von TBitmap oder TCanvas stammen.
TImage wiederum kapselt ein TPicture und bietet v.a. folgende Möglichkeiten: Bildproportionen verankern, Bild zentriert in einem Bereich anzeigen, Bild skalieren (und das sehr schnell). Den Rest von TImage habe ich noch nicht so intensiv verwendet.
Am Rande noch: Mit TShape lassen sich einfachste geometrische Figuren darstellen und TPaintBox soll einfach nur dafür sorgen, daß nicht unmittelbar auf den Canvas der TForm gepinselt wird sondern auf einen festgelegten Bereich - nämlich die TPaintBox.
Dann werfen wir mal einen genaueren Blick auf TImage. Timage beinhaltet TPicture - und einen TCanvas. Jup, das TImage hat einen eigenen
TCanvas. Und auch das TPicture hat einen eigenen TCanvas. Und die TBitmap im TPicture im TImage hat auch einen eigenen TCanvas! Was soll denn der Blödsinn?
TImage
|
+---> TCanvas
|
+--->TPicture
|
+---> TCanvas
|
+---> TBitmap
|
+---> TCanvas
Welches ist jetzt der richtige?
Du kannst dir das grob vereinfacht so vorstellen:
Den TCanvas von TPicture kannst du vergessen. Der hat keinen echten Wert. Der TCanvas von TBitmap ist das, was an TImage zur Darstellung weitergereicht wird. Bevor TImage aber das Bild in TBitmap anzeigt, wird es noch umgewandelt (skaliert, zentriert, proportioniert usw., was TImage halt so gemäß seiner Einstellungen mit dem Bild macht). Wenn TImage dann endlich fertig ist mit dem Verwursten der Bilddaten, dann werden die an den TCanvas von TImage durchgereicht und auf den Bildschirm gezeichnet.
Klar soweit? (im Ton von Cpt. Jack Sparrow)
Nachdem ich mich mal durch diesen Wust durchgewühlt hatte, habe ich zwei Dinge beschlossen:
1. Soweit und wann immer möglich, bearbeite ich Bilddaten als TBitmap.
2. Ich zeichne niemals direkt auf einen TForm.Canvas, sondern verwende als Anzeigefläche immer ein TImage. Denn mit diesem TImage kann ich die Anzeige noch ein wenig steuern und sehr viel bequemer erweitern als wenn alles im TForm in einem Haufen vorliegt.
Damit ist der allererste Teile deiner Frage beantwortet.
Den Smily würde ich also (für's Erste) als Bitmap speichern.
Auf das Formular würde ich ein TImage knallen. Dieses heisst bei mir normalerweise "Image". Sehr phantasielos, ich weiß. Aber da ich in der Regel nur ein einziges Image verwende, ist es dann doch eindeutig.
Zum Laden und Anzeigen des Smilys würde ich dann so vorgehen:
Mit dem Source kannst du jetzt eine beliebige Bitmap laden und anzeigen. Bewegen ist aber wieder eine andere Baustelle.Code :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
prodecure ShowSmily; var bmSmily: TBitmap; begin bmSmily := TBitmap.Create; bmSmily.LoadFromFile('smily.bmp'); // Nach dem Laden ist kein explizites Zuweisen von Width und Height erforderlich. // Diese werden implizit durch das Laden des bmp-Files gesetzt // Ich sitze jetzt gerade nicht am Compiler, deswegen kann ich nicht // ausprobieren, ob die TBitmap unten in TImage defaultmässig schon // existiert. Daher diese "Nummer Sicher" if Image.Picture = nil then Image.Picture := TPicture.Create; if Image.Picture.Bitmap := nil then Image.Picture.Bitmap := TBitmap.Create; // Ok, furzegal, was ist, ich habe jetzt mal meine Bitmap mit dem Smily // und es sind alle Ebenen von TImage eingerichtet. Auch wenn die Bitmap // von TImage nichts anzeigt, weil bislang weder eine Bitmap geladen noch // die Dimensionen der Bitmap eingerichtet worden sind. Image.Picture.Bitmap.Assign(bmSmily); // Tadaaa! Jetzt wäre schon mal der Smily in der Bitmap des Image. // Ich habe aber schlechte Erfahrungen mit Assign gemacht, vor allem, // wenn das Ursprungsobjekt, wie in diesem Falle die bmSmily am // Prozedurende wieder aufgeräumt wird. // Aus diesem Grund bevorzuge ich das explizite Kopieren der Bilddaten // und deren explizite Zuweisung. Image.Picture.Bitmap.Width := bmSmily.Width; Image.Picture.Bitmap.Height := bmSmily.Height; Image.Picture.Bitmap.Canvas.CopyRect(Rect(0, 0, bmSmily.Width - 1, bmSmily.Height - 1), bmSmily, Rect(0, 0, bmSmily.Width - 1, bmSmily.Height - 1)); // Da jetzt die Bilddaten von bmSmily ganz ausdrücklich kopiert und damit // vervielfältigt wurden, sind die Ursprungsdaten nicht mehr notwendig. FreeAndNil(bmSmily); // Kann aber immer noch sein, daß der Smily nicht korrekt angezeigt wird. // Das ist u.a. davon abhängig, was alles im Hintergrund auf dem Rechner // läuft, welche Prozeßpriorität dieses Programm hier hat usw. // Da mir das aber alles zu blöd ist, sage ich dem Programm mit Brachialgewalt // "Hallo, da wurde ein neues Bild geladen, zeige das gefälligst an!": Image.Invalidate; // Oder alternativ: Image.Refresh; end;
Wenn's um Animation geht, kommen mir sehr viele Leute immer gleich mit OpenGL, DirectX und wasweissichnichtalles an. Und erklären mich dann für irre, wenn ich behaupte, daß sich am Prinzip der Animation seit 1906 nichts Grundlegendes geändert hat.
Das Prinzip ist bis heute das gleich:
- Hintergrund zeichnen
- Smily auf den Hintergrund zeichnen
- Hintergrund neu zeichnen
- Smily versetzt auf den Hintergrund zeichnen
- Hintergrund neu zeichnen
- Smily noch etwas weiter versetzt auf den Hintergrund zeichnen
usw.usf.
Und das Ganze richtig schnell. Je schneller es geht, desto flüssiger erscheint die Animation (genau das drückt der Begriff "frames per second" aus, der aber wiederum so ziemlich jedem Gamer bekannt ist...)
Mist, daß der Smily im Codestück weiter oben direkt in den Hintergrund (nämlich die TBitmap des TPicture des TImage) gezeichnet wurde.
Aus diesem Grunde arbeite ich normal mit mehreren Bitmaps: Eine Bitmap für den Hintergrund, eine Bitmap für den Smily, eine Bitmap zum Zusammenführen des Ganzen. Und dann wird das Zusammengeführte in die Bitmap des TImage geleitet und fertig ist der Käse.
Hier ein einfacher, unkommentierter Source, der dieses Geschehen veranschaulichen soll:
Code :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
procedure Demo; var bmBackground: TBitmap; bmSmily: TBitmap; bmBlitter: TBitmap; i: Integer; begin if Image.Picture = nil then Image.Picture := TPicture.Create; if Image.Picture.Bitmap = nil then Image.Picture.Bitmap := TBitmap.Create; Image.Picture.Bitmap.Width := 800; Image.Picture.Bitmap.Height := 600; Image.Width := 800; Image.Height := 800; bmBackground := TBitmap.Create; bmBackground.LoadFromFile('background.bmp'); bmSmily := TBitmap.Create; bmSmily.LoadFromFile('smily.bmp'); bmBlitter := TBitmap.Create; bmBlitter.Width := Image.Picture.Bitmap.Width; bmBlitter.Height := Image.Picture.Bitmap.Height; for i := 0 to 700 do begin bmBlitter.Canvas.CopyRect(Rect(0, 0, bmBackground.Width - 1, bmBackground.Height - 1), bmBackground, Rect(0, 0, bmBackground.Width - 1, bmBackground.Height - 1); bmBlitter.Canvas.CopyRect(Rect(i, 50, i + bmSmily.Width, 50 + bmSmily.Height), bmSmily, Rect(0, 0, bmSmily.Width - 1, bmSmily.Height - 1)); Image.Picture.Bitmap.Canvas.CopyRect(Rect(0, 0, bmBlitter.Width - 1, bmBlitter.Height - 1), bmBlitter, Rect(0, 0, bmBlitter.Width - 1, bmBlitter.Height - 1)); Image.Refresh; end; FreeAndNil(bmBackground); FreeAndNil(bmSmily); FreeAndNil(bmBlitter); end;
Wie im Sourcekommentar weiter oben erwähnt, habe ich gerade keinen Compiler zur Hand zum Austesten, aber ich bin mit ziemlich sicher, daß die Sources funktionieren oder wenn dann nur geringe Fehler drin sind (z.B. bin ich mir mit den -1 bei den Dimensionierungen der CopyRects gerade nicht 100% sicher).
Ich hoffe, das hilft jetzt etwas weiter.
-
da kannst du als zweiten Parameter, nicht die Bitmap nehmen, da CopyRect hier einen Canvas verlangt. Aber mit Assign klappts bei mir, deshalb ist das jetzt nicht das Problem.
Zitat von CSANecromancer
Dein Prinzip mit der Bilderstellung zur Laufzeit habe ich verstanden, kann es auch gut anwenden. Aber ich verstehe nicht, wie das mit der Animation klappen soll. Könntest du deinen Sourcecode da vielleicht ein wenig kommentieren? Dann wäre ich einen Schritt weiter und könnte es selbstständig anwenden.
-
21.02.09 19:33 #8
- Registriert seit
- Mar 2004
- Beiträge
- 441
Ist ja kein Problem, dann gibst du halt statt bmBackground bmBackground.Canvas an.

Ausnahmsweise, weil ich mir vorstellen kann, daß das alles etwas schnell war.
Code :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
procedure Demo; var bmBackground: TBitmap; bmSmily: TBitmap; bmBlitter: TBitmap; i: Integer; begin // Zu Beginn des ganzen Gekrempels gehe ich mal davon aus, daß Image // noch überhaupt nicht richtig eingerichtet ist. Daher hier wieder die Instantierung // aller Ebenen von Image. if Image.Picture = nil then Image.Picture := TPicture.Create; if Image.Picture.Bitmap = nil then Image.Picture.Bitmap := TBitmap.Create; Image.Picture.Bitmap.Width := 800; Image.Picture.Bitmap.Height := 600; // Bloß weil die im Hintergrund liegende Bitmap von Image mit der Größe // 800x600 eingerichtet worden ist, heißt das noch lange nicht, daß die sichtbare // Fläche von Image auch schon ein bestimmtes Ausmaß hat. // Ich will aber nicht nur eine 800x600 Bitmap anzeigen, ich will sie auch im // Originalformat 800x600 anzeigen, weswegen das Image eine entsprechende // Größe braucht. Image.Width := 800; Image.Height := 800; // Jetzt lade ich mir ein x-beliebiges Hintergrundbild... bmBackground := TBitmap.Create; bmBackground.LoadFromFile('background.bmp'); // ...und natürlich auch den Smily bmSmily := TBitmap.Create; bmSmily.LoadFromFile('smily.bmp'); // Der Blitter ist das Bild (genauer: Die Bitmap), wo ich Hintergrund // und Smily miteinander verschmelze, um dann das Ergebnis von Image // anzeigen zu lassen. Logisch, daß der Blitter damit die gleiche Größe // braucht wie die Bitmap des Image. bmBlitter := TBitmap.Create; bmBlitter.Width := Image.Picture.Bitmap.Width; bmBlitter.Height := Image.Picture.Bitmap.Height; // Es sollen 700 "Animations"-Schritte durchlaufen werden, genauer formuliert // soll der Smily von links nach rechts 700 Pixel weit verschoben werden. for i := 0 to 700 do begin // Als allererstes muß ein sauberer Hintergrund aufgebaut werden. // Nochmal: Im Blitter wird alles zusammen gebaut. Deswegen wird hier // der Hintergrund auf die Bitmap des Blitters kopiert. bmBlitter.Canvas.CopyRect(Rect(0, 0, bmBackground.Width - 1, bmBackground.Height - 1), bmBackground.Canvas, Rect(0, 0, bmBackground.Width - 1, bmBackground.Height - 1); // Nur ein leerer Hintergrund ist zu langweilig, deswegen wird hier noch der // Smily auf den Blitter kopiert. Wenn die komischen Koordinaten und das i // völlig unbekannt und nicht nachvollziehbar erscheinen: // Onlinehilfe, Suchbegriff CopyRect bmBlitter.Canvas.CopyRect(Rect(i, 50, i + bmSmily.Width, 50 + bmSmily.Height), bmSmily.Canvas, Rect(0, 0, bmSmily.Width - 1, bmSmily.Height - 1)); // Der Hintergrund ist im Blitter gelandet, der Smily ist im Blitter gelandet, // das Bild ist aufgebaut und kann angezeigt werden. Dazu wird einfach der // Blitter in die Bitmap des Image kopiert. Image.Picture.Bitmap.Canvas.CopyRect(Rect(0, 0, bmBlitter.Width - 1, bmBlitter.Height - 1), bmBlitter.Canvas, Rect(0, 0, bmBlitter.Width - 1, bmBlitter.Height - 1)); // Abschließend muß dem Programm noch mitgeteilt werden, daß sich das // Image geändert hat. Image.Refresh; end; // Aufräumen der Resourcen nach Gebrauch nicht vergessen FreeAndNil(bmBackground); FreeAndNil(bmSmily); FreeAndNil(bmBlitter); end;
-
23.02.09 10:52 #9
- Registriert seit
- Mar 2004
- Beiträge
- 441
Wie ich mit Bildern generell umgehe, habe ich dir ja bereits geschrieben. Aber ich fand das, was du da machen willst, ganz witzig und habe mir deswegen jetzt mal was gebastelt (was es sicher schon zigtausendfach und viel performanter und eleganter als fertige Komponenten gibt): Eine eigene Sprite-Klasse.
Und da es auch um Sourcecodes geht, hier einfach mal die Sources sowohl des Demo-Programms als auch des Sprites.
Demoprogramm, Hauptformular:
Name: Main
KeyPreview: true
Darauf gehört ein TImage mit folgenden Eigenschaften platziert:
Name: Main
Align: alClient
Picture: (irgendein Bild laden. Ich habe ein Wallpaper geladen).
Ebenfalls auf das Formular gehört noch ein TButton mit folgenden Eigenschaften:
Name: btDemo
Caption: Demo starten
Wenn du es dir noch etwas einfacher machen willst, dann richtest du schon mal folgende Ereignismethoden ein:
Main.OnCreate
Main.OnClose
Main.OnKeyDown
Main.Demo.OnClick (die Buttonmethode)
Hier der Source der Main:
Code :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
unit Dialogs.Main; interface uses Classes, Controls, Dialogs, ExtCtrls, Forms, Graphics, Jpeg, Messages, StdCtrls, SysUtils, Variants, Windows, Data.Sprite; type TMain = class(TForm) btDemo: TButton; Image: TImage; procedure btDemoClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); private Sprite: TSprite; end; var Main: TMain; implementation {$R *.dfm} procedure TMain.FormCreate(Sender: TObject); begin // Damit die Animation etwas flackerfreier läuft self.DoubleBuffered := true; Sprite := TSprite.Create; end; procedure TMain.FormClose(Sender: TObject; var Action: TCloseAction); begin FreeAndNil(Sprite); end; procedure TMain.btDemoClick(Sender: TObject); begin Sprite.Load('sprite.bmp'); Sprite.Width := 32; Sprite.Height := 32; Sprite.Position.X := Round(ClientWidth / 2); Sprite.Position.Y := Round(ClientHeight / 2); // Das Sprite wird direkt auf dem Canvas der Form angezeigt Sprite.Canvas := self.Canvas; Sprite.SetSpeed(100); // Die einzelnen Frames des Sprites festlegen Sprite.SetFrame(0, 0, 0); Sprite.SetFrame(1, 32, 0); Sprite.SetFrame(2, 64, 0); Sprite.SetFrame(3, 96, 0); // Transparenz ist Magenta (so definiert durch 0/0 der Spritebitmap) Sprite.SetTransparency(Point(0, 0)); Sprite.Show; end; procedure TMain.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin // Q if Key = 81 then begin Sprite.Left(3); Key := 0; end; // W if Key = 87 then begin Sprite.Right(3); Key := 0; end; // A if Key = 65 then begin Sprite.Up(3); Key := 0; end; // Y if Key = 89 then begin Sprite.Down(3); Key := 0; end; // Space blendet das Sprite ein und aus. if Key = 32 then begin Key := 0; if Sprite.Visible = true then Sprite.Hide else Sprite.Show; end; end; end.
Und hier der Source der Spriteklasse ("Data.Sprite.pas"):
Code :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
{*----------------------------------------------------------------------------- Klasse zur Darstellung von Sprites -----------------------------------------------------------------------------*} unit Data.Sprite; interface uses Controls, ExtCtrls, Graphics, SysUtils, Types, Windows; type {*----------------------------------------------------------------------------- Klassendefinition eines Sprites -----------------------------------------------------------------------------*} TSprite = class(TObject) private m_Bitmap: Graphics.TBitmap; /// Bitmap mit allen Frames des Sprites m_Background: Graphics.TBitmap; /// Buffer für den Hintergrund des Sprites m_Mask: Graphics.TBitmap; /// Bildmaske für alle Frames zur transparenten Anzeige m_Blitter: Graphics.TBitmap; /// Buffer zum Zusammenbauen des Sprites m_Transparency: TColor; /// Transparenzfarbe des Sprites m_FramePos: array[0..15] of TPoint; /// Positionen der einzelnen Frames in der Spritebitmap m_FrameIndex: Byte; /// Index des aktuell angezeigten Frames m_FrameMax: Byte; /// Index des maximalen Animationsframes m_Timer: TTimer; /// Animationstimer m_Ascending: boolean; /// Animationsrichtungsflag (aufsteigend, absteigend) m_Visible: boolean; /// Sichtbarkeitsflag des Sprites m_Width: Byte; /// Breite des Sprites in Pixeln m_Height: Byte; /// Höhe des Sprites in Pixeln procedure ShowFrame; /// Anzeige eines einzelnen Frames procedure SpriteTimer(Sender: TObject); /// Ereignismethode für den Animationstimer procedure MoveBy(p_nX: Integer; p_nY: Integer); /// Bewegen des Sprites procedure SetWidth(p_nValue: Byte); /// Festlegen der Spritebreite procedure SetHeight(p_nValue: Byte); /// Festlegen der Spritehöhe public Position: TPoint; /// Position des Sprites auf dem Darstellungs-Canvas Canvas: TCanvas; /// Handle auf den Canvas, auf dem das Sprite dargestellt werden soll constructor Create; destructor Free; procedure Load(const p_strFileName: String); procedure SetFrame(const p_nIndex: Integer; const p_nX: Integer; const p_nY: Integer); procedure SetSpeed(const p_nSpeed: Integer); procedure SetTransparency(const p_Point: TPoint); procedure Show; procedure Hide; procedure Left(p_nValue: Integer); procedure Right(p_nValue: Integer); procedure Up(p_nValue: Integer); procedure Down(p_nValue: Integer); property Visible: boolean read m_Visible; /// Lesezugriff auf das Visibility-Flag property Width: Byte read m_Width write SetWidth; /// Angepasster Schreibzugriff auf die Breite property Height: Byte read m_Height write SetHeight;/// Angepasster Schreibzugriff auf die Höhe end; implementation {*----------------------------------------------------------------------------- Konstruktor -----------------------------------------------------------------------------*} constructor TSprite.Create; var i: Integer; begin // Erstellen des Timers für eine evtl. Spriteanimation m_Timer := TTimer.Create(nil); m_Timer.Enabled := false; m_Timer.OnTimer := self.SpriteTimer; // Zyklusreihenfolge für eine Animation m_Ascending := true; // Startindex des Frames für die Animation m_FrameIndex := 0; // Anzahl der Spriteanimationsschritte m_FrameMax := 0; // Erstellen der Spritebitmap m_Bitmap := Graphics.TBitmap.Create; m_Bitmap.Width := 0; m_Bitmap.Height := 0; // Erstellen der Maskenbitmap m_Mask := Graphics.TBitmap.Create; m_Mask.Width := 0; m_Mask.Height := 0; // Speicher für den Hintergrund, vor dem das Sprite dargestellt wird m_BackGround := Graphics.TBitmap.Create; m_BackGround.Width := 0; m_BackGround.Height := 0; // Speicher für den Aufbau des Sprites (v.a. für Transparaenz) m_Blitter := Graphics.TBitmap.Create; m_Blitter.Width := 0; m_Blitter.Height := 0; // Startposition des Sprites Position := Point(0, 0); // Extractionkoordinaten für die einzelnen Frames for i := 0 to 15 do m_FramePos[i] := Point(-1, -1); // Standardmässig ist das Sprite nicht sichtbar m_Visible := false; end; {*----------------------------------------------------------------------------- Destruktor Auflösen verwendeter Resourcen -----------------------------------------------------------------------------*} destructor TSprite.Free; begin // Deaktivieren und Löschen des Timers m_Timer.Enabled := false; FreeAndNil(m_Timer); // Löschen aller Bitmaps FreeAndNil(m_Bitmap); FreeAndNil(m_Mask); FreeAndNil(m_Background); FreeAndNil(m_Blitter); end; {*----------------------------------------------------------------------------- Laden des Sprites -----------------------------------------------------------------------------*} procedure TSprite.Load(const p_strFileName: String); begin if FileExists(p_strFileName) then m_Bitmap.LoadFromFile(p_strFileName); end; {*----------------------------------------------------------------------------- Wenn die Spritebreite festgelegt wird (Property), dann wird auch die Blitterbreite entsprechend angepasst. -----------------------------------------------------------------------------*} procedure TSprite.SetWidth(p_nValue: Byte); begin if (p_nValue > 0) then begin m_Width := p_nValue; m_Blitter.Width := p_nValue; end; end; {*----------------------------------------------------------------------------- Wenn die Spritehöhe festgelegt wird (Property), dann wird auch die Blitterhöhe entsprechend angepasst. -----------------------------------------------------------------------------*} procedure TSprite.SetHeight(p_nValue: Byte); begin if (p_nValue > 0) then begin m_Height := p_nValue; m_Blitter.Height := p_nValue; end; end; {*----------------------------------------------------------------------------- Festlegen der Framepositionen in der Spritebitmap -----------------------------------------------------------------------------*} procedure TSprite.SetFrame(const p_nIndex: Integer; const p_nX: Integer; const p_nY: Integer); var i: Integer; begin if (p_nIndex >= 0) or (p_nIndex <= 15) then // Die Frameposition wird festgelegt m_FramePos[p_nIndex] := Point(p_nX, p_nY); // Automatische Berechnung des Maximalframes m_FrameMax := 0; for i := 0 to 15 do if m_FramePos[i].X = -1 then break; m_FrameMax := i - 1; end; {*----------------------------------------------------------------------------- Animationsgeschiwndigkeit ändern -----------------------------------------------------------------------------*} procedure TSprite.SetSpeed(const p_nSpeed: Integer); begin m_Timer.Enabled := false; m_Timer.Interval := p_nSpeed; if m_Timer.Interval > 0 then m_Timer.Enabled := true; end; {*----------------------------------------------------------------------------- Transparenz einstellen -----------------------------------------------------------------------------*} procedure TSprite.SetTransparency(const p_Point: TPoint); var x, y: Integer; begin // Die Transparenzfarbe ist diejenige in der Spritebitmap, auf die mit der // Methode gezeigt wird. Alle entsprechend gefärbten Pixel der Spritebitmap // gelten daraufhin als transparent. m_Transparency := m_Bitmap.Canvas.Pixels[p_Point.X, p_Point.Y]; m_Mask.Width := m_Bitmap.Width; m_Mask.Height := m_Bitmap.Height; // Jedes reine Schwarz (RGB 0/0/0) wird durch ein "Fast-Schwarz" (RGB 0, 0, 1) // ersetzt und jedes Transparenzpixel durch ein reines Schwarz for y := 0 to m_Bitmap.Height do begin for x := 0 to m_Bitmap.Width do begin if m_Bitmap.Canvas.Pixels[x, y] = clBlack then m_Bitmap.Canvas.Pixels[x, y] := RGB(0, 0, 1); if m_Bitmap.Canvas.Pixels[x, y] = m_Transparency then m_Bitmap.Canvas.Pixels[x, y] := clBlack; end; end; // Jetzt wird die Maske aufgebaut: Jedes sichtbare Pixel wird schwarz, // jedes transparente Pixel wird Weiß. for y := 0 to m_Bitmap.Height do begin for x := 0 to m_Bitmap.Width do begin if m_Bitmap.Canvas.Pixels[x, y] <> clBlack then m_Mask.Canvas.Pixels[x, y] := clBlack else m_Mask.Canvas.Pixels[x, y] := clWhite; end; end; end; {*----------------------------------------------------------------------------- Spriteanimation über den Timer -----------------------------------------------------------------------------*} procedure TSprite.SpriteTimer(Sender: TObject); begin if m_FrameMax > 0 then begin // Aufsteigende Animationsfolge if m_Ascending then begin if m_FrameIndex < m_FrameMax then Inc(m_FrameIndex) else m_Ascending := false; end // Abteigende Animationsfolge else begin if m_FrameIndex > 0 then Dec(m_FrameIndex) else m_Ascending := true; end; end; ShowFrame; end; {*----------------------------------------------------------------------------- Anzeige eines einzelnen Spriteframes -----------------------------------------------------------------------------*} procedure TSprite.ShowFrame; var rtScreen: TRect; rtSprite: TRect; rtFrame: TRect; begin // Die Anzeige erfolgt nur, wenn ein Zielcanvas vorgegeben wurde if Canvas <> nil then begin // Rectangle für Position auf dem Zielcanvas rtScreen := Rect(Position.X, Position.Y, (Position.X + m_Width), (Position.Y + m_Height)); // Rectangle für einfache Spriteposition rtSprite := Rect(0, 0, Width, Height); // Rectangle für Frame innerhalb der Spriebitmap rtFrame := Rect(m_FramePos[m_FrameIndex].X , m_FramePos[m_FrameIndex].Y, m_FramePos[m_FrameIndex].X + m_Width, m_FramePos[m_FrameIndex].Y + m_Height); // Falls bis jetzt noch kein Hintergrund angelegt wurde, so ist jetzt der // letztmögliche Zeitpunkt dafür. Dies kann z.B. beim Bewegen des Sprites // geschehen, wenn es verschoben wird. if (m_Background.Width = 0) then begin m_Background.Width := m_Width; m_Background.Height := m_Height; m_Background.Canvas.CopyMode := cmSrcCopy; m_Background.Canvas.CopyRect(rtSprite, Canvas, rtScreen); end; // Das Sprite wird gelöscht, indem der Hintergrund wieder an die Position // eingeblendet wird, wo sich das Sprite befand. m_Blitter.Canvas.CopyMode := cmSrcCopy; m_Blitter.Canvas.CopyRect(rtSprite, m_Background.Canvas, rtSprite); // Wenn eine Maske existiert, dann soll ein transparentes Sprite angezeigt // werden. Dies geschieht durch eine Maske: // Das Sprite selbst ist entsprechend in seiner Bitmap bearbeitet worden. // Alle auszublendenden Pixel sind Schwarz gefärbt worden und die Maske // beinhaltet für alle nicht darzustellenden Pixel die Farbe Weiß. // Durch entsprechende booelsche Zeichenoperation (AND-Verknüpfung mit der // Maske) werden nur die nicht ausgeblendeten Pixel in den Blitter übertragen. if m_Mask.Width > 0 then begin m_Blitter.Canvas.CopyMode := cmSrcAnd; m_Blitter.Canvas.CopyRect(rtSprite, m_Mask.Canvas, rtFrame); m_Blitter.Canvas.CopyMode := cmSrcPaint; end else m_Blitter.Canvas.CopyMode := cmSrcCopy; // Die Zeichenoperationen (Transparenz oder nicht Transparanz, Maske oder nicht Maske) // sind bereits eingestellt, es muß nur noch der richtige Frame in den Blitter kopiert werden. m_Blitter.Canvas.CopyRect(rtSprite, m_Bitmap.Canvas, rtFrame); // Der Blitter enthält jetzt das Hintergrundbild und den richtigen Spriteframe, ggf. mit // entsprechender Transparenz. Also ab damit auf den Anzeige-Canvas. Canvas.CopyRect(rtScreen, m_Blitter.Canvas, rtSprite); end; end; {*----------------------------------------------------------------------------- Generelle Anzeige des Sprites -----------------------------------------------------------------------------*} procedure TSprite.Show; begin // Wie gehabt: Anzeige nur, wenn auch ein Anzeige-Canvas zugewiesen wurde if (Canvas <> nil) then begin // Falls der Hintergrund noch nicht gespeichert wurde (z.B. bei Erstanzeige des // Sprites), dann wird er gespeichert. if m_Background.Width = 0 then begin m_Background.Width := Width; m_Background.Height := Height; Canvas.CopyMode := cmSrcCopy; m_Background.Canvas.CopyRect(Rect(0, 0, Width, Height), Canvas, Rect(Position.X, Position.Y, (Position.X + Width), (Position.Y + Height))); end; // Anzeige des Sprites und aktivieren einer evtl. vorhandenen Animation ShowFrame; m_Visible := true; m_Timer.Enabled := true; end; end; {*----------------------------------------------------------------------------- Sprite verstecken -----------------------------------------------------------------------------*} procedure TSprite.Hide; begin // Das Verstecken klappt nur, wenn der Hintergrund gespeichert wurde if (m_Background.Width <> 0) and (Canvas <> nil) then begin // Zum Verstecken wird der Hintergrund wieder eingeblendet (ohne Sprite) // und die weitere Spriteanzeige unterdrückt. Canvas.CopyMode := cmSrcCopy; Canvas.CopyRect(Rect(Position.X, Position.Y, (Position.X + Width), (Position.Y + Height)), m_Background.Canvas, Rect(0, 0, Width, Height)); m_Background.Width := 0; // Unterdrücken der weiteren Spriteanzeige m_Timer.Enabled := false; m_Visible := false; end; end; {*----------------------------------------------------------------------------- Spritebewegung -----------------------------------------------------------------------------*} procedure TSprite.MoveBy(p_nX: Integer; p_nY: Integer); var ScreenPos: TRect; begin // Derzeitige Position auf dem Anzeige-Canvas. An dieser Position muß das Sprite // gelöscht (also der Hintergrund wieder eingeblendet) werden, ScreenPos := Rect(Position.X, Position.Y, (Position.X + Width), (Position.Y + Height)); if (m_Background.Width <> 0) then begin Canvas.CopyMode := cmSrcCopy; Canvas.CopyRect(ScreenPos, m_Background.Canvas, Rect(0, 0, Width, Height)); m_Background.Width := 0; end; // Danach kann das Sprite passend verschoben und neu angezeigt werden Position.X := Position.X + p_nX; Position.Y := Position.Y + p_nY; ShowFrame; end; {*----------------------------------------------------------------------------- Sprite nach links bewegen -----------------------------------------------------------------------------*} procedure TSprite.Left(p_nValue: Integer); begin MoveBy(-p_nValue, 0); end; {*----------------------------------------------------------------------------- Sprite nach rechts bewegen -----------------------------------------------------------------------------*} procedure TSprite.Right(p_nValue: Integer); begin MoveBy(p_nValue, 0); end; {*----------------------------------------------------------------------------- Sprite nach oben bewegen -----------------------------------------------------------------------------*} procedure TSprite.Up(p_nValue: Integer); begin MoveBy(0, -p_nValue); end; {*----------------------------------------------------------------------------- Sprite nach unten bewegen -----------------------------------------------------------------------------*} procedure TSprite.Down(p_nValue: Integer); begin MoveBy(0, p_nValue); end; end.
Im Anhang findest du noch die von mir verwendete sprite.bmp.
Viel Spaß beim Basteln und Ausprobieren.
-
irgendwie wird die Bitmap-Datei, die ich in das Sprite lade, nicht angezeigt. Stattdessen erscheint auf dem Formular der Abschnitt aus dem Fenster, was dahinter liegt (Buchstaben aus dem Quellcode-Fenster). Wie kann das sein?
-
23.02.09 14:01 #11
- Registriert seit
- Mar 2004
- Beiträge
- 441
Starte das Ganze mal außerhalb der IDE, ohne break points. Das von dir beschriebene Verhalten hatte ich teilweise auch, wenn ich zwischen IDE und Programm hin- und hergeschaltet habe. Liegt daran, daß die Formulardarstellung dann teilweise durch die IDE unterbrochen wird.
-
also SetFrame legt die einzelnen Bilder fest, die hintereinander angezeigt werden...ok.
Aber wofür sind die Prozeduren "MoveBy", "Left", "Right", "Up", "Down"? Es bewegt sich doch auch, wenn man Frames + Speed festlegt und dann Show aufruft.
-
23.02.09 19:09 #13
- Registriert seit
- Mar 2004
- Beiträge
- 441
Mit Speed legst du die Animationsgeschwindigkeit fest, also die Geschwindigkeit, mit der die einzelnen Frames des Sprites angezeigt werden.
MoveBy ist eine Prozedur in der ich sowohl horizontale als auch vertikale Bewegung zusammengefasst habe. Allerdings ist MoveBy von außen nicht sichtbar.
Das sind dann Left, Right, Up und Down. Und mit den Parametern gibst du an, wieviele Pixel das Sprite in die jeweilige Richtung bewegt werden soll.
In diesem Falle: Bewegung != Animation.
Falls du dir den Source wirklich angeschaut und versucht hast, ihn nachzuvollziehen, dann wird dir nicht entgangen sein, daß du das Sprite in der Demo mit den Tasten Q, W, A und Y steuern und mit Space an- und ausschalten kannst...
-
habe mich nun nochmal mit dem programm beschäftigt...ich habe den Smiley nun so als Bitmap eingebunden, wie wir dies am Anfang besprochen haben...Jetzt ist die Hintergrundfarbe des Rechtecks schwarz. Kann man die irgendwie ändern?
-
Mir ist da noch was aufgefallen. Ich erstelle also 3 Objekte vom Typ TSprite auf dem Canvas einer Image-Komponente. Die Positionen wähle ich zufällig aus, das führt jedoch dazu, dass sich die Sprites oft überlappen. Wie kann ich überprüfen, ob sich auf einem Pixel schon ein Sprite befindet?
Ähnliche Themen
-
Image als Button: Image einbinden geht nicht
Von YelloW22 im Forum CSSAntworten: 5Letzter Beitrag: 30.12.10, 14:06 -
document.getElementById(ID).style.background-image:url(Image) bewirkt nichts :(
Von Jabba-deHut im Forum Javascript & AjaxAntworten: 2Letzter Beitrag: 06.07.08, 01:12 -
image fade und image als link verwenden
Von phamez im Forum Javascript & AjaxAntworten: 5Letzter Beitrag: 08.04.07, 12:34 -
image/jpeg gegen image/pjpeg
Von achimj im Forum PHPAntworten: 2Letzter Beitrag: 04.01.05, 20:34 -
Image Ready, Eine Image Map einem Rollover Sate hinzufügen
Von gummy im Forum PhotoshopAntworten: 1Letzter Beitrag: 02.05.04, 11:59





Zitieren
Login





