createjs进阶—sprite精灵图的使用(二)
今天这篇文章前端童鞋需要着重看,因为不会用到flash或者animateCC。
在讲之前大家先看一个demo:http://www.ajexoop.com/demo/spriteTest2/index2.html这个demo可不是死的动画,看下代码就可以知道,我只是在某个时间点让角色做出了动作,也就是如果加上控制器,你就可以控制屏幕里的角色,非常适用于游戏编程。

先从简单的讲起,这个demo的制作。
首先准备素材,sprite图,这个图里面的人都是对称,也就是说可以不用flash做出来。
然后就是代码了,先写好sprite数据:
var spriteData = {
images: ["images/woody_0.png","images/woody_1.png","images/woody_2.png"],
frames: {width:80, height:80, regX: 40, regY:40},
animations: {
stand:[0,3,"stand",0.3],
walk:{
frames: [4,5,6,7,6,5],
next: "walk",
speed: 0.3
},
run:{
frames: [20,21,22,21],
next: "run",
speed: 0.3
},
somersault:{
frames: [58,59,69],
next: "stand",
speed: 0.3
},
attack1:[10,13,"stand",0.3],
attack2:[14,17,"stand",0.3],
attack3:{
frames: [8,9,19],
next: "stand",
speed: 0.3
},
jump:{
frames: [60,61,62],
next: "jumpSky",
speed: 0.3
},
jumpSky:{
frames: [62],
speed: 0.3
},
crouch:{
frames: [61],
next: "stand",
speed: 0.3
},
runJump:{
frames: [112],
speed: 0.3
}
}
};
大家可以从代码中看出来,代码里的images是这个sprite需要的素材,可以是多张,frames是截取的宽高,regX,regY是中心点,而animations是我给角色定义的一些动作,其中如果是数组,第一个参数代表起始帧,第二个参数代表结束帧,第三个参数代表结束后跳到哪个动作,第四个参数代表帧频。如果是对象那frames代表的是动作帧的顺序,next代表结束后跳转的动作,speed代表帧频。
接下来就是放在sprite中了,这个很简单:
var spriteSheet = new createjs.SpriteSheet(spriteData);
var sprite = new createjs.Sprite(spriteSheet,"stand");
container.addChild(sprite);
sprite.x = 200;
sprite.y = 200;
sprite.gotoAndPlay("run")
上面代码中sprite的第二个参数代表默认的动作,最后gotoAndPlay就是你要跳转的动作,如果做游戏的话,你就可以按下按钮后执行这个动作。
全部代码放出来:
var canvas,stage,container;
canvas = document.getElementById("mainView");
function init()
{
stage = new createjs.Stage(canvas);
createjs.Touch.enable(stage);
var loader = new createjs.LoadQueue(false);
loader.addEventListener("complete", loadCompleteHandler);
loader.loadManifest([
{src:"images/woody_0.png", id:"woody_0"},
{src:"images/woody_1.png", id:"woody_1"},
{src:"images/woody_2.png", id:"woody_2"}
]);
container = new createjs.Container();
stage.addChild(container);
createjs.Ticker.setFPS(40);
createjs.Ticker.addEventListener("tick", stageBreakHandler);
}
function loadCompleteHandler(event)
{
event.currentTarget.removeEventListener("complete",loadCompleteHandler);
var spriteData = {
images: ["images/woody_0.png","images/woody_1.png","images/woody_2.png"],
frames: {width:80, height:80, regX: 40, regY:40},
animations: {
stand:[0,3,"stand",0.3],
walk:{
frames: [4,5,6,7,6,5],
next: "walk",
speed: 0.3
},
run:{
frames: [20,21,22,21],
next: "run",
speed: 0.3
},
somersault:{
frames: [58,59,69],
next: "stand",
speed: 0.3
},
attack1:[10,13,"stand",0.3],
attack2:[14,17,"stand",0.3],
attack3:{
frames: [8,9,19],
next: "stand",
speed: 0.3
},
jump:{
frames: [60,61,62],
next: "jumpSky",
speed: 0.3
},
jumpSky:{
frames: [62],
speed: 0.3
},
crouch:{
frames: [61],
next: "stand",
speed: 0.3
},
runJump:{
frames: [112],
speed: 0.3
}
}
};
var spriteSheet = new createjs.SpriteSheet(spriteData);
var sprite = new createjs.Sprite(spriteSheet,"stand");
container.addChild(sprite);
sprite.x = 200;
sprite.y = 200;
sprite.gotoAndPlay("run")
}
function stageBreakHandler(event)
{
stage.update();
}
代码运行之后就是这样:http://www.ajexoop.com/demo/spriteTest2/index1.html这样我们的demo就完成了么,并没有。一个角色的代码就那么多,一个游戏会有多少角色?代码会有多少冗余?所以我们需要一个比较好的封装,这时候就需要用到OOP。
》》》》》》》》》分割线(下面的内容可能有点难)》》》》》》》》》》
首先我们要有这样一个思想,所有角色都是角色吧,他们有共同的特性吧,那我们就需要创建一个BasePeople类,让所有的游戏角色继承与他。不管什么游戏角色都有跑跳攻击吧,那基类BasePeople就需要拥有这些方法,那么下面我先写个基类:
/**
* 人物基类 所有人物角色均继承与此类
*/
//BasePeople
(function() {
function BasePeople(){
this.Container_constructor();
this.walkSpeedX = 2;
this.walkSpeedY = 0.5;
this.runSpeedX = 5;
this.runSpeedY = 0.5;
this.jumpHeight = 30;
this.runJumpHeight = 35;
this.arrow = "right";
this.setSpriteData();
}
var p = createjs.extend(BasePeople,createjs.Container);
p.setSpriteData = function (){
}
p.stand = function (){
this.animation.gotoAndPlay("stand");
};
p.move = function (x,y){
this.x +=x;
this.y +=y;
}
p.startWalk = function (sx,sy){
this.changeStop();
this.animation.gotoAndPlay("walk");
var a ;
if(sx > 0)
{
a = "right";
}
else if(sx < 0)
{
a = "left";
}
if(this.arrow != a) this.changeArrow(a);
this.sx = sx;
this.sy = sy;
var _this = this;
this.addEventListener("tick",this._walking = function (){_this.walking()})
}
p.walking = function (){
this.move(this.sx,this.sy)
}
p.stopWalk = function (){
this.animation.gotoAndPlay("stand");
this.removeEventListener("tick",this._walking)
}
p.startRun = function (sx,sy){
this.changeStop();
this.animation.gotoAndPlay("run");
var a ;
if(sx > 0)
{
a = "right";
}
else if(sx < 0)
{
a = "left";
}
if(this.arrow != a) this.changeArrow(a);
this.sx = sx;
this.sy = sy;
var _this = this;
this.addEventListener("tick",this._runing = function (){_this.runing()})
}
p.runing = function (){
this.move(this.sx,this.sy)
}
p.stopRun = function (){
this.animation.gotoAndPlay("stand");
this.removeEventListener("tick",this._runing)
}
p.startDecelerate = function (){
this.decelerateTime = 0;
this.changeStop();
this.animation.gotoAndPlay("somersault");
var _this = this;
this.addEventListener("tick",this._decelerateing = function (){_this.decelerateing()})
}
p.decelerateing = function (){
this.decelerateTime +=1;
this.sx = this.sx*0.95;
this.sy = this.sy*0.95;
this.move(this.sx,this.sy);
if( this.animation.currentFrame == 0)
{
this.stopDecelerate();
}
}
p.stopDecelerate = function (){
this.animation.gotoAndPlay("stand");
this.removeEventListener("tick",this._decelerateing)
}
p.changeArrow = function(arrow){
this.arrow = arrow;
if(arrow == "left")
{
this.animation.scaleX = Math.abs( this.animation.scaleX) * -1;
}
else
{
this.animation.scaleX = Math.abs( this.animation.scaleX);
}
}
p.startAttack = function (type){//攻击动作 1 2 是左勾拳和右勾拳随机出现 3是最后的浮空攻击
this.changeStop();
if(type == 3)
{
this.animation.gotoAndPlay("attack3");
}
else
{
if(Math.random() > 0.5)
{
this.animation.gotoAndPlay("attack1");
}
else
{
this.animation.gotoAndPlay("attack2");
}
}
this.removeEventListener("tick",this._attacking);//之后需要换成排队攻击
var _this = this;
this.addEventListener("tick",this._attacking = function (){_this.attacking()})
}
p.attacking = function (){
// this.move(-0.5,0);
if(this.animation.currentFrame == 0)
{
this.stopAttack();
}
}
p.stopAttack = function (){
this.animation.gotoAndPlay("stand");
this.removeEventListener("tick",this._attacking);
}
p.jump = function (){
this.animation.gotoAndPlay("jump");
this.jumpNum = this.jumpHeight;
this.jumpY = this.y;
var _this = this;
this.addEventListener("tick",this._jumping = function (){_this.jumping()})
}
p.jumping = function (){
var list = this.data.animations.jump.frames;
if( this.animation.currentFrame == list[list.length - 1])
{
this.jumpNum -=3;
this.move(0,-this.jumpNum);
if(this.y >= this.jumpY)
{
this.y = this.jumpY;
this.stopJump();
}
}
}
p.stopJump = function(){
this.removeEventListener("tick",this._jumping);
this.changeStop();
this.animation.gotoAndPlay("crouch");
}
p.runJump = function (){
this.animation.gotoAndPlay("runJump");
this.jumpNum = this.runJumpHeight;
this.jumpY = this.y;
var _this = this;
this.addEventListener("tick",this._runJumping = function (){_this.runJumping()})
}
p.runJumping = function (){
this.jumpNum -=4;
this.move(this.sx,-this.jumpNum);
var list = this.data.animations.runJump.frames;
if( this.animation.currentFrame == list[list.length - 1] && this.jumpNum < 20)//之后会改成有踢腿标识时开始踢
{
this.animation.gotoAndPlay("runJumpAttack");
}
if(this.y >= this.jumpY)
{
this.y = this.jumpY;
this.stopRunJump();
}
}
p.stopRunJump = function (){
this.removeEventListener("tick",this._runJumping);
this.changeStop();
this.animation.gotoAndPlay("crouch");
}
p.changeStop = function (){//因需要切换动作而停止当前的动作侦听
this.removeEventListener("tick",this._walking);
this.removeEventListener("tick",this._runing);
this.removeEventListener("tick",this._decelerateing);
}
cls.BasePeople = createjs.promote(BasePeople, "Container");
}());
我们分析下上面的代码:
首先人物的一些动作我写成了方法,一些数值我写成了属性,那我要任务做动作的时候就非常简单了,比如 李小龙.开始攻击() xxx.startAttack()就可以了,要判断数值也很简单,xxx.hp就可以了。但是大家看到我这里并没有对sprite动画的实现,因为每个人的动画都不一样,所以动画的实现要在子类中,好的现在来写个子类。
/**
* 角色Woody
*/
//Woody
(function() {
"use strict";
function Woody(){
this.BasePeople_constructor();
this.runSpeedX = 6;
}
var p = createjs.extend(Woody,cls.BasePeople);
p.setSpriteData = function (){
if(this.animation)
{
if(this.animation.parent)
{
this.animation.parent.removeChild(this.animation);
}
}
this.data = {
images: ["images/woody_0.png","images/woody_1.png","images/woody_2.png"],
frames: {width:80, height:80, regX: 40, regY:40},
animations: {
stand:[0,3,"stand",0.3],
walk:{
frames: [4,5,6,7,6,5],
next: "walk",
speed: 0.3
},
run:{
frames: [20,21,22,21],
next: "run",
speed: 0.3
},
somersault:{
frames: [58,59,69],
next: "stand",
speed: 0.3
},
attack1:[10,13,"stand",0.3],
attack2:[14,17,"stand",0.3],
attack3:{
frames: [8,9,19],
next: "stand",
speed: 0.3
},
jump:{
frames: [60,61,62],
next: "jumpSky",
speed: 0.3
},
jumpSky:{
frames: [62],
speed: 0.3
},
crouch:{
frames: [61],
next: "stand",
speed: 0.3
},
runJump:{
frames: [112],
speed: 0.3
},
runJumpAttack:{
frames: [107,108,109],
speed: 0.3
},
guiqizhan:{
frames: [140,141,142,143,144,145,146,147,148,149,150,151],
next: "stand",
speed: 0.3
}
}
};
this.spriteSheet = new createjs.SpriteSheet(this.data);
this.animation = new createjs.Sprite(this.spriteSheet, "stand");
this.addChild(this.animation);
}
p.startguiqizhan = function (){
this.changeStop();
this.animation.gotoAndPlay("guiqizhan");
var _this = this;
this.addEventListener("tick",this._guiqizhaning = function (){_this.guiqizhaning()});
}
p.guiqizhaning = function (){
if(this.animation.currentFrame == 144)
{
if(this.guiqizhan1 != 1)
{
var guiqizhan1 = new cls.Guiqizhan();
this.parent.addChild(guiqizhan1);
guiqizhan1.x = this.x + (this.arrow == "left"?-30:30);
guiqizhan1.y = this.y;
guiqizhan1.scaleX = Math.abs(guiqizhan1.scaleX) * (this.arrow == "left"?-1:1);
var num = (this.arrow == "left"?-10:10);
guiqizhan1.startRun(num,0.5);
this.guiqizhan1 = 1;
}
}
else if(this.animation.currentFrame == 147)
{
if(this.guiqizhan2 != 1)
{
var guiqizhan2 = new cls.Guiqizhan();
this.parent.addChild(guiqizhan2);
guiqizhan2.x = this.x + (this.arrow == "left"?-30:30);
guiqizhan2.y = this.y;
guiqizhan2.scaleX = Math.abs(guiqizhan2.scaleX) * (this.arrow == "left"?-1:1);
var num = (this.arrow == "left"?-10:10);
guiqizhan2.startRun(num,-0.5);
this.guiqizhan2 = 1
}
}
else if(this.animation.currentFrame == 151)
{
this.stopguiqizhan();
this.guiqizhan1 = 0;
this.guiqizhan2 = 0;
}
}
p.stopguiqizhan = function (){
this.animation.gotoAndPlay("stand");
this.removeEventListener("tick",this._guiqizhaning);
}
cls.Woody = createjs.promote(Woody, "BasePeople");
}());
大家可以看到,我在子类中实现了sprite。细心的同学会发现我还额外显示了一些方法,这个呢是人物的技能,因为每个角色的技能都不一样所以我就把他放在子类中实现,那人物有基类,技能有吗,答案是有的,但是和人物有点不一样,因为技能分弹道类的技能,范围类的技能,近战技能等等,所以每种类型的技能都要做一个基类,我这里展示下弹道类技能的代码。
首先是基类:
/**
* 弹道技能基类 所有弹道技能均继承与此类
*/
//Barrage
(function (){
"use strict";
function Barrage() {
this.Container_constructor();
this.arrow = "right";
this.setSpriteData();
}
var p = createjs.extend(Barrage,createjs.Container);
p.move = function (x,y){
this.x +=x;
this.y +=y;
}
p.setSpriteData = function (){
}
p.startRun = function (sx,sy){
this.animation.gotoAndPlay("run");
this.sx = sx;
this.sy = sy;
var _this = this;
this.addEventListener("tick",this._runing = function (){_this.runing()})
}
p.runing = function (){
this.move(this.sx,this.sy);
if(this.x > (1206 + 100)||this.x < -100||this.y < -100||this.y > 1206)
{
this.stopRun()
}
}
p.stopRun = function (){
this.removeEventListener("tick",this._runing)
if(this.parent)
{
this.parent.removeChild(this);
}
}
p.startHit = function (){
this.changeStop();
this.animation.gotoAndPlay("hit");
var _this = this;
this.addEventListener("tick",this._hitting = function (){_this.hitting()})
}
p.hitting = function (){
var list = this.data.animations.hit.frames;
if( this.animation.currentFrame == list[list.length - 1] )
{
this.stopHit();
}
}
p.stopHit = function (){
this.removeEventListener("tick",this._hitting);
if(this.parent)
{
this.parent.removeChild(this);
}
}
p.changeStop = function (){//因需要切换动作而停止当前的动作侦听
this.removeEventListener("tick",this._runing);
this.removeEventListener("tick",this._hitting)
}
cls.Barrage = createjs.promote(Barrage, "Container");
}())
然后是子类实现:
/**
* 技能鬼气斩
*/
//Guiqizhan
(function (){
"use strict";
function Guiqizhan() {
this.Barrage_constructor();
}
var p = createjs.extend(Guiqizhan,cls.Barrage);
p.setSpriteData = function () {
if (this.animation) {
if (this.animation.parent) {
this.animation.parent.removeChild(this.animation);
}
}
this.data = {
images: ["images/guiqizhan.png"],
frames: {width: 82, height: 83, regX: 41, regY: 41.5},
animations: {
run: [0, 3, "run", 0.3],
hit: [4, 7, "", 0.3],
run2: [8, 11, "run2", 0.3]
}
};
this.spriteSheet = new createjs.SpriteSheet(this.data);
this.animation = new createjs.Sprite(this.spriteSheet, "run");
this.addChild(this.animation);
}
p.run2 = function (sx,sy){
this.animation.gotoAndPlay("run2");
this.sx = sx;
this.sy = sy;
var _this = this;
this.addEventListener("tick",this._runing = function (){_this.runing()})
}
cls.Guiqizhan = createjs.promote(Guiqizhan, "Barrage");
}())
这样角色的一个技能就完成了,放在角色子类中实现,角色就可以使用技能。
当然一个真正的格斗游戏要做的还有很多,但是基本上都可以用OOP这种思维来完成,使代码更具有可读性,更容易维护,也少有有冗余的代码。今天讲的是sprite,游戏的其他方面知识就不赘述了。下面放出demo的链接:
百度网盘:http://pan.baidu.com/s/1bpA23iJ后话:
一个月没更新,实在是因为最近忙,等空下来我会恢复持续发博文,谢谢大家支持!

发表评论