API Docs for: 0.1.0
Show:

File: src/zerk/class/game/engine/system/sprite.js

/**
 * Sprite Systems
 * 
 * Renders sprites.
 * 
 * @class sprite
 * @namespace zerk.game.engine.system
 * @extends zerk.game.engine.system
 * @module zerk
 **/
zerk.define({
	
	name: 'zerk.game.engine.system.sprite',
	extend: 'zerk.game.engine.system'
	
},{
	
	/**
	 * Name of the system
	 * 
	 * @property _name
	 * @type String
	 * @protected
	 */
	_name: 'sprite',
	
	/**
	 * Thread that runs this system
	 * 
	 * @property _thread
	 * @type String
	 * @protected
	 **/
	_thread: 'render',
	
	/**
	 * Priority of this system
	 * 
	 * @property _priority
	 * @type Integer
	 * @protected
	 **/
	_priority: 101,
	
	/**
	 * Viewport system instance
	 * 
	 * @property _viewport
	 * @type zerk.game.engine.system.viewport
	 * @protected
	 **/
	_viewport: null,
	
	/**
	 * Class constructor
	 * 
	 * @method init
	 * @param {zerk.game.engine} engine Game engine
	 * @param {Object} config System configuration
	 **/
	init: function(engine,config) {
		
		zerk.parent('zerk.game.engine.system.sprite').init.apply(
			this,
			arguments
		);
		
		this._viewport=this._getSystem('viewport');
		
	},
	
	/**
	 * Returns true when the system is interested in given component
	 * 
	 * @method useComponent
	 * @param {String} name Component name
	 * @return {Boolean} True when the system is intereseted in given component
	 **/
	useComponent: function(name) {
		
		return (name=='sprite');
		
	},
	
	/**
	 * Starts the system
	 * 
	 * @method start
	 **/
	start: function() {
		
		zerk.parent('zerk.game.engine.system.sprite').start.apply(
			this,
			arguments
		);
		
	},
	
	/**
	 * Stops the system
	 * 
	 * @method stop
	 **/
	stop: function() {
		
		zerk.parent('zerk.game.engine.system.sprite').stop.apply(
			this,
			arguments
		);
		
	},
	
	/**
	 * Adds an entity to the system
	 * 
	 * @method addEntity
	 * @param {config.entity} entity Entity state
	 **/
	addEntity: function(entity) {
		
		zerk.parent('zerk.game.engine.system.sprite').addEntity.apply(
			this,
			arguments
		);
		
		/*
		var componentSprite=entity.components.sprite;
		
		if (componentSprite.width==0) {
			
			zerk.error({
				message: 'Sprite property "width" cannot be zero.'
					+'Setup sprite with in entity configuration.',
				source: this,
				entity: entity.name
			})
			
		}
		
		if (componentSprite.height==0) {
			
			zerk.error({
				message: 'Sprite property "height" cannot be zero.'
					+'Setup sprite height in entity configuration.',
				source: this,
				entity: entity.name
			})
			
		}
		*/
		
	},
	
	/**
	 * Updates the system
	 * 
	 * @method update
	 **/
	update: function() {
		
		zerk.parent('zerk.game.engine.system.sprite').update.apply(
			this,
			arguments
		);
		
		// Render all entities in the view area
		var entityStates=this._viewport.getEntitiesInViewport();
		
		for (var i=0;i<entityStates.length;i++) {
			
			// Check if this entity has the sprite component
			if (typeof entityStates[i].components.sprite=='undefined') {
				
				continue;
				
			}
			
			if (!entityStates[i].components.sprite.visible) continue;
			
			this._renderEntity(entityStates[i]);
		}
		
	},
	
	/**
	 * Renders an entity onto the game canvas
	 * 
	 * @method _renderEntity 
	 * @param {config.entity} entity Entity state
	 * @protected
	 **/
	_renderEntity: function(entity) {
		
		// Render all bodies of the entity
		
		var bodies=entity.components.physics._bodyList;
		
		for (var i=0;i<bodies.length;i++) {
			
			this._renderBody(entity,bodies[i]);
			
		}
		
	},
	
	/**
	 * Calculate buffer size of a body
	 * 
	 * @method __getBodyBufferSize
	 * @param {config.entity} entity Entity state
	 * @param {config.component.physics.body} body Body state
	 * @return {Object} An object containing width and height
	 * @protected
	 **/
	_getBodyBufferSize: function(entity,body) {
		
		// Check for primitive body
		if (zerk.objectCount(entity.bodies)>1) {
			
			// Use static configuration, we currently dont calculate that
			
			var sprite=entity.components.sprite;
			
			return {
				width: sprite.combinedWidth,
				height: sprite.combinedHeight
			};
			
		} else {
			
			// Get first fixture
			
			var fixture;
			
			for (var key in body.fixtures) {
				
				/*
				 * TODO Replace this with index access [0]
				 */
				fixture=body.fixtures[key];
				break;
				
			}
			
			return this._getFixtureBufferSize(entity,body,fixture);
		
		}
		
	},
	
	/**
	 * Calculate buffer size of a fixture
	 * 
	 * @method _getFixtureBufferSize
	 * @param {config.entity} entity Entity state
	 * @param {config.component.physics.body} body Body state
	 * @param {config.component.physics.fixture} fixture Fixture state
	 * @return {Object} An object containing width and height
	 * @protected
	 **/
	_getFixtureBufferSize: function(entity,body,fixture) {
		
		switch (fixture.shape) {
			
			case 'box':
				
				/*
				 * TODO Remove the possibility of making relative sized rectangle fixtures
				 */
				var width=((typeof fixture.width=='string') 
					? (body.width/100)*parseInt(fixture.width)
					: fixture.width);
				
				var height=((typeof fixture.height=='string') 
					? (body.height/100)*parseInt(fixture.height)
					: fixture.height);
				
				
				if (true) { // Rotateable
					
					/*
					 * TODO Check if ceil is really needed here
					 */
					var diagonal=Math.ceil(
						zerk.helper.fromMeter(
							Math.sqrt(
								Math.pow(width,2)
								+Math.pow(height,2)
							)
						)
					);
					
					return {
						width: diagonal,
						height: diagonal
					};
					
				} else {
					
					return {
						width: width,
						height: height
					};
					
				}
				
				break;
			
			case 'circle':
				
				var diagonal=Math.ceil(
					zerk.helper.fromMeter(fixture.radius*2)
				);
				
				return {
					width: diagonal,
					height: diagonal
				};
				
				break;
				
			case 'polygon':
				
				if (true) { // Rotateable
					
					var maxDistance=0;
					
					var center=zerk.helper.getCenterOfPolygon(fixture.vertices);
					
					for (var i=0;i<fixture.vertices.length;i++) {
						
						var distance=zerk.helper.calculateDistance(
							//0,
							//0,
							center.x,
							center.y,
							fixture.vertices[i][0],
							fixture.vertices[i][1]
						);
						
						if (distance>maxDistance) {
							
							maxDistance=distance;
							
						}
						
					}
					
					var diagonal=zerk.helper.fromMeter(maxDistance*2);
					
					return {
						width: diagonal,
						height: diagonal
					};
					
				} else {
					
					/*
					 * TODO Implement code for non rotatable polygons
					 */
					
				}
				
				break;
				
		}
		
	},
	
	/**
	 * Renders a body onto the game canvas
	 * 
	 * @method _renderBody
	 * @param {config.entity} entity Entity state
	 * @param {config.component.physics.body} body Body state
	 * @protected
	 **/
	_renderBody: function(entity,body) {
		
		var bodyState=entity.components.physics.bodies[body.key];
		var position=entity.components.position;
		
		var bufferSize=this._getBodyBufferSize(entity,bodyState);
		
		this._viewport.bufferInit(
			'body',
			bufferSize.width,
			bufferSize.height,
			bufferSize.width/2,
			bufferSize.height/2,
			bodyState.angle
		);
		
		// Render all the fixtures of the body
		for (var i=0;i<body._fixtureList.length;i++) {
			
			this._renderFixture(entity,body,body._fixtureList[i]);
			
		}
		
		// Draw the buffer onto the display
		this._viewport.bufferFlush(
			'body',
			'display',
			this._viewport._getCanvasX(position.x+bodyState.x,-(bufferSize.width/2)),
			this._viewport._getCanvasY(position.y+bodyState.y,-(bufferSize.height/2)),
			this._viewport.toScaleX(bufferSize.width),
			this._viewport.toScaleY(bufferSize.height)
		);
		
	},
	
	/**
	 * Renders a fixture onto the body buffer canvas
	 * 
	 * @method _renderFixture
	 * @param {config.entity} entity Entity state
	 * @param {config.component.physics.body} body Body state
	 * @param {config.component.physics.fixture} fixture Fixture state
	 * @protected
	 **/
	_renderFixture: function(entity,body,fixture) {
		
		if (fixture.isSensor) return;
		
		var bufferSize=this._getFixtureBufferSize(entity,body,fixture);
		
		this._viewport.bufferInit(
			'fixture',
			bufferSize.width,
			bufferSize.height,
			bufferSize.width/2,
			bufferSize.height/2,
			fixture.angle
		);
		
		// Render all sprites of the fixture
		
		for (var i=0;i<fixture.sprites.length;i++) {
			
			this._renderSprite(entity,body,fixture,fixture.sprites[i]);
			
		}
		
		this._renderFixtureBoundingShape(entity,body,fixture);
		
		
		this._viewport.bufferFlush(
			'fixture',
			'body',
			zerk.helper.fromMeter(fixture.x)-(bufferSize.width/2),
			zerk.helper.fromMeter(fixture.y)-(bufferSize.height/2),
			bufferSize.width,
			bufferSize.height
		);
		
	},
	
	/**
	 * Renders a shape indicating the bounding area of a fixture
	 * 
	 * Delegate method for the fixture bounding shape methods.
	 * 
	 * @method _renderFixtureBoundingShape
	 * @param {config.entity} entity Entity state
	 * @param {config.component.physics.body} body Body state
	 * @param {config.component.physics.fixture} fixture Fixture state
	 * @protected
	 **/
	_renderFixtureBoundingShape: function(entity,body,fixture) {
		
		switch (fixture.shape) {
			case 'box':
				this._renderFixtureBoundingBox(entity,body,fixture);
				break;
			case 'circle':
				this._renderFixtureBoundingCircle(entity,body,fixture);
				break;
			case 'polygon':
				this._renderFixtureBoundingPolygon(entity,body,fixture);
				break;
		}
		
	},
	
	/**
	 * Renders the bounding area for a box shaped fixture
	 * 
	 * @method _renderFixtureBoundingShape
	 * @param {config.entity} entity Entity state
	 * @param {config.component.physics.body} body Body state
	 * @param {config.component.physics.fixture} fixture Fixture state
	 * @protected
	 **/
	_renderFixtureBoundingBox: function(entity,body,fixture) {
		
		var width=((typeof fixture.width=='string') 
			? (body.width/100)*parseInt(fixture.width)
			: fixture.width);
		
		var height=((typeof fixture.height=='string') 
			? (body.height/100)*parseInt(fixture.height)
			: fixture.height);
			
		if (fixture.isSensor) {
			
			var style='rgb(255,0,0)';
			
		} else {
			
			var style='rgb(0,255,0)';
			
		}
		
		this._viewport.fillRect(
			'fixture',
			zerk.helper.fromMeter(-(width/2)),
			zerk.helper.fromMeter(-(height/2)),
			zerk.helper.fromMeter(width),
			zerk.helper.fromMeter(height),
			null
		);
		
	},
	
	/**
	 * Renders the bounding area for a circle shaped fixture
	 * 
	 * @method _renderFixtureBoundingCircle
	 * @param {config.entity} entity Entity state
	 * @param {config.component.physics.body} body Body state
	 * @param {config.component.physics.fixture} fixture Fixture state
	 * @protected
	 **/
	_renderFixtureBoundingCircle: function(entity,body,fixture) {
		
		this._viewport.fillArc(
			'body',
			0,
			0,
			zerk.helper.fromMeter(fixture.radius),
			0,
			Math.PI*2,
			true,
			null
		);
		
	},
	
	/**
	 * Renders the bounding area for a polygon shaped fixture
	 * 
	 * @method _renderFixtureBoundingPolygon
	 * @param {config.entity} entity Entity state
	 * @param {config.component.physics.body} body Body state
	 * @param {config.component.physics.fixture} fixture Fixture state
	 * @protected
	 **/
	_renderFixtureBoundingPolygon: function(entity,body,fixture) {
		
		var vertices=[];
		
		for (var i=0;i<fixture.vertices.length;i++) {
			
			vertices.push([
				zerk.helper.fromMeter(fixture.vertices[i][0]),
				zerk.helper.fromMeter(fixture.vertices[i][1])
			]);
			
		}
		
		this._viewport.fillPolygon(
			'fixture',
			vertices,
			null
		);
		
	},
	
	/**
	 * Render sprite
	 * 
	 * @method _renderSprite
	 * @param {config.entity} entity Entity state
	 * @param {config.component.physics.body} body Body state
	 * @param {config.component.physics.fixture} fixture Fixture state
	 * @param {config.component.sprite} sprite
	 * @protected
	 **/
	_renderSprite: function(
		entity,
		body,
		fixture,
		sprite
	) {
		
		/*
		 * TODO Create code for fixture oriented sprite rendering
		 */
		
	}
	
});