Unknown universe

Have you made some tiny manual like :

W,A,S,D : scroll the map

Left click on a fleet : select the fleet
Then

  • right click on a planet : send the fleet to the planet

Left click on a unit : select the unit
Then :

  • right click on a fleet : send the unit in the fleet
  • right click on the unit : modify/info/…

It can help to see problem (and you can put it with your download link ;))

Yes, I did. People don’t read the manual and get pissed.

So I included all ingame buttons shown if you click “Options” when starting the game and am trying to make everything as obvious as possible without the help of a manual. Though I am far from reaching that goal, which is why I need help from you guys with comments and these kind of stuff! :slight_smile:

Eagerly waiting for the next version

Take your time though, I don’t want to test a crashing game! xD

:slight_smile:

Wondering, do you rather want a quick version (tommorow) with a few noticeable changes - or do you prefer a slow but a whole new version there everything feel new and fresh (next week)?

If you have to spend a lot of time making a working version, I can wait. It’s the process and the end result that’s exciting anyway! ;D

There’s already a working version, question is rather if the you the players rather prefer a more “new” experience than a “patched” experience. :wink:

The current trend today is that people prefer a “patched” experience. A really good example is Legends of Yore: fast and small updates. People feel as if the game evolves with them.

I know some people dislike when they don’t feel it revolutionary as it’s a waste of their time to download it over and over again. So I thought I had to ask. :slight_smile:

Hot-keys work like this: When selecting a star and typing 0-9 the object which you have researched in that order is built. Regular army is always 0 and then after 0 it’s what’s researched thereafter.

You can open a tab with the help of keys (r = research, v = minimap, n = menu, c = society).

To launch in window/no border view ingame go to options after launching the game and click on your preferred configuration. :slight_smile:

You move over the map with the help of the mouse by moving the mouse-pointer towards the borders of the map (which will make it move in the direction your mouse is in, speed of the movement is regulated by the speed of the game).

http://www.mediafire.com/?6p9sjszglyb0aku

Working mainly on making planets more interactive.

Having mainly problems with making both “earth” and gas-giants look good at the same time. Gas-giants currently has a bad point of looking like stars currently.

Though here is the current mockup of a few planetary systems:

After a little bit of work I got the version of planets I probably keep for a while (suggestions/comments upon the planets are highly approved):

I think you are doing rendering base on Perlin noise ? 2D ? 3D ? Sinus one ? Inverse one ? …

The “stripped” planets look good. You can add some irregularity to improve it.
The dotted one are too dark (too mush contrast), you might use another perlin noise function too.
Pure magenta is uggly/unrealistic and pure blue is too dark.
Your light is from the front, it should be better to make come from the upper left.
When a planet have an atmospher, there is a small halo around it.

For “earth” planet, you can try a two pass rendering :

  • perlin noise (with a lower persistant than the one you use for dotted one) with a correction on the poles (something like +1 on poles and +0 at equator) with a gradiant blue (big range) -> yellow (small range) -> light green (medium) -> dark green (medium) -> brown (medium / small) -> white (small). It will create ground
  • perlin noise (…) with a gradiant transparent (big) -> light grey (small) -> white (small/medium) for cloud.

You can adjust a bit colors and ranges to make diversity.

Using a by myself modified Perlin noise (2D).

Thanks for the suggestions, will try to implement them! :slight_smile:

Do you happen to know any good algorithm to calculate the light on a sphere from a given position (so I can move the light to the top left)? Currently I’m just using a simple circle/distance algorithm.

I suppose that you are only using the “basic” output of the perlin nose : P(x,y) = sum( octave(i,x,y) * persistance^i ).
If you take this value and use different function, you can have different aspect :

  • sin(P(x,y))
  • 1/(1+P(x,y))

You can simply shift the center of your circle or you can use an “accurate” lighting :

Create a light “vector” (play with it to change direction) :

double lx = 2;
double ly = 2;
double lz = 4;

Norme the light “vector” :

double norm = Math.sqrt(lx*lx+ly*ly+lz*lz);
lx /= norm;
ly /= norm;
lz /= norm;

Then for each pixel, calculate lighting ( x and y are between -1 and 1, and Math.sqrt(xx+yy) < 1) :

