Seite 1 von 1

[XNA] Kommunikation mit WindowsForm

Verfasst: 06.10.2010, 16:10
von Raven280438
Hi,

ich habe nach folgendem Tutorial eine XNA-Klasse mit einem WindowsForm verbunden:
http://www.xnamag.de/forum/viewtopic.php?p=8875

Meine Frage: wie kann ich am besten zwischen den beiden Klassen komunizieren? Ich möchte abhängig davon,
welche Methode ich in dem WindowsForm auswähle, etwas in das XNA-Element zeichnen.
Und Variablen übergeben usw.

Wie mach ich das am Besten?



Gruß

Re: [XNA] Kommunikation mit WindowsForm

Verfasst: 06.10.2010, 17:18
von Despotist
Am besten wäre es wohl du fügst in der Form-Klasse für die Steuerelemente Ereignisbehandler hinzu (z.B. ButtonPressed) und fügst da den Code ein der das Gerenderte (Game-Klasse) modifiziert.

Edit: Falls es dir um den Aufruf Klassenfremder Methoden an sich geht solltest du in jeder Klasse eine Referenz auf die andere speichern. Übergeben könntest du das im Constructor oder einer Init-Funktion. Also die Forms-Klasse bekommt eine Referenz auf die XNA-Klasse und kann dann deren Methoden aufrufen und deren Eigenschaften auslesen (sofern public).

Re: [XNA] Kommunikation mit WindowsForm

Verfasst: 06.10.2010, 17:21
von Raven280438
Hi,

Code: Alles auswählen

[...]und fügst da den Code ein der das gerenderte (Game-Klasse) modifiziert.
Wie genau meinst du das? Ich versteh das nicht richtig. Kannst du kurz ein Beispiel geben?


Gruß

Re: [XNA] Kommunikation mit WindowsForm

Verfasst: 06.10.2010, 17:38
von Despotist
Du hast in der Forms-Klasse einen Button und für den eine Ereignishandler-Methode für Pressed. In dieser Methode rufst du über die Erwähnt Referenz auf die Game-Klasse eine Funktion dieser auf. Zum Beispiel kannst du so die Position der Kamera ändern, Beleuchtung an/ausschalten, Modelle durchschalten usw.. Alles was die Game-Klasse an Methoden und Variablen anbietet. Genauso kannst du auch statt Methoden der Game-Klasse aufzurufen Variablen dieser direkt modifizieren sofern sie public sind. Also z.B. auch Positionen verändern.

Du kannst auch den umgekehrten Weg gehen und in der Game-Klasse den Zustand von Steuerelementen aus der Forms-Klasse abfragen. Aber ich weiß nicht wie das da mit der Update-Methode läuft wenn das ganze ans Form gekoppelt ist.

Falls dir das als Beschreibung nicht reicht poste mal deinen Code (Game+Form) damit man dir spezifischer helfen kann.

Re: [XNA] Kommunikation mit WindowsForm

Verfasst: 06.10.2010, 18:19
von Raven280438
Hi,

hier mal meine beiden Klassen:

game.cs

Code: Alles auswählen

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace WindowsGame1
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        private IntPtr drawSurface;

        public Game1(IntPtr drawSurface)
        {
            /*graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";*/
            Content.RootDirectory = "Content";
            graphics = new GraphicsDeviceManager(this);
            this.drawSurface = drawSurface;
            graphics.PreparingDeviceSettings += new EventHandler<PreparingDeviceSettingsEventArgs>(graphics_PreparingDeviceSettings);
            System.Windows.Forms.Control.FromHandle((this.Window.Handle)).VisibleChanged += new EventHandler(Game1_VisibleChanged); 
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here

            base.Draw(gameTime);
        }

        void graphics_PreparingDeviceSettings(object sender, PreparingDeviceSettingsEventArgs e)
        {
            e.GraphicsDeviceInformation.PresentationParameters.DeviceWindowHandle = drawSurface;
        }
        private void Game1_VisibleChanged(object sender, EventArgs e)
        {
            if (System.Windows.Forms.Control.FromHandle((this.Window.Handle)).Visible == true)
            {
                System.Windows.Forms.Control.FromHandle((this.Window.Handle)).Visible = false;
            }
        }
    }
}
und meine main.cs

Code: Alles auswählen

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsGame1
{
    public partial class Main : Form
    {
        public Main()
        {
            InitializeComponent();
        }

        public IntPtr getDrawSurface()
        {
            return this.pictureBox1.Handle;  //pictureBox1 muss natürlich der Name eurer PictureBox sein!
        }

        private void beendenToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
    }
}
Ist noch nicht viel drin, nur der Grundaufbau



Gruß

Re: [XNA] Kommunikation mit WindowsForm

Verfasst: 06.10.2010, 20:30
von Despotist
Oh nein, das war doch nicht ernst gemeint ;).

In der Form hälst du einen Zeiger auf die Game1 Klasse.
In die Form fügst du im Editor einen Button ein. Nennst den zb SetColor. Dann doppelclickst du darauf und es wird eine Methode für das Click_Event erzeugt. Diese füllst du dann mit Code so dass das ganze in der Main-Klasse etwa so aussieht.

