Pitchaya Kunarochrakse / Gameplay, Tools Developer 

LinkedIn E-mail

Touch Controller Interface

Genre : 2D Top-Down Shooter

Technology : C++, Android NDK, Tegra Android Development Pack (TADP),  Nvidia Nsight

Thesis objective : Experiment how well people can handle each input scheme. Is it possible to measure this? Below is my thinking process about how to conclude this topic.

How to measure : I developed a data-driven control scheme system, which allows me to change movement component and shooting component for each control scheme. I developed  a shooting game, built levels, let people play with them and collect the players’ data. After testers are finished with the game, they have to fill the post-test survey.

Controller Description :
Movement component :

  • Joystick fixed at bottom-left corner
  • Joystick fixed at center
  • Joystick is Drag-able
  • Swipe

Shooting component :

  • Two buttons
  • One button
  • Second joystick
  • Touch to shoot

Game Mechanics Description :

The ultimate goal is to make people moving around with great precision as much as possible. Here is the game mechanics that encourage testers to do that.

  • Game objects
    • Coins – collectible objects, disappear when a player touches it.
    • Targets – disappear when a player’s bullet touches it
    • Walls – used for collecting number of wall hits
    • Cannons – shoot bullets, used for collecting number of bullet hits
  • Level design principle
    • I split the levels into three categories
      • First, to test the directional movement of the scheme, I built the maze level and evaluate the player by the time he takes to finish the level. The level consists of long, narrow, zigzag and U-turn hallway.
      • Second, to test the precision of the scheme, I built a Targets level which have many targets in various positions. Players have to move and turn to shoot down all targets
      • Third, to test directional movement in tight space, I built levels with cannons, which shoot bullets every few seconds. Players have to move through field of bullets to test their capability to move between dynamic obstacles (bullets)
    • The level can be easily built in 10 minutes, thanks to my 2D Editor. The levels are built on Windows and can be used on Android without any problems.

Lesson Learned : 

  • Working on new technology is hard. I used Google as much as I can and found almost nothing. Mostly I have to experiment them by myself.
  • Working on new technology with special condition is exceptionally hard. I started out the project with Intel tablet, which required a special driver and setup. With some limitations (obviously, not technical limitation), I change the tablet to Nexus7 later on and found that a lot of work I have done and not work before magically work just because I change the tablet. On top of that, most of the solution I found on Google cannot be applied to my problem because of this special condition on that tablet.
  • I didn’t plan to have the game run on Windows at the first place. Thanks to NDK lacking of debugging feature (yes, I can use print log to debug but imagine how annoying+time consuming it is), so I come up with a better way – working on Windows and move all the game code directly to Android after I finished debugging. To make this happen, my engine must not have any platform-specific code, which I think it’s very good for learning experience and keeping my code clean at the same time.
    • Sadly, the result is I have to finish everything on Android first, and then try to port it to Windows after that. The reason is, Android has very weird way to handle things and has a lot more limitation compare to Windows. For example, I used to initiate the Shader and my actor objects in the engine constructor. I cannot do that in Android because my actors need a texture and the texture can be loaded after I already initiate the OGL context. In Android, I can only have OGL context after I have the surface (AKA android game screen), so I must let it run into the main loop for a few millisecond before I can used the OGL context data. With that limitation, it’s impossible to initiate anything that need OGL context in the constructor.
    • There is also a lot of minor conditions that get in my way to port from Windows to Android (and vice versa). Mostly because Android code and example heavily rely on its own platform-specific code.
  • Working on familiar IDE make things 100 times easier. I have to work on Eclipse (NDK) for a while and everything is slow – I can’t use the short keys and debugger, the intellisense is not working. I found Nsight for Visual Studio and I put my time to configure it. The payoff is very nice; I wouldn’t be able to figure out weird memory bug in Android with print log nor just my logic error very easy without debugger. In addition, my coding speed is a lot faster because I can use short keys like I normally did. It doesn’t mean I have to use debugger, but it’s a very nice feature to have *if* you work on something that you never did it before. You have no idea where it’s going to crash and why.
  • It doesn’t matter I’m a programmer, a level designer or an artist. Responsibility and determination are the keys. If I have to get things done, it’s better to rely on myself. It’s also fun to explore new things (design levels or photoshopped arts) that I never did before.

Engine Code Sample

#ifndef __ENGINE_H
#define __ENGINE_H

#include "Renderer.h"
#include "EGLInterface.h"
#include "drawRect.h"
#include "drawVertex.h"
#include "SoundPlayer.h"
#include "TextRenderer.h"
#include "NvMultiInput.h"

#include "PlayerPawn.h"
#include "EnemyPawn.h"
#include "TurretPawn.h"
#include "Spawner.h"
#include "MessageActor.h"
#include "InputController.h"
#include "BulletPatternGenerator.h"
#include "TestBulletGeneratorPackage.h"
#include "TestEnemyGeneratorPackage.h"
#include "Level_Loader.h"




// set max const beyond anything available soon. :) 
#define MAX_TOUCH_COUNT     20

namespace banknamespace
{
	class Engine
	{
	public:
		Engine();
		~Engine();

		bool isGameplayMode() { return mGameplayMode; }

		void updateFrame(bool interactible, long deltaTime);

		void init(std::vector<void*> args );
		void setGameplayMode(bool paused);

		void advanceTime(long time) 
		{ mTimeVal = Time::GetAbsoluteTimeSeconds(); }

		bool isForcedRenderPending() { return mForceRender > 0; }
		void requestForceRender() { mForceRender = 4; }

		bool checkWindowResized();

		bool renderFrame(bool allocateIfNeeded);

		void writeStatLog();
		void loadNewLevel();
		bool initUI();

		bool resizeIfNeeded();

		int loadBulletPackage( const char* path );
		std::map<std::string, int> packageNameMap;



		void* mApp;

		EGLInterface* mEgl;

		bool mResizePending;

		bool mGameplayMode;

		int mForceRender;

		double mTimeVal;

		bool m_uiInitialized;

		NvMultiInput *startTouchList; // input multitouch
		int m_pointerCount;	
	
		GLint m_touchTexture;
		drawRect *m_drawRect;


		///// my variable
		// text on screen
		int txt_playerHP;
		int txt_playerXP;
		int txt_playerLV;
		int txt_playerControl;
		int txt_clockText;

		PlayerPawn reassignedPlayer;

		// controller texture
		unsigned int tex_ButtonA;
		unsigned int tex_ButtonB;
		unsigned int tex_baseController;
		unsigned int tex_stickController;
		unsigned int tex_panel;

		// feedback texture
		Actor SparkleActor;
		MessageActor LvUpActor;
		/////
		
		///// engine
		// renderer
		Renderer* renderer;
		TextRenderer* t_renderer;
		bool alreadyInitTextRenderer;

		double lastFrameTime;
		double currentTime;

		double aspectRatio;
		mat16f projectionMatrix;

		unsigned int textureId;

		std::vector< actorVertex > vertices;
		std::vector< unsigned int > indices;
		unsigned int VBO;
		unsigned int IBO;

		// sound
		SoundPlayer* m_soundPlayer;
		unsigned int bgm_sound;
		unsigned int playerShoot;
		unsigned int enemyShoot;
		unsigned int feederShoot;
		unsigned int turretShoot;
		

		///// shooter game variable
		vec3f playerImpulse;
		Camera m_camera;
		
		Level_Loader level;
		std::vector<std::string> levelPath;
		std::vector<std::string> levelSoundPath;
		bool isLevelChange;
		int levelNum;
		int maxLevelNum;
		
		EnemyPawn feederPrototype;
		TurretPawn turretPrototype;
		Spawner spawnerPrototype;
		Actor healthPrototype;
		
		std::vector<initPackage> bulletPackageArr;
		std::vector< PlayerPawn > playerActor;
		std::vector< EnemyPawn > enemyActorArr;
		std::vector< Spawner > spawnerActorArr;
		std::vector< EnemyPawn > feederActorArr;
		std::vector< Actor > healthActorArr;
		std::vector< TurretPawn > turretActorArr;

		TestEnemyGeneratorPackage enPack;
		std::vector<BulletPatternGenerator> playerBulletGenerator;
		std::vector<BulletPatternGenerator> enemyBulletGenerator;

		unsigned int playerIndex;
		/////
	};

}

#endif // __ENGINE_H

#include "StdAfx.h"
#include "engine.h"

#include "Define.h"

static const float color[4] = {1.0f, 1.0f, 1.0f, 1.0f};

namespace banknamespace
{

	Engine::Engine()
	{
		mResizePending = false;

		mGameplayMode = true;

		mForceRender = 4;

		mTimeVal = 0.0;

		m_uiInitialized = false;

		m_pointerCount = 0;

		lastFrameTime = 0;

		alreadyInitTextRenderer = false;

		isLevelChange = false;
		levelNum = 0;

		txt_playerHP = 0;
		txt_playerXP = 0;
		txt_playerLV = 0;
		txt_playerControl = 0;
		txt_clockText = 0;
	}

	void Engine::init( std::vector<void*> args )
	{
		///// args[0] = android_app* mApp
		///// args[1] = Renderer* renderer
		///// args[2] = NvEGLUtil* mEgl
		///// args[3] = NvMultiInput* startTouchList
		///// args[4] = TextRenderer* t_renderer;
		///// args[5] = levelNum;
		mApp = args[0];
		renderer = reinterpret_cast<Renderer*>( args[1] );
		mEgl = reinterpret_cast<EGLInterface*>( args[2] );
		startTouchList = reinterpret_cast<NvMultiInput*>( args[3] );
		t_renderer = reinterpret_cast<TextRenderer*>( args[4] );
		
		int tmpNum = *reinterpret_cast<int*>( args[5] );

		levelPath.push_back("Resources/SaveFile/tutorial01.edt");
		levelPath.push_back("Resources/SaveFile/bank_LevelMaze.edt");
		levelPath.push_back("Resources/SaveFile/bank_LevelEnemies1.edt");
		levelPath.push_back("Resources/SaveFile/bank_LevelEnemies2.edt");
		levelPath.push_back("Resources/SaveFile/bank_LevelEnemies3.edt");
		levelPath.push_back("Resources/SaveFile/bank_LevelEnemies4.edt");
		levelPath.push_back("Resources/SaveFile/bank_LevelEnemies5.edt");
		

		maxLevelNum = levelPath.size() - 1;
		if (tmpNum > maxLevelNum) tmpNum = maxLevelNum;
		levelNum = tmpNum;

		InputController::isControlChanged = true;
	}

