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.jsCré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.gopackage PerlinNoise
import (
"math"
"math/rand"
)
func Noise(x, y float32) float32 {
left := float32(math.Floor(float64(x)))
top := float32(math.Floor(float64(y)))
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:
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 pixelsCHUNK_SIZE est la taille du morceau, dans ce cas 16 * 16PERLIN_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
}
}
}
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
}
}
}
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.gopackage 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
}
}
}
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
}
}
}
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.gopackage PerlinNoise
import (
"math"
"math/rand"
)
func Noise(x, y float32) float32 {
left := float32(math.Floor(float64(x)))
top := float32(math.Floor(float64(y)))
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.gopackage 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.