Code: Alles auswählen

// Main-form
namespace WindowsGame1
{
public partial class Main : Form
{
Game1 gameref; // da wird eine Referenz auf Game gespeichert damit du in der Form darauf zugreifen kannst um Werte zu setzen und Funktionen aufzurufen
// ... das ganze andere Zeug
        private void SetColor_Click(object sender, EventArgs e)
        {
            gameref.SetBkgColor();// über die referenz rufst du eine eigene methode auf um die hintergrundfarbe zu ändern
        }
//...
[STAThread]
        static void Main(string[] args)
        {
            ControlForm form = new ControlForm();
            form.Show();           
            Game1 game = new Game1(form.getDrawSurface());
            gameref = game;// hier wird die referenz auf game gefüllt
            game.Run();       
        }
}
In der Game Klasse brauchst du entsprechend die SetBkgColor() Methode als public damit sie vom Form aufgerufen werden kann. Weiterhin eine Variable für die Backgroundcolor welche in der Draw-Methode zum clearen benutzt wird.

Code: Alles auswählen

// Game1
Color BkgColor;// wird zum clearen benutzt
protected override void Initialize()
{
BkgColor = Color.Black;// zum anfang wird schwarz verwendet

base.Initialize();
}

public void SetBkgColor()
{
BkgColor = Color.White;// nach Button drücken sollte der Hintergrund weiß sein (vorher schwarz)
}

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(BkgColor);// mit der farbe wird der hintergrund gecleared
base.Draw(gameTime);
}
Hab das nicht probiert weiß daher nicht ob es kompiliert und läuft. Aber so in etwa sollte das ganze aussehen.

Bis zu einem Editor ist es noch ein weiter Weg. Aber wenn du das Grundgerüst hast kannst du es Schritt für Schritt mit deiner geüwnschten Funktionalität erweitern. Umgekehrt geht es natürlich auch dass die Game-Klasse eine Referenz auf die Form hat und da die Werte in Steuerelementen abfragt. Musst dich halt entscheiden in welche Richtung die Kommunikation laufen soll.

Viel Erfolg!

Re: [XNA] Kommunikation mit WindowsForm

Verfasst: 06.10.2010, 20:36
von Raven280438
Hi,

super vielen Dank für die ausführliche Erklärung. Jetzt hab ich verstanden wie das funktioniert :)

In diesem Forum wird man auch als totaler Anfänger kompetent und schnell beraten. Weiter so.



Gruß

Re: [XNA] Kommunikation mit WindowsForm

Verfasst: 07.10.2010, 19:56
von Raven280438
Hi,

ich bins nochmal. Ich bin grad eben erst dazu gekommen, das oben geschriebene zu testen.
Ganz so wie es oben steht hat es nicht fukntioniert.
Ich musst das gameref im Konstruktor erst initialisieren.

Code: Alles auswählen

public Form1()
{
    InitializeComponent();
    gameref = new Game1(this.getDrawSurface());
}
Mein Problem was ich jetz habe:
Ich möchte, dass wenn ich das Form schließe, das komplette Programm beendet wird. Ich hab dazu einen EventHandler für FormClosing erstellt:

Code: Alles auswählen

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    gameref.Exit();
}
Das funktioniert aber nicht, das Programm wird nicht komplett beendent. Ich muss es immer mit dem TaskManager richtig beenden. Wo liegt der Fehler?


Gruß

Re: [XNA] Kommunikation mit WindowsForm

Verfasst: 07.10.2010, 23:09
von Despotist
Bin nicht sicher und kann nicht nachschauen aber müsstest du nicht neben dem Game.Exit auch noch form.close oder sowas aufrufen? Mach mal unter Gameref.Exit() noch this. und schau welche Funktionen aufgelistet werden udn schau dir die beschreibungen an. Close Exit oder sowas muss es sein.

Re: [XNA] Kommunikation mit WindowsForm

Verfasst: 08.10.2010, 08:43
von Raven280438
Hi,

ich habs schon mit this.Close() probiert, das geht auch nicht richtig.

Ich nutze jetzt Application.Exit(), aber ich hab irgendwo mal gelesen, dass man das nicht machen sollte.


Gruß

Re: [XNA] Kommunikation mit WindowsForm

Verfasst: 08.10.2010, 22:02
von Raven280438
Hi,

ich bins nochmal.
Ich hab das Programm jetzt dahingehend erweitert, dass man im WindowsForm eine Bilddatei auswählen kann, die ich dann dynamische in das Game-Form laden und anzeigen will. Dazu hab ich das Tutorial http://florianblock.blogspot.com/2008/0 ... ap-to.html als Grundlage genommen.

form1.cs

Code: Alles auswählen

Bitmap bitmap = new Bitmap(this.openFileDialog1.FileName);

