HTML5游戏开发(十一)

HTML5游戏开发(十一)

一、精灵

  精灵是一种可以集成入动画之中的图形对象。并赋予它们各种行为。
精灵对象有两个方法:
paint()与update()。update()方法用于执行每个精灵的行为,执行顺序就是这些行为被加入精灵之中的顺序。paint()方法则将精灵绘制代理给绘制器来做,不过仅仅在精灵确实有绘制器并且可见是时,此方法才会生效。
Sprite构造器接受三个参数:精灵的名称,绘制器及行为数组。

1.精灵对象

/**
 * 创建精灵对象
 * @param {Object} name 名称
 * @param {Object} painter 绘制器
 * @param {Object} behaviors 对象行为
 */
var Sprite = function (name, painter, behaviors) {
   if (name !== undefined)      this.name = name;
   if (painter !== undefined)   this.painter = painter;
   if (behaviors !== undefined) this.behaviors = behaviors;

   return this;
};
//精灵绘制器
Sprite.prototype = {
   left: 0,//
   top: 0,//
   width: 10,//宽
   height: 10,//高
   velocityX: 0,//X速度
   velocityY: 0,//Y速度
   visible: true,//是否可见
   animating: false,//动画
   painter: undefined, //绘制器 paint(sprite, context)
   behaviors: [], //对象行为  execute(sprite, context, time)
    //绘制精灵
    paint: function (context) {
     if (this.painter !== undefined && this.visible) {
        this.painter.paint(this, context);
     }
    },
    //执行行为
   update: function (context, time) {
      //执行所有的行为方法
      for (var i = this.behaviors.length; i > 0; --i) {
         //调用对象的execute方法
         this.behaviors[i-1].execute(this, context, time);
      }
   }
};

2.精灵绘制器

(1)描边与填充绘制器
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>精灵示例</title>
        <style>
            body {
                background: #dddddd;
            }
            #canvas {
                position: absolute;
                left: 0px;
                top: 20px;
                margin: 20px;
                background: #ffffff;
                border: thin inset rgba(100, 150, 230, 0.5);
            }
        </style>
    </head>
    <body>
        <canvas id='canvas' width='300' height='300'>
      </canvas>
        <script type="text/javascript" src="js/Sprite.js"></script>
        <script type="text/javascript" src="js/clock.js"></script>
    </body>

</html>

JS脚本

var canvas = document.getElementById('canvas'),
    context = canvas.getContext('2d'),
    //时钟半径
    CLOCK_RADIUS = canvas.width / 2 - 15;

context.lineWidth = 0.5;
context.strokeStyle = 'rgba(0,0,0,0.2)';
context.shadowOffsetX = 2;
context.shadowOffsetY = 2;
context.shadowBlur = 4; 
context.stroke();

//--------------------------1、编写绘制器
//绘制器 
var ballPainter = {
    //绘制方法
    paint: function(sprite, context) {
        //精灵参数
        var x = sprite.left + sprite.width / 2,
            y = sprite.top + sprite.height / 2,
            width = sprite.width,
            height = sprite.height,
            radius = sprite.width / 2;

        context.save();
        context.beginPath();
        context.arc(x, y, radius, 0, Math.PI * 2, false);
        context.clip();
        //阴影
        context.shadowColor = 'rgb(0,0,0)';
        context.shadowOffsetX = -4;
        context.shadowOffsetY = -4;
        context.shadowBlur = 8;
        //填充    
        context.fillStyle = 'rgba(218, 165, 32, 0.1)';
        context.fill();
        //路径
        context.lineWidth = 2;
        context.strokeStyle = 'rgb(100,100,195)';
        context.stroke();

        context.restore();
    }
};

//---------------------------------2、使用精灵创建钟表
//创建精灵对象   
var ball = new Sprite('ball', ballPainter);

//制表面
function drawClockFace() {
    context.beginPath();
    context.arc(canvas.width / 2, canvas.height / 2,
        CLOCK_RADIUS, 0, Math.PI * 2, false);

    context.save();
    context.strokeStyle = 'rgba(0,0,0,0.2)';
    context.stroke();
    context.restore();
}

