mirror of
https://github.com/tobspr/shapez.io.git
synced 2026-03-02 03:39:21 +00:00
Start to optimize rendering
This commit is contained in:
@@ -22,9 +22,5 @@ export class DrawParameters {
|
||||
// FIXME: Not really nice
|
||||
/** @type {GameRoot} */
|
||||
this.root = root;
|
||||
|
||||
if (G_IS_DEV && globalConfig.debug.testClipping) {
|
||||
this.visibleRect = this.visibleRect.expandedInAllDirections(-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
* @typedef {import("./draw_parameters").DrawParameters} DrawParameters
|
||||
*/
|
||||
|
||||
import { globalConfig } from "./config";
|
||||
import { createLogger } from "./logging";
|
||||
import { Rectangle } from "./rectangle";
|
||||
|
||||
const logger = createLogger("draw_utils");
|
||||
|
||||
export function initDrawUtils() {
|
||||
CanvasRenderingContext2D.prototype.beginRoundedRect = function (x, y, w, h, r) {
|
||||
this.beginPath();
|
||||
@@ -52,9 +58,64 @@ export function initDrawUtils() {
|
||||
* @param {number=} param0.offsetY
|
||||
*/
|
||||
export function drawRotatedSprite({ parameters, sprite, x, y, angle, size, offsetX = 0, offsetY = 0 }) {
|
||||
if (angle === 0) {
|
||||
sprite.drawCachedCentered(parameters, x + offsetX, y + offsetY, size);
|
||||
return;
|
||||
}
|
||||
|
||||
parameters.context.translate(x, y);
|
||||
parameters.context.rotate(angle);
|
||||
sprite.drawCachedCentered(parameters, offsetX, offsetY, size, false);
|
||||
parameters.context.rotate(-angle);
|
||||
parameters.context.translate(-x, -y);
|
||||
}
|
||||
|
||||
let warningsShown = 0;
|
||||
|
||||
/**
|
||||
* Draws a sprite with clipping
|
||||
* @param {object} param0
|
||||
* @param {DrawParameters} param0.parameters
|
||||
* @param {HTMLCanvasElement} param0.sprite
|
||||
* @param {number} param0.x
|
||||
* @param {number} param0.y
|
||||
* @param {number} param0.w
|
||||
* @param {number} param0.h
|
||||
* @param {number} param0.originalW
|
||||
* @param {number} param0.originalH
|
||||
*/
|
||||
export function drawSpriteClipped({ parameters, sprite, x, y, w, h, originalW, originalH }) {
|
||||
const rect = new Rectangle(x, y, w, h);
|
||||
const intersection = rect.getIntersection(parameters.visibleRect);
|
||||
if (!intersection) {
|
||||
// Clipped
|
||||
if (++warningsShown % 200 === 1) {
|
||||
logger.warn(
|
||||
"Sprite drawn clipped but it's not on screen - perform culling before (",
|
||||
warningsShown,
|
||||
"warnings)"
|
||||
);
|
||||
}
|
||||
if (G_IS_DEV && globalConfig.debug.testClipping) {
|
||||
parameters.context.fillStyle = "yellow";
|
||||
parameters.context.fillRect(x, y, w, h);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
parameters.context.drawImage(
|
||||
sprite,
|
||||
|
||||
// src pos and size
|
||||
((intersection.x - x) / w) * originalW,
|
||||
((intersection.y - y) / h) * originalH,
|
||||
(originalW * intersection.w) / w,
|
||||
(originalH * intersection.h) / h,
|
||||
|
||||
// dest pos and size
|
||||
intersection.x,
|
||||
intersection.y,
|
||||
intersection.w,
|
||||
intersection.h
|
||||
);
|
||||
}
|
||||
|
||||
@@ -395,22 +395,14 @@ export class Rectangle {
|
||||
|
||||
/**
|
||||
* Returns a new recangle in tile space which includes all tiles which are visible in this rect
|
||||
* @param {boolean=} includeHalfTiles
|
||||
* @returns {Rectangle}
|
||||
*/
|
||||
toTileCullRectangle(includeHalfTiles = true) {
|
||||
let scaled = this.allScaled(1.0 / globalConfig.tileSize);
|
||||
|
||||
if (includeHalfTiles) {
|
||||
// Increase rectangle size
|
||||
scaled = Rectangle.fromTRBL(
|
||||
Math.floor(scaled.y),
|
||||
Math.ceil(scaled.right()),
|
||||
Math.ceil(scaled.bottom()),
|
||||
Math.floor(scaled.x)
|
||||
);
|
||||
}
|
||||
|
||||
return scaled;
|
||||
toTileCullRectangle() {
|
||||
return new Rectangle(
|
||||
Math.floor(this.x / globalConfig.tileSize),
|
||||
Math.floor(this.y / globalConfig.tileSize),
|
||||
Math.ceil(this.w / globalConfig.tileSize),
|
||||
Math.ceil(this.h / globalConfig.tileSize)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,9 +59,9 @@ export class BaseItem extends BasicSerializableObject {
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {DrawParameters} parameters
|
||||
* @param {number=} size
|
||||
* @param {number=} diameter
|
||||
*/
|
||||
draw(x, y, parameters, size) {}
|
||||
drawCentered(x, y, parameters, diameter) {}
|
||||
|
||||
getBackgroundColorAsResource() {
|
||||
abstract;
|
||||
|
||||
@@ -1195,7 +1195,7 @@ export class BeltPath extends BasicSerializableObject {
|
||||
|
||||
const distanceAndItem = this.items[currentItemIndex];
|
||||
if (parameters.visibleRect.containsCircle(worldPos.x, worldPos.y, 10)) {
|
||||
distanceAndItem[_item].draw(worldPos.x, worldPos.y, parameters);
|
||||
distanceAndItem[_item].drawCentered(worldPos.x, worldPos.y, parameters);
|
||||
}
|
||||
|
||||
// Check for the next item
|
||||
|
||||
@@ -255,8 +255,7 @@ export class StaticMapEntityComponent extends Component {
|
||||
worldX - extrudePixels * size.x,
|
||||
worldY - extrudePixels * size.y,
|
||||
globalConfig.tileSize * size.x + 2 * extrudePixels * size.x,
|
||||
globalConfig.tileSize * size.y + 2 * extrudePixels * size.y,
|
||||
false
|
||||
globalConfig.tileSize * size.y + 2 * extrudePixels * size.y
|
||||
);
|
||||
} else {
|
||||
const rotationCenterX = worldX + globalConfig.halfTileSize;
|
||||
@@ -264,16 +263,14 @@ export class StaticMapEntityComponent extends Component {
|
||||
|
||||
parameters.context.translate(rotationCenterX, rotationCenterY);
|
||||
parameters.context.rotate(Math.radians(this.rotation));
|
||||
|
||||
sprite.drawCached(
|
||||
parameters,
|
||||
-globalConfig.halfTileSize - extrudePixels * size.x,
|
||||
-globalConfig.halfTileSize - extrudePixels * size.y,
|
||||
globalConfig.tileSize * size.x + 2 * extrudePixels * size.x,
|
||||
globalConfig.tileSize * size.y + 2 * extrudePixels * size.y,
|
||||
false
|
||||
false // no clipping possible here
|
||||
);
|
||||
|
||||
parameters.context.rotate(-Math.radians(this.rotation));
|
||||
parameters.context.translate(-rotationCenterX, -rotationCenterY);
|
||||
}
|
||||
|
||||
@@ -369,6 +369,13 @@ export class GameCore {
|
||||
}
|
||||
|
||||
// Transform to world space
|
||||
|
||||
if (G_IS_DEV && globalConfig.debug.testClipping) {
|
||||
params.visibleRect = params.visibleRect.expandedInAllDirections(
|
||||
-200 / this.root.camera.zoomLevel
|
||||
);
|
||||
}
|
||||
|
||||
root.camera.transform(context);
|
||||
|
||||
assert(context.globalAlpha === 1.0, "Global alpha not 1 on frame start");
|
||||
@@ -385,9 +392,6 @@ export class GameCore {
|
||||
} else {
|
||||
root.map.drawBackground(params);
|
||||
|
||||
// Underlays for splitters / balancers
|
||||
systems.beltUnderlays.drawUnderlays(params);
|
||||
|
||||
// Belt items
|
||||
systems.belt.drawBeltItems(params);
|
||||
|
||||
@@ -439,6 +443,9 @@ export class GameCore {
|
||||
params.zoomLevel = 1;
|
||||
params.desiredAtlasScale = ORIGINAL_SPRITE_SCALE;
|
||||
params.visibleRect = new Rectangle(0, 0, this.root.gameWidth, this.root.gameHeight);
|
||||
if (G_IS_DEV && globalConfig.debug.testClipping) {
|
||||
params.visibleRect = params.visibleRect.expandedInAllDirections(-200);
|
||||
}
|
||||
|
||||
// Draw overlays, those are screen space
|
||||
root.hud.drawOverlays(params);
|
||||
@@ -469,5 +476,13 @@ export class GameCore {
|
||||
20
|
||||
);
|
||||
}
|
||||
|
||||
if (G_IS_DEV && globalConfig.debug.testClipping) {
|
||||
context.strokeStyle = "red";
|
||||
context.lineWidth = 1;
|
||||
context.beginPath();
|
||||
context.rect(200, 200, this.root.gameWidth - 400, this.root.gameHeight - 400);
|
||||
context.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ export class HUDWireInfo extends BaseHUDPart {
|
||||
} else if (!network.currentValue) {
|
||||
this.spriteEmpty.draw(parameters.context, mousePos.x + 10, mousePos.y - 10, 40, 40);
|
||||
} else {
|
||||
network.currentValue.draw(mousePos.x + 20, mousePos.y, parameters, 40);
|
||||
network.currentValue.drawCentered(mousePos.x + 20, mousePos.y, parameters, 40);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,17 +42,17 @@ export class BooleanItem extends BaseItem {
|
||||
/**
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {number} size
|
||||
* @param {number} diameter
|
||||
* @param {DrawParameters} parameters
|
||||
*/
|
||||
draw(x, y, parameters, size = 12) {
|
||||
drawCentered(x, y, parameters, diameter = 12) {
|
||||
let sprite;
|
||||
if (this.value) {
|
||||
sprite = Loader.getSprite("sprites/wires/boolean_true.png");
|
||||
} else {
|
||||
sprite = Loader.getSprite("sprites/wires/boolean_false.png");
|
||||
}
|
||||
sprite.drawCachedCentered(parameters, x, y, size * 1.5);
|
||||
sprite.drawCachedCentered(parameters, x, y, diameter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,26 +50,26 @@ export class ColorItem extends BaseItem {
|
||||
/**
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {number} size
|
||||
* @param {number} diameter
|
||||
* @param {DrawParameters} parameters
|
||||
*/
|
||||
draw(x, y, parameters, size = 12) {
|
||||
drawCentered(x, y, parameters, diameter = 12) {
|
||||
if (!this.bufferGenerator) {
|
||||
this.bufferGenerator = this.internalGenerateColorBuffer.bind(this);
|
||||
}
|
||||
|
||||
const dpi = smoothenDpi(globalConfig.shapesSharpness * parameters.zoomLevel);
|
||||
|
||||
const key = size + "/" + dpi;
|
||||
const key = diameter + "/" + dpi;
|
||||
const canvas = parameters.root.buffers.getForKey({
|
||||
key,
|
||||
subKey: this.color,
|
||||
w: size,
|
||||
h: size,
|
||||
w: diameter,
|
||||
h: diameter,
|
||||
dpi,
|
||||
redrawMethod: this.bufferGenerator,
|
||||
});
|
||||
parameters.context.drawImage(canvas, x - size / 2, y - size / 2, size, size);
|
||||
parameters.context.drawImage(canvas, x - diameter / 2, y - diameter / 2, diameter, diameter);
|
||||
}
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -52,9 +52,9 @@ export class ShapeItem extends BaseItem {
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {DrawParameters} parameters
|
||||
* @param {number=} size
|
||||
* @param {number=} diameter
|
||||
*/
|
||||
draw(x, y, parameters, size) {
|
||||
this.definition.draw(x, y, parameters, size);
|
||||
drawCentered(x, y, parameters, diameter) {
|
||||
this.definition.drawCentered(x, y, parameters, diameter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { GameRoot, enumLayer } from "./root";
|
||||
import { globalConfig } from "../core/config";
|
||||
import { Vector } from "../core/vector";
|
||||
import { Entity } from "./entity";
|
||||
import { createLogger } from "../core/logging";
|
||||
import { BaseItem } from "./base_item";
|
||||
import { MapChunkView } from "./map_chunk_view";
|
||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||
|
||||
const logger = createLogger("map");
|
||||
import { BaseItem } from "./base_item";
|
||||
import { Entity } from "./entity";
|
||||
import { MapChunkView } from "./map_chunk_view";
|
||||
import { enumLayer, GameRoot } from "./root";
|
||||
|
||||
export class BaseMap extends BasicSerializableObject {
|
||||
static getId() {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { Entity } from "./entity";
|
||||
import { COLOR_ITEM_SINGLETONS } from "./items/color_item";
|
||||
import { enumLayer, GameRoot } from "./root";
|
||||
import { enumSubShape } from "./shape_definition";
|
||||
import { Rectangle } from "../core/rectangle";
|
||||
|
||||
const logger = createLogger("map_chunk");
|
||||
|
||||
@@ -26,18 +27,47 @@ export class MapChunk {
|
||||
this.tileX = x * globalConfig.mapChunkSize;
|
||||
this.tileY = y * globalConfig.mapChunkSize;
|
||||
|
||||
/** @type {Array<Array<?Entity>>} */
|
||||
/**
|
||||
* Stores the contents of the lower (= map resources) layer
|
||||
* @type {Array<Array<?BaseItem>>}
|
||||
*/
|
||||
this.lowerLayer = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize);
|
||||
|
||||
/**
|
||||
* Stores the contents of the regular layer
|
||||
* @type {Array<Array<?Entity>>}
|
||||
*/
|
||||
this.contents = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize);
|
||||
|
||||
/** @type {Array<Array<?Entity>>} */
|
||||
/**
|
||||
* Stores the contents of the wires layer
|
||||
* @type {Array<Array<?Entity>>}
|
||||
*/
|
||||
this.wireContents = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize);
|
||||
|
||||
/** @type {Array<Array<?BaseItem>>} */
|
||||
this.lowerLayer = make2DUndefinedArray(globalConfig.mapChunkSize, globalConfig.mapChunkSize);
|
||||
|
||||
/** @type {Array<Entity>} */
|
||||
this.containedEntities = [];
|
||||
|
||||
/**
|
||||
* World space rectangle, can be used for culling
|
||||
*/
|
||||
this.worldSpaceRectangle = new Rectangle(
|
||||
this.tileX * globalConfig.tileSize,
|
||||
this.tileY * globalConfig.tileSize,
|
||||
globalConfig.mapChunkWorldSize,
|
||||
globalConfig.mapChunkWorldSize
|
||||
);
|
||||
|
||||
/**
|
||||
* Tile space rectangle, can be used for culling
|
||||
*/
|
||||
this.tileSpaceRectangle = new Rectangle(
|
||||
this.tileX,
|
||||
this.tileY,
|
||||
globalConfig.mapChunkSize,
|
||||
globalConfig.mapChunkSize
|
||||
);
|
||||
|
||||
/**
|
||||
* Which entities this chunk contains, sorted by layer
|
||||
* @type {Object<string, Array<Entity>>}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Entity } from "./entity";
|
||||
import { MapChunk } from "./map_chunk";
|
||||
import { enumLayer, GameRoot } from "./root";
|
||||
import { THEME } from "./theme";
|
||||
import { drawSpriteClipped } from "../core/draw_utils";
|
||||
|
||||
export const CHUNK_OVERLAY_RES = 3;
|
||||
|
||||
@@ -41,6 +42,7 @@ export class MapChunkView extends MapChunk {
|
||||
drawBackgroundLayer(parameters) {
|
||||
const systems = this.root.systemMgr.systems;
|
||||
systems.mapResources.drawChunk(parameters, this);
|
||||
systems.beltUnderlays.drawChunk(parameters, this);
|
||||
systems.belt.drawChunk(parameters, this);
|
||||
}
|
||||
|
||||
@@ -61,11 +63,12 @@ export class MapChunkView extends MapChunk {
|
||||
* @param {DrawParameters} parameters
|
||||
*/
|
||||
drawOverlay(parameters) {
|
||||
const overlaySize = globalConfig.mapChunkSize * CHUNK_OVERLAY_RES;
|
||||
const sprite = this.root.buffers.getForKey({
|
||||
key: "chunk@" + this.root.currentLayer,
|
||||
subKey: this.renderKey,
|
||||
w: globalConfig.mapChunkSize * CHUNK_OVERLAY_RES,
|
||||
h: globalConfig.mapChunkSize * CHUNK_OVERLAY_RES,
|
||||
w: overlaySize,
|
||||
h: overlaySize,
|
||||
dpi: 1,
|
||||
redrawMethod: this.generateOverlayBuffer.bind(this),
|
||||
});
|
||||
@@ -74,7 +77,17 @@ export class MapChunkView extends MapChunk {
|
||||
|
||||
// Draw chunk "pixel" art
|
||||
parameters.context.imageSmoothingEnabled = false;
|
||||
parameters.context.drawImage(sprite, this.x * dims, this.y * dims, dims, dims);
|
||||
drawSpriteClipped({
|
||||
parameters,
|
||||
sprite,
|
||||
x: this.x * dims,
|
||||
y: this.y * dims,
|
||||
w: dims,
|
||||
h: dims,
|
||||
originalW: overlaySize,
|
||||
originalH: overlaySize,
|
||||
});
|
||||
|
||||
parameters.context.imageSmoothingEnabled = true;
|
||||
|
||||
// Draw patch items
|
||||
@@ -82,12 +95,13 @@ export class MapChunkView extends MapChunk {
|
||||
for (let i = 0; i < this.patches.length; ++i) {
|
||||
const patch = this.patches[i];
|
||||
|
||||
patch.item.draw(
|
||||
this.x * dims + patch.pos.x * globalConfig.tileSize,
|
||||
this.y * dims + patch.pos.y * globalConfig.tileSize,
|
||||
parameters,
|
||||
Math.min(80, 30 / parameters.zoomLevel)
|
||||
);
|
||||
const destX = this.x * dims + patch.pos.x * globalConfig.tileSize;
|
||||
const destY = this.y * dims + patch.pos.y * globalConfig.tileSize;
|
||||
const destSize = Math.min(80, 30 / parameters.zoomLevel);
|
||||
|
||||
if (parameters.visibleRect.containsCircle(destX, destY, destSize)) {
|
||||
patch.item.drawCentered(destX, destY, parameters, destSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,23 +140,23 @@ export class MapView extends BaseMap {
|
||||
* @param {function} method
|
||||
*/
|
||||
drawVisibleChunks(parameters, method) {
|
||||
const cullRange = parameters.visibleRect.toTileCullRectangle();
|
||||
const cullRange = parameters.visibleRect.allScaled(1 / globalConfig.tileSize);
|
||||
const top = cullRange.top();
|
||||
const right = cullRange.right();
|
||||
const bottom = cullRange.bottom();
|
||||
const left = cullRange.left();
|
||||
|
||||
const border = 1;
|
||||
const border = 0;
|
||||
const minY = top - border;
|
||||
const maxY = bottom + border;
|
||||
const minX = left - border;
|
||||
const maxX = right + border - 1;
|
||||
const maxX = right + border;
|
||||
|
||||
const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize);
|
||||
const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize);
|
||||
|
||||
const chunkEndX = Math.ceil(maxX / globalConfig.mapChunkSize);
|
||||
const chunkEndY = Math.ceil(maxY / globalConfig.mapChunkSize);
|
||||
const chunkEndX = Math.floor(maxX / globalConfig.mapChunkSize);
|
||||
const chunkEndY = Math.floor(maxY / globalConfig.mapChunkSize);
|
||||
|
||||
// Render y from top down for proper blending
|
||||
for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) {
|
||||
@@ -230,7 +230,6 @@ export class MapView extends BaseMap {
|
||||
const chunkEndX = Math.ceil(maxX / globalConfig.mapChunkSize);
|
||||
const chunkEndY = Math.ceil(maxY / globalConfig.mapChunkSize);
|
||||
|
||||
// Render y from top down for proper blending
|
||||
for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) {
|
||||
for (let chunkY = chunkStartY; chunkY <= chunkEndY; ++chunkY) {
|
||||
parameters.context.fillStyle = "#ffaaaa";
|
||||
|
||||
@@ -280,24 +280,25 @@ export class ShapeDefinition extends BasicSerializableObject {
|
||||
* @param {number} x
|
||||
* @param {number} y
|
||||
* @param {DrawParameters} parameters
|
||||
* @param {number=} diameter
|
||||
*/
|
||||
draw(x, y, parameters, size = 20) {
|
||||
drawCentered(x, y, parameters, diameter = 20) {
|
||||
const dpi = smoothenDpi(globalConfig.shapesSharpness * parameters.zoomLevel);
|
||||
|
||||
if (!this.bufferGenerator) {
|
||||
this.bufferGenerator = this.internalGenerateShapeBuffer.bind(this);
|
||||
}
|
||||
|
||||
const key = size + "/" + dpi;
|
||||
const key = diameter + "/" + dpi;
|
||||
const canvas = parameters.root.buffers.getForKey({
|
||||
key,
|
||||
subKey: this.cachedHash,
|
||||
w: size,
|
||||
h: size,
|
||||
w: diameter,
|
||||
h: diameter,
|
||||
dpi,
|
||||
redrawMethod: this.bufferGenerator,
|
||||
});
|
||||
parameters.context.drawImage(canvas, x - size / 2, y - size / 2, size, size);
|
||||
parameters.context.drawImage(canvas, x - diameter / 2, y - diameter / 2, diameter, diameter);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,7 @@ import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||
import { MapChunkView } from "../map_chunk_view";
|
||||
import { defaultBuildingVariant } from "../meta_building";
|
||||
import { getCodeFromBuildingData } from "../building_codes";
|
||||
import { enumLayer } from "../root";
|
||||
|
||||
export const BELT_ANIM_COUNT = 14;
|
||||
|
||||
@@ -495,17 +496,13 @@ export class BeltSystem extends GameSystemWithFilter {
|
||||
((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) *
|
||||
globalConfig.itemSpacingOnBelts
|
||||
);
|
||||
const contents = chunk.contents;
|
||||
for (let y = 0; y < globalConfig.mapChunkSize; ++y) {
|
||||
for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
|
||||
const entity = contents[x][y];
|
||||
|
||||
if (entity && entity.components.Belt) {
|
||||
const direction = entity.components.Belt.direction;
|
||||
const sprite = this.beltAnimations[direction][animationIndex % BELT_ANIM_COUNT];
|
||||
|
||||
entity.components.StaticMapEntity.drawSpriteOnFullEntityBounds(parameters, sprite, 0);
|
||||
}
|
||||
const contents = chunk.containedEntitiesByLayer[enumLayer.regular];
|
||||
for (let i = 0; i < contents.length; ++i) {
|
||||
const entity = contents[i];
|
||||
if (entity.components.Belt) {
|
||||
const direction = entity.components.Belt.direction;
|
||||
const sprite = this.beltAnimations[direction][animationIndex % BELT_ANIM_COUNT];
|
||||
entity.components.StaticMapEntity.drawSpriteOnFullEntityBounds(parameters, sprite, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ import { drawRotatedSprite } from "../../core/draw_utils";
|
||||
import { Loader } from "../../core/loader";
|
||||
import { enumDirectionToAngle } from "../../core/vector";
|
||||
import { BeltUnderlaysComponent } from "../components/belt_underlays";
|
||||
import { Entity } from "../entity";
|
||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||
import { BELT_ANIM_COUNT } from "./belt";
|
||||
import { MapChunkView } from "../map_chunk_view";
|
||||
import { DrawParameters } from "../../core/draw_parameters";
|
||||
import { enumLayer } from "../root";
|
||||
|
||||
export class BeltUnderlaysSystem extends GameSystemWithFilter {
|
||||
constructor(root) {
|
||||
@@ -19,49 +21,47 @@ export class BeltUnderlaysSystem extends GameSystemWithFilter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the acceptor underlays
|
||||
* @param {import("../../core/draw_utils").DrawParameters} parameters
|
||||
* Draws a given chunk
|
||||
* @param {DrawParameters} parameters
|
||||
* @param {MapChunkView} chunk
|
||||
*/
|
||||
drawUnderlays(parameters) {
|
||||
this.forEachMatchingEntityOnScreen(parameters, this.drawEntityUnderlays.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import("../../core/draw_utils").DrawParameters} parameters
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
drawEntityUnderlays(parameters, entity) {
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
const underlayComp = entity.components.BeltUnderlays;
|
||||
|
||||
if (!staticComp.shouldBeDrawn(parameters)) {
|
||||
return;
|
||||
}
|
||||
|
||||
drawChunk(parameters, chunk) {
|
||||
// Limit speed to avoid belts going backwards
|
||||
const speedMultiplier = Math.min(this.root.hubGoals.getBeltBaseSpeed(), 10);
|
||||
|
||||
const underlays = underlayComp.underlays;
|
||||
for (let i = 0; i < underlays.length; ++i) {
|
||||
const { pos, direction } = underlays[i];
|
||||
const contents = chunk.containedEntitiesByLayer[enumLayer.regular];
|
||||
for (let i = 0; i < contents.length; ++i) {
|
||||
const entity = contents[i];
|
||||
const underlayComp = entity.components.BeltUnderlays;
|
||||
if (underlayComp) {
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
const underlays = underlayComp.underlays;
|
||||
for (let i = 0; i < underlays.length; ++i) {
|
||||
const { pos, direction } = underlays[i];
|
||||
const transformedPos = staticComp.localTileToWorld(pos);
|
||||
|
||||
const transformedPos = staticComp.localTileToWorld(pos);
|
||||
const angle = enumDirectionToAngle[staticComp.localDirectionToWorld(direction)];
|
||||
if (!chunk.tileSpaceRectangle.containsPoint(transformedPos.x, transformedPos.y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// SYNC with systems/belt.js:drawSingleEntity!
|
||||
const animationIndex = Math.floor(
|
||||
((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) *
|
||||
globalConfig.itemSpacingOnBelts
|
||||
);
|
||||
const angle = enumDirectionToAngle[staticComp.localDirectionToWorld(direction)];
|
||||
|
||||
drawRotatedSprite({
|
||||
parameters,
|
||||
sprite: this.underlayBeltSprites[animationIndex % this.underlayBeltSprites.length],
|
||||
x: (transformedPos.x + 0.5) * globalConfig.tileSize,
|
||||
y: (transformedPos.y + 0.5) * globalConfig.tileSize,
|
||||
angle: Math.radians(angle),
|
||||
size: globalConfig.tileSize,
|
||||
});
|
||||
// SYNC with systems/belt.js:drawSingleEntity!
|
||||
const animationIndex = Math.floor(
|
||||
((this.root.time.realtimeNow() * speedMultiplier * BELT_ANIM_COUNT * 126) / 42) *
|
||||
globalConfig.itemSpacingOnBelts
|
||||
);
|
||||
|
||||
drawRotatedSprite({
|
||||
parameters,
|
||||
sprite: this.underlayBeltSprites[animationIndex % this.underlayBeltSprites.length],
|
||||
x: (transformedPos.x + 0.5) * globalConfig.tileSize,
|
||||
y: (transformedPos.y + 0.5) * globalConfig.tileSize,
|
||||
angle: Math.radians(angle),
|
||||
size: globalConfig.tileSize,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,8 +85,7 @@ export class DisplaySystem extends GameSystemWithFilter {
|
||||
globalConfig.tileSize
|
||||
);
|
||||
} else if (value.getItemType() === enumItemType.shape) {
|
||||
// @todo
|
||||
value.draw(
|
||||
value.drawCentered(
|
||||
(origin.x + 0.5) * globalConfig.tileSize,
|
||||
(origin.y + 0.5) * globalConfig.tileSize,
|
||||
parameters,
|
||||
|
||||
@@ -47,7 +47,7 @@ export class HubSystem extends GameSystemWithFilter {
|
||||
|
||||
const definition = this.root.hubGoals.currentGoal.definition;
|
||||
|
||||
definition.draw(pos.x - 25, pos.y - 10, parameters, 40);
|
||||
definition.drawCentered(pos.x - 25, pos.y - 10, parameters, 40);
|
||||
|
||||
const goals = this.root.hubGoals.currentGoal;
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ export class ItemAcceptorSystem extends GameSystemWithFilter {
|
||||
fadeOutDirection.x * (animProgress / 2 - 0.5),
|
||||
fadeOutDirection.y * (animProgress / 2 - 0.5)
|
||||
);
|
||||
item.draw(
|
||||
item.drawCentered(
|
||||
(finalTile.x + 0.5) * globalConfig.tileSize,
|
||||
(finalTile.y + 0.5) * globalConfig.tileSize,
|
||||
parameters
|
||||
|
||||
@@ -379,7 +379,7 @@ export class ItemEjectorSystem extends GameSystemWithFilter {
|
||||
const worldX = tileX * globalConfig.tileSize;
|
||||
const worldY = tileY * globalConfig.tileSize;
|
||||
|
||||
ejectedItem.draw(worldX, worldY, parameters);
|
||||
ejectedItem.drawCentered(worldX, worldY, parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { DrawParameters } from "../../core/draw_parameters";
|
||||
import { GameSystem } from "../game_system";
|
||||
import { MapChunkView } from "../map_chunk_view";
|
||||
import { THEME } from "../theme";
|
||||
import { drawSpriteClipped } from "../../core/draw_utils";
|
||||
|
||||
export class MapResourcesSystem extends GameSystem {
|
||||
/**
|
||||
@@ -21,13 +22,16 @@ export class MapResourcesSystem extends GameSystem {
|
||||
});
|
||||
|
||||
parameters.context.imageSmoothingEnabled = false;
|
||||
parameters.context.drawImage(
|
||||
basicChunkBackground,
|
||||
chunk.tileX * globalConfig.tileSize,
|
||||
chunk.tileY * globalConfig.tileSize,
|
||||
globalConfig.mapChunkWorldSize,
|
||||
globalConfig.mapChunkWorldSize
|
||||
);
|
||||
drawSpriteClipped({
|
||||
parameters,
|
||||
sprite: basicChunkBackground,
|
||||
x: chunk.tileX * globalConfig.tileSize,
|
||||
y: chunk.tileY * globalConfig.tileSize,
|
||||
w: globalConfig.mapChunkWorldSize,
|
||||
h: globalConfig.mapChunkWorldSize,
|
||||
originalW: globalConfig.mapChunkSize,
|
||||
originalH: globalConfig.mapChunkSize,
|
||||
});
|
||||
parameters.context.imageSmoothingEnabled = true;
|
||||
|
||||
parameters.context.globalAlpha = 0.5;
|
||||
@@ -36,13 +40,12 @@ export class MapResourcesSystem extends GameSystem {
|
||||
// LOW QUALITY: Draw patch items only
|
||||
for (let i = 0; i < chunk.patches.length; ++i) {
|
||||
const patch = chunk.patches[i];
|
||||
|
||||
patch.item.draw(
|
||||
chunk.x * globalConfig.mapChunkWorldSize + patch.pos.x * globalConfig.tileSize,
|
||||
chunk.y * globalConfig.mapChunkWorldSize + patch.pos.y * globalConfig.tileSize,
|
||||
parameters,
|
||||
Math.min(80, 40 / parameters.zoomLevel)
|
||||
);
|
||||
const destX = chunk.x * globalConfig.mapChunkWorldSize + patch.pos.x * globalConfig.tileSize;
|
||||
const destY = chunk.y * globalConfig.mapChunkWorldSize + patch.pos.y * globalConfig.tileSize;
|
||||
const destSize = Math.min(80, 40 / parameters.zoomLevel);
|
||||
if (parameters.visibleRect.containsCircle(destX, destY, destSize / 2)) {
|
||||
patch.item.drawCentered(destX, destY, parameters, destSize);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// HIGH QUALITY: Draw all items
|
||||
@@ -55,25 +58,12 @@ export class MapResourcesSystem extends GameSystem {
|
||||
if (lowerItem) {
|
||||
const worldY = (chunk.tileY + y) * globalConfig.tileSize;
|
||||
|
||||
if (
|
||||
!parameters.visibleRect.containsRect4Params(
|
||||
worldX,
|
||||
worldY,
|
||||
globalConfig.tileSize,
|
||||
globalConfig.tileSize
|
||||
)
|
||||
) {
|
||||
// Clipped
|
||||
continue;
|
||||
}
|
||||
const destX = worldX + globalConfig.halfTileSize;
|
||||
const destY = worldY + globalConfig.halfTileSize;
|
||||
|
||||
// parameters.context.fillStyle = lowerItem.getBackgroundColorAsResource();
|
||||
// parameters.context.fillRect(worldX, worldY, globalConfig.tileSize, globalConfig.tileSize);
|
||||
lowerItem.draw(
|
||||
worldX + globalConfig.halfTileSize,
|
||||
worldY + globalConfig.halfTileSize,
|
||||
parameters
|
||||
);
|
||||
if (parameters.visibleRect.containsCircle(destX, destY, globalConfig.tileSize / 2)) {
|
||||
lowerItem.drawCentered(destX, destY, parameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,7 +83,6 @@ export class MapResourcesSystem extends GameSystem {
|
||||
generateChunkBackground(chunk, canvas, context, w, h, dpi) {
|
||||
if (this.root.app.settings.getAllSettings().disableTileGrid) {
|
||||
// The map doesn't draw a background, so we have to
|
||||
|
||||
context.fillStyle = THEME.map.background;
|
||||
context.fillRect(0, 0, w, h);
|
||||
} else {
|
||||
|
||||
@@ -130,7 +130,7 @@ export class MinerSystem extends GameSystemWithFilter {
|
||||
}
|
||||
|
||||
if (minerComp.cachedMinedItem) {
|
||||
minerComp.cachedMinedItem.draw(
|
||||
minerComp.cachedMinedItem.drawCentered(
|
||||
(0.5 + staticComp.origin.x) * globalConfig.tileSize,
|
||||
(0.5 + staticComp.origin.y) * globalConfig.tileSize,
|
||||
parameters
|
||||
|
||||
@@ -65,7 +65,7 @@ export class StorageSystem extends GameSystemWithFilter {
|
||||
if (storedItem !== null) {
|
||||
context.globalAlpha = storageComp.overlayOpacity;
|
||||
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
|
||||
storedItem.draw(center.x, center.y, parameters, 30);
|
||||
storedItem.drawCentered(center.x, center.y, parameters, 30);
|
||||
|
||||
this.storageOverlaySprite.drawCached(parameters, center.x - 15, center.y + 15, 30, 15);
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ export class WiredPinsSystem extends GameSystemWithFilter {
|
||||
const value = slot.value;
|
||||
if (value) {
|
||||
const offset = new Vector(0, -9).rotated(effectiveRotation);
|
||||
value.draw(worldPos.x + offset.x, worldPos.y + offset.y, parameters, 9);
|
||||
value.drawCentered(worldPos.x + offset.x, worldPos.y + offset.y, parameters, 9);
|
||||
}
|
||||
|
||||
// Debug view
|
||||
|
||||
Reference in New Issue
Block a user