Posts tagged ‘RPG’

3 "S"’s RPG Sample – Step 1

So like any game development project, the first steps we’ll take in implementing our 3 “S”‘s game will be putting together a design document and starting the shell of our actual game. Since we’ll be developing the game using Unity (shocker, I know) the first steps are pretty easy:

  • Create the project
  • Set up the Assets structure
  • Create the typical scenes

I have to assume that you have at least some basic knowledge about using Unity. If I had to start from the very beginning, we’d never actually finish building a game. The game interface and visuals of the game will be very simple as that’s not what we’re concentrating on here. Getting a working game completed is more important than a visually stunning game that never gets finished.

Start a new Unity project, select the 2D mode, give it a name. After Unity starts, create the following folders:

  • Audio
  • Prefabs
  • Scenes
  • Scripts
  • Sprites

In the Scenes folder create the following scenes:

  • CreateCharacter
  • Game
  • MainMenu

Open the Menu scene and add a couple of buttons:

  • New Game
  • Load Game
  • Options
  • Exit

We’ll look into what it takes to start a new game in a bit. For the Load Game menu, it’s up to you how you want to handle saving games and this will influence how the loading of games works. If you want to allow the player to save at any time with an unlimited number of saves, you could potentially have a lot of save files to deal with. You probably don’t want to allow this however. It’s more typical to give the player a limited amount of saves that he can overwrite. This is much easier to deal with when constructing your Load Game scene, although the theory is the same as if you were allowing an unlimited amount of save files. The player just ends up with a smaller list to scroll through.

What options you allow the player to tweak is up to you. At the very least, however, you’ll want to give the player some options for audio and video. Turning on and off and adjusting the volume level of audio is pretty standard. Setting the resolution and allowing the player to toggle between full-screen and windowed mode is the other usual group of option settings.

We need to add some scripts to hook up the basic UI elements. I usually name my scripts for this the same as the scene they’re used in. If you want to append “Scene” to a script to be more descriptive that’s fine. Add C# files for the MainMenu, Options, and Game scene.

The MainMenu script is pretty simple. It just transitions to the appropriate scene for the button clicked:

using UnityEngine.SceneManagement;
using UnityEngine;
using UnityEngine.UI;

public class MainMenu : MonoBehaviour {

    public GameObject ButtonPanel;
    public GameObject LoadGamePanel;

        void Start ()
    {
            //check to see if game has already been started and adjust buttons accordingly
       
        }
       
    public void NewGameButtonClick()
    {
        SceneManager.LoadScene("CreateCharacter");
    }

    public void LoadGameButtonClick()
    {
        LoadGamePanel.SetActive(true);
        ButtonPanel.SetActive(false);
    }

    public void OptionsButtonClick()
    {
        SceneManager.LoadScene("Options");
    }

    public void ExitButtonClick()
    {
#if UNITY_EDITOR
        UnityEditor.EditorApplication.isPlaying = false;
#else
        Application.Quit();
#endif
    }

    public void SavedGamesCancelButtonClick()
    {
        LoadGamePanel.SetActive(false);
        ButtonPanel.SetActive(true);
    }

    public void SavedGamesOKButtonClick()
    {

    }
}

Hook each button to the appropriate method. We’ll be adding more to this for loading saved games, but this will do for now.

Open the Options scene and add an OK and Cancel button, add the following to the Options script, then hook the buttons to the methods:

using UnityEngine;
using UnityEngine.SceneManagement;

public class Options : MonoBehaviour {

    private GameOptions _options;

    void Start()
    {
        //load current settings

    }

    public void OKButtonClick()
    {
        //save changed settings

        SceneManager.LoadScene("Menu");
    }

    public void CancelButtonClick()
    {
        SceneManager.LoadScene("Menu");
    }
}

We’ll need to do the same thing for the CreateCharacter scene. As the player will be performing a number of steps to fill in all of the character info, we’ll add Next and Previous buttons as well:

Note that the OK button has been disabled. It will only enable once all of the steps for creating the character have been completed.

The code to handle navigation for the buttons is pretty simple at this point:

using UnityEngine;
using UnityEngine.UI;

public class CreateCharacter : MonoBehaviour {

    private int _curStep;

    public Button NextButton;
    public Button PreviousButton;

        void Start ()
    {
        _curStep = 0;  
        }
       
        public void NextStepButtonClick()
    {
        _curStep++;
        if(_curStep == 3)
        {
            PreviousButton.interactable = true;
            NextButton.interactable = false;
        }

        ProcessStep();
    }

