Basic implementations
This commit is contained in:
parent
893c226fe4
commit
fb762d1778
|
@ -8,6 +8,7 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
background-color: #6096ff;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script type="module" src="js/module.js"></script>
|
<script type="module" src="js/module.js"></script>
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
export default class Key
|
||||||
|
{
|
||||||
|
constructor(name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
this.pressed = false;
|
||||||
|
|
||||||
|
window.addEventListener(
|
||||||
|
'keydown',
|
||||||
|
(event) => {
|
||||||
|
if (event.code === this.name) {
|
||||||
|
this.pressed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
window.addEventListener(
|
||||||
|
'keyup',
|
||||||
|
(event) => {
|
||||||
|
if (event.code === this.name) {
|
||||||
|
this.pressed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
isPressed()
|
||||||
|
{
|
||||||
|
return this.pressed;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import InterfaceEvent from "./events/InterfaceEvent.js";
|
||||||
|
import ImageLoadedEvent from "./events/ImageLoadedEvent";
|
||||||
|
|
||||||
|
export default class MediaImage
|
||||||
|
{
|
||||||
|
constructor(filename)
|
||||||
|
{
|
||||||
|
this.image = new Image();
|
||||||
|
this.image.src = filename;
|
||||||
|
this.image.onload = function () {
|
||||||
|
window.dispatchEvent(new ImageLoadedEvent(filename));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import InterfaceEvent from "./events/InterfaceEvent.js";
|
||||||
|
import ImageLoadedEvent from "./events/ImageLoadedEvent";
|
||||||
|
|
||||||
|
export default class MediaImageCollection
|
||||||
|
{
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
this.mediaImages = [];
|
||||||
|
this.numberImagesLoaded = 0;
|
||||||
|
|
||||||
|
window.addEventListener(
|
||||||
|
ImageLoadedEvent.NAME,
|
||||||
|
() => {
|
||||||
|
this.numberImagesLoaded++;
|
||||||
|
|
||||||
|
if (this.numberImagesLoaded === this.mediaImages.length) {
|
||||||
|
window.dispatchEvent(new InterfaceEvent.MEDIA_COLLECTION_LOADED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
addMediaImage(mediaImage)
|
||||||
|
{
|
||||||
|
this.mediaImages.push(mediaImage);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
import GeometryPoint from "./geometry/GeometryPoint.js";
|
||||||
|
|
||||||
|
export default class Movable
|
||||||
|
{
|
||||||
|
constructor(defaultAnimation, speed = 1)
|
||||||
|
{
|
||||||
|
this.currentAnimation = 'DEFAULT';
|
||||||
|
this.animations = {
|
||||||
|
DEFAULT: defaultAnimation,
|
||||||
|
};
|
||||||
|
this.position = new GeometryPoint();
|
||||||
|
this.speed = speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
playAnimation(animation, timestamp)
|
||||||
|
{
|
||||||
|
this.currentAnimation = animation;
|
||||||
|
this.animations[animation].play(timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
addAnimation(name, animation)
|
||||||
|
{
|
||||||
|
this.animations[name] = animation;
|
||||||
|
}
|
||||||
|
|
||||||
|
moveLeft(delta = 1)
|
||||||
|
{
|
||||||
|
this.position.x -= this.speed * delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
moveRight(delta = 1)
|
||||||
|
{
|
||||||
|
this.position.x += this.speed * delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(context)
|
||||||
|
{
|
||||||
|
this.animations[this.currentAnimation].setFootPosition(this.position.x, this.position.y);
|
||||||
|
this.animations[this.currentAnimation].draw(context);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
import Movable from "./Movable.js";
|
||||||
|
import RetroAnimation from "./retro/RetroAnimation.js";
|
||||||
|
|
||||||
|
export default class MrCroc extends Movable
|
||||||
|
{
|
||||||
|
constructor() {
|
||||||
|
super(new RetroAnimation('graphics/mr-croc-walk-right.png', 2, 5), 5);
|
||||||
|
|
||||||
|
this.addAnimation('WALK_RIGHT', new RetroAnimation('graphics/mr-croc-walk-right.png', 2, 5, 10));
|
||||||
|
this.addAnimation('WALK_LEFT', new RetroAnimation('graphics/mr-croc-walk-left.png', 2, 5, 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
moveRight(timestamp, delta = 1)
|
||||||
|
{
|
||||||
|
this.playAnimation('WALK_RIGHT', timestamp);
|
||||||
|
super.moveRight(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
moveLeft(timestamp, delta = 1)
|
||||||
|
{
|
||||||
|
this.playAnimation('WALK_LEFT', timestamp);
|
||||||
|
super.moveLeft(delta);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
export default class AudioLoadedEvent extends CustomEvent
|
||||||
|
{
|
||||||
|
constructor(filename) {
|
||||||
|
super('audioloaded', {detail: filename});
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilename()
|
||||||
|
{
|
||||||
|
this.detail;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
export default class BaseEvent extends CustomEvent
|
||||||
|
{
|
||||||
|
constructor(name, detail) {
|
||||||
|
super(name, {detail: filename});
|
||||||
|
this.NAME = name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import BaseEvent from "./BaseEvent.js";
|
||||||
|
|
||||||
|
export default class ImageLoadedEvent extends BaseEvent
|
||||||
|
{
|
||||||
|
constructor(filename) {
|
||||||
|
super('imgloaded', filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilename()
|
||||||
|
{
|
||||||
|
this.detail;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
export default InterfaceEvent;
|
||||||
|
|
||||||
|
const InterfaceEvent = {
|
||||||
|
IMAGE_LOADED: 'imgloaded',
|
||||||
|
MEDIA_COLLECTION_LOADED: 'mediacollectionloaded',
|
||||||
|
};
|
|
@ -1,6 +1,6 @@
|
||||||
export default class GeometryPoint
|
export default class GeometryPoint
|
||||||
{
|
{
|
||||||
constructor(x, y)
|
constructor(x = 0, y = 0)
|
||||||
{
|
{
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import GeometryPoint from "./GeometryPoint.js";
|
import GeometryPoint from "./GeometryPoint.js";
|
||||||
import GeometryStroke from "./GeometryStroke.js";
|
import GeometryStroke from "./GeometryStroke.js";
|
||||||
import GeometryPointCollection from "./GeometryPointCollection.js";
|
import GeometryPointCollection from "./GeometryPointCollection.js";
|
||||||
|
import GeometryLine from "./GeometryLine.js";
|
||||||
|
|
||||||
export default class GeometryRect
|
export default class GeometryRect
|
||||||
{
|
{
|
||||||
|
@ -31,6 +32,11 @@ export default class GeometryRect
|
||||||
return containsHorizontally && containsVertically;
|
return containsHorizontally && containsVertically;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasIntersectionWithRect(rect)
|
||||||
|
{
|
||||||
|
return this.getBorderIntersectonsWithRect(rect).getLength() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
getBorderTop()
|
getBorderTop()
|
||||||
{
|
{
|
||||||
return new GeometryStroke(
|
return new GeometryStroke(
|
||||||
|
@ -92,6 +98,12 @@ export default class GeometryRect
|
||||||
return intersections;
|
return intersections;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the intersection points between the border lines and the given stroke.
|
||||||
|
*
|
||||||
|
* @param stroke
|
||||||
|
* @returns {GeometryPointCollection}
|
||||||
|
*/
|
||||||
getBorderIntersectionsWithStroke(stroke)
|
getBorderIntersectionsWithStroke(stroke)
|
||||||
{
|
{
|
||||||
let borders = [
|
let borders = [
|
||||||
|
@ -172,6 +184,12 @@ export default class GeometryRect
|
||||||
*/
|
*/
|
||||||
getRectIntersection(rect)
|
getRectIntersection(rect)
|
||||||
{
|
{
|
||||||
|
let distanceToOrigin = this.getCenter().getDistanceToPoint(rect.getCenter());
|
||||||
|
|
||||||
|
if (distanceToOrigin > this.getDiagonal() * 0.5 + rect.getDiagonal() * 0.5) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
let intersectionPoints = new GeometryPointCollection();
|
let intersectionPoints = new GeometryPointCollection();
|
||||||
|
|
||||||
this.getRectanglePoints().forEach(
|
this.getRectanglePoints().forEach(
|
||||||
|
@ -211,6 +229,22 @@ export default class GeometryRect
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCenter()
|
||||||
|
{
|
||||||
|
return new GeometryPoint(
|
||||||
|
this.position.x + this.width * 0.5,
|
||||||
|
this.position.y + this.height * 0.5
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDiagonal()
|
||||||
|
{
|
||||||
|
return new GeometryStroke(
|
||||||
|
this.position,
|
||||||
|
new GeometryPoint(this.position.x + this.width, this.position.y + this.height)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visualizes the rect.
|
* Visualizes the rect.
|
||||||
*
|
*
|
||||||
|
|
|
@ -21,6 +21,11 @@ export default class GeometryStroke extends GeometryLine
|
||||||
return new GeometryLine(this.pointA, this.pointB);
|
return new GeometryLine(this.pointA, this.pointB);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLength()
|
||||||
|
{
|
||||||
|
return this.pointA.getDistanceToPoint(this.pointB);
|
||||||
|
}
|
||||||
|
|
||||||
getIntersectionWithStroke(stroke)
|
getIntersectionWithStroke(stroke)
|
||||||
{
|
{
|
||||||
let intersection = super.getIntersectionWithLine(stroke);
|
let intersection = super.getIntersectionWithLine(stroke);
|
||||||
|
|
120
js/module.js
120
js/module.js
|
@ -1,17 +1,117 @@
|
||||||
import RetroSprite from "./retro/RetroSprite.js";
|
import RetroSprite from "./retro/RetroSprite.js";
|
||||||
|
import Key from "./Key.js";
|
||||||
|
import MrCroc from "./MrCroc.js";
|
||||||
|
|
||||||
|
const MEDIA_READY_EVENT = 'mediaready';
|
||||||
|
const IMAGE_READY_EVENT = 'imgready';
|
||||||
|
|
||||||
|
class ImageLoader
|
||||||
|
{
|
||||||
|
images = [];
|
||||||
|
numberImagesLoaded = 0;
|
||||||
|
|
||||||
|
update()
|
||||||
|
{
|
||||||
|
this.numberImagesLoaded++;
|
||||||
|
|
||||||
|
if (this.numberImagesLoaded === this.images.length) {
|
||||||
|
window.dispatchEvent(new Event('imagesloaded'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentProgress()
|
||||||
|
{
|
||||||
|
return this.numberImagesLoaded / this.images.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
addImage(image)
|
||||||
|
{
|
||||||
|
image.addEventListener(
|
||||||
|
'load', () => {
|
||||||
|
this.update();1
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.images.push(image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function MainLoop(timestamp)
|
||||||
|
{
|
||||||
|
if (lastRendered === undefined && lastTimestamp === undefined) {
|
||||||
|
lastRendered = timestamp;
|
||||||
|
lastTimestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
let delta = (timestamp - lastTimestamp) / (10 / GAME_SPEED);
|
||||||
|
|
||||||
|
if (KeyLeft.isPressed()) {
|
||||||
|
mrCroc.moveLeft(timestamp, delta);
|
||||||
|
} else if (KeyRight.isPressed()) {
|
||||||
|
mrCroc.moveRight(timestamp, delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastRendered >= FRAME_DURATION) {
|
||||||
|
context.clearRect(0, 0, window.innerWidth, window.innerHeight);
|
||||||
|
|
||||||
|
ground.draw(context);
|
||||||
|
mrCroc.draw(context);
|
||||||
|
lastRendered = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastTimestamp = timestamp;
|
||||||
|
|
||||||
|
window.requestAnimationFrame(MainLoop);
|
||||||
|
}
|
||||||
|
|
||||||
|
const FPS = 60;
|
||||||
|
const FRAME_DURATION = 1000 / FPS;
|
||||||
|
const GAME_SPEED = 1;
|
||||||
|
|
||||||
|
let lastRendered = undefined;
|
||||||
|
let lastTimestamp = undefined;
|
||||||
|
let context;
|
||||||
|
let ground, mrCroc;
|
||||||
|
|
||||||
|
let KeyLeft = new Key('ArrowLeft');
|
||||||
|
let KeyRight = new Key('ArrowRight');
|
||||||
|
|
||||||
|
let loader = new ImageLoader();
|
||||||
|
|
||||||
let image = new Image();
|
let image = new Image();
|
||||||
image.src = 'graphics/mario.png';
|
image.src = 'graphics/mr-croc-stand.png';
|
||||||
|
loader.addImage(image);
|
||||||
|
|
||||||
image.onload = function () {
|
let imgAnimation = new Image();
|
||||||
let sprite = new RetroSprite(image, 10);
|
imgAnimation.src = 'graphics/mr-croc-walk-right.png';
|
||||||
let canvas = document.getElementById('canvas');
|
loader.addImage(imgAnimation);
|
||||||
canvas.width = window.innerWidth;
|
|
||||||
canvas.height = window.innerHeight;
|
|
||||||
|
|
||||||
let context = canvas.getContext('2d');
|
let imgAnimationB = new Image();
|
||||||
|
imgAnimationB.src = 'graphics/mr-croc-walk-left.png';
|
||||||
|
loader.addImage(imgAnimationB);
|
||||||
|
|
||||||
console.log(context);
|
let imgBackground = new Image();
|
||||||
|
imgBackground.src = 'graphics/ground.jpg';
|
||||||
|
loader.addImage(imgBackground);
|
||||||
|
|
||||||
sprite.draw(context);
|
window.addEventListener(
|
||||||
};
|
'imagesloaded',
|
||||||
|
() => {
|
||||||
|
console.log('Loaded');
|
||||||
|
ground = new RetroSprite('graphics/ground.jpg', 4);
|
||||||
|
ground.position.y = window.innerHeight - ground.getRect().height;
|
||||||
|
|
||||||
|
let canvas = document.getElementById('canvas');
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
|
||||||
|
context = canvas.getContext('2d');
|
||||||
|
|
||||||
|
mrCroc = new MrCroc();
|
||||||
|
mrCroc.position.x = 100;
|
||||||
|
mrCroc.position.y = window.innerHeight - ground.getHeight();
|
||||||
|
ground.draw(context);
|
||||||
|
|
||||||
|
window.requestAnimationFrame(MainLoop);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
import RetroSprite from "./RetroSprite.js";
|
||||||
|
|
||||||
|
export default class RetroAnimation extends RetroSprite
|
||||||
|
{
|
||||||
|
constructor(image, frames, scale = 1, fps = 6)
|
||||||
|
{
|
||||||
|
super(image, scale);
|
||||||
|
this.frames = frames;
|
||||||
|
this.fps = fps;
|
||||||
|
this.frameDuration = 1000 / this.fps;
|
||||||
|
this.currentFrame = 0;
|
||||||
|
this.lastUpdate = undefined;
|
||||||
|
this.frameWidth = this.canvas.width / this.frames;
|
||||||
|
this.isPlaying = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getWidth() {
|
||||||
|
return this.frameWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
setFootPosition(x, y) {
|
||||||
|
this.position.x = x - this.frameWidth * 0.5;
|
||||||
|
this.position.y = y - this.canvas.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
play(timestamp)
|
||||||
|
{
|
||||||
|
this.isPlaying = true;
|
||||||
|
|
||||||
|
if (this.lastUpdate === undefined) {
|
||||||
|
this.lastUpdate = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timestamp - this.lastUpdate >= this.frameDuration) {
|
||||||
|
this.currentFrame = (this.currentFrame + 1) % this.frames;
|
||||||
|
this.lastUpdate = timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw(context)
|
||||||
|
{
|
||||||
|
if (!this.isPlaying) {
|
||||||
|
this.currentFrame = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.drawImage(
|
||||||
|
this.canvas,
|
||||||
|
this.frameWidth * this.currentFrame,
|
||||||
|
0,
|
||||||
|
this.frameWidth,
|
||||||
|
this.canvas.height,
|
||||||
|
this.position.x,
|
||||||
|
this.position.y,
|
||||||
|
this.frameWidth,
|
||||||
|
this.canvas.height
|
||||||
|
);
|
||||||
|
|
||||||
|
this.isPlaying = false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,12 @@
|
||||||
import GeometryPoint from "../geometry/GeometryPoint.js";
|
import GeometryPoint from "../geometry/GeometryPoint.js";
|
||||||
|
import GeometryRect from "../geometry/GeometryRect.js";
|
||||||
|
|
||||||
export default class RetroSprite
|
export default class RetroSprite
|
||||||
{
|
{
|
||||||
constructor(image, scale = 1)
|
constructor(image, scale = 1)
|
||||||
{
|
{
|
||||||
this.image = image;
|
this.image = new Image();
|
||||||
|
this.image.src = image;
|
||||||
this.canvas = document.createElement('canvas');
|
this.canvas = document.createElement('canvas');
|
||||||
this.context = this.canvas.getContext('2d');
|
this.context = this.canvas.getContext('2d');
|
||||||
this.position = new GeometryPoint();
|
this.position = new GeometryPoint();
|
||||||
|
@ -13,6 +15,37 @@ export default class RetroSprite
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the position of the sprite's center
|
||||||
|
*
|
||||||
|
* @returns {GeometryPoint}
|
||||||
|
*/
|
||||||
|
getCenter()
|
||||||
|
{
|
||||||
|
this.getRect().getCenter();
|
||||||
|
}
|
||||||
|
|
||||||
|
setFootPosition(x, y)
|
||||||
|
{
|
||||||
|
this.position.x = x - this.canvas.width * 0.5;
|
||||||
|
this.position.y = y - this.canvas.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRect()
|
||||||
|
{
|
||||||
|
return new GeometryRect(this.position.x, this.position.y, this.canvas.width, this.canvas.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
getWidth()
|
||||||
|
{
|
||||||
|
return this.canvas.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
getHeight()
|
||||||
|
{
|
||||||
|
return this.canvas.height;
|
||||||
|
}
|
||||||
|
|
||||||
draw(context)
|
draw(context)
|
||||||
{
|
{
|
||||||
context.drawImage(this.canvas, this.position.x, this.position.y);
|
context.drawImage(this.canvas, this.position.x, this.position.y);
|
||||||
|
@ -20,7 +53,7 @@ export default class RetroSprite
|
||||||
|
|
||||||
hasRectCollisionWith(sprite)
|
hasRectCollisionWith(sprite)
|
||||||
{
|
{
|
||||||
|
return this.getRect().getRectIntersection(sprite.getRect()) !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasCollisionWith(sprite)
|
hasCollisionWith(sprite)
|
||||||
|
@ -31,6 +64,9 @@ export default class RetroSprite
|
||||||
render()
|
render()
|
||||||
{
|
{
|
||||||
let canvasTemp = document.createElement('canvas');
|
let canvasTemp = document.createElement('canvas');
|
||||||
|
canvasTemp.width = this.image.width * this.scale;
|
||||||
|
canvasTemp.height = this.image.height * this.scale;
|
||||||
|
|
||||||
let contextTemp = canvasTemp.getContext('2d');
|
let contextTemp = canvasTemp.getContext('2d');
|
||||||
contextTemp.drawImage(this.image, 0, 0);
|
contextTemp.drawImage(this.image, 0, 0);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue