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); 
   },
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章