Midterm Assignment – Wordlaga!

Concept

As soon as I heard our midterm assignment was to make a game, I had a galaga in mind because I knew that would be challenging to make yet so fun to both make and play it. Plus I love galaga, it’s one my favorite classic games. However, for the purpose of this assignment, I came up with my own variation of the galaga game, the wordlaga.

Wordlaga is a game where instead of the galaga’s bug enemies, I had a set of alphabets that form a word as the enemies. Players, as galaga, must shoot down the letterin the correct order that forms the target word. If the enemy collides with player or if player shoots the wrong letter, game will be over. Player can move the galaga left and right using arrow keys and shoot using the up arrow key to shoot down the enemy. The goal of the game is shoot down as many words as you can and achieve the highest score possible, or finish the game by emptying the preloaded word bank… if you can.

I came up with this idea while having a conversation with a friend about educational games and I remembered that I mostly played/enjoyed galaga when I was young. Since galaga is such a simple game, why not recreate it for a somewhat educational purpose where young children can learn words by shooting down the letter that form the word in correct order? Thus, wordlaga was created.

Code

This 400 lines worth of coding took hours of debugging and consists of many parts that I am proud of. The two particular parts I would like to mention where one is a mechanism that I am proud for and one that caused me a mind-splitting headache trying to make it work. The proud part of my code is the enemy-bullet collision detection.

for(let i = 0; i < enemyArr.length; i++){
    enemyArr[i].display();
    enemyArr[i].updateVel(player);
    for(let j = 0; j < galagaBulletFired.length; j++){
      if(enemyArr[i].checkHit(galagaBulletFired[j])){
        //if player didn't hit the right word, game is over
        if(enemyArr[i].ascii + 97 != correctAnswer[count]){
          wrongChoiceSound.play();
          gameOver = true;
        }
        else{
          //for each enemy killed correctly, player gets 100 score
          destroyedEnemyArr.push(i);
          score += 100;
          enemyKillSound.play();
          usedBulletArr.push(j);
          count++;
        }
      }
      
      //if the bullets leave the screen, elliminate them
      if(galagaBulletFired[j].position.y < -20)
        galagaBulletFired.splice(usedBulletArr[j],1)
    }
    //if player collides with enemy game is over
    if(enemyArr[i].collisionDetection(player)){
      explosionSound.play();
      gameOver = true;
    }
  }

In this code, I have a nested for loop where one iterates through the array that contains the enemy instances and the the nested loop iterates through the array of bullets fired. For each each enemy, if the enemy instance returns true for a simple collision detection, then the index of this instance will be recorded in a separate array. The enemy instance array will be iterated again after the check for the collision, where it will be checked if the index stored earlier matches an instance. If it does, then that element will be spliced. This collision also checks the content of the enemy instance and if the wrong letter is shot down, the player will lose the game. I’m particularly proud of this piece of code because while it was a bit challenging to figure out how to destroy enemies when they are shot down, I came up with this solution relatively quickly and it worked right away in few attempts.

Now for the code that caused me anger equivalent to the consumption of mint chocolate,

if(frameCount % (310 - 10 * stageLvl) == 0){
  moveEnemy(enemyArr, int(random(0, enemyArr.length)))
}

...

function moveEnemy(enemyArr, enemyNum){
  enemyArr[enemyNum].movement(player); 
}

...

//helper function for enemy movement. 
  updateVel(player){
    //if this enemy is chosen to move, it will start moving towards the player by the player position passed
    //the speed of the enemy moving will be based on the stage level
    if(this.isMoving){
      if(this.position.y < 400){
        let distanceBetween = player.position.x - this.position.x;
        this.velX = distanceBetween * 0.01;
        this.velY = enemySpeed;
      }
      //if the enemy went passed the player and is off the screen, make it return to the top of the screen
      else{
        this.position.y = -150;
      }
    }
  }

...

movement(player){
  this.isMoving = true;
  this.updateVel(player);
}

...

  //if the enemy instance is not called to move, its velocity is fixed 0, but if it is, display will update the enemy's position
  display(){
    if(!this.isMoving && this.position.y == 75){
      this.velY = 0;
    }
    this.position.x += this.velX;
    this.position.y += this.velY;
    image(sprite[this.ascii], this.position.x - enemySize/2, this.position.y - enemySize/2, enemySize, enemySize);
  }
}

This code deals with the movement mechanism of the enemy. In wordlaga, the enemy alphabet, after some time, will move towards the player and try to collide with them. This particular function makes that happen where after some pass in framecount, a random enemy from the array of instances will specifically move towards the player, and return to the top of the screen after it misses the player and goes out of the screen. Each step of making this was so troublesome because satisfying a condition for one of the said functions became the cause for a bug for another. For example, in order for the enemy to track the player position, the movement function must be kept called in the draw function since the player position will be constantly updated. However, if the movement function is kept called in the draw function, all the enemies continuously started to move instead of one by one. This problem was fixed by separating the function that called for movement and actually updating the enemy position. The time restriction in calling this function periodically was done by using the frameCount and modulo division. After a total full day worth of time of debugging, I have reached this stage of this code where it works. Honestly I at this point I am not even proud of this code I am just thankful that it works.

Areas for Improvements

While I am satisfied with my finished product, I can definitely see some potential improvements for this game. First of all, there could be a different stage design where the number of letters could increase as the stage level progresses or having a multiple life system where the player gets three lives instead of just one. I could also add the mechanism of enemy shooting bullets at the player to make the game more interesting or add explosion animation to make the game more visually appealing. I could think of couple other minor improvements to perfect this game, but frankly I need more time to be able to make these improvements since I was sick the past week and took a toll in preparation time. Also, while it might sound easy to make these improvements, we never know what kind of headache causing bugs could occur. This is actually the lesson I learned from this project while dealing with the enemy movement function bugs: what I might consider to be simple implementation could be very difficult for the computer to understand. Overall I had fun making this whole project and hopefully one day I’ll come back to improve it.

Leave a Reply