星系地图生成器总是允许一些恒星系统接触
Galaxy map generator always allows some star systems to touch
我正在使用程序生成创建一个 List<int[]>
,其中每个 int[]
都包含星系中恒星系统的 x 和 y 坐标。我使用最小距离参数来避免将恒星系统彼此靠得太近。不过,这似乎并不重要,我总能得到至少几个接触的恒星系统。
如何才能让最小距离为 2 或更大,生成的坐标永远不会接触?
using System;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// Procedurally generates a new galaxy.
///
/// This class uses a relatively complex manner of ensuring no solar systems are
/// too close to each other. This is designed to allow slower hardware to handle
/// the generation fairly quickly.
///
/// To achieve this, the program will break the given area of the galaxy into
/// smaller squares. Each square will be given several solar systems that are at
/// least a user-given distance apart. Then each square will be placed within
/// the galaxy at the same user-given distance apart from each other. This
/// ensures no solar systems are within the given distance from one another.
/// </summary>
public class GalGen
{
/// <summary>
/// Generates a new galaxy.
///
/// This generates a new galaxy by creating many small sections of the galaxy
/// and combining them together.
/// </summary>
/// <param name="galaxyHeight"> The height of the galaxy in sections. </param>
/// <param name="galaxyWidth"> The width of the galaxy in sections. </param>
/// <param name="sectionSize"> The height and width of galaxy sections in tiles. </param>
/// <param name="minStars"> The lowest possible amount of stars per section. </param>
/// <param name="maxStars"> The highest possible amount of stars per section. </param>
/// <param name="minDistance"> Both the minimum distance between star
/// systems, and the distance of each sector from it's neighbors and the
/// borders of the galaxy. </param>
/// <returns> A List<int[]> of (x, y) star system locations </returns>
public static List<int[]>
generate(int galaxyHeight, int galaxyWidth, int sectionSize, int minStars, int maxStars, int minDistance)
{
Random rand = new Random();
List<int[]> galaxy = new List<int[]>(2_500);
for (int x = 0; x < galaxyWidth; x++)
{
for (int y = 0; y < galaxyHeight; y++)
{
// randomly determine the amount of star systems in the section
int stars = rand.Next(minStars, maxStars + 1);
// create the section
List<int[]> section = createSection(sectionSize, minDistance, stars);
// adjust the section's position according to it's placement in the galaxy
section = adjustSystemPosition(section, sectionSize, x, y);
// add the star system's coordinates to the galaxy
galaxy = galaxy.Concat(section).ToList();
}
}
return galaxy;
}
/// <summary>
/// Adjusts the position of the star systems in a section according to their
/// position in the galaxy, as indicated by the y and x arguments.
/// </summary>
/// <param name="section"> The section to modify. </param>
/// <param name="x"> The amount of sections to the left of the section argument. </param>
/// <param name="y"> The amount of sections above the section argument. </param>
/// <returns> A List<int[]> of (x, y) star system coordinates. </returns>
public static List<int[]>
adjustSystemPosition(List<int[]> section, int sectionSize, int x, int y)
{
for (int i = 0; i < section.Count; i++)
{
int xPos;
int yPos;
// adjust for tile size
xPos = section[i][0] << 5; // 32
yPos = section[i][1] << 5; // 32
// adjust for section position
xPos += (x * sectionSize) << 5; // 32
yPos += (y * sectionSize) << 5; // 32
// adjust for section borders
xPos += x << 5; // 32
yPos += y << 5; // 32
section[i] = new int[] {xPos, yPos};
}
return section;
}
/// <summary>
/// Generates a small, square, section of the galaxy.
/// </summary>
/// <param name="sectionSize"> The height and width of the galaxy section. </param>
/// <param name="minDistance"> The minimum distance between star systems. </param>
/// <param name="stars"> The amount of stars to create. </param>
/// <returns> A List<int[]> of (x, y) star system locations. </returns>
private static List<int[]>
createSection(int sectionSize, int minDistance, int stars)
{
Random rand = new Random();
List<int[]> section = new List<int[]>(stars);
// determine the first star system's location
int x = rand.Next(0, sectionSize + 1);
int y = rand.Next(0, sectionSize + 1);
int[] location = new int[] {x, y};
section.Add(location);
for(int i = (stars - 1); i > 0; i--)
{
// create new star coordinates
x = rand.Next(0, sectionSize + 1);
y = rand.Next(0, sectionSize + 1);
location = new int[] {x, y};
// avoid locations that would put it too close to other stars
for(int j = 0; j < section.Count; j++)
{
if (findDistance(location, section[j]) < minDistance)
{
j = 0; // restart loop
// find new potential star coordinates
x = rand.Next(0, sectionSize);
y = rand.Next(0, sectionSize);
location = new int[] {x, y};
}
}
section.Add(location);
}
return section;
}
/// <summary>
/// Finds the distance between two stars.
/// </summary>
/// <param name="star1"> The coordinates of the first star </param>
/// <param name="star2"> The coordinates of the second star. </param>
/// <returns> The distance between the two stars. </returns>
private static double findDistance(int[] star1, int[] star2)
{
double exponent1 = Math.Pow((star1[0] - star2[0]), 2.0);
double exponent2 = Math.Pow((star1[1] - star2[1]), 2.0);
return Math.Sqrt(exponent1 + exponent2);
}
}
问题出在这一段:
for(int j = 0; j < section.Count; j++)
{
if (findDistance(location, section[j]) < minDistance)
您遍历所有这些并在它们接近时重新生成。然而,它可能只会生成与前一个碰撞的坐标。
改为这样做:
(用于演示目的的最小更改。适当的解决方案会有所不同。而且如果没有空间,它永远不会完成!)
for(int i = (stars - 1); i > 0; i--)
{
do {
bool collisionFound = false;
// create new star coordinates
x = rand.Next(0, sectionSize + 1);
y = rand.Next(0, sectionSize + 1);
location = new int[] {x, y};
// avoid locations that would put it too close to other stars
for(int j = 0; j < section.Count; j++)
{
if (findDistance(location, section[j]) < minDistance)
{
collisionFound = true;
break;
}
}
} while (collisionFound);
section.Add(location);
}
我正在使用程序生成创建一个 List<int[]>
,其中每个 int[]
都包含星系中恒星系统的 x 和 y 坐标。我使用最小距离参数来避免将恒星系统彼此靠得太近。不过,这似乎并不重要,我总能得到至少几个接触的恒星系统。
如何才能让最小距离为 2 或更大,生成的坐标永远不会接触?
using System;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// Procedurally generates a new galaxy.
///
/// This class uses a relatively complex manner of ensuring no solar systems are
/// too close to each other. This is designed to allow slower hardware to handle
/// the generation fairly quickly.
///
/// To achieve this, the program will break the given area of the galaxy into
/// smaller squares. Each square will be given several solar systems that are at
/// least a user-given distance apart. Then each square will be placed within
/// the galaxy at the same user-given distance apart from each other. This
/// ensures no solar systems are within the given distance from one another.
/// </summary>
public class GalGen
{
/// <summary>
/// Generates a new galaxy.
///
/// This generates a new galaxy by creating many small sections of the galaxy
/// and combining them together.
/// </summary>
/// <param name="galaxyHeight"> The height of the galaxy in sections. </param>
/// <param name="galaxyWidth"> The width of the galaxy in sections. </param>
/// <param name="sectionSize"> The height and width of galaxy sections in tiles. </param>
/// <param name="minStars"> The lowest possible amount of stars per section. </param>
/// <param name="maxStars"> The highest possible amount of stars per section. </param>
/// <param name="minDistance"> Both the minimum distance between star
/// systems, and the distance of each sector from it's neighbors and the
/// borders of the galaxy. </param>
/// <returns> A List<int[]> of (x, y) star system locations </returns>
public static List<int[]>
generate(int galaxyHeight, int galaxyWidth, int sectionSize, int minStars, int maxStars, int minDistance)
{
Random rand = new Random();
List<int[]> galaxy = new List<int[]>(2_500);
for (int x = 0; x < galaxyWidth; x++)
{
for (int y = 0; y < galaxyHeight; y++)
{
// randomly determine the amount of star systems in the section
int stars = rand.Next(minStars, maxStars + 1);
// create the section
List<int[]> section = createSection(sectionSize, minDistance, stars);
// adjust the section's position according to it's placement in the galaxy
section = adjustSystemPosition(section, sectionSize, x, y);
// add the star system's coordinates to the galaxy
galaxy = galaxy.Concat(section).ToList();
}
}
return galaxy;
}
/// <summary>
/// Adjusts the position of the star systems in a section according to their
/// position in the galaxy, as indicated by the y and x arguments.
/// </summary>
/// <param name="section"> The section to modify. </param>
/// <param name="x"> The amount of sections to the left of the section argument. </param>
/// <param name="y"> The amount of sections above the section argument. </param>
/// <returns> A List<int[]> of (x, y) star system coordinates. </returns>
public static List<int[]>
adjustSystemPosition(List<int[]> section, int sectionSize, int x, int y)
{
for (int i = 0; i < section.Count; i++)
{
int xPos;
int yPos;
// adjust for tile size
xPos = section[i][0] << 5; // 32
yPos = section[i][1] << 5; // 32
// adjust for section position
xPos += (x * sectionSize) << 5; // 32
yPos += (y * sectionSize) << 5; // 32
// adjust for section borders
xPos += x << 5; // 32
yPos += y << 5; // 32
section[i] = new int[] {xPos, yPos};
}
return section;
}
/// <summary>
/// Generates a small, square, section of the galaxy.
/// </summary>
/// <param name="sectionSize"> The height and width of the galaxy section. </param>
/// <param name="minDistance"> The minimum distance between star systems. </param>
/// <param name="stars"> The amount of stars to create. </param>
/// <returns> A List<int[]> of (x, y) star system locations. </returns>
private static List<int[]>
createSection(int sectionSize, int minDistance, int stars)
{
Random rand = new Random();
List<int[]> section = new List<int[]>(stars);
// determine the first star system's location
int x = rand.Next(0, sectionSize + 1);
int y = rand.Next(0, sectionSize + 1);
int[] location = new int[] {x, y};
section.Add(location);
for(int i = (stars - 1); i > 0; i--)
{
// create new star coordinates
x = rand.Next(0, sectionSize + 1);
y = rand.Next(0, sectionSize + 1);
location = new int[] {x, y};
// avoid locations that would put it too close to other stars
for(int j = 0; j < section.Count; j++)
{
if (findDistance(location, section[j]) < minDistance)
{
j = 0; // restart loop
// find new potential star coordinates
x = rand.Next(0, sectionSize);
y = rand.Next(0, sectionSize);
location = new int[] {x, y};
}
}
section.Add(location);
}
return section;
}
/// <summary>
/// Finds the distance between two stars.
/// </summary>
/// <param name="star1"> The coordinates of the first star </param>
/// <param name="star2"> The coordinates of the second star. </param>
/// <returns> The distance between the two stars. </returns>
private static double findDistance(int[] star1, int[] star2)
{
double exponent1 = Math.Pow((star1[0] - star2[0]), 2.0);
double exponent2 = Math.Pow((star1[1] - star2[1]), 2.0);
return Math.Sqrt(exponent1 + exponent2);
}
}
问题出在这一段:
for(int j = 0; j < section.Count; j++)
{
if (findDistance(location, section[j]) < minDistance)
您遍历所有这些并在它们接近时重新生成。然而,它可能只会生成与前一个碰撞的坐标。
改为这样做: (用于演示目的的最小更改。适当的解决方案会有所不同。而且如果没有空间,它永远不会完成!)
for(int i = (stars - 1); i > 0; i--)
{
do {
bool collisionFound = false;
// create new star coordinates
x = rand.Next(0, sectionSize + 1);
y = rand.Next(0, sectionSize + 1);
location = new int[] {x, y};
// avoid locations that would put it too close to other stars
for(int j = 0; j < section.Count; j++)
{
if (findDistance(location, section[j]) < minDistance)
{
collisionFound = true;
break;
}
}
} while (collisionFound);
section.Add(location);
}