Browse Source

copied W11 from SoSe2021 as starting point, without any of the solutions

master
jkonert 3 years ago
parent
commit
15a635fed4
  1. 5
      .gitignore
  2. 10
      GameProject/.classpath
  3. 17
      GameProject/.project
  4. 15
      GameProject/.settings/org.eclipse.jdt.core.prefs
  5. BIN
      GameProject/audio/laser.wav
  6. BIN
      GameProject/audio/shot.wav
  7. BIN
      GameProject/audio/smash.wav
  8. 202
      GameProject/lib/apache-log4j-2.13.3-bin/LICENSE.txt
  9. 17
      GameProject/lib/apache-log4j-2.13.3-bin/NOTICE.txt
  10. 60
      GameProject/lib/apache-log4j-2.13.3-bin/RELEASE-NOTES.md
  11. BIN
      GameProject/lib/apache-log4j-2.13.3-bin/log4j-1.2-api-2.13.3-javadoc.jar
  12. BIN
      GameProject/lib/apache-log4j-2.13.3-bin/log4j-1.2-api-2.13.3-sources.jar
  13. BIN
      GameProject/lib/apache-log4j-2.13.3-bin/log4j-1.2-api-2.13.3.jar
  14. BIN
      GameProject/lib/apache-log4j-2.13.3-bin/log4j-api-2.13.3-javadoc.jar
  15. BIN
      GameProject/lib/apache-log4j-2.13.3-bin/log4j-api-2.13.3-sources.jar
  16. BIN
      GameProject/lib/apache-log4j-2.13.3-bin/log4j-api-2.13.3.jar
  17. BIN
      GameProject/lib/apache-log4j-2.13.3-bin/log4j-core-2.13.3-javadoc.jar
  18. BIN
      GameProject/lib/apache-log4j-2.13.3-bin/log4j-core-2.13.3-sources.jar
  19. BIN
      GameProject/lib/apache-log4j-2.13.3-bin/log4j-core-2.13.3-tests.jar
  20. BIN
      GameProject/lib/apache-log4j-2.13.3-bin/log4j-core-2.13.3.jar
  21. 34
      GameProject/src/base/BreakoutGame.java
  22. 42
      GameProject/src/base/BuggyGame.java
  23. 56
      GameProject/src/base/BuggyGameTest.java
  24. 201
      GameProject/src/base/GameLoop.java
  25. 41
      GameProject/src/base/MovingObjectsGame.java
  26. 88
      GameProject/src/collider/CircleCollider.java
  27. 127
      GameProject/src/collider/Collider.java
  28. 127
      GameProject/src/collider/RectCollider.java
  29. 84
      GameProject/src/controller/CollisionAwareEgoController.java
  30. 216
      GameProject/src/controller/EgoController.java
  31. 36
      GameProject/src/controller/EnemyController.java
  32. 24
      GameProject/src/controller/FallingStarController.java
  33. 36
      GameProject/src/controller/LimitedTimeController.java
  34. 47
      GameProject/src/controller/MineController.java
  35. 110
      GameProject/src/controller/ObjectController.java
  36. 16
      GameProject/src/controller/SimpleShotController.java
  37. 32
      GameProject/src/controller/ZickZackController.java
  38. 11
      GameProject/src/controller/package-info.java
  39. 42
      GameProject/src/gameobjects/AnimatedGameobject.java
  40. 34
      GameProject/src/gameobjects/EgoObject.java
  41. 28
      GameProject/src/gameobjects/FallingStar.java
  42. 377
      GameProject/src/gameobjects/GameObject.java
  43. 76
      GameProject/src/gameobjects/RectObject.java
  44. 70
      GameProject/src/gameobjects/TextObject.java
  45. 30
      GameProject/src/log4j2.xml
  46. 84
      GameProject/src/playground/Animation.java
  47. 55
      GameProject/src/playground/HighscoreManager.java
  48. 73
      GameProject/src/playground/HitTwiceLevel.java
  49. 15
      GameProject/src/playground/Level1.java
  50. 17
      GameProject/src/playground/Level2.java
  51. 40
      GameProject/src/playground/Level3.java
  52. 63
      GameProject/src/playground/Level4.java
  53. 112
      GameProject/src/playground/LevelBoss.java
  54. 123
      GameProject/src/playground/LevelBreakout0.java
  55. 180
      GameProject/src/playground/LevelBreakoutBase.java
  56. 98
      GameProject/src/playground/LevelBreakoutBaseAdvanced.java
  57. 20
      GameProject/src/playground/LevelMovingHitObjects.java
  58. 20
      GameProject/src/playground/LevelMovingObjects.java
  59. 48
      GameProject/src/playground/Music.java
  60. 374
      GameProject/src/playground/Playground.java
  61. 62
      GameProject/src/playground/SaveGame.java
  62. 678
      GameProject/src/playground/SpaceInvadersLevel.java
  63. 82
      GameProject/src/playground/SpaceInvadersLevelTest.java
  64. 93
      GameProject/src/rendering/AnimationArtist.java
  65. 40
      GameProject/src/rendering/Artist.java
  66. 34
      GameProject/src/rendering/CircleArtist.java
  67. 33
      GameProject/src/rendering/RectArtist.java
  68. 81
      GameProject/src/rendering/TextArtist.java
  69. 54
      GameProject/src/ui/AboutFrame.java
  70. 145
      GameProject/src/ui/GamePanel.java
  71. 243
      GameProject/src/ui/GameUI.java
  72. BIN
      GameProject/video/alexG.jpg
  73. BIN
      GameProject/video/alien (Custom).png
  74. BIN
      GameProject/video/alien.png
  75. 1
      GameProject/video/alien.txt
  76. BIN
      GameProject/video/alien2.png
  77. 3
      GameProject/video/bored.txt
  78. BIN
      GameProject/video/boredAlien0.png
  79. BIN
      GameProject/video/boredAlien1.png
  80. BIN
      GameProject/video/boredAlien2.png
  81. 2
      GameProject/video/heart.txt
  82. BIN
      GameProject/video/heart0.png
  83. BIN
      GameProject/video/heart1.png
  84. BIN
      GameProject/video/player.png
  85. 1
      GameProject/video/player.txt
  86. 31
      GameProject/video/relaunch.txt
  87. 8
      GameProject/video/sweetAlien.txt
  88. BIN
      GameProject/video/sweetAlien0.png
  89. BIN
      GameProject/video/sweetAlien1.png
  90. BIN
      GameProject/video/sweetAlien2.png
  91. BIN
      GameProject/video/sweetAlien3.png
  92. BIN
      GameProject/video/sweetAlien4.png
  93. BIN
      GameProject/video/sweetAlien5.png
  94. BIN
      GameProject/video/sweetAlien6.png
  95. BIN
      GameProject/video/sweetAlien7.png
  96. 1
      GameProject/video/test.txt
  97. 6
      gui/.classpath
  98. 1
      gui/.gitignore
  99. 17
      gui/.project
  100. 14
      gui/.settings/org.eclipse.jdt.core.prefs

5
.gitignore

@ -1 +1,6 @@
/.metadata/ /.metadata/
*.class
/GameProject/doc/
/GameProject/log/
/GameProject/highscore.txt
/GameProject/bin/

10
GameProject/.classpath

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
<classpathentry kind="lib" path="lib/apache-log4j-2.13.3-bin/log4j-1.2-api-2.13.3.jar"/>
<classpathentry kind="lib" path="lib/apache-log4j-2.13.3-bin/log4j-api-2.13.3.jar"/>
<classpathentry kind="lib" path="lib/apache-log4j-2.13.3-bin/log4j-core-2.13.3.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

17
GameProject/.project

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>GameProject</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

15
GameProject/.settings/org.eclipse.jdt.core.prefs

@ -0,0 +1,15 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=12
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=12
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=12

BIN
GameProject/audio/laser.wav

BIN
GameProject/audio/shot.wav

BIN
GameProject/audio/smash.wav

202
GameProject/lib/apache-log4j-2.13.3-bin/LICENSE.txt

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 1999-2005 The Apache Software Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

17
GameProject/lib/apache-log4j-2.13.3-bin/NOTICE.txt

