Autor |
Beitrag |
dubstep
      
Beiträge: 72
Win XP, Win 7
C# (VS 2010)
|
Verfasst: So 11.07.10 21:09
Folgendes Problem:
Mein Ziel ist es, dass der User mithilfe einer Steuerung (z.B. Pfeiltasten am Computer) ein Objekt entlang einer Linie bewegen kann.
Ich habe mir diesbezüglich auch schon überlegt, wie man dies angehen könnte, bin mir aber nicht sicher, ob sich dies so umsetzen lässt, wie ich mir das vorstelle?
Die Umsetzung im Detail:
- Bild mit einer schwarzen Linie (die Linie teilt sich dann in zwei Linien auf) mit einem ganz normalen Zeichenprogrogramm erstellen
- Dieses Bild dann in "Form xyz" als BackgroundImage importieren
- Weiteres Bild als PictureBox in "Form xyz" importieren - Dies soll dann das Objekt darstellen, welches entlang dieser schwarzen Linie gesteuert werden kann
- Begrenze Bereiche/Flächen für das Objekt - erreicht wird dies durch Definition von "zugänglichen" und "gesperrten" Pixel in "Form xyz". Nur entlang dieser als "erlaubt" definieren Pixel darf sich das Objekt bewegen. Ich würde alle Pixel, auf dieser schwarzen Linie als "erlaubt" kennzeichnen.
Die Idee mit den Pixeln ist so ziemlich die Einzige, die mir derzeit als Lösungsansatz einfällt - das könnte aber sehr, sehr viel Arbeit werden - geht das auch einfacher?
Wie kann man das mit der Abzweigung (schwarze Linie teilt sich in zwei schwarze Linien auf) realisieren? Die Möglichkeit, dass an dieser Abzweigung eine MessageBox mit einem Text "Wollen Sie rechts oder links fahren?" erscheint, ist ein wenig mühsam.
In weiterer Folge wäre auch eine Art "Stop-Button" ganz nett - mit diesem wird das Objekt gestoppt.
Ich hoffe ich liege mit meinen Lösungsansätzen nicht komplett daneben. 
|
|
Christoph1972
      
Beiträge: 690
Erhaltene Danke: 16
VS2015 Pro / C# & VB.Net
|
Verfasst: Mo 12.07.10 07:32
Hallo,
ich denke die Linie solltest du mit GDI+ zeichnen und nicht mit einem Grafikprogramm. So würdest du schon mal die Koordinaten der Line kennen, die dann die Begrenzung darstellen und bei der Bewegung zu überwachen sind.
_________________ Gruß
Christoph
|
|
Yogu
      
Beiträge: 2598
Erhaltene Danke: 156
Ubuntu 13.04, Win 7
C# (VS 2013)
|
Verfasst: Mo 12.07.10 08:18
Hallo,
wie Christoph1972 schon gesagt hat, eine Vektorgrafik wäre einfacher zu verwenden. Ist das in der Aufgabenstellung erlaubt?
Wenn nicht, könntest du versuchen, die Bitmap zu vektorisieren. Das ist zwar eigentlich ein sehr komplexes Thema, aber wenn es nur um gerade Linien geht, könnte man es vielleicht vereinfachen. Du könntest die Bitmap in z. B. 4x4-große Bereiche aufteilen, und dann schauen, wie viele Punkte darin schwarz sind. So kannst du vielleicht herausfinden, ob in dem jeweiligen Bereich nichts, eine Linie oder ein Eckpunkt ist. Am Ende verbindest du einfach mal auf Glück alle Eckpunkte.
Grüße,
Yogu
|
|
dubstep 
      
