Sprite Animation Revisited


Last year I had blogged about animating sprite using Kinetic JS.
Code in that post was part of a simple game I had created. So the code specific for animating sprite was explained in snippets. A reader of that post contacted me with a request to provide a complete example. So I created a small demo of sprite animation only. If you are interested, you can download it from here. This demo animates images in the sprite sheet at a fixed location, it does not move the image. So I thought  it would be interesting to add motion to sprite images when they are animated.

First, take a look at the demo. Click on the Walk button to make the person walk. You can stop any time. If the person hits right side wall, then she falls, which is also simulated using sprite animation. I know the ‘walk’ does not look natural, but creating graphics is not my string point and this is the best I could manage.

Walking Speed : (Enter value from 1 to 10)

The above example contains two instances of sprite animation. One for walking and another to simulate fall. I used a single sprite sheet to contain images required for both these animations.

walking_person_sprite_sheet

 

The first four images are used for walking action and the next four are used to simulate fall action. I create a single JavaScript object with two animation keys (walk and fall).

Kinetic.Sprite object has a method afterFrame. You can specify a callback function to be invoked after certain number of frames are displayed. I thought I would use this function to increment X position of images, to move them along the X axis. However I found that this function is called only once. Looking at the source code of KineticJS confirmed this. In fact the callback function is removed from the object after invoking it once.

I then ended up using setInterval function to increment the X position. However I had to make sure that interval is set equal to what KineticJS uses to change frames. Sprite object has an attribute called frameRate, which is the number of frames displayed per second. So the value of interval I set (in setInterval function) is 1000 / frameRate. This values is specified in milli seconds, hence use of 1000.

You can download the complete demo code and images from here. And here is the source code, if you just want to browse it.

<!DOCTYPE html>

