Pages

Saturday, August 25, 2012

11: 3-Hit Barrage

This time we actually give Tanner access to his full three hit combo, and let him swing to the left finally.

Explaining this part is going to be a little difficult, as my approach was much different than most other moments. See, the next two steps are fairly simple in theory, but I ran into problems every step of the way.

The easier issue to deal with is Tanner's inability to strike anything to his left. After duplicating and flipping Tanner's combo graphic to face the left, all you really have to do is add a bunch of OR statements to any if statement involving the attack graphic.

The more difficult issue is letting him perform swing 2 and 3. Just like with the directional issue, I had to add a bunch of OR statements for the 9th and 19th frame. The next step is a little more complicated, sorta.

Every time the player presses Z to reactivate the attack, the frame is reset to 0. We need an exception. There's a few we could use, but I just ended up with this:

Step Event:
if ((sprite_index == spr_tswingr) || (sprite_index == spr_tswingl))
{
     image_index += 1
}
else
{
     image_index = 0
}

And... that's about it. Every single part of this post was a mess of trial and error. I added some code, tested to see if it worked, and if it didn't, I fixed the typo or adjusted the logic. For the most part, you should definitely check out the .gml file and read the code.

A couple of pieces of advice I learned from this however:

   Be very careful with your parenthesis, especially when it comes to ANDs and ORs.

   Be careful mixing "!=" (NOT) with ANDs and ORs. The logic becomes fairly difficult to follow, and usually your code won't work.

Here's the .gml file:
     https://www.dropbox.com/s/4n0ewv0lyvfj9ni/11%203-Hit%20Barrage.gm81
Here's the .exe file:
     https://www.dropbox.com/s/ly8i6mo3vxjpi1l/11%203-Hit%20Barrage.exe

Friday, August 24, 2012

10b: Taking Swipes Return

