Enemy setup

The first thing I had to do to make the enemy sprite was to create a canvas and a new object class called “Enemy”. Then, I had to draw the enemy to the canvas. This means I had to understand the object oriented programming (OOP) first put into place by one of my groupmates. As I understand it (which is no very well), the main principles of OOP are object classes, functions and exporting/importing.

Object Classes

Object classes have parameters that are passed in when a new instance of that object is created. These parameters can be used to create polymorphic behavior, where two different instances of the same object behave differently based on the parameters that are passed into them. This is useful for games if you have multiple types of enemies, and you can have parameters for different stats like health, speed, and starting location. This process can make code very efficient and also make development much easier.

Functions

Functions are similar to object classes in that they are defined once and can be called throughout the code. They can also have a variable passed into them that outputs a different value, much like mathematical functions. These are good for when an action needs to be repeated multiple times.

Import / Export

Object classes and functions cannot be referenced from a different file. In order to use functions/classes/variables from another file they need to be exported from their original file and then imported to the new file. The benefit of this is that different parts of the code can be in different files and still work together. That way, multiple people can work on different files at the same time and not worry about merge issues. It also makes it easier to organize code because all of the related parts are grouped together.

Enemy Behavior

Compared to OOP, the logic for the enemy behavior is a lot simpler and easier to understand. This is where my experience with Scratch coding finally came in handy. Once I was able to figure out how to create three new parameters for the enemy sprite, I could get started. First, I defined a variable called “domain”. I used this as the distance the enemy would travel before turning around. I also defined a variable called “domainOffset”. This variable reported the x position of the enemy relative to its domain. The last variable was “enemySpeed” which defined how far the enemy would move every game update. Here is the code which controls the enemy movement, which is run for every instance of the game loop. (Note that “x”, which was already defined since the “Enemy” class is an extension of the “GameObject” class, is the enemy’s x position on the screen.)

this.domainOffset += this.enemySpeed; // "domainOffset" essentially works as a timer that counts up every second.
            if(this.domainOffset > -1 && this.domainOffset < this.domain) { // move right until the timer equals "domain"
                this.x += this.enemySpeed; // move two spaces to the right
            } else if (this.domainOffset > this.domain && this.domainOffset < this.domain * 2) {
                this.x -= this.enemySpeed; //move two spaces to the left
            } else if (this.domainOffset > this.domain * 2) {
                this.domainOffset = 0; // reset the counter back to 0
            }
if(score===10 && this.enemySpeed===4) {
    this.enemySpeed += 4;
};
<IPython.core.display.Javascript object>

Retrospective

I think that creating this enemy sprite, my main contribution to this project, was a very helpful introductory experience into javascript code and the programming workflow. I have learned a lot about how websites are made. I am proud of my work, and I’m excited for the next trimester of this class now that I have some basic skills. I feel like I’m ready to take on bigger challenges.

Below is the entire code if anyone is interested, but most of it is OOP, which in my opinion is pretty much the same across different projects. I think the more personalized and creative coding was already showed off above, and the OOP is more like following a checklist. Also, some of the code was not written by me.

import Character from './Character.js';
import { incrementScore, score } from './Score.js';

// Creates animation for the enemy
const enemyAnimation = {
    scale: 0.15,
    width: 798,
    height: 735,
    attack: { row: 3, frames: 9, idleFrame: { column: 7, frames: 0 } },
};

//Variables to Delay Animation timing
let frameUpdateDelay = 2;
let frameCounter = 0;

// Enemy class
export class Enemy extends Character {
    constructor(enemyCanvas, image, speedRatio, domain, domainOffset) {
        //Defines Dimension of Enemy Hitbox
        const hitbox = {
            width: 50,
            height: 50,
        };

        super (enemyCanvas, image, speedRatio, enemyAnimation.width, enemyAnimation.height, enemyAnimation.scale);
        
        const enemyCanvasStyle =  document.getElementById("enemies"); //Used for reverse scaling of the canvas to make the enemy turn left 
        this.enemyCanvasStyle = enemyCanvasStyle

        //Hitbox is now a property of enemy
        this.hitbox = hitbox;

        this.domain = domain; // Sets the distance that the enemy will walk before turning around
        this.domainOffset = domainOffset; // Sets the distance that the enemy will walk before turning around
        };

        //Updates hitbox to match enemy position
        updateHitbox() {
            this.hitbox.x = this.x
            this.hitbox.y = this.y
            //console.log('Hitbox:', this.hitbox); //logs the position of the hitbox
        }

        setDomain(domain){
            this.domain = domain;
        };

        setDomainOffset(domainOffset){
            this.domainOffset = domainOffset;
        };
        
        //update the enemy every repeat of the game loop
        update() {
            this.domainOffset += this.enemySpeed; // "domainOffset" essentially works as a timer that counts up every second.
            if(this.domainOffset > -1 && this.domainOffset < this.domain) { // move right until the timer equals "domain"
                this.x += this.enemySpeed; // move two spaces to the right
                this.enemyCanvasStyle.style.transform = 'scaleX(1)'; //makes enemy face right
            } else if (this.domainOffset > this.domain && this.domainOffset < this.domain * 2) {
                this.x -= this.enemySpeed; //move two spaces to the left
                this.enemyCanvasStyle.style.transform = 'scaleX(-1)'; //makes enemy face left
            } else if (this.domainOffset > this.domain * 2) {
                this.domainOffset = 0; // reset the counter back to 0
                incrementScore(); //add 5 to the score
            //If Enemy reaches right side, add 5 to the score
            } else if (this.domainOffset === this.domain) {
                incrementScore();
            }
            if(score===10 && this.enemySpeed===4) {
                this.enemySpeed += 4;
                console.log(this.enemySpeed)
            };

             //Delays Frame Animation
            frameCounter++
            if (frameCounter >= frameUpdateDelay) {
                //Resets FrameCount
                frameCounter = 0;
                
                //Updates Animation
                if (this.frameX < this.maxFrame) {
                    this.frameX++;
                } else {
                    this.frameX = 0;
                }
            }
        };
};

// Initializes the enemy 
export function initEnemy(_canvasId, image, gameSpeed, speedRatio, domain, domainOffset, enemySpeed) {
    var enemy = new Enemy(_canvasId, image, gameSpeed, speedRatio, domain, domainOffset, enemySpeed);
        enemy.setFrameY(enemyAnimation['attack'].row);
        enemy.setFrameX(enemyAnimation['attack'].column);
        enemy.setMaxFrame(enemyAnimation['attack'].frames);
        enemy.setX(1200); // Set an initial X position for the enemy
        enemy.setY(520); // Set an initial Y position for the enemy
        enemy.setDomain(1200); // Sets the distance that the enemy will walk before turning around
        enemy.setDomainOffset(1200); // The current x location of the enemy relative to its domain
        enemy.enemySpeed = 4; // the speed of the enemy
    return enemy;
};

export default Enemy;