double z = Math.sqrt(1-x*x+y*y);
double lighting = x*lx+y*ly+z*lz;

if lighting < 0, its mean there is not lighting… but you should impose a minimum lighting.

if(lighting < 0.2) { lighting = 0.2; }

Darmn… you make me want to do some generative planet ;D (psss quite some time that I will not pass on my projects :P)

It is far from uptimized and the code is a lot messy :wink:

Class for the “rendering”


package net.bonbonchan.planet;

import java.awt.image.BufferedImage;

/**
 *
 * @author Bonbon-Chan
 */
public class Planet
{
  private BufferedImage img;
  
  private int           width;
  private int           height;
  private double        halfX;
  private double        halfY;
    
  private double        lx;
  private double        ly;
  private double        lz;
  private double        light;

  public Planet(BufferedImage img,double lx,double ly,double lz,double light)    
  {
    this.img = img;
    width = img.getWidth();
    halfX = width/2.0f;
    
    height = img.getWidth();
    halfY = height/2.0f;
    
    double norm = Math.sqrt(lx*lx+ly*ly+lz*lz);
    
    this.lx = lx/norm;
    this.ly = ly/norm;
    this.lz = lz/norm;
    
    this.light = light;
  }
  
  public BufferedImage gen(Gradiant g,Height h,double size)
  {
    for(int x=0;x<width;x++)
    {
      for(int y=0;y<width;y++)
      {
        double xx = (x-halfX)/size;
        double yy = (y-halfY)/size;
        
        double d = xx*xx+yy*yy;
        
        if(d<=1.0)
        {
          double zz = Math.sqrt(1-d);
          
          double val = h.get(xx, yy, zz);
          int    v = (int)(val*255)&0xFF;
                    
          int c = g.colors[v];
          
          double l = xx*lx+yy*ly+zz*lz;
          if(l<light) { l = light; }
          
          int red = (int)(((c>>16)&0xFF)*l);
          int green = (int)(((c>>8)&0xFF)*l);
          int blue = (int)(((c)&0xFF)*l);
          int alpha = c&0xFF000000;
          
          img.setRGB(x,y, alpha+(red<<16)+(green<<8)+(blue));
        }
        else
        {
          img.setRGB(x, y, 0x00000000);
        }
      }
    }
    
    return img;
  }
}

Class Gradiant


package net.bonbonchan.planet;

/**
 *
 * @author Bonbon-Chan
 */
public class Gradiant
{
  public int colors[];
  public int index;
  public int oldColor;
  
  public Gradiant()
  {
    colors = new int[256];
  }
  
  public void start(int c)
  {
    oldColor = c;
    index = 0;
  }
  
  public void add(int i,int c)
  {
    for(int j=index;j<=i;j++)
    {
      colors[j] = interpol(oldColor,c, ((j-index)*255)/(i-index) );
    }
      
    index = i;
    oldColor = c;
  }
  
  public int interpol(int c1,int c2,int a)
  {   
    int r1 = c1&0x00FF;
    int r2 = c2&0x00FF;
    
    int g1 = (c1>>8)&0x00FF;
    int g2 = (c2>>8)&0x00FF;
    
    int b1 = (c1>>16)&0x00FF;
    int b2 = (c2>>16)&0x00FF;
    
    int a1 = (c1>>24)&0x00FF;
    int a2 = (c2>>24)&0x00FF;
    
    r1 = ((r1*(255-a))/255+(r2*a)/255)&0xFF;
    g1 = ((g1*(255-a))/255+(g2*a)/255)&0xFF;
    b1 = ((b1*(255-a))/255+(b2*a)/255)&0xFF;
    a1 = ((a1*(255-a))/255+(a2*a)/255)&0xFF;
    
    return (a1<<24)+(b1<<16)+(g1<<8)+r1;
  }
}

Interface for rendering data


package net.bonbonchan.planet;

/**
 *
 * @author Bonbon-Chan
 */
public interface Height
{
   double get(double x,double y,double z);
}

Dotted planet


package net.bonbonchan.planet;

import java.util.Random;

/**
 *
 * @author Bonbon-Chan
 */
public class Dotted implements Height
{
    private double x[];
    private double y[];
    private double z[];
    private double w[];
    
