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

Chapter 7

We create the projectile with an offset from the player and a velocity required by the projectile type. Also, depending on if this projectile is shot by an enemy or the player, we will have different directions. We do not want the enemy bullets to go upwards like the player's bullets or the other way around.

Implementing gunfire for enemies is now a tiny step; instead of calling fire() when keys are pressed, we just call it always. We do this by adding the following code to the beginning of the checkProjectileLaunch() function:

if (!isAllied()) fire();

Now we have bullets that fly and split the sky.

Homing missiles

What would a modern aircraft be if it hadn't got an arsenal of homing missiles?

This is where we start to add intelligence to our missiles; they should be capable of seeking enemies autonomously.

Let's first look at what we need to implement on the projectile site. For homing missiles, the functions guideTowards() and isGuided(), as well as the variable mTargetDirection are important. Their implementation looks as follows:

bool Projectile::isGuided() const

{

return mType == Missile;

}

void Projectile::guideTowards(sf::Vector2f position)

{

assert(isGuided());

mTargetDirection = unitVector(position - getWorldPosition());

}

The function unitVector() is a helper we have written. It divides a vector by its length, thus, always returns a vector of length one. The target direction is therefore a unit vector headed towards the target.

In the function updateCurrent(), we steer our missile. We change the current missile's velocity by adding small contributions of the target direction vector to it. By doing so, the velocity vector continuously approaches the target direction, having the effect that the missile flies along a curve towards the target.

[ 167 ]

www.it-ebooks.info

Warfare Unleashed – Implementing Gameplay

approachRate is a constant that determines, to what extent the target direction contributes to the velocity. newVelocity, which is the weighted sum of the two vectors, is scaled to the maximum speed of the missile. It is assigned to the missile's velocity, and its angle is assigned to the missile's rotation. We use +90 here, because the missile texture points upwards (instead of right).

void Projectile::updateCurrent(sf::Time dt, CommandQueue& commands)

{

if (isGuided())

{

const float approachRate = 200.f;

sf::Vector2f newVelocity = unitVector(approachRate

* dt.asSeconds() * mTargetDirection + getVelocity());

newVelocity *= getMaxSpeed();

float angle = std::atan2(newVelocity.y, newVelocity.x);

setRotation(toDegree(angle) + 90.f); setVelocity(newVelocity);

}

Entity::updateCurrent(dt, commands);

}

Note that there are many possibilities to guide a missile. Steering behaviors define a whole field of AI; they incorporate advanced mechanisms such as evasion, interception, and group behavior.

Don't hesitate to search on the internet if you're interested.

Now, we have guided the missile to a certain position, but how to retrieve that position?

We want our missile to pursuit the closest enemy. For this, we switch from Projectile to the World class, where we write a new function. First, we store all currently active (that is, already spawned and not yet destroyed) enemies in the member variable mActiveEnemies. With the command facility, this task is almost trivial:

void World::guideMissiles()

{

Command enemyCollector;

enemyCollector.category = Category::EnemyAircraft; enemyCollector.action = derivedAction<Aircraft>( [this] (Aircraft& enemy, sf::Time)

[ 168 ]

www.it-ebooks.info

Chapter 7

{

if (!enemy.isDestroyed()) mActiveEnemies.push_back(&enemy);

});

Next, we have to find the nearest enemy for each missile. We set up another command, now for projectiles, that iterates through the active enemies to find the closest one. Here, distance() is a helper function that returns the distance between the centers of two scene nodes.

Command missileGuider;

missileGuider.category = Category::AlliedProjectile; missileGuider.action = derivedAction<Projectile>( [this] (Projectile& missile, sf::Time)

{

// Ignore unguided bullets if (!missile.isGuided())

return;

float minDistance = std::numeric_limits<float>::max(); Aircraft* closestEnemy = nullptr;

FOREACH(Aircraft* enemy, mActiveEnemies)

{

float enemyDistance = distance(missile, *enemy);

if (enemyDistance < minDistance)

{

closestEnemy = enemy; minDistance = enemyDistance;

}

}

In case we found a closest enemy, we let the missile chase it.

if (closestEnemy) missile.guideTowards( closestEnemy->getWorldPosition());

});

After defining the second command, we push both to our queue, and reset the container of active enemies. Remember that the commands are not yet executed, they wait in the queue until they are invoked on the scene graph in World::update().

mCommandQueue.push(enemyCollector);

mCommandQueue.push(missileGuider);

[ 169 ]

www.it-ebooks.info

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