The best 2D engine nowadays..

Started by
15 comments, last by JBourrie 17 years, 11 months ago
I know you don't want to do it "from scratch", but coding up a bit of OpenGL to "blit" sprites (texture sections, actually) is very easy, especially if you are already familiar with the library. It would probably be more work to learn a large library to do essentially the same thing. With OpenGL for instance, all you are doing is drawing a textured quad. That's it. And how hard is that? Here, i'll even get you started. Here's my texture class. It has all the blitting routines already:

Texture.h
#ifndef DRAWING_H#define DRAWING_H#include <SDL/SDL.h>#include <SDL/SDL_image.h>#include <GL/gl.h>#include <string>namespace LGC {// ===============================================================================// This stores a rectangle. As a template, you can use any kind of number typetemplate< class TYPE > class AreaRect {public:	TYPE x1;	TYPE y1;	TYPE x2;	TYPE y2;		AreaRect( TYPE x, TYPE y, TYPE x2, TYPE y2 ): x1(x), y1(y), x2(x2), y2(y2) {}	AreaRect(): x1(0), y1(0), x2(0), y2(0) {}		inline TYPE Width() { return x2 - x1; }	inline TYPE Height() { return y2 - y1; }	inline bool Valid() { return !(!x1 && !x2 && !y1 && !y2); }			inline AreaRect<TYPE>& operator /= (const AreaRect<TYPE>& r) {		x1 / r.x1;		x2 / r.x2;		y1 / r.y1;		y2 / r.y2;		return *this;		}	inline AreaRect<TYPE>& operator += (const AreaRect<TYPE>& r) {		x1 + r.x1;		x2 + r.x2;		y1 + r.y1;		y2 + r.y2;		return *this;		}	inline AreaRect<TYPE>& operator -= (const AreaRect<TYPE>& r) {		x1 - r.x1;		x2 - r.x2;		y1 - r.y1;		y2 - r.y2;		return *this;		}	inline AreaRect<TYPE>& operator *= (const AreaRect<TYPE>& r) {		x1 * r.x1;		x2 * r.x2;		y1 * r.y1;		y2 * r.y2;		return *this;		}				inline AreaRect<TYPE> operator / (const AreaRect<TYPE>& r) {		return AreaRect<TYPE> (		x1 / r.x1,		x2 / r.x2,		y1 / r.y1,		y2 / r.y2 );		}	inline AreaRect<TYPE> operator + (const AreaRect<TYPE>& r) {		return AreaRect<TYPE> (		x1 + r.x1,		x2 + r.x2,		y1 + r.y1,		y2 + r.y2 );		}	inline AreaRect<TYPE> operator - (const AreaRect<TYPE>& r) {		return AreaRect<TYPE> (		x1 - r.x1,		x2 - r.x2,		y1 - r.y1,		y2 - r.y2 );		}	inline AreaRect<TYPE> operator * (const AreaRect<TYPE>& r) {		return AreaRect<TYPE> (		x1 * r.x1,		x2 * r.x2,		y1 * r.y1,		y2 * r.y2 );		}		inline bool operator == (const AreaRect<TYPE>& r) {		return  (		x1 == r.x1 &&		x2 == r.x2 &&		y1 == r.y1 &&		y2 == r.y2 );		}		};					// ===============================================================================enum BlendMode {	BLEND_NORMAL,	BLEND_NONE,	BLEND_ADD,	BLEND_SUBTRACT	};						// ===============================================================================class Texture {public:	Texture( 		std::string name, 		int texture_id, 		AreaRect<int>& texture_coords, 		AreaRect<int>& area_coords  		)  		: name(name), id (texture_id), pixels(area_coords)				{		ratios.x1 = float(area_coords.x1) / float(texture_coords.x2);		ratios.x2 = float(area_coords.x2) / float(texture_coords.x2);		ratios.y1 = float(area_coords.y1) / float(texture_coords.y2);		ratios.y2 = float(area_coords.y2) / float(texture_coords.y2);		is_tilable = ( area_coords == texture_coords );		}	Texture() : name("NO_HANDLE"), id(0), pixels(AreaRect<int>()), ratios(AreaRect<float>()), is_tilable(false) {} 		 		~Texture() {};	// allows textures to draw correctly with roto/zoom	static void SetScreenOffset( float x, float y ) {		screen_x = x;		screen_y = y;		}		// Drawing	void Blit						( float x, float y, BlendMode blend=BLEND_NORMAL );	void BlitFromCenter				( float x, float y, BlendMode blend=BLEND_NORMAL );	void BlitFlip					( float x, float y, bool flip_horz=false, bool flip_vert=false, BlendMode blend=BLEND_NORMAL );	void BlitFlipFromCenter			( float x, float y, bool flip_horz=false, bool flip_vert=false, BlendMode blend=BLEND_NORMAL );			void BlitRotoZoom				( float x, float y, float rot, float scale=1.0, BlendMode blend=BLEND_NORMAL );	void BlitRotoZoomFromCenter		( float x, float y, float rot, float scale=1.0, BlendMode blend=BLEND_NORMAL );		void BlitRotoZoomFlip			( float x, float y, float rot, float scale=1.0, bool flip_horz=false, bool flip_vert=false, BlendMode blend=BLEND_NORMAL );	void BlitRotoZoomFlipFromCenter	( float x, float y, float rot, float scale=1.0, bool flip_horz=false, bool flip_vert=false, BlendMode blend=BLEND_NORMAL );		void BlitCustomStretch			( float x, float y, float w, float h, BlendMode blend=BLEND_NORMAL );	void TileAcross				( float x, float y, float w, float h, BlendMode blend=BLEND_NORMAL );	void TileAcrossWithOffset		( float x, float y, float w, float h, float xoff, float yoff, BlendMode blend=BLEND_NORMAL );	// Note: "offset" is a ratio of the texture. 0.5 offsets 50% into the texture.			// setters 	inline void SetID( GLuint x ) { id = x; }	// Basic info	inline int 			ID() 		{ return id; }	inline int 			Width() 		{ return pixels.Width(); }	inline int 			Height() 		{ return pixels.Height(); }	inline std::string 		Handle() const { return name; }	inline AreaRect<int> 	GetPixels()	{ return pixels; }	inline AreaRect<float> 	GetRatios()	{ return ratios; }	inline bool 			IsTilable() 	{ return is_tilable; }	inline bool 			IsValid() 	{ return ( id != 0 ); }				// Get the texture coords as a ratio of the full texture	inline float Left() 	{ return ratios.x1; }	inline float Top() 		{ return ratios.y1; }	inline float Right() 	{ return ratios.x2; }	inline float Bottom() 	{ return ratios.y2; }	// Get the texture coords as a pixel of the full texture	inline float LeftPx() 	{ return pixels.x1; }	inline float TopPx() 	{ return pixels.y1; }	inline float RightPx() 	{ return pixels.x2; }	inline float BottomPx()	{ return pixels.y2; }private:		std::string 		name;	int 				id; 				// the actual opengl texture ID		AreaRect<int> 		pixels; 			// a list of area coords for fast lookup	AreaRect<float> 	ratios; 			// a list of area coords for fast lookup	bool is_tilable;		static BlendMode global_blend_mode;	static void SwitchBlendMode(BlendMode blend);		static float screen_x;	static float screen_y;	};	} // end namespace LGC	#endif


