Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ package-lock.json
package.json

.hugo_build.lock
hugo.exe
Binary file not shown.
76 changes: 76 additions & 0 deletions content/game-dev/part-iii/MakingLevels.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
+++
title = "Making Levels"
weight = 4
+++

---

{{%attachments style="blue" title="Powerups Template Code" /%}}

### Making Levels

Now that we are in the 3rd session, some levels in the game are long overdue. Now that we are more familiar with the concept of arrays and arraylists, let’s see how we can implement them

{{% notice info "Key Concepts" %}}

1. How do we represent a level in code? And how do we store information for it?
2. What happens when a level is being played, and when a level isn't being played?
3. How can we generate levels easily?

{{% /notice %}}

##### Representing a level in code

Before we get to coding anything, we must understand what a level actually is. In bloons tower defense, levels are best defined by the balloon wave that you must pop. For example, in level 1, there are weak balloons that you must pop. In level 2, the balloons are stronger and more numerous. This trend continues as you move to higher and higher levels.

We already have 1 default level that starts immediately when you open the game. What we need to do now is make many distinct levels, and have some control over when each level starts.

##### Making many levels.

As you might have seen from previous lessons, a single balloon is represented by an array of floats, which is a list of values that contain information about the position and state of the balloon. These float arrays were then stored in a larger, master array, which contained all the “balloons” for the default level.

You can thing of it kind of like a folder/file system

![Interface](/img/ArrayVisual1.png)

Now, you could say we need a larger “folder” to store many level folders that contain balloon data. In other words, we need a bigger container that will store each level

![Interface](/img/ArrayVisual2.png)

To do this, we can make some new arrays similar to the default level, and put all of those arrays into a larger array, called levels. We can represent this structure using a 3D array.

```Java
ArrayList<ArrayList<float[]>> levels = new
ArrayList< ArrayList< float[]>>();
/*Main “levels folder” //A Level folder //List of balloon
which can store many which can store information that
levels many balloons defines a balloon
*/
```

We also need to control what can happen when a level is and isn't being played. For that, we have a boolean isPlaying variable, which disables certain features when false. For example, if a person is not playing yet, the code for _tower tracking_ and _projectile creation_ are essentially turned off, which makes sense since there are no balloons to turn to and shoot new projectiles at. The isPlaying variable is turned to true when the “next level” button pressed, and false when there are no more balloons in the level.

##### Levels API

Now, a way to quickly create balloons for a level would be very nice if you wanted to make a playable game. For that, we have another, very simple API which you can use in the createWaves function in Balloons.pde.

```java
void createWaves(){
//1
createLevels(numberOfLevels);

//2
createBalloons(
levelTheBalloonsAreFor,
numberOfBalloons,
delayOfTheFirstBalloon,
delayBetweenBalloons,
speedOfTheBalloons,
hP
);
}
```

The first function, **createLevels()**, will help determine how many levels there are in your game. Once you have called/used this function, you can call the **createBalloons()** function to make balloons for a specific level. First, you specify the level you want to assign balloons to (starting from 0, since we are dealing with arrays!). Then you specify the number of balloons and the first balloon’s delay, which essentially controls when this collection of balloons will appear on the screen. Then you can tell the function the delay between the balloons, which controls how spaced apart they are, and also the speed and hp of each balloon.

And there you go! Now you can create your own levels!
Binary file not shown.
80 changes: 80 additions & 0 deletions content/game-dev/part-iii/PathCreationAPI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
+++
title = "Path Creation API"
weight = 4
+++

---

{{%attachments style="blue" title="Powerups Template Code" /%}}

### Path Creation API

Before this, balloons have moved in straight lines. That’s cool and all, but what if we wanted to implement some maps like this?

![Interface](/img/PathAPI/RealPathExamples.png)

Obviously, straight lines are not going to cut it. Instead, we need a way to implement **curves**, and we need to have some tools that will make it easy for us to smoothly connect them. As a result, we developed an API (a set of useful functions) that will help us do this.