One thing I neglected to mention in the last post was the bugs that crept up on me. When i originally worked on the last blog post, I actually wrote it all out before even trying it. Not the smartest thing to do, no, but since what I was doing would work in theory (and wasn't too complicated), I figured it was worth a shot. Fortunately it worked, but not without a lot of bugs. What you ended up seeing me typing was a fully working version, but I actually had several problems along the way. I figure I should go over them now, since debugging is a very important skill to learn.

The biggest problem early on is the alarm event. Originally the code read like this:

End Step:

if (sprite_index == spr_tswingr)
{
     if (image_index == 4)
     {
          image_speed = 0
          lightable = 1
          alarm[0] = 8
     }
}

This originally caused me trouble. Why? Because in the current code, the alarm is reset to 8 every frame as long as Tanner is on his 4th slashing frame, leaving him stuck there. That's why I finally touched it up to:

End Step:

if (sprite_index == spr_tswingr)
{
     if (image_index == 4)
     {
          image_speed = 0
          lightable = 1
          if (alarm < 0)
          {
               alarm[0] = 8
          }
     }
}

With this if statement added, the alarm won't constantly reset itself. Unfortunately, this also means that we have to make sure the alarm is turned off before it reaches this bit of code.

Next off is a small detail, but one I forgot early on.


Step Event:
if (keyboard_check_pressed(z) && lightable == 1)
{
     runable = 0
     jumpable = 0
     lightable = 0
     sprite_index = spr_tswingr
     image_index = 0
     image_speed = (1/2)
}

Early on when repeating the attack animation, I noticed that he wouldn't actually change frames because I never bothered to type in image_index = 0.

That's about it for now. I just thought I'd address the issue, since the last post I ignored all the bugs I ran into.


Thursday, August 23, 2012

10: Taking Swipes

I'm back!

Now that we've given our hero the ability to move about, it's time to move onto the real fun stuff, swinging his sword. Don't laugh please...

No laughing permitted. You can however, chuckle or snicker.

From this point on, I honestly don't know what I'm doing. I've read enough guides on how to make a platformer I could do so without too much hassle. From this point forward, all bets are off, and I'll be trying whatever I can think of. This again stresses what this blog is really supposed to be about: expressing the mindset of a programmer, particularly from a beginner.

As before with movement, the first thing to do is add the sprites into Game Maker. It can be especially troublesome since the sword's width means you have to really extend the sprite's boundaries, which can make adjusting the sprite rather frustrating.


Unlike running and jumping, we need to add the animation first. Now that we have the graphics loaded, we should be able to make our hero slash by typing something like:

Step Event:
if keyboard_check_pressed(z)
{
     sprite_index = spr_tswingr
}

Or not. This code will do absolutely nothing. The problem here is that no matter what, our character's sprite changes based on his movement. Everything we've done so far has been done assuming that running and jumping is the only thing our character can do. From this point forward we have to use variables to set up what your character can and cannot do, and what actions he is performing.

The way I originally did this was through a universal variable called state. Depending on what number it was set to would define what action the character was in the middle of. For example: 1 would mean the player could move, 2 would mean the player was performing a light attack, 3 would mean the player was performing a strong attack, 4 would mean the player was taking damage and so on. The problem with this method was that it was either too general or each state had to be made so specific it became tedious.

Instead we're going to use a series of variables to determine what the player is allowed to do. This means we can controls things a lot closer, and change things on the fly as well. Instead of me rambling on about this though, let's actually try it out.

First let's add the variables:

Create Event:
runable = 1
jumpable = 1
lightable = 1             //able to perform light attack
gravable = 1             //if 0, deactivates gravity for player (for aerial attacks perhaps)

Next, we have to add these new conditions to our original code. We could add new if statements around everything, but it'll look really messy and could be trouble. Fortunately, there's a much easier solution, AND. By adding and/&& inbetween conditional statements, both have to be true in order to work.


From this point on, we can turn the player's controls on or off whenever we want. For instance, now when we add in our sword slash:

Step Event:
if (keyboard_check_pressed(z) && lightable == 1)
{
     runable = 0
     jumpable = 0
     lightable = 0
     sprite_index = spr_tswingr
     image_index = 0
     image_speed = (1/2)
}

...it negates our player's ability to move, jump, or activate the attack swing once more.

Can't... move...

Unfortunately this just causes the poor character to be stuck in his idle animation, since he cannot move and the End Step still changes his sprite despite the restrictions. Perhaps we should fix this.


Now our hero finally can slash that stupid sword of his, and he will continually slash it until his arm falls off. He's stuck in a loop because we haven't actually added anything to stop him from swinging, and perhaps more importantly, let him move once more. This is where things can get a bit dicey, so I'll try and lay it out as plainly as possible.

Again, no laughing, but perhaps a chortle.
It's a very badly flowing flow chart, but this is laying out exactly how I want the character to perform his 3-hit combo. Try playing any 2 games with simple 3-hit combos and you'll find they all have different rules and nuances. To the player, these minor elements aren't really thought about in any kind of intense detail, but to programmers like you and me spreading everything out not only allows us better control over how our game is played, but makes translating ideas to code much easier.

For now, I just want to work with the very first sword swing animation. Let's go to our End Step, shall we?

End Step:
if (sprite_index == spr_tswingr)
{
     if (image_index == 4)
     {
          image_speed = 0
          lightable = 1
          if (alarm < 0)
          {
               alarm[0] = 8
          }
     }
}

Now Tanner stops swinging at the last frame of his sword swing. You may have noticed I used two if statements instead of just &&. I did this solely because I will be adding more conditional statements later, and it just seemed easier to bunch them altogether underneath one if statement, but that is for later. For now, we should actually write some code for that alarm, or else Tanner is now stuck awkwardly holding his blade to the side. Add an event for Alarm 0 and add:

Alarm 0:
if (sprite_index == spr_tswingr) && (image_index ==4)
{
     alarm[0] = -1
     runable = 1
     jumpable = 1
     movable = 1
     lightable = 1
}

As you can see, the if statements are starting to get much more specific, but that isn't necessarily a bad thing. The ifs are actually unnecessary, but it's good to be careful now instead of having to fix everything later.

As a final touch, now that we are using an alarm, we should turn it off whenever we start a new attack to prevent it from stopping the character mid-swing, so let's add one final bit:

Step Event:
if (keyboard_check_pressed(z)) && (lightable == 1)
{
     runable = 0
     jumpable = 0
     lightable = 0
     alarm[0] = -1
     sprite_index = spr_tswingr
     image_index = 0
     image_speed = (1/2)
}

As a little thing to note, alarms are a very useful feature of Game Maker. You can simulate your own fairly easily, but they actually gave them some nice functionality. They always stop at 0, and setting them to any negative number turns them off immediately. The only annoying part to me is that in order to use them you have to actually create an alarm Event for them to function, instead of just working automatically and allowing you to use a simple if statement.

Next, we add more to the combo, and try to get Tanner to swing to the left.

Here's the .gml file:
     https://www.dropbox.com/s/pyyn63er0e14spd/10%20Taking%20Swipes.gm81
Here's the .exe:
     https://www.dropbox.com/s/g9elezj611gblnk/10%20Taking%20Swipes.exe

Tuesday, July 10, 2012

Apologies

Unfortunately, I won't have regular access to Internet soon. Fortunately, I will still have my laptop with me, ensuring my continued work on blog posts. I do apologize for the lack of posting. We have finally reached the point where the blog and my personal work on the project are almost the same. Coupled with me having to rewrite a lot of code due to some mistakes and it has taken longer than I thought to update. Thank you for your patience, and your time.

Tuesday, July 3, 2012

9: Bug Busting

I'm sure the one or two of you fans are excited to get to the inevitable next part, attacks. Unfortunately, at this point, my chief bug tester and I found a few bugs, one of which stuck poor Tanner in the floor. Bugs are inevitable in anything you code, and plenty will exist in your final product. Being an indie game maker, people will typically look over your mistakes, but taking the time to fix bugs adds a excellent polished feel to a game.

I understand that we've just started the game, and it is only natural wanting to get farther, but bugs are always easier to remove the earlier you find them because there is less code to slog through. So what are these bugs I'm talking about? Aside from one particular bug I purposely left in, most of them are not in the code I've been providing, but I would still like to go over them to show what kinds of mistakes can happen.

The worst bug we found was Tanner getting stuck in the floor. When falling at just the right speed and height, Tanner would end up buried into the ground, and wouldn't be able to jump out of it. I'm still not entirely sure how exactly this glitch happened, but after I noticed and fixed up a few smaller mistakes in my code it went away. The biggest mistake was this embarrassing piece:

// Gravity check
if place_meeting(x,y+vspeed,obj_floor)
{
     vspeed = 0
     move_contact_solid(270,vspeed)
etc


It's always a good idea to use a variable for something right after you reset it (note: sarcasm). Again, I'm not the proudest of this blunder, but I would rather a bug come about because I mistyped something than my code just not working overall.

The next bug is something a lot less obvious to find, but a lot easier to fix. In this case, when just mashing the jump button to see if I could find a bug, I noticed that Tanner would do a sort of triple-jump. Basically, when I tapped jump just before touching the ground, he would jump in air, but jump as high as a normal jump. What was happening? Well, first thing to note is that the original code was arranged so:

gravity code
jumping code


If you think logically, you might be able to piece it together. Tanner isn't actually triple-jumping. He is moved to the ground with move_contact_solid(), then immediately jumping within the same frame. In other words, he moves downward, then upward in the code, but only moves upward onscreen. The fix is simple:

jumping code
gravity code


Changing the order removes the possibility of this happening. Now the player can only jump when he can visually see Tanner touching the ground.

The last bug is is mainly asthetic, but it looks terrible. This bug is actually present in the code I've provided. It only became noticeable when we added the animations. Whenever Tanner touches the ground, his graphic glitches slightly. Now it is time to put debug mode to the test. Try testing the game in Game Maker (this doesn't work with just the exe), but this time in debug mode. One of my favorite features of debug mode is the "Set Speed" command.

I typically set it to 5 or 10 when I want to see what is happening frame by frame. Do this, and then jump. When Tanner lands, he goes into the standing animation like you would expect, but for a single frame, he switches to the falling graphic. It has nothing to do with our animation code. If you use the debug functions to watch the variable inair, you'll notice it turns to 1 for a single frame after Tanner lands. The fix for this is small, but how do we figure it out? Just try and think through everything logically.

We know that when Tanner lands, move_contact_solid() places him onto the floor. It also sets the inair variable to 0. Apparently at this point, the code sets inair back to 1, and then to 0 again. It only changes in the gravity event, so it has something to do with that.


if place_meeting(x,y+vspeed,obj_floor)
{
     move_contact_solid(270,vspeed)
     vspeed = 0
     doublejump = 1
     inair = 0
}
     else
     {
          vspeed += 2
          inair = 1
     }


The only way inair returns to 1 is if the place_meeting() check fails. This is where I figured it out, so I'll lay it out completely.

Tanner touches the ground, and the gravity check does it's work. Afterwards, vspeed is set to 0, and Tanner is placed just above the floor. When the check is made again, it tests if Tanner would collide with the ground by checking his current position, and adding vspeed, which is zero. Since he isn't actually touching the floor, this means for that single frame, the game assumes Tanner is in the air and moves him 2 pixels downward. After that, the code works fine because Tanner is actually inside the floor at this point.

How do we fix it? It seems really minor, but it works.

if place_meeting(x,y+vspeed+1,obj_floor)


If we simply add 1, the game will check just underneath Tanner's feet even if vspeed is 0. Now the glitchy effect no longer happens.

Next time, we finally let our hero swing his sword around. The horrors await!

Also, I'm not posting the gm81 or exe file this time. I added two characters to the code.

Friday, June 29, 2012

Drawing is Easy... for Artists

Hey guys, no update today. I just thought that today I'd make a quick post about spriting.

If you're reading this blog trying to learn something, then I'm guessing you are not the best at programming. If fate has smiled upon you, you probably are not very good at programming, but are an excellent artist. If so, good for you. However, if you are like me, you probably cannot draw to save your life. Being a good artist is a talent gained through time and practice. If you don't want to take that time and effort, there are alternatives. Not nearly as good alternatives, but still there.

One way is to use a sprite template. As mentioned before, I used one by Chumbucket, and it actually contains a ton of animations that you can use. I honestly don't know of any others, but editing pre-existing sprites is a possibility as long as your game is freeware.

For me though, it wasn't enough. While I have not shown them yet, the attack animations are actually edits of the template's graphics, for better or for worse, but I like to have at least a slight personal touch in what I use. Here are some pretty good pixel art tutorials I have read and learned a few tricks from. Again, these will not make you great artists, but they teach skills worth learning.

A tutorial by Derek Yu. A personal favorite artist.
     http://www.derekyu.com/?page_id=218
A tutorial by The Mechanical Maniacs.
     http://themechanicalmaniacs.com/guides/spriteguide.php#Type4
A collection of tutorials on Serebii forums. It's a pokemon site, but there are several good thorough guides.
     http://www.serebiiforums.com/showthread.php?59110-Sprite-Tutorials-56K-Warning!
I tutorial by... gas13(?). He actually has several, and they are very thorough and well thought out.
     http://gas13.ru/v3/tutorials/sywtbapa_almighty_grass_tile.php

Wednesday, June 27, 2012

8: Mario Posing

Our character has a face now, but he has an odd tendency to run while jumping in the air. Fixing this next part is pretty easy. How do we stop Tanner from running in the air? How do you determine if he is in the air?

place_meeting(x,y,obj)

Jackpot, sorta. We could just as easily add this to our animation code, but I would rather avoid unnecessary checks when a variable will do just fine. Let's add a new one to the fold:

inair = 0

Then introduce it to our gravity code:


if place_meeting(x,y+vspeed,obj_floor)
{
     move_contact_solid(270,vspeed)
     vspeed = 0
     doublejump = 1
     inair = 0
}
     else
     {
          vspeed += 2
          inair = 1
     }


There we go. No we have a variable we can call whenever we need to know if Tanner is touching the ground. Now all we need to do is use this in our animation code:

if (inair = 0)
     then run and idle animation
else if (inair = 1)
     then in air animation


If you're following along, you'll soon get to feel the joy of rearranging your code to keep it looking pretty as you add if statements. For now though, we'll just focus on the in air portion. This next portion is a little weird, mainly because I found a goofy way of doing it. First off, we can add:

if (inair = 1)
{
     image_speed = 0
     sprite_index = spr_tinair
}


We know that inair contains 4 frames, but each is intended as a completely different graphic.



Therefore the first thing we do is set the speed to 0. Next, we need to check if Tanner is actually rising or falling. We could possibly use prevy, but we have something else at our disposal that's very helpful, vspeed.

if (inair = 1)
{
     image_speed = 0
     sprite_index = spr_tinair
     if (vspeed < 0)
     {
          image_index = 0
     }
     else if (vspeed > 0)
     {
          image_index = 2
     }
}


We're almost there. Tanner is rising and falling, but he's always facing left. It's time to put face to use, but how? We could add more if statements, but it will look pretty cluttered, and be a bit repetitive (something that's going to be unavoidable later anyway, but stick with me). How about just adding face to image_index?


