1. 程式人生 > >canvas動畫效果之星球守護

canvas動畫效果之星球守護

程式碼檔案

如題,我取了個比較炫酷的名字——星球守護。其實呢,也就是一個簡單的射擊小遊戲。作者並不是我,具體的效果可以去這裡體驗。我對程式碼做了部分修改與註釋,並且添加了爆炸音效,和遊戲結束音效。

ok!接下來我們就來一步一步的介紹這個遊戲完成過程。

1.檔案結構及靜態資源介紹

檔案結構簡單到令人髮指

--css                   //css檔案
--imgs                  //圖片檔案
--js                    //game.js檔案
--media                 //音訊檔案
--index.html            //主檔案

我們用到的圖片資源比較少,總共兩幅雪碧圖,我們現在來介紹第一幅。

這幅圖包括了我們遊戲中要用到的所有元素,星球,飛機, 子彈, 隕石,以及控制按鈕。另一幅,是爆炸的雪碧圖,我就不列出來了,把檔案下下來看看就好。

主檔案和樣式檔案都很簡單,在這我就不做過多介紹了,我們直接開始我們呢的遊戲流程。

2.遊戲流程分析

仔細觀察上面的動圖,我們大致的可以把遊戲分為三部分:開始前遊戲進行中遊戲結束

在開始前我們的頁面主要呈現:星球, 飛機, 和開始按鈕。此時當滑鼠在canvas畫布上滑動時,飛機是不會做出任何反應,因為此時遊戲還沒開始。唯一運動的東西是一個自轉的星球。

當我們點選了開始按鈕以後,遊戲開始。這時候

  1. 在左上角會有個record用於記錄遊戲的最高紀錄,如果你是第一次玩它就是0。

  2. 飛機會跟隨我們的滑鼠方向做旋轉(似成相識吧,是不是我們的滑鼠跟隨動畫)。

  3. 在星球的最中間,開始按鈕消失,中間的數字用於實時計算我們消滅的隕石數量。

  4. 從螢幕的四周會隨機的出現大小不一的隕石

  5. 點選滑鼠發射子彈,基於距離做碰撞檢測。如果發生碰撞,則畫爆炸的雪碧圖。

  6. 當隕石距離星球一定距離遊戲結束(這裡也是用基於距離的碰撞檢測)

遊戲結束這個介面主要有重新開始按鈕,本次遊戲總共消滅的隕石,和記錄。

3.初始化

首先把資原始檔載入進來,這裡我們為window物件添加了DOMContentLoaded事件,當內容完全載入完畢,執行game函式

(function(){
    window.addEventListener("DOMContentLoaded", game);

    //載入雪碧圖&&爆炸圖片
    var sprite = new Image();
    var spriteExplosion = new Image();

    window.onload = function(){
        sprite.src = "imgs/sprite.png";
        spriteExplosion.src = "imgs/explosion.png";
    }

    function game(){
     //一堆函式
    }
})()

在game函式中,我們先初始化一些變數

var canvas = doplaybackRate = document.getElementById('canvas'),
    ctx = canvas.getContext('2d');

var W = canvas.width = window.innerWidth,
    H = canvas.height = window.innerHeight;

var bullets   = [], //子彈
    asteroids = [], //隕石
    explosions = [], //爆炸
    destroyed = 0, //總的摧毀數
    record    = 0, //記錄
    count     = 0,//用於記錄遊戲中實時的摧毀數目
    playing   = false, //遊戲狀態
    gameover  = false, //用於判斷遊戲是否結束
    _planet   = {deg: 0}; //行星角度

//飛機初始位置
var player = {
            posX  : -35,
            posY  : -(100 + 82),
            width : 70,
            height: 79,
            deg   : 0
        };

變數初始化完成後,就開始我們真正的邏輯部分了,我們這裡先看一下在game中包括哪些函式。

//判斷滑鼠的點選位置,判斷遊戲是否開始
function action(e){...}

//畫星球
function planet(){...}

//畫飛船
function _player(){...}

//發射子彈
function fire(){...}

//飛船轉動角度
function move(e){...}

