typescript
This commit is contained in:
parent
0fa8b899d4
commit
03d9a398e9
@ -8,6 +8,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/main.js"></script>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
183
main.js
183
main.js
@ -1,183 +0,0 @@
|
||||
import * as PIXI from 'pixi.js';
|
||||
import * as p2 from 'p2'
|
||||
import './style.css'
|
||||
|
||||
function randomInt(max) {
|
||||
return Math.floor(Math.random() * (max + 1))
|
||||
}
|
||||
|
||||
const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
|
||||
const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)
|
||||
|
||||
let app = new PIXI.Application({
|
||||
width: vw,
|
||||
height: vh,
|
||||
antialias: true,
|
||||
backgroundColor: 0xffffff
|
||||
});
|
||||
document.body.appendChild(app.view);
|
||||
|
||||
|
||||
class PhysicsObject {
|
||||
body;
|
||||
shape;
|
||||
graphics;
|
||||
parentContainer;
|
||||
|
||||
constructor(body, shape, graphics) {
|
||||
this.body = body;
|
||||
this.shape = shape;
|
||||
this.graphics = graphics;
|
||||
|
||||
this.body.addShape(this.shape);
|
||||
|
||||
this.graphics.interactive = true;
|
||||
this.graphics.on('click', (e) => {
|
||||
this.body.applyImpulse([0, 25]);
|
||||
})
|
||||
}
|
||||
|
||||
assign(world, container) {
|
||||
world.addBody(this.body);
|
||||
container.addChild(this.graphics);
|
||||
}
|
||||
|
||||
update() {
|
||||
// Transfer positions of the physics objects to Pixi.js
|
||||
this.graphics.position.x = this.body.position[0];
|
||||
this.graphics.position.y = this.body.position[1];
|
||||
this.graphics.rotation = this.body.angle;
|
||||
}
|
||||
}
|
||||
|
||||
let worldObjects = [];
|
||||
|
||||
let zoom = 15;
|
||||
let world = new p2.World({ gravity: [0, -9.81] });
|
||||
// Add a plane
|
||||
|
||||
// let planeShape = new p2.Plane();
|
||||
// let planeBody = new p2.Body({
|
||||
// position: [0, -2]
|
||||
// });
|
||||
// planeBody.addShape(planeShape);
|
||||
// world.addBody(planeBody);
|
||||
|
||||
let container = new PIXI.Container();
|
||||
container.interactive = true;
|
||||
container.position.x = app.renderer.width / 2; // center at origin
|
||||
container.position.y = app.renderer.height / 2 + 50;
|
||||
container.scale.x = zoom; // zoom in
|
||||
container.scale.y = -zoom; // Note: we flip the y axis to make "up" the physics "up"
|
||||
container.on('mousedown', e => {
|
||||
console.log(e.data.global.x, e.data.global.y)
|
||||
})
|
||||
app.stage.addChild(container);
|
||||
|
||||
// Add a box
|
||||
let box = new PhysicsObject(
|
||||
new p2.Body({
|
||||
mass: 1,
|
||||
position: [0, 2],
|
||||
angularVelocity: 1
|
||||
}),
|
||||
new p2.Box({
|
||||
width: 2,
|
||||
height: 1
|
||||
}),
|
||||
new PIXI.Graphics()
|
||||
)
|
||||
box.graphics.beginFill(randomInt(0xffffff));
|
||||
box.graphics.drawRect(
|
||||
-box.shape.width / 2, -box.shape.height / 2,
|
||||
box.shape.width, box.shape.height);
|
||||
box.graphics.endFill();
|
||||
|
||||
box.assign(world, container);
|
||||
worldObjects.push(box);
|
||||
|
||||
let box2 = new PhysicsObject(
|
||||
new p2.Body({
|
||||
mass: 2,
|
||||
position: [-1, 3],
|
||||
angularVelocity: 0
|
||||
}),
|
||||
new p2.Box({
|
||||
width: 2,
|
||||
height: 1
|
||||
}),
|
||||
new PIXI.Graphics()
|
||||
)
|
||||
box2.graphics.beginFill(0x00ff00);
|
||||
box2.graphics.drawRect(
|
||||
-box.shape.width / 2, -box.shape.height / 2,
|
||||
box.shape.width, box.shape.height);
|
||||
box2.graphics.endFill();
|
||||
|
||||
box2.assign(world, container);
|
||||
worldObjects.push(box2);
|
||||
|
||||
|
||||
// static ground box
|
||||
let box3 = new PhysicsObject(
|
||||
new p2.Body({
|
||||
mass: 0,
|
||||
position: [0, -2]
|
||||
}),
|
||||
new p2.Box({
|
||||
width: 50,
|
||||
height: 0.2
|
||||
}),
|
||||
new PIXI.Graphics()
|
||||
)
|
||||
box3.graphics.beginFill(0x666666);
|
||||
box3.graphics.drawRect(
|
||||
-box3.shape.width / 2, -box3.shape.height / 2,
|
||||
box3.shape.width, box3.shape.height);
|
||||
box3.graphics.endFill();
|
||||
|
||||
box3.assign(world, container);
|
||||
worldObjects.push(box3);
|
||||
|
||||
app.ticker.add((delta) => {
|
||||
world.step(1 / 60);
|
||||
worldObjects.forEach(o => o.update());
|
||||
});
|
||||
|
||||
function randomBox() {
|
||||
let width = 1 + Math.random() * 3;
|
||||
let height = 1 + Math.random() * 3;
|
||||
// Add a box
|
||||
let box = new PhysicsObject(
|
||||
new p2.Body({
|
||||
mass: 0.5 * Math.sqrt(width * height),
|
||||
position: [randomInt(25) - 12.5, randomInt(10) + 5],
|
||||
angularVelocity: 0
|
||||
}),
|
||||
new p2.Box({
|
||||
width: width,
|
||||
height: height
|
||||
}),
|
||||
new PIXI.Graphics()
|
||||
)
|
||||
box.graphics.beginFill(randomInt(0xffffff));
|
||||
box.graphics.drawRect(
|
||||
-box.shape.width / 2, -box.shape.height / 2,
|
||||
box.shape.width, box.shape.height);
|
||||
box.graphics.endFill();
|
||||
|
||||
box.assign(world, container);
|
||||
worldObjects.push(box);
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
function doZoom(e) {
|
||||
zoom += -e.deltaY * 0.05;
|
||||
zoom = Math.min(Math.max(1, zoom), 100)
|
||||
container.scale.x = zoom; // zoom in
|
||||
container.scale.y = -zoom; // Note: we flip the y axis to make "up" the physics
|
||||
}
|
||||
window.addEventListener('wheel', doZoom)
|
||||
window.addEventListener('click', randomBox)
|
||||
window.addEventListener('touchstart', randomBox)
|
11
package.json
11
package.json
@ -1,15 +1,22 @@
|
||||
{
|
||||
"name": "pixi",
|
||||
"name": "physik-piet",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"build": "tsc && vite build",
|
||||
"serve": "vite preview"
|
||||
},
|
||||
"prettier": {
|
||||
"trailingComma": "none",
|
||||
"semi": false
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/p2": "^0.7.39",
|
||||
"typescript": "^4.3.2",
|
||||
"vite": "^2.5.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/offscreencanvas": "^2019.6.3",
|
||||
"p2": "^0.7.1",
|
||||
"pixi.js": "^6.1.2"
|
||||
}
|
||||
|
52
src/lib/PhysicsObject.ts
Normal file
52
src/lib/PhysicsObject.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import * as PIXI from "pixi.js"
|
||||
import * as p2 from "p2"
|
||||
|
||||
export class PhysicsObject {
|
||||
body
|
||||
shape
|
||||
graphics
|
||||
alive = true
|
||||
world?: p2.World
|
||||
|
||||
constructor(body: p2.Body, shape: p2.Box, graphics: PIXI.Graphics) {
|
||||
this.body = body
|
||||
this.shape = shape
|
||||
this.graphics = graphics
|
||||
|
||||
this.body.addShape(this.shape)
|
||||
|
||||
this.graphics.interactive = true
|
||||
this.graphics.on("click", (_e) => {
|
||||
this.body.applyImpulse([0, 25])
|
||||
})
|
||||
}
|
||||
|
||||
assign(world: p2.World, container: PIXI.Container) {
|
||||
this.world = world
|
||||
world.addBody(this.body)
|
||||
container.addChild(this.graphics)
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.graphics.parent.removeChild(this.graphics)
|
||||
this.graphics.destroy()
|
||||
this.world!.removeBody(this.body)
|
||||
|
||||
this.alive = false
|
||||
}
|
||||
|
||||
update() {
|
||||
if (!this.alive) {
|
||||
return
|
||||
}
|
||||
|
||||
// Transfer positions of the physics objects to Pixi.js
|
||||
this.graphics.position.x = this.body.position[0]
|
||||
this.graphics.position.y = this.body.position[1]
|
||||
this.graphics.rotation = this.body.angle
|
||||
|
||||
if (this.graphics.position.y < -25) {
|
||||
this.destroy()
|
||||
}
|
||||
}
|
||||
}
|
141
src/main.ts
Normal file
141
src/main.ts
Normal file
@ -0,0 +1,141 @@
|
||||
import * as PIXI from "pixi.js"
|
||||
import * as p2 from "p2"
|
||||
import "./style.css"
|
||||
import { PhysicsObject } from "./lib/PhysicsObject"
|
||||
|
||||
const vw = Math.max(
|
||||
document.documentElement.clientWidth || 0,
|
||||
window.innerWidth || 0
|
||||
)
|
||||
const vh = Math.max(
|
||||
document.documentElement.clientHeight || 0,
|
||||
window.innerHeight || 0
|
||||
)
|
||||
let zoom = 15
|
||||
|
||||
function randomInt(max: number) {
|
||||
return Math.floor(Math.random() * (max + 1))
|
||||
}
|
||||
|
||||
function makeApp() {
|
||||
let app = new PIXI.Application({
|
||||
width: vw,
|
||||
height: vh,
|
||||
antialias: true,
|
||||
backgroundColor: 0xffffff
|
||||
})
|
||||
document.body.appendChild(app.view)
|
||||
return app
|
||||
}
|
||||
|
||||
function makeBox(
|
||||
mass = 1,
|
||||
x = 0,
|
||||
y = 2,
|
||||
width = 2,
|
||||
height = 1,
|
||||
color = 0xff0000
|
||||
) {
|
||||
let body = new p2.Body({
|
||||
mass: mass,
|
||||
position: [x, y],
|
||||
angularVelocity: mass === 0 ? 0 : 1
|
||||
}),
|
||||
shape = new p2.Box({
|
||||
width: width,
|
||||
height: height
|
||||
}),
|
||||
graphics = new PIXI.Graphics()
|
||||
|
||||
let box = new PhysicsObject(body, shape, graphics)
|
||||
box.graphics.beginFill(color)
|
||||
box.graphics.drawRect(
|
||||
-box.shape.width / 2,
|
||||
-box.shape.height / 2,
|
||||
box.shape.width,
|
||||
box.shape.height
|
||||
)
|
||||
box.graphics.endFill()
|
||||
box.assign(world, container)
|
||||
worldObjects.push(box)
|
||||
|
||||
return box
|
||||
}
|
||||
|
||||
function makeContainer() {
|
||||
let container = new PIXI.Container()
|
||||
|
||||
container.interactive = true
|
||||
container.position.x = app.renderer.width / 2
|
||||
container.position.y = app.renderer.height / 2 + 50
|
||||
container.scale.x = zoom // zoom in
|
||||
container.scale.y = -zoom // flip the y axis for physics
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
function makeFloor() {
|
||||
// zero mass box == static
|
||||
return makeBox(0, 0, -2, 50, 0.2, 0x666666)
|
||||
}
|
||||
|
||||
function makeRandomBox() {
|
||||
const width = 1 + Math.random() * 3,
|
||||
height = 1 + Math.random() * 3,
|
||||
mass = 0.5 * Math.sqrt(width * height),
|
||||
position = [randomInt(25) - 12.5, randomInt(10) + 5],
|
||||
color = randomInt(0xffffff)
|
||||
|
||||
let box = makeBox(mass, position[0], position[1], width, height, color)
|
||||
box.body.angularVelocity = Math.random() * 2
|
||||
|
||||
return box
|
||||
}
|
||||
|
||||
const app = makeApp()
|
||||
|
||||
let worldObjects: PhysicsObject[] = []
|
||||
|
||||
let world = new p2.World({ gravity: [0, -9.81] })
|
||||
|
||||
// Add a plane
|
||||
|
||||
// let planeShape = new p2.Plane()
|
||||
// let planeBody = new p2.Body({
|
||||
// position: [0, -2]
|
||||
// })
|
||||
// planeBody.addShape(planeShape)
|
||||
// world.addBody(planeBody)
|
||||
|
||||
let container = makeContainer()
|
||||
app.stage.addChild(container)
|
||||
|
||||
// Add a box
|
||||
makeBox()
|
||||
// static ground box
|
||||
makeFloor()
|
||||
|
||||
app.ticker.add((_delta) => {
|
||||
// cleanup dead objects
|
||||
if (worldObjects.length > 100) {
|
||||
worldObjects = worldObjects.filter((o) => o.alive)
|
||||
console.log(worldObjects.length)
|
||||
}
|
||||
|
||||
world.step(1 / 60)
|
||||
worldObjects.forEach((o) => o.update())
|
||||
})
|
||||
|
||||
function doZoom(e: WheelEvent) {
|
||||
zoom += -e.deltaY * 0.05
|
||||
zoom = Math.min(Math.max(1, zoom), 100)
|
||||
container.scale.x = zoom // zoom in
|
||||
container.scale.y = -zoom // Note: we flip the y axis to make "up" the physics
|
||||
}
|
||||
window.addEventListener("wheel", doZoom)
|
||||
window.addEventListener("click", makeRandomBox)
|
||||
window.addEventListener("touchstart", makeRandomBox)
|
||||
|
||||
window.setInterval(() => {
|
||||
makeRandomBox()
|
||||
}, 100)
|
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
18
tsconfig.json
Normal file
18
tsconfig.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"moduleResolution": "Node",
|
||||
"strict": true,
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"noEmit": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true
|
||||
},
|
||||
"include": ["./src"]
|
||||
}
|
28
yarn.lock
28
yarn.lock
@ -192,6 +192,23 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/earcut/-/earcut-2.1.1.tgz#573a0af609f17005c751f6f4ffec49cfe358ea51"
|
||||
integrity sha512-w8oigUCDjElRHRRrMvn/spybSMyX8MTkKA5Dv+tS1IE/TgmNZPqUYtvYBXGY8cieSE66gm+szeK+bnbxC2xHTQ==
|
||||
|
||||
"@types/offscreencanvas@^2019.6.3":
|
||||
version "2019.6.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/offscreencanvas/-/offscreencanvas-2019.6.3.tgz#0508eec9641f3228bf8a27009670166887dc966c"
|
||||
integrity sha512-957DsU187Y90EmlXneuqrqnwmwCkC9oPyQ/IEGwmDRpMfuhcGtjIz72Me7HvdK04ZC16A0VWmv0TSNLwC9ZvXw==
|
||||
dependencies:
|
||||
"@types/webgl2" "*"
|
||||
|
||||
"@types/p2@^0.7.39":
|
||||
version "0.7.39"
|
||||
resolved "https://registry.yarnpkg.com/@types/p2/-/p2-0.7.39.tgz#ba6c2328083e36afb841e8183c33d9616d4630e7"
|
||||
integrity sha512-aDRdPLYKTNpVCE5RBrjvckOZvjc42ycKVRCtElZ0IDsBYE0MQ7o06BmrGrd7UNDGjQOfQwF6uOmQmqiiu/4F1g==
|
||||
|
||||
"@types/webgl2@*":
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/webgl2/-/webgl2-0.0.6.tgz#1ea2db791362bd8521548d664dbd3c5311cdf4b6"
|
||||
integrity sha512-50GQhDVTq/herLMiqSQkdtRu+d5q/cWHn4VvKJtrj4DJAjo1MNkWYa2MA41BaBO1q1HgsUjuQvEOk0QHvlnAaQ==
|
||||
|
||||
colorette@^1.2.2:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.3.0.tgz#ff45d2f0edb244069d3b772adeb04fed38d0a0af"
|
||||
@ -203,9 +220,9 @@ earcut@^2.2.2:
|
||||
integrity sha512-iRDI1QeCQIhMCZk48DRDMVgQSSBDmbzzNhnxIo+pwx3swkfjMh6vh0nWLq1NdvGHLKH6wIrAM3vQWeTj6qeoug==
|
||||
|
||||
esbuild@^0.12.17:
|
||||
version "0.12.24"
|
||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.24.tgz#21966fad25a80f368ed308101e88102bce0dc68f"
|
||||
integrity sha512-C0ibY+HsXzYB6L/pLWEiWjMpghKsIc58Q5yumARwBQsHl9DXPakW+5NI/Y9w4YXiz0PEP6XTGTT/OV4Nnsmb4A==
|
||||
version "0.12.25"
|
||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.25.tgz#c2131cef022cf9fe94aaa5e00110b27fc976221a"
|
||||
integrity sha512-woie0PosbRSoN8gQytrdCzUbS2ByKgO8nD1xCZkEup3D9q92miCze4PqEI9TZDYAuwn6CruEnQpJxgTRWdooAg==
|
||||
|
||||
eventemitter3@^3.1.0:
|
||||
version "3.1.2"
|
||||
@ -353,6 +370,11 @@ source-map-js@^0.6.2:
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
|
||||
integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
|
||||
|
||||
typescript@^4.3.2:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.2.tgz#6d618640d430e3569a1dfb44f7d7e600ced3ee86"
|
||||
integrity sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==
|
||||
|
||||
url@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
|
||||
|
Loading…
Reference in New Issue
Block a user