	Engine::~Engine()
	{
		m_soundPlayer->play(bgm_sound, false);
		delete m_drawRect;
	}

	int Engine::loadBulletPackage( const char* path )
	{
		// load from file
		int length = 0;
		char* raw_data = loadNDKfile(renderer->getAndroidApp() , path, length);
		
		std::vector<bulletStateInit> tmpInit;
		std::vector<bulletStateMovement> tmpMovement;

		// found file
		fileStruct tmpStruct;
		memcpy( &tmpStruct, raw_data, sizeof(fileStruct) );
			 
		// make a room for data
		tmpInit.resize(tmpStruct.initSize);
		tmpMovement.resize(tmpStruct.moveSize);
		memcpy( tmpInit.data(), raw_data + sizeof(fileStruct), sizeof(bulletStateInit) * tmpStruct.initSize );
		memcpy( tmpMovement.data(), raw_data + sizeof(fileStruct) + sizeof(bulletStateInit) * tmpStruct.initSize, sizeof(bulletStateMovement) * tmpStruct.moveSize );

		// import data to tmp package

		initPackage tmp;
		tmp.name = path;
		tmp.initList = tmpInit;
		tmp.movementList = tmpMovement;
		tmp.collisionRadius = 8.0f;
		
		bulletPackageArr.push_back( tmp );

		free(raw_data );

		return bulletPackageArr.size()-1;
	}

	void Engine::writeStatLog()
	{
		// log stat before load new level
		std::string tmpStr;
		// dieCount, takeHit, XPgained, 
		tmpStr.append("\nlv=");
		tmpStr.append( levelPath[levelNum] );
		switch (InputController::currentControlType)
		{
		case CONTROLTYPE_ORIGINAL : tmpStr.append("\tOriginal"); break;
		case CONTROLTYPE_MOVINGCENTER : tmpStr.append("\tMovingCenter"); break;
		case CONTROLTYPE_PLAYERCENTER : tmpStr.append("\tPlayerCenter"); break;
		case CONTROLTYPE_SWIPE : tmpStr.append("\tSwipe"); break;
		default : break;
		}
		int mins = mTimeVal / 60;
		float secs = (float)mTimeVal - mins*60;
		char str[32];
		sprintf(str, "\t%03d:%05.2f", mins, secs);
		tmpStr.append( str );
		tmpStr.append("\tHP=");
		tmpStr.append( lexical_cast<std::string, int>(playerActor[0].HP) );
		tmpStr.append("\tXP=");
		tmpStr.append( lexical_cast<std::string, int>(playerActor[0].XP) );
		tmpStr.append("/");
		tmpStr.append( lexical_cast<std::string, int>(playerActor[0].nextLV_XP) );
		tmpStr.append("\tLV=");
		tmpStr.append( lexical_cast<std::string, int>(playerActor[0].LV) );
		tmpStr.append("\t");
		tmpStr.append( lexical_cast<std::string, int>(playerActor[0].dieCount) );
		tmpStr.append("\t");
		tmpStr.append( lexical_cast<std::string, int>(playerActor[0].takeHit) );
		tmpStr.append("\t");
		tmpStr.append( lexical_cast<std::string, int>(playerActor[0].XPgained) );
		tmpStr.append("\t");
		tmpStr.append( lexical_cast<std::string, int>(playerActor[0].turretKilled) );
		tmpStr.append("\t");
		tmpStr.append( lexical_cast<std::string, int>(playerActor[0].spawnerKilled) );
		tmpStr.append("\t");
		tmpStr.append( lexical_cast<std::string, int>(playerActor[0].otherKilled) );

		writeFile("/sdcard/playerStat.txt", tmpStr.c_str());
	}

	void Engine::loadNewLevel()
	{
		// log stat before load new level
		writeStatLog();

		// load new level
		isLevelChange = true;
		levelNum++;
	}

