Monday, October 21, 2013

Blog #2: Flash-UDK Workflow

Monday, October 21, 2013

Hello again. It has been a few weeks since my last blog posting, and I am back again to do another. This time around, I am going to explain the Flash-UDK Workflow and take you through something I created for my most recent project. I will be showing you how I made a custom HUD in Flash, with a checkpoint counter on it, and how I got it to work in UDK. Let's begin.

First off, I will show you the final product:


As you can see, the HUD does not really do anything except display the number of checkpoints the player has left to use in the lower right hand corner. This differs drastically from the normal UDK HUD which has things like health, ammo and what weapon is currently equipped. For our custom game, all we needed was this very simple HUD. Getting this working in UDK, however, is another challenge.

There are two ways to get a HUD in UDK. The first way, and not the recommended way, is to create your HUD, import it into UDK and use Kismet to open a GFxMovie to display your HUD. It works, but it can be very buggy and unpredictable. Especially if you need to use other Flash elements in your game. The way I did it, and the recommended way to do it, is to create your HUD, import it into UDK and make a custom gametype script that will tell UDK to use your HUD and not the default one. 

I am going to start by showing you the Flash first. Below is a picture of the HUD from the above picture open in Adobe Flash:


You cannot see anything as the text is colored white, but it is contained within those two blue boxes in the lower right. The left box holds text that is static, meaning it does not change. The right bow contains a dynamic text field, which means it can change at some point. Now, I have named the dynamic field CPLeft. Let's see the actual ActionScript that controls this HUD:

stop();

var checkpointsLeft:Number;

var keyListener = {};


function setCheckPointsLeft()
{
checkPointsLeft = 5;
_root.CPLeft.text = checkPointsLeft.toString();
}

function useCheckPoint()
{
if(checkPointsLeft > 0)
{
checkPointsLeft--;
}
else
{
checkPointsLeft = 0;
}
_root.CPLeft.text = checkPointsLeft.toString();
}

setCheckPointsLeft();

keyListener.onKeyDown = function()
{
    var theKey = Key.getCode();
if(theKey == 88)
{
useCheckPoint();
}
};

Key.addListener( keyListener );

NOTE: This code is attached to the stage itself, not the text fields. If you are unfamiliar with Flash, I suggest you look up how to add code to things in Flash.

Don't worry, I will go through this code line by line to show you how it works. First of all, the stop() call just makes sure that Flash will not advance to another frame. The next two lines are variable declarations, one being a Number (like int in most other languages) and the other being just another variable. In Flash, you do not need to give your variable types if you don't have to, which is why the keylistener variable has no type. 

The next two functions are purely declarations and implementations. They do nothing on their own. They are put in that spot to make sure they are declared before use. The function setCheckPointsLeft does a very simple job. It sets the global variable checkPointsLeft to 5, then assigns the number 5 to be written in the dynamic text field I described earlier. The call to the dynamic text field is quite simple, _root tells the code we are accessing the root of the stage (where the text fields are located), CPLeft is that name of the dynamic text field (can be thought of as an object) and text is the name of the variable that holds the text that will be written in the dynamic text field. The toString() function call turns the data in the variable checkPointsLeft into a string so it can be displayed by the text field. So, in simple terms, it sets the field to display the number 5.

The function useCheckPoint does what its name implies, it uses a checkpoint and subtracts it from the number left. The if-else statement is a bit of error checking to make sure the number displayed in the text field does not go below 0. Then the line _root.CPLeft.text = checkPointsLeft.toString() uses the same idea as the above description.

Then we call the setCheckPoints() function so it is run when the Flash is started (i.e. when UDK draws it on the screen). The next line is another function declaration. However, it is a little different from the other two. In this case, we are telling the keyListener variable to use its onKeyDown function in a certain way, which we define below that line. We create a variable to hold the key code that is returned from the keyListener. We then check it against whichever key we want that will serve as the button to decrease the amount of checkpoints we have left (meaning the key we setup in UDK as the set checkpoint key). 88 is the ActionScript keycode for the letter x, without having the shift button pressed. Then, after that function declaration, we tell Flash to add our custom keyListener and use it to determine if we have pressed the x button or not.

Now, that wasn't so bad. Let's move on to the UDK stuff. First of all, you would need to save your Flash file in this path (assuming you installed UDk to your C drive, otherwise change that letter to the drive letter where UDK is installed): C://UDK/UDKGame/Flash and put it into a folder with a different name. In my case, the file path of my Flash files was C://UDK/UDKGame/Flash/CPFlash. Now that it is saved, you will need to publish the Flash using FlashPlayer 8 and ActionScript 2.

