Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Using SpriteLibrary to develop a Role Playing Game

5.00/5 (6 votes)
4 Aug 2016CPOL20 min read 18.7K   707  
A sample Role Playing Game using SpriteLibrary to do the graphics

Introduction

The game iconThe code is a demo game, written with SpriteLibrary.  This article is an attempt to explain how to design a game with SpriteLibrary, with the code showing the finished product.  The game is easy enough to modify, allowing you to draw and populate your own maps.

Background

A while back I made the SpriteLibrary, but realized that people will probably need a lot more in the way of examples for how to make a game.  I hope to publish a few of these, which, I hope will help.  The idea behind SpriteLibrary is to make the graphical portion of simple games a lot easier.  But, the SpriteLibrary has strengths and weaknesses, some of which will be emphasized in this article.

Installation

After downloading the AdvDemo package and the SpriteLibrary package, you will need to unpack them and compile them in C#.  You will need to build the SpriteLibrary first, and then the AdvDemo.  AdvDemo may need you to open the Solution Explorer and right-click Resources and "Add a Resource." From there you can browse to where SpriteLibrary was built.  If you just want to download the pre-compiled game, you can find it here.  (disclaimer: downloading and installing random people's programs is a dangerous thing to do.  But, hey.  I am from the Internet.  I must be truet-worthy and friendly.  Use the source-code if you are able to.  It is a good habit to form.)

Designing a RPG using SpriteLibrary

There were a number of design considerations for this game.  My starting point was that I was going to use SpriteLibrary, and I wanted to make some sort of Role Playing Game.  I spent a little bit of time deciding if I wanted to make it first-person, second-person, or overhead (which I chose).

First-Person / Third-person

An image of the game being playedIn these two modes, you run around and see a three-dimensional view of the playscape before you.  SpriteLibrary, while it can do some of this OK, does not have the best clipping.  If you have sprites behind other sprites, then occasionally they can show through.  It is a real drag if the treasure you are trying to hunt for keeps being visible through the walls.

First person is a bit easier than third-person. We usually just have a sword or weapon hanging in front of us, showing us what weapon we have out, while in third-person you need to have the back-side of all your adventurers.

I am more of a programmer than a graphics person.  I ended up putting as much time into getting the graphics drawn for this game as I did into programming it.

My decision was made because the SpriteLibrary very easily moves sprites around in an X/Y grid, and does not have much built into it yet for 3D.  I chose to look down on our adventurer from above.  I also chose not to have light / darkness.  It just makes things much simpler to program.  This is supposed to be a “simple” demo, not a work of a lifetime.

The Screen Layout

A battleThe next main design consideration was how to show our party status, enemies, and the like.  I chose to have a pane off to the side of the map, which shows our party, how much health we have, and the like.  When we face enemies, we see them in the same pane.  And, when we go to a store, we see the items we can purchase in that pane also.

The reason for this is fairly simple.  The SpriteLibrary works on individual PictureBoxes.  For each PictureBox in the game, we need to load and initialize all the graphics which will be used on that pane.  We could have done it such that we load them individually, when that image is first needed.  But that would take a bit more infrastructure than I wanted to make at this time.  So, I have it so all the close-ups of the enemies and all the possessions all show up on the one PictureBox.  When the game loads, it does pop up a window telling you it is getting the game ready.  What it is doing during that time is getting the sprites ready; reading in the images and processing them.  The sprite controller is fairly slow in doing this, but once it is done, it is pretty efficient with the images.

The interface

I use the SpriteController (part of the SpriteLibrary) to watch for key presses.  I get mouse-movement from the form, and I pass it on to the SpriteController when I am dragging sprites around on the screen.  This is more because I want to show how to do the sprite dragging than because it is the best way to do it.

The code.

Party Setup

The store, where you can buy stuff or hire more adventurersI wanted to have a few different choices in your party.  You can hire everyone eventually, once you have enough money to do so.  But you can only start with two people.  So I made one PictureBox, and made a SpriteController to manage the sprites in that box.  I have a line down the middle of the box, and you can drag adventurer sprites into the “selected” area.  When we have two adventurers ready to play, you can play the game.

Because Role-Playing-Games encourage you to grow closer to your digital selves, I also allow you to change the names of the players.  Because of this, when we play the game, I make sure I pass the party members back and forth between the Party-Setup window and the AdDemoForm (where the game is played).

AdvDemoForm

This is where the heart of the game is being played.  Everything running on SpriteLibrary uses a “tick” to make things happen.  This is explained in some depth on the SpriteLibrary page, but I will describe some of it here too, mainly to explain how things work.

