CocosCreator Typescript macht Tetris-Spiel

CocosCreator Typescript macht Tetris-Spiel

1. Einleitung

Ich habe vor Kurzem angefangen, Cocos zu lernen. Nachdem ich die Typescript-Syntax gelernt hatte, schaute ich mir die offizielle Dokumentation von Cocos an. Nachdem ich ein paar Tage damit herumgebastelt hatte, schrieb ich eine sehr einfache Schlange, die nicht einmal eine anständige Kollisionserkennung hatte. Ich fand es langweilig, also ließ ich es für eine Weile liegen. In den letzten Wochen habe ich Cocos erneut aufgegriffen und bin auf die Idee gekommen, Tetris umzusetzen. Zuerst dachte ich daran, online nach Informationen zu suchen, aber ich fand nur sehr wenige Artikel über die Entwicklung von Tetris für Cocos (es könnte auch sein, dass ich nicht richtig gesucht habe). Noch frustrierender war, dass die wenigen geteilten Artikel, die ich fand, nur wenige Codekommentare enthielten. Es könnte auch sein, dass mein Verständnis nicht gut genug war. Später verbrachte ich mehrere Tage damit, konnte es aber immer noch nicht vollständig verstehen. Also beschloss ich, es selbst zu schreiben, und nach zwei Wochen war ich endlich fertig.

Am Ende des Artikels werde ich die gesamte Cocos-Projektdatei zu Ihrer Information anhängen. Der Code ist nicht gut geschrieben, also geben Sie mir bitte Ihren Rat.

2. Mehrere wichtige Probleme, die gelöst werden müssen

1. Wie lagern wir die Blöcke im Spielbereich?

Da Tetris ein Pixelspiel ist, können wir jeden Block als Pixel betrachten, sodass der gesamte Spielbereich eine Ansammlung von Pixeln ist. In Kombination mit Cocos definieren wir jeden Block als cc.Node-Typ, sodass unser Spielbereich ein zweidimensionales Array vom Typ cc.Node verwenden kann, um die Blöcke zu speichern, was für wichtige Operationen wie Drehen, Verschieben, Stapeln und Löschen praktisch ist. Hier verwende ich ein zweidimensionales 20*10-Array.

//Das Raster des gesamten Spielbereichs wird in einer zweidimensionalen Array-Box gespeichert: cc.Node[][] = [];
// Initialisiere das zweidimensionale Array der Box. [0][0] dieses Arrays befindet sich in der unteren linken Ecke des Spielbereichs. InitBox() {
        für (sei i = 0; i < 20; i++) {
            diese.box[i] = [];
            für (sei j = 0; j < 10; j++) {
                diese.box[i][j] = null;
            }
        }
        //Verschiedene Blocksätze generieren this.buildBlock();
    }

2. Aufbau der einzelnen Blocksatztypen

Wie wir alle wissen, gibt es bei Tetris sieben Blocktypen, und zwar: umgekehrter Z-Typ, L-Typ, umgekehrter L-Typ, Z-Typ, Streifentyp, T-Typ und Quadrat.

Wir können feststellen, dass jeder Blocksatz aus vier kleinen Blöcken besteht. Wir können diese Funktion verwenden, um eine einheitliche Konstruktionsmethode zu erstellen.

Zur Vereinfachung der späteren Verwendung habe ich zunächst für jeden kleinen Block ein Prefab und für einen leeren Knoten ein Prefab definiert. Der von diesem Prefab generierte Knoten wird zum Installieren der später erstellten Blockknoten verwendet. Strukturell handelt es sich also um eine Vater-Sohn-Beziehung.

    //Quadratischer Unterblock @property(cc.Prefab)
    block_0: cc.Prefab = null;
    //Z-förmiger Unterblock @property(cc.Prefab)
    block_1: cc.Prefab = null;
    //Linker L-förmiger Unterblock @property(cc.Prefab)
    block_2: cc.Prefab = null;
    //Rechter L-förmiger Unterblock @property(cc.Prefab)
    block_3: cc.Prefab = null;
    //Anti-Z-Unterblock @property(cc.Prefab)
    block_4: cc.Prefab = null;
    //Langer Streifen-Unterblock @property(cc.Prefab)
    block_5: cc.Prefab = null;
    //T-förmiger Unterblock @property(cc.Prefab)
    block_6: cc.Prefab = null;
    //Das Zentrum der Blocksammlung @property(cc.Prefab)
    aktuellesBlockCentre = null;
    //Aktueller BlockcurrentBlock: cc.Node = null; //Spezifische Implementierung von currentBlockCentrecurrentBlockPart01: cc.Node = null; //Spezifische Implementierung von vier UnterblöckencurrentBlockPart02: cc.Node = null;
    aktuellerBlockTeil03: cc.Node = null;
    aktuellerBlockTeil04: cc.Node = null;

