HTML5 Canvas实战
8.3 创建英雄和敌人的Actor类
阅读(

路径和文本

图形及组合

处理图像和视频

画布变换

动画给画布带来生机

与画布交互:为图形和区域附加事件监听器

创建图表

通过游戏开发来拯救世界

创建英雄和敌人的精灵表

创建关卡图像和边界地图

创建英雄和敌人的Actor类

现在,我们已经创建好所有的主要图像,并且已经准备就绪,随着我们使用JavaScript 和 HTML5画布把虚拟世界变成现实,该是好玩的部分了(至少我是这么认为)。我们首先要做的就是创建Actor类,该类包含英雄和坏人都需要的属性和方法。换句话说,英雄和坏人都将是Actor类的实例。Actor类将负责使用moveRight() 和 moveLeft()方法指挥角色,并使用精灵表通过动画把角色渲染到画布上。

操作步骤

按照以下步骤,创建Actor类,该类可以用来实例化英雄或坏人:

1. 定义Actor构造函数:

/* Actor类应该对Level或HealthBar类一无所知,
 * 以便它们之间是解耦的
 */
function Actor(config){
  this.controller  = config.controller;
  this.normalSpriteSheet  = config.normalSpriteSheet; 
  this.hitSpriteSheet  = config.hitSpriteSheet; 
  this.x  = config.x;  // absolute x
  this.y  = config.y;  // absolute y
  this.playerSpeed  = config.playerSpeed;  // px  / s this.motions  = config.motions;
  this.startMotion  = config.startMotion;
  this.facingRight  = config.facingRight; 
  this.moving  = config.moving;
  this.spriteInterval  = config.spriteInterval;  // ms this.maxHealth  = config.maxHealth;
  this.attackRange  = config.attackRange;
  this.minAttackInterval  = config.minAttackInterval;
  this.SPRITE_SIZE  =  144;
  this.FADE_RATE  =  1;  // full fade in  1s
  this.spriteSheet = this.normalSpriteSheet; 
  this.vx  =  0;
  this.vy  =  0;
  this.spriteSeq  =  0;
  this.motion  = this.startMotion; 
  this.lastMotion  = this.motion; 
  this.airborne  = false;
  this.attacking  = false;
  this.canAttack  = true;
  this.health  = this.maxHealth; 
  this.alive  = true;
  this.opacity  =  1;
  this.timeSinceLastSpriteFrame  =  0;
}

2. 定义attack()方法,该方法触发一次攻击:

Actor.prototype.attack  = function(){
  this.attacking  = true;
  this.canAttack  = false; 
  var that  = this;
  setTimeout(function(){
    that.canAttack  = true;
  }, this.minAttackInterval);
};

3. 定义stop()方法,该方法阻止角色移动:

 Actor.prototype.stop  = function(){
  this.moving  = false;
};

4. 定义isFacingRight()方法:

Actor.prototype.isFacingRight  = function(){
    return this.facingRight;
};

5. 定义moveRight()方法:

Actor.prototype.moveRight  = function(){
  this.moving  = true;
  this.facingRight  = true;
};

6. 定义moveLeft()方法:

Actor.prototype.moveLeft  = function(){
  this.moving  = true;
  this.facingRight  = false;
};

7. 定义jump()方法,该方法触发角色跳跃:

 Actor.prototype.jump  = function(){
  if  (!this.airborne)  {
    this.airborne  = true; this.vy  =  -1;
  }
};

8. 定义draw ()方法:

Actor.prototype.draw  = function(pos){
  var context  = this.controller.view.context;
  var sourceX  = this.spriteSeq  * this.SPRITE_SIZE;
  var sourceY  = this.motion.index  * this.SPRITE_SIZE;
  context.save();
  context.translate(pos.x, pos.y);
  if  (this.facingRight)  {
    context.translate(this.SPRITE_SIZE,  0); context.scale(-1,  1);
  }
  context.globalAlpha  = this.opacity;
  context.drawImage(this.spriteSheet, sourceX, sourceY, this. SPRITE_SIZE, this.SPRITE_SIZE,  0,  0, this.SPRITE_SIZE, this.
  SPRITE_SIZE);
  context.restore();
};

9. 定义fade()方法,当某角色被击败时,该方法使其淡出:

  Actor.prototype.fade  = function(){
  var opacityChange  = this.controller.anim.getTimeInterval()  * this.FADE_RATE  /  1000;
  this.opacity  -= opacityChange;
  if  (this.opacity  <  0)  {
       this.opacity  =  0;
  }
};

10. 定义updateSpriteMotion()方法:

Actor.prototype.updateSpriteMotion  = function(){
  // 如果攻击完成,设置attacking  = false
  if  (this.attacking && this.spriteSeq  == this.motion.numSprites -  1)  {
    this.attacking  = false;
  }
  if  (this.attacking)  {
    this.motion  = this.motions.ATTACKING;
  }
  else  {
    if  (this.airborne)  {
      this.motion  = this.motions.AIRBORNE;
    }
    else  {
      this.vy  =  0;
      if  (this.moving)  {
        this.motion  = this.motions.RUNNING;
      }
      else  {
        this.motion  = this.motions.STANDING;
      }
    }
  }
};

11. 定义updateSpriteSeqNum ()方法,该方法在每个精灵间隔中增加或重置精灵序列号:

Actor.prototype.updateSpriteSeqNum  = function()  {
  var anim  = this.controller.anim;
  this.timeSinceLastSpriteFrame  += anim.getTimeInterval();
  if  (this.timeSinceLastSpriteFrame  > this.spriteInterval)  {
    if  (this.spriteSeq  < this.motion.numSprites  -  1)  {
      this.spriteSeq++;
    }
    else  {
      if  (this.motion.loop)  {
        this.spriteSeq  =  0; 
      }
    }
    this.timeSinceLastSpriteFrame  =  0;
  }
  if  (this.motion  != this.lastMotion)  {
    this.spriteSeq  =  0;
    this.lastMotion  = this.motion;
  }
};

12. 定义damage()方法,该方法减少角色的生命值,并把精灵表设置为被击中的精灵表,使角色瞬间闪白光:

Actor.prototype.damage  = function(){
  this.health  = this.health  <=  0  ?  0  : this.health  -  1;
  this.spriteSheet  = this.hitSpriteSheet;
  var that  = this;
  setTimeout(function(){
    that.spriteSheet  = that.normalSpriteSheet; },  200);
};

13. 定义getCenter ()方法,该方法返回角色中心位置的坐标:

Actor.prototype.getCenter  = function(){
  return  {
    x: Math.round(this.x)  + this.SPRITE_SIZE  /  2,
    y: Math.round(this.y)  + this.SPRITE_SIZE  /  2
 };
};

工作原理

Actor类的思想是,创建一个既可实例化英雄也可实例化坏人的类。它包含控制角色的方法,如moveRight(),moveLeft(),jump(),和attack(),游戏引擎和人类玩家都可以调用这些方法。游戏引擎使用这些方法来控制坏人,而人类玩家通过键盘按键来调用这些方法控制英雄。

除了控制外,Actor类也通过updateSpriteMotion()方法更新精灵动作来管理精灵动画,并通过updateSpriteSeqNum()方法来增加或循环精灵的序号。

最后,draw()方法根据角色的动作挑选精灵图像,如果角色是朝右的,则在水平方向上翻转图像,然后调用画布上下文的drawImage()方法把角色绘制到屏幕上。

更多参考

  • 第3章 裁剪图像
  • 第4章 平移画布
  • 第4章 创建镜像变换

如果本教程对您帮助很大,请随意打赏。您的支持,将鼓励我们提供更好的教程!

← 键盘方向键翻页 →
返回顶部 手机访问 关注微信 返回底部

扫码访问歪脖网

随时随地,想看就看

关注歪脖网微信

分享 web 知识、交流 web 经验