The DoTick function

The SpriteLibrary has a timer “tick” that goes off very rapidly.  It happens about 100 times a second.  Each time this tick happens, the “DoTick” function fires off.  (That is set in the program, around line 151 of the AdvDemoForm.cs file) We want the DoTick function to complete relatively fast, so it should not be too complex.  We use the CurrentMode enum to keep track of what the game is expecting, so we know how to handle things.

If you read the DoTick code, you will see that we exit the function immediately if we should do so.  For example, we check to see if any of the sprites on the map are in the process of moving from point a to point b and exit the function if that is the case.  When we tell our adventurer to move up, down, left, or right, we tell each of the sprites that make up the playing-field to move around on the map (using the Sprite.MoveTo function).  We do not want to accept new instructions until we have arrived at the next location, so we simply jump out of the DoTick if we are waiting on the Sprites.

If none of the sprites are moving, but the code knows we had just been moving (IsMoving = true), we have just arrived into our new space.  We set a Boolean value (isMoving=false) to say that we are no longer moving, and then we spend some time processing the space we just landed on.  If we stopped, and the space has a bad-guy on it, we set up to fight.  If it is a door, we move to that location, and if it is a store, we process that.  You will find that most everything in the game (life, death, fighting, shopping) all happens from the DoTick function.  The Tick is the life-blood of the SpriteLibrary, and using a Tick is the best way to program things using it.  You can do some programming using form events (form button-clicks), but a lot of things you would expect to happen do not happen the way you would like.

For example:  If you click on a button and tell a Sprite to move, the sprite does not move until after the Click function has completed.  All the functions use the same “thread”, and the form events need to complete before the Sprite events can happen.  It is very common for someone to try to tell a sprite to do something and then do a Thread.Pause to wait for the sprite to do it.  But the Pause keeps the sprite code from running, as it pauses the entire thread (Sprite and all).  If you use the Tick, and a short function, then the function fires off lots of times, and leaves a lot of time in-between for the Sprites to do their thing.

CheckKeypress

In the CheckKeypress function, we process most of the user input.  That is where we look for moving up, down, left, and right.  You can have many keys pressed at any given time.  So the SpriteController is usually easiest to use by asking it if a particular key is pressed.  The function asks if either the Up, or the W keys are pressed, and if so, it sets a Boolean saying that we are to go Up.  Later on, we check to make sure we are not set to go both up and down.  If we are going in two conflicting directions, we do not go anywhere.

The rest of the function processes the keypress.  If we are in a store, there are things we can do.  If we are in an encounter, we cannot move any direction we please, we can either fight, run, or do a healing-potion.  So there is a little bit of logic in the function.

DoFight

Us against a lot of badguysDoFight is the function that does a battle.  The Roll-Playing Games end up relying on a few key pieces, mostly fighting, to make it work.  This has a very simple fighting algorithm; probably much simpler than anyone in their right mind who is making an RPG would make it.  Basically, both sides figure out how much damage they can do, and then they do it to the chosen target.  If that target dies, the damage gets applied to the next creature in line.  So both teams do damage first, then take damage.  It is possible, then, to have both teams kill each-other.  We do not really care if the bad guys are dead or alive.  If the player’s team is dead, they are dead and the game is over.

We use a Random Number Generator.  The SpriteController has one that it creates when it is instantiated.  You can create your own Random Number Generator, but if you do not use the same oen throughout the game, the random numbers turn out not to be too random.  Each player “rolls” against their dexterity to see if they hit, and if they do, then they roll against the damage of their weapon to see how much damage they do.  The resulting damage is added to the list of potential damage.  Missile weapons get to hit twice a turn, which makes them formidable weapons indeed.  But, the archer-type players are not as tough as the melee players.  Melee players take a reduced amount of damage, simply because they are tougher.

Armor absorbs an amount of damage a turn.  If you have plate armor, then it absorbs 20 hits a turn.  If the opposing force does 25 cumulative damage that turn, then the player takes 5 damage. 

Sprites

In making this game, I ended up making a lot of sprites.  I have one for each bad guy we end up fighting.  I have a number of simple sprites, and some that are more complex.  I thought I would briefly discuss how I made those sprites here.

Making A Warrior

I am not an artist, so I stole most of my images from another game I had made a long time ago.  All of those images were 200x200 in size, and were single frame images, not animations.  So, what I needed to do was make them move around a little bit to make them feel a bit more like animations than still-frame images.