//初始化&&建立隕石
function newAsteroid(){...}

//隕石運動
function _asteroids(){...}

//爆炸
function explosion(asteroid){...}

//啟動函式
function start(){...}

//動畫迴圈
(function drawFrame(){
    window.requestAnimationFrame(drawFrame,canvas);
    start();
}())

4.畫星球

畫星球的函式非常簡單

function planet(){
            var rotateSpeed = 0.1; //星球的旋轉速度
            ctx.save();
            ctx.translate(W/2, H/2); //置於畫布中心
            
            //每一幀都加上rotateSpeed
            ctx.rotate((_planet.deg += rotateSpeed)*Math.PI/180);
            
            //畫雪碧圖中的星球
            ctx.drawImage(sprite, 0, 0, 200, 200, -100, -100, 200, 200);
            ctx.restore();
        }

這裡你要知道drawImage這個方法中的各項引數

draw(image,sx, sy, sw, sh, dx, dy, dw, dh)

sx,sy為物體在雪碧圖中的位置,sw,sh是在雪碧圖中的寬高,s可以理解為source(源)。
dx,dy為物體在canvas畫布中的位置, dw,dh是在canvas畫布中的寬高, d可以理解為destination(目的地),後面的其他圖形的繪製也都遵循這個方法。

5.畫飛機

function _player(){
            ctx.save();
            ctx.translate(W/2, H/2);
            ctx.rotate(player.deg);
            ctx.drawImage(sprite, 200, 0, player.width, player.height, player.posX, player.posY, player.width, player.height);
            ctx.restore();

            ...
}

在初始化的時候,我們設定了飛船的一些初始狀態,我們呼叫這些屬性,將其畫在canvas畫布中,這時候你可以把這兩個函式放在動畫迴圈中你可以看到星球與飛船已經畫出來了,但是,到現在為止還是不會對我們的滑鼠做出任何反應。下一步,我們就讓飛船可以跟隨我們的滑鼠做出相應的轉動。

6.互動邏輯


action()這個函式中,主要做的是整個canvas畫布與使用者的互動。通過變數playing來控制遊戲是否在進行。如果,palyingtrue,就是遊戲正在進行,那麼每點選一次我們就往bullets數組裡填充一顆子彈。如果,playingfalse, 就說明遊戲沒有在進行,那麼可能有兩種情況遊戲還沒開始,或是遊戲已經結束。

我們先設定一個變數dist,它的作用是判斷滑鼠的點選位置。仔細觀察遊戲你會發現,開始按鈕位於星球的中間,遊戲gameover後重新開始的圖示也位於星球的中心。

所以,在這裡要用到另一個變數gameover來判斷。如果gameoverfalse,那說明遊戲處於還沒開始的狀態。這時候通過判斷dist的值來確定是否點選了開始按鈕(注意:此時我們還沒有繪製開始按鈕),dist的值通過函式Math.sqrt(...)來計算,如果你看原始碼,你會發現我們直接使用的e.offsetX和e.offsetY來確定滑鼠點選的位於canvas畫布上的位置,這是因為我們將canvas畫布的寬高都設定為與window的寬高一樣,所以可以直接這麼寫。

如果gameovertrue表示遊戲已經結束,這時候之前canvas畫布上的監聽事件都需要移除。點選重新開始後gameover變為falsecavnas畫布重新新增監聽函式,所有的子彈,隕石等需要清空。

action函式作為canvas畫布的事件監聽函式,具體可以檢視程式碼。

//飛機轉動角度
function move(e){
    player.deg = Math.atan2(e.offsetX - (W/2), -(e.offsetY  - (H/2)));
}

飛機跟隨滑鼠轉動的函式如上,通過Math.atan2()來計算角度,它的呼叫在action()函式中,需要判斷當前遊戲所處的狀態動態的新增。

7,子彈發射

通過action函式,我們完成了遊戲與使用者的互動。當飛機可以跟隨滑鼠進行旋轉後,下一步就是點選滑鼠可以發射子彈。子彈的各項引數我們在遊戲開始後,通過點選滑鼠,push進了bullets陣列中。這時候我們只需要拿出來用就可以了。