Beiträge: 72
Win XP, Win 7
C# (VS 2010)
|
Verfasst: Mo 12.07.10 11:01
Danke! Das ist genau die Art von Vorschlag die ich gebraucht habe.
Ich hatte keine Ahnung, dass es soetwas wie GDI+ gibt - derzeit kann ich immerhin schon einmal einen Kreis implementieren.
Christoph1972 hat folgendes geschrieben : | Hallo,
So würdest du schon mal die Koordinaten der Line kennen, die dann die Begrenzung darstellen und bei der Bewegung zu überwachen sind. |
Das stimmt - nun sind die Koordinaten bekannt. Dank dieser nun gewonnenen Information könnte man doch der Picture Box sagen: "Wenn User klickt "Objekt nach rechts oben", dann bewege dich nach Pixel x/y - dazu bewege dich über Pixel x1/y1, x2/y2, ...
Das praktische ist, dass ich nicht vorhabe, abgerundete Linien oder gar Kreise darzustellen. Es soll lediglich horizontal und vertikale Linien geben, welche miteinander verbunden werden oder sich aufteilen ...
Yogu hat folgendes geschrieben : | Hallo,
Ist das in der Aufgabenstellung erlaubt? |
Es sind mir keine Beschränkungen vorgegeben.
|
|
dubstep 
      
Beiträge: 72
Win XP, Win 7
C# (VS 2010)
|
Verfasst: Di 13.07.10 01:33
Bitte um Entschuldigung für das Doppelpost!
Ich habe jetzt als Test eine Linie mit zwei 90°-Ecken erzeugt. Weiters habe ich ein Bild in eine erstellte PictureBox importiert - dieses Bild stellt mein Objekt dar. Zusätzlich habe ich zwei Buttons implementiert. Klickt man auf jeweils einen der zwei Buttons - so springt das Objekt von "Station1" ( = Linienanfang) zu "Station2" ( = Linienende).
Hier einmal der 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:
| protected override void OnPaint(System.Windows.Forms.PaintEventArgs pe) { base.OnPaint(pe); System.Drawing.Graphics g = pe.Graphics; System.Drawing.Pen p1 = new System.Drawing.Pen(System.Drawing.Color.Red);
System.Drawing.Point point1 = new System.Drawing.Point(); point1.X = 120; point1.Y = 100; System.Drawing.Point point2 = new System.Drawing.Point(); point2.X = 100; point2.Y = 100; System.Drawing.Point point3 = new System.Drawing.Point(); point3.X = 120; point3.Y = 120; System.Drawing.Point point4 = new System.Drawing.Point(); point4.X = 140; point4.Y = 120;
g.DrawLine(p1, point1, point2); g.DrawLine(p1, point1, point3); g.DrawLine(p1, point3, point4); }
private void buttonStation1Station2_Click(object sender, EventArgs e) { this.pictureBox1.Left = 140; this.pictureBox1.Top = 120; }
private void buttonStation2Station1_Click(object sender, EventArgs e) { this.pictureBox1.Left = 100; this.pictureBox1.Top = 100; } } } |
Ich habe zwar schon nach Lösungen gesucht und versucht selbständig darauf zu kommen - aber ich werde nicht fündig. Gibt es einen Weg, dass die PictureBox entlang dieser Linie "fährt" (statt bisher springt)? Weiters will ich mich mit ".Left" und ".Top" nicht wirklich zufrieden geben - gibt es da keine Möglichkeit einfach die x-/y-Koordinaten anzugeben?
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 13.07.10 06:15
dubstep hat folgendes geschrieben : | Gibt es einen Weg, dass die PictureBox entlang dieser Linie "fährt" (statt bisher springt)? |
Du kannst zum Beispiel einen Timer verwenden und dann nach den jeweils x Millisekunden die PictureBox um einen gewissen Wert verschieben.
dubstep hat folgendes geschrieben : | Weiters will ich mich mit ".Left" und ".Top" nicht wirklich zufrieden geben - gibt es da keine Möglichkeit einfach die x-/y-Koordinaten anzugeben? |
Direkt nicht, aber du kannst dir ja eine Methode schreiben SetPictureBoxPos, der du die PictureBox und die Koordinaten übergibst. Dann kannst du alles weitere dort berechnen.
Zudem kannst du die PictureBox selbst erweitern, evtl. per partial class, ich weiß nicht ob das bei der Klasse geht.
|
|
Yogu
      