<html>
	<head>
		<script type="text/javascript" src="kinetic-v4.4.3.min.js"></script>

		<script type="text/JavaScript">

			var initX = 5,
				initY = 5,
				//Width and height of Kinetic Stage
				stageWidth = 500,
				stageHeight = 115,
				currX = initX,
				frameRate = 5,
				averageSpriteWidth = 42,
				averageFallSpriteWidth = 100,
				walking = false;

			//Positions of images in sprite
			walkingPersonAnimationInfo = {
			    walk :[
			    	{
				        x:4,
				        y:3,
				        width:42,
				        height:100
			    	},
			    	{
				        x:66,
				        y:3,
				        width:42,
				        height:100
			    	},
			    	{
				        x:128,
				        y:3,
				        width:42,
				        height:100
			    	},
			    	{
				        x:197,
				        y:3,
				        width:42,
				        height:100
			    	}

			    	],
			    fall :[
			    	{
				        x:258,
				        y:3,
				        width:42,
				        height:100
			    	},
				    {
				        x:314,
				        y:3,
				        width:68,
				        height:100
			    	},
			    	{
				        x:389,
				        y:3,
				        width:93,
				        height:100
			    	},
			    	{
				        x:500,
				        y:3,
				        width:99,
				        height:100
			    	}
			    ]
			};

			function onBodyLoad()
			{
				//Create Kinetic Stage
				stage = new Kinetic.Stage({
					container: "walkingStage",
					width:stageWidth,
					height:stageHeight
				});

				layer = new Kinetic.Layer();

				//create a border
				var border = new Kinetic.Rect({
					x:0,
					y:0,
					width: stage.getWidth()-2,
					height: stage.getHeight()-2,
					stroke:'black',
					strokeWidth:1
				});

				layer.add(border);

				loadStandingPersonImage();
				loadFallenPersonImage();
				loadSpriteImage();

				//Add layer to the stage
				stage.add(layer);			
			}

			function loadStandingPersonImage()
			{
				//Load Standing Person image
				standingImg = new Image();
				standingImg.onload = function (){
					kjsStandingImg = new Kinetic.Image({
						x:initX,
						y:initY,
						image:standingImg
					});
					layer.add(kjsStandingImg);
					layer.draw();
				}
				standingImg.src = "standing_person.png";
			}

			function loadFallenPersonImage()
			{
				//Load Fallen Person image
				fallenImg = new Image();
				fallenImg.onload = function (){
					kjsFallenImg = new Kinetic.Image({
						x:initX,
						y:initY,
						image:fallenImg
					});
					kjsFallenImg.hide();
					layer.add(kjsFallenImg);
				}
				fallenImg.src = "fallen_person.png";
			}

			function loadSpriteImage()
			{
				//Load sprite image
				spriteImg = new Image();
				spriteImg.onload= function()
				{
					kjsSprite = new Kinetic.Sprite({
	   					x:0,y:0,
	    				image:spriteImg,
	    				animation:"walk",
	    				animations:walkingPersonAnimationInfo,
	    				frameRate:frameRate
					});

					kjsSprite.hide();
					layer.add(kjsSprite);
				}
				spriteImg.src = "walking_person_sprite_sheet.png";
			}

			function onWalkBtnClick()
			{
				var btnElement = document.getElementById("walkBtn"),
					mode = btnElement.getAttribute("value");

				if (mode == "Walk")
				{
					mode = "Stop";
					startWalking(); 		
				} 
				else
				{
					mode = "Walk";
					stopWalking();	
				}

				document.getElementById("walkBtn").setAttribute("value",mode);
			}

			//Starts sprite animation			
			function startWalking()
			{
				var speedStr = document.getElementById("speedTxt").value;
				var speed = Number(speedStr);
				if (isNaN(speed) || speed > 10 || speed < 1)
				{
					speed = 5;
					document.getElementById("speedTxt").value = speed;
				}

				frameRate = speed;

				kjsFallenImg.hide();
				kjsStandingImg.hide();

				kjsSprite.setAnimation("walk");
				kjsSprite.setFrameRate(frameRate);

				//Move sprite image to the location of standing person image
				currX = kjsStandingImg.getX();
				kjsSprite.setX(currX);
				kjsSprite.setY(kjsStandingImg.getY());

				kjsSprite.show();

				var stageWidth = stage.getWidth(),
					stopWalking = false,
				 	walkInterval = setInterval(function(){
				 		if (!walking)
				 		{
				 			clearInterval(walkInterval);	
				 			return;
				 		}

						currX += 10;
						if (currX + averageSpriteWidth > stageWidth)
						{
							if (currX < stageWidth && !stopWalking)
							{
								currX = stageWidth - averageSpriteWidth;
								stopWalking = true;			
							}	 
							else
							{
					 			clearInterval(walkInterval);	
								startFalling();
								return;
							}		
						}					

						kjsSprite.setX(currX);
					}, 1000 / frameRate);

    			kjsSprite.start();
    			walking = true;
			}

			//Starting falling animation
			function startFalling()
			{
				walking = false;
				kjsSprite.setAnimation("fall");
				kjsSprite.setX(stage.getWidth()-averageFallSpriteWidth);

				kjsSprite.afterFrame(3,function(){
					kjsSprite.stop();
					kjsFallenImg.setX(stage.getWidth()-kjsFallenImg.getWidth()-4);
					kjsFallenImg.setY(stage.getHeight()-kjsFallenImg.getHeight()-4);
					kjsSprite.hide();
					kjsFallenImg.show();
					layer.draw();
					document.getElementById("walkBtn").setAttribute("value","Walk");
				});

			}

			function stopWalking()
			{
				walking = false;
				kjsSprite.stop();
				kjsSprite.hide();
				kjsFallenImg.hide();
				kjsStandingImg.show();
				layer.draw();
				document.getElementById("walkBtn").setAttribute("value","Walk");
			}

		</script>						
	</head>

	<bodyonload="onBodyLoad()">
		<divid="walkingStage"></div>
		<br>
		<div>
			Walking Speed : <inputtype="text"id="speedTxt"value="5"size="2"/> (Enter value from 1 to 10)<br>
			<inputtype="button"id="walkBtn"value="Walk"onclick="onWalkBtnClick()"/>
		</div>

	</body>
</html>

-Ram Kulkarni

 

This entry was posted in HTML5, JavaScript and tagged , , . Bookmark the permalink.

2 Responses to Sprite Animation Revisited

  1. M.G.Rajendra Prasad says:

    Dear Ram,

    I downloaded Small demo of sprite animation and It’s not working properly in my System.

Leave a Reply