8.3 创建英雄和敌人的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章 创建镜像变换