Chapter 16.  Extending SFML Classes, Particle Systems, and Shaders

In this final chapter, we will explore the C++ concept of extending other people's classes. More specifically, we will look at the SFML Drawable class and the benefits of using it as a base class for our own classes. We will also scratch the surface of the topic of OpenGL shaders and see how writing code in another language OpenGL Shading Language (GLSL), which can be run directly on the graphics card, can lead to smooth graphical effects that might otherwise be impossible. As usual, we will also use our new skills and knowledge to enhance the current project.

Here is a list of the topics in the order we will cover them:

  • The SFML Drawable class
  • Building a particle system
  • OpenGl shaders and GLSL
  • Using shaders in the Thomas Was Late game

The SFML Drawable class

The Drawable class has just one function. It has no variables either. Furthermore, its one and only function is pure virtual. This means that if we inherit from Drawable, we must implement its one and only function. The purpose of this, which you may remember from Chapter 12Abstraction and Code Management â€” Making Better Use of OOP, is that we can then use our class, which inherits from drawable, as a polymorphic type. Put more simply, anything that SFML allows us to do with a Drawable object, we will be able to do with our class that inherits from it. The only requirement is that we must provide a definition for the pure virtual function, draw.

Some classes that inherit from Drawable already include Sprite and VertexArray (among others). Whenever we have used Sprite or VertexArray, we have passed them to the draw function of the RenderWindow class.

The reason that we have been able to draw every object we have drawn in this book is that they have all inherited from Drawable. We can use this knowledge to our advantage.

We can inherit from Drawable with any object we like, as long as we implement the pure virtual draw function. This is also a straightforward process. The header file (SpaceShip.h) of a hypothetical SpaceShip class that inherits from Drawable would look as follows:

class SpaceShip : public Drawable 
{ 
private: 
   Sprite m_Sprite; 
   // More private members 
public: 
 
   virtual void draw(RenderTarget& target,  
      RenderStates states) const; 
 
   // More public members 
 
}; 

In the previous code, we can see the pure virtual draw function and a Sprite. Notice there is no way to access the private Sprite outside of the class, not even a getSprite function!

The SpaceShip.cpp file would then look something like the following:

void SpaceShip::SpaceShip 
{ 
   // Set up the spaceship 
} 
 
void SpaceShip::draw(RenderTarget& target, RenderStates states) const 
{ 
   target.draw(m_Sprite, states); 
} 
 
// Any other functions 

In the previous code, notice the simple implementation of the draw function. The parameters are beyond the scope of the book. Just note that the target parameter is used to call draw and passes in m_Sprite as well as states, the other parameter.

Tip

While it is not necessary to understand the parameters to take full advantage of Drawable, in the context of the book, you might be intrigued. You can read more about the SFML Drawable class on the SFML website: http://www.sfml-dev.org/tutorials/2.3/graphics-vertex-array.php#creating-an-sfml-like-entity

In the main game loop, we could now treat a SpaceShip instance as if it were a Sprite, or any other class that inherits from Drawable:

SpaceShip m_SpaceShip; 
// create other objects here 
// ... 
 
// In the draw function 
// Rub out the last frame 
m_Window.clear(Color::Black); 
 
// Draw the spaceship 
m_Window.draw(m_SpaceShip); 
// More drawing here 
// ... 
 
// Show everything we have just drawn 
m_Window.display(); 

It is because SpaceShip is a Drawable that we can treat it like a Sprite or VertexArray, and because we overrode the pure virtual draw function, everything just works as we want it to. Let's look at an alternative way of encapsulating the drawing code into the game object.

An alternative to inheriting from Drawable

It is also possible to keep all the drawing functionality within the class that is the object to be drawn by implementing our own function, within our class, perhaps like the following code:

void drawThisObject(RenderWindow window) 
{ 
   window.draw(m_Sprite) 
} 

The previous code assumes that m_Sprite represents the visual appearance of the current class we are drawing, as it has throughout this and the previous project. Assuming that the instance of the class that contains the drawThisObject function is called playerHero, and further assuming we have an instance of RenderWindow called m_Window, we could then draw the object from the main game loop with the following code:

 playerHero.draw(m_Window); 

In this solution, we pass the RenderWindow, m_Window, into the drawThisObject function as a parameter. The drawThisObject function then uses the RenderWindow to draw the Sprite, m_Sprite.

This solution certainly seems simpler than extending Drawable. The reason we do things the way suggested (extending Drawable) isn't really of any great benefit, in its own right, for this project. The actual reason we will soon draw a neat explosion using this method is because it is a good technique to learn.

Why it is best to inherit from Drawable?

With each project we have completed throughout the book, we have learned more about games, C++, and SFML. Possibly the biggest improvements we have made from one game to the next is in the structure of our code–the programming patterns that we have used.

If there were a fourth project to this book, we could take things even further. Unfortunately, there isn't, but have a think about the following idea for improving our code.

Imagine every object in our game is derived from a single, simple, abstract base class. Let's call it GameObject. Game object would probably have concrete functions for getPosition and others. It would likely have a pure virtual update function (because every object updates differently). Furthermore, consider that GameObject inherits from Drawable.

Now look at this hypothetical code:

vector<GameObject> m_GameObjects; 
// Code to initialise all game objects 
// Including tiles, characters, enemies, bullets and anything else 
 
// In the update function 
for (i = m_GameObjects.begin(); i != m_GameObjects.end(); i++) 
{ 
   (*i).update(elapsedTime); 
} 
// That's it! 
 
// In the draw function 
// Rub out the last frame 
m_Window.clear(Color::Black); 
 
for (i = m_GameObjects.begin(); i != m_GameObjects.end(); i++) 
{ 
   m_Window.draw(*i); 
} 
 
// Show everything we have just drawn 
m_Window.display(); 
// That's it! 

The preceding code is a big step up in terms of encapsulation, code manageability, and elegance when compared to even this final project. If you look at the previous code, you will notice there are, however, unanswered questions, such as where collision detection fits in, for example. Hopefully, however, you can see that further study (by building lots of games) will be necessary to master C++.

Although we will not be implementing an entire game in this manner, we will see how we can design a class (ParticleSystem) and pass it directly to m_Window.draw(m_MyParticleSystemInstance).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.20.236