Learning Classes

Still learning c++ I finally hit classes (yeah yeah slow learner I know :sweat_smile:) , but although I get the concept it seems there is something missing in my writing of classes.

This is where I got using soloLearn,

In the Main.cpp I need to include the .h file where the class is

#include "ClassFile.h"

Then inside the ClassFile.h I can setup a simple class

class GameObject
{
    public://declare public variables
        int x;
        int y;
        int width;
        int height;
        int direction;
        int speed;

    GameObject()//constructor, runs once to create the object
    {
        x = positionX;//take over the X position of the turret
        y = positionY;//take over the y position of the turret
        width = 1;
        height = 3;
        speed = 1;
    }

    void update()
    {

    // Draw and change the location of the object.

    Pokitto.Display.drawLine(x,y,x+height,y);
    x++;


    }

    bool collidesWidth(GameObject *o)
    {
        // Hit test
 
    }
};

If I want to use this in the main.cpp I can use

// spawn a bullet
    GameObject spawnBullet(positionX,positionY);

And use spawnBullet.update() to update the position, in a loop somewhere

I am using Embitz to try this but i get a few errors so there is something amiss…

  • PositionX and PositionY was not declared in this scope, if I understand this correctly the PositionX / Y object was not found,how do i place this correctly ?
  • error: expected primary-expression before ‘.’ token| because this Pokitto.Display.drawLine(x,y,x+height,y); x++; Is in the Class, seems the same thing that the Pokitto lib is not found in the class ?

I feel like i should place a include in the ClassFile.h but wouldnt this create a loop between the 2 files ?

1 Like

I’m counting minutes to @Pharap response…

2 Likes

Are you trying to declare a function called spawnBullet or are you trying to actually make a bullet?


Try changing your GameObject constructor to:

GameObject(int positionX, int positionY)//constructor, runs once to create the object
{
	x = positionX;//take over the X position of the turret
	y = positionY;//take over the y position of the turret
	width = 1;
	height = 3;
	speed = 1;
}

Then to create a GameObject, you do:

GameObject gameObject = GameObject(3, 4); // 3, 4 are example coordinates

Essentially constructors are very similar to normal functions (and technically they are actually functions) and can include arguments just like normal functions.
(There’s a few extra rules though.)

Everything else looks fine technically apart from Pokitto.Display.drawLine(x,y,x+height,y).
That might have to be Pokitto::Display::drawLine(x,y,x+height,y).

(Stylistically I have a few issues with some of it, but I find style issues with 90% of the code I look at, so that’s entirely expected :P.)


I noticed this about 1 minute earlier than I otherwise would have done because of @HomineLudens’ mention.
I praise the “C++ question? Summon Pharap!” mentality though. :P

2 Likes

hahaha, but you did get first reply :smile:

Did it work?
Are there any other questions?
And would you like to learn a few C++11 tricks involving constructors?


And because I forgot to cover it:

Yes, that would create a circular dependency, circular dependencies are generally bad,
though they sometimes work without breaking anything.
That’s also why #pragma once/include guards are important.
You don’t need to do that here anyway.

1 Like

I am trying to spawn a bullet object that reads the x,y position at moment of creation

Sorry forgot to mention PositionX and Y are already declared in the main.cpp

#include "Pokitto.h"
#include "Turret.h"


enum class GameState //This sets up a class that limits the possibilities to the items in it.
{
TitleScreen,
Gameplay,
GameOver,
};



// Create an instance of Pokitto::Core
Pokitto::Core pokitto;

//declare variables
int step = 2;//how many pixels the turrets moves
int positionX = 50;//places the turret in center of screen
int positionY = 74;//places the turret on bottom of screen
int TurretW = 10;//width of turret bitmap
int TurretH = 8;//height of turret bitmap
int XBullet;//x position of bullet
int YBullet;//y position of bullet
bool Bullet = false;//bullet is present or not
int BulletNr = 0;//to limit max number of bullets
int MaxBullets = 3;//actual maximum bullets on screen



GameState gameState = GameState::TitleScreen; //sets the gamestate to start in the titlescreen ?