    public void PreviousStepButtonClick()
    {
        _curStep--;
        if (_curStep == 0)
        {
            PreviousButton.interactable = false;
            NextButton.interactable = true;
        }

        ProcessStep();
    }

    private void ProcessStep()
    {
        switch(_curStep)
        {
            case 0:
                {

                    break;
                }
            case 1:
                {

                    break;
                }
           case 2:
                {

                    break;
                }
            case 3:
                {

                    break;
                }      
        }
    }
}

We’ll start with 4 steps and can add more later if needed. The ProcessSteps method will simply toggle panels for each step as needed.

The last piece we’ll start on is the pause menu so we have a way to get out of the Game scene. This is a simple Panel with some buttons:

In the Game script is the typical button handler:

public class Game : MonoBehaviour
{

    public GameObject PausePanel;
    private bool _gamePaused;


        // Use this for initialization
        void Start ()
    {
               
        }
       
        // Update is called once per frame
        void Update ()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            if(_gamePaused)
            {
                ResumeButtonClick();
            }
            else
            {
                PausePanel.SetActive(true);
                _gamePaused = true;
            }
        }
    }

    public void ResumeButtonClick()
    {
        PausePanel.SetActive(false);
        _gamePaused = false;
    }

    public void SaveGameButtonClick()
    {

    }

    public void ExitButtonClick()
    {
        SceneManager.LoadScene("MainMenu");
    }
}

We check each frame to see if the Esc key has been pressed and, if so, toggle the PausePanel based on whether or not the game is already paused.

This is the basic skeleton for our game that allows navigation between the current scenes. We’ll add more to this soon, but we need to do some work on the first two “S”’s before that happens. That’s coming up next.

The Three “S”’s of RPG Development

In all the years I’ve spent looking into and working on RPGs, I’ve found that most can be summarized into 3 sections:

  • Story
  • System
  • Setting

The 3 are independent of each other to some extent, but can be connected in some ways depending on selections made in each.

