CheatCat's picture

Rayman 2 2D

Table of contest:

The beginning
Drawing images
Player movement

------
The beginning

The background to this project is this YouTube video:
http://www.youtube.com/watch?v=LW-fpwjT9I0&feature=PlayList&p=A047FCF1E8...
I want to do a fan-game to this game because it was canceled before the game was done!

First I have to decide what I should do the game with..
CsGL http://csgl.sourceforge.net : The page hasn't be updated since 2003. I want something newer.
Allegro http://www.talula.demon.co.uk/allegro : Hard to install and you cannot use it for C#. There is wrappers, but they are old.
AgateLib http://www.agatelib.org : It's slow, I want something faster!
SDL.NET http://cs-sdl.sourceforge.net : The forum is dead, I can look in the sky for help..
OpenTK http... Wait, it is this page! xD : It seems to be a great wrapper, let us try! :D

------
Drawing images

First I run the QuickStart example and get a stupid MS-DOS window in front of the program and I ask for help: http://www.opentk.com/node/890

Now I try to make a simple game!
The first program I use was GameMaker. I just need to load a sprite/image, make a object for the sprite, put the object i a room and hit the play button. Then I have a cute window with the image. Simple.

I want do something harder so I switch to C++ with The Game Creators DarkGDK library (using DirectX). To draw a image to the screen I only need write this code:

#include "DarkGDK.h"
 
void DarkGDK()
{
  dbSyncOn();
  dbLoadImage("img.png", 1);
  dbSprite(1, 10, 20, 1);
 
  while(LoopGDK())
  {
     dbSync();
  }
}

And I have a image at the position 10x20 in a window.
I also try XNA and it was little harder to draw a image but it was something similar as DarkGDK. Of course I have tried other programs and libraries and you draw images with them calling simple functions.

Okey, back to OpenGL and OpenTK. To draw a image you first need to load the image. It's hard and I don't understand what the loading function do in the NeHe Examples (google it). However it load a image and it's all I need to know. I get help at http://www.opentk.com/node/899 and learned that I can use Objarni's TexUtil class to load images.

Now I try to draw the image and to do that you must put it on a sort of plain. You can read more at http://www.opentk.com/node/909 and http://www.opentk.com/node/919

At last I want to put a transparent image in front of another image. But the transparent image get a blue border and cut of the red rectangle! I search for help again.

Well, it is not a very big problem. Moving on..

------
Player movement

Okey, let's move a image! I want add some keyboard functions that check what key the user press down. It's time for search!

Get it, Keyboard[] checks the keys! I add a wall sprite and try to check collision between it and the player. A collision function would be great and it can be inverted by myself if I don't find a pre-done function.
I do the collision function by myself since I cannot find any other. Now I should make the player to fall down if there are walls under the player. Done! (I am almost done! xD) The game go very fast, I must find a way to change the framerate or maybe move the sprite with smaller steps.

It's time to make the sprite jump when pressing Ctrl. Then I can make a level!

I get collision error now: When the player collide with a wall it cannot move! I should try to fix this...


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
CheatCat's picture

Now I understand! Thank you!

flopoloco's picture

I have a question, how can I perform my own Matrix rotate/tranlate operations?
After I have done these operations, I would simply use OpenGL rotate/translate for displaying the data.

The reason to do this, is crucial, especially in collision detection or storing data of a scenegraph. I see that OpenTK has the Matrix4 class, but I can't use it properly.

For example, how can I move my object to 10, 10 (x,y) based on rotation and translation?

y=10
_________ x=10
|
|
|

// It's easy to think of it like

// Create the matrix
Matrix4 mat = new Matrix4();

// Move 10 steps
mat.Translate(0, 0, 10);

// Turn to East
mat.RotateY(90);

// Move 10 steps
mat.Translate(0, 0, 10);

// Get the result of matrix

Any ideas for it? :)

CheatCat's picture

I think you must count the new position using trigonometry. I have no idea how to do that..

objarni's picture

flopoloco/CheatCat

Yes trigonometry is one way.

But matrices are easier, once you know how to use them. flopo, you are close to the solution. What you want to do is "Load" the Matrix4 you have computed into OpenGL, but I don't know the proper way how to do it in OpenTK. Someone?

the Fiddler's picture

The Matrix API in OpenTK is a bit of a mess, but I've started cleaning it up. What you need to do is create a translation and rotation matrix and multiply them together:

Matrix4 result;
Matrix4 translation = Matrix4.Translation(0, 0, 10);
Matrix4 rotation = Matrix4.RotateY((float)System.Math.PI / 2);
 
Matrix4.Mult(ref translation, ref rotation, out result);

You can then load the result into OpenGL or use it as input to a shader:

// Fixed function pipeline:
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadMatrix(ref result);
// Shaders:
GL.UniformMatrix4(uniform_id, false, ref result);

Note that the order of operations matters in matrix multiplication.

flopoloco's picture

I have troubles... I made this example to see it working in action, but I can not use Matrix4 properly despite Fiddler's help, if anyone brave man implements the Update method on Player class then we will come up with a nice conclusion on how to use Matrices.

using System;
using System.Drawing;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using OpenTK.Platform;
using OpenTK.Math;
 
namespace OpenTKMatrixTest
{
	public class MatrixExample : GameWindow
	{
		private Player player;
 
		public MatrixExample()
		{
			player = new Player();
		}
 
		public override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);
			GL.ClearColor(Color.MidnightBlue);
			GL.Enable(EnableCap.DepthTest);
		}
 
		public override void OnRenderFrame(RenderFrameEventArgs e)
		{
			GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();
 
            Glu.LookAt(0.0, 0.0,50.0,
                       0.0, 0.0, 0.0,
                       0.0, 1.0, 0.0);
 
            player.Render();
 
            this.SwapBuffers();
		}
 
		protected override void OnResize(ResizeEventArgs e)
		{
			base.OnResize(e);
 
            GL.Viewport(0, 0, Width, Height);
 
            double aspect_ratio = e.Width / (double)e.Height;
 
            GL.MatrixMode(MatrixMode.Projection);
            if (Keyboard[OpenTK.Input.Key.Space])
            {
                OpenTK.Math.Matrix4 perspective = OpenTK.Math.Matrix4.Perspective(45, (float)aspect_ratio, 1, 100);
                GL.LoadMatrix(ref perspective);
            }
            else
            {
                GL.LoadIdentity();
                Glu.Perspective(45, (float)aspect_ratio, 1, 64);
            }
		}
 
		public override void OnUpdateFrame(UpdateFrameEventArgs e)
		{
			if (Keyboard[Key.Escape])
				Exit();
 
			if (Keyboard[Key.Left])
				player.SetRotation(0, 0, -1);
			if (Keyboard[Key.Right])
				player.SetRotation(0, 0,  1);
			if (Keyboard[Key.W])
				player.SetMovement(0, 1, 0);
			if (Keyboard[Key.S])
				player.SetMovement(0, -1, 0);
			if (Keyboard[Key.A])
				player.SetMovement(-1, 0, 0);
			if (Keyboard[Key.D])
				player.SetMovement( 1, 0, 0);
 
			player.Update();
 
			base.OnUpdateFrame(e);
		}
 
		public static void Main()
		{
			using (MatrixExample me = new MatrixExample())
			{
				me.Run();
			}
		}
	}
 
	public class Player
	{
		public float X, Y, Z;
		public float rotationZ;
 
		public Matrix4 ResultMatrix, RotationMatrix, TranslationMatrix;
 
		public Player()
		{
			ResultMatrix = new Matrix4();
			RotationMatrix = new Matrix4();
			TranslationMatrix = new Matrix4();
		}
 
		public void SetMovement(float x, float y, float z)
		{
			X += x;
			Y += y;
			Z += z;
		}
 
		public void SetRotation(float x, float y, float z)
		{
			rotationZ += z;
		}
 
		public void Update()
		{
//			This method needs implementation		
//			Matrix4.CreateTranslation(X, Y, Z, out ResultMatrix);
//			RotationMatrix = Matrix4.RotateZ(rotationZ);
//			Matrix4.Mult(ref TranslationMatrix, ref RotationMatrix, out ResultMatrix);
//			TranslationMatrix = Matrix4.Translation(X, Y, Z);
//			ResultMatrix = Matrix4.Mult(TranslationMatrix, RotationMatrix);
		}
 
		public void Render()
		{
			GL.PushMatrix();
//			GL.LoadMatrix(ref ResultMatrix);
			GL.Translate(X, Y, Z); // Just for testing only, remove this line...
			GL.Begin(BeginMode.Quads);
			GL.Vertex2(-1f, 1f);
			GL.Vertex2(-1f,-1f);
			GL.Vertex2( 1f,-1f);
			GL.Vertex2( 1f, 1f);
			GL.End();
			GL.PopMatrix();
		}
	}
}
Entropy's picture
flopoloco wrote:
public void Update()
		{
//			This method needs implementation		
//			Matrix4.CreateTranslation(X, Y, Z, out ResultMatrix);
//			RotationMatrix = Matrix4.RotateZ(rotationZ);
//			Matrix4.Mult(ref TranslationMatrix, ref RotationMatrix, out ResultMatrix);
//			TranslationMatrix = Matrix4.Translation(X, Y, Z);
//			ResultMatrix = Matrix4.Mult(TranslationMatrix, RotationMatrix);
		}

