Tutorial Series - A simple 3D game

Tutorials concerning the OpenGL® ES cross-platform API for full-function 2D and 3D graphics on the Google-Android platform.

Tutorial Series - A simple 3D game

Postby seed » Thu May 20, 2010 1:10 pm

Hey guys. I wanted to do a tutorial for a simple 3D game. I hope that this tutorial will help people to better understand a couple of concepts that I had trouble with when first learning how to write a game for OpenGl. Specifically, I will try to explain the following concepts that I use in my game, Lava Ball, which is coming to the Android Market soon.

(1) How to organize your game objects into classes and how to call them for rendering.
(2) Rotation of objects made simple with Quarternions.
(3) Code for creating a sphere.
(4) Collision detection framework done the right way. (Simplified)
(5) Creating the gameplay for a simple game.

Some disclaimers:

First, I am not an OpenGl expert under any circumstances. I have just learned it in the last couple months. My knowledge is not broad at all. Rather, it is limited to only that which I needed to make my game. That said, I have done pretty well in a short time and I hope that what I have learned is useful to someone else. Lava Ball is a pretty complex game that is running at between 40 and 50 frames a second on my Droid. So, I have done something right. The game I create here will in no way be nearly as complex as LavaBall. So, we should end up with something that performs exceeding well.

Second, you should note that I have limited time until Lava Ball is released. I will be rolling this tutorial out as time permits.

Finally, I am sure that there are other ways, probably even better ways, to organize and create a game. I am sure other people will chime in with comments about what I write. They will probably be right and I will probably be wrong. That said, what I did works and will work for you. Use what you want and leave the rest.

A Request:

Please check out Lava Ball 3D on the android market. Thanks.

Later today I will post "Part 1 - Vector Math Class"
Last edited by seed on Tue Jul 20, 2010 5:00 pm, edited 1 time in total.
Visit Exit 4 Gaming - http://www.exit4games.com/
Home of LavaBall - http://exit4games.com/?page_id=3
Home of Rebound - http://exit4games.com/?page_id=138
Home of Tap Crazy - http://exit4games.com/?page_id=219
Download "Caveman Pool" From the Market Today!
seed
Senior Developer
Senior Developer
 
Posts: 103
Joined: Mon Mar 15, 2010 3:22 pm

Top

Part 1 - A Vector Math Class

Postby seed » Thu May 20, 2010 1:57 pm

Vector Math is a key element to any 3D Game. You can't do anything without it. So, it seems like a great place to start. This is the vector math class that I use in my game. Hopefully this is useful to someone even if they don't follow the rest of this tutorial.

The class is pretty basic except for one thing, I implemented a simple stack. We will come back to that. Other than the unique stack, the rest of the class is self explanatory. Here is the class:


Code: Select all
public class Vector3
{
    public float x,y,z;
   
    // ****************************
    // Stack allocation and freeing
    // ****************************
    private static int STACK_SIZE = 100;
    private static Vector3[] stack;
    private static boolean initialized = false;
    private static int stack_index = 0;
     
    public static Vector3 alloc()
    {
         if (!initialized)
              init();
         
         return stack[stack_index++];
    }   
     
    public static void free()
    {
         if (!initialized)
              init();
         
         if (stack_index > 0)
              stack_index--;
    }
     
    private static void init()
    {
         stack = new Vector3[STACK_SIZE];
         for (int i=0; i<STACK_SIZE; i++)
         {
              stack[i] = new Vector3();
         }
         initialized = true;
    }
     
    // Constructor
    public Vector3()
    {
         x = 0;
         y = 0;
         z = 0;
    }

   public Vector3(float xin, float yin, float zin)
   {
      x=xin; y=yin; z=zin;
   }
   
   public void Set(float xin, float yin, float zin)
   {
      x=xin; y=yin; z=zin;
   }
   
    static public void add(Vector3 v1, Vector3 v2, Vector3 v3)
    {
         v1.x = v2.x + v3.x;
         v1.y = v2.y + v3.y;
         v1.z = v2.z + v3.z;
    }
     
    static public void subtract(Vector3 v1, Vector3 v2, Vector3 v3)
    {
         v1.x = v2.x - v3.x;
         v1.y = v2.y - v3.y;
         v1.z = v2.z - v3.z;
    }
     
    static public void copy(Vector3 v1, Vector3 v2)
    {
         v1.x = v2.x;
         v1.y = v2.y;
         v1.z = v2.z;
    }
   
    static public void reflect(Vector3 result, Vector3 dir, Vector3 normal, float damp)
    {
         float len;
         float ratio;
         float dot;
         
         Vector3 v1 = alloc();
         Vector3 v2 = alloc();
         Vector3 v3 = alloc();
         
         len = length(dir);
         normalize(v1,dir);
         normalize(v2,normal);
         inverse(v3,v1);
         dot = (1+damp)*dot_product(v3,v2);
         scale(v2,v2,dot);
         add(result,v1,v2);
         ratio = length(v1);
         scale(result,result,len*ratio);
         
         free();
         free();
         free();
    }
   
    static public void inverse(Vector3 v1, Vector3 v2)
    {
         v1.x = -v2.x;
         v1.y = -v2.y;
         v1.z = -v2.z;
    }
   
   static public void scale(Vector3 v1, Vector3 v2, float s)
   {
      v1.x = v2.x * s;
      v1.y = v2.y * s;
      v1.z = v2.z * s;
   }

    static public float length(Vector3 v1)
    {
         return (float) Math.sqrt(v1.x * v1.x + v1.y * v1.y + v1.z * v1.z);
    }
     