//绘制小球
function drawHand(loc, isHour) {
    //计算角度
    var angle = (Math.PI * 2) * (loc / 60) - Math.PI / 2,
        //半径
        handRadius = CLOCK_RADIUS,
        //结束点                   
        lineEnd = {
            x: canvas.width / 2 +
                Math.cos(angle) * (handRadius - ball.width / 2),

            y: canvas.height / 2 +
                Math.sin(angle) * (handRadius - ball.width / 2)
        };

    context.beginPath();
    //起始点,中心点
    context.moveTo(canvas.width / 2, canvas.height / 2);
    //圆周长上
    context.lineTo(lineEnd.x, lineEnd.y);
    context.stroke();

    //设置小球参数
    ball.left = canvas.width / 2 +
        Math.cos(angle) * handRadius - ball.width / 2;

    ball.top = canvas.height / 2 +
        Math.sin(angle) * handRadius - ball.height / 2;
    //调用绘制器方法
    ball.paint(context);
}

//小球处理
function drawHands() {
    //秒球
    var date = new Date(),
        hour = date.getHours();

    ball.width = 10;
    ball.height = 10;
    //大小为20的 指向秒的小球
    drawHand(date.getSeconds(), false);
    //分钟球
    hour = hour > 12 ? hour - 12 : hour;
    ball.width = 20;
    ball.height = 20;
    drawHand(date.getMinutes(), false);
    //小时球
    ball.width = 30;
    ball.height = 30;
    drawHand(hour * 5 + (date.getMinutes() / 60) * 5);

    //中心点
    ball.width = 10;
    ball.height = 10;
    ball.left = canvas.width / 2 - ball.width / 2;
    ball.top = canvas.height / 2 - ball.height / 2;
    ballPainter.paint(ball, context);
}

//绘制时钟
function drawClock() {
    //绘制时钟表面
    drawClockFace();
    //绘制时分秒
    drawHands();
}
//---------------------------------------3、创建动画
//动画
function animate() {
   //清除画布
   context.clearRect(0,0,canvas.width,canvas.height);
   //时钟绘制
   drawClock();
   //动画
   window.requestAnimationFrame(animate);
}

window.requestAnimationFrame(animate);

显示效果:
image

(2)图像绘制器
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>图像绘制器</title>
        <style>
            body {
                background: #eeeeee;
            }   
            #canvas {
                position: absolute;
                left: 0px;
                top: 20px;
                margin-left: 10px;
                background: lightskyblue;
                border: thin solid rbga(0, 0, 0, 1.0);
                box-shadow: rgba(0, 0, 0, 0.5) 4px 4px 6px;
            }
        </style>
    </head>
    <body>
        <canvas id='canvas' width='200' height='160'>
       </canvas>
        <script type="text/javascript" src="js/Sprite.js"></script>
        <script type="text/javascript" src="js/tree.js"></script>
    </body>
</html>
//图像绘制器
var ImagePainter = function (imageUrl) {
   this.image = new Image;
   this.image.src = imageUrl;
};

ImagePainter.prototype = {
   image: undefined,
   paint: function (sprite, context) {
      if (this.image !== undefined) {
        //complete 属性可返回浏览器是否已完成对图像的加载。
         if ( ! this.image.complete) {
            //加载图像
            this.image.onload = function (e) {
               sprite.width = this.width;
               sprite.height = this.height;
               //图像绘制
               context.drawImage(this,  
                  sprite.left, sprite.top,
                  sprite.width, sprite.height);
            };
         }
         else {
           context.drawImage(this.image, sprite.left, sprite.top,
                             sprite.width, sprite.height); 
         }
      }
   }
};
//图像加载
var canvas = document.getElementById('canvas'),
    context = canvas.getContext('2d'),
    tree = new Sprite('tree', new ImagePainter('img/smalltree.png')),

    TREE_LEFT = 10,
    TREE_TOP = 10,
    TREE_WIDTH = 180,
    TREE_HEIGHT = 130;

//绘制图像
function paint() {
   tree.paint(context);
}

//动画
function animate(now) {
   context.clearRect(0,0,canvas.width,canvas.height);
   paint();
   window.requestAnimationFrame(animate);
}

tree.left = TREE_LEFT;
tree.top = TREE_TOP;
tree.width = TREE_WIDTH;
tree.height = TREE_HEIGHT;

window.requestAnimationFrame(animate);

显示效果:
image

(3)精灵表绘制器

  为了节省磁盘空间,减收下载次数,如果用于制作动画的精灵其每帧所用的图像都比较小,那么就可以把它们都放在一张图中,包含动画每一帧图像的图片,就叫做精灵表。
