Basic implementations

This commit is contained in:
Mal 2020-01-22 22:50:45 +01:00
parent 893c226fe4
commit fb762d1778
17 changed files with 424 additions and 13 deletions

View File

@ -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
View 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
View 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));
}
}
}

View 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
View 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
View 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);
}
}

View 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
View File

@ -0,0 +1,7 @@
export default class BaseEvent extends CustomEvent
{
constructor(name, detail) {
super(name, {detail: filename});
this.NAME = name;
}
}

View File

View File

@ -0,0 +1,13 @@
import BaseEvent from "./BaseEvent.js";
export default class ImageLoadedEvent extends BaseEvent
{
constructor(filename) {
super('imgloaded', filename);
}
getFilename()
{
this.detail;
}
}

View File

@ -0,0 +1,6 @@
export default InterfaceEvent;
const InterfaceEvent = {
IMAGE_LOADED: 'imgloaded',
MEDIA_COLLECTION_LOADED: 'mediacollectionloaded',
};

View File

@ -1,6 +1,6 @@
export default class GeometryPoint
{
constructor(x, y)
constructor(x = 0, y = 0)
{
this.x = x;
this.y = y;

View File

@ -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.
*

View File

@ -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);

View File

@ -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);
}
);

View 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;
}
}

View File

@ -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);