const int screenW = Pokitto::Display::getWidth() - 1;//the width of the screen - 1 110x88
const int screenH = Pokitto::Display::getHeight() - 1;//the Height of the screen - 1

// Predeclare functions so they can be called, fill them after the main
void updateTitleScreen(void);
void drawTitleScreen(void);

void updateGameplay(void);
void drawGameplay(void);

void updateGameOver(void);
void drawGameOver(void);




int main(void)//main game loop
{
pokitto.begin();

pokitto.display.persistence = 0; // 0 to clear screen on every update, 1 to sustain picture
pokitto.display.load565Palette(Turret_pal);//load the color pallet from the Turret.h file


pokitto.setFrameRate(20);//set frame rate to 20

while(pokitto.isRunning())
{
	if(pokitto.update())//updates according to framerate
	{
		// Update logic
		switch(gameState)
		{
			case GameState::TitleScreen: updateTitleScreen(); break;
			case GameState::Gameplay: updateGameplay(); break;
			case GameState::GameOver: updateGameOver(); break;
			//nobase case
		}
		// Drawing logic
		switch(gameState)
		{
			case GameState::TitleScreen: drawTitleScreen(); break;
			case GameState::Gameplay: drawGameplay(); break;
			case GameState::GameOver: drawGameOver(); break;
			//nobasecase
		}
	}
}
}





void updateTitleScreen(void)
{

//to update the titlescreen, like movement or something

}


void drawTitleScreen(void)
{
// Your titlescreen code here

//pokitto.display.drawBitmap(0,0,Etch_title);//Title
pokitto.display.bgcolor = 1;
pokitto.display.setColor(0);
pokitto.display.println("Press B");

// Move to gameplay when B is pressed
if (pokitto.buttons.repeat(BTN_B, 0))
{
	gameState = GameState::Gameplay; //set the gamestate to gameplay
}

}






void updateGameplay(void)
{
// Input handling
if (pokitto.buttons.repeat(BTN_LEFT, 0))
{
    // Don't go below zero
    if(positionX >= step)
    positionX -= step;//positionX = PositionX - step
    else
    positionX = 0;
}

if (pokitto.buttons.repeat(BTN_RIGHT, 0))
{
	const int difference = screenW - positionX - TurretW;//10 is width of turret bitmap
	if(difference >= step)//as long as difference is larger then the step (1 in this case)
		positionX += step;//positionX = positionX + step
	else
		positionX = screenW - TurretW;//TurretW = 10 and is width of turret bitmap
}


// Pressing the A button fires gun
if (pokitto.buttons.repeat(BTN_A, 0))
{
    //spawn a bullet
    GameObject spawnBullet(positionX,positionY);
    spawnBullet.update()

}


if (pokitto.buttons.repeat(BTN_C,0))
{
   gameState = GameState::GameOver; //set the gamestate to GameOver
}


}












void drawGameplay(void)
{
// Background first, everything else is drawn on top of that
pokitto.display.bgcolor = 1;

//Turret

pokitto.display.drawBitmap(positionX,positionY,Turret);
//pokitto.display.drawBitmap(0,0,Turret);

/*
if (Bullet = true)
{
    if (BulletNr<1)
    {
    XBullet = positionX; //copy position of turret to the bullet but this shifts the bullet back to start whenever i press A again...
    YBullet = positionY;
    }


    pokitto.display.drawLine(XBullet + 4,YBullet,XBullet + 4,YBullet - 6);
    pokitto.display.drawLine(XBullet + 4,YBullet,XBullet + 4,YBullet - 6);
    YBullet--;
    BulletNr = 1;

        if (YBullet<0)
        {
        Bullet = false;
        BulletNr = 0;
        pokitto.display.println("Ybullet = false");

        }
    }
*/

}



void updateGameOver(void)
{
//pokitto.display.setColor(1);
if (pokitto.buttons.repeat(BTN_B, 0))
{
 pokitto.display.clear();//clear the screen

    gameState = GameState::Gameplay; //set the gamestate to Gameplay

}
}

