Skip to content

Commit ddaa957

Browse files
committed
feat: fixtures and shapes components
1 parent 3e31317 commit ddaa957

25 files changed

+650
-240
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ A KaPlay plugin that integrates Planck, while keeping the simple/fun API of KaPl
55
## Installation
66

77
```shell
8-
npm i planck kaplanck
8+
npm i kaplanck
99
```
1010

1111
## Usage

index.html

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
<!DOCTYPE html>
1+
<!doctype html>
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8" />
55
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7-
<title>KaPlank</title>
7+
<title>KaPlanck</title>
88
</head>
99
<body style="overflow: hidden">
1010
<script type="module" src="/src/main.ts"></script>

package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@
6565
"vite-plugin-dts": "^4.3.0"
6666
},
6767
"peerDependencies": {
68-
"kaplay": "^3001.0.2",
68+
"kaplay": "^3001.0.2"
69+
},
70+
"dependencies": {
6971
"planck": "^1.0.6"
7072
},
7173
"lint-staged": {
@@ -74,4 +76,4 @@
7476
"npm run lint"
7577
]
7678
}
77-
}
79+
}

src/examples/8-Ball.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ import type {
3333
KPFixtureComp,
3434
KPPosComp,
3535
} from "../lib";
36-
import type { KAPLANCKCtx } from "../types";
37-
import addScenesButtons from "./shared";
36+
import { addScenesButtons, type KAPLANCKCtx } from "./shared";
3837