Beiträge: 2598
Erhaltene Danke: 156
Ubuntu 13.04, Win 7
C# (VS 2013)
|
Verfasst: Di 13.07.10 18:26
dubstep hat folgendes geschrieben : | Gibt es einen Weg, dass die PictureBox entlang dieser Linie "fährt" (statt bisher springt)? |
Ein kleines Kochrezept:
- Ermittle die x- und y-Komponenten der Strecke von Punkt A zu Punkt B.
- Berechne die Länge der Strecke mit dem Pythagoras.
- Ermittle die Dauer der Animation, indem du die Länge aus Schritt 2 durch eine Geschwindigkeitskonstante teilst (herauskommen soll eine Anzahl von Zeitabschnitten, z.B. Fünfundzwanzigstelsekunden)
- Bewege das Objekt jeden Zeitabschnitt um die x- und y-Komponente geteilt durch die Anzahl der Zeitabschnitte aus Schritt 3 (ob mit einem Timer, einer Endlosschleife und Application.DoEvents(); oder einer Endlosschleife in einem zweiten Thread und Invoke, bleibt dir überlassen).
dubstep hat folgendes geschrieben : | Weiters will ich mich mit ".Left" und ".Top" nicht wirklich zufrieden geben - gibt es da keine Möglichkeit einfach die x-/y-Koordinaten anzugeben? |
Suchst du eine Möglichkeit, einer Komponente einen System.Drawing.Point zuzuweisen? Schau mal nach Location
Grüße,
Yogu
Für diesen Beitrag haben gedankt: dubstep
|
|
dubstep 
      
Beiträge: 72
Win XP, Win 7
C# (VS 2010)
|
Verfasst: Di 13.07.10 20:51
Hmm, gar nicht so einfach.
Habe jetzt einmal einen Timer initialisiert. Die Schwierigkeit dabei ist, dies jetzt mit der PictureBox zu verknüpfen. So stelle ich mir das vor: Eine Schleife, die so lange läuft, bis die vorgegebene x-/y-Koordinate von der PictureBox erreicht wurde. In der Schleife selbst steht drinnen: Bewege die Picture Box nach/bei jedem Intervall um n-Pixel. Ich habe zwar schon den Beitrag von "Timer Class" auf "msdn.." gelesen (inkl. Bsp.), aber so wirklich viel mehr Information, als ich ohnehin schon in meinem Code habe, steht hier nicht drinnen. Funktioniert dies so? > Man hat einen Timer mit gewissen Parametern (z.B. Interval, ...), die der User vorgibt. Der User kann dann selbst beliebig viele Events hinzufügen - ein Event stellt dabei eine Art Bedingung dar - also z.B. nach n-Sekunden rufe Event-xyz ab. Im Event steht dann, welcher Vorgang stattfinden soll?
C#-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| private void Form1_Load(object sender, EventArgs e) { System.Windows.Forms.Timer formsTimer = new System.Windows.Forms.Timer(); formsTimer.Interval = 1000; formsTimer.Enabled = true; formsTimer.Tick += new EventHandler(TimerEvent); }
private void TimerEvent(Object myObject, EventArgs myEventArgs) { this.Text = DateTime.Now.ToString(); } |
Yogu hat folgendes geschrieben : |
Suchst du eine Möglichkeit, einer Komponente einen System.Drawing.Point zuzuweisen? Schau mal nach [cs]Location |
Danke  - das funktioniert nun:
this.pictureBox1.Location = new System.Drawing.Point(100, 100);
Zuletzt bearbeitet von dubstep am Di 13.07.10 21:00, insgesamt 3-mal bearbeitet
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 13.07.10 20:53
dubstep hat folgendes geschrieben : | Im Event steht dann, welcher Vorgang stattfinden soll? |
Das programmierst du dort, ja. In Abhängigkeit von der verstrichenen Zeit kannst du ausrechnen wo sich deine Box dann befindet.
Eine Schleife ist das dann nicht, sondern einfach alle x Millisekunden ein ausgeführter Code.
|
|
dubstep 
      
