Compare commits
10 Commits
74e9b34253
...
f67def6a8d
Author | SHA1 | Date |
---|---|---|
Alexander Popov | f67def6a8d | |
Alexander Popov | 28adc41ca4 | |
Alexander Popov | fa74956afa | |
Alexander Popov | d2ea90101b | |
Alexander Popov | 96c73f49be | |
Alexander Popov | f19dafdd37 | |
Alexander Popov | e652dfba1f | |
Alexander Popov | 4ad80bcc7d | |
Alexander Popov | a4bb1f7dc9 | |
Alexander Popov | a740e6c863 |
|
@ -12,7 +12,7 @@ insert_final_newline = true
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
[{*.html,*.css,*.json}]
|
[{*.html,*.css,*.json,*.webmanifest}]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
|
@ -20,6 +20,10 @@ indent_size = 4
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.webmanifest]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
**/node_modules
|
**/node_modules
|
||||||
|
|
||||||
*.html
|
*.html
|
||||||
|
*.css
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
![engine_icon](/src/icons/apple-touch-icon.png)
|
||||||
|
|
||||||
|
## Build `ENGINE`
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# install rollup
|
||||||
|
npm install --global rollup
|
||||||
|
|
||||||
|
# compile engine
|
||||||
|
cd src/js/
|
||||||
|
rollup main.js --file engine.js --format es
|
||||||
|
```
|
|
@ -0,0 +1,2 @@
|
||||||
|
/fonts/monogram*
|
||||||
|
/assets/
|
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
After Width: | Height: | Size: 766 B |
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
|
@ -5,6 +5,10 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>ujs</title>
|
<title>ujs</title>
|
||||||
<link rel="stylesheet" type="text/css" href="styles.css">
|
<link rel="stylesheet" type="text/css" href="styles.css">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/icons/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/icons/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/icons/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="/site.webmanifest">
|
||||||
<script src="./js/game.js" type="module"></script>
|
<script src="./js/game.js" type="module"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
game.js*
|
||||||
|
engine.js
|
|
@ -0,0 +1,62 @@
|
||||||
|
import { App } from './app.js';
|
||||||
|
import { Scene, SceneLayer } from './scene.js';
|
||||||
|
import { Rect, StrokeRect, Sprite } from './objects.js';
|
||||||
|
|
||||||
|
// init player
|
||||||
|
let Player = {
|
||||||
|
x: 400 / 2 - 5,
|
||||||
|
y: 400 / 2 - 5,
|
||||||
|
rect: null,
|
||||||
|
};
|
||||||
|
Player.rect = new Rect(Player.x, Player.y, 10, 10, 'black');
|
||||||
|
Player.rect.ticker = () => {
|
||||||
|
Player.rect.y = Player.y;
|
||||||
|
Player.rect.x = Player.x;
|
||||||
|
};
|
||||||
|
|
||||||
|
// init scene layers
|
||||||
|
let layerBg = new SceneLayer('background', [
|
||||||
|
new Rect(50, 50, 100, 100, 'red'),
|
||||||
|
new StrokeRect(150, 150, 40, 40, 'green', 'blue', 1),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let layerHud = new SceneLayer('hud', [
|
||||||
|
new Sprite('compass.png', 15, 15),
|
||||||
|
new Sprite('compass-arrow.png', 27, 21),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let layerInstances = new SceneLayer('Instances', [Player.rect], { debug: true });
|
||||||
|
|
||||||
|
// init app
|
||||||
|
let app = new App(document.querySelector('canvas'), 400, 400);
|
||||||
|
|
||||||
|
// init scene
|
||||||
|
let firstScene = new Scene(app.canvas, app.context, 400, 400);
|
||||||
|
firstScene.addLayer(layerBg);
|
||||||
|
firstScene.addLayer(layerInstances);
|
||||||
|
firstScene.addLayer(layerHud);
|
||||||
|
|
||||||
|
app.scene = firstScene;
|
||||||
|
|
||||||
|
// add app view in document
|
||||||
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
document.body.appendChild(app.view);
|
||||||
|
});
|
||||||
|
|
||||||
|
// player key press listener
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
switch (e.code) {
|
||||||
|
case 'ArrowUp':
|
||||||
|
Player.y -= 1;
|
||||||
|
break;
|
||||||
|
case 'ArrowDown':
|
||||||
|
Player.y += 1;
|
||||||
|
break;
|
||||||
|
case 'ArrowLeft':
|
||||||
|
Player.x -= 1;
|
||||||
|
break;
|
||||||
|
case 'ArrowRight':
|
||||||
|
Player.x += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,19 +0,0 @@
|
||||||
import { App } from './app.js';
|
|
||||||
import { Scene, SceneLayer } from './scene.js';
|
|
||||||
import { Rect, StrokeRect } from './objects.js';
|
|
||||||
|
|
||||||
let app = new App(document.querySelector('canvas'), 400, 400);
|
|
||||||
|
|
||||||
let firstScene = new Scene(app.canvas, app.context, 400, 400);
|
|
||||||
let firstLayer = new SceneLayer('background', [
|
|
||||||
new Rect(50, 50, 100, 100, 'red'),
|
|
||||||
new StrokeRect(150, 150, 40, 40, 'green', 'blue', 1),
|
|
||||||
]);
|
|
||||||
firstScene.addLayer(firstLayer);
|
|
||||||
|
|
||||||
app.scene = firstScene;
|
|
||||||
|
|
||||||
// add app view in document
|
|
||||||
window.addEventListener('DOMContentLoaded', () => {
|
|
||||||
document.body.appendChild(app.view);
|
|
||||||
});
|
|
|
@ -1,25 +1,45 @@
|
||||||
import { Scene } from './scene.js';
|
|
||||||
import { Settings } from './settings.js';
|
import { Settings } from './settings.js';
|
||||||
import { Pointer } from './pointer.js';
|
import { Pointer } from './pointer.js';
|
||||||
|
import { Scene, SceneLayer } from './scene.js';
|
||||||
|
import { Rect, StrokeRect, Sprite, TiledSprite } from './objects.js';
|
||||||
|
|
||||||
export class App {
|
export class App {
|
||||||
|
#version;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
canvas,
|
canvas,
|
||||||
w,
|
w,
|
||||||
h,
|
h,
|
||||||
options = {
|
options = {
|
||||||
backgroundColor: '#ffcc68',
|
backgroundColor: '#ffcc68',
|
||||||
|
welcome: true,
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
this.#version = '0.1.0';
|
||||||
|
|
||||||
this.view = document.createElement('canvas');
|
this.view = document.createElement('canvas');
|
||||||
|
|
||||||
this.canvas = this.view;
|
this.canvas = this.view;
|
||||||
this.context = this.canvas.getContext('2d');
|
this.context = this.canvas.getContext('2d');
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
|
||||||
|
// check options || FIXIT:
|
||||||
|
if (typeof this.options.backgroundColor === 'undefined') {
|
||||||
|
this.options.backgroundColor = '#ffcc68';
|
||||||
|
}
|
||||||
|
if (typeof this.options.welcome === 'undefined') {
|
||||||
|
this.options.welcome = true;
|
||||||
|
}
|
||||||
|
|
||||||
this.scene = new Scene(this.canvas, this.context, w, h);
|
this.scene = new Scene(this.canvas, this.context, w, h);
|
||||||
this.prevTime = Date.now();
|
this.prevTime = Date.now();
|
||||||
|
|
||||||
|
if (this.options.welcome) {
|
||||||
|
console.log('ujs engine');
|
||||||
|
console.log('version:', this.#version);
|
||||||
|
console.log('feedback:', 'iiiypuk {dog} fastmail.fm');
|
||||||
|
}
|
||||||
|
|
||||||
Pointer.init();
|
Pointer.init();
|
||||||
|
|
||||||
this.run();
|
this.run();
|
||||||
|
@ -47,3 +67,5 @@ export class App {
|
||||||
requestAnimationFrame(this.run);
|
requestAnimationFrame(this.run);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { Scene, SceneLayer, Rect, StrokeRect, Sprite, TiledSprite };
|
|
@ -4,6 +4,7 @@ class Object {
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.width = w;
|
this.width = w;
|
||||||
this.height = h;
|
this.height = h;
|
||||||
|
this.ticker = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +14,15 @@ export class Rect extends Object {
|
||||||
|
|
||||||
this.fillColor = fillColor;
|
this.fillColor = fillColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
draw(context, debug = false) {
|
||||||
|
context.fillStyle = this.fillColor;
|
||||||
|
context.fillRect(this.x, this.y, this.width, this.height);
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
drawDebug(context, this.x, this.y, this.width, this.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StrokeRect extends Rect {
|
export class StrokeRect extends Rect {
|
||||||
|
@ -22,4 +32,109 @@ export class StrokeRect extends Rect {
|
||||||
this.strokeWidth = strokeWidth;
|
this.strokeWidth = strokeWidth;
|
||||||
this.strokeColor = strokeColor;
|
this.strokeColor = strokeColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
draw(context, debug = false) {
|
||||||
|
context.fillStyle = this.fillColor;
|
||||||
|
context.fillRect(this.x, this.y, this.width, this.height);
|
||||||
|
|
||||||
|
context.lineWidth = this.strokeWidth;
|
||||||
|
context.strokeStyle = this.strokeColor;
|
||||||
|
context.strokeRect(this.x, this.y, this.width, this.height);
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
drawDebug(context, this.x, this.y, this.width, this.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Sprite {
|
||||||
|
#loaded;
|
||||||
|
#image;
|
||||||
|
|
||||||
|
constructor(src = null, x, y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.width = null;
|
||||||
|
this.height = null;
|
||||||
|
this.angle = 0;
|
||||||
|
|
||||||
|
this.loaded = false;
|
||||||
|
|
||||||
|
this.#image = new Image();
|
||||||
|
this.#image.onload = this.#onload();
|
||||||
|
|
||||||
|
this.#image.src = src;
|
||||||
|
}
|
||||||
|
|
||||||
|
#onload() {
|
||||||
|
this.width = this.#image.width;
|
||||||
|
this.height = this.#image.height;
|
||||||
|
|
||||||
|
this.loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
image() {
|
||||||
|
return this.#image;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(context, debug = false) {
|
||||||
|
if (this.loaded != true) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.save();
|
||||||
|
context.translate(this.x + this.#image.width / 2, this.y + this.#image.height / 2);
|
||||||
|
context.rotate((this.angle * Math.PI) / 180);
|
||||||
|
context.drawImage(this.#image, -this.#image.width / 2, -this.#image.height / 2);
|
||||||
|
context.restore();
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
drawDebug(context, this.x, this.y, this.#image.width, this.#image.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TiledSprite extends Sprite {
|
||||||
|
constructor(src = null, x, y) {
|
||||||
|
super(src, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(context, debug = false) {
|
||||||
|
if (this.loaded != true) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.drawImage(this.image(), this.x, this.y);
|
||||||
|
let copyUpByY = Math.ceil(this.y / this.image().height);
|
||||||
|
let copyDownByY = Math.ceil(
|
||||||
|
(context.canvas.height - this.y - this.image().height) / this.image().height
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let i = 0; i <= copyUpByY; i++) {
|
||||||
|
if (copyUpByY === Infinity) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.drawImage(this.image(), this.x, this.y - this.image().height * i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i <= copyDownByY; i++) {
|
||||||
|
if (copyDownByY === Infinity) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.drawImage(this.image(), this.x, this.y + this.image().height * i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawDebug(context, x, y, w, h) {
|
||||||
|
context.font = '16px Monogram';
|
||||||
|
context.fillStyle = '#ffffff';
|
||||||
|
context.fillText(`${x}x${y}`, x + 1, y - 2);
|
||||||
|
context.fillStyle = '#000000';
|
||||||
|
context.fillText(`${x}x${y}`, x, y - 3);
|
||||||
|
context.lineWidth = 1;
|
||||||
|
context.strokeStyle = 'blue';
|
||||||
|
context.strokeRect(x, y, w, h);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,50 +14,21 @@ export class Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
run() {
|
run() {
|
||||||
this.#scene();
|
|
||||||
}
|
|
||||||
|
|
||||||
#scene() {
|
|
||||||
// read layers & draw items
|
|
||||||
this.#layers.forEach((layer) => {
|
this.#layers.forEach((layer) => {
|
||||||
layer.objects().forEach((item) => {
|
layer.objects().forEach((item) => {
|
||||||
switch (item.constructor.name) {
|
if (typeof item.ticker == 'function') {
|
||||||
case 'Rect':
|
item.ticker();
|
||||||
this.#drawRect(item.x, item.y, item.width, item.height, item.fillColor);
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case 'StrokeRect':
|
if (typeof item.draw == 'function') {
|
||||||
this.#drawStrokeRect(
|
item.draw(this.#context, layer.options.debug);
|
||||||
item.x,
|
} else {
|
||||||
item.y,
|
console.log(`⛔ Error display '${item.constructor.name}' object.`);
|
||||||
item.width,
|
|
||||||
item.height,
|
|
||||||
item.fillColor,
|
|
||||||
item.strokeColor,
|
|
||||||
item.strokeWidth
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
console.log(`⛔ Error display '${item.constructor.name}' object.`);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#drawRect(x, y, w, h, fillColor) {
|
|
||||||
this.#context.fillStyle = fillColor;
|
|
||||||
this.#context.fillRect(x, y, w, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
#drawStrokeRect(x, y, w, h, fillColor, strokeColor, strokeWidth) {
|
|
||||||
this.#drawRect(x, y, w, h, fillColor);
|
|
||||||
|
|
||||||
this.#context.lineWidth = strokeWidth;
|
|
||||||
this.#context.strokeStyle = strokeColor;
|
|
||||||
this.#context.strokeRect(x, y, w, h);
|
|
||||||
}
|
|
||||||
|
|
||||||
addLayer(layer) {
|
addLayer(layer) {
|
||||||
this.#layers.push(layer);
|
this.#layers.push(layer);
|
||||||
}
|
}
|
||||||
|
@ -66,6 +37,7 @@ export class Scene {
|
||||||
this.#layers = Array();
|
this.#layers = Array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: remove it
|
||||||
setScreenSize(w, h) {
|
setScreenSize(w, h) {
|
||||||
this.#canvas.width = w;
|
this.#canvas.width = w;
|
||||||
this.#canvas.height = h;
|
this.#canvas.height = h;
|
||||||
|
@ -75,9 +47,21 @@ export class Scene {
|
||||||
export class SceneLayer {
|
export class SceneLayer {
|
||||||
#objects;
|
#objects;
|
||||||
|
|
||||||
constructor(name, objects = Array()) {
|
constructor(
|
||||||
|
name,
|
||||||
|
objects = Array(),
|
||||||
|
options = {
|
||||||
|
debug: false,
|
||||||
|
}
|
||||||
|
) {
|
||||||
// TODO: check types
|
// TODO: check types
|
||||||
this.#objects = Array();
|
this.#objects = Array();
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
// check options || FIXIT:
|
||||||
|
if (typeof this.options.debug === 'undefined') {
|
||||||
|
this.options.debug = false;
|
||||||
|
}
|
||||||
|
|
||||||
objects.forEach((object) => {
|
objects.forEach((object) => {
|
||||||
this.#objects.push(object);
|
this.#objects.push(object);
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"short_name": "",
|
||||||
|
"icons": [
|
||||||
|
{ "src": "/icons/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" },
|
||||||
|
{ "src": "/icons/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" }
|
||||||
|
],
|
||||||
|
"theme_color": "#ffffff",
|
||||||
|
"background_color": "#ffffff",
|
||||||
|
"display": "standalone"
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Monogram';
|
||||||
|
src: url('/fonts/monogramextended.woff2') format('woff2'),
|
||||||
|
url('/fonts/monogramextended.woff') format('woff'),
|
||||||
|
url('/fonts/monogramextended.ttf') format('truetype');
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
image-rendering: crisp-edges;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
}
|
Loading…
Reference in New Issue