CyberLord09 / CSSE1_Final

MIT License
0 stars 0 forks source link

Triangle#2 PullRequest#(Jittering-Movement-Fix-&-Add-AnimationSpeed-Control) Explanation #5

Open TianbinLiu opened 3 months ago

TianbinLiu commented 3 months ago

Enemy Animation

The enemy doesn't have animation, it is just a single image without using SpriteSheet. Image Image

The steps for fixing this problem are below:

  1. Creating a new js file called "Enemy.js", copy and paste the original code from Goomba.js and change the class name from Goomba to Enemy.
  2. Adding an attribute "this.direction" to the constructor and set it to "d" (which means let it face to the right in the beginning)

    
    constructor(canvas, image, data, xPercentage, yPercentage, name, minPosition){
        super(canvas, image, data, 0.0, 0.2);
    ...//code hidden
    
        this.direction = "d"; // initially facing right

...//code hidden }

3.  Change direction when a collision happens (change this.direction)
```javascript
        if (this.x <= this.minPosition || (this.x + this.canvasWidth >= this.maxPosition)) {
            if(this.direction === "a"){
                this.direction = "d";
            }
            else if(this.direction === "d"){
                this.direction = "a";
            }
        };

//code hidden

        if (this.collisionData.touchPoints.other.left || this.collisionData.touchPoints.other.right) {
                if(this.direction === "a"){
                    this.direction = "d";
                }
                else if(this.direction === "d"){
                    this.direction = "a";
                }       
            }
        }
//code hidden

        if (this.collisionData.touchPoints.other.id === "jumpPlatform") {
            if (this.collisionData.touchPoints.other.left || this.collisionData.touchPoints.other.right) {
                if(this.direction === "a"){
                    this.direction = "d";
                }
                else if(this.direction === "d"){
                    this.direction = "a";
                }
            }
        }
  1. Let the enemy move followed by its direction.
    
        if(this.direction === "d"){
            this.speed = Math.abs(this.speed)
            this.canvas.style.transform = 'none';  //flip the animation, used for spritesheet that only have one direction
        }
        else if(this.direction === "a"){
            this.speed = -Math.abs(this.speed);  
    //Set the speed to negative. Instead of setting this.speed = -this.speed, using this way will be more accurate
            this.canvas.style.transform = 'scaleX(-1)'; //flip the animation, used for spritesheet that only have one direction
        }

//Code hidden

    this.x += this.speed;         // Move the enemy
5. Set the animation, using the setAnimation() function from character.js and do changes (remove unnecessary code)
```javascript
    setAnimation(key) {
        // animation comes from playerData
        var animation = this.playerData[key]

        // set frame and idle frame
        this.setFrameY(animation.row);
        this.setMaxFrame(animation.frames);
        if (this.isIdle && animation.idleFrame) {
            this.setFrameX(animation.idleFrame.column)
            this.setMinFrame(animation.idleFrame.frames);
        }
    }

//code hidden

    update() {
        //code hidden

        this.setAnimation(this.direction); 
//if you are using a sprite sheet that has both directions, keep this but remove the lines "this.canvas.style.transform = ..."

        //code hidden
}

that it is for enemy animation, but don't forget to change the enemy setting in GameSetup.js, like below: Before:

        mushroom: {
          src: "/images/platformer/platforms/mushroom.png",
          width: 200,
          height: 180,
          hitbox: { widthPercentage: 0.0, heightPercentage: 0.2}
        },

After

        zombie: {
          src: "/images/platformer/sprites/Zombiev1/SpriteSheet/Zombie_v1_Sheet.png",
          width: 130,
          height: 70,
          scaleSize: 70,
          speedRatio: 0.4,
          xPercentage: 0.6,
          animationSpeed: 5,
          wa: {row: 6, min:0, frames: 12},  //wa and wd are the same because the sprite sheet is one direction
          wd: {row: 6, min:0, frames: 12},
          idle: { row: 2, min:0, frames: 12, idleFrame: {column: 1, frames: 0} },
          a: { row: 3, min:0, frames: 12, idleFrame: { column: 1, frames: 0 } }, // Right Movement
          s: {row: 3, min:0, frames: 12}, // Stop the movement 
          d: { row: 3, min:0, frames: 12, idleFrame: { column: 1, frames: 0 } }, // Left Movement 
        },

Add AnimationSpeed Control

During the gameplay, have you ever felt that the animation of the characters is "too fast" By fixing that, we can let each sprite inside the sprite sheet show repeatly.

Steps to follow:

  1. Add two new attributes to Character.js
    
    constructor(canvas, image, data) {

//code hidden this.animationSpeed = data?.animationSpeed; //higher "animationSpeed" means slower animation this.counter = data?.animationSpeed;

}

2. Change the updateFrameX() in Character.js
Before:
```javascript
    updateFrameX() {
        if (this.frameX < this.maxFrame) {
            this.frameX++;
        } else {
            this.frameX = this.minFrame;
        }
    }

After:

    updateFrameX() {
        // Update animation frameX of the object
        if (this.frameX < this.maxFrame) {
            if(this.counter > 0){
                this.frameX = this.frameX; 
                this.counter--;
            }
            else{
                this.frameX++
                this.counter = this.animationSpeed;
            }
        } else {
            this.frameX = this.minFrame;
        }
    }

Also don't forget to change the enemy/player setting in GameSetup.js:

Before:

        boss: {
          src: "/images/platformer/sprites/boss.png",
          width: 64,
          height: 64,
          scaleSize: 320,
          speedRatio: 0.7,
          wa: { row: 9, min: 0, frames: 8 },
          wd: { row: 11, min: 0, frames: 8 },
          a: { row: 9, frames: 8, idleFrame: { column: 7, frames: 0 } },
          d: { row: 11, frames: 8, idleFrame: { column: 7, frames: 0 } }
        },

after:

        boss: {
          src: "/images/platformer/sprites/boss.png",
          width: 64,
          height: 64,
          scaleSize: 320,
          speedRatio: 0.7,
          animationSpeed: 5,   //higher animationSpeed means slower animation Speed, the number represent how many time each sprite repeated. (number 5 means each sprite will repeated 5 times instead of 1)
          wa: { row: 9, min: 0, frames: 8 },
          wd: { row: 11, min: 0, frames: 8 },
          a: { row: 9, frames: 8, idleFrame: { column: 7, frames: 0 } },
          d: { row: 11, frames: 8, idleFrame: { column: 7, frames: 0 } }
        },

Jittering Movement

Sometimes the Goombas will start jittering in place upon colliding with one another or the obstacles. The jittering movement may be caused by the overlapping of Goombas' hitboxes.

Frequent changes of direction in obstacle-heavy maps can cause if-statement conflicts, which can accidentally allow hitboxes to overlap, therefore causing both Goombas repeatedly change direction and cause both Goombas to start jittering

Example: after experiencing frequent changes of direction, causing both Goombas to go in the same direction and overlap. Because they are overlapping, both Goombas will change direction at the same time, and because they are in the same direction, which means no matter how many times they change, their directions will still same, causing an infinite loop.

The easier way to solve is to force the Goomba to change direction based on which side it collided with others (instead of just making the speed opposite each collision)

Example: Before:


        // Check for boundaries
        if (this.x <= this.minPosition || (this.x + this.canvasWidth >= this.maxPosition)) {
            this.speed = -this.speed;
        };

//code hidden

        if (this.collisionData.touchPoints.other.id === "tube") {
            if (this.collisionData.touchPoints.other.left || this.collisionData.touchPoints.other.right) {
                this.speed = -this.speed;            
            }
        }

After:


        // Check for boundaries
        if (this.x <= this.minPosition){
            this.speed = Math.abs(this.speed);   
        }
        else if(this.x + this.canvasWidth >= this.maxPosition){
            this.speed = -Math.abs(this.speed);
        };

//code hidden

        if (this.collisionData.touchPoints.other.id === "tube") {
            if(this.collisionData.touchPoints.other.left){
                this.speed = -Math.abs(this.speed);   
            }
            else if(this.collisionData.touchPoints.other.right){
                this.speed = Math.abs(this.speed);
            }
        }