Golang + Phaser3 = MMORPG - Wir bilden die Basis für die endlose Generation der Welt

Bild

Es ist schwierig, die richtigen Informationen zu russischsprachigen Ressourcen zu finden. Vielleicht können Sie mit diesem Material einige der Grundlagen für die Erstellung von Multiplayer-Spielen und mehr verstehen. Ich plane, eine Reihe von Artikeln über die Erstellung von 2.5D-MMORPG zu verfassen, dh in der Isometrie wird unsere Welt in prozedural erzeugte Abschnitte unterteilt, die aus Titeln bestehen. Der Server wird in der Sprache Golang geschrieben, was mir perfekt geeignet erscheint. Der Client-Teil wird in JavaScript unter Verwendung des Frameworks - Phaser.js - ausgeführt

Erschaffe eine Weltgeneration


Und so werden wir in diesem Artikel einen Chunk-Generator für IMO auf dem Golang schreiben, wir werden Phaser vorerst nicht berücksichtigen. Für die prozedurale Generierung benötigen wir eine Rauschfunktion. Wir verwenden Perlin Noise . Ich empfehle, dass Sie diesen Artikel lesen und den Code für Go neu schreiben oder meine Version übernehmen.

Perlin.go
package PerlinNoise

import (
	"math"
	"math/rand"
)

func Noise(x, y float32) float32 {
	//Coordinate left and top vertex square
	left := float32(math.Floor(float64(x)))
	top := float32(math.Floor(float64(y)))

	//Local coordinate
	localPoinX := x - left
	localPoiny := y - top

	topLeft := getRandomVector(left, top)
	topRight := getRandomVector(left+1, top)
	bottomLeft := getRandomVector(left, top+1)
	bottomRight := getRandomVector(left+1, top+1)
	//      
	DtopLeft := []float32{localPoinX, localPoiny}
	DtopRight := []float32{localPoinX - 1, localPoiny}
	DbottomLeft := []float32{localPoinX, localPoiny - 1}
	DbottomRight := []float32{localPoinX - 1, localPoiny - 1}

	// 
	tx1 := dot(DtopLeft, topLeft)
	tx2 := dot(DtopRight, topRight)
	bx1 := dot(DbottomLeft, bottomLeft)
	bx2 := dot(DbottomRight, bottomRight)

	//  
	pointX := curve(localPoinX)
	pointY := curve(localPoiny)

	//

	tx := lerp(tx1, tx2, pointX)
	bx := lerp(bx1, bx2, pointX)
	tb := lerp(tx, bx, pointY)
	return tb

}
func getRandomVector(x, y float32) []float32 {
	rand.Seed(int64(x * y))
	v := rand.Intn(3)

	switch v {

	case 0:
		return []float32{-1, 0}
	case 1:
		return []float32{1, 0}
	case 2:
		return []float32{0, 1}
	default:
		return []float32{0, -1}

	}
}
func dot(a []float32, b []float32) float32 {

	return (a[0]*b[0] + b[1]*a[1])
}
func lerp(a, b, c float32) float32 {

	return a*(1-c) + b*c

}
func curve(t float32) float32 {

	return (t * t * t * (t*(t*6-15) + 10))

}


Lassen Sie uns ein kleines Projekt erstellen, in dem wir die Funktionalität unserer Funktion testen. Hier ist die Struktur meines Projekts:

Bild

Fügen Sie der Datei main.go Folgendes hinzu:

func main() {
	var a, b float32= 1330, 2500
	v:= PerlinNoise.Noise(a/2500, b/2500)
	fmt.Println(v)
}

Seien Sie vorsichtig mit den Arten von Zahlen, geben Sie die Typen immer explizit an, dies erspart Ihnen in Zukunft Probleme, die Ausgabe der Funktion:

-0.23416707

Und so haben wir eine Rauschfunktion, um unsere Welten zu erzeugen. Beginnen wir mit der Erstellung von Chunks. Erstellen Sie das Chunk-Verzeichnis und die darin enthaltene Chunk.go-Datei und definieren Sie sofort die Hauptkonstanten:

