# HexMap with ECS: Irregularization

### Development of MMO Game Notes 23 based on the latest ECS architecture of Unity2019

## Dead work

If the Big Guys are interested in the ECS version of the Hax Infinite Map, you might want to join us and welcome Star/Folk Source code

0 Download Unity Editor (updated version of 2019.1.12f1 or), if (downloaded) continue;

1 Clone: git clone https://github.com/cloudhu/HexMapMadeInUnity2019ECS.git --recurse or Download Zip Compressed Pack

2 If you are downloading a compressed package, you need to decompress it first.Then add the HexMapMadeInUnity2019ECS to the Unity Hub project;

3 Open source project opened with Unity Hub: HexMapMadeInUnity2019ECS, waiting for Unity to compile;

4 After you open the project, launch the scene in the Scenes directory and open the AdvancedHexMap scene (a version optimized for refactoring).

## Failures

Yesterday we had a very entangled day. Let's first show the failed works:

Where did it fail?The ideal effect should be as follows:

The requirement is to achieve irregularity while preserving some regularity, such as that hexagonal elements are no longer regular six edges but irregular polygons, but the whole surface is relatively flat.The essence of the failure is that ECS cannot use Texture2D-related methods, such as Texture2D.GetPixelBilinear(), probably because Texture2D is an object under the OOP architecture, or Texture2D can only run on the main thread, or in general it cannot be used.

This causes the noise interference that should have occurred in CellSystem to have to be delayed to the OOP world, which results in inaccurate specific interference.

Although it's a failed case, let's go through Code Review from scratch. Noise is the first thing you need, as the original tutorial is available here, and you can use it directly:

The diagram must also be set according to the original tutorial:

Then the noise is sampled through code, written in the HexMetrics script:

#region noise interference /// <summary> ///Altitude Interference /// </summary> public const float elevationPerturbStrength = 1.5f; /// <summary> ///Noise Source /// </summary> public static Texture2D noiseSource; /// <summary> ///Noise Scaling /// </summary> public const float noiseScale = 0.003f; /// <summary> ///Noise Sampling /// </summary> /// <param name="position">vertex position</param> /// <returns>bilinear filtering </returns> public static Vector4 SampleNoise(Vector3 position) { return noiseSource.GetPixelBilinear(position.x * noiseScale, position.z * noiseScale); } public const float cellPerturbStrength = 3f; #endregion

This allows us to call it in OOP and interfere directly with each vertex, rather than just the vertices of a particular area, as the original author did.

It's embarrassing, but first do it, and the code is written in the MainWorld script

/// <summary> ///Noise interference /// </summary> /// <param name="position">vertex position</param> /// <returns>Disturbed location </returns> Vector3 Perturb(Vector3 position) { Vector4 sample = HexMetrics.SampleNoise(position); position.x += (sample.x * 2f - 1f) * HexMetrics.cellPerturbStrength; position.y += (sample.y * 2f - 1f) * HexMetrics.cellPerturbStrength; position.z += (sample.z * 2f - 1f) * HexMetrics.cellPerturbStrength; return position; }

Next, a violent, uniform disturbance is applied to each vertex acquired to produce a bizarre, completely irregular map:

Vector3 vertex = Perturb(vertexBuffer[j]);

The vertices are derived from dynamic caching, as described in previous ECS blogs.

I've also thought about using Berlin noise, then using it in Job, so there's no Texture2D limitation, and it takes advantage of multithreading.

So there's a direct Copy of the Berlin noise algorithm from the original author:

#region Perlin Noise /// <summary> /// Perlin Hash Table /// </summary> private readonly static int[] hash = { 151,160,137, 91, 90, 15,131, 13,201, 95, 96, 53,194,233, 7,225, 140, 36,103, 30, 69,142, 8, 99, 37,240, 21, 10, 23,190, 6,148, 247,120,234, 75, 0, 26,197, 62, 94,252,219,203,117, 35, 11, 32, 57,177, 33, 88,237,149, 56, 87,174, 20,125,136,171,168, 68,175, 74,165, 71,134,139, 48, 27,166, 77,146,158,231, 83,111,229,122, 60,211,133,230,220,105, 92, 41, 55, 46,245, 40,244,102,143, 54, 65, 25, 63,161, 1,216, 80, 73,209, 76,132,187,208, 89, 18,169, 200,196,135,130,116,188,159, 86,164,100,109,198,173,186, 3, 64, 52,217,226,250,124,123, 5,202, 38,147,118,126,255, 82, 85,212, 207,206, 59,227, 47, 16, 58, 17,182,189, 28, 42,223,183,170,213, 119,248,152, 2, 44,154,163, 70,221,153,101,155,167, 43,172, 9, 129, 22, 39,253, 19, 98,108,110, 79,113,224,232,178,185,112,104, 218,246, 97,228,251, 34,242,193,238,210,144, 12,191,179,162,241, 81, 51,145,235,249, 14,239,107, 49,192,214, 31,181,199,106,157, 184, 84,204,176,115,121, 50, 45,127, 4,150,254,138,236,205, 93, 222,114, 67, 29, 24, 72,243,141,128,195, 78, 66,215, 61,156,180 }; /// <summary> ///Hash mask /// </summary> private const int hashMask = 255; #region Perlin1D public static float Perlin1D(Vector3 point, float frequency) { point *= frequency; int i0 = Mathf.FloorToInt(point.x); float t0 = point.x - i0; float t1 = t0 - 1f; i0 &= hashMask; int i1 = i0 + 1; float g0 = gradients1D[hash[i0] & gradientsMask1D]; float g1 = gradients1D[hash[i1] & gradientsMask1D]; float v0 = g0 * t0; float v1 = g1 * t1; float t = Smooth(t0); return Mathf.Lerp(v0, v1, t) * 2f; } private static float[] gradients1D = { 1f, -1f }; private const int gradientsMask1D = 1; #endregion #region Perlin2D public static float Perlin2D(Vector3 point, float frequency) { point *= frequency; int ix0 = Mathf.FloorToInt(point.x); int iy0 = Mathf.FloorToInt(point.y); float tx0 = point.x - ix0; float ty0 = point.y - iy0; float tx1 = tx0 - 1f; float ty1 = ty0 - 1f; ix0 &= hashMask; iy0 &= hashMask; int ix1 = ix0 + 1; int iy1 = iy0 + 1; int h0 = hash[ix0]; int h1 = hash[ix1]; Vector2 g00 = gradients2D[hash[h0 + iy0] & gradientsMask2D]; Vector2 g10 = gradients2D[hash[h1 + iy0] & gradientsMask2D]; Vector2 g01 = gradients2D[hash[h0 + iy1] & gradientsMask2D]; Vector2 g11 = gradients2D[hash[h1 + iy1] & gradientsMask2D]; float v00 = Dot(g00, tx0, ty0); float v10 = Dot(g10, tx1, ty0); float v01 = Dot(g01, tx0, ty1); float v11 = Dot(g11, tx1, ty1); float tx = Smooth(tx0); float ty = Smooth(ty0); return Mathf.Lerp( Mathf.Lerp(v00, v10, tx), Mathf.Lerp(v01, v11, tx), ty) * sqr2; } private readonly static Vector2[] gradients2D = { new Vector2( 1f, 0f), new Vector2(-1f, 0f), new Vector2( 0f, 1f), new Vector2( 0f,-1f), new Vector2( 1f, 1f).normalized, new Vector2(-1f, 1f).normalized, new Vector2( 1f,-1f).normalized, new Vector2(-1f,-1f).normalized }; private const int gradientsMask2D = 7; private static float sqr2 = Mathf.Sqrt(2f); #endregion #region Perlin3D private static Vector3[] gradients3D = { new Vector3( 1f, 1f, 0f), new Vector3(-1f, 1f, 0f), new Vector3( 1f,-1f, 0f), new Vector3(-1f,-1f, 0f), new Vector3( 1f, 0f, 1f), new Vector3(-1f, 0f, 1f), new Vector3( 1f, 0f,-1f), new Vector3(-1f, 0f,-1f), new Vector3( 0f, 1f, 1f), new Vector3( 0f,-1f, 1f), new Vector3( 0f, 1f,-1f), new Vector3( 0f,-1f,-1f), new Vector3( 1f, 1f, 0f), new Vector3(-1f, 1f, 0f), new Vector3( 0f,-1f, 1f), new Vector3( 0f,-1f,-1f) }; private const int gradientsMask3D = 15; public static float Perlin3D(Vector3 point, float frequency) { point *= frequency; int ix0 = Mathf.FloorToInt(point.x); int iy0 = Mathf.FloorToInt(point.y); int iz0 = Mathf.FloorToInt(point.z); float tx0 = point.x - ix0; float ty0 = point.y - iy0; float tz0 = point.z - iz0; float tx1 = tx0 - 1f; float ty1 = ty0 - 1f; float tz1 = tz0 - 1f; ix0 &= hashMask; iy0 &= hashMask; iz0 &= hashMask; int ix1 = ix0 + 1; int iy1 = iy0 + 1; int iz1 = iz0 + 1; int h0 = hash[ix0]; int h1 = hash[ix1]; int h00 = hash[h0 + iy0]; int h10 = hash[h1 + iy0]; int h01 = hash[h0 + iy1]; int h11 = hash[h1 + iy1]; Vector3 g000 = gradients3D[hash[h00 + iz0] & gradientsMask3D]; Vector3 g100 = gradients3D[hash[h10 + iz0] & gradientsMask3D]; Vector3 g010 = gradients3D[hash[h01 + iz0] & gradientsMask3D]; Vector3 g110 = gradients3D[hash[h11 + iz0] & gradientsMask3D]; Vector3 g001 = gradients3D[hash[h00 + iz1] & gradientsMask3D]; Vector3 g101 = gradients3D[hash[h10 + iz1] & gradientsMask3D]; Vector3 g011 = gradients3D[hash[h01 + iz1] & gradientsMask3D]; Vector3 g111 = gradients3D[hash[h11 + iz1] & gradientsMask3D]; float v000 = Dot(g000, tx0, ty0, tz0); float v100 = Dot(g100, tx1, ty0, tz0); float v010 = Dot(g010, tx0, ty1, tz0); float v110 = Dot(g110, tx1, ty1, tz0); float v001 = Dot(g001, tx0, ty0, tz1); float v101 = Dot(g101, tx1, ty0, tz1); float v011 = Dot(g011, tx0, ty1, tz1); float v111 = Dot(g111, tx1, ty1, tz1); float tx = Smooth(tx0); float ty = Smooth(ty0); float tz = Smooth(tz0); return Mathf.Lerp( Mathf.Lerp(Mathf.Lerp(v000, v100, tx), Mathf.Lerp(v010, v110, tx), ty), Mathf.Lerp(Mathf.Lerp(v001, v101, tx), Mathf.Lerp(v011, v111, tx), ty), tz); } #endregion private static float Dot(Vector3 g, float x, float y, float z) { return g.x * x + g.y * y + g.z * z; } private static float Dot(Vector2 g, float x, float y) { return g.x * x + g.y * y; } /// <summary> ///Smoothing /// </summary> /// <param name="t">parameter</param> /// <returns></returns> private static float Smooth(float t) { return t * t * t * (t * (t * 6f - 15f) + 10f); } #endregion