	bool Engine::initUI()
	{
		if (m_uiInitialized)
			return true;

		// calc aspect ratio
		float w = mEgl->getWidth();
		float h = mEgl->getHeight();
		aspectRatio = w / h;

		InputController::width = mEgl->getWidth();
		InputController::height = mEgl->getHeight();

		float projection[16];
		buildProjectionMatrix( projection, 45.0f, static_cast<float>(aspectRatio), 0.1f, 10000.0f );
		projectionMatrix = mat16f( projection );

		
		////////////////////////////////////////
		///// set static variable here
		////////////////////////////////////////
		///// init actor
		// debug texture
		Actor::hurtTextureId = renderer->loadTexture( "Sprite/explosion.png" );
		Actor::shieldTextureId = renderer->loadTexture( "Sprite/energyBallBlue.png" );
		Actor::setDebugTextureId( renderer->loadTexture( "Sprite/textureBoundary.png" ) );
		Actor::setDebugCircleTextureId( renderer->loadTexture( "Sprite/circleBoundary.png" ) );

		BulletPatternGenerator::setDebugTextureId( renderer->loadTexture( "Sprite/textureBoundary.png" ) );
		BulletPatternGenerator::setDebugCircleTextureId( renderer->loadTexture( "Sprite/circleBoundary.png" ) );

		Spawner::enemyActorArr = &enemyActorArr;
		Spawner::enemyBulletGenerator = &enemyBulletGenerator;

		Actor::m_renderer = renderer;
		Actor::m_soundplayer = m_soundPlayer;

		// stage
		level.init( renderer, levelPath[levelNum].c_str() );
		Actor::setLevel( &level );

		////////////////////////////////////////
		///// end of static variable 
		////////////////////////////////////////

		enPack.init(renderer, &packageNameMap);

		/////
		// load all bullet packages
		Pawn::bulletPackageArr = &bulletPackageArr;
		
		// player bullet got first slot
		std::string name = "bulletInitFiles/bPlayer01.bInit";
		packageNameMap[name] = loadBulletPackage(name.c_str());
		name = "bulletInitFiles/bPlayer02.bInit";
		packageNameMap[name] = loadBulletPackage(name.c_str());
		name = "bulletInitFiles/bPlayer03.bInit";
		packageNameMap[name] = loadBulletPackage(name.c_str());
		name = "bulletInitFiles/bPlayer04.bInit";
		packageNameMap[name] = loadBulletPackage(name.c_str());
		name = "bulletInitFiles/bPlayer05.bInit";
		packageNameMap[name] = loadBulletPackage(name.c_str());
		
		// enemies bullet
		name = "bulletInitFiles/bSingle.bInit";
		packageNameMap[name] = loadBulletPackage(name.c_str());

		name = "bulletInitFiles/bTriple.bInit";
		packageNameMap[name] = loadBulletPackage(name.c_str());

		name = "bulletInitFiles/bSixCircle.bInit";
		packageNameMap[name] = loadBulletPackage(name.c_str());

		name = "bulletInitFiles/bFeeder.bInit";
		packageNameMap[name] = loadBulletPackage(name.c_str());

		name = "bulletInitFiles/bDualShot.bInit";
		packageNameMap[name] = loadBulletPackage(name.c_str());

		name = "bulletInitFiles/bSpin.bInit";
		packageNameMap[name] = loadBulletPackage(name.c_str());
		/////

		//// player
		playerIndex = playerActor.size();

		{
			PlayerPawn tmpActor;
			tmpActor.init(*renderer, &playerBulletGenerator, &LvUpActor);
			tmpActor.setBulletTexture( renderer->loadTexture( "Sprite/playerBullet.png" ), playerShoot );
			tmpActor.setTexture( renderer->loadTexture( "Sprite/playerSprite.png" ) );
			tmpActor.fireInterval = 0.3;
			playerActor.push_back( tmpActor );
		}

		// text on screen (+player data) init
		if (!alreadyInitTextRenderer)
		{
			t_renderer->init();

			txt_playerHP = t_renderer->allocTextBlock();
			txt_playerXP = t_renderer->allocTextBlock();
			txt_playerLV = t_renderer->allocTextBlock();
			txt_playerControl = t_renderer->allocTextBlock();
			txt_clockText = t_renderer->allocTextBlock();

			alreadyInitTextRenderer = true;
		}
		else
		{
			// persistent player data
			playerActor[0].gainLV(reassignedPlayer.LV-1);
			playerActor[0].HP = reassignedPlayer.HP;
			playerActor[0].XP = reassignedPlayer.XP;
		}

		#ifdef _GLWINDOWS
			t_renderer->setTextProperties(txt_playerHP, 25, FONTCOLOR_RED);
			t_renderer->setTextProperties(txt_playerXP, 25, FONTCOLOR_RED);
			t_renderer->setTextProperties(txt_playerLV, 25, FONTCOLOR_RED);
			t_renderer->setTextProperties(txt_playerControl, 25, FONTCOLOR_RED);
			t_renderer->setTextProperties(txt_clockText, 15, FONTCOLOR_WHITE);

			{
				int w = mEgl->getWidth();
				int h = mEgl->getHeight();
				int height = (w > h) ? (h / 16) : (w / 16);

				drawRect::setScreenResolution(w, h);
				drawVertex::setScreenResolution(w, h);

				t_renderer->setScreenResolution(w, h);
		
				t_renderer->setTextPosition(txt_playerControl, 40, 70);
				t_renderer->setTextPosition(txt_playerHP, 40, 70+(height));
				t_renderer->setTextPosition(txt_playerXP, 40, 70+(2*height));
				t_renderer->setTextPosition(txt_playerLV, 40, 70+(3*height));
		
				t_renderer->setTextPosition(txt_clockText, w-100, 70);
			}
		#elif defined(_NVANDROID)
			t_renderer->setTextProperties(txt_playerHP, 40, FONTCOLOR_RED);
			t_renderer->setTextProperties(txt_playerXP, 40, FONTCOLOR_RED);
			t_renderer->setTextProperties(txt_playerLV, 40, FONTCOLOR_RED);
			t_renderer->setTextProperties(txt_playerControl, 40, FONTCOLOR_RED);
			t_renderer->setTextProperties(txt_clockText, 20, FONTCOLOR_WHITE);

			{
				int w = mEgl->getWidth();
				int h = mEgl->getHeight();
				int height = (w > h) ? (h / 16) : (w / 16);

				drawRect::setScreenResolution(w, h);
				drawVertex::setScreenResolution(w, h);

				t_renderer->setScreenResolution(w, h);
		
				t_renderer->setTextPosition(txt_playerControl, 40, 40);
				t_renderer->setTextPosition(txt_playerHP, 40, 40+(height));
				t_renderer->setTextPosition(txt_playerXP, 40, 40+(2*height));
				t_renderer->setTextPosition(txt_playerLV, 40, 40+(3*height));
		
				t_renderer->setTextPosition(txt_clockText, w-80, 10);
			}
		#endif

		///// feedback actor
		SparkleActor.init(*renderer);
		SparkleActor.setTexture( renderer->loadTexture( "Sprite/sparkles.png" ) );
		SparkleActor.setDrawSize(10.0f);

		LvUpActor.init(*renderer);
		LvUpActor.setDrawSize(20.0f,20.0f);
		LvUpActor.setTexture( renderer->loadTexture( "Sprite/LevelUp.png" ) );
		/////

		EnemyPawn::playerPawn = &playerActor[0];
		Spawner::playerPawn = &playerActor[0];

		vec3f tmpPos = vec3f(level.enterGrid[0].getPosition());
		playerActor[0].setPosition(tmpPos);
		playerActor[0].update(0.01);

		// set prototype here
		feederPrototype.init(*renderer, &enemyBulletGenerator);
		feederPrototype.HP = 150.0f;
		feederPrototype.currentEnemyType = eENEMY_FEEDER;
		feederPrototype.setBulletTexture( renderer->loadTexture("Sprite/feederBullet.png"), feederShoot );
		feederPrototype.setTexture( renderer->loadTexture("Sprite/feederSprite.png") );
		feederPrototype.maxLifeTime = 20.0;
		feederPrototype.fireInterval = 1.0f;
		feederPrototype.loadBulletPackage( packageNameMap["bulletInitFiles/bFeeder.bInit"] );

		turretPrototype.init(*renderer, &enemyBulletGenerator);
		turretPrototype.HP = 250.0f;
		turretPrototype.currentEnemyType = eENEMY_TURRET;
		turretPrototype.setBulletTexture( renderer->loadTexture("Sprite/TurretBullet.png"), turretShoot );
		turretPrototype.setTexture( renderer->loadTexture("Sprite/turretGun.png"), renderer->loadTexture("Sprite/turretBase.png") );
		turretPrototype.maxLifeTime = 20.0;
		turretPrototype.loadBulletPackage( packageNameMap["bulletInitFiles/bPlayer01.bInit"] );

		// set spawner prototype
		initSpawnerPackage spawnPack;
		enPack.getSpawnerPackage0(spawnPack, enemyShoot);
		spawnerPrototype.setDrawSize( level.gridSize );
		spawnerPrototype.setCollisionSize( level.gridSize );
		spawnerPrototype.setSpawnerType(spawnPack);

		healthPrototype.init(*renderer);
		healthPrototype.setTexture( renderer->loadTexture("Sprite/healthPickup.png") );

		for (int i = 0; i < level.generatorGrid.size(); ++i)
		{
			Spawner tmpActor = spawnerPrototype;
			tmpActor.setPosition( vec3f(level.generatorGrid[i].getPosition()) );
			spawnerActorArr.push_back( tmpActor );
		}

		for (int i = 0; i < level.feederGrid.size(); ++i)
		{
			EnemyPawn tmpActor =  feederPrototype;
			tmpActor.setPosition(level.feederGrid[i].getPosition());
			feederActorArr.push_back( tmpActor );
		}

		for (int i = 0; i < level.turretGrid.size(); ++i)
		{
			TurretPawn tmpActor =  turretPrototype;
			tmpActor.setPosition(level.turretGrid[i].getPosition());
			turretActorArr.push_back( tmpActor );
		}

		for (int i = 0; i < level.healthGrid.size(); ++i)
		{
			Actor tmpActor =  healthPrototype;
			tmpActor.setPosition(level.healthGrid[i].getPosition());
			tmpActor.update(0.01);
			healthActorArr.push_back( tmpActor );
		}

		m_camera.setPosition( playerActor[playerIndex].getPosition() + vec3f(0.0f, 300.0f, 0.0f) );
		m_camera.setRotation(vec3f(-90.0f, 180.0f, 0.0f));

		InputController::player = &playerActor[0];

		tex_ButtonA = renderer->loadTexture( "ControlTexture/buttonA.png" );
		tex_ButtonB = renderer->loadTexture( "ControlTexture/buttonB.png" );
		tex_baseController = renderer->loadTexture( "ControlTexture/controllerBase.png" );
		tex_stickController = renderer->loadTexture( "ControlTexture/controllerStick.png" );
		tex_panel = renderer->loadTexture( "ControlTexture/panel.png" );


		m_touchTexture = renderer->loadTexture( "Sprite/bullet3.png" );
		m_drawRect = new drawRect();
		
		m_uiInitialized = true;

		return true;
	}

	void Engine::setGameplayMode(bool running)
	{
		if (mGameplayMode != running)
			requestForceRender();

		mGameplayMode = running;
	}

	bool Engine::checkWindowResized()
	{
		if (mEgl->checkWindowResized())
		{
			mResizePending = true;
			requestForceRender();
			return true;
		}

		return false;
	}

	bool Engine::resizeIfNeeded()
	{
		if (!mResizePending)
			return false;

		int w = mEgl->getWidth();
		int h = mEgl->getHeight();
		int height = (w > h) ? (h / 16) : (w / 16);

		drawRect::setScreenResolution(w, h);
		drawVertex::setScreenResolution(w, h);

		t_renderer->setScreenResolution(w, h);
		
		t_renderer->setTextPosition(txt_playerControl, 40, 40);
		t_renderer->setTextPosition(txt_playerHP, 40, 40+(height));
		t_renderer->setTextPosition(txt_playerXP, 40, 40+(2*height));
		t_renderer->setTextPosition(txt_playerLV, 40, 40+(3*height));
		
		t_renderer->setTextPosition(txt_clockText, w-80, 10);

		mResizePending = false;

		return true;
	}

