initial commit
This commit is contained in:
BIN
src/assets/.DS_Store
vendored
Normal file
BIN
src/assets/.DS_Store
vendored
Normal file
Binary file not shown.
BIN
src/assets/images/background_single.png
Normal file
BIN
src/assets/images/background_single.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
BIN
src/assets/images/background_single2.png
Normal file
BIN
src/assets/images/background_single2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 540 KiB |
28
src/assets/scripts/background.js
Normal file
28
src/assets/scripts/background.js
Normal file
@ -0,0 +1,28 @@
|
||||
class Background {
|
||||
constructor(game){
|
||||
this.game = game;
|
||||
this.image = document.getElementById('background');
|
||||
this.width = 2400;
|
||||
this.height = this.game.baseHeight;
|
||||
this.scaledWidth;
|
||||
this.scaledHeight;
|
||||
this.x
|
||||
}
|
||||
update(){
|
||||
this.x -= this.game.speed;
|
||||
if(this.x <= -this.scaledWidth) this.x = 0;
|
||||
}
|
||||
|
||||
draw(){
|
||||
this.game.ctx.drawImage(this.image, this.x, 0, this.scaledWidth, this.scaledHeight)
|
||||
this.game.ctx.drawImage(this.image, this.x + this.scaledWidth -1 , 0, this.scaledWidth, this.scaledHeight)
|
||||
this.game.ctx.drawImage(this.image, this.x + 2 * this.scaledWidth -1 , 0, this.scaledWidth, this.scaledHeight)
|
||||
|
||||
}
|
||||
resize(){
|
||||
this.scaledWidth = this.width * this.game.ratio;
|
||||
this.scaledHeight = this.height * this.game.ratio;
|
||||
|
||||
this.x = 0;
|
||||
}
|
||||
}
|
||||
167
src/assets/scripts/main.js
Normal file
167
src/assets/scripts/main.js
Normal file
@ -0,0 +1,167 @@
|
||||
class Game {
|
||||
constructor(canvas, context) {
|
||||
this.canvas = canvas;
|
||||
this.ctx = context;
|
||||
this.width = this.canvas.width;
|
||||
this.height = this.canvas.height;
|
||||
this.baseHeight = 720;
|
||||
this.ratio = this.height / this.baseHeight;
|
||||
this.background = new Background(this);
|
||||
this.player = new Player(this);
|
||||
this.obstacles = [];
|
||||
this.numberOfObstacles = 2;
|
||||
this.gravity;
|
||||
this.speed;
|
||||
this.minSpeed;
|
||||
this.maxSpeed;
|
||||
this.score;
|
||||
this.gameOver;
|
||||
this.timer;
|
||||
this.message1;
|
||||
this.message2;
|
||||
this.eventTimer = 0;
|
||||
this.eventInterval = 150;
|
||||
this.eventUpdate = true;
|
||||
this.touchStartX;
|
||||
this.swipeDistance = 50;
|
||||
|
||||
this.resize(window.innerWidth, window.innerHeight);
|
||||
|
||||
window.addEventListener('resize', e => {
|
||||
this.resize(e.currentTarget.innerWidth, e.currentTarget.innerHeight);
|
||||
});
|
||||
|
||||
window.addEventListener('mousedown', e => {
|
||||
this.player.flap();
|
||||
})
|
||||
window.addEventListener('keydown', e => {
|
||||
if (e.key === ' ' || e.key === 'Enter') this.player.flap();
|
||||
if (e.key === 'Shift' || e.key.toLowerCase() === 'c') this.player.startCharge();
|
||||
})
|
||||
this.canvas.addEventListener('touchstart', e => {
|
||||
this.player.flap();
|
||||
this.touchStartX = e.changedTouches[0].pageX;
|
||||
})
|
||||
this.canvas.addEventListener('touchmove', e => {
|
||||
if(e.changedTouches[0].pageX - this.touchStartX > this.swipeDistance ){
|
||||
this.player.startCharge();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
resize(width, height) {
|
||||
this.canvas.width = width;
|
||||
this.canvas.height = height;
|
||||
//this.ctx.fillStyle = 'blue';
|
||||
this.ctx.font = '15px Bungee'
|
||||
this.ctx.textAlign = 'right';
|
||||
this.ctx.lineWidth = 3;
|
||||
this.ctx.strokeStyle = 'white'
|
||||
this.width = this.canvas.width;
|
||||
this.height = this.canvas.height;
|
||||
this.ratio = this.height / this.baseHeight;
|
||||
|
||||
this.gravity = 0.15 * this.ratio;
|
||||
this.speed = 2 * this.ratio;
|
||||
this.minSpeed = this.speed;
|
||||
this.maxSpeed = this.speed * 5;
|
||||
this.background.resize();
|
||||
this.player.resize();
|
||||
this.createObstacles();
|
||||
this.obstacles.forEach(obstacle => {
|
||||
obstacle.resize();
|
||||
});
|
||||
this.score = 0;
|
||||
this.gameOver = false;
|
||||
this.timer = 0;
|
||||
}
|
||||
render(deltaTime) {
|
||||
if (!this.gameOver) this.timer += deltaTime;
|
||||
this.handlePeriodicEvents(deltaTime);
|
||||
this.background.update();
|
||||
this.background.draw();
|
||||
this.drawStatusText();
|
||||
this.player.update();
|
||||
this.player.draw();
|
||||
this.obstacles.forEach(obstacle => {
|
||||
obstacle.update();
|
||||
obstacle.draw();
|
||||
});
|
||||
}
|
||||
createObstacles() {
|
||||
this.obstacles = [];
|
||||
const firstX = 100;
|
||||
const obstacleSpacing = 600 * this.ratio;
|
||||
for (let i = 0; i < this.numberOfObstacles; i++) {
|
||||
this.obstacles.push(new Obstacle(this, firstX + i * obstacleSpacing))
|
||||
}
|
||||
}
|
||||
checkCollision(a, b) {
|
||||
const dx = a.collisionX - b.collisionX;
|
||||
const dy = a.collisionY - b.collisionY;
|
||||
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||
const sumOfRadii = a.collisionRadius + b.collisionRadius;
|
||||
return distance <= sumOfRadii;
|
||||
}
|
||||
formatTimer() {
|
||||
return (this.timer * 0.001).toFixed(1)
|
||||
}
|
||||
handlePeriodicEvents(deltaTime){
|
||||
if(this.eventTimer < this.eventInterval){
|
||||
this.eventTimer += deltaTime;
|
||||
this.eventUpdate = false;
|
||||
} else {
|
||||
this.eventTimer = this.eventTimer % this.eventInterval;
|
||||
this.eventUpdate = true;
|
||||
}
|
||||
}
|
||||
drawStatusText() {
|
||||
this.ctx.save();
|
||||
this.ctx.fillText('Score: ' + this.score, this.width - 10, 30);
|
||||
this.ctx.textAlign = 'left';
|
||||
this.ctx.fillText('Timer: ' + this.formatTimer(), 10, 30);
|
||||
if (this.gameOver) {
|
||||
if (this.player.collided) {
|
||||
this.message1 = "Getting Rusty?";
|
||||
this.message2 = "Collision time " + this.formatTimer() + " seconds!";
|
||||
} else if (this.obstacles.length <= 0) {
|
||||
this.message1 = "Nailed it!";
|
||||
this.message2 = "Can you do it faster than " + this.formatTimer() + " seconds?";
|
||||
}
|
||||
this.ctx.textAlign = 'center'
|
||||
this.ctx.font = '30px Bungee'
|
||||
this.ctx.fillText(this.message1, this.width * 0.5, this.height * 0.5 - 40);
|
||||
this.ctx.font = '15px Bungee'
|
||||
this.ctx.fillText(this.message2, this.width * 0.5, this.height * 0.5 - 20);
|
||||
this.ctx.fillText("Press 'R' to try again", this.width * 0.5, this.height * 0.5);
|
||||
|
||||
}
|
||||
if (this.player.energy <= 20) this.ctx.fillStyle = 'red'
|
||||
else if (this.player.energy >= this.player.maxEnergy) this.ctx.fillStyle = 'red';
|
||||
for (let i = 0; i < this.player.energy; i++) {
|
||||
//this.ctx.fillRect(10 + i * 6, 40, 5, 15);
|
||||
this.ctx.fillRect(10, this.height - 10 - this.player.barSize * i, this.player.barSize * 5, this.player.barSize);
|
||||
}
|
||||
|
||||
this.ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('load', function () {
|
||||
const canvas = document.getElementById('canvas1');
|
||||
const ctx = canvas.getContext('2d');
|
||||
canvas.width = 720;
|
||||
canvas.height = 720;
|
||||
|
||||
const game = new Game(canvas, ctx);
|
||||
|
||||
let lastTime = 0;
|
||||
function animate(timeStamp) {
|
||||
const deltaTime = timeStamp - lastTime;
|
||||
lastTime = timeStamp;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
game.render(deltaTime);
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
requestAnimationFrame(animate);
|
||||
});
|
||||
51
src/assets/scripts/obstacle.js
Normal file
51
src/assets/scripts/obstacle.js
Normal file
@ -0,0 +1,51 @@
|
||||
class Obstacle{
|
||||
constructor(game, x){
|
||||
this.game = game;
|
||||
this.spriteWidth = 120;
|
||||
this.spriteHeight = 120;
|
||||
this.scaledWidth = this.spriteWidth * this.game.ratio;
|
||||
this.scaledHeight = this.spriteHeight * this.game.ratio;
|
||||
this.x = x;
|
||||
this.y = Math.random() * this.game.height - this.scaledHeight
|
||||
this.collisionX;
|
||||
this.collisiony;
|
||||
this.collisionRadius = this.scaledWidth * 0.5;
|
||||
this.speedY = this.game.ratio * (Math.random() < 0.5 ? -1 : 1);
|
||||
this.markedForDeletion;
|
||||
}
|
||||
update(){
|
||||
this.x -= this.game.speed;
|
||||
this.y += this.speedY;
|
||||
this.collisionX = this.x + this.scaledWidth * 0.5;
|
||||
this.collisionY = this.y + this.scaledHeight * 0.5;
|
||||
if(this.y < 0 || this.y >= this.game.height - this.scaledHeight){
|
||||
this.speedY *= -1;
|
||||
}
|
||||
if(this.isOffScreen()) {
|
||||
this.markedForDeletion = true;
|
||||
this.game.obstacles = this.game.obstacles.filter(o => !o.markedForDeletion);
|
||||
console.log(this.game.obstacles.length)
|
||||
this.game.score++;
|
||||
if(this.game.obstacles.length <= 0) this.game.gameOver = true;
|
||||
|
||||
}
|
||||
if(this.game.checkCollision(this, this.game.player)){
|
||||
this.game.gameOver = true;
|
||||
this.game.player.collided=true;
|
||||
}
|
||||
}
|
||||
draw(){
|
||||
this.game.ctx.fillRect(this.x, this.y, this.scaledWidth, this.scaledHeight);
|
||||
this.game.ctx.beginPath();
|
||||
this.game.ctx.arc(this.collisionX, this.collisionY, this.collisionRadius, 0, Math.PI * 2)
|
||||
this.game.ctx.stroke();
|
||||
}
|
||||
resize(){
|
||||
this.scaledWidth = this.spriteWidth * this.game.ratio;
|
||||
this.scaledHeight = this.spriteHeight * this.game.ratio;
|
||||
}
|
||||
|
||||
isOffScreen(){
|
||||
return this.x < 0 - this.scaledWidth;
|
||||
}
|
||||
}
|
||||
87
src/assets/scripts/player.js
Normal file
87
src/assets/scripts/player.js
Normal file
@ -0,0 +1,87 @@
|
||||
class Player {
|
||||
constructor(game){
|
||||
this.game = game;
|
||||
this.x = 20;
|
||||
this.y;
|
||||
this.spriteWidth = 200;
|
||||
this.spriteHeight = 200;
|
||||
this.width;
|
||||
this.height;
|
||||
this.speedY;
|
||||
this.flapSpeed;
|
||||
this.collisionX;
|
||||
this.collisionY;
|
||||
this.collisionRadius;
|
||||
this.collided = false;
|
||||
this.energy = 30;
|
||||
this.maxEnergy = this.energy * 2;
|
||||
this.minEnergy = 15;
|
||||
this.charging = false;
|
||||
this.barSize = 5 * this.game.ratio;
|
||||
}
|
||||
draw(){
|
||||
this.game.ctx.strokeRect(this.x, this.y, this.width, this.height);
|
||||
this.game.ctx.beginPath();
|
||||
this.game.ctx.arc(this.collisionX, this.collisionY, this.collisionRadius, 0, Math.PI * 2)
|
||||
this.game.ctx.stroke();
|
||||
}
|
||||
update(){
|
||||
this.handleEnergy();
|
||||
this.y += this.speedY;
|
||||
this.collisionY = this.y + this.height * 0.5;
|
||||
|
||||
if (!this.isTouchingBottom()){
|
||||
this.speedY += this.game.gravity;
|
||||
}
|
||||
// bottom boundary
|
||||
if (this.isTouchingBottom()){
|
||||
this.y = this.game.height - this.height;
|
||||
}
|
||||
}
|
||||
resize(){
|
||||
this.width = this.spriteWidth * this.game.ratio;
|
||||
this.height = this.spriteHeight * this.game.ratio;
|
||||
this.y = this.game.height * 0.5 - this.height * 0.5;
|
||||
this.speedY = -8 * this.game.ratio;
|
||||
this.flapSpeed = 5 * this.game.ratio;
|
||||
this.collisionRadius = this.width * 0.5
|
||||
this.collisionX = this.x + this.width * 0.5;
|
||||
this.collided = false;
|
||||
this.barSize = Math.ceil(5 * this.game.ratio);
|
||||
}
|
||||
startCharge(){
|
||||
this.charging = true;
|
||||
this.game.speed = this.game.maxSpeed;
|
||||
}
|
||||
stopCharge(){
|
||||
this.charging = false;
|
||||
this.game.speed = this.game.minSpeed;
|
||||
}
|
||||
isTouchingTop(){
|
||||
return this.y <= 0;
|
||||
}
|
||||
isTouchingBottom(){
|
||||
return this.y >= this.game.height - this.height;
|
||||
}
|
||||
handleEnergy(){
|
||||
if(this.game.eventUpdate){
|
||||
if(this.energy < this.maxEnergy){
|
||||
this.energy += 1;
|
||||
}
|
||||
if(this.charging){
|
||||
this.energy -= 6;
|
||||
if(this.energy <= 0){
|
||||
this.energy = 0;
|
||||
this.stopCharge();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
flap(){
|
||||
this.stopCharge();
|
||||
if(!this.isTouchingTop()) {
|
||||
this.speedY = -this.flapSpeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user