mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-01 11:12:09 +00:00
Improve icons readability (#1321)
## Description: REQUIRES THE LEVEL PR TO BE MERGED FIRST (as it is using the same layer) https://github.com/openfrontio/OpenFrontIO/pull/1305 #### Icons only zoom level  #### Icons and sprite zoom level  ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: Vivacious Box --------- Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
This commit is contained in:
@@ -20,11 +20,12 @@ class StructureRenderInfo {
|
||||
public owner: PlayerID,
|
||||
public pixiContainer: PIXI.Container,
|
||||
public level: number = 0,
|
||||
public underConstruction: boolean = true,
|
||||
) {}
|
||||
}
|
||||
const ZOOM_THRESHOLD = 2.8; // below this zoom level, structures are not rendered
|
||||
const ZOOM_THRESHOLD = 2.5;
|
||||
const ICON_SIZE = 24;
|
||||
const OFFSET_ZOOM_Y = 15; // offset for the y position of the icon to avoid hiding the structure beneath
|
||||
const OFFSET_ZOOM_Y = 12; // offset for the y position of the icon to avoid hiding the structure beneath
|
||||
|
||||
export class StructureIconsLayer implements Layer {
|
||||
private pixicanvas: HTMLCanvasElement;
|
||||
@@ -110,7 +111,7 @@ export class StructureIconsLayer implements Layer {
|
||||
}
|
||||
|
||||
resizeCanvas() {
|
||||
if (this.renderer.view) {
|
||||
if (this.renderer) {
|
||||
this.pixicanvas.width = window.innerWidth;
|
||||
this.pixicanvas.height = window.innerHeight;
|
||||
this.renderer.resize(innerWidth, innerHeight, 1);
|
||||
@@ -143,10 +144,14 @@ export class StructureIconsLayer implements Layer {
|
||||
if (this.seenUnits.has(unitView)) {
|
||||
const render = this.findRenderByUnit(unitView);
|
||||
if (render) {
|
||||
this.checkForConstructionState(render, unitView);
|
||||
this.checkForOwnershipChange(render, unitView);
|
||||
this.checkForLevelChange(render, unitView);
|
||||
}
|
||||
} else if (this.structures.has(unitView.type())) {
|
||||
} else if (
|
||||
this.structures.has(unitView.type()) ||
|
||||
unitView.type() === UnitType.Construction
|
||||
) {
|
||||
this.addNewStructure(unitView);
|
||||
}
|
||||
}
|
||||
@@ -159,8 +164,23 @@ export class StructureIconsLayer implements Layer {
|
||||
}
|
||||
}
|
||||
|
||||
private checkForConstructionState(
|
||||
render: StructureRenderInfo,
|
||||
unit: UnitView,
|
||||
) {
|
||||
if (
|
||||
render.underConstruction &&
|
||||
render.unit.type() !== UnitType.Construction
|
||||
) {
|
||||
render.underConstruction = false;
|
||||
render.pixiContainer?.destroy();
|
||||
render.pixiContainer = this.createPixiSprite(unit);
|
||||
this.shouldRedraw = true;
|
||||
}
|
||||
}
|
||||
|
||||
private checkForOwnershipChange(render: StructureRenderInfo, unit: UnitView) {
|
||||
if (render && render.owner !== unit.owner().id()) {
|
||||
if (render.owner !== unit.owner().id()) {
|
||||
render.owner = unit.owner().id();
|
||||
render.pixiContainer?.destroy();
|
||||
render.pixiContainer = this.createPixiSprite(unit);
|
||||
@@ -169,7 +189,7 @@ export class StructureIconsLayer implements Layer {
|
||||
}
|
||||
|
||||
private checkForLevelChange(render: StructureRenderInfo, unit: UnitView) {
|
||||
if (render && render.level !== unit.level()) {
|
||||
if (render.level !== unit.level()) {
|
||||
render.level = unit.level();
|
||||
render.pixiContainer?.destroy();
|
||||
render.pixiContainer = this.createPixiSprite(unit);
|
||||
@@ -200,22 +220,41 @@ export class StructureIconsLayer implements Layer {
|
||||
}
|
||||
|
||||
private createTexture(unit: UnitView): PIXI.Texture {
|
||||
const cacheKey = `${unit.owner().id()}-${unit.type()}`;
|
||||
const isConstruction = unit.type() === UnitType.Construction;
|
||||
const constructionType = unit.constructionType();
|
||||
if (isConstruction && constructionType === undefined) {
|
||||
console.warn(
|
||||
`Unit ${unit.id()} is a construction but has no construction type.`,
|
||||
);
|
||||
return PIXI.Texture.EMPTY;
|
||||
}
|
||||
const structureType = isConstruction ? constructionType! : unit.type();
|
||||
const cacheKey = isConstruction
|
||||
? `construction-${structureType}`
|
||||
: `${unit.owner().id()}-${structureType}`;
|
||||
if (this.textureCache.has(cacheKey)) {
|
||||
return this.textureCache.get(cacheKey)!;
|
||||
}
|
||||
|
||||
const structureCanvas = document.createElement("canvas");
|
||||
structureCanvas.width = ICON_SIZE;
|
||||
structureCanvas.height = ICON_SIZE;
|
||||
const context = structureCanvas.getContext("2d")!;
|
||||
context.fillStyle = this.theme
|
||||
.territoryColor(unit.owner())
|
||||
.lighten(0.1)
|
||||
.toRgbString();
|
||||
const borderColor = this.theme
|
||||
.borderColor(unit.owner())
|
||||
.darken(0.2)
|
||||
.toRgbString();
|
||||
|
||||
let borderColor: string;
|
||||
if (isConstruction) {
|
||||
context.fillStyle = "rgb(198, 198, 198)";
|
||||
borderColor = "rgb(128, 127, 127)";
|
||||
} else {
|
||||
context.fillStyle = this.theme
|
||||
.territoryColor(unit.owner())
|
||||
.lighten(0.06)
|
||||
.toRgbString();
|
||||
borderColor = this.theme
|
||||
.borderColor(unit.owner())
|
||||
.darken(0.08)
|
||||
.toRgbString();
|
||||
}
|
||||
context.strokeStyle = borderColor;
|
||||
context.beginPath();
|
||||
context.arc(
|
||||
@@ -228,9 +267,9 @@ export class StructureIconsLayer implements Layer {
|
||||
context.fill();
|
||||
context.lineWidth = 1;
|
||||
context.stroke();
|
||||
const structureInfo = this.structures.get(unit.type());
|
||||
const structureInfo = this.structures.get(structureType);
|
||||
if (!structureInfo?.image) {
|
||||
console.warn(`Image not loaded for unit type: ${unit.type()}`);
|
||||
console.warn(`Image not loaded for unit type: ${structureType}`);
|
||||
return PIXI.Texture.from(structureCanvas);
|
||||
}
|
||||
context.drawImage(
|
||||
@@ -266,10 +305,17 @@ export class StructureIconsLayer implements Layer {
|
||||
text.position.y = -ICON_SIZE / 2 - 2;
|
||||
parentContainer.addChild(text);
|
||||
}
|
||||
parentContainer.position.set(
|
||||
Math.round(screenPos.x),
|
||||
Math.round(screenPos.y - this.transformHandler.scale * OFFSET_ZOOM_Y),
|
||||
);
|
||||
const posX = Math.round(screenPos.x);
|
||||
let posY = Math.round(screenPos.y);
|
||||
if (this.transformHandler.scale >= ZOOM_THRESHOLD) {
|
||||
// Adjust the y position based on zoom level to avoid hiding the structure beneath
|
||||
posY = Math.round(
|
||||
screenPos.y - this.transformHandler.scale * OFFSET_ZOOM_Y,
|
||||
);
|
||||
} else {
|
||||
posY = Math.round(screenPos.y);
|
||||
}
|
||||
parentContainer.position.set(posX, posY);
|
||||
parentContainer.scale.set(Math.min(1, this.transformHandler.scale));
|
||||
this.stage.addChild(parentContainer);
|
||||
return parentContainer;
|
||||
@@ -298,9 +344,14 @@ export class StructureIconsLayer implements Layer {
|
||||
new Cell(worldX, worldY),
|
||||
);
|
||||
screenPos.x = Math.round(screenPos.x);
|
||||
screenPos.y = Math.round(
|
||||
screenPos.y - this.transformHandler.scale * OFFSET_ZOOM_Y,
|
||||
);
|
||||
if (this.transformHandler.scale >= ZOOM_THRESHOLD) {
|
||||
// Adjust the y position based on zoom level to avoid hiding the structure beneath
|
||||
screenPos.y = Math.round(
|
||||
screenPos.y - this.transformHandler.scale * OFFSET_ZOOM_Y,
|
||||
);
|
||||
} else {
|
||||
screenPos.y = Math.round(screenPos.y);
|
||||
}
|
||||
|
||||
// Check if the sprite is on screen (with margin for partial visibility)
|
||||
const margin = ICON_SIZE;
|
||||
@@ -329,6 +380,7 @@ export class StructureIconsLayer implements Layer {
|
||||
unitView.owner().id(),
|
||||
this.createPixiSprite(unitView),
|
||||
unitView.level(),
|
||||
unitView.type() === UnitType.Construction,
|
||||
);
|
||||
this.renders.push(render);
|
||||
this.computeNewLocation(render);
|
||||
|
||||
@@ -24,6 +24,7 @@ const selectedUnitColor = colord({ r: 0, g: 255, b: 255 });
|
||||
const BASE_BORDER_RADIUS = 16.5;
|
||||
const BASE_TERRITORY_RADIUS = 13.5;
|
||||
const RADIUS_SCALE_FACTOR = 0.5;
|
||||
const ZOOM_THRESHOLD = 2.5; // below this zoom level, structures are not rendered
|
||||
|
||||
interface UnitRenderConfig {
|
||||
icon: string;
|
||||
@@ -151,6 +152,9 @@ export class StructureLayer implements Layer {
|
||||
}
|
||||
|
||||
renderLayer(context: CanvasRenderingContext2D) {
|
||||
if (this.transformHandler.scale <= ZOOM_THRESHOLD) {
|
||||
return;
|
||||
}
|
||||
context.drawImage(
|
||||
this.canvas,
|
||||
-this.game.width() / 2,
|
||||
|
||||
Reference in New Issue
Block a user