PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Visual Basic 2010 - Bild zeichnen?


Geldmann3
2013-03-02, 18:21:24
Hallo,
bisher habe ich Bilder immer einfach mit Picture-boxen dargestellt. Doch wenn ich mit diesen ein kleines Spiel erstellen möchte, sind sie viel zu langsam.

Nun habe ich es ohne PictureBox so versucht:

Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click
Dim myBitmap As New Bitmap(My.Resources.Raumschiff)
Dim g As Graphics = Graphics.FromImage(myBitmap)
g.DrawImage(myBitmap, 200, 200, Me.Width, Me.Height)
End Sub

Es gibt keine Fehlermeldung wenn ich auf den Button klicke, dennoch erscheint auch kein Bild. Weiß jemand, was es damit auf sich hat?

Monger
2013-03-02, 18:35:17
Naja, du hast jetzt eine Grafikobjekt "g", das keinerlei Bezug zu irgendeinem Control auf deiner Oberfläche besitzt. Da wird natürlich nirgendwo hin gerendert.

Geldmann3
2013-03-02, 19:03:03
Warum, sollte DrawImage myBitmap nicht ab den Koordinaten 200/200 mit Me.Width (Also der Größe des Formulars zeichnen?)

PatkIllA
2013-03-02, 19:06:21
Warum, sollte DrawImage myBitmap nicht ab den Koordinaten 200/200 mit Me.Width (Also der Größe des Formulars zeichnen?)
Du malst jetzt das Bild auf sich selbst.
Du brauchst das Graphic vom Formular. Control.CreateGraphics

Besser wäre es die Control OnPaint Methode zu überschreiben und dort zu zeichnen. Das wird immer dann aufgerufen wenn nötig und erlaubt es auch kleinere Bereiche neu zu zeichnen.

Gast
2013-03-02, 19:09:08
für das was du machen solltest, könnte die writeablebitmap klasse aus wpf nützlich sein. da kann man nämlich auch einzelne bildelemente(px) oder bereiche ändern, ohne das gesamte bild immer neu rendern zu müssen

FlashBFE
2013-03-02, 19:12:46
Warum, sollte DrawImage myBitmap nicht ab den Koordinaten 200/200 mit Me.Width (Also der Größe des Formulars zeichnen?)

Nein, in Windows Forms musst du die Zeichenschleife beachten.

Wenn du in ein Formular zeichnen willst, dann benutze das paint-Ereignis des Forms und hole aus den Ereignisparametern das Graphicsobject und arbeite innerhalb dieses Eventhandlers damit. Wenn du mit dem Zeichnen eines Bildes fertig bist, dann rufe die Form.refresh-Methode auf, die dann indirekt das nächste paint-Ereignis auslöst.

Geldmann3
2013-03-02, 19:13:07
Me.CreateGraphics.DrawImage(My.Resources.Raumschiff, 0, 0)

Wooooow! Danke, so einfach kann es sein.

Besser wäre es die Control OnPaint Methode zu überschreiben und dort zu zeichnen. Das wird immer dann aufgerufen wenn nötig und erlaubt es auch kleinere Bereiche neu zu zeichnen.
Danke, werde mich mal informieren, klingt nützlich.

PatkIllA
2013-03-02, 19:16:10
Me.CreateGraphics.DrawImage(My.Resources.Raumschiff, 0, 0)

Wooooow! Danke, so einfach kann es sein.Das klappt aber wie gesagt mit dem Refresh usw nicht vernünftig. OnPaint und Control.Invalidate sind da schon eher das Mittel der Wahl. Ob das für ein Spiel ideal ist sei mal dahingestellt.

Wenn du auf dem Level zeichnest solltest du das auch alles ordentlich disposen.

für das was du machen solltest, könnte die writeablebitmap klasse aus wpf nützlich sein. da kann man nämlich auch einzelne bildelemente(px) oder bereiche ändern, ohne das gesamte bild immer neu rendern zu müssenEr ist aber jetzt in Forms. Außerdem hat man da auch erstmal nur die Speicheradresse. Da muss man sich dann noch was zum Zeichnen suchen.