void drawGameOver(void)
{

pokitto.display.setColor(0);
pokitto.display.println ("Game over");

}

Not sure about the current location of the creation call

1 Like

trying it out…

It did compile without errors now,

do i understand correctly that

GameObject gameobject

is bassicly the same as:

int something

I am declaring a object from type GameObject and naming it gameobject, correct ?

(then right behind it i give it values).

Yes, but one small caveat.

At the moment, using just the parameterised constructor (i.e. a constuctor with arguments/parameters) I gave you, you can’t just declare an object, you have to initialise it using the constructor, e.g.

GameObject gameObject = GameObject(0, 0);

To be able to just do

GameObject gameObject;

You’d need GameObject to have what’s called a ‘default constructor’, which is a constructor that takes no arguments.

You can write one yourself, or since you’re using C++11 you can just put GameObject() = default; in your class and the compiler should be able to generate one for you.


First thing’s first: enum class isn’t a class.
The reason you use enum class to declare it is because the standards comittee is terrified of adding new keywords to the language, they prefer to recycle old ones.

The technical term for an enum class is a scoped enumeration, and you get brownie points for using one.

Ok, here’s part of the problem.
From the looks of it you tried to refer to some global variables in your class.
Refering to global variables from inside a class is generally not a good idea because it limits their reusability by coupling them to the global.

The constructor I demonstrated instead takes the x and y values as arguments so you can choose to pass the global variables (or indeed any value you want).

I.e. you could do

GameObject gameObject = GameObject(positionX, positionY);

Remember though, this means gameObject is just initialised with a copy of the values of positionX and positionY, its own gameObject.x and gameObject.y aren’t permenantly bound to positionX and positionY.

Looking at your later code I notice this:

pokitto.display.drawBitmap(positionX,positionY,Turret);

That’s drawing the position recorded by positionX and positionY, and won’t draw gameObject.
To draw gameObject you’ll have to do:

pokitto.display.draw(gameObject.x, gameObject.y, Turret);

If your plan was to track the turret’s position with positionX and positionY,
get rid of positionX and positionY completely and just use gameObject.x and gameObject.y.


I don’t know what tutorials you’ve read, but basically you should think of a class as a blueprint for an object that specifies what an object of that class can do (member functions) and what data it holds (member variables), so an object itself is essentially like a bundle of variables grouped together into a single block.
And of course, each object gets its own unique set of variables.

Thank you, i will try it tonight :grin:

I do like Brownies, but to be honest you mentioned this to me. :grinning:

Here is the complete .h i am using:

#include <stdint.h>

//Total colors 2
const uint16_t Turret_pal[] = {
65535,0,
};

const uint8_t Turret[] =
{
10,8,
17,17,0,17,17,
17,17,0,17,17,
17,17,0,17,17,
17,0,0,0,17,
0,0,0,0,0,
0,0,0,0,0,
0,0,0,0,0,
0,0,0,0,0,
};



class GameObject
{
    public://declare public variables
        int x;
        int y;
        int width;
        int height;
        int direction;
        int speed;

    /*GameObject()//constructor, this builds the object
    {
        x = positionX;//take over the X position of the turret
        y = positionY;//take over the y position of the turret
        width = 1;
        height = 3;
        speed = 1;
    }
*/

GameObject(int positionX, int positionY)//constructor, runs once to create the object
{
	x = positionX;//take over the X position of the turret
	y = positionY;//take over the y position of the turret
	width = 1;
	height = 3;
	speed = 1;
}
    void update()
    {

    // Draw and change the location of the object.

    Pokitto::Display::drawLine(x,y,x+height,y);


    }

    bool collidesWidth(GameObject *o)
    {
        // Test if the bullet collides with Enemy.
        // If it does, make it invisible and return true
 }

};

/*

GameObject bullet[10];
GameObject enemy[5];

while(true)
{
    for(int x=0; x<=10;x++)
    {
        bullet[x].update();
        for(int y=0;y<=5;y++)
        {
            if(bullet[x].collidesWith(&enemy[y])
            {
                // Make explosion, etc, etc.
            }
        }
    }
}

*/

