Basic implementations
This commit is contained in:
parent
893c226fe4
commit
fb762d1778
@ -8,6 +8,7 @@
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: #6096ff;
|
||||
}
|
||||
</style>
|
||||
<script type="module" src="js/module.js"></script>
|
||||
|
31
js/Key.js
Normal file
31
js/Key.js
Normal file
@ -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;
|
||||
}
|
||||
}
|
14
js/MediaImage.js
Normal file
14
js/MediaImage.js
Normal file
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
28
js/MediaImageCollection.js
Normal file
28
js/MediaImageCollection.js
Normal file
@ -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);
|
||||
|
||||
}
|
||||
}
|
41
js/Movable.js
Normal file
41
js/Movable.js
Normal file
@ -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);
|
||||
}
|
||||
}
|
24
js/MrCroc.js
Normal file
24
js/MrCroc.js
Normal file
@ -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);
|
||||
}
|
||||
}
|
11
js/events/AudioLoadedEvent.js
Normal file
11
js/events/AudioLoadedEvent.js
Normal file
@ -0,0 +1,11 @@
|
||||
export default class AudioLoadedEvent extends CustomEvent
|
||||
{
|
||||
constructor(filename) {
|
||||
super('audioloaded', {detail: filename});
|
||||
}
|
||||
|
||||
getFilename()
|
||||
{
|
||||
this.detail;
|
||||
}
|
||||
}
|
7
js/events/BaseEvent.js
Normal file
7
js/events/BaseEvent.js
Normal file
@ -0,0 +1,7 @@
|
||||
export default class BaseEvent extends CustomEvent
|
||||
{
|
||||
constructor(name, detail) {
|
||||
super(name, {detail: filename});
|
||||
this.NAME = name;
|
||||
}
|
||||
}
|
0
js/events/ImageCollectionLoadedEvent.js
Normal file
0
js/events/ImageCollectionLoadedEvent.js
Normal file
13
js/events/ImageLoadedEvent.js
Normal file
13
js/events/ImageLoadedEvent.js
Normal file
@ -0,0 +1,13 @@
|
||||
import BaseEvent from "./BaseEvent.js";
|
||||
|
||||
export default class ImageLoadedEvent extends BaseEvent
|
||||
{
|
||||
constructor(filename) {
|
||||
super('imgloaded', filename);
|
||||
}
|
||||
|
||||
getFilename()
|
||||
{
|
||||
this.detail;
|
||||
}
|
||||
}
|
6
js/events/InterfaceEvent.js
Normal file
6
js/events/InterfaceEvent.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default InterfaceEvent;
|
||||
|
||||
const InterfaceEvent = {
|
||||
IMAGE_LOADED: 'imgloaded',
|
||||
MEDIA_COLLECTION_LOADED: 'mediacollectionloaded',
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
export default class GeometryPoint
|
||||
{
|
||||
constructor(x, y)
|
||||
constructor(x = 0, y = 0)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import GeometryPoint from "./GeometryPoint.js";
|
||||
import GeometryStroke from "./GeometryStroke.js";
|
||||
import GeometryPointCollection from "./GeometryPointCollection.js";
|
||||
import GeometryLine from "./GeometryLine.js";
|
||||
|
||||
export default class GeometryRect
|
||||
{
|
||||
@ -31,6 +32,11 @@ export default class GeometryRect
|
||||
return containsHorizontally && containsVertically;
|
||||
}
|
||||
|
||||
hasIntersectionWithRect(rect)
|
||||
{
|
||||
return this.getBorderIntersectonsWithRect(rect).getLength() > 0;
|
||||
}
|
||||
|
||||
getBorderTop()
|
||||
{
|
||||
return new GeometryStroke(
|
||||
@ -92,6 +98,12 @@ export default class GeometryRect
|
||||
return intersections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the intersection points between the border lines and the given stroke.
|
||||
*
|
||||
* @param stroke
|
||||
* @returns {GeometryPointCollection}
|
||||
*/
|
||||
getBorderIntersectionsWithStroke(stroke)
|
||||
{
|
||||
let borders = [
|
||||
@ -172,6 +184,12 @@ export default class GeometryRect
|
||||
*/
|
||||
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();
|
||||
|
||||
this.getRectanglePoints().forEach(
|
||||
@ -211,6 +229,22 @@ export default class GeometryRect
|
||||
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.
|
||||
*
|
||||
|
@ -21,6 +21,11 @@ export default class GeometryStroke extends GeometryLine
|
||||
return new GeometryLine(this.pointA, this.pointB);
|
||||
}
|
||||
|
||||
getLength()
|
||||
{
|
||||
return this.pointA.getDistanceToPoint(this.pointB);
|
||||
}
|
||||
|
||||
getIntersectionWithStroke(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 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();
|
||||
image.src = 'graphics/mario.png';
|
||||
image.src = 'graphics/mr-croc-stand.png';
|
||||
loader.addImage(image);
|
||||
|
||||
image.onload = function () {
|
||||
let sprite = new RetroSprite(image, 10);
|
||||
let canvas = document.getElementById('canvas');
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
let imgAnimation = new Image();
|
||||
imgAnimation.src = 'graphics/mr-croc-walk-right.png';
|
||||
loader.addImage(imgAnimation);
|
||||
|
||||
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);
|
||||
}
|
||||
);
|
||||
|
60
js/retro/RetroAnimation.js
Normal file
60
js/retro/RetroAnimation.js
Normal file
@ -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 GeometryRect from "../geometry/GeometryRect.js";
|
||||
|
||||
export default class RetroSprite
|
||||
{
|
||||
constructor(image, scale = 1)
|
||||
{
|
||||
this.image = image;
|
||||
this.image = new Image();
|
||||
this.image.src = image;
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.context = this.canvas.getContext('2d');
|
||||
this.position = new GeometryPoint();
|
||||
@ -13,6 +15,37 @@ export default class RetroSprite
|
||||
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)
|
||||
{
|
||||
context.drawImage(this.canvas, this.position.x, this.position.y);
|
||||
@ -20,7 +53,7 @@ export default class RetroSprite
|
||||
|
||||
hasRectCollisionWith(sprite)
|
||||
{
|
||||
|
||||
return this.getRect().getRectIntersection(sprite.getRect()) !== null;
|
||||
}
|
||||
|
||||
hasCollisionWith(sprite)
|
||||
@ -31,6 +64,9 @@ export default class RetroSprite
|
||||
render()
|
||||
{
|
||||
let canvasTemp = document.createElement('canvas');
|
||||
canvasTemp.width = this.image.width * this.scale;
|
||||
canvasTemp.height = this.image.height * this.scale;
|
||||
|
||||
let contextTemp = canvasTemp.getContext('2d');
|
||||
contextTemp.drawImage(this.image, 0, 0);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user