Im letzten Artikel haben wir mit Ihnen sozusagen ein Leerzeichen erstellt, auf dessen Grundlage unser Universum erstellt wird. Die Visualisierung mit der Konsole mag gut aussehen, aber Textzeichen sind langweilig und nicht sehr schön. In diesem Artikel konzentrieren wir uns auf die Visualisierung unserer Kacheln mit Phaser.jsIm letzten Artikel sah unser Projekt folgendermaßen aus:
Jetzt werden wir andere Tools für die Webentwicklung verwenden. Ich hoffe, Sie haben Node.js und npm installiert, wenn nicht, installieren Sie es dringend. Und so öffnen wir das Terminal und starten:$ npm install phaser@3.22.0
Nach erfolgreichem Abschluss des Teams sollten wir Folgendes sehen:+ phaser@3.22.0
added 15 packages from 48 contributors and audited 20 packages in 4.38s
Es sind so großartige Module erschienen, dass wir jetzt ein Verzeichnis für unseren Kunden erstellen.
In Inhalten werden wir Spielressourcen speichern, d. H. unsere Sprites. Wir werden auch zwei Dateien game.js und MainScene.js erstellen, im Stammverzeichnis (wo sich die Datei main.go befindet) create index.htmlgame.js - speichert die Haupteinstellungen für das SpielMainScene.js - es enthält die Klasse der Hauptspielszeneindex.html - Die Seite, auf der die Szene gerendert wird.Verbinden Sie unsere Skripte sofort mit index.html und wir werden nicht mehr zu dieser Datei zurückkehren: <script src="node_modules/phaser/dist/phaser.js" type="module"></script>
<script src="Client/game.js" type="module"></script>
In MainScene.js werden wir eine kleine Klassenvorlage für unsere zukünftige Szene erstellen:export {MainScene}
class MainScene extends Phaser.Scene{
constructor() {
super({key: 'MainScene'})
}
preload() {
}
create() {
}
update() {
}
}
Fügen Sie in game.js typische Einstellungen hinzu, die Ihnen gefallen. Hier sind meine:import {MainScene} from "./MainScene.js";
let config = {
type: Phaser.AUTO,
width: 800,
height: 600,
disableContextMenu: true,
background: 'black',
physics: {
default: 'arcade',
arcadePhysics: {
overlapBias: 1
}
},
scene:[MainScene],
pixelArt: true,
roundPixels: true,
antialias: true
}
let game = new Phaser.Game(config);
Jetzt brauchen wir einen HTTP-Server, auf dem dies in wenigen Zeilen erfolgt. Gehen Sie zu main.go und erstellen Sie einen Server:package main
import (
"fmt"
"html/template"
"net/http"
)
func main() {
http.HandleFunc("/", indexHandler)
http.Handle("/node_modules/phaser/dist/", http.StripPrefix("/node_modules/phaser/dist/", http.FileServer(http.Dir("./node_modules/phaser/dist/"))))
http.Handle("/Client/", http.StripPrefix("/Client/", http.FileServer(http.Dir("./Client/"))))
http.Handle("/Client/Content/", http.StripPrefix("/Client/Content/", http.FileServer(http.Dir("./Client/Content/"))))
err := http.ListenAndServe(":8080", nil)
if err != nil {
fmt.Println(err.Error())
}
}
func indexHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("indexAction")
t, _ := template.ParseFiles("index.html")
err := t.Execute(w, "index")
if err != nil {
fmt.Println(err.Error())
}
}
Nun, wir haben unseren eigenen Webserver und Client! Lass uns anfangen! Öffnen Sie die Konsole:$ go run main.go
Wir öffnen den Browser und versuchen, eine Verbindung zu unserem Server herzustellenlocalhost: 8080
Wenn Sie einen schwarzen Bildschirm gesehen haben, haben Sie alles richtig gemacht.Erstellen wir also einen weiteren Handler, mit dem wir unseren Block im JSON-Format erhalten. Erstellen Sie ein separates Verzeichnis und nennen Sie es GameController. Hier arbeiten alle Handler mit den Spieldaten. Erstellen Sie die Datei Map_Controller.go.Außerdem benötigen wir eine verbesserteChunk.gopackage Chunk
import (
"exampleMMO/PerlinNoise"
"fmt"
)
var TILE_SIZE = 16
var CHUNK_SIZE = 16 * 16
var PERLIN_SEED float32 = 160
type Chunk struct {
ChunkID [2]int `json:"chunkID"`
Map map[Coordinate]Tile `json:"map"`
}
type Tile struct {
Key string `json:"key"`
X int `json:"x"`
Y int `json:"y"`
}
type Coordinate struct {
X int `json:"x"`
Y int `json:"y"`
}
func NewChunk(idChunk Coordinate) Chunk {
fmt.Println("New Chank", idChunk)
chunk := Chunk{ChunkID: [2]int{idChunk.X, idChunk.Y}}
var chunkXMax, chunkYMax int
var chunkMap map[Coordinate]Tile
chunkMap = make(map[Coordinate]Tile)
chunkXMax = idChunk.X * CHUNK_SIZE
chunkYMax = idChunk.Y * CHUNK_SIZE
switch {
case chunkXMax < 0 && chunkYMax < 0:
{
for x := chunkXMax + CHUNK_SIZE; x > chunkXMax; x -= TILE_SIZE {
for y := chunkYMax + CHUNK_SIZE; y > chunkYMax; y -= TILE_SIZE {
posX := float32(x - (TILE_SIZE / 2))
posY := float32(y + (TILE_SIZE / 2))
tile := Tile{}
tile.X = int(posX)
tile.Y = int(posY)
perlinValue := PerlinNoise.Noise(posX/PERLIN_SEED, posY/PERLIN_SEED)
switch {
case perlinValue < -0.01:
tile.Key = "Water"
case perlinValue >= -0.01 && perlinValue < 0:
tile.Key = "Sand"
case perlinValue >= 0 && perlinValue <= 0.5:
tile.Key = "Ground"
case perlinValue > 0.5:
tile.Key = "Mount"
}
chunkMap[Coordinate{X: tile.X, Y: tile.Y}] = tile
}
}
}
case chunkXMax < 0:
{
for x := chunkXMax + CHUNK_SIZE; x > chunkXMax; x -= TILE_SIZE {
for y := chunkYMax - CHUNK_SIZE; y < chunkYMax; y += TILE_SIZE {
posX := float32(x - (TILE_SIZE / 2))
posY := float32(y + (TILE_SIZE / 2))
tile := Tile{}
tile.X = int(posX)
tile.Y = int(posY)
perlinValue := PerlinNoise.Noise(posX/PERLIN_SEED, posY/PERLIN_SEED)
switch {
case perlinValue < -0.12:
tile.Key = "Water"
case perlinValue >= -0.12 && perlinValue <= 0.5:
tile.Key = "Ground"
case perlinValue > 0.5:
tile.Key = "Mount"
}
chunkMap[Coordinate{X: tile.X, Y: tile.Y}] = tile
}
}
}
case chunkYMax < 0:
{
for x := chunkXMax - CHUNK_SIZE; x < chunkXMax; x += TILE_SIZE {
for y := chunkYMax + CHUNK_SIZE; y > chunkYMax; y -= TILE_SIZE {
posX := float32(x + (TILE_SIZE / 2))
posY := float32(y - (TILE_SIZE / 2))
tile := Tile{}
tile.X = int(posX)
tile.Y = int(posY)
perlinValue := PerlinNoise.Noise(posX/PERLIN_SEED, posY/PERLIN_SEED)
switch {
case perlinValue < -0.12:
tile.Key = "Water"
case perlinValue >= -0.12 && perlinValue <= 0.5:
tile.Key = "Ground"
case perlinValue > 0.5:
tile.Key = "Mount"
}
chunkMap[Coordinate{X: tile.X, Y: tile.Y}] = tile
}
}
}
default:
{
for x := chunkXMax - CHUNK_SIZE; x < chunkXMax; x += TILE_SIZE {
for y := chunkYMax - CHUNK_SIZE; y < chunkYMax; y += TILE_SIZE {
posX := float32(x + (TILE_SIZE / 2))
posY := float32(y + (TILE_SIZE / 2))
tile := Tile{}
tile.X = int(posX)
tile.Y = int(posY)
perlinValue := PerlinNoise.Noise(posX/PERLIN_SEED, posY/PERLIN_SEED)
switch {
case perlinValue < -0.12:
tile.Key = "Water"
case perlinValue >= -0.12 && perlinValue <= 0.5:
tile.Key = "Ground"
case perlinValue > 0.5:
tile.Key = "Mount"
}
chunkMap[Coordinate{X: tile.X, Y: tile.Y}] = tile
}
}
}
}
chunk.Map = chunkMap
return chunk
}
Wir haben gerade json-Schlüssel zu unseren Strukturen hinzugefügt und die Chunk-Erstellung ein wenig verbessert. Wirkehren zu Map_Controller zurück.package GameController
import (
"encoding/json"
"exampleMMO/Chunk"
"fmt"
"net/http"
)
func Map_Handler(w http.ResponseWriter, r *http.Request) {
c:= Chunk.NewChunk(Chunk.Coordinate{1,1})
js, e :=json.Marshal(c)
if e!= nil {
fmt.Println(e.Error())
}
fmt.Println(string(js))
}
und fügen Sie eine Zeile zu main.go hinzu http.HandleFunc("/map", GameController.Map_Handler)
Versuchen wir, den Server zu starten und gehen Sie zu localhost: 8080 / map.Ausgabe im Terminal:New Chank {1 1}
json: unsupported type: map[Chunk.Coordinate]Chunk.Tile
Ja, wir haben vergessen, dass in Golang bei der Serialisierung die Kartenschlüssel eine Zeichenfolge sein müssen. Für die Serialisierung prüft Go, ob der Typ mit der TextMarshaler-Schnittstelle übereinstimmt, und ruft die MarshalText () -Methode auf. Wir müssen lediglich die MarshalText () -Methode für unseren Koordinatentyp erstellen. Wirkehren zu Chunk.go zurück und fügen den folgenden Code hinzu:func (t Coordinate) MarshalText() ([]byte, error) {
return []byte("[" + strconv.Itoa(t.X) + "," + strconv.Itoa(t.Y) + "]"), nil
}
Sie können Ihre Implementierung schreiben. Das Wichtigste ist, dass diese Methode eine eindeutige Zeichenfolge zurückgibt. Wir werden diesen Schlüssel verwenden, um Chunks auf dem Client zu verwalten. Lassen Sie uns nun überprüfen, wie unser Controller funktioniert, den Server erneut starten und die Ausgabe an die Konsole anzeigen.
Ja, alles ist in Ordnung. Lassen Sie uns nun einen Abschluss für den Stream ziehen und zwei Zeilen am Ende unseres Controllers hinzufügen:
w.Header().Set("Content-Type", "application/json")
w.Write(js)
Beenden Sie vorerst Golang und kehren Sie zum Kunden zurück. Wir werden drei Titel brauchen, obwohl wir tatsächlich vier davon haben, aber im Moment haben wir drei oder sogar zwei.