I use the GIMP, a free and open-source package that is perfect for this sort of thing.  The first step was to make a grid, so I knew where to put my images.  The SpriteController, when it makes a Sprite, reads in the A grid, showing how the spritecontroller reads in framescontents of a grid, one image after the other.  It is best to have a transparent background, so that you can see whatever is behind it.  For this game, it does not matter too much, since we only have a grey background.  But, for other games, it will matter.

The image, added onceThen I paste in the one picture and resize it.  For this game, I made them 100x100.  That is fairly small, but they rarely get that big anyway.  Most of the time they are kept pretty small on the screen.  If you make them too big, the SpriteController will bog down trying to print them.  If you make them too small, they look all grainy and poor quality.  You need to figure out for your game what the best resolution is to work with.

I duplicated my image so I had as many frames as I wanted.  For this one, I had just a few of them.  Then, I figured out what movement I wanted from it.  I mainly just made her head and sword move.  The GIMP has three great tools for this.  The selection tool, the rotation tool, and the movement tool.  We circle the area we want to move with the selection tool.  Then, we press ctrl-x and ctrl-y.  That copies it and pastes it.  Then, while it is pasted but not “anchored” (put back on the image), we can process it a bit.  We can move it a little with the movement tool, but I usually like to rotate it first, then move it to line up where I want it. The gimp tools Finally, you “anchor” it back in place, which pasts it into whatever current layer you have open.  When it is done, I need to do a little coloring to fill in where things were cut and pasted, but it does not take too much to get a little bit of movement.

One nice thing to know about the rotation tool, is that it allows you to change the center of the item you are going to rotate.  If I have cut an arm out, I make the center be the place where the arm would rotate from, either the shoulder or the elbow.  That usually means I do not need to use the movement tool as much when I am putting it back in position, as well as it gives me a better preview of what it looks like when I am done.

The object of the sprite movement is that you need to have an idea of what you want the sprite to be doing, whether walking, twitching, or whatnot.  And then you want to have a few frames to do that in.  You can make your sprites animate fast, or slow, depending on what the sprite should be doing and how many frames you have to do it in.  With a lot of frames, you can get it to move fairly smoothly, but it takes a lot of time to draw every frame, as well as it takes more processing power.

Making A Dragon

A dragon animationNow that we have made a simple animation of three frames, let’s look at a more complex one; the dragon.  When I designed this RPG, the first though that went through my mind was, “Cool!  I get to make a fire-breathing dragon!”  Dragons are awesome.  I wanted it to look awesome.  And as such, I wanted it to be a much more complex sprite than my other ones.

All my other critters have one animation.  They waggle their head, they stomp their sword, they swing their axe.  But that is all they do, over and over.  I wanted my dragon to be different.  So I gave every one of them two animations.  They will do their normal movement most of the time, but every so often they will branch out into their second animation.  And, every so often, they also breathe fire.

I make the animation the same way as I did with my other critters.  I start with an image (this dragon started life as a coloring-book image), and I cut out a leg and rotate it, I cut off his head and rotate it, or I cut off his neck and rotate the whole head.  For one of them, I cut the front-part of the dragon away and rotated it, making it look like he is lunging his front feet off the ground…  Not too complex, but effective.

But what is happening behind the scenes, is that I am telling each Sprite to animate once.  Then, when the animation is complete, I have an event (PartyClass.SpriteAnimationEnd) that fires off.  This function checks to see how many animations the sprite has.  If it has just one, it immediately tells the sprite to do that animation again.  But, if it has more than one animation, it randomly chooses which animation to use, favoring the first animation.

And, that same event (PartyClass.SpriteAnimationEnd) checks to see if we are a dragon.  If so, we also choose a random number and choose whether or not to breathe fire.  The fire (and again, you can tell I am not an artist) is a second sprite that gets added temporarily.  You can see this a little bit when the dragon dies mid-breath.  He turns into a tombstone while the ball of fire continues to shoot out.  That was a small detail that I could have easily programmed out of there, but I left it mainly to emphasize that the fire is a second sprite.  I could easily turn a mummy (flammable thing that it is) into a fire-breathing beast.  Now that I have my fire-breathing sprite, all I need to do is decide what should breathe fire and make the sprite animate.

Showing us fighting a dragonThere was a little bit of extra work that needed to be done to make the fire work nicely.  The fire needs to come out of his mouth, not his eyes, ears, or belly-button.  I have a few different dragons, and their heads are in different locations.  So I needed to check which dragon I had, and choose a different fire starting position for each one.  All that code can be seen in the PartyClass.SpriteAnimationEnd function.

The final thing you will see is that, when the flame finishes its animation, that the sprite gets destroyed.  We do not have the flame repeating over and over, it happens once, and then we wait for him to breathe again before it comes out.  So we make the fire sprite on demand.

