perf: Render half the amount of characters

Reduced the needed amount of characters for rendering a scene by half,
by using ▄ character.
This commit is contained in:
2025-12-16 17:35:02 +01:00
parent 862baf9809
commit 1e8e5cff6d
9 changed files with 45 additions and 33 deletions

View File

@@ -26,7 +26,7 @@ public record RerenderScreen(ScreenPart[] parts) implements Event {
public static ScreenPart full(TerminalSize terminalSize) {
return new ScreenPart(
new TerminalPosition(0, 0),
new TerminalPosition(terminalSize.getColumns() - 1, terminalSize.getRows() - 1)
new TerminalPosition(terminalSize.getColumns() - 1, terminalSize.getRows() * 2 - 1)
);
}

View File

@@ -38,14 +38,26 @@ public class CliHandler extends AbstractEventHandler<RerenderScreen> {
for (RerenderScreen.ScreenPart part : parts) {
var start = part.getStart();
int startYNormalized = (start.getRow() / 2) * 2; // Round to multiple of 2 down
var end = part.getEnd();
int endYNormalized = ((end.getRow() - 1) / 2) * 2; // Round to multiple of 2 ceil
for (int y = start.getRow(); y <= end.getRow(); y++) {
for (int y = startYNormalized; y <= endYNormalized; y += 2) {
for (int x = start.getColumn(); x <= end.getColumn(); x++) {
Pixel pixel = buffer[y][x];
TextColor color = pixel.getClass().equals(Empty.class) ? Constants.DEFAULT_COLOR : pixel.getColor();
Pixel topPixel = buffer[y][x];
Pixel bottomPixel = (y + 1 <= end.getRow())
? buffer[y + 1][x]
: new Empty();
drawPixel(tg, x, y, color);
TextColor topColor = topPixel instanceof Empty
? Constants.DEFAULT_COLOR
: topPixel.getColor();
TextColor bottomColor = bottomPixel instanceof Empty
? Constants.DEFAULT_COLOR
: bottomPixel.getColor();
drawHalfPixel(tg, x, y / 2, topColor, bottomColor);
}
}
}
@@ -57,8 +69,11 @@ public class CliHandler extends AbstractEventHandler<RerenderScreen> {
}
}
private void drawPixel(TextGraphics tg, int x, int y, TextColor color) {
tg.setForegroundColor(color);
tg.setCharacter(x, y, '█');
private void drawHalfPixel(TextGraphics tg, int x, int y,
TextColor topColor,
TextColor bottomColor) {
tg.setBackgroundColor(topColor); // upper half
tg.setForegroundColor(bottomColor); // lower half
tg.setCharacter(x, y, '▄');
}
}

View File

@@ -93,21 +93,18 @@ public class FullRoomDrawHandler extends AbstractEventHandler<FullRoomDraw> {
int green = (pixel >> 8) & 0xff;
int blue = pixel & 0xff;
Pixel overridePixelLeft = overrideBuffer[y][x * 2];
Pixel overridePixelRight = overrideBuffer[y][x * 2 + 1];
Pixel overridePixel = overrideBuffer[y][x];
Pixel pixel1 = new ColoredPixel(new TextColor.RGB(red, green, blue));
Pixel finalPixelLeft = overridePixelLeft.getClass() == Empty.class || pixelResult.isPlayer() ? pixel1 : overridePixelLeft;
Pixel finalPixelRight = overridePixelRight.getClass() == Empty.class || pixelResult.isPlayer() ? pixel1 : overridePixelRight;
Pixel finalPixelLeft = overridePixel.getClass() == Empty.class || pixelResult.isPlayer() ? pixel1 : overridePixel;
buffer[y + startY][x * 2 + startX] = finalPixelLeft;
buffer[y + startY][x * 2 + 1 + startX] = finalPixelRight;
buffer[y + startY][x + startX] = finalPixelLeft;
}
}
partsToRerender.add(new RerenderScreen.ScreenPart(
new TerminalPosition(startX, startY),
new TerminalPosition(startX + width * 2, startY + height - 1)
new TerminalPosition(startX + width, startY + height - 1)
));
if (renderState.isFirstRender() || event.isFullRerender()) {

View File

@@ -67,10 +67,10 @@ public class MouseMoveEventHandler extends AbstractEventHandler<MouseMoveEvent>
BufferedImage texture = gameObject.getTexture();
RoomCords cords = gameObject.getCords();
return
(mouseX - startX) / 2 >= cords.getX() &&
(mouseX - startX) / 2 < cords.getX() + texture.getWidth() &&
mouseY >= cords.getY() + startY &&
mouseY < cords.getY() + startY + texture.getHeight();
mouseX - startX >= cords.getX() &&
mouseX - startX < cords.getX() + texture.getWidth() &&
(mouseY * 2 - startY) >= cords.getY() &&
(mouseY * 2 - startY) < cords.getY() + texture.getHeight();
}).collect(Collectors.toSet());
Set<GameObject> changedObjects = new HashSet<>();
@@ -96,7 +96,7 @@ public class MouseMoveEventHandler extends AbstractEventHandler<MouseMoveEvent>
int forStartX = cords.getX();
int forStartY = cords.getY();
int forEndX = cords.getX() + objectTexture.getWidth() - 1;
int forEndY = cords.getY() + objectTexture.getHeight() - 1;
int forEndY = cords.getY() + objectTexture.getHeight();
RerenderUtils.rerenderPart(
forStartX,

View File

@@ -111,7 +111,7 @@ public class PlayerMoveEventHandler extends AbstractEventHandler<PlayerMoveEvent
eventManager.emitEvent(new RerenderScreen(
new RerenderScreen.ScreenPart(
new TerminalPosition(forStartX + startX, forStartY + startY),
new TerminalPosition(forEndX * 2 + 1 + startX, forEndY + startY)
new TerminalPosition(forEndX + 1 + startX, forEndY + startY)
)
));
}