```java
//1
addLine(startPointX,startPointY,endPointX,endPointY);

//2
addArc(startPointX,startPointY,centerPointX,centerPointY, angleOfRotation);

//3
addSmoothLine(lineLength);

//4
addSmoothArc(distanceAwayFromPathTip, angleOfRotation);
```

{{% expand "addLine(startPointX, startPointY, endPointX, endPointY);" "false" %}}
This is the most straightforward function. Just define a line with 2 points - a start and end point - and it will draw it for you.

Eg. addLine(0,200,300,200); gives:
![Interface](/img/PathAPI/AddLine.png)
{{% /expand %}}

{{% expand "addArc(startPointX, startPointY, centerPointX, centerPointY, angleOfRotation);" "false" %}}

To draw a circular path segment using this function, you must first define where the segment starts - startPoint. Then, you define the center point of the arc (centerPoint), which is the point from where the arc is curved around. Finally you have an angle of rotation, which determines how much curve is in the arc.

Eg. addArc(0,200,200,100,-radians(90)); gives:
![Interface](/img/PathAPI/AddArc.png)

**Note**: The _radians()_ function simply converts angles from degrees to radians, which is perferred by Processing. Also, the point you see in the center of the arc is just for understanding - it will not appear when you are making your own path.
{{% /expand %}}

{{% expand "addSmoothLine(lineLength);" "false" %}}

This function is used to extend the paths of arcs in a smooth way. The lineLength argument (or input) is used to describe how far the arc should be extended

Eg.

addArc(0,200,200,100,-radians(90));

addSmoothLine(200);

gives:
![Interface](/img/PathAPI/AddSmoothLine.png)
{{% /expand %}}

{{% expand "addSmoothArc(distanceAwayFromPathTip, angleOfRotation);" "false" %}}

This function draws a smooth arc from an already existing line or arc. To understand it, you must imagine an arrow that starts from the path’s end, and points in a direction so that it is a +90 degree rotation from the path.

![Interface](/img/PathAPI/AddSmoothArcBefore.png)

{{% notice info "Processing Angles"%}}
Remember that Processing angles start from east and increase counterclockwise. This should be taken into account when imagining the arrows.
{{%/notice%}}

_distanceAwayFromPathTip_ describes how far away the **center** of the arc should be from the tip of the path, along the line described by the red arrow. If you would like to have the center of the arc be in the opposite direction of the arrow, simply put a negative value.

//addSmoothArc(100,radians(105)); and addSmoothArc(-100,-radians(105)); respectively give:
![Interface](/img/PathAPI/AddSmoothArcAfter.png)

As you can see, the addSmoothCurve() function can be used with other arcs to make more complex curves. You can also put many addSmoothCurve()’s together to make interesting shapes.

{{% /expand %}}
193 changes: 193 additions & 0 deletions content/game-dev/part-iii/Upgrades.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
+++
title = "Tower Upgrades"
weight = 2
+++

---

{{%attachments style="orange" title="Upgrades Template" /%}}

### Introduction
Personally, the part I enjoyed most about Bloons Tower Defence is trying out each path for each tower to see what they all do. Now, in Part III, we’ve finally implemented upgrades. We’ll be teaching you how to edit the different values that you may want to use when making your own upgrade paths.

### Framework for Upgrades

Before making upgrades, we need to add a state that stores the value of the tower currently clicked. Think back to BTD and how clicking on a tower will show the upgrades that you can get. For our game, we want to have a similar thing so that we don’t end up upgrading all towers at the same time.

To do this, let’s add a new variable called `towerClicked`. If we set no tower to -1, then our tower will have a default value of -1 and will also become -1 when we click on anything that is not a tower. To check. Here is the implementation of `towerClicked`:

{{% expand "See code" "false" %}}
```Java
int towerClicked = -1; //no tower clicked
void towerClickCheck() {
if (mousePressed) {
towerClicked = -1;
}
for (int i = 0; i < towers.size(); i++) {
float xPos = towers.get(i).x, yPos = towers.get(i).y;
if(pointRectCollision(mouseX, mouseY, xPos, yPos, towerSize) && mousePressed) {
// Drawing the tower range visually
towerClicked = i; //clicked the ith tower, from indices 0 to towers.size() - 1
}
}
}
```
{{% /expand %}}