Looks like you're translating twice to me.

I'm not the expert on matrices, but I think you want something like this:

public void Update()
		{
			TranslationMatrix = Matrix4.Translation(X, Y, Z);
			RotationMatrix = Matrix4.RotateZ(rotationZ);
			Matrix4.Mult(ref RotationMatrix, ref TranslationMatrix, out ResultMatrix);
 
		}

If that doen't work, it's probably rotating/translating in the wrong order. Try swapping the RotationMatrix and TranslationMatrix in the Matrix4.Mult(...) call.

Edit: OpenGL matrices completely baffled me until I used TextPrinter to output a Matrix4.ToString for the transforms of a user-controlled entity to screen. Try different keys to move it, or rotate it without changing the translation etc and see how the Matrix4 changes. You'll see that the top-left 3x3 (Row0 to Row2) specify the x,y and z axes of the object's orientation, and the translation is stored along the bottom row (Row3). It's worth taking the time to play around like this and learn how it all works.

nythrix's picture

Full bown 3D transformations are a bit tricky. Especially the rotations. This code from my own engine, should tell more than a thousand words:

using System;
namespace Brain
{
    public class Transform
    {
        private Quaternion rotation = new Quaternion( 1, 0, 0, 0 );
        private Vector3 translation = new Vector3();
        private Matrix4 matrix = Matrix4.Identity;
 
        public Matrix4 GetMatrix()
        {
            return matrix;
        }
 
        public Quaternion GetRotation()
        {
            return rotation;
        }
 
        public Vector3 GetTranslation()
        {
            return translation;
        }
 
        public void Add( AxisAngle a )
        {
            Add( a.ToQuaternion() );
        }
 
        public void Add( Quaternion q )
        {
            rotation = rotation * q;
            matrix = matrix * q.ToMatrix();
        }
 
        public void Add( Vector3 v )
        {
            translation = translation + v;
            matrix = matrix * v.ToMatrix();
        }
 
        public void Set( Quaternion q )
        {
            rotation = q;
            matrix = rotation.ToMatrix();
        }
 
        public void Set( Vector3 v )
        {
            translation = v;
            matrix = v.ToMatrix();
        }
 
        public override string ToString()
        {
            return matrix.ToString();
        }
    }
}

Note that I don't use OpenTK's matrices, quaternions and vectors but the whole is self explanatory, I guess. Any restrictions to your moving around (say you just turn left or right) make the thing much easier.
Transformation concatenating is done using the Add methods. Make sure you have unit quaternions entering the Add(...) methods or your matrix won't be orthogonal anymore. Hope this helps a bit.

Edit: Typos...
Edit2:
The lines
matrix = matrix * v.ToMatrix();
and
matrix = matrix * q.ToMatrix();
might still cause the matrix drift away after a big number of evaluations. In this case you have to reorthogonalize it. I didn't run into this problem but it might happen. Or to put it the Murphy way: It WILL happen :)

CheatCat's picture

There is any function that return if there is a sprite/image in a specific position?
There is a collision function at NeHe, but I understand nothing! :(

objarni's picture

Collision detection is a whole computer graphics subject.

Some pointers:

1. Learn basic geometry algebra (point-point distance, point-circle distance, line-line intersection)
2. With those "primitives" you can build up more advanced collision detection (point-in-circle, point-in-rectangle, polygon-polygon collision)
3. Often, you can "trick" your way out, for example, checking if a point is inside a rectangle is really simple:
xmin <= x <= xmax && ymin <= y <= ymax
.. where (xmin,ymin) - (xmax,ymax) represents the rectangle and (x,y) is the point to check

Checking whether a sprite lies on a specific pixel: just check if the point lies in the "bounding box"/2d-extents of the sprite. If it does, also check whether the pixel in the coordinate is "filled" or not, by looking into the bitmap data.