var TILE_SIZE = 16
var CHUNK_SIZE = TILE_SIZE * TILE_SIZE
var PERLIN_SEED float32 = 150

TILE_SIZE ist die Auflösung unserer zukünftigen Chunks in Pixel.
CHUNK_SIZE ist die Größe des Chunks , in diesem Fall 16 * 16
PERLIN_SEED. Hier können Sie eine beliebige Zahl eingeben. Je höher diese ist, desto gleichmäßiger ist das Rauschen, d. H. Wenn Sie kleine Inseln wollen, geben Sie die Zahl weniger ein, wenn die riesigen Kontinente höher sind.

Erstellen Sie als Nächstes einen Datentyp für die Koordinaten:

type Coordinate struct {
	X int 
	Y int 
}

Dieser Typ wird für uns in Zukunft sehr nützlich sein, und jetzt werden wir eine weitere wichtige Funktion erstellen, um die eindeutigen Koordinaten unseres Chunks in Zukunft zu bestimmen. Wir werden ihre ID nennen:

func GetChunkID(x, y int) Coordinate {
	tileX := float64(float64(x) / float64(TILE_SIZE))
	tileY := float64(float64(y) / float64(TILE_SIZE))

	var ChunkID Coordinate
	if tileX < 0 {
		ChunkID.X = int(math.Floor(tileX / float64(TILE_SIZE)))
	} else {
		ChunkID.X = int(math.Ceil(tileX / float64(TILE_SIZE)))
	}
	if tileY < 0 {
		ChunkID.Y = int(math.Floor(tileY / float64(TILE_SIZE)))
	} else {
		ChunkID.Y = int(math.Ceil(tileY / float64(TILE_SIZE)))
	}
	if tileX == 0 {
		ChunkID.X = 1
	}
	if tileY == 0 {
		ChunkID.Y = 1
	}
	return ChunkID

}

Die Funktion zum Bestimmen der ID des Blocks ist recht einfach. Wir teilen einfach die Position auf der Karte durch die Größe der Kachel und dann das Ergebnis erneut durch die Größe der Kachel mit Auf- oder Abrunden, abhängig von der ID des Blocks seitdem Unsere Welt wird endlos in jede Richtung erzeugt.

Fügen Sie als Nächstes unseren Baustein hinzu, um den Block zu erstellen. Dies ist die Kachel und der Block selbst:

type Chunk struct {
	ChunkID [2]int
	Map     map[Coordinate]Tile
}
type Tile struct {
	Key string
	X   int
	Y   int
}

Der Block enthält eine Kachelkarte. Kacheln speichern ihre Koordinaten und Schlüssel (der Schlüssel ist die Art Ihres Titels: Land, Wasser, Berge usw.).

Kommen wir nun zu den wichtigsten Funktionen zum Erstellen unseres Blocks. Ich habe meine Arbeitsfunktion aus dem Projekt übernommen und sie für diesen Artikel ein wenig überarbeitet:

func NewChunk ()
func NewChunk(idChunk Coordinate) Chunk {
	//    
	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 = "~" //
					case perlinValue >= -0.01 && perlinValue <= 0.5:
						tile.Key = "1" //

					case perlinValue > 0.5:
						tile.Key = "^" // 
					}
					chunkMap[Coordinate{X: tile.X, Y: tile.Y}] = tile

				}
			}
		}
		//  X 
	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 = "~"
					case perlinValue >= -0.12 && perlinValue <= 0.5:
						tile.Key = "1"

					case perlinValue > 0.5:
						tile.Key = "^"
					}

					chunkMap[Coordinate{X: tile.X, Y: tile.Y}] = tile

				}
			}
		}
		//  Y 
	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 = "~"
					case perlinValue >= -0.12 && perlinValue <= 0.5:
						tile.Key = "1"
					case perlinValue > 0.5:
						tile.Key = "^"
					}
					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 = "~"
					case perlinValue >= -0.12 && perlinValue <= 0.5:
						tile.Key = "1"
					case perlinValue > 0.5:
						tile.Key = "^"
					}
					chunkMap[Coordinate{X: tile.X, Y: tile.Y}] = tile

				}
			}
		}

	}

	chunk.Map = chunkMap
	return chunk
}


