Jump to content

  • Log In with Google      Sign In   
  • Create Account





Let's Make: BattleGame! [Part 1 - Classes]

Posted by SkylearJ, 06 May 2013 · 3,195 views

C++ Game Beginner text based Lets Make BattleGame
Introductions!
Hello everehbody! Posted Image

This is my first journal entry/tutorial here on GameDev, and I hope I do a good job at explaining the concepts I want to get across. This tutorial series is mainly aimed at beginners (given the blog's name) and I hope it's simple enough.


What We'll Be Making.
Using the fantastical language of C++ (which is what this blog is primarily for), we'll be making a turn-and-text-based combat game.

"What's a turn-and-text-based combat game," you ask? Well, you'll see by the end of this tutorial.

The point of this series is to introduce beginners to the basic game loop and to simple algorithms, such as would be seen in any basic RPG game. We'll be making a game where you have a set of things you can do while fighting an enemy.

The target product of the first few tutorials is a game where you:
  • Have an intro to set your name
  • Allow the player to allocate his/her stat points to various upgrades
  • Allow the player to choose what enemy he/she will be fighting
  • Give the player three options during combat: Melee Attack, Gun Attack and using a Health Potion
  • Create an enemy that can perform all three of the above
Hopefully, as we go along, this list will become more apparent. Posted Image


Let's Get Started!
Alright, so fire up whatever IDE you're using and let's get started!

So, first off, we're only making the barebones of the system in this tutorial. We'll be implementing an Engine later. Open up a new C++ file and name it Being.h.
#ifndef Being_h
#define	Being_h

#include <iostream>

class Being {
    protected:
        std::string name;
        int health, maxHealth;
    
        int baseDamage, damage, gunDamage;
    
        int ammo, maxAmmo;
        int potions, maxPotions;
    
    public:
    
        void meleeAttack(Being& target);
        void gunAttack(Being& target);
        void useHP(Being& target);
        
        std::string getName();
        int getHealth();
        int getAmmo();
        int getPotions();
    
        void Reset();
};

#endif
What're we doing here? This is actually pretty simple. We're going to use inheritance to make our Character and Monster classes smaller and simpler.
In Protected, we're defining the values that both Character and Monster will need. Protected makes sure that only Being and it's 'children' will be able to get/modify those values.
In Public, we're defining the methods that other classes (outside of Character and Monster) will need to use; for example, since string name; is a Protected value, we'll need some way for other classes to find out what that Being's name is. The attack types will also need to be used outside of Being.


Not too bad, right? Grab a cup of joe and let's continue. Posted Image


Alright, so we've got our Being class now. Create a new file and name it Being.cpp, but leave it alone for now. Create a new header file and call it Character.h. Put this stuff in it:
#ifndef Character_h
#define	Character_h

#include "Being.h"

class Character : public Being {
        public:
          Character(string newName, int newHealth, int newDamage, int newAmmo, int newPotions);
          
          void Display();
};

#endif
Much shorter, thanks to Being. Time for explanations!
Character is inheriting all the values that Being.h has, and that's why we don't need to redefine all the ints. You can call Character a 'child' of Being.

Like before, make a new file called Character.cpp and leave it be. Now, we're going to need to be able to create a Monster just like we will for Character. Create a new header called Monster.h.
#ifndef Monster_h
#define	Monster_h

#include "Being.h"

class Monster : public Being {
      public:
           Monster(string newName, short newHealth, short newDamage);
};

#endif
Hopefully, this is self-explanatory. Monster is also a 'child' of Being, since we're inheriting it from Being as well. If you haven't noticed, we're defining fewer values in Monster's constructor; this is because (for now) Monster will only have the Melee Attack type. Once we delve into a more involved combat system, we'll give the monsters their own special attacks.


Mortal Combat~!
I'm sure you've gathered what class is next, based on the title of this section. We'll need to have our Combat class up and ready for the next part of the tutorial, and I think this will be the most interesting part of our game.
#ifndef Combat_h
#define	Combat_h

#include "Character.h"
#include "Monster.h"

class Combat {
    private:
        Monster& M;
        
        void combatChoice(Character& C);
    
    public:
        Combat(Monster& newM);
          
        void combat1(Character& C);
};

#endif
Although the class itself is small, our Combat.cpp is going to be pretty involved. Alright, time to explain the things we see in here...
  • Monster& M; - This is creating a reference to Monster, and it's how we'll be affecting the monster in Combat.cpp. Our reference here will be referencing the instance of the Monster we'll make in Main.cpp.
  • Combat(Monster& newM); - This is Combat's constructor, which is how we'll create new combat instances for when we start a fight with a monster. We're referencing a new Monster here, similar to Monster& M.
  • void combatChoice(Character& C); - Here, we're going through loop for the player's options in battle. We're referencing the Character instance (or object?) that will be created in Main.cpp.
  • void combat1(Character& C); - This will be the loop for our combat. We'll use this to go through the attacks, and check to see wether or not the Monster died. We're referencing the Character instance (or object?) here.
Simple enough, right? Now, the reason we're putting our reference to Monster (Monster& M) and our combatChoice() into Private, is because only Combat will need to use these. For an explanation of the reasons behind using Public, Private and Protected, click here.

Conclusion
Well, we've set up all the classes we'll need to begin our game, and that's what we'll be doing in the next part. Hopefully I get it out by tomorrow, or tonight if I'm lucky. Posted Image

Leave all the feedback, positive or negative, you want! I'm taking all criticisms into account. Posted Image




Note bad for a beginning, so I get it right that you are aiming at somewhat experienced C++ programmers that just want to start out with game programming? Otherwise, you need to explain much more on the C++ stuff, most of this is not self-explanatory to a complete beginner.

 

One thing about the code itself, I know it is aimed at startes, but still, you should make more use of "private" and make the buisness logic more immanent for each class. Right now everything is a public field, thats an important thing even for beginners to get right - having probper encapsulation and not a can-access-all state, that is. Since you obviously target user with some knowledge of c++, this is even more important to get right.

 

Other than that, I don't see many things to complain, its not the best possible design (composition is a far better approach than inheritance for modeling game units IMHO), but its fairly well for starters. Keep up, hope you can take something out of this criticism!

Thanks for your feedback, Juliean!

 

At this point, I'm actually writing this program myself, while teaching it as I go along. Right now, I don't see much use for Private. I think I could put some things into Private later, but we'll see once I release Part 2. Also, I'm not entirely sure how composition works, so I'm going to use the method I've tried-and-tested.

 

As for it's 'absolute beginner' aspect, I'm hoping that whomever decides to read this has enough C++ background to understand what's going on here. It's fairly simple to myself, and I included a couple links that might help.

 

I know it's not the most efficient design, but I'll be changing that in future tutorials.

 

Thanks again! ^_^

You are very welcome.

 

Seeing that you are writing along with this tutorial, I'd even more encourage you to have a close read at "private". Using this corretly will enforce a better design, make your code possibly more cohecent (a good thing), and less error proof. If you really have a class that has no private state whatsoever, you can declare it as a "struct" instead and skip the "public:" keyword, just so you know ;) Seeing however that e.g. there probably is no need for the Monster-reference in the Combat-class to be access from outside, this would be a perfect kandidate for "private". But before you think yourself: "yah just keep talking, I don't need this" or something on that line, like I suggested have a good read at what the different access modifiers for c++ do and how they are useful ;) Just trying to give you some good word of advice, trust me, you'll see the necessarity of those soon enought :)

 

Composition is actually just the like inheritance, but instead of a parent class, you'd have multiple "component" classes you use to put your object together. For example a "health" component, or an "range" / "melee" - attack components (seperate). This gets useful if you e.g. want to have some friendly NPC that can be harmed but not attack, without having to duplicate code or having to dead-override methods. But thats j ust a small hint for the future, depending on how large this should get you might want to come back and refactore this some time later, for now it would probably be overkill. Just keep going ;)

 

Furthermore, good luck with your tutorial and in your further time as a game dev ;)

