From 73a7d39a15a692799eefb2e5991e819b7410835b Mon Sep 17 00:00:00 2001 From: Victor Brun Date: Tue, 2 May 2023 15:46:13 +0200 Subject: [PATCH] debut --- .vscode/tasks.json | 19 ++ build/index.htm | 29 +++ build/jeu.css | 44 +++++ source/Jeu.ts | 37 ++++ source/Scene.ts | 108 +++++++++++ source/Sprite.ts | 468 +++++++++++++++++++++++++++++++++++++++++++++ tsconfig.json | 12 ++ 7 files changed, 717 insertions(+) create mode 100644 .vscode/tasks.json create mode 100644 build/index.htm create mode 100644 build/jeu.css create mode 100644 source/Jeu.ts create mode 100644 source/Scene.ts create mode 100644 source/Sprite.ts create mode 100644 tsconfig.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..f8df9c0 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,19 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "typescript", + "tsconfig": "tsconfig.json", + "problemMatcher": [ + "$tsc" + ], + "group": "build", + }, + { + "type": "shell", + "command": "del build\\*.js", + "group": "build", + "label": "tsc: clean" + } + ] +} \ No newline at end of file diff --git a/build/index.htm b/build/index.htm new file mode 100644 index 0000000..284d020 --- /dev/null +++ b/build/index.htm @@ -0,0 +1,29 @@ + + + + Template Jeu + + + + + + + + + +
+ + + + +
+ + diff --git a/build/jeu.css b/build/jeu.css new file mode 100644 index 0000000..ab1d259 --- /dev/null +++ b/build/jeu.css @@ -0,0 +1,44 @@ +body { + position: absolute; + overflow: hidden; + padding: 0px; + margin: 0px; + width: 100%; + height: 100%; + + background-color: #DDDDDD; +} + +#scene { + user-select: none; + /*overflow: hidden;*/ + + background-color: #FFFFFF; + box-shadow: 12px 12px 4px rgba(150,150,150,0.75); +} + +#scene.fullscreen { + box-shadow: none; +} + +#fullscreen { + position: absolute; + right: 10px; + bottom: 10px; + width: 2em; + height: 1.2em; + + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + + font-size: 1em; + + background-color: #FFFFFF; + border: 1px solid #000000; + box-shadow: 4px 4px 2px rgba(0,0,0,0.2); + border-radius: 0.6em; + + cursor: pointer; +} diff --git a/source/Jeu.ts b/source/Jeu.ts new file mode 100644 index 0000000..cebe91e --- /dev/null +++ b/source/Jeu.ts @@ -0,0 +1,37 @@ +//================================================================================================== +// ANIMATION AVEC TYPESCRIPT Jeu.ts +//================================================================================================== + +// Classe J e u //--------------------------------------------------------------------------------- +class Jeu extends Scene { + //----------------------------------------------------------------------------------------Attributs + /* Declarer ici les attributs de la scene. */ + + //-------------------------------------------------------------------------------------Constructeur + public constructor(element : HTMLElement) { + super(element,false); + /* Ecrire ici le code qui initialise la scene. */ + } + + //--------------------------------------------------------------------------------------------start + public override start() { + /* Ecrire ici le code qui demarre la scene. */ + } + + //--------------------------------------------------------------------------------------------pause + public override pause() { + /* Ecrire ici le code qui met la scene en pause. */ + } + + //------------------------------------------------------------------------------------------unpause + public override unpause() { + /* Ecrire ici le code qui sort la scene de la pause. */ + } + + //--------------------------------------------------------------------------------------------clean + public override clean() { + /* Ecrire ici le code qui nettoie la scene en vue d'un redemarrage. */ + } +} + +// Fin //------------------------------------------------------------------------------------------- diff --git a/source/Scene.ts b/source/Scene.ts new file mode 100644 index 0000000..8382fed --- /dev/null +++ b/source/Scene.ts @@ -0,0 +1,108 @@ +//================================================================================================== +// ANIMATION WITH TYPESCRIPT Scene.ts +// By Bruno Bachelet +//================================================================================================== +// Copyright (c) 2017-2023 +// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the license, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details (http://www.gnu.org). + +// S c e n e Class //------------------------------------------------------------------------------ +class Scene extends Sprite { + //---------------------------------------------------------------------------------------Attributes + private resize_ : boolean; + private scale_ : number; + + //--------------------------------------------------------------------------------------Constructor + public constructor(element : HTMLElement, resize : boolean) { + super(element); + + this.resize_ = resize; + this.scale_ = 1; + + let box : DOMRect = this.getParentNode().getBoundingClientRect(); + + this.setDimension(640,480); + this.setX((box.width - this.getWidth()) / 2); + this.setY((box.height - this.getHeight()) / 2); + } + + //-------------------------------------------------------------------------------------isFullscreen + public isFullscreen() : boolean { return (document.fullscreenElement != null); } + + //---------------------------------------------------------------------------------toggleFullscreen + public toggleFullscreen(event : Event) { + if (this.isFullscreen()) { + this.getElement().className = ""; + document.exitFullscreen(); + } + else { + this.getElement().className = "fullscreen"; + document.body.requestFullscreen(); + } + + event.stopPropagation(); + } + + //-------------------------------------------------------------------------------------------resize + public resize() { + let space : number = (this.isFullscreen() ? 0 : 50); + let box : DOMRect = this.getParentNode().getBoundingClientRect(); + let rx = this.getWidth() / (box.width - space); + let ry = this.getHeight() / (box.height - space); + let s = 1 / Math.max(rx,ry); + + if (this.resize_ || this.isFullscreen()) { + this.getStyle().transform = "scale(" + s + ")"; + this.scale_ = s; + } + else { + this.getStyle().transform = ""; + this.scale_ = 1; + } + + this.setX((box.width - this.getWidth())/2); + this.setY((box.height - this.getHeight())/2); + } + + //-------------------------------------------------------------------------------------scaledMouseX + public override scaledMouseX(x : number) : number { + return (this.getCenterX() + (x - this.getCenterX())/this.scale_); + } + + //-------------------------------------------------------------------------------------scaledMouseY + public override scaledMouseY(y : number) : number { + return (this.getCenterY() + (y - this.getCenterY())/this.scale_); + } + + //--------------------------------------------------------------------------------------------start + public start() {} + + //--------------------------------------------------------------------------------------------pause + public pause() {} + + //------------------------------------------------------------------------------------------unpause + public unpause() {} + + //--------------------------------------------------------------------------------------------clean + public clean() { + while (this.getChildren().length > 0) this.removeChild(this.getChildren()[0]); + } + + //------------------------------------------------------------------------------------------restart + public restart() { + this.pause(); + this.clean(); + this.start(); + } +} + +// End //------------------------------------------------------------------------------------------- diff --git a/source/Sprite.ts b/source/Sprite.ts new file mode 100644 index 0000000..0333b42 --- /dev/null +++ b/source/Sprite.ts @@ -0,0 +1,468 @@ +//================================================================================================== +// ANIMATION WITH TYPESCRIPT Sprite.ts +// By Bruno Bachelet +//================================================================================================== +// Copyright (c) 2017-2023 +// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the license, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details (http://www.gnu.org). + +// S p r i t e Class //---------------------------------------------------------------------------- +class Sprite { + //---------------------------------------------------------------------------------------Attributes + private static counter_ : number = 0; + private static instances_ : Map = new Map(); + + private element_ : HTMLElement; + private parent_ : Sprite; + private children_ : Array; + private nativeParent_ : HTMLElement; + + private x_ : number; + private y_ : number; + private width_ : number; + private height_ : number; + + private rotation_ : number; + private opacity_ : number; + + //--------------------------------------------------------------------------------------Constructor + public constructor(element : HTMLElement) { + this.element_ = element; + this.parent_ = null; + this.children_ = new Array(); + this.nativeParent_ = ((element.parentNode)); + + if (element.dataset.spriteId) + console.error("HTML element #" + element.dataset.spriteId + + " '" + element.id + "' already associated with a sprite."); + + this.setId(); + + if (this.nativeParent_ != null && this.nativeParent_.tagName.toLowerCase() != "body") { + let parent = null; + + if (this.nativeParent_.dataset.spriteId) + parent = Sprite.instances_.get(this.nativeParent_.dataset.spriteId); + else + parent = new Sprite(this.nativeParent_); + + parent.appendChild(this); + } + + try { + let box : DOMRect = element.getBoundingClientRect(); + + this.x_ = box.left; + this.y_ = box.top; + this.width_ = box.width; + this.height_ = box.height; + + if (this.parent_ != null) { + box = this.parent_.element_.getBoundingClientRect(); + this.x_ -= box.left; + this.y_ -= box.top; + } + } + + catch(e) { + this.x_ = 0; + this.y_ = 0; + this.width_ = 0; + this.height_ = 0; + } + + this.setRotation(0); + this.setOpacity(1); + + this.element_.style.position = "absolute"; + + if (this.element_ instanceof HTMLImageElement) + ((this.element_)).draggable = false; + } + + //-----------------------------------------------------------------------------------------------ID + public getId() : string { return this.element_.dataset.spriteId; } + + private setId() { + if (!this.element_.dataset.spriteId) { + this.element_.dataset.spriteId = String(++Sprite.counter_); + Sprite.instances_.set(this.element_.dataset.spriteId,this); + } + } + + private removeId() { + if (this.element_.dataset.spriteId) { + Sprite.instances_.delete(this.element_.dataset.spriteId); + this.element_.removeAttribute("data-sprite-id"); + } + } + + //----------------------------------------------------------------------------------------------Log + public static logInstances() { for (let sprite of Sprite.instances_) console.log(sprite); } + + public log() { + let message : string; + + message = `${this.getId()}`; + message += `{${this.x_};${this.y_}`; + message += ` ${this.width_}x${this.height_}`; + message += ` ${this.rotation_}°`; + message += ` [${this.children_.length}]`; + message += " -> "+(this.parent_ == null ? "null" : `#${this.parent_.getId()}`); + message += "}"; + + console.log(message); + } + + //------------------------------------------------------------------------------------------Element + public getElement() : HTMLElement { return this.element_; } + + //----------------------------------------------------------------------------------------Shortcuts + public getStyle() : CSSStyleDeclaration { return this.element_.style } + public getBoundingClientRect() : DOMRect { return this.element_.getBoundingClientRect(); } + public getParentNode() : HTMLElement { return (this.element_.parentNode); } + + //-------------------------------------------------------------------------------------------Parent + public getParent() : Sprite { return this.parent_; } + + //-----------------------------------------------------------------------------------------Children + public getChildren() : Array { return this.children_; } + + public appendChild(sprite : Sprite|HTMLElement) { + if (sprite instanceof Sprite) { + if (sprite.parent_ != null) + console.error(`HTML element #${sprite.getId()} already has a parent.`); + else { + if (sprite.nativeParent_ == null) this.element_.appendChild(sprite.element_); + sprite.parent_ = this; + this.children_.push(sprite); + sprite.setId(); + } + } + else if (sprite instanceof HTMLElement) this.element_.appendChild(sprite); + } + + public removeChild(sprite : Sprite|HTMLElement) { + if (sprite instanceof Sprite) { + if (sprite.parent_ != this) + console.error(`Attempt to remove HTML element #${sprite.getId()} from the wrong parent.`); + else { + if (sprite.nativeParent_ == null) this.element_.removeChild(sprite.element_); + sprite.parent_ = null; + sprite.removeId(); + + for (let i : number = 0; i < this.children_.length; ++i) { + if (this.children_[i] == sprite) { + let last : Sprite = this.children_.pop(); + if (i < this.children_.length) this.children_[i] = last; + i = this.children_.length; + } + } + } + } + else if (sprite instanceof HTMLElement) this.element_.removeChild(sprite); + } + + //-----------------------------------------------------------------------------------------Position + public getX() : number { return this.x_; } + public getY() : number { return this.y_; } + + public setX(x : number) { + this.element_.style.left = x + "px"; + this.x_ = x; + } + + public setY(y : number) { + this.element_.style.top = y + "px"; + this.y_ = y; + } + + public setXY(x : number, y : number) { + this.element_.style.left = x + "px"; + this.element_.style.top = y + "px"; + this.x_ = x; + this.y_ = y; + } + + //----------------------------------------------------------------------------------------Dimension + public getWidth() : number { return this.width_; } + public getHeight() : number { return this.height_; } + + public setWidth(width : number) { + this.element_.style.width = width + "px"; + this.width_ = width; + + if (this.element_ instanceof HTMLCanvasElement) + ((this.element_)).width = width; + } + + public setHeight(height : number) { + this.element_.style.height = height + "px"; + this.height_ = height; + + if (this.element_ instanceof HTMLCanvasElement) + ((this.element_)).height = height; + } + + public setDimension(width : number, height : number) { + this.element_.style.width = width + "px"; + this.element_.style.height = height + "px"; + this.width_ = width; + this.height_ = height; + + if (this.element_ instanceof HTMLCanvasElement) { + ((this.element_)).width = width; + ((this.element_)).height = height; + } + } + + //-----------------------------------------------------------------------------------------Geometry + public getCenterX() : number { return (this.x_ + this.width_/2); } + public getCenterY() : number { return (this.y_ + this.height_/2); } + public getLeft() : number { return this.x_; } + public getRight() : number { return (this.x_ + this.width_); } + public getTop() : number { return this.y_; } + public getBottom() : number { return (this.y_ + this.height_); } + + public getPoint() : Sprite.Point { + return new Sprite.Point(this.x_ + this.width_/2, + this.y_ + this.height_/2); + } + + public getRectangle() : Sprite.Rectangle { + return new Sprite.Rectangle(this.x_, this.y_, + this.width_, this.height_); + } + + public getCircle(ratio : number = 1) : Sprite.Circle { + return new Sprite.Circle(this.x_ + this.width_/2, + this.y_ + this.height_/2, + ratio*this.width_/2); + } + + //--------------------------------------------------------------------------------------------Image + public setImage(url : string, width : number, height : number) { + if (this.element_ instanceof HTMLImageElement) { + ((this.element_)).src = url; + this.setWidth(width); + this.setHeight(height); + } + } + + //------------------------------------------------------------------------------------------Context + public getContext() : Sprite.Context2D { + if (this.element_ instanceof HTMLCanvasElement) + return ((this.element_)).getContext("2d"); + + return null; + } + + //---------------------------------------------------------------------------------------Visibility + // public hide() { this.element_.style.display = "none"; } + // public show() { this.element_.style.display = "block"; } + // public isVisible() : boolean { return (this.element_.style.display != "none"); } + + public hide() { this.element_.style.visibility = "hidden"; } + public show() { this.element_.style.visibility = "visible"; } + public isVisible() : boolean { return (this.element_.style.visibility != "hidden"); } + + //-----------------------------------------------------------------------------------------Rotation + public getRotation() : number { return this.rotation_; } + + public setRotation(angle : number) { + this.rotation_ = angle; + this.element_.style.transform = "rotate(" + angle + "deg)"; + } + + public setRotationPivot(x : number, y : number) { + this.element_.style.transformOrigin = x + "px " + y + "px"; + } + + //------------------------------------------------------------------------------------------Opacity + public getOpacity() : number { return this.opacity_; } + + public setOpacity(opacity : number) { + this.opacity_ = opacity; + this.element_.style.opacity = "" + this.opacity_; + } + + //-------------------------------------------------------------------------------------------Events + public addEventListener(type : string, action : any) { + this.element_.addEventListener(type,action); + } + + public removeEventListener(type : string, action : any) { + this.element_.removeEventListener(type,action); + } + + //--------------------------------------------------------------------------------------------Mouse + public scaledMouseX(x : number) : number { return x; } + public scaledMouseY(y : number) : number { return y; } + + //-----------------------------------------------------------------------------------------Follower + public follow(target : Sprite, x : number, y: number, frequency : number) : number { + return setInterval( () => { this.placeOnTarget(target,x,y); }, frequency); + } + + private placeOnTarget(target : Sprite, x : number, y : number) { + let distance : number = Math.sqrt(x*x + y*y); + let angle1 : number = target.getRotation()/180 * Math.PI; + let angle2 : number = (y < 0 ? -Math.acos(x/distance) : Math.acos(x/distance)); + + x = distance * Math.cos(angle1+angle2); + y = distance * Math.sin(angle1+angle2); + + this.setX(target.getCenterX() - this.getWidth()/2 + x); + this.setY(target.getCenterY() - this.getHeight()/2 + y); + } +} + +// Type Aliases //---------------------------------------------------------------------------------- +namespace Sprite { + export type Context2D = CanvasRenderingContext2D; +} + +// Collision Tests //------------------------------------------------------------------------------- +namespace Sprite { + //----------------------------------------------------------------------------------------Collision + export function collision(a : Shape, b : Shape) { + if (a instanceof Circle) { + if (b instanceof Circle) + return collisionCircleCircle(a,b); + else if (b instanceof Rectangle) + return collisionCircleRectangle(a,b); + else if (b instanceof Point) + return collisionCirclePoint(a,b); + } + else if (a instanceof Rectangle) { + if (b instanceof Circle) + return collisionCircleRectangle(b,a); + else if (b instanceof Rectangle) + return collisionRectangleRectangle(a,b); + else if (b instanceof Point) + return collisionRectanglePoint(a,b); + } + else if (a instanceof Point) { + if (b instanceof Circle) + return collisionCirclePoint(b,a); + else if (b instanceof Rectangle) + return collisionRectanglePoint(b,a); + else if (b instanceof Point) + return collisionPointPoint(a,b); + } + } + + //--------------------------------------------------------------------------------------Shape Class + export class Shape {} + + //--------------------------------------------------------------------------------------Point Class + export class Point extends Shape { + public x_; + public y_; + + public constructor(x : number, y : number) { + super(); + this.x_ = x; + this.y_ = y; + } + } + + //-------------------------------------------------------------------------------------Circle Class + export class Circle extends Shape { + public cx_ : number; + public cy_ : number; + public radius_ : number; + + public constructor(centerX : number, centerY : number, radius : number) { + super(); + this.cx_ = centerX; + this.cy_ = centerY; + this.radius_ = radius; + } + } + + //---------------------------------------------------------------------------------Classe Rectangle + export class Rectangle extends Shape { + public x_ : number; + public y_ : number; + public width_ : number; + public height_ : number; + + public constructor(cornerX : number, cornerY : number, width : number, height : number) { + super(); + this.x_ = cornerX; + this.y_ = cornerY; + this.width_ = width; + this.height_ = height; + } + } + + //-----------------------------------------------------------------------Circle-Rectangle Collision + function collisionCircleRectangle(circle : Circle, rectangle : Rectangle) : boolean { + let px : number = Math.max(rectangle.x_, + Math.min(circle.cx_, rectangle.x_ + rectangle.width_)); + let py : number = Math.max(rectangle.y_, + Math.min(circle.cy_, rectangle.y_ + rectangle.height_)); + let dx : number = px - circle.cx_; + let dy : number = py - circle.cy_; + let distance : number = dx*dx + dy*dy; + let radius : number = circle.radius_ * circle.radius_; + + return (distance < radius); + } + + //--------------------------------------------------------------------------Circle-Circle Collision + function collisionCircleCircle(circle1 : Circle, circle2 : Circle) : boolean { + let dx : number = circle1.cx_ - circle2.cx_; + let dy : number = circle1.cy_ - circle2.cy_; + let distance : number = dx*dx + dy*dy; + let radius : number = circle1.radius_ + circle2.radius_; + + return (distance < radius*radius); + } + + //---------------------------------------------------------------------------Circle-Point Collision + function collisionCirclePoint(circle : Circle, point : Point) : boolean { + let dx : number = point.x_ - circle.cx_; + let dy : number = point.y_ - circle.cy_; + let distance : number = dx*dx + dy*dy; + let radius : number = circle.radius_ * circle.radius_; + + return (distance < radius); + } + + //--------------------------------------------------------------------Rectangle-Rectangle Collision + function collisionRectangleRectangle(rectangle1 : Rectangle, + rectangle2 : Rectangle) : boolean { + return (rectangle1.x_ < rectangle2.x_+rectangle2.width_ + && rectangle1.x_+rectangle1.width_ > rectangle2.x_ + && rectangle1.y_ < rectangle2.y_+rectangle2.height_ + && rectangle1.y_+rectangle1.height_ > rectangle2.y_); + } + + //------------------------------------------------------------------------Rectangle-Point Collision + function collisionRectanglePoint(rectangle : Rectangle, point : Point) : boolean { + return (point.x_ >= rectangle.x_ && point.x_ <= rectangle.x_+rectangle.width_ + && point.y_ >= rectangle.y_ && point.y_ <= rectangle.y_+rectangle.height_); + } + + //----------------------------------------------------------------------------Point-Point Collision + function collisionPointPoint(point1 : Point, point2 : Point) : boolean { + let dx : number = point1.x_ - point2.x_; + let dy : number = point1.y_ - point2.y_; + let distance : number = dx*dx + dy*dy; + + return (distance < 1); + } +} + +// End //------------------------------------------------------------------------------------------- diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c8d81ad --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES6", + "lib": [ "ES6", "DOM" ], + "strict": true, + "strictNullChecks": false, + "noImplicitOverride": true, + "removeComments": true, + "outDir": "build", + "rootDir": "source" + } +}