Skip to content

Commit

Permalink
feat: Add gravity and player
Browse files Browse the repository at this point in the history
  • Loading branch information
johnedvard committed Aug 16, 2022
1 parent 8e0f5fd commit 54f50cb
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 43 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.parcel-cache/
dist/
node_modules/
node_modules/
parcel-bundle-reports/
.DS_Store
2 changes: 1 addition & 1 deletion src/BoneLink.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Link between two PointMasses
*/
export class BoneLink {
restingDistance = 10;
restingDistance = 30;
stiffness = 1;
pointMassA;
pointMassB;
Expand Down
51 changes: 14 additions & 37 deletions src/Game.js
Original file line number Diff line number Diff line change
@@ -1,56 +1,33 @@
import { initPointer, onPointer, init, getPointer, GameLoop } from 'kontra';
import { PointMass } from './PointMass';
import { init, initPointer, initInput, GameLoop, onPointer } from 'kontra';
import { Player } from './Player';

export class Game {
rope = []; // list of pointmasses
canvas;
context;
player;
constructor() {
const game = this;
let { canvas, context } = init();
this.canvas = canvas;
this.context = context;
initPointer();
this.createTestRope();
initInput();
this.createPlayer();

let loop = GameLoop({
update: function () {
game.updateTestRope();
game.dragTestRope();
game.player.update();
},
render: function () {
game.renderTestRope(context);
game.player.render(context);
},
});
loop.start(); // start the game
this.addPointerListeners();
}

dragTestRope() {
if (this.isDragging && this.rope.length) {
const pointer = getPointer();
const acnhorPoint = this.rope[0];
acnhorPoint.setPos(pointer.x, pointer.y);
}
}

updateTestRope() {
this.rope.forEach((p) => {
p.update();
});
}

renderTestRope(ctx) {
this.rope.forEach((p) => {
p.render(ctx);
});
}

createTestRope() {
const anchor = new PointMass(0, 0, true);
this.rope.push(anchor);
for (let i = 1; i < 10; i++) {
const p1 = this.rope[this.rope.length - 1];
const p2 = new PointMass(i * 10, i * 10);
p1.attachTo(p2);
this.rope.push(p2);
}
console.log('created rope', this.rope);
createPlayer() {
this.player = new Player(40, 40, this);
}

addPointerListeners() {
Expand Down
92 changes: 92 additions & 0 deletions src/Player.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { getPointer } from 'kontra';
import { PlayerControls } from './PlayerControls';
import { PointMass } from './PointMass';

export class Player {
x;
y;
game;
rope = []; // list of pointmasses
pointMass;
playerControls;

constructor(x, y, game) {
this.x = x;
this.y = y;
this.game = game;
this.pointMass = new PointMass(x, y, { game, mass: 2 });
this.createTestRope();
this.playerControls = new PlayerControls(this);
}

hasRope() {
return !!this.rope.length;
}
removeRope() {
this.pointMass.removeLink();
this.rope.length = 0;
}
shootRope() {
this.createTestRope();
}
updateTestRope() {
this.rope.forEach((p) => {
p.update();
});
}

renderTestRope(ctx) {
this.rope.forEach((p) => {
p.render(ctx);
});
}

createTestRope() {
const anchor = new PointMass(this.game.canvas.width / 2, 100, {
isAnchor: true,
game: this.game,
});
this.rope.push(anchor);
for (let i = 1; i < 7; i++) {
const p1 = this.rope[this.rope.length - 1];
const p2 = new PointMass(i * 10, i * 10, { game: this.game });
p1.attachTo(p2);
this.rope.push(p2);
}
this.pointMass.attachTo(this.rope[this.rope.length - 1]);
this.rope.push(this.pointMass);
}

dragTestRope() {
if (this.game.isDragging && this.rope.length) {
const pointer = getPointer();
const acnhorPoint = this.rope[this.rope.length - 1];
acnhorPoint.setPos(pointer.x, pointer.y);
}
}

renderPlayer(ctx) {
ctx.lineWidth = 2;

ctx.beginPath();
ctx.arc(this.x, this.y, 5, 0, Math.PI * 2);
ctx.stroke();
}

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

render(ctx) {
this.renderTestRope(ctx);
this.renderPlayer(ctx);
}

update() {
this.x = this.pointMass.x;
this.y = this.pointMass.y;
this.updateTestRope();
this.dragTestRope();
this.playerControls.updateControls();
}
}
32 changes: 32 additions & 0 deletions src/PlayerControls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { keyPressed, onInput } from 'kontra';

export class PlayerControls {
player;
constructor(player) {
this.player = player;
this.initControls();
}

updateControls() {
// TODO (johnedvard) add support for touch gesture and gamepad (if enough space)
if (keyPressed('arrowleft')) {
this.player.applyForce(-1.5, 0);
}
if (keyPressed('arrowright')) {
this.player.applyForce(1.5, 0);
}
}

initControls() {
onInput(['space'], this.toggleRope);
}

toggleRope = (e) => {
// TODO (johnedvard) Maybe use state machine
if (this.player.hasRope()) {
this.player.removeRope();
} else {
this.player.shootRope();
}
};
}
29 changes: 25 additions & 4 deletions src/PointMass.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { BoneLink } from './BoneLink';
import { gravity } from './constants';

export class PointMass {
sprite; // TODO (johnedvard) maybe remove
Expand All @@ -12,13 +13,15 @@ export class PointMass {
anchorX;
anchorY;
mass = 1;
game;

constructor(x, y, isAnchor) {
constructor(x, y, { isAnchor, game, mass }) {
this.game = game;
this.x = x;
this.y = y;
this.lastX = x;
this.lastY = y;
console.log('this', this);
this.mass = mass || 1;
if (isAnchor) {
this.anchorX = x;
this.anchorY = y;
Expand Down Expand Up @@ -46,8 +49,8 @@ export class PointMass {
this.links.push(link);
}

removeLink(link) {
this.links.remove(link);
removeLink() {
this.links.length = 0;
}

render(ctx) {
Expand All @@ -67,6 +70,8 @@ export class PointMass {
this.updatePhysics();
}
updatePhysics() {
this.applyForce(0, this.mass * gravity);

let velX = this.x - this.lastX;
let velY = this.y - this.lastY;

Expand All @@ -92,11 +97,27 @@ export class PointMass {
this.accY = 0;
}

applyForce(fX, fY) {
this.accX += fX / this.mass;
this.accY += fY / this.mass;
}

solveConstraints() {
if (!this.game.canvas) return;
this.links.forEach((link) => {
link.solveConstraint();
});

/* Boundary Constraints */
// These if statements keep the PointMasss within the screen
if (this.y < 1) this.y = 2 * 1 - this.y;
if (this.y > this.game.canvas.height - 1)
this.y = 2 * (this.game.canvas.height - 1) - this.y;

if (this.x < 1) this.x = 2 * 1 - this.x;
if (this.x > this.game.canvas.width - 1)
this.x = 2 * (this.game.canvas.width - 1) - this.x;

/* Other Constraints */
// make sure the PointMass stays in its place if it's pinned
if (this.isAnchor()) {
Expand Down
1 change: 1 addition & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const gravity = 2;

0 comments on commit 54f50cb

Please sign in to comment.