Skip to content

Commit

Permalink
fix: Create new, better, stronger, faster rope
Browse files Browse the repository at this point in the history
  • Loading branch information
johnedvard committed Aug 25, 2022
1 parent 949e891 commit 5f141d7
Show file tree
Hide file tree
Showing 12 changed files with 230 additions and 55 deletions.
3 changes: 3 additions & 0 deletions src/Game.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { init, initPointer, initInput, GameLoop, onPointer, on } from 'kontra';
import { LEVEL_COMPLETE } from './gameEvents';
import { Level } from './Level';
import { playSong } from './sound';
import { setGameHeight, setGameWidth } from './store';

export class Game {
canvas;
Expand All @@ -19,6 +20,8 @@ export class Game {
this.context = context;
initPointer();
initInput();
setGameHeight(canvas.height);
setGameWidth(canvas.width);
this.addPointerListeners();

this.loadLevel(1);
Expand Down
2 changes: 1 addition & 1 deletion src/Level.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export class Level {

update() {
if (!this.isLevelLoaded) return;
this.checkCollisions();
// this.checkCollisions();
this.player.update();
this.saws.forEach((saw) => {
saw.update();
Expand Down
75 changes: 37 additions & 38 deletions src/Player.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { PlayerControls } from './PlayerControls';
import { PointMass } from './PointMass';
import { fgc2, RESTING_DISTANCE } from './constants';
import { ARCADIAN_ADDED, GOAL_COLLISION } from './gameEvents';
import { Rope } from './Rope';

export class Player {
game;
Expand All @@ -25,11 +26,6 @@ export class Player {
const ropeLength = levelData.r;
const startX = levelData.p.x;
const startY = levelData.p.y;
this.pointMass = new PointMass(
startX,
startY + ropeLength * RESTING_DISTANCE,
{ game, mass: 2 }
);
this.createRope({ startX, startY, ropeLength });
this.createSprite();
this.playerControls = new PlayerControls(this);
Expand All @@ -38,16 +34,12 @@ export class Player {

updateRope() {
if (this.hasWon) return;
this.rope.forEach((p) => {
p.update();
});
this.rope.update();
}

renderRope(ctx) {
if (this.hasWon) return;
this.rope.forEach((p) => {
p.render(ctx);
});
this.rope.render(ctx);
}

createSprite() {
Expand All @@ -58,8 +50,8 @@ export class Player {
};
image.onload = () => {
this.sprite = Sprite({
x: this.pointMass.x,
y: this.pointMass.y,
x: this.rope.endNode.x,
y: this.rope.endNode.y,
anchor: { x: 0.5, y: 0.5 },
width: 8,
height: 8,
Expand All @@ -74,8 +66,8 @@ export class Player {
let scaleX = scale;
if (this.isLeft) scaleX = scaleX * -1;
this.headSprite = Sprite({
x: this.pointMass.x - img.width,
y: this.pointMass.y - img.height,
x: this.rope.endNode.x - img.width,
y: this.rope.endNode.y - img.height,
anchor: { x: 0.5, y: 0.5 },
width: 8,
height: 8,
Expand All @@ -86,30 +78,37 @@ export class Player {
}

createRope({ startX, startY, ropeLength }) {
const anchor = new PointMass(startX, startY, {
isAnchor: true,
game: this.game,
this.rope = new Rope({
x: startX,
y: startY,
numNodes: ropeLength,
level: this.game.level,
});
this.rope.push(anchor);
for (let i = 1; i < ropeLength; i++) {
const p1 = this.rope[this.rope.length - 1];
const p2 = new PointMass(startX, i * RESTING_DISTANCE + startY, {
game: this.game,
});
p1.attachTo(p2);
this.rope.push(p2);
}
// make player's pointmass attach to the rope
this.rope[this.rope.length - 1].attachTo(this.pointMass);
this.rope.push(this.pointMass);
// const anchor = new PointMass(startX, startY, {
// isAnchor: true,
// game: this.game,
// });
// this.rope.push(anchor);
// for (let i = 1; i < ropeLength; i++) {
// const p1 = this.rope[this.rope.length - 1];
// const p2 = new PointMass(startX, i * RESTING_DISTANCE + startY, {
// game: this.game,
// });
// p1.attachTo(p2);
// this.rope.push(p2);
// }
// // make player's pointmass attach to the rope
// this.rope[this.rope.length - 1].attachTo(this.pointMass);
// this.rope.push(this.pointMass);
}

// Debug purpose only
dragRope() {
if (this.game.isDragging && this.rope.length) {
const pointer = getPointer();
const acnhorPoint = this.rope[this.rope.length - 1];
acnhorPoint.setPos(pointer.x, pointer.y);
const acnhorPoint = this.rope.nodes[0];
acnhorPoint.pos.x = pointer.x;
acnhorPoint.pos.y = pointer.y;
}
}

Expand All @@ -119,7 +118,7 @@ export class Player {
}

applyForce(fX, fY) {
this.pointMass.applyForce(fX, fY);
this.rope.endNode.applyForce(fX, fY);
}

changePlayerDirection(isLeft) {
Expand Down Expand Up @@ -157,14 +156,14 @@ export class Player {
}

update() {
this.sprite.x = this.pointMass.x;
this.sprite.y = this.pointMass.y;
this.sprite.x = this.rope.endNode.pos.x;
this.sprite.y = this.rope.endNode.pos.y;
this.headSprite.x =
this.pointMass.x -
this.rope.endNode.pos.x -
(this.headImg.width - this.headOffset.x) * Math.sign(this.sprite.scaleX);

this.headSprite.y =
this.pointMass.y - this.headImg.height + +this.headOffset.y;
this.rope.endNode.pos.y - this.headImg.height + +this.headOffset.y;

this.updateRope();
this.dragRope(); // TODO (johnedvard) Only enable in local and beta env
Expand All @@ -174,7 +173,7 @@ export class Player {
climbRope() {
if (!this.rope || this.rope.length < 2) return;

const lastPointMassWithLink = this.rope[this.rope.length - 2];
const lastPointMassWithLink = this.rope.nodes[this.rope.length - 2];
lastPointMassWithLink.reduceRestingDistance(0.1);
if (lastPointMassWithLink.restingDistance <= 0) {
this.reArrangeRope();
Expand Down
6 changes: 3 additions & 3 deletions src/PlayerControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@ export class PlayerControls {
if (this.player.isRopeCut) return;
// TODO (johnedvard) add support for touch gesture and gamepad (if enough space)
if (keyPressed('arrowleft')) {
this.player.applyForce(-1.5, 0);
this.player.applyForce(-2.5, -1);
this.player.changePlayerDirection(true);
}
if (keyPressed('arrowright')) {
this.player.applyForce(1.5, 0);
this.player.applyForce(2.5, -1);
this.player.changePlayerDirection(false);
}
if (keyPressed('arrowup')) {
this.player.climbRope();
}
if (keyPressed('space')) {
this.player.applyForce(0, -6);
this.player.applyForce(0, -10);
}
}

Expand Down
135 changes: 135 additions & 0 deletions src/Rope.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { Vector } from 'kontra';
import { fgc2, gravity, RESTING_DISTANCE } from './constants';
import { gameHeight, gameWidth } from './store';
import { isBoxCollision } from './utils';
import { VerletLink } from './VerletLink';
import { VerletNode } from './VerletNode';

export class Rope {
nodes = [];
links = [];
anchor;
iterations = 20;

constructor({ x, y, numNodes, level }) {
this.level = level;
this.anchor = Vector(x, y);
for (let i = 0; i < numNodes; i++) {
this.nodes.push(
new VerletNode({
x,
y: i * RESTING_DISTANCE + y,
})
);
}
console.log(this.nodes);
for (let i = 0; i < this.nodes.length - 1; i++) {
const n1 = this.nodes[i];
const n2 = this.nodes[i + 1];
this.links.push(new VerletLink(n1, n2));
}
console.log('links', this.links);
}

update() {
this.updateNodes();
for (let i = 0; i < this.iterations; i++) {
this.updateLinks();
this.constrainNodes();
}
}
render(ctx) {
if (!ctx) return;
this.renderRope(ctx);
}
updateNodes() {
this.nodes.forEach((n) => {
if (n === this.nodes[0]) n.pos = this.anchor;
const vxy = n.pos.subtract(n.oldPos);
n.oldPos = n.pos;
n.pos = n.pos.add(vxy).add(Vector(0, gravity));
});
}
constrainNodes() {
this.nodes.forEach((n) => {
if (n === this.nodes[0]) n.pos = this.anchor;
this.handleWallCollision(n);
this.handleBoxCollision(n);
});
}
updateLinks() {
this.links.forEach((l) => {
const dxy = l.n2.pos.subtract(l.n1.pos);
// console.log(l.n1.pos);
const distance = dxy.length();
const diff = RESTING_DISTANCE - distance;
// console.log(distance);
const percent = diff / distance / 2;
const offset = Vector(dxy.x * percent, dxy.y * percent);
l.n1.pos = l.n1.pos.subtract(offset);
l.n2.pos = l.n2.pos.add(offset);
});
}

renderRope(ctx) {
ctx.lineWidth = 4;
ctx.strokeStyle = fgc2;

ctx.beginPath();
this.links.forEach((l) => {
ctx.moveTo(l.n1.x, l.n1.y);
ctx.lineTo(l.n2.x, l.n2.y);
});

ctx.stroke();
}

handleWallCollision(node) {
if (node.x > gameWidth) {
node.pos.x = gameWidth;
}
if (node.x < 0) {
node.pos.x = 0;
}
if (node.y > gameHeight) {
node.pos.y = gameHeight;
}
if (node.y < 0) {
node.pos.y = 0;
}
}
handleBoxCollision(node, vxy) {
if (node === this.nodes[0]) return;
const bricks = this.level.bricks;
bricks.forEach((b) => {
// check left edge
if (isBoxCollision(b, node)) {
Math.max();
const left = Math.abs(node.pos.x - b.x);
const right = Math.abs(node.pos.x - (b.x + b.width));
const top = Math.abs(node.pos.y - b.y);
const bot = Math.abs(node.pos.y - (b.y + b.height));
// hhit left wall
const max = Math.max(left, right, top, bot);

if (max === right) {
node.pos.x = b.x - 5;
} else if (max === left) {
node.pos.x = b.x + b.width + 5;
} else if (max === bot) {
node.pos.y = b.y - 5;
} else if (max === top) {
node.pos.y = b.y + b.height + 5;
}
}
});
}

get length() {
return this.nodes.length;
}

get endNode() {
return this.nodes[this.nodes.length - 1];
}
}
8 changes: 8 additions & 0 deletions src/VerletLink.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export class VerletLink {
n1;
n2;
constructor(n1, n2) {
this.n1 = n1;
this.n2 = n2;
}
}
24 changes: 24 additions & 0 deletions src/VerletNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Vector } from 'kontra';

export class VerletNode {
pos;
oldPos;
width = 5;
height = 5;
constructor({ x, y }) {
this.pos = Vector(x, y);
console.log(this.pos);
this.oldPos = Vector(x, y);
}

get x() {
return this.pos.x;
}
get y() {
return this.pos.y;
}
applyForce(fX, fY) {
this.pos.x += fX;
this.pos.y += fY;
}
}
1 change: 0 additions & 1 deletion src/arcadianApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export async function queryArcadian(id) {
if (xhr.status !== 200) reject(null);
let gender = 'female';
var data = JSON.parse(xhr.response);
console.log('data', data);

const headPart = data.attributes.find(
(trait) => trait.trait_type === 'Head'
Expand Down
2 changes: 1 addition & 1 deletion src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ export const bgc2 = '#555568';
export const fgc = '#a0a08b';
export const fgc2 = '#e9efec';

export const RESTING_DISTANCE = 25;
export const RESTING_DISTANCE = 7;
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { NearConnection } from './near/nearConnection';
const init = () => {
new Game();
initNear();
fetchArcadianHeads();
// fetchArcadianHeads();
};

const initNear = () => {
Expand Down
Loading

0 comments on commit 5f141d7

Please sign in to comment.