Und so bestimmen wir in dieser Funktion oder besser gesagt im Konstruktor unseres Blocks die maximalen Koordinaten des Blocks, zu denen wir uns nacheinander bewegen und die Kacheln mit den erforderlichen Informationen füllen. ChunkMax wird auch ganz einfach bestimmt. Dazu multiplizieren wir die Chunk-ID mit ihrer Größe (CHUNK_SIZE), dh mit der ID {1; 1} sind unsere Koordinaten chunkXMax und chunkYMax 256.

In posX / posY bestimmen wir die Koordinaten zum Einfügen unserer Grafiken:

					posX := float32(x + (TILE_SIZE / 2))
					posY := float32(y + (TILE_SIZE / 2))

Wir verwenden den Schalter, um die Logik abhängig vom Wert der ID unseres Blocks auszuwählen (es kann positive und negative Werte geben). Der Schlüssel der Kachel bestimmt das Rauschen von Perlin. Wenn beispielsweise das Rauschen von Perlin unter 0 liegt, ist es Wasser, darüber Land. Wir machen das:

					case perlinValue < -0.12:
						tile.Key = "~" // 
					case perlinValue >= -0.12 && perlinValue <= 0.5:
						tile.Key = "1" // 
					case perlinValue > 0.5:
						tile.Key = "^" // 

Mal sehen, wie unsere Funktion funktioniert, ersetzen Sie den Code in main durch den folgenden Inhalt:

func main() {
	coord := Chunk.Coordinate{Y: 1, X: 1}
	chunk := Chunk.NewChunk(coord)
	m := chunk.Map
	out := os.Stdout
	for y := 8; y < 16*16; y += 16 {
		for x := 8; x < 16*16; x += 16 {
			c := Chunk.Coordinate{X: x, Y: y}
			out.Write([]byte(m[c].Key))

		}
		out.Write([]byte("\n"))
	}

}

Fazit:

11~~~11111111111
11~~~11111111111
11~~~~1111111111
11~~~~1111111111
11~~~~~111111111
11~~~~~~1111111~
11~~~~~~~~~11~~~
11~~~~~~~~~~~~~~
11~~~~~~~~~~~~~~
11~~~~~~~~~~~~~~
11~~~~~~~~~~~~~~
11~~~~~~~~~~~~~~
11~~~~~~~~~~~~~~
11~~~~~~~~~~~~~~
11~~~~~~~~~~1111
11~~~~~~~~~11111

Es sieht gut aus, Sie können die Ausgabefunktion ändern und mit den Parametern spielen:

11111~~~~~~~~111111111111111111111111111111~~~~~~~11111111111111
11111~~~~~~~~111111111111111111111111111111~~~~~~~11111111111111
11111~~~~~~~~111111111111111111111111111111~~~~~~~11111111111111
11111~~~~~~~~111111111111111111111111111111~~~~~~~~1111111111111
11111~~~~~~~~11111111111111111111111111111~~~~~~~~~1111111111111
11111~~~~~~~~11111111111111111111111111111~~~~~~~~~1111111111111
11111~~~~~~~~~111111111111111111111111111~~~~~~~~~~~111111111111
11111~~~~~~~~~111111111111111111111111111~~~~~~~~~~~~11111111111
11111~~~~~~~~~~1111111111111111111111111~~~~~~~~~~~~~11111111111
11111~~~~~~~~~~~11111111111111111111111~~~~~~~~~~~~~~~1111111111
11111~~~~~~~~~~~1111111111111111111111~~~~~~~~~~~~~~~~~111111111
11111~~~~~~~~~~~~~11111111111111111111~~~~~~~~~~~~~~~~~111111111
11111~~~~~~~~~~~~~~111111111111111111~~~~~~~~~~~~~~~~~~~11111111
11111~~~~~~~~~~~~~~~~11111111111111~~~~~~~~~~~~~~~~~~~~~~1111111
11111~~~~~~~~~~~~~~~~~~11111111111~~~~~~~~~~~~~~~~~~~~~~~~111111
11111~~~~~~~~~~~~~~~~~~~~~111111~~~~~~~~~~~~~~~~~~~~~~~~~~~11111
11111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~1111
11111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~111
11111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~11
11111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~1
11111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~~~~~~~~~~~1111111111~~~~~~~~~~~~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~~~~~~~~~111111111111111~~~~~~~~~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~~~~~~~1111111111111111111~~~~~~~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~~~~~~111111111111111111111~~~~~~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~~~~~11111111111111111111111~~~~~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~~~~1111111111111111111111111~~~~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~~~111111111111111111111111111~~~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~~11111111111111111111111111111~~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~~111111111111111111111111111111~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~~1111111111111111111111111111111~~~~~~~~~~~
11111~~~~~~~~~~~~~~~~111111111111111111111111111111111~~~~~~~~~~
11111~~~~~~~~~~~~~~~~1111111111111111111111111111111111~~~~~~~~~
11111~~~~~~~~~~~~~~~~11111111111111111111111111111111111~~~~~~~~
11111~~~~~~~~~~~~~~~~11111111111111111111111111111111111~~~~~~~~
11111~~~~~~~~~~~~~~~~111111111111111111111111111111111111~~~~~~~
11111~~~~~~~~~~~~~~~~1111111111111111111111111111111111111~~~~~~
11111~~~~~~~~~~~~~~~~1111111111111111111111111111111111111~~~~~~
11111~~~~~~~~~~~~~~~~11111111111111111111111111111111111111~~~~~
11111~~~~~~~~~~~~~~~~11111111111111111111111111111111111111~~~~~
11111~~~~~~~~~~~~~~~~~11111111111111111111111111111111111111~~~~
11111~~~~~~~~~~~~~~~~~11111111111111111111111111111111111111~~~~
11111~~~~~~~~~~~~~~~~~~11111111111111111111111111111111111111~~~
1111~~~~~~~~~~~~~~~~~~~11111111111111111111111111111111111111~~~
1111~~~~~~~~~~~~~~~~~~~~1111111111111111111111111111111111111~~~
1111~~~~~~~~~~~~~~~~~~~~~111111111111111111111111111111111111~~~
1111~~~~~~~~~~~~~~~~~~~~~1111111111111111111111111111111111111~~
1111~~~~~~~~~~~~~~~~~~~~~~111111111111111111111111111111111111~~
1111~~~~~~~~~~~~~~~~~~~~~~~11111111111111111111111111111111111~~
1111~~~~~~~~~~~~~~~~~~~~~~~~1111111111111111111111111111111111~~
1111~~~~~~~~~~~~~~~~~~~~~~~~1111111111111111111111111111111111~~
1111~~~~~~~~~~~~~~~~~~~~~~~~~111111111111111111111111111111111~~

Der Code:

Chunk.go
package Chunk

import (
	"PerlinNoise"
	"math"
)

var TILE_SIZE = 16
var CHUNK_SIZE = 32 * 32
var PERLIN_SEED float32 = 600

type Coordinate struct {
	X int `json:"x"`
	Y int `json:"y"`
}
type Chunk struct {
	ChunkID [2]int
	Map     map[Coordinate]Tile
}
type Tile struct {
	Key string
	X   int
	Y   int
}