精灵表创建:

//精灵表绘制器
SpriteSheetPainter = function (cells) {
   this.cells = cells;
};

SpriteSheetPainter.prototype = {
   cells: [],//存储表
   cellIndex: 0,
   //获取不同的精灵对象
   advance: function () {
      if (this.cellIndex == this.cells.length-1) {
         this.cellIndex = 0;
      }
      else {
         this.cellIndex++;
      }
   },
   //绘制精灵
   paint: function (sprite, context) {
      var cell = this.cells[this.cellIndex];
      context.drawImage(spritesheet, cell.left, cell.top,
                                     cell.width, cell.height,
                                     sprite.left, sprite.top,
                                     cell.width, cell.height);
   }
};

精灵表使用:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>精灵表绘制器</title>
        <style>
            body {
                background: #eeeeee;
            }

            #canvas {
                position: absolute;
                left: 0px;
                top: 35px;
                margin-left: 10px;
                background: "#99CC99";
                border: thin solid rbga(0, 0, 0, 1.0);
                box-shadow: rgba(0, 0, 0, 0.5) 4px 4px 6px;
            }
        </style>
    </head>
    <body>
        <input id='animateButton' type='button' value='开始' />
        <canvas id='canvas' width='256' height='256'>
       </canvas>

        <script type="text/javascript" src="js/Sprite.js"></script>
        <script type="text/javascript" src="js/spriteTable.js"></script>
    </body>
</html>

JS脚本:

var canvas = document.getElementById('canvas'),
    context = canvas.getContext('2d'),
    animateButton = document.getElementById('animateButton'),
    spritesheet = new Image(),
    //精灵表
    runnerCells = [{
        left: 0,
        top: 0,
        width: 256,
        height: 256
    }, {
        left: 256,
        top: 0,
        width: 256,
        height: 256
    }, {
        left: 512,
        top: 0,
        width: 256,
        height: 256
    }, {
        left: 768,
        top: 0,
        width: 256,
        height: 256
    }, {
        left: 1024,
        top: 0,
        width: 256,
        height: 256
    }, {
        left: 1280,
        top: 0,
        width: 256,
        height: 256
    }, {
        left: 1536,
        top: 0,
        width: 256,
        height: 256
    }, {
        left: 1792,
        top: 0,
        width: 256,
        height: 256
    }],
    //创建精灵对象
    sprite = new Sprite('runner', new SpriteSheetPainter(runnerCells)),
    lastAdvance = 0,
    paused = false,
    PAGEFLIP_INTERVAL = 100; //动画间的时间间隔

//暂停动画
function pauseAnimation() {
    animateButton.value = '开始';
    paused = true;
}
//开始动画
function startAnimation() {
    animateButton.value = '暂停';
    paused = false;
    lastAdvance = 0;
    window.requestAnimationFrame(animate);
}

//------------------------------事件处理
//动画开始按钮
animateButton.onclick = function(e) {
    if(animateButton.value === '开始') startAnimation();
    else pauseAnimation();
};

//动画
function animate(time) {
    if(!paused) {
        //清除画布
        context.clearRect(0, 0, canvas.width, canvas.height);
        drawBackground();
        //保存画布
        context.save();
        //绘制
        sprite.paint(context);
        if(time - lastAdvance > PAGEFLIP_INTERVAL) {
            //不断累加获取对象
            sprite.painter.advance();
            lastAdvance = time;
        }
        //恢复画布
        context.restore();
        window.requestAnimationFrame(animate);
    }
}

//-------------------------初始设置

function drawBackground() {
    //重绘背景
    context.fillStyle = "#99CC99";
    context.fillRect(0, 0, canvas.width, canvas.height);
}

spritesheet.src = 'img/sprite.png';
spritesheet.onload = function(e) {
    //加载图像
    context.drawImage(spritesheet, 0, 0);
};

drawBackground();
sprite.left = 0;
sprite.top = 0;

显示效果:
image

3.精灵对象的行为

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>精灵对象行为</title>
        <style>
            body {
                background: #eeeeee;
            }

            #canvas {
                position: absolute;
                left: 0px;
                top: 35px;
                margin-left: 10px;
                background: "#99CC99";
                border: thin solid rbga(0, 0, 0, 1.0);
                box-shadow: rgba(0, 0, 0, 0.5) 4px 4px 6px;
            }
        </style>
    </head>
    <body>
        <canvas id='canvas' width='256' height='256'>
      </canvas>
        <script type="text/javascript" src="js/Sprite.js"></script>
        <script type="text/javascript" src="js/behaviors.js"></script>
    </body>