Beiträge: 72
Win XP, Win 7
C# (VS 2010)
|
Verfasst: Di 13.07.10 21:11
Wäre dies eine Möglichkeit? Man sagt: Rufe nach 3000ms EventHandler "TimerEvent" ab.
C#-Quelltext 1: 2: 3: 4:
| if (formsTimer.Interval.Equals(3000)) { formsTimer.Tick += new EventHandler(TimerEvent); } |
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 13.07.10 21:15
Nein, der EventHandler wird automatisch im eingestellten Intervall immer wieder aufgerufen.
Also zum Beispiel alle 3000 Millisekunden.
|
|
dubstep 
      
Beiträge: 72
Win XP, Win 7
C# (VS 2010)
|
Verfasst: Di 13.07.10 21:23
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 13.07.10 21:28
|
|
dubstep 
      
Beiträge: 72
Win XP, Win 7
C# (VS 2010)
|
Verfasst: Di 13.07.10 21:48
jaenicke hat folgendes geschrieben : | Nein, dann führst du die Aktion eben nur bei jedem fünften Timeraufruf, eben wenn 5 Sekunden vorbei sind, durch.  |
Stimmt - das klingt logisch.
Gibt es einen Grund, warum sich die PictureBox nicht bewegt? Eigentlich sollte sie nach 5000ms zu 300/100 "springen" und ab dann dort verharren.
Gibt es eine Möglichkeit, dieses Event dann zu stoppen, weil das Ziel - die PictureBox nach 300/100 zu bewegen ist ja dann (wenn es funktioniert) erreicht - somit ist jeder weitere Aufruf dieses Events unnötig?!
C#-Quelltext 1: 2: 3: 4:
| private void TimerEvent(Object myObject, EventArgs myEventArgs) { this.pictureBox1.Location = new System.Drawing.Point(300, 100); } |
Edit: Wobei ich gerade sehe, dass ich hier einen Denkfehler habe. Es ergibt ja gar keinen Sinn, dass ich fixe Koordinatenpunkte angebe - dazu würde ich ja keinen Timer benötigen. Sinnvoller wäre es, im Event zu definieren, um wie viele Pixel sich die PictureBox bewegen soll - User "Yogu" hat bereits beschrieben wie hier vorgegangen werden muss.
|
|
jaenicke
      
Beiträge: 19314
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 13.07.10 21:55
Ein kleines Beispiel im Anhang, das macht es vielleicht klarer.
Die Berechnung sieht bei dir dann natürlich anders aus, aber da bewegt sich schonmal was. 
Einloggen, um Attachments anzusehen!
|
|
Yogu
      
Beiträge: 2598
Erhaltene Danke: 156
Ubuntu 13.04, Win 7
C# (VS 2013)
|
Verfasst: Di 13.07.10 22:05
|
|
dubstep 
      
Beiträge: 72
Win XP, Win 7
C# (VS 2010)
|
Verfasst: Di 13.07.10 22:12
|
|
dubstep 
      
