# How to create 2d terrain in cocos2d-x?

#1

I want to create a 2D terrain like in this tutorial: https://www.youtube.com/watch?v=jSCsRXMiT7Q The most confusing part for me is that after generating the curve I need to map 2 textures one for the hill border and one for hill texture. How can I do this? Any tutorial in any language actually is welcome as I don’t understand the technique.

#2

This is what I do:
First I have a collection of Terrain Points that describe the basic shape of the terrain. This just contains ‘control points’.
At the start of the level I calculate points between these control points according to a number of parameters (that vary for different levels). For example a point at (0,0) followed by a point at (10,2) might just calculate a straight line between the two, whereas points closer together may calculate curves between the two. (NB coordinates are all calculated in meters and only converted to pixels before being actually drawn)

So, now I have a collection of points that describe my terrain.

Inside the 'game loop
I take the collection of ‘detail points’ and calculate the triangles needed to draw the ‘sub terrain’. That bit is easy - each triangle comprises two adjacent points and a third point low enough down that it is just off the bottom of the screen.

Using this collection of ‘triangle points’ I draw the sub terrain using this method:

WorldLayer::calculateFloorTexturePoints();

// Tell OpenGL this is the texture we’re using
GL::bindTexture2D(_subterraneanTexture->getName());

// wrap in both the S and T directions (that’s X and Y for the rest of us!)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

// Enable the arrays
GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POSITION | GL::VERTEX_ATTRIB_FLAG_TEX_COORD);

_glProgram->use();

_glProgram->setUniformsForBuiltins(_modelViewTransform);

// Give OpenGl our array of points that we want to fill
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, sizeof(Vec2), _subterraneanFloorTextureTrianglePoints);
// Give OpenGl the array of points in the texture we want to use to fill the above array of points
glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORDS, 2, GL_FLOAT, GL_FALSE, sizeof(Vec2), _subterraneanFloorTexturePoints);

// Tell OpenGl to draw the arrays we gave it
glDrawArrays(GL_TRIANGLE_STRIP, 0, _numberOfFloorSubterraneanTextureTrianglePoints);

## Now, using the original collection of detail points, I position sprites to form the surface. This, I admit, was tricky! I use a long, thin sprite, and here’s the code for drawing sprite(s) between two adjacent points of my surface…

auto pt0 = pFrom->getLocation();
auto pt1 = pTo->getLocation();

// Get the distance between the two points in Meters
float distance = pt0.getDistance(pt1);
// If the distance is greater than the width of our surface sprite, then we need to draw many sprites
double dx = pt1.x - pt0.x;
double dy = pt1.y - pt0.y;
float normal = atan(dy / dx);
dy = WIDTHOFSURFACESPRITEINMETERS * sin(normal);
dx = WIDTHOFSURFACESPRITEINMETERS * cos(normal);

// dx and dy now store x and y distance for the surface sprite, in meters

auto surfaceFrom = pt0;

do
{
Vec2 p = Utils::screenPointFromWorldPoint(surfaceFrom );
p += pFrom->getOffset(); // Add on the offset which is specified i teh pList in m but handled internally in pixels

SpriteFrameCache* frameCache = SpriteFrameCache::getInstance();
SpriteFrame* frame = frameCache->getSpriteFrameByName(pFrom->getSurfaceSpriteName());
Sprite* sprite = Sprite::createWithSpriteFrameName(pFrom->getSurfaceSpriteName());
sprite->setAnchorPoint(Vec2(0,0)); // anchor bottom left so surface is right on Box2d surface (except for applying any offset)
sprite->setSpriteFrame(frame);

// TODO: set the sprites anchor point so different sprites align differently with the surface
//		sprite.anchorPoint = from.surfaceSpriteOffset;

sprite->setPosition(p);

cocos2d::Rect rect = sprite->getTextureRect();
float width = MIN(distance, WIDTHOFSURFACESPRITEINMETERS);
rect.size.width = Utils::screenFromMeters(width);
sprite->setTextureRect(rect);
sprite->setRotation(-1 * normal);

sprite->setLocalZOrder(100);

distance -= WIDTHOFSURFACESPRITEINMETERS;

surfaceFrom.x += dx;
surfaceFrom.y += dy;

}
while (distance > 0);

So, I end up with a gl drawn subterrain topped by sprites.
I actually repeat this for a roof, too.

there’s a video on Youtube of me editing a level while developing this

#3

Thank you for you explanation @Maxxx. The core idea is the same as in this tutorial: http://www.raywenderlich.com/32954/how-to-create-a-game-like-tiny-wings-with-cocos2d-2-x-part-1 and you have added the sprites for boarders. Cool, thanks!

#4

Yes - I actually looked at that tutorial early on - then decided I wanted to design my levels not generate them, and I wanted to have a real surface.
I also looked at PRKit originally for the gl stuff - but had to translate it, and didn’t like the thought of the inefficiencies of using a generic solution rather than having a bit more control, so wrapped my own.
Using the sprites for borders is cool - you can really play around (for example, if you want you can animate the grass, or have different height grass etc.

Cheers

#5

@Maxxx you have a sample project I can see how it all comes together? Thanks for any ideas!

#6