Geldmann3
2013-03-02, 19:29:50
Wenn du in ein Formular zeichnen willst, dann benutze das paint-Ereignis des Forms und hole aus den Ereignisparametern das Graphicsobject und arbeite innerhalb dieses Eventhandlers damit. Wenn du mit dem Zeichnen eines Bildes fertig bist, dann rufe die Form.refresh-Methode auf, die dann indirekt das nächste paint-Ereignis auslöst.

Gibt es dazu Beispiele?

Wenn du auf dem Level zeichnest solltest du das auch alles ordentlich disposen.
Disposen höre ich zum ersten mal, ?entsorgen?
Also es wieder aus dem Speicher entfernen? Wie?

PatkIllA
2013-03-02, 19:32:19
Gibt es dazu Beispiele?
Gibt es direkt bei der Beschreibung der Methoden in der MSDN. Das ist die gleiche Gruppe von Methoden die ich auch schon meinte.

Geldmann3
2013-03-02, 19:40:58
Bin jetzt hier
http://msdn.microsoft.com/de-de/library/system.windows.forms.control.paint.aspx?cs-save-lang=1&cs-lang=vb#code-snippet-1

Auf der Seite gibt es einen Unterpunkt Syntax, unter dem für VB
'Declaration
Public Event Paint As PaintEventHandler
steht.

Bedeutet das, dass ich diese Deklaration dann auch nochmal in meinem Projekt brauche?
Denn wenn ich sie einfüge, gibt es eine Fehlermeldung, sie würde Konflikte verursachen. Ich denke mal nicht oder?

Ok, die Frage habe ich mir gerade selbst beantworten können.... NEIN
-------------------------------------------------------------------------------------------------------------------------------------------------
Das Beispiel aus der MSDN funktioniert zwar, aber irgendwie komm ich net klar, ich komm mir voll noobig vor...
Ok, bisher mit der PictureBox bewege ich mein Raumschiff so:
If rechts = True Then
PictureBox2.Location = New Point(PictureBox2.Location.X + breitenprozent, PictureBox2.Location.Y)
rechts = False
End If

If links = True Then
PictureBox2.Location = New Point(PictureBox2.Location.X - breitenprozent, PictureBox2.Location.Y)
links = False

