kippy 0.1.0
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 +137 -0
- package/dist/entity.d.ts +16 -0
- package/dist/entity.js +24 -0
- package/dist/game.d.ts +18 -0
- package/dist/game.js +35 -0
- package/dist/input.d.ts +22 -0
- package/dist/input.js +70 -0
- package/dist/scene.d.ts +10 -0
- package/dist/scene.js +17 -0
- package/dist/sprite.d.ts +12 -0
- package/dist/sprite.js +10 -0
- package/index.d.ts +5 -0
- package/index.js +5 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
## Kippy
|
|
2
|
+
|
|
3
|
+
Kippy is a 2D JS game engine written purely for fun and simplicity. It currently utilizes the Canvas 2D context for rendering and aims to have a small set of APIs viable for game dev, but do expect a lot of components to change in the future.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
Install through npm:
|
|
8
|
+
```
|
|
9
|
+
npm install kippy
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Tutorial
|
|
13
|
+
|
|
14
|
+
Here is a vaguely-written tutorial for now:
|
|
15
|
+
|
|
16
|
+
### Initialize game
|
|
17
|
+
|
|
18
|
+
First, prepare a canvas tag in your html file:
|
|
19
|
+
```html
|
|
20
|
+
<canvas></canvas>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Then mount your game there:
|
|
24
|
+
```js
|
|
25
|
+
import { Game } from "kippy";
|
|
26
|
+
|
|
27
|
+
const canvas = document.querySelector("canvas");
|
|
28
|
+
const game = new Game({
|
|
29
|
+
canvas
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Start the game loop
|
|
33
|
+
game.start();
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Create a scene
|
|
37
|
+
|
|
38
|
+
Here is how you can create a scene and load that scene into your game object:
|
|
39
|
+
```js
|
|
40
|
+
import { Scene } from "kippy";
|
|
41
|
+
|
|
42
|
+
class Main extends Scene {
|
|
43
|
+
// Runs when this scene gets loaded into a game
|
|
44
|
+
init() {}
|
|
45
|
+
// Runs on every frame, dt is the time between each frame
|
|
46
|
+
update(dt) {}
|
|
47
|
+
// Runs when another scene replaces this scene in a game
|
|
48
|
+
exit() {}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Create scene and load into game
|
|
52
|
+
const main = new Main();
|
|
53
|
+
game.setScene(main);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Create an entity
|
|
57
|
+
|
|
58
|
+
A scene can have multiple entities like players, mobs, obstacles, etc in it. This is how you can create an entity:
|
|
59
|
+
```js
|
|
60
|
+
import { Entity } from "kippy";
|
|
61
|
+
|
|
62
|
+
const entity = new Entity({
|
|
63
|
+
sprite, // Entity's sprite to be rendered, type Sprite
|
|
64
|
+
x, // Entity's x position (centered), type number
|
|
65
|
+
y, // Entity's y position (centered), type number
|
|
66
|
+
rotation // Entity's rotation in radians, type number
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Add it to a scene
|
|
70
|
+
scene.addEntity(entity);
|
|
71
|
+
// Remove it from a scene
|
|
72
|
+
scene.removeEntity(entity);
|
|
73
|
+
|
|
74
|
+
// These props contain movement info and you can mutate them to edit its position
|
|
75
|
+
entity.x; // Initialized from the "x" param above, 0 if not specified
|
|
76
|
+
entity.y; // Initialized from the "y" param above, 0 if not specified
|
|
77
|
+
entity.rotation; // Initialized from the "rotation" param above, 0 if not specified
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Create a sprite
|
|
81
|
+
|
|
82
|
+
A sprite represents what an entity looks like, the "graphics part", you can create a sprite like this:
|
|
83
|
+
```js
|
|
84
|
+
const sprite = new Sprite({
|
|
85
|
+
texture, // Sprite's texture, can be HTMLImageElement, HTMLCanvasElement, OffscreenCanvas, ImageBitmap
|
|
86
|
+
width, // Sprite's width, type number
|
|
87
|
+
height // Sprite's height, type number
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Set sprite for an entity
|
|
91
|
+
entity.setSprite(sprite);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Add controls
|
|
95
|
+
|
|
96
|
+
Game controls like mouse presses, key presses, and mouse cursor traking (in the game canvas, not the web window) can be done with the `Input` class:
|
|
97
|
+
```js
|
|
98
|
+
const input = new Input({
|
|
99
|
+
canvas
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// You can assign it into a game during initialization
|
|
103
|
+
const game = new Game({
|
|
104
|
+
// other stuff
|
|
105
|
+
input
|
|
106
|
+
})
|
|
107
|
+
// or after
|
|
108
|
+
game.setInput(input);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Then in a scene's `update` method, you can use these utilities to check for key presses:
|
|
112
|
+
```js
|
|
113
|
+
// Keyboard
|
|
114
|
+
input.isKeyDown(/* Character/key here */); // Key hold
|
|
115
|
+
input.isKeyPressed(/* Character/key here */); // Key press
|
|
116
|
+
input.isKeyReleased(/* Character/key here */); // Key released
|
|
117
|
+
// Mouse
|
|
118
|
+
input.isMouseDown(/* 0 for left, 1 for right */); // Mouse hold
|
|
119
|
+
input.isMousePressed(/* 0 for left, 1 for right */); // Mouse press
|
|
120
|
+
input.isMouseReleased(/* 0 for left, 1 for right */); // Mouse released
|
|
121
|
+
input.mouseX; // Current X position of mouse
|
|
122
|
+
input.mouseY; // Current Y position of mouse
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Physics
|
|
126
|
+
|
|
127
|
+
To be added, will have rigidBody for entities with collision and movement-related stuff.
|
|
128
|
+
|
|
129
|
+
### Audio
|
|
130
|
+
|
|
131
|
+
To be added, for now use web's built-in `Audio` class.
|
|
132
|
+
|
|
133
|
+
## Copyrights and License
|
|
134
|
+
|
|
135
|
+
Copyrights © 2026 Nguyen Phu Minh.
|
|
136
|
+
|
|
137
|
+
This project is licensed under the Apache 2.0 license.
|
package/dist/entity.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Sprite } from "./sprite.js";
|
|
2
|
+
export interface EntityOptions {
|
|
3
|
+
sprite?: Sprite;
|
|
4
|
+
x?: number;
|
|
5
|
+
y?: number;
|
|
6
|
+
rotation?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class Entity {
|
|
9
|
+
sprite?: Sprite;
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
rotation: number;
|
|
13
|
+
constructor(options: EntityOptions);
|
|
14
|
+
setSprite(sprite: Sprite): void;
|
|
15
|
+
render(ctx: CanvasRenderingContext2D): void;
|
|
16
|
+
}
|
package/dist/entity.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export class Entity {
|
|
2
|
+
sprite;
|
|
3
|
+
x;
|
|
4
|
+
y;
|
|
5
|
+
rotation;
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.sprite = options.sprite;
|
|
8
|
+
this.x = options.x || 0;
|
|
9
|
+
this.y = options.y || 0;
|
|
10
|
+
this.rotation = options.rotation || 0;
|
|
11
|
+
}
|
|
12
|
+
setSprite(sprite) {
|
|
13
|
+
this.sprite = sprite;
|
|
14
|
+
}
|
|
15
|
+
render(ctx) {
|
|
16
|
+
if (this.sprite) {
|
|
17
|
+
ctx.save();
|
|
18
|
+
ctx.translate(this.x, this.y);
|
|
19
|
+
ctx.rotate(this.rotation);
|
|
20
|
+
ctx.drawImage(this.sprite.texture, -this.sprite.width / 2, -this.sprite.height / 2);
|
|
21
|
+
ctx.restore();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
package/dist/game.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Input } from "./input.js";
|
|
2
|
+
import { Scene } from "./scene.js";
|
|
3
|
+
export interface GameOptions {
|
|
4
|
+
canvas: HTMLCanvasElement;
|
|
5
|
+
input?: Input;
|
|
6
|
+
}
|
|
7
|
+
export declare class Game {
|
|
8
|
+
canvas: HTMLCanvasElement;
|
|
9
|
+
ctx: CanvasRenderingContext2D;
|
|
10
|
+
scene?: Scene;
|
|
11
|
+
lastTime: number;
|
|
12
|
+
input?: Input;
|
|
13
|
+
constructor(options: GameOptions);
|
|
14
|
+
setScene(scene: Scene): void;
|
|
15
|
+
setInput(input: Input): void;
|
|
16
|
+
start(): void;
|
|
17
|
+
loop(timestamp: number): void;
|
|
18
|
+
}
|
package/dist/game.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export class Game {
|
|
2
|
+
canvas;
|
|
3
|
+
ctx;
|
|
4
|
+
scene;
|
|
5
|
+
lastTime = 0;
|
|
6
|
+
input;
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.canvas = options.canvas;
|
|
9
|
+
this.ctx = this.canvas.getContext("2d");
|
|
10
|
+
this.input = options.input;
|
|
11
|
+
}
|
|
12
|
+
setScene(scene) {
|
|
13
|
+
this.scene?.exit();
|
|
14
|
+
this.scene = scene;
|
|
15
|
+
this.scene.init();
|
|
16
|
+
}
|
|
17
|
+
setInput(input) {
|
|
18
|
+
this.input = input;
|
|
19
|
+
}
|
|
20
|
+
start() {
|
|
21
|
+
requestAnimationFrame(this.loop.bind(this));
|
|
22
|
+
}
|
|
23
|
+
// Game loop
|
|
24
|
+
loop(timestamp) {
|
|
25
|
+
const dt = (timestamp - this.lastTime) / 1000;
|
|
26
|
+
this.lastTime = timestamp;
|
|
27
|
+
// Update state, clear canvas, re-render
|
|
28
|
+
this.scene?.update(dt);
|
|
29
|
+
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
|
|
30
|
+
this.scene?.render(this.ctx);
|
|
31
|
+
// Update input info
|
|
32
|
+
this.input?.update();
|
|
33
|
+
requestAnimationFrame(this.loop.bind(this));
|
|
34
|
+
}
|
|
35
|
+
}
|
package/dist/input.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface InputOptions {
|
|
2
|
+
canvas: HTMLCanvasElement;
|
|
3
|
+
}
|
|
4
|
+
export declare class Input {
|
|
5
|
+
canvas: HTMLCanvasElement;
|
|
6
|
+
keys: Set<string>;
|
|
7
|
+
keysPressed: Set<string>;
|
|
8
|
+
keysReleased: Set<string>;
|
|
9
|
+
mouseX: number;
|
|
10
|
+
mouseY: number;
|
|
11
|
+
mouseButtons: Set<number>;
|
|
12
|
+
mousePressed: Set<number>;
|
|
13
|
+
mouseReleased: Set<number>;
|
|
14
|
+
constructor(options: InputOptions);
|
|
15
|
+
update(): void;
|
|
16
|
+
isKeyDown(key: string): boolean;
|
|
17
|
+
isKeyPressed(key: string): boolean;
|
|
18
|
+
isKeyReleased(key: string): boolean;
|
|
19
|
+
isMouseDown(button?: number): boolean;
|
|
20
|
+
isMousePressed(button?: number): boolean;
|
|
21
|
+
isMouseReleased(button?: number): boolean;
|
|
22
|
+
}
|
package/dist/input.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export class Input {
|
|
2
|
+
canvas;
|
|
3
|
+
keys = new Set(); // Key on hold
|
|
4
|
+
keysPressed = new Set(); // Key pressed
|
|
5
|
+
keysReleased = new Set(); // Key released
|
|
6
|
+
mouseX = 0; // Mouse coord x in canvas
|
|
7
|
+
mouseY = 0; // Mouse coord y in canvas
|
|
8
|
+
mouseButtons = new Set(); // Mouse button on hold
|
|
9
|
+
mousePressed = new Set(); // Mouse button pressed
|
|
10
|
+
mouseReleased = new Set(); // Mouse button released
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.canvas = options.canvas;
|
|
13
|
+
// Keyboard
|
|
14
|
+
window.addEventListener("keydown", (e) => {
|
|
15
|
+
if (!this.keys.has(e.key)) {
|
|
16
|
+
this.keysPressed.add(e.key);
|
|
17
|
+
}
|
|
18
|
+
this.keys.add(e.key);
|
|
19
|
+
});
|
|
20
|
+
window.addEventListener("keyup", (e) => {
|
|
21
|
+
this.keys.delete(e.key);
|
|
22
|
+
this.keysReleased.add(e.key);
|
|
23
|
+
});
|
|
24
|
+
// Mouse
|
|
25
|
+
this.canvas.addEventListener("mousemove", (e) => {
|
|
26
|
+
const rect = this.canvas.getBoundingClientRect();
|
|
27
|
+
this.mouseX = e.clientX - rect.left;
|
|
28
|
+
this.mouseY = e.clientY - rect.top;
|
|
29
|
+
});
|
|
30
|
+
this.canvas.addEventListener("mousedown", (e) => {
|
|
31
|
+
if (!this.mouseButtons.has(e.button)) {
|
|
32
|
+
this.mousePressed.add(e.button);
|
|
33
|
+
}
|
|
34
|
+
this.mouseButtons.add(e.button);
|
|
35
|
+
});
|
|
36
|
+
this.canvas.addEventListener("mouseup", (e) => {
|
|
37
|
+
this.mouseButtons.delete(e.button);
|
|
38
|
+
this.mouseReleased.add(e.button);
|
|
39
|
+
});
|
|
40
|
+
// Prevent right-click menu
|
|
41
|
+
this.canvas.addEventListener("contextmenu", (e) => {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
// Called every frames
|
|
46
|
+
update() {
|
|
47
|
+
this.keysPressed.clear();
|
|
48
|
+
this.keysReleased.clear();
|
|
49
|
+
this.mousePressed.clear();
|
|
50
|
+
}
|
|
51
|
+
// Helper methods
|
|
52
|
+
isKeyDown(key) {
|
|
53
|
+
return this.keys.has(key);
|
|
54
|
+
}
|
|
55
|
+
isKeyPressed(key) {
|
|
56
|
+
return this.keysPressed.has(key);
|
|
57
|
+
}
|
|
58
|
+
isKeyReleased(key) {
|
|
59
|
+
return this.keysReleased.has(key);
|
|
60
|
+
}
|
|
61
|
+
isMouseDown(button = 0) {
|
|
62
|
+
return this.mouseButtons.has(button);
|
|
63
|
+
}
|
|
64
|
+
isMousePressed(button = 0) {
|
|
65
|
+
return this.mousePressed.has(button);
|
|
66
|
+
}
|
|
67
|
+
isMouseReleased(button = 0) {
|
|
68
|
+
return this.mouseReleased.has(button);
|
|
69
|
+
}
|
|
70
|
+
}
|
package/dist/scene.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Entity } from "./entity.js";
|
|
2
|
+
export declare abstract class Scene {
|
|
3
|
+
entities: Entity[];
|
|
4
|
+
init(): void;
|
|
5
|
+
update(deltaTime: number): void;
|
|
6
|
+
exit(): void;
|
|
7
|
+
addEntity(entity: Entity): void;
|
|
8
|
+
removeEntity(entity: Entity): void;
|
|
9
|
+
render(ctx: CanvasRenderingContext2D): void;
|
|
10
|
+
}
|
package/dist/scene.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class Scene {
|
|
2
|
+
entities = [];
|
|
3
|
+
init() { }
|
|
4
|
+
update(deltaTime) { }
|
|
5
|
+
exit() { }
|
|
6
|
+
addEntity(entity) {
|
|
7
|
+
this.entities.push(entity);
|
|
8
|
+
}
|
|
9
|
+
removeEntity(entity) {
|
|
10
|
+
this.entities = this.entities.filter(childEntities => childEntities !== entity);
|
|
11
|
+
}
|
|
12
|
+
render(ctx) {
|
|
13
|
+
for (const entity of this.entities) {
|
|
14
|
+
entity.render(ctx);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
package/dist/sprite.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type Texture = HTMLImageElement | HTMLCanvasElement | OffscreenCanvas | ImageBitmap;
|
|
2
|
+
export interface SpriteOptions {
|
|
3
|
+
texture: Texture;
|
|
4
|
+
width?: number;
|
|
5
|
+
height?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare class Sprite {
|
|
8
|
+
texture: Texture;
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
constructor(options: SpriteOptions);
|
|
12
|
+
}
|
package/dist/sprite.js
ADDED
package/index.d.ts
ADDED
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kippy",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Kippy 2D web game engine for JS",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"kippy",
|
|
7
|
+
"game-engine",
|
|
8
|
+
"game",
|
|
9
|
+
"2d",
|
|
10
|
+
"renderer",
|
|
11
|
+
"graphics",
|
|
12
|
+
"physics",
|
|
13
|
+
"library",
|
|
14
|
+
"framework",
|
|
15
|
+
"engine"
|
|
16
|
+
],
|
|
17
|
+
"homepage": "https://github.com/nguyenphuminh/kippy#readme",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/nguyenphuminh/kippy/issues"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/nguyenphuminh/kippy.git"
|
|
24
|
+
},
|
|
25
|
+
"license": "Apache-2.0",
|
|
26
|
+
"author": "nguyenphuminh",
|
|
27
|
+
"type": "module",
|
|
28
|
+
"main": "index.js",
|
|
29
|
+
"scripts": {
|
|
30
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"typescript": "^5.9.3",
|
|
34
|
+
"vite": "^7.3.1"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist/",
|
|
38
|
+
"index.d.ts",
|
|
39
|
+
"index.js",
|
|
40
|
+
"LICENSE",
|
|
41
|
+
"README.md"
|
|
42
|
+
]
|
|
43
|
+
}
|