6

Did you ever played the "Tank wars" game?

enter image description here

I'm programming this game with JavaScript + Canvas (for a personal challenge), and what I need is an algorithm for generating that random green land every time I start the game, but I'm not too good at maths, so I can't do it myself.

I don't want someone to give me the code, I only want the idea for the algorithm.

Thanks!

elp
  • 8,021
  • 7
  • 61
  • 120
user2479365
  • 613
  • 1
  • 5
  • 12

6 Answers6

5

enter image description here
(9 segments)

Fiddle demo

enter image description here
(7 segments)

The main generation function look like this:

var numOfSegments = 9;                      // split horizontal space
var segment = canvas.width / numOfSegments; // calc width of each segment
var points = [], calcedPoints;
var variations = 0.22;                      // adjust this: lower = less variations
var i;

//produce some random heights across the canvas
for(i=0; i < numOfSegments + 1; i++) {
    points.push(segment * i);
    points.push(canvas.height / 2.8 + canvas.height * variations * Math.random());
}

//render the landscape
ctx.beginPath();
ctx.moveTo(canvas.width, canvas.height);
ctx.lineTo(0, canvas.height);

calcedPoints = ctx.curve(points);           // see below

ctx.closePath();
ctx.fillStyle = 'green';
ctx.fill();

The curve() function is a separate function which generate a cardinal spline. In here you can modify it to also store tension values to make more spikes. You can also used the generated points as a basis for where and at what angle the tanks will move at.

The function for cardinal spline:

CanvasRenderingContext2D.prototype.curve = function(pts, tension, numOfSegments) {

    tension = (tension != 'undefined') ? tension : 0.5;
    numOfSegments = numOfSegments ? numOfSegments : 16;

    var _pts = [], res = [], t, i, l, r = 0,
        x, y, t1x, t2x, t1y, t2y,
        c1, c2, c3, c4, st, st2, st3, st23, st32;

    _pts = pts.concat();
    _pts.unshift(pts[1]);
    _pts.unshift(pts[0]);
    _pts.push(pts[pts.length - 2]);
    _pts.push(pts[pts.length - 1]);

    l = (_pts.length - 4);
    for (i = 2; i < l; i+=2) {

        //overrides and modifies tension for each segment.
        tension = 1 * Math.random() - 0.3;

        for (t = 0; t <= numOfSegments; t++) {
            t1x = (_pts[i+2] - _pts[i-2]) * tension;
            t2x = (_pts[i+4] - _pts[i]) * tension;      
            t1y = (_pts[i+3] - _pts[i-1]) * tension;
            t2y = (_pts[i+5] - _pts[i+1]) * tension;

            st = t / numOfSegments;
            st2 = st * st;
            st3 = st2 * st;
            st23 = st3 * 2;
            st32 = st2 * 3;

            c1 = st23 - st32 + 1; 
            c2 = -(st23) + st32; 
            c3 = st3 - 2 * st2 + st; 
            c4 = st3 - st2;

            x = c1 * _pts[i]    + c2 * _pts[i+2] + c3 * t1x + c4 * t2x;
            y = c1 * _pts[i+1]  + c2 * _pts[i+3] + c3 * t1y + c4 * t2y;

            res[r++] = x;
            res[r++] = y;               
        } //for t
    } //for i

    l = res.length;
    for(i=0;i<l;i+=2) this.lineTo(res[i], res[i+1]);

    return res;  //return calculated points
}
  • 1
    Thanks! I will study the code and implement it on my game. It's perfect – user2479365 Jun 12 '13 at 22:10
  • 1
    @user2479365 No problem. Updated answer/fiddle with minor adjustments and to show how to get the calculated points from the spline. –  Jun 12 '13 at 22:29
  • so you did want code after all – tgkprog Jun 13 '13 at 13:02
  • 1
    Ken: omg, it's epic! Thanks! I have a question: what is the "segments" thing? @tgkprog no, I won't use that code. I will study it and write it on my own, because my challenge is learn, not the game itself. – user2479365 Jun 13 '13 at 16:32
  • 1
    @user2479365 The segments for the canvas is just where to place the y points (the more segment the more slopey slope). The segments for the spline is sort of the resolution. The more segments the finer the curve. Also see this example extended here: http://stackoverflow.com/questions/17083580/i-want-to-do-animation-of-an-object-along-a-particular-path/17095740#17095740 –  Jun 13 '13 at 19:58
  • Ok, I use only 5 because of the resolution (320x480). But now I need to make the tank move, and with curved lines, I can't do it because the XY points show this: http://puu.sh/3fyhU.png The problem is that curved lines will make the game buggy. How can I make the tank understand that there are curved lines? PD: I see that you explained it in another post, but I'm too bad at maths. Could you explain it a bit more, please? – user2479365 Jun 14 '13 at 15:24
  • 1
    @user2479365 Just open a new question and others and I will try to help with that :) –  Jun 14 '13 at 20:02
  • @Ken-AbdiasSoftware the game is finished. :) not a CoD or Battlefield, but it's my first game :D https://dl.dropboxusercontent.com/u/90221277/tank%20wars/tank%20wars.html – user2479365 Jun 15 '13 at 20:10
  • @user2479365 Good work :) Simple games tend to be the most addictive ones. –  Jun 15 '13 at 21:52