The turret does already displays and moves when using buttons, I am making space invaders 1 step at the time. When the fire button is used i want to pass the current position to a bullet object.

I could make the turret a object as well, but first I am working out how to setup the stucture correctly.

In that case you get the points for listening to advice and not being an ignoramus.

It all looks fine, apart from the commented out loop.
Functionally the loop is fine, but GameObject will need that default constructor I mentioned if you want to make some arrays.
(And obviously the loop will have to go inside a function somewhere, but I’ll assume you knew that.)

So I’m going to take this opportunity to teach you a few C++11 things.

So, here’s the class with some changes:

class GameObject
{
public: //declare public variables
	int x;
	int y;
	// C++11 feature: provide default values for member variables
	int width = 1;
	int height = 3;
	int speed = 1;
	int direction;

	// C++11 feature: let the compiler generate the default constructor
	GameObject() = default;

	// Constructor, runs once to create the object
	GameObject(int positionX, int positionY)
	{
		// Assign the game object positions
		x = positionX;
		y = positionY;
	}
	
	void update()
	{
		// Draw and change the location of the object.
		Pokitto::Display::drawLine(x, y, x + height, y);
	}

	bool collidesWidth(GameObject *o)
	{
		// Test if the bullet collides with Enemy.
		// If it does, make it invisible and return true
	}
};

Firstly I’m using a default member initialiser to give GameObject's member variables a default value.
That default value will be used by any constructor that doesn’t provide a different value.

Secondly I’m using an ‘explicitly defaulted default constructor’, which means I’m letting the compiler generate the default constructor.

The default constructor is the constructor that is called when you declare a variable for a class but don’t explicitly initialise it, e.g. GameObject gameObject;.
The default constructor is also called when you explicitly initialise the object with no arguments, e.g. GameObject gameObject = GameObject();.

The constructor the compiler generates will initialise member variables to their provided default values if they have them.
Otherwise if the member variable is a class type then it will call the object’s default constructor.
I need to check the rules for what happens to non-class types. It definitely either leaves them with ‘indeterminate’ values or zero-initialises them.
(The rules are hopelessly complicated, I’ve spent at least the last half an hour reading them.)


Originally I was going to teach you 5 things in total, but I decided to drop down to 2 to avoid overwhelming you.
If you want I can teach you the other things later, but only 2 of them are related to classes.
(The third is an alternative to pointers.)

1 Like

I am working out the tips you gave, but although it works it doesnt do what i want (does compile)

I get the feeling there is a flaw in my logic.

If i press the A button a gameobject is created, using the x,y coordinates from the turret (because it would be nice to see the bullet coming out of the barrel)

I placed a

gameObject.update();//updates the position of the bullet

inside this to test and that does draw the line i want to use as a bullet.

But when I place this outside the button loop it doesnt work(because it isnt present yet ?)

What would the normal structure be ?

  • press A button to fire, creates a bullet object at certain coordinates
  • use a update() in the main game loop to update the position of the object.

I copied the last class code you posted as a remark in the .h file, i want to use this later once I got it working better.

Before making suggestions about the rest of your code, I’d need to have an idea of what functionality you’re actually aiming for.

Is the player just controlling one turret that fires bullets?
Is the player going to be able to control multiple turrets?

You always have to think a little bit ahead when designing classes.
(Personally I often end up drawing a diagram of class interaction before I even start attempting to write code.)

What are you calling the ‘button loop’ here?

(It might help if you put your code up on Github and kept it updated with your changes.)


While I’m thinking of it, a suggestion for your class:
Have a separate update and draw method.
Keeping updating the state and rendering separate is usually a good idea.

Space invaders, that is what I am trying to make. Sofar I have a turret that moves left and right.
Using the enum class there is a startscreen, a play loop and a gameoverscreen.

I want to use a class to generate bullets from the current location of the turret when i press A (fire).

This piece of code :

