Halo semuanya! Baru-baru ini saya memutuskan untuk menulis algoritme generasi medan saya sendiri untuk game saya di mesin game Unity 3D. Faktanya, algoritme saya sangat cocok untuk mesin lain dan tidak hanya mesin, karena hanya menggunakan C # murni. Untuk melakukan ini dengan bantuan kebisingan bagi saya tidak menarik, dan saya memutuskan untuk menerapkan semuanya menggunakan interpolasi. Tentu saja, semua orang akan mengatakan mengapa menemukan kembali roda, tetapi ini juga merupakan praktik yang baik, dan semuanya akan berguna dalam hidup. Jika Anda tidak menyukai implementasi saya melalui interpolasi, saya akan menulis algoritme untuk menghasilkan dengan Perlin Noise di bagian akhir. Jadi, mari kita mulai.
1. Kurva Bezier.
Saya memutuskan untuk melakukan cara implementasi yang pertama melalui rumus kurva Bezier. Rumus jumlah titik ke-n di ruang angkasa:
, di mana B - fungsi dasar dari kurva Bezier, dengan kata lain - polinomial Bernstein. Formula mereka adalah
... [satu]
Sangat mudah untuk membuat kode ini, jadi mari kita mulai.
1) Mari buat struktur Titik, yang akan memiliki dua parameter - koordinat x dan y dan tentukan ulang beberapa operator (+, -, *, /) untuknya.
[Serializable]
public struct Point
{
public float x, y;
public Point(float x, float y)
{
this.x = x;
this.y = y;
}
public static Point operator +(Point a, Point b) => new Point(a.x + b.x, a.y + b.x);
public static Point operator -(Point a, float d) => new Point(a.x - d, a.y - d);
public static Point operator -(Point a, Point b) => new Point(a.x - b.x, a.y - b.y);
public static Point operator *(float d, Point a) => new Point(a.x * d, a.y * d);
public static Point operator *(Point a, float d) => new Point(a.x * d, a.y * d);
public static Point operator *(Point a, Point b) => new Point(a.x * b.x, a.x * b.y);
public static Point operator /(Point a, float d) => new Point(a.x / d, a.y / d);
public static Point operator /(Point a, Point b) => new Point(a.x / b.x, a.y / b.y);
public static bool operator ==(Point lhs, Point rhs) => lhs.x == rhs.x && lhs.y == rhs.y;
public static bool operator !=(Point lhs, Point rhs) => lhs.x != rhs.x || lhs.y != rhs.y;
}
2) Sekarang mari kita tulis metode itu sendiri untuk mendapatkan poin dengan parameter t. Kita juga perlu membuat fungsi untuk menghitung faktorial.
int factorial(int n)
{
int f = 1;
for (int i = 1; i < n; i++)
{
f *= i;
}
return f;
}
Point curveBezier(Point[] points, float t)
{
Point curve = new Point(0, 0);
for (int i = 0; i < points.Length; i++)
curve += points[i] * factorial(points.Length - 1) / (factorial(i) * factorial(points.Length - 1 - i)) * (float)Math.Pow(t, i) * (float)Math.Pow(1 - t, points.Length - 1 - i);
return curve;
}
, , . , t , x. , .[2] . , c_n * x ^ n c_n * n * x ^ (n-1). .
3) .
Point derivative(Point[] points, float t)
{
Point curve = new Point(0, 0);
for (int i = 0; i < points.Length; i++)
{
Point c = points[i] * factorial(points.Length - 1) / (factorial(i) * factorial(points.Length - 1 - i));
if (i > 1)
{
curve += c * i * (float)Math.Pow(t, i - 1) * (float)Math.Pow(1 - t, points.Length - 1 - i);
}
if (points.Length - 1 - i > 1)
{
curve -= c * (float)Math.Pow(t, i) * (points.Length - 1 - i) * (float)Math.Pow(1 - t, points.Length - 2 - i);
}
}
return curve;
}
4) t .
float timeBezier(Point[] points, float x, float e = 0.0001f)
{
float t = 0.5f;
float h = (curveBezier(points, t).x - x) / (derivative(points, t).x - 1);
while (Math.Abs(h) >= e)
{
h = (curveBezier(points, t).x - x) / (derivative(points, t).x - 1);
t -= h;
}
return t;
}
. x, y . , . Unity, .
public Point[] points;
public GameObject prefab;
public int length;
private void Start()
{
for(int i = 0; i < length; i++)
{
GameObject block = Instantiate(prefab) as GameObject;
float t = timeBezier(points, points[0] + (points[points.Length-1].x – points[0].x) * i / length);
block.name = i.ToString();
block.transform.parent = transform;
block.transform.position = transform.position + new Vector3(curveBezier(points, t).x, curveBezier(points, t).y, 0);
}
}
z x, .
public Point[] px, pz;
public GameObject prefab;
public int length;
private void Start()
{
for(int i = 0; i < length; i++)
{
for(int j = 0; j < length; j++)
{
GameObject block = Instantiate(prefab) as GameObject;
float tx = timeBezier(points, px[0] + (px[px.Length-1].x – px[0].x) * i / length);
float tz = timeBezier(points, pz[0] + (pz[pz.Length-1].x – pz[0].x) * i / length);
block.name = i.ToString() + “ “ + j.ToString();
block.transform.parent = transform;
block.transform.position = transform.position + new Vector3(curveBezier(px, tx).x, (curveBezier(px, tx).y + curveBezier(pz, tz).y), curveBezier(pz, tz).x);
}
}
}
, , . , , Random() System. , , . – NextDouble(), float, 0 1 .
2.
[3]. , x, t.
1) x y x.
Point curveLagrange(Point points, float x) { Vector2 curve = new Vector2(x, 0);
for(int i = 0; i < points.Length; i++)
{
float dx = points[i].y;
for (int k = 0; k < points.Length; k++)
if (k != i)
dx *= (x - points[k].x) / (points[i].x - points[k].x);
curve.y += dx;
}
return curve; }
2) Start() .
for(int i = 0; i < length; i++)
{
GameObject block = Instantiate(prefab) as GameObject;
block.name = i.ToString();
block.transform.parent = transform;
block.transform.position = transform.position + new Vector3(curveLagrange(points, points[0].x + (points[points.Length - 1].x - points[0].x) * (float)i / (float)(length - 1)).x, curveLagrange(points, points[0].x + (points[points.Length - 1].x - points[0].x) * (float)i / (float)(length - 1)).y);
}
, .
( Unity).
for(int i = 0; i < size_x; i++)
{
for(int j = 0; j < size_z; j++)
{
GameObject block = Instantiate(prefab) as GameObject;
block.transform.parent = transform;
block.transform.position = transform.position + new Vector3(i, Mathf.PerlinNoise(i, j), j);
}
}
, , . , . , .
:
[1] – https://en.wikipedia.org/wiki/B%C3%A9zier_curve
[2] – https://en.wikipedia.org/wiki/Newton%27s_method
[3] – https://en.wikipedia.org/wiki/Lagrange_polynomial