    private int    nb;
    private Random random;
    
    public Dotted(long seed)
    {
      random = new Random(seed);
              
      nb = random.nextInt(100)+50;
      
      x = new double[nb];
      y = new double[nb];
      z = new double[nb];
      w = new double[nb];
      
      for(int i=0;i<nb;i++)
      {
        x[i] = random.nextDouble()*2-1;
        y[i] = random.nextDouble()*2-1;
        z[i] = random.nextDouble()+0.2;
        
        double dist = Math.sqrt(x[i]*x[i]+y[i]*y[i]+z[i]*z[i]);
        
        x[i] /= dist;
        y[i] /= dist;
        z[i] /= dist;
        
        w[i] = random.nextDouble()*0.3+0.1;
      }
    }
    
    @Override
    public double get(double px, double py, double pz)
    {
      double v = 0;  
      
      for(int i=0;i<nb;i++)
      {
        double dx = px-x[i];
        double dy = py-y[i];
        double dz = pz-z[i];
        
        double dist = (w[i]-Math.sqrt(dx*dx+dy*dy*2+dz*dz))/w[i];
        
        if(dist>v) { v = dist; }
      }
       
      return v;
    }     
}

Stripped planet


package net.bonbonchan.planet;

import java.util.Random;

/**
 *
 * @author Bonbon-Chan
 */
public class Stripped implements Height
{    
    private double x[];
    private double y[];
    private double z[];
    private double w[];
    
    private int    nb;
    private Random random;
    
    private Perlin1D perlin;
    
    public Stripped(long seed)
    {
      random = new Random(seed);
        
      perlin = new Perlin1D(random.nextLong(),6,0.8);
      
      nb = random.nextInt(10);
      
      x = new double[nb];
      y = new double[nb];
      z = new double[nb];
      w = new double[nb];
      
      for(int i=0;i<nb;i++)
      {
        x[i] = random.nextDouble()*2-1;
        y[i] = random.nextDouble()*1.5-0.75;
        z[i] = random.nextDouble();
        w[i] = random.nextDouble()*0.3+0.1;
      }
    }
    
    @Override
    public double get(double px, double py, double pz)
    {
      double v2 = Math.sin(perlin.getRaw(py*0.5+0.5)*5);
      double v = 0;  
      
      for(int i=0;i<nb;i++)
      {
        double dx = px-x[i];
        double dy = py-y[i];
        double dz = pz-z[i];
        
        double dist = (w[i]-Math.sqrt(dx*dx+dy*dy*2+dz*dz))/w[i];
        
        if(dist>v) { v = dist; }
      }
       
      return v2 + v;
    }     
}

Earth planet


package net.bonbonchan.planet;

/**
 *
 * @author Bonbon-Chan
 */
public class Earth implements Height
{
    Perlin3D p;
    
    public Earth()
    {
      p = new Perlin3D(System.currentTimeMillis(),7,0.6);
    }
    
    @Override
    public double get(double x, double y, double z)
    {
      double v = Math.sin(p.getRaw(x*0.5+0.5, y*0.5+0.5, z*0.5+0.5)*20)*0.48+0.5;
      
      if(v>1) { v = 1; }
      
      return v;
    }     
}

Clouds


package net.bonbonchan.planet;

import java.util.Random;

/**
 *
 * @author Bonbon-Chan
 */
public class Cloud implements Height
{
    private static final double COEF = 0.5/Math.exp(1)*Math.PI;
    
    Perlin3D p;
    
    private double x[];
    private double y[];
    private double z[];
    private double w[];
    
    private Random random;
    private int    nb;
    
    public Cloud(long seed)
    {
      random = new Random(seed);
        
      nb = random.nextInt(5)+5;
      
      p = new Perlin3D(System.currentTimeMillis(),6,0.9);
      
      x = new double[nb];
      y = new double[nb];
      z = new double[nb];
      w = new double[nb];
      
      for(int i=0;i<nb;i++)
      {
        x[i] = Math.random()*2-1;
        y[i] = Math.random()*2-1;
        z[i] = Math.random()+0.2;
        
        double dist = Math.sqrt(x[i]*x[i]+y[i]*y[i]+z[i]*z[i]);
        
        x[i] /= dist;
        y[i] /= dist;
        z[i] /= dist;
        
        w[i] = Math.random()*0.2+0.1;
      }
    }
    