	bool Engine::renderFrame(bool allocateIfNeeded)
	{
		if (!mEgl->isReadyToRender(allocateIfNeeded))
			return false;

		if (!initUI())
		{
			#ifdef _GLWINDOWS
				return false;
			#elif defined(_NVANDROID)
				android_app* tmpApp = reinterpret_cast<android_app*>(mApp);
				ANativeActivity_finish(tmpApp->activity);

				return false;
			#endif
		}

		resizeIfNeeded();

		const int w = mEgl->getWidth();
		const int h = mEgl->getHeight();
	
		// set up viewport
		glViewport((GLint)0, (GLint)0, (GLsizei)w, (GLsizei)h);

		// clear buffers as necessary
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		// do some game rendering here
		if (mForceRender > 0)
			mForceRender--;
		
		InputController::updateFrame();

		// smooth camera
		vec3f tmpPos = Lerp(m_camera.position(), playerActor[playerIndex].getPosition() + vec3f(0.0f, 300.0f, 0.0f), 0.2f);
		m_camera.setPosition( tmpPos );

		m_camera.update();
		m_camera.setViewTransformation();

		mat16f vpMatrix = m_camera.camMatrixInv * projectionMatrix;

		currentTime = Time::GetAbsoluteTimeSeconds();
		double deltaTime = currentTime - lastFrameTime;
		deltaTime = 0.003;

		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

		level.draw(0, vpMatrix);


		// update player
		for (unsigned int i = 0; i < playerActor.size(); ++i)
		{
			playerActor[i].update( deltaTime );
			if (!playerActor[i].getIsDead())
			{
				playerActor[i].draw(0, vpMatrix);
			}
		}

		// update spawners
		for (unsigned int i = 0; i < spawnerActorArr.size(); ++i)
		{
			if (!spawnerActorArr[i].getIsDead())
			{
				spawnerActorArr[i].update( deltaTime );
				spawnerActorArr[i].draw(0, vpMatrix);
			}
		}

		// update turret
		for (unsigned int i = 0; i < turretActorArr.size(); ++i)
		{
			if (!turretActorArr[i].getIsDead())
			{
				turretActorArr[i].update( deltaTime );
				turretActorArr[i].draw(0, vpMatrix);
			}
		}

		// update health
		for (unsigned int i = 0; i < healthActorArr.size(); ++i)
		{
			if (!healthActorArr[i].getIsDead())
			{
				// chk collision with player
				Actor* tmpActor = &healthActorArr[i];
				vec3f tmpVec = tmpActor->getPosition();
				if (!tmpActor->getIsDead())
				if ( playerActor[0].chkCollision(tmpVec, tmpActor->getCollisionSize()) )
				{
					// add HP
					playerActor[0].getHealthPickup();
					tmpActor->setIsDead(true);
				}

				healthActorArr[i].draw(0, vpMatrix);
			}
		}

		// update feeder
		Tick_killDead(feederActorArr, deltaTime, 0, vpMatrix);

		// update enemies
		if (enemyActorArr.size() > 6)
		{
			int removeElements = enemyActorArr.size() - 6;
			enemyActorArr.erase(enemyActorArr.begin());
		}
		Tick_killDead(enemyActorArr, deltaTime, 0, vpMatrix);

		// update player bullet
		TouhouTick_killDead(playerBulletGenerator, 0.01f);
		for (int k = 0; k < playerBulletGenerator.size(); ++k)
		{
			// draw 
			playerBulletGenerator[k].draw(0, vpMatrix);
		}

		// update enemies bullet
		TouhouTick_killDead(enemyBulletGenerator, 0.01f);
		for (int k = 0; k < enemyBulletGenerator.size(); ++k)
		{
			// draw 
			enemyBulletGenerator[k].draw(0, vpMatrix);
		}

		// for each player bullet
		for (int k = 0; k < playerBulletGenerator.size(); ++k)
		{
			BulletPatternGenerator& tmpBullet = playerBulletGenerator[k];
			// for each wall
			for (int l = 0; l < level.wallGrid.size(); ++l)
			{
				Actor* tmpActor = &level.wallGrid[l];
				if ( tmpBullet.chkCollision(tmpActor->getPosition(), tmpActor->getCollisionSize()) )
				{
					// bullet hit wall - bullet will kill itself anyway
				}
			}
			// for each enemy
			for (int l = 0; l < enemyActorArr.size(); ++l)
			{
				Actor* tmpActor = &enemyActorArr[l];
				if (!tmpActor->getIsDead())
				if ( tmpBullet.chkCollision(tmpActor->getPosition(), tmpActor->getCollisionSize()) )
				{
					// bullet hit enemy - hurt it
					tmpActor->TakeDamage(50.0f);
					if (tmpActor->getIsDead())
					{
						// add kill count
						playerActor[0].otherKilled++;
					}
				}
			}
			// for each feeder
			for (int l = 0; l < feederActorArr.size(); ++l)
			{
				Actor* tmpActor = &feederActorArr[l];
				if (!tmpActor->getIsDead())
				if ( tmpBullet.chkCollision(tmpActor->getPosition(), tmpActor->getCollisionSize()) )
				{
					// bullet hit enemy - hurt it
					tmpActor->TakeDamage(50.0f);
					if (tmpActor->getIsDead())
					{
						// add kill count
						playerActor[0].otherKilled++;
					}
				}
			}
			// for each spawner
			for (int l = 0; l < spawnerActorArr.size(); ++l)
			{
				Actor* tmpActor = &spawnerActorArr[l];
				if (!tmpActor->getIsDead())
				if ( tmpBullet.chkCollision(tmpActor->getPosition(), tmpActor->getCollisionSize()) )
				{
					// bullet hit enemy - hurt it
					tmpActor->TakeDamage(30.0f);
					if (tmpActor->getIsDead())
					{
						// add kill count
						playerActor[0].spawnerKilled++;
					}
				}
			}
			// for each turret
			for (int l = 0; l < turretActorArr.size(); ++l)
			{
				Actor* tmpActor = &turretActorArr[l];
				if (!tmpActor->getIsDead())
				if ( tmpBullet.chkCollision(tmpActor->getPosition(), tmpActor->getCollisionSize()) )
				{
					// bullet hit enemy - hurt it
					tmpActor->TakeDamage(50.0f);
					if (tmpActor->getIsDead())
					{
						// add kill count
						playerActor[0].turretKilled++;
					}
				}
			}
		}

		// for each enemy bullet
		for (int k = 0; k < enemyBulletGenerator.size(); ++k)
		{
			BulletPatternGenerator& tmpBullet = enemyBulletGenerator[k];
			// for each wall
			for (int l = 0; l < level.wallGrid.size(); ++l)
			{
				Actor* tmpActor = &level.wallGrid[l];
				if ( tmpBullet.chkCollision(tmpActor->getPosition(), tmpActor->getCollisionSize()) )
				{
					// bullet hit wall - bullet will kill itself anyway
				}
			}
			// for each player
			for (int l = 0; l < playerActor.size(); ++l)
			{
				int grazeCount = 0;
				int hitCount = 0;
				PlayerPawn* tmpActor = &playerActor[l];
				if (!tmpActor->getIsDead())
				tmpBullet.chkGrazeCollision(tmpActor->getPosition(), tmpActor->getCollisionSize(), tmpActor->grazeRadius, grazeCount, hitCount);
				if (hitCount > 0)
				{
					// bullet hit player - hurt it TODO
					tmpActor->TakeDamage(hitCount * 5.0f);
				}
				if (grazeCount > 0)
				{
					// bullet graze player - add XP
					float xp = (float)grazeCount * 40.0f * deltaTime;
					tmpActor->gainXP(xp);
					vec3f tmppos = tmpActor->getPosition();
					tmppos.z += 25.0f;
					SparkleActor.setPosition(tmppos);
					SparkleActor.update(deltaTime);
					SparkleActor.draw(0, vpMatrix);
				}
			}
		}

		///// feedback update
		LvUpActor.update(deltaTime);
		LvUpActor.draw(0, vpMatrix);
		/////

		// draw panel for text
		m_drawRect->draw(tex_panel, 0, 0, 250, 270, color); 

		// start rendering bitfont text overlaid here.
		t_renderer->startDrawText();
	
		// draw HP text
		{
			std::string tmpStr;
			tmpStr.append( "HP : " );
			tmpStr.append( lexical_cast<std::string, int>( playerActor[0].HP ) );
			tmpStr.append( "/" );
			tmpStr.append( lexical_cast<std::string, int>( playerActor[0].maxHP ) );

			t_renderer->setText(txt_playerHP, tmpStr.c_str());
			t_renderer->draw(txt_playerHP);
		}
		// draw XP text
		{
			std::string tmpStr;
			tmpStr.append( "XP : " );
			tmpStr.append( lexical_cast<std::string, int>( playerActor[0].XP ) );
			tmpStr.append( "/" );
			tmpStr.append( lexical_cast<std::string, int>( playerActor[0].nextLV_XP ) );
		
			t_renderer->setText(txt_playerXP, tmpStr.c_str());
			t_renderer->draw(txt_playerXP);
		}
		// draw LV text
		{
			std::string tmpStr;
			tmpStr.append( "LV : " );
			tmpStr.append( lexical_cast<std::string, int>( playerActor[0].LV ) );
			if ( playerActor[0].LV == playerActor[0].maxLV ) tmpStr.append( " (MAX)" );
		
			t_renderer->setText(txt_playerLV, tmpStr.c_str());
			t_renderer->draw(txt_playerLV);
		}
		// draw control text
		{
			std::string tmpStr;
			switch (InputController::currentControlType)
			{
			case CONTROLTYPE_ORIGINAL : tmpStr.append("Original"); break;
			case CONTROLTYPE_MOVINGCENTER : tmpStr.append("MovingCenter"); break;
			case CONTROLTYPE_PLAYERCENTER : tmpStr.append("PlayerCenter"); break;
			case CONTROLTYPE_SWIPE : tmpStr.append("Swipe"); break;
			default : break;
			}
			t_renderer->setText(txt_playerControl, tmpStr.c_str());
			t_renderer->draw(txt_playerControl);
		}

		// draw clock
		{		
			t_renderer->draw(txt_clockText);
		
			// we update the clock after drawing to ensure it will change on pause
			int mins = mTimeVal / 60;
			float secs = (float)mTimeVal - mins*60;
			char str[32];
			sprintf(str, "%03d:%05.2f", mins, secs);

			t_renderer->setText(txt_clockText, str);
		}
	
		// done rendering overlaid text.
		t_renderer->endDrawText();

		// draw controller texture
		vec4f buttonA = InputController::posButtonA;
		vec4f buttonB = InputController::posButtonB;
		vec4f controllerBase = InputController::posControllerBase;
		vec4f controllerStick = InputController::posControllerStick;

		switch (InputController::currentControlType)
		{
		case CONTROLTYPE_ORIGINAL : 
			{
				m_drawRect->draw(tex_baseController, controllerBase.x, controllerBase.z, controllerBase.y, controllerBase.w, color); 
				m_drawRect->draw(tex_stickController, controllerStick.x, controllerStick.z, controllerStick.y, controllerStick.w, color); 

				m_drawRect->draw(tex_ButtonA, buttonA.x, buttonA.z, buttonA.y, buttonA.w, color); 
				m_drawRect->draw(tex_ButtonB, buttonB.x, buttonB.z, buttonB.y, buttonB.w, color); 

				break;
			}
		case CONTROLTYPE_MOVINGCENTER : 
			{
				m_drawRect->draw(tex_baseController, controllerBase.x, controllerBase.z, controllerBase.y, controllerBase.w, color); 
				m_drawRect->draw(tex_stickController, controllerStick.x, controllerStick.z, controllerStick.y, controllerStick.w, color); 

				m_drawRect->draw(tex_ButtonA, buttonA.x, buttonA.z, buttonA.y, buttonA.w, color); 
				m_drawRect->draw(tex_ButtonB, buttonB.x, buttonB.z, buttonB.y, buttonB.w, color); 

				break;
			}
		case CONTROLTYPE_PLAYERCENTER : 
			{
				m_drawRect->draw(tex_ButtonA, buttonA.x, buttonA.z, buttonA.y, buttonA.w, color); 
				m_drawRect->draw(tex_ButtonB, buttonB.x, buttonB.z, buttonB.y, buttonB.w, color); 

				break;
			}
		case CONTROLTYPE_SWIPE : 
			{
				m_drawRect->draw(tex_ButtonA, buttonA.x, buttonA.z, buttonA.y, buttonA.w, color); 
				m_drawRect->draw(tex_ButtonB, buttonB.x, buttonB.z, buttonB.y, buttonB.w, color); 

				break;
			}
		default : break;
		}

		glDisable(GL_BLEND);




		
		m_soundPlayer->update();
		mEgl->swap();
		renderer->swapBuffer();

		lastFrameTime = Time::GetAbsoluteTimeSeconds();
		
		// check reach exit?
		vec3f tmpVec = level.exitGrid[0].getPosition();
		if (playerActor[0].chkCollision(tmpVec, level.exitGrid[0].getCollisionSize()))
		{
			// reach exit - load new level
			loadNewLevel();
		}

		return true;
	}