注意看程式碼,我們發現在fire函式中,我們定義了一個變數distance,用於與隕石做碰撞檢測。子彈繪製的部分你看看程式碼就懂了。重點是在計運算元彈在運動中的實際位置,隕石與子彈間的碰撞就是基於隕石與子彈之間的距離。

我們先不管fire()函式中的碰撞檢測部分。此時點選滑鼠應該是可以發射子彈的了,那fire()函式應該在哪呼叫呢?我們把它放在_player()函式中,當滿足一定的條件時就執行fire()函式。

這樣基本上當我們再點選滑鼠時你就會發現,子彈從飛機裡發射出去。

8.隕石

隕石這一塊可以分為兩個部分:1.隕石的建立 2.隕石的運動。
在圖中,隕石是有大有小,並且是從各個角度向中間運動。但是我們的雪碧圖只提供了一種隕石圖片,所以它們的大小還有位置我們通過隨機數來獲取。然後,將其放進asteroids陣列中。這樣就完成了隕石的初始化。

隕石運動的部分在_asteroids()函式中,這個函式中也定義了一個distance變數它的作用是判斷隕石距離星球的距離。當distance小於某個值時,遊戲結束。

另外,為了維持隕石的在遊戲中的數目,我們這樓里加上了一個判定條件。

if(asteroids.length - destroyed < 10 + (Math.floor(destroyed/6))){
                newAsteroid();
            }

這樣只要有隕石被擊碎,就會重新生成一個隕石。這裡我們也要獲取隕石在遊戲中的實際座標,為的是判斷遊戲石佛結束。檢視程式碼你會發現,我們將子彈與隕石之間的碰撞放在了fire()函式, 將隕石與星球的碰撞(即判斷遊戲是否結束)放到了_asteroids()函式中。當然你也可以把兩個檢測全部放在_asteroids()中,不過並不建議這麼做。

9.子彈與隕石之間的碰撞檢測

現在,當你開始遊戲,點選滑鼠。你會發現子彈從飛機發射,隕石從四面八方飛過來。但是兩者相遇的時候並沒有發生碰撞。回到fire()函式中,我們為子彈與隕石做基於距離的碰撞檢測:

function fire(){
...
//碰撞檢測
for(var j=0; j<asteroids.length; j++){
    if(!asteroids[j].destroyed){
         distance = Math.sqrt(Math.pow(
             asteroids[j].realX - bullets[i].realX, 2) +
             Math.pow(asteroids[j].realY - bullets[i].realY, 2));
    };
if(distance < (((asteroids[j].width/asteroids[j].size) / 2) - 4) + ((19 / 2) - 4)){
         destroyed += 1;
         asteroids[j].destroyed = true; //隕石消失
         bullets[i].destroyed   = true; //子彈消失
         explosions.push(asteroids[j]); //繪製爆炸圖片
         expMusic.play(); //播放爆炸聲
}}


}

與我們前面講的一樣,先通過兩者的座標計算距離,然後通過距離判斷是否發生碰撞。如果發生碰撞就如註釋中的一樣,隕石消失,子彈消失,繪製爆炸圖片,播放爆炸聲。

10.爆炸圖片繪製

爆炸圖是一幅雪碧圖,我們需要在子彈與隕石發生碰撞的時候對其經行繪製。具體看函式explosion(asteroid),之所以傳入隕石的作為引數,是因為我們要確定繪製爆炸圖片的位置。

11.隕石雨星球的碰撞

隕石與相求的碰撞,就是判斷遊戲是否結束。我們把它放在了_asteroid()函式中:

。。。

 distance = Math.sqrt(Math.pow(asteroids[i].realX -  W/2, 2) + Math.pow(asteroids[i].realY - H/2, 2));
 if (distance < (((asteroids[i].width/asteroids[i].size) / 2) - 4) + 100) {

      gameoverMusic.play();//音訊播放
      gameover = true; //遊戲結束
      playing  = false; 
      canvas.addEventListener('mousemove', action);
 }
 
 。。。

