#include "main_code.h" #ifdef TARGET_ANDROID #include "android_api.h" #endif #include #include #include #include #include "include/Engine.h" #include "main_code.h" //Better move those to external config or something... const cardinal CONST_LASER_TIMER = 200; const int CONST_HIT_SCORE_POINTS = 100; const float CONST_TIME_SCALE = 0.01f; const float CONST_PLAYER_ACCELERATION = 3.f; const float CONST_VELOCITY_FADE = 0.95f; const float CONST_LASER_VELOCITY = 100.f; const vec2 CONST_PLAYER_HALF_SIZE(30.f, 30.f); const vec2 CONST_LASER_HALF_SIZE(10.f, 10.f); const vec2 CONST_AST_POS1(40, 40); const vec2 CONST_AST_POS2(40, 280); const vec2 CONST_AST_POS3(440, 160); float HealthToScale(int health) { return 0.5f + health * 0.3f; } float HealthToHitDistance(int health) { const float CONST_HIT_DISTANCE = 15.f; return max(CONST_HIT_DISTANCE * HealthToScale(health), 10.f); } void TMyApplication::InnerInit() { *Console<<"Inner init go!\n"; #ifdef TARGET_ANDROID ST::PathToResources = ""; #endif #ifdef TARGET_WIN32 #ifdef NDEBUG ST::PathToResources = "assets/"; #else ST::PathToResources = "../../../assets/"; #endif #endif #ifdef TARGET_IOS ST::PathToResources = "assets/"; #endif RandomGenerator.seed(static_cast(std::time(0))); TapIsDown = false; ResourceManager->TexList.AddTexture(CONST_CONSOLE_TEX_NAME); ResourceManager->ShaderManager.AddShader("DefaultShader", "gui_transparent.vertex", "gui_transparent.fragment"); Renderer->PushShader("DefaultShader"); ResourceManager->FontManager.AddFont("droid_sans14", "droid_sans14_font_bitmap.bmp32", "droid_sans14_font_charmap.txt"); ResourceManager->FontManager.PushFont("droid_sans14"); ResourceManager->TexList.Serialize(*FileToPropertyTree("textures.xml")); Renderer->PushProjectionMatrix(Renderer->GetScreenWidth(), Renderer->GetScreenHeight()); Level = 1; Score = 0; InitLevel(); Player.RenderPair.first.SamplerMap[CONST_STRING_TEXTURE_UNIFORM] = "shipTexture"; Player.RenderPair.second.Data = MakeDataTriangleList(Player.Pos - CONST_PLAYER_HALF_SIZE, Player.Pos + CONST_PLAYER_HALF_SIZE); Player.RenderPair.second.RefreshBuffer(); BackgroundRenderPair.first.SamplerMap[CONST_STRING_TEXTURE_UNIFORM] = "backgroundTexture"; BackgroundRenderPair.second.Data = MakeDataTriangleList(vec2(0,0), vec2(Renderer->GetScreenWidth(), Renderer->GetScreenHeight())); BackgroundRenderPair.second.RefreshBuffer(); *Console<<"Inner init end!\n"; glDepthFunc(GL_LEQUAL); } void TMyApplication::InnerDeinit() { } void TMyApplication::InnerDraw() { glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Draw background TRenderParamsSetter renderParamSetter(BackgroundRenderPair.first); Renderer->DrawTriangleList(BackgroundRenderPair.second); //Draw asteroids BOOST_FOREACH(CAsteroidStruct& asteroid, Asteroids) { TRenderParamsSetter renderParamSetter(asteroid.RenderPair.first); Renderer->DrawTriangleList(asteroid.RenderPair.second); } //Draw player if (GameState != GS_LOST) { TRenderParamsSetter renderParamSetter(Player.RenderPair.first); Renderer->DrawTriangleList(Player.RenderPair.second); } //Draw lasers BOOST_FOREACH(CLaserStruct& laser, Lasers) { TRenderParamsSetter renderParamSetter(laser.RenderPair.first); Renderer->DrawTriangleList(laser.RenderPair.second); } //Draw texts glBindTexture(GL_TEXTURE_2D, ResourceManager->TexList[ResourceManager->FontManager.GetCurrentFontTextureName()]); Renderer->DrawTriangleList(LevelScoreText); Renderer->DrawTriangleList(LevelMessageText); } void TMyApplication::InnerUpdate(cardinal dt) { float scaledTime = CONST_TIME_SCALE * dt; //Update Asteroids BOOST_FOREACH(CAsteroidStruct& asteroid, Asteroids) { float difAngle = asteroid.AngularVelocity * scaledTime; asteroid.Angle += difAngle; mat3 rotationMatrix = CreateZRotationMatrix(difAngle); MoveDataTriangleList(asteroid.RenderPair.second.Data, -vec3(asteroid.CenterPos, 0)); RotateDataTriangleList(asteroid.RenderPair.second.Data, rotationMatrix); MoveDataTriangleList(asteroid.RenderPair.second.Data, vec3(asteroid.CenterPos, 0)); vec2 dif = asteroid.Velocity * scaledTime; vec2 newCenterPos = NormalizePos(asteroid.CenterPos + dif); dif = newCenterPos - asteroid.CenterPos; asteroid.CenterPos = newCenterPos; MoveDataTriangleList(asteroid.RenderPair.second.Data, vec3(dif, 0)); asteroid.RenderPair.second.RefreshBuffer(); } UpdatePlayerPosition(scaledTime); //Shoot laser LaserTimer.Update(dt); if (LaserTimer.IsOver()) { LaserTimer.SetTimer(CONST_LASER_TIMER); if (GameState == GS_PLAY) { AddNewLaser(); } } //Update lasers BOOST_FOREACH(CLaserStruct& laser, Lasers) { vec2 dif = CONST_LASER_VELOCITY *laser.Dir * scaledTime; MoveDataTriangleList(laser.RenderPair.second.Data, vec3(dif, 0)); laser.Pos += dif; laser.RenderPair.second.RefreshBuffer(); } //Do all checks... ProcessLaserHit(); ProcessPlayerCollisions(); ClearUnusedLasers(); if (Asteroids.size() == 0 && GameState == GS_PLAY) { GameState = GS_WON; RefreshScoreText(); } } void TMyApplication::InnerOnTapDown(vec2 p) { TapIsDown = true; LastTap = p; } void TMyApplication::InnerOnTapUp(vec2 p) { TapIsDown = false; if (GameState == GS_LOST) { Level = 1; Score = 0; InitLevel(); } else if (GameState == GS_WON) { Level++; InitLevel(); } } void TMyApplication::InnerOnTapUpAfterMove(vec2 p) { TapIsDown = false; } void TMyApplication::InnerOnMove(vec2 shift) { LastTap -= shift; } void TMyApplication::InitLevel() { GameState = GS_PLAY; LaserTimer.SetTimer(CONST_LASER_TIMER); Asteroids.clear(); Lasers.clear(); CreateRandomAsteroid(CONST_AST_POS1, Level); CreateRandomAsteroid(CONST_AST_POS2, Level); CreateRandomAsteroid(CONST_AST_POS3, Level); Player.Angle = 0; Player.Pos = vec2(Renderer->GetScreenWidth()/2, Renderer->GetScreenHeight()/2); RefreshScoreText(); } CAsteroidStruct TMyApplication::CreateAsteroid(vec2 pos, vec2 dir, int health) { static const float CONST_TEX_COORD_SCALE = 0.01f; static const float CONST_ANGULAR_VELOCITY_SCALE = 0.001f; static const boost::random::uniform_int_distribution<> velocityDistribution(5, 10); static const boost::random::uniform_int_distribution<> angularVelocityDistribution(0, 10); static const boost::random::uniform_int_distribution<> vertexNumberDistribution(5, 10); static const boost::random::uniform_int_distribution<> vertexShiftDistribution(20, 30); CAsteroidStruct result; result.Health = health; result.CenterPos = pos; result.Angle = 0; result.AngularVelocity = angularVelocityDistribution(RandomGenerator) * CONST_ANGULAR_VELOCITY_SCALE; result.Velocity = velocityDistribution(RandomGenerator) * dir; int numberOfVertices = vertexNumberDistribution(RandomGenerator); std::vector distArr(numberOfVertices); for (int i=0; i(numberOfVertices); vec2 shift = distArr[i % numberOfVertices] * vec2(cosf(angle), sinf(angle)); vec2 texCoordShift = shift * CONST_TEX_COORD_SCALE; result.RenderPair.second.Data.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].push_back(vec3(pos, 0)); result.RenderPair.second.Data.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].push_back(vec3(pos + prevShift, 0)); result.RenderPair.second.Data.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].push_back(vec3(pos + shift, 0)); result.RenderPair.second.Data.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].push_back(vec2(0.5f, 0.5f)); result.RenderPair.second.Data.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].push_back(vec2(0.5f, 0.5f) + prevTexCoordShift); result.RenderPair.second.Data.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].push_back(vec2(0.5f, 0.5f) + texCoordShift); prevShift = shift; prevTexCoordShift = texCoordShift; } result.RenderPair.first.SamplerMap[CONST_STRING_TEXTURE_UNIFORM] = "asteroidTexture"; result.RenderPair.second.RefreshBuffer(); return result; } void TMyApplication::CreateRandomAsteroid(vec2 pos, int health) { static const boost::random::uniform_int_distribution<> angleDistribution(0, 36); float randomAngle = angleDistribution(RandomGenerator) * pi / 18.f; Asteroids.push_back(CreateAsteroid(pos, vec2(cosf(randomAngle), sinf(randomAngle)), health)); } void TMyApplication::CreateAsteroidParts(vec2 pos, int newHealth) { static const boost::random::uniform_int_distribution<> CreateAsteroidParts(2, 5); static const boost::random::uniform_int_distribution<> angleDistribution(0, 36); float randomAngle = angleDistribution(RandomGenerator) * pi / 18.f; int number = CreateAsteroidParts(RandomGenerator); for (int i = 0; i < number; i++) { float partAngle = randomAngle + i * 2 * pi / static_cast(number); Asteroids.push_back(CreateAsteroid(pos, vec2(cosf(partAngle), sinf(partAngle)), newHealth)); } } vec2 TMyApplication::NormalizePos(vec2 pos) { vec2 result = pos; while (result.v[0] < 0) { result.v[0] += Renderer->GetScreenWidth(); } while (result.v[1] < 0) { result.v[1] += Renderer->GetScreenHeight(); } while (result.v[0] >= Renderer->GetScreenWidth()) { result.v[0] -= Renderer->GetScreenWidth(); } while (result.v[1] >= Renderer->GetScreenHeight()) { result.v[1] -= Renderer->GetScreenHeight(); } return result; } void TMyApplication::RefreshPlayerPos() { Replace6PointsInTriangleList(Player.RenderPair.second.Data, 0, (-1) * CONST_PLAYER_HALF_SIZE, CONST_PLAYER_HALF_SIZE); mat3 rotationMatrix = CreateZRotationMatrix(Player.Angle); RotateDataTriangleList(Player.RenderPair.second.Data, rotationMatrix); MoveDataTriangleList(Player.RenderPair.second.Data, vec3(Player.Pos, 0)); Player.RenderPair.second.RefreshBuffer(); } void TMyApplication::AddNewLaser() { CLaserStruct laser; float angle = Player.Angle; laser.Dir = vec2(cosf(angle), sinf(angle)); laser.Pos = Player.Pos; laser.RenderPair.second.Data = MakeDataTriangleList((-1) * CONST_LASER_HALF_SIZE, CONST_LASER_HALF_SIZE); mat3 rotationMatrix = CreateZRotationMatrix(angle); RotateDataTriangleList(laser.RenderPair.second.Data, rotationMatrix); MoveDataTriangleList(laser.RenderPair.second.Data, vec3(laser.Pos, 0)); laser.RenderPair.first.SamplerMap[CONST_STRING_TEXTURE_UNIFORM] = "fireTexture"; laser.RenderPair.second.RefreshBuffer(); Lasers.push_back(laser); } void TMyApplication::UpdatePlayerPosition(float scaledTime) { if (TapIsDown) { vec2 dir = Normalize(LastTap - Player.Pos); Player.Velocity += CONST_PLAYER_ACCELERATION * scaledTime * dir; Player.Angle = acosf(DotProduct(dir, vec2(1, 0))); if (dir.v[1] < 0) { Player.Angle = -Player.Angle; } } Player.Pos += Player.Velocity * scaledTime; Player.Velocity *= powf(CONST_VELOCITY_FADE, clamp(scaledTime, 0.f, 1.5f)); Player.Pos = NormalizePos(Player.Pos); RefreshPlayerPos(); } void TMyApplication::ProcessLaserHit() { for (auto laserItr = Lasers.begin(); laserItr != Lasers.end(); ) { for (auto astItr = Asteroids.begin(); astItr != Asteroids.end(); ) { float realDistance = HealthToHitDistance(astItr->Health); if (fabs(laserItr->Pos.v[0] - astItr->CenterPos.v[0]) < realDistance && fabs(laserItr->Pos.v[1] - astItr->CenterPos.v[1]) < realDistance) { laserItr = Lasers.erase(laserItr); if (astItr->Health > 0) { CreateAsteroidParts(astItr->CenterPos, astItr->Health - 1); } astItr = Asteroids.erase(astItr); Score += CONST_HIT_SCORE_POINTS; RefreshScoreText(); break; } if (astItr != Asteroids.end()) { astItr++; } } if (laserItr != Lasers.end()) { laserItr++; } } } void TMyApplication::ProcessPlayerCollisions() { for (auto astItr = Asteroids.begin(); astItr != Asteroids.end(); astItr++) { float realDistance = HealthToHitDistance(astItr->Health); if (fabs(astItr->CenterPos.v[0] - Player.Pos.v[0]) < realDistance && fabs(astItr->CenterPos.v[1] - Player.Pos.v[1]) < realDistance) { GameState = GS_LOST; RefreshScoreText(); } } } void TMyApplication::ClearUnusedLasers() { for (auto itr = Lasers.begin(); itr != Lasers.end(); ) { if (itr->Pos.v[0] < 0 || itr->Pos.v[1] < 0 || itr->Pos.v[0] > Renderer->GetScreenWidth() || itr->Pos.v[1] > Renderer->GetScreenHeight()) { itr = Lasers.erase(itr); } if (itr != Lasers.end()) { itr++; } } } void TMyApplication::RefreshScoreText() { LevelScoreText = ResourceManager->FontManager.DrawStringToVBO(vec2(10, 20), TTextBasicAreaParams(), "Score: "+ tostr(Score)); if (GameState == GS_PLAY) { LevelMessageText = ResourceManager->FontManager.DrawStringToVBO(vec2(10, 300), TTextBasicAreaParams(), "Now playing "+ tostr(Level) + " level"); } else if (GameState == GS_WON) { LevelMessageText = ResourceManager->FontManager.DrawStringToVBO(vec2(10, 300), TTextBasicAreaParams(), "You won! Tap on screen to start next level"); } else if (GameState == GS_LOST) { LevelMessageText = ResourceManager->FontManager.DrawStringToVBO(vec2(10, 300), TTextBasicAreaParams(), "You failed! Tap on screen to restart"); } }