Into the caverns we go!

Greetings, adventurer!

Today, we’re going to create some caverns. Correction, we’ll make the computer create these caverns for us. that’s right it’s randomly generated dungeons.

I want to make it very clear that there are many ways to do this. You can for instance use TinyKeepDev method. There is also this fantastic presentation by Zach Aikman of 17-BIT, which I highly recommend.

We went for a fairly simple implementation. The first thing we did was to create a 2d int array map which hold either 1 or 0. 1 means wall, 0 means floor. We then fill it, using a fill percent variable which dictates the amount of wall percentage to fill in the array. Something to note is that we want a special if statement to cover the borders, such that the cavern has borders. Moving on, we now  have a 2d array map[x,y] with 0’s and 1’s.

What do we do with it? We then run it through a modified version of Conway’s game of life in order to smooth the map a couple of times and we only need to draw the map. We now have a map with potentially many different spaces. In this context we define a space as an area of floor tiles.We need the largest such space and to do that we run a depth first search where each tile is a vertex. Now all we need is spawning “stuff” (monsters, treasure chests, etc…) and we’re done.

Now into some detail and code.

Let’s start with the creation of the map.

createMap

Now, you might think that this randomiser is an overkill, but well… Why not? Anyway, the principle is simple.

for each pair of coodinates x,y :

if map[x,y] is a border -> make it a wall n make it a wall

else -> if it’s below the fillPercent variable, make it a wall, else make it a floor.

Earlier I mentioned The Game of Life, here it is.

smooth and getAdj

Now that we have a map, we need to determine the largest space. It’s a very standard implementation of DFS (Depth First Search) using recursion. You can use stack as well. However we also need to loop through the graph.

runthrough

I love traversing a grid like graph, it’s just so cozy and intuitive.

DFS

All we need now is to draw the map.

drawmap

The position is x*0.32, y*0.32 because we’re using 32 bit sprites.

That’s all for today, hope you found this educational and and enjoyable. I actually over complicated things a bit, however I did so because I wanted a set of features like storing the most southern tile in the largest space in a cavern as a spawning point for the player, much of this could be done in far simpler manners if your game doesn’t need the same features as ours.

Best Regards

p219 Development Team

A deeper look at the enemies

Greetings, adventurer.

Today, we are taking a deeper look into the enemies. How they are built, how they behave and then we will take a look at the scripts.

For this post, I ‘am going to create a new enemy from scratch, while trying to reuse general scripts.

Today I will show you how to create “Ghost”, a completely new enemy that I will create as I write this. Ghost’s ability will be partly invisible. It will do damage on impact, and move to another location and attack again. A dungeon nightmare with other words.

Step 1: Manage the sprites

To begin with, we need a sprite. We have downloaded free sprites from LPC. The first thing we have to do, is slicing the sprite. The sprite is represented as a “.png” file. To slice the sprite, I highlight the sprite, select sprite mode: multiple and press “Sprite editor”. From here we have to do a little try and fail to slice the sprite correctly. Sometimes it slices automatically correct, but sometimes we have to manually try different slice sizes to find the correct one. In this case, I had to slice it manually with pixel size 40×46.

2015-11-17

Step 2: Creating animation

After the slicing, I now have 12 different pictures that represent the enemy. I now have to create animations. In total 8 animations. Four moving animations, one In each direction, and four idle animations. We also need an death animation, which is created is the same way, but it can be whatever you like. I will use a green explosion animation.

To do this, I select those pictures I want to use for an animation, right click -> create -> animation, and give it a proper name, e.g. GhostMoveLeft or GhostIdleRight.

Next, I move all of my animations to the animation folder, and create an animator controller called GhostAC.

Here we create 3 new states: idle, isWalking and isDead with transition between them as shows in the picture below. We also need to create some parameters: valueX, valueY, isWalking and isDead. We use isWaling and isDead as conditions for entering/leaving isWaling and isDead animations. After we have created the motion field, it is important to check “Loop time”, so the animation is not just played once.

That’s all for the animations, lets move over to create the physical part of the ghost.

 

Step 3: Adding the enemy to the game

In my scene I create an empty game object. I start by moving this game object closer to the player, so I can see the enemy when I start the game. But the enemy is not visible yet. First we have to add a sprite render. For the game object, we have an inspector panel to edit it. From here we add the sprite render (which is a component).  Now we just have to drag the sprite into the sprite render, and we have a visual enemy.

2015-11-17 (3).png

Step 4: Adding the animator controller