Obviously, each section is pretty big and there will be some sub-sections in each that are still big. Let’s discuss each a bit:

  • Story – this covers everything from the major plot of the game to the dialog for every NPC.  It doesn’t necessarily describe every detail, although it can. When it does, it overlaps with the Setting “S” below. A story can be generic enough that it can fit into any setting. If you create the story with broad strokes and then very fine strokes, you can leave the setting out of it enough to fit the story into multiple settings. This can enable you to create multiple games out of the same basic story.Story is one area that cannot really be learned, except for some things like basic writing. You have to have a good imagination and talent for developing the broad story elements without resorting to copying or plagiarizing. Obviously there are some storylines or plots that are basic to many games that you can start from. The typical plot for RPGs is of the Hero’s Journey type, but there are other plots that could be used:
    • Overcoming the Monster – The protagonist sets out to defeat an antagonistic force (often evil) which threatens the protagonist and/or protagonist’s homeland.
    • Rags to Riches – The poor protagonist acquires things such as power, wealth, and a mate, before losing it all and gaining it back upon growing as a person.
    • The Quest – The protagonist and some companions set out to acquire an important object or to get to a location, facing many obstacles and temptations along the way.
    • Voyage and Return – The protagonist goes to a strange land and, after overcoming the threats it poses to him or her, returns with experience.
    • Comedy – Light and humorous character with a happy or cheerful ending; a dramatic work in which the central motif is the triumph over adverse circumstance, resulting in a successful or happy conclusion.
    • Tragedy – The protagonist is a hero with one major character flaw or great mistake which is ultimately their undoing. Their unfortunate end evokes pity at their folly and the fall of a fundamentally ‘good’ character.
    • Rebirth – During the course of the story, an important event forces the main character to change their ways, often making them a better person.

    Some of these plots would make for a very different type of RPG from other plots, although you could probably find a way to mix and match. A game like The Bard’s Tale could mix in some Comedy with the Quest. Of course, a big RPG could have several different plots merging together in the full game.

  • System – this is all the typical pencil and paper RPG systems. A non-exhaustive list could contain:
    • Character classes
    • Races
    • Skills
    • Magic (or psionics for sci-fi)
    • Items – weapons, armor, etc.
    • Stats
    • Combat
    • Character progression (level-based, skill-based, etc.)

    Character classes will vary depending on the setting. A fantasy setting will probably use completely different classes than a modern-day setting, although there could be some overlap such as Fighter or Thief. You more than likely won’t see a Wizard or Cleric class in a modern-day day however.

    Races are going to be used mostly in fantasy and sci-fi settings (where they would be types of aliens).

    Skills will vary depending on the setting. You could have fantasy type skills such as wielding swords, fighting in armor, thieving, and outdoor survival. Sci-fi skills could include firing laser pistols, moving and fighting in a spacesuit, operating spacecraft, and alien communication and customs.

    A magic system could be fit into almost any setting, whether it’s a fantasy setting or based in the past, present, or future in a non-fantasy setting. In the case of a sci-fi setting, however, you would probably make it a psionic system instead. While a psionic system would be more limited than a fantasy magical system, the abilities would fit in with the setting. A character with psionic abilities can gain master over himself (healing, enhanced senses and abilities like speed, strength, agility, dexterity, etc.), others (taking control of another entity’s movements, reading their mind, seeing through their eyes, etc.), and the environment (telekinesis, force shields and blasts, pyrokinesis, teleportation, etc.)

    While items will differ based on the setting, you’ll still end up with groups of items for combat (weapons and armor), supporting the character (food & water if you go that deep, healing, etc.), and managing the character in their environment (rope, thieving tools, etc.). The items available will depend on how deep and rich you want to make your systems. If you don’t have a thief type class you won’t need tools for picking locks. If you go with a modern-day setting you probably won’t need armor and shields, or fantasy type weapons. You may not even have weapons depending on the story.

    Stats will probably remain consistent across settings, but could vary depending on the story. Non-action type stories may not need stats like Strength, Dexterity, Agility, and Constitution. You could even feasibly have an RPG that uses just one or two stats or doesn’t use any stats at all.

    Combat could vary greatly across settings or even within the same setting depending on what you prefer or what you believe your players would prefer. Turn-based, real-time, a mixture of the two – all of these are valid ways of doing combat. You can even go the route of some games and hide a turn-based system in what appears to be real-time.

    Combat will more than likely be one of the areas you spend a lot of time tweaking so make sure you put it in place as soon as possible. Isolating the combat system into a management system will enable you to test it even before you have any of your game in a runnable state. In my XNA RPG book, I developed a Windows Forms app that could have character data loaded into it to test and balance parts of the system. I highly recommend doing something like this is you can. That also allows you to re-use the system in multiple games, which is almost always a good thing. If you plan on doing a lot of RPG games, being able to reuse as much of the code and assets from a previous game will save you a lot of headaches.

    Character progression probably won’t be affected as much by your setting as it will your story. Almost any system for making the character stronger or more experienced will work in any setting. How a character progresses is much more dependent on the things a character does, be it fighting, performing tasks, interacting with other characters, etc. This is more story-dependent.

    The typical progression systems will be some type of level based or skill based. With a level based system, you have to figure out what gives the character experience in order to progress through levels. This is usually fighting things or performing tasks, such as thieving, finding things, interacting with NPCs (in both a positive or negative way. The latter could potentially cause the character to lose experience if you’re feeling evil :D). You’ll also have to figure out the threshold for each level. Usually this will be a bit more than a linear progression. The Dungeons and Dragons level progression looks like this:

    Level Experience Required
    1 0
    2 1000
    3 3000
    4 6000
    5 10000
    6 15000
    7 21000
    8 28000
    9 36000
    10 45000
    11 55000
    12 66000
    13 78000
    14 91000
    15 105000
    16 120000
    17 136000
    18 153000
    19 171000
    20 190000

    You want to be sure that the player can get through the first couple of levels early in the game to make them feel like they’re progressing. At higher levels they’ll become more powerful with every level so you’ll have to balance this carefully. Increasing levels too quickly and they’ll be able to walk through the game with little difficulty, which isn’t very fun. Too much time spent at a level will frustrate the player however.

  • Setting – this is where the game takes place. You can think of this as the game world. This is different than story in that the same story can take place in a completely different setting. “The princess is kidnapped by the evil king and is being forced to marry him unless she’s rescued” story plot can take place in a fantasy setting or a sci-fi setting. Depending on the story there way be some of that overlap I mentioned earlier. The story could include specific locations that could only be in one type of setting (a spaceship, for example).

Setting would probably also include the types of PCs and NPCs that populate the world. Fantasy monsters such as trolls, dragons, and elves or alien creatures and artificial intelligences in a sci-fi setting. Modern-day or near modern day (in one direction or another, past or future) would probably not include many, if any, variations of beings other than human, unless you mix in a different setting or invoke some mechanism such as time travel. Something like a portal opening to a different dimension when demon-like creatures dwell would be an example of this.

Settings could include:

  • Fantasy
  • Sci-fi
  • Steampunk
  • Modern-Day
  • Near future or past (not so far in the future as sci-fi and past era like Medieval, Jurassic era with dinosaurs, England in a Sherlock Holmes time-period, etc.)

    Be aware that the setting you select could affect story elements such as dialog, level design, art assets, etc. Keeping these elements compartmentalized would enable you to reuse them easily with a different game while keeping the organization consistent, making it easier to get new games up and running. You could also easily change the setting, duplicate the assets to use the new setting and have a new game.