I understand you are a beginner, so let's try to move you toward being an intermediate programmer. 

 

Header files should be kept minimal.  Do not include things you do not use, and do not interfere with other code.

 

In your headers you have these lines:

#include <iostream> //Standard input/output library.
using namespace std; //So we don't have to repeatedly use std::

 

The line to include <iostream> is not necessary for your class since nothing in the header requires the streams library.  Sometimes it happens that you forget to remove a header file's include after you remove references to an object.  You should try to keep them clean, always removing unnecessary headers.

 

 

The line 'using namespace std;' is a painfully beginner mistake.  I don't know why you got in to the habit of adding this to a header file, but break yourself of the habit now.  With this line you bring the entire std namespace into any file that uses your header, even if the file did not want it.  Pulling a namespace into a header file is like inviting an invading army over so you can have a few drinking buddies.

 

 

 

As for the rest, it will be interesting to see where you take it.  Looks like it has the potential to go somewhere fun.

Thanks for those tips, Frob!

 

I edited the code here again, and hopefully minimized on unused headers. I usually include anything that need to be included in the header, and then just include the header in my .cpp files. I missed the mark by forgetting that, since I'm using inheritance, I wouldn't need to include iostream into Character and Monster, haha.

 

Hopefully I fixed most, if not all, of the issues you pointed out.

 

