createjs进阶—sprite精灵图的使用(二)

  • 内容
  • 评论
  • 相关

今天这篇文章前端童鞋需要着重看,因为不会用到flash或者animateCC。

在讲之前大家先看一个demo:http://www.ajexoop.com/demo/spriteTest2/index2.html这个demo可不是死的动画,看下代码就可以知道,我只是在某个时间点让角色做出了动作,也就是如果加上控制器,你就可以控制屏幕里的角色,非常适用于游戏编程。

QQ图片20160714151239

先从简单的讲起,这个demo的制作。

首先准备素材,sprite图,这个图里面的人都是对称,也就是说可以不用flash做出来。woody_0然后就是代码了,先写好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后话:
一个月没更新,实在是因为最近忙,等空下来我会恢复持续发博文,谢谢大家支持!
 

1542183776360437.jpg

评论

0条评论

发表评论

电子邮件地址不会被公开。