Golang + Phaser3 = MMORPG - نحن نصنع الأساس للجيل اللامتناهي من العالم

صورة

من الصعب العثور على المعلومات الصحيحة حول موارد اللغة الروسية ، ربما ستسمح لك هذه المادة بفهم بعض الأساسيات لإنشاء ألعاب متعددة اللاعبين والمزيد. أخطط لصنع سلسلة من المقالات حول إنشاء 2.5D MMORPG ، أي في علم القياس ، سيتم تقسيم عالمنا إلى قطع متولدة إجرائيًا تتكون من عناوين. سيتم كتابة الخادم بلغة Golang ، والتي تبدو لي مناسبة تمامًا لهذا ، سيكون جزء العميل في JavaScript باستخدام إطار العمل - Phaser.js

إنشاء جيل عالمي


وهكذا في هذه المقالة سوف نكتب مولدًا للقطعة لـ MMO على Golang ، ولن نعتبر Phaser في الوقت الحالي. من أجل توليد الإجراءات ، نحتاج إلى وظيفة ضوضاء ، وسوف نستخدم Perlin Noise ، أوصي بقراءة هذه المقالة وإعادة كتابة الرمز لـ Go أو أخذ نسختي.

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:

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

كن حذرا مع أنواع الأرقام ، حدد دائما الأنواع بشكل صريح ، وهذا سيوفر عليك من المشاكل في المستقبل ، إخراج الوظيفة:

-0.23416707

وبالتالي لدينا وظيفة ضوضاء لتوليد عوالمنا. لنبدأ في إنشاء قطع. قم بإنشاء دليل Chunk وملف Chunk.go فيه وحدد الثوابت الرئيسية على الفور:

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

TILE_SIZE هو دقة قطعنا المستقبلية بالبكسل
CHUNK_SIZE هو حجم القطعة ، في هذه الحالة 16 * 16
PERLIN_SEED - هنا يمكنك وضع أي رقم ، وكلما كان أعلى ، كلما كان الضجيج أكثر اتساقًا ، أي إذا كنت تريد جزرًا صغيرة ، فضع الرقم أقل ، إذا كانت القارات الضخمة أعلى.

بعد ذلك ، أنشئ نوع بيانات للإحداثيات:

type Coordinate struct {
	X int 
	Y int 
}

سيكون هذا النوع مفيدًا جدًا لنا في المستقبل ، والآن سننشئ وظيفة مهمة أخرى ، لتحديد الإحداثيات الفريدة لمجموعتنا في المستقبل ، وسنتصل بمعرفتهم:

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

}

إن وظيفة تحديد هوية القطعة بسيطة للغاية ، نقوم فقط بتقسيم الموضع على الخريطة حسب حجم البلاط ، ثم نقسم النتيجة مرة أخرى على حجم البلاط مع التقريب لأعلى أو لأسفل ، اعتمادًا على معرف القطعة منذ عالمنا سينتج إلى ما لا نهاية في أي اتجاه.

بعد ذلك ، أضف كتلة البناء الخاصة بنا لإنشاء القطعة ، وهذا هو البلاط والجزء نفسه:

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

تحتوي القطعة على خريطة تجانب. يقوم Tiles بتخزين إحداثياته ​​ومفتاحه (المفتاح هو نوع عنوانك: الأرض ، الماء ، الجبال ، إلخ.)

الآن دعنا ننتقل إلى أهم شيء ، وظائف إنشاء القطعة لدينا ، أخذت وظيفتي العملية من المشروع وأعدتها قليلاً لهذه المقالة:

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
}


وهكذا ، في هذه الوظيفة أو بالأحرى مُنشئ القطعة ، نحدد الإحداثيات القصوى للقطعة ، والتي سننتقل إليها بالتتابع ، ونملأ البلاط بالمعلومات الضرورية. يتم تحديد ChunkMax أيضًا بكل بساطة ، لأننا نضرب معرف القطعة في حجمها (CHUNK_SIZE) ، أي بالمعرّف {1؛ 1} ، ستصبح إحداثياتنا chunkXMax و chunkYMax 256.

في posX / posY ، نحدد إحداثيات إدراج رسوماتنا:

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

نستخدم التبديل لاختيار المنطق اعتمادًا على قيمة معرف القطعة (قد تكون هناك قيم موجبة وسالبة). سيحدد مفتاح البلاط ضجيج البيرلين ، على سبيل المثال ، إذا كان ضجيج البيرلين أقل من 0 ، فسيكون الماء ، وفوق ذلك سيكون الأرض. سوف نفعل ذلك:

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

دعونا نرى كيف تعمل وظيفتنا ، استبدل الكود بشكل رئيسي بالمحتويات التالية:

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

}

استنتاج:

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

يبدو جيدًا ، يمكنك تغيير وظيفة الإخراج واللعب بالمعلمات:

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

الرمز:

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

}


في المقالة التالية ، سننظر في العمل مع HTTP ، وإذا أثرنا عليه ، فسيكون اتصال WS. لنقم بإنشاء نوع من بطاقة اللعبة ، والتي سنقوم بعمل تسلسل لها بتنسيق json لعرضها على العميل ونرى بشكل عام كيف نتفاعل مع العميل.

All Articles