Index: vulkan-game.cpp
===================================================================
--- vulkan-game.cpp	(revision 3950236c3448866af72c329d4983aa407e1d1646)
+++ vulkan-game.cpp	(revision 7297892d3ee9f767f9adec44726c263f23bbb4da)
@@ -707,8 +707,18 @@
                   laserObjects[leftLaserIdx].modified = true;
                   leftLaserIdx = -1;
+
+                  if (leftLaserEffect != nullptr) {
+                     leftLaserEffect->deleted = true;
+                     leftLaserEffect = nullptr;
+                  }
                } else if (e.key.keycode == SDL_SCANCODE_X && rightLaserIdx != -1) {
                   laserObjects[rightLaserIdx].ssbo.deleted = true;
                   laserObjects[rightLaserIdx].modified = true;
                   rightLaserIdx = -1;
+
+                  if (rightLaserEffect != nullptr) {
+                     rightLaserEffect->deleted = true;
+                     rightLaserEffect = nullptr;
+                  }
                }
                break;
@@ -786,9 +796,22 @@
    }
 
+   for (vector<BaseEffectOverTime*>::iterator it = effects.begin(); it != effects.end(); ) {
+      if ((*it)->deleted) {
+         delete *it;
+         it = effects.erase(it);
+      } else {
+         BaseEffectOverTime* eot = *it;
+
+         eot->applyEffect();
+
+         it++;
+      }
+   }
+
    for (SceneObject<AsteroidVertex, SSBO_Asteroid>& asteroid : this->asteroidObjects) {
       if (!asteroid.ssbo.deleted) {
          vec3 objCenter = vec3(viewMat * vec4(asteroid.center, 1.0f));
 
-         if ((objCenter.z - asteroid.radius) > -NEAR_CLIP) {
+         if ((objCenter.z - asteroid.radius) > -NEAR_CLIP || asteroid.ssbo.hp <= 0.0f) {
             asteroid.ssbo.deleted = true;
 
@@ -1663,4 +1686,27 @@
 
    if (laser.targetAsteroid != closestAsteroid) {
+      if (laser.targetAsteroid != nullptr) {
+         if (index == leftLaserIdx && leftLaserEffect != nullptr) {
+            leftLaserEffect->deleted = true;
+         } else if (index == rightLaserIdx && rightLaserEffect != nullptr) {
+            rightLaserEffect->deleted = true;
+         }
+      }
+
+      EffectOverTime<AsteroidVertex, SSBO_Asteroid>* eot = nullptr;
+
+      if (closestAsteroid != nullptr) {
+         // TODO: Use some sort of smart pointer instead
+         eot = new EffectOverTime(asteroidPipeline, asteroidObjects, closestAsteroidIndex,
+            offset_of(&SSBO_Asteroid::hp), -20.0f);
+         effects.push_back(eot);
+      }
+
+      if (index == leftLaserIdx) {
+         leftLaserEffect = eot;
+      } else if (index == rightLaserIdx) {
+         rightLaserEffect = eot;
+      }
+
       laser.targetAsteroid = closestAsteroid;
    }
Index: vulkan-game.hpp
===================================================================
--- vulkan-game.hpp	(revision 3950236c3448866af72c329d4983aa407e1d1646)
+++ vulkan-game.hpp	(revision 7297892d3ee9f767f9adec44726c263f23bbb4da)
@@ -107,4 +107,64 @@
 static float curTime;
 
+
+// TODO: Look into using dynamic_cast to check types of SceneObject and EffectOverTime
+
+struct BaseEffectOverTime {
+   bool deleted;
+
+   virtual void applyEffect() = 0;
+
+   BaseEffectOverTime() :
+         deleted(false) {
+   }
+
+   virtual ~BaseEffectOverTime() {
+   }
+};
+
+template<class VertexType, class SSBOType>
+struct EffectOverTime : public BaseEffectOverTime {
+   GraphicsPipeline_Vulkan<VertexType, SSBOType>& pipeline;
+   vector<SceneObject<VertexType, SSBOType>>& objects;
+   unsigned int objectIndex;
+   size_t effectedFieldOffset;
+   float startValue;
+   float startTime;
+   float changePerSecond;
+
+   EffectOverTime(GraphicsPipeline_Vulkan<VertexType, SSBOType>& pipeline,
+         vector<SceneObject<VertexType, SSBOType>>& objects, unsigned int objectIndex,
+         size_t effectedFieldOffset, float changePerSecond) :
+         pipeline(pipeline),
+         objects(objects),
+         objectIndex(objectIndex),
+         effectedFieldOffset(effectedFieldOffset),
+         startTime(curTime),
+         changePerSecond(changePerSecond) {
+      size_t ssboOffset = offset_of(&SceneObject<VertexType, SSBOType>::ssbo);
+
+      unsigned char* effectedFieldPtr = reinterpret_cast<unsigned char*>(&objects[objectIndex]) +
+         ssboOffset + effectedFieldOffset;
+
+      startValue = *reinterpret_cast<float*>(effectedFieldPtr);
+   }
+
+   void applyEffect() {
+      if (objects[objectIndex].ssbo.deleted) {
+         this->deleted = true;
+         return;
+      }
+
+      size_t ssboOffset = offset_of(&SceneObject<VertexType, SSBOType>::ssbo);
+
+      unsigned char* effectedFieldPtr = reinterpret_cast<unsigned char*>(&objects[objectIndex]) +
+         ssboOffset + effectedFieldOffset;
+
+      *reinterpret_cast<float*>(effectedFieldPtr) = startValue + (curTime - startTime) * changePerSecond;
+
+      objects[objectIndex].modified = true;
+   }
+};
+
 class VulkanGame {
    public:
@@ -226,4 +286,6 @@
       UBO_VP_mats laser_VP_mats;
 
+      vector<BaseEffectOverTime*> effects;
+
       time_point<steady_clock> startTime;
       float prevTime, elapsedTime;
@@ -236,6 +298,8 @@
 
       unsigned int leftLaserIdx = -1;
+      EffectOverTime<AsteroidVertex, SSBO_Asteroid>* leftLaserEffect = nullptr;
 
       unsigned int rightLaserIdx = -1;
+      EffectOverTime<AsteroidVertex, SSBO_Asteroid>* rightLaserEffect = nullptr;
 
       bool initWindow(int width, int height, unsigned char guiFlags);
