You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

675 lines
22 KiB

  1. package playground;
  2. import java.awt.Color;
  3. import java.awt.Font;
  4. import java.awt.Graphics2D;
  5. import java.awt.Polygon;
  6. import java.awt.RenderingHints;
  7. import java.awt.font.TextAttribute;
  8. import java.awt.image.BufferedImage;
  9. import java.io.*;
  10. import java.text.AttributedString;
  11. import java.util.LinkedList;
  12. import controller.EnemyController;
  13. import controller.FallingStarController;
  14. import controller.LimitedTimeController;
  15. import controller.ObjectController;
  16. import controller.EgoController;
  17. import controller.CollisionAwareEgoController;
  18. import gameobjects.AnimatedGameobject;
  19. import gameobjects.FallingStar;
  20. import gameobjects.GameObject;
  21. import gameobjects.EgoObject;
  22. import gameobjects.TextObject;
  23. import org.apache.logging.log4j.Logger;
  24. import org.apache.logging.log4j.LogManager;
  25. /**
  26. * Class that realizes all the game logic of a very simple game level. The level contains for now
  27. * only two objects that are {@link GameObject} subclasses: {@link FallingStar} and
  28. * {@link EgoObject}. Functions performed by this class are:
  29. * <ul>
  30. * <li>initially set up the level, spawn all object etc., in method {@link #prepareLevel}
  31. * <li>React to game events in {@link #actionIfEgoCollidesWithCollect(GameObject, GameObject)} ,
  32. * {@link #actionIfEgoCollidesWithEnemy(GameObject, GameObject)}, etc.
  33. * <li>define basic object movement rules for all objects in the level in the various
  34. * ObjectController subclasses: {@link EgoController} and {@link FallingStarController}.
  35. * </ul>
  36. */
  37. public class SpaceInvadersLevel extends Playground {
  38. public static final double SHOTSPEED = 175;
  39. public static final double EGOSPEED = 220;
  40. protected static final int LEVEL2STARS = 80;
  41. protected static final double BONUS_DURATION = 1.;
  42. protected static final double ENEMYSPEEDX = 60;
  43. protected static final double ENEMYSPEEDY = 40;
  44. protected static final double ENEMYSCALE = 1.0;
  45. protected static final double ENEMYSHOTSPEED = 75;
  46. protected static final int NRSHARDS = 50;
  47. protected static final double EXPL_DURATION = 1.;
  48. protected static final int NR_ENEMIES = 30;
  49. protected static final int NR_COLLECT = 5;
  50. protected static final Color EXPL_COLOR = Color.RED;
  51. protected static final double SHARDSPEED = 200;
  52. protected static final double STARSPEED = 100;
  53. protected static final double STARTTEXTSPEED = 75;
  54. protected static final double STARTPERIOD = 5.;
  55. protected static final double DYING_INTERVAL = 2.0;
  56. protected static final int CANVASX = 700;
  57. protected static final int CANVASY = 700;
  58. protected static final double EGORAD = 15;
  59. protected static final double LEVEL_INIT_TIME = 1.0;
  60. protected int nextShot = 0;
  61. protected boolean lost = false;
  62. protected boolean doneLevel = false;
  63. protected double starttime = 0;
  64. protected File smash = null;
  65. protected File laser = null;
  66. protected BufferedImage[] alienImage = null;
  67. protected double[] alienshowTime = null;
  68. protected BufferedImage[] heartImage = null;
  69. protected double[] heartshowTime = null;
  70. protected Animation enemyAnim = null;
  71. protected Animation heartAnim = null;
  72. protected static Logger logger = LogManager.getLogger(SpaceInvadersLevel.class);
  73. public SpaceInvadersLevel() {
  74. super();
  75. this.canvasX = this.preferredSizeX();
  76. this.canvasY = this.preferredSizeY();
  77. }
  78. /**
  79. * initially sets up the level. Not called by user interaction, but called every time a layer is
  80. * restarted from scratch. So make sure that this is possible. Here, resources are loaded only
  81. * once even if method is called several times.
  82. *
  83. * @param id String identifies level.
  84. */
  85. @Override
  86. public void prepareLevel(String id) {
  87. logger.info("PREPARE");
  88. reset();
  89. this.doneLevel = false;
  90. this.lost = false;
  91. resetFlags(FLAGS_LEVEL);
  92. // set up flags that some objects rely on
  93. setLevelFlag("delete", new LinkedList<String>());
  94. setLevelFlag("replace", new LinkedList<String>());
  95. getOrCreateGlobalFlag("points", Integer.valueOf(0));
  96. setLevelFlag("enemyShotCounter", Integer.valueOf(0));
  97. setLevelFlag("gameStatus", "start");
  98. setLevelFlag("detailedStatus", "std");
  99. getOrCreateGlobalFlag("egoLives", Integer.valueOf(5));
  100. setLevelFlag("dying", Double.valueOf(-1));
  101. // start time measure
  102. this.starttime = this.getGameTime();
  103. // music load
  104. if (this.smash == null) {
  105. this.smash = new File("./audio/smash.wav");
  106. }
  107. if (this.laser == null) {
  108. this.laser = new File("./audio/laser.wav");
  109. }
  110. // ----- Alien
  111. if (this.enemyAnim == null) {
  112. String dateiName = "./video/sweetAlien.txt";
  113. this.enemyAnim = new Animation(dateiName);
  114. }
  115. // -----Heart
  116. if (this.heartAnim == null) {
  117. String heartName = "./video/heart.txt";
  118. this.heartAnim = new Animation(heartName);
  119. }
  120. // load highscore and update
  121. HighscoreManager dh = new HighscoreManager();
  122. int alltimeHighscore = dh.readHSFromFile();
  123. setGlobalFlag("highscore", alltimeHighscore);
  124. logger.info("HIGHSCORE" + alltimeHighscore);
  125. }
  126. /**
  127. * (re)draws the level but NOT the objects, they draw themselves. Called by the main game loop.
  128. * This method mainly draws the background and the scoreboard.
  129. *
  130. * @param g2 Graphics2D object that can, and should be, draw on.
  131. */
  132. @Override
  133. public void redrawLevel(Graphics2D g2) {
  134. // Set anti-alias!
  135. g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
  136. // Set anti-alias for text
  137. g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
  138. RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
  139. // fill background with black
  140. int[] x = {0, canvasX, canvasX, 0};
  141. int[] y = {0, 0, canvasY, canvasY};
  142. Polygon bg = new Polygon(x, y, 4);
  143. g2.setColor(Color.BLACK);
  144. g2.fill(bg);
  145. // draw score in upper left part of playground
  146. Integer pts = (Integer) getGlobalFlag("points");
  147. Font drawFont = new Font("SansSerif", Font.PLAIN, 20);
  148. AttributedString as = new AttributedString("Points: " + pts);
  149. as.addAttribute(TextAttribute.FONT, drawFont);
  150. as.addAttribute(TextAttribute.FOREGROUND, Color.yellow);
  151. g2.drawString(as.getIterator(), 10, 20);
  152. // draw lives counter in upper left part of playground
  153. Integer lives = (Integer) getGlobalFlag("egoLives");
  154. Font drawFont2 = new Font("SansSerif", Font.PLAIN, 20);
  155. AttributedString as2 = new AttributedString("Lives: " + lives);
  156. as2.addAttribute(TextAttribute.FONT, drawFont2);
  157. as2.addAttribute(TextAttribute.FOREGROUND, Color.yellow);
  158. g2.drawString(as2.getIterator(), canvasX - 100, 20);
  159. // draw highscore in left part of playground under score
  160. Integer highscore = (Integer) getGlobalFlag("highscore");
  161. Font drawFont3 = new Font("SansSerif", Font.PLAIN, 20);
  162. AttributedString as3 = new AttributedString("Highscore: " + highscore);
  163. as3.addAttribute(TextAttribute.FONT, drawFont3);
  164. as3.addAttribute(TextAttribute.FOREGROUND, Color.yellow);
  165. g2.drawString(as3.getIterator(), 10, 40);
  166. if (isPaused()) {
  167. Font drawFont4 = new Font("SansSerif", Font.PLAIN, 50);
  168. AttributedString as4 = new AttributedString("Das Spiel wurde pausiert.");
  169. as4.addAttribute(TextAttribute.FONT, drawFont4);
  170. as4.addAttribute(TextAttribute.FOREGROUND, Color.red);
  171. g2.drawString(as4.getIterator(), 30, 400);
  172. }
  173. }
  174. /**
  175. * applies the logic of the level: For now, this is just about deleting shots that are leaving the
  176. * screen and calling methods 'actionIf..' in case objects collide.
  177. *
  178. */
  179. @Override
  180. public void applyGameLogic() {
  181. double gameTime = this.getGameTime();
  182. String gameStatus = (String) getLevelFlag("gameStatus");
  183. String subStatus = (String) getLevelFlag("detailedStatus");
  184. if (gameStatus.equals("start") == true) {
  185. setupInitialState();
  186. } else if (gameStatus.equals("starting") == true) {
  187. if ((gameTime - starttime) > LEVEL_INIT_TIME) {
  188. setLevelFlag("gameStatus", "init");
  189. }
  190. } else if (gameStatus.equals("init") == true) {
  191. this.createEnemies();
  192. this.createCollectables();
  193. setLevelFlag("gameStatus", "playing");
  194. } else if (gameStatus.equals("playing") == true) {
  195. GameObject s = this.getObject("ego");
  196. if (subStatus.equals("std")) {
  197. // check for collisions of enemy and shots, reuse shots list from before..
  198. LinkedList<GameObject> enemies = collectObjects("enemy", false);
  199. // check whether all enemies have been destroyed or escaped
  200. if (enemies.size() == 0) {
  201. HighscoreManager.writeHSToFile((Integer) Playground.getGlobalFlag("points"),
  202. (Integer) Playground.getGlobalFlag("highscore"));
  203. this.doneLevel = true;
  204. logger.info("no enemies left, level done.");
  205. }
  206. // loop over enemies to check for collisions or suchlike ...
  207. LinkedList<GameObject> shots = collectObjects("simpleShot", true);
  208. for (GameObject e : enemies) {
  209. // if ego collides with enemy..
  210. if (s.collisionDetection(e)) {
  211. actionIfEgoCollidesWithEnemy(e, s);
  212. }
  213. // if shot collides with enemy
  214. for (GameObject shot : shots) {
  215. if (e.collisionDetection(shot)) {
  216. actionIfEnemyIsHit(e, shot);
  217. }
  218. }
  219. }
  220. // collecting hearts
  221. LinkedList<GameObject> collects = collectObjects("collect", false);
  222. for (GameObject c : collects) {
  223. if (s.collisionDetection(c)) {
  224. actionIfEgoCollidesWithCollect(c, s);
  225. }
  226. }
  227. // loop over enemies and, with a certain probability, launch an enemy shot for each one
  228. for (GameObject e : enemies) {
  229. createEnemyShot(e);
  230. }
  231. // check for collisions between ego object and enemy shots
  232. LinkedList<GameObject> eshots = collectObjects("enmyShot", true);
  233. for (GameObject eshot : eshots) {
  234. if (s.collisionDetection(eshot)) {
  235. logger.trace("COLLISION" + eshot.scol.get(0) + "/" + s.scol.get(0));
  236. actionIfEgoObjectIsHit(eshot, s);
  237. }
  238. }
  239. } // if substatus..
  240. else if (subStatus.equals("dying")) {
  241. Double t0 = (Double) getLevelFlag("t0");
  242. if (gameTime - t0 > DYING_INTERVAL) {
  243. LinkedList<GameObject> enemies = collectObjects("enemy", false);
  244. setLevelFlag("detailedStatus", "std");
  245. s.setActive(true);
  246. for (GameObject e : enemies) {
  247. logger.trace("activating" + e.getId());
  248. e.setActive(true);
  249. }
  250. }
  251. }
  252. } // if (gameStatus == "playing")
  253. }
  254. public boolean gameOver() {
  255. return lost;
  256. }
  257. public boolean levelFinished() {
  258. return doneLevel;
  259. }
  260. /**
  261. * calculates and returns the preferred size of the level (in pixel) for X-direction
  262. */
  263. public int preferredSizeX() {
  264. return CANVASX;
  265. }
  266. /**
  267. * calculates and returns the preferred size of the level (in pixel) for Y-direction
  268. */
  269. public int preferredSizeY() {
  270. return CANVASY;
  271. }
  272. /** Adds ego object and stars and displays startup message. Is called from applyGameLogic */
  273. protected void setupInitialState() {
  274. double gameTime = this.getGameTime();
  275. setLevelFlag("gameStatus", "starting");
  276. this.createStars();
  277. // set up ego object
  278. this.createEgoObject();
  279. // add text object to playground
  280. ObjectController ctrl = new LimitedTimeController(gameTime, 3.);
  281. GameObject readyText = new TextObject("ready?", this, getSizeX() / 2, 0, 0, 100,
  282. this.getStartupMessage(), 50, Color.RED).addController(ctrl);
  283. addObject(readyText);
  284. }
  285. /**
  286. * simply returns the text that should be displayed at level start
  287. *
  288. * @return a string that is displayed at start. Should be not longer than 30 characters.
  289. */
  290. protected String getStartupMessage() {
  291. return "Get ready for level X!";
  292. }
  293. /**
  294. * returns a number representing the speed of an enemy object in X direction (pixels/second)
  295. *
  296. * @return a positive value
  297. */
  298. protected double calcEnemySpeedX() {
  299. return SpaceInvadersLevel.ENEMYSPEEDX;
  300. }
  301. /**
  302. * returns a number representing the speed of an enemy object in Y direction (pixels/second)
  303. *
  304. * @return a positive value
  305. */
  306. protected double calcEnemySpeedY() {
  307. return SpaceInvadersLevel.ENEMYSPEEDY;
  308. }
  309. /**
  310. * returns the maximum number of enemy instances (which are created at level start)
  311. *
  312. * @return a positive value
  313. */
  314. protected int calcNrEnemies() {
  315. return SpaceInvadersLevel.NR_ENEMIES;
  316. }
  317. /**
  318. * returns the maximum number of collectables' instances (which are created at level start)
  319. *
  320. * @return a positive value
  321. */
  322. protected int calcNrCollect() {
  323. return SpaceInvadersLevel.NR_COLLECT;
  324. }
  325. protected GameObject createEnemyShotObject(GameObject parentObject, String name,
  326. ObjectController limitedTimeController) {
  327. GameObject to =
  328. new TextObject(name, this, parentObject.getX(), parentObject.getY(), 0, ENEMYSHOTSPEED, "I",
  329. 20, Color.YELLOW).generateColliders().addController(limitedTimeController);
  330. /*
  331. * // also possible: GameObject to = new RectObject(name, this, parentObject.getX(),
  332. * parentObject.getY(), 0, ENEMYSHOTSPEED, 4, 20,
  333. * Color.YELLOW).addController(limitedTimeController) ; to.generateColliders();
  334. */
  335. return to;
  336. }
  337. protected void createEnemyShot(GameObject e) {
  338. double gameTime = this.getGameTime();
  339. double PROB = calcEnemyShotProb();
  340. double diceThrow = Math.random();
  341. Integer nrEnemyShots = (Integer) (getLevelFlag("enemyShotCounter"));
  342. if (diceThrow < PROB) {
  343. setLevelFlag("enemyShotCounter", Integer.valueOf(++nrEnemyShots));
  344. LimitedTimeController limitedTimeController = new LimitedTimeController(gameTime, 10.);
  345. GameObject textObject =
  346. createEnemyShotObject(e, "enmyShot" + nrEnemyShots, limitedTimeController);
  347. addObject(textObject);
  348. }
  349. }
  350. /**
  351. * calculates and returns the probability that an enemy shots. Used by
  352. * {@link SpaceInvadersLevel#createEnemyShot(GameObject)}.
  353. *
  354. * @return a positive value between 0 (no chance) to 1 (for sure).
  355. */
  356. double calcEnemyShotProb() {
  357. return 0.1 * this.getTimestep();
  358. }
  359. ObjectController createEnemyController() {
  360. return new EnemyController();
  361. }
  362. GameObject createSingleEnemy(String name, double x_enemy, double y_enemy, double vx_enemy,
  363. double vy_enemy, ObjectController enemyController, double gameTime) {
  364. GameObject tmp =
  365. new AnimatedGameobject(name, this, x_enemy, y_enemy, vx_enemy, vy_enemy, ENEMYSCALE,
  366. this.enemyAnim, gameTime, "loop").addController(enemyController).generateColliders();
  367. return tmp;
  368. }
  369. GameObject createSingleCollect(String name) {
  370. double gameTime = this.getGameTime();
  371. double cspeedy = 20.;
  372. double x_collect = Math.random() * this.canvasX;
  373. double y_collect = Math.random() * this.canvasY / 3;
  374. double vx_collect = 2 * (Math.random() - 0.5) * 0;
  375. double vy_collect = Math.random() * cspeedy;
  376. GameObject tmp = new AnimatedGameobject(name, this, x_collect, y_collect, vx_collect,
  377. vy_collect, 0.3, this.heartAnim, gameTime, "loop").generateColliders()
  378. .addController(new EnemyController());
  379. return tmp;
  380. }
  381. void createEnemies() {
  382. // create enemies
  383. double gameTime = this.getGameTime();
  384. double speedx = this.calcEnemySpeedX();
  385. double speedy = this.calcEnemySpeedY();
  386. for (int i = 0; i < this.calcNrEnemies(); i++) {
  387. double x_enemy = Math.random() * this.canvasX;
  388. double y_enemy = Math.random() * this.canvasY / 3;
  389. double vx_enemy = 2 * (Math.random() - 0.5) * speedx;
  390. double vy_enemy = Math.random() * speedy;
  391. ObjectController enemyController = createEnemyController();
  392. GameObject enemy = createSingleEnemy("enemy" + i, x_enemy, y_enemy, vx_enemy, vy_enemy,
  393. enemyController, gameTime);
  394. addObject(enemy);
  395. }
  396. }
  397. void createCollectables() {
  398. // create collectables
  399. for (int i = 0; i < this.calcNrCollect(); i++) {
  400. GameObject collect = createSingleCollect("collect" + i);
  401. addObject(collect);
  402. }
  403. }
  404. void createEgoObject() {
  405. // add ego to playground at lower bottom
  406. EgoController egoController =
  407. new CollisionAwareEgoController(SpaceInvadersLevel.EGORAD, this.laser);
  408. GameObject ego = new EgoObject("ego", this, canvasX / 2, canvasY - (EGORAD * 2), 0, 0, EGORAD)
  409. .addController(egoController).generateColliders();
  410. addObject(ego);
  411. }
  412. void createStars() {
  413. // add stars to playground
  414. for (int i = 1; i <= LEVEL2STARS; i++) {
  415. FallingStarController fsc = new FallingStarController();
  416. GameObject star = new FallingStar("star" + i, this, Math.random() * canvasX,
  417. Math.random() * 15, 0.0, Math.random() * STARSPEED, Color.WHITE, 1.).addController(fsc);
  418. addObject(star);
  419. }
  420. }
  421. void createExplosion(double gameTime, GameObject e, String basename, double interval,
  422. Color color) {
  423. // spawn a cloud of exploded shards
  424. for (int i = 0; i < NRSHARDS; i++) {
  425. double vx = 2 * (Math.random() - 0.5) * SHARDSPEED + e.getVX();
  426. double vy = 2 * (Math.random() - 0.5) * SHARDSPEED + e.getVY();
  427. addObject(
  428. new FallingStar("shard" + gameTime + "/" + i, this, e.getX(), e.getY(), vx, vy, color, 2)
  429. .addController(new LimitedTimeController(gameTime, interval)));
  430. }
  431. }
  432. /**
  433. * implements game behavior if an enemy object is hit by a players' shot. It creates an explosion
  434. * effect, plays a sound and adds 200 points to the current score (and it removes the enemy object
  435. * and the shot object).
  436. *
  437. * @param e enemy which was hit
  438. * @param shot the shot object that hit the enemy
  439. */
  440. void actionIfEnemyIsHit(GameObject e, GameObject shot) {
  441. double gameTime = this.getGameTime();
  442. createExplosion(gameTime, e, "shard", DYING_INTERVAL, Color.RED);
  443. Music.music(smash);
  444. // delete enemy
  445. deleteObject(e.getId());
  446. // delete shot
  447. deleteObject(shot.getId());
  448. // add to points counter
  449. Integer pts = (Integer) getGlobalFlag("points");
  450. setGlobalFlag("points", pts + 200);
  451. }
  452. /**
  453. * implements game behavior if ego object of player touches a collectable
  454. * {@link gameobjects.GameObject }. Here it adds one extra life and displays a text for a limited
  455. * time on screen "Extra life!!". The duration is set in constant
  456. * {@link SpaceInvadersLevel#EXPL_DURATION}.
  457. *
  458. *
  459. * @param collect item touched by ego
  460. * @param ego the players ego object
  461. */
  462. void actionIfEgoCollidesWithCollect(GameObject collect, GameObject ego) {
  463. double gameTime = this.getGameTime();
  464. if (this.getObject("bonustext") == null) {
  465. // spawn a bonus points object
  466. double vx = 2 * (Math.random() - 0.5) * SHARDSPEED + collect.getVX();
  467. double vy = 2 * (Math.random() - 0.5) * SHARDSPEED + collect.getVY();
  468. LimitedTimeController bonusTextController =
  469. new LimitedTimeController(gameTime, SpaceInvadersLevel.EXPL_DURATION);
  470. GameObject bonusText = new TextObject("bonustext", this, collect.getX(), collect.getY(), vx,
  471. vy, "Extra life!!", 20, Color.YELLOW).addController(bonusTextController);
  472. addObject(bonusText);
  473. // delete collect
  474. deleteObject(collect.getId());
  475. // add to points counter
  476. Integer lives = (Integer) getGlobalFlag("egoLives");
  477. lives++;
  478. setGlobalFlag("egoLives", lives);
  479. }
  480. }
  481. /**
  482. * implements behaviour of game when ego object is touching an enemy. It displays a text "AUAA!!"
  483. * for a certain time on the game screen. Time span is defined as constant in
  484. * {@link SpaceInvadersLevel#BONUS_DURATION } .
  485. *
  486. * @param enemy the enemy that was hit
  487. * @param ego the ego object of the player
  488. */
  489. void actionIfEgoCollidesWithEnemy(GameObject enemy, GameObject ego) {
  490. /**
  491. * set temporary text over ego. This object lives only for a short time. While it lives, further
  492. * collisions are ignored. Otherwise a single collision would result in a lot of deducted
  493. * points...
  494. */
  495. double gameTime = this.getGameTime();
  496. GameObject auaObj = this.getObject("AUA-EGO");
  497. if (auaObj == null) {
  498. addObject(new TextObject("AUA-EGO", this, ego.getX(), ego.getY() - 20, ego.getVX(),
  499. ego.getVY(), "AUAA!!", 10, Color.RED)
  500. .addController(new LimitedTimeController(gameTime, BONUS_DURATION)));
  501. // deduct points
  502. Integer pts = (Integer) getGlobalFlag("points");
  503. setGlobalFlag("points", pts - 500);
  504. }
  505. }
  506. /**
  507. * implements what happens if the eog object of player is hit by a shot. It removes the shot from
  508. * screen, reduces lives by 1, removes the ego and end current playing.
  509. *
  510. * @param eshot the shot object that hits the ego
  511. * @param ego the ego object of the player
  512. */
  513. void actionIfEgoObjectIsHit(GameObject eshot, GameObject ego) {
  514. logger.debug("collision of " + eshot.getId() + " and " + ego.getId());
  515. double gameTime = this.getGameTime();
  516. this.deleteObject(eshot.getId());
  517. Integer oldLives = (Integer) getGlobalFlag("egoLives");
  518. int newLives = oldLives - 1;
  519. setGlobalFlag("egoLives", newLives);
  520. logger.debug("set egoLives to " + newLives + " (was " + oldLives + ")");
  521. if (newLives <= 0) {
  522. lost = true;
  523. HighscoreManager.writeHSToFile((Integer) Playground.getGlobalFlag("points"),
  524. (Integer) Playground.getGlobalFlag("highscore"));
  525. }
  526. LinkedList<GameObject> eshots = collectObjects("enmyShot", true);
  527. for (GameObject _eshot : eshots) {
  528. deleteObject(_eshot.getId());
  529. }
  530. setLevelFlag("detailedStatus", "dying");
  531. setLevelFlag("t0", gameTime);
  532. ego.setActive(false);
  533. createExplosion(gameTime, ego, "egoexp", DYING_INTERVAL, Color.WHITE);
  534. LinkedList<GameObject> enemies = collectObjects("enemy", false);
  535. for (GameObject enemy : enemies) {
  536. enemy.setY(0);
  537. enemy.setActive(false);
  538. }
  539. }
  540. }