1
0
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:
tobspr
2020-08-15 19:43:03 +02:00
parent b1fb0fca7e
commit 7b40292be3
34 changed files with 550 additions and 463 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,

View File

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

View File

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

View File

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

View File

@@ -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 {

View File

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

View File

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

View File

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