package game; import myutils.MyRandom; /** * Terrain Generator */ public class TerrainGenerator { private int mSize; private float mHillMin; private float mHillMax; private int mNumHills; private int mFlattening; private boolean mIsland; private MyRandom mMyRandom; private float mMap[]; /** * Gets */ int getSize () { return mSize; } float getHillMin() { return mHillMin; } float getHillMax() { return mHillMax; } int getNumhills() { return mNumHills; } int getFlattening() { return mFlattening; } boolean isIsland() { return mIsland; } /** * Sets */ void setHillMin(float p) { mHillMin = p; } void setHillMax(float p) { mHillMax = p; } void setHillSize(float p1, float p2) { mHillMin = p1; mHillMax = p2; } void setNumHills(int p) { mNumHills = p; } void setFlattening(int p) { mFlattening = p; } void setIsland(boolean p) { mIsland = p; } /** * Initialization */ public TerrainGenerator (int pSize, float pHillMin, float pHillMax, int pNumHills, int pFlattening, boolean pIsland, MyRandom pMyRandom) { mSize = pSize; mHillMin = pHillMin; mHillMax = pHillMax; mNumHills = pNumHills; mFlattening = pFlattening; mIsland = pIsland; mMyRandom = pMyRandom; mMap = new float [mSize*mSize]; } /** * Initialization (quick) */ public TerrainGenerator (MyRandom pMyRandom) { mSize = 640; mHillMin = -120.0f; mHillMax = 90.0f; mNumHills = 5000; mFlattening = 1; mIsland = false; mMyRandom = pMyRandom; mMap = new float [mSize*mSize]; } /** * Generate the map */ public void generate() { clear(); // Add as many hills as needed for( int i = 0; i < mNumHills; ++i ) addHill(); // Now clean it up normalize(); flatten(); } /** * Clear the map */ private void clear() { for (int x = 0; x < mSize; ++x) { for (int y = 0; y < mSize; ++y) { setCell( x, y, 0 ); } } } /** * Add a hill */ private void addHill() { // Pick a size for the hill float fRadius = mMyRandom.getRandFloat (mHillMin, mHillMax); // Pick a centerpoint for the hill float x, y; if (mIsland) { // Island code: float fTheta = mMyRandom.getRandFloat(0f, 6.28f); // This determines in which direction from the center of the map the // hill will be placed. float fDistance = mMyRandom.getRandFloat(fRadius/2, mSize/2 - fRadius); // This is how far from the center of the map the hill be placed. note // that the radius of the hill is subtracted from the range to prevent // any part of a hill from reaching the very edge of the map. x = (float) (mSize/2.0 + Math.cos(fTheta) * fDistance); y = (float) (mSize/2.0 + Math.sin(fTheta) * fDistance); // Converts theta and a distance into x and y coordinates. } else { // non-island code: x = mMyRandom.getRandFloat(-fRadius, mSize + fRadius); y = mMyRandom.getRandFloat(-fRadius, mSize + fRadius); // Note that the range of the hill is used to determine the // centerpoint. this allows hills to have their centerpoint off the // edge of the terrain as long as part of the hill is in bounds. this // makes the terrains appear continuous all the way to the edge of the // map. } // Square the hill radius so we don't have to square root the distance float fRadiusSq = fRadius * fRadius; float fDistSq; float fHeight; // Find the range of cells affected by this hill int xMin = (int) (x - fRadius - 1); int xMax = (int) (x + fRadius + 1); // Don't affect cell outside of bounds if (xMin < 0) xMin = 0; if (xMax >= mSize) xMax = mSize - 1; int yMin = (int) (y - fRadius - 1); int yMax = (int) (y + fRadius + 1); // Don't affect cell outside of bounds if (yMin < 0) yMin = 0; if (yMax >= mSize) yMax = mSize - 1; // For each affected cell, determine the height of the hill at that point // and add it to that cell for (int h = xMin; h <= xMax; ++h) { for (int v = yMin; v <= yMax; ++v) { // Determine how far from the center of the hill this point is fDistSq = (x - h) * (x - h) + (y - v) * (y - v); // Determine the height of the hill at this point fHeight = fRadiusSq - fDistSq; // Don't add negative hill values (i.e. outside the hill's radius) if (fHeight > 0) { // add the height of this hill to the cell offsetCell(h, v, fHeight); } } } } /** * Normalize (0, 1) the map */ private void normalize() { float fMin = getCell (0, 0); float fMax = getCell (0, 0); // Find the min and max for (int x = 0; x < mSize; ++x) { for (int y = 0; y < mSize; ++y) { float z = getCell (x, y); if (z < fMin) fMin = z; if (z > fMax) fMax = z; } } // Avoiding divide by zero (unlikely with floats, but just in case) if (fMax != fMin) { // Divide every height by the maximum to normalize to (0.0, 1.0) for (int x = 0; x < mSize; ++x) { for (int y = 0; y < mSize; ++y) { setCell (x, y, (getCell( x, y ) - fMin) / (fMax - fMin)); } } } else { // If the min and max are the same, then the terrain has no height, so just clear it // to 0.0. clear(); } } /** * Flatten the valleys (smooth surfaces) */ private void flatten() { // If flattening is one, then nothing would be changed, so just skip the // process altogether. if (mFlattening > 1) { for (int x = 0; x < mSize; ++x) { for (int y = 0; y < mSize; ++y) { float fFlat = 1.0f; float fOriginal = getCell (x, y); // Flatten as many times as desired for (int i = 0; i < mFlattening; ++i) { fFlat *= fOriginal; } // Put it back into the cell setCell (x, y, fFlat); } } } } /** * Get Cell */ public float getCell (int x, int y) { return (mMap[ x + (y * mSize )]); } /** * Set Cell */ private void setCell (int x, int y, float pValue) { // Set the cell mMap[x + (y * mSize)] = pValue; } /** * OffsetCell */ private void offsetCell (int x, int y, float pValue) { // Offset the cell mMap[x + (y * mSize)] += pValue; } }