Bezüglich der zufälligen Generierung von Farbe und Art der Blöcke habe ich einfach das integrierte Math.random() gewählt.

    buildBlock() {
        this.rand = Math.floor(7 * Math.random()); //Wählen Sie zum Erstellen zufällig einen von sieben aus this.chooseColor(this.rand);
        dies.wählenTyp(dieses.rand);
    }

Im nächsten Schritt werden Farbe und Typ des Bausteinsatzes basierend auf dem Eingabeparameter „rand“ ausgewählt. Um die Konstruktion genauer zu beschreiben, müssen Sie den Mittelpunkt dieses Blocksatzes auswählen. Am besten wählen Sie den Mittelpunkt eines Unterblocks und legen die Position auf (0, 0) fest. Auf diese Weise lässt sich die nachfolgende Drehung besonders bequem durchführen. Nach Auswahl des Mittelpunkts werden die Positionen anderer untergeordneter Blöcke entsprechend diesem Mittelpunkt festgelegt. Die Position der untergeordneten Knoten in Cocos ist relativ zum übergeordneten Knoten. Wenn die Position eines untergeordneten Knotens auf (0, 0) festgelegt ist, befindet sich die Position des untergeordneten Knotens am Mittelpunkt des übergeordneten Knotens.

Darüber hinaus beträgt die vorgefertigte Größe jedes Unterblocks 60 x 60, was bedeutet, dass der Abstand zwischen jedem Raster im Spielbereich 60 beträgt.

Dieser Codeabschnitt ist ziemlich lang, deshalb werde ich nicht im Detail darauf eingehen.

    //Wähle die Farbe des Blocksatzes chooseColor(rand) {
        …
        //Z-förmige Blockfarbe if (rand == 1) {
            this.currentBlockPart01 = cc.instantiate(this.block_1);
            this.currentBlockPart02 = cc.instantiate(this.block_1);
            this.currentBlockPart03 = cc.instantiate(this.block_1);
            this.currentBlockPart04 = cc.instantiate(this.block_1);
            dieser.aktuellerBlock = cc.instantiate(dieser.aktuellerBlockCentre);
            dies.node.addChild(dieser.aktuellerBlock);
            this.currentBlock.setPosition(30, 510); //Legt die Position des aktuell generierten Blocksatzes über dem Spielbereich fest, um ihn für den nachfolgenden Abwurf vorzubereiten}
        //Die Farbe des linken L-förmigen Quadrats, wenn (rand == 2)
        …
    }
    //Form auswählen chooseType(rand) {
        …
        //Erstelle einen Zickzack if (rand == 1) {
            //Z-förmig links this.currentBlockPart01.setPosition(-60, 0);
            this.currentBlockPart01Pos = cc.v2(18, 4); //Initialisiere die Position des aktuellen Blocks relativ zu currentBlock
            //In der Z-Form this.currentBlockPart02.setPosition(0, 0);
            this.currentBlockPart02Pos = cc.v2(18, 5);
            //Z-förmig this.currentBlockPart03.setPosition(0, -60);
            this.currentBlockPart03Pos = cc.v2(17, 5);
            //Z-förmig rechts this.currentBlockPart04.setPosition(60, -60);
            this.currentBlockPart04Pos = cc.v2(17, 6);
        }
        //Linken L-Typ erstellen, wenn (rand == 2)
        …
    }

3. So kombinieren Sie den erstellten Blocksatz mit dem zweidimensionalen Knoten-Array

Der obige Code enthält die folgende Variable: currentBlockPart0XPos, die die spezifische Position jedes Unterblocks currentBlockPart0X des aktuell bedienbaren Blocksatzes currentBlock im zweidimensionalen Array des Boxknotens definiert. Diese vier Variablen sind sehr nützlich. Nachdem der aktuell bedienbare Block verschoben wurde, können die Positionsinformationen im zweidimensionalen Array des Boxknotens gespeichert werden.

//Die aktuelle Unterblockposition currentBlockPart01Pos: cc.Vec2 = null;
    currentBlockPart02Pos: cc.Vec2 = null;
    currentBlockPart03Pos: cc.Vec2 = null;
    currentBlockPart04Pos: cc.Vec2 = null;

