SpaceshipGame.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. #include "SpaceshipGame.h"
  2. // Declare our game instance
  3. SpaceshipGame game;
  4. // Collision constants
  5. #define ROOF_HEIGHT 11.6f
  6. #define FLOOR_HEIGHT 0.6f
  7. #define MAP_LENGTH 1000.0f
  8. #define CAMERA_RANGE_FRONT -1
  9. #define CAMERA_RANGE_BACK 8
  10. // Mass of the space ship
  11. #define MASS 50.0f
  12. // Gravity constant (earth's gravity is ~9.8m/s/s)
  13. #define GRAVITY 9.8f
  14. // Force applied due to gravity (F = mg)
  15. #define GRAVITATIONAL_FORCE (MASS * GRAVITY)
  16. // Normal force used in friction computations (Fn = -mg)
  17. #define FORCE_NORMAL (-GRAVITATIONAL_FORCE)
  18. // Friction between floor and spaceship (Ff = u * Fn)
  19. #define FLOOR_FRICTION (0.4f * FORCE_NORMAL)
  20. // Friction between roof and spaceship (Ff = u * Fn)
  21. #define ROOF_FRICTION (0.035f * FORCE_NORMAL)
  22. // Distance from space ship (screen-space) after which force is no longer applied to the ship
  23. #define TOUCH_DISTANCE_MAX 400.0f
  24. // Amount to scale screen-space push/force vector by
  25. #define FORCE_SCALE 4.0f
  26. // Constant for the maximum force that can be applied to the ship
  27. #define FORCE_MAX (TOUCH_DISTANCE_MAX * FORCE_SCALE)
  28. // Maximum velocity (+/-)
  29. #define VELOCITY_MAX 75.0f
  30. // Maximum ship tilt (degrees)
  31. #define SHIP_TILT_MAX 45.0f
  32. // Maximum ship rotation (degrees) at full throttle
  33. #define SHIP_ROTATE_SPEED_MAX 500.0f
  34. // Multiplier for engine glow effect (based on throttle)
  35. #define ENGINE_POWER 2.5f
  36. // Multiplier for sound pitch (based on throttle)
  37. #define SOUND_PITCH_SCALE 2.0f
  38. // Lighting constants
  39. #define AMBIENT_LIGHT_COLOR Vector3(0.2f, 0.2f, 0.2f)
  40. #define SPECULAR 20.0f
  41. // Clamp function
  42. #define CLAMP(x, min, max) (x < min ? min : (x > max ? max : x))
  43. SpaceshipGame::SpaceshipGame()
  44. : _scene(NULL), _cameraNode(NULL), _shipGroupNode(NULL), _shipNode(NULL), _propulsionNode(NULL), _glowNode(NULL),
  45. _stateBlock(NULL), _font(NULL), _throttle(0), _shipTilt(0), _finished(true), _finishedTime(0), _pushing(false), _time(0),
  46. _glowDiffuseParameter(NULL), _shipSpecularParameter(NULL), _spaceshipSound(NULL)
  47. {
  48. }
  49. SpaceshipGame::~SpaceshipGame()
  50. {
  51. }
  52. void SpaceshipGame::initialize()
  53. {
  54. // TODO: Not working on iOS
  55. // Display the gameplay splash screen for at least 1 second.
  56. displayScreen(this, &SpaceshipGame::drawSplash, NULL, 1000L);
  57. // Create our render state block that will be reused across all materials
  58. _stateBlock = RenderState::StateBlock::create();
  59. _stateBlock->setDepthTest(true);
  60. _stateBlock->setCullFace(true);
  61. _stateBlock->setBlend(true);
  62. _stateBlock->setBlendSrc(RenderState::BLEND_SRC_ALPHA);
  63. _stateBlock->setBlendDst(RenderState::BLEND_ONE_MINUS_SRC_ALPHA);
  64. // Load our scene from file
  65. _scene = Scene::load("res/spaceship.gpb");
  66. // Update the aspect ratio for our scene's camera to match the current device resolution
  67. _scene->getActiveCamera()->setAspectRatio(getAspectRatio());
  68. // Initialize scene data
  69. initializeSpaceship();
  70. initializeEnvironment();
  71. // Create a background audio track
  72. _backgroundSound = AudioSource::create("res/background.ogg");
  73. if (_backgroundSound)
  74. _backgroundSound->setLooped(true);
  75. // Create font
  76. _font = Font::create("res/airstrip.gpb");
  77. // Store camera node
  78. _cameraNode = _scene->findNode("camera1");
  79. // Store initial ship and camera positions
  80. _initialShipPos = _shipGroupNode->getTranslation();
  81. _initialShipRot = _shipGroupNode->getRotation();
  82. _initialCameraPos = _cameraNode->getTranslation();
  83. }
  84. void SpaceshipGame::initializeSpaceship()
  85. {
  86. Material* material;
  87. _shipGroupNode = _scene->findNode("gSpaceShip");
  88. // Setup spaceship model
  89. // Part 0
  90. _shipNode = _scene->findNode("pSpaceShip");
  91. material = _shipNode->getModel()->setMaterial("res/shaders/colored.vert", "res/shaders/colored.frag", "SPECULAR;DIRECTIONAL_LIGHT_COUNT 1", 0);
  92. material->getParameter("u_diffuseColor")->setValue(Vector4(0.53544f, 0.53544f, 0.53544f, 1.0f));
  93. initializeMaterial(material, true, true);
  94. // Part 1
  95. material = _shipNode->getModel()->setMaterial("res/shaders/colored.vert", "res/shaders/colored.frag", "SPECULAR;DIRECTIONAL_LIGHT_COUNT 1", 1);
  96. material->getParameter("u_diffuseColor")->setValue(Vector4(0.8f, 0.8f, 0.8f, 1.0f));
  97. _shipSpecularParameter = material->getParameter("u_specularExponent");
  98. initializeMaterial(material, true, true);
  99. // Part 2
  100. material = _shipNode->getModel()->setMaterial("res/shaders/colored.vert", "res/shaders/colored.frag", "SPECULAR;DIRECTIONAL_LIGHT_COUNT 1", 2);
  101. material->getParameter("u_diffuseColor")->setValue(Vector4(0.280584f, 0.5584863f, 0.6928f, 1.0f));
  102. initializeMaterial(material, true, true);
  103. // Setup spaceship propulsion model
  104. _propulsionNode = _scene->findNode("pPropulsion");
  105. material = _propulsionNode->getModel()->setMaterial("res/shaders/colored.vert", "res/shaders/colored.frag", "SPECULAR;DIRECTIONAL_LIGHT_COUNT 1");
  106. material->getParameter("u_diffuseColor")->setValue(Vector4(0.8f, 0.8f, 0.8f, 1.0f));
  107. initializeMaterial(material, true, true);
  108. // Glow effect node
  109. _glowNode = _scene->findNode("pGlow");
  110. material = _glowNode->getModel()->setMaterial("res/shaders/textured.vert", "res/shaders/textured.frag", "MODULATE_COLOR");
  111. material->getParameter("u_diffuseTexture")->setValue("res/propulsion_glow.png", true);
  112. _glowDiffuseParameter = material->getParameter("u_modulateColor");
  113. initializeMaterial(material, false, false);
  114. // Setup the sound
  115. _spaceshipSound = AudioSource::create("res/spaceship.wav");
  116. if (_spaceshipSound)
  117. {
  118. _spaceshipSound->setLooped(true);
  119. }
  120. }
  121. void SpaceshipGame::initializeEnvironment()
  122. {
  123. Material* material;
  124. std::vector<Node*> nodes;
  125. // Setup ground model
  126. _scene->findNodes("pGround", nodes, true, false);
  127. for (size_t i = 0, count = nodes.size(); i < count; ++i)
  128. {
  129. Node* pGround = nodes[i];
  130. material = pGround->getModel()->setMaterial("res/shaders/colored.vert", "res/shaders/colored.frag", "SPECULAR;DIRECTIONAL_LIGHT_COUNT 1");
  131. material->getParameter("u_diffuseColor")->setValue(Vector4(0.280584f, 0.5584863f, 0.6928f, 1.0f));
  132. initializeMaterial(material, true, true);
  133. }
  134. // Setup roof model
  135. nodes.clear();
  136. _scene->findNodes("pRoof", nodes, true, false);
  137. for (size_t i = 0, count = nodes.size(); i < count; ++i)
  138. {
  139. Node* pRoof = nodes[i];
  140. material = pRoof->getModel()->setMaterial("res/shaders/colored.vert", "res/shaders/colored.frag", "SPECULAR;DIRECTIONAL_LIGHT_COUNT 1");
  141. material->getParameter("u_diffuseColor")->setValue(Vector4(0.280584f, 0.5584863f, 0.6928f, 1.0f));
  142. initializeMaterial(material, true, true);
  143. }
  144. // Setup background model
  145. nodes.clear();
  146. Node* pBackground = _scene->findNode("pBackground");
  147. material = pBackground->getModel()->setMaterial("res/shaders/textured.vert", "res/shaders/textured.frag", "DIRECTIONAL_LIGHT_COUNT 1");
  148. material->getParameter("u_diffuseTexture")->setValue("res/background.png", true);
  149. initializeMaterial(material, true, false);
  150. }
  151. void SpaceshipGame::initializeMaterial(Material* material, bool lighting, bool specular)
  152. {
  153. // Set the common render state block for the material
  154. material->setStateBlock(_stateBlock);
  155. // Bind the WorldViewProjection matrix
  156. material->setParameterAutoBinding("u_worldViewProjectionMatrix", RenderState::WORLD_VIEW_PROJECTION_MATRIX);
  157. if (lighting)
  158. {
  159. // Apply lighting material parameters
  160. material->setParameterAutoBinding("u_inverseTransposeWorldViewMatrix", RenderState::INVERSE_TRANSPOSE_WORLD_VIEW_MATRIX);
  161. material->getParameter("u_ambientColor")->setValue(AMBIENT_LIGHT_COLOR);
  162. Node* lightNode = _scene->findNode("directionalLight1");
  163. Vector3 lightDirection = lightNode->getForwardVector();
  164. lightDirection.normalize();
  165. if (lightNode)
  166. {
  167. material->getParameter("u_directionalLightColor[0]")->setValue(lightNode->getLight()->getColor());
  168. material->getParameter("u_directionalLightDirection[0]")->setValue(lightDirection);
  169. }
  170. if (specular)
  171. {
  172. // Apply specular lighting parameters
  173. material->getParameter("u_specularExponent")->setValue(SPECULAR);
  174. material->setParameterAutoBinding("u_worldViewMatrix", RenderState::WORLD_VIEW_MATRIX);
  175. material->setParameterAutoBinding("u_cameraPosition", RenderState::CAMERA_WORLD_POSITION);
  176. }
  177. }
  178. }
  179. void SpaceshipGame::finalize()
  180. {
  181. SAFE_RELEASE(_backgroundSound);
  182. SAFE_RELEASE(_spaceshipSound);
  183. SAFE_RELEASE(_font);
  184. SAFE_RELEASE(_stateBlock);
  185. SAFE_RELEASE(_scene);
  186. }
  187. void SpaceshipGame::update(float elapsedTime)
  188. {
  189. // Calculate elapsed time in seconds
  190. float t = (float)elapsedTime / 1000.0;
  191. if (!_finished)
  192. {
  193. _time += t;
  194. // Play the background track
  195. if (_backgroundSound->getState() != AudioSource::PLAYING)
  196. _backgroundSound->play();
  197. }
  198. else
  199. {
  200. // Stop the background track
  201. if (_backgroundSound->getState() == AudioSource::PLAYING || _backgroundSound->getState() == AudioSource::PAUSED)
  202. {
  203. _backgroundSound->stop();
  204. _throttle = 0.0f;
  205. }
  206. }
  207. // Set initial force due to gravity
  208. _force.set(0, -GRAVITATIONAL_FORCE);
  209. // While we are pushing/touching the screen, apply a push force vector based on the distance from
  210. // the touch point to the center of the space ship.
  211. if (_pushing)
  212. {
  213. // Get the center point of the space ship in screen coordinates
  214. Vector3 shipCenterScreen;
  215. _scene->getActiveCamera()->project(getViewport(), _shipGroupNode->getBoundingSphere().center, &shipCenterScreen.x, &shipCenterScreen.y);
  216. // Compute a screen-space vector between the center point of the ship and the touch point.
  217. // We will use this vector to apply a "pushing" force to the space ship, similar to what
  218. // happens when you hold a magnet close to an object with opposite polarity.
  219. Vector2 pushForce((shipCenterScreen.x - _pushPoint.x), -(shipCenterScreen.y - _pushPoint.y));
  220. // Transform the vector so that a smaller magnitude emits a larger force and applying the
  221. // maximum touch distance.
  222. float distance = (std::max)(TOUCH_DISTANCE_MAX - pushForce.length(), 0.0f);
  223. pushForce.normalize();
  224. pushForce.scale(distance * FORCE_SCALE);
  225. _force.add(pushForce);
  226. // Compute a throttle value based on our force vector, minus gravity
  227. Vector2 throttleVector(_force.x, _force.y + GRAVITATIONAL_FORCE);
  228. _throttle += throttleVector.length() / FORCE_MAX * t;
  229. }
  230. else
  231. {
  232. // Gradually decrease the throttle
  233. if (_throttle > 0.0f)
  234. {
  235. _throttle *= 1.0f - t;
  236. }
  237. }
  238. // Clamp the throttle
  239. _throttle = CLAMP(_throttle, 0.0f, 1.0f);
  240. // Update acceleration (a = F/m)
  241. _acceleration.set(_force.x / MASS, _force.y / MASS);
  242. // Update velocity (v1 = v0 + at)
  243. _velocity.x += _acceleration.x * t;
  244. _velocity.y += _acceleration.y * t;
  245. // Clamp velocity to its maximum range
  246. _velocity.x = CLAMP(_velocity.x, -VELOCITY_MAX, VELOCITY_MAX);
  247. _velocity.y = CLAMP(_velocity.y, -VELOCITY_MAX, VELOCITY_MAX);
  248. // Move the spaceship based on its current velocity (x1 = x0 + vt)
  249. _shipGroupNode->translate(_velocity.x * t, _velocity.y * t, 0);
  250. // Check for collisions
  251. handleCollisions(t);
  252. // Update camera
  253. updateCamera();
  254. // Reset ship rotation
  255. _shipGroupNode->setRotation(_initialShipRot);
  256. // Apply ship tilt
  257. if (_force.x != 0 && fabs(_velocity.x) > 0.1f)
  258. {
  259. // Compute an angle based on the dot product between the force vector and the Y axis
  260. Vector2 fn;
  261. _force.normalize(&fn);
  262. float angle = MATH_RAD_TO_DEG(acos(Vector2::dot(Vector2(0, 1), fn)));
  263. if (_force.x > 0)
  264. angle = -angle;
  265. angle *= _throttle * t;
  266. _shipTilt += angle;
  267. _shipTilt = _shipTilt < -SHIP_TILT_MAX ? -SHIP_TILT_MAX : (_shipTilt > SHIP_TILT_MAX ? SHIP_TILT_MAX : _shipTilt);
  268. }
  269. else
  270. {
  271. // Interpolate tilt back towards zero when no force is applied
  272. _shipTilt = (_shipTilt + ((0 - _shipTilt) * t * 2.0f));
  273. }
  274. _shipGroupNode->rotateZ(MATH_DEG_TO_RAD(_shipTilt));
  275. if (_throttle > MATH_EPSILON)
  276. {
  277. // Apply ship spin
  278. _shipNode->rotateY(MATH_DEG_TO_RAD(SHIP_ROTATE_SPEED_MAX * t * _throttle));
  279. // Play sound effect
  280. if (_spaceshipSound->getState() != AudioSource::PLAYING)
  281. _spaceshipSound->play();
  282. // Set the pitch based on the throttle
  283. _spaceshipSound->setPitch(_throttle * SOUND_PITCH_SCALE);
  284. }
  285. else
  286. {
  287. // Stop sound effect
  288. _spaceshipSound->stop();
  289. }
  290. // Modify ship glow effect based on the throttle
  291. _glowDiffuseParameter->setValue(Vector4(1, 1, 1, _throttle * ENGINE_POWER));
  292. _shipSpecularParameter->setValue(SPECULAR - ((SPECULAR-2.0f) * _throttle));
  293. }
  294. void SpaceshipGame::handleCollisions(float t)
  295. {
  296. float friction = 0.0f;
  297. // Use the ship's bounding sphere for roof collisions
  298. const BoundingSphere& shipBounds = _shipNode->getBoundingSphere();
  299. // Compute a bounding box for floor collisions
  300. BoundingBox propulsionBounds = _propulsionNode->getModel()->getMesh()->getBoundingBox();
  301. propulsionBounds.transform(_propulsionNode->getWorldMatrix());
  302. if (propulsionBounds.min.y <= FLOOR_HEIGHT)
  303. {
  304. // Floor collision
  305. friction = FLOOR_FRICTION;
  306. _shipGroupNode->translateY(FLOOR_HEIGHT - propulsionBounds.min.y);
  307. if (_velocity.y < 0.0f)
  308. {
  309. // Cancel vertical velocity
  310. _velocity.y = 0.0f;
  311. }
  312. }
  313. else if ((shipBounds.center.y + shipBounds.radius) >= ROOF_HEIGHT)
  314. {
  315. // Roof collision
  316. friction = ROOF_FRICTION;
  317. _shipGroupNode->translateY(ROOF_HEIGHT - (shipBounds.center.y + shipBounds.radius));
  318. if (_velocity.y >= 0.0f)
  319. {
  320. // Cancel vertical velocity
  321. _velocity.y = 0.0f;
  322. }
  323. }
  324. // Apply friction to velocity
  325. if (friction != 0.0f)
  326. {
  327. if (_velocity.x > 0)
  328. {
  329. _velocity.x = (std::max)(_velocity.x + friction * t, 0.0f);
  330. }
  331. else if (_velocity.x < 0)
  332. {
  333. _velocity.x = (std::min)(_velocity.x - friction * t, 0.0f);
  334. }
  335. _hitSomething = true;
  336. }
  337. // Keep the ship within the playable area of the map
  338. const Vector3& shipPos = _shipGroupNode->getTranslation();
  339. if (shipPos.x < _initialShipPos.x)
  340. {
  341. _shipGroupNode->translateX(_initialShipPos.x - shipPos.x);
  342. _velocity.x = 0.0f;
  343. }
  344. else if (shipPos.x > (_initialShipPos.x + MAP_LENGTH))
  345. {
  346. if (!_finished)
  347. {
  348. // Passed the finish line
  349. _finished = true;
  350. _finishedTime = getAbsoluteTime();
  351. _pushing = false;
  352. }
  353. }
  354. }
  355. void SpaceshipGame::updateCamera()
  356. {
  357. if (_finished)
  358. return;
  359. // Keep the camera focused on the ship
  360. const Vector3& cameraPos = _cameraNode->getTranslation();
  361. const Vector3& shipPos = _shipGroupNode->getTranslation();
  362. float diff = cameraPos.x - shipPos.x;
  363. if (diff > CAMERA_RANGE_BACK)
  364. {
  365. _cameraNode->translateX(-(diff - CAMERA_RANGE_BACK));
  366. }
  367. else if (diff < -CAMERA_RANGE_FRONT)
  368. {
  369. _cameraNode->translateX(-(diff + CAMERA_RANGE_FRONT));
  370. }
  371. }
  372. void SpaceshipGame::resetGame()
  373. {
  374. _time = 0;
  375. _finished = false;
  376. _velocity.set(0, 0);
  377. _shipGroupNode->setTranslation(_initialShipPos);
  378. _cameraNode->setTranslation(_initialCameraPos);
  379. _hitSomething = false;
  380. }
  381. void SpaceshipGame::render(float elapsedTime)
  382. {
  383. clear(CLEAR_COLOR_DEPTH, Vector4::zero(), 1.0f, 0);
  384. // Visit scene nodes for opaque drawing
  385. _scene->visit(this, &SpaceshipGame::drawScene, (void*)0);
  386. // Visit scene nodes for transparent drawing
  387. _scene->visit(this, &SpaceshipGame::drawScene, (void*)1);
  388. // Draw game text (yellow)
  389. drawText();
  390. }
  391. void SpaceshipGame::drawSplash(void* param)
  392. {
  393. clear(CLEAR_COLOR_DEPTH, Vector4(0, 0, 0, 1), 1.0f, 0);
  394. SpriteBatch* batch = SpriteBatch::create("res/logo_powered_white.png");
  395. batch->start();
  396. batch->draw(this->getWidth() * 0.5f, this->getHeight() * 0.5f, 0.0f, 512.0f, 512.0f, 0.0f, 1.0f, 1.0f, 0.0f, Vector4::one(), true);
  397. batch->finish();
  398. SAFE_DELETE(batch);
  399. }
  400. bool SpaceshipGame::drawScene(Node* node, void* cookie)
  401. {
  402. Model* model = node->getModel();
  403. if (model)
  404. {
  405. // Transparent nodes must be drawn last (stage 1)
  406. bool isTransparent = (node == _glowNode);
  407. // Skip transparent objects for stage 0
  408. if ((!isTransparent && (int*)cookie == 0) || (isTransparent && (int*)cookie == (int*)1))
  409. model->draw();
  410. }
  411. return true;
  412. }
  413. void SpaceshipGame::drawText()
  414. {
  415. _font->start();
  416. char text[1024];
  417. sprintf(text, "%dsec.", (int)_time);
  418. _font->drawText(text, getWidth() - 120, 10, Vector4(1, 1, 0, 1), _font->getSize());
  419. if (_finished)
  420. {
  421. _font->drawText("Click to Play Again", getWidth()/2 - 175, getHeight()/2 - 40, Vector4::one(), _font->getSize());
  422. }
  423. _font->finish();
  424. }
  425. void SpaceshipGame::keyEvent(Keyboard::KeyEvent evt, int key)
  426. {
  427. if (evt == Keyboard::KEY_PRESS)
  428. {
  429. switch (key)
  430. {
  431. case Keyboard::KEY_ESCAPE:
  432. exit();
  433. break;
  434. }
  435. }
  436. }
  437. void SpaceshipGame::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
  438. {
  439. switch (evt)
  440. {
  441. case Touch::TOUCH_PRESS:
  442. if (_finished && (getAbsoluteTime() - _finishedTime) > 1000L)
  443. {
  444. resetGame();
  445. }
  446. case Touch::TOUCH_MOVE:
  447. if (!_finished)
  448. {
  449. _pushing = true;
  450. _pushPoint.set(x, y);
  451. }
  452. break;
  453. case Touch::TOUCH_RELEASE:
  454. _pushing = false;
  455. break;
  456. }
  457. }