Best practice - extend sprite or store it in a custom class?

I read that its not recommended to extend the sprite class (perhaps because of the single responsibility principle).

How then do we track data pertaining to a sprite? Say the sprite is a brave knight who has armor and hit points and other stats.

Would you do something like this?
class Knight : public Ref { Knight::Knight(); Knight::Knight(Sprite* sprite); Knight::~Knight(); Sprite* sprite; }

1 Like

Could you tell me where you saw that it is not recommended to extend Sprite?

I do this:

class PlantSprite : public cocos2d::Sprite
{
   
}

and to instantiate:

PlantSprite* PlantSprite::create(const std::string& _file, int _g, int _pID)
{
	PlantSprite *pSprite = new PlantSprite();
	
	if (pSprite && pSprite->initWithSpriteFrameName(_file.c_str()))
	{
		pSprite->autorelease();
        
                pSprite->init(_g, _pID);
        
                pSprite->scheduleUpdate(); // runs update()

		return pSprite;
	}

	CC_SAFE_DELETE(pSprite);
	return NULL;
}
1 Like

I believe it was here. I made some sense to me as it seems the Sprite class is responsible for graphics and things related to that.

http://gamedev.stackexchange.com/questions/12598/should-i-be-subclassing-ccsprites

Thanks :slight_smile:

That is CCSprite (Cocos2d-x v2.x) , which is not the same thing as Sprite (Cocos2d-x v3) anymore. The rules have changed.

Thank you I will take your advice.

I am curious though does the Sprite class violate SRP?

Could you tell me what SRP is?

If you’re going with OOP then ask yourself about the IS A or the HAS A relationship.

For example, a Banana IS A Fruit, therefore extend the Fruit class to make a Banana class. A BananaTree HAS A Banana, therefore the BananaTree class will have the Banana class as a component.

So the question is, is the Knight a sprite or does a Knight have a sprite?

If the Knight is a sprite then extend the sprite class. If the Knight has a sprite then make the sprite a component of the Knight.

It sounds to me like you’re making a Character. A Knight is a Character and a Character has a Sprite. So, this is probably what you want:

`
class Character
{
Sprite *mSprite;
int mHitPoints;
int mHealth;
// Other things that a character might have or do.
};

class Knight : public Character
{
HeavyArmor mHeavyArmor;
// Other things that are specific or special about a Knight.
};
`

Then keep a pointer to all of your Characters in an array so you have immediate access to them and can reuse them if needed.

1 Like

I always take the ‘Knight has a Sprite’ principal.

Knight would inherit from a ‘GameEntity’ class, which itself has a Sprite property.

That allows me to model my world (however I choose) and position Knights within it.
If a knight wears a helmet and carries a sword, then the Knight class might have two properties (more likely a collection property) of a Sword class and a Helmet class - each of which inherit from the GameEntity class.

A GameEntity is responsible for the modelling of a single ‘thing’ in the game (Knight, Helmet, Sword).

When I want to draw the entities, I use the Sprite contained within them.

Using this method keeps responsibility for the game separate from the displaying of the game - so SRP is alive and well, and somewhat follows the MVC pattern, wherein I Model the world, have a View of the world (containing sprites) and have a Controller to control the world.

Incidentally, I also steer clear of using PhysicsSprites for the same reason - I keep the physics logic at the model level, then render the model in the view.

I felt like the “Has A” relationship was the most correct based on SRP.

Thanks for the feedback!

What are these for ?

These are things I pass in for my own use. Ignore them.

1 Like

Hi Slack

How then would you initialize your object in your scene… from your example

cocos2d::Sprite* sprite = PlantSprite::create("image.png");

??

Depends, but usually I do something like:

 CornSprite* corn = CornSprite::createSprite(getKernelSpriteFileNames(), 
            getPopcornSpriteFileNames());

O God. Why is new code always scary

I never seen a get kernal thing or two fucntions in a create sprite call

You example also only has ::create()

mmmm… OK … I suppose … :frowning:

I am doing this

Scene Class

GameObject* object = new GameObject();
    object->createWithSpriteFrameName("enemy.png");
    object->setPosition(cocos2d::Vec2(randX, randY));
    object->setScale(m_Scale);
    m_MainLayer->addChild(sprite);

Game Object Class

H

#ifndef GAMEOBJECT_H
#define GAMEOBJECT_H

#include "cocos2d.h"
#include "Box2D\Box2D.h"

#include "Settings.h"

class GameObject : public cocos2d::Sprite {

	public:

		GameObject();
		~GameObject() {};

		GameObject* create(const std::string& file);

		b2Body* getBody() { return m_Body; };

		cocos2d::Vec2 getPosition() { return m_Position; };

		virtual void update();

	private:

		b2FixtureDef m_BodyFixture;
		b2BodyDef m_BodyDef;
		b2Body* m_Body;
		b2CircleShape m_Shape;
		float32 m_AngularVelocity;

		cocos2d::Vec2 m_Position;

};
#endif // DEFINE - GAMEOBJECT_H

CPP

GameObject* GameObject::create(const std::string& file){

	GameObject* p_Sprite = new GameObject();

	if (p_Sprite && p_Sprite->initWithSpriteFrameName(file.c_str()))
	{
		p_Sprite->autorelease();

		p_Sprite->init();

		p_Sprite->scheduleUpdate();

		return p_Sprite;
	}

	CC_SAFE_DELETE(p_Sprite);
	return NULL;

}

What confuses me is the second time i create a pointer object in the CPP… I did this already in the scene :frowning:

IIRC you are using code that I wrote.

I dont see why you need this. pass in the file name as part of the create() function you have defined in your header.

Yes using your example from above in this tread…

I only using that file for batching, although I know now it auto batches. Ill test that out.