|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionI wanted to write a simple strategy game and went looking for a 2D isometric engine , but most were very slow (mainly due to using GDI+) so I thought I would write my own using managed DirectX. The only one I found on line was a VB isometric engine but I wanted to use hexes. The results are pretty good so far , by using DirectX the program gives excellent performance and allows high speed scrolling, zooming etc.BackgroundWhy use hexagons? For a start board games have been using them for a long time yet the printer can deliver perfect quality. The reason is gameplay - hexes have the advantage of 6 directions without having to deal with the diagonal issues you can get from isometric tiles, in addition, when compared to pure 3D landscapes hexes have the advantage that they easily convey ownership allowing you to give areas properties etc. The main disadvantage of hexes is performance but games that emphasise gameplay and strategy dont usually have high performance . Still the perfomance is quite good using my crappy TNT32 card which only supports software vertex processing. In Part II I will post how to use heights with hexes , I have not seen this done but it is certainly possible and we can put in vertical cliffs which many 3D terrain engines can not handle. FeaturesThe engine supports the following features.
Using the codeBefore we start I need to give a disclaimer I am no DIrectX guru, so any tips would be appreciated. Second this is all pretty rushed so go easy on the grammar, spelling and poor comments in the code. The code is pretty easy to use but will require some work to bolt into your progam and the textures are hard coded at present. Basically you create the hexes and pass them to the The code requires Managed DirectX 9 and the .NET framework installed. The classes used:
Points of InterestWhen I started I first wrote The first big 3D issue was how to handle Vertexes , Texture coordinates and Indices. Since each hex corner was going to border 2 other hexes each of which could have a different texture. By default a vertex can only hold one texture coordinate. Looking futher into things multiple texture coordinates are possible but these have some limitations.
So I had a rough choice to make but mainly due to the vid card I decided that each hex would have its own vertices. This meant there were nearly 3 times as many! This vertex structure though made it simpler to render, saved me buying a new vid card (since my HD died and I needed a new HD..), and resulted in smaller vertices (5 or 6 floats instead of 11 -12) partially offsetting the additional numbers. I could then easily calculate index buffers for each terrain and leave the vertes buffer untouched. There was also the minor benefit of having vertical cliffs and hexes could be raised if I wanted to do some terrain. Calculating the vertex was pretty simple as can be seen below, for each hex there are 6 vertexes. The texture coordinates public void GenerateVertexBufferData(VertexBuffer source)
{
const float yValue = 0f;
VertexBuffer vb = source;
// Create a vertex buffer (100 customervertex) and lock it
CustomVertex.PositionNormalTextured[] verts =
(CustomVertex.PositionNormalTextured[])vb.Lock(0,0);
for (int y = 0; y < this.Y; y++)
for (int x = 0; x < this.X; x++)
{
//Hexagon hex = this.hexagons[x,y];
PointF topLeft= this.GetTopLeftPixelBoundingRectangle(x, y);
// vertex 0
verts[6*(x+ y*this.X)].SetPosition(new Vector3(topLeft.X, yValue,
topLeft.Y + this.H));
verts[6*(x+ y*this.X)].Tu = 0.0f;
verts[6*(x+ y*this.X)].Tv = (float) this.H / this.height;
// vertex 1 top
verts[6*(x+ y*this.X)+1].SetPosition(new Vector3(topLeft.X +
this.Radius, yValue, topLeft.Y));
verts[6*(x+ y*this.X)+1].Tu = 0.5f;
verts[6*(x+ y*this.X)+1].Tv = 0.0f;
// vertex 2
verts[6*(x+ y*this.X)+2].SetPosition(new Vector3( topLeft.X +
this.width, yValue, topLeft.Y + this.H ));
verts[6*(x+ y*this.X)+2].Tu = 1.0f;
verts[6*(x+ y*this.X)+2].Tv = (float) this.H/ this.height;
// vertex 3
verts[6*(x+ y*this.X)+3].SetPosition(new Vector3( topLeft.X +
this.width, yValue, topLeft.Y + this.H + this.side ));
verts[6*(x+ y*this.X)+3].Tu = 1.0f;
verts[6*(x+ y*this.X)+3].Tv = (float) (this.H + this.side) /
this.height;
// vertex 4 bottom
verts[6*(x+ y*this.X)+4].SetPosition(new Vector3( topLeft.X +
this.Radius, yValue, topLeft.Y + this.height ));
verts[6*(x+ y*this.X)+4].Tu = 0.5f;
verts[6*(x+ y*this.X)+4].Tv = 1.0f ;
// vertex 5
verts[6*(x+ y*this.X)+5].SetPosition(new Vector3( topLeft.X,
yValue, topLeft.Y + this.H + this.side ));
verts[6*(x+ y*this.X)+5].Tu = 0.0f;
verts[6*(x+ y*this.X)+5].Tv = (float) (this.H + this.side) /
this.height;
}
vb.Unlock();
}
I then had to consider how to index the vertexes. At first I used triangle fans as most doco says these are for things like HEXAGONS... That was a big mistake. Triangle fans are good for one hexagon. If I was to render 2500 (50*50) it would result in 2500 Draw calls to the device.. Ouch . Also a single fan was not possible as they were all drawn from one point. To change the structure all I had to do was update the indexes so I benchmarked all the options and at low hex counts it was all pretty even. At high hex counts triangle strips were the best by 20-50%, the second best was triangle lists. So I decided on triangle strips. The next issue was how to combine multiple hexes into one strip. The answer was to use a degenerative triangle at the end of each hex; this was done by repeating the last vertex. This means the last triangle of the current hexagon had 2 vertexes the same (eg had no area, but you can see them with the default wireframe) and the first triangle of the next hexagon. This allowed me to send all hexes of one terrain type in one big index and making big chunky calls has to be good. The second issue was culling; working out the order of the strip so it complied with culling and which allowed an efficient strip , I ended up with a strip which was 7 vertexes per hex which was pretty good I think. I then decided on a project space and decided on perspective so I could zoom in and out and I wanted a top down view. This proved a problem since whenever I aligned the camera and the viewing point exactly the image would not display at all! I left it as a little offset and hoped the math rounding of such a small difference would not get me into trouble. After much fiddling the display seemed to come together . The next hurdle was picking, eg clicking on part of the screen and showing the coordinates. I tried playing around with unproject but did not have a lot of luck (mainly due to a bug in another part but the complexity hid it) . So I went for a simpler method , I used project to calculate where the top left and bottom right corners of The last bit to get going was scrolling . I wanted to use repeat keys and the only way to get this happening was ugly if anyone else knows a cleaner way I would appreciate it . The Outstanding Issues
Historyv00 posted 22/8/2003
|
||||||||||||||||||||||