Golang + Phaser3 = MMORPG - Nous jetons les bases de la génération sans fin du monde

image

Il est difficile de trouver les bonnes informations sur les ressources en russe, peut-être que ce matériel vous permettra de comprendre certaines des bases de la création de jeux multijoueurs et plus encore. Je prévois de faire une série d'articles sur la création de MMORPG 2.5D, c'est-à-dire qu'en isométrie, notre monde sera divisé en morceaux générés de manière procédurale et composés de titres. Le serveur sera écrit dans le langage Golang, ce qui me semble parfaitement adapté à cela, la partie client sera en JavaScript en utilisant le framework - Phaser.js

Créer une génération mondiale


Et donc dans cet article, nous allons écrire un générateur de morceaux pour MMO sur le Golang, nous ne considérerons pas Phaser pour l'instant. Pour la génération procédurale, nous avons besoin d'une fonction de bruit, nous utiliserons Perlin Noise , je vous recommande de lire cet article et de réécrire le code pour Go ou de prendre ma version.

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))

}


Créons un petit projet où nous testons la fonctionnalité de notre fonction, voici la structure de mon projet:

image

Ajoutez ce qui suit au fichier main.go:

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

Soyez prudent avec les types de nombres, spécifiez toujours les types explicitement, cela vous évitera des problèmes à l'avenir, la sortie de la fonction:

-0.23416707

Et nous avons donc une fonction de bruit pour générer nos mondes. Commençons à créer des morceaux. Créez-y le répertoire Chunk et le fichier Chunk.go et définissez immédiatement les constantes principales:

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

TILE_SIZE est la résolution de nos futurs morceaux en pixels
CHUNK_SIZE est la taille du morceau, dans ce cas 16 * 16
PERLIN_SEED - ici vous pouvez mettre n'importe quel nombre, plus il est élevé, plus le bruit est uniforme, c'est-à-dire si vous voulez de petites îles, alors mettez le nombre moins, si les immenses continents sont plus hauts.

Ensuite, créez un type de données pour les coordonnées:

type Coordinate struct {
	X int 
	Y int 
}

Ce type nous sera très utile à l'avenir, et maintenant nous allons créer une autre fonction importante, pour déterminer les coordonnées uniques de notre morceau à l'avenir, nous appellerons leur ID:

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

}

La fonction pour déterminer l'ID du morceau est assez simple, nous divisons simplement la position sur la carte par la taille de la tuile, puis nous divisons à nouveau le résultat par la taille de la tuile avec arrondi vers le haut ou vers le bas, selon l'ID du morceau depuis notre monde sera généré à l'infini dans toutes les directions.

Ensuite, ajoutez notre bloc de construction pour créer le bloc, voici la tuile et le bloc lui-même:

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

Le morceau contient une carte de tuiles. Les tuiles stockent leurs coordonnées et leur clé (la clé est le type de votre titre: terre, eau, montagnes, etc.)

Passons maintenant au plus important, les fonctions de création de notre morceau, j'ai pris ma fonction de travail du projet et l'ai refaite un peu pour cet article:

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
}


Et donc, dans cette fonction ou plutôt le constructeur de notre bloc, nous déterminons les coordonnées maximales du bloc, vers lesquelles nous nous déplacerons séquentiellement, en remplissant les tuiles avec les informations nécessaires. ChunkMax est également déterminé tout simplement, pour cela nous multiplions l'ID de bloc par sa taille (CHUNK_SIZE), c'est-à-dire qu'avec l'ID {1; 1} nos coordonnées chunkXMax et chunkYMax seront 256.

Dans posX / posY nous déterminons les coordonnées pour insérer nos graphiques:

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

Nous utilisons switch pour sélectionner la logique en fonction de la valeur de l'ID de notre bloc (il peut y avoir des valeurs positives et négatives). La clé de la tuile déterminera le bruit du perlin, par exemple, si le bruit du perlin est inférieur à 0, ce sera l'eau, au-dessus ce sera la terre. Nous allons le faire:

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

Voyons comment fonctionne notre fonction, remplacez le code en principal par le contenu suivant:

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"))
	}

}

Conclusion:

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

Il a l'air bien, vous pouvez changer la fonction de sortie et jouer avec les paramètres:

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~~

Le 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"))
	}

}


Dans le prochain article , nous envisagerons de travailler avec HTTP, et si nous l'affectons, alors la connexion WS. Créons un type de carte de jeu, que nous sérialiserons au format json pour le rendu sur le client et verrons généralement comment nous interagissons avec le client.

All Articles