End If
ich verschiebe also immer einfach die Location. (ich weiß, sicherlich extrem noobig)
(Ein Timertick und ein Tastendruck gemeinsam, setzen die entsprechenden Werte immer bis zu 25x in der Sekunde auf True. Wobei die Grafik dann so oft verschoben wird.

Das funktioniert solange gut, bis etwas mehr im Spiel los ist, dann gibt es "voll krasse Grafikfehler".
Scheinbar wird dann versucht es wiederzuzeichnen, obwohl der letzte Vorgang noch nicht abgeschlossen ist, dann gibt es Lücken in der Grafik und die Grafiken verschwinden teilweise.
(Warum eigentlich? Sollen heutige CPU's nicht so sau leistungsstark sein? :freak:)

Wie macht man es besser?

Edit:
Habs jetzt im Paint Event
Private Sub ktinvaders_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
Me.CreateGraphics.DrawImage(My.Resources.Raumschiff, PlayerX, PlayerY)
If rechts = True Then
PlayerX = PlayerX + 10
End If
End Sub
Doch dann wird das Bild immer wieder gezeichnet, wie entferne ich die vorherige Version wieder?

PatkIllA
2013-03-02, 20:37:22
Damit das ganze sauber wird müssen in der richtigen Reihenfolge die richtigen Bildbereiche gezeichnet und entsprechend zur Oberfläche gebracht werden. Das hat weniger mit der Geschwindigkeit der CPUs zu tun.

IMO ist der ganze Ansatz über WinForms für ein performantes Spiel ungeeignet. DirectX gibt es ja nicht umsonst.

Geldmann3
2013-03-02, 20:41:22
DirektX ist für mich jetzt wahrscheinlich der Overload schlechthin, soll ja nur ein kleines Game werden, in dem höchsten 10 kleine Grafiken durcheinander fliegen.

Mir ist hierbei aufgefallen

Private Sub ktinvaders_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
Me.CreateGraphics.DrawImage(My.Resources.Raumschiff, PlayerX, PlayerY)
If rechts = True Then
PlayerX = PlayerX + 10
End If
End Sub
Das das Raumschiff so auch erst reagiert, wenn ich die Taste länger gedrückt halte?? Außerdem werden immer wieder Kopien gezeichnet. Eigentlich logisch, weil ich immer wieder neu zeichne und das alte nicht entferne, aber wie auch?

PatkIllA
2013-03-02, 20:51:07
Graphics.Clear löscht das ganze. Da solltest du schon drüber gestolpert sein, wenn du DrawImage gefunden hast.
Außerdem solltest du nicht ein neues Graphics erzeugen (was du übrigens nicht wieder wegräumst) sondern das aus den PaintEventArgs nehmen.

Geldmann3
2013-03-02, 20:57:50
Wie?
Und beim Einfügen von Graphics.Clear() bekomme ich die Meldung
Der Verweis auf einen nicht freigegebenen Member erfordert einen Objektverweis.

FlashBFE
2013-03-02, 21:22:16
Also noch mal langsam:

Statt Me.Creategraphics, was jedes Mal ein neues graphics-Objekt erzeugt und so nur Probleme macht, nimmst du das hier:
e.graphics.drawimage

um alles zu löschen, entsprechend
e.graphics.clear(color)

Um das Zeichnen eines neuen Bilds auszulösen, benutzt du entweder am Ende der Ereignisprozedur oder durch einen Timer ausgelöst
Me.refresh

Normalerweise wird es so flimmern. Damit das nicht passiert, aktivierst du in den Eigenschaften der Form (entweder im Designer oder so:)
me.doublebuffered=true

PatkIllA
2013-03-02, 21:51:47
Jetzt musste ich doch auch mal ein Raumshiff über das Bild laufen lassen. Ich kann aber nur C#
Eigentlich hat FlashBFE aber schon alles gesagt.
using System;
using System.Drawing;
using System.Windows.Forms;

namespace PaintTest
{
public partial class Spaceship : Form
{
private Bitmap _Spaceship;
private PointF _Position;
private double _Direction;

public Spaceship()
{
InitializeComponent();
_Spaceship = new Bitmap("alienblaster.png");
DoubleBuffered = true;
}

protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_Position = new PointF(ClientSize.Width * 0.5f, ClientSize.Height * 0.5f);
}

protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
bool handled = true;
const double directionChange = (Math.PI / 180) * 5;
const float distance = 5;
switch (e.KeyCode)
{
case Keys.Right:
_Direction += directionChange;
break;
case Keys.Left:
_Direction -= directionChange;
break;
case Keys.Up:
_Position = Spaceship.GetPoint(_Position, distance, _Direction);
break;
case Keys.Space:
_Position = new PointF(ClientSize.Width * 0.5f, ClientSize.Height * 0.5f);
_Direction = 0;
break;
default:
handled = false;
break;
}
if (handled)
{
e.Handled = true;
Invalidate(true);
}
}

private static PointF GetPoint(PointF point, float distance, double angle)
{
return new PointF(
point.X + distance * (float)Math.Cos(angle),
point.Y + distance * (float)Math.Sin(angle));
}

protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);

e.Graphics.Clear(Color.White);

const float size = 64;

PointF[] destPoints = new PointF[]
{
Spaceship.GetPoint(_Position, size, _Direction + Math.PI *1.25),
Spaceship.GetPoint(_Position, size, _Direction + Math.PI *0.75),
Spaceship.GetPoint(_Position, size, _Direction + Math.PI *1.75),
};

e.Graphics.DrawImage(_Spaceship, destPoints);
}
}
}

Geldmann3
2013-03-02, 23:56:34
Danke

Hmm habe ein Problem.

Wenn ich das Raumschiff ganz einfach immer beim OnPaint Event zeichnen lasse funktioniert es, das Raumschiff wird an der gewünschten Stelle angezeigt.

