C# DirectX - Problem mit Listen

MikeLadurner

Cadet 1st Year
Registriert
Sep. 2011
Beiträge
14
Hallo Zusammen

Ich muss für die Schule ein Projekt programmieren. Ich habe mich für DirectX und C# entschieden, mit dem ich ein Tanks programmiere. Einige kennens unter dem Namen Cannons und so. Es geht darum einander abzuschiessen, in 2D.

Mein Problem ist der Rauch hinter der Kanonenkugel. Ich will das mit einer Liste machen, darin werden SmokeParticles gespeichert. Das SmokeParticle dings ist ein Struct, mit einem Vector2 Size und einem für die Position. Position muss nur gelesen werden nach dem erstellen. Die Size hingegen möchte ich bei jedem durchlauf erhöhen.

Bei einer Liste muss man ja immer das ganze objekt zuweisen. Beim erstellen dieses neuen SmokeParticle weise ich ihm gerade den betroffenen zu. Dann ändere ich die grösse und so.

Seht selbst:

Code:
public void ResizeSmoke()
        {
            for (int i = 0; i <= smokeList.Count; i++)
            {
                SmokeParticle temporarySmokeParticle = smokeList[i];
                temporarySmokeParticle.size.X += 0.1f;
                temporarySmokeParticle.size.Y += 0.1f;
                smokeList[i] = temporarySmokeParticle;

                if (smokeList[i].size.X >= 30)
                {
                    smokeList.RemoveAt(i);
                }
            }
        }

Dieser Code ist in der Klasse Smoke, die von der MainForm instanziert wird im Konstruktor.

Wenn ich denn alles Laufen lasse, dann kommt ein Weisses Fenster mit einem Roten Kreuz darin. Werdet ihr wohl auch schon gesehen haben. Wenn ich aber die Vier ersten zeilen in der For schleife auskommentiere geht es. Wie kann ich das anderst machen dass es funktioniert? Ich kann euch von mir aus auch das ganze projekt zuschicken, ist nicht gross. Oder weiteren Code posten...

Wäre toll wenn mal wer schauen könnte.
 
Hast du mal versucht "temporarySmokeParticle" vor der Schleife zu erstellen?
 
Ja, das hab ich auch schon. Der genau gleiche Effekt.
Es muss an der Zuweisung liegen. Oder allgemein mit Listen, denn egal was ich für eine Liste mach und wo, ich kann diese nicht verändern ohne dass dies passiert.
 
Zuletzt bearbeitet:
Dir ist bewußt, das du durch RemoveAt die Liste verkleinerst und dadurch der Count nicht mehr richtig ist und du Elemente überspringst?
 
Oh, nein. Das ist ein Fehler.
Aber daran kann es auch nicht liegen, da die liste leer ist. Danke aber schonmal.

PS: Noch ne Frage.
Ich hab n Array, mit Farben drin. Es soll eine Map 500x500 generiert werden. Nun muss ich irgendwie die textur erstellen. Hier der Code:

Code:
        private void CreateTerrain()
        {
            terrain = new int[500];

            for (int x = 0; x < 500; x++)
                terrain[x] = 500 / 2;
        }

        private void CreateTextureTerrain()
        {
            Color[] colors = new Color[500 * 500];

            for (int x = 0; x < 500; x++)
            {
                for (int y = 0; y < 500; y++)
                {
                    if (y > terrain[x])
                        colors[x + y * 500] = Color.SandyBrown;
                    else
                        colors[x + y * 500] = Color.Transparent;
                }
            }
            contentmap1 = new Texture(graphicDevice, 500, 500, 1, 0, Format.A8R8G8B8, Pool.Managed);
            terrain.SetValue(colors, 0);
            
        }

Folgender Fehler:
InvalidCastException - Das Objekt kann nicht in diesem Arraytyp gespeichert werden.
Bei der Zeile terrain.SetValue... Hab keine Ahnung ob das Format überhaupt stimmt. Welches müsste ich nehmen?

Weiss einer wie das geht?
 
problem wird sein, das du versuchst ein Color[] in ein int[] auf eine Position zuzuweisen.

generell mal einfach den Indexer verwenden als die SetValue Methode (typsicherheit!)
 
Hm, auf riemers.net oder so gibts eben ein Tutorial für XNA mit folgendem Code:

Code:
 private void GenerateTerrainContour()
 {
     terrainContour = new int[screenWidth];
 
     for (int x = 0; x < screenWidth; x++)
         terrainContour[x] = screenHeight / 2;
 }

 private void CreateForeground()
 {
     Color[] foregroundColors = new Color[screenWidth * screenHeight];
 
     for (int x = 0; x < screenWidth; x++)
     {
         for (int y = 0; y < screenHeight; y++)
         {
             if (y > terrainContour[x])
                 foregroundColors[x + y * screenWidth] = Color.Green;
             else                        
                 foregroundColors[x + y * screenWidth] = Color.Transparent;
         }
     }
    foregroundTexture = new Texture2D(device, screenWidth, screenHeight, false, SurfaceFormat.Color);
    foregroundTexture.SetData(foregroundColors);
 }