Texture.cpp
#include "Texture.h"namespace LGC {BlendMode Texture::global_blend_mode = BLEND_NORMAL;float Texture::screen_x = 0;float Texture::screen_y = 0;void Texture::Blit( float x, float y, BlendMode blend ) {	if (!id) { return; }		glBindTexture( GL_TEXTURE_2D, id );	if ( blend != global_blend_mode ) { SwitchBlendMode( blend ); }	 	glBegin(GL_QUADS);		/* Bottom Left Of The Texture and Quad */		glTexCoord2f( Left(), Bottom() ); 		glVertex2f( x, y+Height() );		/* Bottom Right Of The Texture and Quad */		glTexCoord2f( Right(), Bottom() ); 		glVertex2f(  x+Width(), y+Height() );		/* Top Right Of The Texture and Quad */		glTexCoord2f( Right(), Top() ); 		glVertex2f( x+Width(), y );		/* Top Left Of The Texture and Quad */		glTexCoord2f( Left(), Top() ); 		glVertex2f( x, y );	glEnd();		// FIXME	if ( blend != BLEND_NORMAL ) { SwitchBlendMode( BLEND_NORMAL ); }	}	void Texture::BlitFromCenter( float x, float y, BlendMode blend ) {	if (!id) { return; }		glBindTexture( GL_TEXTURE_2D, id );	if ( blend != global_blend_mode ) { SwitchBlendMode( blend ); }	 	float half_w = Width() * 0.5;	float half_h = Height() * 0.5;		glBegin(GL_QUADS);		/* Bottom Left Of The Texture and Quad */		glTexCoord2f( Left(), Bottom() ); 		glVertex2f( x-half_w, y+half_h );		/* Bottom Right Of The Texture and Quad */		glTexCoord2f( Right(), Bottom() ); 		glVertex2f(  x+half_w, y+half_h );		/* Top Right Of The Texture and Quad */		glTexCoord2f( Right(), Top() ); 		glVertex2f( x+half_w, y-half_h );		/* Top Left Of The Texture and Quad */		glTexCoord2f( Left(), Top() ); 		glVertex2f( x-half_w, y-half_h );	glEnd();		// FIXME	if ( blend != BLEND_NORMAL ) { SwitchBlendMode( BLEND_NORMAL ); }	}	void Texture::BlitFlip( float x, float y, bool flip_horz, bool flip_vert, BlendMode blend ) {	if (!id) { return; }		glBindTexture( GL_TEXTURE_2D, id );	if ( blend != global_blend_mode ) { SwitchBlendMode( blend ); }	 	float left = 	(flip_horz) ? Right() 	: Left();	float right = 	(flip_horz) ? Left() 	: Right();	float bottom = (flip_vert) ? Top() 	: Bottom();	float top = 	(flip_vert) ? Bottom() 	: Top();		glBegin(GL_QUADS);		/* Bottom Left Of The Texture and Quad */		glTexCoord2f( left, bottom ); 		glVertex2f( x, y+Height() );		/* Bottom Right Of The Texture and Quad */		glTexCoord2f( right, bottom ); 		glVertex2f(  x+Width(), y+Height() );		/* Top Right Of The Texture and Quad */		glTexCoord2f( right, top ); 		glVertex2f( x+Width(), y );		/* Top Left Of The Texture and Quad */		glTexCoord2f( left, top ); 		glVertex2f( x, y );	glEnd();		// FIXME	if ( blend != BLEND_NORMAL ) { SwitchBlendMode( BLEND_NORMAL ); }	}		void Texture::BlitFlipFromCenter( float x, float y, bool flip_horz, bool flip_vert, BlendMode blend ) {	if (!id) { return; }		glBindTexture( GL_TEXTURE_2D, id );	if ( blend != global_blend_mode ) { SwitchBlendMode( blend ); }	 	float half_w = Width() * 0.5;	float half_h = Height() * 0.5;	float left = 	(flip_horz) ? Right() 	: Left();	float right = 	(flip_horz) ? Left() 	: Right();	float bottom = (flip_vert) ? Top() 	: Bottom();	float top = 	(flip_vert) ? Bottom() 	: Top();			glBegin(GL_QUADS);		/* Bottom Left Of The Texture and Quad */		glTexCoord2f( left, bottom ); 		glVertex2f( x-half_w, y+half_h );		/* Bottom Right Of The Texture and Quad */		glTexCoord2f( right, bottom ); 		glVertex2f(  x+half_w, y+half_h );		/* Top Right Of The Texture and Quad */		glTexCoord2f( right, top ); 		glVertex2f( x+half_w, y-half_h );		/* Top Left Of The Texture and Quad */		glTexCoord2f( left, top ); 		glVertex2f( x-half_w, y-half_h );	glEnd();		// FIXME	if ( blend != BLEND_NORMAL ) { SwitchBlendMode( BLEND_NORMAL ); }	}					void Texture::BlitRotoZoom( float x, float y, float rot, float scale, BlendMode blend ) {	if (!id) { return; }		glBindTexture( GL_TEXTURE_2D, id );	if ( blend != global_blend_mode ) { SwitchBlendMode( blend ); }	float adjust_x = Width() * scale;	float adjust_y = Height() * scale;			glPushMatrix();	glLoadIdentity();		// TRANSLATE FIRST, ROTOZOOM SECOND!	glTranslatef( x - screen_x + adjust_x, y - screen_y + adjust_y, 0  );	if (scale != 1.0) { glScalef( scale, scale, 0); }			if (rot) { glRotatef( rot, 0, 0, 1 ); }	//glTranslatef( adjust_x, adjust_y, 0  );		glBegin(GL_QUADS);		/* Bottom Left Of The Texture and Quad */		glTexCoord2f( Left(), Bottom() ); 		glVertex2f( 0, Height() );		/* Bottom Right Of The Texture and Quad */		glTexCoord2f( Right(), Bottom() ); 		glVertex2f(  Width(), Height() );		/* Top Right Of The Texture and Quad */		glTexCoord2f( Right(), Top() ); 		glVertex2f( Width(), 0 );		/* Top Left Of The Texture and Quad */		glTexCoord2f( Left(), Top() ); 		glVertex2f( 0, 0 );	glEnd();	glPopMatrix();		// FIXME	if ( blend != BLEND_NORMAL ) { SwitchBlendMode( BLEND_NORMAL ); }	}	 void Texture::BlitRotoZoomFromCenter( float x, float y, float rot, float scale, BlendMode blend ) {	if (!id) { return; }		glBindTexture( GL_TEXTURE_2D, id );	if ( blend != global_blend_mode ) { SwitchBlendMode( blend ); }	glPushMatrix();	glLoadIdentity();		// TRANSLATE FIRST, ROTOZOOM SECOND!		glTranslatef(  x - screen_x,  y - screen_y, 0   );	if (scale != 1.0) 	{ glScalef( scale, scale, 0); }			if (rot != 0.0) 	{ glRotatef( rot, 0, 0, 1 ); }		glTranslatef(  -(Width() * 0.5),  -(Height() * 0.5), 0   );	glBegin(GL_QUADS);		/* Bottom Left Of The Texture and Quad */		glTexCoord2f( Left(), Bottom() ); 		glVertex2f( 0, Height() );		/* Bottom Right Of The Texture and Quad */		glTexCoord2f( Right(), Bottom() ); 		glVertex2f(  Width(), Height() );		/* Top Right Of The Texture and Quad */		glTexCoord2f( Right(), Top() ); 		glVertex2f( Width(), 0 );		/* Top Left Of The Texture and Quad */		glTexCoord2f( Left(), Top() ); 		glVertex2f( 0, 0 );	glEnd();	glPopMatrix();		// FIXME	if ( blend != BLEND_NORMAL ) { SwitchBlendMode( BLEND_NORMAL ); }	}	void Texture::BlitRotoZoomFlip( float x, float y, float rot, float scale, bool flip_horz, bool flip_vert, BlendMode blend ) {	if (!id) { return; }		glBindTexture( GL_TEXTURE_2D, id );	if ( blend != global_blend_mode ) { SwitchBlendMode( blend ); }	float adjust_x = Width() * scale;	float adjust_y = Height() * scale;	float left = 	(flip_horz) ? Right() 	: Left();	float right = 	(flip_horz) ? Left() 	: Right();	float bottom = (flip_vert) ? Top() 	: Bottom();	float top = 	(flip_vert) ? Bottom() 	: Top();				glPushMatrix();	glLoadIdentity();		// TRANSLATE FIRST, ROTOZOOM SECOND!	glTranslatef( x - screen_x + adjust_x, y - screen_y + adjust_y, 0  );	if (scale != 1.0) { glScalef( scale, scale, 0); }			if (rot) { glRotatef( rot, 0, 0, 1 ); }	//glTranslatef( adjust_x, adjust_y, 0  );		glBegin(GL_QUADS);		/* Bottom Left Of The Texture and Quad */		glTexCoord2f( left, bottom ); 		glVertex2f( 0, Height() );		/* Bottom Right Of The Texture and Quad */		glTexCoord2f( right, bottom ); 		glVertex2f(  Width(), Height() );		/* Top Right Of The Texture and Quad */		glTexCoord2f( right, top ); 		glVertex2f( Width(), 0 );		/* Top Left Of The Texture and Quad */		glTexCoord2f( left, top ); 		glVertex2f( 0, 0 );	glEnd();	glPopMatrix();		// FIXME	if ( blend != BLEND_NORMAL ) { SwitchBlendMode( BLEND_NORMAL ); }	}	 void Texture::BlitRotoZoomFlipFromCenter( float x, float y, float rot, float scale, bool flip_horz, bool flip_vert, BlendMode blend ) {	if (!id) { return; }		glBindTexture( GL_TEXTURE_2D, id );	if ( blend != global_blend_mode ) { SwitchBlendMode( blend ); }		float left = 	(flip_horz) ? Right() 	: Left();	float right = 	(flip_horz) ? Left() 	: Right();	float bottom = (flip_vert) ? Top() 	: Bottom();	float top = 	(flip_vert) ? Bottom() 	: Top();		glPushMatrix();	glLoadIdentity();		// TRANSLATE FIRST, ROTOZOOM SECOND!		glTranslatef(  x - screen_x,  y - screen_y, 0   );	if (scale != 1.0) 	{ glScalef( scale, scale, 0); }			if (rot != 0.0) 	{ glRotatef( rot, 0, 0, 1 ); }		glTranslatef(  -(Width() * 0.5),  -(Height() * 0.5), 0   );	glBegin(GL_QUADS);		/* Bottom Left Of The Texture and Quad */		glTexCoord2f( left, bottom ); 		glVertex2f( 0, Height() );		/* Bottom Right Of The Texture and Quad */		glTexCoord2f( right, bottom ); 		glVertex2f(  Width(), Height() );		/* Top Right Of The Texture and Quad */		glTexCoord2f( right, top ); 		glVertex2f( Width(), 0 );		/* Top Left Of The Texture and Quad */		glTexCoord2f( left, top ); 		glVertex2f( 0, 0 );	glEnd();	glPopMatrix();		// FIXME	if ( blend != BLEND_NORMAL ) { SwitchBlendMode( BLEND_NORMAL ); }	}	void Texture::BlitCustomStretch ( float x, float y, float w, float h, BlendMode blend) {	if (!id) { return; }		glBindTexture( GL_TEXTURE_2D, id );	if ( blend != global_blend_mode ) { SwitchBlendMode( blend ); }	 	glBegin(GL_QUADS);		/* Bottom Left Of The Texture and Quad */		glTexCoord2f( Left(), Bottom() ); 		glVertex2f( x, y+h );		/* Bottom Right Of The Texture and Quad */		glTexCoord2f( Right(), Bottom() ); 		glVertex2f(  x+w, y+h );		/* Top Right Of The Texture and Quad */		glTexCoord2f( Right(), Top() ); 		glVertex2f( x+w, y );		/* Top Left Of The Texture and Quad */		glTexCoord2f( Left(), Top() ); 		glVertex2f( x, y );	glEnd();		// FIXME	if ( blend != BLEND_NORMAL ) { SwitchBlendMode( BLEND_NORMAL ); }	}	void Texture::TileAcross( float x, float y, float w, float h, BlendMode blend ) {	if ( !id || !is_tilable ) { return; }		glBindTexture( GL_TEXTURE_2D, id );	if ( blend != global_blend_mode ) { SwitchBlendMode( blend ); }	 	float x_ratio = w / (float)Width();	float y_ratio = h / (float)Height();		glBegin(GL_QUADS);		/* Bottom Left Of The Texture and Quad */		glTexCoord2f( 0, y_ratio ); 		glVertex2f( x, y+h );		/* Bottom Right Of The Texture and Quad */		glTexCoord2f( x_ratio, y_ratio ); 		glVertex2f(  x+w, y+h );		/* Top Right Of The Texture and Quad */		glTexCoord2f( x_ratio, 0 ); 		glVertex2f( x+w, y );		/* Top Left Of The Texture and Quad */		glTexCoord2f( 0, 0 ); 		glVertex2f( x, y );	glEnd();		// FIXME	if ( blend != BLEND_NORMAL ) { SwitchBlendMode( BLEND_NORMAL ); }		}		void Texture::TileAcrossWithOffset( float x, float y, float w, float h, float xoff, float yoff, BlendMode blend ) {	if ( !id || !is_tilable ) { return; }		glBindTexture( GL_TEXTURE_2D, id );	if ( blend != global_blend_mode ) { SwitchBlendMode( blend ); }	 	float x_ratio = w / (float)Width();	float y_ratio = h / (float)Height();		glBegin(GL_QUADS);		/* Bottom Left Of The Texture and Quad */		glTexCoord2f( xoff, y_ratio + yoff ); 		glVertex2f( x, y+h );		/* Bottom Right Of The Texture and Quad */		glTexCoord2f( x_ratio + xoff, y_ratio + yoff ); 		glVertex2f(  x+w, y+h );		/* Top Right Of The Texture and Quad */		glTexCoord2f( x_ratio + xoff, yoff ); 		glVertex2f( x+w, y );		/* Top Left Of The Texture and Quad */		glTexCoord2f( xoff, yoff ); 		glVertex2f( x, y );	glEnd();		// FIXME	if ( blend != BLEND_NORMAL ) { SwitchBlendMode( BLEND_NORMAL ); }	}			void Texture::SwitchBlendMode(BlendMode blend) {	switch(blend) {		case BLEND_NORMAL:			glEnable(GL_BLEND);			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);			break;		case BLEND_NONE: 			glDisable(GL_BLEND);			break;					case BLEND_ADD: 			glEnable(GL_BLEND);			glBlendFunc(GL_SRC_ALPHA,GL_ONE);			break;		case BLEND_SUBTRACT: 			// TODO: NOT IMPLEMENTED YET			glEnable(GL_BLEND);			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);			break;					}	global_blend_mode = blend;	}		} // end namespace LGC	
Advertisement
Well, thanks everyone for the answers, specially for the code.

