Monday, February 13, 2017

Basic bullet movement patterns in Asteroid Invaders


In this part we will go through some basic movement patters for the bullets in Asteroid Invaders, the game that we began with in OpenGL 4 with OpenTK in C# Part 10: Asteroid Invaders.

Wave pattern

Here we add a displacement on the X axis based on either Sine or Cosine function (every other bullet) to get a wave like pattern for the bullet.
public override void Update(double time, double delta)
{
 _life += delta;
 if (_life > 5)
  ToBeRemoved = true;
 if (_bulletNumber % 2 == 0)
  _direction.X = (float)Math.Sin(_position.Y * 33f);
 else
  _direction.X = (float)Math.Cos(_position.Y * 33f);
 _direction.Normalize();
 _rotation = _direction * _velocity;
 base.Update(time, delta);
}
This is quite basic, and the amount of waves is set with the 33f, in this case quite rapid. The wave form is based of the location of the bullet in on the Y axis. As it moves upwards this value changes and it is sent to the Sin or Cos functions.
Lastly our new direction is normalized, note that this takes away some velocity as the object now moves on a curved road and the velocity is distributed on more than 1 axis. We could set the Y to 1 to achieve the original upward velocity. Up to the coder :)

Seeker/Homing

A little more advanced. Here we try to get the bullet to seek towards the asteroid that it has locked on to.
First we need to lock the bullet to an Asteroid.
public void SetTarget(Asteroid target)
{
    _target = target;
    target.LockBullet(this);
}

This in turn calls LockBullet on the Asteroid and we change the model of the asteroid to the bullet model during the time of the lock.
public void LockBullet(Bullet bullet)
{
    _lockedBullet = bullet;
    _model = bullet.Model;
}
In the Asteroid update method we add the following code to reset the model if the bullet misses
public override void Update(double time, double delta)
{
    _rotation.X = (float)Math.Sin((time + GameObjectNumber) * 0.3);
    _rotation.Y = (float)Math.Cos((time + GameObjectNumber) * 0.5);
    _rotation.Z = (float)Math.Cos((time + GameObjectNumber) * 0.2);
    var d = new Vector4(_rotation.X, _rotation.Y, 0, 0);
    d.Normalize();
    _direction = d;
    if (_lockedBullet != null && _lockedBullet.ToBeRemoved)
    {
        _lockedBullet = null;
        _model = _original;
    }
    base.Update(time, delta);
}

In our bullet update code we add the following to nudge the direction towards our target over time.
if (_target != null && !_target.ToBeRemoved)
{
    _direction = _direction + ((float)delta * (_target.Position - Position));
}
This gets normalized as well.
Lastly, when the bullet object is created, we assign
var bullet = _gameObjectFactory.CreateBullet(_player.Position, _bulletType);
var asteroids = _gameObjects.Where(x => x.GetType() == typeof (Asteroid)).ToList();
bullet.SetTarget((Asteroid) asteroids[bullet.GameObjectNumber%asteroids.Count]);
_gameObjects.Add(bullet);

Here we lock the asteroid directly after creation of the bullet. We just pick one asteroid from the list based on the bullets GameObjectNumber.
This is quite naive homing algorithm, it does not take into account any obstacles on its path and hits them many times instead.

For demo


For the complete source code for the tutorial at the end of this part, go to: https://github.com/eowind/dreamstatecoding

So there, thank you for reading. Hope this helps someone out there : )

Until next time: Work to Live, Don’t Live to Work

No comments:

Post a Comment