3

Look into perlin noise generation, this in combination with a good smoothing algorithm can produce some pretty good terrain, and is fairly quick. There is a reference version of the code kicking around the net somewhere, which should provide you with a fairly hefty headstart

quelbe
  • 41
  • 4
  • I found another way, but thanks for Perlin Noise, I was searching for something similar for another project. +1 – user2479365 Jun 12 '13 at 22:02
1

First you need a point that is random y (between 55,65); got x=0 So this is the origin point for the green, lets keep it as x1,y1 (x1 always 0).

Then you need a random integer between 30 to 40. This is x2. And a random y which is in the range y1 + 8 to y1 + 20.

Then x3 and y3 on same principle (lets call it formula type 1)

Now you need to first get a random either -1 or 1, this will be directions of y4. So y4 can go higher than y3 or lower ... this will be formula type 2.

You need to keep a max and min y for a new y, if it crosses that then go the other way -> this will be a correction type formula 3.

Xn keeps increasing till its >= width of board.

Join the lines in a eclipses ... and looks like web searches is the way to go !

tgkprog
  • 4,493
  • 4
  • 41
  • 70
1

I am sure there are a lot of coded libraries that you could use to make this easy. But if you are trying to code this by yourself, here is my idea.

You need to define terrain from everything else. So every part of your environment is a cluster for example. You need to define how are separated these clusters, by nodes(points) for example.

You can create a polygon from a sequence of points, and this polygon can become whatever you want, in this case terrain.

See that on the image you passed, there are peaks, those are the nodes (points). Remember to define also nodes on the borders of your environment.

eLRuLL
  • 18,488
  • 9
  • 73
  • 99
1

There are surely a novel, written algorithms, either fractal as @DesertIvy pointed out or others, maybe there are libraries as well, but if you want toi generate what is in the image, it can be pretty straightforward, since it is just (slightly curved) lines between points. If you do it in phases, not trying to be correct at once, it is easy:

  1. Split x region of your game screen into sections (with some minimal and maximal width) using random (you may be slightly off in last section, but it does not matter as much, I think). Remember the x-es where sections meet (including the ones at game screen border)
  2. Prepare some data structure to include y-s as well, on previously remembered x-s. Start with leftmost.y = 0, slope = Math.random()-0.5;.
  3. Generate each next undefined y beginning with 1: right.y = left.y + slope * (right.x-left.x); as well as update slope after each y: slope += Math.random()-0.5;. Do not bother, for the moment, if it all fits into game screen.
  4. If you want arcs, you can generate "curviness" parameter for each section randomly which represent how much the middle of the line is bumped compared to straight lines.
  5. Fit the ys into the game screen: first find maximal and minimal generated y (mingeny, maxgeny) (you can track this while generating in point 4). Choose where the max and min y in game screen (minscry, maxscry) (say at the top fourth and at the bottom fourth). Then transform generated ys so that it spans between minscry and maxscry: for every point, do apoint.y = minscry + (maxscry-minscry)/(maxgeny-mingeny)*(apoint.y-mingeny).
  6. Now use lines between [x,y] points as a terrain, if you want to use "curviness", than add curvemodifier to y for any particular x in a section between leftx and rightx. The arc need not to be a circle: I would suggest a parabola or cosine which are easy to produce: var middle = (left.x+right.x)/2; var excess = (x-left)/(middle-left); and then either var curvemodifier = curviness * (1-excess*excess); or var curvemodifier = curviness * Math.cos(Math.PI/2*excess).
1

Wow...At one point I was totally addicted to tank wars.

Since you are on a learning adventure...

You might also learn about the context.globalCompositeOperation.

This canvas operation will let you grab an image of actual grass and composite it into your game.

You can randomize the grass appearance by changing the x/y of your drawImage();

Yes, the actual grass would probably be too distracting to include in your finished game, but learning about compositing would be valuable knowledge to have.

...and +1 for the question: Good for you in challenging yourself !

markE
  • 102,905
  • 11
  • 164
  • 176