Danach können wir jedes Mal, wenn sich der Satz der bedienbaren Blöcke ändert, die folgenden beiden Methoden aufrufen, um die Position des bedienbaren Blocksatzes im Box-Array zu aktualisieren.

    //Positionsinformationen des aktuellen Operationsblocksatzes lesen checkCurrentBlockPos() {
        diese.box[diese.currentBlockPart01Pos.x][diese.currentBlockPart01Pos.y] = dieses.currentBlockPart01;
        diese.box[diese.currentBlockPart02Pos.x][diese.currentBlockPart02Pos.y] = dieses.currentBlockPart02;
        diese.box[diese.currentBlockPart03Pos.x][diese.currentBlockPart03Pos.y] = dieses.currentBlockPart03;
        diese.box[dieses.currentBlockPart04Pos.x][dieses.currentBlockPart04Pos.y] = dieses.currentBlockPart04;
    }
    // Den aktuellen Operationsblock löschen Positionsinformationen der vorherigen Position festlegen deleteCurrentBlockPos() {
        diese.box[diese.currentBlockPart01Pos.x][diese.currentBlockPart01Pos.y] = null;
        diese.box[diese.currentBlockPart02Pos.x][diese.currentBlockPart02Pos.y] = null;
        diese.box[diese.currentBlockPart03Pos.x][diese.currentBlockPart03Pos.y] = null;
        diese.box[dieses.currentBlockPart04Pos.x][dieses.currentBlockPart04Pos.y] = null;
    }

4. Verschieben und drehen Sie den Blocksatz

Bezüglich der Bewegung folgt es der Funktionsweise der meisten Tetris-Spiele: Linke Taste bewegt nach links, rechte Taste bewegt nach rechts, Aufwärtstaste dreht, Abwärtstaste bewegt nach unten und es erfolgt ein automatisches Fallen.

    //Automatisch löschen autoDown() {
        dieser.Zeitplan(() => {
            //Weiter fallen, bis die untere Grenze erreicht ist if (this.isClashBottom()) {
                this.deleteRow(); //Erkennung der Zeileneliminierung this.buildBlock(); //Erstellen Sie ein neues Blockset } else if (this.isClashBlockDown()) { //Fallen Sie weiter, bis Sie andere Blöcke treffen this.isGameOver(); //Beurteilen Sie, ob das Spiel vorbei ist this.deleteRow();
                dies.buildBlock();
            } anders {
                //Einen Block weiter unten this.currentBlock.y -= 60;
                this.deleteCurrentBlockPos();
                this.currentBlockPart01Pos.x -= 1;
                this.currentBlockPart02Pos.x -= 1;
                this.currentBlockPart03Pos.x -= 1;
                this.currentBlockPart04Pos.x -= 1;
                dies.checkCurrentBlockPos();
            }
        }, 1);
    }
    
    //Tastaturüberwachung onKeyDown(e) {
        Schalter (e.keyCode) {
            Fall cc.macro.KEY.left:
                if (this.isClashLeft()) { //Beurteilen, ob der linke Randumbruch erreicht wird;
                } else if (this.isClashBlockLeft()) { // Beurteilen, ob der aktuelle Operationsblock beim linken Bruch mit anderen Unterblöcken kollidiert;
                } anders {
                    dieser.aktuellerBlock.x -= 60;
                    this.deleteCurrentBlockPos();
                    this.currentBlockPart01Pos.y -= 1;
                    this.currentBlockPart02Pos.y -= 1;
                    this.currentBlockPart03Pos.y -= 1;
                    this.currentBlockPart04Pos.y -= 1;
                    dies.checkCurrentBlockPos();
                    brechen;
                }
            Fall cc.macro.KEY.right:
                …
            Fall cc.macro.KEY.up:
                //Form ändernif (this.isClashLeft()) { //Beurteilen, ob der linke Borderbreak erreicht wird;
                } else if (this.isClashRight()) { //Beurteilen, ob der rechte Randumbruch erreicht wird;
                } else if (this.isClashBottom()) { //Beurteilen, ob die untere Grenze erreicht ist.
                } else if (this.isClashBlockLeft()) { // Beurteilen, ob der aktuelle Operationsblock beim linken Bruch mit anderen Unterblöcken kollidiert;
                } sonst wenn (this.isClashBlockRight()) { // Beurteilen, ob die rechte Seite des aktuellen Operationsblocks mit anderen Unterblöcken kollidiert break;
                } sonst wenn (this.isClashBlockDown()) { //Beurteilen, ob der aktuelle Operationsblock mit anderen Unterblöcken darunter kollidiert break;
                } anders {
                    this.deleteCurrentBlockPos();
                    this.changeShape(); //Rotation und Formänderung this.checkCurrentBlockPos();
                    brechen;
                }
            Fall cc.macro.KEY.down:
                …
        }
    }