	void Engine::updateFrame(bool interactible, long deltaTime)
	{
		if (interactible)
		{
			// Each frame, we check to see if the window has resized.  While the
			// various events we get _should_ cover this, in practice, it appears
			// that the safest move across all platforms and OSes is to check at 
			// the top of each frame
			checkWindowResized();

			// Time stands still when we're auto-paused, and we don't
			// automatically render
			if (mGameplayMode)
			{
				advanceTime(deltaTime);

				// This will try to set up EGL if it isn't set up
				// When we first set up EGL completely, we also load our GLES resources
				// If these are already set up or we succeed at setting them all up now, then
				// we go ahead and render.
				renderFrame(true);
			}
			else if (isForcedRenderPending()) // forced rendering when needed for UI, etc
			{
				renderFrame(true);
			}
		}
		else
		{
			// Even if we are not interactible, we may be visible, so we
			// HAVE to do any forced renderings if we can.  We must also
			// check for resize, since that may have been the point of the
			// forced render request in the first place!
			if (isForcedRenderPending() && mEgl->isReadyToRender(false)) 
			{
				checkWindowResized();
				renderFrame(false);
			}
		}
	}
}

Rendering Code Sample

#pragma once
#include <string>
#include <vector>
#include "util.h"

namespace banknamespace
{
	class Renderer
	{
	public:
		Renderer();
		virtual ~Renderer(void);

		virtual void init(std::vector<void*> args ) = 0;

		virtual void clear() = 0;
		virtual void swapBuffer() = 0;

		virtual unsigned int loadTexture( const char* szFilePath ) = 0;
		virtual void clearAllTextures() = 0;
		virtual unsigned int loadMemoryTexture( unsigned char* pixels, int width, int height ) = 0;
		virtual void bindTexture( unsigned int textureState, unsigned int textureId ) = 0;
		virtual void disableTexture( unsigned int textureState ) = 0;

		virtual int createProgram(const char* programName, std::vector<attrStruct>& attrString, std::vector<const char*>& uniformString) = 0;
		virtual void useProgram(int progID) = 0;
		virtual int getLocation(const char* name) = 0;
		virtual void setVariable(int ulocation, shaderType type, void* value) = 0;
		
		virtual unsigned int createBuffer( void* start, int bufferSize, bufferType type ) = 0;
		virtual void drawIndex(unsigned int idVBO, unsigned int idIBO, int strideSize, unsigned int startVertex, unsigned int endVertex, int indexCount, int startIndex, int baseVertex) = 0;
		virtual void drawRect(unsigned int tex, void* mvp, void* vertpos) = 0;

		virtual void* getAndroidApp() = 0;
	};
}

#ifndef __ANDROIDGLRENDERER_H
#define __ANDROIDGLRENDERER_H


#include <cassert>
#include <vector>
#include <cstdarg>

#include <nv_and_util/nv_native_app_glue.h>
#include <nv_egl_util/nv_egl_util.h>
#include <nv_glesutil/nv_draw_rect.h>

#include "util.h"
#include "ShaderManagerOpenGL.h"

#include "Renderer.h"
#include "drawVertex.h"

#define MAX_LOADSTRING 100

namespace banknamespace
{
	

	class AndroidGLrenderer : public Renderer
	{
	public:
		void* a_app;
		android_app* mApp;
		NvEGLUtil* egl;
		drawVertex *m_drawRect;

		AndroidGLrenderer();
		~AndroidGLrenderer(void);

		void init( std::vector<void*> args );

		// GL method
		void clear();
		void swapBuffer();

		unsigned int loadTexture( const char* szFilePath );
		void clearAllTextures();
		unsigned int loadMemoryTexture( unsigned char* pixels, int width, int height );
		void bindTexture( unsigned int textureState, unsigned int textureId );
		void disableTexture( unsigned int textureState );

		int createProgram(const char* programName, std::vector<attrStruct>& attrString, std::vector<const char*>& uniformString);
		void useProgram(int progID);
		int getLocation(const char* name);
		void setVariable(int ulocation, shaderType type, void* value);

		unsigned int createBuffer( void* start, int bufferSize, bufferType type );
		void drawIndex(unsigned int idVBO, unsigned int idIBO, int strideSize, unsigned int startVertex, unsigned int endVertex, int indexCount, int startIndex, int baseVertex);
		void drawRect(unsigned int tex, void* mvp, void* vertpos);

		void* getAndroidApp();
	protected:
		ShaderManagerOpenGL shaderManager;
		std::vector<unsigned int> textureIdArr;

		void InitializeOpenGL();
		void ShutdownOpenGL();
	};

}

#endif // __ANDROIDGLRENDERER_H
#pragma once
#include <cassert>
#include <vector>
#include "ShaderManagerOpenGL.h"

#include "Resource.h"

#include "util.h"
#include "Renderer.h"
#include "drawVertex.h"

#include "IL/il.h"
#pragma comment (lib, "DevIL.lib")

#define MAX_LOADSTRING 100

namespace banknamespace
{
	

	class OpenGLrenderer : public Renderer
	{
	public:
		HDC* m_hdcWindow;
		HGLRC m_renderingContext;
		drawVertex *m_drawRect;

		OpenGLrenderer();
		~OpenGLrenderer(void);

		void init( std::vector<void*> args );

		// GL method
		void clear();
		void swapBuffer();

		unsigned int loadTexture( const char* szFilePath );
		void clearAllTextures();
		unsigned int loadMemoryTexture( unsigned char* pixels, int width, int height );
		void bindTexture( unsigned int textureState, unsigned int textureId );
		void disableTexture( unsigned int textureState );

		int createProgram(const char* programName, std::vector<attrStruct>& attrString, std::vector<const char*>& uniformString);
		void useProgram(int progID);
		int getLocation(const char* name);
		void setVariable(int ulocation, shaderType type, void* value);

		unsigned int createBuffer( void* start, int bufferSize, bufferType type );
		void drawIndex(unsigned int idVBO, unsigned int idIBO, int strideSize, unsigned int startVertex, unsigned int endVertex, int indexCount, int startIndex, int baseVertex);
		void drawRect(unsigned int tex, void* mvp, void* vertpos);

		void* getAndroidApp();
	protected:
		ShaderManagerOpenGL shaderManager;
		std::vector<unsigned int> textureIdArr;

		void InitializeOpenGL();
		void ShutdownOpenGL();
	};

}



#ifndef _DRAW_VERT_H
#define _DRAW_VERT_H

#include <string>

#include "Define.h"
#ifdef _GLWINDOWS
	#ifdef _WIN32
		
	#endif
#elif defined(_NVANDROID)
	#include <GLES2/gl2.h>
#endif


class drawVertex {
public:
	drawVertex();
	~drawVertex();

	static void setScreenResolution(int w, int h);

	// A GLES context must be bound when calling this, and it must be
	// the same GLES context for all calls.  If you need to delete the
	// GLES context, you must either delete all instances, or call
	// releaseGLES
	void draw(GLint tex, void* mvp, void* vertices, const float color[4]);

	// Most apps can avoid this if they always delete their NvDrawRects
	// when they lose their GLES context
	static void releaseGLES();

protected:

	class RectShader {
	public:
		RectShader(const char* vertText, const char* fragText);
		void bindShader(void* mvp, void* vert, const float color[4]);
		GLint m_program;
		GLint m_mvp;
		GLint m_destRectUniform;
		GLint m_colorUniform;
		GLint m_posAttrib;
		GLint m_uvAttrib;
	};

	static void initGLES();
	static int draw_glsl_log(GLuint obj, GLenum check_compile);
	static GLint draw_load_program_from_strings(const char *vertStr, const char *fragStr);
	
	static bool ms_glesInitialized;
	static RectShader* ms_shaderTex;
	static RectShader* ms_shaderColor;
	static float ms_screenWidth;
	static float ms_screenHeight;
	static int ms_instanceCount;
};

#endif // _DRAW_VERT_H

#include "StdAfx.h"
#include "drawVertex.h"

#include "Define.h"
#ifdef _GLWINDOWS
	#ifdef _WIN32
		
	#endif
#elif defined(_NVANDROID)
	#include <GLES2/gl2ext.h>
#endif



bool drawVertex::ms_glesInitialized = false;
drawVertex::RectShader* drawVertex::ms_shaderTex = NULL;
drawVertex::RectShader* drawVertex::ms_shaderColor = NULL;
float drawVertex::ms_screenWidth = 1.0f;
float drawVertex::ms_screenHeight = 1.0f;

int drawVertex::ms_instanceCount = 0;

static GLushort indices [4] = {  2, 3, 1, 0 };
static GLfloat vertices[8] = {  100.0f, 0.0f,
							    0.0f, 0.0f,
								100.0f, 100.0f,
							    0.0f, 100.0f };

static GLfloat uvs [8] = {  
							1.0f,  1.0f,
							0.0f,  1.0f,
							0.0f,  0.0f,
						    1.0f,  0.0f
						      };

static const char s_vertShader[] = 
"uniform mat4 mvp;\n"
"uniform vec4 uDestRect;\n"
"attribute vec2 aPos;\n"
"attribute vec2 aUV;\n"
"varying vec2 vTexCoord;\n"
"void main() {\n"
"   vec2 pos = mix(uDestRect.xy, uDestRect.zw, aPos);\n"
"   vec4 tmp = vec4(aPos.x, 0.0, aPos.y,1.0);\n"
"	gl_Position = mvp * tmp;\n"
"   vTexCoord = aUV;\n"
"}\n";

static const char s_fragShaderTex[] =
"precision lowp float;\n"
"uniform sampler2D uTex;\n"
"varying vec2 vTexCoord;\n"
"uniform vec4 uColor;\n"
"void main() {\n"
"    gl_FragColor = uColor * texture2D(uTex, vTexCoord);\n"
"}\n";