</html>

JS脚本

var canvas = document.getElementById('canvas'),
    context = canvas.getContext('2d'),
    animateButton = document.getElementById('animateButton'),
    spritesheet = new Image(),
    //精灵表
    runnerCells = [{left: 0,top: 0, width: 256, height: 256 },
    {left: 256, top: 0, width: 256, height: 256 }, 
    {left: 512, top: 0, width: 256, height: 256 }, 
    {left: 768, top: 0, width: 256, height: 256 }, 
    {left: 1024,top: 0, width: 256, height: 256 }, 
    {left: 1280,top: 0, width: 256, height: 256 }, 
    {left: 1536,top: 0, width: 256, height: 256 }, 
    {left: 1792,top: 0, width: 256, height: 256 }];

//行为对象,原地行走
var runInPlace = {
   lastAdvance: 0,
   PAGEFLIP_INTERVAL: 100,//运行速度
    //执行器
   execute: function (sprite, context, time) {
      if (time - this.lastAdvance > this.PAGEFLIP_INTERVAL) {
         //精灵绘制
         sprite.painter.advance();
         this.lastAdvance = time;
      }
   }
};
//从右向左移动
moveLeftToRight = {
   lastMove: 0,
   //执行器
   execute: function (sprite, context, time) {
     if (this.lastMove !== 0) {
        //精灵的左坐标按像素减去
       sprite.left -= sprite.velocityX *
                      ((time - this.lastMove) / 1000); 
        //如果精灵left小于0时
       if (sprite.left < -(canvas.width/2).toFixed()) {
          //精灵出现位置
          sprite.left = canvas.width/2;
       }
     }
     this.lastMove = time;
   }
};
//创建精灵对象,并添加行为对象
var  sprite = new Sprite('runner',
                        new SpriteSheetPainter(runnerCells),
                        //一个方法为移动,一个方法为从右向左,组合而行成的动作
                        [ runInPlace,moveLeftToRight]);

function animate(time) {
   context.clearRect(0,0,canvas.width,canvas.height);
   drawBackground();
   //调用更新方法,用来运动
   sprite.update(context, time);
   //绘制精灵
   sprite.paint(context); 
   window.requestAnimationFrame(animate);
}

//---------------------------------初始设置
//绘制背景
function drawBackground() {
    //重绘背景
    context.fillStyle = "#99CC99";
    context.fillRect(0, 0, canvas.width, canvas.height);
}

spritesheet.src = 'img/sprite.png';
spritesheet.onload = function(e) {
   context.drawImage(spritesheet, 100, 0);
};

sprite.velocityX = 50;  //每秒移动像素为50
sprite.left = canvas.width/2; //精灵出现位置
sprite.top = 0;

window.requestAnimationFrame(animate);

显示效果:
iamge

4.动画制作器

//精灵动画器
var SpriteAnimator = function (painters, elapsedCallback) {
   this.painters = painters;
   if (elapsedCallback) {
      this.elapsedCallback = elapsedCallback;
   }
};
//精灵动画器
SpriteAnimator.prototype = {
   painters: [],
   duration: 1000,  //持续时间
   startTime: 0, //开始时间
   index: 0,
   elapsedCallback: undefined, //间隔回调
   //动画终止
   end: function (sprite, originalPainter) {
      sprite.animating = false;
      if (this.elapsedCallback) {
         this.elapsedCallback(sprite);
      }
      else {
         sprite.painter = originalPainter;
      }              
   },
   //动画开始
   start: function (sprite, duration) {
      var endTime = +new Date() + duration,
          period = duration / (this.painters.length),
          interval = undefined,
          animator = this, // for setInterval() function
          originalPainter = sprite.painter;

      this.index = 0;
      sprite.animating = true;
      sprite.painter = this.painters[this.index];
      //周期执行
      interval = setInterval(function() {
         if (+new Date() < endTime) {
            sprite.painter = animator.painters[++animator.index];
         }
         else {
            animator.end(sprite, originalPainter);
            clearInterval(interval);
         }
      }, period); 
   },
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值