GameplayScreen.cs 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996
  1. //-----------------------------------------------------------------------------
  2. // GameplayScreen.cs
  3. //
  4. // Microsoft XNA Community Game Platform
  5. // Copyright (C) Microsoft Corporation. All rights reserved.
  6. //-----------------------------------------------------------------------------
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Collections.ObjectModel;
  10. using System.IO;
  11. using Microsoft.Xna.Framework;
  12. using Microsoft.Xna.Framework.Audio;
  13. using Microsoft.Xna.Framework.GamerServices;
  14. using Microsoft.Xna.Framework.Graphics;
  15. using Microsoft.Xna.Framework.Input;
  16. using Microsoft.Xna.Framework.Storage;
  17. using Microsoft.Xna.Framework.Content;
  18. using Microsoft.Xna.Framework.Media;
  19. namespace AlienGameSample
  20. {
  21. /// <summary>
  22. /// This component draws the entire background for the game. It handles
  23. /// drawing the ground, clouds, sun, and moon. also handles animating them
  24. /// and day/night transitions
  25. /// </summary>
  26. public class GameplayScreen : GameScreen
  27. {
  28. //
  29. // Game Play Members
  30. //
  31. Player player;
  32. List<Alien> aliens;
  33. List<Bullet> alienBullets;
  34. List<Bullet> playerBullets;
  35. Rectangle worldBounds;
  36. bool gameOver;
  37. int baseLevelKillCount;
  38. int levelKillCount;
  39. float alienSpawnTimer;
  40. float alienSpawnRate;
  41. float alienMaxAccuracy;
  42. float alienSpeedMin;
  43. float alienSpeedMax;
  44. int alienScore;
  45. int nextLife;
  46. int hitStreak;
  47. int highScore;
  48. Random random;
  49. //
  50. // Rendering Members
  51. //
  52. Texture2D cloud1Texture;
  53. Texture2D cloud2Texture;
  54. Texture2D sunTexture;
  55. Texture2D moonTexture;
  56. Texture2D groundTexture;
  57. Texture2D tankTexture;
  58. Texture2D alienTexture;
  59. Texture2D badguy_blue;
  60. Texture2D badguy_red;
  61. Texture2D badguy_green;
  62. Texture2D badguy_orange;
  63. Texture2D mountainsTexture;
  64. Texture2D hillsTexture;
  65. Texture2D bulletTexture;
  66. Texture2D laserTexture;
  67. SpriteFont scoreFont;
  68. SpriteFont menuFont;
  69. Vector2 cloud1Position;
  70. Vector2 cloud2Position;
  71. Vector2 sunPosition;
  72. // Level changes, nighttime transitions, etc
  73. float transitionFactor; // 0.0f == day, 1.0f == night
  74. float transitionRate; // > 0.0f == day to night
  75. ParticleSystem particles;
  76. //
  77. // Audio Members
  78. //
  79. SoundEffect alienFired;
  80. SoundEffect alienDied;
  81. SoundEffect playerFired;
  82. SoundEffect playerDied;
  83. Song backMusic;
  84. public GameplayScreen()
  85. {
  86. random = new Random();
  87. worldBounds = new Rectangle(0, 0, 320, 480);
  88. player = new Player();
  89. playerBullets = new List<Bullet>();
  90. aliens = new List<Alien>();
  91. alienBullets = new List<Bullet>();
  92. gameOver = true;
  93. TransitionOnTime = TimeSpan.FromSeconds(0.0);
  94. TransitionOffTime = TimeSpan.FromSeconds(0.0);
  95. }
  96. #region LOADING AND UNLOADING
  97. /// <summary>
  98. /// Loads all of the content needed by the game. All of this should have already been cached
  99. /// into the ContentManager by the LoadingScreen.
  100. /// </summary>
  101. public override void LoadContent()
  102. {
  103. backMusic = ScreenManager.Game.Content.Load<Song>("backmusic");
  104. cloud1Texture = ScreenManager.Game.Content.Load<Texture2D>("cloud1");
  105. cloud2Texture = ScreenManager.Game.Content.Load<Texture2D>("cloud2");
  106. sunTexture = ScreenManager.Game.Content.Load<Texture2D>("sun");
  107. moonTexture = ScreenManager.Game.Content.Load<Texture2D>("moon");
  108. groundTexture = ScreenManager.Game.Content.Load<Texture2D>("ground");
  109. tankTexture = ScreenManager.Game.Content.Load<Texture2D>("tank");
  110. mountainsTexture = ScreenManager.Game.Content.Load<Texture2D>("mountains_blurred");
  111. hillsTexture = ScreenManager.Game.Content.Load<Texture2D>("hills");
  112. alienTexture = ScreenManager.Game.Content.Load<Texture2D>("alien1");
  113. badguy_blue = ScreenManager.Game.Content.Load<Texture2D>("badguy_blue");
  114. badguy_red = ScreenManager.Game.Content.Load<Texture2D>("badguy_red");
  115. badguy_green = ScreenManager.Game.Content.Load<Texture2D>("badguy_green");
  116. badguy_orange = ScreenManager.Game.Content.Load<Texture2D>("badguy_orange");
  117. bulletTexture = ScreenManager.Game.Content.Load<Texture2D>("bullet");
  118. laserTexture = ScreenManager.Game.Content.Load<Texture2D>("laser");
  119. alienFired = ScreenManager.Game.Content.Load<SoundEffect>("Tank_Fire");
  120. alienDied = ScreenManager.Game.Content.Load<SoundEffect>("Alien_Hit");
  121. playerFired = ScreenManager.Game.Content.Load<SoundEffect>("Tank_Fire");
  122. playerDied = ScreenManager.Game.Content.Load<SoundEffect>("Player_Hit");
  123. scoreFont = ScreenManager.Game.Content.Load<SpriteFont>("ScoreFont");
  124. menuFont = ScreenManager.Game.Content.Load<SpriteFont>("menufont");
  125. player.Width = tankTexture.Width;
  126. player.Height = tankTexture.Height;
  127. cloud1Position = new Vector2(224 - cloud1Texture.Width, 32);
  128. cloud2Position = new Vector2(64, 80);
  129. sunPosition = new Vector2(16, 16);
  130. particles = new ParticleSystem(ScreenManager.Game.Content, ScreenManager.SpriteBatch);
  131. LoadHighscore();
  132. base.LoadContent();
  133. // Automatically start the game once all loading is done
  134. Start();
  135. }
  136. /// <summary>
  137. /// Save the scores and dispose of the particles
  138. /// </summary>
  139. public override void UnloadContent()
  140. {
  141. particles = null;
  142. base.UnloadContent();
  143. }
  144. /// <summary>
  145. /// Saves the current highscore to a text file. The StorageDevice was selected during the loading screen.
  146. /// </summary>
  147. private void SaveHighscore()
  148. {
  149. StorageDevice device = (StorageDevice)ScreenManager.Game.Services.GetService(typeof(StorageDevice));
  150. if (device != null)
  151. {
  152. StorageContainer container = device.OpenContainer("AlienGame");
  153. StreamWriter writer = new StreamWriter(Path.Combine(container.Path, "highscores.txt"));
  154. writer.Write(highScore.ToString(System.Globalization.CultureInfo.InvariantCulture));
  155. writer.Flush();
  156. writer.Close();
  157. container.Dispose();
  158. }
  159. }
  160. /// <summary>
  161. /// Loads the high score from a text file. The StorageDevice was selected during the loading screen.
  162. /// </summary>
  163. private void LoadHighscore()
  164. {
  165. StorageDevice device = (StorageDevice)ScreenManager.Game.Services.GetService(typeof(StorageDevice));
  166. if (device != null)
  167. {
  168. StorageContainer container = device.OpenContainer("AlienGame");
  169. if (File.Exists(Path.Combine(container.Path, "highscores.txt")))
  170. {
  171. StreamReader reader = null;
  172. try
  173. {
  174. reader = new StreamReader(Path.Combine(container.Path, "highscores.txt"));
  175. highScore = Int32.Parse(reader.ReadToEnd(), System.Globalization.CultureInfo.InvariantCulture);
  176. }
  177. catch(FormatException)
  178. {
  179. highScore = 10000;
  180. }
  181. finally
  182. {
  183. if (reader != null)
  184. {
  185. reader.Close();
  186. reader.Dispose();
  187. }
  188. }
  189. }
  190. container.Dispose();
  191. }
  192. }
  193. #endregion
  194. #region UPDATE AND SIMULATION
  195. /// <summary>
  196. /// Runs one frame of update for the game.
  197. /// </summary>
  198. /// <param name="gameTime">Provides a snapshot of timing values.</param>
  199. public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
  200. {
  201. float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;
  202. if (IsActive)
  203. {
  204. // Move the player
  205. if (player.IsAlive == true)
  206. {
  207. player.Position += player.Velocity * 128.0f * elapsed;
  208. player.FireTimer -= elapsed;
  209. if (player.Position.X <= 0.0f)
  210. player.Position = new Vector2(0.0f, player.Position.Y);
  211. if (player.Position.X + player.Width >= worldBounds.Right)
  212. player.Position = new Vector2(worldBounds.Right - player.Width, player.Position.Y);
  213. }
  214. Respawn(elapsed);
  215. UpdateAliens(elapsed);
  216. UpdateBullets(elapsed);
  217. CheckHits();
  218. if (player.IsAlive && player.Velocity.LengthSquared() > 0.1f)
  219. particles.CreatePlayerDust(player);
  220. particles.Update(elapsed);
  221. }
  222. base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
  223. }
  224. /// <summary>
  225. /// Input helper method provided by GameScreen. Packages up the various input
  226. /// values for ease of use. Here it checks for pausing and handles controlling
  227. /// the player's tank.
  228. /// </summary>
  229. /// <param name="input">The state of the gamepads</param>
  230. public override void HandleInput(InputState input)
  231. {
  232. if (input == null)
  233. throw new ArgumentNullException("input");
  234. if (input.PauseGame)
  235. {
  236. if (gameOver == true)
  237. {
  238. MediaPlayer.Stop();
  239. foreach (GameScreen screen in ScreenManager.GetScreens())
  240. screen.ExitScreen();
  241. ScreenManager.AddScreen(new BackgroundScreen());
  242. ScreenManager.AddScreen(new MainMenuScreen());
  243. }
  244. else
  245. {
  246. ScreenManager.AddScreen(new PauseMenuScreen());
  247. }
  248. }
  249. else
  250. {
  251. bool touchShoot = false;
  252. #if TARGET_IPHONE_SIMULATOR
  253. // This section handles tank movement. We only allow one "movement" action
  254. // to occur at once so that touchpad devices don't get double hits.
  255. player.Velocity.X = MathHelper.Min(input.CurrentGamePadStates.ThumbSticks.Left.X * 2.0f, 1.0f);
  256. touchShoot = input.MenuSelect;
  257. #else
  258. // Add the accelerometer support
  259. player.Velocity.X = MathHelper.Min(Accelerometer.GetState().Acceleration.X * 2.0f, 1.0f);
  260. // tap the screen to shoot
  261. foreach (TouchLocation location in input.TouchStates)
  262. {
  263. switch (location.State)
  264. {
  265. case TouchLocationState.Pressed:
  266. touchShoot = true;
  267. break;
  268. case TouchLocationState.Moved:
  269. break;
  270. case TouchLocationState.Released:
  271. break;
  272. }
  273. }
  274. #endif
  275. if (touchShoot)
  276. {
  277. //Mouse.SetPosition(0,0);
  278. if (player.FireTimer <= 0.0f && player.IsAlive && !gameOver)
  279. {
  280. Bullet bullet = CreatePlayerBullet();
  281. bullet.Position = new Vector2((int)(player.Position.X + player.Width / 2) - bulletTexture.Width / 2, player.Position.Y - 4);
  282. bullet.Velocity = new Vector2(0, -256.0f);
  283. player.FireTimer = 0.5f;
  284. particles.CreatePlayerFireSmoke(player);
  285. playerFired.Play();
  286. }
  287. }
  288. }
  289. }
  290. /// <summary>
  291. /// Handles respawning the player if we are playing a game and the player is dead.
  292. /// </summary>
  293. /// <param name="elapsed">Time elapsed since Respawn was called last.</param>
  294. void Respawn(float elapsed)
  295. {
  296. if (gameOver)
  297. return;
  298. if (!player.IsAlive)
  299. {
  300. player.RespawnTimer -= elapsed;
  301. if (player.RespawnTimer <= 0.0f)
  302. {
  303. // See if there are any bullets close...
  304. int left = worldBounds.Width / 2 - tankTexture.Width / 2 - 8;
  305. int right = worldBounds.Width / 2 + tankTexture.Width / 2 + 8;
  306. for (int i = 0; i < alienBullets.Count; ++i)
  307. {
  308. if (alienBullets[i].IsAlive == false)
  309. continue;
  310. if (alienBullets[i].Position.X >= left || alienBullets[i].Position.X <= right)
  311. return;
  312. }
  313. player.IsAlive = true;
  314. player.Position = new Vector2(worldBounds.Width / 2 - player.Width / 2, worldBounds.Bottom - (int)(groundTexture.Height*1.3f) + 2 - player.Height);
  315. player.Velocity = Vector2.Zero;
  316. player.Lives--;
  317. }
  318. }
  319. }
  320. /// <summary>
  321. /// Moves all of the bullets (player and alien) and prunes "dead" bullets.
  322. /// </summary>
  323. /// <param name="elapsed"></param>
  324. void UpdateBullets(float elapsed)
  325. {
  326. for (int i = 0; i < playerBullets.Count; ++i)
  327. {
  328. if (playerBullets[i].IsAlive == false)
  329. continue;
  330. playerBullets[i].Position += playerBullets[i].Velocity * elapsed;
  331. if (playerBullets[i].Position.Y < -32)
  332. {
  333. playerBullets[i].IsAlive = false;
  334. hitStreak = 0;
  335. }
  336. }
  337. for (int i = 0; i < alienBullets.Count; ++i)
  338. {
  339. if (alienBullets[i].IsAlive == false)
  340. continue;
  341. alienBullets[i].Position += alienBullets[i].Velocity * elapsed;
  342. if (alienBullets[i].Position.Y > worldBounds.Height - groundTexture.Height - laserTexture.Height)
  343. alienBullets[i].IsAlive = false;
  344. }
  345. }
  346. /// <summary>
  347. /// Moves the aliens and performs there "thinking" by determining if they
  348. /// should shoot and where.
  349. /// </summary>
  350. /// <param name="elapsed">The elapsed time since UpdateAliens was called last.</param>
  351. private void UpdateAliens(float elapsed)
  352. {
  353. // See if it's time to spawn an alien;
  354. alienSpawnTimer -= elapsed;
  355. if (alienSpawnTimer <= 0.0f)
  356. {
  357. SpawnAlien();
  358. alienSpawnTimer += alienSpawnRate;
  359. }
  360. for (int i = 0; i < aliens.Count; ++i)
  361. {
  362. if (aliens[i].IsAlive == false)
  363. continue;
  364. aliens[i].Position += aliens[i].Velocity * elapsed;
  365. if ((aliens[i].Position.X < -aliens[i].Width - 64 && aliens[i].Velocity.X < 0.0f) ||
  366. (aliens[i].Position.X > worldBounds.Width + 64 && aliens[i].Velocity.X > 0.0f))
  367. {
  368. aliens[i].IsAlive = false;
  369. continue;
  370. }
  371. aliens[i].FireTimer -= elapsed;
  372. if (aliens[i].FireTimer <= 0.0f && aliens[i].FireCount > 0)
  373. {
  374. if (player.IsAlive)
  375. {
  376. Bullet bullet = CreateAlienBullet();
  377. bullet.Position.X = aliens[i].Position.X + aliens[i].Width / 2 - laserTexture.Width / 2;
  378. bullet.Position.Y = aliens[i].Position.Y + aliens[i].Height;
  379. if ((float)random.NextDouble() <= aliens[i].Accuracy)
  380. {
  381. bullet.Velocity = Vector2.Normalize(player.Position - aliens[i].Position) * 64.0f;
  382. }
  383. else
  384. {
  385. bullet.Velocity = new Vector2(-8.0f + 16.0f * (float)random.NextDouble(), 64.0f);
  386. }
  387. alienFired.Play();
  388. }
  389. aliens[i].FireCount--;
  390. }
  391. }
  392. }
  393. /// <summary>
  394. /// Performs all bullet and player/alien collision detection. Also handles game logic
  395. /// when a hit occurs, such as killing something, adding score, ending the game, etc.
  396. /// </summary>
  397. void CheckHits()
  398. {
  399. if (gameOver)
  400. return;
  401. for (int i = 0; i < playerBullets.Count; ++i)
  402. {
  403. if (playerBullets[i].IsAlive == false)
  404. continue;
  405. for (int a = 0; a < aliens.Count; ++a)
  406. {
  407. if (aliens[a].IsAlive == false)
  408. continue;
  409. if ((playerBullets[i].Position.X >= aliens[a].Position.X && playerBullets[i].Position.X <= aliens[a].Position.X + aliens[a].Width) &&
  410. (playerBullets[i].Position.Y >= aliens[a].Position.Y && playerBullets[i].Position.Y <= aliens[a].Position.Y + aliens[a].Height))
  411. {
  412. playerBullets[i].IsAlive = false;
  413. aliens[a].IsAlive = false;
  414. hitStreak++;
  415. player.Score += aliens[a].Score * (hitStreak / 5 + 1);
  416. if (player.Score > highScore)
  417. highScore = player.Score;
  418. if (player.Score > nextLife)
  419. {
  420. player.Lives++;
  421. nextLife += nextLife;
  422. }
  423. levelKillCount--;
  424. if (levelKillCount <= 0)
  425. AdvanceLevel();
  426. particles.CreateAlienExplosion(new Vector2(aliens[a].Position.X + aliens[a].Width / 2, aliens[a].Position.Y + aliens[a].Height / 2));
  427. alienDied.Play();
  428. }
  429. }
  430. }
  431. if (player.IsAlive == false)
  432. return;
  433. for (int i = 0; i < alienBullets.Count; ++i)
  434. {
  435. if (alienBullets[i].IsAlive == false)
  436. continue;
  437. if ((alienBullets[i].Position.X >= player.Position.X + 2 && alienBullets[i].Position.X <= player.Position.X + player.Width - 2) &&
  438. (alienBullets[i].Position.Y >= player.Position.Y + 2 && alienBullets[i].Position.Y <= player.Position.Y + player.Height))
  439. {
  440. alienBullets[i].IsAlive = false;
  441. player.IsAlive = false;
  442. hitStreak = 0;
  443. player.RespawnTimer = 3.0f;
  444. particles.CreatePlayerExplosion(new Vector2(player.Position.X + player.Width / 2, player.Position.Y + player.Height / 2));
  445. playerDied.Play();
  446. if (player.Lives <= 0)
  447. {
  448. gameOver = true;
  449. SaveHighscore();
  450. }
  451. }
  452. }
  453. }
  454. /// <summary>
  455. /// Starts a new game session, setting all game state to initial values.
  456. /// </summary>
  457. void Start()
  458. {
  459. MediaPlayer.Volume = 0.2f;
  460. MediaPlayer.Play(backMusic);
  461. if (gameOver)
  462. {
  463. player.Score = 0;
  464. player.Lives = 3;
  465. player.RespawnTimer = 0.0f;
  466. gameOver = false;
  467. aliens.Clear();
  468. alienBullets.Clear();
  469. playerBullets.Clear();
  470. Respawn(0.0f);
  471. }
  472. transitionRate = 0.0f;
  473. transitionFactor = 0.0f;
  474. levelKillCount = 5;
  475. baseLevelKillCount = 5;
  476. alienScore = 25;
  477. alienSpawnRate = 1.0f;
  478. alienMaxAccuracy = 0.25f;
  479. alienSpeedMin = 24.0f;
  480. alienSpeedMax = 32.0f;
  481. alienSpawnRate = 2.0f;
  482. alienSpawnTimer = alienSpawnRate;
  483. nextLife = 5000;
  484. }
  485. /// <summary>
  486. /// Advances the difficulty of the game one level.
  487. /// </summary>
  488. void AdvanceLevel()
  489. {
  490. baseLevelKillCount += 5;
  491. levelKillCount = baseLevelKillCount;
  492. alienScore += 25;
  493. alienSpawnRate -= 0.3f;
  494. alienMaxAccuracy += 0.1f;
  495. if (alienMaxAccuracy > 0.75f)
  496. alienMaxAccuracy = 0.75f;
  497. alienSpeedMin *= 1.35f;
  498. alienSpeedMax *= 1.35f;
  499. if (alienSpawnRate < 0.33f)
  500. alienSpawnRate = 0.33f;
  501. if (transitionFactor == 1.0f)
  502. {
  503. transitionRate = -0.5f;
  504. }
  505. else
  506. {
  507. transitionRate = 0.5f;
  508. }
  509. }
  510. /// <summary>
  511. /// Returns an instance of a usable player bullet. Prefers reusing an existing (dead)
  512. /// bullet over creating a new instance.
  513. /// </summary>
  514. /// <returns>A bullet ready to place into the world.</returns>
  515. Bullet CreatePlayerBullet()
  516. {
  517. Bullet b = null;
  518. for (int i = 0; i < playerBullets.Count; ++i)
  519. {
  520. if (playerBullets[i].IsAlive == false)
  521. {
  522. b = playerBullets[i];
  523. break;
  524. }
  525. }
  526. if (b == null)
  527. {
  528. b = new Bullet();
  529. playerBullets.Add(b);
  530. }
  531. b.IsAlive = true;
  532. return b;
  533. }
  534. /// <summary>
  535. /// Returns an instance of a usable alien bullet. Prefers reusing an existing (dead)
  536. /// bullet over creating a new instance.
  537. /// </summary>
  538. /// <returns>A bullet ready to place into the world.</returns>
  539. Bullet CreateAlienBullet()
  540. {
  541. Bullet b = null;
  542. for (int i = 0; i < alienBullets.Count; ++i)
  543. {
  544. if (alienBullets[i].IsAlive == false)
  545. {
  546. b = alienBullets[i];
  547. break;
  548. }
  549. }
  550. if (b == null)
  551. {
  552. b = new Bullet();
  553. alienBullets.Add(b);
  554. }
  555. b.IsAlive = true;
  556. return b;
  557. }
  558. /// <summary>
  559. /// Returns an instance of a usable alien instance. Prefers reusing an existing (dead)
  560. /// alien over creating a new instance.
  561. /// </summary>
  562. /// <returns>An alien ready to place into the world.</returns>
  563. Alien CreateAlien()
  564. {
  565. Alien b = null;
  566. for (int i = 0; i < aliens.Count; ++i)
  567. {
  568. if (aliens[i].IsAlive == false)
  569. {
  570. b = aliens[i];
  571. break;
  572. }
  573. }
  574. if (b == null)
  575. {
  576. b = new Alien();
  577. aliens.Add(b);
  578. }
  579. b.IsAlive = true;
  580. return b;
  581. }
  582. /// <summary>
  583. /// Creats an instance of an alien, sets the initial state, and places it into the world.
  584. /// </summary>
  585. private void SpawnAlien()
  586. {
  587. Alien newAlien = CreateAlien();
  588. if (random.Next(2) == 1)
  589. {
  590. newAlien.Position.X = -64.0f;
  591. newAlien.Velocity.X = random.Next((int)alienSpeedMin, (int)alienSpeedMax);
  592. }
  593. else
  594. {
  595. newAlien.Position.X = worldBounds.Width + 32;
  596. newAlien.Velocity.X = -random.Next((int)alienSpeedMin, (int)alienSpeedMax);
  597. }
  598. newAlien.Position.Y = 31.2f + 105.0f * (float)random.NextDouble();
  599. // Aliens
  600. if (transitionFactor > 0.0f)
  601. {
  602. switch (random.Next(4))
  603. {
  604. case 0:
  605. newAlien.Texture = badguy_blue;
  606. break;
  607. case 1:
  608. newAlien.Texture = badguy_red;
  609. break;
  610. case 2:
  611. newAlien.Texture = badguy_green;
  612. break;
  613. case 3:
  614. newAlien.Texture = badguy_orange;
  615. break;
  616. }
  617. }
  618. else
  619. {
  620. newAlien.Texture = alienTexture;
  621. }
  622. newAlien.Width = newAlien.Texture.Width;
  623. newAlien.Height = newAlien.Texture.Height;
  624. newAlien.IsAlive = true;
  625. newAlien.Score = alienScore;
  626. float duration = 320.0f / newAlien.Velocity.Length();
  627. newAlien.FireTimer = duration * (float)random.NextDouble();
  628. newAlien.FireCount = 1;
  629. newAlien.Accuracy = alienMaxAccuracy;
  630. }
  631. #endregion
  632. #region DRAWING
  633. /// <summary>
  634. /// Draw the game world, effects, and hud
  635. /// </summary>
  636. /// <param name="gameTime">The elapsed time since last Draw</param>
  637. public override void Draw(GameTime gameTime)
  638. {
  639. float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
  640. ScreenManager.SpriteBatch.Begin();
  641. DrawBackground(elapsedTime);
  642. DrawAliens();
  643. DrawPlayer();
  644. DrawBullets();
  645. particles.Draw();
  646. DrawForeground(elapsedTime);
  647. DrawHud();
  648. #if TARGET_IPHONE_SIMULATOR
  649. // Draw the GamePad
  650. GamePad.Draw(gameTime,ScreenManager.SpriteBatch);
  651. #endif
  652. ScreenManager.SpriteBatch.End();
  653. }
  654. /// <summary>
  655. /// Draws the player's tank
  656. /// </summary>
  657. void DrawPlayer()
  658. {
  659. if (!gameOver && player.IsAlive)
  660. {
  661. ScreenManager.SpriteBatch.Draw(tankTexture, player.Position, Color.White);
  662. }
  663. }
  664. /// <summary>
  665. /// Draws all of the aliens.
  666. /// </summary>
  667. void DrawAliens()
  668. {
  669. for (int i = 0; i < aliens.Count; ++i)
  670. {
  671. if (aliens[i].IsAlive)
  672. ScreenManager.SpriteBatch.Draw(aliens[i].Texture, new Rectangle((int)aliens[i].Position.X, (int)aliens[i].Position.Y, (int)aliens[i].Width, (int)aliens[i].Height), Color.White);
  673. }
  674. }
  675. /// <summary>
  676. /// Draw both the player and alien bullets.
  677. /// </summary>
  678. private void DrawBullets()
  679. {
  680. for (int i = 0; i < playerBullets.Count; ++i)
  681. {
  682. if (playerBullets[i].IsAlive)
  683. ScreenManager.SpriteBatch.Draw(bulletTexture, playerBullets[i].Position, Color.White);
  684. }
  685. for (int i = 0; i < alienBullets.Count; ++i)
  686. {
  687. if (alienBullets[i].IsAlive)
  688. ScreenManager.SpriteBatch.Draw(laserTexture, alienBullets[i].Position, Color.White);
  689. }
  690. }
  691. /// <summary>
  692. /// Draw the foreground, which is basically the clouds. I think I had planned on one point
  693. /// having foreground grass that was drawn in front of the tank.
  694. /// </summary>
  695. /// <param name="elapsedTime">The elapsed time since last Draw</param>
  696. private void DrawForeground(float elapsedTime)
  697. {
  698. // Move the clouds. Movement seems like a Update thing to do, but this animations
  699. // have no impact over gameplay.
  700. cloud1Position += new Vector2(24.0f, 0.0f) * elapsedTime;
  701. if (cloud1Position.X > 320.0f)
  702. cloud1Position.X = -cloud1Texture.Width * 2.0f;
  703. cloud2Position += new Vector2(16.0f, 0.0f) * elapsedTime;
  704. if (cloud2Position.X > 320.0f)
  705. cloud2Position.X = -cloud1Texture.Width * 2.0f;
  706. ScreenManager.SpriteBatch.Draw(cloud1Texture, cloud1Position, Color.White);
  707. ScreenManager.SpriteBatch.Draw(cloud2Texture, cloud2Position, Color.White);
  708. }
  709. /// <summary>
  710. /// Draw the grass, hills, mountains, and sun/moon. Handles transitioning
  711. /// between day and night as well.
  712. /// </summary>
  713. /// <param name="elapsedTime">The elapsed time since last Draw</param>
  714. private void DrawBackground(float elapsedTime)
  715. {
  716. transitionFactor += transitionRate * elapsedTime;
  717. if (transitionFactor < 0.0f)
  718. {
  719. transitionFactor = 0.0f;
  720. transitionRate = 0.0f;
  721. }
  722. if (transitionFactor > 1.0f)
  723. {
  724. transitionFactor = 1.0f;
  725. transitionRate = 0.0f;
  726. }
  727. Vector3 day = Color.White.ToVector3();
  728. Vector3 night = new Color(80, 80, 180).ToVector3();
  729. Vector3 dayClear = Color.CornflowerBlue.ToVector3();
  730. Vector3 nightClear = night;
  731. Color clear = new Color(Vector3.Lerp(dayClear, nightClear, transitionFactor));
  732. Color tint = new Color(Vector3.Lerp(day,night,transitionFactor));
  733. // Clear the background, using the day/night color
  734. ScreenManager.Game.GraphicsDevice.Clear(clear);
  735. // Draw the mountains
  736. ScreenManager.SpriteBatch.Draw(mountainsTexture, new Rectangle(0, 480 - (int)(mountainsTexture.Height*1.3f),320,(int)(mountainsTexture.Height*1.3f)), tint);
  737. // Draw the hills
  738. ScreenManager.SpriteBatch.Draw(hillsTexture, new Rectangle(0, 480 - (int)(hillsTexture.Height*1.3f),320,(int)(hillsTexture.Height*1.3f)), tint);
  739. // Draw the ground
  740. ScreenManager.SpriteBatch.Draw(groundTexture, new Rectangle(0, 480 - (int)(groundTexture.Height*1.3f),320,(int)(groundTexture.Height*1.3f)), tint);
  741. // Draw the sun or moon (based on time)
  742. ScreenManager.SpriteBatch.Draw(sunTexture, sunPosition, new Color(255,255,255, (byte)(255.0f * (1.0f - transitionFactor))));
  743. ScreenManager.SpriteBatch.Draw(moonTexture, sunPosition, new Color(255,255,255, (byte)(255.0f * transitionFactor)));
  744. }
  745. /// <summary>
  746. /// Draw the hud, which consists of the score elements and the GAME OVER tag.
  747. /// </summary>
  748. void DrawHud()
  749. {
  750. if (gameOver)
  751. {
  752. Vector2 size = menuFont.MeasureString("GAME OVER");
  753. DrawString(menuFont, "GAME OVER", new Vector2(ScreenManager.Game.GraphicsDevice.Viewport.Width / 2 - size.X / 2, ScreenManager.Game.GraphicsDevice.Viewport.Height / 2 - size.Y / 2), new Color(255,64,64));
  754. }
  755. else
  756. {
  757. int bonus = 100 * (hitStreak / 5);
  758. string bonusString = (bonus > 0 ? " (" + bonus.ToString(System.Globalization.CultureInfo.CurrentCulture) + "%)" : "");
  759. // Score
  760. DrawString(scoreFont, "SCORE: " + player.Score.ToString(System.Globalization.CultureInfo.CurrentCulture) + bonusString, new Vector2(6, 4), Color.Yellow);
  761. string text = "LIVES: " + player.Lives.ToString(System.Globalization.CultureInfo.CurrentCulture);
  762. Vector2 size = scoreFont.MeasureString(text);
  763. // Lives
  764. DrawString(scoreFont, text, new Vector2(314 - (int)size.X, 4), Color.Yellow);
  765. DrawString(scoreFont, "LEVEL: " + (((baseLevelKillCount - 5) / 5) + 1).ToString(System.Globalization.CultureInfo.CurrentCulture), new Vector2(6, 460), Color.Yellow);
  766. text = "HIGH SCORE: " + highScore.ToString(System.Globalization.CultureInfo.CurrentCulture);
  767. size = scoreFont.MeasureString(text);
  768. DrawString(scoreFont, text, new Vector2(314 - (int)size.X, 460), Color.Yellow);
  769. }
  770. }
  771. /// <summary>
  772. /// A simple helper to draw shadowed text.
  773. /// </summary>
  774. void DrawString(SpriteFont font, string text, Vector2 position, Color color)
  775. {
  776. ScreenManager.SpriteBatch.DrawString(font, text, new Vector2(position.X + 1, position.Y + 1), Color.Black);
  777. ScreenManager.SpriteBatch.DrawString(font, text, position, color);
  778. }
  779. #endregion
  780. }
  781. /// <summary>
  782. /// Represents either an alien or player bullet
  783. /// </summary>
  784. public class Bullet
  785. {
  786. public Vector2 Position;
  787. public Vector2 Velocity;
  788. public bool IsAlive;
  789. }
  790. /// <summary>
  791. /// The player's state
  792. /// </summary>
  793. public class Player
  794. {
  795. public Vector2 Position;
  796. public Vector2 Velocity;
  797. public float Width;
  798. public float Height;
  799. public bool IsAlive;
  800. public float FireTimer;
  801. public float RespawnTimer;
  802. public string Name;
  803. public Texture2D Picture;
  804. public int Score;
  805. public int Lives;
  806. }
  807. /// <summary>
  808. /// Data for an alien. The only difference between the ships
  809. /// and the badguys are the texture used.
  810. /// </summary>
  811. public class Alien
  812. {
  813. public Vector2 Position;
  814. public Texture2D Texture;
  815. public Vector2 Velocity;
  816. public float Width;
  817. public float Height;
  818. public int Score;
  819. public bool IsAlive;
  820. public float FireTimer;
  821. public float Accuracy;
  822. public int FireCount;
  823. }
  824. }