記一次遊戲H5開發經驗

快到年終的時候作了一個以遊戲形式展現的h5活動頁,第一次嘗試使用js寫小遊戲,頗有趣的過程,很寶貴的經驗。css

效果圖

直接上個效果的gif圖,遊戲的一小部分效果,錄出來有點卡
圖片描述canvas

結果頁:
圖片描述微信

原由

產品妹子忽然給我拉進來一個羣,跟咱們講作了這麼久的製做平臺(用戶製做手機主題的平臺),咱們是否是應該反饋給用戶點什麼東西,就像以前特別火的微信年終總結那樣。總之就是要打動用戶,要特別酷。說特別酷的時候她回頭朝我微微一笑,微笑中帶着一點點,嗯,殺意。
活動形式,展示方式,什麼數據反正就是通通都沒想好,整個過程當中你們討論的熱火朝天。當時不知道我爲啥腦子一熱,跟她說了一句:「沒事兒,搞吧,你能想出來我就給你作出來。」而我也由於這句話把本身置身於水深火熱之中。。
討論的結果就是你們的idea感受都不是特別酷,又很差玩兒,乾脆就作個遊戲形式的吧!全部人都轉頭看向我,我想了想以前說的話,只吐出來一個字,「搞」。而心裏中五味雜陳,「遊戲?有意思啊?搞!沒搞過啊?能搞定嗎?搞!」。最終敲定,兩週時間,遊戲方式,展示用戶在魔秀的點點滴滴。架構

準備工做

遊戲的形式大概相似一個滑雪大冒險和賽車的結合,以賽車的形式進行僞3d效果的展示,滑雪大冒險的樣式做爲咱們的主題,同時你們還給咱們的遊戲起了個酷炫的名字----魔秀時光道。app

遊戲引擎

遊戲的展示形式肯定後,直覺告訴我,想要將遊戲快速穩定的呈現,免去圖片加載控制,動畫控制之類的複雜處理,我須要一個JS遊戲引擎。最終在EgretPhaserPixiJS中選定了PixiJS,雖然不像Egret同樣有完善的中文文檔,可是它提供了清晰易懂的examples可快速上手,沒有複雜的生態,簡單的幾行代碼就能夠用js實現我想要如下幾點功能:ide

容器渲染及背景描繪

我須要定製整個畫布的大小和背景,我須要使用不一樣的容器來承接不一樣的內容,而且靈活控制每一個容器的屬性:函數

//畫布
var app = new PIXI.Application(800, 600, {backgroundColor : 0x1099bb});
document.body.appendChild(app.view);

//定製container
var container = new PIXI.Container();

container.x = (app.renderer.width - container.width) / 2;
container.y = (app.renderer.height - container.height) / 2;

app.stage.addChild(container);

圖片加載及動畫處理

你們都知道,使用canvas進行圖片繪製的時候,須要肯定圖片已經成功加載,而遊戲中有着大量的圖片資源須要去維護,PixiJS已經爲咱們提供此項服務:工具