Now, inside UDK, you would open the Content Browser and click the Import button on the lower left. Navigate to the place where your Flash is stored, and double click the file you want to import. UDK will create a package for you with the name being the same as the folder where the Flash is stored, then click OK. Now, we step into UnrealScript.

This part is not very hard. If you are familiar with how to write a custom gametype and get it to work in UDK, you can skip this step.

What we are going to do is create a custom gametype that will use our HUD instead of the default UDK HUD. 

Navigate to your UDK install folder, and go to Development/Src/MyMod/Classes. Create a new text file and name it NewGame.uc. Open that file and paste in this code:

class NewGame extends UTDeathMatch;


DefaultProperties
{
    HUDType = class'MyMod.NewHUD'
    bUseClassicHUD = true            
}

The first line tells us the name of the class (MUST BE THE SAME AS THE FILE NAME) and what it extends from. In our case, NewGame is extending from UTDeathmatch. DefaultProperties can be found in all UDK scripts, and is the place where variables can be set. So, we are telling UDK that our HUDType is of a class we are going to create soon called NewHUD. Then, the bUseClassicHUD = true tells UDK that we are using a classic HUD. Save this file.

Next, make two new text files in the MyMod folder and name them NewHUD.uc and NewGFxHUD.uc. Open NewGFxHUD and paste in this code:

class CPGFxHUD extends GFxMoviePlayer;

function Init(optional LocalPlayer PC)
{
  Start();
  Advance(0.0f);

}

function UpdateHUD()
{
}

DefaultProperties
{
 MovieInfo = SwfMovie'<insert name of package where your Flash is stored in UDK>.<name of Flash file>'
}

Again, we are first defining the class and what it extends from. Then, our Init function tells UDK to start the Flash file with Start() (this MUST be called) and to keep executing the function with Advance(0.0f). Putting something larger than 0 in the parameter list of Advance() will make UDK sleep for that duration before continuing.

UpdateHUD does nothing, but if we wanted to have dynamic health bars or ammo counters, we would put code in that function to update those specific HUD elements when needed. The DefaultProperties is where we tell UDK what Flash file to use for the HUD. We are setting it to the Flash file you imported into UDK earlier. Save this file.

Open NewHUD.uc and paste in this code:

class NewHUD extends UTHUDBase;

var NewGFxHUD HUDGFx;

simulated function PostBeginPlay()
{
  super.PostBeginPlay();

  HUDGFx = new class 'NewGFxHUD';

  HUDGFx.setTimingMode(TM_Real);

  HUDGFx.Init(LocalPlayer(PlayerOwner.Player));
}

event PostRender()
{
 HUDGFx.UpdateHUD();
}

DefaultProperties
{

}

We are declaring our class and what it extends from, and then creating a variable named HUDGFx that is of type NewGFxHUD. Remember the class we created earlier with the same name? We are using an object of that class to tell UDK to draw our HUD. The function PostBeginPlay is defined next. The first line super.PostBeginPlay() tells UDK to call the PostBeginPlay function and continue from that point. Then, we set the variable to a new object of type NewGFxHUD. Then, we set the timing mode of the HUD to Real, which tells UDK to keep updating the Flash, no matter what. We then call the Init function from our previous class and tell it to set the HUD to the Player so that they can see the HUD.

The next function is not really a function, but an event. This even calls the UpdateHUD function from the previous class. DefaultProperties can be left blank in this class, but it has to be present or UDK will error out.

Now, save that file and close it. You will now have to navigate to UDK/UDKGame/Config and open DefaultEngineUDK.ini. Navigate to the line that says ;ModEditPackages=MyMod and remove the semi-colon. This tells UDK to include scripts you wrote in the MyMod folder, as it does not do this by default. Then save that file.

Open UDK and you will be prompted to rebuild the scripts. Click yes and let it run. If we did everything right, it will have no errors or warnings. Now, open UDK again and it will load the editor. Under the View menu, click World Properties. Then, expand the Game Type field and change both dropdowns to NewGame. Then start the game by clicking the green arrow in the upper right corner. I would suggest testing this on a pre-built level. If everything worked, you should see your HUD on screen. Press the X button to make sure our HUD updates the number of checkpoints left.

