Sunday, June 16, 2013

Simple Turret Using UDK and Kismet

/* Long-winded introduction */

Hello, internet!  As I probably haven't mentioned, last semester I was in a course called Virtual Environments at my university.  For our final project, I ended up in a group with a guy who was very eager to make more than just an environment-- he had an idea for an interesting yet simple game level.  Since I like to pretend I'm a programmer, I immediately said I'd love to try some Kismet and see how game-like we could make the level in the few weeks we had to do the assignment.

The "level" we turned in was definitely impressive, considering it came from a beginner-level class that had just barely skimmed over some of the basics of Kismet.  I only feel comfortable saying that because the rest of the class said it was awesome when we presented it, by the way!  However, it was missing one of the most interesting gameplay aspects-- turrets.

Yesterday I started work to add them.  This is what I have so far.

Please note: this is a work-in-progress.  You are welcome to use this code as you see fit as long as you're aware that parts may not work right all the time or at all on your build.  I'm new to this, so I may not be able to answer all of your questions about this code or the inner workings of Kismet/UDK.

Note: the game type used in our level was UTDeathMatch.  I'm not sure if it's a necessary setting to make this stuff work, so I figure it's best to let you know just in case.

/* Turret details */


Our level's turrets were imagined to be rather simple-- player gets in front of turret, player gets shot; player shoots turret, turret falls over and stops working.  The best comparison I can think of is the turrets from Portal.  To accomplish this, I needed to main functions:

  1. Shoot at player
  2. Respond to getting shot by player
/* Kismet details */


Our first function is circled in red and labeled #1.  My Kismet is a complete mess, so I'll summarize.  The code begins when the player Touches the Trigger Volume I have placed in front of the turret.  Note: don't forget to set the Touch node's "max trigger count" to 0 like I always do!  On touched, it sets our "if" statement to true; if untouched, false.  If true, we go to a section of vector math that gives us a location to spawn projectiles.  For this mesh in particular, I want it to be about 150 units above the pivot point, where the pivot point is currently slightly in front of the turret's gun but level with the floor.  After that, the code gets the player's location as a vector.  This is necessary because the Spawn Projectile node can't have a Pawn as an input for some reason.  Now the PlayerLocation and SpawnLocation vectors are fed into Spawn Projectile.  I have chosen the rocket from the drop-down list in the node's settings, so a rocket will be fired at wherever the player was standing when this function was called.  I included a delay at the end of Spawn Projectile to keep it from shooting too rapidly-- if the player stands in the Trigger Volume, the turret will shoot rockets at him/her every two seconds until the player either leaves the Volume or dies.

Whew!  Seems a lot less complicated in my head!  This next part gets even hairier, so take a breather if that one wore you out.

The second function is the part that's messy and may not be working right.  I seriously doubt this is the best way to handle this, so please let me know if you have a better solution.  Note: the turret is currently set as a KActor-- I don't know if this code will work with any other physics setting!  Our first step, labeled in blue as #2, is actually quite simple.  Here we just get the turret's rotation when the level first starts.  This is so we have a point of comparison in the next step.  In the section labeled in orange as #3, we are telling our turret what it should do when it gets shot.  In this case, we want it to figure out if it was rotated far enough to be considered toppled over.  I put a brief delay after the Take Damage node to give it time to fall, and then get its new rotation.  The code looks like a huge mess here because, unless I'm just doing it wrong, vector math in Kismet is unnecessarily complicated.  It's really just checking to see if the difference between the new rotation and old rotation is significant enough by subtracting the two, getting the absolute value of the result (I had to code this node myself, mind you), and then compare it to a value I determined to be "far enough" (in other words, about 45 degrees multiplied by 182.044 to get the UnrealScript rotational units).  If the rotation of the X or Z component is "far enough," then the turret's Trigger Volume is destroyed and a message pops up letting me know it worked.  Note: you might notice that the Y component is there but not being considered-- that is because shooting the turret in just the right place long enough will cause it to rotate in place but not fall over, which will incorrectly trigger the "death" of the turret.  It looks like it should be the Z component doing this when looking at the turret in the editor, though, so I think it's a byproduct of creating the mesh in Maya, where Y is up.  I'm not sure about this theory, so let me know if you have an explanation!

/* Conclusion */

It always kinda surprises me when I get stuff like this working, even though it looks so simple when you're the one playing the game.  I think next I'm going to see if I can't add a dynamic trigger volume so it'll keep shooting you if you rotate it in place, and then a blinking light or something to make it more obvious which turrets are on and which are off.  I'm starting to think just diving into UnrealScript and making a new class for this would be the best way to finish it off, but I'm not sure how to translate some of the Kismet functions just yet.  I'll be sure to post an update if I do, as it'd be a lot easier to test out a new class than try to reproduce my Kismet!

Thanks for checking out my work.  If you have any questions or suggestions for improvement, please let me know.