if (inair = 1)
{
     image_speed = 0
     sprite_index = spr_tinair
     if (vspeed < 0)
     {
          image_index = 0
          image_index += face
     }
     else if (vspeed > 0)
     {
          image_index = 2
          image_index += face
     }
}


Think about it. The variable face is always 0 for left and 1 for right. I arranged the frames in spr_tinair so that the first frame was left, and the second was right. Adding face works perfectly, and now that we know we can do this, we might be able to put it to use at some other point.

With that all done, our animation code (at least for movement) is perfect. Next time, I touch on some small debugging. And  for completion sake, here's the full code:


if (inair = 0)
{
     if (playerdir = 0)

     {
          sprite_index = spr_trunl
          image_speed = (1/3)
     }
     else if (playerdir = 1)
     {
          sprite_index = spr_trunr
          image_speed = (1/3)
     }
     else if (playerdir = 2)
     {
          image_speed = 0
          if (face = 0)
          {
               sprite_index = spr_tidlel
          }
          else if (face = 1)
          {
               sprite_index = spr_tidler
          }
     }
}

else if (inair = 1)
{


     image_speed = 0
     sprite_index = spr_tinair
     if (vspeed < 0)
     {
          image_index = 0
          image_index += face
     }
     else if (vspeed > 0)
     {
          image_index = 2
          image_index += face
     }
}


Here's the gm81 file:
     https://www.dropbox.com/s/70ubcxv1cb76ju6/8%20Mario%20Posing.gm81
Here's the executable:
     https://www.dropbox.com/s/ddy7ztx0fcc4guz/8%20Mario%20Posing.exe