Refactoring, implemented enemies

This commit is contained in:
Mottributo 2023-03-26 23:49:48 +03:00
parent 2f832bd2e6
commit 2daca56a91

379
sketch.js
View File

@ -6,7 +6,7 @@ var fps_recent_values = [];
var death_timer; var death_timer;
var showDebugData = false; var showDebugData = false;
var debug_charTrace = []; var debug_charTrace = [];
var shadows_enabled = false; var shadows_enabled = true;
var gameChar; var gameChar;
@ -43,6 +43,7 @@ function GameCharacter() {
this.isOnPlatform = false; this.isOnPlatform = false;
this.curGroundIndex = 0; this.curGroundIndex = 0;
this.memorizedPlatform; this.memorizedPlatform;
this.invincibility_time = 0;
this._updateSprite = function () { this._updateSprite = function () {
if (this.isPlummeting) { if (this.isPlummeting) {
@ -114,7 +115,6 @@ function GameCharacter() {
stroke(1); stroke(1);
this.x_step *= this.scale; this.x_step *= this.scale;
this.y_step *= this.scale; this.y_step *= this.scale;
// Standing, facing frontwards // Standing, facing frontwards
if (this.sprite == 1) { if (this.sprite == 1) {
// Body // Body
@ -226,6 +226,12 @@ function GameCharacter() {
console.error('Bad this sprite number: ' + this.sprite); console.error('Bad this sprite number: ' + this.sprite);
noLoop(); noLoop();
} }
// Drawing an aura of invincibility
if (this.invincibility_time > 0 && floor(frameCount/10)%2==0) {
fill('rgba(255,255,255,0.2)');
noStroke();
ellipse(this.x, this.y - this.y_step*3, this.x_step*9, this.y_step*9);
}
this.x_step /= this.scale; this.x_step /= this.scale;
this.y_step /= this.scale; this.y_step /= this.scale;
pop(); pop();
@ -249,6 +255,10 @@ function GameCharacter() {
this.y_step /= this.scale; this.y_step /= this.scale;
pop(); pop();
}; };
this.takeLife = function () {
gameChar.curLives--;
gameChar.invincibility_time+=180;
}
this._checkPlayerDie = function () { this._checkPlayerDie = function () {
if (frameCount - death_timer > 60) { if (frameCount - death_timer > 60) {
if (this.curLives > 1) { if (this.curLives > 1) {
@ -263,6 +273,7 @@ function GameCharacter() {
} }
}; };
this.act = function () { this.act = function () {
this.invincibility_time = max(0, this.invincibility_time-1);
if (!this.isPlummeting) { if (!this.isPlummeting) {
if (this.isLeft && !this.isRight) { if (this.isLeft && !this.isRight) {
this.x -= this.speed; this.x -= this.speed;
@ -323,7 +334,6 @@ function GameCharacter() {
this.y += 3; this.y += 3;
} }
}; };
this.getCurGroundY = function () { this.getCurGroundY = function () {
return groundPositions[this.curGroundIndex]; return groundPositions[this.curGroundIndex];
}; };
@ -346,8 +356,9 @@ function GameCharacter() {
this.scale += 0.1; this.scale += 0.1;
} }
}; };
} }
// NB: All the platforms mechanics were done // NB: All the platforms and enemies mechanics were done
// before the task was seen on Week 20. // before the task was seen on Week 20.
// Therefore, the implementation may // Therefore, the implementation may
// differ from one in the lecture. // differ from one in the lecture.
@ -380,7 +391,7 @@ function Platform(curGroundIndex, x, width, y) {
push(); push();
// drawing shadow on the ground // drawing shadow on the ground
noStroke(); noStroke();
fill('rgba(0,0,0, 0.15)'); fill('rgba(0,0,0, 0.08)');
rect( rect(
this.x - this.width, this.x - this.width,
groundPositions[curGroundIndex], groundPositions[curGroundIndex],
@ -412,6 +423,41 @@ function Platform(curGroundIndex, x, width, y) {
return false; return false;
}; };
} }
function Enemy(curGroundIndex, x, y, size) {
this.x = x;
this.y = y;
this.size = size;
this.curGroundIndex = curGroundIndex;
this.draw = function () {
push();
fill(lerpColor(palette.enemy_head_color, palette.ground_colors[this.curGroundIndex], 0.7));
ellipse(this.x, this.y, this.size);
fill(255,0,0);
ellipse(this.x - this.size/2, this.y, this.size/8, this.size/4);
ellipse(this.x + this.size/2, this.y, this.size/8, this.size/4);
textSize(this.size/1.5);
textAlign(CENTER);
fill(palette.enemy_body_color);
text(this.curGroundIndex, this.x, this.y+this.size/4);
pop();
}
this.drawShadow = function () {
push();
fill('rgba(0,0,0,0.2)');
noStroke();
ellipse(this.x, groundPositions[this.curGroundIndex], this.size/2, this.size/4);
pop();
}
this.collidesWith = function (who) {
if (dist(who.x, who.y, this.x, this.y) < this.size/2 &&
who.curGroundIndex == this.curGroundIndex) return true;
return false;
}
this.updatePosition = function () {
this.x = this.x + sin(frameCount/random(20,50)*random(0.5, 10));
this.y = this.y + cos(frameCount/random(10,20)*random(0.5, 10));
}
}
var text_size; var text_size;
// Variables to set colors. Set in setup() // Variables to set colors. Set in setup()
@ -440,8 +486,8 @@ function setup() {
coin_middle: color('#ababab'), coin_middle: color('#ababab'),
coin_inner: color('#FDC334'), coin_inner: color('#FDC334'),
heart_color: color('darkred'), heart_color: color('darkred'),
enemy_head_color: color('red'), enemy_head_color: color('rgba(0,0,0,0.5)'),
enemy_body_color: color('red'), enemy_body_color: color('rgba(255,255,255,0.3)')
}; };
startGame((level_start = true)); startGame((level_start = true));
} }
@ -457,7 +503,7 @@ function startGame(level_start) {
camera_speed = 0.9; camera_speed = 0.9;
gravity = 1; gravity = 1;
game_score = 0; game_score = 0;
finish_position_x = 2000; finish_position_x = 6000;
textSize(width / 20); textSize(width / 20);
flagpole = { flagpole = {
x: finish_position_x, x: finish_position_x,
@ -471,147 +517,7 @@ function startGame(level_start) {
if (level_start) { if (level_start) {
gameChar = new GameCharacter(); gameChar = new GameCharacter();
cameraPosX = gameChar.x - width / 2; cameraPosX = gameChar.x - width / 2;
// Creating trees, clouds, mountains, rivers, collectables. generateObjects();
{
// Creating trees, clouds, mountains, rivers, collectables, platforms, enemies.
for (i = 0; i < 150; i++) {
trees_x[i] = random(-100, 10000);
}
for (i = 0; i < 100; i++) {
clouds[i] = {
x: random(-100, 10000),
y: random(0, floorPos_y / 2),
size: random(0.4, 2),
};
}
// Latter mountains.
for (i = 0; i < finish_position_x / 90; i++) {
mountains[i] = {
x: random(-200, finish_position_x + 200),
width: random(60, 150),
height: random(floorPos_y - 50, floorPos_y - 150),
skew: random(-20, 20),
};
// To prevent spiky mountains which are quite ugly
mountains[i].height = max(
mountains[i].width,
mountains[i].height,
);
}
// Start mountains.
for (i = 0; i < 6; i++) {
mountains[i] = {
x: width / (i + 1.5),
width: width / (i + random(2, 4)),
height: height / (2 + 0.8 * i),
skew: (random(-10, 20) * i) / 2,
};
}
for (i = 0; i < 100; i++) {
if (i == 0) {
rivers[0] = {
x: 700,
width: 50,
points: [],
};
} else {
if (rivers[i - 1].x + 600 > finish_position_x) break;
rivers[i] = {
x: rivers[i - 1].x + 300 + 200 * random(0.5, 1),
width: 50 + 30 * random(),
points: [],
};
}
}
for (i = 0; i < 100; i++) {
if (i == 0) {
collectables[0] = {
x: 600,
curGroundIndex: 0,
y:
groundPositions[0] -
10 /*taking 10 for better visuals*/,
size: 75,
isFound: false,
};
} else {
// Skipping generating coins after the finish line
if (collectables[i - 1].x + 200 > finish_position_x) {
break;
}
r = floor(random(0, groundPositions.length));
collectables[i] = {
x: collectables[i - 1].x + 50 + 100 * random(0.5, 1),
curGroundIndex: r,
y:
groundPositions[r] -
10 /*taking 10 for better visuals*/,
size: 75,
isFound: false,
};
// Checking whether the coin is over a river;
// marking it as isFound (making it disabled) in case if.
for (k = 0; k < rivers.length; k++) {
if (
rivers[k].x - rivers[k].width / 2 <
collectables[i].x &&
rivers[k].x + rivers[k].width / 2 >
collectables[i].x
) {
collectables[i].isFound = true;
}
}
}
}
for (i = 0; i < groundPositions.length; i++) {
platforms[i] = [];
}
for (i = 0; i < 100; i++) {
if (i == 0) {
platforms[0][0] = new Platform(
0,
700,
50,
groundPositions[0] - Platform.max_y_deviation,
);
platforms[1][0] = new Platform(
1,
800,
50,
groundPositions[1] - Platform.max_y_deviation,
);
platforms[2][0] = new Platform(
2,
900,
50,
groundPositions[2] - Platform.max_y_deviation,
);
} else {
let groundPosIndex = floor(
random(0, groundPositions.length),
);
// array.slice(-1)[0] gets the last element of an array without removing it,
// contrary to array.pop().
prev = platforms[groundPosIndex].slice(-1)[0];
// stopping generation after the flagpole
if (prev.x + prev.width + 155 > finish_position_x) break;
platforms[groundPosIndex].push(
new Platform(
groundPosIndex,
prev.x + random(prev.width + 70, prev.width + 155),
random(15, 60),
max(
random(
prev.y - prev.max_y_deviation,
floorPos_y - prev.max_y_deviation,
),
height / 8,
),
),
);
}
}
}
} else { } else {
gameChar.x = width / 2; gameChar.x = width / 2;
gameChar.y = floorPos_y + (height - floorPos_y) / 6; gameChar.y = floorPos_y + (height - floorPos_y) / 6;
@ -625,6 +531,166 @@ function startGame(level_start) {
gameChar.isOnPlatform = false; gameChar.isOnPlatform = false;
} }
} }
function generateObjects() {
// Creating trees, clouds, mountains, rivers, collectables, platforms, enemies.
// Trees coords
for (i = 0; i < 150; i++) {
trees_x[i] = random(-100, 10000);
}
// Clouds
for (i = 0; i < 100; i++) {
clouds[i] = {
x: random(-100, 10000),
y: random(0, floorPos_y / 2),
size: random(0.4, 2),
};
}
// Latter mountains.
for (i = 0; i < finish_position_x / 90; i++) {
mountains[i] = {
x: random(-200, finish_position_x + 200),
width: random(60, 150),
height: random(floorPos_y - 50, floorPos_y - 150),
skew: random(-20, 20),
};
// To prevent spiky mountains which are quite ugly
mountains[i].height = max(
mountains[i].width,
mountains[i].height,
);
}
// Start mountains.
for (i = 0; i < 6; i++) {
mountains[i] = {
x: width / (i + 1.5),
width: width / (i + random(2, 4)),
height: height / (2 + 0.8 * i),
skew: (random(-10, 20) * i) / 2,
};
}
// Rivers
for (i = 0; i < 100; i++) {
if (i == 0) {
rivers[0] = {
x: 700,
width: 50,
points: [],
};
} else {
if (rivers[i - 1].x + 600 > finish_position_x) break;
rivers[i] = {
x: rivers[i - 1].x + 100 + 200 * random(0.5, 1),
width: 50 + 30 * random(),
points: [],
};
}
}
// Collectables
for (i = 0; i < 100; i++) {
if (i == 0) {
collectables[0] = {
x: 600,
curGroundIndex: 0,
y:
groundPositions[0] -
10 /*taking 10 for better visuals*/,
size: 75,
isFound: false,
};
} else {
// Skipping generating coins after the finish line
if (collectables[i - 1].x + 200 > finish_position_x) {
break;
}
r = floor(random(0, groundPositions.length));
collectables[i] = {
x: collectables[i - 1].x + 50 + 100 * random(0.5, 1),
curGroundIndex: r,
y:
groundPositions[r] -
10 /*taking 10 for better visuals*/,
size: 75,
isFound: false,
};
// Checking whether the coin is over a river;
// marking it as isFound (making it disabled) in case if.
for (k = 0; k < rivers.length; k++) {
if (
rivers[k].x - rivers[k].width / 2 <
collectables[i].x &&
rivers[k].x + rivers[k].width / 2 >
collectables[i].x
) {
collectables[i].isFound = true;
}
}
}
}
// Platform rows
for (i = 0; i < groundPositions.length; i++) {
platforms[i] = [];
}
// Platforms
for (i = 0; i < 100; i++) {
if (i == 0) {
platforms[0][0] = new Platform(
0,
700,
50,
groundPositions[0] - Platform.max_y_deviation,
);
platforms[1][0] = new Platform(
1,
800,
50,
groundPositions[1] - Platform.max_y_deviation,
);
platforms[2][0] = new Platform(
2,
900,
50,
groundPositions[2] - Platform.max_y_deviation,
);
} else {
let groundPosIndex = floor(
random(0, groundPositions.length),
);
// array.slice(-1)[0] gets the last element of an array without removing it,
// contrary to array.pop().
prev = platforms[groundPosIndex].slice(-1)[0];
// stopping generation after the flagpole
if (prev.x + prev.width + 155 > finish_position_x) break;
// adding platforms
platforms[groundPosIndex].push(
new Platform(
groundPosIndex,
prev.x + random(prev.width + 70, prev.width + 155),
random(15, 60),
max(
random(
prev.y - prev.max_y_deviation,
floorPos_y - prev.max_y_deviation,
),
height / 8,
),
),
);
}
}
// Enemies
enemies = [];
for (i = 0; i < floor(finish_position_x/130); i++) {
index = floor(random(0, groundPositions.length))
enemies.push(
new Enemy(
index,
random(500, finish_position_x),
groundPositions[index] - random(50, floorPos_y),
random(40, 60)
)
)
}
}
function draw() { function draw() {
// -------- SKY ---------------- // -------- SKY ----------------
background(palette.sky_color); background(palette.sky_color);
@ -672,8 +738,17 @@ function draw() {
// -------- FLAGPOLE ------------ // -------- FLAGPOLE ------------
renderFlagpole(); renderFlagpole();
if (!flagpole.isReached) checkFlagpole(); if (!flagpole.isReached) checkFlagpole();
// GAMECHAR, PLATFORMS & ENEMIES RENDER // GAMECHAR & PLATFORMS RENDER --
complexDraw(); complexDraw();
// -------- ENEMIES -------------
for (i=0; i<enemies.length; i++) {
enemies[i].updatePosition();
enemies[i].draw();
if (shadows_enabled) enemies[i].drawShadow();
if (enemies[i].collidesWith(gameChar) && gameChar.invincibility_time <= 0) {
gameChar.takeLife();
}
}
// - ------ GAME CHARACTER ------ // - ------ GAME CHARACTER ------
gameChar.act(); gameChar.act();
pop(); // Scrolling end pop(); // Scrolling end
@ -681,12 +756,12 @@ function draw() {
drawInterface(); drawInterface();
} }
function complexDraw() { function complexDraw() {
// Since platforms require being drawn in three different ordrs relative to enemies and the game character, // Since platforms require being drawn in three different ordrs relative to the game character,
// a function is made to handle this and to decrease clutter. // a function is made to handle this and to decrease clutter.
for (i = 0; i <= gameChar.curGroundIndex; i++) { for (i = 0; i <= gameChar.curGroundIndex; i++) {
drawPlatforms(i); drawPlatforms(i);
} }
if (shadows_enabled) gameChar.drawShadow(); if (shadows_enabled) {gameChar.drawShadow();}
gameChar.draw(); gameChar.draw();
for (i = gameChar.curGroundIndex + 1; i < groundPositions.length; i++) { for (i = gameChar.curGroundIndex + 1; i < groundPositions.length; i++) {
drawPlatforms(i); drawPlatforms(i);