static const char s_fragShaderColor[] =
"precision lowp float;\n"
"varying vec2 vTexCoord;\n"
"uniform vec4 uColor;\n"
"void main() {\n"
"    gl_FragColor = uColor;\n"
"}\n";

int drawVertex::draw_glsl_log(GLuint obj, GLenum check_compile)
{
    if (check_compile == GL_COMPILE_STATUS)
    {
        int len = 0;
        glGetShaderiv(obj, GL_INFO_LOG_LENGTH, &len);
        if(len > 0)
        {
            char *str = (char *) malloc(len * sizeof(char));
            if (str)
            {
                glGetShaderInfoLog(obj, len, NULL, str);
                free(str);
            }
        }
    }
    else
    {
        int len = 0;
        glGetProgramiv(obj, GL_INFO_LOG_LENGTH, &len);
        if(len > 0)
        {
            char *str = (char *)malloc(len * sizeof(char));
            if (str)
            {
                glGetProgramInfoLog(obj, len, NULL, str);
                free(str);
            }
        }
    }
    return 0;
}

GLint drawVertex::draw_load_program_from_strings(const char *vertStr, const char *fragStr)
{
    const char* shaders[2];
    int sizes[2];

    GLuint vert = glCreateShader(GL_VERTEX_SHADER);
    GLuint frag = glCreateShader(GL_FRAGMENT_SHADER);

    GLuint program = glCreateProgram();
    glAttachShader(program, vert);
    glAttachShader(program, frag);

    if(vertStr)
    {
		shaders[0] = vertStr;
		sizes[0] = strlen(shaders[0]);
    }
	else
	{
    	return (GLint)0;
	}

    glShaderSource(vert, 1, (const char **) shaders, sizes);
    glCompileShader(vert);
    draw_glsl_log(vert, GL_COMPILE_STATUS);

    if(fragStr)
    {
		shaders[0] = fragStr;
		sizes[0] = strlen(shaders[0]);
    }
	else
	{
    	return (GLint)0;
	}

    glShaderSource(frag, 1, (const char **) shaders, sizes);
    glCompileShader(frag);
    draw_glsl_log(frag, GL_COMPILE_STATUS);

    glLinkProgram(program);
    draw_glsl_log(program, GL_LINK_STATUS);

    return program;
}

drawVertex::RectShader::RectShader(const char* vertText, const char* fragText)
{
	m_program = draw_load_program_from_strings(vertText, fragText);
    glUseProgram(m_program);

	m_mvp = glGetUniformLocation(m_program, "mvp");
    m_destRectUniform = glGetUniformLocation(m_program, "uDestRect");
	m_colorUniform = glGetUniformLocation(m_program, "uColor");
	m_posAttrib = glGetAttribLocation(m_program, "aPos");
	m_uvAttrib = glGetAttribLocation(m_program, "aUV");

	GLint texUni = glGetUniformLocation(m_program, "uTex");
	if (texUni != -1)
		glUniform1i(texUni, 0);
}

void drawVertex::RectShader::bindShader(void* mvp, void* vert, const float color[4])
{
    glUseProgram(m_program);

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glVertexAttribPointer(m_posAttrib, 2, GL_FLOAT, GL_FALSE, 0, vert);
    glEnableVertexAttribArray(m_posAttrib);

	if (m_uvAttrib != -1)
	{
		glVertexAttribPointer(m_uvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uvs);
		glEnableVertexAttribArray(m_uvAttrib);
	}

	glUniformMatrix4fv( m_mvp, 1, 0, reinterpret_cast<float*>( mvp ) ); 
	glUniform4fv(m_colorUniform, 1, color);
}

drawVertex::drawVertex()
{
	ms_instanceCount++;
}

drawVertex::~drawVertex()
{
	ms_instanceCount--; 
	if (!ms_instanceCount)  
		releaseGLES();
}

void drawVertex::initGLES()
{
	ms_glesInitialized = true;
	ms_shaderTex = new RectShader(s_vertShader, s_fragShaderTex);
	ms_shaderColor = new RectShader(s_vertShader, s_fragShaderColor);
}

void drawVertex::releaseGLES()
{
	ms_glesInitialized = false;
	delete ms_shaderTex;
	delete ms_shaderColor;
	ms_shaderTex = NULL;
	ms_shaderColor = NULL;
}

void drawVertex::setScreenResolution(int w, int h)
{
	ms_screenWidth = (float)w;
	ms_screenHeight = (float)h;
}

void drawVertex::draw(GLint tex, void* mvp, void* vertices, const float color[4])
{
	RectShader* shader = tex ? ms_shaderTex : ms_shaderColor;

	if (!ms_glesInitialized) 
	{
		initGLES();
		shader = tex ? ms_shaderTex : ms_shaderColor;
		if (!shader) 
			return;
	}
	
	shader->bindShader(mvp, vertices, color);

	if (tex)
	{
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, tex);
	}

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices);
}

Support Classes Code Sample

#ifndef __EGLINTERFACE_H
#define __EGLINTERFACE_H

#include "Define.h"

#include <vector>

namespace banknamespace
{
	class EGLInterface
	{
	public:
		EGLInterface();
		~EGLInterface();

		void init( std::vector<void*> args );
		void setWidth(int w);
		void setHeight(int h);
		int getWidth();
		int getHeight();

		bool setWindow(void* window);
		bool checkWindowResized();
		bool isReadyToRender(bool allocateIfNeeded = false);

		bool swap();

		void* egl;
	protected:
		
		int width;
		int height;
	};
}




#endif
#include "StdAfx.h"
#include "EGLInterface.h"

#ifdef _GLWINDOWS

	namespace banknamespace
	{
	
		EGLInterface::EGLInterface()
		{
			width = 0;
			height = 0;
			egl = 0;
		}

		EGLInterface::~EGLInterface()
		{
			
		}

		void EGLInterface::init( std::vector<void*> args )
		{
			///// args[0] = void* egl
			egl = args[0];
		}

		void EGLInterface::setWidth(int w)
		{
			this->width = w;
		}
		void EGLInterface::setHeight(int h)
		{
			this->height = h;
		}
		int EGLInterface::getWidth()
		{
			return this->width;
		}
		int EGLInterface::getHeight()
		{
			return this->height;
		}

		bool EGLInterface::setWindow(void* window)
		{
			return true;
		}

		bool EGLInterface::checkWindowResized()
		{
			return false;
		}
		bool EGLInterface::isReadyToRender(bool allocateIfNeeded)
		{
			return true;
		}

		bool EGLInterface::swap()
		{
			return true;
		}
	}

#elif defined(_NVANDROID)

	#include <nv_egl_util/nv_egl_util.h>
	namespace banknamespace
	{
	
		EGLInterface::EGLInterface()
		{
			width = 0;
			height = 0;
			egl = 0;
		}

		EGLInterface::~EGLInterface()
		{
			
		}

		void EGLInterface::init( std::vector<void*> args )
		{
			///// args[0] = void* egl
			egl = args[0];
		}

		void EGLInterface::setWidth(int w)
		{
			this->width = w;
		}
		void EGLInterface::setHeight(int h)
		{
			this->height = h;
		}
		int EGLInterface::getWidth()
		{
			NvEGLUtil* eglutil = reinterpret_cast<NvEGLUtil*>( egl );
			return eglutil->getWidth();
		}
		int EGLInterface::getHeight()
		{
			NvEGLUtil* eglutil = reinterpret_cast<NvEGLUtil*>( egl );
			return eglutil->getHeight();
		}

		bool EGLInterface::setWindow(void* window)
		{
			NvEGLUtil* eglutil = reinterpret_cast<NvEGLUtil*>( egl );
			ANativeWindow* win = reinterpret_cast<ANativeWindow*>( window );
			bool tmp = eglutil->setWindow(win);
			return tmp;
		}

		bool EGLInterface::checkWindowResized()
		{
			NvEGLUtil* eglutil = reinterpret_cast<NvEGLUtil*>( egl );
			return eglutil->checkWindowResized();
		}
		bool EGLInterface::isReadyToRender(bool allocateIfNeeded)
		{
			NvEGLUtil* eglutil = reinterpret_cast<NvEGLUtil*>( egl );
			return eglutil->isReadyToRender(allocateIfNeeded);
		}

		bool EGLInterface::swap()
		{
			NvEGLUtil* eglutil = reinterpret_cast<NvEGLUtil*>( egl );
			return eglutil->swap();
		}
	}

#else

#error implementation missing

#endif
#ifndef __SOUNDPLAYER_H
#define __SOUNDPLAYER_H

#include "Define.h"

#include <vector>

namespace banknamespace
{
	class SoundPlayer
	{
	public:
		SoundPlayer();
		~SoundPlayer();

		unsigned int init(std::vector<void*> args );

		// returns true if the audio is playing, false if not
		bool isPlaying(unsigned int id);
	
		// starts playback on true, stops on false
		// Playback is currently looped
		bool play(unsigned int id, bool play);
		bool setLoop(unsigned int id, bool isLoop);
		bool setVolume(unsigned int id, int millibel);

		void update();
		
	protected:
		void* soundSystem;
		
		std::vector<float> volume;
		std::vector<void*> soundPointerId;
	};
}




#endif
#include "StdAfx.h"
#include "SoundPlayer.h"