Beim Drehen habe ich eigentlich eine Abkürzung genommen. Ich habe die Positionen einiger Unterblöcke absichtlich als Mittelpunkte festgelegt, was meinen Drehvorgang überhaupt erst möglich macht.

Der in der Abbildung durch den grauen Kreis gekennzeichnete Unterblock ist der von mir festgelegte Mittelpunkt. Wenn der Mittelpunkt als Ursprung des zweidimensionalen Koordinatensystems betrachtet wird, kann es in acht Bereiche unterteilt werden: die obere Hälfte der y-Achse, die untere Hälfte der y-Achse, die linke Hälfte der x-Achse, die rechte Hälfte der x-Achse, der erste Quadrant, der zweite Quadrant, der dritte Quadrant und der vierte Quadrant.

Am Beispiel der Z-Rotation lässt sich feststellen, dass sich bei den Unterblöcken auf den vier Koordinatenachsen das x und das y ändern, während sich bei den Unterblöcken auf dem Quadranten nur eines von x und y ändert und der Wert dem ursprünglichen Wert entgegengesetzt ist. Wenn wir die Rotation auf diese Weise implementieren, wird tatsächlich nur die Position des Unterblocks geändert, die Richtung des Unterblocks ändert sich nicht.

//Rotationsänderung shapechangeShape() {
        dies.whichPartChange(dieses.currentBlockPart01, dieses.currentBlockPart01Pos);
        dies.whichPartChange(dieses.currentBlockPart02, dieses.currentBlockPart02Pos);
        dies.whichPartChange(dieses.currentBlockPart03, dieses.currentBlockPart03Pos);
        dies.whichPartChange(dieses.currentBlockPart04, dieses.currentBlockPart04Pos);
    }
    
    // Übergeben Sie den zu beurteilenden Teil whichPartChange(currentBlockPart: cc.Node, currentBlockPartPos: cc.Vec2) {
        //Änderungsparameter, der verwendet wird, um die Position von currentBlockPartPos von links nach oben, von oben nach rechts, von rechts nach unten und von unten nach links zu drehen. Er wird im Quadranten nicht benötigt. let modParameterX = Math.abs(currentBlockPart.position.x / 60);
        let modParameterY = Math.abs(currentBlockPart.position.y / 60);
        let modParameterMax = Math.max(modParameterX, modParameterY);
        //Obere Hälfte der y-Achse if (currentBlockPart.position.x == 0 && currentBlockPart.position.y > 0) {
            //Zeile-Spalte+
            aktuelleBlockPartPos.x -= modParameterMax;
            aktuelleBlockPartPos.y += modParameterMax;
            //Position des aktuellen Blocks drehen currentBlockPart.setPosition(currentBlockPart.position.y, currentBlockPart.position.x);
        }
        // Linke Hälfte der x-Achse, sonst wenn (currentBlockPart.position.x < 0 && currentBlockPart.position.y == 0) {
            …
        }
        //untere Hälfte der y-Achse sonst wenn (currentBlockPart.position.x == 0 && currentBlockPart.position.y < 0) {
            …
        }
        //rechte Hälfte der x-Achse sonst wenn (currentBlockPart.position.x > 0 && currentBlockPart.position.y == 0) {
            …
        }
        //Erster Quadrantif (currentBlockPart.position.x > 0 && currentBlockPart.position.y > 0) {
            //OK-
            wenn (aktuelleBlockPart.position.x >= 60 und aktuelleBlockPart.position.y >= 60) {
                aktuelleBlockTeilPos.x -= 2;
            } anders {
                aktuelleBlockTeilPos.x -= 1;
            }
            //Position des aktuellen Blocks drehencurrentBlockPart.setPosition(currentBlockPart.position.x, -currentBlockPart.position.y);
        }
        //Zweiter Quadrant, sonst wenn (currentBlockPart.position.x < 0 && currentBlockPart.position.y > 0) {
            …
        }
        //Der dritte Quadrant, sonst wenn (currentBlockPart.position.x < 0 && currentBlockPart.position.y < 0) {
            …
        }
        //Der vierte Quadrant, sonst wenn (currentBlockPart.position.x > 0 und currentBlockPart.position.y < 0) {
            …
        }
    }