I hope you enjoyed this tutorial. The above example can be applied to anything that has to be counted down (or up, by changing the Flash) on key press. We used it to keep track of the number of checkpoints left, but you can edit it to be anything you want.

Monday, September 30, 2013

Blog #1: Grayboxing

Monday September 30, 2013

Hey all, this is my first blog in a long time. As of right now I am in GSP 340, which is Level Design and Modification. This blog is going to focus on one portion of this huge topic, which is grayboxing.

What is grayboxing? Grayboxing is a technique used by level designers in order to map out in rough detail the shape and design of the level they are working on. With a graybox level, there are no textures, particles effects and minimal lighting. The object of building a graybox level is to see how the level flows from its different areas and if the proportions you planned out are either too big or small. It can also help you determine if your initial design is going to work or not. In our group project, our graybox level was a real pain to build, but that is because the shape of our towers is octagonal and not very easy to line the brush up with. Since UDK has weird snapping when you change the rotation of an object, some of the geometry isnt flush with the walls. Just something to keep in mind if you decide to build octagonal towers.

In our graybox, which I will post a picture of below, you can see that we have no textures whatsoever. The only texture/lighting is the skydome and the sun that allows us to see everything we need to.


After you are satisfied with the design and function of your graybox level, you can begin adding textures and static meshes into your level. If you built geometry that is serving as a placeholder for a static mesh or other actor, you can delete those and put in the correct object. It really is a simple concept, but it helps tremendously in the planning and initial building of your level. It keeps you from having to constantly delete textures/meshes/lighting that would mess up your level if you had those things placed in there. Not having textures/meshes/lighting allows you to work with the simplest form of your level.

That's all for today, keep an eye out for my next blog in a few more weeks.

Tuesday, June 25, 2013

Dev Blog #7

Tuesday June 25, 2013

Here we are, at the end of our journey. Matt and I have worked tirelessly on this project for the past 16 weeks, and we are very proud of the things we accomplished. We both learned a ton from making this game from the ground up and we have become better programmers for doing so. You can find the website for the game at LINK where you can see some information about the game, Matt and myself and view the trailer we made for the game. There is also a download link on the website so you can try the game for yourself and let us know how you like it and what we could have done to make it better. I will admit that the game is very short, but the point of this class was to take an idea from concept to code and make it work. We did that and our game came out really good.

I hope you enjoyed reading about the game and, at least, my ideas behind the game. You got to see some code that no one else got to see. I also hope you learned something or got a different view on something you already knew. Thanks for reading. Until next time.

Wednesday, June 19, 2013

Dev Blog #6

Wednesday June 19, 2013

Here we are, down to the final week of development. Matt and I had our weekly meeting with our professor on Monday and he seemed really happy with what we had. He only had a few requirements of us. The two major ones are animations during battle and a gear system. The battle animations are an easy fix. All we need to do is create either another sprite sheet for each monster or make more cells in their existing sprite sheet that has a few frames in it. The frames would alternate between the entity being drawn and not drawn, and when played would give a flickering effect. The gear is a little bit trickier. Matt has to create a new menu that will show equipped gear and I have to make sure that the items draw in the correct slots. That shouldn't be too bad. Then I have to make sure the gear affects the stats correctly, which won't be too bad either.

Matt also has to send me his information for the website and has to create the trailer for the game, as I am handling the website. We also have to make posts to our Career Web Portfolios with information about this project, as DeVry is making that a bigger part of our curriculum. We shall see how this week plays out. Until next week.

Thursday, June 13, 2013

Dev Blog #5

Thursday June 13, 2013

Sorry for this blog being late. I totally forgot last night to write one. This week I will have a code snippet to show you guys in illustrate one of the issues with OOP that can be somewhat frustrating to fix if you don't know where to look.

The problem stemmed from a bug Matt and I were having with items. You could use items just fine and buy items, but that is where the problem reared its nasty head. When you would go to buy potions from the merchant, the game would show you buying 1 potion and then get stuck at 2 potions left in the shop. If you left the shop interface and opened your inventory, there were 2 potions there, even though the game only showed you buying 1. If you used 1 potion or both, the amount in the shop would go down to 1. It frustrated me for days. Finally, I had a breakthrough two days ago.

