Most of the screenshots so far have taken place in the outside “Grasslands”. This past week I have focused more on buildings (houses), NPCs and text boxes!
Houses
Hannah had supplied version one of the houses last week, so I wanted to begin working on a building structure. One idea I wanted to try out with this game is having no loading zones. None. Zero. When the player goes into a dungeon, it will have already been loaded. When the player goes inside a building, it will also have already been loaded. Essentially, the entire map will be one big “room”.
To do this, all buildings and structures will need to be able to be toggled seamlessly when the player enters or exits one of these buildings. From an artistic standpoint, all buildings on the outside and inside need to be the same size. That is what we worked on this past week, and it seems to be working great so far:
The first picture shows the outside of the houses. When the player enters a house (in the above picture, the left house of the first picture), a black surface is drawn over top the parts the player should not be able to see since the walls would be blocking visibility. This feature had already been built when going into and out of forts. However, this week was refined to work better with concepts of being inside and outside. For example, when we in a fort, it is okay for rain to be visible, even though the game considers the player “inside”. Inside an actual house, it would be weird for rain to visible, since there is a roof over top our heads. Now all building structures have a hasRoof
property. If a building “has a roof”, we should not let the rain be visible inside the house.
I experimented with the day and night effect as well when being inside a house. Initially I wanted to turn that off the night effect when inside. However, as I was working late one night, I realized that if it weren’t for my lights being on in my office, it would be pitch black. The same now goes for our buildings in the game. If it is dark outside, it will be dark inside. That is why the house pictured above appears to be “on fire”. It’s actually a torch object that gives off light. At night, it makes the house have light.
Mice
Originally, the houses were kind of sad and empty. I decide to make them more sad by adding mice to them:
Since I have a Simple Animal
object, I was able to create a Mouse
object very quickly. Mice’s movement are very quick and chaotic. When a mouse spots you, unlike other enemies, they will turn the opposite direction and run. If they get cornered, they will do their best to avoid taking damage. But they are just mice after all — they are not the smartest creatures.
Throwing Objects
One feature that was made very quickly a while back was the ability to pick up objects and throw them. This was originally because I wanted to make an enemy type that hid under these objects (and still do). However, there was no collision when the object was thrown and it was limited on two specific objects.
Now, all objects that can be thrown are a child of Interactable
. If an object has a pickable
property on it, it is allowed to be “picked up”. Enemies, mice, stones, bushes, anything really, can now be picked up and thrown. There is currently a weight
variable attached to anything that can be picked up. The heavier the object is, the slower the player is to move with the object. Also, weight contributes to how far the player will throw an object. Heavy objects will not be thrown very far, will lighter objects can be tossed quite a distance.
It may appear that these objects are just on the ground, but that is because we currently do not have proper animation for picking up or throwing. I promise it’s not a Photoshop trick. 😉
When throwing objects, they will have an arc to them, as if “gravity” was pulling them downwards. If the player is not targeting anything, an object will be thrown in the relative direction the player is facing. If the player is targeting, an object will be thrown in the relative direction of the target.
Objects can now collide with walls and other enemies, taking damage to both. The more weight
an object being thrown has, the more damage that will be dealt to both the thrown and the collided. When colliding with solid objects, the object will also “bounce”. To bounce an object, we can simply “mirror” the object’s current direction it’s traveling, represented by:
var direction = 2 * n – v – 180;
Where n
is the angle of surface and v
is the current direction the object is traveling in. I was stumped for a good hour or so at first because I assumed that n
was always 90 (straight line). However, if the object is traveling between 315° and 45°, the surface angle is actually 0
(assuming the wall is a straight line, which we are treating all collisions as straight lines at this point). If the object is traveling between 45° and 135°, the surface angle is 90
. etc. For our case, n
represents either 0
, 90
, 180
, or 270
, depending on the cardinal direction the object is moving. Once I figured that out, we had objects bouncing around beautifully.
Freezing Enemies
Since we now have the ability to pick up things, the player should have the ability to pick up enemies if they are frozen. After breathing “ice” (that’s supposed to sound like “life”) into the ice rod, the projectiles that come out of the rod is now able to be collided with. If an object that can be frozen collides with this projectile, it will take damage and freeze. When frozen, an object cannot move or do anything. There currently is no way to be unfrozen, but that shouldn’t be too hard of a feature to build. However, when frozen, an enemy can now be picked up, which allows us to carry enemies around, and throw enemies at enemies.
It’s a lot of fun to carry enemies around and toss them into other enemies! These enemies are pretty heavy, so movement is pretty slow as expected. At some point there will probably be a cap on how much weight
a player is allowed to pick up. Perhaps an item will allow the player to pick up heavier objects???
NPCs
The bulk of the work this week has been around NPCs. The first thing that was implemented was movement. Moving a simple NPC was relatively easy since we were able to inherit the behavior of similar objects. However, this worked good for an NPC with no objective, appearing “mindless”. My next task was to build movement for an NPC that could follow a path. I thought this wouldn’t be too hard since the Soldier
type enemy already computes, and then follows a path to get to their target. However, I ran into a few hurdles.
For one, Soldier
enemy’s paths are from point a to b. Our NPC paths could be that, or they could be circular. A Soldier
‘s path also doesn’t have points that repeat, or come near each other. Our NPC paths could do that.
Though we are using Game Maker Studio’s (GMS) path creator, I am not using their path manager. This is because I personally think it’s terrible. This is mainly because it updates an object’s x
and y
positions, as expected. However, it does not update variables like speed
. This is rough because we are currently preventing collision into objects (such as walls) by checking if an object’s horizontal and vertical speed components added with an object’s x
and y
position respectively is inside an object. If GMS simply sets positions, but not speed, objects will not check collisions correctly. This will put unintended objects on top of walls, for example.
The hurdle then was that Soldier
and other enemies that use “Dan’s Path Manager” currently keep track of where it is at on a path with this script:
/// @description path_get_rough_position(path) returns a position/point identity based on current x/y location /// @param path var path = argument0; var smallest = 1000000; var index = -1; var length = path_get_number(path); for (var i=0; i<length; i++) { var ptX = path_get_point_x(path, i); var ptY = path_get_point_y(path, i); var dist = point_distance(x, y, ptX, ptY); if (dist < smallest) { smallest = dist; index = i; } } return index;
The script is actually named path_get_rough_position
and was never meant to do much other than return a rough index
of where we were at on a path. It essentially compares an object’s x
and y
position with each point on the path. The script decides that whatever point an object’s x
and y
position is closer to, that is where “it is at” on the path. If you remember from earlier, an NPC path could cross with itself, or have points that come close to one another. Because of this, an NPC determining where it was at on a path failing because point 2 could be the same or close to point 98.
The solution was actually pretty simple. Sometimes figuring out what the problem is when it comes to programming is half the battle. 🙂 The solution was to manage the NPC’s path index
manually (not to be confused with GMS built in path_index
variable). Since a Soilder
‘s path is usually on average 4 points (thus not being very expensive), is rapidly changing, and from point a to b, it doesn’t make sense to manage the path index. For an NPC though, a path could be hundreds of points, rarely changes, and may not be from point a to b. Because of all of this, it made sense to manage that ourselves.
Managing a path index
meant we could do paths like the red line in the screenshot below:
Currently the path is very square, but that is because we were simply testing. We can easily do much nicer, “natural” paths.
The NPCs that can follow paths do so on two different timers:
- Independent
- Global
An independent timer simply means the NPC will move along the path at its own speed. A global timer means the NPC will move along the path at the rate at which the global time of day timer moves. For example, in the above screenshot, the starting point is where the NPC is at near the upper right of the white rectangle. A NPC should complete “one lap” around this entire path in the time it takes for a day/night cycle of the game (approximately six minutes). If for some reason a rock gets in the way because we were throwing it at the NPC, or we were talking to the NPC, or just standing in its way, the NPC will start moving faster on the path to catch up to where it should be on the path. And vice-versa — if the NPC is moving too fast, it will slow down.
Text Boxes
Even though it’s fun to stand in the way of NPCs, or even throw mice and rocks at them, most games have the ability to talk to NPCs. I began work on this feature last night and into late afternoon, with great success. Most games “freeze” game play when talking to NPCs. This is so we don’t get hit by enemies, or miss an event that was supposed to happen. However, I noticed in Breath of the Wild, the environment will continue to “live” — the wind is still blowing, rain is still falling, clouds are still moving, etc. (interestingly, objects like Link and the NPC currently being interacted don’t freeze). I ended up mimicking this idea of the environment staying alive for the most part, with a couple tweaks we will work on later.
Currently text boxes support these features:
- Text is pulled in from a language file — so that text is not hard coded and can be adapted for translations.
- Text boxes can have multiple pages (think paragraphs you continue on with by pressing a button)
- There is currently a typewriter effect. Don’t worry speed runners…
- Holding the talk button will make the text go faster.
- Pressing the cancel button will make the letters stop typing and immediately show up.
- Pressing the talk button once all the letters are on a “page” will begin the next page with the typing effect.
- Pressing the cancel button once all the letters are on a “page” will begin the next page with the text immediately there.
- Finally, text boxes support a title (like the NPC’s name) and a portrait.
Here is a current screenshot of the concept:
Conclusion
Somehow I managed to make this post as long, if not longer than the last one. I am thinking that the next few days are going to be refactor days. We’ve added a lot of new features, but not a lot of clean up has been done, and my brain is starting to twirl. Plus, some thorough testing ought to be done. Either way, I will of course keep you updated!