Path: blob/main/public/games/files/algaes-escapade/js/lib/camera.js
1036 views
/**1* Represents the a camera, which is capable of panning around the world2*3* @author David North4*/5function camera( world )6{7//The maximum speed that the camera can move at8const MAX_VELOCITY = 300;910/**11* @var world The world that is being looked at12*/13var _world = world;1415/**16* @var boolean|gamejs.Rect Whether or not the camera is currently17* tracking an object. This variable contains the object being tracked if so.18*/19var _track = false;2021/**22* @var boolean Whether or not the camera is animating to position23*/24var _animating = false;2526/**27* @var gamejs.Rect The size and position of the viewport28* (what the player can see)29*/30var _viewport = new gamejs.Rect([0, 0], [0, 0]);3132/**33* Sets the width of the viewport34*35* @param float width36*37* @return camera38*/39this.setWidth = function( width ){40if ( typeof(width) != 'number' )41{42throw 'Width must be a number';43}4445_viewport.width = width;46return this;47}4849/**50* Sets the height of the viewport51*52* @param float height53*54* @return camera55*/56this.setHeight = function( height ){57if ( typeof(height) != 'number' )58{59throw 'Height must be a number';60}6162_viewport.height = height;63return this;64}6566/**67* Sets the new position of the camera. Sanatises the position so that it68* never looks outside of the world69*70* @param float x71* @param float y72*73* @return camera74*/75this.setPosition = function( x, y ){76if ( typeof(x) != 'number' )77{78throw 'X position must be a number';79}8081if ( typeof(y) != 'number' )82{83throw 'Y position must be a number';84}8586//Get the last position that the camera was at before being moved87var oldX = _viewport.x;88var oldY = _viewport.y;8990//Sanitise the position and set the camera91var newPosition = _getSanatisedPosition(x, y);92_viewport.x = newPosition.x;93_viewport.y = newPosition.y;9495//Update the objects in the world (i.e. shift them, the number of pixels96//the camera has 'moved', giving the impression of movement)97_updateObjects((_viewport.x - oldX), (_viewport.y - oldY));9899return this;100}101102/**103* Whether or not the camera is currently animating towards an object104*105* @return boolean106*/107this.isAnimating = function(){108return _animating;109}110111/**112* Focuses the camera on a rectangle, with the option to track it113* continuously and to animate the movement114*115* @param gamejs.Rect rect The rectangle to focus on116* @param boolean track Optional. Whether to track the object continuously117* @param boolean animate Optional. Whether to animate the camera or not118*119* @return camera120*/121this.focusOn = function( rect, track, animate ){122//Mmmmm.. type hinting123if ( !(rect instanceof gamejs.Rect) )124{125throw 'Rectangle must be an instance of gamejs.Rect';126}127128//If track is not defined, then set the default value to false.129//Otherwise, ensure that it's a boolean130if (typeof(track) === "undefined")131{132track = false;133}134else if ( typeof(track) !== 'boolean' )135{136throw 'Optional track flag must be a boolean';137}138139//If animate is not defined, then set the default value to false.140//Otherwise, ensure that it's a boolean141if (typeof(animate) === "undefined")142{143animate = false;144}145else if ( typeof(animate) !== 'boolean' )146{147throw 'Optional animate flag must be a boolean';148}149150//The new Camera position should have the middle of the camera pointing151//at the middle of the rectangle152var newCameraX = rect.center[0] - (_viewport.width / 2);153var newCameraY = rect.center[1] - (_viewport.height / 2);154155//If we are contantly tracking this object, then set that here156if ( track )157{158_track = rect;159}160else161{162_track = false;163}164165//If we are animating, don't move the camera (let the update method166//do it), otherwise set the position now167if ( animate )168{169_animating = true;170}171else172{173this.setPosition( newCameraX, newCameraY );174}175176return this;177}178179/**180* Updates the camera position if it is tracking and also moves the camera181* animation if animating182*183* @param int msDuration184*/185this.update = function( msDuration ){186if ( _track )187{188//Get the new X and Y c-ordinates, so that the camera is focused189//on the middle of the object190var destinationX = _track.center[0] - (_viewport.width / 2);191var destinationY = _track.center[1] - (_viewport.height / 2);192193destinationX += _viewport.x194destinationY += _viewport.y195196//If the tracking is animated then get the next frame, before197//setting the new position to that instead198if ( _animating )199{200var pos = _getNextAnimatedPosition(201destinationX, destinationY, msDuration202);203204destinationX = pos.x;205destinationY = pos.y;206}207208this.setPosition( destinationX, destinationY );209}210}211212/**213* Ensures that the camera is not intersecting the level (i.e. going over214* the bounding box). This is so that the camera is always focused on215* objects inside the level, not outside it216*217* @param float x The proposed X position218* @param float y The proposed Y position219*220* @return object An object containing the x and y position221*/222var _getSanatisedPosition = function( x, y ){223var position = { 'x': x, 'y': y };224var level = _world.getBoundingRect();225226//Set up the collision test object (essentially a copy of the cameras227//viewport, with the new x and y co-ordinates)228var collideTest = new gamejs.Rect(229[x, y], [_viewport.width, _viewport.height]230);231232//Set up the edges of trhe level to test collisions on233var rightEdge = [234[level.right, level.top],235[level.right, level.bottom]236];237238var leftEdge = [239[level.left, level.top],240[level.left, level.bottom]241];242243var topEdge = [244[level.left, level.top],245[level.right, level.top]246];247248var bottomEdge = [249[level.left, level.bottom],250[level.right, level.bottom]251];252253//Test the left and right edges, setting as appropriate254if ( collideTest.collideLine(rightEdge[0], rightEdge[1]) )255{256position['x'] = level.right - collideTest.width;257}258else if ( collideTest.collideLine(leftEdge[0], leftEdge[1]) )259{260position['x'] = level.left;261}262263//Test the top and bottom edges, setting as appropriate264if ( collideTest.collideLine( topEdge[0], topEdge[1]) )265{266position['y'] = level.top;267}268else if ( collideTest.collideLine(bottomEdge[0], bottomEdge[1]) )269{270position['y'] = (level.bottom - collideTest.height);271}272273return position;274}275276/**277* Gets the next frame for the camera animation278*279* @param float destinationX The target destination X position280* @param float destinationY Tha target destination Y position281* @param int msDuration The amount of time that has passed since the282* last frame283*284* @return object An object containing the new X and Y285*/286var _getNextAnimatedPosition = function(287destinationX, destinationY, msDuration288){289//Make sure that the new destination is not outside the world290var sanePosition = _getSanatisedPosition(destinationX, destinationY);291292var position = { 'x': sanePosition.x, 'y': sanePosition.y };293294var targetX = position.x;295var targetY = position.y;296var deltaX = _viewport.x - targetX;297var deltaY = _viewport.y - targetY;298var velocityX = velocityY = MAX_VELOCITY;299300//If the delta Y is zero, then the velocity is zero as the camera is301//not moving anywhere along the Y axis302if ( 0 === deltaY )303{304velocityY = 0;305}306307//If the delta X is zero, then the velocity is zero as the camera is308//not moving anywhere along the X axis309if ( 0 === deltaX )310{311velocityX = 0;312}313314//Find out if the difference on the X or Y axis is bigger and slow315//down the smaller of the two. This gives a nice diagonal effect so316//that the camera doesn't look like it's panning around trying to find317//the object318if ( Math.abs(deltaX) > Math.abs(deltaY) )319{320if ( 0 != deltaY )321{322velocityY *= (deltaX / deltaY);323}324}325else if ( Math.abs(deltaX) < Math.abs(deltaY) )326{327if ( 0 != deltaX )328{329velocityX *= (deltaX / deltaY);330}331}332333//A small delta X means that the camera needs to move to the right, and334//a large delta means to move it to the left335if ( deltaX < 0 )336{337position.x = _viewport.x + ( velocityX * (msDuration / 1000) );338}339else340{341position.x = _viewport.x - ( velocityX * (msDuration / 1000) );342}343344//A small delta X means that the camera needs to move down, and345//a large delta means to move it up346if ( deltaY < 0 )347{348position.y = _viewport.y + ( velocityY * (msDuration / 1000) );349}350else if ( deltaY < 0 )351{352position.y = _viewport.y - ( velocityY * (msDuration / 1000) );353}354355356//Check to see if the camera is close to the target. If it is, then move357//it to the target so that on the next frame it doesn't overshoot358if ( position.x <= (targetX + 5) && position.x >= (targetX - 5) )359{360position.x = targetX;361}362363if ( position.y <= (targetY + 5) && position.y >= (targetY - 5) )364{365position.y = targetY;366}367368//If the camera has reached it's target, then stop it animating369if ( targetX == position.x && targetY == position.y )370{371_animating = false;372}373374return position;375}376377/**378* Updates all objects with their new position. This gives the illusion that379* the camera has moved, when in reality it's all the objects that380* have moved381*382* @param float distanceX The distance the camera has travelled on the X383* @param float distanceY The distance the camera has travelled on the Y384*/385var _updateObjects = function( distanceX, distanceY ){386var objects = _world.getObjects();387for ( var i = 0; i < objects.length; i++)388{389objects[i].forEach(function(obj){390obj.rect.x -= distanceX;391obj.rect.y -= distanceY;392});393}394}395}396397