var bunny = PIXI.Sprite.fromImage('required/assets/basics/bunny.png’);

bunny.x = app.renderer.width / 2;
bunny.y = app.renderer.height / 2;

app.stage.addChild(bunny);

同時,咱們須要一個動畫控制器,來控制各Sprite的運動和重繪,而不是生硬的對各項屬性進行從新修改:學習

app.ticker.add(function(delta) {
  bunny.rotation += 0.1 * delta;
});

須要注意的是,咱們會發現,此處的Sprite動畫控制,至關於添加了運動的動畫隊列,而且實現了相似transformjs的效果,可直接對實例的屬性進行操做。而我在寫項目的時候官方的例子是經過統一animate函數進行操做,經過requestAnimationFrame進行幀動畫控制,更推薦新的方式而不是以下:動畫

function animate () {
  requestAnimationFrame(animate);
  
  bunny.rotation += 1;
  renderer.render(stage);
}

事件處理

遊戲最重要的部分至關於用戶的交互了,也就是所謂的事件處理,爲Sprite添加事件監聽,很簡單,以下所示:

//元素可點擊
sprite.interactive = true;

//鼠標移入cursor
sprite.buttonMode = true;

sprite.on('click', onClick); // mouse-only
prite.on('tap', onClick); // touch-only

function onClick () {
  sprite.scale.x *= 1.25;
  sprite.scale.y *= 1.25;
}

設計圖

設計圖固然也是很重要的,決定了咱們如何去實現這個遊戲,當我拿到設計圖的時候,他是長成這樣的,個人心裏是崩潰的。我能怎麼樣,我也很無奈呀~ 開搞吧!
圖片描述

實現思路

根據以上,PixiJS已經基本知足咱們的需求,也就是說,工具準備和素材準備已經都完成了。在動手書寫以前,咱們須要把實現思路想好,才能保證書寫過程的清晰,避免沒必要要的麻煩。

背景滑動效果實現

就像咱們平時玩兒賽車遊戲同樣,咱們感受賽車在跑道上進行比賽,實際上賽車只進行左右移動而已,而運動的則是背景,如何規劃好路線,讓背景按照既定的場景去運動,並展示不一樣的視角,特地向央美的同胞諮詢了下,他們是用一個叫「攝像機」的東西實現的。對於咱們來講,不須要那麼複雜的場景,只需讓背景像前規律的「平移」,形成「樹動我不動」的視覺效果,同時咱們利用「透視」的原理,讓背景以「近大遠小」的方式進行變化,就會產生一種low low的立體效果。

關鍵詞:透視 近大遠小(偏移,大小,速度)

偏移路線

對於背景及物體的運動,大概路線規劃以下:
圖片描述

肯定視覺焦點後,咱們只需隨機生成物體出現的位置,計算出a,b相對固定,使其y進行相應速度的增長,x根據運動軌跡進行對應偏移,則可實現往近跑的效果。針對運動軌跡, 假設物體向下偏移距離爲N,則對應水平針對中軸線的偏移爲:
圖片描述

大小

同時,咱們還需對物體進行近大遠小的顯示,這個比較簡單,以焦點爲0,頁面最底端爲1,進行對應比例放大便可:

scale = (curY - startY) / ( endY - startY);

運動速度

針對物體的運動速度,也應在遠近有不一樣的體現。

背景樹與碰撞物體的區別

針對背景樹,咱們需在最初對全部的樹進行展示,鋪滿兩邊背景。每列樹對應的運動路線一致,可直接讓其進行循環展現,當樹運動到最底時,讓其出如今最頂點。所以只需肯定一共有幾行幾列樹,並設定其邊界,根據行列肯定初始惟一併對其進行運動。同時,可讓樹進行小範圍的隨機偏移,使樹錯落有致。以下所示:

export default function Tree (row, col, direction) {
  this.cfg = {
    direction: direction, //方向
    col: col, //第幾列
    row: row, //第幾行
    MaxX: 440,
    minY: 210,
    maxY: 500,
    range: 10 //座標浮動範圍
  }
};

而針對物體,則須要隨機生成它的初始x座標,並計算出其對於的路線進行運動,在運動過程當中,進行碰撞檢測,檢測是否與人物進行相撞。

人物滑動實現

人物滑動的操做,用了最簡單的實現方式:按鈕。當用戶點擊不一樣方向時,讓人物向對應方向進行偏移。同時,爲了讓人物滑動不僵硬,在左右滑動過程當中,人也應該隨着運動有對應角度的傾斜,就像咱們平時玩兒滑雪拐彎時,會改變中心同樣。思路以下:

  1. 點擊按鈕時,改變方向

  2. 運動時檢測方向,若向左,則x減少,向右,則增長

  3. 向右(左)運動時,人物對應rotation也進行增長(減少)

  4. 鬆開手時,人物對應rotation慢慢恢復成0;

碰撞檢測

因爲人物有吃東西的環節(否則這還叫什麼遊戲呢),所以碰撞檢測確定是必須的啦。咱們能夠經過兩種方式進行碰撞檢測

  1. 人物檢測碰撞物體,需實時遍歷物體座標列表,進行檢測

  2. 每一個物體自身進行碰撞檢測,檢測自身與人物位置的對應差

我很機智的選擇了第二個,畢竟每一個物體的位置都是實時變更的,而每次碰撞檢測都進行一次循環的方法,太笨重啦。在這裏咱們設置碰撞檢測的區域(寬高),在物體運動時,針對人物的x,y座標,與自身的x,y座標加減造成的四條邊界進行比較便可,若進行碰撞,則進行對應的操做便可,如播放音頻,得分+1等。

架構設計

思路理清楚以後,後面的路就很明朗啦。接下來咱們就能夠着手設計下如何實現這個東西了,很顯然,遊戲中咱們擁有許許多多的「角色」,使用「面向對象」的方式再好不過了。大概的劃分以下

  • stage //舞臺,進行基本場景渲染,遊戲總體控制(開始,中止)等

  • player //玩兒家,也就是對應的人物

  • sprite //出現的物體,如蛋糕等,提供玩兒家吃。 包含碰撞檢測等,會本身運動

  • tree //由於tree自身會運動,因此每一個tree爲一個類

  • score //進行分數控制及顯示

  • cfg.js //包含總體遊戲配置

對象內部劃分

每一個對象包含如下幾點屬性及功能:

1. 對象配置

每一個對象包含其內部自身基本配置,包括位置,邊界,圖片等。直觀,便於調試

export default function People (stage) {
  this.cfg = {
    img: require('./img/people.png'),
    anchor: {
      x: 0.5,
      y: 0.5,
    },
    position: {
      x: cfg.width / 2,
      y: 500
    },
    speed: 5,
  }
}

2. 其餘方法

每一個對象都包含其自身方法,以下所示:

  • render //進行圖片等渲染

  • animate //動畫function

  • init //一些初始化配置

實現

經過以上思路的設計和結構的設計,我很快的將這個遊戲實現了。。。沒錯,理清思路和結構的重要性就是這樣。固然,在實現過程當中,也有一些小的點能夠記錄下:

資源加載器(圖片)

爲了遊戲的進行效果,仍是決定在加載完全部資源(尤爲是圖片資源)後,才中止loading頁面。如何判斷全部內容都加載完畢了呢?寫了個小loader

var pics = [
  require('./img/bg-start.png'),
  require('./img/btn-start.png'),
  ...
];

function loadImages (pics, callback) {
  if(pics.length) {
    var img = new Image(),
      pic = pics.shift();

    img.onload = callback;
    img.src = pic;
    loadImages(pics, callback);
  } else{
    return;
  }
}

$(function() {
  loadImages(pics, function () {
    if (!pics.length) {
      $('.loading').hide();
    };
  })
});

強制橫屏

遊戲是橫屏展現的,那就強制橫屏好啦。這個當時還糾結挺久,仍是本身功底不紮實腦子走私了,還在想是監聽resize事件仍是旋轉屏幕事件,都沒有這些事兒啊好嗎!直接讓它旋轉就好。

if(window.orientation==180||window.orientation==0) {
  $('#main').height(winW);
  $('#main').width(winH);

  $(‘#main’).css({
    'transform': 'rotate(90deg)',
  });
} else{
  $(‘#main’).css('transform', 'rotate(0)');
}

timer控制

理清思路後,最亂的仍是各類定時器啦。 爲了實現物體隨機出現的效果,讓每一個物體隨機多少秒後開始出現;最後一個物體出現完,多少秒後出現結束畫面等等,須要理清楚各個定時器的關係,並對其添加語義化良好的標記,及時對未完結的定時器進行清除,防止定時器帶來的意想不到的問題。

寫在最後

最終遊戲的效果基本讓你們滿意啦,也是第一次嘗試這方面的開發,周圍也徹底沒有作過這東西的人。從開始的忐忑和一無所措,到過程當中理清思路和結構,到書寫中的各類未知的坑,本身在這兩週感受經歷了很充實的一件事情。同時也對後續進行一些未知事物的探索和學習有了更豐富的經驗,找對路子纔是王道呀!

相關文章
相關標籤/搜索