5. Grenz- und Blockerkennung

Es gibt drei Arten der Randerkennung: linke Randerkennung, rechte Randerkennung und untere Randerkennung. Zudem gibt es drei Arten der Blockerkennung, nämlich die Erkennung unterhalb des aktuell bedienbaren Blocksatzes, die Erkennung nach links und die Erkennung nach rechts.

//Beurteilen, ob eine Kollision mit der linken Grenze bevorsteht isClashLeft(): boolean {
        wenn (this.currentBlockPart01Pos.y - 1 < 0 || this.currentBlockPart02Pos.y - 1 < 0 ||
            this.currentBlockPart03Pos.y - 1 < 0 || this.currentBlockPart04Pos.y - 1 < 0) {
            gibt true zurück;
        }
        gibt false zurück;
    }
 
    //Beurteilen, ob eine Kollision mit der rechten Grenze bevorsteht isClashRight(): boolean {
        …
    }
 
    //Beurteilen, ob eine Kollision mit der Untergrenze bevorsteht isClashBottom(): boolean {
        …
    }
//Beurteilen Sie, ob eine Kollision mit anderen Blöcken droht (unten)
    isClashBlockDown(): boolean {
        //Blockkollision nach unten erkennen, wenn (this.box[this.currentBlockPart01Pos.x - 1][this.currentBlockPart01Pos.y] != null && !this.isCurrentBlockChild(this.box[this.currentBlockPart01Pos.x - 1][this.currentBlockPart01Pos.y]) ||
            diese.box[dieses.currentBlockPart02Pos.x - 1][dieses.currentBlockPart02Pos.y] != null && !dieses.istCurrentBlockChild(diese.box[dieses.currentBlockPart02Pos.x - 1][dieses.currentBlockPart02Pos.y]) ||
            diese.box[dieses.currentBlockPart03Pos.x - 1][dieses.currentBlockPart03Pos.y] != null && !dieses.istCurrentBlockChild(diese.box[dieses.currentBlockPart03Pos.x - 1][dieses.currentBlockPart03Pos.y]) ||
            diese.box[dieses.currentBlockPart04Pos.x - 1][dieses.currentBlockPart04Pos.y] != null && !dieses.istCurrentBlockChild(diese.box[dieses.currentBlockPart04Pos.x - 1][dieses.currentBlockPart04Pos.y])) {
            gibt true zurück;
        }
    }
 
    // Beurteilen Sie, ob eine Kollision mit anderen Blöcken bevorsteht (links)
    istClashBlockLeft() {
        …
    }
 
    //Beurteilen Sie, ob eine Kollision mit anderen Blöcken bevorsteht (rechts)
    istClashBlockRight() {
        …
    }
 
    //Beurteilen, ob es sich um einen untergeordneten Block des aktuellen Operationsblocksatzes handelt isCurrentBlockChild(judgeObj: cc.Node): boolean {
        für (sei i = 0; i < 4; i++) {
            wenn (judgeObj === dieser.aktuellerBlock.kinder[i]) {
                gibt true zurück;
            }
        }
        gibt false zurück;
    }

Denn wenn jeder untergeordnete Block einen Block erkennt, muss er nach links, rechts oder daneben schauen, um festzustellen, ob andere Blöcke vorhanden sind, und es ist möglich, dass der ermittelte Block derselben übergeordneten Klasse angehört wie er selbst. Daher muss bei der Beurteilung auch festgestellt werden, ob es sich um einen untergeordneten Block des aktuell betriebenen Blocksatzes handelt.

6. Beseitigen Sie die gesamte Blockreihe

Es ist zu beachten, dass, wenn Sie die Blöcke im Spiel Reihe für Reihe betrachten, manchmal Hohlräume vorhanden sind. In diesem Fall müssen Sie überlegen, wie Sie die Blöcke ein Raster nach unten verschieben, wenn sie ausgehöhlt sind. Wenn daher in der Methode rowDown() beim Absteigen der gesamten Spalte festgestellt wird, dass die vorherige Zelle in derselben Spalte leer ist, wird ihr Null zugewiesen und die Informationen des gerade in die nächste Zelle verschobenen Blocks werden gelöscht.