Now, let’s add an extra set of values to our `towerData` integer arrays that represent our tower’s current upgrade level. Every time we upgrade our current tower, we increment that tower’s upgrade index in its respective integer array in `towerData`.

{{% expand "See code" "false" %}}
```Java
final int cooldownRemaining = 0, maxCooldown = 1, towerVision = 2, projectileType = 3, upgrade = 4;
//initial values of 1 for upgrade since they are level 1
int[] makeTowerData(int towerID) {
if (towerID == def) {
return new int[] {
10, // Cooldown between next projectile
10, // Max cooldown
towerVisions[def], // Tower Vision
0, // Projectile ID
1
};
} else if (towerID == eight) {
return new int[] {
25, // Cooldown between next projectile
25, // Max cooldown
towerVisions[eight], // Tower Vision
1, // Projectile ID
1
};
} else if (towerID == slow) {
return new int[] {
35,
35,
towerVisions[slow], // Tower Vision
2,
1
};
}
return new int[] {}; //filler since we need to return something
}
```
{{% /expand %}}

To do this, we need to create an `upgradeCheck()` method that checks if you click the upgrade button. To check for upgrades, we just need to check if there is a valid tower under `towerClicked` and if we click on the button that will represent our upgrade. If we do click on it, then we’ll take the value of the towerData at `towerClicked` and edit the values there. We increment the index representing upgrades, which will affect the types of upgrades we do. For this workshop, we decided that upgrades would cost half the amount of the tower cost, but you can play around with this value.

{{% expand "See code" "false" %}}
```Java
if (currentBalance >= towerPrice[temp[projectileType]] / 2) {
temp[upgrade]++; currentBalance -= towerPrice[temp[projectileType]] / 2 ;
}
```
{{% /expand %}}

### Different Upgrades

For upgrades, our default tower first increases its attack speed and then increases its damage by one every upgrade after that. For our eight shot tower, it first increases its range, then the number of shots from 8 to 16, then increases the damage. For our slow tower, the first upgrade will increase the slow from 70% of the balloon’s original speed to 50%, and then it will increase the range for every update after that. While some of the updates are easily done by editing values in `towerData`, other values require for us to go into `Projectile.pde` and make a few edits.

To increase the attack speed, we just need to reduce the `maxCooldown` of the tower in the towerData. Increasing the range just requires us to increase our value at `towerVision`. Here is the implementation:

{{% expand "See code" "false" %}}
```Java
int[] temp = towerData.get(towerClicked);
temp[maxCooldown] = 8; //increases attack speed
temp[towerVision] += 50; //increases range
```
{{% /expand %}}

Increasing damage, number of shots, and slow reduction is a bit harder. Let’s look at damage first. To make things simpler, we decided to create variables to represent the default damage of our towers without any upgrades.

{{% expand "See code" "false" %}}
```Java
int defdmg = 6, eightdmg = 4, slowdmg = 1;
```
{{% /expand %}}

Notice how every time we want to shoot a projectile, we create a new amount of damage for that projectile that represents how much damage that projectile does. Usually it’s just `damage = defdmg`, but let’s change this up a bit to make it better. Recall that we have a value that represents our upgrade level in `towerData`. For our situation, since it is always adding more damage including and after the 2nd upgrade, we can set a new equation representing what our new value for damage should be:

{{% expand "See code" "false" %}}
```Java
if (data[upgrade] >= 3) { //notice that it’s 3 since we are level 3 at the second upgrade
damage = defdmg + data[upgrade] - 2; //we subtract 2 because at level 3, we want to add 1 damage
}
```
{{% /expand %}}

The same logic can be applied to our number of shots and slow percent. We can set a default number of shots and slow percent, and edit those values depending on the upgrade level. The implementation is shown below:

{{% expand "See code" "false" %}}
```Java
//eight shot implementation
int shots = 8;
int curShots = shots;
if (data[upgrade] >= 3) {
curShots = shots + 8;
}
//slowing implementation
float slowPercent = 0.7;
float slowNum = slowPercent;
if (data[upgrade] >= 2) {
slowNum -= 0.2;
}
```
{{% /expand %}}