func GetChunkID(x, y int) Coordinate {
	tileX := float64(x)
	tileY := float64(y)

	var ChunkID Coordinate
	if tileX < 0 {
		ChunkID.X = int(math.Floor(tileX / float64(TILE_SIZE)))
	} else {
		ChunkID.X = int(math.Ceil(tileX / float64(TILE_SIZE)))
	}
	if tileY < 0 {
		ChunkID.Y = int(math.Floor(tileY / float64(TILE_SIZE)))
	} else {
		ChunkID.Y = int(math.Ceil(tileY / float64(TILE_SIZE)))
	}
	if tileX == 0 {
		ChunkID.X = 1
	}
	if tileY == 0 {
		ChunkID.Y = 1
	}
	return ChunkID

}
func NewChunk(idChunk Coordinate) Chunk {
	//    
	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 = "~" //
					case perlinValue >= -0.01 && perlinValue <= 0.5:
						tile.Key = "1" //

					case perlinValue > 0.5:
						tile.Key = "^" // 
					}
					chunkMap[Coordinate{X: tile.X, Y: tile.Y}] = tile

				}
			}
		}
		//  X 
	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 = "~"
					case perlinValue >= -0.12 && perlinValue <= 0.5:
						tile.Key = "1"

					case perlinValue > 0.5:
						tile.Key = "^"
					}

					chunkMap[Coordinate{X: tile.X, Y: tile.Y}] = tile

				}
			}
		}
		//  Y 
	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 = "~"
					case perlinValue >= -0.12 && perlinValue <= 0.5:
						tile.Key = "1"
					case perlinValue > 0.5:
						tile.Key = "^"
					}
					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 = "~"
					case perlinValue >= -0.12 && perlinValue <= 0.5:
						tile.Key = "1"
					case perlinValue > 0.5:
						tile.Key = "^"
					}
					chunkMap[Coordinate{X: tile.X, Y: tile.Y}] = tile

				}
			}
		}

	}

	chunk.Map = chunkMap
	return chunk
}


Perlin.go
package PerlinNoise

import (
	"math"
	"math/rand"
)

func Noise(x, y float32) float32 {
	//Coordinate left and top vertex square
	left := float32(math.Floor(float64(x)))
	top := float32(math.Floor(float64(y)))

	//Local coordinate
	localPoinX := x - left
	localPoiny := y - top

	topLeft := getRandomVector(left, top)
	topRight := getRandomVector(left+1, top)
	bottomLeft := getRandomVector(left, top+1)
	bottomRight := getRandomVector(left+1, top+1)
	//      
	DtopLeft := []float32{localPoinX, localPoiny}
	DtopRight := []float32{localPoinX - 1, localPoiny}
	DbottomLeft := []float32{localPoinX, localPoiny - 1}
	DbottomRight := []float32{localPoinX - 1, localPoiny - 1}

	// 
	tx1 := dot(DtopLeft, topLeft)
	tx2 := dot(DtopRight, topRight)
	bx1 := dot(DbottomLeft, bottomLeft)
	bx2 := dot(DbottomRight, bottomRight)

	//  
	pointX := curve(localPoinX)
	pointY := curve(localPoiny)

	//

	tx := lerp(tx1, tx2, pointX)
	bx := lerp(bx1, bx2, pointX)
	tb := lerp(tx, bx, pointY)
	return tb

}
func getRandomVector(x, y float32) []float32 {
	rand.Seed(int64(x * y))
	v := rand.Intn(3)

	switch v {

	case 0:
		return []float32{-1, 0}
	case 1:
		return []float32{1, 0}
	case 2:
		return []float32{0, 1}
	default:
		return []float32{0, -1}

	}
}
func dot(a []float32, b []float32) float32 {

	return (a[0]*b[0] + b[1]*a[1])
}
func lerp(a, b, c float32) float32 {

	return a*(1-c) + b*c

}
func curve(t float32) float32 {

	return (t * t * t * (t*(t*6-15) + 10))

}


main.go
package main

import (
	"fmt"
	"habr/Chunk"
	"os"
)

func main() {
	coord:= Chunk.GetChunkID(0,0)
	fmt.Println(coord)
	chunk := Chunk.NewChunk(coord)
	m := chunk.Map
	out := os.Stdout
	for y := 8; y < 32*32; y += 16 {
		for x := 8; x < 32*32; x += 16 {
			c := Chunk.Coordinate{X: x, Y: y}
			out.Write([]byte(m[c].Key))

		}
		out.Write([]byte("\n"))
	}

}


Im nächsten Artikel werden wir überlegen, mit HTTP zu arbeiten, und wenn wir dies beeinflussen, dann die WS-Verbindung. Lassen Sie uns eine Art Spielkarte erstellen, die wir im JSON-Format für das Rendern auf dem Client serialisieren und im Allgemeinen sehen, wie wir mit dem Client interagieren.

All Articles