diff --git a/src/cat.js b/src/cat.js index 569cd72..8d24c44 100644 --- a/src/cat.js +++ b/src/cat.js @@ -1,5 +1,5 @@ export class Cat { - constructor(x, y, targetSize) { + constructor(x, y, targetSize, groundRef, obstacleRefs) { this.x = x; this.y = y; this.size = 171; @@ -7,6 +7,8 @@ export class Cat { this.sprite = null; this.loaded = false; this.velocity = 24; + this.ground = groundRef; + this.obstacles = obstacleRefs; this.steps = []; @@ -15,10 +17,14 @@ export class Cat { this.stepTimer = 0; this.stepDuration = 40; // frames to complete one movement this.moveDirection = null; + this.lastDirection = null; + this.hasJumped = false; loadImage('assets/cat.webp', (img) => { - this.sprite = new Sprite(this.x, this.y, this.size, this.size); + this.sprite = new Sprite(this.x, this.y, this.size, this.size, 'dynamic'); + this.sprite.rotationLock = true; + this.sprite.bounciness = 0.1; this.sprite.spriteSheet = img; this.sprite.anis.offset.y = 3; this.sprite.anis.frameDelay = 8; @@ -39,7 +45,6 @@ export class Cat { } update() { - // if (!this.sprite) return; if (!this.sprite || this.steps.length === 0) return; console.log(`updating sprite...`); @@ -49,27 +54,18 @@ export class Cat { this._startMovement(this.steps[this.currentStepIndex]); } - this.sprite.position.x = this.x; - this.sprite.position.y = this.y; - - // console.log(`steps to run: ${this.steps}`); - this._handleInput(); - }; - update() { - if (!this.sprite || this.steps.length === 0) return; + // update() { + // if (!this.sprite || this.steps.length === 0) return; - if (this.isMoving) { - this._continueMovement(); - } else if (this.currentStepIndex < this.steps.length) { - this._startMovement(this.steps[this.currentStepIndex]); - } - - this.sprite.position.x = this.x; - this.sprite.position.y = this.y; - } + // if (this.isMoving) { + // this._continueMovement(); + // } else if (this.currentStepIndex < this.steps.length) { + // this._startMovement(this.steps[this.currentStepIndex]); + // } + // } run(steps) { @@ -79,33 +75,177 @@ export class Cat { } _startMovement(direction) { + const blockSize = 100; this.moveDirection = direction; this.stepTimer = 0; this.isMoving = true; - + if (direction === 'right') { + this.targetX = this.sprite.x + blockSize; + this.sprite.vel.x = this.velocity; + this.lastDirection = 'right'; this.changeAni('w'); - } else if (direction === 'up' || direction === 'down') { - this.changeAni('j'); + } else if (direction === 'left') { + this.targetX = this.sprite.x - blockSize; + this.sprite.vel.x = -this.velocity; + this.lastDirection = 'left'; + this.changeAni('w'); + } else if (direction === 'up') { + this.targetY = this.sprite.y - blockSize; + this.hasJumped = false; + + // Check platform + let isOnPlatform = this._checkPlatform(); + if (isOnPlatform) { + this.sprite.vel.y = -10; + if (this.lastDirection === 'right') this.sprite.vel.x = this.velocity; + else if (this.lastDirection === 'left') this.sprite.vel.x = -this.velocity; + else this.sprite.vel.x = 0; + this.changeAni('j'); + } } } + _checkPlatform() { + if (!this.sprite) return false; + // if (this.ground && this.sprite.colliding(this.ground)) return true; + + if (this.ground && Array.isArray(this.ground)) { + for (let block of this.ground) { + if (this.sprite.colliding(block)) return true; + } + } + + if (this.obstacles && Array.isArray(this.obstacles)) { + for (let block of this.obstacles) { + if (this.sprite.colliding(block)) return true; + } + } + return false; + } + + // _continueMovement() { + // this.stepTimer++; + + // // let isOnPlatform = false; + // let isOnPlatform = this._checkPlatform(); // Use existing _checkPlatform method instead + + + // if (this.ground && this.sprite.colliding(this.ground)) { + // isOnPlatform = true; + // } + + // if (this.obstacles && Array.isArray(this.obstacles)) { + // for (let block of this.obstacles) { + // if (this.sprite.colliding(block)) { + // isOnPlatform = true; + // break; + // } + // } + // } + + // if (this.moveDirection === 'right') { + // this.sprite.vel.x = 3; + // this.lastDirection = 'right'; + // } + + // if (this.moveDirection === 'left') { + // this.sprite.vel.x = -3; + // this.lastDirection = 'left'; + // } + + // if (this.moveDirection === 'up' && isOnPlatform && !this.hasJumped) { + // this.sprite.vel.y = -20; + + // // use lastDirection to add horizontal push + // if (this.lastDirection === 'right') { + // this.sprite.vel.x = 3; + // } else if (this.lastDirection === 'left') { + // this.sprite.vel.x = -3; + // } else { + // this.sprite.vel.x = 0; // jump straight up + // } + // } + + // this.hasJumped = true; + + // if (this.stepTimer >= this.stepDuration) { + // this.isMoving = false; + // this.currentStepIndex++; + // this.sprite.vel.x = 0; + // this.changeAni('i'); + // } + // } + _continueMovement() { this.stepTimer++; - const delta = this.velocity / this.stepDuration; + let isOnPlatform = this._checkPlatform(); // ✅ use this only - if (this.moveDirection === 'right') this.x += delta; - if (this.moveDirection === 'up') this.y -= delta; - if (this.moveDirection === 'down') this.y += delta; + if (this.moveDirection === 'right') { + this.sprite.vel.x = 3; + this.lastDirection = 'right'; + } + + if (this.moveDirection === 'left') { + this.sprite.vel.x = -3; + this.lastDirection = 'left'; + } + + if (this.moveDirection === 'up' && isOnPlatform && !this.hasJumped) { + this.sprite.vel.y = -20; + + if (this.lastDirection === 'right') { + this.sprite.vel.x = 3; + } else if (this.lastDirection === 'left') { + this.sprite.vel.x = -3; + } else { + this.sprite.vel.x = 0; + } + } + + this.hasJumped = true; if (this.stepTimer >= this.stepDuration) { this.isMoving = false; this.currentStepIndex++; - this.changeAni('i'); // back to idle after each move + this.sprite.vel.x = 0; + this.changeAni('i'); } } + + + restart() { + console.log('resetting initial position.....'); + + if (!this.sprite) return null; + + // Reset position to initial coordinates (from constructor) + this.sprite.x = this.x; + this.sprite.y = this.y; + + // Reset velocities + this.sprite.vel.x = 0; + this.sprite.vel.y = 0; + + // Reset movement states + this.steps = []; + this.currentStepIndex = 0; + this.isMoving = false; + this.stepTimer = 0; + this.moveDirection = null; + this.targetX = null; + this.targetY = null; + + // Reset animation + this.changeAni('i'); + + setTimeout(() => { + console.log('finish resetting...') + }, 1000); + } + keyPressed(key) { console.log(`receiving keyboard input: ${key}`); @@ -154,11 +294,6 @@ export class Cat { this.sprite = null; } - moveUp(){ - console.log(`sprite should move up...`); - this.changeAni('j') - } - // helper _getSpritePosition(){ console.log(`sprite's actual position: ${this.sprite.x}, ${this.sprite.y}`); diff --git a/src/components/ClickableArrow.js b/src/components/ClickableArrow.js index 2211fb8..6695f69 100644 --- a/src/components/ClickableArrow.js +++ b/src/components/ClickableArrow.js @@ -12,7 +12,6 @@ export class ClickableArrow { } constructor(direction, clickable = false) { - console.log(`constructing a new clickable arrow....`); this.direction = direction; this.image = ClickableArrow.images[direction]; this.clickable = clickable; diff --git a/src/main.js b/src/main.js index f8ef094..1a260d3 100644 --- a/src/main.js +++ b/src/main.js @@ -8,7 +8,7 @@ let mgr; function setup(){ createCanvas(windowWidth, windowHeight); - console.log(`window size: ${windowWidth}x${windowHeight}`); + world.gravity.y = 50; textFont('Pixelify Sans', 'sans-serif'); textAlign(CENTER); textSize(128); diff --git a/src/scenes/gameScene.js b/src/scenes/gameScene.js index 25dbe10..a3622eb 100644 --- a/src/scenes/gameScene.js +++ b/src/scenes/gameScene.js @@ -2,16 +2,33 @@ import { colors } from '../utils/theme.js'; import { Cat } from '../cat.js'; import { buttonS } from '../utils/theme.js'; import { MyButton } from '../utils/components.js'; -import { Arrow } from '../components/Arrow.js'; import { ClickableArrow } from '../components/ClickableArrow.js'; import { ControlPanel } from '../components/controlPanel.js'; export default function GameScene() { - const groundHeight = 100; - const worldHeight = windowHeight - groundHeight; + // for ground + let ground; + let blocksGround = []; + let blockSprites = []; + + let restart = false; + const catSize = 150; const worldBlockSize = 100; + const groundHeight = worldBlockSize; + // const worldHeight = windowHeight - groundHeight; + const maxIdx = { + x: Math.ceil(windowWidth / worldBlockSize), + y: Math.floor(windowHeight / worldBlockSize) + } + + const obstacle = [ + ...Array(maxIdx.y - 3 - 1).fill(...Array(maxIdx.x).fill(0)), + [...Array(9).fill(0), ...Array(maxIdx.x - 9).fill(1)], + [...Array(8).fill(0), ...Array(maxIdx.x - 8).fill(1)], + [...Array(7).fill(0), ...Array(maxIdx.x - 7).fill(1)], + ]; let cat; let runButton; @@ -45,15 +62,13 @@ export default function GameScene() { this.name = "GameScene"; this.setup = () => { - cat = new Cat(width / 4, height - 175 - 2, catSize); - runButton = new MyButton({ - x: width / 32 * 28.5, + x: (width / 32) * 28.5, y: height / 32, text: "run >>", mode: "CORNER", style: buttonS, - onPress: () => cat.run(steps.contents), + onPress: () => this.startGame(), }); blocks = new ControlPanel({ @@ -78,6 +93,42 @@ export default function GameScene() { // y: height / 32, // numBoxes: 4 // }); + + for (let i = 0; i < width; i += worldBlockSize) { + let b = new Sprite( + i + worldBlockSize / 2, // x position for each block + height - groundHeight / 2, // y position + worldBlockSize, // width of each block + worldBlockSize, // height of each block + 'static' + ); + const i_cnt = Math.floor(i / worldBlockSize); + + b.color = (i_cnt % 2) === 0 ? color(colors.secondary) : color(colors.darkRed); + b.strokeWeight = 5; + b.stroke = (i_cnt % 2) === 0 ? color(colors.darkRed) : color(colors.darkRed); + blocksGround.push(b); + } + + // Obstacles (physical) + for (let y = windowHeight - groundHeight - 2; y > 0; y -= worldBlockSize) { + for (let x = 0; x < width; x += worldBlockSize) { + const x_cnt = Math.floor(x / worldBlockSize); + const y_cnt = Math.floor(y / worldBlockSize); + const isObs = obstacle[y_cnt] && obstacle[y_cnt][x_cnt]; + + if (isObs) { + let b = new Sprite(x + worldBlockSize / 2, y + worldBlockSize / 2, worldBlockSize, worldBlockSize, 'static'); + b.color = (x_cnt + y_cnt) % 2 === 0 ? color(colors.tertiary) : color(colors.accent); + b.strokeWeight = 0; + blockSprites.push(b); + } + } + } + + // Cat sprite + // cat = new Cat(width / 4, height - catSize * 13/12, catSize, blocksGround, blockSprites); + cat = new Cat(3.5 * worldBlockSize, height - catSize * 13/12, catSize, blocksGround, blockSprites); }; this.draw = () => { @@ -90,35 +141,10 @@ export default function GameScene() { textAlign(LEFT, TOP); text('lvl.1', width / 32, height /32 - 4); - // Ground - rectMode(CORNER); - fill(colors.secondary); - rect(0, height - groundHeight, width, groundHeight); - - console.log(`ground is drawn at (0, ${height - groundHeight}) with size of ${width}x${groundHeight}`) - - // Obstacle - console.log(`world size: ${windowWidth}, ${windowHeight}`); - for (let y = windowHeight - groundHeight - 2; y > 0; y -= worldBlockSize) { - for (let x = 0; x < width ; x += worldBlockSize) { - stroke(0) - strokeWeight(1) - noFill() - rect(x, y, worldBlockSize, worldBlockSize); - textSize(24); - const x_cnt = Math.floor(x / worldBlockSize); - const y_cnt = Math.floor(y / worldBlockSize); - const txt = `${x_cnt}, ${y_cnt}`; - text(txt, x + 8, y + 8); - } - } - runButton.draw(); blocks.draw(); steps.draw(); // loops.draw(); - - // Sprite cat.draw(); }; @@ -171,4 +197,10 @@ export default function GameScene() { }); }; + + this.startGame = () => { + if (restart) cat.restart(); + cat.run(steps.contents); + this.restart = true; + } } diff --git a/src/utils/theme.js b/src/utils/theme.js index f5cd09d..4df1761 100644 --- a/src/utils/theme.js +++ b/src/utils/theme.js @@ -3,6 +3,7 @@ const colors = { secondary: "#ff4f64", tertiary: "#fff29d", accent: "#a4e4b6", + darkRed: "#C0404F" }; const button = {