一樣是基於距離的碰撞檢測

12.啟動函式

start()函式用於初始化星球,飛船等。然後,通過變數palying來繪製遊戲的分數記錄等,這些都是很簡單的東西,你只需要看看程式碼就懂了,這裡我就不多做介紹了。

13.新增音效

media檔案中我放了兩個音訊檔案,一個是爆炸音效,一個是遊戲結束音效。他們都在碰撞檢測的時候播放。唯一的問題是當滑鼠點選的過快時,由於音訊的播放需要在這段完成後再播放,所以你就會發現有的爆炸播放了聲音,有的沒有播放聲音。在程式碼中我採取了一個簡單的方案,減輕了這個效果,但是並沒有完全消除。

expMusic.playbackRate = 2.0; //音訊以2倍速度播放

相關推薦

canvas動畫效果星球守護

程式碼檔案 如題,我取了個比較炫酷的名字——星球守護。其實呢,也就是一個簡單的射擊小遊戲。作者並不是我,具體的效果可以去這裡體驗。我對程式碼做了部分修改與註釋,並且添加了爆炸音效,和遊戲結束音效。 ok!接下來我們就來一步一步的介紹這個遊戲完成過程。 1.檔

更新——Canvas畫布動畫效果實現倒計時

判斷 大神 radius 希望 日期 ~~ width radi 多少 Hello,大家好! 小W復活啦!繼續歡樂的給大家更博,輸送新知識~~ 不開玩笑啦!秒進正題~~~ 上次更博,小W給大家介紹了Canvas畫布的基礎部分,以及實現了一個由7*10點陣圖顯示的倒計時的基本