    @Override
    public double get(double px, double py, double pz)
    {
      double v = p.getRaw(px*0.5+0.5, py*0.5+0.5, pz*0.5+0.5);
        
      double v2 = 0;  
      
      for(int i=0;i<nb;i++)
      {
        double dx = px-x[i];
        double dy = py-y[i];
        double dz = pz-z[i];
        
        double dist = (w[i]-Math.sqrt(dx*dx+dy*dy*3+dz*dz))/w[i];
        
        v2 += Math.sin(Math.exp(dist));
      }
      
      if(v2>1) { v2 = 1; }
      
      v2 = v2*v;
      
      if(v2>1) { v2 = 1; }
      
      return v2;
    }     
}

My perlin classes


package net.bonbonchan.planet;



import java.util.Random;

public class Perlin1D
{
   protected int rnd_size;

   protected double rnd[];
   protected double persistance = 0.7f;
   protected int    octave      = 5;
   private   double totalPersistance;
   
   public Perlin1D(long seed,int octave,double persistance)
   {
     rnd_size = 1<<octave;

     rnd = new double[rnd_size];
     Random random = new Random();
     random.setSeed(seed);

     this.persistance = persistance;
     this.octave = octave;

     for(int x=0;x<rnd_size;x++)
     {
       rnd[x] = random.nextDouble();
     }
     
     totalPersistance = 0;
     double c = 1;

     for(int i=0;i<octave;i++)
     {
       totalPersistance += c;
       c = c*persistance;
     }
   }

   public double getRandom(double x)
   {
     int ix = (int)(Math.floor(x));
     double dx = x-ix;
     
     double v1 = rnd[ix];
     double v2 = rnd[ix+1];
     
     return interpole(v1,v2,dx);
   }

   public double getRaw(double x)
   {
     double value = 0;
     double c = 1;

     for(int i=0;i<octave;i++)
     {
       int size = 1<<i;

       double v = getRandom(x*size);

       value = value + v*c;
       c = c*persistance;
     }

     return value/totalPersistance;
   }

  public double interpole(double a,double b,double x)
  {
    float y = (float)Math.sin((x-0.5)*Math.PI)*0.5f+0.5f;
      
    return  a*(1-y) + b*y;
  }
}


package net.bonbonchan.planet;



import java.util.Random;

public class Perlin3D
{
   protected int rnd_size;

   protected double rnd[][][];
   protected double persistance = 0.7f;
   protected int    octave      = 5;
   private   double totalPersistance;
   
   public Perlin3D(long seed,int octave,double persistance)
   {
     rnd_size = 1<<octave;

     rnd = new double[rnd_size][rnd_size][rnd_size];
     Random random = new Random();
     random.setSeed(seed);

     this.persistance = persistance;
     this.octave = octave;

     for(int x=0;x<rnd_size;x++)
     {
       for(int y=0;y<rnd_size;y++)
       {
         for(int z=0;z<rnd_size;z++)
         {
           rnd[x][y][z] = random.nextDouble();
         }
       }
     }
     
     totalPersistance = 0;
     double c = 1;

     for(int i=0;i<octave;i++)
     {
       totalPersistance += c;
       c = c*persistance;
     }
   }

   public double getRandom(double x,double y,double z)
   {
     int ix = (int)(Math.floor(x));
     double dx = x-ix;
     int iy = (int)(Math.floor(y));
     double dy = y-iy;

     int iz = (int)(Math.floor(z));
     double dz = z-iz;
     
     double v1 = rnd[ix][iy][iz];
     double v2 = rnd[ix+1][iy][iz];
     double v3 = rnd[ix][iy+1][iz];
     double v4 = rnd[ix+1][iy+1][iz];

     double v12 = interpole(v1,v2,dx);
     double v34 = interpole(v3,v4,dx);

     double vv = interpole(v12,v34,dy);
     
     double w1 = rnd[ix][iy][iz+1];
     double w2 = rnd[ix+1][iy][iz+1];
     double w3 = rnd[ix][iy+1][iz+1];
     double w4 = rnd[ix+1][iy+1][iz+1];

     double w12 = interpole(w1,w2,dx);
     double w34 = interpole(w3,w4,dx);

     double ww = interpole(w12,w34,dy);
     
     return interpole(vv,ww,dz);
   }

