phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
925 lines (713 loc) • 29.2 kB
JavaScript
var CameraManager = require('../../../src/cameras/2d/CameraManager');
describe('CameraManager', function ()
{
var manager;
var mockScene;
function createMockScene (roundPixels)
{
var scene = {};
scene.sys = {
game: {
config: { roundPixels: roundPixels !== undefined ? roundPixels : false },
scale: {
on: vi.fn(),
off: vi.fn()
},
scene: {
customViewports: 0
}
},
scale: { width: 800, height: 600 },
settings: {},
cameras: null,
events: {
once: vi.fn(),
on: vi.fn(),
off: vi.fn(),
emit: vi.fn()
}
};
return scene;
}
function createMockCamera (options)
{
options = options || {};
var cam = {
id: options.id !== undefined ? options.id : 0,
name: options.name || '',
x: options.x !== undefined ? options.x : 0,
y: options.y !== undefined ? options.y : 0,
_x: options.x !== undefined ? options.x : 0,
_y: options.y !== undefined ? options.y : 0,
width: options.width !== undefined ? options.width : 800,
height: options.height !== undefined ? options.height : 600,
_width: options.width !== undefined ? options.width : 800,
_height: options.height !== undefined ? options.height : 600,
visible: options.visible !== undefined ? options.visible : true,
alpha: options.alpha !== undefined ? options.alpha : 1,
inputEnabled: options.inputEnabled !== undefined ? options.inputEnabled : true,
roundPixels: false,
renderList: [],
setRoundPixels: function (v) { this.roundPixels = v; },
setSize: vi.fn(function (w, h) { this._width = w; this._height = h; }),
destroy: vi.fn(),
update: vi.fn(),
preRender: vi.fn()
};
return cam;
}
beforeEach(function ()
{
mockScene = createMockScene();
manager = new CameraManager(mockScene);
mockScene.sys.cameras = manager;
});
describe('constructor', function ()
{
it('should set the scene reference', function ()
{
expect(manager.scene).toBe(mockScene);
});
it('should set the systems reference', function ()
{
expect(manager.systems).toBe(mockScene.sys);
});
it('should set roundPixels to false from game config', function ()
{
expect(manager.roundPixels).toBe(false);
});
it('should set roundPixels to true when game config has roundPixels true', function ()
{
var scene = createMockScene(true);
var mgr = new CameraManager(scene);
expect(mgr.roundPixels).toBe(true);
});
it('should initialize cameras as an empty array', function ()
{
expect(Array.isArray(manager.cameras)).toBe(true);
expect(manager.cameras.length).toBe(0);
});
it('should register a BOOT event listener on sys events', function ()
{
expect(mockScene.sys.events.once).toHaveBeenCalled();
});
it('should register a START event listener on sys events', function ()
{
expect(mockScene.sys.events.on).toHaveBeenCalled();
});
});
describe('add', function ()
{
it('should add a camera and return it', function ()
{
var cam = manager.add(0, 0, 800, 600);
expect(cam).toBeDefined();
expect(manager.cameras.length).toBe(1);
expect(manager.cameras[0]).toBe(cam);
});
it('should use default x and y of 0 when not specified', function ()
{
var cam = manager.add();
expect(cam._x).toBe(0);
expect(cam._y).toBe(0);
});
it('should use scene scale width and height as defaults', function ()
{
var cam = manager.add();
expect(cam._width).toBe(800);
expect(cam._height).toBe(600);
});
it('should use provided x, y, width and height', function ()
{
var cam = manager.add(10, 20, 320, 240);
expect(cam._x).toBe(10);
expect(cam._y).toBe(20);
expect(cam._width).toBe(320);
expect(cam._height).toBe(240);
});
it('should set main to the new camera when makeMain is true', function ()
{
var cam = manager.add(0, 0, 800, 600, true);
expect(manager.main).toBe(cam);
});
it('should not set main when makeMain is false', function ()
{
var cam = manager.add(0, 0, 800, 600, false);
expect(manager.main).not.toBe(cam);
});
it('should set the camera name', function ()
{
var cam = manager.add(0, 0, 800, 600, false, 'myCam');
expect(cam.name).toBe('myCam');
});
it('should assign a non-zero id to the first camera', function ()
{
var cam = manager.add();
expect(cam.id).not.toBe(0);
});
it('should assign unique ids to multiple cameras', function ()
{
var cam1 = manager.add();
var cam2 = manager.add();
var cam3 = manager.add();
expect(cam1.id).not.toBe(cam2.id);
expect(cam2.id).not.toBe(cam3.id);
expect(cam1.id).not.toBe(cam3.id);
});
it('should add multiple cameras to the cameras array', function ()
{
manager.add();
manager.add();
manager.add();
expect(manager.cameras.length).toBe(3);
});
it('should assign id 0 after 32 cameras have been created', function ()
{
for (var i = 0; i < 32; i++)
{
manager.add();
}
var overflow = manager.add();
expect(overflow.id).toBe(0);
});
});
describe('addExisting', function ()
{
it('should add a new camera and return it', function ()
{
var cam = createMockCamera();
var result = manager.addExisting(cam);
expect(result).toBe(cam);
expect(manager.cameras.length).toBe(1);
});
it('should return null if the camera already exists in the manager', function ()
{
var cam = createMockCamera();
manager.addExisting(cam);
var result = manager.addExisting(cam);
expect(result).toBeNull();
});
it('should not add a duplicate camera', function ()
{
var cam = createMockCamera();
manager.addExisting(cam);
manager.addExisting(cam);
expect(manager.cameras.length).toBe(1);
});
it('should set main to the camera when makeMain is true', function ()
{
var cam = createMockCamera();
manager.addExisting(cam, true);
expect(manager.main).toBe(cam);
});
it('should not set main when makeMain is false', function ()
{
var cam = createMockCamera();
manager.addExisting(cam, false);
expect(manager.main).not.toBe(cam);
});
it('should assign a non-zero id to the added camera', function ()
{
var cam = createMockCamera({ id: 0 });
manager.addExisting(cam);
expect(cam.id).not.toBe(0);
});
it('should set roundPixels on the added camera', function ()
{
manager.roundPixels = true;
var cam = createMockCamera();
manager.addExisting(cam);
expect(cam.roundPixels).toBe(true);
});
});
describe('getTotal', function ()
{
it('should return 0 when no cameras exist', function ()
{
expect(manager.getTotal()).toBe(0);
});
it('should return the total number of cameras', function ()
{
manager.cameras.push(createMockCamera());
manager.cameras.push(createMockCamera());
manager.cameras.push(createMockCamera());
expect(manager.getTotal()).toBe(3);
});
it('should count all cameras regardless of visibility when isVisible is false', function ()
{
manager.cameras.push(createMockCamera({ visible: true }));
manager.cameras.push(createMockCamera({ visible: false }));
manager.cameras.push(createMockCamera({ visible: false }));
expect(manager.getTotal(false)).toBe(3);
});
it('should count only visible cameras when isVisible is true', function ()
{
manager.cameras.push(createMockCamera({ visible: true }));
manager.cameras.push(createMockCamera({ visible: false }));
manager.cameras.push(createMockCamera({ visible: true }));
expect(manager.getTotal(true)).toBe(2);
});
it('should return 0 when isVisible is true and no cameras are visible', function ()
{
manager.cameras.push(createMockCamera({ visible: false }));
manager.cameras.push(createMockCamera({ visible: false }));
expect(manager.getTotal(true)).toBe(0);
});
});
describe('fromJSON', function ()
{
it('should return this for chaining', function ()
{
var result = manager.fromJSON({ x: 0, y: 0, width: 800, height: 600 });
expect(result).toBe(manager);
});
it('should create a camera from a single config object', function ()
{
manager.fromJSON({ x: 10, y: 20, width: 400, height: 300 });
expect(manager.cameras.length).toBe(1);
});
it('should create cameras from an array of config objects', function ()
{
manager.fromJSON([
{ x: 0, y: 0, width: 400, height: 300 },
{ x: 400, y: 0, width: 400, height: 300 }
]);
expect(manager.cameras.length).toBe(2);
});
it('should set camera name from config', function ()
{
manager.fromJSON({ name: 'testCam' });
expect(manager.cameras[0].name).toBe('testCam');
});
it('should set camera zoom from config', function ()
{
manager.fromJSON({ zoom: 2 });
expect(manager.cameras[0].zoom).toBe(2);
});
it('should set camera rotation from config', function ()
{
manager.fromJSON({ rotation: 1.5 });
expect(manager.cameras[0].rotation).toBeCloseTo(1.5);
});
it('should set camera scrollX from config', function ()
{
manager.fromJSON({ scrollX: 100 });
expect(manager.cameras[0].scrollX).toBe(100);
});
it('should set camera scrollY from config', function ()
{
manager.fromJSON({ scrollY: 200 });
expect(manager.cameras[0].scrollY).toBe(200);
});
it('should set camera visible from config', function ()
{
manager.fromJSON({ visible: false });
expect(manager.cameras[0].visible).toBe(false);
});
it('should use game width and height as defaults when not specified', function ()
{
manager.fromJSON({});
expect(manager.cameras[0]._width).toBe(800);
expect(manager.cameras[0]._height).toBe(600);
});
it('should use specified x and y from config', function ()
{
manager.fromJSON({ x: 50, y: 75 });
expect(manager.cameras[0]._x).toBe(50);
expect(manager.cameras[0]._y).toBe(75);
});
});
describe('getCamera', function ()
{
it('should return a camera by name', function ()
{
var cam = createMockCamera({ name: 'myCamera' });
manager.cameras.push(cam);
expect(manager.getCamera('myCamera')).toBe(cam);
});
it('should return null when no camera with that name exists', function ()
{
var cam = createMockCamera({ name: 'foo' });
manager.cameras.push(cam);
expect(manager.getCamera('bar')).toBeNull();
});
it('should return null when cameras array is empty', function ()
{
expect(manager.getCamera('anything')).toBeNull();
});
it('should return the first camera when multiple cameras share a name', function ()
{
var cam1 = createMockCamera({ name: 'same' });
var cam2 = createMockCamera({ name: 'same' });
manager.cameras.push(cam1);
manager.cameras.push(cam2);
expect(manager.getCamera('same')).toBe(cam1);
});
});
describe('getCamerasBelowPointer', function ()
{
it('should return cameras that contain the pointer position', function ()
{
var cam = createMockCamera({ x: 0, y: 0, width: 800, height: 600 });
manager.cameras.push(cam);
var pointer = { x: 400, y: 300 };
var result = manager.getCamerasBelowPointer(pointer);
expect(result.length).toBe(1);
expect(result[0]).toBe(cam);
});
it('should return an empty array when pointer is outside all cameras', function ()
{
var cam = createMockCamera({ x: 0, y: 0, width: 400, height: 300 });
manager.cameras.push(cam);
var pointer = { x: 700, y: 500 };
var result = manager.getCamerasBelowPointer(pointer);
expect(result.length).toBe(0);
});
it('should not include invisible cameras', function ()
{
var cam = createMockCamera({ x: 0, y: 0, width: 800, height: 600, visible: false });
manager.cameras.push(cam);
var pointer = { x: 400, y: 300 };
var result = manager.getCamerasBelowPointer(pointer);
expect(result.length).toBe(0);
});
it('should not include cameras with inputEnabled false', function ()
{
var cam = createMockCamera({ x: 0, y: 0, width: 800, height: 600, inputEnabled: false });
manager.cameras.push(cam);
var pointer = { x: 400, y: 300 };
var result = manager.getCamerasBelowPointer(pointer);
expect(result.length).toBe(0);
});
it('should return top-most camera first (unshift order)', function ()
{
var cam1 = createMockCamera({ x: 0, y: 0, width: 800, height: 600 });
var cam2 = createMockCamera({ x: 0, y: 0, width: 800, height: 600 });
manager.cameras.push(cam1);
manager.cameras.push(cam2);
var pointer = { x: 400, y: 300 };
var result = manager.getCamerasBelowPointer(pointer);
expect(result.length).toBe(2);
expect(result[0]).toBe(cam2);
expect(result[1]).toBe(cam1);
});
it('should return multiple cameras that overlap the pointer', function ()
{
var cam1 = createMockCamera({ x: 0, y: 0, width: 800, height: 600 });
var cam2 = createMockCamera({ x: 100, y: 100, width: 400, height: 300 });
var cam3 = createMockCamera({ x: 600, y: 400, width: 200, height: 200 });
manager.cameras.push(cam1);
manager.cameras.push(cam2);
manager.cameras.push(cam3);
var pointer = { x: 200, y: 200 };
var result = manager.getCamerasBelowPointer(pointer);
expect(result.length).toBe(2);
});
});
describe('remove', function ()
{
it('should remove a single camera and return 1', function ()
{
var cam = createMockCamera();
manager.cameras.push(cam);
var total = manager.remove(cam);
expect(total).toBe(1);
expect(manager.cameras.length).toBe(0);
});
it('should remove an array of cameras and return the count', function ()
{
var cam1 = createMockCamera();
var cam2 = createMockCamera();
manager.cameras.push(cam1);
manager.cameras.push(cam2);
var total = manager.remove([ cam1, cam2 ]);
expect(total).toBe(2);
expect(manager.cameras.length).toBe(0);
});
it('should return 0 when camera is not in the manager', function ()
{
var cam = createMockCamera();
var outsideCam = createMockCamera();
manager.cameras.push(cam);
var total = manager.remove(outsideCam);
expect(total).toBe(0);
expect(manager.cameras.length).toBe(1);
});
it('should call destroy on the camera by default', function ()
{
var cam = createMockCamera();
manager.cameras.push(cam);
manager.remove(cam);
expect(cam.destroy).toHaveBeenCalledOnce();
});
it('should not call destroy when runDestroy is false', function ()
{
var cam = createMockCamera();
manager.cameras.push(cam);
manager.remove(cam, false);
expect(cam.destroy).not.toHaveBeenCalled();
});
it('should clear renderList instead of destroying when runDestroy is false', function ()
{
var cam = createMockCamera();
cam.renderList = [ {}, {}, {} ];
manager.cameras.push(cam);
manager.remove(cam, false);
expect(cam.renderList).toEqual([]);
});
it('should only remove cameras that exist in the manager from an array', function ()
{
var cam1 = createMockCamera();
var cam2 = createMockCamera();
var outsideCam = createMockCamera();
manager.cameras.push(cam1);
manager.cameras.push(cam2);
var total = manager.remove([ cam1, outsideCam ]);
expect(total).toBe(1);
expect(manager.cameras.length).toBe(1);
});
});
describe('getVisibleChildren', function ()
{
it('should return children that willRender returns true for', function ()
{
var cam = createMockCamera();
var child1 = { willRender: function () { return true; } };
var child2 = { willRender: function () { return false; } };
var child3 = { willRender: function () { return true; } };
var result = manager.getVisibleChildren([ child1, child2, child3 ], cam);
expect(result.length).toBe(2);
expect(result[0]).toBe(child1);
expect(result[1]).toBe(child3);
});
it('should return an empty array when no children pass willRender', function ()
{
var cam = createMockCamera();
var child1 = { willRender: function () { return false; } };
var result = manager.getVisibleChildren([ child1 ], cam);
expect(result.length).toBe(0);
});
it('should return all children when all pass willRender', function ()
{
var cam = createMockCamera();
var children = [
{ willRender: function () { return true; } },
{ willRender: function () { return true; } }
];
var result = manager.getVisibleChildren(children, cam);
expect(result.length).toBe(2);
});
it('should return an empty array when given an empty children array', function ()
{
var cam = createMockCamera();
var result = manager.getVisibleChildren([], cam);
expect(result.length).toBe(0);
});
it('should pass the camera to each child willRender call', function ()
{
var cam = createMockCamera();
var receivedCam;
var child = {
willRender: function (c)
{
receivedCam = c;
return true;
}
};
manager.getVisibleChildren([ child ], cam);
expect(receivedCam).toBe(cam);
});
});
describe('render', function ()
{
it('should call renderer.render for visible cameras with alpha > 0', function ()
{
var cam = createMockCamera({ visible: true, alpha: 1 });
manager.cameras.push(cam);
var visibleChild = { willRender: function () { return true; } };
var mockRenderer = { render: vi.fn() };
var mockDisplayList = { getChildren: vi.fn().mockReturnValue([ visibleChild ]) };
manager.render(mockRenderer, mockDisplayList);
expect(mockRenderer.render).toHaveBeenCalledOnce();
});
it('should not call renderer.render for invisible cameras', function ()
{
var cam = createMockCamera({ visible: false, alpha: 1 });
manager.cameras.push(cam);
var mockRenderer = { render: vi.fn() };
var mockDisplayList = { getChildren: vi.fn().mockReturnValue([]) };
manager.render(mockRenderer, mockDisplayList);
expect(mockRenderer.render).not.toHaveBeenCalled();
});
it('should not call renderer.render for cameras with alpha of 0', function ()
{
var cam = createMockCamera({ visible: true, alpha: 0 });
manager.cameras.push(cam);
var mockRenderer = { render: vi.fn() };
var mockDisplayList = { getChildren: vi.fn().mockReturnValue([]) };
manager.render(mockRenderer, mockDisplayList);
expect(mockRenderer.render).not.toHaveBeenCalled();
});
it('should call preRender on each visible camera', function ()
{
var cam = createMockCamera({ visible: true, alpha: 1 });
manager.cameras.push(cam);
var mockRenderer = { render: vi.fn() };
var mockDisplayList = { getChildren: vi.fn().mockReturnValue([]) };
manager.render(mockRenderer, mockDisplayList);
expect(cam.preRender).toHaveBeenCalledOnce();
});
it('should pass the scene and visible children to the renderer', function ()
{
var cam = createMockCamera({ visible: true, alpha: 1 });
manager.cameras.push(cam);
var child = { willRender: function () { return true; } };
var mockRenderer = { render: vi.fn() };
var mockDisplayList = { getChildren: vi.fn().mockReturnValue([ child ]) };
manager.render(mockRenderer, mockDisplayList);
expect(mockRenderer.render).toHaveBeenCalledWith(mockScene, [ child ], cam);
});
it('should call renderer.render once per visible camera', function ()
{
manager.cameras.push(createMockCamera({ visible: true, alpha: 1 }));
manager.cameras.push(createMockCamera({ visible: true, alpha: 1 }));
manager.cameras.push(createMockCamera({ visible: false, alpha: 1 }));
var mockRenderer = { render: vi.fn() };
var mockDisplayList = { getChildren: vi.fn().mockReturnValue([]) };
manager.render(mockRenderer, mockDisplayList);
expect(mockRenderer.render).toHaveBeenCalledTimes(2);
});
});
describe('resetAll', function ()
{
it('should clear all existing cameras', function ()
{
var cam1 = createMockCamera();
var cam2 = createMockCamera();
manager.cameras.push(cam1);
manager.cameras.push(cam2);
manager.resetAll();
expect(manager.cameras.length).toBe(1);
});
it('should destroy all existing cameras', function ()
{
var cam1 = createMockCamera();
var cam2 = createMockCamera();
manager.cameras.push(cam1);
manager.cameras.push(cam2);
manager.resetAll();
expect(cam1.destroy).toHaveBeenCalledOnce();
expect(cam2.destroy).toHaveBeenCalledOnce();
});
it('should set main to the new camera', function ()
{
var result = manager.resetAll();
expect(manager.main).toBe(result);
});
it('should return the new main camera', function ()
{
var result = manager.resetAll();
expect(result).toBeDefined();
expect(result).toBe(manager.cameras[0]);
});
});
describe('update', function ()
{
it('should call update on all cameras', function ()
{
var cam1 = createMockCamera();
var cam2 = createMockCamera();
manager.cameras.push(cam1);
manager.cameras.push(cam2);
manager.update(1000, 16);
expect(cam1.update).toHaveBeenCalledOnce();
expect(cam2.update).toHaveBeenCalledOnce();
});
it('should pass time and delta to each camera update', function ()
{
var cam = createMockCamera();
manager.cameras.push(cam);
manager.update(5000, 32);
expect(cam.update).toHaveBeenCalledWith(5000, 32);
});
it('should not throw when cameras array is empty', function ()
{
expect(function ()
{
manager.update(1000, 16);
}).not.toThrow();
});
});
describe('onResize', function ()
{
it('should resize cameras that are at 0,0 and match previous dimensions', function ()
{
var cam = createMockCamera({ x: 0, y: 0, width: 800, height: 600 });
manager.cameras.push(cam);
var gameSize = {};
var baseSize = { width: 1024, height: 768 };
manager.onResize(gameSize, baseSize, null, 800, 600);
expect(cam.setSize).toHaveBeenCalledWith(1024, 768);
});
it('should not resize cameras that are not at 0,0', function ()
{
var cam = createMockCamera({ x: 100, y: 0, width: 800, height: 600 });
manager.cameras.push(cam);
var gameSize = {};
var baseSize = { width: 1024, height: 768 };
manager.onResize(gameSize, baseSize, null, 800, 600);
expect(cam.setSize).not.toHaveBeenCalled();
});
it('should not resize cameras whose size does not match previousWidth/previousHeight', function ()
{
var cam = createMockCamera({ x: 0, y: 0, width: 400, height: 300 });
manager.cameras.push(cam);
var gameSize = {};
var baseSize = { width: 1024, height: 768 };
manager.onResize(gameSize, baseSize, null, 800, 600);
expect(cam.setSize).not.toHaveBeenCalled();
});
it('should resize multiple matching cameras', function ()
{
var cam1 = createMockCamera({ x: 0, y: 0, width: 800, height: 600 });
var cam2 = createMockCamera({ x: 0, y: 0, width: 800, height: 600 });
var cam3 = createMockCamera({ x: 50, y: 0, width: 800, height: 600 });
manager.cameras.push(cam1);
manager.cameras.push(cam2);
manager.cameras.push(cam3);
var gameSize = {};
var baseSize = { width: 1024, height: 768 };
manager.onResize(gameSize, baseSize, null, 800, 600);
expect(cam1.setSize).toHaveBeenCalledWith(1024, 768);
expect(cam2.setSize).toHaveBeenCalledWith(1024, 768);
expect(cam3.setSize).not.toHaveBeenCalled();
});
});
describe('resize', function ()
{
it('should call setSize on all cameras', function ()
{
var cam1 = createMockCamera();
var cam2 = createMockCamera();
manager.cameras.push(cam1);
manager.cameras.push(cam2);
manager.resize(1024, 768);
expect(cam1.setSize).toHaveBeenCalledWith(1024, 768);
expect(cam2.setSize).toHaveBeenCalledWith(1024, 768);
});
it('should not throw when cameras array is empty', function ()
{
expect(function ()
{
manager.resize(1024, 768);
}).not.toThrow();
});
it('should pass the correct dimensions to setSize', function ()
{
var cam = createMockCamera();
manager.cameras.push(cam);
manager.resize(320, 240);
expect(cam.setSize).toHaveBeenCalledWith(320, 240);
});
});
});