UNPKG

phaser

Version:

A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.

414 lines (366 loc) 15.3 kB
var TransformerTile = require('../../../../../src/renderer/webgl/renderNodes/transformer/TransformerTile'); describe('TransformerTile', function () { var mockManager; var node; beforeEach(function () { mockManager = {}; node = new TransformerTile(mockManager); }); // ------------------------------------------------------------------------- // Constructor // ------------------------------------------------------------------------- describe('constructor', function () { it('should create an instance of TransformerTile', function () { expect(node).toBeDefined(); }); it('should set the name from defaultConfig', function () { expect(node.name).toBe('TransformerTile'); }); it('should store the manager reference', function () { expect(node.manager).toBe(mockManager); }); it('should initialise quad as a Float32Array of length 8', function () { expect(node.quad).toBeInstanceOf(Float32Array); expect(node.quad.length).toBe(8); }); it('should initialise quad values to zero', function () { for (var i = 0; i < 8; i++) { expect(node.quad[i]).toBe(0); } }); it('should create internal _spriteMatrix', function () { expect(node._spriteMatrix).toBeDefined(); }); it('should create internal _calcMatrix', function () { expect(node._calcMatrix).toBeDefined(); }); it('should accept a custom config object', function () { var custom = new TransformerTile(mockManager, { name: 'CustomTile' }); expect(custom.name).toBe('CustomTile'); }); }); // ------------------------------------------------------------------------- // defaultConfig // ------------------------------------------------------------------------- describe('defaultConfig', function () { it('should have name set to TransformerTile', function () { expect(TransformerTile.prototype.defaultConfig.name).toBe('TransformerTile'); }); it('should have role set to Transformer', function () { expect(TransformerTile.prototype.defaultConfig.role).toBe('Transformer'); }); }); // ------------------------------------------------------------------------- // run // ------------------------------------------------------------------------- describe('run', function () { var mockCalcMatrix; var mockSpriteMatrix; var drawingContext; var gameObject; var texturerNode; var element; beforeEach(function () { mockCalcMatrix = { matrix: [ 1, 0, 0, 1, 0, 0 ], copyWithScrollFactorFrom: vi.fn(), multiply: vi.fn(), setQuad: vi.fn(function (x, y, x2, y2, quad) { quad[0] = x; quad[1] = y; quad[2] = x2; quad[3] = y; quad[4] = x; quad[5] = y2; quad[6] = x2; quad[7] = y2; }) }; mockSpriteMatrix = { applyITRS: vi.fn() }; node._calcMatrix = mockCalcMatrix; node._spriteMatrix = mockSpriteMatrix; drawingContext = { camera: { getViewMatrix: vi.fn(function () { return {}; }), scrollX: 0, scrollY: 0 }, useCanvas: false }; gameObject = { scrollFactorX: 1, scrollFactorY: 1, scaleX: 1, scaleY: 1, x: 0, y: 0, gidMap: { 0: { tileOffset: { x: 0, y: 0 } } }, willRoundVertices: vi.fn(function () { return false; }) }; texturerNode = { frameWidth: 32, frameHeight: 32 }; element = { index: 0, pixelX: 0, pixelY: 0, flipX: false, flipY: false, rotation: 0 }; }); it('should call copyWithScrollFactorFrom on calcMatrix', function () { node.run(drawingContext, gameObject, texturerNode, null, element); expect(mockCalcMatrix.copyWithScrollFactorFrom).toHaveBeenCalledOnce(); }); it('should call getViewMatrix on the camera', function () { node.run(drawingContext, gameObject, texturerNode, null, element); expect(drawingContext.camera.getViewMatrix).toHaveBeenCalledOnce(); }); it('should call applyITRS on spriteMatrix', function () { node.run(drawingContext, gameObject, texturerNode, null, element); expect(mockSpriteMatrix.applyITRS).toHaveBeenCalledOnce(); }); it('should call setQuad on calcMatrix', function () { node.run(drawingContext, gameObject, texturerNode, null, element); expect(mockCalcMatrix.setQuad).toHaveBeenCalledOnce(); }); it('should call multiply on calcMatrix twice when no parentMatrix', function () { node.run(drawingContext, gameObject, texturerNode, null, element); // Once for spriteMatrix multiply expect(mockCalcMatrix.multiply).toHaveBeenCalledTimes(1); }); it('should call multiply on calcMatrix twice when parentMatrix is provided', function () { var parentMatrix = {}; node.run(drawingContext, gameObject, texturerNode, parentMatrix, element); // Once for parentMatrix, once for spriteMatrix expect(mockCalcMatrix.multiply).toHaveBeenCalledTimes(2); }); it('should multiply by parentMatrix first when provided', function () { var parentMatrix = {}; node.run(drawingContext, gameObject, texturerNode, parentMatrix, element); expect(mockCalcMatrix.multiply).toHaveBeenCalledWith(parentMatrix); }); it('should pass element rotation to applyITRS', function () { element.rotation = Math.PI / 4; node.run(drawingContext, gameObject, texturerNode, null, element); var call = mockSpriteMatrix.applyITRS.mock.calls[0]; expect(call[2]).toBeCloseTo(Math.PI / 4); }); it('should pass gameObject scale to applyITRS', function () { gameObject.scaleX = 2; gameObject.scaleY = 3; node.run(drawingContext, gameObject, texturerNode, null, element); var call = mockSpriteMatrix.applyITRS.mock.calls[0]; expect(call[3]).toBe(2); expect(call[4]).toBe(3); }); it('should compute srcX using gameObject position, pixelX, scaleX, frameWidth, and tileOffset', function () { gameObject.x = 100; gameObject.scaleX = 2; gameObject.scaleY = 2; element.pixelX = 10; texturerNode.frameWidth = 32; gameObject.gidMap[0].tileOffset.x = 4; // srcX = 100 + 10*2 + (16*2 - 4) = 100 + 20 + 28 = 148 node.run(drawingContext, gameObject, texturerNode, null, element); var call = mockSpriteMatrix.applyITRS.mock.calls[0]; expect(call[0]).toBeCloseTo(148); }); it('should compute srcY using gameObject position, pixelY, scaleY, frameHeight, and tileOffset', function () { gameObject.y = 50; gameObject.scaleX = 2; gameObject.scaleY = 2; element.pixelY = 5; texturerNode.frameHeight = 32; gameObject.gidMap[0].tileOffset.y = 8; // srcY = 50 + 5*2 + (16*2 - 8) = 50 + 10 + 24 = 84 node.run(drawingContext, gameObject, texturerNode, null, element); var call = mockSpriteMatrix.applyITRS.mock.calls[0]; expect(call[1]).toBeCloseTo(84); }); it('should negate width and offset x origin when flipX is true', function () { element.flipX = true; texturerNode.frameWidth = 32; texturerNode.frameHeight = 32; node.run(drawingContext, gameObject, texturerNode, null, element); var setQuadCall = mockCalcMatrix.setQuad.mock.calls[0]; // x becomes -halfWidth + frameWidth = -16 + 32 = 16 // x + width = 16 + (-32) = -16 expect(setQuadCall[0]).toBe(16); expect(setQuadCall[2]).toBe(-16); }); it('should negate height and offset y origin when flipY is true', function () { element.flipY = true; texturerNode.frameWidth = 32; texturerNode.frameHeight = 32; node.run(drawingContext, gameObject, texturerNode, null, element); var setQuadCall = mockCalcMatrix.setQuad.mock.calls[0]; // y = -halfHeight = -16, height = -32, so y + height = -48 expect(setQuadCall[1]).toBe(-16); expect(setQuadCall[3]).toBe(-48); }); it('should use standard quad x and y when no flip is applied', function () { texturerNode.frameWidth = 64; texturerNode.frameHeight = 64; node.run(drawingContext, gameObject, texturerNode, null, element); var setQuadCall = mockCalcMatrix.setQuad.mock.calls[0]; // x = -halfWidth = -32, y = -halfHeight = -32 expect(setQuadCall[0]).toBe(-32); expect(setQuadCall[1]).toBe(-32); // x + width = -32 + 64 = 32, y + height = -32 + 64 = 32 expect(setQuadCall[2]).toBe(32); expect(setQuadCall[3]).toBe(32); }); it('should look up the tileset via element.index in gidMap', function () { gameObject.gidMap = { 5: { tileOffset: { x: 10, y: 20 } } }; element.index = 5; gameObject.x = 0; gameObject.y = 0; gameObject.scaleX = 1; gameObject.scaleY = 1; element.pixelX = 0; element.pixelY = 0; texturerNode.frameWidth = 32; texturerNode.frameHeight = 32; // srcX = 0 + 0 + (16 - 10) = 6, srcY = 0 + 0 + (16 - 20) = -4 node.run(drawingContext, gameObject, texturerNode, null, element); var call = mockSpriteMatrix.applyITRS.mock.calls[0]; expect(call[0]).toBeCloseTo(6); expect(call[1]).toBeCloseTo(-4); }); it('should not round quad values when willRoundVertices returns false', function () { gameObject.willRoundVertices = vi.fn(function () { return false; }); // setQuad writes fractional values into quad mockCalcMatrix.setQuad = vi.fn(function (x, y, x2, y2, quad) { quad[0] = 1.7; quad[1] = 2.3; quad[2] = 3.9; quad[3] = 4.1; quad[4] = 5.5; quad[5] = 6.6; quad[6] = 7.2; quad[7] = 8.8; }); node.run(drawingContext, gameObject, texturerNode, null, element); expect(node.quad[0]).toBeCloseTo(1.7); expect(node.quad[1]).toBeCloseTo(2.3); }); it('should round quad values when willRoundVertices returns true', function () { gameObject.willRoundVertices = vi.fn(function () { return true; }); mockCalcMatrix.setQuad = vi.fn(function (x, y, x2, y2, quad) { quad[0] = 1.7; quad[1] = 2.3; quad[2] = 3.9; quad[3] = 4.1; quad[4] = 5.5; quad[5] = 6.6; quad[6] = 7.2; quad[7] = 8.8; }); node.run(drawingContext, gameObject, texturerNode, null, element); expect(node.quad[0]).toBe(2); expect(node.quad[1]).toBe(2); expect(node.quad[2]).toBe(4); expect(node.quad[3]).toBe(4); expect(node.quad[4]).toBe(6); expect(node.quad[5]).toBe(7); expect(node.quad[6]).toBe(7); expect(node.quad[7]).toBe(9); }); it('should pass camera and onlyTranslate flag to willRoundVertices', function () { node.run(drawingContext, gameObject, texturerNode, null, element); expect(gameObject.willRoundVertices).toHaveBeenCalledWith( drawingContext.camera, expect.any(Boolean) ); }); it('should detect identity matrix as onlyTranslate=true', function () { // matrix [1,0,0,1,...] is identity — no rotation/scale/skew mockCalcMatrix.matrix = [ 1, 0, 0, 1, 0, 0 ]; node.run(drawingContext, gameObject, texturerNode, null, element); var args = gameObject.willRoundVertices.mock.calls[0]; expect(args[1]).toBe(true); }); it('should detect scaled matrix as onlyTranslate=false', function () { mockCalcMatrix.matrix = [ 2, 0, 0, 2, 0, 0 ]; node.run(drawingContext, gameObject, texturerNode, null, element); var args = gameObject.willRoundVertices.mock.calls[0]; expect(args[1]).toBe(false); }); it('should detect rotated matrix as onlyTranslate=false', function () { mockCalcMatrix.matrix = [ 1, 0.5, -0.5, 1, 0, 0 ]; node.run(drawingContext, gameObject, texturerNode, null, element); var args = gameObject.willRoundVertices.mock.calls[0]; expect(args[1]).toBe(false); }); it('should pass useCanvas flag to camera.getViewMatrix', function () { drawingContext.useCanvas = true; node.run(drawingContext, gameObject, texturerNode, null, element); // getViewMatrix receives !useCanvas = false expect(drawingContext.camera.getViewMatrix).toHaveBeenCalledWith(false); }); it('should pass useCanvas=false flag to camera.getViewMatrix correctly', function () { drawingContext.useCanvas = false; node.run(drawingContext, gameObject, texturerNode, null, element); // getViewMatrix receives !useCanvas = true expect(drawingContext.camera.getViewMatrix).toHaveBeenCalledWith(true); }); it('should pass the quad Float32Array to setQuad', function () { node.run(drawingContext, gameObject, texturerNode, null, element); var setQuadCall = mockCalcMatrix.setQuad.mock.calls[0]; expect(setQuadCall[4]).toBe(node.quad); }); }); });