返回目录
1.什么是精灵对象(sprite)?
所谓的精灵对象,就是游戏中的一个具有行为的元素,以超级玛丽为例,玛丽,敌人都算是一个精灵对象。在cnGameJS框架中,精灵对象如下几个特点:
1.添加动画:在之前的动画篇中,我们介绍过cnGameJS如何实现帧动画。而作为精灵对象,就是动画的使用者。例如我们控制玛丽向不同方向的行走,玛丽会产生行走的动画。
2.包含图像:对于另外一些精灵对象,它可能不需要运动动画,这时我们就可以只让它使用图像。
3.能进行不同类型的运动:可以让精灵对象向不同方向,以不同加速度进行移动。
2.demo展现
这里以一个简单的demo进行展现,我们通过鼠标控制玛丽的行动(匀加速运动),当玛丽停止时,使用图片。当玛丽移动时,使用动画,键盘左右方向键控制玛丽的移动。
效果:
代码:
<body> <div><canvas id="gameCanvas">请使用支持canvas的浏览器查看</canvas></div> </body> <script src="http://files.cnblogs.com/Cson/cnGame_v1.0.js"></script> <script> var Src="http://images.cnblogs.com/cnblogs_com/Cson/290336/o_player.png"; /* 初始化 */ cnGame.init('gameCanvas',{width:300,height:150}); var floorY=cnGame.height-40; var gameObj=(function(){ /* 玩家对象 */ var player=function(options){ this.init(options); this.speedX=0; this.moveDir; this.isJump=false; } cnGame.core.inherit(player,cnGame.Sprite); player.prototype.initialize=function(){ this.addAnimation(new cnGame.SpriteSheet("playerRight",Src,{frameSize:[50,60],loop:true,width:150,height:60})); this.addAnimation(new cnGame.SpriteSheet("playerLeft",Src,{frameSize:[50,60],loop:true,width:150,height:120,beginY:60})); } player.prototype.moveRight=function(){ if(cnGame.core.isUndefined(this.moveDir)||this.moveDir!="right"){ this.moveDir="right"; this.speedX<0&&(this.speedX=0); this.setMovement({aX:10,maxSpeedX:15}); this.setCurrentAnimation("playerRight"); } } player.prototype.moveLeft=function(){ if(cnGame.core.isUndefined(this.moveDir)||this.moveDir!="left"){ this.moveDir="left"; this.speedX>0&&(this.speedX=0); this.setMovement({aX:-10,maxSpeedX:15}); this.setCurrentAnimation("playerLeft"); } } player.prototype.stopMove=function(){ if(this.speedX<0){ this.setCurrentImage(Src,0,60); } else if(this.speedX>0){ this.setCurrentImage(Src); } this.moveDir=undefined; this.resetMovement(); } player.prototype.update=function(){ player.prototype.parent.prototype.update.call(this);//调用父类update if(cnGame.input.isPressed("right")){ this.moveRight(); } else if(cnGame.input.isPressed("left")){ this.moveLeft(); } else{ this.stopMove(); } } return { initialize:function(){ cnGame.input.preventDefault(["left","right","up","down"]); this.player=new player({src:Src,width:50,height:60,x:0,y:floorY-60}); this.player.initialize(); }, update:function(){ this.player.update(); }, draw:function(){ this.player.draw(); } }; })(); cnGame.loader.start([Src],gameObj); </script> 复制代码
3.实现
和动画篇spriteSheet对象一样,sprite对象同样划分三个阶段:初始化,更新,绘制。
首先看sprite的初始化函数:
/** *初始化 **/ init:function(options){ /** *默认对象 **/ var defaultObj={ x:0, y:0, imgX:0, imgY:0, width:32, height:32, angle:0, speedX:0, speedY:0, aX:0, aY:0, maxSpeedX:postive_infinity, maxSpeedY:postive_infinity, maxX:postive_infinity, maxY:postive_infinity, minX:-postive_infinity, minY:-postive_infinity }; options=options||{}; options=cg.core.extend(defaultObj,options); this.x=options.x; this.y=options.y; this.angle=options.angle; this.width=options.width; this.height=options.height; this.angle=options.angle; this.speedX=options.speedX; this.speedY=options.speedY; this.aX=options.aX; this.aY=options.aY; this.maxSpeedX=options.maxSpeedX; this.maxSpeedY=options.maxSpeedY; this.maxX=options.maxX; this.maxY=options.maxY; this.minX=options.minX; this.minY=options.minY; this.spriteSheetList={}; if(options.src){ //传入图片路径 this.setCurrentImage(options.src,options.imgX,options.imgY); } else if(options.spriteSheet){//传入spriteSheet对象 this.addAnimation(options.spriteSheet); setCurrentAnimation(options.spriteSheet); } }
参数很多,主要包括:对象位置,旋转角度,尺寸,xy方向的速度,xy方向的加速度,xy方向的最大速度。另外如果用户传入图片地址,则设置当前sprite对象使用图片,否则使用spriteSheet动画。
先看看sprite对象如何使用图像:
/** *设置当前显示图像 **/ setCurrentImage:function(src,imgX,imgY){ if(!this.isCurrentImage(src,imgX,imgY)){ imgX=imgX||0; imgY=imgY||0; this.image=cg.loader.loadedImgs[src]; this.imgX=imgX; this.imgY=imgY; this.spriteSheet=undefined; } },
首先检测现在是否正在使用该图像,如果不是,则从loader里获取下载好的image对象(所有图像资源在游戏开始时已下载好,),并且设置spriteSheet为undefined(表示不使用spriteSheet动画),这样sprite对象就可以使用图像了。
再看看如何使用动画:
/** *设置当前显示动画 **/ setCurrentAnimation:function(id){//可传入id或spriteSheet if(!this.isCurrentAnimation(id)){ if(cg.core.isString(id)){ this.spriteSheet=this.spriteSheetList[id]; this.image=this.imgX=this.imgY=undefined; } else if(cg.core.isObject(id)){ this.spriteSheet=id; this.addAnimation(id); this.image=this.imgX=this.imgY=undefined; } } }, 复制代码
首先根据传入的spriteSheet或spriteSheet的id判断是否正在使用该动画,如果不是,则设置sprite使用spriteSheet动画。
设置好sprite对象使用动画后,核心函数update就负责调用spriteSheet的update,更新sprite使用的动画,需要注意的是使spriteSheet的xy为sprite的xy:
if(this.spriteSheet){//更新spriteSheet动画 this.spriteSheet.x=this.x this.spriteSheet.y=this.y; this.spriteSheet.update(); }
这样就完成的sprite对象动画的展示。
最后看看如何实现最后一个特点:使sprite能进行变速的运动。
要进行变速运动,我们需要确立如下几个变量:初始速度,经过的时间,以及加速度。现在看cnGameJS负责变速运动的部分:
/** *设置移动参数 **/ setMovement:function(options){ isUndefined=cg.core.isUndefined; isUndefined(options.speedX)?this.speedX=this.speedX:this.speedX=options.speedX; isUndefined(options.speedY)?this.speedY=this.speedY:this.speedY=options.speedY; isUndefined(options.aX)?this.aX=this.aX:this.aX=options.aX; isUndefined(options.aY)?this.aY=this.aY:this.aY=options.aY; isUndefined(options.maxX)?this.maxX=this.maxX:this.maxX=options.maxX; isUndefined(options.maxY)?this.maxY=this.maxY:this.maxY=options.maxY; isUndefined(options.minX)?this.minX=this.minX:this.minX=options.minX; isUndefined(options.minY)?this.minY=this.minY:this.minY=options.minY; if(this.aX!=0){ this.startTimeX=new Date().getTime(); this.oriSpeedX=this.speedX; isUndefined(options.maxSpeedX)?this.maxSpeedX=this.maxSpeedX:this.maxSpeedX=options.maxSpeedX; } if(this.aY!=0){ this.startTimeY=new Date().getTime(); this.oriSpeedY=this.speedY; isUndefined(options.maxSpeedY)?this.maxSpeedY=this.maxSpeedY:this.maxSpeedY=options.maxSpeedY; } }
每次用户调用setMovement,就保留sprite的初速度,和运动开始的时间。这样在每次update的时候,就可以根据前面两个变量,获取到sprite现时的速度,并计算现时的XY方向上的位移:
if(this.aX!=0){ var now=new Date().getTime(); var durationX=now-this.startTimeX; var speedX=this.oriSpeedX+this.aX*durationX/1000; if(this.maxSpeedX<0){ this.maxSpeedX*=-1; } if(speedX<0){ this.speedX=Math.max(speedX,this.maxSpeedX*-1) ; } else{ this.speedX=Math.min(speedX,this.maxSpeedX); } } if(this.aY!=0){ var now=new Date().getTime(); var durationY=now-this.startTimeY; this.speedY=this.oriSpeedY+this.aY*durationY/1000; } this.move(this.speedX,this.speedY); 复制代码
当update更新了sprite的位移,就可以通过第三个阶段draw方法,把sprite绘制出来。
/** *绘制出sprite **/ draw:function(){ var context=cg.context; if(this.spriteSheet){ this.spriteSheet.x=this.x this.spriteSheet.y=this.y; this.spriteSheet.draw(); } else if(this.image){ context.save() context.translate(this.x, this.y); context.rotate(this.angle * Math.PI / 180); context.drawImage(this.image,this.imgX,this.imgY,this.width,this.height,0,0,this.width,this.height); context.restore(); } },
注意sprite在使用spriteSheet动画或在使用图像,draw方法执行都不相同。当使用sprieSheet动画时,draw方法实质上是调用了spriteSheet的draw方法绘制帧图像,而当sprite使用图像,我们还可以让图像旋转之后再绘制。
sprite对象还提供一个getRect方法,返回包含该sprite对象的矩形。该方法为检测sprite对象和其他对象的碰撞带来方便。有关碰撞检测请看:HTML5游戏框架cnGameJS开发实录(碰撞检测篇)
另外sprite对象同样具有move,moveTo,resize,resizeTo等功能,方便对该对象的位置和尺寸进行操控。
sprite对象所有源码:
/** * *sprite对象 * **/ cnGame.register("cnGame",function(cg){ var postive_infinity=Number.POSITIVE_INFINITY; var sprite=function(id,options){ if(!(this instanceof arguments.callee)){ return new arguments.callee(id,options); } this.init(id,options); } sprite.prototype={ /** *初始化 **/ init:function(options){ /** *默认对象 **/ var defaultObj={ x:0, y:0, imgX:0, imgY:0, width:32, height:32, angle:0, speedX:0, speedY:0, aX:0, aY:0, maxSpeedX:postive_infinity, maxSpeedY:postive_infinity, maxX:postive_infinity, maxY:postive_infinity, minX:-postive_infinity, minY:-postive_infinity }; options=options||{}; options=cg.core.extend(defaultObj,options); this.x=options.x; this.y=options.y; this.angle=options.angle; this.width=options.width; this.height=options.height; this.angle=options.angle; this.speedX=options.speedX; this.speedY=options.speedY; this.aX=options.aX; this.aY=options.aY; this.maxSpeedX=options.maxSpeedX; this.maxSpeedY=options.maxSpeedY; this.maxX=options.maxX; this.maxY=options.maxY; this.minX=options.minX; this.minY=options.minY; this.spriteSheetList={}; if(options.src){ //传入图片路径 this.setCurrentImage(options.src,options.imgX,options.imgY); } else if(options.spriteSheet){//传入spriteSheet对象 this.addAnimation(options.spriteSheet); setCurrentAnimation(options.spriteSheet); } }, /** *返回包含该sprite的矩形对象 **/ getRect:function(){ return new cg.shape.Rect({x:this.x,y:this.y,width:this.width,height:this.height}); }, /** *添加动画 **/ addAnimation:function(spriteSheet){ this.spriteSheetList[spriteSheet.id]=spriteSheet; }, /** *设置当前显示动画 **/ setCurrentAnimation:function(id){//可传入id或spriteSheet if(!this.isCurrentAnimation(id)){ if(cg.core.isString(id)){ this.spriteSheet=this.spriteSheetList[id]; this.image=this.imgX=this.imgY=undefined; } else if(cg.core.isObject(id)){ this.spriteSheet=id; this.addAnimation(id); this.image=this.imgX=this.imgY=undefined; } } }, /** *判断当前动画是否为该id的动画 **/ isCurrentAnimation:function(id){ if(cg.core.isString(id)){ return (this.spriteSheet&&this.spriteSheet.id===id); } else if(cg.core.isObject(id)){ return this.spriteSheet===id; } }, /** *设置当前显示图像 **/ setCurrentImage:function(src,imgX,imgY){ if(!this.isCurrentImage(src,imgX,imgY)){ imgX=imgX||0; imgY=imgY||0; this.image=cg.loader.loadedImgs[src]; this.imgX=imgX; this.imgY=imgY; this.spriteSheet=undefined; } }, /** *判断当前图像是否为该src的图像 **/ isCurrentImage:function(src,imgX,imgY){ imgX=imgX||0; imgY=imgY||0; var image=this.image; if(cg.core.isString(src)){ return (image&&image.srcPath===src&&this.imgX===imgX&&this.imgY===imgY); } }, /** *设置移动参数 **/ setMovement:function(options){ isUndefined=cg.core.isUndefined; isUndefined(options.speedX)?this.speedX=this.speedX:this.speedX=options.speedX; isUndefined(options.speedY)?this.speedY=this.speedY:this.speedY=options.speedY; isUndefined(options.aX)?this.aX=this.aX:this.aX=options.aX; isUndefined(options.aY)?this.aY=this.aY:this.aY=options.aY; isUndefined(options.maxX)?this.maxX=this.maxX:this.maxX=options.maxX; isUndefined(options.maxY)?this.maxY=this.maxY:this.maxY=options.maxY; isUndefined(options.minX)?this.minX=this.minX:this.minX=options.minX; isUndefined(options.minY)?this.minY=this.minY:this.minY=options.minY; if(this.aX!=0){ this.startTimeX=new Date().getTime(); this.oriSpeedX=this.speedX; isUndefined(options.maxSpeedX)?this.maxSpeedX=this.maxSpeedX:this.maxSpeedX=options.maxSpeedX; } if(this.aY!=0){ this.startTimeY=new Date().getTime(); this.oriSpeedY=this.speedY; isUndefined(options.maxSpeedY)?this.maxSpeedY=this.maxSpeedY:this.maxSpeedY=options.maxSpeedY; } }, /** *重置移动参数回到初始值 **/ resetMovement:function(){ this.speedX=0; this.speedY=0; this.aX=0; this.aY=0; this.maxSpeedX=postive_infinity; this.maxSpeedY=postive_infinity; this.maxX=postive_infinity; this.minX=-postive_infinity; this.maxY=postive_infinity; this.minY=-postive_infinity; }, /** *更新位置和帧动画 **/ update:function(){ if(this.aX!=0){ var now=new Date().getTime(); var durationX=now-this.startTimeX; var speedX=this.oriSpeedX+this.aX*durationX/1000; if(this.maxSpeedX<0){ this.maxSpeedX*=-1; } if(speedX<0){ this.speedX=Math.max(speedX,this.maxSpeedX*-1) ; } else{ this.speedX=Math.min(speedX,this.maxSpeedX); } } if(this.aY!=0){ var now=new Date().getTime(); var durationY=now-this.startTimeY; this.speedY=this.oriSpeedY+this.aY*durationY/1000; } this.move(this.speedX,this.speedY); if(this.spriteSheet){//更新spriteSheet动画 this.spriteSheet.x=this.x this.spriteSheet.y=this.y; this.spriteSheet.update(); } }, /** *绘制出sprite **/ draw:function(){ var context=cg.context; if(this.spriteSheet){ this.spriteSheet.x=this.x this.spriteSheet.y=this.y; this.spriteSheet.draw(); } else if(this.image){ context.save() context.translate(this.x, this.y); context.rotate(this.angle * Math.PI / 180); context.drawImage(this.image,this.imgX,this.imgY,this.width,this.height,0,0,this.width,this.height); context.restore(); } }, /** *移动一定距离 **/ move:function(dx,dy){ dx=dx||0; dy=dy||0; var x=this.x+dx; var y=this.y+dy; this.x=Math.min(Math.max(this.minX,x),this.maxX); this.y=Math.min(Math.max(this.minY,y),this.maxY); return this; }, /** *移动到某处 **/ moveTo:function(x,y){ this.x=Math.min(Math.max(this.minX,x),this.maxX); this.y=Math.min(Math.max(this.minY,y),this.maxY); return this; }, /** *旋转一定角度 **/ rotate:function(da){ this.angle+=da; return this; }, /** *旋转到一定角度 **/ rotateTo:function(){ this.angle=da; return this; }, /** *改变一定尺寸 **/ resize:function(dw,dh){ this.width+=dw; this.height+=dh; return this; }, /** *改变到一定尺寸 **/ resizeTo:function(width,height){ this.width=width; this.height=height; return this; } } this.Sprite=sprite; }); 复制代码
Atas ialah kandungan terperinci HTML5游戏框架cnGameJS开发实录-精灵对象篇. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!