Skip to content

Commit 54f50cb

Browse files
committed
feat: Add gravity and player
1 parent 8e0f5fd commit 54f50cb

7 files changed

Lines changed: 168 additions & 43 deletions

File tree

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
.parcel-cache/
22
dist/
3-
node_modules/
3+
node_modules/
4+
parcel-bundle-reports/
5+
.DS_Store

src/BoneLink.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Link between two PointMasses
33
*/
44
export class BoneLink {
5-
restingDistance = 10;
5+
restingDistance = 30;
66
stiffness = 1;
77
pointMassA;
88
pointMassB;

src/Game.js

Lines changed: 14 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,33 @@
1-
import { initPointer, onPointer, init, getPointer, GameLoop } from 'kontra';
2-
import { PointMass } from './PointMass';
1+
import { init, initPointer, initInput, GameLoop, onPointer } from 'kontra';
2+
import { Player } from './Player';
33

44
export class Game {
5-
rope = []; // list of pointmasses
5+
canvas;
6+
context;
7+
player;
68
constructor() {
79
const game = this;
810
let { canvas, context } = init();
11+
this.canvas = canvas;
12+
this.context = context;
913
initPointer();
10-
this.createTestRope();
14+
initInput();
15+
this.createPlayer();
16+
1117
let loop = GameLoop({
1218
update: function () {
13-
game.updateTestRope();
14-
game.dragTestRope();
19+
game.player.update();
1520
},
1621
render: function () {
17-
game.renderTestRope(context);
22+
game.player.render(context);
1823
},
1924
});
2025
loop.start(); // start the game
2126
this.addPointerListeners();
2227
}
2328

24-
dragTestRope() {
25-
if (this.isDragging && this.rope.length) {
26-
const pointer = getPointer();
27-
const acnhorPoint = this.rope[0];
28-
acnhorPoint.setPos(pointer.x, pointer.y);
29-
}
30-
}
31-
32-
updateTestRope() {
33-
this.rope.forEach((p) => {
34-
p.update();
35-
});
36-
}
37-
38-
renderTestRope(ctx) {
39-
this.rope.forEach((p) => {
40-
p.render(ctx);
41-
});
42-
}
43-
44-
createTestRope() {
45-
const anchor = new PointMass(0, 0, true);
46-
this.rope.push(anchor);
47-
for (let i = 1; i < 10; i++) {
48-
const p1 = this.rope[this.rope.length - 1];
49-
const p2 = new PointMass(i * 10, i * 10);
50-
p1.attachTo(p2);
51-
this.rope.push(p2);
52-
}
53-
console.log('created rope', this.rope);
29+
createPlayer() {
30+
this.player = new Player(40, 40, this);
5431
}
5532

5633
addPointerListeners() {

src/Player.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import { getPointer } from 'kontra';
2+
import { PlayerControls } from './PlayerControls';
3+
import { PointMass } from './PointMass';
4+
5+
export class Player {
6+
x;
7+
y;
8+
game;
9+
rope = []; // list of pointmasses
10+
pointMass;
11+
playerControls;
12+
13+
constructor(x, y, game) {
14+
this.x = x;
15+
this.y = y;
16+
this.game = game;
17+
this.pointMass = new PointMass(x, y, { game, mass: 2 });
18+
this.createTestRope();
19+
this.playerControls = new PlayerControls(this);
20+
}
21+
22+
hasRope() {
23+
return !!this.rope.length;
24+
}
25+
removeRope() {
26+
this.pointMass.removeLink();
27+
this.rope.length = 0;
28+
}
29+
shootRope() {
30+
this.createTestRope();
31+
}
32+
updateTestRope() {
33+
this.rope.forEach((p) => {
34+
p.update();
35+
});
36+
}
37+
38+
renderTestRope(ctx) {
39+
this.rope.forEach((p) => {
40+
p.render(ctx);
41+
});
42+
}
43+
44+
createTestRope() {
45+
const anchor = new PointMass(this.game.canvas.width / 2, 100, {
46+
isAnchor: true,
47+
game: this.game,
48+
});
49+
this.rope.push(anchor);
50+
for (let i = 1; i < 7; i++) {
51+
const p1 = this.rope[this.rope.length - 1];
52+
const p2 = new PointMass(i * 10, i * 10, { game: this.game });
53+
p1.attachTo(p2);
54+
this.rope.push(p2);
55+
}
56+
this.pointMass.attachTo(this.rope[this.rope.length - 1]);
57+
this.rope.push(this.pointMass);
58+
}
59+
60+
dragTestRope() {
61+
if (this.game.isDragging && this.rope.length) {
62+
const pointer = getPointer();
63+
const acnhorPoint = this.rope[this.rope.length - 1];
64+
acnhorPoint.setPos(pointer.x, pointer.y);
65+
}
66+
}
67+
68+
renderPlayer(ctx) {
69+
ctx.lineWidth = 2;
70+
71+
ctx.beginPath();
72+
ctx.arc(this.x, this.y, 5, 0, Math.PI * 2);
73+
ctx.stroke();
74+
}
75+
76+
applyForce(fX, fY) {
77+
this.pointMass.applyForce(fX, fY);
78+
}
79+
80+
render(ctx) {
81+
this.renderTestRope(ctx);
82+
this.renderPlayer(ctx);
83+
}
84+
85+
update() {
86+
this.x = this.pointMass.x;
87+
this.y = this.pointMass.y;
88+
this.updateTestRope();
89+
this.dragTestRope();
90+
this.playerControls.updateControls();
91+
}
92+
}

src/PlayerControls.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { keyPressed, onInput } from 'kontra';
2+
3+
export class PlayerControls {
4+
player;
5+
constructor(player) {
6+
this.player = player;
7+
this.initControls();
8+
}
9+
10+
updateControls() {
11+
// TODO (johnedvard) add support for touch gesture and gamepad (if enough space)
12+
if (keyPressed('arrowleft')) {
13+
this.player.applyForce(-1.5, 0);
14+
}
15+
if (keyPressed('arrowright')) {
16+
this.player.applyForce(1.5, 0);
17+
}
18+
}
19+
20+
initControls() {
21+
onInput(['space'], this.toggleRope);
22+
}
23+
24+
toggleRope = (e) => {
25+
// TODO (johnedvard) Maybe use state machine
26+
if (this.player.hasRope()) {
27+
this.player.removeRope();
28+
} else {
29+
this.player.shootRope();
30+
}
31+
};
32+
}

src/PointMass.js

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { BoneLink } from './BoneLink';
2+
import { gravity } from './constants';
23

34
export class PointMass {
45
sprite; // TODO (johnedvard) maybe remove
@@ -12,13 +13,15 @@ export class PointMass {
1213
anchorX;
1314
anchorY;
1415
mass = 1;
16+
game;
1517

16-
constructor(x, y, isAnchor) {
18+
constructor(x, y, { isAnchor, game, mass }) {
19+
this.game = game;
1720
this.x = x;
1821
this.y = y;
1922
this.lastX = x;
2023
this.lastY = y;
21-
console.log('this', this);
24+
this.mass = mass || 1;
2225
if (isAnchor) {
2326
this.anchorX = x;
2427
this.anchorY = y;
@@ -46,8 +49,8 @@ export class PointMass {
4649
this.links.push(link);
4750
}
4851

49-
removeLink(link) {
50-
this.links.remove(link);
52+
removeLink() {
53+
this.links.length = 0;
5154
}
5255

5356
render(ctx) {
@@ -67,6 +70,8 @@ export class PointMass {
6770
this.updatePhysics();
6871
}
6972
updatePhysics() {
73+
this.applyForce(0, this.mass * gravity);
74+
7075
let velX = this.x - this.lastX;
7176
let velY = this.y - this.lastY;
7277

@@ -92,11 +97,27 @@ export class PointMass {
9297
this.accY = 0;
9398
}
9499

100+
applyForce(fX, fY) {
101+
this.accX += fX / this.mass;
102+
this.accY += fY / this.mass;
103+
}
104+
95105
solveConstraints() {
106+
if (!this.game.canvas) return;
96107
this.links.forEach((link) => {
97108
link.solveConstraint();
98109
});
99110

111+
/* Boundary Constraints */
112+
// These if statements keep the PointMasss within the screen
113+
if (this.y < 1) this.y = 2 * 1 - this.y;
114+
if (this.y > this.game.canvas.height - 1)
115+
this.y = 2 * (this.game.canvas.height - 1) - this.y;
116+
117+
if (this.x < 1) this.x = 2 * 1 - this.x;
118+
if (this.x > this.game.canvas.width - 1)
119+
this.x = 2 * (this.game.canvas.width - 1) - this.x;
120+
100121
/* Other Constraints */
101122
// make sure the PointMass stays in its place if it's pinned
102123
if (this.isAnchor()) {

src/constants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const gravity = 2;

0 commit comments

Comments
 (0)