Beim XNA kann die Textur direkt das ColorArray laden, was bei DirectX nicht der Fall ist.
Kann mir jemand einen vernünftigen Lösungsansatz zeigen? Ich möchte nicht extra ein Bitmap generieren, da bei mir dann immer der Fehler mit dem Roten Kreuz kommt. Vieleicht wegen falschem BitmapFormat oder so.

Vielen Dank schonmal
 
Zuletzt bearbeitet:
Das Problem hat sich erledigt. Jedenfalls das erste.
So ein dummer Fehler sollte mir nicht mehr passieren...

Code:
for(int i = 0; i= < Blabla.Count; i ++)
musste das = weg, da sonst die Liste auch durchlaufen wird wenn sie nichts beinhaltet...

Aber hat niemand eine Lösung zum 2ten Problem?

Gruss Mike
 
Ähm! ich bin jetzt kein c# oder dx mensch aber:

if (smokeList.size.X >= 30)
{
smokeList.RemoveAt(i);
--i; // das nächste element rückt auf i wird aber trotzdem erhöht -> nächstes element wird übersprungen.
}

könnte mich jetzt irren unc c# macht das automatisch, glaube ich aber nicht.
 
kA, ob du's schon gelöst hast, aber das kann ja wohl nicht gehen....
Was machst du denn da? Du weist dem terrain deine colors zu...

Der Fehler ist ja offensichtlich...du kannst einem int-Array keinen Color-Array zuweisen

Riemer weist in seinem Tutorial der Textur den Color-Array zu (mit SetData)

lg
Bergi
 
Das Problem hat sich erledigt. Jedenfalls das erste.
So ein dummer Fehler sollte mir nicht mehr passieren...

Code:
for(int i = 0; i= < Blabla.Count; i ++)
musste das = weg, da sonst die Liste auch durchlaufen wird wenn sie nichts beinhaltet... Aber das einige übersprungen werden ist egal, denn es sind so viele dass es auf drei vier auch nicht ankommt.

Das mit dem Color Array hab ich auch geschnallt, aber gibt es keine Funktion so wie SetData einfach für DirectX ohne XNA? Einer hat was von LockRectangle() gesagt, aber das versteh ich nicht ganz.


Und ein Letztes (hoffentlich) Problem:
Wie kann ich im DirectX feststellen welche Backbuffer grössen das im Fullscreen modus möglich sind? Bei einigen Crashts ja, und ich möchte eine Liste machen wo man sieht welche das funktionieren.

Gruss Mike
 
Zuletzt bearbeitet:
Die möglichen Auflösungen findest du mit Hilfe der Device Caps. Bezüglich den Caps gibts im Riemer-Tutorial glaub ich eh ein Kapitel. Ich weiß es nicht genau aber irgendwo muss es da dann eben auch ein Property geben, welches dir die unterstützten Fullscreen-Auflösungen liefert.

Das mit LockRectangle geht so:


Code:
            m_cTexture = new D3D.Texture(p_cGraphicsDevice,
                                         100,
                                         100,
                                         1,
                                         D3D.Usage.None,
                                         D3D.Format.R5G6B5,
                                         D3D.Pool.Managed);

            int[,] numArray = (int[,])m_cTexture.LockRectangle(typeof(int),
                                                               0,
                                                               D3D.LockFlags.None,
                                                               new int[] { 100, 100 });

            numArray[0, 0] = Color.White.ToArgb();
            numArray[1, 0] = Color.Gainsboro.ToArgb();
            ...
            numArray[99, 99] = Color.Fuchsia.ToArgb();

            m_cTexture.UnlockRectangle(0);

Du bekommst den Farben-Array der Textur mit LockRectangle. Es gibt mehrere Überladungen.
Bei dieser hier wird zuerst der gewünschte Typ angegeben (hier wollen wir ein int), dann das Mipmap-Level (0 für die normale Textur), LockFlags benutze ich hier keine, und dann als letzten parameter gibt man noch irgend ein Array an, das die Dimensionen des gewünschten Arrays angibt. Hier wollen wir die Textur in einem 100x100-Array haben. Man könnte auch ein eindimensionales Array angeben.

Danach einfach die Farben zuweisen und zum Schluss wieder Unlocken.

lg
Bergi
 
Danke vielmal für die Antwort.
Das mit den Device Caps hab ich gefunden. Ist nicht sonderlich schwer. Zum LockRectangle hab ich noch einige Fragen. Ich habe versucht mit Try Catch einige bekannte Auflösungen durchzutesten und dann die Liste zu erstellen, was anscheinend eine zimlich dumme Idee war.

