abstracted rendering to renderingManager
This commit is contained in:
parent
3d275ba7f9
commit
b6a0aa569a
|
@ -9,6 +9,8 @@ import SceneManager from "../Scene/SceneManager";
|
||||||
import AudioManager from "../Sound/AudioManager";
|
import AudioManager from "../Sound/AudioManager";
|
||||||
import Stats from "../Debug/Stats";
|
import Stats from "../Debug/Stats";
|
||||||
import ArrayUtils from "../Utils/ArrayUtils";
|
import ArrayUtils from "../Utils/ArrayUtils";
|
||||||
|
import RenderingManager from "../Rendering/RenderingManager";
|
||||||
|
import CanvasRenderer from "../Rendering/CanvasRenderer";
|
||||||
|
|
||||||
export default class GameLoop {
|
export default class GameLoop {
|
||||||
gameOptions: GameOptions;
|
gameOptions: GameOptions;
|
||||||
|
@ -70,6 +72,7 @@ export default class GameLoop {
|
||||||
private resourceManager: ResourceManager;
|
private resourceManager: ResourceManager;
|
||||||
private sceneManager: SceneManager;
|
private sceneManager: SceneManager;
|
||||||
private audioManager: AudioManager;
|
private audioManager: AudioManager;
|
||||||
|
private renderingManager: RenderingManager;
|
||||||
|
|
||||||
constructor(options?: Record<string, any>){
|
constructor(options?: Record<string, any>){
|
||||||
// Typecast the config object to a GameConfig object
|
// Typecast the config object to a GameConfig object
|
||||||
|
@ -100,7 +103,10 @@ export default class GameLoop {
|
||||||
// Give the canvas a size and get the rendering context
|
// Give the canvas a size and get the rendering context
|
||||||
this.WIDTH = this.gameOptions.viewportSize.x;
|
this.WIDTH = this.gameOptions.viewportSize.x;
|
||||||
this.HEIGHT = this.gameOptions.viewportSize.y;
|
this.HEIGHT = this.gameOptions.viewportSize.y;
|
||||||
this.ctx = this.initializeCanvas(this.GAME_CANVAS, this.WIDTH, this.HEIGHT);
|
|
||||||
|
// For now, just hard code a canvas renderer. We can do this with options later
|
||||||
|
this.renderingManager = new CanvasRenderer();
|
||||||
|
this.ctx = this.renderingManager.initializeCanvas(this.GAME_CANVAS, this.WIDTH, this.HEIGHT);
|
||||||
|
|
||||||
// Size the viewport to the game canvas
|
// Size the viewport to the game canvas
|
||||||
this.viewport = new Viewport();
|
this.viewport = new Viewport();
|
||||||
|
@ -114,24 +120,12 @@ export default class GameLoop {
|
||||||
this.inputReceiver.setViewport(this.viewport);
|
this.inputReceiver.setViewport(this.viewport);
|
||||||
this.recorder = new Recorder();
|
this.recorder = new Recorder();
|
||||||
this.resourceManager = ResourceManager.getInstance();
|
this.resourceManager = ResourceManager.getInstance();
|
||||||
this.sceneManager = new SceneManager(this.viewport, this);
|
this.sceneManager = new SceneManager(this.viewport, this, this.renderingManager);
|
||||||
this.audioManager = AudioManager.getInstance();
|
this.audioManager = AudioManager.getInstance();
|
||||||
|
|
||||||
Stats.initStats();
|
Stats.initStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeCanvas(canvas: HTMLCanvasElement, width: number, height: number): CanvasRenderingContext2D {
|
|
||||||
canvas.width = width;
|
|
||||||
canvas.height = height;
|
|
||||||
let ctx = canvas.getContext("2d");
|
|
||||||
|
|
||||||
// For crisp pixel art
|
|
||||||
ctx.imageSmoothingEnabled = false;
|
|
||||||
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO - This currently also changes the rendering framerate
|
|
||||||
/**
|
/**
|
||||||
* Changes the maximum allowed physics framerate of the game
|
* Changes the maximum allowed physics framerate of the game
|
||||||
* @param initMax
|
* @param initMax
|
||||||
|
@ -278,7 +272,7 @@ export default class GameLoop {
|
||||||
*/
|
*/
|
||||||
render(): void {
|
render(): void {
|
||||||
this.ctx.clearRect(0, 0, this.WIDTH, this.HEIGHT);
|
this.ctx.clearRect(0, 0, this.WIDTH, this.HEIGHT);
|
||||||
this.sceneManager.render(this.ctx);
|
this.sceneManager.render();
|
||||||
Debug.render(this.ctx);
|
Debug.render(this.ctx);
|
||||||
Stats.render();
|
Stats.render();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import AABB from "../DataTypes/Shapes/AABB";
|
||||||
/**
|
/**
|
||||||
* The representation of an object in the game world that can be drawn to the screen
|
* The representation of an object in the game world that can be drawn to the screen
|
||||||
*/
|
*/
|
||||||
export default abstract class CanvasNode extends GameNode implements Region, Renderable {
|
export default abstract class CanvasNode extends GameNode implements Region {
|
||||||
private _size: Vec2;
|
private _size: Vec2;
|
||||||
private _scale: Vec2;
|
private _scale: Vec2;
|
||||||
private _boundary: AABB;
|
private _boundary: AABB;
|
||||||
|
@ -75,6 +75,4 @@ export default abstract class CanvasNode extends GameNode implements Region, Ren
|
||||||
contains(x: number, y: number): boolean {
|
contains(x: number, y: number): boolean {
|
||||||
return this._boundary.containsPoint(new Vec2(x, y));
|
return this._boundary.containsPoint(new Vec2(x, y));
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract render(ctx: CanvasRenderingContext2D): void;
|
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@ import Color from "../Utils/Color";
|
||||||
*/
|
*/
|
||||||
export default abstract class Graphic extends CanvasNode {
|
export default abstract class Graphic extends CanvasNode {
|
||||||
|
|
||||||
protected color: Color;
|
color: Color;
|
||||||
|
|
||||||
constructor(){
|
constructor(){
|
||||||
super();
|
super();
|
||||||
|
|
|
@ -10,14 +10,4 @@ export default class Point extends Graphic {
|
||||||
}
|
}
|
||||||
|
|
||||||
update(deltaT: number): void {}
|
update(deltaT: number): void {}
|
||||||
|
|
||||||
render(ctx: CanvasRenderingContext2D): void {
|
|
||||||
let origin = this.scene.getViewTranslation(this);
|
|
||||||
let zoom = this.scene.getViewScale();
|
|
||||||
|
|
||||||
ctx.fillStyle = this.color.toStringRGBA();
|
|
||||||
ctx.fillRect((this.position.x - origin.x - this.size.x/2)*zoom, (this.position.y - origin.y - this.size.y/2)*zoom,
|
|
||||||
this.size.x*zoom, this.size.y*zoom);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -23,6 +23,10 @@ export default class Rect extends Graphic {
|
||||||
this.borderColor = color;
|
this.borderColor = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getBorderColor(): Color {
|
||||||
|
return this.borderColor;
|
||||||
|
}
|
||||||
|
|
||||||
/**Sets the border width of this rectangle
|
/**Sets the border width of this rectangle
|
||||||
*
|
*
|
||||||
* @param width The width of the rectangle in pixels
|
* @param width The width of the rectangle in pixels
|
||||||
|
@ -31,20 +35,9 @@ export default class Rect extends Graphic {
|
||||||
this.borderWidth = width;
|
this.borderWidth = width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getBorderWidth(): number {
|
||||||
|
return this.borderWidth;
|
||||||
|
}
|
||||||
|
|
||||||
update(deltaT: number): void {}
|
update(deltaT: number): void {}
|
||||||
|
|
||||||
render(ctx: CanvasRenderingContext2D): void {
|
|
||||||
let origin = this.scene.getViewTranslation(this);
|
|
||||||
let zoom = this.scene.getViewScale();
|
|
||||||
|
|
||||||
if(this.color.a !== 0){
|
|
||||||
ctx.fillStyle = this.color.toStringRGB();
|
|
||||||
ctx.fillRect((this.position.x - this.size.x/2 - origin.x)*zoom, (this.position.y - this.size.y/2 - origin.y)*zoom, this.size.x*zoom, this.size.y*zoom);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.strokeStyle = this.borderColor.toStringRGB();
|
|
||||||
ctx.lineWidth = this.borderWidth;
|
|
||||||
ctx.strokeRect((this.position.x - this.size.x/2 - origin.x)*zoom, (this.position.y - this.size.y/2 - origin.y)*zoom, this.size.x*zoom, this.size.y*zoom);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -6,8 +6,8 @@ import Vec2 from "../../DataTypes/Vec2";
|
||||||
* The representation of a sprite - an in-game image
|
* The representation of a sprite - an in-game image
|
||||||
*/
|
*/
|
||||||
export default class Sprite extends CanvasNode {
|
export default class Sprite extends CanvasNode {
|
||||||
private imageId: string;
|
imageId: string;
|
||||||
private imageOffset: Vec2;
|
imageOffset: Vec2;
|
||||||
|
|
||||||
constructor(imageId: string){
|
constructor(imageId: string){
|
||||||
super();
|
super();
|
||||||
|
@ -26,20 +26,4 @@ export default class Sprite extends CanvasNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
update(deltaT: number): void {}
|
update(deltaT: number): void {}
|
||||||
|
|
||||||
render(ctx: CanvasRenderingContext2D): void {
|
|
||||||
let image = ResourceManager.getInstance().getImage(this.imageId);
|
|
||||||
let origin = this.scene.getViewTranslation(this);
|
|
||||||
let zoom = this.scene.getViewScale();
|
|
||||||
|
|
||||||
ctx.drawImage(image,
|
|
||||||
this.imageOffset.x, this.imageOffset.y, this.size.x, this.size.y,
|
|
||||||
(this.position.x - origin.x - this.size.x*this.scale.x/2)*zoom, (this.position.y - origin.y - this.size.y*this.scale.y/2)*zoom,
|
|
||||||
this.size.x * this.scale.x*zoom, this.size.y * this.scale.y*zoom);
|
|
||||||
|
|
||||||
ctx.lineWidth = 4;
|
|
||||||
ctx.strokeStyle = "#00FF00"
|
|
||||||
let b = this.boundary;
|
|
||||||
ctx.strokeRect(b.x - b.hw - origin.x, b.y - b.hh - origin.y, b.hw*2*zoom, b.hh*2*zoom);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -84,6 +84,4 @@ export default abstract class Tilemap extends CanvasNode {
|
||||||
*/
|
*/
|
||||||
// TODO: This shouldn't use tiled data specifically - it should be more general
|
// TODO: This shouldn't use tiled data specifically - it should be more general
|
||||||
protected abstract parseTilemapData(tilemapData: TiledTilemapData, layer: TiledLayerData): void;
|
protected abstract parseTilemapData(tilemapData: TiledTilemapData, layer: TiledLayerData): void;
|
||||||
|
|
||||||
abstract render(ctx: CanvasRenderingContext2D): void;
|
|
||||||
}
|
}
|
|
@ -128,27 +128,4 @@ export default class OrthogonalTilemap extends Tilemap {
|
||||||
}
|
}
|
||||||
|
|
||||||
update(deltaT: number): void {}
|
update(deltaT: number): void {}
|
||||||
|
|
||||||
// TODO: Don't render tiles that aren't on screen
|
|
||||||
render(ctx: CanvasRenderingContext2D) {
|
|
||||||
let previousAlpha = ctx.globalAlpha;
|
|
||||||
ctx.globalAlpha = this.getLayer().getAlpha();
|
|
||||||
|
|
||||||
let origin = this.scene.getViewTranslation(this);
|
|
||||||
let zoom = this.scene.getViewScale();
|
|
||||||
|
|
||||||
if(this.visible){
|
|
||||||
for(let i = 0; i < this.data.length; i++){
|
|
||||||
let tileIndex = this.data[i];
|
|
||||||
|
|
||||||
for(let tileset of this.tilesets){
|
|
||||||
if(tileset.hasTile(tileIndex)){
|
|
||||||
tileset.renderTile(ctx, tileIndex, i, this.numCols, origin, this.scale, zoom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.globalAlpha = previousAlpha;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -7,11 +7,11 @@ import Vec2 from "../DataTypes/Vec2";
|
||||||
*/
|
*/
|
||||||
export default abstract class UIElement extends CanvasNode {
|
export default abstract class UIElement extends CanvasNode {
|
||||||
// Style attributes
|
// Style attributes
|
||||||
protected backgroundColor: Color;
|
backgroundColor: Color;
|
||||||
protected borderColor: Color;
|
borderColor: Color;
|
||||||
protected borderRadius: number;
|
borderRadius: number;
|
||||||
protected borderWidth: number;
|
borderWidth: number;
|
||||||
protected padding: Vec2;
|
padding: Vec2;
|
||||||
|
|
||||||
// EventAttributes
|
// EventAttributes
|
||||||
onClick: Function;
|
onClick: Function;
|
||||||
|
@ -115,14 +115,14 @@ export default abstract class UIElement extends CanvasNode {
|
||||||
/**
|
/**
|
||||||
* Overridable method for calculating background color - useful for elements that want to be colored on different after certain events
|
* Overridable method for calculating background color - useful for elements that want to be colored on different after certain events
|
||||||
*/
|
*/
|
||||||
protected calculateBackgroundColor(): string {
|
calculateBackgroundColor(): string {
|
||||||
return this.backgroundColor.toStringRGBA();
|
return this.backgroundColor.toStringRGBA();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overridable method for calculating border color - useful for elements that want to be colored on different after certain events
|
* Overridable method for calculating border color - useful for elements that want to be colored on different after certain events
|
||||||
*/
|
*/
|
||||||
protected calculateBorderColor(): string {
|
calculateBorderColor(): string {
|
||||||
return this.borderColor.toStringRGBA();
|
return this.borderColor.toStringRGBA();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@ export default class Button extends Label {
|
||||||
this.textColor = new Color(255, 255, 255);
|
this.textColor = new Color(255, 255, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected calculateBackgroundColor(): string {
|
calculateBackgroundColor(): string {
|
||||||
// Change the background color if clicked or hovered
|
// Change the background color if clicked or hovered
|
||||||
if(this.isEntered && !this.isClicked){
|
if(this.isEntered && !this.isClicked){
|
||||||
return this.backgroundColor.lighten().toStringRGBA();
|
return this.backgroundColor.lighten().toStringRGBA();
|
||||||
|
|
|
@ -3,8 +3,8 @@ import Color from "../../Utils/Color";
|
||||||
import UIElement from "../UIElement";
|
import UIElement from "../UIElement";
|
||||||
|
|
||||||
export default class Label extends UIElement{
|
export default class Label extends UIElement{
|
||||||
protected textColor: Color;
|
textColor: Color;
|
||||||
protected text: string;
|
text: string;
|
||||||
protected font: string;
|
protected font: string;
|
||||||
protected fontSize: number;
|
protected fontSize: number;
|
||||||
protected hAlign: string;
|
protected hAlign: string;
|
||||||
|
@ -33,10 +33,14 @@ export default class Label extends UIElement{
|
||||||
this.textColor = color;
|
this.textColor = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getFontString(): string {
|
||||||
|
return this.fontSize + "px " + this.font;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overridable method for calculating text color - useful for elements that want to be colored on different after certain events
|
* Overridable method for calculating text color - useful for elements that want to be colored on different after certain events
|
||||||
*/
|
*/
|
||||||
protected calculateTextColor(): string {
|
calculateTextColor(): string {
|
||||||
return this.textColor.toStringRGBA();
|
return this.textColor.toStringRGBA();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,9 +51,8 @@ export default class Label extends UIElement{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the offset of the text - this is useful for rendering text with different alignments
|
* Calculate the offset of the text - this is useful for rendering text with different alignments
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
protected calculateTextOffset(ctx: CanvasRenderingContext2D): Vec2 {
|
calculateTextOffset(ctx: CanvasRenderingContext2D): Vec2 {
|
||||||
let textWidth = this.calculateTextWidth(ctx);
|
let textWidth = this.calculateTextWidth(ctx);
|
||||||
|
|
||||||
let offset = new Vec2(0, 0);
|
let offset = new Vec2(0, 0);
|
||||||
|
@ -80,49 +83,22 @@ export default class Label extends UIElement{
|
||||||
this.sizeAssigned = true;
|
this.sizeAssigned = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected autoSize(ctx: CanvasRenderingContext2D){
|
protected autoSize(ctx: CanvasRenderingContext2D): void {
|
||||||
let width = this.calculateTextWidth(ctx);
|
let width = this.calculateTextWidth(ctx);
|
||||||
let height = this.fontSize;
|
let height = this.fontSize;
|
||||||
this.size.set(width + this.padding.x*2, height + this.padding.y*2);
|
this.size.set(width + this.padding.x*2, height + this.padding.y*2);
|
||||||
this.sizeAssigned = true;
|
this.sizeAssigned = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** On the next render, size this element to it's current text using its current font size */
|
handleInitialSizing(ctx: CanvasRenderingContext2D): void {
|
||||||
sizeToText(): void {
|
|
||||||
this.sizeAssigned = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
render(ctx: CanvasRenderingContext2D): void {
|
|
||||||
// If the size is unassigned (by the user or automatically) assign it
|
|
||||||
if(!this.sizeAssigned){
|
if(!this.sizeAssigned){
|
||||||
this.autoSize(ctx);
|
this.autoSize(ctx);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Grab the global alpha so we can adjust it for this render
|
/** On the next render, size this element to it's current text using its current font size */
|
||||||
let previousAlpha = ctx.globalAlpha;
|
sizeToText(): void {
|
||||||
|
this.sizeAssigned = false;
|
||||||
let origin = this.scene.getViewTranslation(this);
|
|
||||||
|
|
||||||
ctx.font = this.fontSize + "px " + this.font;
|
|
||||||
let offset = this.calculateTextOffset(ctx);
|
|
||||||
|
|
||||||
// Stroke and fill a rounded rect and give it text
|
|
||||||
ctx.globalAlpha = this.backgroundColor.a;
|
|
||||||
ctx.fillStyle = this.calculateBackgroundColor();
|
|
||||||
ctx.fillRoundedRect(this.position.x - origin.x - this.size.x/2, this.position.y - origin.y - this.size.y/2,
|
|
||||||
this.size.x, this.size.y, this.borderRadius);
|
|
||||||
|
|
||||||
ctx.strokeStyle = this.calculateBorderColor();
|
|
||||||
ctx.globalAlpha = this.borderColor.a;
|
|
||||||
ctx.lineWidth = this.borderWidth;
|
|
||||||
ctx.strokeRoundedRect(this.position.x - origin.x - this.size.x/2, this.position.y - origin.y - this.size.y/2,
|
|
||||||
this.size.x, this.size.y, this.borderRadius);
|
|
||||||
|
|
||||||
ctx.fillStyle = this.calculateTextColor();
|
|
||||||
ctx.globalAlpha = this.textColor.a;
|
|
||||||
ctx.fillText(this.text, this.position.x + offset.x - origin.x - this.size.x/2, this.position.y + offset.y - origin.y - this.size.y/2);
|
|
||||||
|
|
||||||
ctx.globalAlpha = previousAlpha;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_render = (ctx: CanvasRenderingContext2D): void => {
|
debug_render = (ctx: CanvasRenderingContext2D): void => {
|
||||||
|
|
|
@ -6,8 +6,8 @@ import UIElement from "../UIElement";
|
||||||
export default class Slider extends UIElement {
|
export default class Slider extends UIElement {
|
||||||
/** The value of the slider from [0, 1] */
|
/** The value of the slider from [0, 1] */
|
||||||
protected value: number;
|
protected value: number;
|
||||||
protected nibColor: Color;
|
public nibColor: Color;
|
||||||
protected sliderColor: Color;
|
public sliderColor: Color;
|
||||||
public onValueChange: (value: number) => void;
|
public onValueChange: (value: number) => void;
|
||||||
public onValueChangeEventId: string;
|
public onValueChangeEventId: string;
|
||||||
|
|
||||||
|
@ -24,6 +24,10 @@ export default class Slider extends UIElement {
|
||||||
this.size.set(200, 20);
|
this.size.set(200, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getValue(): number {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
protected valueChanged(): void {
|
protected valueChanged(): void {
|
||||||
if(this.onValueChange){
|
if(this.onValueChange){
|
||||||
this.onValueChange(this.value);
|
this.onValueChange(this.value);
|
||||||
|
@ -43,34 +47,4 @@ export default class Slider extends UIElement {
|
||||||
this.valueChanged();
|
this.valueChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render(ctx: CanvasRenderingContext2D): void {
|
|
||||||
// Grab the global alpha so we can adjust it for this render
|
|
||||||
let previousAlpha = ctx.globalAlpha;
|
|
||||||
ctx.globalAlpha = this.getLayer().getAlpha();
|
|
||||||
|
|
||||||
let origin = this.scene.getViewTranslation(this);
|
|
||||||
|
|
||||||
// Calcualate the slider size
|
|
||||||
let sliderSize = new Vec2(this.size.x, 2);
|
|
||||||
|
|
||||||
// Draw the slider
|
|
||||||
ctx.fillStyle = this.sliderColor.toString();
|
|
||||||
ctx.fillRoundedRect(this.position.x - origin.x - sliderSize.x/2, this.position.y - origin.y - sliderSize.y/2,
|
|
||||||
sliderSize.x, sliderSize.y, this.borderRadius);
|
|
||||||
|
|
||||||
// Calculate the nib size and position
|
|
||||||
let nibSize = new Vec2(10, this.size.y);
|
|
||||||
let x = MathUtils.lerp(this.position.x - this.size.x/2, this.position.x + this.size.x/2, this.value);
|
|
||||||
let nibPosition = new Vec2(x, this.position.y);
|
|
||||||
|
|
||||||
// Draw the nib
|
|
||||||
ctx.fillStyle = this.nibColor.toString();
|
|
||||||
ctx.fillRoundedRect(nibPosition.x - origin.x - nibSize.x/2, nibPosition.y - origin.y - nibSize.y/2,
|
|
||||||
nibSize.x, nibSize.y, this.borderRadius);
|
|
||||||
|
|
||||||
|
|
||||||
// Reset the alpha
|
|
||||||
ctx.globalAlpha = previousAlpha;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -58,24 +58,4 @@ export default class TextInput extends Label {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render(ctx: CanvasRenderingContext2D): void {
|
|
||||||
// Show a cursor sometimes
|
|
||||||
if(this.focused && this.cursorCounter % 60 > 30){
|
|
||||||
this.text += "|";
|
|
||||||
}
|
|
||||||
|
|
||||||
super.render(ctx);
|
|
||||||
|
|
||||||
if(this.focused){
|
|
||||||
if(this.cursorCounter % 60 > 30){
|
|
||||||
this.text = this.text.substring(0, this.text.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.cursorCounter += 1;
|
|
||||||
if(this.cursorCounter >= 60){
|
|
||||||
this.cursorCounter = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
152
src/Rendering/CanvasRenderer.ts
Normal file
152
src/Rendering/CanvasRenderer.ts
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
import Map from "../DataTypes/Map";
|
||||||
|
import CanvasNode from "../Nodes/CanvasNode";
|
||||||
|
import Graphic from "../Nodes/Graphic";
|
||||||
|
import Point from "../Nodes/Graphics/Point";
|
||||||
|
import Rect from "../Nodes/Graphics/Rect";
|
||||||
|
import Sprite from "../Nodes/Sprites/Sprite";
|
||||||
|
import Tilemap from "../Nodes/Tilemap";
|
||||||
|
import OrthogonalTilemap from "../Nodes/Tilemaps/OrthogonalTilemap";
|
||||||
|
import UIElement from "../Nodes/UIElement";
|
||||||
|
import UILayer from "../Scene/Layers/UILayer";
|
||||||
|
import Scene from "../Scene/Scene";
|
||||||
|
import GraphicRenderer from "./CanvasRendering/GraphicRenderer";
|
||||||
|
import RenderingManager from "./RenderingManager"
|
||||||
|
import TilemapRenderer from "./CanvasRendering/TilemapRenderer";
|
||||||
|
import UIElementRenderer from "./CanvasRendering/UIElementRenderer";
|
||||||
|
import Label from "../Nodes/UIElements/Label";
|
||||||
|
import Button from "../Nodes/UIElements/Button";
|
||||||
|
import Slider from "../Nodes/UIElements/Slider";
|
||||||
|
import TextInput from "../Nodes/UIElements/TextInput";
|
||||||
|
|
||||||
|
export default class CanvasRenderer extends RenderingManager {
|
||||||
|
protected ctx: CanvasRenderingContext2D;
|
||||||
|
protected graphicRenderer: GraphicRenderer;
|
||||||
|
protected tilemapRenderer: TilemapRenderer;
|
||||||
|
protected uiElementRenderer: UIElementRenderer;
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
super();;
|
||||||
|
}
|
||||||
|
|
||||||
|
setScene(scene: Scene){
|
||||||
|
this.scene = scene;
|
||||||
|
this.graphicRenderer.setScene(scene);
|
||||||
|
this.tilemapRenderer.setScene(scene);
|
||||||
|
this.uiElementRenderer.setScene(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeCanvas(canvas: HTMLCanvasElement, width: number, height: number): CanvasRenderingContext2D {
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
|
||||||
|
this.ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
this.graphicRenderer = new GraphicRenderer(this.ctx);
|
||||||
|
this.tilemapRenderer = new TilemapRenderer(this.ctx);
|
||||||
|
this.uiElementRenderer = new UIElementRenderer(this.ctx)
|
||||||
|
|
||||||
|
// For crisp pixel art
|
||||||
|
this.ctx.imageSmoothingEnabled = false;
|
||||||
|
|
||||||
|
return this.ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
render(visibleSet: CanvasNode[], tilemaps: Tilemap[], uiLayers: Map<UILayer>): void {
|
||||||
|
// Sort by depth, then by visible set by y-value
|
||||||
|
visibleSet.sort((a, b) => {
|
||||||
|
if(a.getLayer().getDepth() === b.getLayer().getDepth()){
|
||||||
|
return (a.boundary.bottom) - (b.boundary.bottom);
|
||||||
|
} else {
|
||||||
|
return a.getLayer().getDepth() - b.getLayer().getDepth();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Render tilemaps
|
||||||
|
tilemaps.forEach(tilemap => {
|
||||||
|
this.renderTilemap(tilemap);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Render visible set
|
||||||
|
visibleSet.forEach(node => {
|
||||||
|
if(node.visible){
|
||||||
|
this.renderNode(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Render the uiLayers
|
||||||
|
uiLayers.forEach(key => uiLayers.get(key).getItems().forEach(node => this.renderNode(<CanvasNode>node)));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderNode(node: CanvasNode): void {
|
||||||
|
if(node instanceof Sprite){
|
||||||
|
this.renderSprite(<Sprite>node);
|
||||||
|
} else if(node instanceof Graphic){
|
||||||
|
this.renderGraphic(<Graphic>node);
|
||||||
|
} else if(node instanceof UIElement){
|
||||||
|
this.renderUIElement(<UIElement>node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderSprite(sprite: Sprite): void {
|
||||||
|
// Get the image from the resource manager
|
||||||
|
let image = this.resourceManager.getImage(sprite.imageId);
|
||||||
|
|
||||||
|
// Calculate the origin of the viewport according to this sprite
|
||||||
|
let origin = this.scene.getViewTranslation(sprite);
|
||||||
|
|
||||||
|
// Get the zoom level of the scene
|
||||||
|
let zoom = this.scene.getViewScale();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Coordinates in the space of the image:
|
||||||
|
image crop start -> x, y
|
||||||
|
image crop size -> w, h
|
||||||
|
Coordinates in the space of the world
|
||||||
|
image draw start -> x, y
|
||||||
|
image draw size -> w, h
|
||||||
|
*/
|
||||||
|
this.ctx.drawImage(image,
|
||||||
|
sprite.imageOffset.x, sprite.imageOffset.y,
|
||||||
|
sprite.size.x, sprite.size.y,
|
||||||
|
(sprite.position.x - origin.x - sprite.size.x*sprite.scale.x/2)*zoom, (sprite.position.y - origin.y - sprite.size.y*sprite.scale.y/2)*zoom,
|
||||||
|
sprite.size.x * sprite.scale.x*zoom, sprite.size.y * sprite.scale.y*zoom);
|
||||||
|
|
||||||
|
// Debug mode
|
||||||
|
if(this.debug){
|
||||||
|
this.ctx.lineWidth = 4;
|
||||||
|
this.ctx.strokeStyle = "#00FF00"
|
||||||
|
let b = sprite.boundary;
|
||||||
|
this.ctx.strokeRect(b.x - b.hw - origin.x, b.y - b.hh - origin.y, b.hw*2*zoom, b.hh*2*zoom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderAnimatedSprite(): void {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderGraphic(graphic: Graphic): void {
|
||||||
|
if(graphic instanceof Point){
|
||||||
|
this.graphicRenderer.renderPoint(<Point>graphic);
|
||||||
|
} else if(graphic instanceof Rect){
|
||||||
|
this.graphicRenderer.renderRect(<Rect>graphic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderTilemap(tilemap: Tilemap): void {
|
||||||
|
if(tilemap instanceof OrthogonalTilemap){
|
||||||
|
this.tilemapRenderer.renderOrthogonalTilemap(<OrthogonalTilemap>tilemap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderUIElement(uiElement: UIElement): void {
|
||||||
|
if(uiElement instanceof Label){
|
||||||
|
this.uiElementRenderer.renderLabel(uiElement);
|
||||||
|
} else if(uiElement instanceof Button){
|
||||||
|
this.uiElementRenderer.renderButton(uiElement);
|
||||||
|
} else if(uiElement instanceof Slider){
|
||||||
|
this.uiElementRenderer.renderSlider(uiElement);
|
||||||
|
} else if(uiElement instanceof TextInput){
|
||||||
|
this.uiElementRenderer.renderTextInput(uiElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
src/Rendering/CanvasRendering/GraphicRenderer.ts
Normal file
44
src/Rendering/CanvasRendering/GraphicRenderer.ts
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import Point from "../../Nodes/Graphics/Point";
|
||||||
|
import Rect from "../../Nodes/Graphics/Rect";
|
||||||
|
import ResourceManager from "../../ResourceManager/ResourceManager";
|
||||||
|
import Scene from "../../Scene/Scene";
|
||||||
|
|
||||||
|
export default class GraphicRenderer {
|
||||||
|
protected resourceManager: ResourceManager;
|
||||||
|
protected scene: Scene;
|
||||||
|
protected ctx: CanvasRenderingContext2D;
|
||||||
|
|
||||||
|
constructor(ctx: CanvasRenderingContext2D){
|
||||||
|
this.resourceManager = ResourceManager.getInstance();
|
||||||
|
this.ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
setScene(scene: Scene): void {
|
||||||
|
this.scene = scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPoint(point: Point): void {
|
||||||
|
let origin = this.scene.getViewTranslation(point);
|
||||||
|
let zoom = this.scene.getViewScale();
|
||||||
|
|
||||||
|
this.ctx.fillStyle = point.color.toStringRGBA();
|
||||||
|
this.ctx.fillRect((point.position.x - origin.x - point.size.x/2)*zoom, (point.position.y - origin.y - point.size.y/2)*zoom,
|
||||||
|
point.size.x*zoom, point.size.y*zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRect(rect: Rect): void {
|
||||||
|
let origin = this.scene.getViewTranslation(rect);
|
||||||
|
let zoom = this.scene.getViewScale();
|
||||||
|
|
||||||
|
// Draw the interior of the rect
|
||||||
|
if(rect.color.a !== 0){
|
||||||
|
this.ctx.fillStyle = rect.color.toStringRGB();
|
||||||
|
this.ctx.fillRect((rect.position.x - rect.size.x/2 - origin.x)*zoom, (rect.position.y - rect.size.y/2 - origin.y)*zoom, rect.size.x*zoom, rect.size.y*zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the border of the rect
|
||||||
|
this.ctx.strokeStyle = rect.getBorderColor().toStringRGB();
|
||||||
|
this.ctx.lineWidth = rect.getBorderWidth();
|
||||||
|
this.ctx.strokeRect((rect.position.x - rect.size.x/2 - origin.x)*zoom, (rect.position.y - rect.size.y/2 - origin.y)*zoom, rect.size.x*zoom, rect.size.y*zoom);
|
||||||
|
}
|
||||||
|
}
|
79
src/Rendering/CanvasRendering/TilemapRenderer.ts
Normal file
79
src/Rendering/CanvasRendering/TilemapRenderer.ts
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import ResourceManager from "../../ResourceManager/ResourceManager";
|
||||||
|
import Scene from "../../Scene/Scene";
|
||||||
|
import OrthogonalTilemap from "../../Nodes/Tilemaps/OrthogonalTilemap";
|
||||||
|
import Vec2 from "../../DataTypes/Vec2";
|
||||||
|
import Tileset from "../../DataTypes/Tilesets/Tileset";
|
||||||
|
|
||||||
|
export default class TilemapRenderer {
|
||||||
|
protected resourceManager: ResourceManager;
|
||||||
|
protected scene: Scene;
|
||||||
|
protected ctx: CanvasRenderingContext2D;
|
||||||
|
|
||||||
|
constructor(ctx: CanvasRenderingContext2D){
|
||||||
|
this.resourceManager = ResourceManager.getInstance();
|
||||||
|
this.ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
setScene(scene: Scene): void {
|
||||||
|
this.scene = scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderOrthogonalTilemap(tilemap: OrthogonalTilemap): void {
|
||||||
|
let previousAlpha = this.ctx.globalAlpha;
|
||||||
|
this.ctx.globalAlpha = tilemap.getLayer().getAlpha();
|
||||||
|
|
||||||
|
let origin = this.scene.getViewTranslation(tilemap);
|
||||||
|
let size = this.scene.getViewport().getHalfSize();
|
||||||
|
let zoom = this.scene.getViewScale();
|
||||||
|
let bottomRight = origin.clone().add(size.scaled(2*zoom));
|
||||||
|
|
||||||
|
if(tilemap.visible){
|
||||||
|
let minColRow = tilemap.getColRowAt(origin);
|
||||||
|
let maxColRow = tilemap.getColRowAt(bottomRight);
|
||||||
|
|
||||||
|
for(let x = minColRow.x; x <= maxColRow.x; x++){
|
||||||
|
for(let y = minColRow.y; y <= maxColRow.y; y++){
|
||||||
|
// Get the tile at this position
|
||||||
|
let tile = tilemap.getTileAtRowCol(new Vec2(x, y));
|
||||||
|
|
||||||
|
// Find the tileset that owns this tile index and render
|
||||||
|
for(let tileset of tilemap.getTilesets()){
|
||||||
|
if(tileset.hasTile(tile)){
|
||||||
|
this.renderTile(tileset, tile, x, y, origin, tilemap.scale, zoom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ctx.globalAlpha = previousAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderTile(tileset: Tileset, tileIndex: number, tilemapRow: number, tilemapCol: number, origin: Vec2, scale: Vec2, zoom: number): void {
|
||||||
|
let image = this.resourceManager.getImage(tileset.getImageKey());
|
||||||
|
|
||||||
|
// Get the true index
|
||||||
|
let index = tileIndex - tileset.getStartIndex();
|
||||||
|
|
||||||
|
// Get the row and col of the tile in image space
|
||||||
|
let row = Math.floor(index / tileset.getNumCols());
|
||||||
|
let col = index % tileset.getNumCols();
|
||||||
|
let width = tileset.getTileSize().x;
|
||||||
|
let height = tileset.getTileSize().y;
|
||||||
|
|
||||||
|
// Calculate the position to start a crop in the tileset image
|
||||||
|
let left = col * width;
|
||||||
|
let top = row * height;
|
||||||
|
|
||||||
|
// Calculate the position in the world to render the tile
|
||||||
|
let x = Math.floor(tilemapRow * width * scale.x);
|
||||||
|
let y = Math.floor(tilemapCol * height * scale.y);
|
||||||
|
|
||||||
|
// Render the tile
|
||||||
|
this.ctx.drawImage(image,
|
||||||
|
left, top,
|
||||||
|
width, height,
|
||||||
|
Math.floor((x - origin.x)*zoom), Math.floor((y - origin.y)*zoom),
|
||||||
|
Math.ceil(width * scale.x * zoom), Math.ceil(height * scale.y * zoom));
|
||||||
|
}
|
||||||
|
}
|
110
src/Rendering/CanvasRendering/UIElementRenderer.ts
Normal file
110
src/Rendering/CanvasRendering/UIElementRenderer.ts
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
import Vec2 from "../../DataTypes/Vec2";
|
||||||
|
import Button from "../../Nodes/UIElements/Button";
|
||||||
|
import Label from "../../Nodes/UIElements/Label";
|
||||||
|
import Slider from "../../Nodes/UIElements/Slider";
|
||||||
|
import TextInput from "../../Nodes/UIElements/TextInput";
|
||||||
|
import ResourceManager from "../../ResourceManager/ResourceManager";
|
||||||
|
import Scene from "../../Scene/Scene";
|
||||||
|
import MathUtils from "../../Utils/MathUtils";
|
||||||
|
|
||||||
|
export default class UIElementRenderer {
|
||||||
|
protected resourceManager: ResourceManager;
|
||||||
|
protected scene: Scene;
|
||||||
|
protected ctx: CanvasRenderingContext2D;
|
||||||
|
|
||||||
|
constructor(ctx: CanvasRenderingContext2D){
|
||||||
|
this.resourceManager = ResourceManager.getInstance();
|
||||||
|
this.ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
setScene(scene: Scene): void {
|
||||||
|
this.scene = scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLabel(label: Label): void {
|
||||||
|
// If the size is unassigned (by the user or automatically) assign it
|
||||||
|
label.handleInitialSizing(this.ctx);
|
||||||
|
|
||||||
|
// Grab the global alpha so we can adjust it for this render
|
||||||
|
let previousAlpha = this.ctx.globalAlpha;
|
||||||
|
|
||||||
|
// Get the origin of the viewport according to this label
|
||||||
|
let origin = this.scene.getViewTranslation(label);
|
||||||
|
|
||||||
|
// Get the font and text position in label
|
||||||
|
this.ctx.font = label.getFontString();
|
||||||
|
let offset = label.calculateTextOffset(this.ctx);
|
||||||
|
|
||||||
|
// Stroke and fill a rounded rect and give it text
|
||||||
|
this.ctx.globalAlpha = label.backgroundColor.a;
|
||||||
|
this.ctx.fillStyle = label.calculateBackgroundColor();
|
||||||
|
this.ctx.fillRoundedRect(label.position.x - origin.x - label.size.x/2, label.position.y - origin.y - label.size.y/2,
|
||||||
|
label.size.x, label.size.y, label.borderRadius);
|
||||||
|
|
||||||
|
this.ctx.strokeStyle = label.calculateBorderColor();
|
||||||
|
this.ctx.globalAlpha = label.borderColor.a;
|
||||||
|
this.ctx.lineWidth = label.borderWidth;
|
||||||
|
this.ctx.strokeRoundedRect(label.position.x - origin.x - label.size.x/2, label.position.y - origin.y - label.size.y/2,
|
||||||
|
label.size.x, label.size.y, label.borderRadius);
|
||||||
|
|
||||||
|
this.ctx.fillStyle = label.calculateTextColor();
|
||||||
|
this.ctx.globalAlpha = label.textColor.a;
|
||||||
|
this.ctx.fillText(label.text, label.position.x + offset.x - origin.x - label.size.x/2, label.position.y + offset.y - origin.y - label.size.y/2);
|
||||||
|
|
||||||
|
this.ctx.globalAlpha = previousAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderButton(button: Button): void {
|
||||||
|
this.renderLabel(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSlider(slider: Slider): void {
|
||||||
|
// Grab the global alpha so we can adjust it for this render
|
||||||
|
let previousAlpha = this.ctx.globalAlpha;
|
||||||
|
this.ctx.globalAlpha = slider.getLayer().getAlpha();
|
||||||
|
|
||||||
|
let origin = this.scene.getViewTranslation(slider);
|
||||||
|
|
||||||
|
// Calcualate the slider size
|
||||||
|
let sliderSize = new Vec2(slider.size.x, 2);
|
||||||
|
|
||||||
|
// Draw the slider
|
||||||
|
this.ctx.fillStyle = slider.sliderColor.toString();
|
||||||
|
this.ctx.fillRoundedRect(slider.position.x - origin.x - sliderSize.x/2, slider.position.y - origin.y - sliderSize.y/2,
|
||||||
|
sliderSize.x, sliderSize.y, slider.borderRadius);
|
||||||
|
|
||||||
|
// Calculate the nib size and position
|
||||||
|
let nibSize = new Vec2(10, slider.size.y);
|
||||||
|
let x = MathUtils.lerp(slider.position.x - slider.size.x/2, slider.position.x + slider.size.x/2, slider.getValue());
|
||||||
|
let nibPosition = new Vec2(x, slider.position.y);
|
||||||
|
|
||||||
|
// Draw the nib
|
||||||
|
this.ctx.fillStyle = slider.nibColor.toString();
|
||||||
|
this.ctx.fillRoundedRect(nibPosition.x - origin.x - nibSize.x/2, nibPosition.y - origin.y - nibSize.y/2,
|
||||||
|
nibSize.x, nibSize.y, slider.borderRadius);
|
||||||
|
|
||||||
|
// Reset the alpha
|
||||||
|
this.ctx.globalAlpha = previousAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTextInput(textInput: TextInput): void {
|
||||||
|
// Show a cursor sometimes
|
||||||
|
if(textInput.focused && textInput.cursorCounter % 60 > 30){
|
||||||
|
textInput.text += "|";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.renderLabel(textInput);
|
||||||
|
|
||||||
|
if(textInput.focused){
|
||||||
|
if(textInput.cursorCounter % 60 > 30){
|
||||||
|
textInput.text = textInput.text.substring(0, textInput.text.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
textInput.cursorCounter += 1;
|
||||||
|
if(textInput.cursorCounter >= 60){
|
||||||
|
textInput.cursorCounter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
39
src/Rendering/RenderingManager.ts
Normal file
39
src/Rendering/RenderingManager.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import Map from "../DataTypes/Map";
|
||||||
|
import CanvasNode from "../Nodes/CanvasNode";
|
||||||
|
import Graphic from "../Nodes/Graphic";
|
||||||
|
import Sprite from "../Nodes/Sprites/Sprite";
|
||||||
|
import Tilemap from "../Nodes/Tilemap";
|
||||||
|
import UIElement from "../Nodes/UIElement";
|
||||||
|
import ResourceManager from "../ResourceManager/ResourceManager";
|
||||||
|
import UILayer from "../Scene/Layers/UILayer";
|
||||||
|
import Scene from "../Scene/Scene";
|
||||||
|
|
||||||
|
export default abstract class RenderingManager {
|
||||||
|
// Give the renderer access to the resource manager
|
||||||
|
protected resourceManager: ResourceManager;
|
||||||
|
protected scene: Scene;
|
||||||
|
debug: boolean;
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
this.resourceManager = ResourceManager.getInstance();
|
||||||
|
this.debug = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setScene(scene: Scene): void {
|
||||||
|
this.scene = scene;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract initializeCanvas(canvas: HTMLCanvasElement, width: number, height: number): any;
|
||||||
|
|
||||||
|
abstract render(visibleSet: Array<CanvasNode>, tilemaps: Array<Tilemap>, uiLayers: Map<UILayer>): void;
|
||||||
|
|
||||||
|
protected abstract renderSprite(sprite: Sprite): void;
|
||||||
|
|
||||||
|
protected abstract renderAnimatedSprite(): void;
|
||||||
|
|
||||||
|
protected abstract renderGraphic(graphic: Graphic): void;
|
||||||
|
|
||||||
|
protected abstract renderTilemap(tilemap: Tilemap): void;
|
||||||
|
|
||||||
|
protected abstract renderUIElement(uiElement: UIElement): void;
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import UILayer from "./Layers/UILayer";
|
||||||
import CanvasNode from "../Nodes/CanvasNode";
|
import CanvasNode from "../Nodes/CanvasNode";
|
||||||
import GameNode from "../Nodes/GameNode";
|
import GameNode from "../Nodes/GameNode";
|
||||||
import ArrayUtils from "../Utils/ArrayUtils";
|
import ArrayUtils from "../Utils/ArrayUtils";
|
||||||
|
import RenderingManager from "../Rendering/RenderingManager";
|
||||||
|
|
||||||
export default class Scene implements Updateable, Renderable {
|
export default class Scene implements Updateable, Renderable {
|
||||||
/** The size of the game world. */
|
/** The size of the game world. */
|
||||||
|
@ -68,6 +69,9 @@ export default class Scene implements Updateable, Renderable {
|
||||||
/** The AI manager of the Scene */
|
/** The AI manager of the Scene */
|
||||||
protected aiManager: AIManager;
|
protected aiManager: AIManager;
|
||||||
|
|
||||||
|
/** The renderingManager of the scene */
|
||||||
|
protected renderingManager: RenderingManager;
|
||||||
|
|
||||||
/** An interface that allows the adding of different nodes to the scene */
|
/** An interface that allows the adding of different nodes to the scene */
|
||||||
public add: FactoryManager;
|
public add: FactoryManager;
|
||||||
|
|
||||||
|
@ -77,7 +81,7 @@ export default class Scene implements Updateable, Renderable {
|
||||||
/** The configuration options for this scene */
|
/** The configuration options for this scene */
|
||||||
public sceneOptions: SceneOptions;
|
public sceneOptions: SceneOptions;
|
||||||
|
|
||||||
constructor(viewport: Viewport, sceneManager: SceneManager, game: GameLoop, options: Record<string, any>){
|
constructor(viewport: Viewport, sceneManager: SceneManager, renderingManager: RenderingManager, game: GameLoop, options: Record<string, any>){
|
||||||
this.sceneOptions = SceneOptions.parse(options);
|
this.sceneOptions = SceneOptions.parse(options);
|
||||||
|
|
||||||
this.worldSize = new Vec2(500, 500);
|
this.worldSize = new Vec2(500, 500);
|
||||||
|
@ -99,6 +103,7 @@ export default class Scene implements Updateable, Renderable {
|
||||||
this.physicsManager = new BasicPhysicsManager(this.sceneOptions.physics);
|
this.physicsManager = new BasicPhysicsManager(this.sceneOptions.physics);
|
||||||
this.navManager = new NavigationManager();
|
this.navManager = new NavigationManager();
|
||||||
this.aiManager = new AIManager();
|
this.aiManager = new AIManager();
|
||||||
|
this.renderingManager = renderingManager;
|
||||||
|
|
||||||
this.add = new FactoryManager(this, this.tilemaps);
|
this.add = new FactoryManager(this, this.tilemaps);
|
||||||
|
|
||||||
|
@ -143,9 +148,8 @@ export default class Scene implements Updateable, Renderable {
|
||||||
this.viewport.update(deltaT);
|
this.viewport.update(deltaT);
|
||||||
}
|
}
|
||||||
|
|
||||||
render(ctx: CanvasRenderingContext2D): void {
|
render(): void {
|
||||||
// For webGL, pass a visible set to the renderer
|
// Get the visible set of nodes
|
||||||
// We need to keep track of the order of things.
|
|
||||||
let visibleSet = this.sceneGraph.getVisibleSet();
|
let visibleSet = this.sceneGraph.getVisibleSet();
|
||||||
|
|
||||||
// Add parallax layer items to the visible set (we're rendering them all for now)
|
// Add parallax layer items to the visible set (we're rendering them all for now)
|
||||||
|
@ -158,31 +162,8 @@ export default class Scene implements Updateable, Renderable {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sort by depth, then by visible set by y-value
|
// Send the visible set, tilemaps, and uiLayers to the renderer
|
||||||
visibleSet.sort((a, b) => {
|
this.renderingManager.render(visibleSet, this.tilemaps, this.uiLayers);
|
||||||
if(a.getLayer().getDepth() === b.getLayer().getDepth()){
|
|
||||||
return (a.boundary.bottom) - (b.boundary.bottom);
|
|
||||||
} else {
|
|
||||||
return a.getLayer().getDepth() - b.getLayer().getDepth();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Render scene graph for demo
|
|
||||||
this.sceneGraph.render(ctx);
|
|
||||||
|
|
||||||
// Render tilemaps
|
|
||||||
this.tilemaps.forEach(tilemap => {
|
|
||||||
tilemap.render(ctx);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Render visible set
|
|
||||||
visibleSet.forEach(node => node.visible ? node.render(ctx) : "");
|
|
||||||
|
|
||||||
// Debug render the physicsManager
|
|
||||||
this.physicsManager.debug_render(ctx);
|
|
||||||
|
|
||||||
// Render the uiLayers
|
|
||||||
this.uiLayers.forEach(key => this.uiLayers.get(key).getItems().forEach(node => (<CanvasNode>node).render(ctx)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setRunning(running: boolean): void {
|
setRunning(running: boolean): void {
|
||||||
|
|
|
@ -2,19 +2,21 @@ import Scene from "./Scene";
|
||||||
import ResourceManager from "../ResourceManager/ResourceManager";
|
import ResourceManager from "../ResourceManager/ResourceManager";
|
||||||
import Viewport from "../SceneGraph/Viewport";
|
import Viewport from "../SceneGraph/Viewport";
|
||||||
import GameLoop from "../Loop/GameLoop";
|
import GameLoop from "../Loop/GameLoop";
|
||||||
|
import RenderingManager from "../Rendering/RenderingManager";
|
||||||
|
|
||||||
export default class SceneManager {
|
export default class SceneManager {
|
||||||
|
protected currentScene: Scene;
|
||||||
|
protected viewport: Viewport;
|
||||||
|
protected resourceManager: ResourceManager;
|
||||||
|
protected game: GameLoop;
|
||||||
|
protected idCounter: number;
|
||||||
|
protected renderingManager: RenderingManager;
|
||||||
|
|
||||||
private currentScene: Scene;
|
constructor(viewport: Viewport, game: GameLoop, renderingManager: RenderingManager){
|
||||||
private viewport: Viewport;
|
|
||||||
private resourceManager: ResourceManager;
|
|
||||||
private game: GameLoop;
|
|
||||||
private idCounter: number;
|
|
||||||
|
|
||||||
constructor(viewport: Viewport, game: GameLoop){
|
|
||||||
this.resourceManager = ResourceManager.getInstance();
|
this.resourceManager = ResourceManager.getInstance();
|
||||||
this.viewport = viewport;
|
this.viewport = viewport;
|
||||||
this.game = game;
|
this.game = game;
|
||||||
|
this.renderingManager = renderingManager;
|
||||||
this.idCounter = 0;
|
this.idCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +25,7 @@ export default class SceneManager {
|
||||||
* @param constr The constructor of the scene to add
|
* @param constr The constructor of the scene to add
|
||||||
*/
|
*/
|
||||||
public addScene<T extends Scene>(constr: new (...args: any) => T, options: Record<string, any>): void {
|
public addScene<T extends Scene>(constr: new (...args: any) => T, options: Record<string, any>): void {
|
||||||
let scene = new constr(this.viewport, this, this.game, options);
|
let scene = new constr(this.viewport, this, this.renderingManager, this.game, options);
|
||||||
this.currentScene = scene;
|
this.currentScene = scene;
|
||||||
|
|
||||||
// Enqueue all scene asset loads
|
// Enqueue all scene asset loads
|
||||||
|
@ -36,6 +38,8 @@ export default class SceneManager {
|
||||||
scene.startScene();
|
scene.startScene();
|
||||||
scene.setRunning(true);
|
scene.setRunning(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.renderingManager.setScene(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -57,8 +61,8 @@ export default class SceneManager {
|
||||||
return this.idCounter++;
|
return this.idCounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public render(ctx: CanvasRenderingContext2D){
|
public render(){
|
||||||
this.currentScene.render(ctx);
|
this.currentScene.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
public update(deltaT: number){
|
public update(deltaT: number){
|
||||||
|
|
|
@ -53,6 +53,13 @@ export default class Color {
|
||||||
return new Color(255, 100, 0, 1);
|
return new Color(255, 100, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set(r: number, g: number, b: number, a: number = 1): void {
|
||||||
|
this.r = r;
|
||||||
|
this.g = g;
|
||||||
|
this.b = b;
|
||||||
|
this.a = a;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new color slightly lighter than the current color
|
* Returns a new color slightly lighter than the current color
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue
Block a user