基於jQuery的一個「射日」小遊戲

寫在最前

本次的分享是一個基於jQuery實現的一個移動端射箭類小遊戲。主要實現了目標物、障礙物的隨機渲染,以及中箭效果的斷定等。
歡迎關注個人博客,不按期更新中——css

效果預覽

game.gif
game.gif

點我查看源碼倉庫git

主要結構規劃

...
//基礎屬性
defaultOption = {}

//繪製總體畫面
function drawGame(defaultOption){}

//障礙物、目標物類
function Hinder(){} 
Hinder.prototype.xxx
function Target(){} 
Target.prototype.xxx

//循環渲染 
function eventLoopHinder(){} 
function eventLoopTarget(){} 

//過時去除障礙物、目標物
function clearHinder(){} 
function clearTarget(){} 

//觸摸射箭事件監聽
function touchEvent(){}
...複製代碼

經過以上的結構劃分及效果圖的展現咱們能夠大概瞭解到,這個遊戲主要涉及的三個較爲關鍵的地方就是:github

  • 目標物的渲染
  • 障礙物的飛行
  • 定時清除過時對象
  • 中箭效果的斷定
  • 箭射到對象身上

故接下來做者會介紹下實現思路,至於具體細節有興趣的同窗能夠在issues下交流。web

目標物的渲染

對於目標物做者用了下面的類來表示:canvas

//如下代碼只做爲例子說明,與源碼有較大刪改
//爲了方便表示 random 爲一個假想的隨機值
function Target(id, nowTime, width, score, x, y, time) {
    this.id = id //id即惟一標示
    this.nowTime = nowTime //當前時間
    this.score = score //表明分數
    this.width = width || random // 可隨機一個寬度
    this.x = x || random //可隨機一個x座標
    this.y = y || random //可隨機一個y座標
    this.time = time || random //可隨機一個過時時間
  }複製代碼

在上面的示例代碼中咱們能夠知道,經過new Target()能夠獲得一個位置隨機,大小隨機,過時時間等等均爲隨機的一個目標物。固然這個隨機做者在代碼中作過限定,都是在一個特定的範圍內隨機出來的。那麼在這期間惟一須要咱們控制的,應該就是出現的位置了,爲何這麼說呢,由於出現的位置不該該重合。而若是隻憑這個類中的屬性來部署目標物,勢必會各類重合。數組

粗略判斷目標物的重合

簡單看下實現過程:bash

//targetArray爲存放目標物對象的數組,每次new出實例後均會放入該數組
Target.prototype.draw = function(newTarget) {
    var img = new Image(),
    ...
    img.onload = function() {
    ...
        targetArray.forEach(function(obj, index) {
            var x = obj.x - newTarget.x,
                y = obj.y - newTarget.y,
                dis = Math.sqrt(x*x + y*y)
            if(dis < newTarget.width / 2 + obj.width / 2) {
              isOk = false //那麼這個對象就不要渲染了
            }
            if(isOk) {
              //渲染該對象到dom
            }
        })
        ...
      }
    }
var newTarget = new Target(id, nowTime, width, scoreTarget)
newTarget.draw(newTarget)複製代碼

當new出一個目標物後,在調用draw方法時將其傳入到方法中。以後遍歷存放目標物對象的數組,暫且將目標物想象成圓,經過兩圓圓心距離與半徑之和的比來斷定要不要將其插入dom節點進行展現。若是童鞋們想要更精確的檢測「碰撞」的方式,能夠將遊戲在canvas畫布中進行實現。canvas中getImageData方法能夠獲取圖像的像素點,經過兩對象其像素點是否重合能夠更爲精準的判斷。dom

障礙物的飛行

關於障礙物的飛行採用了css中的transition來進行控制:oop

function setTransition(property, timing, speed) {
    return {
        'transition-property': property,
        '-moz-transition-property': property, 
        '-webkit-transition-property': property, 
        '-o-transition-property': property, 
        'transition-timing-function': timing,
        '-moz-transition-timing-function': timing, 
        '-webkit-transition-timing-function': timing, 
        '-o-transition-timing-function': timing, 
        'transition-duration': speed + 's',
        '-moz-transition-duration': speed + 's', 
        '-webkit-transition-duration': speed + 's', 
        '-o-transition-duration': speed + 's', 
    }
  }
  Hinder.prototype.draw = function() {
    var img = new Image(),
    ...
    img.onload = function() {
        ...
        $(img).css(setTransition('left', 'linear', this.speed))
        ...
    }
  }複製代碼

經過一個較通用的方法返回一個可配置的transition動畫對象,在障礙物類的原型方法中動態改變其css樣式,其中動畫的持續時間設定爲障礙物的速度屬性。這樣來達到每一個新生成的障礙物有着不一樣的飛行速度。動畫

定時清除過時對象

因爲整個遊戲會一直跑循環來快速的生成新的目標和障礙物。同時會維護一個存儲目標和障礙物對象的數組,當有需求如碰撞檢測時須要遍歷整個數組。那麼這麼操做就意味着咱們的數組不能太大否則會極大地佔用內存。同時當障礙物移出屏幕後理應清理掉這個節點,釋放內存。故做者用兩個定時器來跑兩個檢測對象過時的方法:

function clearTarget() { //清理過時目標
    var nowTime = new Date().getTime()
    targetArray.forEach(function(item, index) {
      if(item.time + item.now < nowTime) { 
      //當生成對象時間+持續時間 > 當前時間即爲過時
        $('#' + item.id).remove()
        targetArray.splice(index, 1)
      }
    })
    targetTimer = setTimeout(clearTarget, xxx)
}
function clearHinder() { //清理過時障礙
    hinderArray.forEach(function(item, index) {
      if(item.shot) {
        if('障礙物移出屏幕') {
          $('#' + item.id).remove()
          hinderArray.splice(index, 1)
        }
      }
    })
    setTimeout(clearHinder, xxx)
}複製代碼

中箭的效果斷定

一樣是採用粗略計算,採用弓箭的中心點和目標物/障礙物水平線兩側的點,以下圖的(X2, Y2)的連線,與水平線的夾角來判斷。


從圖上能夠看出按照上面的方法來判斷的話。圖中的α角是一個最小角。最大角應是目標對象水平線上左側的點到弓箭中心點連線與水平線的夾角。故做者將計算當前對象與弓箭的夾角方法放到了對象原型中,以便射箭後遍歷對象判斷角度使用。

//yMax, yMin, xMax, xMin 爲方便展現使用。具體數值從上圖能夠很快求出
Target.prototype.angle = function() {
    var anglemax = Math.atan2(yMax, xMax)
    var anglemin = Math.atan2(yMin, xMin)
    function angleCal(angle) { //轉化爲°數
      return (angle / Math.PI * 180) < 0 ? angle / Math.PI * 180 - 90 : angle / Math.PI * 180
    }
    var angleMax = angleCal(anglemax)
    var angleMmin = angleCal(anglemin)
    return {
      max: angleMax,
      min: angleMmin
    }
  }複製代碼

需注意的是裏面使用了Math.atan2(y, x)方法。注意裏面參數順序哦=。=

箭射到對象身上

不知道嘗試了這個遊戲的童鞋有沒有注意到,當點擊了某一個方向後,箭會順着這個方向射出,而且會沿途判斷有沒有射中什麼東西,若是射中了那麼就要停在那個對象身上,沒射中就無所謂了。那麼這一點的實現咱們就不能是使用如目標物身上綁定監聽事件來解決了。那麼如今再來看這個圖:


(X1, Y1)爲點擊的位置,沿着這個方向延伸就會延伸到(X2, Y2),那麼應該是射中了,這個時候須要渲染箭到(X2, Y2)這個點。如何實現呢,其實圖中已經有告終果。X2爲當前對象的left + width, X1爲觸摸點的client.X, Y1爲畫布高 - client.Y - 弓箭高/2;那麼結果是否是已經很明瞭了呢。再加上以前的角度咱們已經有原型方法計算過,能夠經過css中的rotate使得射出的箭改變方向,那麼至此這個需求也算是基本完成。

小結

本次本來打算採用canvas來作這個遊戲,可是初步嘗試以後發現可能因爲繪製得太頻繁,手機顯示出來效果實在是。。固然也由於做者對這種小遊戲的實現經驗不足。只借此分享一個小case,歡迎指正。

最後

慣例po做者的博客,不定時更新中——有問題歡迎在issues下交流,捂臉求star=。=

相關文章
相關標籤/搜索