// Pressing the A button fires gun
if (pokitto.buttons.repeat(BTN_A, 0))
{
    //spawn a bullet
    GameObject gameObject = GameObject(positionX + 10, positionY - 10);

}

At the moment I have so little code, that I can copy paste it. so for now I dont use it yet.

Right, I was getting a bit confused there.
Normally with space invaders the thing firing bullets is classed as a ship.

Herein lies your problem.
You create the gameObject and then it exists until the end of the if statement, after which it’s destroyed.

To get it to persist, it has to exist outside the function somehow.
The easiest way for now would be to have a global list (i.e. a std::vector<GameObject>) of bullets.
(A std::vector isn’t always the best option because it does dynamic memory allocation, but right now it would be easier for you than some of the more optimal solutions.)

So what you end up with is:

// In your includes
#include <vector>

// In your global variables
std::vector<GameObject> bullets = std::vector<GameObject>();

// In updateGameplay
	// Pressing the A button fires gun
	if (pokitto.buttons.repeat(BTN_A, 0))
	{
		// Spawn a bullet
		if(bullets.size() < MaxBullets)
		{
			GameObject bullet = GameObject(positionX + 10, positionY - 10);
			bullets.push_back(bullet);
		}
	}

// In drawGameplay
void drawGameplay(void)
{
	// Background first, everything else is drawn on top of that
	pokitto.display.bgcolor = 1;

	//Turret

	pokitto.display.drawBitmap(positionX,positionY,Turret);
	//pokitto.display.drawBitmap(0,0,Turret);
	
	// Technically this is the wrong type
	for(std::size_t i = 0; i < bullets.size(); ++i)
	{
		bullets[i].update();
	}
}

So your full code ends up becoming:

#include "Pokitto.h"
#include "Turret.h"

#include <vector>

enum class GameState //This sets up a class that limits the possibilities to the items in it.
{
	TitleScreen,
	Gameplay,
	GameOver,
};

// Create an instance of Pokitto::Core
Pokitto::Core pokitto;

//declare variables
int step = 2;//how many pixels the turrets moves
int positionX = 50;//places the turret in center of screen
int positionY = 74;//places the turret on bottom of screen
int TurretW = 10;//width of turret bitmap
int TurretH = 8;//height of turret bitmap
int XBullet;//x position of bullet
int YBullet;//y position of bullet
bool Bullet = false;//bullet is present or not
int BulletNr = 0;//to limit max number of bullets
int MaxBullets = 3;//actual maximum bullets on screen

std::vector<GameObject> bullets = std::vector<GameObject>()


GameState gameState = GameState::TitleScreen; //sets the gamestate to start in the titlescreen ?

const int screenW = Pokitto::Display::getWidth() - 1;//the width of the screen - 1 110x88
const int screenH = Pokitto::Display::getHeight() - 1;//the Height of the screen - 1

// Predeclare functions so they can be called, fill them after the main
void updateTitleScreen(void);
void drawTitleScreen(void);

void updateGameplay(void);
void drawGameplay(void);

void updateGameOver(void);
void drawGameOver(void);

int main(void)//main game loop
{
	pokitto.begin();

	pokitto.display.persistence = 0; // 0 to clear screen on every update, 1 to sustain picture
	pokitto.display.load565Palette(Turret_pal);//load the color pallet from the Turret.h file


	pokitto.setFrameRate(20);//set frame rate to 20

	while(pokitto.isRunning())
	{
		if(pokitto.update())//updates according to framerate
		{
			// Update logic
			switch(gameState)
			{
				case GameState::TitleScreen: updateTitleScreen(); break;
				case GameState::Gameplay: updateGameplay(); break;
				case GameState::GameOver: updateGameOver(); break;
				//nobase case
			}
			// Drawing logic
			switch(gameState)
			{
				case GameState::TitleScreen: drawTitleScreen(); break;
				case GameState::Gameplay: drawGameplay(); break;
				case GameState::GameOver: drawGameOver(); break;
				//nobasecase
			}
		}
	}
}

void updateTitleScreen(void)
{

//to update the titlescreen, like movement or something

}

