Dies ist ein Thread, auf den aus der FAQ verwiesen wird. Bitte keine weitere Diskussion, sondern nur wichtige Ergänzungen und diese bitte knapp und präzise. Vielen Dank!
Hallo
Ich habe mal wieder ein Problem(chen).
Ich habe eine Form, in die ich ein Reihe von Bilder zeichen möchte (ein 50x50 feld - sprich 2500 Bilder und anschließend einen Kasten (250x250 Pixel), in dem jedes Pixel einzelnt gezeichnet wird.
Das ganze ist für ein Spiel. Die Bilder stellen die Spielfelder da und der kasten ist eine Art übersicht.
Mein Problem ist jetzt, dass das ganze ca 15 Sekunden dauert um das zu zeichnen und das ist eindeutig zu lange dafür, dass andere das auch spielen sollen.
Gibt es eine möglichkeit das ganze (schnell) vorher zu generieren und dann auf einen Schlag anzeigen zu lassen? Das ganze (anzeigen, ohne den Algorithmus) sollte unter 1/2 Sekunde dauern
Visit me @ www.beremote.net
naja, kannst du denn mal deine zeichenroutine posten? vlei kann man da ja was optimieren.
hast du dein zeichenalgorithmus selbst ausgedacht oder benutzt du algorithmen die du im netz gefunden hast?
du könntest ja den hintergrund auf ein bitmap zeichnen was d udann immer anzeigen läßt dann soltle das z.b. schon mal fixer geht
doublebuffering wär z.b. auch noch ein stichwort
Also ich hab ein 2D-Array fields[,]
Darin stehen die Feldinformationen (z.B. Wasser = 0, Land = 1 usw)
Um die Große Karte (siehe Anhang) zu erstellen benutze ich das hier:
//Felder füllen
for (int i = 0; i < 50; i++)
for (int j = 0; j < 50; j++)
{
if (fields[i + shiftleft, j + shiftup] == 0) gO.DrawImage(imageList1.Images[0], i * 16 + gridshiftx, j * 16 + gridshifty, 16, 16);//wasser
else if (fields[i + shiftleft, j + shiftup] == 1) gO.DrawImage(imageList1.Images[1], i * 16 + gridshiftx, j * 16 + gridshifty, 16, 16);//leeres, nicht bebaubares Land
else if (fields[i + shiftleft, j + shiftup] == 2) gO.DrawImage(imageList1.Images[2], i * 16 + gridshiftx, j * 16 + gridshifty, 16, 16);//wiese
else if (fields[i + shiftleft, j + shiftup] == 3) gO.DrawImage(imageList1.Images[3], i * 16 + gridshiftx, j * 16 + gridshifty, 16, 16);//moor
else if (fields[i + shiftleft, j + shiftup] == 4) gO.DrawImage(imageList1.Images[4], i * 16 + gridshiftx, j * 16 + gridshifty, 16, 16);//ackerland
else if (fields[i + shiftleft, j + shiftup] == 5) gO.DrawImage(imageList1.Images[5], i * 16 + gridshiftx, j * 16 + gridshifty, 16, 16);//wald
else if (fields[i + shiftleft, j + shiftup] == 6) gO.DrawImage(imageList1.Images[6], i * 16 + gridshiftx, j * 16 + gridshifty, 16, 16);//Gebirge
}
Um die Minimap anzuzeigen benutze ich das hier:
Pen myPen = new Pen(Color.Black);
//880,85
for (int i = 0; i < 250; i++)
for (int j = 0; j < 250; j++)
{
if (fields[i, j] == 0)
{ myPen.Color = Color.Blue; }
else if (fields[i, j] == 1)
{ myPen.Color = Color.Yellow; }
else if (fields[i, j] == 2)
{ myPen.Color = Color.Green; }
else if (fields[i, j] == 3)
{ myPen.Color = Color.Green; }
else if (fields[i, j] == 4)
{ myPen.Color = Color.Green; }
else if (fields[i, j] == 5)
{ myPen.Color = Color.Green; }
else if (fields[i, j] == 6)
{ myPen.Color = Color.Gray; }
gO.DrawRectangle(myPen, 880 + i, 85+j, 1, 1);
}
Visit me @ www.beremote.net
Btw.: Du musst deine Grafikobjekte disposen(Pen, Brush, ...) wenn du sie nicht mehr brauchst. Auch solltest du diese Objekte nicht immer neu anlegen sondern dir die Objekte irgendwo speichern, oder gleich Pens.(Black|Blue|...) verwenden.
Wenn ich jetzt aber das Spielfeld (das große) weiter nach rechts scrolle, dann muss ich nunmal alles neu machen und nicht nur einzelne Felder. Und das ist das, was so tierisch lange dauert und noch schlimmer dann das ganze in Kombi mit der Minikarte.
Visit me @ www.beremote.net
du mußt aber bei dem großen nur den sichtbaren bereich quasi zeichnen, nicht das gesammte feld...
deine minimap mußt du natürlich 1x initialisieren
nachtrag:
wies scheint machst du ja nen editor für ne map...
mal zurück zu meinr anfangsidee: deine gesammte große map kannst du ja in eine bitmap zeichnen, diese benutzt du dann als hintergrund und zeigst immer nur den ausschnitt an den du gerade brauchst
wenn du nun was neus auf die map zeichen nwillst übermalst du das bild an der stelle einfach
deine minimap erstellst du einmal komplett wenn du die große map gezeichnet hast und änderst sie ebenfalsl wenn du die große änderst
denk mal so könnte das funzen, oder?
das Problem ist nur, dass sich die Form ständig "aktualisiert" (oder was auch immer).
Jedenfalls ist danach immer alles komplett weg, sodass ich das ständig alles neuzeichnen muss
Visit me @ www.beremote.net
Ich glaube, in deinem Fall (Spiel) rentiert sich DirectDraw - das ist wesentlich schneller.
Original von Hunv
das Problem ist nur, dass sich die Form ständig "aktualisiert" (oder was auch immer).
Jedenfalls ist danach immer alles komplett weg, sodass ich das ständig alles neuzeichnen muss wenn alles immer weg is dann hast du noch ne nanderen fehler drin
meine obigen post hab ihc mal editiert, lies mal meinen vorschlag...
dann würde ic hdas so machen das ich im paintevent oder im onpaint immer den aktuellen ausschnitt der hintergrundkarte zeichnen lasse bzw die minimap
d.h. einmal ein teil eines bildes und einmal ein kleines bild
wenn du nun mit der maus was veränderst (wasser da wo vorher grün is) würde ich dsa große bild ander stelle ändern und dsa bild der minimap und ein repaint anfordern
Hallo zusammen
Original von winSharp93
Ich glaube, in deinem Fall (Spiel) rentiert sich DirectDraw - das ist wesentlich schneller.
DirectDraw sollte man nicht mehr benutzen, das ist veraltet.
@Hunv
Ich würde dir du DirectX raten, Direct3D kann man gut für zweidimensionale Darstellungen benutzen, das Buzzword dazu heisst: Direct2.5D 😉
Schau mal hier vorbei.
Oder allgemein, die Suchbegriffe benutzen: directx, direct3d, 2d, ...
Gruss Peter
--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011
@sheitman: Ich habe auch schon daran gemacht das so zu machen, aber dann wäre eine Map seeeehr groß (jetzt in der Entwicklungsphase haben wir die maps auf 250x250, aber später sollen die so groß wie möglich werden, mindestens 1000x1000 felder => heißt eine Bitmap von 17000x17000 Pixeln (16 pixel ein bild, 1 pixel der rand). Das wäre etwas zu sehr groß.
Direct Draw hatte ich schon gefunden - auch ein Tut, aber als ich angefangen hab, stand in den Tooltips, das man das nicht mehr benutzen sollte - hab dann mal n bisschen im Internet geguckt und wollte dann das mit Direct3D machen.
Zu Direct3D habe ich bis jetzt aber nur 3D-Tuts gefunden oder Tuts, die den Aspekt, dass man Grafiken anzeigen lassen will total ignorieren.
@Peter: Ich guck mal deine Links an.
Visit me @ www.beremote.net
Solche Spiele wie deines habe ich auch schon auf einem 300MHz Rechner gespielt. Und diese wurden auch nicht mit directX erstellt. *Zeichne so viel wie nötig und nicht mehr. *Deine Bilder sollten bereits die richtige Größe haben, damit nicht ständig skaliert werden muss. *Verwende einen anderen Graphics.DrawImage Overload. Da gibt es sehr große *Performanceunterschiede zwischen den einzelnen Versionen. *Cache so viel wie nur möglich: Die Minimap musst du nur einmal erstellen(Graphics.DrawLine ist vermutlich auch nicht die schnellste Methode zum erstellen...); Die Hauptkarte würde ich nicht zwischenspeichern, da bei großen Karten der Speicherverbrauch schnell steigt.
Hallo marsgk
Original von marsgk
Solche Spiele wie deines habe ich auch schon auf einem 300MHz Rechner gespielt. Und diese wurden auch nicht mit directX erstellt.
Ja, aber sicher nicht mit GDI oder GDI+ 😉
Und ausserdem ziemlich sicher relativ hardwarenah?
Mit C# ist die schnellste und vorallem bequemste Variante immer noch, eine Grafik API zu benutzen. Sei es OpenGL oder DirectX.
Managed DirectX ist IMO die komfortabelste Möglichkeit, um schnelle und anspruchsvolle Grafik auf den Schirm zu bringen.
Mit GDI+ z.B. hast du einen riesen und komplizierten Aufwand, um es einigermassen schnell hinzukriegen. Und wenn die Ansprüche dann mal steigen, hast du schon wieder ein Problem.
Gruss Peter
--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011
Ich würde auch liebend gerne Direct X benutzen, nur finde ich halt leider keine tutorials/hilfen oder ähnliches, die annährend auf das eingehen, was ich brauche. Die Tuts gehen an der 2D-Ebene fast immer so schnell wie möglich vorbei.
Visit me @ www.beremote.net
Ich habe es gerade ausprobiert:
Tilegröße: 16x16; den ganzen Fensterinhalt mit Bitmaps füllen @ 1200MHz Athlon mit 768 MB Ram .Net 2.0
=>1024x768 Pixel(3072 Bitmaps) ~65ms pro frame -> ~15 fps
=>800x600(1875 Bitmaps) ~35ms pro frame ->27 fps
=>640x480(1200 Bitmaps) ~20ms pro frame -> 50 fps
Dies allerdings ohne Optimierungen(immer gesamten Fenster neu zeichnen, ohne cachen).
Hallo marsgk
1024x768 Pixel ist meiner Meinung nach ein Minimum für sinnvolle Spiele im Strategie Bereich.
Wenn jetzt noch pixelgenaues Scrolling, mehrere Layer, Einheiten, Bäume, etc.. dazukommen, stösst man sehr schnell an die Grenzen mit GDI+.
Ganz zu schweigen von Partikeleffekten, hier muss man sogar mit DirectX auf die Performance schauen 🙂
Meiner Meinung nach ist GDI+ nett für ein kleines Spielchen, sobald aber ein wenig mehr Ansprüche fällig werden, siehts eng aus.
Ganz zu schweigen von dem grausligen Code, wenn man mit Optimierungen alles rausholen möchte, und trotzdem steht man wieder an.
@Hunv
Wenn du dann tiefer in die Materie eingehen willst, bzw. auch schon jetzt, ist Englisch praktisch Pflicht.
Zudem gibt es viele relevante Tutorials nicht in C#, sondern z.B. in Delphi, C++, etc...
Das Grundprinzip bleibt aber gleich.
Gruss Peter
--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011
Zeichne im Hintergrund einfach nur dann, wenn sich etwas ändert in zwei Bitmaps. In die eine die komplette Karte (also jeweils die Gelände-Bitmaps), in die andere einfach nur farbige Punkte für die Minimap.
Dazu bietet es sich an, die Datenstruktur komplett zu ändern.
public enum UndergroundTypes
{
Empty = 0,
Water = 1,
Meadow = 2,
Moor = 3,
Field = 4,
Forest = 5,
Mountain = 6,
}
public struct UndergroundGraphics
{
public Image MapImage;
public Pen MinimapPen;
}
// hier die Verknüpfung von GeländeTypen mit den Zeichenobjekten
public Distionary< UndergroundTypes, UndergroundGraphics> UndergroundData = ... ;
public class GameMaps
{
// der Faktor zwischen Spielkoordinaten und Bildschirmkoordinaten
public const int TileSize = 16; // Größe 16 * 16
// hier die Verknüpfung von Spielkoordinaten mit Geländetypen
private UndergroundTypes[,] Tiles;
// hier das Spielfeld
internal Bitmap Map;
// und die Minimap
internal Bitmap Minimap;
// Spielfeld-Graphics
private Graphics MapGraphics;
private Graphics MinimapGraphics;
// erzeugt die Karten mit der maximalen Größe x und y
public GameMaps( int x, int y)
{
Tiles = new UndergroundTypes[ x , y ];
Map = new Bitmap( x * TileSize, y * TileSize );
MapGraphics = Graphics.FromImage( Map );
Minimap = new Bitmap( x, y );
MinimapGraphics = Graphics.FromImage( Minimap );
}
public UndergroundTypes GetUnderground( int x, int y )
{
return Tiles[x,y];
}
public void SetUnderground( int x, int y, UndergroundTypes undrgrnd )
{
Tiles[x,y] = undrgrnd;
MapGraphics.DrawImageUnscaled( UndergroundData[undrgrnd].MapImage, x * TileSize, y * TileSize );
MinimapGraphics.DrawRectangle( UndergroundData[undrgrnd].MinimapPen, x, y, 1, 1 );
}
}
Jetzt wird in die Bitmaps nur gezeichnet, wenn sich etwas ändert. Im Paint brauchst du dann nur noch Map und Minimap mittels Graphics.DrawImage(Image,Rectangle,Rectangle,GraphicsUnit) darstellen.
@Peter: Der erste Link gefällt mir im ersten Moment schon super. Sieht bis jetzt genau nach dem aus, was ich brauche! Großes Danke!
@Borg: Wie ich schon erwähnte, würde ich im Hinblick auf die Zukunft des Spiels lieber DirectX benutzen, da es für eben soetwas gedacht ist. Es ist außerdem zu rechenaufwendig (soweit ich das jetzt überschauen kann) nach jeder Änderung eine neue Bitmap zu erzeugen und anzeigen zu lassen.
@all: Danke nochmal! Mal sehen, was ich jetzt so hinbekommen kann. Beim nächsten Problem oder der nächsten Frage melde ich mich wieder 😉
Visit me @ www.beremote.net
Die Karte würde ich nicht vorgenerieren. Bei einer (durchaus realistischen) Größe von 1000x1000 Tiles wobei jede Tile 16x16 Pixel hat würde die resultierende Bitmap ~ 1GB Speicher verbrauchen.
Btw.: Mit java 1.6 und 1024x768 komme ich auf ~30fps.
Hallo marsgk
Original von marsgk
Btw.: Mit java 1.6 und 1024x768 komme ich auf ~30fps.
Mit was genau?
Gruss Peter
--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011
PC Athlon 1200MHz mit 768MB RAM und Windows XP sp2
Fenster mit 20x20 Tiles füllen:
.Net 2.0
1280x1000 => ~80ms/frame; 12fps
1024x768 => ~50ms/frame; 20fps
800x600 => ~26ms/frame; 38fps
640x480 => ~16ms/frame; 62fps
java 1.6.0
1280x1000 => ~35ms/frame; 28fps
1024x768 => ~23ms/frame; 43fps
800x600 => ~14ms/frame; 70fps
640x480 => ~9ms/frame; 110fps