3938
interface UserData {
4039
gameObj: GameObj;
@@ -107,6 +106,7 @@ const eightBallScene = (k: KAPLANCKCtx) => () => {
107106
worldContainer.add([
108107
k.color(40, 40, 40),
109108
k.kpPos(k.kpCenter()),
109+
k.kpRotate(),
110110
k.kpPolygonShape({
111111
vertices: railV,
112112
draw: true,
@@ -117,6 +117,7 @@ const eightBallScene = (k: KAPLANCKCtx) => () => {
117117
worldContainer.add([
118118
k.color(40, 40, 40),
119119
k.kpPos(k.kpCenter()),
120+
k.kpRotate(),
120121
k.kpPolygonShape({
121122
vertices: mirror(railV, -1, 1),
122123
draw: true,
@@ -129,6 +130,7 @@ const eightBallScene = (k: KAPLANCKCtx) => () => {
129130
worldContainer.add([
130131
k.color(40, 40, 40),
131132
k.kpPos(k.kpCenter()),
133+
k.kpRotate(),
132134
k.kpPolygonShape({
133135
vertices: railH,
134136
draw: true,
@@ -139,6 +141,7 @@ const eightBallScene = (k: KAPLANCKCtx) => () => {
139141
worldContainer.add([
140142
k.color(40, 40, 40),
141143
k.kpPos(k.kpCenter()),
144+
k.kpRotate(),
142145
k.kpPolygonShape({
143146
vertices: mirror(railH, -1, 1),
144147
draw: true,
@@ -149,6 +152,7 @@ const eightBallScene = (k: KAPLANCKCtx) => () => {
149152
worldContainer.add([
150153
k.color(40, 40, 40),
151154
k.kpPos(k.kpCenter()),
155+
k.kpRotate(),
152156
k.kpPolygonShape({
153157
vertices: mirror(railH, 1, -1),
154158
draw: true,
@@ -159,6 +163,7 @@ const eightBallScene = (k: KAPLANCKCtx) => () => {
159163
worldContainer.add([
160164
k.color(40, 40, 40),
161165
k.kpPos(k.kpCenter()),
166+
k.kpRotate(),
162167
k.kpPolygonShape({
163168
vertices: mirror(railH, -1, -1),
164169
draw: true,
@@ -171,13 +176,15 @@ const eightBallScene = (k: KAPLANCKCtx) => () => {
171176
worldContainer.add([
172177
k.color(60, 60, 60),
173178
k.kpPos(k.kpCenter().sub({ x: 0, y: -height * 0.5 - pocketRadius * 1.5 })),
179+
k.kpRotate(),
174180
k.kpCircleShape({ radius: pocketRadius, draw: true }),
175181
k.kpBody(),
176182
k.kpFixture(pocketFixDef),
177183
]);
178184
worldContainer.add([
179185
k.color(60, 60, 60),
180186
k.kpPos(k.kpCenter().sub({ x: 0, y: height * 0.5 + pocketRadius * 1.5 })),
187+
k.kpRotate(),
181188
k.kpCircleShape({ radius: pocketRadius, draw: true }),
182189
k.kpBody(),
183190
k.kpFixture(pocketFixDef),
@@ -190,6 +197,7 @@ const eightBallScene = (k: KAPLANCKCtx) => () => {
190197
y: height * 0.5 + pocketRadius * 0.7,
191198
}),
192199
),
200+
k.kpRotate(),
193201
k.kpCircleShape({ radius: pocketRadius, draw: true }),
194202
k.kpBody(),
195203
k.kpFixture(pocketFixDef),
@@ -202,6 +210,7 @@ const eightBallScene = (k: KAPLANCKCtx) => () => {
202210
y: height * 0.5 + pocketRadius * 0.7,
203211
}),
204212
),
213+
k.kpRotate(),
205214
k.kpCircleShape({ radius: pocketRadius, draw: true }),
206215
k.kpBody(),
207216
k.kpFixture(pocketFixDef),
@@ -214,6 +223,7 @@ const eightBallScene = (k: KAPLANCKCtx) => () => {
214223
y: -height * 0.5 - pocketRadius * 0.7,
215224
}),
216225
),
226+
k.kpRotate(),
217227
k.kpCircleShape({ radius: pocketRadius, draw: true }),
218228
k.kpBody(),
219229
k.kpFixture(pocketFixDef),
@@ -226,6 +236,7 @@ const eightBallScene = (k: KAPLANCKCtx) => () => {
226236
y: -height * 0.5 - pocketRadius * 0.7,
227237
}),
228238
),
239+
k.kpRotate(),
229240
k.kpCircleShape({ radius: pocketRadius, draw: true }),
230241
k.kpBody(),
231242
k.kpFixture(pocketFixDef),
@@ -254,6 +265,7 @@ const eightBallScene = (k: KAPLANCKCtx) => () => {
254265
const ball = worldContainer.add([
255266
k.color(color[0], color[1], color[2]),
256267
k.kpPos(k.kpCenter().add(balls[i])),
268+
k.kpRotate(),
257269
k.kpCircleShape({ radius: ballRadius, draw: true }),
258270
k.kpBody(ballBodyDef),
259271
k.kpFixture(ballFixDef),

src/examples/AddPair.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@
2121
* SOFTWARE.
2222
*/
2323

24-
import type { KAPLANCKCtx } from "../types";
25-
import addScenesButtons from "./shared";
24+
import { addScenesButtons, type KAPLANCKCtx } from "./shared";
2625

2726
const addPairScene = (k: KAPLANCKCtx) => () => {
2827
const scene = k.add([]);
@@ -34,6 +33,7 @@ const addPairScene = (k: KAPLANCKCtx) => () => {
3433
k.kpPos(
3534
k.kpCenter().add({ x: Math.random() * -6, y: Math.random() * 2 - 1 }),
3635
),
36+
k.kpRotate(),
3737
k.kpCircleShape({ radius: 0.1, draw: true }),
3838
k.kpBody({ type: "dynamic" }),
3939
k.kpFixture(),
@@ -42,6 +42,7 @@ const addPairScene = (k: KAPLANCKCtx) => () => {
4242

4343
const box = worldContainer.add([
4444
k.kpPos(k.kpCenter().add({ x: -40, y: 0 })),
45+
k.kpRotate(),
4546
k.kpBoxShape({ halfWidth: 1.5, halfHeight: 1.5, draw: true }),
4647
k.kpBody({ type: "dynamic", bullet: true }),
4748
k.kpFixture({ density: 1 }),

src/examples/ApplyForce.ts

+186
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
/*
2+
* MIT License
3+
* Copyright (c) 2019 Erin Catto
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in all
13+
* copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
* SOFTWARE.
22+
*/
23+
24+
import { FrictionJoint, Transform, Vec2 } from "planck";
25+
import { KPFixtureDef } from "../lib";
26+
import { addScenesButtons, type KAPLANCKCtx } from "./shared";
27+
28+
const applyForceScene = (k: KAPLANCKCtx) => () => {
29+
const scene = k.add([]);
30+
const worldContainer = scene.add([k.kpWorld()]);
31+
32+
// ground
33+
const wallFixtureDef: KPFixtureDef = {
34+
density: 0,
35+
restitution: 0.4,
36+
};
37+
38+
const ground = worldContainer.add([
39+
k.kpPos(k.kpCenter()),
40+
k.kpRotate(),
41+
k.kpShapes([
42+
{
43+
type: "edge",
44+
opt: {
45+
v1: { x: -20, y: -20 },
46+
v2: { x: -20, y: 20 },
47+
draw: true,
48+
},
49+
},
50+
{
51+
type: "edge",
52+
opt: {
53+
v1: { x: 20, y: -20 },
54+
v2: { x: 20, y: 20 },
55+
draw: true,
56+
},
57+
},
58+
{
59+
type: "edge",
60+
opt: {
61+
v1: { x: -20, y: 20 },
62+
v2: { x: 20, y: 20 },
63+
draw: true,
64+
},
65+
},
66+
{
67+
type: "edge",
68+
opt: {
69+
v1: { x: -20, y: -20 },
70+
v2: { x: 20, y: -20 },
71+
draw: true,
72+
},
73+
},
74+
]),
75+
k.kpBody(),
76+
k.kpFixtures([
77+
{ ...wallFixtureDef },
78+
{ ...wallFixtureDef },
79+
{ ...wallFixtureDef },
80+
{ ...wallFixtureDef },
81+
]),
82+
]);
83+
84+
const xf1 = new Transform();
85+
const xf2 = new Transform();
86+
87+
xf1.q.set(0.3524 * Math.PI);
88+
xf1.p.set(xf1.q.getXAxis());
89+
xf2.q.set(-0.3524 * Math.PI);
90+
xf2.p.set(Vec2.neg(xf2.q.getXAxis()));
91+
92+
const jet = worldContainer.add([
93+
k.kpPos(k.kpCenter().add({ x: 0, y: 18 })),
94+
k.kpRotate(),
95+
k.kpShapes([
96+
{
97+
type: "polygon",
98+
opt: {
99+
vertices: [
100+
new Vec2(-1.0, 0.0),
101+
new Vec2(1.0, 0.0),
102+
new Vec2(0.0, 0.5),
103+
].map((v) => Transform.mul(xf1, v)),
104+
draw: true,
105+
},
106+
},
107+
{
108+
type: "polygon",
109+
opt: {
110+
vertices: [
111+
new Vec2(-1.0, 0.0),
112+
new Vec2(1.0, 0.0),
113+
new Vec2(0.0, 0.5),
114+
].map((v) => Transform.mul(xf2, v)),
115+
draw: true,
116+
},
117+
},
118+
]),
119+
k.kpBody({
120+
type: "dynamic",
121+
angularDamping: 2,
122+
linearDamping: 0.5,
123+
allowSleep: false,
124+
}),
125+
k.kpFixtures([{ density: 2 }, { density: 2 }]),
126+
]);
127+
128+
if (!ground.body) return;
129+
130+
const boxFixtureDef: KPFixtureDef = {
131+
density: 1,
132+
friction: 0.3,
133+
};
134+
135+
for (let i = 0; i < 10; ++i) {
136+
const box = worldContainer.add([
137+
k.kpPos(k.kpCenter().add({ x: 0, y: 15 - 1.54 * i })),
138+
k.kpRotate(),
139+
k.kpBoxShape({ halfWidth: 0.5, halfHeight: 0.5, draw: true }),
140+
k.kpBody({ type: "dynamic" }),
141+
k.kpFixture(boxFixtureDef),
142+
]);
143+
144+
if (!box.body) return;
145+
146+
const gravity = 10;
147+
const I = box.body.getInertia();
148+
const mass = box.body.getMass();
149+
const radius = Math.sqrt((2 * I) / mass);
150+
151+
// TODO: create a component so that the joint can be drawn/visualized on debug mode
152+
worldContainer.world.createJoint(
153+
new FrictionJoint(
154+
{
155+
collideConnected: true,
156+
maxForce: mass * gravity,
157+
maxTorque: mass * radius * gravity,
158+
},
159+
ground.body,
160+
box.body,
161+
Vec2.zero(),
162+
),
163+
);
164+
}
165+
166+
scene.onUpdate(() => {
167+
if (k.isKeyDown(["right", "a"]) && !k.isKeyDown(["left", "d"])) {
168+
jet.body?.applyAngularImpulse(0.2, true);
169+
} else if (k.isKeyDown(["left", "d"]) && !k.isKeyDown(["right", "a"])) {
170+
jet.body?.applyAngularImpulse(-0.2, true);
171+
}
172+
173+
if (k.isKeyDown(["up", "w"])) {
174+
if (!jet.body) return;
175+
176+
const f = jet.body.getWorldVector(new Vec2(0, -1));
177+
const p = jet.body.getWorldPoint(new Vec2(0, 2));
178+
179+
jet.body?.applyLinearImpulse(f, p, true);
180+
}
181+
});
182+
183+
addScenesButtons(k, scene);
184+
};
185+
186+
export default applyForceScene;

src/examples/Sample.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import type { KPShapeComp, KPWorldComp } from "../lib";
22

33
import type { GameObj } from "kaplay";
44
import { Vec2 } from "planck";
5-
import type { KAPLANCKCtx } from "../types";
6-
import addScenesButtons from "./shared";
5+
import { addScenesButtons, type KAPLANCKCtx } from "./shared";
76

87
const sampleScene = (k: KAPLANCKCtx) => () => {
98
const scene = k.add([]);
@@ -144,6 +143,7 @@ function addShape(
144143
worldContainer.add([
145144
k.color(color.r, color.g, color.b),
146145
k.kpPos(k.kpCenter().sub({ x: k.rand(-10, 10), y: k.rand(10, 15) })),
146+
k.kpRotate(),
147147
comp,
148148
k.kpBody({ type: "dynamic" }),
149149
k.kpFixture({ density: 1, friction: 0.3 }),

src/examples/shared.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import type { GameObj, KAPLAYCtx } from "kaplay";
22

3-
export default function addScenesButtons(k: KAPLAYCtx, scene: GameObj) {
3+
import type KaPlanckPlugin from "../lib";
4+
5+
export type KAPLANCKCtx = KAPLAYCtx & KaPlanckPlugin;
6+
7+
export function addScenesButtons(k: KAPLAYCtx, scene: GameObj) {
48
const scenes = Object.keys(k._k.game.scenes);
59
const spacing = 10;
610
const width = 200;

0 commit comments

Comments
 (0)