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 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.
@@ -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
+ }
@@ -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
+ }
@@ -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
+ }
@@ -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
@@ -0,0 +1,10 @@
1
+ export class Sprite {
2
+ texture;
3
+ width;
4
+ height;
5
+ constructor(options) {
6
+ this.texture = options.texture;
7
+ this.width = options.width || this.texture.width;
8
+ this.height = options.height || this.texture.height;
9
+ }
10
+ }
package/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export * from "./dist/game.js";
2
+ export * from "./dist/scene.js";
3
+ export * from "./dist/entity.js";
4
+ export * from "./dist/sprite.js";
5
+ export * from "./dist/input.js";
package/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from "./dist/game.js";
2
+ export * from "./dist/scene.js";
3
+ export * from "./dist/entity.js";
4
+ export * from "./dist/sprite.js";
5
+ export * from "./dist/input.js";
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
+ }