implement basic arkanoid version
This commit is contained in:
+2
-2
@@ -95,8 +95,8 @@ module.exports = {
|
|||||||
'linebreak-style': 'error',
|
'linebreak-style': 'error',
|
||||||
'lines-around-comment': 0,
|
'lines-around-comment': 0,
|
||||||
'lines-around-directive': 'error',
|
'lines-around-directive': 'error',
|
||||||
'lines-between-class-members': 'error',
|
'lines-between-class-members': 0,
|
||||||
'max-classes-per-file': 'error',
|
'max-classes-per-file': 0,
|
||||||
'max-depth': 'error',
|
'max-depth': 'error',
|
||||||
'max-len': ['error', 180],
|
'max-len': ['error', 180],
|
||||||
'max-lines': 'error',
|
'max-lines': 'error',
|
||||||
|
|||||||
Generated
+1214
-13819
File diff suppressed because it is too large
Load Diff
+1
-4
@@ -14,11 +14,8 @@
|
|||||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||||
"eslint-plugin-react": "^7.22.0",
|
"eslint-plugin-react": "^7.22.0",
|
||||||
"eslint-plugin-react-hooks": "^4.2.0",
|
"eslint-plugin-react-hooks": "^4.2.0",
|
||||||
"parcel": "^2.0.0-rc.0",
|
"parcel": "^2.0.1",
|
||||||
"tslib": "^2.0.3",
|
"tslib": "^2.0.3",
|
||||||
"typescript": "^4.0.5"
|
"typescript": "^4.0.5"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"eslint-plugin-react": "^7.26.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
import { Game } from '../Game';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
game: Game;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import { Puck } from './Puck';
|
||||||
|
import { Board } from './Board';
|
||||||
|
import { Bomb } from './Bomb';
|
||||||
|
import { Element } from './Element';
|
||||||
|
import { Position, Size } from './types';
|
||||||
|
import { Ball } from './Ball';
|
||||||
|
import * as sound from './sound';
|
||||||
|
|
||||||
|
export class Block extends Element {
|
||||||
|
bomb: Bomb | undefined;
|
||||||
|
|
||||||
|
private constructor(
|
||||||
|
public board: Board,
|
||||||
|
public ball: Ball,
|
||||||
|
public puck: Puck,
|
||||||
|
pos: Position,
|
||||||
|
size: Size = { width: 70, height: 20 }
|
||||||
|
) {
|
||||||
|
super(size, pos);
|
||||||
|
const { style } = this;
|
||||||
|
|
||||||
|
style.position = 'absolute';
|
||||||
|
style.zIndex = '1';
|
||||||
|
style.width = `${size.width}px`;
|
||||||
|
style.height = `${size.height}px`;
|
||||||
|
style.borderRadius = '10%';
|
||||||
|
style.backgroundColor = '#e836ca';
|
||||||
|
style.boxShadow = ' 2px 3px 5px #333, inset -2px -2px 4px black, inset 2px 2px 4px white';
|
||||||
|
this.translate();
|
||||||
|
}
|
||||||
|
|
||||||
|
static create(board: Board, ball: Ball, puck: Puck, pos: Position, size?: Size): Block {
|
||||||
|
return new Block(board, ball, puck, pos, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): this {
|
||||||
|
super.reset();
|
||||||
|
this.isDestroyed = false;
|
||||||
|
this.style.display = 'block';
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(): this {
|
||||||
|
super.update();
|
||||||
|
// const { board, puck, ball, pos, destroy, isDestroyed: destroyed, bounce } = this;
|
||||||
|
const { ball, destroy, isDestroyed: destroyed, bounce } = this;
|
||||||
|
|
||||||
|
if (destroyed) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bounce(ball)) {
|
||||||
|
// board.appendElement(Bomb.create(board, puck, pos));
|
||||||
|
destroy();
|
||||||
|
sound.block();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
import { Board } from './Board';
|
||||||
|
import { Element } from './Element';
|
||||||
|
import { Puck } from './Puck';
|
||||||
|
import { Position, Size, Velocity } from './types';
|
||||||
|
|
||||||
|
export class Bomb extends Element {
|
||||||
|
static SIZE: Size = { width: 25, height: 25 };
|
||||||
|
static VELOCITY: Velocity = Velocity.create(0, 5);
|
||||||
|
|
||||||
|
private constructor(public board: Board, public puck: Puck, pos: Position) {
|
||||||
|
super(Bomb.SIZE, pos, Bomb.VELOCITY);
|
||||||
|
|
||||||
|
const { style, size } = this;
|
||||||
|
|
||||||
|
style.position = 'absolute';
|
||||||
|
style.width = `${size.width}px`;
|
||||||
|
style.height = `${size.height}px`;
|
||||||
|
style.borderRadius = '50%';
|
||||||
|
style.backgroundColor = '#000000';
|
||||||
|
// style.boxShadow = 'inset -2px -2px 8px #aaaaaa88, inset 2px 2px 8px #ffffff';
|
||||||
|
style.boxShadow = 'rgb(170 170 170 / 40%) -1px -1px 4px inset, rgb(255 255 255 / 40%) 1px 1px 4px inset';
|
||||||
|
|
||||||
|
this.translate();
|
||||||
|
}
|
||||||
|
|
||||||
|
static create(board: Board, puck: Puck, pos: Position): Bomb {
|
||||||
|
return new Bomb(board, puck, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(): this {
|
||||||
|
super.update();
|
||||||
|
const { puck, vel, pos, board, destroy, intersects } = this;
|
||||||
|
if (!board.intersects(this)) {
|
||||||
|
return destroy();
|
||||||
|
}
|
||||||
|
if (intersects(puck)) {
|
||||||
|
board.reset();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
pos.x += vel.x;
|
||||||
|
pos.y += vel.y;
|
||||||
|
return this.translate();
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): this {
|
||||||
|
super.reset();
|
||||||
|
return this.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { Ball } from './Ball';
|
||||||
|
import { Element } from './Element';
|
||||||
|
import { Game } from './Game';
|
||||||
|
import { Position, Size } from './types';
|
||||||
|
|
||||||
|
export class DebugPanel extends Element {
|
||||||
|
ballPosition: HTMLDivElement;
|
||||||
|
level: HTMLDivElement;
|
||||||
|
frameCount: HTMLDivElement;
|
||||||
|
ball: Ball;
|
||||||
|
|
||||||
|
private constructor(public game: Game) {
|
||||||
|
super(Size.create(), Position.create());
|
||||||
|
|
||||||
|
const { style, instance } = this;
|
||||||
|
|
||||||
|
this.ball = game.ball;
|
||||||
|
|
||||||
|
style.position = 'fixed';
|
||||||
|
style.top = '0px';
|
||||||
|
style.left = '0px';
|
||||||
|
style.fontSize = '18pt';
|
||||||
|
|
||||||
|
this.ballPosition = document.createElement('div');
|
||||||
|
instance.appendChild(this.ballPosition);
|
||||||
|
|
||||||
|
this.frameCount = document.createElement('div');
|
||||||
|
instance.appendChild(this.frameCount);
|
||||||
|
|
||||||
|
this.level = document.createElement('div');
|
||||||
|
instance.appendChild(this.level);
|
||||||
|
}
|
||||||
|
|
||||||
|
static create(game: Game): DebugPanel {
|
||||||
|
return new DebugPanel(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(): this {
|
||||||
|
super.update();
|
||||||
|
if (this.game.frameCount % 10) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
this.ballPosition.innerText = `Position: ${this.ball.pos.x}:${this.ball.pos.y}`;
|
||||||
|
this.level.innerText = `Level: ${this.game.levels.currentLevelIndex + 1}`;
|
||||||
|
this.frameCount.innerText = `Frame count: ${this.game.frameCount}`;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
+181
@@ -0,0 +1,181 @@
|
|||||||
|
import { Position, Size, Velocity } from './types';
|
||||||
|
import { between } from './utils';
|
||||||
|
|
||||||
|
export abstract class Element {
|
||||||
|
instance: HTMLElement;
|
||||||
|
elements: Element[] = [];
|
||||||
|
style: CSSStyleDeclaration;
|
||||||
|
isDestroyed = false;
|
||||||
|
|
||||||
|
protected constructor(public size: Size, public pos: Position, public vel: Velocity = Velocity.create()) {
|
||||||
|
this.instance = document.createElement('div');
|
||||||
|
this.style = this.instance.style;
|
||||||
|
this.style.transition = 'transform linear 20ms';
|
||||||
|
this.translate = this.translate.bind(this);
|
||||||
|
this.bounce = this.bounce.bind(this);
|
||||||
|
this.update = this.update.bind(this);
|
||||||
|
this.reset = this.reset.bind(this);
|
||||||
|
this.destroy = this.destroy.bind(this);
|
||||||
|
this.intersects = this.intersects.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(): this {
|
||||||
|
this.isDestroyed = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
get top(): number {
|
||||||
|
return this.pos.y;
|
||||||
|
}
|
||||||
|
get right(): number {
|
||||||
|
return this.pos.x + this.size.width;
|
||||||
|
}
|
||||||
|
get bottom(): number {
|
||||||
|
return this.pos.y + this.size.height;
|
||||||
|
}
|
||||||
|
get left(): number {
|
||||||
|
return this.pos.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
appendElement(element: Element): this {
|
||||||
|
this.elements.push(element);
|
||||||
|
this.instance.appendChild(element.instance);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
removeElement(element: Element): this {
|
||||||
|
this.elements.splice(this.elements.indexOf(element), 1);
|
||||||
|
this.instance.removeChild(element.instance);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
removeAllElements(): this {
|
||||||
|
while (this.instance.childNodes.length) {
|
||||||
|
this.instance.removeChild(this.instance.childNodes[0]);
|
||||||
|
}
|
||||||
|
this.elements = [];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
move(pos: Position): this {
|
||||||
|
this.pos = pos;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
translate(): this {
|
||||||
|
this.style.transform = `translate(${this.pos.x}px, ${this.pos.y}px)`;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
isInBoundsX(x: number, w: number): boolean {
|
||||||
|
return x >= 0 && x + w <= this.size.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
isInBoundsY(y: number, h: number): boolean {
|
||||||
|
return y >= 0 && y + h <= this.size.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
isInBounds(pos: Position, size: Size): boolean {
|
||||||
|
return this.isInBoundsX(pos.x, size.width) && this.isInBoundsY(pos.y, size.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
intersectsTop(element: Element): boolean {
|
||||||
|
let yIntersects = false;
|
||||||
|
let xIntersects = false;
|
||||||
|
|
||||||
|
if (between(element.bottom, this.top, this.top + Math.round(this.size.height / 2))) {
|
||||||
|
yIntersects = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (between(element.left, this.left, this.right) || between(element.right, this.left, this.right)) {
|
||||||
|
xIntersects = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return yIntersects && xIntersects;
|
||||||
|
}
|
||||||
|
|
||||||
|
intersectsBottom(element: Element): boolean {
|
||||||
|
let yIntersects = false;
|
||||||
|
let xIntersects = false;
|
||||||
|
|
||||||
|
if (between(element.top, this.bottom, this.bottom - Math.round(this.size.height / 2))) {
|
||||||
|
yIntersects = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (between(element.left, this.left, this.right) || between(element.right, this.left, this.right)) {
|
||||||
|
xIntersects = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return yIntersects && xIntersects;
|
||||||
|
}
|
||||||
|
|
||||||
|
intersectsLeft(element: Element): boolean {
|
||||||
|
let yIntersects = false;
|
||||||
|
let xIntersects = false;
|
||||||
|
|
||||||
|
if (between(element.right, this.left, this.left + Math.round(this.size.width / 2))) {
|
||||||
|
xIntersects = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (between(element.top, this.top, this.bottom) || between(element.bottom, this.top, this.bottom)) {
|
||||||
|
yIntersects = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return yIntersects && xIntersects;
|
||||||
|
}
|
||||||
|
|
||||||
|
intersectsRight(element: Element): boolean {
|
||||||
|
let yIntersects = false;
|
||||||
|
let xIntersects = false;
|
||||||
|
|
||||||
|
if (between(element.left, this.right - Math.round(this.size.width / 2), this.right)) {
|
||||||
|
xIntersects = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (between(element.top, this.top, this.bottom) || between(element.bottom, this.top, this.bottom)) {
|
||||||
|
yIntersects = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return yIntersects && xIntersects;
|
||||||
|
}
|
||||||
|
|
||||||
|
intersects(element: Element): boolean {
|
||||||
|
return (
|
||||||
|
this.intersectsTop(element) ||
|
||||||
|
this.intersectsRight(element) ||
|
||||||
|
this.intersectsLeft(element) ||
|
||||||
|
this.intersectsBottom(element)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bounce(element: Element): boolean {
|
||||||
|
if (this.intersectsTop(element)) {
|
||||||
|
element.vel.y = Math.abs(element.vel.y) * -1;
|
||||||
|
return true;
|
||||||
|
} else if (this.intersectsBottom(element)) {
|
||||||
|
element.vel.y = Math.abs(element.vel.y);
|
||||||
|
return true;
|
||||||
|
} else if (this.intersectsLeft(element)) {
|
||||||
|
element.vel.x = Math.abs(element.vel.x) * -1;
|
||||||
|
return true;
|
||||||
|
} else if (this.intersectsRight(element)) {
|
||||||
|
element.vel.x = Math.abs(element.vel.x);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(): this {
|
||||||
|
this.elements.forEach(element => {
|
||||||
|
if (element.isDestroyed) {
|
||||||
|
this.removeElement(element);
|
||||||
|
} else {
|
||||||
|
element.update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): this {
|
||||||
|
this.elements.forEach(element => element.reset());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
+44
-38
@@ -1,45 +1,51 @@
|
|||||||
import { board } from './board';
|
import { Board } from './Board';
|
||||||
import { puck } from './puck';
|
import { Element } from './Element';
|
||||||
import { Position, Size, Element, Velocity } from './types';
|
import { Position, Velocity } from './types';
|
||||||
|
|
||||||
export const ball = ((): Element => {
|
export class Ball extends Element {
|
||||||
const instance = document.createElement('div');
|
static VELOCITY: Velocity = Velocity.create(3, 5);
|
||||||
const { style } = instance;
|
|
||||||
|
|
||||||
const size: Size = { width: 20, height: 20 };
|
private constructor(public board: Board, pos?: Position) {
|
||||||
const vel: Velocity = { x: 5, y: 5 };
|
super({ width: 20, height: 20 }, pos || Position.create(), Ball.VELOCITY.clone());
|
||||||
const pos = new Position(Math.round(board.size.width / 2), Math.round(board.size.height / 2));
|
|
||||||
|
|
||||||
style.position = 'absolute';
|
const { style, size } = this;
|
||||||
style.transition = 'transform linear 20ms';
|
|
||||||
style.transform = `translate(${pos.x}px, ${pos.y}px)`;
|
style.position = 'absolute';
|
||||||
style.width = `${size.width}px`;
|
style.width = `${size.width}px`;
|
||||||
style.height = `${size.height}px`;
|
style.height = `${size.height}px`;
|
||||||
style.borderRadius = '50%';
|
style.borderRadius = '50%';
|
||||||
style.backgroundColor = '#ff5656';
|
style.backgroundColor = '#eeeeee';
|
||||||
|
style.boxShadow = 'inset -2px -2px 8px #aaaaaa88, inset 2px 2px 8px #ffffff';
|
||||||
|
|
||||||
|
this.reset().translate();
|
||||||
|
}
|
||||||
|
|
||||||
|
static create(board: Board, pos?: Position): Ball {
|
||||||
|
return new Ball(board, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): this {
|
||||||
|
const { board } = this;
|
||||||
|
this.vel = Ball.VELOCITY.clone();
|
||||||
|
this.move(Position.create(Math.round(board.size.width / 2), Math.round(board.size.height / 2) + 50));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(): this {
|
||||||
|
super.update();
|
||||||
|
const { vel, pos, size, board, translate } = this;
|
||||||
|
|
||||||
const update = () => {
|
|
||||||
if (!board.isInBoundsX(pos.x + vel.x, size.width)) {
|
|
||||||
vel.x *= -1;
|
|
||||||
}
|
|
||||||
if (!board.isInBoundsY(pos.y + vel.y, size.height)) {
|
|
||||||
vel.y *= -1;
|
|
||||||
}
|
|
||||||
pos.x += vel.x;
|
pos.x += vel.x;
|
||||||
pos.y += vel.y;
|
pos.y += vel.y;
|
||||||
if (puck.intersects(ball)) {
|
|
||||||
vel.y = Math.abs(vel.y) * -1;
|
|
||||||
}
|
|
||||||
if (pos.y + size.height >= board.size.height) {
|
|
||||||
pos.move(Math.round(board.size.width / 2), Math.round(board.size.height / 2));
|
|
||||||
}
|
|
||||||
style.transform = `translate(${pos.x}px, ${pos.y}px)`;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
if (!board.isInBoundsX(pos.x, size.width)) {
|
||||||
size,
|
vel.x *= -1;
|
||||||
pos,
|
}
|
||||||
instance,
|
|
||||||
update,
|
if (!board.isInBoundsY(pos.y, size.height)) {
|
||||||
};
|
vel.y *= -1;
|
||||||
})();
|
}
|
||||||
|
|
||||||
|
return translate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+29
-20
@@ -1,26 +1,35 @@
|
|||||||
|
import { Element } from './Element';
|
||||||
import { Position, Size } from './types';
|
import { Position, Size } from './types';
|
||||||
|
|
||||||
export const board = (() => {
|
export class Board extends Element {
|
||||||
const instance = document.createElement('div');
|
static SIZE: Size = { width: 833, height: 800 };
|
||||||
const { style } = instance;
|
|
||||||
|
|
||||||
const boardSize: Size = { width: 433, height: 800 };
|
private constructor() {
|
||||||
|
super(Board.SIZE, Position.create(0, 0));
|
||||||
|
const { style, size } = this;
|
||||||
|
|
||||||
style.width = `${boardSize.width}px`;
|
style.width = `${size.width}px`;
|
||||||
style.height = `${boardSize.height}px`;
|
style.height = `${size.height}px`;
|
||||||
style.backgroundColor = '#2d2d2d';
|
style.backgroundColor = '#2d2d2d';
|
||||||
style.margin = '40px auto';
|
style.overflow = 'hidden';
|
||||||
|
style.margin = '40px auto';
|
||||||
|
style.backgroundColor = '#3a457b';
|
||||||
|
// style.opacity = '0.8';
|
||||||
|
style.backgroundImage = `linear-gradient(30deg, #0a1941 12%, transparent 12.5%, transparent 87%, #0a1941 87.5%, #0a1941),
|
||||||
|
linear-gradient(150deg, #0a1941 12%, transparent 12.5%, transparent 87%, #0a1941 87.5%, #0a1941),
|
||||||
|
linear-gradient(30deg, #0a1941 12%, transparent 12.5%, transparent 87%, #0a1941 87.5%, #0a1941),
|
||||||
|
linear-gradient(150deg, #0a1941 12%, transparent 12.5%, transparent 87%, #0a1941 87.5%, #0a1941),
|
||||||
|
linear-gradient(60deg, #0a194177 25%, transparent 25.5%, transparent 75%, #0a194177 75%, #0a194177),
|
||||||
|
linear-gradient(60deg, #0a194177 25%, transparent 25.5%, transparent 75%, #0a194177 75%, #0a194177)
|
||||||
|
`;
|
||||||
|
// style.backgroundSize = '40px 70px';
|
||||||
|
style.backgroundSize = '20% 50%';
|
||||||
|
style.backgroundPosition = '0 0, 0 0, 20px 35px, 20px 35px, 0 0, 20px 35px';
|
||||||
|
|
||||||
const isInBoundsX = (x: number, w: number): boolean => x >= 0 && x + w <= boardSize.width;
|
this.reset().translate();
|
||||||
const isInBoundsY = (y: number, h: number): boolean => y >= 0 && y + h <= boardSize.height;
|
}
|
||||||
const isInBounds = (pos: Position, size: Size): boolean =>
|
|
||||||
isInBoundsX(pos.x, size.width) && isInBoundsY(pos.y, size.height);
|
|
||||||
|
|
||||||
return {
|
static create(): Board {
|
||||||
size: boardSize,
|
return new Board();
|
||||||
instance,
|
}
|
||||||
isInBoundsX,
|
}
|
||||||
isInBoundsY,
|
|
||||||
isInBounds,
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|||||||
+59
-1
@@ -1 +1,59 @@
|
|||||||
export const game = { paused: false };
|
import { Ball } from './Ball';
|
||||||
|
import { Board } from './Board';
|
||||||
|
import { Element } from './Element';
|
||||||
|
import { Levels } from './levels';
|
||||||
|
import { Puck } from './Puck';
|
||||||
|
import { Position, Size } from './types';
|
||||||
|
|
||||||
|
export class Game extends Element {
|
||||||
|
public paused = false;
|
||||||
|
public frameCount = 0;
|
||||||
|
public level = 0;
|
||||||
|
|
||||||
|
private constructor(
|
||||||
|
public instance: HTMLElement,
|
||||||
|
public board: Board,
|
||||||
|
public puck: Puck,
|
||||||
|
public ball: Ball,
|
||||||
|
public levels: Levels
|
||||||
|
) {
|
||||||
|
super(Size.create(), Position.create());
|
||||||
|
this.style.position = 'relative';
|
||||||
|
|
||||||
|
this.appendElement(board);
|
||||||
|
board.appendElement(levels);
|
||||||
|
board.appendElement(this.puck);
|
||||||
|
board.appendElement(this.ball);
|
||||||
|
}
|
||||||
|
|
||||||
|
gameLoop(updateGameLoop: () => void): this {
|
||||||
|
const triggerGameLoop = (): void =>
|
||||||
|
void requestAnimationFrame((): void => {
|
||||||
|
this.frameCount += 1;
|
||||||
|
try {
|
||||||
|
return updateGameLoop();
|
||||||
|
} finally {
|
||||||
|
triggerGameLoop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
triggerGameLoop();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(): this {
|
||||||
|
super.update();
|
||||||
|
|
||||||
|
const { board, ball, reset } = this;
|
||||||
|
|
||||||
|
if (ball.pos.y + ball.size.height >= board.size.height) {
|
||||||
|
return reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
static create(instance: HTMLElement, board: Board, puck: Puck, ball: Ball, levels: Levels): Game {
|
||||||
|
return new Game(instance, board, puck, ball, levels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+37
-21
@@ -1,30 +1,46 @@
|
|||||||
import { keyboard, Keys } from './keyboard';
|
import { keyboard, Keys } from './keyboard';
|
||||||
import { board } from './board';
|
import { Game } from './Game';
|
||||||
import { puck } from './puck';
|
import { Board } from './Board';
|
||||||
import { ball } from './ball';
|
// import { DebugPanel } from './DebugPanel';
|
||||||
import { game } from './game';
|
import { Levels, Level1, Level2 } from './levels';
|
||||||
|
import { Ball } from './Ball';
|
||||||
const triggerGameLoop = () =>
|
import { Puck } from './Puck';
|
||||||
requestAnimationFrame(() => {
|
import * as sound from './sound';
|
||||||
if (keyboard.isKeyPressed(Keys.P)) {
|
|
||||||
game.paused = !game.paused;
|
|
||||||
}
|
|
||||||
if (!game.paused) {
|
|
||||||
puck.update();
|
|
||||||
ball.update();
|
|
||||||
}
|
|
||||||
triggerGameLoop();
|
|
||||||
});
|
|
||||||
|
|
||||||
const run = (): void => {
|
const run = (): void => {
|
||||||
const gameElement = document.querySelector('#game');
|
const gameElement = document.querySelector<HTMLElement>('#game');
|
||||||
|
|
||||||
if (!gameElement) {
|
if (!gameElement) {
|
||||||
return console.error('No dom element with id game found');
|
return console.error('No dom element with id game found');
|
||||||
}
|
}
|
||||||
gameElement.appendChild(board.instance);
|
|
||||||
board.instance.appendChild(puck.instance);
|
const board = Board.create();
|
||||||
board.instance.appendChild(ball.instance);
|
const ball = Ball.create(board);
|
||||||
return void triggerGameLoop();
|
const puck = Puck.create(board, ball);
|
||||||
|
const levels = Levels.create(board, ball, puck, [Level1, Level2]);
|
||||||
|
|
||||||
|
const game = Game.create(gameElement, board, puck, ball, levels);
|
||||||
|
window.game = game;
|
||||||
|
|
||||||
|
// const debugPanel = DebugPanel.create(game);
|
||||||
|
// game.appendElement(debugPanel);
|
||||||
|
|
||||||
|
return void game.gameLoop(() => {
|
||||||
|
if (keyboard.isKeyPressed(Keys.r)) {
|
||||||
|
game.reset();
|
||||||
|
}
|
||||||
|
if (keyboard.isKeyPressed(Keys.p)) {
|
||||||
|
game.paused = !game.paused;
|
||||||
|
}
|
||||||
|
if (keyboard.isKeyPressed(Keys.s)) {
|
||||||
|
sound.toggleEnabledDisabled();
|
||||||
|
}
|
||||||
|
if (game.paused) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
game.update();
|
||||||
|
// debugPanel.update();
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', run);
|
document.addEventListener('DOMContentLoaded', run);
|
||||||
|
|||||||
+16
-4
@@ -1,22 +1,34 @@
|
|||||||
export enum Keys {
|
export enum Keys {
|
||||||
ARROW_RIGHT = 'ArrowRight',
|
ARROW_RIGHT = 'ArrowRight',
|
||||||
ARROW_LEFT = 'ArrowLeft',
|
ARROW_LEFT = 'ArrowLeft',
|
||||||
P = 'p',
|
p = 'p',
|
||||||
|
s = 's',
|
||||||
|
r = 'r',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const downKeys: Partial<Record<Keys, boolean>> = {};
|
||||||
const pressedKeys: Partial<Record<Keys, boolean>> = {};
|
const pressedKeys: Partial<Record<Keys, boolean>> = {};
|
||||||
|
|
||||||
document.addEventListener('keydown', e => {
|
document.addEventListener('keydown', e => {
|
||||||
|
downKeys[e.key as Keys] = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('keypress', e => {
|
||||||
pressedKeys[e.key as Keys] = true;
|
pressedKeys[e.key as Keys] = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('keyup', e => {
|
document.addEventListener('keyup', e => {
|
||||||
pressedKeys[e.key as Keys] = false;
|
downKeys[e.key as Keys] = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const keyboard = {
|
export const keyboard = {
|
||||||
pressedKeys,
|
pressedKeys: downKeys,
|
||||||
isKeyPressed(key: Keys): boolean {
|
isKeyPressed(key: Keys): boolean {
|
||||||
return Boolean(pressedKeys[key]);
|
const isPressed = Boolean(pressedKeys[key]);
|
||||||
|
pressedKeys[key] = false;
|
||||||
|
return isPressed;
|
||||||
|
},
|
||||||
|
isKeyDown(key: Keys): boolean {
|
||||||
|
return Boolean(downKeys[key]);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { Level } from './types';
|
||||||
|
|
||||||
|
export const Level1 = Level.create([
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||||
|
] as const);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { Level } from './types';
|
||||||
|
|
||||||
|
export const Level2: Level = Level.create([
|
||||||
|
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
|
||||||
|
] as const);
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import { Puck } from '../Puck';
|
||||||
|
import { Board } from '../Board';
|
||||||
|
import { Level, LevelInstance } from './types';
|
||||||
|
import { Position, Size } from '../types';
|
||||||
|
import { Element } from '../Element';
|
||||||
|
import { Block } from '../Block';
|
||||||
|
import { Ball } from '../Ball';
|
||||||
|
|
||||||
|
export class Levels extends Element {
|
||||||
|
currentLevelIndex = -1;
|
||||||
|
currentLevelInstance: LevelInstance | undefined;
|
||||||
|
blocks: Block[] = [];
|
||||||
|
|
||||||
|
private constructor(public board: Board, public ball: Ball, public puck: Puck, public levels: Level[]) {
|
||||||
|
super(board.size, Position.create());
|
||||||
|
const { style } = this;
|
||||||
|
|
||||||
|
style.position = 'absolute';
|
||||||
|
style.top = '0';
|
||||||
|
style.left = '0';
|
||||||
|
style.width = '100%';
|
||||||
|
style.height = '100%';
|
||||||
|
|
||||||
|
if (levels.length === 0) {
|
||||||
|
throw new Error('No levels have been specified');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.update = this.update.bind(this);
|
||||||
|
this.gotoNextLevel = this.gotoNextLevel.bind(this);
|
||||||
|
|
||||||
|
this.gotoNextLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasNextLevel(): boolean {
|
||||||
|
return this.levels.length > this.currentLevelIndex + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
gotoNextLevel(): this {
|
||||||
|
if (!this.hasNextLevel) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
this.currentLevelIndex += 1;
|
||||||
|
return this.resetLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
resetLevel(): this {
|
||||||
|
const { board, size, puck, ball } = this;
|
||||||
|
this.currentLevelInstance = this.levels[this.currentLevelIndex].create();
|
||||||
|
|
||||||
|
const { grid } = this.currentLevelInstance;
|
||||||
|
|
||||||
|
const numberOfRows = grid.length;
|
||||||
|
const numberOfBlocksPerRow = grid[0].length;
|
||||||
|
|
||||||
|
const blockHeight = Math.round((size.height - 10) / 2 / numberOfRows) - 5;
|
||||||
|
const blockWidth = Math.round((size.width - 10) / numberOfBlocksPerRow) - 5;
|
||||||
|
|
||||||
|
this.removeAllElements();
|
||||||
|
for (let x = 0; x < numberOfBlocksPerRow; x += 1) {
|
||||||
|
for (let y = 0; y < numberOfRows; y += 1) {
|
||||||
|
const blockValue = grid[y][x];
|
||||||
|
if (blockValue) {
|
||||||
|
const block = Block.create(
|
||||||
|
board,
|
||||||
|
ball,
|
||||||
|
puck,
|
||||||
|
Position.create(5 + x * (blockWidth + 5), 5 + y * (blockHeight + 5)),
|
||||||
|
Size.create(blockWidth, blockHeight)
|
||||||
|
);
|
||||||
|
this.appendElement(block);
|
||||||
|
this.blocks.push(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(): this {
|
||||||
|
super.update();
|
||||||
|
// this.blocks.forEach(block => block.update());
|
||||||
|
const allDestroyed = this.blocks.reduce((all, block) => all && block.isDestroyed, true);
|
||||||
|
|
||||||
|
if (allDestroyed) {
|
||||||
|
this.gotoNextLevel();
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): this {
|
||||||
|
super.reset();
|
||||||
|
this.currentLevelIndex = 0;
|
||||||
|
return this.resetLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
static create(board: Board, ball: Ball, puck: Puck, levels: Level[]): Levels {
|
||||||
|
return new Levels(board, ball, puck, levels);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export * from './types';
|
||||||
|
export * from './Levels';
|
||||||
|
export * from './Level1';
|
||||||
|
export * from './Level2';
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
export type BlockState = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
|
||||||
|
export type RowState = [
|
||||||
|
BlockState,
|
||||||
|
BlockState,
|
||||||
|
BlockState,
|
||||||
|
BlockState,
|
||||||
|
BlockState,
|
||||||
|
BlockState,
|
||||||
|
BlockState,
|
||||||
|
BlockState,
|
||||||
|
BlockState,
|
||||||
|
BlockState,
|
||||||
|
BlockState,
|
||||||
|
BlockState,
|
||||||
|
BlockState,
|
||||||
|
BlockState
|
||||||
|
];
|
||||||
|
export type GridState = [
|
||||||
|
RowState,
|
||||||
|
RowState,
|
||||||
|
RowState,
|
||||||
|
RowState,
|
||||||
|
RowState,
|
||||||
|
RowState,
|
||||||
|
RowState,
|
||||||
|
RowState,
|
||||||
|
RowState,
|
||||||
|
RowState
|
||||||
|
];
|
||||||
|
|
||||||
|
export type Block = Readonly<0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9>;
|
||||||
|
export type Row = Readonly<
|
||||||
|
[Block, Block, Block, Block, Block, Block, Block, Block, Block, Block, Block, Block, Block, Block]
|
||||||
|
>;
|
||||||
|
export type Grid = Readonly<[Row, Row, Row, Row, Row, Row, Row, Row, Row, Row]>;
|
||||||
|
|
||||||
|
export class LevelInstance {
|
||||||
|
constructor(public grid: GridState) {}
|
||||||
|
|
||||||
|
static create(grid: GridState): LevelInstance {
|
||||||
|
return new LevelInstance(grid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Level {
|
||||||
|
constructor(private grid: Grid) {}
|
||||||
|
|
||||||
|
create(): LevelInstance {
|
||||||
|
return LevelInstance.create(this.grid as GridState);
|
||||||
|
}
|
||||||
|
|
||||||
|
static create(grid: Grid): Level {
|
||||||
|
return new Level(grid);
|
||||||
|
}
|
||||||
|
}
|
||||||
+59
-57
@@ -1,60 +1,62 @@
|
|||||||
import { board } from './board';
|
import { Ball } from './Ball';
|
||||||
|
import { Board } from './Board';
|
||||||
import { keyboard, Keys } from './keyboard';
|
import { keyboard, Keys } from './keyboard';
|
||||||
import { Position, Size, Element } from './types';
|
import { Element } from './Element';
|
||||||
|
import { Position, Size } from './types';
|
||||||
|
import * as sound from './sound';
|
||||||
|
|
||||||
export interface Puck extends Element {
|
export class Puck extends Element {
|
||||||
intersects(element: Element): boolean;
|
static BOTTOM_MARGIN = 30;
|
||||||
|
static SIZE: Size = { width: 90, height: 30 };
|
||||||
|
static STEP_SIZE = 10;
|
||||||
|
|
||||||
|
private constructor(public board: Board, public ball: Ball, pos?: Position) {
|
||||||
|
super(Puck.SIZE, pos || Position.create());
|
||||||
|
const { style, size } = this;
|
||||||
|
|
||||||
|
style.position = 'absolute';
|
||||||
|
style.zIndex = '1';
|
||||||
|
style.width = `${size.width}px`;
|
||||||
|
style.height = `${size.height}px`;
|
||||||
|
style.borderRadius = '6px';
|
||||||
|
style.backgroundColor = '#36afe8';
|
||||||
|
style.boxShadow = ' 2px 3px 5px #333, inset -2px -2px 4px black, inset 2px 2px 4px white';
|
||||||
|
|
||||||
|
this.reset().translate();
|
||||||
|
}
|
||||||
|
|
||||||
|
static create(board: Board, ball: Ball, pos?: Position): Puck {
|
||||||
|
return new Puck(board, ball, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
reset(): this {
|
||||||
|
super.reset();
|
||||||
|
const { board, size } = this;
|
||||||
|
this.move(
|
||||||
|
Position.create(
|
||||||
|
Math.round((board.size.width - size.width) / 2),
|
||||||
|
board.size.height - size.height - Puck.BOTTOM_MARGIN
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
update(): this {
|
||||||
|
super.update();
|
||||||
|
const { translate, board, pos, size, ball, bounce } = this;
|
||||||
|
|
||||||
|
if (keyboard.isKeyDown(Keys.ARROW_RIGHT) && board.isInBounds(pos.clone().addX(Puck.STEP_SIZE), size)) {
|
||||||
|
pos.addX(Puck.STEP_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyboard.isKeyDown(Keys.ARROW_LEFT) && board.isInBounds(pos.clone().addX(-Puck.STEP_SIZE), size)) {
|
||||||
|
pos.addX(-Puck.STEP_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bounce(ball)) {
|
||||||
|
sound.puck();
|
||||||
|
}
|
||||||
|
|
||||||
|
return translate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const puck = ((): Puck => {
|
|
||||||
const instance = document.createElement('div');
|
|
||||||
const { style } = instance;
|
|
||||||
|
|
||||||
const bottomMargin = 30;
|
|
||||||
const size: Size = { width: 70, height: 20 };
|
|
||||||
const pos = new Position(0, board.size.height - size.height - bottomMargin);
|
|
||||||
const stepSize = 10;
|
|
||||||
|
|
||||||
style.position = 'absolute';
|
|
||||||
style.zIndex = '1';
|
|
||||||
style.transition = 'transform linear 20ms';
|
|
||||||
style.transform = `translate(${pos.x}px, ${pos.y}px)`;
|
|
||||||
style.width = `${size.width}px`;
|
|
||||||
style.height = `${size.height}px`;
|
|
||||||
style.borderRadius = '10%';
|
|
||||||
style.backgroundColor = '#36afe8';
|
|
||||||
|
|
||||||
const update = () => {
|
|
||||||
if (keyboard.isKeyPressed(Keys.ARROW_RIGHT) && board.isInBounds(pos.clone().addX(stepSize), size)) {
|
|
||||||
pos.addX(stepSize);
|
|
||||||
} else if (keyboard.isKeyPressed(Keys.ARROW_LEFT) && board.isInBounds(pos.clone().addX(-stepSize), size)) {
|
|
||||||
pos.addX(-stepSize);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
style.transform = `translate(${pos.x}px, ${pos.y}px)`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const intersects = (element: Element): boolean => {
|
|
||||||
let yIntersects = false;
|
|
||||||
let xIntersects = false;
|
|
||||||
if (element.pos.y + element.size.height >= pos.y && element.pos.y + element.size.height <= pos.y + 1) {
|
|
||||||
yIntersects = true;
|
|
||||||
}
|
|
||||||
if (element.pos.x > pos.x && element.pos.x < pos.x + size.width) {
|
|
||||||
xIntersects = true;
|
|
||||||
}
|
|
||||||
if (element.pos.x + element.size.width > pos.x && element.pos.x + element.size.width < pos.x + size.width) {
|
|
||||||
xIntersects = true;
|
|
||||||
}
|
|
||||||
return yIntersects && xIntersects;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
size,
|
|
||||||
pos,
|
|
||||||
instance,
|
|
||||||
update,
|
|
||||||
intersects,
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
const audioContext = new AudioContext();
|
||||||
|
|
||||||
|
let isSoundDisabled = true;
|
||||||
|
|
||||||
|
export const disable = (): void => void (isSoundDisabled = true);
|
||||||
|
export const enable = (): void => void (isSoundDisabled = false);
|
||||||
|
export const toggleEnabledDisabled = (): void => void (isSoundDisabled = !isSoundDisabled);
|
||||||
|
|
||||||
|
export const puck = (): void => {
|
||||||
|
if (isSoundDisabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const oscillator = audioContext.createOscillator();
|
||||||
|
oscillator.type = 'triangle';
|
||||||
|
const gainNode = audioContext.createGain();
|
||||||
|
oscillator.connect(gainNode);
|
||||||
|
gainNode.connect(audioContext.destination);
|
||||||
|
|
||||||
|
gainNode.gain.value = 0;
|
||||||
|
oscillator.frequency.value = 200;
|
||||||
|
oscillator.start(0);
|
||||||
|
oscillator.frequency.value = 100;
|
||||||
|
gainNode.gain.value = 0.1;
|
||||||
|
setTimeout(() => {
|
||||||
|
gainNode.gain.value = 0;
|
||||||
|
}, 250);
|
||||||
|
oscillator.frequency.value += 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const block = (): void => {
|
||||||
|
if (isSoundDisabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const oscillator = audioContext.createOscillator();
|
||||||
|
oscillator.type = 'triangle';
|
||||||
|
const gainNode = audioContext.createGain();
|
||||||
|
oscillator.connect(gainNode);
|
||||||
|
gainNode.connect(audioContext.destination);
|
||||||
|
|
||||||
|
gainNode.gain.value = 0;
|
||||||
|
oscillator.frequency.value = 200;
|
||||||
|
oscillator.start(0);
|
||||||
|
oscillator.frequency.value = 200;
|
||||||
|
gainNode.gain.value = 0.1;
|
||||||
|
setTimeout(() => {
|
||||||
|
gainNode.gain.value = 0;
|
||||||
|
}, 250);
|
||||||
|
oscillator.frequency.value += 1;
|
||||||
|
};
|
||||||
+27
-12
@@ -1,11 +1,30 @@
|
|||||||
export interface Velocity {
|
export class Velocity {
|
||||||
x: number;
|
constructor(public x: number, public y: number) {}
|
||||||
y: number;
|
|
||||||
|
get absX(): number {
|
||||||
|
return Math.abs(this.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
get absY(): number {
|
||||||
|
return Math.abs(this.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
clone(): Velocity {
|
||||||
|
return Velocity.create(this.x, this.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static create(x?: number, y?: number): Velocity {
|
||||||
|
return new Velocity(x || 0, y || 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Position {
|
export class Position {
|
||||||
constructor(public x: number, public y: number) {}
|
constructor(public x: number, public y: number) {}
|
||||||
|
|
||||||
|
static create(x?: number, y?: number): Position {
|
||||||
|
return new Position(x || 0, y || 0);
|
||||||
|
}
|
||||||
|
|
||||||
clone(): Position {
|
clone(): Position {
|
||||||
return new Position(this.x, this.y);
|
return new Position(this.x, this.y);
|
||||||
}
|
}
|
||||||
@@ -41,14 +60,10 @@ export class Position {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Size {
|
export class Size {
|
||||||
width: number;
|
constructor(public width: number, public height: number) {}
|
||||||
height: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Element {
|
static create(width?: number, height?: number): Size {
|
||||||
size: Size;
|
return new Size(width || 0, height || 0);
|
||||||
pos: Position;
|
}
|
||||||
instance: HTMLElement;
|
|
||||||
update(): void;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export const between = (value: number, boundary1: number, boundary2: number): boolean =>
|
||||||
|
boundary1 < boundary2 ? boundary1 <= value && boundary2 >= value : boundary2 <= value && boundary1 >= value;
|
||||||
+1
-1
@@ -17,7 +17,7 @@
|
|||||||
"downlevelIteration": true,
|
"downlevelIteration": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
"typeRoots": ["./src/types", "./types", "./node_modules/@types"]
|
"typeRoots": ["./src/@types", "./node_modules/@types"]
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
|
|||||||
Reference in New Issue
Block a user