I guess one last thing to mention, is that you can click on an enemy to target him.  The enemy is enlarged, and they are the enemy your team targets.  This is handy when you want to kill a more dangerous enemy first.  But, it is a little odd for dragons.  When you click on a dragon mid-breath, I needed to find the corresponding fire-sprite, and enlarge that (as well as moving it to where the mouth is, since the mouth changes location when the whole image is enlarged.)

Modifying the game

The nice thing about CodeProject is that you can take code and re-use it.  This game is a fully functional game, but it is not very long.  I have about the world’s wimpiest storyline.  You go until you fight and kill the main dragon.  If someone wants to, they can make a much longer and more interesting game from this.  There are a few areas you may want to work on to do so.

Changing the map

The map file is in the resources directory, the AdvDemoMap.advx file.  Inside that file are fairly good instructions for expanding, or creating your own map.  The map file is in XML, but is not too complex to work with.  If you get something wrong, the game will give you a fairly complete error message.  You can copy that map to your desktop or somewhere else and create your own from it.  If someone else has AdvDemo installed on their computer, they can open your map-file and play your adventure.

The main map is built through the <line></line> tags.  They look something like:

C#
<Name>Town of Nod</Name>
    <Line>wwwwww?wwwwwww</Line>
    <Line>w h t    t htw</Line>
    <Line>w       tt h w</Line>
    <Line>wPh?t S      w</Line>
    <Line>w        t h w</Line>
    <Line>w h   h    h w</Line>
    <Line>wwwwwwwwwwwwww</Line>

Where every W is a wall, the H are houses, and the S is a store.  The ? is a marker that reminds us to make something on that space later on (we need to manually put things on top of the ?; the ? is simply ignored.)

Changing the weapons

You can change the value of, or damage of weapons in the ItemClass.cs file.

Adding weapons is a bit trickier.  Not only do you need to create a sprite for the item, but you need to hard-code the new weapon in a few places.

For every sprite, you need to load it into the AdvDemoForm.cs file in the LoadMultipurposeSprites function.  The LoadPlayingFieldSprites function is for terrain (trees, stores, etc).

You need to create an entry in the PossessionName enum in the AdvDemoForm.cs file. 

C#
public enum PossessionName { potion_small, potion_large, armor_leather, armor_chain, armor_plate,
        spear, club, scimitar, sword, dagger, battleaxe, crossbow, longbow, throwingstar, 
        sling, newWeaponName
    }

You also need to make an entry for the sprite, in the SpriteName enum in the same file.

C#
public enum SpriteName { wall, walldoordown, walldoorleft, tree_1, tree_2, tree_3, tree_4,
        cavewall, cavedoordown, cavedoorleft, NewWeaponSpriteName

Then, in the item class, you need to make a new entry:

C#
case PossessionName.newWeaponName:
       Name = "Lightsaber";
       WeaponDamage = 50;
       Description = "A glowing saber of death";
       Value = 7500;
       WhatType = PossessionType.melee_weapon;
       break;

The weapon will automatically appear in the store.  At the moment, the only way to get weapons or armor, is by buying them.  (or, equipping them at the start of the game.  You can do this by changing the CreatureClass instantiation of the player_knight1 or other player_* creatures.)

Adding creatures

You can use pre-existing sprites to add creatures, or you can add more sprites. 

First, add it to the SpriteNames enum on the AdvDemoForm.cs file.  After creating a sprite image and adding it to the resources, make sure you also load it with the LoadMultipurposeSprites function.  Now, you need to make a creature enum, also in the AdvDemoForm.cs file, and finally, add a small bit to the CreatureClass instantiation function:

C#
case CreatureName.NewCreature:
            SpriteImage = SpriteName.NewSpriteName;
            size = .5; //Size in feet.  6.1 is just over 6 feet tall
            strength = 1; //How tough this creature is
            dexterity = 3;  //How well it hits (out of 10)
            intelligence = 1;  //How smart it is.  Smarter creatures are harder to run away from.
            CreatureWepDamage = 5;  //How much damage it is capable of doing
            Name = "A ghastly tree elf.";
            break;

 

Conclusion

I do hope that you find the SpriteLibrary helpful for making fun games.  Again, SpriteLibrary will make simple games simple.  But, if you want to really get into making hard-core games, you will need to use some of the other graphical systems.

 

History

  • Aug 4 2016 - Minor bug fixes, and added the ability to play a game file (.rpgx) file that you are making yourself, or that someone has emailed you.
  • July 4 2016 - Created first version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)