    static public void normalize(Vector3 result, Vector3 v1)
    {
         float len = length(v1);
         if (len != 0)
         {
              result.x = v1.x / len;
              result.y = v1.y / len;
              result.z = v1.z / len;
         }
    }
     
    // Written with temp vector in order to work when result is also one of   
    // other two parameters
    static public void cross_product(Vector3 result, Vector3 v1, Vector3 v2)
    {
         Vector3 temp = alloc();
         
         temp.x = v1.y * v2.z - v1.z * v2.y;
         temp.y = v1.z * v2.x - v1.x * v2.z;
         temp.z = v1.x * v2.y - v1.y * v2.x;
         copy(result,temp);
         
         free();
    }
     
    static public float dot_product(Vector3 v, Vector3 w)
    {
         return( v.x * w.x + v.y * w.y + v.z * w.z);
    }
   
}


An instance of this class holds three member variables for the vector: x,y,z. The rest of the class is a series of static methods for doing math. Personally, I prefer that the math methods are static because it makes other code more readable (at least to me) and it is a little faster at execution. You can change this class to have these methods not be static if you want, but I wouldn't.

There are methods for scaling, addition, subtraction, normalization, dot and cross products, and reflection. Each method, other than those that return a variable, puts the result of the operation in the first parameter. Here is an example of some code using the class.

Code: Select all
// A function to keep the direction of your velocity but change the magnitude
public void changeVelocity(Vector3 velocity, float magnitude)
{
    // Normalize the velocity
    Vector3.normalize(velocity,velocity);
    // Scale the normalized velocity using the incoming magnitude
    Vector3.scale(velocity,magnitude);
}


The only really unique concept in the class is the stack. Garbage collection is a very bad thing for games. You don't want ANY garbage collection in your games. That said, there is often the need to create temporary objects in functions to hold working data. But, you don't want to create these objects using new or you get garbage. So, my solution, although others probably won't like it, was to create a stack for my objects. In this case, if you need a temp Vector3 object in a function, you can call the static alloc to get it and later call the static free to release it. Typically, you would allocate at the beginning of a function and free at the end. For example:

Code: Select all
// In this method we want to change v1 using v2 BUT NOT CHANGE the data in v2.
// In order to do this we need to copy v2 into a temporary variable, manipulate it,
// and then add it to v1. 
public void doSomeStuff (Vector3 v1, Vector3 v2)
{
    // Allocate a temp Vector3 on the stack
    Vector3 temp_vector = Vector3.alloc();

    Vector3.normalize(temp_vector,v1);
    Vector3.inverse(temp_vector,temp_vector);
    Vector3.add(v1,temp_vector);

    Vector3.free();   // temp_vector
}


OK, well this is really convenient and creates NO garbage. Are there any problems with it. The short answer is TONS! You must be extremely careful with the stack allocation. I wrote this for my own use and have not tried to make it safe to use in any way. I am depending on my own ability to be safe when using it. In a tutorial, providing this unsafe method is probably a really bad thing to do. But, it is what I am doing. I will leave it up to others to provide better solutions. I will tell you what to look out for.

1) Stack size. You must size your stack correctly. If you don't, you will get a bounds exception.
2) Don't forget the frees. If you do, well then you are in trouble. You will probably get unexplained exceptions. What I do is comment every free with the variable that it applies to. You need an equal number of frees at the end of each function to the number of allocs at the beginning of the function.
3) Don't return early. If you do, make sure you free there too.
4) THIS IS NOT THREAD SAFE. Don't allocate Vector3s in multiple threads using alloc, period. Making it thread safe is probably pretty easy, I just didn't have a need to do it.

Well, that is is. My Vector3 class. Not much of a start but without it we go nowhere.

Coming next:

Part 2 - An OpenGl framework application.
Part 3 - Organizing Game Objects into Classes
Visit Exit 4 Gaming - http://www.exit4games.com/
Home of LavaBall - http://exit4games.com/?page_id=3
Home of Rebound - http://exit4games.com/?page_id=138
Home of Tap Crazy - http://exit4games.com/?page_id=219
Download "Caveman Pool" From the Market Today!
seed
Senior Developer
Senior Developer
 
Posts: 103
Joined: Mon Mar 15, 2010 3:22 pm

Re: Tutorial Series - A simple 3D game

Postby nEx.Software » Fri May 28, 2010 6:26 pm

Cool, looking forward to seeing more. :)
nEx.Software
Freshman
Freshman
 
Posts: 2
Joined: Wed Jan 28, 2009 7:01 pm

Re: Tutorial Series - A simple 3D game

Postby seed » Fri Jul 16, 2010 7:28 pm

OK, guys, I am back. Over the next few weeks, I will work on this tutorial and the associated game. The next post will provide a very simple OpenGL framework to develop a game in. No complicated multithreaded design here. Just draw, move, draw, move. If it worked for Carmack and Quake, then it is good enough for me;)

In the mean time, go download Lava Ball 3D from the market and let me know what you think.

Thanks,
Seed
Visit Exit 4 Gaming - http://www.exit4games.com/
Home of LavaBall - http://exit4games.com/?page_id=3
Home of Rebound - http://exit4games.com/?page_id=138
Home of Tap Crazy - http://exit4games.com/?page_id=219
Download "Caveman Pool" From the Market Today!
seed
Senior Developer
Senior Developer
 
Posts: 103
Joined: Mon Mar 15, 2010 3:22 pm

Top

Return to Android 2D/3D Graphics - OpenGL Tutorials

Who is online

Users browsing this forum: No registered users and 3 guests