Android動畫效果Frame Animation(逐幀動畫

想要 顯示 star 載體 rop 復雜 ide sources post 前言: 上一篇介紹了Android的Tween Animation(補間動畫) Android動畫效果之Tween Animation(補間動畫),今天來總結下Android的另外一種動

jQuery實現加入購物車飛入動畫效果開發不停,填坑不止(起點位置在Y軸方向位置偏移)

開發時為了完成購物車的飛入拋物線,因為懶惰隨大流使用了fly.js外掛,用的時候遇到的兩個坑坑~~ 1. 有滾動條時,拋物體的起點位置在Y軸方向上有位置偏移,偏大 2. 頁面有滾動條時,拋物體的結束位置不一樣,偏大 我:(⊙o⊙)…煩煩的。。。這就是用別人東西的代價 。。。。。。 不

Android酷炫動畫效果進度載入動畫

package com.eftimoff.androipathview; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android

Android動畫效果自定義ViewGroup添加布局動畫

前言:      前面幾篇文章介紹了補間動畫、逐幀動畫、屬性動畫,大部分都是針對View來實現的動畫,那麼該如何為了一個ViewGroup新增動畫呢?今天結合自定義ViewGroup來學習一下佈局動畫。本文將通過對自定義圖片選擇控制元件設定動畫為例來學習佈局動畫。      其他幾種動畫效果:  自定義

Android酷炫動畫效果3D星體旋轉效果

  在Android中,如果想要實現3D動畫效果一般有兩種選擇:一是使用Open GL ES,二是使用Camera。Open GL ES使用起來太過複雜,一般是用於比較高階的3D特效或遊戲,並且這個也不是開源的,像比較簡單的一些3D效果,使用Camera就足夠了。   一些

Android實現動畫效果淡入淡出

QQ群:372135639 View漸隱動畫效果 /** * View漸隱動畫效果 */ public void setHideAnimation( View view, int duration) {

css動畫效果transition(動畫效果屬性)

狀態 ani rdquo 動效 無限 doctype sta name style <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8">

Canvas動畫基礎碰撞檢測

listener tran 重新 檢測 如何判斷 .com 功能 lca 節點 在Canvas中進行碰撞檢測,大家往往直接采用遊戲引擎(Cocos2d-JS、Egret)或物理引擎(Box2D)內置的碰撞檢測功能,好奇的你有思考過它們的內部運行機制嗎?下面將針對基本的碰撞檢

canvas動畫三 -- 黑客帝國文字掉落效果

今天要實現的效果是黑客帝國裡面的文字掉落效果,先來看一下圖 點選這裡,檢視demo 其實效果也是比較好實現的,只要計算出每個文字該出現的地方,然後繪製文字就可以了。 下面就來說具體的實現方法吧。 首先,新建頁面寫上canvas標籤,在js中獲取到並設

用css3和canvas實現的蜂窩動畫效果

image() all nim 自己 clas 函數 顯示 var 不兼容 近期工作時研究了一下css3動畫和js動畫。主要是工作中為了增強頁面的趣味性,大家都有意無意的加入了非常多動畫效果。當然大部分都是css3動畫效果。能夠gpu加速,這會降低移動端的性能需求。 今

[js高手路]html5 canvas動畫教程 - 自己動手做一個類似windows的畫圖軟件

箭頭 erb 寬度 pow(x type row center shape htm 這個繪圖工具,我還沒有做完,不過已經實現了總架構,以及常見的簡易圖形繪制功能: 1,可以繪制直線,圓,矩形,正多邊形【已完成】 2,填充顏色和描邊顏色的選擇【已完成】 3,描邊和填充功

two.js實現動畫效果

矢量圖 bsp 面向 tex get yellow text city ctype 一、什麽是two.js? Two.js 是面向現代 Web 瀏覽器的一個二維繪圖 API。Two.js 可以用於多個場合:SVG,Canvas 和 WebGL,旨在使平面形狀和動畫的創建更方

Windows程序設計簡單的動畫效果(小球彈一彈)

Windows程序設計之小球彈一彈構造一個矩形位圖,位圖內有一個帶有陰影和紫色間隙的小球,程序使用定時器來控制小球的動作,實際上是每當接收到定時器消息時將位圖通過BitBlt函數復制到客戶區,每當小球碰撞到客戶區上下左右四邊時就反彈回來。下面代碼有本人的理解註釋可供參考,本人才疏學淺,不妥請見諒。效果圖如下:

WPF編遊戲系列 動畫效果(2)

sed 其中 所有 wpf olt targe 針對 font bar 原文:WPF編遊戲系列 之七 動畫效果(2)       上一篇已經對關閉窗口圖標進行了動畫效果處理,本篇將對窗口界面的顯示和關閉效果進行處理

iOS動畫效果實戰篇CABasicAnimation的使用

最近正在研究iOS動畫效果的實現,目的也是為了自己能夠寫出比較炫酷的動畫效果。趁著專案不怎麼忙,抽出時間寫篇文章來記錄一下自己的學習成果及實戰效果。由於本人是最近才開始寫部落格,不善言辭,不喜勿噴,如果錯誤,還請指正。 本篇文章的動畫效果也是我在學習動畫效果的過程中其中一個頁面的動畫

canvas實現紅心飄飄的動畫效果

兩週前,專案裡需要實現一個紅心飄飄的點贊效果。抓耳撓腮了老半天,看了幾篇大佬的文章,終於算是摸了個七七八八。不禁長嘆一聲,還是菜啊。先來看一下效果:(傳送門進去點一波) 一、Bezier曲線運動軌跡 其實用大白話描述一下需求就是讓一個紅心圖片沿著貝塞爾曲線的軌跡走,然後邊走邊消失。核心在於得到貝塞爾曲線

打地鼠遊戲(3)動畫效果

rand pre 繼續 ood 擴展 child timeout cti java 在第一篇中,我們已經實現了單擊開始後遊戲的倒計時,那麽下面,我們需要繼續在Game這個簡直對對象中進行擴展屬性和方法: 首先我們需要在Game中添加幾組初始鍵值對: //所有的地鼠dom元

(三)高德地圖自定義縮放及縮放動畫效果

這一節主要實現的功能是地圖的自定義縮放及縮放的動畫效果,還是直接放上程式碼更直觀些,主要部位裡面基本有註解 還是老樣子,首先是新建activity_zoom_animate.xml佈局檔案 <?xml version="1.0" encoding="utf-8"?> <