Heim > Artikel > Backend-Entwicklung > Teilen von Grafik- und Textcode zum Schreiben von Puzzles in C# (Teil 2)
In diesem Artikel wird hauptsächlich der zweite Teil des Codes für das C#-Puzzlespiel vorgestellt. Interessierte Freunde können sich auf das
-Vorwort beziehen: In der C#-Implementierung von „Jigsaw Game“ (Teil 1) in C# Jigsaw Game Writing Code Programming, hochgeladen die Codes jedes Moduls. In diesem Artikel werden die Prinzipien im Detail analysiert, um es den Lesern zu erleichtern, sie zu verstehen Das Programm hat viele Fragen, wir können gerne darauf hinweisen und wir können gemeinsam lernen und wachsen!
Text:
Puzzle ist im Grunde ein sehr klassisches Spiel und wie es begann. Ende. Wie fangen wir also an, wenn wir ein Puzzle machen wollen? Die Antwort lautet: Beginnen Sie mit der Realität und beschreiben Sie die Anforderungen (versuchen Sie, sie als Dokumente zu beschreiben). Wenn wir umfassende Anforderungen haben, können wir zuverlässige Strategien bereitstellen, die im Code implementiert und schließlich zu einer Arbeit werden können!
(1) Anforderungen: (Diese Anforderung ist ziemlich schlampig geschrieben und auf Anfänger zugeschnitten. Sie basiert auf der Denkweise der gängigsten Menschen und dem Prozess der Teilnahme am Spiel )
1. Bild: Wenn wir Puzzles spielen, haben wir zumindest ein Bild
2. Ausschneiden: Das Puzzle ist kein Bild, wir müssen es ausschneiden ganzes Bild in N*N kleine Stücke Bild
3. Unterbrechen: Mische die Reihenfolge dieser N*N kleinen Bilder, aber stelle sicher, dass sie durch Gehen gemäß den Spielregeln wiederhergestellt werden kann
4. Beurteilung: Bewerten Sie das Rätsel als erfolgreich
5. Interaktion: Welche Interaktionsmethode verwenden wir? Hier wähle ich Mausklick
6. Zeigen Sie das vollständige Thumbnail des an Originalbild
Das Obige sind die Grundfunktionen, die folgenden sind erweiterte Funktionen
7. Notieren Sie die Anzahl der Schritte: Zeichnen Sie auf, wie viele Schritte zum Abschließen erforderlich sind
8. Bilder ändern: Nachdem wir lange mit einem Bild gespielt haben, können wir es ändern, haha
9. Schwierigkeitsgrad auswählen: zu einfach? will nicht! Nach 3*3 gibt es 5*5 und nach 5*5 gibt es 9*9. Mein Mitbewohner hat den höchsten Schwierigkeitsgrad 300 mit mehr als 0 Schritten herausgefordert
(2) Analyse:
Mit der Nachfrage können wir analysieren, wie wir sie realisieren können (die tatsächliche Nachfrage im Computer abbilden), einschließlich:
1. Entwicklungsplattform: Wählen Sie hier die Sprache C#
1). In welcher Struktur speichern wir es? Wenn wir auf die Anforderungen zurückblicken, werden wir feststellen, dass es einige Ressourcen gibt, die gespeichert werden müssen
Bild: Verwenden Sie das Bild Objekt , um
Einheit (eine Sammlung) zu speichern Anzahl der Teilbilder nach dem Ausschneiden des Originalbilds): seitdem Definieren Sie die -Struktur struct Knoten, der das Image-Objekt zum Speichern kleiner Bilder der Einheit und die in einer Ganzzahl gespeicherte Anzahl enthält (nach dem Ausschneiden jeweils (Eine kleine Einheit erhält eine Nummer, um leichter überprüfen zu können, ob das Spiel abgeschlossen ist.)
Jede Einheit (eine Sammlung von Unterbildern nach dem Ausschneiden des Originalbilds): Verwenden Sie zweidimensionale Arrays (z. B. Puzzle, Backgammon, Eliminierungsmusik, Lianliankan, Tetris und andere Ebenenpunkte). Matrix-Spiele) Sie können es zum Speichern verwenden, warum? Weil es ähnlich aussieht! : Gestaltung
Variableint Num-Speicherung
Mit der Speicherung können wir über die Aufteilung von Modulen nachdenken (Die korrekte logische Aufteilung wurde erweitert und kann auch die Kommunikation klarer machen) und erstellen Sie es und implementieren Sie die spezifischen Algorithmen, die in jedem Modul enthalten sind
Zunächst sind die Programmmodule in vier Module unterteilt:
Logischer Typ:
1. Puzzle-Klasse: wird zur Beschreibung von Rätseln verwendet 2. Konfigurationsklasse: Speicherkonfigurationsvariablen
Interaktiv:
3. Spielmenüfenster: Menüoptionen erstellen 4. Spiellauffenster: Die Hauptoberfläche des Spiels
1 . Über die Spielmenüs können Konfigurationen wie Schwierigkeitsgrad oder Grafik manipuliert werden.2. Das laufende Fenster kann auf die Spielkonfiguration zugreifen und diese abrufen sowie die entsprechenden Konstruktionspuzzleobjekte verwenden.
3. Der Benutzer interagiert über das laufende Fenster und veranlasst indirekt, dass das Puzzleobjekt die Bewegungsmethode aufruft und die Mustermethode erhält
Studenten, die den Code lesen, denken, dass der problematischste und unvernünftigste Teil darin besteht, dass die Aufzählungsart in der Rätselklasse geschrieben wird. Sie sollte in der Konfigurationsklasse geschrieben werden, oder sie sollte in a geschrieben werden separate Klasse. Leser Wir können es selbst ändern
public enum Diff //游戏难度 { simple,//简单 ordinary,//普通 difficulty//困难 }
Wir können uns die Konfigurationsklasse als Datenspeicher vorstellen, während die Puzzle-Klasse für die logische Verarbeitung verwendet wird , und das Menü und das Ausführungsfenster dienen der Präsentation. Ich gebe zu, dass dieses Design nicht sehr sinnvoll ist, aber wenn der Umfang des Problems nicht groß genug ist, führt eine übermäßige Berücksichtigung des Designs dazu, dass das Programm aufgebläht wird? Ich denke, es muss ein gewisses Maß geben, ich weiß nicht genau, was es ist, aber ich habe das Gefühl, dass es bei diesem Programm einfach ist, es umzusetzen, und dass die Besessenheit vom Design (Routinetyp) manchmal den Gewinn und den Verlust überwiegt. (Persönliche unreife Meinung)
(3) Code-Implementierung:
Beschreibung: Dieser Block konzentriert sich auf die Beschreibung der Puzzle-Klasse und der spezifischen Implementierung Entitätskommunikation der Spiellaufklasse:
Puzzles Konstruktionsmethode:
1 🎜>public Puzzle(Image Img,int Width, Diff GameDif)
// Bild des Puzzles, Breite (Erklärung: die Seitenlänge des Quadrats, die Einheit sind Pixel, die Name ist Ambiguity, sorry), der Schwierigkeitsgrad des Spiels Der Schwierigkeitsgrad des Spiels bestimmt den Grad Ihrer Teilung. Der Grad der Teilung bestimmt die Größe des von Ihnen gespeicherten Arrays Zeilen und 3 Spalten, normal entspricht 5 Zeilen und 5 Spalten. Der Schwierigkeitsgrad entspricht 9 Zeilen und 9 Spalten
switch(this._gameDif) { case Diff.simple: //简单则单元格数组保存为3*3的二维数组 this.N = 3; node=new Node[3,3]; break; case Diff.ordinary: //一般则为5*5 this.N = 5; node = new Node[5, 5]; break; case Diff.difficulty: //困难则为9*9 this.N = 9; node = new Node[9, 9]; break; }
Tatsächlich verwendet der Prozess der Zuweisung von Werten zum Einheitenarray eine zweischichtige
for-Schleife
//分割图片形成各单元保存在数组中 int Count = 0; for (int x = 0; x < this.N; x++) { for (int y = 0; y < this.N; y++) { node[x, y].Img = CaptureImage(this._img, this.Width / this.N, this.Width / this.N, x * (this.Width / this.N), y * (this.Width / this.N)); node[x, y].Num = Count; Count++; } }
Weisen Sie dann node[x, y].Img einen Wert zu Die Zuweisungsmethode besteht darin, mithilfe der C#-Bild--Klassenbibliothek eine Screenshot-Methode zu schreiben und mit dieser Methode das kleine Bild der entsprechenden Größe abzufangen, das der entsprechenden Position im Großen entspricht Bild und speichere es in node[x,y].Img;
Was ist width/N? Es ist die Seitenlänge dividiert durch die Anzahl der Zeilen, was das Intervall darstellt, und das Intervall ist die Seitenlänge jeder Einheit! Dann bedeutet die Startkoordinate (X, Y), dass meine Position nach ein paar Einheiten ist, das heißt: (x, y)
=(Einheit Seitenlänge *Die Anzahl der Einheiten vom Startpunkt auf der X-Achse, Seitenlänge der Einheit *Die Anzahl der Einheiten vom Startpunkt auf der Y-Achse); dann werden sie es natürlich verstehen;
public Image CaptureImage(Image fromImage, int width, int height
, int spaceX, int spaceY)Hauptlogik: Verwenden Sie die DrawImage-Methode:
Nach der Segmentierung müssen wir einen speziellen Prozess durchführen, weil wir das wissen ist immer eine Position, die weiß ist, oder? Wir verwenden standardmäßig die letzte Position, d . Um es auffälliger zu machen, habe ich bei anderen vorherigen Einheiten auch Ränder gezeichnet, diese waren jedoch weiß, auch um die Rätsel hinsichtlich ihres Zierwerts zu unterscheiden. Dieser Code wird nicht eingeführt.
//创建新图位图 Bitmap bitmap = new Bitmap(width, height); //创建作图区域 Graphics graphic = Graphics.FromImage(bitmap); //截取原图相应区域写入作图区 graphic.DrawImage(fromImage, 0, 0, new Rectangle(x, y, width, height), GraphicsUnit.Pixel); //从作图区生成新图 Image saveImage = Image.FromHbitmap(bitmap.GetHbitmap());
Tatsächlich können wir das zweidimensionale Array verschlüsseln Methoden, aber bitte beachten! Nicht jede Störung kann wiederhergestellt werden!
Wie kann das gemacht werden? Die Methode ist sehr einfach zu verstehen. Sie besteht darin, dass unser Computer die vollständigen und geordneten Einheiten in unregelmäßigen Abständen und in großer Zahl gemäß der in den Regeln angegebenen Gehmethode laufen lässt, bevor das Spiel beginnt! Mit anderen Worten, Sie können auf jeden Fall auf diesem Weg zurückkehren! Verstehen Sie es zuerst, die spezifische Methode der Störung wird später erklärt.
Bewegungsmethode (Move):Die Bewegung von Quadraten im Puzzlespiel ist eigentlich der Austausch zweier benachbarter Einheiten, und zwischen diesen beiden Einheiten muss es einen geben Es gibt eine weiße Einheit (d. h. die oben erwähnte Knoteneinheit [N-1, N-1], ihre Nummer ist N*N-1, es wird empfohlen, die Berechnung selbst durchzuführen)
So unser Urteil Bedingungen Ja, wenn Sie einen Block bewegen und in seinen vier Richtungen eine benachbarte weiße Einheit vorhanden ist, nach oben, unten, links und rechts, also Einheit Nr. N*N-1, wird diese ausgetauscht. Dies ist die Grundlogik, aber sie enthält nicht die Bedingung
Einschränkung. Wenn unser Array die Grenze erreicht, können wir nicht auf Daten außerhalb der Grenzen zugreifen, wenn die Einheit beispielsweise Knoten[0,0 ist ] können Sie nicht auf die Daten oben und rechts zugreifen, da Node[-1,0] Node[0,-1] außerhalb der Grenzen liegt und eine Ausnahme auftritt Die Verschiebung ist erfolgreich und WAHR wird zurückgegeben
Die Verschiebung ist fehlgeschlagen, Rückgabe FALSE
/// <summary> /// 移动坐标(x,y)拼图单元 /// </summary> /// <param name="x">拼图单元x坐标</param> /// <param name="y">拼图单元y坐标</param> public bool Move(int x,int y) { //MessageBox.Show(" " + node[2, 2].Num); if (x + 1 != N && node[x + 1, y].Num == N * N - 1) { Swap(new Point(x + 1, y), new Point(x, y)); return true; } if (y + 1 != N && node[x, y + 1].Num == N * N - 1) { Swap(new Point(x, y + 1), new Point(x, y)); return true; } if (x - 1 != -1 && node[x - 1, y].Num == N * N - 1) { Swap(new Point(x - 1, y), new Point(x, y)); return true; } if (y - 1 != -1 && node[x, y - 1].Num == N * N - 1) { Swap(new Point(x, y - 1), new Point(x, y)); return true; } return false; }
交换方法(Swap):
交换数组中两个元素的位置,该方法不应该被类外访问,顾设置为private私有权限
//交换两个单元格 private void Swap(Point a, Point b) { Node temp = new Node(); temp = this.node[a.X, a.Y]; this.node[a.X, a.Y] = this.node[b.X, b.Y]; this.node[b.X, b.Y] = temp; }
打乱方法:
前面提到,其实就是让电脑帮着乱走一通,说白了就是大量的调用Move(int X,int y)方法,也就是对空白位置的上下左右四个相邻的方块中随机抽取一个,并把它的坐标传递给Move使其进行移动,同样要进行越界考虑,这样的操作大量重复!代码自己看吧 ,利用随机数。
/// <summary> /// 打乱拼图 /// </summary> public void Upset() { int sum = 100000; if (this._gameDif == Diff.simple) sum = 10000; //if (this._gameDif == Diff.ordinary) sum = 100000; Random ran = new Random(); for (int i = 0, x = N - 1, y = N - 1; i < sum; i++) { long tick = DateTime.Now.Ticks; ran = new Random((int)(tick & 0xffffffffL) | (int)(tick >> 32)|ran.Next()); switch (ran.Next(0, 4)) { case 0: if (x + 1 != N) { Move(x + 1, y); x = x + 1; } break; case 1: if (y + 1 != N) { Move(x, y + 1); y = y + 1; } break; case 2: if (x - 1 != -1) { Move(x - 1, y); x = x - 1; } break; case 3: if (y - 1 != -1) { Move(x, y - 1); y = y - 1; } break; } } }
返回图片的方法:
当时怎么起了个这样的鬼名字。。。DisPlay。。。
这个方法与分割方法刚好相背,这个方法其实就是遍历数组,并将其进行组合,组合的方法很简单,就是将他们一个一个的按位置画在一张与原图相等大小的空白图纸上!最后提交图纸,也就是return一个Image;
public Image Display() { Bitmap bitmap = new Bitmap(this.Width, this.Width); //创建作图区域 Graphics newGra = Graphics.FromImage(bitmap); for (int x = 0; x < this.N; x++) for (int y = 0; y < this.N; y++) newGra.DrawImage(node[x, y].Img, new Point(x * this.Width / this.N, y * this.Width / this.N)); return bitmap; }
同样利用的是DrawImage方法,知道如何分割,这个应该很容易理解,自己算一算,在纸上比划比划就明白了;
判断方法:
该方法很容易理解,就是按序按序!遍历所有单元,如果他们的结果中有一个单元的编号
node[x, y].Num 不等于遍历的序号,那么说明,该单元不在原有位置上,即整个图片还没有完成,我们就可以直接返回假值false
如果所有遍历结果都正确,我们可认为,图片已复原,此时返回真值true
public bool Judge() { int count=0; for (int x = 0; x < this.N; x++) { for (int y = 0; y < this.N; y++) { if (this.node[x, y].Num != count) return false; count++; } } return true; }
游戏运行窗口:即游戏玩耍时用于交互的窗口
这里只讲一个方法:即当接受用户鼠标点击事件时我们应该怎么处理并作出什么样反应
其实说白了就这句难懂:
puzzle.Move(e.X / (puzzle.Width / puzzle.N), e.Y / (puzzle.Width / puzzle.N))
调用了移动方法,移动方块
横坐标为:e.X / (puzzle.Width / puzzle.N)
纵坐标为:e.Y / (puzzle.Width / puzzle.N)
我们编程中的整数除法和数学里的除法是不一样的!比如10/4数学上等于2余2或者2.5,计算机里直接就是等于2了,只取整数部分
行数=行坐标 / 方块边长
列数=列坐标 / 方块边长
我们看P1,P2这两点
P1:40/30*30=1
P2:50/30*30=1
我们会发现同在一个单元格中,无论点击哪个位置,通过这个算法都能转化为
同一个坐标。
(e.x,e.y)为鼠标点击事件点击坐标
private void pictureBox1_MouseClick(object sender, MouseEventArgs e) { if (puzzle.Move(e.X / (puzzle.Width / puzzle.N), e.Y / (puzzle.Width / puzzle.N))) { Num++; pictureBox1.Image = puzzle.Display(); if (puzzle.Judge()) { if (MessageBox.Show("恭喜过关", "是否重新玩一把", MessageBoxButtons.OKCancel) == DialogResult.OK) { Num = 0; puzzle.Upset(); pictureBox1.Image = puzzle.Display(); } else { Num = 0; closefather(); this.Close(); } } } NumLabel.Text = Num.ToString(); }
好,那么大体的逻辑,程序中最需要思考的算法已经讲完了,还有不太懂的地方,欢迎交流~么么哒~
加了点小功能 音乐历史成绩
Das obige ist der detaillierte Inhalt vonTeilen von Grafik- und Textcode zum Schreiben von Puzzles in C# (Teil 2). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!