diff --git a/src/_cat.js b/src/_cat.js new file mode 100644 index 0000000..34f2e66 --- /dev/null +++ b/src/_cat.js @@ -0,0 +1,159 @@ +export class Cat { + constructor(x, y, targetSize) { + this.x = x; + this.y = y; + this.size = 171; + this.targetSize = targetSize; + this.sprite = null; + this.loaded = false; + this.velocity = 24; + this.count = 0; + + loadImage('assets/cat.webp', (img) => { + this.sprite = new Sprite(this.x, this.y, this.size, this.size); + this.sprite.spriteSheet = img; + this.sprite.anis.offset.y = 3; + this.sprite.anis.frameDelay = 8; + this.sprite.friction = 0; + this.sprite.addAnis({ + jump: { row: 0, frames: 4 }, + death: { row: 1, frames: 4 }, + hurt: { row: 2, frames: 2 }, + idle: { row: 3, frames: 4 }, + walk: { row: 4, frames: 6 } + }); + this.sprite.changeAni('idle'); + + const scaleFactor = this.targetSize / 171; + this.sprite.scale = scaleFactor; + this.loaded = true; + }); + } + + update() { + if (!this.sprite) return; + console.log(`updating sprite...`); + + this.sprite.position.x = this.x; + this.sprite.position.y = this.y; + + if (kb.pressing('left')) { + console.log("accepting pressed kb"); + this.sprite.vel.x = -1; + } else if (kb.pressing('right')) { + this.sprite.vel.x = 1; + } else { + this.sprite.vel.x = 0; // Stop the hero if no keys are pressed + } + }; + + draw() { + if (!this.sprite) return; + // this.sprite.draw(); + this.sprite.update(); + }; + + changeAni(key) { + if (!this.sprite) return; + + switch (key) { + case 'w': + this.sprite.changeAni("walk"); + break; + case 'j': + this.sprite.changeAni("jump"); + break; + case 'i': + this.sprite.changeAni("idle"); + break; + case 'h': + this.sprite.changeAni("hurt"); + break; + case 'd': + this.sprite.changeAni("death"); + break; + default: + break; + }; + }; + + setPosition(x, y) { + if (this.sprite) { + this.sprite.position.x = x; + this.sprite.position.y = y; + }; + }; + + keyPressed(key) { + if (key === 'ArrowRight') { + this.moveRight(); + } + } + + + remove() { + if (!this.sprite) return; + + this.sprite.remove(); + this.sprite = null; + } + + /// i want to add this part to move sprite across the canvas when user presses right arrow + // if (kb.presses('right')) { + // this.move(30, 'right', 3) + // } + + move(direction){ + if (!direction) return; + console.log(`entered move method`); + if (direction == "right") this.moveRight(); + if (direction == "up") this.moveUp(); + } + + moveRight(){ + console.log(`${this.count}`); + this.changeAni('w') + this.sprite.vel.x = 1; + this.count += 1; + } + + moveUp(){ + console.log(`sprite should move up...`); + this.changeAni('j') + } + + runCat(steps){ + steps.forEach(element => { + setTimeout(() => { + console.log(element) + }, 1000); + }); + } + + + // helper + _getSpritePosition(){ + console.log(`sprite's actual position: ${this.sprite.x}, ${this.sprite.y}`); + return (this.sprite.x, this.sprite.y); + } + + _translate(step){ + switch (step) { + case 'up': + console.log('move: up'); + return 'jump' + break; + case 'right': + console.log('move: down'); + return 'walk' + break; + case 'down': + console.log('move: up'); + return 'jump' + break; + default: + break; + } + } +} + \ No newline at end of file diff --git a/src/cat.js b/src/cat.js index 9f4b390..569cd72 100644 --- a/src/cat.js +++ b/src/cat.js @@ -8,6 +8,15 @@ export class Cat { this.loaded = false; this.velocity = 24; + this.steps = []; + + this.currentStepIndex = 0; + this.isMoving = false; + this.stepTimer = 0; + this.stepDuration = 40; // frames to complete one movement + this.moveDirection = null; + + loadImage('assets/cat.webp', (img) => { this.sprite = new Sprite(this.x, this.y, this.size, this.size); this.sprite.spriteSheet = img; @@ -30,20 +39,87 @@ export class Cat { } update() { - if (this.sprite) { - this.sprite.position.x = this.x; - this.sprite.position.y = this.y; + // if (!this.sprite) return; + if (!this.sprite || this.steps.length === 0) return; + console.log(`updating sprite...`); + + 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; + + // console.log(`steps to run: ${this.steps}`); + + this._handleInput(); + }; + 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; + } + + + run(steps) { + this.steps = steps.map(e => e.direction); + this.currentStepIndex = 0; + this.isMoving = false; + } + + _startMovement(direction) { + this.moveDirection = direction; + this.stepTimer = 0; + this.isMoving = true; + + if (direction === 'right') { + this.changeAni('w'); + } else if (direction === 'up' || direction === 'down') { + this.changeAni('j'); + } + } + + _continueMovement() { + this.stepTimer++; + + const delta = this.velocity / this.stepDuration; + + if (this.moveDirection === 'right') this.x += delta; + if (this.moveDirection === 'up') this.y -= delta; + if (this.moveDirection === 'down') this.y += delta; + + if (this.stepTimer >= this.stepDuration) { + this.isMoving = false; + this.currentStepIndex++; + this.changeAni('i'); // back to idle after each move + } + } + + + keyPressed(key) { + console.log(`receiving keyboard input: ${key}`); + } + draw() { - if (this.sprite) this.sprite.draw(); + if (!this.sprite) return; + this.update(); + this.sprite.update(); }; changeAni(key) { if (!this.sprite) return; - console.log('changing animation...'); - console.log(key); + switch (key) { case 'w': this.sprite.changeAni("walk"); @@ -72,37 +148,44 @@ export class Cat { }; }; - moveRight() { - if (this.x + this.targetSize >= width) { - this.x = width - this.targetSize; // Ensure it doesn't go beyond the boundary - this.setPosition(this.x, this.y); - this.changeAni('i'); // Optionally change to idle if boundary reached - return; - } - this.x += this.velocity; // Keep moving - this.setPosition(this.x, this.y); - console.log(`sprite's actual position: ${this._getSpritePosition()}`) - this.changeAni('w'); // Keep walking animation - } - - keyPressed(key) { - if (key === 'ArrowRight') { - this.moveRight(); - } - } - - remove() { if (!this.sprite) return; - this.sprite.remove(); 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}`); return (this.sprite.x, this.sprite.y); } + + _translate(step){ + switch (step) { + case 'up': + console.log('move: up'); + return 'jump' + break; + case 'right': + console.log('move: down'); + return 'walk' + break; + case 'down': + console.log('move: up'); + return 'jump' + break; + default: + break; + } + } + + _handleInput(){ + console.log('assume i have something....'); + } } \ No newline at end of file diff --git a/src/components/ClickableArrow.js b/src/components/ClickableArrow.js index 9289775..2211fb8 100644 --- a/src/components/ClickableArrow.js +++ b/src/components/ClickableArrow.js @@ -1,47 +1,72 @@ +import { colors } from "../utils/theme"; + export class ClickableArrow { - static images = {}; + static images = {}; - static preload() { - ClickableArrow.images.up = loadImage('/assets/up.png'); - ClickableArrow.images.down = loadImage('/assets/down.png'); - ClickableArrow.images.left = loadImage('/assets/left.png'); - ClickableArrow.images.right = loadImage('/assets/right.png'); - ClickableArrow.images.empty = loadImage('/assets/empty.png'); - } - - constructor(direction, clickable=false) { - this.direction = direction; - this.image = ClickableArrow.images[direction]; - this.clickable = clickable; - // this.onClick = onClick | null; - this.x = 0; - this.y = 0; - this.width = 40; - this.height = 40; - } - - draw(x, y) { - this.x = x; - this.y = y; - stroke(255); - image(this.image, x, y, 40, 40); - } - - handleClick(mouseX, mouseY) { - if (this._isMouseOver(mouseX, mouseY) && this._onClick) { - this._onClick(); - } - } - - //helpers - _onClick(){ - console.log('clicked!'); - } - - _isMouseOver(mouseX, mouseY) { - return mouseX > this.x && mouseX < this.x + this.width && - mouseY > this.y && mouseY < this.y + this.height; - } - + static preload() { + ClickableArrow.images.up = loadImage('/assets/up.png'); + ClickableArrow.images.down = loadImage('/assets/down.png'); + ClickableArrow.images.left = loadImage('/assets/left.png'); + ClickableArrow.images.right = loadImage('/assets/right.png'); + ClickableArrow.images.empty = loadImage('/assets/empty.png'); } + + constructor(direction, clickable = false) { + console.log(`constructing a new clickable arrow....`); + this.direction = direction; + this.image = ClickableArrow.images[direction]; + this.clickable = clickable; + this.selected = false; + this.x = 0; + this.y = 0; + this.width = 40; + this.height = 40; + } + + draw(x, y) { + this.x = x; + this.y = y; + stroke(255); + fill(200); + + if (this.selected) { + fill(colors.tertiary); + } else { + fill(255); + } + rect(x, y, 40, 40); + image(this.image, x, y, 40, 40); + } + + handleClick(mouseX, mouseY) { + if (this._isMouseOver(mouseX, mouseY) && this._onClick) { + this._onClick(); + } + } + + set(direction){ + this.direction = direction; + this.image = ClickableArrow.images[direction]; + } + + select(){ + this.selected = true; + } + + unselect(){ + this.selected = false; + } + + //helpers + _onClick(){ + if (!this.clickable) return; + console.log('clicked!'); + } + + _isMouseOver(mouseX, mouseY) { + return mouseX > this.x && mouseX < this.x + this.width && + mouseY > this.y && mouseY < this.y + this.height; + } + +} \ No newline at end of file diff --git a/src/components/ControlPanel.js b/src/components/ControlPanel.js index 69b51ac..f5f1bbc 100644 --- a/src/components/ControlPanel.js +++ b/src/components/ControlPanel.js @@ -11,15 +11,14 @@ export class ControlPanel { this.boxWidth = 48; this.boxHeight = 48; this.boxSpacing = 8; - this.contents = Array(numBoxes).fill(null); - this.empty = new Arrow('empty'); + this.contents = Array(numBoxes).fill(null).map(() => new ClickableArrow('empty', true)); + this.empty = new ClickableArrow('empty', true); this.fontSize = 20; this.gap = this.fontSize; } setContents(contents){ this.contents = contents; - console.log(this.contents); } updateBox(index, content) { diff --git a/src/main.js b/src/main.js index 9b5d8ce..f8ef094 100644 --- a/src/main.js +++ b/src/main.js @@ -8,6 +8,7 @@ let mgr; function setup(){ createCanvas(windowWidth, windowHeight); + console.log(`window size: ${windowWidth}x${windowHeight}`); textFont('Pixelify Sans', 'sans-serif'); textAlign(CENTER); textSize(128); @@ -21,6 +22,9 @@ function setup(){ function draw(){ background(colors.primary); mgr.draw(); + + // custom event handler, manually called + // mgr.handleEvent('keyPressing'); }; function windowResized() { @@ -28,10 +32,8 @@ function windowResized() { mgr.handleEvent('onResize'); }; - function mousePressed(){ mgr.handleEvent('mousePressed'); - gameScene.handleClick(mouseX, mouseY); }; function preload(){ @@ -43,9 +45,10 @@ function keyPressed(){ mgr.handleEvent('keyPressed'); } + window.setup = setup; window.draw = draw; -// window.windowResized = windowResized; window.mousePressed = mousePressed; window.preload = preload; -window.keyPressed = keyPressed; \ No newline at end of file +window.keyPressed = keyPressed; + diff --git a/src/scenes/gameScene.js b/src/scenes/gameScene.js index 6a2db9f..25dbe10 100644 --- a/src/scenes/gameScene.js +++ b/src/scenes/gameScene.js @@ -8,6 +8,11 @@ import { ControlPanel } from '../components/controlPanel.js'; export default function GameScene() { + const groundHeight = 100; + const worldHeight = windowHeight - groundHeight; + const catSize = 150; + const worldBlockSize = 100; + let cat; let runButton; let blocks; @@ -15,6 +20,22 @@ export default function GameScene() { let loops; let clickArrow; + let clicked; + + let selectedStepIndex = null; + + const buildingBlocks = [ + new ClickableArrow('up', false), + new ClickableArrow('right', false), + ]; + + // changed on user input + let selectedSteps; + let selectedBlock; + + // selectedSteps = ['up', 'right', 'up', 'up', 'right', 'down']; + // selectedSteps = selectedSteps.map((e) => new ClickableArrow(e, true)); + const slots = { blocks: 2, steps: 6, @@ -24,10 +45,7 @@ export default function GameScene() { this.name = "GameScene"; this.setup = () => { - cat = new Cat(width / 6, height - 167.5, 150); - - // test - clickArrow = new ClickableArrow('up', true); + cat = new Cat(width / 4, height - 175 - 2, catSize); runButton = new MyButton({ x: width / 32 * 28.5, @@ -35,9 +53,7 @@ export default function GameScene() { text: "run >>", mode: "CORNER", style: buttonS, - onPress: () => { - console.log("Run button pressed"); - } + onPress: () => cat.run(steps.contents), }); blocks = new ControlPanel({ @@ -46,6 +62,7 @@ export default function GameScene() { y: height / 32, numBoxes: 2 }); + blocks.setContents(buildingBlocks); steps = new ControlPanel({ name: 'steps', @@ -53,21 +70,14 @@ export default function GameScene() { y: height / 32, numBoxes: 6 }); + - loops = new ControlPanel({ - name: 'loop', - x: width / 32 * 7 + 48 * (slots.blocks + slots.steps + 2.75), - y: height / 32, - numBoxes: 4 - }); - - blocks.setContents([ - new Arrow('right'), - // new Arrow('up') - new ClickableArrow('up', true), - ]); - - + // loops = new ControlPanel({ + // name: 'loop', + // x: width / 32 * 7 + 48 * (slots.blocks + slots.steps + 2.75), + // y: height / 32, + // numBoxes: 4 + // }); }; this.draw = () => { @@ -78,22 +88,38 @@ export default function GameScene() { stroke(colors.secondary); strokeWeight(7); textAlign(LEFT, TOP); - text('lvl.3', width / 32, height /32 - 4); + text('lvl.1', width / 32, height /32 - 4); // Ground - rectMode(CENTER); + rectMode(CORNER); fill(colors.secondary); - rect(width / 2, height - 100 / 2, width, 80); + 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(); + // loops.draw(); // Sprite - camera.on(); cat.draw(); - camera.off(); }; this.onResize = () => { @@ -104,20 +130,45 @@ export default function GameScene() { } this.keyPressed = () => { + console.log(`key: ${key}, keyCode: ${keyCode}`); + const _key = key; console.log(`key passed: ${_key}`); - if (_key == "ArrowRight") { - cat.keyPressed(_key); + + if (keyCode === ESCAPE) { + // Deselect the currently selected arrow if any + if (selectedStepIndex !== null && steps.contents[selectedStepIndex]) { + steps.contents[selectedStepIndex].selected = false; + } + selectedStepIndex = null; } else { - cat.changeAni(_key); + cat.keyPressed(key); } } - this.handleClick = function(mx, my) { - this.arrows.forEach(arrow => { - if (arrow.clickable) { - arrow.handleClick(mx, my); + this.mousePressed = function() { + console.log(`canvas clicked at ${mouseX}, ${mouseY}`); + + steps.contents.forEach((arrow, index) => { + if (arrow._isMouseOver(mouseX, mouseY)) { + selectedStepIndex = index; + steps.contents.forEach(a => a.selected = false); + arrow.select(); } }); - } + + blocks.contents.forEach((arrow) => { + if (arrow._isMouseOver(mouseX, mouseY)) { + // console.log("Clicked arrow", arrow.direction); + if (selectedStepIndex !== null) { + const selectedArrow = steps.contents[selectedStepIndex]; + selectedArrow.set(arrow.direction); + selectedArrow.clickable = true; + selectedArrow.selected = false; + selectedStepIndex = null; + } + } + }); + + }; } diff --git a/src/scenes/startScene.js b/src/scenes/startScene.js index 63a6e4d..d0f826e 100644 --- a/src/scenes/startScene.js +++ b/src/scenes/startScene.js @@ -1,5 +1,5 @@ import { colors } from '../utils/theme.js'; -import { Cat } from '../cat.js'; +import { Cat } from '../_cat.js'; import { buttonL } from '../utils/theme.js'; import GameScene from './gameScene.js'; diff --git a/src/style.css b/src/style.css index 498990a..a4855c1 100644 --- a/src/style.css +++ b/src/style.css @@ -6,9 +6,9 @@ body { } body { - color: #333; - margin: 0; - padding: 8px; + /* color: #333; + margin: 0; */ + /* padding: 8px; */ box-sizing: border-box; /* font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif; */ diff --git a/src/utils/world_1.js b/src/utils/world_1.js new file mode 100644 index 0000000..e69de29