Siehe Code:
Private Sub ktinvaders_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint

e.Graphics.Clear(Color.AliceBlue)
e.Graphics.DrawImage(My.Resources.Raumschiff, PlayerX, PlayerY)
PlayerX = PlayerX + 10
Me.DoubleBuffered = True
End Sub

Doch jetzt möchte ich es natürlich nur entsprechend neu Zeichnen, wenn es sich auch nach rechts oder links bewegen soll. Füge es es in diese If Verzweigung ein, erscheint das Raumschiff einfach nicht mehr, auch wenn die Bedingung wahr ist.

Private Sub ktinvaders_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint

If rechts = True Then
e.Graphics.Clear(Color.AliceBlue)
e.Graphics.DrawImage(My.Resources.Raumschiff, PlayerX, PlayerY)
PlayerX = PlayerX + 10
Me.DoubleBuffered = True
End If
End Sub

Jemand eine Idee?

Edit: Hab jetzt mal was Verrücktes versucht, hab das ganze Fenster meines Programmes gepackt und es vom Bildschirm gezogen und wieder zurückgeholt, danach war das Raumschiff verzogen. Daraus schließe ich, dass das OnPaint Event einfach nur aufgerufen wird, wenn es nötig ist.

Edit: Ok, hatte Me.refresh vergessen.


Edit: Mir ist aufgefallen, dass mein gezeichnetes Raumschiff viel langsamer reagiert als die PictureBox, auch wenn es im Gegensatz dazu korrekt gezeichnet wird. Ist das normal? Außerdem bewegt es sich immer gleich an ganzes Stück weiter, als ob das Paintevent immer gleich 40 mal ausgeführt werden würde.

Hier mal der gesamte Quelltext:
Imports System.Drawing.Drawing2D

Public Class ktinvaders
Public rechts As Boolean
Public links As Boolean
Public PlayerX As Integer
Public PlayerY As Integer
Private Sub ktinvaders_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

PlayerX = 100
PlayerY = 100
End Sub

Private Sub ktinvaders_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint
Me.DoubleBuffered = True
e.Graphics.Clear(Color.AliceBlue)
e.Graphics.DrawImage(My.Resources.Raumschiff, PlayerX, PlayerY)
If rechts = True Then
e.Graphics.Clear(Color.AliceBlue)
e.Graphics.DrawImage(My.Resources.Raumschiff, PlayerX, PlayerY)
PlayerX = PlayerX + 20
rechts = False
End If


If links = True Then
e.Graphics.Clear(Color.AliceBlue)
e.Graphics.DrawImage(My.Resources.Raumschiff, PlayerX, PlayerY)
PlayerX = PlayerX - 20
links = False
End If

End Sub

Private Sub A(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
If e.KeyCode = Keys.D Then
rechts = True
ElseIf e.KeyCode = Keys.A Then
links = True
End If

End Sub

Private Sub Timer_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer.Tick
Me.Refresh()
End Sub

End Class

PatkIllA
2013-03-03, 00:23:30
Das Paint wird alle naselang gerufen. Da darfst du deinen internen Zustand nicht verändern und es muss immer alles nötige zeichnen. Auf keinen Fall solltest du in der Methode Refresh oder Invalidate aufrufen.
Das DoubleBuffered setzt man auch einmal am Anfang und fummelt da nicht beim Zeichnen dran rum.

Geldmann3
2013-03-03, 00:41:03
Danke, für deine Hilfe noch so spät in der Nacht :)

Quelltext aktualisiert.

Setze Me.DoubleBuffered jetzt nur noch einmal im OnPaint Event auf True und Refreshe nur noch durch den Timer, kann man das so machen?

Das Raumschiff reagiert jetzt viel flüssiger und wird richtig gezeichnet.

->Woran liegt es eigentlich, dass die Geschwindigkeit bei weniger als ~25ms nicht mehr mit dem Timer skaliert? Habe ihn auf 5ms daher müsste das Raumschiff mit 100FPS über den Bildschirm schießen. An der unflüssigen Bewegung sieht man aber, dass es höchstens 30FPS sind.