Fügen Sie unsere Kacheln zum Inhaltsverzeichnis hinzu und beginnen Sie mit MainScene.js zu arbeiten. Für die ersten Ergebnisse benötigen wir einige Funktionen:class MainScene extends Phaser.Scene{
constructor() {
super({key: 'MainScene'})
}
preload() {
this.load.image("Ground", "Client/Content/sprGrass.png")
this.load.image("Water", "Client/Content/sprWater1.png")
this.load.image("Sand", "Client/Content/sprGrass.png")
}
create() {
this.getGameMap()
}
update() {
}
async getGameMap() {
let res = await fetch("/map")
let result = await res.json()
this.drawChunk(result.map)
}
drawChunk(map) {
for (let chunkKey in map) {
this.add.image(map[chunkKey].x,map[chunkKey].y, map[chunkKey].key)
}
}
}
Der Server gibt unseren Chunk in Form eines JSON-Objekts an uns zurück. Sie können seine Struktur in der Browserkonsole anzeigen:
Und so hat Phaser ihn im Browser gerendert:
Wir haben die einfachste Arbeit zwischen Server und Client untersucht. Im nächsten Artikel werden wir sofort 9 Chunks zeichnen und navigieren zur Welt. Den gesamten Code und die Ressourcen aus dem Artikel finden Sie hier .