View File

@@ -35,7 +35,7 @@ public class TerminalResizeEventHandler extends AbstractEventHandler<TerminalRes
public void handle(TerminalResizeEvent event) {
TerminalSize size = event.getNewSize();
int width = size.getColumns();
int height = size.getRows();
int height = size.getRows() * 2;
Pixel[][] buffer = new Pixel[height][width];
for (int x = 0; x < width; x++) {

View File

@@ -31,6 +31,7 @@ public class GameSetup {
Chest chest = new Chest(dependencyManager, resourceManager, new RoomCords(100, 45), new GameItem[]{
new WoodenSword(resourceManager),
new WoodenSword(resourceManager),
new WoodenSword(resourceManager),
});
mainRoom.addObject(chest);

View File

@@ -74,12 +74,14 @@ public final class Chest extends GameObject implements Interactable, UIClickHand
int itemCount = items.size();
int chestUIStartX = getCords().getX() * 2;
int chestUIStartX = getCords().getX();
int chestUIStartY = getCords().getY();
int chestUISizeY = 16 + 4;
int chestUISizeX = 8 + itemCount * 32 + (itemCount - 1) * 2;
int chestUISizeX = 8 + itemCount * 16 + (itemCount - 1);
int chestGUIStartY = chestUIStartY - chestUISizeY - 1;
int chestGUIStartX = chestUIStartX + chest.getWidth() - (chestUISizeX / 2);
int chestGUIStartX = chestUIStartX + (chest.getWidth() / 2) - (chestUISizeX / 2);
TerminalPosition guiStart = new TerminalPosition(chestGUIStartX / 2, chestGUIStartY);
TerminalPosition guiEnd = new TerminalPosition((chestGUIStartX + chestUISizeX - 1) / 2, ((chestGUIStartY + chestUISizeY - 1) / 2));

View File

@@ -22,10 +22,10 @@ public class RerenderUtils {
public static RoomCords getStart(BufferedImage room, TerminalSize terminalSize) {
int width = room.getWidth();
int height = room.getHeight();
int terminalHeight = terminalSize.getRows();
int terminalHeight = terminalSize.getRows() * 2;
int terminalWidth = terminalSize.getColumns();
int x = (terminalWidth - (width * 2)) / 2;
int x = (terminalWidth - width) / 2;
int y = (terminalHeight - height) / 2;
return new RoomCords(x, y);
@@ -45,15 +45,12 @@ public class RerenderUtils {
int green = (pixel >> 8) & 0xff;
int blue = pixel & 0xff;
Pixel overridePixelLeft = overrideBuffer[y][x * 2];
Pixel overridePixelRight = overrideBuffer[y][x * 2 + 1];
Pixel overridePixel = overrideBuffer[y][x];
Pixel pixel1 = new ColoredPixel(new TextColor.RGB(red, green, blue));
Pixel finalPixelLeft = overridePixelLeft.getClass() == Empty.class || pixelResult.isPlayer() ? pixel1 : overridePixelLeft;
Pixel finalPixelRight = overridePixelRight.getClass() == Empty.class || pixelResult.isPlayer() ? pixel1 : overridePixelRight;
Pixel finalPixel = overridePixel.getClass() == Empty.class || pixelResult.isPlayer() ? pixel1 : overridePixel;
buffer[y + screenStartY][x * 2 + screenStartX] = finalPixelLeft;
buffer[y + screenStartY][x * 2 + 1 + screenStartX] = finalPixelRight;
buffer[y + screenStartY][x + screenStartX] = finalPixel;
}
}
}