core2d 2.10.4 → 2.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,54 +1,127 @@
1
1
  ![core2d logo](./core2d.jpg)
2
2
 
3
3
  # About
4
+
4
5
  Core2D is the powerhouse used by [Maragato マラガト](https://maragato.itch.io) apps, among others. It is the evolution of Videogame, which in turn was the evolution of Quick. In its current form, it adopts [JavaScript modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules), leveraging the full power of [Object-oriented programming](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object-oriented_programming).
5
6
 
6
7
  ## Concept
8
+
7
9
  Apps created with Core2D are made of one or more [scenes](src/Scene.mjs), which may contain one or more [sprites](src/Sprite.mjs). These objects have properties that can be customized to shape their behavior. It's that simple.
8
10
 
9
11
  # Get Started
12
+
10
13
  [Download](https://github.com/dgchrt/core2d-skel/archive/refs/heads/main.zip) or clone/fork the [skeleton project](https://github.com/dgchrt/core2d-skel/) to start building your app. Alternatively, the library can be also installed to your existing/new project:
14
+
11
15
  ```shell
12
16
  npm install core2d
13
17
  ```
14
18
 
15
19
  ## Learn
20
+
16
21
  The best way to learn is by doing, and you can see what Core2D is capable of through existing open-sourced apps. Check the [Hall of Fame](#hall-of-fame) below for some source code.
17
22
 
18
23
  ## Support
24
+
19
25
  Please consider joining the [Discussions](https://github.com/dgchrt/core2d/discussions) for collaboration and support.
20
26
 
21
27
  # Features
22
28
 
29
+ ## Scene Management
30
+
31
+ Core2D facilitates the organization of your app into one or more [scenes](src/Scene.mjs), which can be transitioned between. Each scene can contain multiple [sprites](src/Sprite.mjs), which are the basic building blocks for game objects.
32
+
33
+ ## Sound Management
34
+
35
+ The engine provides a simple yet powerful sound system that allows for playing sound effects and background music. It also supports fading out the current theme, which is useful for smooth transitions between different music tracks.
36
+
23
37
  ## Collision Detection
38
+
24
39
  Translated to callbacks, to keep update logic clean.
25
40
 
26
41
  ## Assets Caching
42
+
27
43
  Assets and their transformations are reused automatically to keep a solid performance.
28
44
 
45
+ ## Transformations
46
+
47
+ Core2D provides a set of functions to manipulate images, including rotation, flipping, and colorization. These transformations are cached, so they are only applied once per image.
48
+
49
+ ## Tilemap
50
+
51
+ The `Scene` class provides a `build` method that allows for creating tile-based scenes from a map. This is useful for creating levels in platformers, RPGs, and other genres that use tile-based graphics.
52
+
29
53
  ## User Input
54
+
30
55
  Human interaction is unified via abstractions, so that apps will just work, regardless of the devices in use.
31
56
 
32
57
  ### Controllers
58
+
33
59
  ![controllers](controller.png)
34
60
 
35
61
  Gamepads or keyboard. When using a keyboard, sensible defaults (minding accessibility) are used, as seen in [KeyMap.mjs](https://github.com/dgchrt/core2d/blob/main/src/KeyMap.mjs).
36
62
 
37
63
  ### Pointers
64
+
38
65
  ![pointer](pointer.png)
39
66
 
40
67
  Mice or touch screen.
41
68
 
42
69
  ## Virtual Resolution
70
+
43
71
  Internal geometry frees the app logic from displays, i.e. your app can have an internal logic resolution of 800x600, while running on any display size.
44
72
 
73
+ # Plugins
74
+
75
+ Core2D comes with a set of optional plugins that can be used to extend its functionality. These plugins are located in the `src/plugin` directory and can be used by importing them into your project.
76
+
77
+ - **BaseTile:** A basic tile sprite that can be used with the `Scene.build` method.
78
+ - **ClickableSprite:** A sprite that can be clicked or tapped.
79
+ - **ControllableSprite:** A sprite that can be controlled by a gamepad or keyboard.
80
+ - **CursorSprite:** A sprite that follows the pointer.
81
+ - **Fog:** A simple fog effect.
82
+ - **FontSprite:** A sprite that displays text using a custom font.
83
+ - **JumperSprite:** A sprite that can jump.
84
+ - **RandomRectTransition:** A transition that fills the screen with random rectangles.
85
+ - **Starfield:** A simple starfield effect.
86
+
45
87
  # Contributing
88
+
46
89
  The core of the library (under `src/`) should remain agnostic and lean. Updates to the core library are usually related to technology developments in the platform (web API advances), while staying true to the basic concepts of the library, which are common to all apps.
47
90
 
48
91
  Opinionated functionality should be implemented in the form of a plugin (under `src/plugin/`). Plugins can add features that are domain driven, such as elements that can be reused by multiple apps, but not necessarily by every app.
49
92
 
93
+ ## Development Setup
94
+
95
+ To contribute to Core2D, you'll need to set up your development environment.
96
+
97
+ 1. **Version Management:**
98
+ This project uses [asdf](https://asdf-vm.com/) to manage the Node.js version. To install the correct version, run:
99
+ ```shell
100
+ asdf install
101
+ ```
102
+ 2. **Install Dependencies:**
103
+ ```shell
104
+ npm install
105
+ ```
106
+ 3. **Recommended VS Code Extensions:**
107
+ This project includes a list of recommended VS Code extensions in `.vscode/extensions.json`. When you open the project in VS Code, you should be prompted to install them. These extensions will help you with code formatting and linting.
108
+ 4. **Formatting and Linting:**
109
+ This project uses [Prettier](https://prettier.io/) for code formatting and [ESLint](https://eslint.org/) for linting. The configuration for these tools is in the `.prettierrc.json` and `eslint.config.js` files, respectively.
110
+ To format the code, run:
111
+ ```shell
112
+ npm run format
113
+ ```
114
+ To lint the code, run:
115
+ ```shell
116
+ npm run lint
117
+ ```
118
+ 5. **Git Hooks:**
119
+ This project uses [husky](https://typicode.github.io/husky/) to manage Git hooks. A `pre-commit` hook is configured to run `npm test` before each commit. This ensures that all tests pass and the code is linted before it's committed. After running `npm install`, the hooks will be automatically configured.
120
+
50
121
  # Hall of Fame
122
+
51
123
  Apps created with Core2D:
124
+
52
125
  - [Asteroids Remake](https://chamun.github.io/asteroids-remake/) ([source](https://github.com/chamun/asteroids-remake))
53
126
  - [Cityscape](https://puter.com/app/cityscape) - Single-player arcade survival game
54
127
  - [Cucurbita's Halloween](https://www.kongregate.com/games/bbastudios/cucurbitas-halloween)
package/package.json CHANGED
@@ -1,50 +1,55 @@
1
1
  {
2
- "name": "core2d",
3
- "type": "module",
4
- "version": "2.10.4",
5
- "description": "Multiplatform 2D interaction engine",
6
- "files": [
7
- "src"
8
- ],
9
- "scripts": {
10
- "lint": "eslint",
11
- "prepublishOnly": "git checkout main && git pull --rebase && npm test && git push && git push --tags",
12
- "test": "npm run lint && for test in ./test/*.mjs; do node $test; done"
13
- },
14
- "repository": {
15
- "type": "git",
16
- "url": "git+https://github.com/dgchrt/core2d.git"
17
- },
18
- "keywords": [
19
- "2d",
20
- "core",
21
- "core2d",
22
- "engine",
23
- "game",
24
- "game-engine",
25
- "gamepad",
26
- "interaction",
27
- "keyboard",
28
- "mouse",
29
- "multi-platform",
30
- "multimedia",
31
- "object-oriented",
32
- "sprites",
33
- "scenes",
34
- "touch"
35
- ],
36
- "author": "Diogo Eichert",
37
- "license": "MIT",
38
- "bugs": {
39
- "url": "https://github.com/dgchrt/core2d/issues"
40
- },
41
- "funding": {
42
- "url": "https://github.com/sponsors/dgchrt"
43
- },
44
- "homepage": "https://dgchrt.github.io/core2d/",
45
- "devDependencies": {
46
- "@eslint/js": "^9.30.1",
47
- "eslint": "^9.30.1",
48
- "globals": "^16.3.0"
49
- }
2
+ "name": "core2d",
3
+ "type": "module",
4
+ "version": "2.11.2",
5
+ "description": "Multiplatform 2D interaction engine",
6
+ "files": [
7
+ "src"
8
+ ],
9
+ "scripts": {
10
+ "format": "prettier --write .",
11
+ "lint": "eslint",
12
+ "prepublishOnly": "git checkout main && git pull --rebase && npm test && git push && git push --tags",
13
+ "test": "npm run lint && for test in ./test/*.mjs; do node $test; done",
14
+ "prepare": "husky"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/dgchrt/core2d.git"
19
+ },
20
+ "keywords": [
21
+ "2d",
22
+ "core",
23
+ "core2d",
24
+ "engine",
25
+ "game",
26
+ "game-engine",
27
+ "gamepad",
28
+ "interaction",
29
+ "keyboard",
30
+ "mouse",
31
+ "multi-platform",
32
+ "multimedia",
33
+ "object-oriented",
34
+ "sprites",
35
+ "scenes",
36
+ "touch"
37
+ ],
38
+ "author": "Diogo Eichert",
39
+ "license": "MIT",
40
+ "bugs": {
41
+ "url": "https://github.com/dgchrt/core2d/issues"
42
+ },
43
+ "funding": {
44
+ "url": "https://github.com/sponsors/dgchrt"
45
+ },
46
+ "homepage": "https://dgchrt.github.io/core2d/",
47
+ "devDependencies": {
48
+ "@eslint/js": "^9.30.1",
49
+ "eslint": "^9.30.1",
50
+ "eslint-config-prettier": "^10.1.5",
51
+ "globals": "^16.3.0",
52
+ "husky": "^9.1.7",
53
+ "prettier": "^3.6.2"
54
+ }
50
55
  }
package/src/ACL.mjs CHANGED
@@ -3,70 +3,70 @@
3
3
  /**
4
4
  * Sets up the environment based on its runtime (browser or not).
5
5
  */
6
- if (typeof(global) != "undefined") {
7
- global.addEventListener = () => {};
6
+ if (typeof global != "undefined") {
7
+ global.addEventListener = () => {};
8
8
 
9
- global.document = {
10
- createElement: (name) => {
11
- if (name == "canvas") {
12
- return {
13
- getContext: () => {
14
- return {
15
- measureText: (text) => {
16
- return {
17
- width: text.length,
18
- };
19
- },
20
- };
21
- },
22
- };
23
- }
24
- },
25
- getElementById: (id) => {
26
- if (id == "app") {
27
- return {
28
- focus: () => {},
29
- getContext: () => {
30
- return {
31
- fillRect: () => {},
32
- };
33
- },
34
- height: 400,
35
- offsetLeft: 0,
36
- offsetTop: 0,
37
- style: {},
38
- width: 640,
39
- };
40
- }
9
+ global.document = {
10
+ createElement: (name) => {
11
+ if (name == "canvas") {
12
+ return {
13
+ getContext: () => {
14
+ return {
15
+ measureText: (text) => {
16
+ return {
17
+ width: text.length,
18
+ };
19
+ },
20
+ };
21
+ },
22
+ };
23
+ }
24
+ },
25
+ getElementById: (id) => {
26
+ if (id == "app") {
27
+ return {
28
+ focus: () => {},
29
+ getContext: () => {
30
+ return {
31
+ fillRect: () => {},
32
+ };
33
+ },
34
+ height: 400,
35
+ offsetLeft: 0,
36
+ offsetTop: 0,
37
+ style: {},
38
+ width: 640,
39
+ };
40
+ }
41
41
 
42
- return {};
43
- },
44
- getElementsByTagName: () => {
45
- return [];
46
- }
47
- };
42
+ return {};
43
+ },
44
+ getElementsByTagName: () => {
45
+ return [];
46
+ },
47
+ };
48
48
 
49
- global.localStorage = {};
49
+ global.localStorage = {};
50
50
 
51
- /**
52
- * For older Node.js versions.
53
- */
54
- if (!global.navigator) {
55
- global.navigator = {};
56
- }
51
+ /**
52
+ * For older Node.js versions.
53
+ */
54
+ if (!global.navigator) {
55
+ global.navigator = {};
56
+ }
57
57
 
58
- global.window = {
59
- focus: () => {},
60
- innerHeight: 600,
61
- innerWidth: 800,
62
- requestAnimationFrame: () => true
63
- };
58
+ global.window = {
59
+ focus: () => {},
60
+ innerHeight: 600,
61
+ innerWidth: 800,
62
+ requestAnimationFrame: () => true,
63
+ };
64
64
  }
65
65
 
66
66
  /**
67
67
  * Anti-Corruption Layer, to be used instead of direct API calls.
68
68
  */
69
69
  export const ACL = {
70
- document,
71
- window,
70
+ document,
71
+ window,
72
72
  };
package/src/Animation.mjs CHANGED
@@ -2,55 +2,92 @@
2
2
 
3
3
  import { Frame } from "./Frame.mjs";
4
4
 
5
+ /**
6
+ * Represents an animation, which is a sequence of frames.
7
+ */
5
8
  export class Animation {
6
- constructor(frames) {
7
- this._frames = frames;
8
- this._index = 0;
9
- this._tick = 0;
10
- }
11
-
12
- static fromImages(images, duration) {
13
- return new this(images.map(image => new Frame(image, duration)));
14
- }
15
-
16
- get image() {
17
- return this._frames[this._index].image;
18
- }
19
-
20
- get width() {
21
- return this._frames[this._index].width;
22
- }
23
-
24
- get height() {
25
- return this._frames[this._index].height;
26
- }
27
-
28
- setFrameIndex(index) {
29
- if (index < this._frames.length) {
30
- this._index = index;
31
- this._tick = 0;
32
- }
33
- }
34
-
35
- set frameIndex(index) {
36
- this.setFrameIndex(index);
37
- }
38
-
39
- sync() {
40
- const DURATION = this._frames[this._index].duration;
41
- let hasLooped = false;
42
-
43
- if (DURATION && ++this._tick >= DURATION) {
44
- let index = this._index + 1;
45
-
46
- if (index == this._frames.length) {
47
- hasLooped = true;
48
- index = 0;
49
- }
50
-
51
- this.setFrameIndex(index);
52
- }
53
-
54
- return hasLooped;
55
- }
9
+ /**
10
+ * Creates a new Animation.
11
+ * @param {Frame[]} frames The frames of the animation.
12
+ */
13
+ constructor(frames) {
14
+ this._frames = frames;
15
+ this._index = 0;
16
+ this._tick = 0;
17
+ }
18
+
19
+ /**
20
+ * Creates a new Animation from a list of images.
21
+ * @param {HTMLImageElement[]|HTMLCanvasElement[]} images The images of the animation.
22
+ * @param {number} duration The duration of each frame in ticks.
23
+ * @returns {Animation} The new animation.
24
+ */
25
+ static fromImages(images, duration) {
26
+ return new this(images.map((image) => new Frame(image, duration)));
27
+ }
28
+
29
+ /**
30
+ * The image of the current frame.
31
+ * @type {HTMLImageElement|HTMLCanvasElement}
32
+ */
33
+ get image() {
34
+ return this._frames[this._index].image;
35
+ }
36
+
37
+ /**
38
+ * The width of the current frame.
39
+ * @type {number}
40
+ */
41
+ get width() {
42
+ return this._frames[this._index].width;
43
+ }
44
+
45
+ /**
46
+ * The height of the current frame.
47
+ * @type {number}
48
+ */
49
+ get height() {
50
+ return this._frames[this._index].height;
51
+ }
52
+
53
+ /**
54
+ * Sets the index of the current frame.
55
+ * @param {number} index The index of the frame.
56
+ */
57
+ setFrameIndex(index) {
58
+ if (index < this._frames.length) {
59
+ this._index = index;
60
+ this._tick = 0;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * The index of the current frame.
66
+ * @type {number}
67
+ */
68
+ set frameIndex(index) {
69
+ this.setFrameIndex(index);
70
+ }
71
+
72
+ /**
73
+ * Synchronizes the animation.
74
+ * @returns {boolean} Whether the animation has looped.
75
+ */
76
+ sync() {
77
+ const DURATION = this._frames[this._index].duration;
78
+ let hasLooped = false;
79
+
80
+ if (DURATION && ++this._tick >= DURATION) {
81
+ let index = this._index + 1;
82
+
83
+ if (index == this._frames.length) {
84
+ hasLooped = true;
85
+ index = 0;
86
+ }
87
+
88
+ this.setFrameIndex(index);
89
+ }
90
+
91
+ return hasLooped;
92
+ }
56
93
  }
package/src/Axis.mjs CHANGED
@@ -2,9 +2,4 @@
2
2
 
3
3
  import { Static } from "./Static.mjs";
4
4
 
5
- export const Axis = Static.makeEnum([
6
- "LEFT_X",
7
- "LEFT_Y",
8
- "RIGHT_X",
9
- "RIGHT_Y",
10
- ]);
5
+ export const Axis = Static.makeEnum(["LEFT_X", "LEFT_Y", "RIGHT_X", "RIGHT_Y"]);
@@ -3,41 +3,41 @@
3
3
  import { Static } from "./Static.mjs";
4
4
 
5
5
  export const ButtonLayout = {
6
- reversed: Static.makeEnum([
7
- "B",
8
- "A",
9
- "Y",
10
- "X",
11
- "L1",
12
- "R1",
13
- "L2",
14
- "R2",
15
- "SELECT",
16
- "START",
17
- "L3",
18
- "R3",
19
- "UP",
20
- "DOWN",
21
- "LEFT",
22
- "RIGHT",
23
- ]),
6
+ reversed: Static.makeEnum([
7
+ "B",
8
+ "A",
9
+ "Y",
10
+ "X",
11
+ "L1",
12
+ "R1",
13
+ "L2",
14
+ "R2",
15
+ "SELECT",
16
+ "START",
17
+ "L3",
18
+ "R3",
19
+ "UP",
20
+ "DOWN",
21
+ "LEFT",
22
+ "RIGHT",
23
+ ]),
24
24
 
25
- standard: Static.makeEnum([
26
- "A",
27
- "B",
28
- "X",
29
- "Y",
30
- "L1",
31
- "R1",
32
- "L2",
33
- "R2",
34
- "SELECT",
35
- "START",
36
- "L3",
37
- "R3",
38
- "UP",
39
- "DOWN",
40
- "LEFT",
41
- "RIGHT",
42
- ])
25
+ standard: Static.makeEnum([
26
+ "A",
27
+ "B",
28
+ "X",
29
+ "Y",
30
+ "L1",
31
+ "R1",
32
+ "L2",
33
+ "R2",
34
+ "SELECT",
35
+ "START",
36
+ "L3",
37
+ "R3",
38
+ "UP",
39
+ "DOWN",
40
+ "LEFT",
41
+ "RIGHT",
42
+ ]),
43
43
  };
@@ -1,5 +1,5 @@
1
1
  "use static";
2
2
 
3
3
  export const ButtonLayoutMap = {
4
- "8bitdo s": "reversed"
4
+ "8bitdo s": "reversed",
5
5
  };