Here is our final upgrade check, including checking whether you click the button and upgrading things accordingly:

{{% expand "See code" "false" %}}
```Java
void upgradeCheck() {
if((upgradeLocation.x - 43 <= mouseX && mouseX <= upgradeLocation.x + 43 && upgradeLocation.y - 12 <= mouseY && mouseY <= upgradeLocation.y + 12) && mousePressed && towerClicked != -1) {
int[] temp = towerData.get(towerClicked);
if (currentBalance >= towerPrice[temp[projectileType]] / 2) {
temp[upgrade]++; currentBalance -= towerPrice[temp[projectileType]] / 2 ;
if (temp[projectileType] == 0) {
if (temp[upgrade] == 2) { //first upgrade
temp[maxCooldown] = 8; //increases attack speed
}
} else if (temp[projectileType] == 1) {
if (temp[upgrade] == 2) { //second upgrade
temp[towerVision] += 50;
}
} else if (temp[projectileType] == 2) {
if (temp[upgrade] > 2) {
temp[towerVision] += 50;
}
}
towerData.set(towerClicked, temp);
println("tower number: " + (towerClicked + 1) + ", upgrade level: " + temp[upgrade]);
}
}
}
```
{{% /expand %}}


### Getting a Tower’s Damage from Projectile Type
{{% expand "See code" "false" %}}
```java
int dmgFromProjectileType(int type, int[] temp){
if(type==0) {
int ret = defdmg;
if (temp[upgrade] >= 3) {
ret += temp[upgrade] - 2;
}
return ret;
}
else if(type==1) {
int ret = eightdmg;
if (temp[upgrade] >= 4) {
ret += temp[upgrade] - 3;
}
return ret;
}
else if(type==2) {
return slowdmg;
}
return 0;
}
{{% /expand %}}
In this return method, we calculate the damage of a tower based on the projectile type and the tower’s current level, then return it. We take in two parameters, `type` which is the projectile’s type, as well as the temp array which is the tower’s data. We have three types of projectiles, so we will create three if/else if blocks. The first block will be `type==0`, which means that it is a “default” projectile. The `ret` variable will store the tower’s damage, and it is initialized as the default projectile’s level 1 damage. We can calculate the updated damage by adding 1 point of damage for every level that the tower is above 3. We can then return the tower’s damage by returning the `ret` variable.
This is similar for the projectile that shoots in 8 directions, and the one that slows. For `type==1`, we set ret to the default level 1 damage for this projectile `eightdmg`, add damage based on the level, and then return the updated damage value.
For the last type of projectile that slows balloons, the if block is even simpler since the upgrades do not affect the damage, but instead change the amount it slows by. This means that we can set ret to the original damage value and then return it. Outside this method, we have a `return 0;` statement because the method needs a ‘default’ return statement. This statement won’t be reached in our code, since type can only be 0,1 or 2.
1 change: 1 addition & 0 deletions content/game-dev/part-iii/_index.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
+++
chapter = true
alwaysopen = false
title = "Part III"
pre = "<b>3.</b> "
weight = 3
Expand Down
27 changes: 27 additions & 0 deletions content/game-dev/part-iii/challenge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
+++
title = "Challenges"
weight = 6
+++
---
## Challenge Prompt

Congratulations on making it to the end of the series!

To celebrate, as well as help you prepare for the Game Jam, we'd like to test your game development skills and see how well you can make this game _your own_!

{{% notice info "Challenge" %}}
Create your own spin-off of the game! This is more or less an open-ended question, you are free to do whatever you want.

The first place winner will earn **400** points, with second place earning **375**, third place earning **350**, etc... with a minimum of **100** points being awarded for successfully attempting the challenge, **on top** of the **200** points for participation.

Placements will be determined by a combination of creativity, number features implemented / changed, as well as _balancing_ of the game; making sure the game is not too easy or too hard.

We suggest any of the following:
* Creating a new path
* Creating a new power-up
* Creating a unique set of upgrades
* Modifying balloon waves / creating new waves for progression

Submit a GitHub / Google Drive link to the form that will be provided during the workshop to earn points!
{{% /notice %}}

Loading