   public double getRaw(double x,double y,double z)
   {
     double value = 0;
     double c = 1;

     for(int i=0;i<octave;i++)
     {
       int size = 1<<i;

       double v = getRandom(x*size,y*size,z*size);

       value = value + v*c;
       c = c*persistance;
     }

     return value/totalPersistance;
   }

  public double interpole(double a,double b,double x)
  {
    float y = (float)Math.sin((x-0.5)*Math.PI)*0.5f+0.5f;
      
    return  a*(1-y) + b*y;
  }
}

And finally how I use all this


Gradiant g = new Gradiant();
    Height   h = null;
    float hsv[] = new float[3];
    
    int type = (int)(Math.random()*3);
    
    hsv[0] = (float)Math.random()*206f/360f;   
    hsv[1] = (float)(Math.random()*0.2+0.8);
    hsv[2] = (float)(Math.random()*0.1+0.9);
        
    switch(type)
    {
      case 0:
        h = new Stripped(System.currentTimeMillis());
          
        g.start( Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]) );
        g.add( 128, Color.HSBtoRGB(hsv[0],
                                   hsv[1]*(float)(Math.random()*0.25+0.75),
                                   hsv[2]*(float)(Math.random()*0.75+0.25)) );
        g.add(255, Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]) );
        
        lbImage.setIcon(
              new ImageIcon(planet.gen(g, h, 90))
            );
        
        break;
      case 1:
        h = new Earth();
          
        g.start(   Color.HSBtoRGB(0.608f, 0.52f, 0.77f) );
        fillRandom(g,5,100,0.608f,0.52f,0.77f);
        fillRandom(g,105,115,0.15f,0.64f,1.00f);
        fillRandom(g,120,180,0.28f,0.47f,0.99f);
        fillRandom(g,185,215,0.28f,0.69f,0.68f);
        fillRandom(g,220,245,0.055f,0.62f,0.61f);        
        g.add( 250, 0xFFFFFFFF );
        g.add( 255, 0xFFFFFFFF );

        BufferedImage img1 = planet.gen(g,h,85);
        
        h = new Cloud(System.currentTimeMillis());
          
        g.start( 0x00000000 );
        g.add( 10, 0x00d4d4d4 );        
        g.add( 64, 0xFFd4d4d4 );
        g.add( 125, 0xFFFFFFFF );
        g.add( 255, 0xFFFFFFFF );
        
        BufferedImage img2 = cloud.gen(g,h,90);
       
        Graphics g2d = img1.getGraphics();
        g2d.drawImage(img2, 0, 0, null);
        g2d.dispose();
        
        lbImage.setIcon(
              new ImageIcon(img1)
            );
        
        break;
      default:
        h = new Dotted(System.currentTimeMillis());
          
        g.start( Color.HSBtoRGB(hsv[0], hsv[1], hsv[2]) );
        g.add( 255, Color.HSBtoRGB(hsv[0], hsv[1]*0.75f, hsv[2]*0.6f) );
          
        lbImage.setIcon(
              new ImageIcon(planet.gen(g, h, 90))
            );
          
    }

private void fillRandom(Gradiant gradiant,int a,int b,float h,float v,float s)
{
   for(int i=a;i<=b;i+=5)
   {
     gradiant.add(i, Color.HSBtoRGB(h,
                           s*(float)(Math.random()*0.1+0.9),
                           v*(float)(Math.random()*0.2+0.8)));
   }
}

If it give you new idea to make your planets :D.

Wow! o.O

Those are gorgeous!

You are not using any special engine? Would I be allowed to use your code in exchange for naming you in my Credits? :slight_smile:

It was generate by the code above no special engine. I mainly work by generating data in 3D (aka double Height.get(double x,double y,double z); ).

You can do whatever you want with this code, there is nothing exceptional about it. But I advice you to REuse it and not use since I did it without really thinking about the structure or speed. The perlin noise was from an old try and I didn’t take a deep look into it. You should modify the code to use a seed to generate the same planet all the time (it didn’t put the seed in every class). There is also some adjustement to do (in the image, I just choose good ones). Some time there too mush clouds, some time there is not enought “strip”, …