void drawTitleScreen(void)
{
	// Your titlescreen code here

	//pokitto.display.drawBitmap(0,0,Etch_title);//Title
	pokitto.display.bgcolor = 1;
	pokitto.display.setColor(0);
	pokitto.display.println("Press B");

	// Move to gameplay when B is pressed
	if (pokitto.buttons.repeat(BTN_B, 0))
	{
		gameState = GameState::Gameplay; //set the gamestate to gameplay
	}
}

void updateGameplay(void)
{
	// Input handling
	if (pokitto.buttons.repeat(BTN_LEFT, 0))
	{
		// Don't go below zero
		if(positionX >= step)
		positionX -= step;//positionX = PositionX - step
		else
		positionX = 0;
	}

	if (pokitto.buttons.repeat(BTN_RIGHT, 0))
	{
		const int difference = screenW - positionX - TurretW;//10 is width of turret bitmap
		if(difference >= step)//as long as difference is larger then the step (1 in this case)
			positionX += step;//positionX = positionX + step
		else
			positionX = screenW - TurretW;//TurretW = 10 and is width of turret bitmap
	}

	// Pressing the A button fires gun
	if (pokitto.buttons.repeat(BTN_A, 0))
	{
		// Spawn a bullet
		if(bullets.size() < MaxBullets)
		{
			GameObject bullet = GameObject(positionX + 10, positionY - 10);
			bullets.push_back(bullet);
		}
	}

	if (pokitto.buttons.repeat(BTN_C,0))
	{
	   gameState = GameState::GameOver; //set the gamestate to GameOver
	}
}

void drawGameplay(void)
{
	// Background first, everything else is drawn on top of that
	pokitto.display.bgcolor = 1;

	//Turret

	pokitto.display.drawBitmap(positionX,positionY,Turret);
	//pokitto.display.drawBitmap(0,0,Turret);
	
	// Technically this is the wrong type
	for(std::size_t i = 0; i < bullets.size(); ++i)
	{
		// This should be called draw, not update
		bullets[i].update();
	}
}

void updateGameOver(void)
{
	//pokitto.display.setColor(1);
	if (pokitto.buttons.repeat(BTN_B, 0))
	{
	 pokitto.display.clear();//clear the screen

		gameState = GameState::Gameplay; //set the gamestate to Gameplay

	}
}

void drawGameOver(void)
{
	pokitto.display.setColor(0);
	pokitto.display.println ("Game over");
}

Some other points:

  • You should probably rename GameObject to Bullet
  • You should probably have a separate update and draw on Bullet

Remind me at a later date and I’ll explain what std::size_t is and why I used it instead of int and why it’s still technically not the right type to use there.
Basically std::size_t is better for indexing things than int and there’s another type that would be even better than std::size_t but then I’d have to explain type aliasing.
Be aware that std::size_t is unsigned, so you should try to only count up with it and avoid counting down with it.

1 Like

Thank you, I will see what i can do with this.

1 question that just pops into mind : Wouldnt be logical to make a ship/turret class as well ? That would give me more possibilaties for its function ( firing / is destroyed / moving)

Yes, making a ship/turret class would be a good idea.

It would let you indicate that the ship ‘owns’ the bullets or devise a system where the ‘world’ ‘owns’ the bullets and the ship spawns bullets into the world.


Getting a bit philosophical for a moment:

When it comes to class design, there’s usually good and bad designs that have different advantages and disadvantages, but there’s no such thing as a ‘perfect’ design, so it pays to weigh up different ideas and see what works best for your situation.


Part of me is tempted to say move this to a PM so I can work on this with you, but part of me thinks other people will benefit from reading my suggestions and seeing the code evolve.

What do you and/or everyone/anyone else think?

2 Likes

I’m following along with interest. It’s helpful to see everyone’s design decisions in the open.

2 Likes

I feel in touch with this part of you :grimacing:

1 Like

Yep, lets keep it here. If just 1 person gets a better understanding trough this then it is worth it.
(even if that is not me :smile:)

I will keep pasting code here, and setup github for future use.

1 Like