I saw that we weren't giving the player a brand new version of the item he was trying to buy. We were essentially copying the exact item the merchant had, which is not a good idea in languages like Java. Java passes everything by reference by default, so if you gave the player an item directly from the merchant, those two items were tied together in memory. So, my idea was to set a bunch of variables to hold the data from the item you were trying to buy and then pass those to the constructor for the Pickups class. Then, that Pickups constructor was used as a parameter for the giveItem() function that is part of the InventoryManager class. I will show you in the code snippet below:

This is only a partial code snippet, but it illustrates my point:

if(tempNPC.getItems().get(ShopInventory.getMerchantSlot()).getStackable())
{
int i = tempNPC.getItems().get(ShopInventory.getMerchantSlot()).getItemID();
int d = tempNPC.getItems().get(ShopInventory.getMerchantSlot()).getDefence();
int a = tempNPC.getItems().get(ShopInventory.getMerchantSlot()).getAttack();
int h = tempNPC.getItems().get(ShopInventory.getMerchantSlot()).getHealth();
double s = tempNPC.getItems().get(ShopInventory.getMerchantSlot()).getSpeed();
String n = tempNPC.getItems().get(ShopInventory.getMerchantSlot()).getItemName();
boolean b = tempNPC.getItems().get(ShopInventory.getMerchantSlot()).getStackable();
int m = tempNPC.getItems().get(ShopInventory.getMerchantSlot()).getMaxStack();
String type = tempNPC.getItems().get(ShopInventory.getMerchantSlot()).getItemType();
EntityManager.getInstance().getDaniel().getInvManager().giveItem(new Pickups(i,d,a,h,s,n,b,m,1, type));
}

Blogger isn't the best on HTML tags, so I just pasted in the code. If it is difficult to read, I apologize. The initial if-statement checks to see if the item you are trying to buy is stackable. Why, you ask? Because stackable and non-stackable items are handled differently by the engine. Above this initial if-statement there is another one that check to see if the item you are trying to buy is already in your inventory. If it is, there is a different part of the code that just adds 1 whenever you buy that item. If it isn't in your inventory, the engine creates a brand new object (you can see this happening in the last line of the code) and places it into an arraylist.

Moral of the story: Be careful about how you pass object around. It could have some nasty consequences that will leave you with a ton of headaches over such a simple coding error.

Wednesday, June 5, 2013

Dev Blog #4

Wednesday June 5, 2013

This past week Matt and I submitted our QA build for testing. We spent a lot of hours the day and night before fixing bugs and implementing features that needed to be tested. When we were done, we submitted the build to our professor. Monday rolled around and our game was there, up and running for people to test out. It really is an amazing feeling seeing something you worked so hard on being tested and critiqued by others. All of the people that I watched test the game seemed to like it a lot, although there were a few bugs that were unintended. First off, the area boss was unbeatable. No matter what you did, you couldn't beat him. That was my bad. I overestimated the amount of stats the player would get by killing all of the other enemies in the area when I set up that feature. Although, the battle system worked beautifully. There was a funny bug that was discovered through testing.

The bug stems from me not range checking the damage done during battle. In this build, it is possible to do negative damage, which would add health to your enemy. The lower level enemies were also able to do negative damage to you, which would heal you. Another bug came from almost the same thing. When trying to select the Use option on an empty inventory slot, the game would crash. This came from the game trying to check an element in an array that simply wasn't there. I fixed that bug earlier, and it was a simple fix.

Matt and I have a lot of work to do over the next few weeks. We have a lot of features to fine tweak, balance to do on monsters and levels to create. It is going to be a hectic next few weeks trying to get everything done in time for our presentation in Week 8.

Wednesday, May 29, 2013

Dev Blog #3

Wednesday May 29, 2013

This past week was an exciting one. Matt and I revised the game code to allow it to read in levels the same way the level editor reads in levels. We also got the game to display multiple monsters on the screen at the same time. We were also able to interact with both of those entities separately, which is a major stride. Matt began creating the QA level in the level editor and it will be finished by the end of the week. We are going to work on implementing code that would randomly disperse the monsters around the QA level and spawn more over time. We want people to be able to level up at least once or twice in order to test balance with the experience ratios and the stat upgrades when people do level up.

I may end up tweaking the experience per level before testing begins. I think it is going to be really high if I leave it the way it is, but I have not tested it yet. Friday will be interesting as that will be our first glance at how the testing level will work out. We should have everything we need for the level to work correctly. Matt just has to take care of allowing the player to see their stats and experience in a menu. He designed the menu system, so I do not like messing with it. It is a rather complex framework, but it works really well.