#ifdef _GLWINDOWS

	#include "fmod.hpp"
	#include "fmod_errors.h"
	#pragma comment(lib, "fmodex_vc.lib")

	
	FMOD::Channel* channels[32];

	namespace banknamespace
	{
	
		SoundPlayer::SoundPlayer()
		{
			FMOD::System* system;
			
			FMOD_RESULT result = FMOD::System_Create( &system );
			result = system->init(32, FMOD_INIT_NORMAL, 0);

			soundSystem = system;
		}

		SoundPlayer::~SoundPlayer()
		{
			FMOD::Sound* sound;
			for (int i = 0; i < soundPointerId.size(); ++i)
			{
				sound = reinterpret_cast<FMOD::Sound*>( soundPointerId[i] );
				sound->release();
			}
		}

		unsigned int SoundPlayer::init( std::vector<void*> args )
		{
			///// args[0] = const char* filename
			///// args[1] = AAssetManager* assetManager
			std::string tmpStr = "assets/";
			tmpStr.append(reinterpret_cast<const char*>( args[0] ));

			FMOD::Sound* sound;
			FMOD::System* system = reinterpret_cast<FMOD::System*>( soundSystem );
			FMOD_RESULT result = system->createStream(tmpStr.c_str(), FMOD_HARDWARE | FMOD_2D, 0, &sound);
			sound->setMode(FMOD_HARDWARE | FMOD_2D);

			soundPointerId.push_back(sound);

			return soundPointerId.size()-1;
		}

		// returns true if the audio is playing, false if not
		bool SoundPlayer::isPlaying(unsigned int id)
		{
			return false;
		}
	
		// starts playback on true, stops on false
		// Playback is currently looped
		bool SoundPlayer::play(unsigned int id, bool play)
		{
			FMOD::Sound* sound = reinterpret_cast<FMOD::Sound*>( soundPointerId[id] );
			FMOD::System* system = reinterpret_cast<FMOD::System*>( soundSystem );

			if (play)
			{
				system->playSound(FMOD_CHANNEL_FREE, sound, false, &channels[id]);
				channels[id]->setVolume(volume[id]);
				channels[id]->setPaused(false);
			}
			else
			{
				channels[id]->stop();
			}
			
			return true;
		}

		bool SoundPlayer::setLoop(unsigned int id, bool isLoop)
		{
			FMOD::Sound* sound = reinterpret_cast<FMOD::Sound*>( soundPointerId[id] );
			if (isLoop) sound->setMode(FMOD_HARDWARE | FMOD_LOOP_NORMAL | FMOD_2D);
			
			return true;
		}

		bool SoundPlayer::setVolume(unsigned int id, int millibel)
		{
			volume.push_back( millibel / 100.0f );
			return true;
		}

		void SoundPlayer::update()
		{
			FMOD::System* system = reinterpret_cast<FMOD::System*>( soundSystem );
			system->update();
		}
	}

#elif defined(_NVANDROID)

	#include "NvSLESPlayer.h"
	namespace banknamespace
	{
	
		SoundPlayer::SoundPlayer()
		{
		
		}

		SoundPlayer::~SoundPlayer()
		{
			for (int i = 0; i < soundPointerId.size(); ++i)
			{
				delete soundPointerId[i];
			}
		}

		unsigned int SoundPlayer::init( std::vector<void*> args )
		{
			///// args[0] = const char* filename
			///// args[1] = AAssetManager* assetManager
			const char* filename = reinterpret_cast<const char*>( args[0] );
			AAssetManager* assetManager = reinterpret_cast<AAssetManager*>( args[1] );

			NvSLESPlayer* sptr = NvSLESPlayer::create(assetManager, filename);

			soundPointerId.push_back(sptr);

			return soundPointerId.size()-1;
		}

		// returns true if the audio is playing, false if not
		bool SoundPlayer::isPlaying(unsigned int id)
		{
			NvSLESPlayer* tmp = reinterpret_cast<NvSLESPlayer*>( soundPointerId[id] );
			return tmp->isPlaying();
		}
	
		// starts playback on true, stops on false
		// Playback is currently looped
		bool SoundPlayer::play(unsigned int id, bool play)
		{
			NvSLESPlayer* tmp = reinterpret_cast<NvSLESPlayer*>( soundPointerId[id] );
			return tmp->play(play);
		}

		bool SoundPlayer::setLoop(unsigned int id, bool isLoop)
		{
			NvSLESPlayer* tmp = reinterpret_cast<NvSLESPlayer*>( soundPointerId[id] );
			return tmp->setLoop(isLoop);
		}

		bool SoundPlayer::setVolume(unsigned int id, int millibel)
		{
			NvSLESPlayer* tmp = reinterpret_cast<NvSLESPlayer*>( soundPointerId[id] );
			return tmp->setVolume(millibel);
		}

		void SoundPlayer::update()
		{
			
		}
	}

#else

#error implementation missing

#endif

Input Controller Code Sample

#pragma once
//#include "KeyboardInput.h"
//#include "MouseInput.h"
#include "util.h"
#include <cmath>
#include <vector>
#include "Pawn.h"
#include "NvMultiInput.h"
#include "Define.h"

namespace banknamespace
{
	enum controlType
	{
		CONTROLTYPE_ORIGINAL,
		CONTROLTYPE_MOVINGCENTER,
		CONTROLTYPE_PLAYERCENTER,
		CONTROLTYPE_SWIPE,
		CONTROLTYPE_LAST
	};

	class InputController
	{
	public:

		// variable
		static bool isInit;
		static bool isControlChanged;

		static Pawn* player;
		static vec3f accel;
		static float width;
		static float height;
		static float buttonSizeWidth;
		static float buttonSizeHeight;
		static float controllerSizeWidth;
		static float controllerSizeHeight;

		static bool touchState[10];
		static bool buttonState[5];

		static vec4f posButtonA;
		static vec4f posButtonB;
		static vec4f posControllerBase;
		static vec4f posControllerStick;

		static vec2f centerBase;
		static vec2f centerStick;

		static controlType currentControlType;
		// end of variable

		static void initVar( std::vector<void*> args );
		//static void updateAccelerometer(float x, float y, float z);
		
		static void updateTouchState(NvMultiInput *start);
		static void updateTouch(NvMultiInput *nvInput);
		static void updateNoTouch();

		///// control scheme here
		static void directionOriginal(NvMultiInput *nvInput);
		static void directionPlayerCenter(NvMultiInput *nvInput);
		static void directionMovingCenter(NvMultiInput *nvInput);
		static void directionSwipe(NvMultiInput *nvInput);
		static bool virtualButtonAB(NvMultiInput *nvInput);

		static void virtualButtonKeyboard(bool buttons[6]);

		static void updateFrame();
	};
}

#include "StdAfx.h"
#include "InputController.h"


#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "InputController.cpp", __VA_ARGS__))

namespace banknamespace
{
	float InputController::buttonSizeWidth = 150.0f;
	float InputController::buttonSizeHeight = 150.0f;
	float InputController::controllerSizeWidth = 300.0f;
	float InputController::controllerSizeHeight = 300.0f;

	Pawn* InputController::player = 0;
	vec3f InputController::accel;
	float InputController::width;
	float InputController::height;
	bool InputController::touchState[10];
	bool InputController::buttonState[5];
	controlType InputController::currentControlType;

	vec4f InputController::posButtonA;
	vec4f InputController::posButtonB;
	vec4f InputController::posControllerBase;
	vec4f InputController::posControllerStick;

	vec2f InputController::centerBase;
	vec2f InputController::centerStick;

	bool InputController::isInit = false;
	bool InputController::isControlChanged = true;

	void InputController::initVar( std::vector<void*> args )
	{
		
	}

	//void InputController::updateAccelerometer(float x, float y, float z)
	//{
	//	// x,y,z range of -10 to 10;
	//	accel.x += -x / 10.0f;
	//	accel.y += -y / 10.0f;
	//	accel.z += -z / 10.0f;
	//}

	void InputController::updateTouchState(NvMultiInput *start)
	{
		for (int i = 0; i < 10; ++i)
		{
			touchState[i] = false;
		}

		for (int i = 0; i < 5; ++i)
		{
			buttonState[i] = false;
		}

		NvMultiInput *td = start->getNextTouch(NULL);
		while (td)
		{
			const int id = td->getId();
			
			touchState[id] = true;
    		td = start->getNextTouch(td);
		}
	}

	void InputController::updateNoTouch()
	{
		if (!buttonState[4])
		{
			centerStick = centerBase;

			posControllerStick.x = centerStick.x - controllerSizeWidth / 4.0f;
			posControllerStick.y = centerStick.x + controllerSizeWidth / 4.0f;
			posControllerStick.z = centerStick.y - controllerSizeHeight / 4.0f;
			posControllerStick.w = centerStick.y + controllerSizeHeight / 4.0f;
		}
	}

	void InputController::updateTouch(NvMultiInput *nvInput)
	{	
		switch (currentControlType)
		{
		case CONTROLTYPE_ORIGINAL : 
			{
				if (virtualButtonAB(nvInput)) break;
				directionOriginal(nvInput);
				
				break;
			}
		case CONTROLTYPE_MOVINGCENTER : 
			{
				if (virtualButtonAB(nvInput)) break;
				directionMovingCenter(nvInput);

				break;
			}
		case CONTROLTYPE_PLAYERCENTER : 
			{
				if (virtualButtonAB(nvInput)) break;
				directionPlayerCenter(nvInput);

				break;
			}
		case CONTROLTYPE_SWIPE : 
			{
				if (virtualButtonAB(nvInput)) break;
				directionSwipe(nvInput);

				break;
			}
		default : break;
		}
	}

	void InputController::directionOriginal(NvMultiInput *nvInput)
	{
		centerBase.x = 30 + controllerSizeWidth / 2.0f;
		centerBase.y = height - 30 - controllerSizeHeight / 2.0f;

		posControllerBase.x = centerBase.x - controllerSizeWidth / 2.0f;
		posControllerBase.y = centerBase.x + controllerSizeWidth / 2.0f;
		posControllerBase.z = centerBase.y - controllerSizeHeight / 2.0f;
		posControllerBase.w = centerBase.y + controllerSizeHeight / 2.0f;

		if ( (nvInput->x > posControllerBase.x - 100 && nvInput->x < posControllerBase.y + 200) &&
				(nvInput->y > posControllerBase.z - 200 && nvInput->y < posControllerBase.w + 100)
			)
		{
			// move stick in AOE
			vec3f tmp(nvInput->x - centerBase.x, nvInput->y - centerBase.y, 0.0f);
			float sizevec = getSize(tmp);
			Normalize(tmp);

			float maxSize = controllerSizeWidth/2.0f;
			if (sizevec > maxSize) sizevec = maxSize;
			tmp = tmp * sizevec;

			centerStick.x = centerBase.x + tmp.x;
			centerStick.y = centerBase.y + tmp.y;

			posControllerStick.x = centerStick.x - controllerSizeWidth / 4.0f;
			posControllerStick.y = centerStick.x + controllerSizeWidth / 4.0f;
			posControllerStick.z = centerStick.y - controllerSizeHeight / 4.0f;
			posControllerStick.w = centerStick.y + controllerSizeHeight / 4.0f;

			buttonState[4] = true;
		}

	}