@ -0,0 +1,17 @@
Apache Log4j
Copyright 1999-2017 Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
ResolverUtil.java
Copyright 2005-2006 Tim Fennell
Dumbster SMTP test server
Copyright 2004 Jason Paul Kitchen
TypeUtil.java
Copyright 2002-2012 Ramnivas Laddad, Juergen Hoeller, Chris Beams
picocli (http://picocli.info)
Copyright 2017 Remko Popma

60
GameProject/lib/apache-log4j-2.13.3-bin/RELEASE-NOTES.md

@ -0,0 +1,60 @@
<!---
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
# Apache Log4j 2.13.3 Release Notes
The Apache Log4j 2 team is pleased to announce the Log4j 2.13.3 release!
Apache Log4j is a well known framework for logging application behavior. Log4j 2 is an upgrade
to Log4j that provides significant improvements over its predecessor, Log4j 1.x, and provides
many other modern features such as support for Markers, lambda expressions for lazy logging,
property substitution using Lookups, multiple patterns on a PatternLayout and asynchronous
Loggers. Another notable Log4j 2 feature is the ability to be "garbage-free" (avoid allocating
temporary objects) while logging. In addition, Log4j 2 will not lose events while reconfiguring.
The artifacts may be downloaded from https://logging.apache.org/log4j/2.x/download.html.
This release contains a fix for bug LOG4J2-2838.
Due to a break in compatibility in the SLF4J binding, Log4j now ships with two versions of the SLF4J to Log4j adapters.
log4j-slf4j-impl should be used with SLF4J 1.7.x and earlier and log4j-slf4j18-impl should be used with SLF4J 1.8.x and
later.
Note that the default XML, JSON and YAML formats changed in the 2.11.0 release: they no longer have the "timeMillis"
attribute and instead have an "Instant" element with "epochSecond" and "nanoOfSecond" attributes. If the previous
behavior is desired the "includeTimeMillis" attribute may be set to true on each of the respective Layouts.
The Log4j 2.13.3 API, as well as many core components, maintains binary compatibility with previous releases.
## GA Release 2.13.3
Changes in this version include:
### Fixed Bugs
* [LOG4J2-2838](https://issues.apache.org/jira/browse/LOG4J2-2838):
Fix NullPointerException in ThreadContextDataInjector.
---
Apache Log4j 2.13.3 requires a minimum of Java 8 to build and run. Log4j 2.3 was the
last release that supported Java 6 and Log4j 2.12.1 is the last release to support Java 7.
For complete information on Apache Log4j 2, including instructions on how to submit bug
reports, patches, or suggestions for improvement, see the Apache Apache Log4j 2 website:
https://logging.apache.org/log4j/2.x/

BIN
GameProject/lib/apache-log4j-2.13.3-bin/log4j-1.2-api-2.13.3-javadoc.jar

BIN
GameProject/lib/apache-log4j-2.13.3-bin/log4j-1.2-api-2.13.3-sources.jar

BIN
GameProject/lib/apache-log4j-2.13.3-bin/log4j-1.2-api-2.13.3.jar

BIN
GameProject/lib/apache-log4j-2.13.3-bin/log4j-api-2.13.3-javadoc.jar

BIN
GameProject/lib/apache-log4j-2.13.3-bin/log4j-api-2.13.3-sources.jar

BIN
GameProject/lib/apache-log4j-2.13.3-bin/log4j-api-2.13.3.jar

BIN
GameProject/lib/apache-log4j-2.13.3-bin/log4j-core-2.13.3-javadoc.jar

BIN
GameProject/lib/apache-log4j-2.13.3-bin/log4j-core-2.13.3-sources.jar

BIN
GameProject/lib/apache-log4j-2.13.3-bin/log4j-core-2.13.3-tests.jar

BIN
GameProject/lib/apache-log4j-2.13.3-bin/log4j-core-2.13.3.jar

34
GameProject/src/base/BreakoutGame.java

@ -0,0 +1,34 @@
package base;
import java.io.IOException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* main class to start a game with only one level.
*
*/
public class BreakoutGame extends GameLoop {
private static Logger logger = LogManager.getLogger(BreakoutGame.class);
public BreakoutGame() {
this.levels.clear(); // removes Level1 added by superclass constructor
// this.levels.add(new LevelBreakout1()); // FIXME add this when your level exists
}
/**
* starts this game.
*
* @param args command line parameters (forwarded to {@link GameLoop#runGame(String[])}).
* @throws IOException if highscore.txt file cannot be written or accessed, the exception is
* thrown (and game ends).
*/
public static void main(String[] args) throws IOException {
GameLoop myGame = new BreakoutGame();
logger.info("BreakoutGame program started.");
myGame.runGame(args);
}
}

42
GameProject/src/base/BuggyGame.java

@ -0,0 +1,42 @@
package base;
import java.io.IOException;
import java.util.ArrayList;
import playground.Level1;
import playground.Level2;
import playground.Level3;
import playground.Level4;
import playground.Playground;
/**
* main class to start a game with four levels {@link playground.Level1} {@link playground.Level2}
* {@link playground.Level3} and {@link playground.Level4}.
*
*/
public class BuggyGame extends GameLoop {
/** constructor that defines {@link GameLoop#levels} with 4 levels (instances of {@link playground.Level1}
* {@link playground.Level2} {@link playground.Level3} and {@link playground.Level4}.
*/
public BuggyGame() {
super();
this.levels = new ArrayList<Playground>(4);
this.levels.add(new Level1());
this.levels.add(new Level2());
this.levels.add(new Level3());
this.levels.add(new Level4());
}
/**
* starts this game.
*
* @param args command line parameters (forwarded to {@link GameLoop#runGame(String[])}).
* @throws IOException if highscore.txt file cannot be written or accessed, the exception is
* thrown (and game ends).
*/
public static void main(String[] args) throws IOException {
GameLoop myGame = new BuggyGame();
myGame.runGame(args);
}
}

56
GameProject/src/base/BuggyGameTest.java

@ -0,0 +1,56 @@
package base;
import static org.junit.Assert.assertTrue;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import playground.Level1;
import playground.Level2;
import playground.Level3;
import playground.Level4;
/**
* Tests {@link BuggyGame} for
* <ol>
* <li>loading correctly 4 levels {@link playground.Level1} {@link playground.Level2}
* {@link playground.Level3} and {@link playground.Level4}
* </ol>
*
*
*/
class BuggyGameTest {
BuggyGame myGame;
@BeforeEach
void setUp() throws Exception {
myGame = new BuggyGame();
}
@AfterEach
void tearDown() throws Exception {
// nothing to do afterwards
}
@Test
void testNumberOfLevelsIsFour() {
assertTrue("levels Array has two entries", myGame.levels != null && myGame.levels.size() == 4);
}
@Test
void testCorrectFourLevelsOrder() {
assertTrue("levels Array has four entries", myGame.levels != null && myGame.levels.size() == 4);
assertTrue("first level is Level1",
myGame.levels.get(0).getClass().equals(new Level1().getClass()));
assertTrue("second level is Level2",
myGame.levels.get(1).getClass().equals(new Level2().getClass()));
assertTrue("third level is Level3",
myGame.levels.get(2).getClass().equals(new Level3().getClass()));
assertTrue("fourth (last) level is Level4",
myGame.levels.get(3).getClass().equals(new Level4().getClass()));
}
}

201
GameProject/src/base/GameLoop.java

@ -0,0 +1,201 @@
package base;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import gameobjects.GameObject;
import playground.Level1;
import playground.Playground;
import ui.GameUI;
/**
* Main class starting any game, contains main(). Apart from that, this class manages all
* non-logical functionalities which should be hidden from a game designer like:
* <ul>
* <li>Setting up windows, panels, buttons, action callbacks, ...
* <li>Reading keyboard inputs
* <li>Redrawing game window if necessary
* <li>managing the game time and calling the appropriate {@link GameObject} or {@link Playground}
* methods periodically, at every time step of the game.
* </ul>
* There will normally never be a need to modify this file, a designer/game programmer should always
* redefine the {@link GameObject} and {@link Playground} classes and implement new functionality
* there. To make a long story short<br>
*/
public class GameLoop {
/** Pixel width of Game GUI ( above 0) */
public static int SIZEX = 300;
/** Pixel height of Game GUI (above 0) */
public static int SIZEY = 200;
/**
* before call to {@link #runGame(String[])} this List should be initialized (in constructor).
*/
protected List<Playground> levels = null;
private static Logger logger = LogManager.getLogger(GameLoop.class);
/** constructor which initializes the {@link #levels} ArrayList of Playground instances (levels) to be played. */
public GameLoop() {
this.levels = new ArrayList<Playground>(1);
this.levels.add( new Level1());
}
/**
* loops over all {@link #levels} and implements the game loop to update continuously the level
* during play time
*
* @param args command line arguments forwarded (currently ignored)
* @throws IOException if hitghscore.txt cannot be written.
*/
public void runGame(String[] args) throws IOException {
logger.info("GUI starts");
GameUI gameUI = new GameUI(SIZEX, SIZEY); // probably change to your new GUI class
double gameTime = -1;
Playground currentPlayground = null;
// loop over different levels
ListIterator<Playground> levelIterator = levels.listIterator();
while (true) {
logger.debug("LevelIndex is " + (levelIterator.nextIndex()) + " (of " + levels.size() + " levels)");
gameTime = 0;
long start = System.nanoTime();
// loop over single level
while (true) {
int act = GameUI.getNewAction();
// Query GameUI for high-level user commands; new game/reset/etc...
if (act == GameUI.ACTION_RESET) {
// ReStart Game in same Level
logger.info("GUI RESET");
currentPlayground.prepareLevel("level" + (levelIterator.nextIndex()-1));
GameUI.resetAction();
}
if (act == GameUI.ACTION_NEW) {
// new game
logger.info("GUI NEW");
start = System.nanoTime();
levelIterator = levels.listIterator(); // reset
currentPlayground = levelIterator.next(); // again level
currentPlayground.prepareLevel("level" + (levelIterator.nextIndex()-1));
gameUI.setPlayground(currentPlayground);
GameUI.resetAction();
break;
}
if (act == GameUI.ACTION_BUTTON) {
// Event of Button pressed --> PAUSE!
logger.info("GUI PAUSE");
if (currentPlayground != null) {
boolean p = currentPlayground.isPaused();
p = !p;
currentPlayground.setPaused(p);
}
GameUI.resetAction();
}
if (act == GameUI.ACTION_SAVE) {
logger.info("GUI SAVE");
// UNDONE save current state (not yet working/implemented)
GameUI.resetAction();
}
if (act == GameUI.ACTION_LOAD) {
logger.info("GUI LOAD");
// load game state (currently simply resets)
GameUI.resetAction();
}
// if game has been created: execute a single iteration of the game loop
if (currentPlayground != null) {
// calc time that was used for painting the game, in seconds since last loop
long end = System.nanoTime();
double realTS = ((double) (end - start) / 1000000000.);
// time calc for one loop of the while
start = System.nanoTime();
if (currentPlayground.levelFinished() || currentPlayground.gameOver() == true) {
break; // leave level; breaks WHILE
}
// paint current state of level and start time measurement
gameUI.waitWhilePainting();
gameUI.grabFocus(); // needed to grab input events in next step
// communicate inputs to level
currentPlayground.processKeyEvents(gameUI.getKeyEvents());
currentPlayground.processMouseEvents(gameUI.getMouseEvents());
if (currentPlayground.isPaused() == false) {
// update objects and level
currentPlayground.updateObjects();
currentPlayground.applyGameLogic();
// update game time
gameTime += realTS;
// communicate gameTime and timestep to level
currentPlayground.setTimestep(realTS);
currentPlayground.setGameTime(gameTime);
Playground.setGlobalFlag("gameTime", Double.valueOf(realTS));
logger.trace("gameTime is now "+gameTime);
} // if
} // if
} // inner while loop within level
// after level is done: leave outer loop if game over
if (currentPlayground.gameOver() == true) {
break; // outer while ends game
}
// after level is done: reset level and go to next, if there is one
if (currentPlayground.levelFinished() == true) {
currentPlayground.reset();
// increase level counter, go on to next one
logger.debug("level finished. now new LevelIndex is " + levelIterator.nextIndex());
if (levelIterator.nextIndex() >= levels.size()) {
logger.info("reached end of levels");
break; // outer while ends game;
}
currentPlayground = levelIterator.next();
currentPlayground.prepareLevel("level" + (levelIterator.nextIndex()-1));
}
} // outer loop over levels
logger.info("Game ends. Bye.");
System.exit(0);
} // main()
/**
* main to start the whole application
*
* @param args Java default command line args, forwarded to {@link #runGame(String[])}
* @throws IOException in case highscore.txt cannot be written.
*/
public static void main(String[] args) throws IOException {
GameLoop gl = new GameLoop();
gl.runGame(args);
}
}

41
GameProject/src/base/MovingObjectsGame.java

@ -0,0 +1,41 @@
package base;
import java.io.IOException;
import java.util.ArrayList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import playground.LevelMovingHitObjects;
import playground.Playground;
/**
* main class to start a game with only one level {@link playground.LevelMovingHitObjects}.
*
*/
public class MovingObjectsGame extends GameLoop {
private static Logger logger = LogManager.getLogger(MovingObjectsGame.class);
/**
* constructor defines {@link GameLoop#levels} with one instance of
* {@link playground.LevelMovingHitObjects}.
*/
MovingObjectsGame() {
super();
this.levels = new ArrayList<Playground>(1);
this.levels.add(new LevelMovingHitObjects());
}
/**
* starts this game.
*
* @param args command line parameters (forwarded to {@link GameLoop#runGame(String[])}).
* @throws IOException if highscore.txt file cannot be written or accessed, the exception is
* thrown (and game ends).
*/
public static void main(String[] args) throws IOException {
logger.info("Starting Game Program...let's play and don't get hit!");
GameLoop myGame = new MovingObjectsGame();
myGame.runGame(args);
}
}

88
GameProject/src/collider/CircleCollider.java

@ -0,0 +1,88 @@
package collider;
import java.awt.Color;
import gameobjects.*;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
/** Collider for round objects */
public class CircleCollider extends Collider {
double x;
double y;
double vx;
double vy;
double r;
private static Logger logger = LogManager.getLogger(Collider.class);
/**
* Constructor which sets the radius to be respected for collisions.
*
* @param id String unique name for the collider instance
* @param o GameObject it belongs to
* @param radius radius in pixels to use as a size
*/
public CircleCollider(String id, GameObject o, double radius) {
super(id, o);
this.r = radius;
}
/** simple concatenation of all attributes (x,y,r) */
public String toString() {
return "circ:" + x + " " + y + "/" + r + " ";
}
/**
* calculates the collission of this with other collider
*
* @param _c2 the other collider
* @return true if a collision was detected
* @throws Exception in case the math operations are invalid (due to illegal values of x y or
* radius)
*/
public boolean checkCollisionCircCirc(Collider _c2) throws Exception {
CircleCollider c2 = (CircleCollider) _c2;
CircleCollider c1 = this;
logger.trace(c1.x + " " + c1.y + " " + c1.r + " " + c2.x + " " + c2.y+ " " + c2.r);
int kathete1 = (int) (Math.abs(c2.gameobject.getX() - c1.gameobject.getX()));
int kathete2 = (int) (Math.abs(c2.gameobject.getX() - c1.gameobject.getY()));
int hypothenuse = (int) (c1.r + c2.r);
logger.trace(kathete1 + " " + kathete2 + " " + hypothenuse + " ");
if (((kathete1 ^ 2) + (kathete2 ^ 2)) <= (hypothenuse ^ 2)) {
logger.trace("Collision");
return true;
}
return false;
}
@Override
public boolean collidesWith(Collider other) {
// circ circ
try {
return checkCollisionCircCirc(other);
} catch (Exception e) {
}
try {
return other.collidesWith(this);
} catch (Exception e) {
}
throw new RuntimeException("Collider type not implemented!");
}
private Color color = Color.WHITE;
}

127
GameProject/src/collider/Collider.java

@ -0,0 +1,127 @@
package collider;
import java.awt.Graphics2D;
import java.util.LinkedList;
import gameobjects.GameObject;
import playground.Playground;
import controller.ObjectController;
/**
* abstract base class for all Colliders to detect collisions between GameObjects
*
*
*/
public abstract class Collider {
/** unique internal name for Collider */
public String id = null;
/** GameObject it belongs to */
protected GameObject gameobject = null;
/** PlayGround instance it belongs to */
protected Playground playground = null;
/** the ObjectController to the corresponding GameObject (can be null) */
protected ObjectController controller = null;
protected double dx = 0.;
double dy = 0.;
/**
*
* @param id unique name for Collider (internally)
* @param o GameObject instance it belongs to
*/
public Collider(String id, GameObject o) {
this.gameobject = o;
this.id = id;
this.controller = o.getObjectController();
this.playground = o.getPlayground();
}
/**
* setter for offset values to be used relative to GameObject center. default is zero.
*
* @param dx offset in X direction (default 0)
* @param dy offset in Y direction (default 0)
* @return this instance of Collider
*/
public Collider setOffsets(double dx, double dy) {
this.dx = dx;
this.dy = dy;
return this;
}
public String toString() {
return "baseColl";
}
/**
* returns the corresponding game objects X coordinate (center) plus this colliders offset in X
* (probably zero).
*
* @return X value
*/
public double getX() {
return this.gameobject.getX() + this.dx;
}
/**
* returns the corresponding game objects Y coordinate (center) plus this colliders offset in Y
* (probably zero).
*
* @return Y value
*/
public double getY() {
return this.gameobject.getY() + this.dy;
}
/**
* returns the internal unique name
*
* @return the String with the name
*/
public String getId() {
return id;
}
/**
* setter for corresponding GameObject
*
* @param gameObject to be saved in attribute
*/
public void setObject(GameObject gameObject) {
this.gameobject = gameObject;
}
/**
* setter for GameController
*
* @param controller to be saved in attribute
*/
public void setController(ObjectController controller) {
this.controller = controller;
}
/**
* setter for Playground instance this collider belongs to
*
* @param playground instance to be stored in attribute
*/
public void setPlayground(Playground playground) {
this.playground = playground;
}
/**
* checks the collission with another collider instance.
*
* @param other the instance to compare to
* @return true if the colliders collide (touch or overlap)
*/
abstract public boolean collidesWith(Collider other);
}

127
GameProject/src/collider/RectCollider.java

@ -0,0 +1,127 @@
package collider;
import java.awt.Color;
import gameobjects.*;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
/** a {@link Collider} for Rectangles, usually used for {@link RectObject} instances.
* @see gameobjects.RectObject#generateColliders()
*/
public class RectCollider extends Collider {
//double x;
//double y;
//double vx;
//double vy;
double w, h;
private Color color = Color.WHITE;
private static Logger logger = LogManager.getLogger(RectCollider.class);
/**
* initializes this RectCollider.
* calls superclass constructor of {@link Collider#Collider(String, GameObject)} with params String id and GameObject o.
*
* @param id String unique name for this RectCollider
* @param o GameObject instance this RectCollider belongs to (cannot be null)
* @param w width in pixels for the collider dimensions (> 0)
* @param h height in pixels for the collider dimensions (>0)
*/
public RectCollider(String id, GameObject o, double w, double h) {
super(id, o);
this.w = w;
this.h = h;
}
public String toString() {
return " " + w + " " + h + " ";
}
/**
* checks collision with other Collider, which needs to be a RectCollider, too.
* @param other RectCollider (is casted) to calculate collision with
* @return true if collission is detected
*/
public boolean checkCollisionRectRect(Collider other) {
RectCollider r1 = this;
RectCollider r2 = (RectCollider) other;
if ((((r1.getX() + r1.w / 2.) >= (r2.getX() - r2.w / 2.)) && ((r1.getX() + r1.w / 2.) <= (r2
.getX() + r2.w / 2.)))
|| (((r2.getX() + r2.w / 2.) >= (r1.getX() - r1.w / 2.)) && ((r2.getX() + r2.w / 2.) <= (r1
.getX() + r1.w / 2.)))) {
if ((((r1.getY() + r1.h / 2.) >= (r2.getY() - r2.h / 2.)) && ((r1.getY() + r1.h / 2.) <= (r2
.getY() + r2.h / 2.)))
|| (((r2.getY() + r2.h / 2.) >= (r1.getY() - r1.h / 2.)) && ((r2.getY() + r2.h / 2.) <= (r1
.getY() + r1.h / 2.)))) {
return true;
}
}
return false;
}
/**
* checks collision with other Collider, which needs to be a CircleCollider
* @param other CircleCollider (is casted) to calculate collision with
* @return true if collission is detected
*/
public boolean checkCollisionRectCirc(Collider other) {
RectCollider r = this;
CircleCollider c = (CircleCollider) (other);
double circleDistX = Math.abs(c.getX() - (r.getX() ));
double circleDistY = Math.abs(c.getY() - (r.getY() ) );
logger.trace("c.x:"+c.x+" "+"c.y:"+c.y+" "+"c.r:"+c.r+" "+"r.x:"+r.getX()+" "+"r.y:"+r.getY()+" "+"r.w:"+r.w+" "+"r.h:"+r.h+" "+"circleDistX:"+circleDistX+" "+"circleDistY:"+circleDistY);
if (circleDistX > (r.w / 2 + c.r))
return false;
if (circleDistY > (r.h / 2 + c.r))
return false;
if (circleDistX <= (r.w / 2)) {
logger.trace("Collision Rect with circle");
return true;
}
if (circleDistY <= (r.h / 2)) {
logger.trace("Collision Rect with circle (second)");
return true;
}
double cornerDistSqr = Math.pow(circleDistX - r.w / 2, 2) + Math.pow(circleDistY - r.h / 2, 2); // Satz
// des
// Pythagoras
return (cornerDistSqr <= c.r * c.r); // falls true zurueckgegeben: Kollision
}
@Override
public boolean collidesWith(Collider other) {
// rect circ
try {
return checkCollisionRectCirc(other);
} catch (Exception e) {
// do nothing
}
// rect rect
try {
return checkCollisionRectRect(other);
} catch (Exception e) {
// do nothing
}
try {
return other.collidesWith(this);
} catch (Exception e) {
// do nothing
}
throw new RuntimeException("Collider type not implemented!");
}
}

84
GameProject/src/controller/CollisionAwareEgoController.java

@ -0,0 +1,84 @@
package controller;
import playground.*;
import gameobjects.*;
import java.util.*;
import java.awt.event.*;
import java.io.File;
/**
* An EgoController which cannot move through obstacle objects (is collission aware). Only respects
* GameObjects that have the String 'obstacle' in their name.
*
*/
public class CollisionAwareEgoController extends EgoController {
double savex, savey, savevx, savevy;
double lastSpaceAt = -1;
private File shot = null;
/**
*
* @param egoRad radius of ego object to be used.
*/
public CollisionAwareEgoController(double egoRad) {
super(egoRad);
}
/**
*
* @param egoRad radius of ego object to be used.
* @param soundOnShot WAV file to be played on shot
*/
public CollisionAwareEgoController(double egoRad, File soundOnShot) {
super(egoRad);
this.shot = soundOnShot;
}
public void saveDynamicState() {
this.savex = this.getX();
this.savey = this.getY();
this.savevx = this.getVX();
this.savevy = this.getVY();
}
public void restoreDynamicState() {
this.setX(savex);
this.setY(savey);
this.setVX(savevx);
this.setVY(savevy);
}
public boolean stopObject() {
boolean s = super.stopObject();
Playground pg = this.getPlayground();
LinkedList<GameObject> obstacles = pg.collectObjects("obstacle", false);
this.saveDynamicState();
this.applySpeedVector();
for (GameObject ob : obstacles) {
if (ob.collisionDetection(this.gameObject)) {
this.restoreDynamicState();
return true;
}
}
this.restoreDynamicState();
return s;
}
public void onSpace(KeyEvent e, GameObject ego) {
double cgt = ego.getGameTime();
if ((cgt - this.lastSpaceAt) > 0.1) {
super.onSpace(e, ego);
Music.music(this.shot);
}
}
}

216
GameProject/src/controller/EgoController.java

@ -0,0 +1,216 @@
package controller;
import java.awt.Color;
import java.awt.event.KeyEvent;
import playground.*;
import gameobjects.*;
import java.util.*;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
/**
* Controller using key events for up, down, left, right and space (shooting) to control the ego
* object behavior.
*/
public class EgoController extends ObjectController {
// either rad is zero or width/height is zero (guaranteed by constructors)
private double rad = 0;
private double width = 0;
private double height = 0;
private Integer pressedKey = null;
private Integer lastPressedKey = null;
private static Logger logger = LogManager.getLogger(EgoController.class);
/**
* constructor that gives the ego controller a radius to stop the ego object when it reaches the level boundaries.
* @param egoRad radius to use as a boundary stop for level borders (usually use the same dimensions as your ego object)
*/
public EgoController(double egoRad) {
this.rad = egoRad;
}
/**
* constructor that gives the ego controller a width and height to stop the ego object when it reaches the level boundaries.
* @param width width to use as a boundary stop for level borders (usually use the same dimensions as your ego object)
* @param height height to use as a boundary stop for level borders (usually use the same dimensions as your ego object)
*/
public EgoController(double width, double height) {
this.width = width;
this.height = height;
}
public void onUp(KeyEvent kc, GameObject ego) {
ego.setVX(0.0);
ego.setVY(-SpaceInvadersLevel.EGOSPEED);
}
public void onDown(KeyEvent kc, GameObject ego) {
ego.setVX(0.0);
ego.setVY(SpaceInvadersLevel.EGOSPEED);
}
public void onLeft(KeyEvent kc, GameObject ego) {
ego.setVY(0.0);
ego.setVX(-SpaceInvadersLevel.EGOSPEED);
}
public void onRight(KeyEvent kc, GameObject ego) {
ego.setVY(0.0);
ego.setVX(SpaceInvadersLevel.EGOSPEED);
}
public void onStop(KeyEvent kc, GameObject ego) {
ego.setVY(0.0);
ego.setVX(0.0);
ego.setComponentProperty("controller", "setDummy", "NEW");
ego.setComponentProperty("controller", "setDummy2", "XXX");
}
/** checks the position and respects level boundaries and own radius or width/height set on constructor.
*
* @return true if the object reached the boundaries of the level, false otherwise
*/
public boolean stopObject() {
// check whether ego object is at level boundaries
// can use radius (rad) and width or height in one check as either rad or width/height is zero.
int pgSizeX = this.getPlayground().getSizeX();
int pgSizeY = this.getPlayground().getSizeY();
double ts = this.getTimestep();
if (this.getX() + rad + (width/2d) + this.getVX() * ts >= pgSizeX
|| this.getX() - rad - (width/2d) + this.getVX() * ts < 0) {
return true;
}
if (this.getY() + rad + (height/2d) + this.getVY() * ts >= pgSizeY
|| this.getY() - rad - (height/2d) + this.getVY() * ts < 0) {
return true;
}
return false;
}
/** behavior for shooting on key space
*
* @param e KeyEvent of the space key
* @param ego EgoObject instance (used to determine position of shot object's start)
*/
public void onSpace(KeyEvent e, GameObject ego) {
pressedKey = lastPressedKey;
lastPressedKey = null;
// create unique name for object
// read Flag nextShot read (if not existing already it will be set)
// it will be updated by 1 and saved
Integer nextShot =
(Integer) this.getPlayground().getOrCreateLevelFlag("nextShot", Integer.valueOf(0));
String shotName = "simpleShot" + nextShot++;
this.getPlayground().setLevelFlag("nextShot", nextShot);
SimpleShotController simpleshot = new SimpleShotController();
GameObject ss = new RectObject(shotName, this.getPlayground(), ego.getX(), ego.getY(), 0,
-1. * SpaceInvadersLevel.SHOTSPEED, 4, 12, Color.CYAN).addController(simpleshot);
ss.generateColliders();
this.getPlayground().addObject(ss);
}
/**
* updates position based on key events (mouse currently ignored)
*/
public void updateObject() {
logger.trace("Playground inst is"+this.getPlayground()) ;
Stack<KeyEvent> keyEvents = this.getPlayground().getKeyEvents();
GameObject ego = this.gameObject;
while (!keyEvents.isEmpty()) {
KeyEvent e = keyEvents.pop();
boolean pressed = false;
boolean released = true;
int kc = e.getKeyCode();
if (e.paramString().indexOf("PRESSED") >= 0) {
pressed = true;
released = false;
}
/**
* Generelle Idee: Wenn eine Taste gedrückt wird wird sie gespeichert. wenn die zuvor
* gespeicherte Taste wieder losgelassen wird stoppt das Ego-Objekt. Falls vor dem Loslassen
* eine andere Taste gedrückt wird, wird diese gespeichert und die alte vergessen. Dh das
* loslassen der alten Taste stoppt das Objekt nicht. Spezialfall: space, das loslassen von
* space stoppt das Objekt nicht!
*/
if (pressed == true) {
lastPressedKey = pressedKey;
pressedKey = kc;
}
/**
* Nur eine losgelassene Taste die auch vorher gedrückt wurde stoppt das Objekt. Eine
* losgelassene Taste die nicht vorher gedrückt wurde bzw vergessen wurde stoppt das Objekt
* nicht
*/
if (released == true) {
if (pressedKey != null) {
if (pressedKey.equals(kc)) {
ego.setVX(0);
ego.setVY(0);
pressedKey = null;
}
}
continue;
}
if (kc == KeyEvent.VK_LEFT) {
this.onLeft(e, ego);
}
if (kc == KeyEvent.VK_RIGHT) {
this.onRight(e, ego);
}
if (kc == KeyEvent.VK_UP) {
this.onUp(e, ego);
}
if (kc == KeyEvent.VK_DOWN) {
this.onDown(e, ego);
}
// stop
if (kc == KeyEvent.VK_Z) {
this.onStop(e, ego);
}
// shot
if (kc == KeyEvent.VK_SPACE) {
// space is not registered! Releasing space does not stop the egoobject
this.onSpace(e, ego);
}
}
boolean stop = this.stopObject();
if (stop) {
this.setVX(0);
this.setVY(0);
}
// updateSpeed and position
applySpeedVector();
}
}

36
GameProject/src/controller/EnemyController.java

@ -0,0 +1,36 @@
package controller;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
/**
* This class controls the space invaders.
*/
public class EnemyController extends ObjectController {
private static Logger logger = LogManager.getLogger(EnemyController.class);
@Override
public void updateObject() {
logger.trace("updatre" + gameObject.getId());
if ((gameObject.getX() > this.getPlayground().getSizeX() * 0.9) && (gameObject.getVX() > 0)) {
logger.trace("toleft!" + gameObject.getX());
gameObject.setVX(-this.getVX());
}
if ((gameObject.getX() < this.getPlayground().getSizeX() * 0.1) && (gameObject.getVX() < 0)) {
logger.trace("toright!" + gameObject.getX());
gameObject.setVX(-this.getVX());
}
// if it reaches the bottom, delete it and deduct points
if (gameObject.getY() >= this.getPlayground().getSizeY()) {
this.getPlayground().deleteObject(gameObject.getId());
// add to points counter
Integer pts = (Integer) this.getPlayground().getGlobalFlag("points");
this.getPlayground().setGlobalFlag("points", pts - 200);
}
applySpeedVector();
}
}

24
GameProject/src/controller/FallingStarController.java

@ -0,0 +1,24 @@
package controller;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
/**
* Controls background stars. When they touch the bottom of the display they reappear on top.
*/
public class FallingStarController extends ObjectController {
int rad = 3;
private static Logger logger = LogManager.getLogger(FallingStarController.class);
@Override
public void updateObject() {
logger.trace(
"+" + this.gameObject.getId() + " HO " + this.gameObject + "/" + this.getPlayground());
if (this.getY() + rad >= this.getPlayground().getSizeY()) {
this.setY(10);
}
applySpeedVector();
}
}

36
GameProject/src/controller/LimitedTimeController.java

@ -0,0 +1,36 @@
package controller;
/**
* Controls and abject that is deleted after a lifetime specified in the constructor, and when it
* leaves the display.
*/
public class LimitedTimeController extends ObjectController {
int rad = 3;
double g0 = -1;
double duration = 0;
/**
* Constructor.
*
* @param g0 int initial game time at creation
* @param duration int duration in seconds
*/
public LimitedTimeController(double g0, double duration) {
this.g0 = g0;
this.duration = duration;
}
@Override
public void updateObject() {
double gameTime = this.getPlayground().getGameTime();
applySpeedVector();
if (gameObject.getY() >= getPlayground().getSizeY() || gameObject.getY() < 0
|| gameObject.getX() >= getPlayground().getSizeX() || gameObject.getX() < 0
|| (gameTime - g0) > duration) {
this.getPlayground().deleteObject(this.gameObject.getId());
}
}
}

47
GameProject/src/controller/MineController.java

@ -0,0 +1,47 @@
package controller;
import controller.ObjectController;
import gameobjects.GameObject;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
public class MineController extends ObjectController {
int rad = 3;
double xSpeed = 0.;
double lineSpeed = 0;
private static Logger logger = LogManager.getLogger(MineController.class);
public MineController(double lineSpeed) {
this.lineSpeed = lineSpeed;
}
@Override
public void updateObject() {
if (gameObject.getY() >= this.getPlayground().getSizeY() - 10) {
this.gameObject.setVY(0);
if (xSpeed == 0.) {
GameObject ego = getPlayground().getObject("ego");
double egoXPos = ego.getX();
if (egoXPos > this.gameObject.getX()) {
xSpeed = 50;
} else {
xSpeed = -50;
}
this.gameObject.setVX(xSpeed);
}
this.gameObject.setVX(xSpeed);
}
if (this.gameObject.getX() < 0 || (this.gameObject.getX() > this.getPlayground().getSizeX())) {
logger.debug("deleting" + this.gameObject.getId());
getPlayground().deleteObject(this.gameObject.getId());
}
applySpeedVector();
}
}

110
GameProject/src/controller/ObjectController.java

@ -0,0 +1,110 @@
package controller;
import gameobjects.GameObject;
import playground.Playground;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
/**
* Class that controls the LOGICAL behavior of an object independently of how it is displayed or
* drawn. The most important method here is {@link #updateObject}: this method is, by various
* indirections, called exactly once per game time step for every object that is on the playground.
* It has, by virtue of the member variables {@link #gameObject} full access to
* <ul>
* <li>the object it is controlling
* <li>the playground this object belongs to
* </ul>
* Typically, updateObject would check whether an object leaves the screen to react appropriately.
* In that case the object can be marked for deletion (by adding it to the flag "deleted" that is
* always defined for any playground), but of course other reactions are possible like rebounding,
* emerging on the other side, ...
*/
public abstract class ObjectController {
protected GameObject gameObject = null;
protected String dummy = "";
private static Logger logger = LogManager.getLogger(ObjectController.class);
public void setObject(GameObject gameObject) {
this.gameObject = gameObject;
}
public void setDummy(String x) {
logger.debug("DUMMY called!!");
this.dummy = x;
logger.debug("DUMMY is now:" + dummy);
}
/**
* Is called once every game time step by the game itself. NEVER call this directly, not
* necessary!<br>
* The method can do whatever it likes, including nothing. The attribute {@link #gameObject}
* contains a reference to the controlled object, which allows access to the Playground the object
* belongs to (useful for getting the pixel size in x and y of the playing field.<br>
* <strong>Recommended:</strong> when implementing this method, call at the end
* {@link #applySpeedVector() } method. This is a helper method that sets the new x,y coordinates
* for the {@link #gameObject} correctly.
*/
public abstract void updateObject();
/**
* Convenience method: simply moves the object forward one step from its present position, using
* its present speed.
*/
public void applySpeedVector() {
double ts = this.getPlayground().getTimestep();
this.setX(this.getX() + this.getVX() * ts);
gameObject.setY(this.getY() + this.getVY() * ts);
}
public double getTimestep() {
return this.gameObject.getPlayground().getTimestep();
}
public double getX() {
return this.gameObject.getX();
}
public double getY() {
return this.gameObject.getY();
}
public double getVX() {
return this.gameObject.getVX();
}
public double getVY() {
return this.gameObject.getVY();
}
public void setX(double x) {
this.gameObject.setX(x);
}
public void setY(double y) {
this.gameObject.setY(y);
}
public void setVX(double vx) {
this.gameObject.setVX(vx);
}
public void setVY(double vy) {
this.gameObject.setVY(vy);
}
public Playground getPlayground() {
return this.gameObject.getPlayground();
}
public void setPlayground(Playground playground) {
this.gameObject.setPlayground(playground);
}
}

16
GameProject/src/controller/SimpleShotController.java

@ -0,0 +1,16 @@
package controller;
public class SimpleShotController extends ObjectController {
int rad = 3;
@Override
public void updateObject() {
if (gameObject.getY() < 0) {
// LinkedList<String> deleteList = (LinkedList<String>) playground.getFlag("delete");
// deleteList.add(gameObject.getId());
getPlayground().deleteObject(this.gameObject.getId());
} else {
applySpeedVector();
}
}
}

32
GameProject/src/controller/ZickZackController.java

@ -0,0 +1,32 @@
package controller;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
public class ZickZackController extends EnemyController {
protected double g0;
protected double dt;
protected double lastMod = -1;
private static Logger logger = LogManager.getLogger(ZickZackController.class);
public ZickZackController(double gameTime, double dt) {
super();
this.dt = dt;
this.g0 = gameTime;
}
public void updateObject() {
double gameTime = this.getPlayground().getGameTime();
logger.trace("current Object x: "+gameObject.getX());
double mod = (gameTime - this.g0) % this.dt;
if (mod < lastMod) {
gameObject.setVX(-1.0 * gameObject.getVX());
logger.trace("inverting VX");
}
lastMod = mod;
super.updateObject();
}
}

11
GameProject/src/controller/package-info.java

@ -0,0 +1,11 @@
/**
* The controller package contains Object controllers that govern an objects behavior, without being
* involved in its collision or drawing properties. Object controllers react to user actions/inputs
* and govern for example the movement of {@link gameobjects.GameObject} instances. They can access
* their parent objects' properties and, by indirection, the properties and methods of the
* associated level. <br>
* Controllers should be designed in such a way that they implement behavior that is
* level-independent. All level-dependent behavior should be implemented in
* {@link playground.Playground#applyGameLogic}.
*/
package controller;

42
GameProject/src/gameobjects/AnimatedGameobject.java

@ -0,0 +1,42 @@
package gameobjects;
import java.util.LinkedList;
import playground.Playground;
import playground.Animation;
import collider.Collider;
import collider.RectCollider;
import rendering.*;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
public class AnimatedGameobject extends GameObject {
protected AnimationArtist animArtist;
private static Logger logger = LogManager.getLogger(AnimationArtist.class);
// auto-generates collider according to box width/height
public GameObject generateColliders() {
logger.debug("Created animated Obj "+ this.animArtist.getW()+" "+this.animArtist.getH()) ;
double w = this.animArtist.getW() ;
double h = this.animArtist.getH() ;
this.addCollider(new RectCollider("RectColl_" + this.id, this, w,
h)) ;
logger.info("ANIMGO-COLL ID="+this.getId()+" WH= "+w+"/"+h) ;
return this ;
}
public AnimatedGameobject(String id, Playground pg, double x, double y, double vx, double vy,
double scale, Animation anim, double t0, String abspielmodus) {
super(id, pg, x, y, vx, vy); // Konstruktor-Aufruf GameObject
this.artist = new AnimationArtist(this, anim, t0, abspielmodus, scale);
this.animArtist = (AnimationArtist) (this.artist);
}
}

34
GameProject/src/gameobjects/EgoObject.java

@ -0,0 +1,34 @@
package gameobjects;
import java.awt.Color;
import collider.*;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import javax.imageio.ImageIO;
import collider.Collider;
import controller.ObjectController;
import playground.Playground;
import rendering.*;
public class EgoObject extends GameObject {
double egoRad = 0;
public EgoObject(String id, Playground pg, double x, double y, double vx, double vy,
double egoRad) {
super(id, pg, x, y, vx, vy);
this.egoRad = egoRad;
this.artist = new CircleArtist(this, egoRad, Color.WHITE);
}
public GameObject generateColliders() {
CircleCollider coll = new CircleCollider("coll", this, this.egoRad);
this.addCollider(coll);
return this;
}
}

28
GameProject/src/gameobjects/FallingStar.java

@ -0,0 +1,28 @@
package gameobjects;
import java.awt.Color;
import java.util.LinkedList;
import collider.*;
import playground.Playground;
import rendering.*;
public class FallingStar extends GameObject {
private Color color = Color.WHITE;
protected double rad = -1;
public FallingStar(String id, Playground playground, double x, double y, double vx, double vy,
Color color, double rad) {
super(id, playground, x, y, vx, vy);
this.rad = rad;
this.color = color;
LinkedList<Collider> cols = new LinkedList<Collider>();
CircleCollider cc = new CircleCollider("cc", this, rad);
cols.add(cc);
setColliders(cols);
this.artist = new CircleArtist(this, rad, color);
}
}

377
GameProject/src/gameobjects/GameObject.java

@ -0,0 +1,377 @@
package gameobjects;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.lang.reflect.*;
import rendering.*;
import collider.Collider;
import controller.ObjectController;
import playground.Playground;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
/**
* The class {@link GameObject} represents a (possibly animated) object appearing in a level of the
* game. It is therefore attached to an instance of the class {@link Playground}. A GameObject has
* at least the following properties:
* <ul>
* <li>2D screen position
* <li>2D speed
* <li>a name that is unique within a certain {@link Playground}<br>
* <li>a reference to the {@link Playground} object it belongs to<br>
* <li>a reference to an instance of {@link controller.ObjectController} that handles the movement
* logic of the object<br>
* <li>a (circular) radius for simple collision checking. This may be handled differently in
* subclasses<br>
* </ul>
* The main task of GameObject, or its subclasses, is to draw the object on the screen, which is
* handles by the {@link #draw(Graphics2D)} method. It is this method that must be redefined if a
* new appearance should be realized. For introducing new behavior, it is sufficient to supply a
* different {@link controller.ObjectController} instance when constructing a GameObject.
*/
public abstract class GameObject {
public static final int RADIUS = 0;
public static final int RECTANGLE = 1;
public static final int MASK = 2;
protected Artist artist = null;
public String id = null;
protected double x = 0;
protected double vx = 0;
protected double y = 0;
protected double vy = 0;
protected BufferedImage mask = null; // UNDONE implement usage of mask
protected boolean active = true;
// public int collisionMode = GameObject.RADIUS;
protected Playground playground = null;
private ObjectController controller = null;
public LinkedList<Collider> scol;
private static Logger logger = LogManager.getLogger(GameObject.class);
/**
* Constructor to initialize a GameObject, respectively set the current {@link Playground}
* instance this GameObject belongs to.
*
* @param id unique ID for this GameObject (should not be null or empty String)
* @param playground the Playground the GameObject belongs to (should not be null)
* @param x initial screen position in direction horizontal (positive value including zero)
* @param y initial screen position in direction vertical (positive value including zero)
* @param vx initial speed (velocity) in direction horizontal (can be negative, zero, positive)
* @param vy initial speed (velocity) in direction horizontal (can be negative, zero, positive)
*/
public GameObject(String id, Playground playground, double x, double y, double vx, double vy) {
setX(x);
setY(y);
setVX(vx);
setVY(vy);
this.id = id;
this.controller = null;
this.scol = new LinkedList<Collider>();
this.setPlayground(playground);
}
/**
* Constructor to initialize a GameObject, respectively set the current Playground instance this
* GameObject belongs to.
*
* @param id unique ID for this GameObject (should not be null or empty String)
* @param playground the Playground the GameObject belongs to (should not be null)
* @param controller controller instance to be used for this GameObject (can be null)
* @param x initial screen position in direction horizontal (positive value including zero)
* @param y initial screen position in direction vertical (positive value including zero)
* @param vx initial speed (velocity) in direction horizontal (can be negative, zero, positive)
* @param vy initial speed (velocity) in direction horizontal (can be negative, zero, positive)
*/
public GameObject(String id, Playground playground, ObjectController controller, double x,
double y, double vx, double vy) {
this(id, playground, x, y, vx, vy);
this.controller = controller;
if (this.controller != null) {
this.controller.setObject(this);
this.controller.setPlayground(playground);
}
}
/**
* sets colliders.
*
* @param l LinkedList of Colliders.
*/
public void setColliders(LinkedList<Collider> l) {
this.scol = l;
}
/**
* generates and sets collider(s) for this GameObject. This implementation does nothing. Intended
* to be overridden by subclasses.
*
* @return instance of this GameObject (this).
*/
public GameObject generateColliders() {
return this;
}
/**
* Sets the controller to use for this GameObject's logical behavior.
*
* @param c instance to be used.
* @return the current instance (this).
*/
public GameObject addController(ObjectController c) {
this.controller = c;
this.controller.setObject(this);
this.controller.setPlayground(playground);
return this;
}
/**
* Sets the artist to be used for drawing the object onto visible canvas area.
*
* @param a instance to be used for calling {@link rendering.Artist#draw(Graphics2D)}.
* @return the current instance (this).
*/
public GameObject addArtist(Artist a) {
this.artist = a;
return this;
}
/**
* saves the collider in the internal list of Colliders to be used for this GameObject.
*
* @param c instance to be added to internal list
*/
public void addCollider(Collider c) {
if (this.scol == null) {
this.scol = new LinkedList<Collider>();
}
this.scol.add(c);
}
public Playground getPlayground() {
return playground;
}
public void setPlayground(Playground playground) {
this.playground = playground;
}
/**
* calls via reflection a method of a component if this GameObjects instance and provides the
* given value as String parameter.
*
* @param comp class name of GameObject component. Currently only "controller" is supported,
* otherwise nothing happens.
* @param property method name of the component to call.
* @param value argument to pass to the method as String parameter.
*/
public void setComponentProperty(String comp, String property, Object value) {
if (comp.equals("controller")) {
Class<? extends Object> clO = this.controller.getClass();
for (Method m : clO.getMethods()) {
if (m.getName().indexOf(property) != -1) {
logger.debug("Method " + property + " found!!");
try {
m.invoke(this.getObjectController(), value);
} catch (Exception e) {
}
}
}
}
}
public void setObjectFlag(String flag, Object value) {
this.playground.setLevelFlag(this.id + "/" + flag, value);
}
public Object getObjectFlag(String flag) {
return this.playground.getLevelFlag(this.id + "/" + flag);
}
public Object getOrCreateObjectFlag(String flag, Object createValue) {
return this.playground.getOrCreateLevelFlag(this.id + "/" + flag, createValue);
}
public boolean isActive() {
return active;
}
public GameObject setActive(boolean flag) {
this.active = flag;
return this;
}
/**
* return the unique object ID.
*
* @return unique object ID
*/
public String getId() {
return id;
}
/**
* gets the screen X position.
*
* @return screen x position
*/
public double getX() {
return x;
}
/**
* gets the screen Y position.
*
* @return screen Y position
*/
public double getY() {
return y;
}
/**
* gets the screen X speed in pixels per frame.
*
* @return screen x speed
*/
public double getVX() {
return vx;
}
/**
* gets the screen Y speed in pixels per frame.
*
* @return screen y speed
*/
public double getVY() {
return vy;
}
/**
* set screen x position.
*
* @param x new position
*/
public void setX(double x) {
if (this.active == true) {
this.x = x;
}
}
/**
* set screen y position.
*
* @param y new position
*/
public void setY(double y) {
if (this.active == true) {
this.y = y;
}
}
/**
* set screen x speed in pixel per frame
*
* @param vx new x speed
*/
public void setVX(double vx) {
if (this.active == true) {
this.vx = vx;
}
}
/**
* set screen y speed in pixel per frame.
*
* @param vy new y speed.
*/
public void setVY(double vy) {
if (this.active == true) {
this.vy = vy;
}
}
/**
* Sets a new object controller (replaces any former one).
*
* @param controller An instance of {@link controller.ObjectController} or one of its subclasses.
*/
public void setObjectController(ObjectController controller) {
this.controller = controller;
}
/**
* Access to object controller.
*
* @return the controller for this object.
*/
public ObjectController getObjectController() {
return this.controller;
}
public double getGameTime() {
return this.playground.getGameTime();
}
/**
* Collision detection implemented by iteration through the own list of {@link collider.Collider}
* and calling their {@link collider.Collider#collidesWith(Collider)} method to check collision
* with the given parameter instance of other {@link GameObject}.
*
* @param other instance of the other GameObject to check collision with
* @return true if collision is detected, false otherwise
*/
public boolean collisionDetection(GameObject other) {
if (this.scol == null) {
return false;
}
for (Collider c : this.scol) {
logger.trace(other.id);
for (Collider o : other.scol) {
if (c.collidesWith(o)) {
logger.trace(c.id + " " + o.id);
return true;
}
}
}
return false;
}
/**
* triggers this GameObjects own controller (if set) to update the object.
*
* @see GameObject#controller
*/
public void updateObject() {
if (this.controller != null) {
controller.updateObject();
}
}
/**
* Draws the object in its current state. Is called by the game engine, should NOT be called
* otherwise.
*
* @param g object that has all the necessary drawing functionalities
*/
public void draw(Graphics2D g) {
if (this.artist != null) {
this.artist.draw(g);
}
}
}

76
GameProject/src/gameobjects/RectObject.java

@ -0,0 +1,76 @@
package gameobjects;
import java.awt.Color;
import java.io.File;
import collider.RectCollider;
import playground.Playground;
import playground.SpaceInvadersLevel;
import rendering.RectArtist;
/**
* A rectangle object. <br>
* If {@link #generateColliders()} is called, it generates a RectCollider with id-prefix
* "shotcollider_" and registers it for this RectObject.
*
*/
public class RectObject extends GameObject {
/** width in pixels of the RectObject (&gt; 0) */
protected double width;
/** height in pixels of the RectObject (&gt; 0) */
protected double height;
/**
* Initializes the RectObject with a suitable RectArtist for drawing the RectObject.
*
* @param id String unique name to be used.
* @param pg {@link Playground} instance this RectObject belongs to (the level it belongs to).
* @param x position in horizontal direction in pixels (zero or positive number).
* @param y position in vertical direction in pixels (zero or positive number).
* @param vx speed/velocity in horizontal direction in pixels (negative, zero or positive number).
* @param vy speed/velocity in vertical direction in pixels (negative, zero or positive number).
* @param width in pixels
* @param height in pixels
* @param color solid color for the whole object, used to initialize an instance of
* {@link rendering.RectArtist} used for this RectObject.
*/
public RectObject(String id, Playground pg, double x, double y, double vx, double vy,
double width, double height, Color color) {
super(id, pg, x, y, vx, vy);
this.width = width;
this.height = height;
this.artist = new RectArtist(this, width, height, color);
}
/**
* generates a new {@link RectCollider} with id-prefix "shotcollider_" and registers it for 'this'
* [@link RectObject}. The {@link RectCollider} uses the same dimensions ({@link #width} and {@link #height}) as this RectObject.
*
* @return this RectObject itself
*/
public RectObject generateColliders() {
this.scol.add(new RectCollider("shotcollider_" + id, this, this.width, this.height));
return this;
}
/** Getter for the width
*
* @return double width value as set by constructor
*/
public double getWidth() {
return this.width;
}
/** Getter for the height
*
* @return double height value as set by constructor
*/
public double getHeight() {
return this.height;
}
}

70
GameProject/src/gameobjects/TextObject.java

@ -0,0 +1,70 @@
package gameobjects;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.text.AttributedString;
import java.util.LinkedList;
import collider.*;
import controller.ObjectController;
import playground.Playground;
import rendering.*;
/**
* Convenience Class subclassing {@link GameObject}, directly instanciating {@link TextArtist} a
* subclass of {@link Artist} that draws a text. The controller is left undefined, the collider as
* well. However, a single call to the overwritten method {@link #generateColliders} will in fact
* generate a {@link RectCollider} of just the right size for the text.
*
*/
public class TextObject extends GameObject {
private String text = null;
protected double rx, ry;
public String getText() {
return this.text;
}
/**
* Constructor.
*
* @param id object name
* @param playground containing {@link Playground} instance
* @param x positionx
* @param y positiony
* @param vx speedx
* @param vy speedy
* @param size font size in Pixel
* @param text String to be displayed
* @param textColor text color, see java.awt.Color
*/
public TextObject(String id, Playground playground, double x, double y, double vx, double vy,
String text, int size, Color textColor) {
super(id, playground, x, y, vx, vy);
this.artist = new TextArtist(this, text, size, textColor);
this.setColliders(new LinkedList<Collider>());
}
public void setText(String s) {
this.text = s;
((TextArtist) this.artist).setText(s);
}
public TextObject generateColliders() {
// we need to Cast to TextArtist as we want to access Width and Height of text
TextArtist kruecke = (TextArtist) (this.artist);
this.scol.clear();
this.scol.add(new RectCollider("rect", this, kruecke.getTextWidth(), kruecke.getTextHeight()));
return this;
}
}

30
GameProject/src/log4j2.xml

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="OFF">
<appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5level %logger{36} - %msg%n" />
</Console>
<File name="File" fileName="log\log4j.log">
<PatternLayout pattern="%d %-5level %logger{36} - %msg%n" />
</File>
</appenders>
<loggers>
<root level="warn">
<appender-ref ref="Console" />
<appender-ref ref="File" />
</root>
<Logger name="base.GameLoop" level="trace">
</Logger>
<Logger name="playground" level="info">
</Logger>
</loggers>
</configuration>

84
GameProject/src/playground/Animation.java

@ -0,0 +1,84 @@
package playground;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.*;
import java.io.FileNotFoundException;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Scanner;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
public class Animation {
public LinkedList<String> fileList = null;
public LinkedList<Double> showtimeList = null;
public LinkedList<BufferedImage> imageList = null;
private static Logger logger = LogManager.getLogger(Animation.class);
public Animation(String datName) {
Scanner scanner;
this.fileList = new LinkedList<String>();
this.showtimeList = new LinkedList<Double>();
this.imageList = new LinkedList<BufferedImage>();
try {
scanner = new Scanner(new File(datName), "UTF-8");
scanner.useLocale(Locale.GERMANY);
String zeile;
double zeit;
int it = 0;
while (scanner.hasNext()) {
if (scanner.hasNextDouble()) {
zeit = scanner.nextDouble();
showtimeList.add(zeit);
} else {
zeile = scanner.next();
Path basePath = Paths.get(datName);
String file = basePath.getParent().toString() + "/" + zeile;
fileList.add(file);
try {
this.imageList.add(ImageIO.read(new File(file)));
logger.info("img added " + file);
} catch (IOException e) {
logger.warn(file + " not found!!");
}
it++;
logger.trace(basePath.getParent().toString() + "/" + zeile);
}
}
scanner.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public LinkedList<String> getFileList() {
return this.fileList;
}
public LinkedList<Double> getShowtimeList() {
return this.showtimeList;
}
public LinkedList<BufferedImage> getImageList() {
return this.imageList;
}
}

55
GameProject/src/playground/HighscoreManager.java

@ -0,0 +1,55 @@
package playground;
import java.io.*;
import java.util.Scanner;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
// Klasse um Highscore umzusetzen
public class HighscoreManager {
Scanner s;
private static Logger logger = LogManager.getLogger(HighscoreManager.class);
public HighscoreManager() {
try {
File f = new File("./highscore.txt");
if (!f.exists()) {
logger.warn("WARNING: Highscore file was not found and reset");
writeHSToFile(0, -1);
}
s = new Scanner(f);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public int readHSFromFile() {
if (s.hasNext()) {
int highscore = s.nextInt();
return highscore;
}
return 0;
}
public static void writeHSToFile(Integer pts, Integer highscore) {
String highscore2 = String.valueOf(pts);
BufferedWriter bw;
try {
if (pts > highscore) {
FileWriter fw = new FileWriter("./highscore.txt");
bw = new BufferedWriter(fw);
bw.write(highscore2);
bw.close();
logger.info("Highscore file was opened and saved score: " + highscore2);
}
} catch (IOException e) {
logger.error("File for Highscore not writeable! Score lost.");
}
}
public void closeFile() {
s.close();
}
}

73
GameProject/src/playground/HitTwiceLevel.java

@ -0,0 +1,73 @@
package playground;
import controller.FallingStarController;
import gameobjects.EgoObject;
import gameobjects.FallingStar;
import gameobjects.GameObject;
import gameobjects.RectObject;
import java.awt.Color;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* extends {@link SpaceInvadersLevel} with 10 enemies that need two shots each to be destroyed.
*
*/
public class HitTwiceLevel extends SpaceInvadersLevel {
/** constant defining the number of shots needed to destroy an enemy */
public static final int MAX_HITS = 2;
private static Logger logger = LogManager.getLogger(HitTwiceLevel.class);
/** constructor setting internal name to 'hitTwice' */
public HitTwiceLevel() {
super();
this.level = "hitTwice";
}
@Override
protected String getStartupMessage() {
return "2 shots at alien required!!!";
}
protected int calcNrEnemies() {
return 10;
}
public void setupInitialState() {
super.setupInitialState();
GameObject ro = new RectObject("obstacleRect", this, 600, 300, 0, 0, 20, 100, Color.RED) // Remove?
.generateColliders();
this.addObject(ro);
logger.debug("added red box on top");
}
@Override
void actionIfEnemyIsHit(GameObject e, GameObject shot) {
Object counterFlag = e.getOrCreateObjectFlag("counter", Integer.valueOf(1));
int counter = (Integer) counterFlag;
if (counter >= MAX_HITS) {
logger.trace("enemy was hit before for " + counter + " times, which is above "
+ HitTwiceLevel.MAX_HITS);
super.actionIfEnemyIsHit(e, shot);
} else {
logger.trace("enemy was hit before for "+counter+" times, which is below "+HitTwiceLevel.MAX_HITS);
e.setObjectFlag("counter", Integer.valueOf(counter + 1));
}
deleteObject(shot.getId());
}
}

15
GameProject/src/playground/Level1.java

@ -0,0 +1,15 @@
package playground;
/**
* extends {@link SpaceInvadersLevel} with a boring start message
*/
public class Level1 extends SpaceInvadersLevel {
@Override
protected String getStartupMessage() {
return "Get ready for boring level 1!";
}
}

17
GameProject/src/playground/Level2.java

@ -0,0 +1,17 @@
package playground;
/**
* extends extends {@link SpaceInvadersLevel} with a different startup message.
*/
public class Level2 extends SpaceInvadersLevel {
@Override
protected String getStartupMessage() {
return "Get ready for level 2!";
}
}

40
GameProject/src/playground/Level3.java

@ -0,0 +1,40 @@
package playground;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import controller.ObjectController;
import controller.ZickZackController;
import gameobjects.AnimatedGameobject;
import gameobjects.GameObject;
/**
* extends {@link SpaceInvadersLevel} with a ZigZack move of the enemies in
* {@link #createSingleEnemy(String, double, double, double, double, ObjectController, double)} and
* sets a different {@link #getStartupMessage()}.
*/
public class Level3 extends SpaceInvadersLevel {
private static Logger logger = LogManager.getLogger(Level3.class);
@Override
protected String getStartupMessage() {
return "Get ready for level 3!!!";
}
@Override
protected GameObject createSingleEnemy(String name, double x_enemy, double y_enemy,
double vx_enemy, double vy_enemy, ObjectController enemyController, double gameTime) {
logger.trace("creating enemy [" + name + "] with ZickZackController ");
ObjectController zzController = new ZickZackController(gameTime, 0.5);
GameObject go = new AnimatedGameobject(name, this, x_enemy, y_enemy, vx_enemy, vy_enemy,
ENEMYSCALE, this.enemyAnim, this.getGameTime(), "loop").addController(zzController)
.generateColliders();
return go.generateColliders();
}
}

63
GameProject/src/playground/Level4.java

@ -0,0 +1,63 @@
package playground;
import gameobjects.GameObject;
import controller.LimitedTimeController;
import gameobjects.TextObject;
import java.awt.Color;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* extends extends {@link SpaceInvadersLevel}
* <ul>
* <li>Hit aliens twice to kill them
* <li>they say AUA when not destroyed
* </ul>
*/
public class Level4 extends SpaceInvadersLevel {
/** constant defining the number of shots needed to destroy an enemy */
public static final int MAX_HITS = 2;
private static Logger logger = LogManager.getLogger(Level4.class);
@Override
protected String getStartupMessage() {
return "Jetzt gibts Saures!";
}
@Override
void actionIfEnemyIsHit(GameObject e, GameObject shot) {
double gameTime = this.getGameTime();
Object counterFlag = e.getOrCreateObjectFlag("counter", Integer.valueOf(1));
int counter = (Integer) counterFlag;
if (counter >= MAX_HITS) {
logger.trace("enemy was hit before for " + counter + " times, which is above "
+ HitTwiceLevel.MAX_HITS);
super.actionIfEnemyIsHit(e, shot);
} else {
logger.trace("enemy was hit before for "+counter+" times, which is below "+HitTwiceLevel.MAX_HITS);
e.setObjectFlag("counter", Integer.valueOf(counter + 1));
// spawn a bonus points object
double vx = 2 * (Math.random() - 0.5) * SHARDSPEED + e.getVX();
double vy = 2 * (Math.random() - 0.5) * SHARDSPEED + e.getVY();
logger.trace("creating new TextObject bonus" + e.getId());
LimitedTimeController bonusTextController =
new LimitedTimeController(gameTime, SpaceInvadersLevel.EXPL_DURATION);
GameObject bonusText = new TextObject("bonus" + e.getId(), this, e.getX(), e.getY(), vx, vy,
"Aua", 20, Color.YELLOW).addController(bonusTextController);
this.addObject(bonusText);
}
deleteObject(shot.getId());
}
}

112
GameProject/src/playground/LevelBoss.java

@ -0,0 +1,112 @@
package playground;
import java.awt.Color;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import controller.EgoController;
import controller.FallingStarController;
import controller.ObjectController;
import gameobjects.AnimatedGameobject;
import gameobjects.EgoObject;
import gameobjects.FallingStar;
import gameobjects.GameObject;
import gameobjects.TextObject;
/**
* Class that realizes all the game logic of a very simple game level. The level contains for now
* only two objects that are {@link GameObject} subclasses: {@link FallingStar} and
* {@link EgoObject}. Functions performed by this class are:
* <ul>
* <li>initially set up the level, spawn all object etc., in method {@link #prepareLevel}
* <li>React to keyboard commands in method {@link #processKeyEvents(java.util.Stack)}
* <li>define basic object movement rules for all objects in the level in the various
* ObjectController subclasses: {@link EgoController} and {@link FallingStarController}.
* </ul>
*/
public class LevelBoss extends SpaceInvadersLevel {
private static int MAX_SHOTS = 10;
private static Logger logger = LogManager.getLogger(LevelBoss.class);
@Override
void actionIfEnemyIsHit(GameObject e, GameObject shot) {
Object counterFlag = e.getOrCreateObjectFlag("counter", Integer.valueOf(1));
int counter = (Integer) counterFlag;
if (counter >= LevelBoss.MAX_SHOTS) {
logger.trace("enemy was hit before for " + counter + " times, which is equal or above "
+ HitTwiceLevel.MAX_HITS);
super.actionIfEnemyIsHit(e, shot);
} else {
logger.trace("enemy was hit before for " + counter + " times, which is below "
+ HitTwiceLevel.MAX_HITS);
e.setObjectFlag("counter", Integer.valueOf(counter + 1));
}
deleteObject(shot.getId());
}
@Override
double calcEnemyShotProb() {
return 1.5 * this.getTimestep();
}
@Override
protected double calcEnemySpeedX() {
return ENEMYSPEEDX * 2;
}
@Override
protected double calcEnemySpeedY() {
return ENEMYSPEEDY * 2;
}
@Override
protected int calcNrEnemies() {
return (int) 1;
}
@Override
protected GameObject createEnemyShotObject(GameObject parentObject, String name,
ObjectController limitedTimeController) {
GameObject ego = this.getObject("ego");
double deltax = parentObject.getX() - ego.getX();
double deltay = parentObject.getY() - ego.getY();
double norm = Math.sqrt(deltax * deltax + deltay * deltay);
deltax *= -ENEMYSHOTSPEED / norm;
deltay *= -ENEMYSHOTSPEED / norm;
logger.trace("Creating EnemyShot as TextObject [" + name + "] in direction " + deltax + "/"
+ deltay + " towards ego");
GameObject to = new TextObject(name, this, parentObject.getX(), parentObject.getY(), deltax,
deltay, "*", 20, Color.GREEN).generateColliders().addController(limitedTimeController);
return to;
}
@Override
protected GameObject createSingleEnemy(String name, double x_enemy, double y_enemy,
double vx_enemy, double vy_enemy, ObjectController enemyController, double gameTime) {
GameObject go = new AnimatedGameobject(name, this, this.canvasX / 2, 10, vx_enemy, 50,
ENEMYSCALE * 3, this.enemyAnim, this.getGameTime(), "loop").generateColliders()
.addController(enemyController);
return go;
}
@Override
protected String getStartupMessage() {
return "BOSS LEVEL!";
}
}

123
GameProject/src/playground/LevelBreakout0.java

@ -0,0 +1,123 @@
package playground;
import java.awt.Color;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import collider.RectCollider;
import controller.EgoController;
import controller.ReboundController;
import gameobjects.FallingStar;
import gameobjects.GameObject;
import gameobjects.RectObject;
/**
* solution example for Breakout game
* <ul>
* <li>colored bricks
* <li>rebound depends on position where ball hits ego object
* <li>rebound on brick hit
*
* not implemented: penalty if balls hits ground, no score for removed bricks, no bonus items, no
* lives.
* </ul>
*
*/
public class LevelBreakout0 extends LevelBreakoutBase {
private static Logger logger = LogManager.getLogger(LevelBreakoutBase.class);
protected GameObject createEgoObject() {
RectObject ro = new RectObject("ego", this, 350, 550, 0, 0, 80, 10, Color.BLUE);
ro.generateColliders();
EgoController ec = new EgoController(80,10);
ro.addController(ec);
logger.info("ego created.");
return ro;
}
protected GameObject createBall() {
GameObject co = new FallingStar("ball1", this, 350, 350, 100, 100, Color.RED, 5);
co.addController(new ReboundController());
logger.info("ball created.");
return co;
}
/**
* creates bricks. For collision {@link RectCollider} is used.
*/
@Override
protected GameObject createBrick(int row, int column) {
double xSize = 60;
double ySize = 30;
double xStart = 40;
double yStart = 40;
double space = 5;
Color c = Color.BLUE;
RectObject ro = new RectObject("brick" + row + "/" + column, this, xStart + column * (xSize + space),
yStart + row * (ySize + space), 0, 0, xSize - 4, ySize - 4, c);
RectCollider rc = new RectCollider("egal", ro, xSize - 4, ySize - 4);
ro.addCollider(rc);
return ro;
}
/**
* lets ball bounce in Y direction, deletes brick and creates a red/blue colored explosion.
*/
@Override
protected void actionIfBallHitsBrick(GameObject ball, GameObject brick) {
ball.setVY(ball.getVY() * -1); // bounce effect for ball
this.deleteObject(brick.getId()); // remove brick from field
logger.debug("deleted brick " + brick.getId());
}
/**
* Let the ball bounce off in Y direction.
*
*/
@Override
protected void actionIfBallHitsEgo(GameObject ball, GameObject ego) {
double ballY = ball.getY();
double egoY = ego.getY();
ball.setY(ballY < egoY ? ballY - 10 : ballY + 10); // radius 5 hard coded.
ball.setVY(ball.getVY() * -1);
logger.debug("ball bounces of ego object.");
}
/**
* Prepares a Breakout level with a 3 x 3 matrix of blocks on top. This method relies on the
* methods {@link #createEgoObject()}, {@link #createBall()} and {@link #createBrick(int, int)}, among others, which
* are meant to be overwritten in subclasses.
*
* @param level String passes by the game engine (not used currently and can be ignored).
*/
@Override
public void prepareLevel(String level) {
for (int y = 3; y < 6; y++) {
for (int x = 0; x < 3; x++) {
logger.trace("trying to create brick X, Y (" + x + "," + y + ")");
GameObject brick = this.createBrick(x, y);
this.addObject(brick);
}
}
this.ego = this.createEgoObject();
this.ball = this.createBall();
this.addObject(this.ego);
this.addObject(this.ball);
logger.info("level preperation succeeded.");
}
}

180
GameProject/src/playground/LevelBreakoutBase.java

@ -0,0 +1,180 @@
package playground;
import gameobjects.*;
import java.util.LinkedList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Polygon;
import controller.*;
public abstract class LevelBreakoutBase extends Playground {
/**
* instance of the ball, needs to be set by {@link #prepareLevel(String) }
*
*/
protected GameObject ball = null,
/**
* instance of the ego objects, needs to be set by {@link #prepareLevel(String) }
*
*/
ego = null;
private static Logger logger = LogManager.getLogger(LevelBreakoutBase.class);
public LevelBreakoutBase() {
super();
this.canvasX = this.preferredSizeX();
this.canvasY = this.preferredSizeY();
}
/**
* signals to game engine that the game has finished by game over. called every game loop. default
* implementation is always false.
*
* @return false
*/
public boolean gameOver() {
return false;
}
/**
* signals to game engine that the game has finished by success. called every game loop. default
* implementation is always false.
*
* @return false
*/
public boolean levelFinished() {
return false;
}
/**
* signals to game engine that the game has been requested to be reseted (restart). called every
* game loop. default implementation is always false.
*
* @return false
*/
public boolean resetRequested() {
return false;
}
/**
* unimplemented empty method called by game engine every loop.
*
*/
public void redrawLevel(Graphics2D g) {
// fill background with light magenta
int[] x = {0, canvasX, canvasX, 0};
int[] y = {0, 0, canvasY, canvasY};
Polygon bg = new Polygon(x, y, 4);
g.setColor(new Color(140,140,200));
g.fill(bg);
}
/**
* Signal that the level has a size of 700x700 pixels.
*
* @return x size of level 700
*/
@Override
public int preferredSizeX() {
return 700;
}
/**
* Signal that the level has a size of 700x700 pixels.
*
* @return y size of level 700
*/
@Override
public int preferredSizeY() {
return 700;
}
/**
* Method that gets called by applyGameLogic() whenever the ball collides with a brick.
*
*
* @param ball A reference to the current ball object
* @param brick A reference to the ego object
*/
protected abstract void actionIfBallHitsBrick(GameObject ball, GameObject brick);
/**
* Method that gets called by applyGameLogic() whenever the ball collides with the ego object.
*
* @param ball A reference to the current ball object
* @param ego A reference to the ego object
*/
protected abstract void actionIfBallHitsEgo(GameObject ball, GameObject ego);
/**
* Models interactions between GameObjects. notably ball/ego and ball/brick. called every game
* loop.
*/
@Override
public void applyGameLogic() {
LinkedList<GameObject> bricks = collectObjects("brick", false);
for (GameObject brick : bricks) {
if (this.ball.collisionDetection(brick)) {
logger.trace("Collision detected of ball and brick " + brick.getId());
this.actionIfBallHitsBrick(this.ball, brick);
}
}
if (ego.collisionDetection(ball)) {
logger.trace("Collision detected of ball and ego");
actionIfBallHitsEgo(this.ball, this.ego);
}
}
/**
* Creates the ego object and returns it, called by {@link #prepareLevel}. Does NOT add the ego
* object to the playground!
*
* @return The created ego object instance (of class {@link RectObject} with
* {@link EgoController}.
*/
protected abstract GameObject createEgoObject();
/**
* Creates the ball object and returns it, called by #prepareLevel. Does NOT add the ball object
* to the playground!
*
* @return The created ball object instance (of class {@link FallingStar})
*/
protected abstract GameObject createBall();
/**
* Creates the GameObject (RectObject) instance representing a single brick at a certain grid
* position. The brick is NOT added here!
*
* @param row row position in the grid, ranges from 0 to calcNrBricksY()-1
* @param column column position in the grid of bricks, ranges from 0 to calcNrBricksX()-1
* @return The GameObject instance (really a RectObject) representing the created brick.
*/
protected abstract GameObject createBrick(int row, int column);
/**
* Prepares a generic Breakout-Type level. This method relies on the methods {@link #createEgoObject()},
* {@link #createBall} and {@link #createBrick}, among others, which are meant to be overwritten
* in subclasses. <br>
* Attention: the attributes {@link #ball} and {@link #ego} need to be set properly to GameObject
* instances when implementing this method {@link #prepareLevel(String)}.
*
* @param level String passes by the game engine (not used currently and can be ignored).
*/
@Override
abstract public void prepareLevel(String level);
}

98
GameProject/src/playground/LevelBreakoutBaseAdvanced.java

@ -0,0 +1,98 @@
package playground;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
/**
* Advanced version of abstract {@link LevelBreakoutBase} providing a complete implementation of
* {@link #prepareLevel(String)}. Additionally abstract methods for number of bricks in X and Y
* direction are provided as well as abstract methods for brick sizes and start coordinates.
*
* @see #calcNrBricksX()
* @see #calcNrBricksY()
* @see #getBrickSizeX()
* @see #getBrickSizeY()
* @see #getBrickStartX()
* @see #getBrickStartY()
*
*/
public abstract class LevelBreakoutBaseAdvanced extends LevelBreakoutBase {
private static Logger logger = LogManager.getLogger(LevelBreakoutBaseAdvanced.class);
/**
* provides the number of bricks to be set in horizontal direction.
*
* @return positive value of how many bricks are to be next to each other in X direction
*/
protected abstract int calcNrBricksX();
/**
* provides the number of bricks to be set in vertical direction.
*
* @return positive value of how many bricks are to be next to each other in Y direction
*/
protected abstract int calcNrBricksY();
/**
* provides the length of one brick.
*
* @return positive value of how long a brick should be in X direction.
*/
protected abstract double getBrickSizeX();
/**
* provides the height of one brick.
*
* @return positive value of how high a brick should be in Y direction.
*/
protected abstract double getBrickSizeY();
/**
* provides the start coordinate of upper left corner (X value).
*
* @return positive value of the X coordinate to use as the starting point of the upper left
* corner of the brick set.
*/
protected abstract double getBrickStartX();
/**
* provides the start coordinate of upper left corner (Y value).
*
* @return positive value of the Y coordinate to use as the starting point of the upper left
* corner of the brick set.
*/
protected abstract double getBrickStartY();
/**
* Prepares a complete Breakout type level and uses the values provided by implementations of
* {@link #calcNrBricksX()} and {@link #calcNrBricksY()} to generate the stone matrix.
* Furthermore, it relies on the methods {@link #createEgoObject()}, {@link #createBall} and {@link #createBrick},
* which are meant to be overwritten in subclasses. <br>
* Attention: For collission detection bricks created by {@link #createBrick(int, int)} need to have the String 'brick' in ID.
*
* @see LevelBreakoutBase#prepareLevel(String) for further information.
*
* @param level String passes by the game engine (not used currently and can be ignored).
*
*/
@Override
public void prepareLevel(String level) {
for (int y = 0; y < this.calcNrBricksY(); y++) {
for (int x = 0; x < this.calcNrBricksX(); x++) {
logger.trace("trying to create brick X, Y (" + x + "," + y + ")");
this.addObject(this.createBrick(x, y));
}
}
this.ego = this.createEgoObject();
this.ball = this.createBall();
this.addObject(this.ego);
this.addObject(this.ball);
logger.info("level preperation succeeded.");
}
}

20
GameProject/src/playground/LevelMovingHitObjects.java

@ -0,0 +1,20 @@
package playground;
// FIXME JavaDoc
public class LevelMovingHitObjects extends SpaceInvadersLevel {
// FIXME implement missing methods
/**
* "Moving Hitting Objects Level!" is the message.
*
* @return String "Moving &amp; Hitting Objects Level!"
*/
@Override
protected String getStartupMessage() {
return "Moving & Hitting Objects Level!";
}
}

20
GameProject/src/playground/LevelMovingObjects.java

@ -0,0 +1,20 @@
package playground;
// FIXME JavaDoc
public class LevelMovingObjects extends SpaceInvadersLevel {
// FIXME implement prepareLevel() method
/**
* "Moving Objects Level!" is the message.
*
* @return String "Moving Objects Level!"
*/
@Override
protected String getStartupMessage() {
return "Moving Objects Level!";
}
}

48
GameProject/src/playground/Music.java

@ -0,0 +1,48 @@
package playground;
import java.io.File;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.FloatControl;
public class Music {
private static final float volume = 0.02f; // scale 0 silence, 1 no change, 2 double. (linear).
public static synchronized void music(File track) {
final File trackname = track;
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Clip clip = AudioSystem.getClip();
AudioInputStream inputstream = AudioSystem.getAudioInputStream(trackname);
clip.open(inputstream);
FloatControl volumeControl =
(FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
if (volumeControl != null) {
volumeControl.setValue(20f * (float) Math.log10(volume));
}
clip.start();
Thread.sleep(clip.getMicrosecondLength() / 1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
}

374
GameProject/src/playground/Playground.java

@ -0,0 +1,374 @@
package playground;
import java.awt.Graphics2D;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Stack;
import java.awt.event.*;
import gameobjects.GameObject;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
/**
* Playground represents a level of the game, focusing on the game LOGIC, i.e., not so much on the
* graphical representation. In particular, an instance of Playground
* <ul>
* <li>manages the different moving or static objects in a level (e.g., collisions, adding objects,
* removing objects). This is mainly done by the methods {@link #addObject}, {@link #deleteObject}.
* <li>processes keyboard inputs provided by GameLoop in {@link #processKeyEvents(Stack)} and
* {@link #processMouseEvents(Stack)}
* <li>represents the state of a level represented by <b>flags</b>. Each flag has a name (a String)
* and an arbitrary value of any type. Methods: {@link #setLevelFlag(String, Object)},
* {@link #getLevelFlag(String)}. As an example, the current score is a flag usually named "points",
* with an Integer as a value. This value can be retrieved and manipulated using the above mentioned
* methods.
* </ul>
*/
public abstract class Playground {
public static final int FLAGS_GLOBAL = 1;
public static final int FLAGS_LEVEL = 2;
public static final int FLAGS_ALL = 3;
protected int canvasX = -1;
protected int canvasY = -1;
/** only one set of objects exists concurrently so this can be static */
protected static HashMap<String, GameObject> gameObjects = new HashMap<String, GameObject>();
/** only one set of objects exists concurrently so this can be static */
protected static HashMap<String, Object> flags = new HashMap<String, Object>();
protected String level = "";
protected double timeStep = 0;
protected double gameTime = 0;
LinkedList<GameObject> addables = new LinkedList<GameObject>();
LinkedList<String> removables = new LinkedList<String>();
// HashMap<Integer,Integer> keys ;
Stack<KeyEvent> keyEvents;
Stack<MouseEvent> mouseEvents;
protected boolean pausedFlag = false;
private static Logger logger = LogManager.getLogger(Playground.class);
public Playground() {
this.canvasX = -1;
this.canvasY = -1;
}
// here, the level communicates its size preference to the GameUI
// called automatically
public abstract int preferredSizeX();
public abstract int preferredSizeY();
/**
* Adds a graphics object to a level.
*
* @param o GameObject The object to be added
*/
public void addObject(GameObject o) {
// gameObjects.put(o.getId(), o);
addables.addLast(o);
}
/**
* Adds a graphics object to a level.
*
* @param o GameObject The object to be added
*/
public void addObjectNow(GameObject o) {
gameObjects.put(o.getId(), o);
}
/**
* Puts objects with a certain substring in their name into a LinkedLisrt and returns them.
*
* @param substr The string that must be part of the object name if object is to be considered
* found.
* @param filterInactive if set true only active objects are considered.
* @return a reference to a LinkedList filled with all objects that have <b>substr</b> in their
* name
*/
public LinkedList<GameObject> collectObjects(String substr, boolean filterInactive) {
LinkedList<GameObject> l = new LinkedList<GameObject>();
for (Map.Entry<String, GameObject> entry : Playground.gameObjects.entrySet()) { // Iterator
// usage
GameObject obj = entry.getValue();
if (obj.getId().contains(substr)) {
if (filterInactive == true) {
if (obj.isActive()) {
l.add(obj);
}
} else {
l.add(obj);
}
}
}
return l;
};
/**
* Removes a graphics object from a level.
*
* @param id String The unique identifier of the object
*/
public void deleteObject(String id) {
// gameObjects.remove(id);
removables.addLast(id);
}
/**
* Removes a graphics object from a level immediately, CAUTION.
*
* @param id String The unique identifier of the object
*/
public void deleteObjectNow(String id) {
gameObjects.remove(id);
}
/**
* Retrieves a graphics object by name.
*
* @param id String Unique id of the object
* @return reference to the requested game object, or null if not found
*/
public GameObject getObject(String id) {
return gameObjects.get(id);
}
/**
* Sets a level-wide permanent flag.
*
* @param flag String Q unique name in this level. If it exists value is overwritten.
* @param value Object Any Object can be the value of a flag!
*/
public static void setGlobalFlag(String flag, Object value) {
flags.put("/global/" + flag, value);
}
public Object setLevelFlag(String flag, Object value) {
flags.put("/" + this.level + "/" + flag, value);
return value;
}
/**
* mode can be: FLAGS_ALL (all), FLAGS_GLOBAL(global), FLAGs_LEVEL(level)
*
* @param mode can be only one of {@link #FLAGS_GLOBAL} {@link #FLAGS_ALL} or
* {@link #FLAGS_LEVEL }
*/
public void resetFlags(int mode) {
LinkedList<String> delKeys = new LinkedList<String>();
for (Map.Entry<String, Object> entry : Playground.flags.entrySet()) {
logger.trace(entry.getKey() + " IndexofGlobal = " + entry.getKey().indexOf("/global/"));
if ((mode == FLAGS_GLOBAL) && (entry.getKey().indexOf("/global/") != -1)) {
logger.debug("GLOBAL: scheduling for removal: " + entry.getKey());
delKeys.add(entry.getKey());
} else if ((mode == FLAGS_LEVEL) && (entry.getKey().indexOf("/global/") == -1)) {
logger.debug("LEVEL: scheduling for removal: " + entry.getKey());
delKeys.add(entry.getKey());
} else if (mode == FLAGS_ALL) {
logger.debug("ALL: scheduling for removal: " + entry.getKey());
delKeys.add(entry.getKey());
}
}
for (String str : delKeys) {
logger.trace("removing key " + str);
flags.remove(str);
}
}
/**
* Retrieves a level-wide flag by name.
*
* @param flag String Unique flag id
* @return the value associated with <b>flag</b>, or <b>null</b> if the flag does not exist.
*/
public static Object getGlobalFlag(String flag) {
return flags.get("/global/" + flag);
}
/** checks for existence and if not creates the new global flag with the given initial value. Returns the value.
* afterwards it is guaranteed that no priorly existing value is overridden and that it definitely exists (created if not present before).
*
* @param flag String name for the global flag (created if not present)
* @param value Object value to be stored (used only if flag was not present)
* @return the current value of the flag (maybe the initial one in case flag was not there before)
*/
public static Object getOrCreateGlobalFlag(String flag, Object value) {
Object tmp = getGlobalFlag(flag);
if (tmp == null) {
setGlobalFlag(flag, value);
return value;
} else {
return tmp;
}
}
public Object getLevelFlag(String flag) {
return flags.get("/" + this.level + "/" + flag);
}
public Object getOrCreateLevelFlag(String flag, Object createValue) {
Object tmp = getLevelFlag(flag);
if (tmp == null) {
return setLevelFlag(flag, createValue);
} else {
return tmp;
}
}
/**
* Reinitializes the level.
*/
public void reset() {
gameObjects.clear();
}
public boolean isPaused() {
return this.pausedFlag;
}
public void setPaused(boolean p) {
this.pausedFlag = p;
}
public void togglePause() {
pausedFlag = !pausedFlag;
}
/**
* Method meant to be filled with own code, processes Keyboard inputs.
*
* @param keyEvents all collected {@link KeyEvent}s collected since last game loop.
*/
public void processKeyEvents(Stack<KeyEvent> keyEvents) {
this.keyEvents = keyEvents;
Playground.setGlobalFlag("inputs", keyEvents);
}
public void processMouseEvents(Stack<MouseEvent> mouseEvents) {
this.mouseEvents = mouseEvents;
Playground.setGlobalFlag("inputs", mouseEvents);
}
public Stack<KeyEvent> getKeyEvents() {
return this.keyEvents;
}
public Stack<MouseEvent> getMouseEvents() {
return this.mouseEvents;
}
/**
* Method meant to be filled with own code, handles the entore game logic (collision checks, timed
* events, ...).
*
*/
public abstract void applyGameLogic();
/**
* Sets up a single level. Prepares all objects etc.
*
* @param level String a string identifying the level number etc
*/
public abstract void prepareLevel(String level);
public abstract boolean gameOver();
public abstract boolean levelFinished();
public int getSizeX() {
return canvasX;
}
public int getSizeY() {
return canvasY;
}
/**
* Calls all object update methods in level. Internal, never call directly.
*
*/
public void updateObjects() {
for (GameObject gameObject : gameObjects.values()) { // Iterator usage
if (gameObject.isActive() == true) {
gameObject.updateObject();
logger.trace("updated object " + gameObject.scol);
}
}
for (GameObject o : addables) { // Iterator usage
addObjectNow(o);
}
for (String s : removables) { // Iterator usage
deleteObjectNow(s);
}
removables.clear();
addables.clear();
}
public void setTimestep(double s) {
timeStep = s;
}
public double getTimestep() {
return timeStep;
}
/** set the game time value (in seconds)
*
* @param s seconds the game is running
*/
public void setGameTime(double s) {
this.gameTime = s;
}
/** returns time in seconds since level start */
public double getGameTime() {
return this.gameTime;
}
/**
* To be redefined!! Draws mainly h level background and global information like points etc.
*
* @param g2 Graphics2D abstract drawing object of java Swing, used to carry out all drawing
* operations.
*/
public abstract void redrawLevel(Graphics2D g2);
/**
* Internal, do not call directly.
*
* @param g2 Graphics2D abstract drawing object of java Swing, used to carry out all drawing
* operations.
*/
public void redraw(Graphics2D g2) {
redrawLevel(g2);
for (GameObject gameObject : gameObjects.values()) {
if (gameObject.isActive()) {
gameObject.draw(g2);
}
}
}
}

62
GameProject/src/playground/SaveGame.java

@ -0,0 +1,62 @@
package playground;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
public class SaveGame {
private static String datnam = "aktuellerSpielzustand.ser";
private static Logger logger = LogManager.getLogger(SaveGame.class);
@SuppressWarnings("unchecked")
public static void save() {
// gameobjects.AnimatedGameobject
ArrayList<String> objArrList = null;
ObjectInputStream in = null;
try {
in = new ObjectInputStream(new FileInputStream(datnam));
objArrList = (ArrayList<String>) in.readObject();
} catch (FileNotFoundException ex) {
logger.warn("Savegame file not (yet) existing!");
} catch (Exception ex) {
logger.error(ex);
} finally {
try {
if (in != null)
in.close();
} catch (IOException e) {
}
}
if (objArrList == null)
objArrList = new ArrayList<String>();
objArrList.add(new String("ArrayListgroesse: " + objArrList.size()));
logger.debug(objArrList);
ObjectOutputStream aus = null;
try {
aus = new ObjectOutputStream(new FileOutputStream(datnam));
aus.writeObject(objArrList);
} catch (IOException ex) {
logger.error(ex);
} finally {
try {
if (aus != null) {
aus.flush();
aus.close();
}
} catch (IOException e) {
}
}
}
}

678
GameProject/src/playground/SpaceInvadersLevel.java

@ -0,0 +1,678 @@
package playground;
// import utilities.* ;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.font.TextAttribute;
import java.awt.image.BufferedImage;
import java.io.*;
import java.text.AttributedString;
import java.util.LinkedList;
import controller.EnemyController;
import controller.FallingStarController;
import controller.LimitedTimeController;
import controller.ObjectController;
import controller.EgoController;
import controller.CollisionAwareEgoController;
import gameobjects.AnimatedGameobject;
import gameobjects.FallingStar;
import gameobjects.GameObject;
import gameobjects.EgoObject;
import gameobjects.TextObject;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
/**
* Class that realizes all the game logic of a very simple game level. The level contains for now
* only two objects that are {@link GameObject} subclasses: {@link FallingStar} and
* {@link EgoObject}. Functions performed by this class are:
* <ul>
* <li>initially set up the level, spawn all object etc., in method {@link #prepareLevel}
* <li>React to game events in {@link #actionIfEgoCollidesWithCollect(GameObject, GameObject)} ,
* {@link #actionIfEgoCollidesWithEnemy(GameObject, GameObject)}, etc.
* <li>define basic object movement rules for all objects in the level in the various
* ObjectController subclasses: {@link EgoController} and {@link FallingStarController}.
* </ul>
*/
public class SpaceInvadersLevel extends Playground {
public static final double SHOTSPEED = 175;
public static final double EGOSPEED = 220;
protected static final int LEVEL2STARS = 80;
protected static final double BONUS_DURATION = 1.;
protected static final double ENEMYSPEEDX = 60;
protected static final double ENEMYSPEEDY = 40;
protected static final double ENEMYSCALE = 1.0;
protected static final double ENEMYSHOTSPEED = 75;
protected static final int NRSHARDS = 50;
protected static final double EXPL_DURATION = 1.;
protected static final int NR_ENEMIES = 30;
protected static final int NR_COLLECT = 5;
protected static final Color EXPL_COLOR = Color.RED;
protected static final double SHARDSPEED = 200;
protected static final double STARSPEED = 100;
protected static final double STARTTEXTSPEED = 75;
protected static final double STARTPERIOD = 5.;
protected static final double DYING_INTERVAL = 2.0;
protected static final int CANVASX = 700;
protected static final int CANVASY = 700;
protected static final double EGORAD = 15;
protected static final double LEVEL_INIT_TIME = 1.0;
protected int nextShot = 0;
protected boolean lost = false;
protected boolean doneLevel = false;
protected double starttime = 0;
protected File smash = null;
protected File laser = null;
protected BufferedImage[] alienImage = null;
protected double[] alienshowTime = null;
protected BufferedImage[] heartImage = null;
protected double[] heartshowTime = null;
protected Animation enemyAnim = null;
protected Animation heartAnim = null;
private static Logger logger = LogManager.getLogger(SpaceInvadersLevel.class);
public SpaceInvadersLevel() {
super();
this.canvasX = this.preferredSizeX();
this.canvasY = this.preferredSizeY();
}
/**
* initially sets up the level. Not called by user interaction, but called every time a layer is
* restarted from scratch. So make sure that this is possible. Here, resources are loaded only
* once even if method is called several times.
*
* @param id String identifies level.
*/
@Override
public void prepareLevel(String id) {
logger.info("PREPARE");
reset();
this.doneLevel = false;
this.lost = false;
resetFlags(FLAGS_LEVEL);
// set up flags that some objects rely on
setLevelFlag("delete", new LinkedList<String>());
setLevelFlag("replace", new LinkedList<String>());
getOrCreateGlobalFlag("points", Integer.valueOf(0));
setLevelFlag("enemyShotCounter", Integer.valueOf(0));
setLevelFlag("gameStatus", "start");
setLevelFlag("detailedStatus", "std");
getOrCreateGlobalFlag("egoLives", Integer.valueOf(5));
setLevelFlag("dying", Double.valueOf(-1));
// start time measure
this.starttime = this.getGameTime();
// music load
if (this.smash == null) {
this.smash = new File("./audio/smash.wav");
}
if (this.laser == null) {
this.laser = new File("./audio/laser.wav");
}
// ----- Alien
if (this.enemyAnim == null) {
String dateiName = "./video/sweetAlien.txt";
this.enemyAnim = new Animation(dateiName);
}
// -----Heart
if (this.heartAnim == null) {
String heartName = "./video/heart.txt";
this.heartAnim = new Animation(heartName);
}
// load highscore and update
HighscoreManager dh = new HighscoreManager();
int alltimeHighscore = dh.readHSFromFile();
setGlobalFlag("highscore", alltimeHighscore);
logger.info("HIGHSCORE" + alltimeHighscore);
}
/**
* (re)draws the level but NOT the objects, they draw themselves. Called by the main game loop.
* This method mainly draws the background and the scoreboard.
*
* @param g2 Graphics2D object that can, and should be, draw on.
*/
@Override
public void redrawLevel(Graphics2D g2) {
// Set anti-alias!
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
// Set anti-alias for text
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
// fill background with black
int[] x = {0, canvasX, canvasX, 0};
int[] y = {0, 0, canvasY, canvasY};
Polygon bg = new Polygon(x, y, 4);
g2.setColor(Color.BLACK);
g2.fill(bg);
// draw score in upper left part of playground
Integer pts = (Integer) getGlobalFlag("points");
Font drawFont = new Font("SansSerif", Font.PLAIN, 20);
AttributedString as = new AttributedString("Points: " + pts);
as.addAttribute(TextAttribute.FONT, drawFont);
as.addAttribute(TextAttribute.FOREGROUND, Color.yellow);
g2.drawString(as.getIterator(), 10, 20);
// draw lives counter in upper left part of playground
Integer lives = (Integer) getGlobalFlag("egoLives");
Font drawFont2 = new Font("SansSerif", Font.PLAIN, 20);
AttributedString as2 = new AttributedString("Lives: " + lives);
as2.addAttribute(TextAttribute.FONT, drawFont2);
as2.addAttribute(TextAttribute.FOREGROUND, Color.yellow);
g2.drawString(as2.getIterator(), canvasX - 100, 20);
// draw highscore in left part of playground under score
Integer highscore = (Integer) getGlobalFlag("highscore");
Font drawFont3 = new Font("SansSerif", Font.PLAIN, 20);
AttributedString as3 = new AttributedString("Highscore: " + highscore);
as3.addAttribute(TextAttribute.FONT, drawFont3);
as3.addAttribute(TextAttribute.FOREGROUND, Color.yellow);
g2.drawString(as3.getIterator(), 10, 40);
if (isPaused()) {
Font drawFont4 = new Font("SansSerif", Font.PLAIN, 50);
AttributedString as4 = new AttributedString("Das Spiel wurde pausiert.");
as4.addAttribute(TextAttribute.FONT, drawFont4);
as4.addAttribute(TextAttribute.FOREGROUND, Color.red);
g2.drawString(as4.getIterator(), 30, 400);
}
}
/**
* applies the logic of the level: For now, this is just about deleting shots that are leaving the
* screen and calling methods 'actionIf..' in case objects collide.
*
*/
@Override
public void applyGameLogic() {
double gameTime = this.getGameTime();
String gameStatus = (String) getLevelFlag("gameStatus");
String subStatus = (String) getLevelFlag("detailedStatus");
if (gameStatus.equals("start") == true) {
setupInitialState();
} else if (gameStatus.equals("starting") == true) {
if ((gameTime - starttime) > LEVEL_INIT_TIME) {
setLevelFlag("gameStatus", "init");
}
} else if (gameStatus.equals("init") == true) {
this.createEnemies();
this.createCollectables();
setLevelFlag("gameStatus", "playing");
} else if (gameStatus.equals("playing") == true) {
GameObject s = this.getObject("ego");
if (subStatus.equals("std")) {
// check for collisions of enemy and shots, reuse shots list from before..
LinkedList<GameObject> enemies = collectObjects("enemy", false);
// check whether all enemies have been destroyed or escaped
if (enemies.size() == 0) {
HighscoreManager hsm = new HighscoreManager();
HighscoreManager.writeHSToFile((Integer) Playground.getGlobalFlag("points"),
(Integer) Playground.getGlobalFlag("highscore"));
this.doneLevel = true;
logger.info("no enemies left, level done.");
}
// loop over enemies to check for collisions or suchlike ...
LinkedList<GameObject> shots = collectObjects("simpleShot", true);
for (GameObject e : enemies) {
// if ego collides with enemy..
if (s.collisionDetection(e)) {
actionIfEgoCollidesWithEnemy(e, s);
}
// if shot collides with enemy
for (GameObject shot : shots) {
if (e.collisionDetection(shot)) {
actionIfEnemyIsHit(e, shot);
}
}
}
// collecting hearts
LinkedList<GameObject> collects = collectObjects("collect", false);
for (GameObject c : collects) {
if (s.collisionDetection(c)) {
actionIfEgoCollidesWithCollect(c, s);
}
}
// loop over enemies and, with a certain probability, launch an enemy shot for each one
for (GameObject e : enemies) {
createEnemyShot(e);
}
// check for collisions between ego object and enemy shots
LinkedList<GameObject> eshots = collectObjects("enmyShot", true);
for (GameObject eshot : eshots) {
if (s.collisionDetection(eshot)) {
logger.trace("COLLISION" + eshot.scol.get(0) + "/" + s.scol.get(0));
actionIfEgoObjectIsHit(eshot, s);
}
}
} // if substatus..
else if (subStatus.equals("dying")) {
Double t0 = (Double) getLevelFlag("t0");
if (gameTime - t0 > DYING_INTERVAL) {
LinkedList<GameObject> enemies = collectObjects("enemy", false);
setLevelFlag("detailedStatus", "std");
s.setActive(true);
for (GameObject e : enemies) {
logger.trace("activating" + e.getId());
e.setActive(true);
}
}
}
} // if (gameStatus == "playing")
}
public boolean gameOver() {
return lost;
}
public boolean levelFinished() {
return doneLevel;
}
/**
* calculates and returns the preferred size of the level (in pixel) for X-direction
*/
public int preferredSizeX() {
return CANVASX;
}
/**
* calculates and returns the preferred size of the level (in pixel) for Y-direction
*/
public int preferredSizeY() {
return CANVASY;
}
/** Adds ego object and stars and displays startup message. Is called from applyGameLogic */
protected void setupInitialState() {
double gameTime = this.getGameTime();
setLevelFlag("gameStatus", "starting");
this.createStars();
// set up ego object
this.createEgoObject();
// add text object to playground
ObjectController ctrl = new LimitedTimeController(gameTime, 3.);
GameObject readyText = new TextObject("ready?", this, getSizeX() / 2, 0, 0, 100,
this.getStartupMessage(), 50, Color.RED).addController(ctrl);
addObject(readyText);
}
/**
* simply returns the text that should be displayed at level start
*
* @return a string that is displayed at start. Should be not longer than 30 characters.
*/
protected String getStartupMessage() {
return "Get ready for level X!";
}
/**
* returns a number representing the speed of an enemy object in X direction (pixels/second)
*
* @return a positive value
*/
protected double calcEnemySpeedX() {
return SpaceInvadersLevel.ENEMYSPEEDX;
}
/**
* returns a number representing the speed of an enemy object in Y direction (pixels/second)
*
* @return a positive value
*/
protected double calcEnemySpeedY() {
return SpaceInvadersLevel.ENEMYSPEEDY;
}
/**
* returns the maximum number of enemy instances (which are created at level start)
*
* @return a positive value
*/
protected int calcNrEnemies() {
return SpaceInvadersLevel.NR_ENEMIES;
}
/**
* returns the maximum number of collectables' instances (which are created at level start)
*
* @return a positive value
*/
protected int calcNrCollect() {
return SpaceInvadersLevel.NR_COLLECT;
}
protected GameObject createEnemyShotObject(GameObject parentObject, String name,
ObjectController limitedTimeController) {
GameObject to =
new TextObject(name, this, parentObject.getX(), parentObject.getY(), 0, ENEMYSHOTSPEED, "I",
20, Color.YELLOW).generateColliders().addController(limitedTimeController);
/*
* // also possible: GameObject to = new RectObject(name, this, parentObject.getX(),
* parentObject.getY(), 0, ENEMYSHOTSPEED, 4, 20,
* Color.YELLOW).addController(limitedTimeController) ; to.generateColliders();
*/
return to;
}
protected void createEnemyShot(GameObject e) {
double gameTime = this.getGameTime();
double PROB = calcEnemyShotProb();
double diceThrow = Math.random();
Integer nrEnemyShots = (Integer) (getLevelFlag("enemyShotCounter"));
if (diceThrow < PROB) {
setLevelFlag("enemyShotCounter", Integer.valueOf(++nrEnemyShots));
LimitedTimeController limitedTimeController = new LimitedTimeController(gameTime, 10.);
GameObject textObject =
createEnemyShotObject(e, "enmyShot" + nrEnemyShots, limitedTimeController);
addObject(textObject);
}
}
/**
* calculates and returns the probability that an enemy shots. Used by
* {@link SpaceInvadersLevel#createEnemyShot(GameObject)}.
*
* @return a positive value between 0 (no chance) to 1 (for sure).
*/
double calcEnemyShotProb() {
return 0.1 * this.getTimestep();
}
ObjectController createEnemyController() {
return new EnemyController();
}
GameObject createSingleEnemy(String name, double x_enemy, double y_enemy, double vx_enemy,
double vy_enemy, ObjectController enemyController, double gameTime) {
GameObject tmp =
new AnimatedGameobject(name, this, x_enemy, y_enemy, vx_enemy, vy_enemy, ENEMYSCALE,
this.enemyAnim, gameTime, "loop").addController(enemyController).generateColliders();
return tmp;
}
GameObject createSingleCollect(String name) {
double gameTime = this.getGameTime();
double cspeedy = 20.;
double x_collect = Math.random() * this.canvasX;
double y_collect = Math.random() * this.canvasY / 3;
double vx_collect = 2 * (Math.random() - 0.5) * 0;
double vy_collect = Math.random() * cspeedy;
GameObject tmp = new AnimatedGameobject(name, this, x_collect, y_collect, vx_collect,
vy_collect, 0.3, this.heartAnim, gameTime, "loop").generateColliders()
.addController(new EnemyController());
return tmp;
}
void createEnemies() {
// create enemies
double gameTime = this.getGameTime();
double speedx = this.calcEnemySpeedX();
double speedy = this.calcEnemySpeedY();
for (int i = 0; i < this.calcNrEnemies(); i++) {
double x_enemy = Math.random() * this.canvasX;
double y_enemy = Math.random() * this.canvasY / 3;
double vx_enemy = 2 * (Math.random() - 0.5) * speedx;
double vy_enemy = Math.random() * speedy;
ObjectController enemyController = createEnemyController();
GameObject enemy = createSingleEnemy("enemy" + i, x_enemy, y_enemy, vx_enemy, vy_enemy,
enemyController, gameTime);
addObject(enemy);
}
}
void createCollectables() {
double gameTime = this.getGameTime();
// create collectables
for (int i = 0; i < this.calcNrCollect(); i++) {
GameObject collect = createSingleCollect("collect" + i);
addObject(collect);
}
}
void createEgoObject() {
// add ego to playground at lower bottom
EgoController egoController =
new CollisionAwareEgoController(SpaceInvadersLevel.EGORAD, this.laser);
GameObject ego = new EgoObject("ego", this, canvasX / 2, canvasY - (EGORAD * 2), 0, 0, EGORAD)
.addController(egoController).generateColliders();
addObject(ego);
}
void createStars() {
// add stars to playground
for (int i = 1; i <= LEVEL2STARS; i++) {
FallingStarController fsc = new FallingStarController();
GameObject star = new FallingStar("star" + i, this, Math.random() * canvasX,
Math.random() * 15, 0.0, Math.random() * STARSPEED, Color.WHITE, 1.).addController(fsc);
addObject(star);
}
}
void createExplosion(double gameTime, GameObject e, String basename, double interval,
Color color) {
// spawn a cloud of exploded shards
for (int i = 0; i < NRSHARDS; i++) {
double vx = 2 * (Math.random() - 0.5) * SHARDSPEED + e.getVX();
double vy = 2 * (Math.random() - 0.5) * SHARDSPEED + e.getVY();
addObject(
new FallingStar("shard" + gameTime + "/" + i, this, e.getX(), e.getY(), vx, vy, color, 2)
.addController(new LimitedTimeController(gameTime, interval)));
}
}
/**
* implements game behavior if an enemy object is hit by a players' shot. It creates an explosion
* effect, plays a sound and adds 200 points to the current score (and it removes the enemy object
* and the shot object).
*
* @param e enemy which was hit
* @param shot the shot object that hit the enemy
*/
void actionIfEnemyIsHit(GameObject e, GameObject shot) {
double gameTime = this.getGameTime();
createExplosion(gameTime, e, "shard", DYING_INTERVAL, Color.RED);
Music.music(smash);
// delete enemy
deleteObject(e.getId());
// delete shot
deleteObject(shot.getId());
// add to points counter
Integer pts = (Integer) getGlobalFlag("points");
setGlobalFlag("points", pts + 200);
}
/**
* implements game behavior if ego object of player touches a collectable
* {@link gameobjects.GameObject }. Here it adds one extra life and displays a text for a limited
* time on screen "Extra life!!". The duration is set in constant
* {@link SpaceInvadersLevel#EXPL_DURATION}.
*
*
* @param collect item touched by ego
* @param ego the players ego object
*/
void actionIfEgoCollidesWithCollect(GameObject collect, GameObject ego) {
double gameTime = this.getGameTime();
if (this.getObject("bonustext") == null) {
// spawn a bonus points object
double vx = 2 * (Math.random() - 0.5) * SHARDSPEED + collect.getVX();
double vy = 2 * (Math.random() - 0.5) * SHARDSPEED + collect.getVY();
LimitedTimeController bonusTextController =
new LimitedTimeController(gameTime, SpaceInvadersLevel.EXPL_DURATION);
GameObject bonusText = new TextObject("bonustext", this, collect.getX(), collect.getY(), vx,
vy, "Extra life!!", 20, Color.YELLOW).addController(bonusTextController);
addObject(bonusText);
// delete collect
deleteObject(collect.getId());
// add to points counter
Integer lives = (Integer) getGlobalFlag("egoLives");
lives++;
setGlobalFlag("egoLives", lives);
}
}
/**
* implements behaviour of game when ego object is touching an enemy. It displays a text "AUAA!!"
* for a certain time on the game screen. Time span is defined as constant in
* {@link SpaceInvadersLevel#BONUS_DURATION } .
*
* @param enemy the enemy that was hit
* @param ego the ego object of the player
*/
void actionIfEgoCollidesWithEnemy(GameObject enemy, GameObject ego) {
/**
* set temporary text over ego. This object lives only for a short time. While it lives, further
* collisions are ignored. Otherwise a single collision would result in a lot of deducted
* points...
*/
double gameTime = this.getGameTime();
GameObject auaObj = this.getObject("AUA-EGO");
if (auaObj == null) {
addObject(new TextObject("AUA-EGO", this, ego.getX(), ego.getY() - 20, ego.getVX(),
ego.getVY(), "AUAA!!", 10, Color.RED)
.addController(new LimitedTimeController(gameTime, BONUS_DURATION)));
// deduct points
Integer pts = (Integer) getGlobalFlag("points");
setGlobalFlag("points", pts - 500);
}
}
/**
* implements what happens if the eog object of player is hit by a shot. It removes the shot from
* screen, reduces lives by 1, removes the ego and end current playing.
*
* @param eshot the shot object that hits the ego
* @param ego the ego object of the player
*/
void actionIfEgoObjectIsHit(GameObject eshot, GameObject ego) {
logger.debug("collision of " + eshot.getId() + " and " + ego.getId());
double gameTime = this.getGameTime();
this.deleteObject(eshot.getId());
Integer oldLives = (Integer) getGlobalFlag("egoLives");
int newLives = oldLives - 1;
setGlobalFlag("egoLives", newLives);
logger.debug("set egoLives to " + newLives + " (was " + oldLives + ")");
if (newLives <= 0) {
lost = true;
HighscoreManager.writeHSToFile((Integer) Playground.getGlobalFlag("points"),
(Integer) Playground.getGlobalFlag("highscore"));
}
LinkedList<GameObject> eshots = collectObjects("enmyShot", true);
for (GameObject _eshot : eshots) {
deleteObject(_eshot.getId());
}
setLevelFlag("detailedStatus", "dying");
setLevelFlag("t0", gameTime);
ego.setActive(false);
createExplosion(gameTime, ego, "egoexp", DYING_INTERVAL, Color.WHITE);
LinkedList<GameObject> enemies = collectObjects("enemy", false);
for (GameObject enemy : enemies) {
enemy.setY(0);
enemy.setActive(false);
}
}
}

82
GameProject/src/playground/SpaceInvadersLevelTest.java

@ -0,0 +1,82 @@
package playground;
import static org.junit.Assert.assertTrue;
import java.awt.Color;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import base.BuggyGame;
import gameobjects.EgoObject;
import gameobjects.GameObject;
import gameobjects.RectObject;
/**
* Tests {@link SpaceInvadersLevel} for
* <ol>
* <li>calcEnemySpeedX() returns the same value as constant SpaceInvadersLevel.ENEMYSPEEDX
* <li>calcEnemySpeedY() returns the same value as constant SpaceInvadersLevel.ENEMYSPEEDY
* <li>calcNrEnemies() returns the same value as constant SpaceInvadersLevel.NR_ENEMIES
* <li>actionIfEnemyIsHit() adds 200 points to score
* <li>actionIfEgoObjectIsHit() reduces number of lives (egoLives)
* </ol>
* @author jkonert
*
*/
class SpaceInvadersLevelTest {
private static SpaceInvadersLevel myLevel;
@BeforeAll
static void setUpBeforeClass() throws Exception {
myLevel = new SpaceInvadersLevel();
SpaceInvadersLevel.setGlobalFlag("egoLives", 5);
SpaceInvadersLevel.setGlobalFlag("points", 500);
SpaceInvadersLevel.setGlobalFlag("highscore", 5000);
}
@AfterAll
static void tearDownAfterClass() throws Exception {
// nothing
}
@Test
void testCalcEnemySpeedX() {
assertTrue("EnemySpeedX is as in SpaceInvadersLevel defined", myLevel.calcEnemySpeedX() == SpaceInvadersLevel.ENEMYSPEEDX);
}
@Test
void testCalcEnemySpeedY() {
assertTrue("EnemySpeedY is as in SpaceInvadersLevel defined", myLevel.calcEnemySpeedY() == SpaceInvadersLevel.ENEMYSPEEDY);
}
@Test
void testCalcNrEnemies() {
assertTrue("NrOfEnemies is as in SpaceInvadersLevel defined", myLevel.calcNrEnemies() == SpaceInvadersLevel.NR_ENEMIES);
}
@Test
void testActionIfEnemyIsHitPointsUp() {
Integer numPointsBefore = (Integer)myLevel.getGlobalFlag("points");
GameObject dummyShot = new RectObject("shot1", myLevel, 0,0,0,0, 12, 12, Color.WHITE);
GameObject dummyEnemy = new RectObject("ego1", myLevel, 0,0,0,0, 12, 12, Color.BLACK);
myLevel.addObject(dummyShot);
myLevel.addObject(dummyEnemy);
myLevel.actionIfEnemyIsHit(dummyEnemy, dummyShot);; // this is the call under test
Integer numPointsAfter = (Integer)myLevel.getGlobalFlag("points"); // changed?
assertTrue("numPoints is up +200 after EnemyIsHit", numPointsAfter == numPointsBefore + 200); // points are set +200 , check.
}
@Test
void testActionIfEgoObjectIsHitLivesDown() {
Integer numLivesBefore = (Integer)myLevel.getGlobalFlag("egoLives");
GameObject dummyShot = new RectObject("shot1", myLevel, 0,0,0,0, 12, 12, Color.RED);
GameObject dummyEgo = new EgoObject("ego1", myLevel, 0,0,0,0, 5);
myLevel.addObject(dummyShot);
myLevel.actionIfEgoObjectIsHit(dummyShot, dummyEgo); // this is the call under test
Integer numLivesAfter = (Integer)myLevel.getGlobalFlag("egoLives"); // changed?
assertTrue("numLives is reduced by one ifEgoIsHit", numLivesAfter == numLivesBefore - 1); // lives is reduced by one
}
}

93
GameProject/src/rendering/AnimationArtist.java

@ -0,0 +1,93 @@
package rendering;
import gameobjects.GameObject;
import playground.Animation;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.LinkedList;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
public class AnimationArtist extends Artist {
protected LinkedList<BufferedImage> imageArray;
protected LinkedList<Double> showtime;
protected double t0;
protected int loopFrame = 0;
protected double w = 0, h = 0, scale = 0;
protected String playmode = ""; // can be loop, forward, backward
private static Logger logger = LogManager.getLogger(AnimationArtist.class);
public AnimationArtist(GameObject go, Animation anim, double t0, String playmode, double scale) {
super(go);
this.imageArray = anim.getImageList();
this.showtime = anim.getShowtimeList();
this.t0 = t0;
logger.debug("AnimationArtist start = ") ;
this.loopFrame = 0;
for (int i = 0; i < imageArray.size(); i++) {
logger.trace("ANIMRAWWH="+imageArray.get(i).getWidth()) ;
this.w = Math.max(this.w, imageArray.get(i).getWidth());
this.h = Math.max(imageArray.get(i).getHeight(), this.h);
}
logger.debug("AnimationArtist RAW WH = "+this.w+"/"+this.h) ;
this.playmode = playmode;
this.w *= scale;
this.h *= scale;
}
public double getW() {
return w;
}
public double getH() {
return h;
}
@Override
public void draw(Graphics2D g) {
double i2 = this.getGameTime();
double elapsedTime = i2 - t0;
// Diff chance from sec to millis
double mseconds = (double) elapsedTime;
logger.debug("showtime= "+showtime.get(loopFrame)+" elapsed= "+elapsedTime+" i2= "+i2);
if (playmode.equals("loop")) {
if (mseconds >= showtime.get(loopFrame)) {
loopFrame++;
if (loopFrame >= imageArray.size())
loopFrame = 0;
t0 = i2;
}
}
if (playmode.equals("forward")) {
if (mseconds >= showtime.get(loopFrame) && loopFrame < imageArray.size() - 1) {
loopFrame++;
t0 = i2;
}
}
if (playmode.equals("backward")) {
if (mseconds >= showtime.get(loopFrame) && loopFrame > 1) {
loopFrame--;
t0 = i2;
}
}
g.drawImage(imageArray.get(loopFrame), (int) Math.round(this.getX() - w / 2.),
(int) Math.round(this.getY() - h / 2.), (int) w, (int) h, null);
}
}

40
GameProject/src/rendering/Artist.java

@ -0,0 +1,40 @@
package rendering;
import gameobjects.GameObject;
import java.awt.Graphics2D;
public abstract class Artist {
protected GameObject gameObject;
Artist(GameObject go) {
this.gameObject = go;
}
public double getX() {
return this.gameObject.getX();
}
public double getY() {
return this.gameObject.getY();
}
public double getVX() {
return this.gameObject.getX();
}
public double getVY() {
return this.gameObject.getX();
}
public double getGameTime() {
return this.gameObject.getGameTime();
}
public abstract void draw(Graphics2D g);
}

34
GameProject/src/rendering/CircleArtist.java

@ -0,0 +1,34 @@
package rendering;
import gameobjects.GameObject;
import java.awt.Color;
import java.awt.Graphics2D;
public class CircleArtist extends Artist {
protected double egoRad;
protected Color color;
public CircleArtist(GameObject go) {
super(go);
this.egoRad = 15.0;
}
public CircleArtist(GameObject go, double egoRad, Color color) {
super(go);
this.egoRad = egoRad;
this.color = color;
}
@Override
public void draw(Graphics2D g) {
g.setColor(this.color);
double x = this.getX();
double y = this.getY();
int posX = (int) (x - egoRad);
int posY = (int) (y - egoRad);
int rad = (int) (2 * egoRad);
g.fillOval(posX, posY, rad, rad);
}
}

33
GameProject/src/rendering/RectArtist.java

@ -0,0 +1,33 @@
package rendering;
import gameobjects.GameObject;
import java.awt.Color;
import java.awt.Graphics2D;
public class RectArtist extends Artist {
protected Color color;
protected double width, height;
public RectArtist(GameObject go, double width, double height, Color color) {
super(go);
this.color = color;
this.width = width;
this.height = height;
}
@Override
public void draw(Graphics2D g) {
g.setColor(this.color);
// g.drawLine((int) (Math.round(this.getX())), (int) (Math.round(this.getY()-this.height/2.)),
// (int) Math.round(this.getX()),
// (int) Math.round(this.getY() + this.height/2.));
int x = (int) this.getX();
int y = (int) this.getY();
g.fillRect((int) (this.getX() - this.width / 2.), (int) (this.getY() - this.height / 2.),
(int) this.width, (int) this.height);
}
}

81
GameProject/src/rendering/TextArtist.java

@ -0,0 +1,81 @@
package rendering;
import gameobjects.GameObject;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.text.AttributedString;
/**
* Rendering an object as a text of a specified color, size and font.
*/
public class TextArtist extends Artist {
private String text = null;
private int size = 1;
private Color textColor = null;
protected double textWidth, textHeight;
Font serifFont = null;
public TextArtist(GameObject go, String text, int size, Color textColor) {
super(go);
this.size = size;
this.text = text;
this.serifFont = new Font("Serif", Font.PLAIN, size);
FontRenderContext frc = new FontRenderContext(null, false, false);
this.textWidth = (int) (serifFont.getStringBounds(text, frc).getWidth());
this.textHeight = (int) (serifFont.getStringBounds(text, frc).getHeight());
this.textColor = textColor;
}
public String getText() {
return this.text;
}
public void setText(String s) {
this.text = s;
}
public double getTextWidth() {
return textWidth;
}
public void setTextWidth(double textWidth) {
this.textWidth = textWidth;
}
public double getTextHeight() {
return textHeight;
}
public void setTextHeight(double textHeight) {
this.textHeight = textHeight;
}
/**
* Draw the text.
*
* @param g The Swing graphics context.
*/
@Override
public void draw(Graphics2D g) {
AttributedString as = new AttributedString(this.text);
as.addAttribute(TextAttribute.FONT, this.serifFont);
as.addAttribute(TextAttribute.FOREGROUND, this.textColor);
g.drawString(as.getIterator(), (int) Math.round(this.getX() - this.textWidth / 2.),
(int) Math.round(this.getY() + this.textHeight / 2.));
}
}

54
GameProject/src/ui/AboutFrame.java

@ -0,0 +1,54 @@
package ui;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
/**
*
* Creates a JFrame with a short text about the creators of the game code.
*
*/
class AboutFrame extends Object {
private JFrame frame = new JFrame();
AboutFrame() {
JPanel aboutPanel = new JPanel();
JLabel text = new JLabel("About", JLabel.CENTER);
JTextArea field = new JTextArea(
"This game was developed by Prof. Gepperth and Prof. Konert and the students of module Programming 2. It contains several levels of SpaceInvadersLevel style and BreakoutLevel style. The framework below allows quick creation of different 2d game types.");
field.setBounds(25, 25, 80, 80);
field.setLineWrap(true);
field.setWrapStyleWord(true);
field.setBackground(aboutPanel.getBackground());
field.setEditable(false);
JButton ok = new JButton("OK");
ok.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent ae) {
frame.setVisible(false);
}
});
aboutPanel.setLayout(new BoxLayout(aboutPanel, BoxLayout.Y_AXIS));
aboutPanel.add(text);
aboutPanel.add(field);
aboutPanel.add(ok);
frame.add(aboutPanel);
frame.setSize(400, 400);
}
protected void show() {
frame.setVisible(true);
}
}

145
GameProject/src/ui/GamePanel.java

@ -0,0 +1,145 @@
package ui;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.HashMap;
import java.util.Stack;
import javax.swing.JPanel;
import playground.Playground;
import java.awt.event.*;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
/**
* UI element that holds the graphic visualization of current {@link Playground}. Registers key and
* mouse events.
*
*/
class GamePanel extends JPanel implements KeyListener, MouseListener {
private static final long serialVersionUID = 1L;
protected volatile boolean painting = false;
private Playground playground = null;
private HashMap<Integer, Integer> keys = new HashMap<Integer, Integer>();
Stack<KeyEvent> keyEvents = new Stack<KeyEvent>();
Stack<MouseEvent> mouseEvents = new Stack<MouseEvent>();
private static Logger logger = LogManager.getLogger(GamePanel.class);
GamePanel() {
super();
setPreferredSize(new Dimension(100, 100));
addKeyListener(this);
}
public void setSize(int sizeX, int sizeY) {
setPreferredSize(new Dimension(sizeX, sizeY));
}
@Override
public void repaint() {
super.repaint();
}
public void setPainting() {
painting = true;
}
public void waitWhilePainting() {
while (painting) {
}
}
Stack<KeyEvent> getKeyEvents() {
return keyEvents;
}
Stack<MouseEvent> getMouseEvents() {
return mouseEvents;
}
public void setPlayground(Playground pg) {
this.playground = pg;
if (this.playground != null) {
setPreferredSize(new Dimension(this.playground.getSizeX(), this.playground.getSizeX()));
setSize(getPreferredSize());
this.invalidate();
}
}
public boolean stillPainting() {
return painting;
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
logger.trace("my Playground: " + playground);
if (playground != null) {
playground.redraw((Graphics2D) g);
}
painting = false;
}
public HashMap<Integer, Integer> getCurrentKey() {
return keys;
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
logger.debug("pressed keyCode " + e.getKeyCode());
logger.trace(e.paramString());
this.keyEvents.push(e);
}
@Override
public void keyReleased(KeyEvent e) {
logger.debug("released keyCode " + e.getKeyCode());
this.keyEvents.push(e);
logger.trace(e.paramString());
}
@Override
public void mouseClicked(MouseEvent e) {
this.mouseEvents.push(e);
}
@Override
public void mouseReleased(MouseEvent e) {
// this.mouseEvents.push(e) ;
}
@Override
public void mousePressed(MouseEvent e) {
// this.mouseEvents.push(e) ;
}
@Override
public void mouseEntered(MouseEvent e) {
// this.mouseEvents.push(e) ;
}
@Override
public void mouseExited(MouseEvent e) {
// this.mouseEvents.push(e) ;
}
}

243
GameProject/src/ui/GameUI.java

@ -0,0 +1,243 @@
package ui;
import java.awt.Dimension;
import java.util.*;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.WindowConstants;
import playground.Playground;
import java.awt.event.*;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
/**
* creates the game UI JFrame containing a canvas (see class {@link GamePanel})) for the levels to
* paint the games. Has a menu for loading, saving games and an about menu item. two buttons allow
* restarting the game or exit.
*
*
*/
public class GameUI implements ActionListener {
private static volatile int newAction = -1;
/** the JFrame instance used in this window */
protected JFrame frame = null;
/** the panel holding all components, uses {@link BoxLayout} Y-Direction */
protected JPanel panel = null;
/**
* the instance of {@link GamePanel} for Playgrounds to paint and refresh their level elements. It
* is added as first component of {@link #panel}
*/
private GamePanel canvas = null;
/**
* the button panel holding buttons, set on {@link #panel} at last, uses {@link BoxLayout}
* X-Direction
*/
protected JPanel buttonPanel = null;
protected JMenuItem playItem;
protected JMenuItem loadItem;
protected JMenuItem saveItem;
protected JMenuItem quitItem;
protected JMenuItem aboutItem;
protected JMenu gameMenu;
protected JMenu helpMenu;
protected JButton button;
protected JButton button2;
public static final int ACTION_NEW = 1;
public static final int ACTION_LOAD = 2;
public static final int ACTION_SAVE = 3;
public static final int ACTION_RESET = 4;
public static final int ACTION_QUIT = 5;
public static final int ACTION_BUTTON = 6;
public static final int ACTION_PAUSE = 6;
public static final int ACTION_ABOUT = 7;
private static Logger logger = LogManager.getLogger(GameUI.class);
/**
* as typical for GUI classes this constructor creates all the components and adds them to the
* frame. It adds this instance as ActionListener for all components. See
* {@link #actionPerformed(ActionEvent)} for details. It directly sets the frame visible as a
* final step.
*
* <p>
* If you want to extend this GUI, create a subclass and add new elements in constructor. It is
* necessary to call {@link JFrame#revalidate()} on {@link #frame} attribute after changing/adding
* components to {@link #panel} or {@link #canvas}, because the constructor here already sets
* visibility to true and renders the IFrame.
* </p>
*
* @param sizeX pixel dimension wanted in x direction
* @param sizeY pixel dimension wanted in y direction
*/
public GameUI(int sizeX, int sizeY) {
// create a canvas on which the levels (Playgrounds) will be painted later when loaded and
// started.
this.canvas = new GamePanel();
// create contentPane
this.panel = new JPanel();
this.panel.setLayout(new BoxLayout(this.panel, BoxLayout.Y_AXIS));
// panel.setLayout(new FlowLayout());
// panel.setLayout(new GridBagLayout());
// panel.setLayout(new SpringLayout());
this.panel.add(canvas);
// create main window
this.frame = new JFrame("Prog2 GameProject!");
this.frame.setContentPane(panel);
// Menu
this.playItem = new JMenuItem("New Game");
this.loadItem = new JMenuItem("Restore game");
this.saveItem = new JMenuItem("Save game");
this.quitItem = new JMenuItem("Exit game");
this.playItem.addActionListener(this);
this.loadItem.addActionListener(this);
this.saveItem.addActionListener(this);
this.quitItem.addActionListener(this);
this.gameMenu = new JMenu("Game");
this.gameMenu.add(playItem);
this.gameMenu.add(loadItem);
this.gameMenu.add(saveItem);
this.gameMenu.addSeparator();
this.gameMenu.add(quitItem);
this.helpMenu = new JMenu("Help");
this.aboutItem = new JMenuItem("About");
this.helpMenu.add(this.aboutItem);
// for extending the code change here to your own class/implementation of an ActionListener or
// extend method this.actionPerformed
this.aboutItem.addActionListener(this);
JMenuBar mb = new JMenuBar();
mb.add(this.gameMenu);
mb.add(this.helpMenu);
frame.setJMenuBar(mb);
this.button = new JButton("(Re)Start");
this.button.addActionListener(this);
this.button2 = new JButton("Pause");
this.button2.addActionListener(this);
this.buttonPanel = new JPanel();
this.buttonPanel.setLayout(new BoxLayout(this.buttonPanel, BoxLayout.X_AXIS));
this.buttonPanel.add(this.button);
this.buttonPanel.add(this.button2);
this.panel.add(this.buttonPanel);
// make it visible (render window)
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
this.canvas.setVisible(true);
frame.pack();
frame.setVisible(true);
}
public HashMap<Integer, Integer> getCurrentKey() {
return canvas.getCurrentKey();
}
public Stack<KeyEvent> getKeyEvents() {
return this.canvas.getKeyEvents();
}
public Stack<MouseEvent> getMouseEvents() {
return this.canvas.getMouseEvents();
}
public void repaint() {
canvas.repaint();
}
public void setPlayground(Playground pg) {
this.canvas.setPlayground(pg);
this.frame.validate();
this.frame.pack();
}
public boolean isPainting() {
return canvas.stillPainting();
}
public void setPainting() {
canvas.setPainting();
}
public void waitWhilePainting() {
canvas.setPainting();
canvas.repaint();
canvas.waitWhilePainting();
}
public static int getNewAction() {
return newAction;
}
public static void resetAction() {
newAction = -1;
}
public void grabFocus() {
canvas.grabFocus();
}
/**
* interface implementation of ActionListener to respond to GUI element actions. It sets the
* attribute of newAction which is read by GameLoop.runGame to check for GUI actions.
*/
public void actionPerformed(ActionEvent ae) {
if (ae.getSource() == this.quitItem) {
System.exit(0);
} else if (ae.getSource() == this.playItem) {
logger.info("new game");
newAction = ACTION_NEW;
} else if (ae.getSource() == this.button) {
logger.info("click");
newAction = ACTION_NEW;
} else if (ae.getSource() == this.button2) {
logger.info("click2");
newAction = ACTION_PAUSE;
} else if (ae.getSource() == this.saveItem) {
logger.info("save");
newAction = ACTION_SAVE;
} else if (ae.getSource() == this.loadItem) {
logger.info("load");
newAction = ACTION_LOAD;
} else if (ae.getSource() == this.aboutItem) {
logger.info("about");
newAction = ACTION_ABOUT;
AboutFrame about = new AboutFrame();
about.show();
}
}
}

BIN
GameProject/video/alexG.jpg

After

Width: 1664  |  Height: 2496  |  Size: 181 KiB

BIN
GameProject/video/alien (Custom).png

After

Width: 70  |  Height: 76  |  Size: 6.8 KiB

BIN
GameProject/video/alien.png

After

Width: 278  |  Height: 300  |  Size: 55 KiB

1
GameProject/video/alien.txt

@ -0,0 +1 @@
alien.png 1,0

BIN
GameProject/video/alien2.png

After

Width: 1183  |  Height: 1475  |  Size: 673 KiB

3
GameProject/video/bored.txt

@ -0,0 +1,3 @@
boredAlien0.png
boredAlien1.png
boredAlien2.png

BIN
GameProject/video/boredAlien0.png

After

Width: 300  |  Height: 236  |  Size: 37 KiB

BIN
GameProject/video/boredAlien1.png

After

Width: 300  |  Height: 236  |  Size: 30 KiB

BIN
GameProject/video/boredAlien2.png

After

Width: 300  |  Height: 236  |  Size: 37 KiB

2
GameProject/video/heart.txt

@ -0,0 +1,2 @@
heart0.png 0,5
heart1.png 0,5

BIN
GameProject/video/heart0.png

After

Width: 138  |  Height: 130  |  Size: 1.4 KiB

BIN
GameProject/video/heart1.png

After

Width: 138  |  Height: 130  |  Size: 1.4 KiB

BIN
GameProject/video/player.png

After

Width: 100  |  Height: 175  |  Size: 12 KiB

1
GameProject/video/player.txt

@ -0,0 +1 @@
player.png 1,0

31
GameProject/video/relaunch.txt

@ -0,0 +1,31 @@
verzstrultur anpassen
frage nach laden/speichern von zuständen. alle sonstigen vaeiablen persistent
einfah plaxground.flags und gameObejcts wegsoeichern er JSON. muss nur member annotieren dann passt das einfach.
collider-struktur überdenken buw dokumentieren
gameobject: wrapper, nur Darstellung wird hier definiert. Ableitungen ändern nur Darstellung sonst nichts
controller
list<Collider>
x,y,vx,vy
collisionMode RAUS
neben __init__ auch setup/reset methode
warum braucht collider zugriff auf controller und playground??
playground:
OK mergr with keyboardController
neben __init__ auch setup/reset methode
OK elegantere Lösung wie gameMain auf keys der LEvels zugreift

8
GameProject/video/sweetAlien.txt

@ -0,0 +1,8 @@
sweetAlien0.png 0,2
sweetAlien1.png 0,5
sweetAlien2.png 0,2
sweetAlien3.png 0,5
sweetAlien4.png 0,4
sweetAlien5.png 0,2
sweetAlien6.png 0,3
sweetAlien7.png 0,5

BIN
GameProject/video/sweetAlien0.png

After

Width: 69  |  Height: 97  |  Size: 4.1 KiB

BIN
GameProject/video/sweetAlien1.png

After

Width: 69  |  Height: 97  |  Size: 3.8 KiB

BIN
GameProject/video/sweetAlien2.png

After

Width: 69  |  Height: 97  |  Size: 4.3 KiB

BIN
GameProject/video/sweetAlien3.png

After

Width: 69  |  Height: 97  |  Size: 3.4 KiB

BIN
GameProject/video/sweetAlien4.png

After

Width: 69  |  Height: 97  |  Size: 3.4 KiB

BIN
GameProject/video/sweetAlien5.png

After

Width: 69  |  Height: 97  |  Size: 3.4 KiB

BIN
GameProject/video/sweetAlien6.png

After

Width: 69  |  Height: 97  |  Size: 3.4 KiB

BIN
GameProject/video/sweetAlien7.png

After

Width: 69  |  Height: 97  |  Size: 3.9 KiB

1
GameProject/video/test.txt

@ -0,0 +1 @@
1 071 1.23 Dies 0xF3 ist 1,4 ein0.png Test

6
gui/.classpath

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>

1
gui/.gitignore

@ -0,0 +1 @@
/bin/

17
gui/.project

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>GuiProjectW11</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

14
gui/.settings/org.eclipse.jdt.core.prefs

@ -0,0 +1,14 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=12
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=12
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=enabled
org.eclipse.jdt.core.compiler.source=12

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save