This calculation is quite complicated. I am not very good at mathematics. I do this with the mindset that I can do it just right. Put the method directly into CellSystem:

//Noise frequency private float frequency = 5f; //height private const float height = 0.2f; /// <summary> ///Noise interference /// </summary> /// <param name="position">vertex position</param> /// <returns>Disturbed location </returns> Vector3 Perturb(Vector3 position) { position.x += (height * HexMetrics.Perlin3D(position, frequency)* 2f - 1f) * HexMetrics.cellPerturbStrength; position.y += (height * HexMetrics.Perlin3D(position, frequency)* 2f - 1f) * HexMetrics.cellPerturbStrength; position.z += (height * HexMetrics.Perlin3D(position, frequency)* 2f - 1f) * HexMetrics.cellPerturbStrength; return position; }

Then interfere at the corresponding location instead of the global interference, and the result is still not good.This is because the algorithm produces hash noise instead of Perlin's algorithm as a whole, like the noise graph.You can use UV to find the corresponding point and interfere.

Disturbing each point individually here is bound to create confusion, not what we need.

So restore a noise map and use the points on the map to interfere?

That's possible, but it's also complicated, and given your math level, you've given up.If you have a good math guy, you can optimize this part. This noise bug is kept in the project first. I also created a new Issue to wait for you to solve it.

Also post in the official forums, hoping that there is an official solution, leave one here Official DOTS Forum Hyperlink to everyone, if you have questions, you can post.

This is a temporary one, and if there are improvements to follow, update it again. Here is the dividing line:

_________

### ECS Catalogue

### ECS Update Plan

### Author's words

If you like, you can give some support. Thank you for your encouragement.If you have any questions, you can leave a message for me. If there are any mistakes or omissions, please criticize and point out!

Technical difficulties?Join the Developer Consortium: 566189328 (QQ Paid Group) provides limited technical discussion, and, Mind Chicken Soup Orz!

Of course, you are welcome to join us without technical discussion. Here you can chop wood, walk a dog, chat, and cat!(*)