The order you select your choices depends on you. If you have some specific ideas you want to use or a favorite choice(s) from one or more areas, they may affect one or more of these. If you are starting from scratch, pick a setting or story plot and go from there.

So let’s take these 3 and actually implement them and see how this works in practice. First we need to pick a setting, story, and some other details.  If you want to weigh in with your opinion head here and let me know what you’d like to see. Future posts will use the answers to produce a small, playable RPG.

Speech Bubble Sample

So I allowed myself to get sidetracked to throw together a sample to answer a question in the App Hub forums. Since I love RPG games, both playing and developing, this will probably be useful to me sometime in the future so that’s how I’m justifying it.

The sample, which you can get from here, shows a quick semi-dynamic speech bubble with a couple of options. The class is less than 150 lines, allows however many lines of text you want, shows a graphic to let the user know if there’s more text that the character has to provide, and shows a pointer to the character that can be placed on either the left or right side of the bottom of the bubble. It uses more of my fantastic programmer art, but should work with whatever graphics you replace it with, assuming you don’t deviate from how I’ve set up the graphics. I could have used just one corner graphic and rotated for the other 3 sides, but I was lazy. Smile

Here’s the class in all its glory:

public enum PointerType
{
    None,
    Left,
    Right
}

public class SpeechBubble
{
    private int _width;

    private Vector2 _location;

    private string[] _text;

    private SpriteFont _font;

    //bubble textures
    private Texture2D _bottomBorder;
    private Texture2D _interior;
    private Texture2D _leftBorder;
    private Texture2D _leftBottomCorner;
    private Texture2D _leftTopCorner;
    private Texture2D _rightBorder;
    private Texture2D _rightBottomCorner;
    private Texture2D _rightTopCorner;
    private Texture2D _topBorder;

    private bool _more;
    private Texture2D _moreGraphic;

    private PointerType _pointerType;
    private Texture2D _pointer;

    public SpeechBubble(ContentManager content, int width, Vector2 location, string[] text, bool more = false, PointerType pointerType = PointerType.None)
    {
        _font = content.Load<SpriteFont>("font");

        _bottomBorder = content.Load<Texture2D>("bottomBorder");
        _interior = content.Load<Texture2D>("interior");
        _leftBorder = content.Load<Texture2D>("leftBorder");
        _leftBottomCorner = content.Load<Texture2D>("leftBottomCorner");
        _leftTopCorner = content.Load<Texture2D>("leftTopCorner");
        _rightBorder = content.Load<Texture2D>("rightBorder");
        _rightBottomCorner = content.Load<Texture2D>("rightBottomCorner");
        _rightTopCorner = content.Load<Texture2D>("rightTopCorner");
        _topBorder = content.Load<Texture2D>("topBorder");

        _moreGraphic = content.Load<Texture2D>("more");

        _pointer = content.Load<Texture2D>("pointer");

        _location = location;
        _width = width;

        _text = text;

        _more = more;

        _pointerType = pointerType;
    }