edit : Little update to introduce antialiasing


package net.bonbonchan.planet;

import java.awt.image.BufferedImage;

/**
 *
 * @author Bonbon-Chan
 */
public class Planet
{
  private BufferedImage img;
  
  private int           width;
  private int           height;
  private double        halfX;
  private double        halfY;
    
  private double        lx;
  private double        ly;
  private double        lz;
  private double        light;

  private double        aliasingSize;

  public Planet(BufferedImage img,double lx,double ly,double lz,double light)    
  {
    this.img = img;
    width = img.getWidth();
    halfX = width/2.0f;
    
    height = img.getWidth();
    halfY = height/2.0f;

    aliasingSize = 1.0/halfX;

    double norm = Math.sqrt(lx*lx+ly*ly+lz*lz);
    
    this.lx = lx/norm;
    this.ly = ly/norm;
    this.lz = lz/norm;
    
    this.light = light;
  }
  
  public BufferedImage gen(Gradiant g,Height h,double size)
  {
    for(int x=0;x<width;x++)
    {
      for(int y=0;y<width;y++)
      {
        double xx = (x-halfX)/size;
        double yy = (y-halfY)/size;
        
        double d = xx*xx+yy*yy;
        
        if(d<=1.0)
        {
          double a = 1;

          if(d>=1.0-aliasingSize)
          {
            a = (1.0-d)/aliasingSize;
          }

          double zz = Math.sqrt(1-d);
          
          double val = h.get(xx, yy, zz);
          int    v = (int)(val*255)&0xFF;
                    
          int c = g.colors[v];
          
          double l = xx*lx+yy*ly+zz*lz;
          if(l<light) { l = light; }
          
          int red = (int)(((c>>16)&0xFF)*l);
          int green = (int)(((c>>8)&0xFF)*l);
          int blue = (int)(((c)&0xFF)*l);
          int alpha = (int)(((c>>24)&0xFF)*a);
          
          img.setRGB(x,y, (alpha<<24)+(red<<16)+(green<<8)+(blue));
        }
        else
        {
          img.setRGB(x, y, 0x00000000);
        }
      }
    }
    
    return img;
  }
}

Been playing around a little with your code and implemented it into the game:

Though haven’t been able to get the clouds to work on earth-like planets. It just get white.

How did you init the cloud and planet parameters (your code omitted that)?

Yes too mush clouds… I have increase the transparency part.
Try


 h = new Cloud(System.currentTimeMillis());
 
 g.start( 0x00000000 );
 g.add( 90, 0x00d4d4d4 );
 g.add( 100, 0xFFd4d4d4 );
 g.add( 175, 0xFFFFFFFF );
 g.add( 255, 0xFFFFFFFF );
        
  BufferedImage img2 = cloud.gen(g,h,60);

For the colors, I have made a limitation base on the hue value… it seems to generator a lot of green. To have the full hue palette :


hsv[0] = (float)Math.random();                  // hue 0.0-1.0
hsv[1] = (float)(Math.random()*0.2+0.8);   // saturation 0.8-1.0
hsv[2] = (float)(Math.random()*0.1+0.9);   // value 0.9-1.0

Still same problem, problem I believe is when I init the cloud and planet variable (planet = new Planet()) or when I init the light.

You omitted that from the code and I wonder what you used as I can’t seem to find any fitting parameters there?

Oh, and when I am asking, do you happen to know how I can properly change the size of your planets? My current hack seem to loose too much of the details as your planet are so large.

Hmmm, I may have done something bad while copying/pasting.

You have only to change the last parameter of Planet.gen(Gradiant g,Height h,double size). I didn’t do mush test with small planet.

I have added the creation on “ring” around planet. I will upload the whole netbean projet tonight, it will be more easier to deal with.

Added :

  • ring
  • sort a little my code :stuck_out_tongue:
  • randomize size to see the quality… not too good for small ones
  • change in gradiant (use double instead of int) to increase quality… but not much :clue:

Planets.zip

For those who just want to see the result, there is the jar in the dist folder.