I think I'll work around with Direct3dSprite and OpenGL a little bit.
What about an Addictive FREE Turn-Based Puzzle/Strategy Game, with a Growing Community Available to Play right now?<a href="http://www.wonderquest.co.nr"><( ) -Hurrahh!/a> <a href="http://www.wonderquest.co.nr"><( ) -Yes!/a> <a href="http://www.wonderquest.co.nr"><( ) -Ai Caramba!/a>
I am also working on a 2D game engine called JEngine SSE. Its current version supports Lua, networking, sound (using OpenAL), free editor and is open source (GPL). Check it out on my website: http://jengine.homedns.org

Crafter 2D: the open source 2D game framework

?Github: https://github.com/crafter2d/crafter2d
Twitter: [twitter]crafter_2d[/twitter]

Just wanted to point out that SDL is not a "2D Engine" like some people were saying. It's a library for developing cross platform multimedia applications. DirectDraw was not a "2D Engine" either.
All engines mentioned are not good enough if you want to do something better. Believe me - if someone learn how to draw 2d stuff and how use alpha blended textures and creates some library - this is not an engine yet! I suggest you to use D3DXSprite interface of DX8 and you do not need any of 2d engines mentioned. Things like these engines can do are not very complicated and you can do it on your own maybe quicker than trying to use some of these engines. And please do not make your engine multiplatform - it is waste of time.
If C# is your fancy, you could check out the FlatRedBall engine.
flatredball.com
Joel Martinez
http://codecube.net
[twitter]joelmartinez[/twitter]
I'll be the third to recommend Torque2D (www.garagegames.com).

Full hardware accelerated 2D, support for tilemaps and a built-in level editor, full UI editor, advanced particle systems with interactive editor, and a powerful rigid body physics system.

All for $100.

Check out my new game Smash and Dash at:

http://www.smashanddashgame.com/

This topic is closed to new replies.

Advertisement