//Erkennung von Zeilenlöschungen deleteRow() {
        für (sei i = 0; i < 18; i++) {
            lass count = 0;
            für (sei j = 0; j < 10; j++) {
                wenn (diese.box[i][j] != null) {
                    zählen++;
                }
            }
            //Wenn alle Blöcke in einer Zeile vorhanden sindif (Anzahl == 10) {
                für (sei j = 0; j < 10; j++) {
                    //Löschung blockieren this.box[i][j].removeFromParent();
                    diese.box[i][j] = null;
                }
                dies.rowDown(i);
                i--; //Weil nach rowDown(i) die ganze Zeile um ein Raster nach unten geht, also i--, andernfalls können mehrere Zeilen nicht eliminiert werden, was dazu führt, dass das Spiel nicht normal läuft}
        }
    }
    
    //Alle Blöcke eine Rasterzeile nach unten verschiebenDown(i: Zahl) {
        //Den Wert von i aufzeichnen, also die Zeile, die aktuell eliminiert wird. let k = i;
        // Spaltendurchlauf für (let j = 0; j < 10; j++) {
            //temp: wird verwendet, um zu berechnen, wie viele Zeilen von Blockelementen sich über der aktuell eliminierten Zeile befinden (einschließlich Aushöhlung der mittleren Ebene)
            lass temp = -1;
            für (i = k; i < 18; i++) {
                Temperatur++;
                wenn (diese.box[i][j] != null) {
                    diese.box[i - 1][j] = diese.box[i][j];
                    diese.box[i][j].y -= 60;
                    wenn (diese.box[i + 1][j] == null) {
                        diese.box[temp + k][j] = null;
                    }
                }
            }
        }
    }

3.Schreiben Sie am Ende

Generell sollte ich das Wichtigste erklärt haben. Falls noch Unklarheiten bestehen, könnt ihr euch gerne die Original-Projektdatei herunterladen:

Link: Baidu Netdisk Bitte geben Sie den Extraktionscode ein Extraktionscode: c4ss

Dies ist das Ende dieses Artikels über die Erstellung von Tetris-Spielen mit CocosCreator. Weitere relevante CocosCreator-Inhalte finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den verwandten Artikeln weiter unten. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

Das könnte Sie auch interessieren:
  • CocosCreator-Tutorial für den Einstieg: Erstellen Sie Ihr erstes Spiel mit TS
  • CocosCreator Skelettanimation Drachenknochen
  • Detaillierte Erklärung zur Erstellung von Schießspielen mit CocosCreator
  • So zeichnen Sie in CocosCreator ein cooles Radardiagramm

<<:  MySQL verwendet Union, um die Daten zweier Tabellen zusammenzuführen und anzuzeigen

>>:  Verschiedene Möglichkeiten, den Aushöhlungseffekt der CSS3-Maskenebene zu erzielen

Artikel empfehlen

Ausführliches Tutorial zur Installation von MySQL 5.6-ZIP-Paketen

Bisher haben wir alle Dateien mit der Endung .msi...

Gemessenes Bild - HTTP-Anforderung

Bitte öffnen Sie die Testseite in einem gängigen ...

jQuery implementiert Akkordeon-Kleinbuchstaben

Dieser Artikel gibt Ihnen den spezifischen Code v...

Beispiele für die korrekte Verwendung von Karten in WeChat-Miniprogrammen

Inhaltsverzeichnis Vorwort 1. Vorbereitung 2. Tat...

Öffnen Sie den Windows-Server-Port (nehmen Sie als Beispiel Port 8080)

Was ist ein Port? Bei den Ports, auf die wir uns ...

MySQL-Einschränkungen - Super detaillierte Erklärung

Inhaltsverzeichnis MySQL-Einschränkungsoperatione...

32 typische spalten-/rasterbasierte Websites

Wenn Sie nach Inspiration für spaltenbasiertes Web...

Serviceverwaltung der Quellpaketinstallation unter Linux

Inhaltsverzeichnis 1. Startverwaltung des Quellpa...

MySQL Dual-Machine Hot-Standby-Implementierungslösung [testbar]

Inhaltsverzeichnis 1. Konzept 2. Umgebungsbeschre...

calc(), um einen Vollbild-Hintergrundinhalt mit fester Breite zu erreichen

In den letzten Jahren gab es im Webdesign einen T...

Perfekte Lösung zur vertikalen Zentrierung von Formelementen

Code kopieren Der Code lautet wie folgt: <!DOC...