this.textur_data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
int bufferSize = this.textur_data.Height * this.textur_data.Stride;
byte[] bytes = new byte[bufferSize];
System.Runtime.InteropServices.Marshal.Copy(this.textur_data.Scan0, bytes, 0, bytes.Length);

bitmap.UnlockBits(this.textur_data);

gameref.set_textur(bytes, bitmap.Width, bitmap.Height);
game.cs

Code: Alles auswählen

public void set_textur(Byte[] textur_data,int width, int height)
{
    customTexture = new Texture2D(this.GraphicsDevice, width, height, 0, TextureUsage.None, SurfaceFormat.Color);
    customTexture.SetData(textur_data);
}
Das gibt aber, sobald die Methode set_textur() aufrufe (wenn ich ein Bild im openFileDialog1 ausgewählt habe) einen Laufzeitfehler:
An unhandled exception of type 'System.ArgumentNullException' occurred in Microsoft.Xna.Framework.dll

Additional information: The GraphicsDevice must not be null when creating new resources.
Wie muss ich das this.GraphicsDevice in der game.cs setzen? Dazu hab ich noch nichts gefunden.


Gruß

Re: [XNA] Kommunikation mit WindowsForm

Verfasst: 09.10.2010, 07:18
von Despotist
Raven280438 hat geschrieben: Wie muss ich das this.GraphicsDevice in der game.cs setzen? Dazu hab ich noch nichts gefunden.
Normalerweise wird das Graphics Device von allein gesetzt denn du siehst ja was wenn du ein naktes Game-Programm startest.

Ich vermute du rufst deine Funktion auf bevor es initialisiert ist was im Constructor von Game geschieht (wo du dann auch Parameter wie Auflösung etv beeinflussen kannst). Versuch einfach mal deinen Aufruf in die Initialize-Methode von Game zu verschieben. Wenn ich mich richtig erinnere ist das Initialisieren erst am Ende des Game-Konstructors abgeschlossen und alle Sachen die das Graphics Device brauchen können daher erst in Initialize initialisiert werden.

Ich bin von C++ zu C# migriert und finde es toll da Exceptions viel aussagekräftiger sind und man die Ursache für Fehler viel schneller findet. Sie sind also dein Freund. Mit Hilfe der Position und der Beschreibung (und evtl der Hilfe zur Exception) solltest du es in den meisten Fällen hinbekommen. Gerade dass Sachen (zb Arrays) nicht initialisiert sind tritt (bei mir) relativ häufig auf.

Re: [XNA] Kommunikation mit WindowsForm

Verfasst: 09.10.2010, 12:03
von Raven280438
Hi,

das versteh ich nicht so ganz.
Die Methode set_textur() wird doch erst aufgerufen, wenn das Programm schon läuft.

Re: [XNA] Kommunikation mit WindowsForm

Verfasst: 10.10.2010, 20:21
von Despotist
Raven280438 hat geschrieben: Die Methode set_textur() wird doch erst aufgerufen, wenn das Programm schon läuft.
Wo genau? Etwas aktueller Code wäre vielleicht hilfreich.

Generell lässt sich der Fehler mit dem Text der Exception sehr genau lokalisieren. Du kannst auch mit dem Debugger durchsteppen und so den Inhalt von Variablen und den Aufruf von Funktionen verfolgen.

Re: [XNA] Kommunikation mit WindowsForm

Verfasst: 10.10.2010, 21:05
von Raven280438
Hi,

Code: Alles auswählen

public void set_textur(Byte[] textur_data,int width, int height)
{
    customTexture = new Texture2D(GraphicsDevice, width, height, 0, TextureUsage.None, SurfaceFormat.Color);
    customTexture.SetData(textur_data);
}
Das Programm lässt sich normal starten, wenn ich dann ein Bild auswähle kommt die oben genannte Fehlermeldung, dass GraphicsDevice null ist.


Gruß

Re: [XNA] Kommunikation mit WindowsForm

Verfasst: 10.10.2010, 21:20
von Despotist
Ich meinte ja mit Code eher wo die Funktion aufgerufen wird ;). Aber wenn du sagst dass es normal läuft heißt dass ja die Initialisierung klappt. Ich hatte halt vermutet dass du settexture aufrufst wenn das Device noch nicht zur Verfügung steht.

Hab jetzt keine IDE zur Hand aber ist deine GraphicsDevice Variable die du rein gibst wirklich das Device was im Constructor der Game-Klasse erzeugt wird? Ich meine das heißt Standardmäßig anders (Groß- und Kleinschreibung beachten). Du musst auch den Variablennamen angeben und nicht den Typ (muss in IDE schwarz aussehen und nicht türkis).
Das wären die Sachen die mir auf die Schnelle einfallen.

Falls das nichts hilft nutze den Debugger wie oben beschrieben. Damit wird vieles klarer.

Re: [XNA] Kommunikation mit WindowsForm

Verfasst: 11.10.2010, 12:20
von Raven280438
Hi,

ich hab in noch keinem Tutorial irgendwas von GraphicsDevice initialisieren gelesen, deshalb bin ich verwirrt. Ich dachte das macht XNA alleine ;)