Skip to content

Commit

Permalink
Merge pull request #453 from Annoraaq/bugfix/#451-fix-depth-sorting-c…
Browse files Browse the repository at this point in the history
…ontainers

#451 fix depth for containers
  • Loading branch information
Annoraaq authored Nov 27, 2023
2 parents bc3ff33 + 5ab4cc5 commit b324d9c
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 67 deletions.
33 changes: 9 additions & 24 deletions src/GridEngine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,17 @@ jest.mock("../package.json", () => ({
import { GridEngine, PathfindingOptions } from "./GridEngine.js";
import { NoPathFoundStrategy } from "./Pathfinding/NoPathFoundStrategy.js";
import { PathBlockedStrategy } from "./Pathfinding/PathBlockedStrategy.js";
import { createSpriteMock } from "./Utils/MockFactory/MockFactory.js";
import {
createContainerMock,
createSpriteMock,
} from "./Utils/MockFactory/MockFactory.js";
import { createPhaserTilemapStub } from "./Utils/MockFactory/MockPhaserTilemap.js";
import { GridEngineStatePhaser } from "./GridEnginePhaser/GridEngineStatePhaser.js";

describe("GridEngine", () => {
let gridEngine: GridEngine;
let sceneMock;
let playerSpriteMock;
let playerSpriteMock: Phaser.GameObjects.Sprite;
let consoleLogBackup;

afterEach(() => {
Expand Down Expand Up @@ -88,25 +91,7 @@ describe("GridEngine", () => {
add: { sprite: jest.fn().mockReturnValue(mockNewSprite) },
};

playerSpriteMock = {
x: 10,
y: 12,
displayWidth: 20,
displayHeight: 40,
width: 20,
setOrigin: jest.fn(),
texture: {
source: [{ width: 240 }],
},
setFrame: jest.fn(function (name) {
this.frame.name = name;
}),
setDepth: jest.fn(),
scale: 2,
frame: {
name: "1",
},
} as any;
playerSpriteMock = createSpriteMock();

gridEngine = new GridEngine(sceneMock);
gridEngine.create(createDefaultMockWithLayer(undefined), {
Expand All @@ -131,7 +116,7 @@ describe("GridEngine", () => {
});

it("should init player", () => {
const containerMock = { setDepth: jest.fn() };
const containerMock = createContainerMock();
gridEngine.create(createDefaultMockWithLayer(undefined), {
characters: [
{
Expand Down Expand Up @@ -191,7 +176,7 @@ describe("GridEngine", () => {
});

it("should init player with facingDirection", () => {
const containerMock = { setDepth: jest.fn() };
const containerMock = createContainerMock();
gridEngine.create(createDefaultMockWithLayer(undefined), {
characters: [
{
Expand Down Expand Up @@ -560,7 +545,7 @@ describe("GridEngine", () => {

it("should get facing position", () => {
const rightStandingFrameNo = 25;
playerSpriteMock.setFrame.mockClear();
(playerSpriteMock.setFrame as any).mockClear();
gridEngine.turnTowards("player", Direction.RIGHT);
expect(playerSpriteMock.setFrame).toHaveBeenCalledWith(
rightStandingFrameNo,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import { GridCharacterPhaser } from "./GridCharacterPhaser.js";
import * as Phaser from "phaser";
import { Direction, NumberOfDirections } from "../../Direction/Direction.js";
import { CharacterData, GridEngineHeadless } from "../../GridEngine.js";
import { createSpriteMock } from "../../Utils/MockFactory/MockFactory.js";
import {
createContainerMock,
createSpriteMock,
} from "../../Utils/MockFactory/MockFactory.js";
import { take } from "rxjs/operators";
import { PhaserTilemap } from "../../GridTilemap/Phaser/PhaserTilemap.js";
import { GridTilemapPhaser } from "../GridTilemapPhaser/GridTilemapPhaser.js";
Expand Down Expand Up @@ -118,7 +121,7 @@ describe("GridCharacterPhaser", () => {
it("should create a grid character", () => {
const walkingAnimationMock = {} as any;
const startPos = { x: 5, y: 6 };
const containerMock = { x: 0, y: 0, setDepth: jest.fn() } as any;
const containerMock = createContainerMock();
const charData = {
id: "charID",
sprite: spriteMock,
Expand Down Expand Up @@ -237,7 +240,7 @@ describe("GridCharacterPhaser", () => {

it("should keep a container", () => {
const { gridCharPhaser } = createChar({}, false);
const containerMock = {} as any;
const containerMock = createContainerMock();
gridCharPhaser.setContainer(containerMock);

expect(gridCharPhaser.getContainer()).toBe(containerMock);
Expand Down Expand Up @@ -303,7 +306,7 @@ describe("GridCharacterPhaser", () => {
const oldAnimation = gridCharPhaser.getAnimation();
gridCharPhaser.setSprite(newSpriteMock);

newSpriteMock.setFrame.mockReset();
(newSpriteMock.setFrame as any).mockReset();
(oldAnimation?.frameChange() as any).next(13);
(gridCharPhaser.getAnimation()?.frameChange() as any).next(13);

Expand Down Expand Up @@ -349,7 +352,12 @@ describe("GridCharacterPhaser", () => {

gridCharPhaser.setSprite(newSpriteMock);

checkSpriteDepth(newSpriteMock, charLayerDepth, "0000");
checkSpriteDepth(
newSpriteMock,
newSpriteMock.displayHeight,
charLayerDepth,
"0000",
);
});

it("should set depth of sprite on char layer", () => {
Expand All @@ -364,15 +372,17 @@ describe("GridCharacterPhaser", () => {

gridCharPhaser.setSprite(newSpriteMock);

checkSpriteDepth(newSpriteMock, charLayerDepth, "0000");
checkSpriteDepth(
newSpriteMock,
newSpriteMock.displayHeight,
charLayerDepth,
"0000",
);
});

it("should set depth of container", () => {
const containerMock = {
y: 20,
displayHeight: 21,
setDepth: jest.fn(),
} as any;
const height = 21;
const containerMock = createContainerMock(0, 20, height);
const startPos = { x: 2, y: 2 };
const charData = {
container: containerMock,
Expand All @@ -385,7 +395,7 @@ describe("GridCharacterPhaser", () => {

gridCharPhaser.setSprite(newSpriteMock);

checkSpriteDepth(containerMock, uppermostCharLayerDepth, "0000");
checkSpriteDepth(containerMock, height, uppermostCharLayerDepth, "0000");
});

it("should set depth of pos above for overlay sprite", () => {
Expand All @@ -400,17 +410,18 @@ describe("GridCharacterPhaser", () => {

gridCharPhaser.setSprite(newSpriteMock);

checkSpriteDepth(overlaySpriteMock, charLayerDepth, "00000");
checkSpriteDepth(
overlaySpriteMock,
overlaySpriteMock.displayHeight,
charLayerDepth,
"00000",
);
});
});

describe("On pixel position change", () => {
it("should update container pixel pos", () => {
const containerMock = {
x: 0,
y: 0,
setDepth: jest.fn(),
} as any;
const containerMock = createContainerMock();
const charData = {
container: containerMock,
offsetX: 10,
Expand Down Expand Up @@ -658,7 +669,12 @@ describe("GridCharacterPhaser", () => {
gridEngineHeadless.update(1000, 10);
gridCharPhaser.update(10);

checkSpriteDepth(spriteMock, charLayerDepth, "0000");
checkSpriteDepth(
spriteMock,
spriteMock.displayHeight,
charLayerDepth,
"0000",
);
});

it("should set depth of sprite on char layer", () => {
Expand All @@ -675,15 +691,17 @@ describe("GridCharacterPhaser", () => {
gridEngineHeadless.update(1000, 10);
gridCharPhaser.update(10);

checkSpriteDepth(spriteMock, charLayerDepth, "0000");
checkSpriteDepth(
spriteMock,
spriteMock.displayHeight,
charLayerDepth,
"0000",
);
});

it("should set depth of container", () => {
const containerMock = {
y: 20,
displayHeight: 21,
setDepth: jest.fn(),
} as any;
const height = 21;
const containerMock = createContainerMock(0, 20, height);
const startPos = { x: 2, y: 2 };
const charData = {
container: containerMock,
Expand All @@ -696,7 +714,7 @@ describe("GridCharacterPhaser", () => {
gridEngineHeadless.move("charID", Direction.RIGHT);
gridCharPhaser.update(10);

checkSpriteDepth(containerMock, uppermostCharLayerDepth, "0000");
checkSpriteDepth(containerMock, height, uppermostCharLayerDepth, "0000");
});

describe("for overlay sprite", () => {
Expand All @@ -719,7 +737,12 @@ describe("GridCharacterPhaser", () => {
gridEngineHeadless.update(1000, 10);
gridCharPhaser.update(10);

checkSpriteDepth(overlaySpriteMock, lowerCharLayerDepth, "00000");
checkSpriteDepth(
overlaySpriteMock,
overlaySpriteMock.displayHeight,
lowerCharLayerDepth,
"00000",
);
});
});
});
Expand Down Expand Up @@ -831,12 +854,13 @@ describe("GridCharacterPhaser", () => {
});

function checkSpriteDepth(
spriteMock,
gameObject: Phaser.GameObjects.Container | Phaser.GameObjects.Sprite,
height: number,
charLayerDepth: number,
zeroPrefix: string,
) {
const pixelDepth = spriteMock.y + spriteMock.displayHeight;
expect(spriteMock.setDepth).toHaveBeenCalledWith(
const pixelDepth = gameObject.y + height;
expect(gameObject.setDepth).toHaveBeenCalledWith(
+`${charLayerDepth}.${zeroPrefix}${pixelDepth}`,
);
}
48 changes: 39 additions & 9 deletions src/GridEnginePhaser/GridCharacterPhaser/GridCharacterPhaser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,35 +243,65 @@ export class GridCharacterPhaser {
}

private updateDepth() {
const gameObject = this.getGameObj();
const gameObj = this.getGameObj();
if (!gameObj) return;

if (!gameObject) return;
const position = new Vector2(this.geHeadless.getPosition(this.charData.id));
const layer = this.geHeadless.getCharLayer(this.charData.id);
this.setDepth(gameObject, { position, layer });

if (this.container) {
this.setContainerDepth(this.container, { position, layer });
} else if (this.sprite) {
this.setSpriteDepth(this.sprite, { position, layer });
}
const layerOverlaySprite = this.getLayerOverlaySprite();

if (layerOverlaySprite) {
const posAbove = new Vector2({
...position,
y: position.y - 1,
});
this.setDepth(layerOverlaySprite, {
this.setSpriteDepth(layerOverlaySprite, {
position: posAbove,
layer,
});
}
}

private setDepth(gameObject: GameObject, position: LayerVecPos): void {
gameObject.setDepth(
private setSpriteDepth(
sprite: Phaser.GameObjects.Sprite,
position: LayerVecPos,
): void {
sprite.setDepth(
this.tilemap.getDepthOfCharLayer(this.getTransitionLayer(position)) +
this.getPaddedPixelDepth(gameObject),
this.getPaddedPixelDepthSprite(sprite),
);
}

private getPaddedPixelDepth(gameObject: GameObject): number {
return Utils.shiftPad(gameObject.y + gameObject.displayHeight, 7);
private setContainerDepth(
container: Phaser.GameObjects.Container,
position: LayerVecPos,
): void {
container.setDepth(
this.tilemap.getDepthOfCharLayer(this.getTransitionLayer(position)) +
this.getPaddedPixelDepthContainer(container),
);
}

private getPaddedPixelDepthSprite(sprite: Phaser.GameObjects.Sprite): number {
return Utils.shiftPad(
sprite.y + sprite.displayHeight,
GridTilemapPhaser.Z_INDEX_PADDING,
);
}

private getPaddedPixelDepthContainer(
container: Phaser.GameObjects.Container,
): number {
return Utils.shiftPad(
container.y + container.getBounds().height,
GridTilemapPhaser.Z_INDEX_PADDING,
);
}

private getTransitionLayer(position: LayerVecPos): CharLayer {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export class GridTilemapPhaser {
private static readonly ALWAYS_TOP_PROP_NAME = "ge_alwaysTop";
private static readonly CHAR_LAYER_PROP_NAME = "ge_charLayer";
private static readonly HEIGHT_SHIFT_PROP_NAME = "ge_heightShift";
private static readonly Z_INDEX_PADDING = 7;
static readonly Z_INDEX_PADDING = 7;
private charLayerDepths = new Map<CharLayer, number>();

constructor(private tilemap: Phaser.Tilemaps.Tilemap) {
Expand Down
29 changes: 26 additions & 3 deletions src/Utils/MockFactory/MockFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ import { LayerVecPos } from "../../Pathfinding/ShortestPathAlgorithm.js";
import { TileLayer, Tile, Tilemap } from "../../GridTilemap/Tilemap.js";
import { MockTile, MockTileLayer, MockTilemap } from "./MockTilemap.js";

export interface LayerData {
name?: string;
properties?: Record<string, string>;
height?: number;
width?: number;
scale?: number;
tilesets?: string[];
data?: Array<Array<Tile | undefined>>;
}
export interface TileCost {
ge_cost?: number;
ge_cost_left?: number;
Expand All @@ -30,7 +39,7 @@ export interface CostMapLayer {
costMap: CostMap;
}

export function createSpriteMock() {
export function createSpriteMock(): Phaser.GameObjects.Sprite {
return {
x: 10,
y: 12,
Expand All @@ -50,10 +59,24 @@ export function createSpriteMock() {
frame: {
name: "1",
},
} as any;
} as unknown as Phaser.GameObjects.Sprite;
}

export function createContainerMock(
x = 0,
y = 0,
height = 0,
): Phaser.GameObjects.Container {
return {
x,
y,
displayHeight: 0,
setDepth: jest.fn(),
getBounds: jest.fn(() => ({ height })),
} as unknown as Phaser.GameObjects.Container;
}

export function createMockLayer(layerData: any): TileLayer {
export function createMockLayer(layerData: LayerData): TileLayer {
return new MockTileLayer(
layerData.name,
layerData.properties,
Expand Down

0 comments on commit b324d9c

Please sign in to comment.