├── .DS_Store
├── .gitignore
├── LICENSE
├── README.md
├── dist
├── index.d.ts
├── index.js
└── index.min.js
├── docs
├── .nojekyll
├── assets
│ ├── highlight.css
│ ├── icons.js
│ ├── icons.svg
│ ├── main.js
│ ├── navigation.js
│ ├── search.js
│ └── style.css
├── enums
│ └── physics.ShapeType.html
├── functions
│ ├── physics.addBody.html
│ ├── physics.addVec2.html
│ ├── physics.allBodies.html
│ ├── physics.atRest.html
│ ├── physics.collidingWithStatic.html
│ ├── physics.createCircle.html
│ ├── physics.createCircleShape.html
│ ├── physics.createJoint.html
│ ├── physics.createRectangle.html
│ ├── physics.createRectangleShape.html
│ ├── physics.createRigidBody.html
│ ├── physics.createWorld.html
│ ├── physics.crossProduct.html
│ ├── physics.disableBody.html
│ ├── physics.dotProduct.html
│ ├── physics.enableBody.html
│ ├── physics.enabledBodies.html
│ ├── physics.excludeCollisions.html
│ ├── physics.getWorldBounds.html
│ ├── physics.includeCollisions.html
│ ├── physics.lengthVec2.html
│ ├── physics.moveBody.html
│ ├── physics.newVec2.html
│ ├── physics.normalize.html
│ ├── physics.removeBody.html
│ ├── physics.rotateBody.html
│ ├── physics.rotateVec2.html
│ ├── physics.scaleVec2.html
│ ├── physics.setCenter.html
│ ├── physics.setRotation.html
│ ├── physics.subtractVec2.html
│ └── physics.worldStep.html
├── index.html
├── interfaces
│ ├── physics.Collision.html
│ ├── physics.DynamicRigidBody.html
│ ├── physics.Joint.html
│ ├── physics.StaticRigidBody.html
│ ├── physics.Vector2.html
│ └── physics.World.html
├── modules.html
├── modules
│ └── physics.html
└── types
│ ├── physics.BaseShape.html
│ ├── physics.Body.html
│ ├── physics.Circle.html
│ ├── physics.Rectangle.html
│ └── physics.Shape.html
├── examples
├── examples-dist
│ ├── examples
│ │ ├── Avian.js
│ │ ├── Avion.js
│ │ ├── Car.js
│ │ ├── Car2.js
│ │ ├── Car3.js
│ │ ├── Car4.js
│ │ ├── CarInteractive.js
│ │ ├── Compound.js
│ │ ├── CompoundJoint.js
│ │ ├── Exclusions.js
│ │ ├── Goo.js
│ │ ├── Joints.js
│ │ ├── Marble.js
│ │ ├── NoGravity.js
│ │ ├── Pile.js
│ │ ├── Platformer.js
│ │ ├── Polybox.js
│ │ ├── Sensor.js
│ │ ├── Simple.js
│ │ ├── Stack.js
│ │ ├── Teleporter.js
│ │ ├── WobblyJoints.js
│ │ └── src
│ │ │ ├── examples
│ │ │ ├── Avian.js
│ │ │ ├── Car.js
│ │ │ ├── Car2.js
│ │ │ ├── Car3.js
│ │ │ ├── Car4.js
│ │ │ ├── CarInteractive.js
│ │ │ ├── Compound.js
│ │ │ ├── CompoundJoint.js
│ │ │ ├── Exclusions.js
│ │ │ ├── Goo.js
│ │ │ ├── Joints.js
│ │ │ ├── Marble.js
│ │ │ ├── NoGravity.js
│ │ │ ├── Pile.js
│ │ │ ├── Platformer.js
│ │ │ ├── Polybox.js
│ │ │ ├── Sensor.js
│ │ │ ├── Simple.js
│ │ │ ├── Stack.js
│ │ │ ├── Teleporter.js
│ │ │ └── WobblyJoints.js
│ │ │ └── index.js
│ ├── index.js
│ ├── patchMathPrecision.js
│ ├── runePatchMathPrecision.js
│ └── src
│ │ └── index.js
├── index.html
├── package.json
├── src
│ ├── examples
│ │ ├── Avian.ts
│ │ ├── Car.ts
│ │ ├── Car2.ts
│ │ ├── Car3.ts
│ │ ├── Car4.ts
│ │ ├── CarInteractive.ts
│ │ ├── Compound.ts
│ │ ├── CompoundJoint.ts
│ │ ├── Exclusions.ts
│ │ ├── Goo.ts
│ │ ├── Joints.ts
│ │ ├── Marble.ts
│ │ ├── NoGravity.ts
│ │ ├── Pile.ts
│ │ ├── Platformer.ts
│ │ ├── Polybox.ts
│ │ ├── Sensor.ts
│ │ ├── Simple.ts
│ │ ├── Stack.ts
│ │ ├── Teleporter.ts
│ │ └── WobblyJoints.ts
│ ├── index.ts
│ └── runePatchMathPrecision.ts
├── tsconfig.json
└── yarn.lock
├── logo.png
├── package.json
├── rune
├── .gitignore
├── .prettierrc.cjs
├── .vscode
│ └── extensions.json
├── README.md
├── eslint.config.mjs
├── index.html
├── package-lock.json
├── package.json
├── src
│ ├── client.ts
│ ├── logic.ts
│ ├── styles.css
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
├── src
└── index.ts
├── tsconfig.json
└── yarn.lock
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevglass/propel-js/9cf493eec6046758f89b270602fbdedafce1b0d0/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 |
93 | # Gatsby files
94 | .cache/
95 | # Comment in the public line in if your project uses Gatsby and not Next.js
96 | # https://nextjs.org/blog/next-9-1#public-directory-support
97 | # public
98 |
99 | # vuepress build output
100 | .vuepress/dist
101 |
102 | # vuepress v2.x temp and cache directory
103 | .temp
104 | .cache
105 |
106 | # Docusaurus cache and generated files
107 | .docusaurus
108 |
109 | # Serverless directories
110 | .serverless/
111 |
112 | # FuseBox cache
113 | .fusebox/
114 |
115 | # DynamoDB Local files
116 | .dynamodb/
117 |
118 | # TernJS port file
119 | .tern-port
120 |
121 | # Stores VSCode versions used for testing VSCode extensions
122 | .vscode-test
123 |
124 | # yarn v2
125 | .yarn/cache
126 | .yarn/unplugged
127 | .yarn/build-state.yml
128 | .yarn/install-state.gz
129 | .pnp.*
130 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Coke And Code
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # propel-js - Tiny Open Physics Library.
4 |
5 | A free, open source, teeny weeny physics library for typescript. Mostly just some utilities for making games.
6 |
7 | [Documentation](https://kevglass.github.io/propel-js/docs)
8 |
9 | [Examples](https://kevglass.github.io/propel-js/examples/)
10 |
11 | ## Install
12 |
13 | ```
14 | npm install propel-js
15 |
16 | or
17 |
18 | yarn add propel-js
19 | ```
20 |
21 | ## Why?
22 |
23 | When working with networked physics games it's useful to be able to seralize state to send across the wire. Other physics
24 | engines do this by providing a seralizer but doing this regularly can be expensive. propel-js aims to keep physics state
25 | in serializable structures with functions/resolvers outside of the data.
26 |
27 | ## Features
28 |
29 | * Rectangles
30 | * Circles
31 | * Joints
32 | * Compound Bodies
33 | * Sensors
34 |
35 | More added as time permits.
36 |
37 | ## Rune Compatible
38 |
39 | This was built to support physics in networked games on the [Rune Platform](https://developers.rune.ai)
40 |
41 | ## Credits
42 |
43 | * This project started as a port and clean up of: https://github.com/xem/mini2Dphysics/tree/gh-pages
44 | * Bjarke Felbo also contributed to the original source: https://github.com/bfelbo
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false.
--------------------------------------------------------------------------------
/docs/assets/highlight.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --light-hl-0: #000000;
3 | --dark-hl-0: #D4D4D4;
4 | --light-hl-1: #001080;
5 | --dark-hl-1: #9CDCFE;
6 | --light-code-background: #FFFFFF;
7 | --dark-code-background: #1E1E1E;
8 | }
9 |
10 | @media (prefers-color-scheme: light) { :root {
11 | --hl-0: var(--light-hl-0);
12 | --hl-1: var(--light-hl-1);
13 | --code-background: var(--light-code-background);
14 | } }
15 |
16 | @media (prefers-color-scheme: dark) { :root {
17 | --hl-0: var(--dark-hl-0);
18 | --hl-1: var(--dark-hl-1);
19 | --code-background: var(--dark-code-background);
20 | } }
21 |
22 | :root[data-theme='light'] {
23 | --hl-0: var(--light-hl-0);
24 | --hl-1: var(--light-hl-1);
25 | --code-background: var(--light-code-background);
26 | }
27 |
28 | :root[data-theme='dark'] {
29 | --hl-0: var(--dark-hl-0);
30 | --hl-1: var(--dark-hl-1);
31 | --code-background: var(--dark-code-background);
32 | }
33 |
34 | .hl-0 { color: var(--hl-0); }
35 | .hl-1 { color: var(--hl-1); }
36 | pre, code { background: var(--code-background); }
37 |
--------------------------------------------------------------------------------
/docs/assets/navigation.js:
--------------------------------------------------------------------------------
1 | window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE5XWz2+bMBTA8f/F52g/orbbckx22mlKpvZQ9eDar2DN2Ag/1rKp//sETWww5pme+eYjY8wL9/8YwguyHavLzinh2IbVHEu2Y5WVrQb38XzhQ4mVZhv2WxnJdlcbJkqlZQOG7e49cip5Db+6GgIDpq0C4oMp9/V1442D1Vo5ZU0wlEFonrgYrcZXU2h7fTOivneGV0ocVaHk3sqOFOOYgn9YZZDUhoIiTshx7dKilmJvQaBttiR3bijmzjZakshQUMSeOxged2Cwq0eCDyLl07cvn6+3Y2myPxEy34/Z7w+qEXpxGW9Xc8YRBHJTLDM+yEnknqzaDy6jI/PUGoHKmuCck6l0czVFbkFsM0ifUIjWeysVOJK5RBSER3BIKkNBEKKfCFKZ4k5h+fbGUF4ip/AGOEJ8kBLqqFvJReeBNhPnIwFH42mJTMyoBJY4+UvgwjtAoSvvftrn+flYXZTTUzWBRjNxCUwMxgizzv1srGxF5hmFjuCkcvxRQ+5mRxmFWVyxslARFJg1ywpVlpL5STMJKfBF6FaC/4ag0Tgm4AJwePx72xpJqtOSIJV5x1pnMQFrMAWWuf+AUBFUZf9kn/SlIRgDz7nlnBMKsU3FtfpLDhUfEVADa24rVBRlkWOe8lWWym1TqAjKCa6zko8oCPAA/RciCV0iGjr2K598/CepS0Zh7SM2XGD2DkcdwT33b+sJoaYsH82gh9eH/8Z27jVsDQAA"
--------------------------------------------------------------------------------
/docs/assets/search.js:
--------------------------------------------------------------------------------
1 | window.searchData = "data:application/octet-stream;base64,H4sIAAAAAAAAE7VcbW/kNg7+L87XaTq2/Jpvm7Q49HC4K7ZF+2GwKJyxkrg3Ywe2k01usf/9INljkzLpl9Hk02IzpB6KD0mJsuxvTlV+rZ2b3Tfnv3mROTf+xinSo3RunOen9zrf187GeakOzo1zLLOXg6x/7P5+/dQcD87G2R/Supa1c+M43zenUcJhmPRwuC2zXA4DPbwU+yYvi2GoXoYYdOM8p5UsGmARiSOL9P4gs1ksJGeBl+W1Gui2zN4n0ICU9dxmoAYhC6RH2fxZVofstnwpsik3YsHViEEgwh70r7+a92e5DuzK+Fs/xqQJ5kjGdAcD3a03OOWYF5cw7rodx8LA0yw5O9O3y9ipx7monSDE9pVMG6mlJ6wFUjaJ87Y/vGTyrjwc8lqNP5U/pqwFbl4sxx3JWuC2XvtnmRfNrG+1lDXWXV7tD1O5C8Ws0T7LfZMWjwsAe0kLzGP5Old0TyIWKLVs7mTRyGoCppexw/lcNqkaeBrpJGWBVakh5nw3CFkgfVUV4rdGPk8A9TI28afSM8uLxz/z5uk35aD9VAyOpS2w0+azrKcSuhWwQCjk1z/k3puA6CQsMA6yeGyeZmAGIRt/ZdkMTCdhk00v902V7psZIChmg7ZPD3IO6iRjs6ktm1+rMnvZT0XbIGRVz8u6nseCYtb1aMaBg5BNJpXVMT3k/5taoXqZC62+vz2lk9vnkezl1uFl0FjcHj1/zLOZxcWQtKsmM1idhE18ygV7jUFoLVLcA2kCfofdlixejgNG//taCHdo5+5++Xz3r58XAVz1stPdxmA2ifj557vfP/37H0tBofh6XG+bRG7gYZf2wKrvMYDXutIEuE1rOQXS/37RLnwK42phtz1YznSsebYG71qLL4Sc6ZXv8QHHEvRe5YIW5MXjbfm23oxe7zK27HEDssSMXuUyFqyNvet1ATiDXsuiLtfNv1e5pAV3p77hDFOg7mVsygtZNXm6LkV7nUtlSfb+y7oq0aucbYFZgY1DDmzEeUcbJsT4ZAOjnH2e4QVDif9D7puyGvbAuUrhh3QPcDqR1cs/5OxtKcDVfAU7mcxAvS+Gej8DCvoOH6oRaGedp41i/dMyiKuT7PSUWqMn4G5XwN3awWV53aTFXi5EBOIWoJVqAPJmMkwAKBC3AJWHtG7y/XJYpGABXJcPC0P0qhO1AVO1c2m49sK2gEsDthdeBwgzvj+Rn4LshewzHyxzs0BXg8b0BIdJTFWB1dC3l4DO5HPztAL4JL8eFtLaHseOzxAIfEPUiuIanxkvRLvq1WYaVWNS8/3WUgOMnqupsx/y+oe8eJJV3sjMwhidovV6j5zUPsSohyrfNzM5T5oFFD/EsErWTd68nGUb1v0Q87K0SVfb1Sl9iEHPsjrK9D4/zCzDpGGG8ocYaPTcS00j+u4LGoVbkKU2nbQ+xCR0MrDUotHpgJ1BcP346b1IjwsXEFP2g1cQEm7pEjKaFxcir7JKH+XdbPjS1pjqlzHqmNaTKwltS6d1GRNe5aGc2/PTZgDNC1G038uDrNK5xYJhCGtfNGo+zdWXyaD5NKozViYVjy+HtPrjbN7GA1zUsE92LJKDXMZA82BwsVFLTweXGqI3NsXj7/nxjKDCypcx6CF/k9noms1ik0z1Cxr1a1nnFkYB9csY1e5j/vPwa7forTZrPMCFvHUo0wYewS931KB5oTybbNeYFFvfry01Z75hYzYg53Vsiwlb0LIxhJ3bs62qT/NN20R9OrNrW2rgXNtGW3ZW37bUpKWNG22aVee2rnadW7I+zKzZ5o3dKZzRvS01aq59o206q3+bMGl0pwI2bsZDxTP6NNgf4pvtxIzPutSOs7adqPGuyyTUlakzvUK1k5hsRFehGyoW4N0bNeaLPjOTN5UsDHis0teZ0gSQB2kLyG4v/1N6fF4IizVs3L0cM7MGK+RbM/0oAsD1whaAf6vHP0uDqBe2AHyo0qO8K1+mHyUDUKRgO1N1S3zBZsmcM1azMELtaOZ6NQAOxC1A9Xs9+CWcaVikYAH8nL7Ucmk098KLAL9snLzI5Jtz8815lZV+2nXjeNfiOnE2zkMuD5l6qbO1ZOPsy+NRjfOl+6296aAkWpEft85mt9344jr2vnzZ7E4K+u/6D1rKdTY7l5BykZTnbHYeIeUhKeFsdoKQEkjKdzY7n5DykVTgbHYBIRUgqdDZ7MKNiK8jF4uFSCxyNruIGCxCUrGz2cWEVIykEmezSwipBLtVedml3O8a/tcEkAxgClzla5ciwcUsuMrdLsWDi4lwlcddigoXc+Eqp7sUGy6mw1Vud0NKEBPiKs+7FCUu5sRVzncpVlxMi6v871LEuJgZT/nfo5jxMDOe8r9HMeMZyaGzg0wPzIyn/O9RzHiYGU/536OY8TAznvK/RzHjYWY85X+PYsbDzHjK/x7FjIeZ8ZT/PYoZDzPjKf97FDMeZkYo/wuKGYGZEcr/gmJGYGaE8r+gmBFG5dKli6xdmBmh/C8oZgRmRij/C4oZgZkRyv+CYkZgZoTyv4ioaicwNUIRIGJSEnMjFAOC4kZgbnzFgE9x42NufJcryT7mxlcM+O5GBNfCj7AkJsdXFPgUi76xsuilhWLRx+T4igLfJ7ExO77iwA9ISUyPrzjwKR59zI6vKPCpDPMxOb6iwCdp9DE7gWaHojHA7AQuG0MBpifw2BgKMD2BIiGgQiPA9ASKhIBK28BY+/XiTxEeYHYCRUFAER5gcgJFQUClbYDJCRQFAbnxwOQEioGAojvA3ISKgYCiO8TchIqAgCqoIaYmVP4PKLZDzEyo/B9SzISYmVD5P6SYCTEzofJ/SDETGhszvTOjmAkxM6Hyf0gxE2JmQuX/kGImxMyEyv8hxUyImYmU/0MyGSJMTeSytSrC3ESKgZBMmwiTE2lyElISsxMpDqItKYnpiRQJkUtKYn4ixULkkZLG3jlia2WEGYoUD5Egx8QURQlbVSPMUayIiKjwiDFFscuSGWOKYsVDRAVSjBmKFQ0RFUgxJijWBFEpHmN+Ys0P2WNgemJND5XiMWYnVhTEVIrHRnOjuxsqxWPMTcyvOjHmJlEMxFQxSDA3iSIgpopBgqlJFAExxXaCqUkUATHFYYKpSRQBMcVhgqlJAjbFE8xNErIpnmBykohN8QSzk8RsiidG+5mwKZ6YLeiWzfH2Nyjrslne/gZlPTbP29+grGAzvf0NymquyKZwazSkW0VJTLaFW6Ml3SpOYrIx3BpN6VaRktBNu9GWbhUrCdm2b43GdKtPDMjGfWswps8GErJ1Hx0bKFISsnk3Dw70+UBCtu/m0YE+IUjIBt48PNBnBAnJlnl8oE8JEpIt8wBBnxMkJFvmEUJ7hrAl6TJPEdpjhC19zNLypY/fXmXVyOyX9hhut+vfn/3m/NWdzfXPhL45oXPz7fvG8V317/fhRE79TyHgO1fDEFE8jBEHnHKW3evnWYOe8Ac94fN6r/qTCIOeB/Q8Vu9wuO8epAya7qDITrJ90AhmJwalKG5dpMoCq66eaXCuioEFcTw9RvuoYtBNgCEqfSaVh+t6AHwLwCNOv6n0d1yAu4HNHuu29vLb2HsJwAynlU9PnoF2ALQ5l9+ntazbV88HTR/M1ed8ZQZkHAEPb7nIUlopVAtAPAacX7WWuvcCUg8QGnK+UXr3CA34JOBCSGsZaMDKcMpKrOYDAgPeke3r3vfqdW+gCybo8+5s31gHah5QE4zaOFh8MEE/aBM1ik4JywVuO075cHqaDcMBeDph7eheuwVlDUSR6PADFv70QvTXvHk6XYIGuQfC2OO8vx/eAQN0AyeGXFFuP0AynoILSw07c6A8yj8BioZgJ69H+Lt9aRWgA9NdLutb3Wp4Ixnog0BwubAz9McTAEaIGSPUXY7R4gZcKKZd+LW9jAEqPYg7VrOs6+fTp4lAwIC12OOqw2hlgQ5zObbaC1WgOAOKoy7fEs7e7rU4EJ8guULW0PZKxMi3wLXTmtl4D5DATcB2Arl7sxdUXrBXCtiJlg1FC6gJHld8u9svY5PjBJrMrRSdOhmNEWA45uoIfMMXzBpEVMjFRvtdVxMVFuRJRYImkH5c9nUfw9yDD1iCIrCFPpsagVCFui5Hl75y/NxfOQZ0gXknnPFau+pvUQNtEN0J5+/hFi9QBOGZcP7WVzX27VUNkBNw48OWzOEmKkhjEJqR6IoA57FH2ehaN17y4Z6GUz5dUgJmw4Viy2WksZ8B6e973S7B7QznsLvPnzKRBgsKR1j/UgGwBCSWn7QWxNwkRotkAMIk4EqC1qrgzRjgvBiazc38dJ8I6IVQj6u/7TcSR50bMNrjVNsXnkDtAoAxr4R2nrAj5TRy3MSCOOY0ytdRiXNB5LJOLOTXkSdgfeNqhLo9hqM3gXDs3gh83w8smqCoCa4gnm74wGKIwpvzJr7SDNwK5hmFp804Cz/ehAOrOS1yEyhACIiubQ84h7VfrRttM4CzBcdt94JOo6+AgQAE8054w8HleeAzMOfI70oT53g1hgmewP2CyxWH4TsioKqANAhZs/U3KEeZAOsJa61WHSUDsNfjKqD+dOhIE9QGj5vo6StYoPQCNZ9L91ZtP3yxCugDN/ncXGvZjJtUF1ZP3uCG2hm4cJVmrR41MsC5gnWu0kKrUwiCMOSqrlZDpxMhqBYht5E4vWsD1EDwRN2qnLDuUermOaYAjhWspv6gDAh34JuQKw7jrjyEi8W2W7m5lb9VJ/oQtEFl6dTa5J4+hEe3LLHdt31HiQNqm8fVNtPHPlDyu2IeJaeizgXJ6+mDXsDvYO4Brzc+woyA52MuvEb9dAxL05abrVar9Ve6Qc7BUso5GW09ApABAWciLrpgrQhI675snOf8WR7yQjo3uy/fv/8faqZvmRNlAAA=";
--------------------------------------------------------------------------------
/docs/functions/physics.collidingWithStatic.html:
--------------------------------------------------------------------------------
1 |
collidingWithStatic | propel-jsFunction collidingWithStatic
- collidingWithStatic(world, body, debug?): boolean
Returns boolean
--------------------------------------------------------------------------------
/docs/functions/physics.enableBody.html:
--------------------------------------------------------------------------------
1 | enableBody | propel-js- enableBody(world, body, dynamics?): void
Returns void
--------------------------------------------------------------------------------
/docs/functions/physics.lengthVec2.html:
--------------------------------------------------------------------------------
1 | lengthVec2 | propel-js- lengthVec2(v): number
Returns number
The length of the vector
4 |
--------------------------------------------------------------------------------
/docs/functions/physics.moveBody.html:
--------------------------------------------------------------------------------
1 | moveBody | propel-js- moveBody(body, v): void
Returns void
--------------------------------------------------------------------------------
/docs/functions/physics.normalize.html:
--------------------------------------------------------------------------------
1 | normalize | propel-js
--------------------------------------------------------------------------------
/docs/functions/physics.rotateBody.html:
--------------------------------------------------------------------------------
1 | rotateBody | propel-js- rotateBody(body, angle): void
Returns void
--------------------------------------------------------------------------------
/docs/functions/physics.setCenter.html:
--------------------------------------------------------------------------------
1 | setCenter | propel-js- setCenter(body, v): void
Returns void
--------------------------------------------------------------------------------
/docs/functions/physics.setRotation.html:
--------------------------------------------------------------------------------
1 | setRotation | propel-js- setRotation(body, angle): void
Returns void
--------------------------------------------------------------------------------
/docs/modules.html:
--------------------------------------------------------------------------------
1 | propel-js
--------------------------------------------------------------------------------
/docs/types/physics.Body.html:
--------------------------------------------------------------------------------
1 | Body | propel-js
--------------------------------------------------------------------------------
/docs/types/physics.Circle.html:
--------------------------------------------------------------------------------
1 | Circle | propel-js
--------------------------------------------------------------------------------
/docs/types/physics.Shape.html:
--------------------------------------------------------------------------------
1 | Shape | propel-js
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Avian.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | export function avianUpdate(world) {
3 | if (world.frameCount === 120) {
4 | const friction = 0.5;
5 | const rest = 0.5;
6 | const ball = physics.createCircle(world, { x: 80, y: 400 }, 10, 20, friction, rest);
7 | physics.addBody(world, ball);
8 | ball.velocity.x = 500 + Math.floor(Math.random() * 100);
9 | ball.velocity.y = -400 + Math.floor(Math.random() * 300);
10 | }
11 | return undefined;
12 | }
13 | export function avianInit() {
14 | const world = physics.createWorld({ x: 0, y: 300 });
15 | world.damp = 0.98;
16 | world.restTime = 10;
17 | const friction = 0.5;
18 | const rest = 0.5;
19 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 600, 30, 0, friction, rest);
20 | physics.addBody(world, rect);
21 | const box = physics.createRectangle(world, { x: 40, y: 410 }, 60, 20, 0, friction, rest);
22 | physics.setRotation(box, -Math.PI / 6);
23 | physics.addBody(world, box);
24 | const circle = physics.createCircle(world, { x: 30, y: 420 }, 15, 0, friction, rest);
25 | physics.addBody(world, circle);
26 | let crate = physics.createRectangle(world, { x: 330, y: 400 }, 170, 15, 1, friction, rest);
27 | physics.addBody(world, crate);
28 | crate = physics.createRectangle(world, { x: 280, y: 420 }, 15, 30, 1, friction, rest);
29 | physics.addBody(world, crate);
30 | crate = physics.createRectangle(world, { x: 380, y: 420 }, 15, 30, 1, friction, rest);
31 | physics.addBody(world, crate);
32 | crate = physics.createRectangle(world, { x: 300, y: 370 }, 15, 30, 1, friction, rest);
33 | physics.addBody(world, crate);
34 | crate = physics.createRectangle(world, { x: 360, y: 370 }, 15, 30, 1, friction, rest);
35 | physics.addBody(world, crate);
36 | crate = physics.createRectangle(world, { x: 330, y: 350 }, 140, 15, 1, friction, rest);
37 | physics.addBody(world, crate);
38 | crate = physics.createRectangle(world, { x: 330, y: 330 }, 30, 50, 1, friction, rest);
39 | physics.addBody(world, crate);
40 | crate = physics.createRectangle(world, { x: 330, y: 300 }, 140, 15, 1, friction, rest);
41 | physics.addBody(world, crate);
42 | crate = physics.createRectangle(world, { x: 310, y: 270 }, 15, 30, 1, friction, rest);
43 | physics.addBody(world, crate);
44 | crate = physics.createRectangle(world, { x: 330, y: 270 }, 15, 30, 1, friction, rest);
45 | physics.addBody(world, crate);
46 | crate = physics.createRectangle(world, { x: 350, y: 270 }, 15, 30, 1, friction, rest);
47 | physics.addBody(world, crate);
48 | return world;
49 | }
50 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Avion.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevglass/propel-js/9cf493eec6046758f89b270602fbdedafce1b0d0/examples/examples-dist/examples/Avion.js
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Car.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // jointed car
3 | export function carInit() {
4 | const world = physics.createWorld({ x: 0, y: 300 });
5 | world.damp = 0.99;
6 | const friction = 0.5;
7 | let rect = physics.createRectangle(world, { x: 150, y: 80 }, 400, 30, 0, friction, 0.5);
8 | physics.rotateBody(rect, Math.PI / 6);
9 | physics.addBody(world, rect);
10 | rect = physics.createRectangle(world, { x: 350, y: 250 }, 400, 30, 0, friction, 0.5);
11 | physics.rotateBody(rect, -Math.PI / 8);
12 | physics.addBody(world, rect);
13 | rect = physics.createRectangle(world, { x: 150, y: 420 }, 400, 30, 0, friction, 0.5);
14 | physics.rotateBody(rect, Math.PI / 8);
15 | physics.addBody(world, rect);
16 | const circle1 = physics.createCircle(world, { x: 50, y: 0 }, 15, 3, friction, 1);
17 | physics.addBody(world, circle1);
18 | const circle2 = physics.createCircle(world, { x: 90, y: 0 }, 15, 3, friction, 1);
19 | physics.addBody(world, circle2);
20 | physics.createJoint(world, circle1, circle2, 0.5, 0.5);
21 | return world;
22 | }
23 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Car2.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // jointed car
3 | export function car2Init() {
4 | const world = physics.createWorld({ x: 0, y: 300 });
5 | world.damp = 0.99;
6 | const friction = 1;
7 | const carX = 100;
8 | let rect = physics.createRectangle(world, { x: 150, y: 80 }, 350, 30, 0, friction, 0);
9 | physics.rotateBody(rect, Math.PI / 12);
10 | physics.addBody(world, rect);
11 | rect = physics.createRectangle(world, { x: 350, y: 250 }, 350, 30, 0, friction, 0);
12 | physics.rotateBody(rect, -Math.PI / 12);
13 | physics.addBody(world, rect);
14 | rect = physics.createRectangle(world, { x: 150, y: 420 }, 400, 30, 0, friction, 0);
15 | physics.rotateBody(rect, Math.PI / 8);
16 | physics.addBody(world, rect);
17 | const circle1 = physics.createCircle(world, { x: carX + 80, y: 0 }, 15, 3, friction, 0);
18 | physics.addBody(world, circle1);
19 | const circle2 = physics.createCircle(world, { x: carX + 120, y: 0 }, 15, 3, friction, 0);
20 | physics.addBody(world, circle2);
21 | const leftAnchor = physics.createCircleShape(world, { x: carX + 80, y: 0 }, 1);
22 | const rightAnchor = physics.createCircleShape(world, { x: carX + 120, y: 0 }, 1);
23 | const base = physics.createRectangleShape(world, { x: carX + 100, y: 0 }, 60, 10, 0);
24 | const chassis = physics.createRigidBody(world, { x: carX + 100, y: 0 }, 1, friction, 0, [base, leftAnchor, rightAnchor]);
25 | physics.addBody(world, chassis);
26 | physics.excludeCollisions(world, chassis, circle1);
27 | physics.excludeCollisions(world, chassis, circle2);
28 | physics.createJoint(world, circle1, leftAnchor, 0.5, 0.5);
29 | physics.createJoint(world, circle2, rightAnchor, 0.5, 0.5);
30 | physics.rotateBody(chassis, Math.PI / 8);
31 | return world;
32 | }
33 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Car3.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // jointed car
3 | export function car3Init() {
4 | const world = physics.createWorld({ x: 0, y: 300 });
5 | world.damp = 0.99;
6 | const friction = 0.5;
7 | let rect = physics.createRectangle(world, { x: 250, y: 420 }, 400, 30, 0, friction, 0.5);
8 | physics.addBody(world, rect);
9 | const circle1 = physics.createCircle(world, { x: 150, y: 0 }, 15, 3, friction, 0);
10 | physics.addBody(world, circle1);
11 | const circle2 = physics.createCircle(world, { x: 190, y: 0 }, 15, 3, friction, 0);
12 | physics.addBody(world, circle2);
13 | const leftAnchor = physics.createCircleShape(world, { x: 150, y: 0 }, 3);
14 | const rightAnchor = physics.createCircleShape(world, { x: 190, y: 0 }, 3);
15 | const base = physics.createRectangleShape(world, { x: 170, y: -25 }, 60, 20, 0);
16 | const chassis = physics.createRigidBody(world, { x: 170, y: 0 }, 1, friction, 0, [base, leftAnchor, rightAnchor]);
17 | physics.addBody(world, chassis);
18 | physics.excludeCollisions(world, chassis, circle1);
19 | physics.excludeCollisions(world, chassis, circle2);
20 | physics.createJoint(world, circle1, leftAnchor, 1, 0);
21 | physics.createJoint(world, circle2, rightAnchor, 1, 0);
22 | return world;
23 | }
24 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Car4.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | let chassisId;
3 | // jointed car
4 | export function car4Init() {
5 | const world = physics.createWorld({ x: 0, y: 300 });
6 | world.damp = 0.99;
7 | const friction = 1;
8 | let lastAngle = 0.1;
9 | for (let i = 0; i < 50; i++) {
10 | let rect = physics.createRectangle(world, { x: 250 + (i * 390), y: 420 }, 400, 30, 0, friction, 0.5);
11 | physics.addBody(world, rect);
12 | physics.rotateBody(rect, i % 2 === 0 ? 0.2 : -0.2);
13 | }
14 | const circle1 = physics.createCircle(world, { x: 150, y: 0 }, 15, 3, friction, 0);
15 | physics.addBody(world, circle1);
16 | const circle2 = physics.createCircle(world, { x: 190, y: 0 }, 15, 3, friction, 0);
17 | physics.addBody(world, circle2);
18 | const leftAnchor = physics.createCircleShape(world, { x: 150, y: 0 }, 3, true);
19 | const rightAnchor = physics.createCircleShape(world, { x: 190, y: 0 }, 3, true);
20 | const base = physics.createRectangleShape(world, { x: 170, y: -25 }, 60, 20, 0);
21 | const chassis = physics.createRigidBody(world, { x: 170, y: 0 }, 1, friction, 0, [base, leftAnchor, rightAnchor]);
22 | physics.addBody(world, chassis);
23 | physics.excludeCollisions(world, chassis, circle1);
24 | physics.excludeCollisions(world, chassis, circle2);
25 | physics.createJoint(world, circle1, leftAnchor, 1, 0);
26 | physics.createJoint(world, circle2, rightAnchor, 1, 0);
27 | chassisId = chassis.id;
28 | return world;
29 | }
30 | export function car4Update(world) {
31 | const chassis = world.dynamicBodies.find(b => b.id === chassisId);
32 | if (chassis) {
33 | chassis.velocity.x = 350;
34 | return chassis;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/CarInteractive.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | let chassisId;
3 | let circle1Id;
4 | let circle2Id;
5 | let left = false;
6 | let right = false;
7 | const MAX_VELOCITY = 10000;
8 | const CAR_ACCEL = 2000;
9 | const CAR_TILT = 2;
10 | let lastOnGround = Date.now();
11 | let rightSensorId;
12 | let leftSensorId;
13 | // jointed car
14 | export function carInteractiveInit() {
15 | const world = physics.createWorld({ x: 0, y: 300 });
16 | world.damp = 0.99;
17 | world.angularDamp = 0.95;
18 | const friction = 1;
19 | let rect = physics.createRectangle(world, { x: 250, y: 458 }, 400, 30, 0, friction, 0);
20 | physics.addBody(world, rect);
21 | for (let i = 1; i < 50; i++) {
22 | let rect = physics.createRectangle(world, { x: 250 + (i * 390), y: 420 }, 400, 30, 0, friction, 0);
23 | physics.addBody(world, rect);
24 | physics.rotateBody(rect, i % 2 === 0 ? 0.2 : -0.2);
25 | }
26 | const leftAnchor = physics.createCircleShape(world, { x: 150, y: 0 }, 3);
27 | const rightAnchor = physics.createCircleShape(world, { x: 190, y: 0 }, 3);
28 | // give them a bit of padding to consume the resolution of wheels against floor
29 | const leftSensor = physics.createCircleShape(world, { x: 150, y: 0 }, 16.5, true);
30 | const rightSensor = physics.createCircleShape(world, { x: 190, y: 0 }, 16.5, true);
31 | leftSensorId = leftSensor.id;
32 | rightSensorId = rightSensor.id;
33 | const base = physics.createRectangleShape(world, { x: 170, y: -25 }, 60, 20, 0);
34 | const chassis = physics.createRigidBody(world, { x: 170, y: 10 }, 1, friction, 0, [base, leftAnchor, rightAnchor, leftSensor, rightSensor]);
35 | const circle1 = physics.createCircle(world, { x: 150, y: 0 }, 15, 3, friction, 0);
36 | const circle2 = physics.createCircle(world, { x: 190, y: 0 }, 15, 3, friction, 0);
37 | chassisId = chassis.id;
38 | circle1Id = circle1.id;
39 | circle2Id = circle2.id;
40 | physics.addBody(world, chassis);
41 | physics.addBody(world, circle1);
42 | physics.addBody(world, circle2);
43 | physics.excludeCollisions(world, chassis, circle1);
44 | physics.excludeCollisions(world, chassis, circle2);
45 | physics.createJoint(world, circle1, leftSensor, 1, 0);
46 | physics.createJoint(world, circle2, rightSensor, 1, 0);
47 | return world;
48 | }
49 | export function carInteractiveInput(world, input, on) {
50 | if (on) {
51 | if (input === "a" || input === "ArrowLeft") {
52 | left = true;
53 | }
54 | if (input === "d" || input === "ArrowRight") {
55 | right = true;
56 | }
57 | }
58 | else {
59 | if (input === "a" || input === "ArrowLeft") {
60 | left = false;
61 | }
62 | if (input === "d" || input === "ArrowRight") {
63 | right = false;
64 | }
65 | }
66 | }
67 | export function carInteractiveUpdate(world, collisions) {
68 | const chassis = world.dynamicBodies.find((b) => b.id === chassisId);
69 | const circle1 = world.dynamicBodies.find((b) => b.id === circle1Id);
70 | const circle2 = world.dynamicBodies.find((b) => b.id === circle2Id);
71 | const leftSensor = chassis.shapes.find((s) => s.id === leftSensorId);
72 | const rightSensor = chassis.shapes.find((s) => s.id === rightSensorId);
73 | const isMidAir = !leftSensor.sensorColliding && !rightSensor.sensorColliding;
74 | chassis.restingTime = 0;
75 | circle1.restingTime = 0;
76 | circle2.restingTime = 0;
77 | const delta = 1 / 60;
78 | // since we're sure the body is on the ground it ok to drive the chassis forward since it
79 | // gives uniform velocity to the wheels
80 | if (!isMidAir) {
81 | if (left) {
82 | chassis.velocity.x = Math.max(-MAX_VELOCITY, chassis.velocity.x - CAR_ACCEL * delta);
83 | }
84 | if (right) {
85 | chassis.velocity.x = Math.min(MAX_VELOCITY, chassis.velocity.x + CAR_ACCEL * delta);
86 | }
87 | }
88 | // if we've been off the ground for a bit then allow explicitly tilting the car. Note that
89 | // this is totally non-physical so we're giving the player direct control of the car's angle
90 | // and want to ignore any other velocity/acceleration on it
91 | if (isMidAir) {
92 | if (left) {
93 | physics.rotateBody(chassis, -CAR_TILT * delta);
94 | chassis.angularVelocity = 0;
95 | }
96 | if (right) {
97 | physics.rotateBody(chassis, CAR_TILT * delta);
98 | chassis.angularVelocity = 0;
99 | }
100 | }
101 | return chassis;
102 | }
103 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Compound.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // simple shapes
3 | export function compoundInit() {
4 | const world = physics.createWorld();
5 | world.damp = 0.98;
6 | world.restTime = 0.25;
7 | const restitution = 0.1;
8 | const friction = 0.5;
9 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, friction, restitution);
10 | physics.addBody(world, rect);
11 | const rect2 = physics.createRectangle(world, { x: 230, y: 300 }, 20, 30, 0, friction, restitution);
12 | physics.addBody(world, rect2);
13 | const shape1 = physics.createRectangleShape(world, { x: 255, y: 50 }, 40, 40);
14 | const shape2 = physics.createRectangleShape(world, { x: 225, y: 50 }, 20, 80);
15 | const shape3 = physics.createRectangleShape(world, { x: 275, y: 50 }, 20, 60);
16 | const body = physics.createRigidBody(world, { x: 255, y: 50 }, 1, friction, restitution, [shape1, shape2, shape3]);
17 | physics.addBody(world, body);
18 | const shape4 = physics.createCircleShape(world, { x: 145, y: 0 }, 20);
19 | const shape5 = physics.createRectangleShape(world, { x: 125, y: 0 }, 20, 80);
20 | const body2 = physics.createRigidBody(world, { x: 145, y: 0 }, 1, friction, restitution, [shape4, shape5]);
21 | physics.addBody(world, body2);
22 | return world;
23 | }
24 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/CompoundJoint.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // simple shapes
3 | export function compoundJointInit() {
4 | const world = physics.createWorld();
5 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
6 | physics.addBody(world, rect);
7 | const rect2 = physics.createRectangle(world, { x: 250, y: 50 }, 100, 30, 0, 0.5, 0.5);
8 | physics.addBody(world, rect2);
9 | const restitution = 0.1;
10 | const friction = 0.5;
11 | const shape4 = physics.createCircleShape(world, { x: 255, y: 150 }, 20);
12 | const shape5 = physics.createRectangleShape(world, { x: 225, y: 150 }, 20, 80);
13 | const shape6 = physics.createRectangleShape(world, { x: 205, y: 120 }, 20, 20);
14 | const body2 = physics.createRigidBody(world, { x: 245, y: 150 }, 1, friction, restitution, [shape4, shape5, shape6]);
15 | physics.addBody(world, body2);
16 | physics.createJoint(world, rect2, shape6);
17 | return world;
18 | }
19 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Exclusions.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // simple shapes with exclusions
3 | export function exclusionsInit() {
4 | const world = physics.createWorld();
5 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
6 | physics.addBody(world, rect);
7 | const circle = physics.createCircle(world, { x: 250, y: 150 }, 40, 1, 0.5, 0.5);
8 | physics.addBody(world, circle);
9 | const circle2 = physics.createCircle(world, { x: 200, y: 150 }, 40, 1, 0.5, 0.5);
10 | physics.addBody(world, circle2);
11 | physics.excludeCollisions(world, circle, circle2);
12 | return world;
13 | }
14 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Goo.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | export function gooInit() {
3 | const world = physics.createWorld();
4 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
5 | physics.addBody(world, rect);
6 | const rect2 = physics.createRectangle(world, { x: 200, y: 410 }, 50, 50, 0, 0.5, 0.5);
7 | physics.addBody(world, rect2);
8 | const rect3 = physics.createRectangle(world, { x: 300, y: 410 }, 50, 50, 0, 0.5, 0.5);
9 | physics.addBody(world, rect3);
10 | const ptCount = 20;
11 | const size = 40;
12 | const bodies = [];
13 | for (let i = 0; i < ptCount; i++) {
14 | const x = 250 + (Math.cos((Math.PI * 2 / ptCount) * i) * size);
15 | const y = 100 + (Math.sin((Math.PI * 2 / ptCount) * i) * size);
16 | const point = physics.createCircle(world, { x, y }, 6, 1, 0.0, 0.0);
17 | bodies.push(point);
18 | physics.addBody(world, point);
19 | }
20 | for (let i = 0; i < ptCount; i++) {
21 | let j = (i + Math.floor(ptCount / 2)) % ptCount;
22 | physics.createJoint(world, bodies[i], bodies[j], 0.05, 0.01, true);
23 | j = (i + 1) % ptCount;
24 | physics.createJoint(world, bodies[i], bodies[j], 0.05, 0.01, true);
25 | }
26 | return world;
27 | }
28 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Joints.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // simple joints
3 | export function jointsInit() {
4 | const world = physics.createWorld();
5 | for (let i = 0; i < 3; i++) {
6 | const rect = physics.createRectangle(world, { x: 150 + (i * 70), y: 50 }, 30, 30, 0, 0, 0.5);
7 | physics.addBody(world, rect);
8 | const circle = physics.createCircle(world, { x: 150 + (i * 70), y: 250 }, 20, 1, 0, 1);
9 | physics.addBody(world, circle);
10 | physics.createJoint(world, rect, circle, 1, 0);
11 | if (i === 0) {
12 | circle.velocity.x = -150;
13 | }
14 | }
15 | return world;
16 | }
17 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Marble.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // simple shapes
3 | export function marbleInit() {
4 | const world = physics.createWorld();
5 | world.damp = 0.99;
6 | world.angularDamp = 0.95;
7 | const marble1 = physics.createCircle(world, { x: 245, y: -200 }, 20, 2, 0.3, 0.5);
8 | physics.addBody(world, marble1);
9 | const marble2 = physics.createCircle(world, { x: 275, y: -300 }, 20, 2, 0.3, 0.3);
10 | physics.addBody(world, marble2);
11 | const slope1 = physics.createRectangle(world, { x: 200, y: 200 }, 200, 10, 0, 0.3, 0.3);
12 | physics.rotateBody(slope1, Math.PI / 6);
13 | physics.addBody(world, slope1);
14 | const slope2 = physics.createRectangle(world, { x: 400, y: 300 }, 200, 10, 0, 0.3, 0.3);
15 | physics.rotateBody(slope2, -Math.PI / 6);
16 | physics.addBody(world, slope2);
17 | const wheelWing1 = physics.createRectangleShape(world, { x: 200, y: 400 }, 10, 100);
18 | const wheelWing2 = physics.createRectangleShape(world, { x: 200, y: 400 }, 10, 100, Math.PI / 2);
19 | const wheel = physics.createRigidBody(world, { x: 200, y: 400 }, 1, 0, 0.3, [wheelWing1, wheelWing2]);
20 | physics.addBody(world, wheel);
21 | const wheelJoint = physics.createCircle(world, { x: 200, y: 400 }, 10, 0, 0.3, 0.3, false);
22 | physics.addBody(world, wheelJoint);
23 | physics.excludeCollisions(world, wheel, wheelJoint);
24 | physics.createJoint(world, wheel, wheelJoint, 0, 0);
25 | return world;
26 | }
27 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/NoGravity.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // simple shapes
3 | export function noGravityInit() {
4 | const world = physics.createWorld({ x: 0, y: 0 }, 20);
5 | const circle = physics.createCircle(world, { x: 250, y: 150 }, 40, 1, 0.5, 0.5);
6 | physics.addBody(world, circle);
7 | const box = physics.createRectangle(world, { x: 255, y: 0 }, 40, 40, 1, 0.5, 0.5);
8 | physics.addBody(world, box);
9 | box.velocity.y = 25;
10 | return world;
11 | }
12 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Pile.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // every growing pile
3 | let frameCount = 0;
4 | export function pileInit() {
5 | const world = physics.createWorld();
6 | world.restTime = 1000;
7 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
8 | physics.addBody(world, rect);
9 | return world;
10 | }
11 | export function pileUpdate(world) {
12 | if (frameCount % 30 === 0) {
13 | if (Math.random() < 0.5) {
14 | const circle = physics.createCircle(world, { x: 50 + Math.floor(Math.random() * 400), y: 0 }, 20 + (Math.random() * 20), 1, 0.5, 1);
15 | physics.setRotation(circle, Math.random() * Math.PI * 2);
16 | physics.addBody(world, circle);
17 | }
18 | else {
19 | const box = physics.createRectangle(world, { x: 50 + Math.floor(Math.random() * 400), y: 0 }, 20 + (Math.random() * 20), 20 + (Math.random() * 20), 1, 0.5, 1);
20 | physics.setRotation(box, Math.random() * Math.PI * 2);
21 | physics.addBody(world, box);
22 | }
23 | }
24 | for (const body of world.dynamicBodies) {
25 | if (body.center.y > 600) {
26 | physics.removeBody(world, body);
27 | }
28 | }
29 | frameCount++;
30 | return;
31 | }
32 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Platformer.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | let playerId;
3 | let left = false;
4 | let right = false;
5 | let onFloor = false;
6 | const MOVE_SPEED = 60;
7 | const JUMP_SPEED = 200;
8 | // simple shapes
9 | export function platformerInit() {
10 | const world = physics.createWorld({ x: 0, y: 300 });
11 | world.damp = 1;
12 | world.angularDamp = 1;
13 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
14 | physics.addBody(world, rect);
15 | const rect2 = physics.createRectangle(world, { x: 435, y: 335 }, 30, 200, 0, 0.5, 0.5);
16 | physics.addBody(world, rect2);
17 | const player = physics.createRectangle(world, { x: 255, y: 400 }, 30, 60, 1, 0, 0.5);
18 | player.fixedRotation = true;
19 | physics.addBody(world, player);
20 | playerId = player.id;
21 | const box = physics.createRectangle(world, { x: 100, y: 400 }, 40, 40, 0, 0.5, 0.5);
22 | physics.addBody(world, box);
23 | const box2 = physics.createRectangle(world, { x: 250, y: 330 }, 140, 10, 0, 0.5, 0.5);
24 | physics.addBody(world, box2);
25 | const circle = physics.createCircle(world, { x: 250, y: 150 }, 20, 5, 0.5, 0.5);
26 | physics.addBody(world, circle);
27 | return world;
28 | }
29 | export function platformerInput(world, input, on) {
30 | const player = world.dynamicBodies.find((b) => b.id === playerId);
31 | if (on) {
32 | if (input === "a" || input === "ArrowLeft") {
33 | left = true;
34 | }
35 | if (input === "d" || input === "ArrowRight") {
36 | right = true;
37 | }
38 | if (input === " " || input === "ArrowUp" || input === "w") {
39 | if (onFloor) {
40 | player.velocity.y = -JUMP_SPEED;
41 | }
42 | }
43 | }
44 | else {
45 | if (input === "a" || input === "ArrowLeft") {
46 | left = false;
47 | }
48 | if (input === "d" || input === "ArrowRight") {
49 | right = false;
50 | }
51 | }
52 | }
53 | export function platformerUpdate(world, collisions) {
54 | const player = world.dynamicBodies.find((b) => b.id === playerId);
55 | player.velocity.x = (left ? -MOVE_SPEED : 0) + (right ? MOVE_SPEED : 0);
56 | player.restingTime = 0;
57 | const allBodies = physics.allBodies(world);
58 | onFloor = collisions.find(c => {
59 | if (c.bodyAId === player.id) {
60 | const other = allBodies.find(b => b.id === c.bodyBId);
61 | return other?.center.y > player.center.y;
62 | }
63 | if (c.bodyBId === player.id) {
64 | const other = allBodies.find(b => b.id === c.bodyAId);
65 | return other?.center.y > player.center.y;
66 | }
67 | return false;
68 | }) != undefined;
69 | return;
70 | }
71 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Polybox.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | export function createNShapes(world, center, size, sides, edgeDepth = 10) {
3 | const edgeLength = 2 * size * Math.sin(Math.PI / sides);
4 | const dis = (size * Math.cos(Math.PI / sides)) - (edgeDepth / 2);
5 | const shapes = [];
6 | for (let i = 0; i < sides; i++) {
7 | const ang = (Math.PI * 2 / sides) * i;
8 | const shape = physics.createRectangleShape(world, { x: center.x + (Math.cos(ang) * dis), y: center.y + (Math.sin(ang) * dis) }, edgeDepth, edgeLength, ang);
9 | shapes.push(shape);
10 | }
11 | return shapes;
12 | }
13 | // simple shapes
14 | export function polyboxInit() {
15 | const world = physics.createWorld();
16 | world.damp = 0.98;
17 | world.restTime = 0.25;
18 | const restitution = 0.1;
19 | const friction = 0.5;
20 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, friction, restitution);
21 | physics.addBody(world, rect);
22 | const body = physics.createRigidBody(world, { x: 255, y: 50 }, 1, friction, restitution, createNShapes(world, { x: 255, y: 50 }, 40, 5, 10));
23 | physics.addBody(world, body);
24 | const body2 = physics.createRigidBody(world, { x: 135, y: 50 }, 1, friction, restitution, createNShapes(world, { x: 135, y: 50 }, 30, 3, 1));
25 | physics.addBody(world, body2);
26 | const body3 = physics.createRigidBody(world, { x: 205, y: 0 }, 1, friction, restitution, createNShapes(world, { x: 205, y: 0 }, 30, 6, 3));
27 | physics.addBody(world, body3);
28 | return world;
29 | }
30 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Sensor.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // simple shapes
3 | export function sensorInit() {
4 | const world = physics.createWorld();
5 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 1, 0);
6 | physics.addBody(world, rect);
7 | const staticSensor = physics.createRectangle(world, { x: 255, y: 200 }, 20, 20, 0, 0.5, 0.5, true);
8 | physics.addBody(world, staticSensor);
9 | const shape = physics.createRectangleShape(world, { x: 255, y: 0 }, 40, 40);
10 | const sensor = physics.createRectangleShape(world, { x: 255, y: 20 }, 40, 10, 0, true);
11 | const box = physics.createRigidBody(world, { x: 255, y: 0 }, 1, 1, 0, [shape, sensor]);
12 | box.fixedRotation = true;
13 | physics.addBody(world, box);
14 | return world;
15 | }
16 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Simple.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // simple shapes
3 | export function simpleInit() {
4 | const world = physics.createWorld();
5 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
6 | physics.addBody(world, rect);
7 | const circle = physics.createCircle(world, { x: 250, y: 150 }, 40, 1, 0.5, 0.5);
8 | physics.addBody(world, circle);
9 | const box = physics.createRectangle(world, { x: 255, y: 0 }, 40, 40, 1, 0.5, 0.5);
10 | physics.addBody(world, box);
11 | return world;
12 | }
13 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Stack.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | export function stackInit() {
3 | const world = physics.createWorld();
4 | // dampen the physics so they come to rest more easily
5 | world.damp = 0.999;
6 | world.restTime = 0.25;
7 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
8 | physics.addBody(world, rect);
9 | for (let i = 0; i < 5; i++) {
10 | const width = 5 - i;
11 | for (let x = 0; x < width; x++) {
12 | const p = 275 - (width * 50 / 2) + (x * 50);
13 | const box = physics.createRectangle(world, { x: p, y: 400 - (i * 50) }, 45, 45, 1, 0.5, 0.1);
14 | physics.addBody(world, box);
15 | }
16 | }
17 | return world;
18 | }
19 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/Teleporter.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | let frameCount = 0;
3 | let teleporter;
4 | let teleporterSpawn;
5 | export function teleporterInit() {
6 | const world = physics.createWorld();
7 | world.restTime = 1000;
8 | // teleporter
9 | teleporter = physics.createRectangle(world, { x: 140, y: 450 }, 180, 30, 0, 0.5, 0.5, true);
10 | physics.addBody(world, teleporter);
11 | // ground
12 | const rect = physics.createRectangle(world, { x: 360, y: 450 }, 180, 30, 0, 0.5, 0.5);
13 | physics.addBody(world, rect);
14 | // this is not really needed as a physical object, it's just here to reference its position
15 | teleporterSpawn = physics.createRectangle(world, { x: 360, y: 150 }, 50, 50, 0, 0.5, 0.5);
16 | physics.addBody(world, teleporterSpawn);
17 | return world;
18 | }
19 | export function teleporterUpdate(world) {
20 | // spawn a new body every second
21 | if (frameCount % 60 === 0) {
22 | const spawnPosition = { x: 50 + Math.floor(Math.random() * 180), y: 0 };
23 | let body;
24 | if (Math.random() < 0.5) {
25 | body = physics.createCircle(world, spawnPosition, 20 + (Math.random() * 20), 1, 0.5, 1, false, { debug: 'circle' });
26 | }
27 | else {
28 | body = physics.createRectangle(world, spawnPosition, 20 + (Math.random() * 20), 20 + (Math.random() * 20), 1, 0.5, 1, false, { debug: 'rectangle' });
29 | }
30 | physics.setRotation(body, Math.random() * Math.PI * 2);
31 | physics.excludeCollisions(world, body, teleporterSpawn);
32 | physics.addBody(world, body);
33 | }
34 | // teleport bodies that touch the teleporter
35 | for (const teleporterShape of teleporter.shapes.filter(shape => shape.sensor)) {
36 | for (const collidingShapeId of teleporterShape.sensorCollisions) {
37 | const collidingBody = world.dynamicBodies.find(body => body.shapes.some(s => s.id === collidingShapeId));
38 | if (collidingBody) {
39 | // teleport the body to the spawn position
40 | physics.setCenter(collidingBody, teleporterSpawn.center);
41 | // reset the body's velocity and angular velocity
42 | // - or keep their original values for maximum chaos :-)
43 | collidingBody.velocity = { x: 0, y: 0 };
44 | collidingBody.angularVelocity = 0;
45 | }
46 | }
47 | }
48 | // remove bodies that fell off the screen
49 | for (const body of world.dynamicBodies) {
50 | if (body.center.y > 600) {
51 | physics.removeBody(world, body);
52 | }
53 | }
54 | frameCount++;
55 | return;
56 | }
57 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/WobblyJoints.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | export function wobblyJointsInit() {
3 | const world = physics.createWorld();
4 | const width = 200;
5 | const center = { x: 250, y: 300 };
6 | const leftAnchorPosition = { x: center.x - width / 2, y: center.y };
7 | const rightAnchorPosition = { x: center.x + width / 2, y: center.y };
8 | const leftAnchor = physics.createCircleShape(world, leftAnchorPosition, 5);
9 | const rightAnchor = physics.createCircleShape(world, rightAnchorPosition, 5);
10 | const shape = physics.createRectangleShape(world, center, width, 50);
11 | const leftCircle = physics.createCircle(world, leftAnchorPosition, 5, 0, 0.3, 0.3);
12 | physics.addBody(world, leftCircle);
13 | const rightCircle = physics.createCircle(world, rightAnchorPosition, 5, 0, 0.3, 0.3);
14 | physics.addBody(world, rightCircle);
15 | const rect = physics.createRigidBody(world, center, 1000, 0.3, 0.3, [shape, leftAnchor, rightAnchor]);
16 | physics.addBody(world, rect);
17 | physics.createJoint(world, leftAnchor, leftCircle, 0, 0);
18 | physics.createJoint(world, rightAnchor, rightCircle, 0, 0);
19 | physics.excludeCollisions(world, rect, leftCircle);
20 | physics.excludeCollisions(world, rect, rightCircle);
21 | return world;
22 | }
23 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Avian.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | export function avianInit() {
3 | const world = physics.createWorld({ x: 0, y: 300 });
4 | world.damp = 0.98;
5 | world.restTime = 10;
6 | const friction = 0.5;
7 | const rest = 0.5;
8 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 600, 30, 0, friction, rest);
9 | physics.addBody(world, rect);
10 | const box = physics.createRectangle(world, { x: 40, y: 410 }, 60, 20, 0, friction, rest);
11 | physics.setRotation(box, -Math.PI / 6);
12 | physics.addBody(world, box);
13 | const circle = physics.createCircle(world, { x: 30, y: 420 }, 15, 0, friction, rest);
14 | physics.addBody(world, circle);
15 | let crate = physics.createRectangle(world, { x: 330, y: 400 }, 170, 15, 1, friction, rest);
16 | physics.addBody(world, crate);
17 | crate = physics.createRectangle(world, { x: 280, y: 420 }, 15, 30, 1, friction, rest);
18 | physics.addBody(world, crate);
19 | crate = physics.createRectangle(world, { x: 380, y: 420 }, 15, 30, 1, friction, rest);
20 | physics.addBody(world, crate);
21 | crate = physics.createRectangle(world, { x: 300, y: 370 }, 15, 30, 1, friction, rest);
22 | physics.addBody(world, crate);
23 | crate = physics.createRectangle(world, { x: 360, y: 370 }, 15, 30, 1, friction, rest);
24 | physics.addBody(world, crate);
25 | crate = physics.createRectangle(world, { x: 330, y: 350 }, 140, 15, 1, friction, rest);
26 | physics.addBody(world, crate);
27 | crate = physics.createRectangle(world, { x: 330, y: 330 }, 30, 50, 1, friction, rest);
28 | physics.addBody(world, crate);
29 | crate = physics.createRectangle(world, { x: 330, y: 300 }, 140, 15, 1, friction, rest);
30 | physics.addBody(world, crate);
31 | crate = physics.createRectangle(world, { x: 310, y: 270 }, 15, 30, 1, friction, rest);
32 | physics.addBody(world, crate);
33 | crate = physics.createRectangle(world, { x: 330, y: 270 }, 15, 30, 1, friction, rest);
34 | physics.addBody(world, crate);
35 | crate = physics.createRectangle(world, { x: 350, y: 270 }, 15, 30, 1, friction, rest);
36 | physics.addBody(world, crate);
37 | setTimeout(() => {
38 | const ball = physics.createCircle(world, { x: 80, y: 400 }, 10, 20, friction, rest);
39 | physics.addBody(world, ball);
40 | ball.velocity.x = 500 + Math.floor(Math.random() * 100);
41 | ball.velocity.y = -400 + Math.floor(Math.random() * 300);
42 | }, 2000);
43 | return world;
44 | }
45 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Car.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // jointed car
3 | export function carInit() {
4 | const world = physics.createWorld({ x: 0, y: 300 });
5 | world.damp = 0.99;
6 | const friction = 0.5;
7 | let rect = physics.createRectangle(world, { x: 150, y: 80 }, 400, 30, 0, friction, 0.5);
8 | physics.rotateBody(rect, Math.PI / 6);
9 | physics.addBody(world, rect);
10 | rect = physics.createRectangle(world, { x: 350, y: 250 }, 400, 30, 0, friction, 0.5);
11 | physics.rotateBody(rect, -Math.PI / 8);
12 | physics.addBody(world, rect);
13 | rect = physics.createRectangle(world, { x: 150, y: 420 }, 400, 30, 0, friction, 0.5);
14 | physics.rotateBody(rect, Math.PI / 8);
15 | physics.addBody(world, rect);
16 | const circle1 = physics.createCircle(world, { x: 50, y: 0 }, 15, 3, friction, 1);
17 | physics.addBody(world, circle1);
18 | const circle2 = physics.createCircle(world, { x: 90, y: 0 }, 15, 3, friction, 1);
19 | physics.addBody(world, circle2);
20 | physics.createJoint(world, circle1, circle2, 0.5, 0.5);
21 | return world;
22 | }
23 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Car2.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // jointed car
3 | export function car2Init() {
4 | const world = physics.createWorld({ x: 0, y: 300 });
5 | world.damp = 0.99;
6 | const friction = 1;
7 | const carX = 100;
8 | let rect = physics.createRectangle(world, { x: 150, y: 80 }, 350, 30, 0, friction, 0);
9 | physics.rotateBody(rect, Math.PI / 12);
10 | physics.addBody(world, rect);
11 | rect = physics.createRectangle(world, { x: 350, y: 250 }, 350, 30, 0, friction, 0);
12 | physics.rotateBody(rect, -Math.PI / 12);
13 | physics.addBody(world, rect);
14 | rect = physics.createRectangle(world, { x: 150, y: 420 }, 400, 30, 0, friction, 0);
15 | physics.rotateBody(rect, Math.PI / 8);
16 | physics.addBody(world, rect);
17 | const circle1 = physics.createCircle(world, { x: carX + 80, y: 0 }, 15, 3, friction, 0);
18 | physics.addBody(world, circle1);
19 | const circle2 = physics.createCircle(world, { x: carX + 120, y: 0 }, 15, 3, friction, 0);
20 | physics.addBody(world, circle2);
21 | const leftAnchor = physics.createCircleShape(world, { x: carX + 80, y: 0 }, 1);
22 | const rightAnchor = physics.createCircleShape(world, { x: carX + 120, y: 0 }, 1);
23 | const base = physics.createRectangleShape(world, { x: carX + 100, y: 0 }, 60, 10, 0);
24 | const chassis = physics.createRigidBody(world, { x: carX + 100, y: 0 }, 1, friction, 0, [base, leftAnchor, rightAnchor]);
25 | physics.addBody(world, chassis);
26 | physics.excludeCollisions(world, chassis, circle1);
27 | physics.excludeCollisions(world, chassis, circle2);
28 | physics.createJoint(world, circle1, leftAnchor, 0.5, 0.5);
29 | physics.createJoint(world, circle2, rightAnchor, 0.5, 0.5);
30 | physics.rotateBody(chassis, Math.PI / 8);
31 | return world;
32 | }
33 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Car3.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // jointed car
3 | export function car3Init() {
4 | const world = physics.createWorld({ x: 0, y: 300 });
5 | world.damp = 0.99;
6 | const friction = 0.5;
7 | let rect = physics.createRectangle(world, { x: 250, y: 420 }, 400, 30, 0, friction, 0.5);
8 | physics.addBody(world, rect);
9 | const circle1 = physics.createCircle(world, { x: 150, y: 0 }, 15, 3, friction, 0);
10 | physics.addBody(world, circle1);
11 | const circle2 = physics.createCircle(world, { x: 190, y: 0 }, 15, 3, friction, 0);
12 | physics.addBody(world, circle2);
13 | const leftAnchor = physics.createCircleShape(world, { x: 150, y: 0 }, 3);
14 | const rightAnchor = physics.createCircleShape(world, { x: 190, y: 0 }, 3);
15 | const base = physics.createRectangleShape(world, { x: 170, y: -25 }, 60, 20, 0);
16 | const chassis = physics.createRigidBody(world, { x: 170, y: 0 }, 1, friction, 0, [base, leftAnchor, rightAnchor]);
17 | physics.addBody(world, chassis);
18 | physics.excludeCollisions(world, chassis, circle1);
19 | physics.excludeCollisions(world, chassis, circle2);
20 | physics.createJoint(world, circle1, leftAnchor, 1, 0);
21 | physics.createJoint(world, circle2, rightAnchor, 1, 0);
22 | return world;
23 | }
24 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Car4.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | let chassis = undefined;
3 | // jointed car
4 | export function car4Init() {
5 | const world = physics.createWorld({ x: 0, y: 300 });
6 | world.damp = 0.99;
7 | const friction = 1;
8 | let lastAngle = 0.1;
9 | for (let i = 0; i < 50; i++) {
10 | let rect = physics.createRectangle(world, { x: 250 + (i * 390), y: 420 }, 400, 30, 0, friction, 0.5);
11 | physics.addBody(world, rect);
12 | physics.rotateBody(rect, i % 2 === 0 ? 0.2 : -0.2);
13 | }
14 | const circle1 = physics.createCircle(world, { x: 150, y: 0 }, 15, 3, friction, 0);
15 | physics.addBody(world, circle1);
16 | const circle2 = physics.createCircle(world, { x: 190, y: 0 }, 15, 3, friction, 0);
17 | physics.addBody(world, circle2);
18 | const leftAnchor = physics.createCircleShape(world, { x: 150, y: 0 }, 3, true);
19 | const rightAnchor = physics.createCircleShape(world, { x: 190, y: 0 }, 3, true);
20 | const base = physics.createRectangleShape(world, { x: 170, y: -25 }, 60, 20, 0);
21 | chassis = physics.createRigidBody(world, { x: 170, y: 0 }, 1, friction, 0, [base, leftAnchor, rightAnchor]);
22 | physics.addBody(world, chassis);
23 | physics.excludeCollisions(world, chassis, circle1);
24 | physics.excludeCollisions(world, chassis, circle2);
25 | physics.createJoint(world, circle1, leftAnchor, 1, 0);
26 | physics.createJoint(world, circle2, rightAnchor, 1, 0);
27 | return world;
28 | }
29 | export function car4Update() {
30 | chassis.velocity.x = 350;
31 | return chassis;
32 | }
33 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/CarInteractive.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | let chassis = undefined;
3 | let circle1 = undefined;
4 | let circle2 = undefined;
5 | let left = false;
6 | let right = false;
7 | const MAX_VELOCITY = 10000;
8 | const CAR_ACCEL = 2000;
9 | const CAR_TILT = 2;
10 | let lastOnGround = Date.now();
11 | let rightSensor;
12 | let leftSensor;
13 | // jointed car
14 | export function carInteractiveInit() {
15 | const world = physics.createWorld({ x: 0, y: 300 });
16 | world.damp = 0.99;
17 | world.angularDamp = 0.95;
18 | const friction = 1;
19 | let rect = physics.createRectangle(world, { x: 250, y: 458 }, 400, 30, 0, friction, 0);
20 | physics.addBody(world, rect);
21 | for (let i = 1; i < 50; i++) {
22 | let rect = physics.createRectangle(world, { x: 250 + (i * 390), y: 420 }, 400, 30, 0, friction, 0);
23 | physics.addBody(world, rect);
24 | physics.rotateBody(rect, i % 2 === 0 ? 0.2 : -0.2);
25 | }
26 | const leftAnchor = physics.createCircleShape(world, { x: 150, y: 0 }, 3);
27 | const rightAnchor = physics.createCircleShape(world, { x: 190, y: 0 }, 3);
28 | // give them a bit of padding to consume the resolution of wheels against floor
29 | leftSensor = physics.createCircleShape(world, { x: 150, y: 0 }, 16.5, true);
30 | rightSensor = physics.createCircleShape(world, { x: 190, y: 0 }, 16.5, true);
31 | const base = physics.createRectangleShape(world, { x: 170, y: -25 }, 60, 20, 0);
32 | chassis = physics.createRigidBody(world, { x: 170, y: 10 }, 1, friction, 0, [base, leftAnchor, rightAnchor, leftSensor, rightSensor]);
33 | circle1 = physics.createCircle(world, { x: 150, y: 0 }, 15, 3, friction, 0);
34 | circle2 = physics.createCircle(world, { x: 190, y: 0 }, 15, 3, friction, 0);
35 | physics.addBody(world, chassis);
36 | physics.addBody(world, circle1);
37 | physics.addBody(world, circle2);
38 | physics.excludeCollisions(world, chassis, circle1);
39 | physics.excludeCollisions(world, chassis, circle2);
40 | physics.createJoint(world, circle1, leftSensor, 1, 0);
41 | physics.createJoint(world, circle2, rightSensor, 1, 0);
42 | return world;
43 | }
44 | export function carInteractiveInput(world, input, on) {
45 | if (on) {
46 | if (input === "a" || input === "ArrowLeft") {
47 | left = true;
48 | }
49 | if (input === "d" || input === "ArrowRight") {
50 | right = true;
51 | }
52 | }
53 | else {
54 | if (input === "a" || input === "ArrowLeft") {
55 | left = false;
56 | }
57 | if (input === "d" || input === "ArrowRight") {
58 | right = false;
59 | }
60 | }
61 | }
62 | export function carInteractiveUpdate(world, collisions) {
63 | const isMidAir = !leftSensor.sensorColliding && !rightSensor.sensorColliding;
64 | chassis.restingTime = 0;
65 | circle1.restingTime = 0;
66 | circle2.restingTime = 0;
67 | const delta = 1 / 60;
68 | // since we're sure the body is on the ground it ok to drive the chassis forward since it
69 | // gives uniform velocity to the wheels
70 | if (!isMidAir) {
71 | if (left) {
72 | chassis.velocity.x = Math.max(-MAX_VELOCITY, chassis.velocity.x - CAR_ACCEL * delta);
73 | }
74 | if (right) {
75 | chassis.velocity.x = Math.min(MAX_VELOCITY, chassis.velocity.x + CAR_ACCEL * delta);
76 | }
77 | }
78 | // if we've been off the ground for a bit then allow explicitly tilting the car. Note that
79 | // this is totally non-physical so we're giving the player direct control of the car's angle
80 | // and want to ignore any other velocity/acceleration on it
81 | if (isMidAir) {
82 | if (left) {
83 | physics.rotateBody(chassis, -CAR_TILT * delta);
84 | chassis.angularVelocity = 0;
85 | }
86 | if (right) {
87 | physics.rotateBody(chassis, CAR_TILT * delta);
88 | chassis.angularVelocity = 0;
89 | }
90 | }
91 | return chassis;
92 | }
93 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Compound.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // simple shapes
3 | export function compoundInit() {
4 | const world = physics.createWorld();
5 | world.damp = 0.98;
6 | world.restTime = 0.25;
7 | const restitution = 0.1;
8 | const friction = 0.5;
9 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, friction, restitution);
10 | physics.addBody(world, rect);
11 | const rect2 = physics.createRectangle(world, { x: 230, y: 300 }, 20, 30, 0, friction, restitution);
12 | physics.addBody(world, rect2);
13 | const shape1 = physics.createRectangleShape(world, { x: 255, y: 50 }, 40, 40);
14 | const shape2 = physics.createRectangleShape(world, { x: 225, y: 50 }, 20, 80);
15 | const shape3 = physics.createRectangleShape(world, { x: 275, y: 50 }, 20, 60);
16 | const body = physics.createRigidBody(world, { x: 255, y: 50 }, 1, friction, restitution, [shape1, shape2, shape3]);
17 | physics.addBody(world, body);
18 | const shape4 = physics.createCircleShape(world, { x: 145, y: 0 }, 20);
19 | const shape5 = physics.createRectangleShape(world, { x: 125, y: 0 }, 20, 80);
20 | const body2 = physics.createRigidBody(world, { x: 145, y: 0 }, 1, friction, restitution, [shape4, shape5]);
21 | physics.addBody(world, body2);
22 | return world;
23 | }
24 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/CompoundJoint.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // simple shapes
3 | export function compoundJointInit() {
4 | const world = physics.createWorld();
5 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
6 | physics.addBody(world, rect);
7 | const rect2 = physics.createRectangle(world, { x: 250, y: 50 }, 100, 30, 0, 0.5, 0.5);
8 | physics.addBody(world, rect2);
9 | const restitution = 0.1;
10 | const friction = 0.5;
11 | const shape4 = physics.createCircleShape(world, { x: 255, y: 150 }, 20);
12 | const shape5 = physics.createRectangleShape(world, { x: 225, y: 150 }, 20, 80);
13 | const shape6 = physics.createRectangleShape(world, { x: 205, y: 120 }, 20, 20);
14 | const body2 = physics.createRigidBody(world, { x: 245, y: 150 }, 1, friction, restitution, [shape4, shape5, shape6]);
15 | physics.addBody(world, body2);
16 | physics.createJoint(world, rect2, shape6);
17 | return world;
18 | }
19 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Exclusions.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // simple shapes with exclusions
3 | export function exclusionsInit() {
4 | const world = physics.createWorld();
5 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
6 | physics.addBody(world, rect);
7 | const circle = physics.createCircle(world, { x: 250, y: 150 }, 40, 1, 0.5, 0.5);
8 | physics.addBody(world, circle);
9 | const circle2 = physics.createCircle(world, { x: 200, y: 150 }, 40, 1, 0.5, 0.5);
10 | physics.addBody(world, circle2);
11 | physics.excludeCollisions(world, circle, circle2);
12 | return world;
13 | }
14 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Goo.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | export function gooInit() {
3 | const world = physics.createWorld();
4 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
5 | physics.addBody(world, rect);
6 | const rect2 = physics.createRectangle(world, { x: 200, y: 410 }, 50, 50, 0, 0.5, 0.5);
7 | physics.addBody(world, rect2);
8 | const rect3 = physics.createRectangle(world, { x: 300, y: 410 }, 50, 50, 0, 0.5, 0.5);
9 | physics.addBody(world, rect3);
10 | const ptCount = 20;
11 | const size = 40;
12 | const bodies = [];
13 | for (let i = 0; i < ptCount; i++) {
14 | const x = 250 + (Math.cos((Math.PI * 2 / ptCount) * i) * size);
15 | const y = 100 + (Math.sin((Math.PI * 2 / ptCount) * i) * size);
16 | const point = physics.createCircle(world, { x, y }, 6, 1, 0.0, 0.0);
17 | bodies.push(point);
18 | physics.addBody(world, point);
19 | }
20 | for (let i = 0; i < ptCount; i++) {
21 | let j = (i + Math.floor(ptCount / 2)) % ptCount;
22 | physics.createJoint(world, bodies[i], bodies[j], 0.05, 0.01, true);
23 | j = (i + 1) % ptCount;
24 | physics.createJoint(world, bodies[i], bodies[j], 0.05, 0.01, true);
25 | }
26 | return world;
27 | }
28 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Joints.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // simple joints
3 | export function jointsInit() {
4 | const world = physics.createWorld();
5 | for (let i = 0; i < 3; i++) {
6 | const rect = physics.createRectangle(world, { x: 150 + (i * 70), y: 50 }, 30, 30, 0, 0, 0.5);
7 | physics.addBody(world, rect);
8 | const circle = physics.createCircle(world, { x: 150 + (i * 70), y: 250 }, 20, 1, 0, 1);
9 | physics.addBody(world, circle);
10 | physics.createJoint(world, rect, circle, 1, 0);
11 | if (i === 0) {
12 | circle.velocity.x = -150;
13 | }
14 | }
15 | return world;
16 | }
17 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Marble.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // simple shapes
3 | export function marbleInit() {
4 | const world = physics.createWorld();
5 | world.damp = 0.99;
6 | world.angularDamp = 0.95;
7 | const marble1 = physics.createCircle(world, { x: 245, y: -200 }, 20, 2, 0.3, 0.5);
8 | physics.addBody(world, marble1);
9 | const marble2 = physics.createCircle(world, { x: 275, y: -300 }, 20, 2, 0.3, 0.3);
10 | physics.addBody(world, marble2);
11 | const slope1 = physics.createRectangle(world, { x: 200, y: 200 }, 200, 10, 0, 0.3, 0.3);
12 | physics.rotateBody(slope1, Math.PI / 6);
13 | physics.addBody(world, slope1);
14 | const slope2 = physics.createRectangle(world, { x: 400, y: 300 }, 200, 10, 0, 0.3, 0.3);
15 | physics.rotateBody(slope2, -Math.PI / 6);
16 | physics.addBody(world, slope2);
17 | const wheelWing1 = physics.createRectangleShape(world, { x: 200, y: 400 }, 10, 100);
18 | const wheelWing2 = physics.createRectangleShape(world, { x: 200, y: 400 }, 10, 100, Math.PI / 2);
19 | const wheel = physics.createRigidBody(world, { x: 200, y: 400 }, 1, 0, 0.3, [wheelWing1, wheelWing2]);
20 | physics.addBody(world, wheel);
21 | const wheelJoint = physics.createCircle(world, { x: 200, y: 400 }, 10, 0, 0.3, 0.3, false);
22 | physics.addBody(world, wheelJoint);
23 | physics.excludeCollisions(world, wheel, wheelJoint);
24 | physics.createJoint(world, wheel, wheelJoint, 0, 0);
25 | return world;
26 | }
27 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/NoGravity.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // simple shapes
3 | export function noGravityInit() {
4 | const world = physics.createWorld({ x: 0, y: 0 }, 20);
5 | const circle = physics.createCircle(world, { x: 250, y: 150 }, 40, 1, 0.5, 0.5);
6 | physics.addBody(world, circle);
7 | const box = physics.createRectangle(world, { x: 255, y: 0 }, 40, 40, 1, 0.5, 0.5);
8 | physics.addBody(world, box);
9 | box.velocity.y = 25;
10 | return world;
11 | }
12 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Pile.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // every growing pile
3 | let frameCount = 0;
4 | export function pileInit() {
5 | const world = physics.createWorld();
6 | world.restTime = 1000;
7 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
8 | physics.addBody(world, rect);
9 | return world;
10 | }
11 | export function pileUpdate(world) {
12 | if (frameCount % 30 === 0) {
13 | if (Math.random() < 0.5) {
14 | const circle = physics.createCircle(world, { x: 50 + Math.floor(Math.random() * 400), y: 0 }, 20 + (Math.random() * 20), 1, 0.5, 1);
15 | physics.setRotation(circle, Math.random() * Math.PI * 2);
16 | physics.addBody(world, circle);
17 | }
18 | else {
19 | const box = physics.createRectangle(world, { x: 50 + Math.floor(Math.random() * 400), y: 0 }, 20 + (Math.random() * 20), 20 + (Math.random() * 20), 1, 0.5, 1);
20 | physics.setRotation(box, Math.random() * Math.PI * 2);
21 | physics.addBody(world, box);
22 | }
23 | }
24 | for (const body of world.dynamicBodies) {
25 | if (body.center.y > 600) {
26 | physics.removeBody(world, body);
27 | }
28 | }
29 | frameCount++;
30 | return;
31 | }
32 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Platformer.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | let player;
3 | let left = false;
4 | let right = false;
5 | let onFloor = false;
6 | const MOVE_SPEED = 60;
7 | const JUMP_SPEED = 200;
8 | // simple shapes
9 | export function platformerInit() {
10 | const world = physics.createWorld({ x: 0, y: 300 });
11 | world.damp = 1;
12 | world.angularDamp = 1;
13 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
14 | physics.addBody(world, rect);
15 | const rect2 = physics.createRectangle(world, { x: 435, y: 335 }, 30, 200, 0, 0.5, 0.5);
16 | physics.addBody(world, rect2);
17 | player = physics.createRectangle(world, { x: 255, y: 400 }, 30, 60, 1, 0, 0.5);
18 | player.fixedRotation = true;
19 | physics.addBody(world, player);
20 | const box = physics.createRectangle(world, { x: 100, y: 400 }, 40, 40, 0, 0.5, 0.5);
21 | physics.addBody(world, box);
22 | const box2 = physics.createRectangle(world, { x: 250, y: 330 }, 140, 10, 0, 0.5, 0.5);
23 | physics.addBody(world, box2);
24 | const circle = physics.createCircle(world, { x: 250, y: 150 }, 20, 5, 0.5, 0.5);
25 | physics.addBody(world, circle);
26 | return world;
27 | }
28 | export function platformerInput(world, input, on) {
29 | if (on) {
30 | if (input === "a" || input === "ArrowLeft") {
31 | left = true;
32 | }
33 | if (input === "d" || input === "ArrowRight") {
34 | right = true;
35 | }
36 | if (input === " " || input === "ArrowUp" || input === "w") {
37 | if (onFloor) {
38 | player.velocity.y = -JUMP_SPEED;
39 | }
40 | }
41 | }
42 | else {
43 | if (input === "a" || input === "ArrowLeft") {
44 | left = false;
45 | }
46 | if (input === "d" || input === "ArrowRight") {
47 | right = false;
48 | }
49 | }
50 | }
51 | export function platformerUpdate(world, collisions) {
52 | player.velocity.x = (left ? -MOVE_SPEED : 0) + (right ? MOVE_SPEED : 0);
53 | player.restingTime = 0;
54 | const allBodies = physics.allBodies(world);
55 | onFloor = collisions.find(c => {
56 | if (c.bodyAId === player.id) {
57 | const other = allBodies.find(b => b.id === c.bodyBId);
58 | return other?.center.y > player.center.y;
59 | }
60 | if (c.bodyBId === player.id) {
61 | const other = allBodies.find(b => b.id === c.bodyAId);
62 | return other?.center.y > player.center.y;
63 | }
64 | return false;
65 | }) != undefined;
66 | return;
67 | }
68 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Polybox.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | export function createNShapes(world, center, size, sides, edgeDepth = 10) {
3 | const edgeLength = 2 * size * Math.sin(Math.PI / sides);
4 | const dis = (size * Math.cos(Math.PI / sides)) - (edgeDepth / 2);
5 | const shapes = [];
6 | for (let i = 0; i < sides; i++) {
7 | const ang = (Math.PI * 2 / sides) * i;
8 | const shape = physics.createRectangleShape(world, { x: center.x + (Math.cos(ang) * dis), y: center.y + (Math.sin(ang) * dis) }, edgeDepth, edgeLength, ang);
9 | shapes.push(shape);
10 | }
11 | return shapes;
12 | }
13 | // simple shapes
14 | export function polyboxInit() {
15 | const world = physics.createWorld();
16 | world.damp = 0.98;
17 | world.restTime = 0.25;
18 | const restitution = 0.1;
19 | const friction = 0.5;
20 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, friction, restitution);
21 | physics.addBody(world, rect);
22 | const body = physics.createRigidBody(world, { x: 255, y: 50 }, 1, friction, restitution, createNShapes(world, { x: 255, y: 50 }, 40, 5, 10));
23 | physics.addBody(world, body);
24 | const body2 = physics.createRigidBody(world, { x: 135, y: 50 }, 1, friction, restitution, createNShapes(world, { x: 135, y: 50 }, 30, 3, 1));
25 | physics.addBody(world, body2);
26 | const body3 = physics.createRigidBody(world, { x: 205, y: 0 }, 1, friction, restitution, createNShapes(world, { x: 205, y: 0 }, 30, 6, 3));
27 | physics.addBody(world, body3);
28 | return world;
29 | }
30 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Sensor.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // simple shapes
3 | export function sensorInit() {
4 | const world = physics.createWorld();
5 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 1, 0);
6 | physics.addBody(world, rect);
7 | const staticSensor = physics.createRectangle(world, { x: 255, y: 200 }, 20, 20, 0, 0.5, 0.5, true);
8 | physics.addBody(world, staticSensor);
9 | const shape = physics.createRectangleShape(world, { x: 255, y: 0 }, 40, 40);
10 | const sensor = physics.createRectangleShape(world, { x: 255, y: 20 }, 40, 10, 0, true);
11 | const box = physics.createRigidBody(world, { x: 255, y: 0 }, 1, 1, 0, [shape, sensor]);
12 | box.fixedRotation = true;
13 | physics.addBody(world, box);
14 | return world;
15 | }
16 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Simple.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | // simple shapes
3 | export function simpleInit() {
4 | const world = physics.createWorld();
5 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
6 | physics.addBody(world, rect);
7 | const circle = physics.createCircle(world, { x: 250, y: 150 }, 40, 1, 0.5, 0.5);
8 | physics.addBody(world, circle);
9 | const box = physics.createRectangle(world, { x: 255, y: 0 }, 40, 40, 1, 0.5, 0.5);
10 | physics.addBody(world, box);
11 | return world;
12 | }
13 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Stack.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | export function stackInit() {
3 | const world = physics.createWorld();
4 | // dampen the physics so they come to rest more easily
5 | world.damp = 0.999;
6 | world.restTime = 0.25;
7 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
8 | physics.addBody(world, rect);
9 | for (let i = 0; i < 5; i++) {
10 | const width = 5 - i;
11 | for (let x = 0; x < width; x++) {
12 | const p = 275 - (width * 50 / 2) + (x * 50);
13 | const box = physics.createRectangle(world, { x: p, y: 400 - (i * 50) }, 45, 45, 1, 0.5, 0.1);
14 | physics.addBody(world, box);
15 | }
16 | }
17 | return world;
18 | }
19 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/Teleporter.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | let frameCount = 0;
3 | let teleporter;
4 | let teleporterSpawn;
5 | export function teleporterInit() {
6 | const world = physics.createWorld();
7 | world.restTime = 1000;
8 | // teleporter
9 | teleporter = physics.createRectangle(world, { x: 140, y: 450 }, 180, 30, 0, 0.5, 0.5, true);
10 | physics.addBody(world, teleporter);
11 | // ground
12 | const rect = physics.createRectangle(world, { x: 360, y: 450 }, 180, 30, 0, 0.5, 0.5);
13 | physics.addBody(world, rect);
14 | // this is not really needed as a physical object, it's just here to reference its position
15 | teleporterSpawn = physics.createRectangle(world, { x: 360, y: 150 }, 50, 50, 0, 0.5, 0.5);
16 | physics.addBody(world, teleporterSpawn);
17 | return world;
18 | }
19 | export function teleporterUpdate(world) {
20 | // spawn a new body every second
21 | if (frameCount % 60 === 0) {
22 | const spawnPosition = { x: 50 + Math.floor(Math.random() * 180), y: 0 };
23 | let body;
24 | if (Math.random() < 0.5) {
25 | body = physics.createCircle(world, spawnPosition, 20 + (Math.random() * 20), 1, 0.5, 1, false, { debug: 'circle' });
26 | }
27 | else {
28 | body = physics.createRectangle(world, spawnPosition, 20 + (Math.random() * 20), 20 + (Math.random() * 20), 1, 0.5, 1, false, { debug: 'rectangle' });
29 | }
30 | physics.setRotation(body, Math.random() * Math.PI * 2);
31 | physics.excludeCollisions(world, body, teleporterSpawn);
32 | physics.addBody(world, body);
33 | }
34 | // teleport bodies that touch the teleporter
35 | for (const teleporterShape of teleporter.shapes.filter(shape => shape.sensor)) {
36 | for (const collidingShapeId of teleporterShape.sensorCollisions) {
37 | const collidingBody = world.dynamicBodies.find(body => body.shapes.some(s => s.id === collidingShapeId));
38 | if (collidingBody) {
39 | // teleport the body to the spawn position
40 | physics.setCenter(collidingBody, teleporterSpawn.center);
41 | // reset the body's velocity and angular velocity
42 | // - or keep their original values for maximum chaos :-)
43 | collidingBody.velocity = { x: 0, y: 0 };
44 | collidingBody.angularVelocity = 0;
45 | }
46 | }
47 | }
48 | // remove bodies that fell off the screen
49 | for (const body of world.dynamicBodies) {
50 | if (body.center.y > 600) {
51 | physics.removeBody(world, body);
52 | }
53 | }
54 | frameCount++;
55 | return;
56 | }
57 |
--------------------------------------------------------------------------------
/examples/examples-dist/examples/src/examples/WobblyJoints.js:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 | export function wobblyJointsInit() {
3 | const world = physics.createWorld();
4 | const width = 200;
5 | const center = { x: 250, y: 300 };
6 | const leftAnchorPosition = { x: center.x - width / 2, y: center.y };
7 | const rightAnchorPosition = { x: center.x + width / 2, y: center.y };
8 | const leftAnchor = physics.createCircleShape(world, leftAnchorPosition, 5);
9 | const rightAnchor = physics.createCircleShape(world, rightAnchorPosition, 5);
10 | const shape = physics.createRectangleShape(world, center, width, 50);
11 | const leftCircle = physics.createCircle(world, leftAnchorPosition, 5, 0, 0.3, 0.3);
12 | physics.addBody(world, leftCircle);
13 | const rightCircle = physics.createCircle(world, rightAnchorPosition, 5, 0, 0.3, 0.3);
14 | physics.addBody(world, rightCircle);
15 | const rect = physics.createRigidBody(world, center, 1000, 0.3, 0.3, [shape, leftAnchor, rightAnchor]);
16 | physics.addBody(world, rect);
17 | physics.createJoint(world, leftAnchor, leftCircle, 0, 0);
18 | physics.createJoint(world, rightAnchor, rightCircle, 0, 0);
19 | physics.excludeCollisions(world, rect, leftCircle);
20 | physics.excludeCollisions(world, rect, rightCircle);
21 | return world;
22 | }
23 |
--------------------------------------------------------------------------------
/examples/examples-dist/patchMathPrecision.js:
--------------------------------------------------------------------------------
1 | // List copied from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math
2 | // Patching done according to https://www.ecma-international.org/wp-content/uploads/ECMA-262.pdf point 21.3.2 (Function properties of the Math Object)
3 | export const MATH_FUNCTIONS = [
4 | // "Math"
5 | // "E"
6 | // "LN10"
7 | // "LN2"
8 | // "LOG10E"
9 | // "LOG2E"
10 | // "PI"
11 | // "SQRT1_2"
12 | // "SQRT2"
13 | // "abs",
14 | "acos",
15 | "acosh",
16 | "asin",
17 | "asinh",
18 | "atan",
19 | "atan2",
20 | "atanh",
21 | "cbrt",
22 | // "ceil",
23 | // "clz32",
24 | "cos",
25 | "cosh",
26 | "exp",
27 | "expm1",
28 | // "floor",
29 | // "fround", -- we use fround for rounding
30 | "hypot",
31 | // "imul", -- patching imul breaks most of other math. Tests inside random/array sort start to fail
32 | "log",
33 | "log10",
34 | "log1p",
35 | "log2",
36 | // "max",
37 | // "min",
38 | "pow",
39 | // "random", -- patched every time we execute logic
40 | // "round",
41 | // "sign",
42 | "sin",
43 | "sinh",
44 | "sqrt",
45 | "tan",
46 | // "tanh",
47 | // "trunc",
48 | ];
49 | function crossBrowserPrecision(math, fn) {
50 | return (...args) => {
51 | return math.fround(fn.apply(math, args));
52 | };
53 | }
54 | // Calling math patching will happen in logicRunner & browser entry point.
55 | // But due to devUI using both at once, we make sure that we don't apply math patching multiple times on the same context.
56 | export function patchMathPrecision() {
57 | // Math was already patched, skip patching it again.
58 | // @ts-ignore
59 | if (globalThis.Math.__SDK_PRECISION_SET__) {
60 | return;
61 | }
62 | MATH_FUNCTIONS.forEach((fnName) => {
63 | globalThis.Math[fnName] = crossBrowserPrecision(globalThis.Math, globalThis.Math[fnName]);
64 | });
65 | // @ts-ignore
66 | globalThis.Math.__SDK_PRECISION_SET__ = true;
67 | }
68 |
--------------------------------------------------------------------------------
/examples/examples-dist/runePatchMathPrecision.js:
--------------------------------------------------------------------------------
1 | // List copied from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math
2 | // Patching done according to https://www.ecma-international.org/wp-content/uploads/ECMA-262.pdf point 21.3.2 (Function properties of the Math Object)
3 | export const MATH_FUNCTIONS = [
4 | // "Math"
5 | // "E"
6 | // "LN10"
7 | // "LN2"
8 | // "LOG10E"
9 | // "LOG2E"
10 | // "PI"
11 | // "SQRT1_2"
12 | // "SQRT2"
13 | // "abs",
14 | "acos",
15 | "acosh",
16 | "asin",
17 | "asinh",
18 | "atan",
19 | "atan2",
20 | "atanh",
21 | "cbrt",
22 | // "ceil",
23 | // "clz32",
24 | "cos",
25 | "cosh",
26 | "exp",
27 | "expm1",
28 | // "floor",
29 | // "fround", -- we use fround for rounding
30 | "hypot",
31 | // "imul", -- patching imul breaks most of other math. Tests inside random/array sort start to fail
32 | "log",
33 | "log10",
34 | "log1p",
35 | "log2",
36 | // "max",
37 | // "min",
38 | "pow",
39 | // "random", -- patched every time we execute logic
40 | // "round",
41 | // "sign",
42 | "sin",
43 | "sinh",
44 | "sqrt",
45 | "tan",
46 | // "tanh",
47 | // "trunc",
48 | ];
49 | function crossBrowserPrecision(math, fn) {
50 | return (...args) => {
51 | return math.fround(fn.apply(math, args));
52 | };
53 | }
54 | // Calling math patching will happen in logicRunner & browser entry point.
55 | // But due to devUI using both at once, we make sure that we don't apply math patching multiple times on the same context.
56 | export function patchMathPrecision() {
57 | // Math was already patched, skip patching it again.
58 | // @ts-ignore
59 | if (globalThis.Math.__SDK_PRECISION_SET__) {
60 | return;
61 | }
62 | MATH_FUNCTIONS.forEach((fnName) => {
63 | globalThis.Math[fnName] = crossBrowserPrecision(globalThis.Math, globalThis.Math[fnName]);
64 | });
65 | // @ts-ignore
66 | globalThis.Math.__SDK_PRECISION_SET__ = true;
67 | }
68 |
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
58 |
59 |
60 |
61 |
62 |
64 |
65 |
66 |
67 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/examples/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "propel-examples",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "tsc",
8 | "watch": "tsc --watch"
9 | },
10 | "author": "",
11 | "license": "MIT",
12 | "devDependencies": {
13 | "typescript": "^5.3.3"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/src/examples/Avian.ts:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 |
3 | export function avianUpdate(world: physics.World) {
4 | if (world.frameCount === 120) {
5 | const friction = 0.5;
6 | const rest = 0.5;
7 | const ball = physics.createCircle(world, { x: 80, y: 400 }, 10, 20, friction, rest) as physics.DynamicRigidBody;
8 | physics.addBody(world, ball);
9 | ball.velocity.x = 500 + Math.floor(Math.random() * 100);
10 | ball.velocity.y = -400 + Math.floor(Math.random() * 300);
11 | }
12 |
13 | return undefined
14 | }
15 |
16 | export function avianInit(): physics.World {
17 | const world = physics.createWorld({ x: 0, y: 300 });
18 | world.damp = 0.98;
19 | world.restTime = 10;
20 |
21 | const friction = 0.5;
22 | const rest = 0.5;
23 |
24 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 600, 30, 0, friction, rest);
25 | physics.addBody(world, rect);
26 | const box = physics.createRectangle(world, { x: 40, y: 410 }, 60, 20, 0, friction, rest);
27 | physics.setRotation(box as physics.DynamicRigidBody, -Math.PI / 6);
28 | physics.addBody(world, box);
29 | const circle = physics.createCircle(world, { x: 30, y: 420 }, 15, 0, friction, rest);
30 | physics.addBody(world, circle);
31 |
32 |
33 | let crate = physics.createRectangle(world, { x: 330, y: 400 }, 170, 15,1, friction, rest);
34 | physics.addBody(world, crate);
35 | crate = physics.createRectangle(world, { x: 280, y: 420 }, 15, 30,1, friction, rest);
36 | physics.addBody(world, crate);
37 | crate = physics.createRectangle(world, { x: 380, y: 420 }, 15, 30,1, friction, rest);
38 | physics.addBody(world, crate);
39 |
40 | crate = physics.createRectangle(world, { x: 300, y: 370 }, 15, 30,1, friction, rest);
41 | physics.addBody(world, crate);
42 | crate = physics.createRectangle(world, { x: 360, y: 370 }, 15, 30,1, friction, rest);
43 | physics.addBody(world, crate);
44 | crate = physics.createRectangle(world, { x: 330, y: 350 }, 140, 15,1, friction, rest);
45 | physics.addBody(world, crate);
46 |
47 | crate = physics.createRectangle(world, { x: 330, y: 330 }, 30, 50,1, friction, rest);
48 | physics.addBody(world, crate);
49 |
50 | crate = physics.createRectangle(world, { x: 330, y: 300 }, 140, 15,1, friction, rest);
51 | physics.addBody(world, crate);
52 |
53 | crate = physics.createRectangle(world, { x: 310, y: 270 }, 15, 30,1, friction, rest);
54 | physics.addBody(world, crate);
55 | crate = physics.createRectangle(world, { x: 330, y: 270 }, 15, 30,1, friction, rest);
56 | physics.addBody(world, crate);
57 | crate = physics.createRectangle(world, { x: 350, y: 270 }, 15, 30,1, friction, rest);
58 | physics.addBody(world, crate);
59 |
60 | return world;
61 | }
62 |
--------------------------------------------------------------------------------
/examples/src/examples/Car.ts:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 |
3 | // jointed car
4 | export function carInit() {
5 | const world = physics.createWorld({ x: 0, y: 300 });
6 | world.damp = 0.99;
7 |
8 | const friction = 0.5;
9 |
10 | let rect = physics.createRectangle(world, { x: 150, y: 80 }, 400, 30, 0, friction, 0.5);
11 | physics.rotateBody(rect, Math.PI / 6);
12 | physics.addBody(world, rect);
13 | rect = physics.createRectangle(world, { x: 350, y: 250 }, 400, 30, 0, friction, 0.5);
14 | physics.rotateBody(rect, -Math.PI / 8);
15 | physics.addBody(world, rect);
16 | rect = physics.createRectangle(world, { x: 150, y: 420 }, 400, 30, 0, friction, 0.5);
17 | physics.rotateBody(rect, Math.PI / 8);
18 | physics.addBody(world, rect);
19 |
20 | const circle1 = physics.createCircle(world, { x: 50, y: 0 }, 15, 3, friction, 1);
21 | physics.addBody(world, circle1);
22 | const circle2 = physics.createCircle(world, { x: 90, y: 0 }, 15, 3, friction, 1);
23 | physics.addBody(world, circle2);
24 | physics.createJoint(world, circle1, circle2, 0.5, 0.5);
25 |
26 | return world;
27 | }
--------------------------------------------------------------------------------
/examples/src/examples/Car2.ts:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 |
3 | // jointed car
4 | export function car2Init() {
5 | const world = physics.createWorld({ x: 0, y: 300 });
6 | world.damp = 0.99;
7 |
8 | const friction = 1;
9 | const carX = 100;
10 |
11 | let rect = physics.createRectangle(world, { x: 150, y: 80 }, 350, 30, 0, friction, 0);
12 | physics.rotateBody(rect, Math.PI / 12);
13 | physics.addBody(world, rect);
14 | rect = physics.createRectangle(world, { x: 350, y: 250 }, 350, 30, 0, friction, 0);
15 | physics.rotateBody(rect, -Math.PI / 12);
16 | physics.addBody(world, rect);
17 | rect = physics.createRectangle(world, { x: 150, y: 420 }, 400, 30, 0, friction, 0);
18 | physics.rotateBody(rect, Math.PI / 8);
19 | physics.addBody(world, rect);
20 |
21 | const circle1 = physics.createCircle(world, { x: carX + 80, y: 0 }, 15, 3, friction, 0);
22 | physics.addBody(world, circle1);
23 | const circle2 = physics.createCircle(world, { x: carX + 120, y: 0 }, 15, 3, friction, 0);
24 | physics.addBody(world, circle2);
25 |
26 | const leftAnchor = physics.createCircleShape(world, { x: carX + 80, y: 0 }, 1);
27 | const rightAnchor = physics.createCircleShape(world, { x: carX + 120, y: 0 }, 1);
28 | const base = physics.createRectangleShape(world, { x: carX + 100, y: 0 }, 60, 10, 0);
29 | const chassis = physics.createRigidBody(world, { x: carX + 100, y: 0 }, 1, friction, 0, [base, leftAnchor, rightAnchor]);
30 | physics.addBody(world, chassis);
31 | physics.excludeCollisions(world, chassis, circle1);
32 | physics.excludeCollisions(world, chassis, circle2);
33 | physics.createJoint(world, circle1, leftAnchor, 0.5, 0.5);
34 | physics.createJoint(world, circle2, rightAnchor, 0.5, 0.5);
35 |
36 | physics.rotateBody(chassis, Math.PI / 8);
37 | return world;
38 | }
--------------------------------------------------------------------------------
/examples/src/examples/Car3.ts:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 |
3 | // jointed car
4 | export function car3Init() {
5 | const world = physics.createWorld({ x: 0, y: 300 });
6 | world.damp = 0.99;
7 |
8 | const friction = 0.5;
9 |
10 | let rect = physics.createRectangle(world, { x: 250, y: 420 }, 400, 30, 0, friction, 0.5);
11 | physics.addBody(world, rect);
12 |
13 | const circle1 = physics.createCircle(world, { x: 150, y: 0 }, 15, 3, friction, 0);
14 | physics.addBody(world, circle1);
15 | const circle2 = physics.createCircle(world, { x: 190, y: 0 }, 15, 3, friction, 0);
16 | physics.addBody(world, circle2);
17 |
18 | const leftAnchor = physics.createCircleShape(world, { x: 150, y: 0 }, 3);
19 | const rightAnchor = physics.createCircleShape(world, { x: 190, y: 0 }, 3);
20 | const base = physics.createRectangleShape(world, { x: 170, y: -25 }, 60, 20, 0);
21 | const chassis = physics.createRigidBody(world, { x: 170, y: 0 }, 1, friction, 0, [base, leftAnchor, rightAnchor]);
22 | physics.addBody(world, chassis);
23 | physics.excludeCollisions(world, chassis, circle1);
24 | physics.excludeCollisions(world, chassis, circle2);
25 | physics.createJoint(world, circle1, leftAnchor, 1, 0);
26 | physics.createJoint(world, circle2, rightAnchor, 1, 0);
27 | return world;
28 | }
--------------------------------------------------------------------------------
/examples/src/examples/Car4.ts:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 |
3 | let chassisId: number;
4 |
5 | // jointed car
6 | export function car4Init() {
7 | const world = physics.createWorld({ x: 0, y: 300 });
8 | world.damp = 0.99;
9 |
10 | const friction = 1;
11 | let lastAngle = 0.1;
12 |
13 | for (let i=0;i<50;i++) {
14 | let rect = physics.createRectangle(world, { x: 250 + (i * 390), y: 420 }, 400, 30, 0, friction, 0.5);
15 | physics.addBody(world, rect);
16 |
17 | physics.rotateBody(rect, i % 2 === 0 ? 0.2 : -0.2 )
18 | }
19 | const circle1 = physics.createCircle(world, { x: 150, y: 0 }, 15, 3, friction, 0);
20 | physics.addBody(world, circle1);
21 | const circle2 = physics.createCircle(world, { x: 190, y: 0 }, 15, 3, friction, 0);
22 | physics.addBody(world, circle2);
23 |
24 | const leftAnchor = physics.createCircleShape(world, { x: 150, y: 0 }, 3, true);
25 | const rightAnchor = physics.createCircleShape(world, { x: 190, y: 0 }, 3, true);
26 | const base = physics.createRectangleShape(world, { x: 170, y: -25 }, 60, 20, 0);
27 | const chassis = physics.createRigidBody(world, { x: 170, y: 0 }, 1, friction, 0, [base, leftAnchor, rightAnchor]) as physics.DynamicRigidBody
28 | physics.addBody(world, chassis);
29 | physics.excludeCollisions(world, chassis, circle1);
30 | physics.excludeCollisions(world, chassis, circle2);
31 | physics.createJoint(world, circle1, leftAnchor, 1, 0);
32 | physics.createJoint(world, circle2, rightAnchor, 1, 0);
33 | chassisId = chassis.id
34 |
35 | return world;
36 | }
37 |
38 | export function car4Update(world: physics.World) {
39 | const chassis = world.dynamicBodies.find(b => b.id === chassisId)
40 | if (chassis) {
41 | chassis.velocity.x = 350;
42 | return chassis;
43 | }
44 | }
--------------------------------------------------------------------------------
/examples/src/examples/CarInteractive.ts:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 |
3 | let chassisId: number;
4 | let circle1Id: number;
5 | let circle2Id: number;
6 |
7 | let left: boolean = false;
8 | let right: boolean = false;
9 |
10 | const MAX_VELOCITY: number = 10000;
11 | const CAR_ACCEL: number = 2000;
12 | const CAR_TILT: number = 2
13 |
14 | let lastOnGround = Date.now();
15 | let rightSensorId: number;
16 | let leftSensorId: number;
17 |
18 | // jointed car
19 | export function carInteractiveInit() {
20 | const world = physics.createWorld({ x: 0, y: 300 });
21 | world.damp = 0.99;
22 | world.angularDamp = 0.95;
23 | const friction = 1;
24 |
25 | let rect = physics.createRectangle(world, { x: 250, y: 458 }, 400, 30, 0, friction, 0);
26 | physics.addBody(world, rect);
27 | for (let i=1;i<50;i++) {
28 | let rect = physics.createRectangle(world, { x: 250 + (i * 390), y: 420 }, 400, 30, 0, friction, 0);
29 | physics.addBody(world, rect);
30 |
31 | physics.rotateBody(rect, i % 2 === 0 ? 0.2 : -0.2 )
32 | }
33 |
34 | const leftAnchor = physics.createCircleShape(world, { x: 150, y: 0 }, 3);
35 | const rightAnchor = physics.createCircleShape(world, { x: 190, y: 0 }, 3);
36 |
37 | // give them a bit of padding to consume the resolution of wheels against floor
38 | const leftSensor = physics.createCircleShape(world, { x: 150, y: 0 }, 16.5, true);
39 | const rightSensor = physics.createCircleShape(world, { x: 190, y: 0 }, 16.5, true);
40 | leftSensorId = leftSensor.id
41 | rightSensorId = rightSensor.id
42 |
43 | const base = physics.createRectangleShape(world, { x: 170, y: -25 }, 60, 20, 0);
44 | const chassis = physics.createRigidBody(world, { x: 170, y: 10 }, 1, friction, 0, [base, leftAnchor, rightAnchor,leftSensor, rightSensor]) as physics.DynamicRigidBody
45 | const circle1 = physics.createCircle(world, { x: 150, y: 0 }, 15, 3, friction, 0) as physics.DynamicRigidBody;
46 | const circle2 = physics.createCircle(world, { x: 190, y: 0 }, 15, 3, friction, 0) as physics.DynamicRigidBody;
47 |
48 | chassisId = chassis.id;
49 | circle1Id = circle1.id;
50 | circle2Id = circle2.id;
51 |
52 | physics.addBody(world, chassis);
53 | physics.addBody(world, circle1);
54 | physics.addBody(world, circle2);
55 |
56 |
57 | physics.excludeCollisions(world, chassis, circle1);
58 | physics.excludeCollisions(world, chassis, circle2);
59 | physics.createJoint(world, circle1, leftSensor, 1, 0);
60 | physics.createJoint(world, circle2, rightSensor, 1, 0);
61 | return world;
62 | }
63 |
64 | export function carInteractiveInput(world: physics.World, input: string, on: boolean) {
65 | if (on) {
66 | if (input === "a" || input === "ArrowLeft") {
67 | left = true;
68 | }
69 | if (input === "d" || input === "ArrowRight") {
70 | right = true;
71 | }
72 | } else {
73 | if (input === "a" || input === "ArrowLeft") {
74 | left = false;
75 | }
76 | if (input === "d" || input === "ArrowRight") {
77 | right = false;
78 | }
79 | }
80 | }
81 |
82 | export function carInteractiveUpdate(world: physics.World, collisions: physics.Collision[]) {
83 | const chassis = world.dynamicBodies.find((b) => b.id === chassisId)!
84 | const circle1 = world.dynamicBodies.find((b) => b.id === circle1Id)!
85 | const circle2 = world.dynamicBodies.find((b) => b.id === circle2Id)!
86 |
87 | const leftSensor = chassis.shapes.find((s) => s.id === leftSensorId)!
88 | const rightSensor = chassis.shapes.find((s) => s.id === rightSensorId)!
89 |
90 | const isMidAir = !leftSensor.sensorColliding && !rightSensor.sensorColliding
91 |
92 | chassis.restingTime = 0;
93 | circle1.restingTime = 0;
94 | circle2.restingTime = 0;
95 |
96 | const delta = 1 / 60;
97 |
98 | // since we're sure the body is on the ground it ok to drive the chassis forward since it
99 | // gives uniform velocity to the wheels
100 | if (!isMidAir) {
101 | if (left) {
102 | chassis.velocity.x = Math.max(-MAX_VELOCITY, chassis.velocity.x - CAR_ACCEL * delta)
103 | }
104 | if (right) {
105 | chassis.velocity.x = Math.min(MAX_VELOCITY, chassis.velocity.x + CAR_ACCEL * delta)
106 | }
107 | }
108 |
109 | // if we've been off the ground for a bit then allow explicitly tilting the car. Note that
110 | // this is totally non-physical so we're giving the player direct control of the car's angle
111 | // and want to ignore any other velocity/acceleration on it
112 | if (isMidAir) {
113 | if (left) {
114 | physics.rotateBody(chassis, -CAR_TILT * delta);
115 | chassis.angularVelocity = 0;
116 | }
117 | if (right) {
118 | physics.rotateBody(chassis, CAR_TILT * delta);
119 | chassis.angularVelocity = 0;
120 | }
121 | }
122 |
123 | return chassis;
124 | }
125 |
--------------------------------------------------------------------------------
/examples/src/examples/Compound.ts:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 |
3 | // simple shapes
4 | export function compoundInit(): physics.World {
5 | const world = physics.createWorld();
6 | world.damp = 0.98;
7 | world.restTime = 0.25;
8 |
9 | const restitution = 0.1;
10 | const friction = 0.5;
11 |
12 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, friction, restitution);
13 | physics.addBody(world, rect);
14 | const rect2 = physics.createRectangle(world, { x: 230, y: 300 }, 20, 30, 0, friction, restitution);
15 | physics.addBody(world, rect2);
16 |
17 | const shape1 = physics.createRectangleShape(world, { x: 255, y: 50 }, 40, 40);
18 | const shape2 = physics.createRectangleShape(world, { x: 225, y: 50 }, 20, 80);
19 | const shape3 = physics.createRectangleShape(world, { x: 275, y: 50 }, 20, 60);
20 | const body = physics.createRigidBody(world, { x: 255, y: 50 }, 1, friction, restitution, [shape1, shape2, shape3]);
21 | physics.addBody(world, body);
22 |
23 | const shape4 = physics.createCircleShape(world, { x: 145, y: 0 }, 20);
24 | const shape5 = physics.createRectangleShape(world, { x: 125, y: 0 }, 20, 80);
25 | const body2 = physics.createRigidBody(world, { x: 145, y: 0 }, 1, friction, restitution, [shape4, shape5]);
26 | physics.addBody(world, body2);
27 | return world;
28 | }
29 |
--------------------------------------------------------------------------------
/examples/src/examples/CompoundJoint.ts:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 |
3 | // simple shapes
4 | export function compoundJointInit(): physics.World {
5 | const world = physics.createWorld();
6 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
7 | physics.addBody(world, rect);
8 | const rect2 = physics.createRectangle(world, { x: 250, y: 50 }, 100, 30, 0, 0.5, 0.5);
9 | physics.addBody(world, rect2);
10 |
11 | const restitution = 0.1;
12 | const friction = 0.5;
13 | const shape4 = physics.createCircleShape(world, { x: 255, y: 150 }, 20);
14 | const shape5 = physics.createRectangleShape(world, { x: 225, y: 150 }, 20, 80);
15 | const shape6 = physics.createRectangleShape(world, { x: 205, y: 120 }, 20, 20);
16 | const body2 = physics.createRigidBody(world, { x: 245, y: 150 }, 1, friction, restitution, [shape4, shape5, shape6]);
17 | physics.addBody(world, body2);
18 |
19 | physics.createJoint(world, rect2, shape6)
20 |
21 | return world;
22 | }
23 |
--------------------------------------------------------------------------------
/examples/src/examples/Exclusions.ts:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 |
3 | // simple shapes with exclusions
4 | export function exclusionsInit(): physics.World {
5 | const world = physics.createWorld();
6 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
7 | physics.addBody(world, rect);
8 | const circle = physics.createCircle(world, { x: 250, y: 150 }, 40, 1, 0.5, 0.5);
9 | physics.addBody(world, circle);
10 | const circle2 = physics.createCircle(world, { x: 200, y: 150 }, 40, 1, 0.5, 0.5);
11 | physics.addBody(world, circle2);
12 |
13 | physics.excludeCollisions(world, circle, circle2);
14 |
15 | return world;
16 | }
17 |
--------------------------------------------------------------------------------
/examples/src/examples/Goo.ts:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 |
3 | export function gooInit(): physics.World {
4 | const world = physics.createWorld();
5 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
6 | physics.addBody(world, rect);
7 | const rect2 = physics.createRectangle(world, { x: 200, y: 410 }, 50, 50, 0, 0.5, 0.5);
8 | physics.addBody(world, rect2);
9 | const rect3 = physics.createRectangle(world, { x: 300, y: 410 }, 50, 50, 0, 0.5, 0.5);
10 | physics.addBody(world, rect3);
11 |
12 | const ptCount = 20;
13 | const size = 40;
14 | const bodies: physics.Body[] = [];
15 | for (let i=0;i 600) {
30 | physics.removeBody(world, body);
31 | }
32 | }
33 | frameCount++;
34 |
35 | return
36 | }
37 |
--------------------------------------------------------------------------------
/examples/src/examples/Platformer.ts:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 |
3 | let playerId: number;
4 | let left: boolean = false;
5 | let right: boolean = false;
6 | let onFloor: boolean = false;
7 |
8 | const MOVE_SPEED = 60;
9 | const JUMP_SPEED = 200;
10 |
11 | // simple shapes
12 | export function platformerInit(): physics.World {
13 | const world = physics.createWorld({ x: 0, y: 300 });
14 | world.damp = 1;
15 | world.angularDamp = 1;
16 |
17 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
18 | physics.addBody(world, rect);
19 | const rect2 = physics.createRectangle(world, { x: 435, y: 335 }, 30, 200, 0, 0.5, 0.5);
20 | physics.addBody(world, rect2);
21 | const player = physics.createRectangle(world, { x: 255, y: 400 }, 30, 60, 1, 0, 0.5) as physics.DynamicRigidBody;
22 | player.fixedRotation = true;
23 | physics.addBody(world, player);
24 | playerId = player.id
25 |
26 | const box = physics.createRectangle(world, { x: 100, y: 400 }, 40, 40, 0, 0.5, 0.5);
27 | physics.addBody(world, box);
28 | const box2 = physics.createRectangle(world, { x: 250, y: 330 }, 140, 10, 0, 0.5, 0.5);
29 | physics.addBody(world, box2);
30 |
31 | const circle = physics.createCircle(world, { x: 250, y: 150 }, 20, 5, 0.5, 0.5);
32 | physics.addBody(world, circle);
33 |
34 | return world;
35 | }
36 |
37 |
38 | export function platformerInput(world: physics.World, input: string, on: boolean) {
39 | const player = world.dynamicBodies.find((b) => b.id === playerId)!
40 |
41 | if (on) {
42 | if (input === "a" || input === "ArrowLeft") {
43 | left = true;
44 | }
45 | if (input === "d" || input === "ArrowRight") {
46 | right = true;
47 | }
48 | if (input === " " || input === "ArrowUp" || input === "w") {
49 | if (onFloor) {
50 | player.velocity.y = -JUMP_SPEED;
51 | }
52 | }
53 | } else {
54 | if (input === "a" || input === "ArrowLeft") {
55 | left = false;
56 | }
57 | if (input === "d" || input === "ArrowRight") {
58 | right = false;
59 | }
60 | }
61 | }
62 |
63 | export function platformerUpdate(world: physics.World, collisions: physics.Collision[]): physics.Body | undefined {
64 | const player = world.dynamicBodies.find((b) => b.id === playerId)!
65 |
66 | player.velocity.x = (left ? -MOVE_SPEED : 0) + (right ? MOVE_SPEED : 0);
67 | player.restingTime = 0;
68 |
69 | const allBodies = physics.allBodies(world);
70 | onFloor = collisions.find(c => {
71 | if (c.bodyAId === player.id) {
72 | const other = allBodies.find(b => b.id === c.bodyBId);
73 | return other?.center.y > player.center.y;
74 | }
75 | if (c.bodyBId === player.id) {
76 | const other = allBodies.find(b => b.id === c.bodyAId);
77 | return other?.center.y > player.center.y;
78 | }
79 | return false;
80 | }) != undefined;
81 |
82 | return;
83 | }
--------------------------------------------------------------------------------
/examples/src/examples/Polybox.ts:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 |
3 | export function createNShapes(world: physics.World, center: physics.Vector2, size: number, sides: number, edgeDepth: number = 10): physics.Shape[] {
4 | const edgeLength = 2 * size * Math.sin(Math.PI / sides);
5 | const dis = (size * Math.cos(Math.PI / sides)) - (edgeDepth / 2);
6 | const shapes: physics.Shape[] = [];
7 | for (let i = 0; i < sides; i++) {
8 | const ang = (Math.PI * 2 / sides) * i;
9 | const shape = physics.createRectangleShape(world, { x: center.x + (Math.cos(ang) * dis), y: center.y + (Math.sin(ang) * dis) }, edgeDepth, edgeLength, ang);
10 | shapes.push(shape);
11 | }
12 |
13 | return shapes;
14 |
15 | }
16 |
17 | // simple shapes
18 | export function polyboxInit(): physics.World {
19 | const world = physics.createWorld();
20 | world.damp = 0.98;
21 | world.restTime = 0.25;
22 |
23 | const restitution = 0.1;
24 | const friction = 0.5;
25 |
26 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, friction, restitution);
27 | physics.addBody(world, rect);
28 |
29 |
30 | const body = physics.createRigidBody(world, { x: 255, y: 50 }, 1, friction, restitution, createNShapes(world, { x: 255, y: 50}, 40, 5, 10));
31 | physics.addBody(world, body);
32 | const body2 = physics.createRigidBody(world, { x: 135, y: 50 }, 1, friction, restitution, createNShapes(world, { x: 135, y: 50}, 30, 3, 1));
33 | physics.addBody(world, body2);
34 | const body3 = physics.createRigidBody(world, { x: 205, y: 0 }, 1, friction, restitution, createNShapes(world, { x: 205, y: 0}, 30, 6, 3));
35 | physics.addBody(world, body3);
36 |
37 | return world;
38 | }
39 |
--------------------------------------------------------------------------------
/examples/src/examples/Sensor.ts:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 |
3 | // simple shapes
4 | export function sensorInit(): physics.World {
5 | const world = physics.createWorld();
6 |
7 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 1, 0);
8 | physics.addBody(world, rect);
9 |
10 | const staticSensor = physics.createRectangle(world, { x: 255, y: 200 }, 20, 20, 0, 0.5, 0.5, true);
11 | physics.addBody(world, staticSensor);
12 |
13 | const shape = physics.createRectangleShape(world, { x: 255, y: 0 }, 40, 40);
14 | const sensor = physics.createRectangleShape(world, { x: 255, y: 20 }, 40, 10, 0, true);
15 |
16 | const box = physics.createRigidBody(world, {x: 255, y: 0 }, 1, 1, 0, [shape, sensor]) as physics.DynamicRigidBody;
17 | box.fixedRotation = true;
18 | physics.addBody(world, box);
19 |
20 | return world;
21 | }
22 |
--------------------------------------------------------------------------------
/examples/src/examples/Simple.ts:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 |
3 | // simple shapes
4 | export function simpleInit(): physics.World {
5 | const world = physics.createWorld();
6 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
7 | physics.addBody(world, rect);
8 | const circle = physics.createCircle(world, { x: 250, y: 150 }, 40, 1, 0.5, 0.5);
9 | physics.addBody(world, circle);
10 | const box = physics.createRectangle(world, { x: 255, y: 0 }, 40, 40, 1, 0.5, 0.5);
11 | physics.addBody(world, box);
12 |
13 | return world;
14 | }
15 |
--------------------------------------------------------------------------------
/examples/src/examples/Stack.ts:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 |
3 | export function stackInit() {
4 | const world = physics.createWorld();
5 |
6 | // dampen the physics so they come to rest more easily
7 | world.damp = 0.999;
8 | world.restTime = 0.25;
9 |
10 | const rect = physics.createRectangle(world, { x: 250, y: 450 }, 400, 30, 0, 0.5, 0.5);
11 | physics.addBody(world, rect);
12 |
13 | for (let i=0;i<5;i++) {
14 | const width = 5 - i;
15 | for (let x=0;x shape.sensor)) {
43 | for (const collidingShapeId of teleporterShape.sensorCollisions) {
44 | const collidingBody = world.dynamicBodies.find(body => body.shapes.some(s => s.id === collidingShapeId));
45 | if (collidingBody) {
46 | // teleport the body to the spawn position
47 | physics.setCenter(collidingBody, teleporterSpawn.center);
48 |
49 | // reset the body's velocity and angular velocity
50 | // - or keep their original values for maximum chaos :-)
51 | collidingBody.velocity = { x: 0, y: 0 };
52 | collidingBody.angularVelocity = 0;
53 | }
54 | }
55 | }
56 |
57 | // remove bodies that fell off the screen
58 | for (const body of world.dynamicBodies) {
59 | if (body.center.y > 600) {
60 | physics.removeBody(world, body);
61 | }
62 | }
63 | frameCount++;
64 |
65 | return;
66 | }
67 |
--------------------------------------------------------------------------------
/examples/src/examples/WobblyJoints.ts:
--------------------------------------------------------------------------------
1 | import { physics } from "../../../dist/index.js";
2 |
3 | export function wobblyJointsInit(): physics.World {
4 | const world = physics.createWorld();
5 |
6 | const width = 200;
7 | const center = { x: 250, y: 300 };
8 |
9 | const leftAnchorPosition = { x: center.x - width / 2, y: center.y };
10 | const rightAnchorPosition = { x: center.x + width / 2, y: center.y };
11 |
12 | const leftAnchor = physics.createCircleShape(world, leftAnchorPosition, 5);
13 | const rightAnchor = physics.createCircleShape(world, rightAnchorPosition, 5);
14 | const shape = physics.createRectangleShape(world, center, width, 50);
15 |
16 | const leftCircle = physics.createCircle(world, leftAnchorPosition, 5, 0, 0.3, 0.3);
17 | physics.addBody(world, leftCircle);
18 |
19 | const rightCircle = physics.createCircle(world, rightAnchorPosition, 5, 0, 0.3, 0.3);
20 | physics.addBody(world, rightCircle);
21 |
22 | const rect = physics.createRigidBody(world, center, 1000, 0.3, 0.3, [shape, leftAnchor, rightAnchor]);
23 | physics.addBody(world, rect);
24 |
25 | physics.createJoint(world, leftAnchor, leftCircle, 0, 0);
26 | physics.createJoint(world, rightAnchor, rightCircle, 0, 0);
27 |
28 | physics.excludeCollisions(world, rect, leftCircle);
29 | physics.excludeCollisions(world, rect, rightCircle);
30 |
31 | return world;
32 | }
--------------------------------------------------------------------------------
/examples/src/runePatchMathPrecision.ts:
--------------------------------------------------------------------------------
1 | // List copied from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math
2 | // Patching done according to https://www.ecma-international.org/wp-content/uploads/ECMA-262.pdf point 21.3.2 (Function properties of the Math Object)
3 |
4 | export const MATH_FUNCTIONS: Exclude<
5 | keyof typeof Math,
6 | // Constants, so not patching these
7 | | "Math"
8 | | "E"
9 | | "LN10"
10 | | "LN2"
11 | | "LOG10E"
12 | | "LOG2E"
13 | | "PI"
14 | | "SQRT1_2"
15 | | "SQRT2"
16 | // To comply with typescript we also exclude Symbols.
17 | | Symbol
18 | >[] = [
19 | // "Math"
20 | // "E"
21 | // "LN10"
22 | // "LN2"
23 | // "LOG10E"
24 | // "LOG2E"
25 | // "PI"
26 | // "SQRT1_2"
27 | // "SQRT2"
28 | // "abs",
29 | "acos",
30 | "acosh",
31 | "asin",
32 | "asinh",
33 | "atan",
34 | "atan2",
35 | "atanh",
36 | "cbrt",
37 | // "ceil",
38 | // "clz32",
39 | "cos",
40 | "cosh",
41 | "exp",
42 | "expm1",
43 | // "floor",
44 | // "fround", -- we use fround for rounding
45 | "hypot",
46 | // "imul", -- patching imul breaks most of other math. Tests inside random/array sort start to fail
47 | "log",
48 | "log10",
49 | "log1p",
50 | "log2",
51 | // "max",
52 | // "min",
53 | "pow",
54 | // "random", -- patched every time we execute logic
55 | // "round",
56 | // "sign",
57 | "sin",
58 | "sinh",
59 | "sqrt",
60 | "tan",
61 | // "tanh",
62 | // "trunc",
63 | ]
64 |
65 | function crossBrowserPrecision(math: Math, fn: Function) {
66 | return (...args: unknown[]) => {
67 | return math.fround(fn.apply(math, args))
68 | }
69 | }
70 |
71 | // Calling math patching will happen in logicRunner & browser entry point.
72 | // But due to devUI using both at once, we make sure that we don't apply math patching multiple times on the same context.
73 | export function patchMathPrecision() {
74 | // Math was already patched, skip patching it again.
75 | // @ts-ignore
76 | if (globalThis.Math.__SDK_PRECISION_SET__) {
77 | return
78 | }
79 |
80 | MATH_FUNCTIONS.forEach((fnName) => {
81 | globalThis.Math[fnName] = crossBrowserPrecision(
82 | globalThis.Math,
83 | globalThis.Math[fnName]
84 | )
85 | })
86 |
87 | // @ts-ignore
88 | globalThis.Math.__SDK_PRECISION_SET__ = true
89 | }
90 |
--------------------------------------------------------------------------------
/examples/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
5 | "outDir": "./examples-dist",
6 | "skipLibCheck": true,
7 | },
8 | "include": ["src"]
9 | }
--------------------------------------------------------------------------------
/examples/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "propel-js@file:../dist":
6 | version "0.0.0"
7 |
8 | typescript@^5.3.3:
9 | version "5.5.4"
10 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba"
11 | integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==
12 |
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kevglass/propel-js/9cf493eec6046758f89b270602fbdedafce1b0d0/logo.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "propel-js",
3 | "version": "1.0.25",
4 | "description": "Tiny Open Physics Engine",
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "files": [
8 | "/dist"
9 | ],
10 | "scripts": {
11 | "build": "tsc && esbuild dist/index.js --minify --outfile=dist/index.min.js",
12 | "doc": "typedoc src/index.ts",
13 | "watch": "tsc --watch",
14 | "serve": "npx serve@latest"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/kevglass/propel-js.git"
19 | },
20 | "author": "Kevin Glass",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/kevglass/propel-js/issues"
24 | },
25 | "homepage": "https://github.com/kevglass/propel-js#readme",
26 | "devDependencies": {
27 | "esbuild": "^0.23.0",
28 | "typedoc": "^0.25.8",
29 | "typescript": "^5.3.3"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/rune/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/rune/.prettierrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | semi: false,
3 | trailingComma: "es5",
4 | }
5 |
--------------------------------------------------------------------------------
/rune/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["dbaeumer.vscode-eslint"]
3 | }
4 |
--------------------------------------------------------------------------------
/rune/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Rune
2 |
3 | ### `npm run dev`
4 |
5 | Runs the game in Dev UI.
6 |
7 | The page will reload when you make changes.
8 |
9 | ### `npm run upload`
10 |
11 | Builds the game and starts upload process to Rune.
12 |
13 | ### `npm run build`
14 |
15 | Builds the game. You can then upload it to Rune using `npx rune@latest upload`.
16 |
17 | ### `npm run lint`
18 |
19 | Runs the validation rules. You can read about them in the [docs on server-side logic](https://developers.rune.ai/docs/advanced/server-side-logic).
20 |
21 | ### `npm run typecheck`
22 |
23 | Verifies that TypeScript is valid.
24 |
25 |
26 | ## Learn More
27 |
28 | See the [Rune docs](https://developers.rune.ai/docs/quick-start) for more info. You can also ask any questions in the [Rune Discord](https://discord.gg/rune-devs), we're happy to help!
29 |
--------------------------------------------------------------------------------
/rune/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import js from "@eslint/js"
2 | import prettier from "eslint-plugin-prettier/recommended"
3 | import globals from "globals"
4 | import runePlugin from "rune-sdk/eslint.js"
5 | import tseslint from "typescript-eslint"
6 |
7 | export default [
8 | {
9 | languageOptions: {
10 | globals: {
11 | ...globals.browser,
12 | ...globals.es2020,
13 | },
14 | ecmaVersion: "latest",
15 | sourceType: "module",
16 | },
17 | },
18 | js.configs.recommended,
19 | ...runePlugin.configs.recommended,
20 | ...tseslint.configs.recommended,
21 | prettier,
22 | {
23 | rules: {
24 | "prettier/prettier": "warn",
25 | },
26 | },
27 | ]
28 |
--------------------------------------------------------------------------------
/rune/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 | Propel Sandbox in Rune
10 |
11 |
12 |
13 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/rune/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rune",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite --host",
8 | "build": "npm run lint && tsc && vite build",
9 | "upload": "npm run build && npx rune@latest upload",
10 | "lint": "eslint src",
11 | "typecheck": "tsc --noEmit"
12 | },
13 | "dependencies": {
14 | "rune-sdk": "^4.25.2"
15 | },
16 | "devDependencies": {
17 | "@eslint/js": "^9.7.0",
18 | "eslint": "^9.7.0",
19 | "eslint-config-prettier": "^9.1.0",
20 | "eslint-plugin-prettier": "^5.2.1",
21 | "globals": "^15.8.0",
22 | "prettier": "^3.3.3",
23 | "typescript": "^5.4.5",
24 | "typescript-eslint": "^8.11.0",
25 | "vite": "^5.2.11",
26 | "vite-plugin-qrcode": "^0.2.2"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/rune/src/client.ts:
--------------------------------------------------------------------------------
1 | import "./styles.css"
2 |
3 | import { DEMOS, GameState } from "./logic.ts"
4 | import { physics } from "../../dist/index"
5 |
6 | const canvas = document.getElementById("render") as HTMLCanvasElement
7 | const ctx = canvas.getContext("2d") as CanvasRenderingContext2D
8 | canvas.width = 500
9 | canvas.height = 500
10 |
11 | document.getElementById("demo")!.addEventListener("change", () => {
12 | Rune.actions.selectDemo(
13 | (document.getElementById("demo") as HTMLSelectElement).value
14 | )
15 | })
16 | document.getElementById("restart")!.addEventListener("click", () => {
17 | Rune.actions.restart()
18 | })
19 |
20 | function render(game: GameState) {
21 | const world = game.world
22 |
23 | ctx.reset()
24 | ctx.resetTransform()
25 | ctx.lineWidth = 3
26 |
27 | ctx.clearRect(0, 0, 500, 500)
28 |
29 | const focusBody = physics
30 | .allBodies(world)
31 | .find((b) => b.id === game.focusBodyId)
32 | if (focusBody) {
33 | ctx.translate(-(focusBody.center.x - 250), -(focusBody.center.y - 250))
34 | }
35 | const bodies = physics.allBodies(world)
36 | for (const joint of world.joints) {
37 | ctx.strokeStyle = "yellow"
38 |
39 | const bodyA = bodies.find((b) => b.id === joint.bodyA)
40 | const centerA = joint.shapeA
41 | ? bodyA!.shapes.find((s) => s.id === joint.shapeA)!.center
42 | : bodyA!.center
43 | const bodyB = bodies.find((b) => b.id === joint.bodyB)
44 | const centerB = joint.shapeB
45 | ? bodyB!.shapes.find((s) => s.id === joint.shapeB)!.center
46 | : bodyB!.center
47 | ctx.beginPath()
48 | ctx.moveTo(centerA.x, centerA.y)
49 | ctx.lineTo(centerB.x, centerB.y)
50 | ctx.stroke()
51 | }
52 | for (const body of bodies.sort(
53 | (a, b) => (a.static ? 0 : 1) - (b.static ? 0 : 1)
54 | )) {
55 | for (const shape of body.shapes) {
56 | ctx.strokeStyle = "white"
57 | ctx.setLineDash([])
58 | if (body.static) {
59 | ctx.strokeStyle = "grey"
60 | } else if (
61 | (body as physics.DynamicRigidBody).restingTime > world.restTime
62 | ) {
63 | ctx.strokeStyle = "green"
64 | }
65 |
66 | if (shape.sensor) {
67 | ctx.strokeStyle = "yellow"
68 | ctx.setLineDash([5, 3])
69 | }
70 | if (shape.type === physics.ShapeType.CIRCLE) {
71 | ctx.save()
72 | ctx.translate(shape.center.x, shape.center.y)
73 | ctx.rotate(body.angle)
74 |
75 | ctx.beginPath()
76 | ctx.arc(0, 0, shape.bounds, 0, Math.PI * 2)
77 | ctx.stroke()
78 | ctx.beginPath()
79 | ctx.moveTo(0, 0)
80 | ctx.lineTo(0, shape.bounds)
81 | ctx.stroke()
82 |
83 | if (shape.sensor && shape.sensorColliding) {
84 | ctx.fillStyle = "rgba(255,255,0,0.7)"
85 | ctx.beginPath()
86 | ctx.arc(0, 0, shape.bounds, 0, Math.PI * 2)
87 | ctx.fill()
88 | ctx.fillStyle = "yellow"
89 | ctx.beginPath()
90 | ctx.moveTo(0, 0)
91 | ctx.lineTo(0, shape.bounds)
92 | ctx.fill()
93 | }
94 |
95 | ctx.restore()
96 | }
97 | if (shape.type === physics.ShapeType.RECTANGLE) {
98 | ctx.fillStyle = "rgba(255,255,0,0.7)"
99 | ctx.save()
100 | ctx.translate(shape.center.x, shape.center.y)
101 | ctx.rotate(body.angle + shape.angle)
102 | ctx.strokeRect(
103 | -shape.width / 2,
104 | -shape.height / 2,
105 | shape.width,
106 | shape.height
107 | )
108 |
109 | if (shape.sensor && shape.sensorColliding) {
110 | ctx.fillRect(
111 | -shape.width / 2,
112 | -shape.height / 2,
113 | shape.width,
114 | shape.height
115 | )
116 | }
117 | ctx.restore()
118 | }
119 | }
120 |
121 | if (!body.static) {
122 | const dynamic = body as physics.DynamicRigidBody
123 | ctx.fillStyle = "blue"
124 | ctx.beginPath()
125 | ctx.arc(
126 | dynamic.centerOfPhysics.x,
127 | dynamic.centerOfPhysics.y,
128 | 2,
129 | 0,
130 | Math.PI * 2
131 | )
132 | ctx.fill()
133 | }
134 | ctx.fillStyle = "red"
135 | ctx.beginPath()
136 | ctx.arc(body.center.x, body.center.y, 2, 0, Math.PI * 2)
137 | ctx.fill()
138 | }
139 | }
140 |
141 | const demoList = document.getElementById("demo") as HTMLSelectElement
142 | for (const demo of DEMOS) {
143 | const option = document.createElement("option") as HTMLOptionElement
144 | option.value = demo.name
145 | option.innerHTML = demo.name
146 | demoList.appendChild(option)
147 | }
148 |
149 | Rune.initClient({
150 | onChange: ({ game }) => {
151 | render(game)
152 | },
153 | })
154 |
--------------------------------------------------------------------------------
/rune/src/logic.ts:
--------------------------------------------------------------------------------
1 | import type { PlayerId, RuneClient } from "rune-sdk"
2 | import { physics } from "../../dist/index"
3 | import { simpleInit } from "../../examples/src/examples/Simple"
4 | import { stackInit } from "../../examples/src/examples/Stack"
5 | import { pileInit, pileUpdate } from "../../examples/src/examples/Pile"
6 | import { jointsInit } from "../../examples/src/examples/Joints"
7 | import { carInit } from "../../examples/src/examples/Car"
8 | import { avianInit, avianUpdate } from "../../examples/src/examples/Avian"
9 | import {
10 | platformerInit,
11 | platformerInput,
12 | platformerUpdate,
13 | } from "../../examples/src/examples/Platformer"
14 | import { compoundInit } from "../../examples/src/examples/Compound"
15 | import { sensorInit } from "../../examples/src/examples/Sensor"
16 | import { polyboxInit } from "../../examples/src/examples/Polybox"
17 | import { exclusionsInit } from "../../examples/src/examples/Exclusions"
18 | import { gooInit } from "../../examples/src/examples/Goo"
19 | import { compoundJointInit } from "../../examples/src/examples/CompoundJoint"
20 | import { noGravityInit } from "../../examples/src/examples/NoGravity"
21 | import { car3Init } from "../../examples/src/examples/Car3"
22 | import { car4Init, car4Update } from "../../examples/src/examples/Car4"
23 | import {
24 | carInteractiveInit,
25 | carInteractiveInput,
26 | carInteractiveUpdate,
27 | } from "../../examples/src/examples/CarInteractive"
28 | import { marbleInit } from "../../examples/src/examples/Marble"
29 | import { wobblyJointsInit } from "../../examples/src/examples/WobblyJoints"
30 | import {
31 | teleporterInit,
32 | teleporterUpdate,
33 | } from "../../examples/src/examples/Teleporter"
34 |
35 | export type DemoInit = () => physics.World
36 | export type DemoUpdate = (
37 | world: physics.World,
38 | collisions: physics.Collision[]
39 | ) => physics.Body | undefined
40 | export type DemoInput = (
41 | world: physics.World,
42 | input: string,
43 | on: boolean
44 | ) => void
45 |
46 | export interface Demo {
47 | name: string
48 | init: DemoInit
49 | update?: DemoUpdate
50 | input?: DemoInput
51 | }
52 |
53 | export const DEMOS: Demo[] = [
54 | { name: "Simple", init: simpleInit },
55 | { name: "Stacks", init: stackInit },
56 | { name: "Pile", init: pileInit, update: pileUpdate },
57 | { name: "Joints", init: jointsInit },
58 | { name: "Wobbly Joints", init: wobblyJointsInit },
59 | { name: "Car", init: carInit },
60 | { name: "Upset Avians", init: avianInit, update: avianUpdate },
61 | {
62 | name: "Platformer",
63 | init: platformerInit,
64 | input: platformerInput,
65 | update: platformerUpdate,
66 | },
67 | { name: "Sensor", init: sensorInit },
68 | { name: "Teleporter", init: teleporterInit, update: teleporterUpdate },
69 | { name: "Compound", init: compoundInit },
70 | { name: "Polybox", init: polyboxInit },
71 | { name: "Exclusions", init: exclusionsInit },
72 | { name: "Goo", init: gooInit },
73 | { name: "Compound Joint", init: compoundJointInit },
74 | { name: "No Gravity", init: noGravityInit },
75 | { name: "Car Flat", init: car3Init },
76 | { name: "Car Road", init: car4Init, update: car4Update },
77 | {
78 | name: "Car Interactive",
79 | init: carInteractiveInit,
80 | input: carInteractiveInput,
81 | update: carInteractiveUpdate,
82 | },
83 | { name: "Marble", init: marbleInit },
84 | ]
85 |
86 | export type Cells = (PlayerId | null)[]
87 | export interface GameState {
88 | world: physics.World
89 | currentDemoName: string
90 | focusBodyId: number
91 | }
92 |
93 | type GameActions = {
94 | selectDemo: (name: string) => void
95 | restart: () => void
96 | }
97 |
98 | declare global {
99 | const Rune: RuneClient
100 | }
101 |
102 | export function selectDemo(demo: string, state: GameState) {
103 | state.currentDemoName = demo
104 | restart(state)
105 | }
106 |
107 | export function restart(state: GameState) {
108 | const currentDemo = DEMOS.find((d: Demo) => d.name === state.currentDemoName)
109 | if (currentDemo) {
110 | state.world = currentDemo.init()
111 | }
112 | }
113 |
114 | Rune.initLogic({
115 | minPlayers: 1,
116 | maxPlayers: 1,
117 | updatesPerSecond: 30,
118 | update: ({ game }) => {
119 | for (let i = 0; i < 3; i++) {
120 | const collisions = physics.worldStep(60, game.world)
121 |
122 | const currentDemo = DEMOS.find(
123 | (d: Demo) => d.name === game.currentDemoName
124 | )
125 | if (currentDemo) {
126 | if (currentDemo.update) {
127 | const body = currentDemo.update(game.world, collisions)
128 | if (body) {
129 | game.focusBodyId = body.id
130 | }
131 | }
132 | }
133 | }
134 | },
135 | reactive: false,
136 | setup: () => {
137 | const state: GameState = {
138 | currentDemoName: "Simple",
139 | world: DEMOS[0].init(),
140 | focusBodyId: 0,
141 | }
142 |
143 | return state
144 | },
145 | actions: {
146 | selectDemo: (name: string, { game }) => {
147 | selectDemo(name, game)
148 | },
149 | restart: (_, { game }) => {
150 | restart(game)
151 | },
152 | },
153 | })
154 |
--------------------------------------------------------------------------------
/rune/src/styles.css:
--------------------------------------------------------------------------------
1 |
2 | body {
3 | font-family: "Roboto", sans-serif;
4 | text-align: center;
5 | }
6 |
7 | #render {
8 | width: 500px;
9 | height: 500px;
10 | max-width: 100%;
11 | background: black;
12 | }
13 |
14 | #controls {
15 | max-width: calc(100% - 10px);
16 | width: 490px;
17 | height: 30px;
18 | background: grey;
19 | margin: auto;
20 | margin-bottom: 10px;
21 | text-align: left;
22 | padding: 5px;
23 | position: relative;
24 | }
25 |
26 | #demo {
27 | position: absolute;
28 | left: 5px;
29 | height: 30px;
30 | font-size: 20px;
31 | }
32 |
33 | #restart {
34 | position: absolute;
35 | right: 5px;
36 | font-size: 20px;
37 | }
38 |
39 | #info {
40 | font-size: 20px;
41 | padding: 10px;
42 | }
43 |
44 | a {
45 | text-decoration: none;
46 | color: black;
47 | }
--------------------------------------------------------------------------------
/rune/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/rune/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
5 | "module": "ESNext",
6 | "skipLibCheck": true,
7 |
8 | /* Bundler mode */
9 | "moduleResolution": "bundler",
10 | "allowImportingTsExtensions": true,
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "noEmit": true,
14 |
15 | /* Linting */
16 | "strict": true,
17 | "noUnusedLocals": false,
18 | "noUnusedParameters": false,
19 | "noFallthroughCasesInSwitch": true
20 | },
21 | "include": ["src"],
22 | "references": [{ "path": "./tsconfig.node.json" }]
23 | }
24 |
--------------------------------------------------------------------------------
/rune/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": ["vite.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/rune/vite.config.ts:
--------------------------------------------------------------------------------
1 | import path from "node:path"
2 | import rune from "rune-sdk/vite"
3 | import { defineConfig } from "vite"
4 | import { qrcode } from "vite-plugin-qrcode"
5 |
6 | // https://vitejs.dev/config/
7 | export default defineConfig({
8 | base: "", // Makes paths relative
9 | plugins: [
10 | qrcode(), // only applies in dev mode
11 | rune({
12 | logicPath: path.resolve("./src/logic.ts"),
13 | minifyLogic: false, // This flag can be used if your logic reaches the allowed limit. However, it will make it significantly more difficult to detect validation issues
14 | ignoredDependencies: [],
15 | }),
16 | ],
17 | })
18 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
5 | "outDir": "./dist",
6 | "module": "ESNext",
7 | "skipLibCheck": true,
8 | /* Bundler mode */
9 | "moduleResolution": "bundler",
10 | "resolveJsonModule": true,
11 | "isolatedModules": true,
12 | "noEmit": false,
13 | "allowImportingTsExtensions": false,
14 | "emitDeclarationOnly": false,
15 | "declaration": true,
16 | /* Linting */
17 | "strict": true,
18 | "noUnusedLocals": false,
19 | "noUnusedParameters": false,
20 | "noFallthroughCasesInSwitch": true
21 | },
22 | "include": ["src"]
23 | }
--------------------------------------------------------------------------------
Get the length of a vector
2 |