Tower Defense is a captivating game that challenges players to strategically place towers to defend their base from waves of relentless enemies. This game is still an MVP. However, with further development, and with a variety of towers, enemies, and maps, players will find themselves immersed in a world of strategic decision-making and excitement.
- Clone the repository
- Open the project in your desired IDE (IntteliJ, Eclipse or else)
- run
gradle build
- run
gradle run
- click start button in separate window
- play and have fun
- User-friendly Interface: Navigate the game with ease through an intuitive and visually appealing interface.
- Purchase Towers: Buy towers and place them on strategically optimal spots.
- Upgrade Tower: Upgrade existing towers to more powerful ones.
- Buy Health: Buy health function to invest your earned money into your health recovery.
- Pause & Attack Phases: Pause phase without enemy attacking, and different attack phases with increasing level of difficulties.
- Java SDK 17
- Gradle Version 8.0.1
- Clean Code Convention
UI Thread GameThread
โโโโโโโโโโโโโโโโโโโโโโ
โ โ
โ GameView.fxml โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโ
โฒ
โ Displays
โ
โโโโโโโโโโโดโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ
โ โ โ โ โ โ
โ GameViewController โโโโโโฌโโโโโบโ GameState โโโโโโฌโโโโโค Game โ
โ โ โ โ โ โ โ โ
โโโโโโโโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโโโโโโโโโ โ โโโโโโโโโโโโโโโโโโโโโโโ
โ โ
Observes Updates
The MVC model communicates with the Observer Pattern.
- GameState holds all the properties that are needed to display in the UI
- The Game class is the Game Thread. It simulates the game rules and physics and updates the Gamestate accordingly
- GameViewController displays the given information of the GameState accordingly.
When Rendering on the canvas the UI Thread is awaited because otherwise to many requests would clog up the UI Thread with the
Platform.runLater(...)
function.
The Game Thread is the main loop of the game. It calls all the important function like
render()
andupdate()
.
The Game Thread is structured like the following:
double previous = getCurrentTime();
double lag = 0.0;
while (true)
{
double current = getCurrentTime();
double elapsed = current - previous;
previous = current;
lag += elapsed;
processInput();
while (lag >= MS_PER_UPDATE)
{
update();
lag -= MS_PER_UPDATE;
}
render();
}
- The
update()
method is only called when the given time per update (100ms) is elapsed. - The
render()
method is always called and renders as many frames as possible
In our case the
render()
method sets the GameState to trigger a render and awaits for the render to finish.
This was implemented in the following PR #48
Sprite means: In computer graphics, a sprite is a two-dimensional bitmap that is part of a larger scene (e.g., a 2D video game). Sprites can be static images or animated graphics. source
The Sprite System loads images of for the given SpritePath
enum. Here the number of variant of the same image and amount of images (more than 2 is animated).
It has its own timer which changes the current image to display in a given interval.
Through this system animations are enabled
This is how the code works:
public class Sprite {
private List<Image> sprites;
private int intervalMs = 300;
private int index = 0;
private long lastUpdate = 0;
public Sprite(SpritePath sprite) {
...
intervalMs = randomUtil.getRandomInRange(sprite.minAnimationTimer, sprite.maxAnimationTimer); // not all animations play at the same speed
lastUpdate = randomUtil.getRandomInRange(sprite.minAnimationTimer, sprite.maxAnimationTimer); // they dont start synchronized
...
}
public Image getSprite() {
if (System.currentTimeMillis() - lastUpdate >= intervalMs) {
continueIndex();
lastUpdate = System.currentTimeMillis();
}
return sprites.get(index);
}
...
getSprite()
returns the image which should be displayed of the whole set and is incremented, if an interval was passed.
Example sprite of enemy walking
The Rendering System is responsible for visualizing elements on the canvas. What classes can be renderd is defined by the
Rendreable
interface.
The rendering is called each game.loop()
. As the Rendering has to be done in the UI Thread the GameThread( game.loop()
) is waiting. This is the case, so that the rendering does not spam the UI Thread until it dies and the calling of the render function doesn't have to be locked to a fixed number.
โโโโโโโโโโโโโโโโโโโโ
โ โ
โ Game.loop โ
โโโโโโโโโโโโโโโโโโโโโโ โ โ
Render in UI Thread โ โ โ update โ
โโโโโโโโโโโโโโโโโโโโโโ โ Game State โ Set Val and Waits โ โ
โ โ โ โ โ โ
โ UI Thread โโโโโโโโโโโโโโโโโโโโโโโโโโค RenderNeeded:Bool โ โโโโโโโโโโโโโโโโโโโโโโผโโsetRender(True) โ
โ โ โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโผโโโโบ continues.. โ
โโโโโโโโโโโโโโโโโโโโโโ On finish โ โ
โ โ
โ โ
โโโโโโโโโโโโโโโโโโโโ
This graph is from PR #52
The following classes implement the Rendreable
interface:
The Rendreable
interface enforces the render(Canvas canvas)
method on it's implementers. Here an Example:
@Override
public void render(Canvas canvas) {
var g2d = canvas.getGraphicsContext2D();
g2d.drawImage(sprite.getSprite(), position.getX() - width / 2, position.getY() - height / 2,
width, height);
g2d.setFill(Color.LIGHTGREEN);
g2d.fillRect(position.getX() - width / 2, position.getY() - height / 2-10,
(((double) width / 100) * health), 2);
}
Here the sprite is drawn with the position of the enemy being in the middle of the Image (hence the - width / 2
and - height / 2
).
The health of the enemy is also represented by a green Rectangle drawn with it's width being relative to the enemies health.
The top element of the render chain is the Game class which starts the rendering. Here is the order and hierarchy of the rendering process.
The Update System is responsible for updating the physics and businessLogic of the Game in a predifined interval.
The update system has the following steps:
His is a overview of all steps done in the
update()
method. Some of them will be explained further in the next points.
The system that dictates, when, where and how many enemies are spawned and are on the game field.
- The
currentPhase
says how many enemies have to be on the field.
while (enemies.size() < currentPhase.getEnemyAmount()) {
spawnEnemy();
}
- When spawning a random Point is selected where no enemy is yet. If an Enemy is already on the generated position the enemy is shifted left (
position.x -= 50
), if there is another enemy, the position is further shifted until no enemy is at the same position:
private void spawnEnemy() {
int radius = path.getRadius();
int y = (int) path.getStart().getY();
var startY = RandomUtil.getInstance().getRandomInRange(y - radius, y + radius);
var startX = RandomUtil.getInstance().getRandomInRange(-200, 0);
var newPost = new Point2D(startX, startY);
while (interSects(newPost)) {
newPost = newPost.add(-50, 0);
}
enemies.add(new Enemy(newPost));
}
the spawn position is always of screen so the spawning of the enemy is not visible to the player.
The crowd behaviour is based on a task of the book: The Nature of code by Daniel Shiffman Exercise 6.14, Chapter 6.12 Combinations
The system that defines how many enemies are present in the game.
The phase system has two phases
- Attack phase here enemies spawns corresponding to the attack pattern
- Pause phase no enemies spawn. The player has a chance to recover
The patterns are defined through a base level of enemies that attack the player.
patterns.add(new int[] { 2, 2, 3, 4, 3, 3, 2 });
patterns.add(new int[] { 2, 2, 2, 5, 2, 2 });
patterns.add(new int[] { 3, 3, 3, 2, 2, 2 });
patterns.add(new int[] { 2, 2, 2, 2, 4, 2 });
patterns.add(new int[] { 2, 3, 3, 4, 2 });
A feature branch was generated for each task. If the feature was completed, a pull request was created. This had to be reviewed and approved by another team member so that the code from the feature branch could be merged into the main branch.
Whenever there was a change, it was consequently merged via a pull request. Suggestions for improvements were either commented within the pull request or discussed in person.
Example: Pull-Request
Not every pull-requests have discussion/improvement points in it, as if the reviewer agreed with the proposed changes, he immediately approved it.
- Challenging Gameplay: Engage in an immersive tower defense with increasingly challenging levels, pushing your strategic thinking to the limit.
- Multiple Tower Types: Choose from a wide variety of towers, each with unique abilities, upgrades, and attributes.
- Diverse Enemy Types: Face off against an array of different enemies with unique abilities and strategies, keeping you on your toes.
- Customizable Maps: Play on various maps with distinct terrain, obstacles, and paths, providing new challenges and opportunities.
- Achievements and Leaderboards: Unlock achievements as you progress and compare your scores with players from around the world.
Tower Defense is developed by Team 5 java.lang.NullPointerException.
If you have any questions, suggestions or concerns, please feel free to contact us:
- Pascal Kรผng ([email protected])
- Manuel Strenge ([email protected])
- Micha Mettler ([email protected])
- Dominik Hartmann ([email protected])