html 游戏 精灵,HTML5游戏框架cnGameJS开发实录-精灵对象篇

返回目录

1.什么是精灵对象(sprite)?

所谓的精灵对象,就是游戏中的一个具有行为的元素,以超级玛丽为例,玛丽,敌人都算是一个精灵对象。在cnGameJS框架中,精灵对象如下几个特点:

1.添加动画:在之前的动画篇中,我们介绍过cnGameJS如何实现帧动画。而作为精灵对象,就是动画的使用者。例如我们控制玛丽向不同方向的行走,玛丽会产生行走的动画。

2.包含图像:对于另外一些精灵对象,它可能不需要运动动画,这时我们就可以只让它使用图像。

3.能进行不同类型的运动:可以让精灵对象向不同方向,以不同加速度进行移动。

2.demo展现

这里以一个简单的demo进行展现,我们通过鼠标控制玛丽的行动(匀加速运动),当玛丽停止时,使用图片。当玛丽移动时,使用动画,键盘左右方向键控制玛丽的移动。

效果:

fa480e6aadcfc1a21896d31d10de1272.png

代码:

请使用支持canvas的浏览器查看

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);

复制代码

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;

});

复制代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值