Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
SFML Game Development.pdf
Скачиваний:
209
Добавлен:
28.03.2016
Размер:
4.19 Mб
Скачать

Every Pixel Counts – Adding Visual Effects

Embedding particles in the world

That was it for the definition of emitters and particle systems. We now add these nodes to the scene graph. First the emitter in the Projectile constructor; we set its position to the tail of the missile (which is half the height away from the center):

if (isGuided()) // if this projectile is a missile

{

std::unique_ptr<EmitterNode> smoke( new EmitterNode(Particle::Smoke));

smoke->setPosition(0.f, getBoundingRect().height / 2.f); attachChild(std::move(smoke));

}

The particle system is attached to a layer node directly under the scene graph's root. Because we want particles and projectiles to appear below the airplanes, we split the existing scene layer Air into two layers LowerAir and UpperAir. In the following, you see an excerpt of World::buildScene():

std::unique_ptr<ParticleNode> smokeNode(

new ParticleNode(Particle::Smoke, mTextures)); mSceneLayers[LowerAir]->attachChild(std::move(smokeNode));

So far we have only considered the smoke effect, but the propellant fire works in exactly the sameway. By adding the fire particle system after the smoke, it is

rendered later, so the burning propellant is still visible and not covered by smoke.

Animated sprites

Alright, now we have got particles, let's accompany them with an animated explosion. So far our aircraft have just disappeared when you shot them down. That's not satisfying. When you destroy something like an aircraft, you expect a huge explosion, don't you?

[ 200 ]

www.it-ebooks.info

Chapter 8

We already had a look at texture rectangles and sprite sheets. This knowledge will be used to build our animation. An animation consists of several frames, and we represent these frames as separate rectangles inside one larger texture, similar to what we now do with entities. Do not confuse animation frames with the game loop frames—the former represents just a state of an animation, which usually lasts for many game loop iterations.

This is the sprite sheet we use for the animation. How does it work? As time elapses, we move the texture rect from one frame to the other, until the animation is finished.

We do not define the animation class as a scene node but only as a drawable and a transformable object. It can then be used by whatever scene node we want, which gives a little more flexibility when and where we can use it.

class Animation : public sf::Drawable, public sf::Transformable

{

public:

...

private:

...

 

 

 

sf::Sprite

mSprite;

sf::Vector2i

mFrameSize;

std::size_t

mNumFrames;

std::size_t

mCurrentFrame;

sf::Time

mDuration;

sf::Time

mElapsedTime;

bool

mRepeat;

};

 

 

 

 

 

 

 

 

 

[ 201 ]

 

 

 

 

www.it-ebooks.info

Every Pixel Counts – Adding Visual Effects

We will have a look at the functions later. As you can see we use internally a sprite.

We could do without a sprite if we so desired, but it defines a lot of functions that we want to use, making our lives easier. After that comes the frame size, this vector defines the size of one frame for us. We have the values concerning frames, how many frames and what frame we are currently drawing. Lastly we have the time for the animation, the total duration, and the elapsed time since the last frame change.

So let us start looking at the actual implementation. Most of our logic is performed in the Animation::update() function, so it grows quite big. Because of that the function is explained in the following sections.

void Animation::update(sf::Time dt)

{

sf::Time timePerFrame = mDuration / static_cast<float>(mNumFrames);

mElapsedTime += dt;

sf::Vector2i textureBounds(mSprite.getTexture()->getSize()); sf::IntRect textureRect = mSprite.getTextureRect();

if (mCurrentFrame == 0)

textureRect = sf::IntRect(0, 0, mFrameSize.x, mFrameSize.y);

In the previous code, we already have the sprite sheet divided per frame and we know how big one frame is. We have to calculate how much time needs to elapse before we progress to the next frame. If we are on the first frame, then our texture rect should start at the beginning of the animation sprite sheet:

while (mElapsedTime >= timePerFrame && (mCurrentFrame <= mNumFrames || mRepeat))

{

So while time has elapsed since we last updated, and is enough to count as a new frame, and we haven't reached the end of the animation, we perform an iteration:

textureRect.left += textureRect.width;

if (textureRect.left + textureRect.width > textureBounds.x)

{

textureRect.left = 0;

textureRect.top += textureRect.height;

}

mElapsedTime -= timePerFrame; if (mRepeat)

[ 202 ]

www.it-ebooks.info

Chapter 8

{

mCurrentFrame = (mCurrentFrame + 1) % mNumFrames;

if (mCurrentFrame == 0)

textureRect = sf::IntRect(0, 0, mFrameSize.x,

mFrameSize.y);

}

else

{

mCurrentFrame++;

}

}

mSprite.setTextureRect(textureRect);

}

In each iteration, we move the resulting texture rect quite easily. We move it a step to the right, all the way until we reach the end, if that occurs we move the rect down one line and start again from the start. Lastly is just some book-keeping for what frame we are on, so we know if we have reached the end of the animation.

This is the core of the animation and covers everything we need. It's very much packed together in the update function, but it is all required.

So we add it now to the aircraft such that it explodes when destroyed. Let's start with the Aircraft constructor.

mExplosion.setFrameSize(sf::Vector2i(256, 256)); mExplosion.setNumFrames(16); mExplosion.setDuration(sf::seconds(1));

centerOrigin(mExplosion);

Here we set up our explosion. We define the size of one frame of the animation, the number of frames and the duration we want it to run for. We also center the animation sprite's origin, so that it is easier to position.

We will have to branch in our code to render either the normal aircraft or the explosion. It's not much but we still have to do it:

if (isDestroyed() && mShowExplosion) target.draw(mExplosion, states);

else

target.draw(mSprite, states);

[ 203 ]

www.it-ebooks.info

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]