First off I'd like to say I'm still very inexperienced, I only started programming last week.
I'm currently developing my first game (and C++ application), I've run into a problem, and haven't managed to solve it, and I'm starting to give up.
I can spawn enemies correctly, but now I want each of them to shoot once each second. The code I'm using for this is as follows:
for (int i = 0; i < 200; i++)
{
if (enemy_basic[i].getPositionY() >= -100 && enemy_basic[i].getPositionY() <= 900)
{
if (enemyBasicLaserNumber[i] < 200 && enemyLaserTimer[i].getElapsedTime().asSeconds() > 1)
{
enemy_laser[enemyBasicLaserNumber[i]].setPosition(enemy_basic[i].getPositionX(), enemy_basic[i].getPositionY());
enemyLaserTimer[i].restart();
enemyBasicLaserNumber[i]++;
cout << enemyBasicLaserNumber[i] << endl;
}
if (enemyBasicLaserNumber[i] >= 199) enemyBasicLaserNumber[enemyBasicNumber] = 0;
}
}
Now, I know there's something wrong in there somewhere, because the bullets keep getting reset back to enemy as soon as there are multiple enemies on screen (as long as it's only 1 enemy it works perfectly), and I still haven't managed to figure out what needs changing, or if I need to do this in an entirely different way.
If someone could just point me in the right direction or help me out in some way, I'd be very grateful!
You should structure your code in a more appropriate Object oriented style. You could create a base class Entity which contains a virtual function update. It also should contains the reference/pointer parent of the entity.
class SceneManager
{
public:
SceneManager();
virtual ~SceneManager();
//put your own parameters
Bullet& createBullet(/* */);
private:
//a list of pointers for polymorphism
boost::ptr_list<Entity> entities_;
};
class Entity
{
public:
Entity(SceneManager& smgr);
virtual ~Entity();
virtual void update(const float timedelta) = 0;
private:
const int id_;
static int count_ = 0;
protected:
SceneManager& smgr_;
//local scale, rotation
Transform transform_;
//position
Vector3d position_;
//the model,sprite representation
char drawable_;
};
class Enemy:public Entity
{
public:
Enemy(SceneManager& smgr);
virtual ~Enemy();
virtual void update(const float timedelta);
void shoot();
};
class Bullet:public Entity
{
public:
Bullet(SceneManager& smgr);
virtual ~Bullet();
virtual void update(const float timedelta);
};
void Enemy::update(const float timedelta)
{
//shooting each second
static float shootDelay = 1;
static Timer timer;
//check if timer has started
if(!timer.started)
timer.start();
//if the timer has reached the desired delay
if(timer.elapsed() > shootDelay)
{
shoot();
timer.restart();
}
}
void Enemy::shoot()
{
//create the bullet via the scenemanager
smgr_.createBullet(/******/);
}
This code is only symbolic code, you will have to use the existing libraries to actually implement the structure. This way of coding is used notably by the Irrlicht and Ogre3D graphic engines, but can be applied as well to the core logic of a game engine.
Related
I am currently benchmarking a program on a Linux system with Valgrind.
I have this strange cache miss with the getter method const int GetID() const,
but I can't really explain where it came from. Does anyone have any idea what's causing this problem?
I thought it might be caused by the constant keyword at the end, but it hasn't changed.
The cache miss occurs in the L1 during the read operation. I have added a screenshot below the code snippet.
class GameObject
{
friend class GameManager;
private:
int id;
GameObject();
static int CreateID() { return /* do some id stuff in here */}
...
public:
~GameObject();
const int GetID() const { return id; }
...
};
KCachegrind Screenshot:
UPDATE:
These are methods of the GameManager class that call the const int GetID() const method. It is called when a GameObject must be destroyed or returned to a specific point. The GameManager contains a vector of all GameObjects, they are created when the application starts, after which the vector does not change at all.
After they are created, the attached components call the GameObject* GetGameObject(int const _gameObjectId) method once to retrieve all required components. So I guess the GameObjects should already be in the cache or did I miss a point?
Maybe the call is so strong that it creates more cache misses at the beginning of the program than the rest of the application at runtime?
void GameManager::DestroyGameObject(const int _id)
{
for (auto it = gameObjects.begin(); it != gameObjects.end(); it++)
{
if (it->GetID() == _id)
{
gameObjects.erase(it);
return;
}
}
}
GameObject* GameManager::GetGameObject(const int _gameObjectId)
{
for (int i = 0; i < gameObjects.size(); i++)
{
if (gameObjects[i].GetID() == _gameObjectId)
{
return &gameObjects[i];
}
}
return nullptr;
}
I am building a tile engine in C++. What is the most efficient way to store the logical properties of the individual tiles in the game? I understand the rendering side of the program, but I am having trouble taking a simple id number and turning it into an actual set of properties ( like whether or not a tile is walkable or flammable or can trigger an event, etc. )
One idea is to have a tile object that has the potential to be any kind of tile, and turns on certain boolean "switches" based on the type ( note that the following is mostly just pseudocode and not meant to actually compile):
class Tile
{
private:
int m_type;
bool m_walkable;
// etc...
public:
Tile( int type ) : m_type( type)
{
if( type == 0 )
{
m_walkable = true;
} else if( type == 1 ) {
m_walkable = false;
}
// etc etc would probably be a switch
// statement but you get the idea
}
};
Personally, I do not like this idea; I think it would be much more elegant for each type of tile to have its own data structure. I imagine using some kind of inheritance based system but I just can't seem to put it all together. Fundamentally, I think it should look something like this:
enum class TileType
{
TILE_TYPE null, // 0
TILE_TYPE floor, // 1
TILE_TYPE wall, // 2
// etc ...
};
class BTile
{
private:
// Location and dimensions of tile
int m_xOffset;
int m_yOffset;
int m_width;
int m_height;
// Type of tile, initialized to 0 for base class
TileType m_type;
public:
// ...
};
class Floor : public BTile
{
private:
TileType = 1;
bool walkable = true;
// etc...
};
class Wall : public BTile
{
private:
TileType = 2;
bool walkable = false;
};
Something like this would feel much more organized and flexible, while also allowing me to plug Floor and Wall objects into any kind of function expecting a Tile object. The problem is that I just cannot seem to put this all together in a way that actually works - for example, how can I provide a specific tile type with the tile it is associated with? If I am reading a text file into my program for example, how can I get from 001 to Tile->Floor? Any advice or input on this subject would be greatly appreciated.
Try using a factory method.
The simplest way to do this is to use a switch.
Tile* createTile(TileType tileType) {
switch(tileType) {
case TileType.floor: return new Floor;
case TileType.wall: return new Wall;
}
return nullptr;
}
This is usually not recommended as you have to update the factory each time you add a new type.
Alternatively you could use some trick to register the types to the factory.
One approach is the one described here. There are a lot more strategies.
Why are you reinventing OO? Objects already have a type, no need for TileType. You may want to read up on virtual functions.
There are several approaches here, depending on what exactly you want the tiles in your game to do. You can go the object oriented way, by having distinct classes for the different tile types that you have, or you can go simpler and just have a bitset that represent the different abilities your tiles will have.
This choice will depend on what you want to do.
Bitset only
Oftentimes, the bitset-only variant is enough. To do that you'll need something along those lines:
You most probably want a set of flags which will represent different abilities of your tiles (e.g IsWalkable, IsWater, etc). Something similar to this:
struct TileFlag
{
bool m_IsWalkable : 1;
bool m_IsWater : 1;
//other flags you might need
};
With this in mind, your Tiles would be something along those lines (Texture and Texture manager are there just for the example):
struct Tile
{
void Render();
void Serialize(const boost::property_tree::ptree& tileData)
{
m_Flags.m_IsWalkable = tileData.get<bool>("walkable", false);
m_Flags.m_IsWater = tileData.get<bool>("water", false);
std::string texturePath = tileData.get<std::string>("texturePath", "");
m_TileTexture = TextureManager::GetOrLoad(texturePath);
}
TileFlags m_Flags;
std::shared_ptr<Texture> m_TileTexture;
};
You would need some kind of registry, where you contain all of your tiles, so that your levels can reference the tiles. This registry can be as simple as an std::map.
Some example code:
struct TileRegistry
{
void LoadTiles(const boost::property_tree::ptree& tiles)
{
for (boost::property_tree::ptree::value_type& tileType : tiles.get_child("tileTypes"))
{
std::unique_ptr<Tile> newTile = std::make_unique<Tile>();
newTile->Serialize(tileType.second);
m_Tiles[tileType.first] = std::move(newTile);
}
}
Tile* FindTile(const std::string& tileType)
{
Tile* result = nullptr;
auto search = m_Tiles.find(tileType);
if (search != m_Tiles.end()) {
result = search->second.get();
}
return result;
}
std::map<std::string, std::unique_ptr<Tile>> m_Tiles;
};
Then, when you load your levels, you just search for the Tile Type in the TileRegistry, and you'll get a pointer to your Tile.
OOP Style object inheritance
This approach would borrow a lot from the previous one, with the biggest difference being in how you are going to create your tiles. You are going to need some kind of Factory, as #artcorpse mentions.
If you want to go a bit more generic, you can do some automation magic with a few macros:
struct TileFactory
{
static std::map<std::string, CreateFunctionPtr> m_FactoryFunctors;
std::unique_ptr<ITile> CreateTile(const std::string& tileType)
{
std::unique_ptr<ITile> result;
auto search = m_FactoryFunctors.find(tileType);
if (search != m_FactoryFunctors.end()) {
auto creationFunctionPtr = search->second;
result = creationFunctionPtr(); //Notice the function invocation here
}
return result;
}
};
template<typename T>
struct TileRegistrator
{
TileRegistrator(const std::string& tileTypeName){
TileFactory::m_FactoryFunctors[tileTypeName] = &T::CreateTile;
}
};
#define DECLARE_TILE_TYPE(TileType) \
static std::unique_ptr<ITile> CreateTile() { return std::make_unique<TileType>();} \
static const TileRegistrator<TileType> s_Registrator;
#define DEFINE_TILE_TYPE(TileType) \
const TileRegistrator<TileType> TileType::s_Registrator = {#TileType};
And how you use those macros:
struct ITile
{
virtual ~ITile() = default; //Don't forget a virtual destructor when you have object which can be accessed by pointer to Base!
virtual bool IsWalkable() const = 0;
virtual bool IsSailable() const = 0;
virtual void Serialize(const boost::property_tree::ptree& tileData) = 0;
};
In your .h files, e.g. OceanTile.h:
struct OceanTile : public ITile
{
DECLARE_TILE_TYPE(OceanTile);
bool IsWalkable() const override;
bool IsSailable() const override;
void Serialize(const boost::property_tree::ptree& tileData) override;
int m_WavesIntensity{0};
};
In your .cpp files, e.g. OceanTile.cpp:
DEFINE_TILE_TYPE(OceanTile)
bool OceanTile::IsWalkable() const {
return false;
}
bool OceanTile::IsSailable() const {
return true;
}
void OceanTile::Serialize(const boost::property_tree::ptree& tileData) {
m_WavesIntensity = tileData.get<int>("WavesIntensity", 0);
}
And creating a new tile object, asuming you know its type as a string (e.g. coming from a data file is very simple:
void LoadTiles(const boost::property_tree::ptree& levelData)
{
for (boost::property_tree::ptree::value_type& tile : levelData.get_child("levelTiles"))
{
std::unique_ptr<ITile> newTile = TileFactory::CreateTile(tile->first);
newTile->Serialize(tile.second);
//Do whatever you want to do with your Tile - maybe store it in some vector of all tiles for the level or something
}
}
Disclaimer: I have not compiled or tested any of the above code, but hopefully it can give you an idea on how to get started. There might be any number of bugs hiding there.
I encourage you to start with the Bitset only option, as this is enough for a lot of different types of games, and is much simpler to work with and reason about.
Hope this gives you some start :)
I work on a project made with cocos2d-x framework (c++).
In my Player class, I have to manage the animations.
Iinitially I had this code that worked without any problem:
First, the animation object is a cocos2d Class cocos2d::Animation. Just remember that this object contains a cocos2d::Vector<AnimationFrame*> _frames; member.
Doc: http://www.cocos2d-x.org/reference/native-cpp/V3.5/d3/dc5/classcocos2d_1_1_animation.html#a0fdc0be158df7e09d04644be353db056
class Player : public cocos2d::Sprite {
private:
cocos2d::Map<std::string, cocos2d::Animation*> animations;
cocos2d::Vector<cocos2d::SpriteFrame*> getAnimation(const char *format, int frameStart, int count);
void update(float delta) override;
bool init() override;
public:
static Player* create();
bool init() override;
//...
};
And the implementation side:
bool Player::init() {
//...
animations.insert("idleN", Animation::createWithSpriteFrames(getAnimation("%04d", 207, 9), 0.1));
//...
}
Vector<SpriteFrame*> Player::getAnimation(const char *format, int frameStart, int count) {
auto spriteCache = SpriteFrameCache::getInstance();
Vector<SpriteFrame*> animFrames;
char str[100] = {0};
for (int i = 1; i <= count; i++)
{
sprintf(str, format, frameStart);
log("%s", str);
animFrames.pushBack(spriteCache->getSpriteFrameByName(str));
frameStart++;
}
return animFrames;
}
//later in the code execution
void Player::manageIdle() {
auto idleAnim = Animate::create(animations[0].anim);
runAction(idleAnim);
}
You can see each Animation is contained in cocos2d::Map<std::string, cocos2d::Animation*> and as I say before, this code worked perfectly, no error.
But I needed some more informations in addition to the name and the object itself so I decided to use a structure to store all infos for each animation. And I replaced the cocos2d::Map<std::string, cocos2d::Animation*> by std::vector<animData> with animData as structure. I refactored the code like so:
class Player : public cocos2d::Sprite {
public:
typedef struct animation {
std::string name;
cocos2d::Animation* anim;
//all others info needed, not relevant here, (just several enum type variables)
} animData;
private:
std::vector<animData > animations; //the new container
//rest of code stay unchanged
};
The changes in the implementation side:
bool Player::init() {
//...
animations.push_back({"idleN", Animation::createWithSpriteFrames(getAnimation("%04d", 207, 9), 0.1)});
//no problem here...
}
But now, when I try to create a new anim with a animation saved in my container (vector) I get a SegV on this line:
void Player::manageIdle() {
auto idleAnim = Animate::create(animations[0].anim); //SegV here, in Animate::create() funct
runAction(idleAnim);
}
After search, I find that each structure member anim which is type of cocos2d::Animation*, now conatains a empty cocos2d::Vector<AnimationFrame*> _frames; and there is the problem !
It’s as if they lose the cocos2d::Vector<AnimationFrame*> ref or something like that.
So my question is why cocos2d::Vector<AnimationFrame*> become empty with my refactored code and not whith the previous one ?
I found this with test like that:
auto test = animList[0].anim->getFrames();
if (test.empty()) {
log("empty"); //The test output empty
}
Debugguer screen in the end of the init() funct:
Debugguer screen in Player::manageIdle() funct:
Edit: when I add animations.back().anim->retain(); right after the line to add an element in the vector, it solves the problem !
animations.push_back({"idleN", Animation::createWithSpriteFrames(getAnimation("%04d", 207, 9), 0.1)});
animations.back().anim->retain();
Because cocos2d::Animation* inherit from cocos2d::Ref, it is an auto-release object. When used inside a cocos2d container like cocos2d::Map or cocos2d::Vector, it is auto managed by the container itself. But I use a std::vector so I lose the ref I think. Something like that.
Now I need to find a way to get rid of this additional line of code because this multiple by twice my number of line here !
So new question here: How I can get rid of the fact I have to call animations.back().anim->retain(); each time I add a new element in my vector ?
You might create a wrapper around Ref, which "retains" ownership, and store this wrapper instead, sort of a std::unique_ptr e.g.
template<typename T> class RefOwner {
public:
RefOwner(T *t) : ref(t) {
ref->retain();
}
~RefOwner() {
ref->release();
}
T *operator->() { return ref; }
private:
T *ref;
};
and then use it as
struct animData {
std::string name;
RefOwner<cocos2d::Animation> anim;
//all others info needed, not relevant here, (just several enum type variables)
};
Disclaimer: I have no experience with cocos2d-x, just looked at Animation and Ref
I'm writing an RPG, and the basic code is below. Each player is supposed to have four stats: earth, air, fire, and water. The problem is this: if fire is less than zero, it should spill over into damage to earth. Here is my code so far:
//DANCE GAME
#include <iostream>
using namespace std;
class element {
public:
//get primary magnitude
int gpm(){
return pm;
};
//set primary magnitude
int spm(int x){
pm = x;
};
protected:
//primary magnitude
int pm;
};
class fire : public element {
};
class earth : public element {
};
class soul {
public:
earth e;
fire f;
};
int main () {
soul p1, p2;
p1.e.spm(5);
cout << p1.e.gpm() << endl;
p1.f.spm(5);
cout << p1.f.gpm() << endl;
return 0;
};
So, I want each element to have a unique spm() function to control its special behavior. For fire, it should have the effect of the pseudocode below:
fire.spm(int x)
pm += x
if pm < 0
x = abs(pm)
pm = 0
owner.earth -= x
Obviously it's that last line that's giving me problems. What is the proper way to accomplish such a thing?
You should pass owner as argument to method spm.
Obviously it's that last line that's giving me problems. What is the proper way to accomplish such a thing?
There is a guide line that states that you should not work on another object's data (Law of Demeter). Instead, you should propagate the changes downwards, from objects that know everyone else, to objects that know only how to handle their own data.
This means that your fire element implementation should not know that a soul has an earth element (let alone try to alter it directly). It is instead the responsibility of the soul to propagate these changes (because a soul instance knows both earth and fire classes).
Based on this, you should consider something like this:
class element
{
public:
// update primary magnitude, taking hints from the provided element
// if this->get_type() == other.get_type()
// at the end of the operation, primary_magnitude_ should be
// other.primary_magnitude_;
virtual void update(const element& other)
{
if(get_type() == other.get_type())
set_magnitude(other.magnitude());
}
protected:
enum { earth_type, fire_type, air_type, water_type } element_type;
virtual element_type get_type() const = 0;
int magnitude() const { return primary_magnitude_; }
void update_magnitude(int delta) { primary_magnitude_ += delta; }
void set_magnitude(int value) { primary_magnitude_ = delta; }
private:
int primary_magnitude_;
};
class earth
{
public:
void update(const element& other) override
{
switch(other.get_type())
{
case element::earth_type:
set_magnitude(other.magnitude());
break;
case element::fire_type:
update_magnitude( std::min(0, other.magnitude()) );
break;
}
}
protected:
element_type get_type() const override { return element::earth_type; }
};
class soul
{
public:
void update(const element& value)
{
earth_.update(value);
fire_.update(value);
}
private:
earth earth_;
fire fire_;
};
spm means "set primary magnitude". Two notes on this. First, it's better to call function SetPrimaryMagnitude or set_primary_magnitude if the function is supposed to actually set the primary magnitude. It makes life so much easier. BTW, element class has only one value, pm, so I don't see the need to call that value "primary". I mean, what would be "secondary"?
Second, you actually don't want to set that value but to have a combination of actions that increase, decrease, and spill the difference to yet another value. Maybe it would be better to call such action "adjust". And make it a member of soul (if that class is the only one that handles the math this specific way). Something like this:
soul::AdjustElementValues(element& primary, element& secondary, int value)
{
// decrease primary by value, and if needed spill the remains to secondary
}
If soul is not the only class that does this math, then move the math to separate class, which has access to all needed objects. Say, class element_spiller with constructor that takes pointers to two elements, primary and secondary, and has a method that takes int value argument and changes the elements accordingly.
I am sort of a noob in C++ and I am trying to make a simplegame. This is my problem:
I created class called sprite:
class Sprite
{
private:
Point2D sp_pos;
Point2D sp_vel;
SDL_Surface* sp_img;
Point2D sp_center;
Point2D sp_size;
double sp_radius;
bool sp_animated;
int sp_frames;
int sp_cur_frame;
public:
Sprite() {}
Sprite(Point2D pos, Point2D vel, SDL_Surface *img, ImageInfo info, bool animated = false, int frames = 0);
virtual void draw(SDL_Surface* screen);
virtual void update();
void setInfo (ImageInfo info);
void setPos( Point2D pos ) { sp_pos = pos; }
void setVel( Point2D vel ) { sp_vel = vel; }
void setImg (SDL_Surface* img) { sp_img = img; }
void setNextFrame() { sp_cur_frame++; }
void setFrame( int frame ) { sp_cur_frame = frame; }
void setAnimated(bool animated) { sp_animated = animated; }
void changeVelX (int c) { sp_vel.setX(c);}
void changeVelY (int c) { sp_vel.setY(c);}
void changePosX (int c) { sp_pos.setX(c);}
void changePosY (int c) { sp_pos.setY(c);}
SDL_Surface* getImg() { return sp_img; }
Point2D getPos() { return sp_pos; }
Point2D getVel() { return sp_vel; }
Point2D getCenter() { return sp_center; }
Point2D getSize() { return sp_size; }
double getRadius() { return sp_radius; }
int getCurFrame() { return sp_cur_frame; }
int getFrames() { return sp_frames; }
bool collide(Sprite &another_sprite);
};
Which has a method called "collide", this method detects a collision between two sprites, and works as follows:
bool Sprite::collide(Sprite &another_sprite)
{
double d = getPos().dist(another_sprite.getPos());
if ( d < ( getRadius() + another_sprite.getRadius() ) )
return true;
else
return false;
}
The method works fine. My problem arises with the following, I have implemented different classes that are subclasses of "Sprite" and will represent enemies in my game, so, for instance I would have objects: Class enemy1 : public Sprite, Class enemy2 : public Sprite, etc. They are different because they have different behaviors. I implemented other two helper functions called group_collide and group_group_collide, that work as follows:
bool group_collide(std::list<Sprite> &group, Sprite other_object)
{
bool collision = false;
for (std::list<Sprite>::iterator sprite = group.begin(), end = group.end(); sprite != end; ++sprite)
{
if (sprite->collide(other_object))
{
Sprite exp = Sprite(sprite->getPos(), Point2D(0, 0), exp_image, exp_info, true, 7);
exp_group.push_back(exp);
if( Mix_PlayChannel( -1, explosion, 0 ) == -1 )
{
//abort();
}
sprite = group.erase(sprite);
collision = true;
}
}
return collision;
}
int group_group_collide(std::list<Sprite> &group, std::list<Sprite> &other_group)
{
int scored = 0;
for (std::list<Sprite>::iterator it1 = group.begin(), end1 = group.end(); it1 != end1; ++it1)
{
if (group_collide(other_group, *it1))
{
it1 = group.erase(it1);
scored += 10;
}
}
return scored;
}
So, in fact, group collide will detect collisons between a sprite and a list of sprites, and group_group_collide will detect collisions between group of sprites (two different lists). The problem that arises is: There will be at least 4 types of enemies, and all of them are subclasses of my Sprite class, but I get compilation errors when I create a list of sprites and add elements that are subclasses of sprites. My solution was writing a method group_collide and group_group collide for all types of enemies, but this is quite inelegant. Is there a better way to approach this problem?
EDIT:
Thanks for your suggestions. I defined the list as a pointers list as you have suggested:
std::list<Sprite*> enemy_group;
And for instance, I am adding elements of class "Kamikaze" which is a subclass of sprite, in this way (the method update is different in this class):
enemy_group.push_back(new Kamikaze(enemy_pos, enemy_vel, 0, enemy_image, enemy_info));
However, when iterating over the list:
for (list<Sprite*>::iterator it = enemy_group.begin(), end = enemy_group.end(); it != end; ++it) {
(*it)->draw(screen);
(*it)->update();
if ((*it)->getPos().getY() > SCREEN_HEIGHT + 30)
{
delete *it;
it = enemy_group.erase(it);
}
}
The method update is called from the Sprite class, not Kamikaze class, therefore I also have object slicing problem with this approach, perhaps there is something wrong with what I have done?
I get compilation errors when I create a list of sprites and add elements that are subclasses of sprites.
All derived classes will be 'sliced' in order to fit into an object container like std::list. A std::list<Sprite*> //(Preferably a smart pointer) will avoid this problem, though the actual objects would have to be stored elsewhere.
As given in the answer by Laserbreath, you should replace std::list<Sprite> with std::list<Sprite*>. Currently, any passed subclass will be reduced to Sprite. Using a list of pointers you can avoid that.
You should use virtual functions to get the desired result.
In base class:
//This way you'll have a default collision method for a Sprite...
//...but you'll be able to re-define it in subclasses.
//I don't do THIS ONE often so I'm not sure if you'll have to perform additional steps
virtual bool Sprite::collide(Sprite &another_sprite)
{
...
}
//And this way you are making collied a pure virtual function and Sprite an abstract class...
//...meaning you can't instantiate it, but you can use it with a pointer or reference.
//Every subclass of Sprite must have a body of function collide defined
//you do this only in header of class Sprite
virtual bool virtual bool Sprite::collide(Sprite &another_sprite) = 0;
In sub-class
//if you have made collide pure virtual function, you can do this
//not sure about the first case though
bool SubSprite::collide(Sprite &another_sprite)
{
...
}
So, when collide method is called from a Sprite pointer or reference, the call will be re-directed to the designated function of a subclass.
Same goes for group_collide.
ADDITIONAL EXPLANATION:
//lets assumme you have 2 sprite types, Kamikaze and NotKamikaze
Sprite *sprite1 = new Kamikaze();
Sprite *sprite2 = new NotKamikaze();
//and lets assume collide uses pointer instead, just for this example, so it's shorter
virtual bool Sprite::collide(Sprite *another_sprite) = 0;
bool Kamikaze::collide(Sprite *another_sprite);
bool NotKamikaze::collide(Sprite *another_sprite);
//when you call collide from Sprite*, it **automatically** picks the sub-class collude method
//sprite1 is of type Sprite*, but allocated memory is of Kamikaze
//so the only function called is Kamikaze::collide
sprite1->collide(sprite2);
//sprite2 is of type Sprite*, but allocated memory is of NotKamikaze
//so the only function called is NotKamikaze::collide
sprite2->collide(sprite2);
So whenever a pure virtual function collide is called from pointer or reference, the program automatically selects the right one. If you want it to be different for every sprite type, then this is what you need.
This is where "object slicing" becomes the issue. Sprite can exist as a pointer or reference, but only to a non-abstract sub-class. So Sprite *Sprite sprite1 = new Sprite(); won't work, but Sprite *Sprite sprite1 = new SpriteSubClass(); works.
If you make Sprite::collide not purely virtual, Sprite would no longer be an abstract class, and you could make the method run from Sprite class itself, and you could do Sprite *Sprite sprite1 = new Sprite();. But I highly doubt you'll need that as you're only looking at subclasses.
Whenever a method name is shared, but its behavior differs by subclass, it must be virtual.