Thanks again! ^_^

It's a good thing you're not doing "using namespace std" anymore :-)

 

I think you should add header <string> however, since you're using std::string later on.

// One advice: depends on what you do with it, you may want to consider not taking std::string argument by value, but by const-reference, this way you might avoid unnecessary copies: http://stackoverflow.com/questions/10789740/passing-stdstring-by-value-or-reference

// Although for some cases std::swap can save the day when you pass-by-value: http://stackoverflow.com/questions/589076/when-is-it-not-a-good-idea-to-pass-by-reference

 

Also, I'd suggest getting rid of the ints and use size_t instead (remember to include the appropriate header) for all the variables that count, measure the size of something, etc.:

http://en.cppreference.com/w/cpp/types/size_t

http://stackoverflow.com/questions/994288/size-t-vs-int-in-c-and-or-c

// otherwise, you're suggesting that ammo, maxAmmo, potions, maxPotions should be able to be negative, which obscures the intent (they're sizes/counters, and so should be non-negative) and thus decreases code readability

 

It's especially important to do it *now*, at the very beginning, to instill good practices from the start (next to "using namespace std", abusing/overusing ints for everything is one of the most common mistakes I've seen beginners make; perhaps it's so prevalent because it's used for "int main", but outside of that there's very rarely a justified reason to use int).

Moreover prior comments:

- Your getter function are not returning a const value, but you should however.

- You should use forward declaration in Monster.h instead including the Character and Monster headers.

- This is just style stuff, but take a look at, for example, Google Style Guide for Cpp Programming.

 

@Matt-D I'm agree of using size_t for counting jobs, beside that, using int is the best one can do instead using char or short (if you are talking here about optimizing).

I  would add a paragraph at the start explaining the game's design at a high level. You mention potions in code, but I have no idea what they're for. What's the mechanics of the game? What is the minute-minute play sequence? How many characters are you allowing, and how many Monsters? Do people have special attacks, or one?

 

For me, I'd like to see the first part in the article lay all of this out so I know what you're introducing, it also lets me experiment on my own whilst still adhering to your design and tutorials.

Boy, I have carefully read your codes, and found a lot of bad problem.
Although does not cause any errors, I would suggest that you read more books about the C++ programming language .
Game development is not easy, but I hope you persist.
Then, I have listed some problems:

1.std::string //using a reference when you get/set it from external (Note: copy constructors)
2.don't add "using namespace std" directly in your code, in especial the C++ header files;
3.what's the difference between private, protected, and public.

Good Luck! smile.png

October 2014 »

S M T W T F S
   1234
567891011
12131415161718
192021222324 25
262728293031 
PARTNERS