Was macht der UnlockRectangle befehl und warum gibts du eine 0 mit?
Wird beim Unlock der Array der Texture zugeordnet?
Geht das schneller als wenn ich ein Bitmap mache, mit SetPixel die Farben setze und dieses Speichere und mit dem TextureLoader lade? Ich denke schon oder?

Erkläre bitte ganz kurz mit deinen Worten bei welchem Befehl und wie das Array in die Textur kommt. Wäre hilfreich wenn ich alles verstehe was ich dem Lehrer abgebe :p

Danke nochmal
 
Zuletzt bearbeitet:
Für genaue Details einfach in der Dokumentation nachschauen. Jedenfalls lockt (also sperrt) der Lock-Befehl einen bestimmten Bereich des VRAMs der Graphikkarte, nämlich genau den Teil, den du als Array haben willst. Mit den LockFlags kannst du dann noch zusätzliche Einstellungen bezüglich dieser Sperre vornehmen (z.B. ob die Graphikkarte warten soll bis du den wieder unlockst oder weitermachen kann, wenn du den Array nicht veränderst).

Der Unlock-Befehl gibt den Speicher wieder frei (kommt auf die LockFlags drauf an) und die Graphikkarte kann diesen Bereich wieder verwenden.

Ich gebe bei Unlock 0 mit, weil dieser Parameter den Mipmap-Level angibt, den ich entsperren will. Da ich vorher beim Lock Level 0 gesperrt habe muss ich hier auch wieder Level 0 entsperren.

Eine Arrayvariable ist ja eigentlich ein Zeiger/Pointer/Referenz auf einen Speicherbereich. In diesem Fall veränderst du mehr oder weniger direkt den VRAM der Graphikkarte. Keine Ahnung, ich habs nocht nicht probiert, was passiert wenn du den Array nach dem Unlock nochmal veränderst, wahrscheinlich kommt dann eine Exception.

Ja, die Bitmap-Variante ist auf jedenfall langsamer^^
 
Zuletzt bearbeitet:
Danke vielmal, ich verstehs jetzt glaub ich. Danke Mr. C# Gott :-D
Werds gleich ausprobieren.
 
Hei Leute
Hab ein Problem... Schnall das mit den DeviceCaps doch nicht ganz.
Welche Eigenschaft behandelt nun das Bildschirmformat?

Gruss
 
Code:
//Alle Grafikkarten durchlaufen
foreach(AdapterInformation ai in Manager.Adapters)
{
    //Alle Fullscreen-Auflösungen der Grafikkarte durchlaufen
    foreach(DisplayMode dm in ai.SupportedDisplayModes)
    {
        //Die Properties von "dm" geben dir alle Infos, die du brauchst
    }
}


lg
Bergi
 
1. Danke vielmal. Ich habe eben aus den Device.DeviceCaps die MaxTexture Dings ausgelesen. Das war falsch.
Jetzt hab ich ein weiteres Problem:

Ich habe ne liste mit dem Typ Size. Nun werfe ich alle doppelten Einträge raus, damit ich nur noch jede Auflösung einmal habe. Kann ich die Size jetzt auch irgendwie sortieren? Wenn ich einfach die Funktion Sort() meiner Liste aufrufe, dann kommt ein Error: Fehler beim Vergleichen von zwei Elementen im Array.

Hat jemand eine Lösung? Muss ich da selber was basteln?

2. LockRectangle Error: Das Objekt enthält keine primitiven daten.
Woran liegt das? Ich lade die Textur mit dem TextureLoader, dann will ich von der Textur ein Array mit den Farben.

Code:
        private int[,] GetColorArrayOffTexture(Texture texture)
        {
            int[,] numArray = (int[,])texture.LockRectangle(typeof(Color), 0, LockFlags.None, new int[] { 100, 100 });
            return numArray;
        }




Gruss
 
Zuletzt bearbeitet:
MikeLadurner schrieb:
Hat jemand eine Lösung? Muss ich da selber was basteln?
Seltsam das bringt immer diese Exception ..

Egal, Lösung ist einfach:
Code:
List<Size> Sizes = new List<Size>();
Sizes.Add(new Size(640,480));
Sizes.Add(new Size(800, 600));
Sizes.Add(new Size(1600, 1200));
Sizes.Add(new Size());
Sizes.Add(new Size(1600, 1024));
Sizes.Add(new Size(800, 600));
Sizes.Sort(CompareSize);

for (int i = 0; i < Sizes.Count; i++) {
  Console.WriteLine("Width: " + Sizes[i].Width + " Height: " + Sizes[i].Height);
}

int CompareSize(Size first, Size second) {
  int c = first.Height.CompareTo(second.Height);
  if (c == 0)
    c = first.Width.CompareTo(second.Width);
  return c;
}

Have fun. :cool_alt:
 
Danke vielmal. Das werd ich gleich testen.

Jetzt muss ich nur noch das mit dem LockRectangle schaffen und dann funktioniert ein grossteil.
 
Zurück
Oben