Now we have to add the Ghost animator controller that we created earlier. There is two ways of doing this. You can do it the normal way by finding “Animation” in the component list, or you can do it the easy way. You can just drag “GhostAC” into the inspector panel, and it automatically add the animation controller. Pretty easy.

Step 5: Adding box collider

Next the enemy need a box collider. This is so other objects, as the player, cannot run though the enemy.

Step 6: Adding rigidbody

Then we need to add a rigidbody, which gives the enemy physics in the game such as mass, gravity and collision.

Step 7: Scripting

Now comes the fun part. The first thing we want to add is the enemy stats script which controll the health, damage and stats. The setup for code is pretty simple.

First, you start with field variables and public variables. public variables can be changed in inspector and will be unique for each game object that uses the script. Field variables will be initialized by the constructor name Start()

</span>Animator anim;
GameObject statScaler;
GameObject player;
Scaler level;
<div>public int attackDamage;
public int health;
public bool debug;
public int damageIncrease;
public int yieldExp; <span style="font-family: Courier New;">

Field variables will be initialized by the constructor name Start()

</span>void Start () {
statScaler = GameObject.Find ("StatScaler");
player = GameObject.Find("Player");
anim = GetComponent&lt;Animator&gt; ();
level = statScaler.GetComponent&lt;Scaler&gt; ();
damageIncrease = level.GetScale ();
attackDamage += damageIncrease;
//health += damageIncrease;
yieldExp = 100;

}
Next, we have the built in function Update(), which is called for each frame. In this script we use Update() to decide is the enemy is dead or alive.
 void Update() {
if (health &lt;= 0) {
Die ();
}
}
A method for getting the enemy health
public int getHealth(){
return this.health;
}
A method to kill the enemy
public void Die() {
if(debug)
print ("dead");
health = 0;
anim.SetBool ("isDead", true);
anim.SetBool ("isWalking", false);
}
A terminate method. This is called on the last frame of the death animation to remove the game object when it is dead.
void Terminate() {
player.GetComponent&lt;PlayerStats&gt;().IncreaseCurrentExperience(yieldExp);
Destroy (gameObject);
}
A method to get the attack damage from the enemy
public int GetAttackDamage() {
return this.attackDamage;
}
And at last we have a method which is called when the enemy is attacked by the player
public void TakeDamage(int damage) {
if (debug)
print ("Taking dmg. health = " + health);
health -= damage;
}
This whole script is a prefab, which means that this script can be Applied to any enemy we decide to create in the future, or something else that need to have a health and can be killed.
Step 7b: Adding attack script
Now we need tok create a script for the enemy to attack the player. As a ghost, i think that is should move slow against the player, hit it once, and move away from the target again in another direction. Since it is a ghost, it should also be invisible from time to time. To do this, I reuse the hitAndRun script we have for the bat and extend it with some lines of code to adjust for the Ghost.
Just to be clear. I am not creating a whole new script for each enemy. I extend those we already have to be general for multiple enemies. This script already have methods for moving toward and away from the player.
For the Ghost to use hitAndRun-script, I added these lines of code.
Some variables
private bool isInvisible = false;
private float invisStart = 0;
private float invisStop = 0;
private float invisCD = 2.0f;
private float invisTime = 1.0f;
The logic. These lines of code will only apply to enemies named “Ghost”. In this way, it will not affect bat or other enemies using this script.

if (this.name.Equals ("Ghost")) {
if ((Time.time &gt; invisStop + invisCD) &amp;&amp; !isInvisible) {
GhostEnableInvisible ();
}
if ((Time.time &gt; invisStart + invisTime) &amp;&amp; isInvisible) {
GhostDisableInvisible ();
}
}

And two methods for change visibility


void GhostEnableInvisible(){
invisStart = Time.time;
isInvisible = true;
spriteRender.enabled = false;
}
void GhostDisableInvisible(){
invisStop = Time.time;
isInvisible = false;
spriteRender.enabled = true;
}

Step 8: Creating prefab

Now, the enemy is complete. The only thing left is creating a prefab of the enemy. This means that it is saved in the prefab folder  as a prefab, and can just be clicked and dragged into any scene you cant. Then you can modify og create as many copies you want out of it. Creating prefabs in Unity is very easy. Just right click -> create -> prefab, and drag the newly created enemy into it and give it a name.

 

2015-11-17 (5).png

Here is a picture of “Ghost” chasing me.

That’s all for today. Hope you enjoyed.

Best regards

P219 Development Team