	void InputController::directionPlayerCenter(NvMultiInput *nvInput)
	{
		centerBase.x = width / 2.0f;
		centerBase.y = height / 2.0f;

		posControllerBase.x = centerBase.x - controllerSizeWidth / 2.0f;
		posControllerBase.y = centerBase.x + controllerSizeWidth / 2.0f;
		posControllerBase.z = centerBase.y - controllerSizeHeight / 2.0f;
		posControllerBase.w = centerBase.y + controllerSizeHeight / 2.0f;

		if ( (nvInput->x > posControllerBase.x - 2000 && nvInput->x < posControllerBase.y + 2000) &&
				(nvInput->y > posControllerBase.z - 2000 && nvInput->y < posControllerBase.w + 2000)
			)
		{
			// move stick in AOE
			vec3f tmp(nvInput->x - centerBase.x, nvInput->y - centerBase.y, 0.0f);
			float sizevec = getSize(tmp);
			Normalize(tmp);

			float maxSize = controllerSizeWidth/2.0f;
			if (sizevec > maxSize) sizevec = maxSize;
			tmp = tmp * sizevec;

			centerStick.x = centerBase.x + tmp.x;
			centerStick.y = centerBase.y + tmp.y;

			posControllerStick.x = centerStick.x - controllerSizeWidth / 4.0f;
			posControllerStick.y = centerStick.x + controllerSizeWidth / 4.0f;
			posControllerStick.z = centerStick.y - controllerSizeHeight / 4.0f;
			posControllerStick.w = centerStick.y + controllerSizeHeight / 4.0f;

			buttonState[4] = true;
		}

	}

	void InputController::directionMovingCenter(NvMultiInput *nvInput)
	{
		if (!isInit)
		{
			centerBase.x = 30 + controllerSizeWidth / 2.0f;
			centerBase.y = height - 30 - controllerSizeHeight / 2.0f;

			posControllerBase.x = centerBase.x - controllerSizeWidth / 2.0f;
			posControllerBase.y = centerBase.x + controllerSizeWidth / 2.0f;
			posControllerBase.z = centerBase.y - controllerSizeHeight / 2.0f;
			posControllerBase.w = centerBase.y + controllerSizeHeight / 2.0f;

			isInit = true;
		}

		if ( (nvInput->x > posControllerBase.x - 200 && nvInput->x < posControllerBase.y + 200) &&
				(nvInput->y > posControllerBase.z - 200 && nvInput->y < posControllerBase.w + 200)
			)
		{
			// move stick in AOE
			vec3f tmp(nvInput->x - centerBase.x, nvInput->y - centerBase.y, 0.0f);
			float sizevec = getSize(tmp);
			Normalize(tmp);

			float maxSize = controllerSizeWidth/2.0f;
			if (sizevec > maxSize) sizevec = maxSize;
			tmp = tmp * sizevec;

			centerStick.x = nvInput->x;
			centerStick.y = nvInput->y;

			posControllerStick.x = centerStick.x - controllerSizeWidth / 4.0f;
			posControllerStick.y = centerStick.x + controllerSizeWidth / 4.0f;
			posControllerStick.z = centerStick.y - controllerSizeHeight / 4.0f;
			posControllerStick.w = centerStick.y + controllerSizeHeight / 4.0f;

			centerBase.x = centerStick.x - tmp.x;
			centerBase.y = centerStick.y - tmp.y;

			posControllerBase.x = centerBase.x - controllerSizeWidth / 2.0f;
			posControllerBase.y = centerBase.x + controllerSizeWidth / 2.0f;
			posControllerBase.z = centerBase.y - controllerSizeHeight / 2.0f;
			posControllerBase.w = centerBase.y + controllerSizeHeight / 2.0f;

			buttonState[4] = true;
		}
	}

	void InputController::directionSwipe(NvMultiInput *nvInput)
	{
		float diffX = nvInput->spdX;
		float diffY = nvInput->spdY;
		if (diffX > 1000.0f) diffX = 1000.0f;
		if (diffX < -1000.0f) diffX = -1000.0f;
		if (diffY > 1000.0f) diffY = 1000.0f;
		if (diffY < -1000.0f) diffY = -1000.0f;

		accel.x += -diffX / 1000.0f;
		accel.z += -diffY / 1000.0f;
		accel.y += 0.0f;
	}
	bool InputController::virtualButtonAB(NvMultiInput *nvInput)
	{
		// x, y = width
		// z, w = height
		posButtonA.y = width-10;
		posButtonA.x = posButtonA.y-buttonSizeWidth;
		posButtonA.w = height-10;
		posButtonA.z = posButtonA.w-buttonSizeHeight;
		
		posButtonB.y = posButtonA.x-10;
		posButtonB.x = posButtonB.y-buttonSizeWidth;
		posButtonB.w = height-10;
		posButtonB.z = posButtonB.w-buttonSizeHeight;

		if ( (nvInput->x > posButtonA.x && nvInput->x < posButtonA.y) &&
				(nvInput->y > posButtonA.z && nvInput->y < posButtonA.w)
			)
		{
			// lock direction
			buttonState[1] = true;
			return true;
		}


		if ( (nvInput->x > posButtonB.x && nvInput->x < posButtonB.y) &&
				(nvInput->y > posButtonB.z && nvInput->y < posButtonB.w)
			)
		{
			// shoot
			buttonState[0] = true;
			return true;
		}
		return false;
	}

	void InputController::virtualButtonKeyboard(bool buttons[6])
	{
		// buttons = w s a d shoot lockFacing
		const float force = 3.0f;

		if (buttons[0])
		{
			accel.z += force;
		}
		if (buttons[1])
		{
			accel.z -= force;
		}
		if (buttons[2])
		{
			accel.x += force;
		}
		if (buttons[3])
		{
			accel.x -= force;
		}
		if (buttons[4])
		{
			player->shoot();
		}
		
		if (buttons[5])
		{
			player->setLockDirection(true);
		}
		else
		{
			player->setLockDirection(false);
		}
		
	}

	void InputController::updateFrame()
	{
		#ifdef _GLWINDOWS
			
		#elif defined(_NVANDROID)
			if (buttonState[0])
			{
				player->shoot();
			}

			if (buttonState[1])
			{
				player->setLockDirection( true );
			}
			else
			{
				player->setLockDirection( false );
			}
		#endif
		

		const float amplifier = 0.15f;
		const float stickModifier = 1.0f;

		switch (currentControlType)
		{
		case CONTROLTYPE_ORIGINAL : 
			{
				vec3f tmp(centerStick.x - centerBase.x, centerStick.y - centerBase.y, 0.0f);
				float maxSize = controllerSizeWidth/2.0f;

				// apply impulse
				float diffX = tmp.x / maxSize;
				float diffY = tmp.y / maxSize;

				accel.x += -diffX / stickModifier;
				accel.z += -diffY / stickModifier;
				accel.y += 0.0f;

				break;
			}
		case CONTROLTYPE_MOVINGCENTER : 
			{
				vec3f tmp(centerStick.x - centerBase.x, centerStick.y - centerBase.y, 0.0f);
				float maxSize = controllerSizeWidth/2.0f;

				// apply impulse
				float diffX = tmp.x / maxSize;
				float diffY = tmp.y / maxSize;

				accel.x += -diffX / stickModifier;
				accel.z += -diffY / stickModifier;
				accel.y += 0.0f;

				break;
			}
		case CONTROLTYPE_PLAYERCENTER : 
			{
				vec3f tmp(centerStick.x - centerBase.x, centerStick.y - centerBase.y, 0.0f);
				float maxSize = controllerSizeWidth/2.0f;

				// apply impulse
				float diffX = tmp.x / maxSize;
				float diffY = tmp.y / maxSize;

				accel.x += -diffX / stickModifier;
				accel.z += -diffY / stickModifier;
				accel.y += 0.0f;

				break;
			}
		default : 
			{
				break;
			}
		}
		
		vec3f tmp = accel;
		tmp.x = std::max(tmp.x, -100.0f);
		tmp.x = std::min(tmp.x, 100.0f);

		tmp.z = std::max(tmp.z, -100.0f);
		tmp.z = std::min(tmp.z, 100.0f);

		tmp.y = 0.0f;

		player->applyImpulseNoEuler(tmp * amplifier);

		// cap player velocity
		float speedCap = 3.0f;
		if (currentControlType == CONTROLTYPE_SWIPE)
		{
			speedCap = 3.5f;
		}
		vec3f velocity = player->getVelocity();
		velocity.x = std::max(velocity.x, -speedCap);
		velocity.x = std::min(velocity.x, speedCap);

		velocity.z = std::max(velocity.z, -speedCap);
		velocity.z = std::min(velocity.z, speedCap);

		velocity.y = 0.0f;

		player->setVelocityNoEuler(velocity);
		accel = vec3f();

		// change control
		int count = 0;
		for (int i = 0; i < 10; ++i)
		{
			if (touchState[i])
			{
				count++;
			}
		}
		
		if (count >= 7) 
		{
			if (!isControlChanged)
			{
				// change control
				isInit = false;
				int tmp = ((int)currentControlType+1) % (int)CONTROLTYPE_LAST;
				currentControlType = (controlType)tmp;

				isControlChanged = true;
				if (currentControlType == CONTROLTYPE_SWIPE)
				{
					player->actorCam.setDrag(5.0f);
				}
				else
				{
					player->actorCam.setDrag(15.0f);
				}
			}
		}
		else
		{
			isControlChanged = false;
		}
	}
}