Beiträge: 72
Win XP, Win 7
C# (VS 2010)
|
Verfasst: Di 13.07.10 22:40
Yogu hat folgendes geschrieben : |
Hast du das soweit verstanden? Schon umgesetzt? |
Ich habe nun z.B. zwei Punkte:
Punkt 1: x1=100/y1=100
Punkt 2: x2=200/y2=200
Satz Pythagoras:
Wurzel((x1-x2)² + (y1-y2)²)
Länge zwischen Punkt 1 und Punkt 2 = 141,4 Pixel
Geschwindigkeitskonstante: 20,20 (frei gewählter Wert)
Dauer der Animation = Länge/Geschwindigkeitskonstante = 7,00 Sekunden
7,00 Sekunden um 141,4 Pixel zu überwinden
Event wird alle 1000ms aufgerufen >> 1000ms = 1 Sekunde
141,4 Pixel / 7,00 Sekunden = 20,2 Pixel/Sekunde = Überwindung von 20,2 Pixel pro Event
Ich hoffe das passt nun - aber was fange ich jetzt mit der gewonnenen Information an?
______________________________
C#-Quelltext 1: 2: 3: 4: 5: 6: 7:
| private void timer1_Tick(Object myObject, EventArgs myEventArgs) { if (pictureBox1.Location == new System.Drawing.Point(100, 81)) { pictureBox1.Hide(); } else {pictureBox1.Left = pictureBox1.Left + 1;} } |
Die PictureBox bewegt sich zwar jetzt brav entlang der mit GDI+ gezeichneten Linie, aber wie bringe ich sie an einem bestimmten Koordinatenpunkt wieder zum stoppen? Da ich keinen Befehl zum stoppen gefunden habe, habe ich es mit "hide" versucht - aber auch hiervon lässt sich die PictureBox nicht beeindrucken. Danach habe ich es noch mit diesem Befehl probiert, aber auch dies war leider erfolglos:
C#-Quelltext 1: 2: 3: 4: 5: 6: 7:
| private void timer1_Tick(Object myObject, EventArgs myEventArgs) { if (pictureBox1.Location == new System.Drawing.Point(100, 81)) { pictureBox1.Left = pictureBox1.Left + 0;} else {pictureBox1.Left = pictureBox1.Left + 1;} } |
Obwohl er prinzipiell über die if-else-Anweisung geht, sonst würde er sich ja nicht andauernd für die else-Anweisung entscheiden. Setzt man "pictureBox1.Left = pictureBox1.Left + 0" bei else ein, dann bewegt sich die PictureBox erst gar nicht, deswegen habe ich dies statt dem Stop-Befehl bei der if-Anweisung versucht.
Edit: Mit "(pictureBox1.Location == new System.Drawing.Point(100, 84))" funktioniert es jetzt! Liegt vielleicht daran, weil sich die PictureBox entlang der Pixel (y kleiner als 81) entlang der x-Achse bewegt.
|
|
Yogu
      
Beiträge: 2598
Erhaltene Danke: 156
Ubuntu 13.04, Win 7
C# (VS 2013)
|
Verfasst: Mi 14.07.10 16:13
dubstep hat folgendes geschrieben : | 141,4 Pixel / 7,00 Sekunden = 20,2 Pixel/Sekunde = Überwindung von 20,2 Pixel pro Event |
Stimmt zwar auch, ist aber nicht, das was du brauchst. Schau mal, was ich geschrieben habe:
Yogu hat folgendes geschrieben : | 4. Bewege das Objekt jeden Zeitabschnitt um die x- und y-Komponente geteilt durch die Anzahl der Zeitabschnitte aus Schritt 3.[/list] |
Du musst nicht die Strecke von 141,4 Pixel durch die Zeit teilen, sondern (x1-x2) und (y1-y2). In jedem Intervall addierst du nun zur aktuellen Position new Point((x1-x2) / 7, (y1-y2) / 7)) (die 7 muss natürlich durch eine Variable ersetzt werden.
Das Problem bei dieser Variante ist, dass das Objekt nicht unbedingt genau dort ankommt, wo es laden soll, weil ja jedes Mal gerundet werden muss. Abhilfe verschaffen könnten zwei float-Variablen, die aktuelle Position als Kommazahl speichern, damit nichts verloren geht.
dubstep hat folgendes geschrieben : | Die PictureBox bewegt sich zwar jetzt brav entlang der mit GDI+ gezeichneten Linie, aber wie bringe ich sie an einem bestimmten Koordinatenpunkt wieder zum stoppen? |
Du zählst einfach bei jedem Event-Handler eine Variable noch, und wenn die die von dir ermittelte Dauer der Animation erreicht hat, schaltest du den Timer einfach per timer.Enabled = false; aus.
|
|
Kha
      
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: Mi 14.07.10 20:59
Wenn man in Vektoren fit ist (was sich bei Grafikprogrammierung eindeutig anbietet), lässt sich Yogus Vorschlag auch so zusammenfassen:
Soll sich ein Objekt geradlinig von a nach b mit der konstanten Geschwindigkeit v bewegen, gilt für seine Position
C#-Quelltext 1:
| p(t) = a + vt/|b-a|*(b-a) |
_________________ >λ=
|
|
|