Edit: Das Hintergrundbild, welches ich im Designer festgelegt habe wird durch diese Methode ja weggewischt. Wie komme ich jetzt wieder zu dem gewünschten Hintergrund?
Auch wenn ich es vor jedem Onpaint zeichne, wird es ja immer durch e.Graphics.Clear(Color.AliceBlue) ersetzt.

RattuS
2013-03-03, 04:21:41
Setze Me.DoubleBuffered jetzt nur noch einmal im OnPaint Event auf True und Refreshe nur noch durch den Timer, kann man das so machen?
DoubleBuffered ist eine einmalige Beschreibung, wie der Komponent gerendert werden soll, d.h. du sagst deinem Control genau einmal, dass es DoubleBuffered ist, und zwar kurz nachdem du es deinem Form hinzufügst (in deinem Falle wohl im Designer). Die Eigenschaft bei jedem Zeichenvorgang neu festzulegen, ist völlig unnötig.

->Woran liegt es eigentlich, dass die Geschwindigkeit bei weniger als ~25ms nicht mehr mit dem Timer skaliert?
Weil das zu schnell für deinen CPU bzw. für die WndProc, der nativen Nachrichtenverwaltung von Windows (Win32-API), ist. Jegliche Fensternachrichten werden bei Windows zunächst eingereiht und dann der Reihe nach abgearbeitet. Ist diese komplette Liste nicht binnen deines 25 ms Zeitrahmens abgearbeitet, verzögert sich die nächste Nachricht fortlaufend. Mit dieser Einschränkung musst du leben, wenn du auf Forms zeichnen möchtest. Daher wurde auch schon zurecht angemerkt, dass das keine solide Grundlage für ein Spiel ist.

Das Hintergrundbild, welches ich im Designer festgelegt habe wird durch diese Methode ja weggewischt. Wie komme ich jetzt wieder zu dem gewünschten Hintergrund?
Auch wenn ich es vor jedem Onpaint zeichne, wird es ja immer durch e.Graphics.Clear(Color.AliceBlue) ersetzt.
Du solltest Graphics.Clear() genau einmal zu Beginn deiner Zeichnung ausführen. Das sorgt dafür, dass der Buffer zu Beginn jedes Frames garantiert leer ist. Jetzt zeichnest du mit aufsteigendem z-Index (vom Hintergrund zum Vordergrund) deine Objekte, angefangen bei deinem Hintergrundbild.

mobius
2013-03-03, 12:10:27
Ich halte den Ansatz hier für wenig erfolgsversprechend. Gut zum verstehen der Basics, aber die Grenzen wirst du sehr bald bemerken. Bevor man sich mit DirectX auseinander setzt ist das XNA mit C# eine nette Anlaufstelle: http://blogs.msdn.com/b/twendel/archive/2010/04/21/xna-sample-moontaxi-teil-1.aspx
Bekommst du direkt eine Schleife in der das Spiel wiederholt wird, Schnittstelle zum Texturenladen etc. etc.

Geldmann3
2013-03-03, 12:12:58
Ich halte den Ansatz hier für wenig erfolgsversprechend.
Danke für den Link, werde mich bei Zeit mal einlesen.

Habe auch nicht vor, bei dieser Technik zu bleiben, nachdem ich meine ersten 2 Minigames so hinbekommen habe, werde ich sicher eine Stufe weiter gehen.
(Vielleicht auch früher)


Habe den Code jetzt geändert:
Public Class ktinvaders
Public rechts As Boolean
Public links As Boolean
Public PlayerX As Integer
Public PlayerY As Integer
Public höhenprozent As Integer
Public breitenprozent As Integer
Private Sub ktinvaders_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

PlayerX = 100
PlayerY = 100
Me.DoubleBuffered = True
End Sub

Private Sub ktinvaders_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint

höhenprozent = Me.Height / 50
breitenprozent = Me.Width / 50