    public void Draw(SpriteBatch sb)
    {
        //top
        sb.Draw(_leftTopCorner, _location, Color.White);
        sb.Draw(_topBorder, new Rectangle((int)_location.X + _leftTopCorner.Width, (int)_location.Y, _width - _leftTopCorner.Width * 2, _leftTopCorner.Height), Color.White);
        sb.Draw(_rightTopCorner, _location + new Vector2(_width - _rightTopCorner.Width, 0), Color.White);

        //lines
        for (int i = 0; i < _text.Length + (_more ? 1 : 0); i++)
        {
            sb.Draw(_leftBorder, new Vector2(_location.X, _location.Y + _leftTopCorner.Height + (i * _leftBorder.Height)), Color.White);
            sb.Draw(_interior, new Rectangle((int)_location.X + _leftBorder.Width,
                                    (int)_location.Y + _leftTopCorner.Height + (i * _leftBorder.Height),
                                    _width - _leftBorder.Width * 2,
                                    _leftBorder.Height),
                    Color.White);
            sb.Draw(_rightBorder, new Vector2(_location.X + _width - _rightBorder.Width, _location.Y + _leftTopCorner.Height + (i * _leftBorder.Height)), Color.White);

            //leave space for more graphic if necessary
            if (i < _text.Length)
                sb.DrawString(_font, _text[i], new Vector2((int)_location.X + _leftBorder.Width, (int)_location.Y + _leftTopCorner.Height + (i * _leftBorder.Height)), Color.Black);
        }


        //bottom
        sb.Draw(_leftBottomCorner, _location + new Vector2(0, _leftTopCorner.Height + (_text.Length + (_more ? 1 : 0)) * _leftBorder.Height), Color.White);

        switch(_pointerType)
        {
            case PointerType.Left:
            {
                sb.Draw(_pointer, _location + new Vector2(_leftBottomCorner.Width, _leftTopCorner.Height + (_text.Length + (_more ? 1 : 0)) * _leftBorder.Height), Color.White);

                sb.Draw(_bottomBorder, new Rectangle((int)_location.X + _leftBorder.Width + _pointer.Width, 
                                                        (int)_location.Y + _leftTopCorner.Height + (_text.Length + (_more ? 1 : 0)) * _leftBorder.Height, 
                                                        _width - _leftBorder.Width - _pointer.Width - _rightBorder.Width, 
                                                        _bottomBorder.Height), 
                                        Color.White);

                break;
            }
            case PointerType.Right:
            {
                sb.Draw(_bottomBorder, new Rectangle((int)_location.X + _leftBorder.Width,
                                                    (int)_location.Y + _leftTopCorner.Height + (_text.Length + (_more ? 1 : 0)) * _leftBorder.Height, 
                                                    _width - _leftBorder.Width - _pointer.Width - _rightBorder.Width, 
                                                    _bottomBorder.Height), 
                                        Color.White);

                sb.Draw(_pointer, _location + new Vector2(_width - _pointer.Width - _rightBorder.Width, _leftTopCorner.Height + (_text.Length + (_more ? 1 : 0)) * _leftBorder.Height), Color.White);

                break;
            }
            case PointerType.None:
            {
                sb.Draw(_bottomBorder, new Rectangle((int)_location.X + _leftBorder.Width,
                                                    (int)_location.Y + _leftTopCorner.Height + (_text.Length + (_more ? 1 : 0)) * _leftBorder.Height,
                                                    _width - _leftBorder.Width - _rightBorder.Width, 
                                                    _bottomBorder.Height), 
                                        Color.White);

                break;
            }
        }

        sb.Draw(_rightBottomCorner, _location +  new Vector2(_width - _rightBottomCorner.Width, _leftTopCorner.Height + (_text.Length + (_more ? 1 : 0)) * _leftBorder.Height), Color.White);

        if (_more)
            sb.Draw(_moreGraphic, new Vector2(_location.X + _width - _rightBorder.Width - _moreGraphic.Width, _location.Y + _leftTopCorner.Height + (_text.Length * _leftBorder.Height)), Color.White);
    }
}

 

Creating an instance is fairly straightforward:

SpeechBubble _bubble1;
SpeechBubble _bubble2;
SpeechBubble _bubble3;

protected override void LoadContent()
{
      ...

      _bubble1 = new SpeechBubble(Content, 150, new Vector2(50, 50), new string[] { "This is a test", "This is only a test", "More text follows..." }, true);
      _bubble2 = new SpeechBubble(Content, 150, new Vector2(250, 250), new string[] { "This is a test", "This is only a test", "Nothing here" },false, PointerType.Left);
      _bubble3 = new SpeechBubble(Content, 150, new Vector2(150, 450), new string[] { "Another test", "Still testing", "More follows..." }, true, PointerType.Right);
}

Simply call the Draw method of the class in your own draw code:

protected override void Draw(GameTime gameTime)
{
    GraphicsDevice.Clear(Color.CornflowerBlue);

    spriteBatch.Begin();
    _bubble1.Draw(spriteBatch);
    _bubble2.Draw(spriteBatch);
    _bubble3.Draw(spriteBatch);
    spriteBatch.End();

    base.Draw(gameTime);
}

Feel free to let me know if I missed something that would be useful in the class.

RPG book now on Kindle, slight Dream Build Play update

Looks like this has been available for a while, but in replying to a question on the App Hub forums, I noticed that my XNA RPG book is available on the Kindle. I thought that was pretty cool.

I should probably take the time to update it to XNA GS 4.0. Maybe after the Dream Build Play competition is over. 🙂

The developer level editor for my Dream Build Play entry is almost done. Progress has been slow as Real Life keeps interfering and it seems every time I look at the project I find something else that needs to be implemented. I’ll have Friday night free to myself so I should get a lot of work done. I ran into a problem with rotating sprites that’s rendering the level incorrectly, but that’s a minor issue. I’m anticipating have a test level up and running in the game by the end of the weekend. After that things should go fairly fast.