e.Graphics.Clear(Color.AliceBlue)
e.Graphics.DrawImage(My.Resources.Matrix_tut_2, 0, 0)
e.Graphics.DrawImage(My.Resources.Raumschiff, PlayerX, PlayerY)
If rechts = True Then
PlayerX = PlayerX + breitenprozent
rechts = False
End If


If links = True Then
PlayerX = PlayerX - breitenprozent
links = False
End If


End Sub
Me.DoubleBuffered = True jetzt nur noch einmal, wenn das Form geladen wird. Mit Hintergrundbild fällt mir auf, dass das Raumschiff nun um etwa 1/4s verzögert reagiert und bei der Bewegung nur noch ca. 5FPS hat. Ist das normal?

Monger
2013-03-03, 13:53:58
Mit Hintergrundbild fällt mir auf, dass das Raumschiff nun um etwa 1/4s verzögert reagiert und bei der Bewegung nur noch ca. 5FPS hat. Ist das normal?
Naja, zumindest erwartungsgemäß.

Ein komplettes Hintergrundbild zu zeichnen ist - vor allem mit der relativ trägen 2D API von Windows Forms - relativ rechenintensiv. Ein Bild mit 1024*768 Pixeln und 24 Bit Farbtiefe hat immerhin schon rund 19 MBit, also rund 2MByte Informationen - die mit jedem Refresh gepaintet werden müssen. Da dein Schiffsposition nur durchs rendern verändert wird, bewegt es sich halt genau so schnell wie refreshed wird. Das wäre dann eine Übung für die Zukunft, die Gameloop von der Renderloop zu trennen...

Eine häufiger Ansatz, um eben das sehr aufwendige Neu zeichnen von eigentlich statischen Elementen zu vermeiden, ist dass du mehrere Zeichenebenen benutzst, und mit Transparenzkanälen arbeitest. Ergo: du legst z.B. eine PictureBox (oder irgendwas anderes was dir ein Graphics Objekt liefert) über dein Hintergrundbild, und gibst der PictureBox erstmal einen einfarbigen, transparenten Hintergrund. Auf dem zeichnest du dann dein Raumschiff.

Geldmann3
2013-03-03, 14:19:10
HAHAHAHAHA:rolleyes:

Irgendwie artet das in voll ineffizientes Gefummel aus. Vielleicht sollte ich es auf diese Art und Weise wirklich aufgeben und mich gleich mit XNA+C# auseinandersetzen. Sieht ja zumindest auf den ersten Blick nicht sehr schwer aus.
:D

FlashBFE
2013-03-03, 14:53:06
HAHAHAHAHA:rolleyes:

Irgendwie artet das in voll ineffizientes Gefummel aus. Vielleicht sollte ich es auf diese Art und Weise wirklich aufgeben und mich gleich mit XNA+C# auseinandersetzen. Sieht ja zumindest auf den ersten Blick nicht sehr schwer aus.
:D

Zuerst: XNA läuft genauso mit VB, also wenn du mit VB schon geübt bist, brauchst du nicht auf die Geschweifteklammersprache umsteigen. ;)

Zweitens: XNA ist einfach und gut, wird aber leider nicht mehr weiterentwickelt. Auf ein paar Einschränkungen wirst du da früher oder später treffen, je nachdem, wie weit du das treiben willst.

Geldmann3
2013-03-03, 15:36:04
Naja, hab ja auch C++ in der BS und sollte daher zumindest auch darin Grundkenntnisse haben.

ich will es erstmal soweit treiben, dass sich etwa 10 Grafiken flüssig auf dem Bildschirm bewegen, die ich mit meinem Raumschiff Space-Invader mäßig abschießen kann. :rolleyes:

Mal sehen wie weit ich komme.
Jetzt werden erstmal 1000 Visual Studio Komponenten installiert und geupdated..

Die Gegner und Schüsse müsste ich dann ja auch irgendwie instanzieren, doch dazu komme ich besser erst, wenn ich soweit bin. Ist immerhin derbst Neuland für mich. (Obwohl, was ich in den Beschreibungen sehe, verstehe ich zumindest theoretisch ziemlich gut)