本文作者:淘系前端團隊-Eva.js作者-明非

背景
現在越來越多的公司和 App 開始使用遊戲化的方式去做産品了,所謂遊戲化,是指在非遊戲環境中将遊戲的思維和遊戲的機制進行整合運用,以引導使用者互動和使用的方法。
支付寶裡面的螞蟻莊園、螞蟻森林,通過遊戲和公益的結合實作使用者的留存和活躍。淘寶支付寶的芭芭農場、京東的東東果園、拼多多的多多果園、美團的小美果園...無一不是通過遊戲化的方式去提升使用者留存的方案。
本篇文章,我們會列出一些遊戲化互動類的遊戲,然後對一個案例進行拆分,帶大家學習一些2D互動最基礎的知識,讓大家能夠快速上手寫互動遊戲。
能做什麼
我們來看幾個2D互動項目,目前,大多數的互動都是以遊戲的形式展現,通過遊戲的玩法和精緻的效果,讓使用者有更好的互動體驗,我們通過養成,采集,塔防,抓娃娃等類似遊戲的形式,結合業務屬性,達到更好的業務效果。
基礎學習
2D互動常用能力
首先,我們看一下2D互動遊戲所用到的常用能力,第一部分是前端知識,主要包括渲染所需的繪制工具,遊戲循環,資源加載的能力。
然後是基礎的繪制和動畫能力,也就是前面提到的一些遊戲基礎元素。在遊戲開發中,會涉及到很多數學相關的知識,比如讓遊戲中的物體模拟真實的實體效果,或者像一些遊戲中的人機對戰中的機器人,是使用遊戲 AI 來實作的,在本文中不會過多講解數學類知識。
互動遊戲是如何運作起來的
互動遊戲是如何運作起來的呢?
首先,我們知道,目前很多前端項目都是通過資料帶動視圖的,遊戲也是這樣的,比如說,我們在遊戲裡面有一個飛機,那麼,我們需要定義飛機的尺寸,以及他在遊戲中的位置,和他對應的飛機圖檔,這些屬于遊戲資料,我們将資料送出給渲染引擎,渲染引擎根據這些資料内容将對應的内容渲染到畫布上。
遊戲是動态運作的,為了實作一些動畫/真實實體效果效果,我們通過動畫,AI,實體引擎等工具控制資料的變化,然後通過循環來持續修改資料,并且渲染到畫布實作遊戲的運作。
循環
我們知道,通過循環來實作遊戲的運作效果,接下來我們來看一下在前端浏覽器環境下,遊戲循環是如何實作的。
浏覽器提供了 requestAnimationFrame 方法,要求浏覽器在下一次繪制之前,調用制定的回調函數,這個方法一般是用于更新動畫的。
浏覽器在的每一次重繪我們叫做1幀,浏覽器預設的繪制頻率是60幀,也就是說,正常情況下,浏覽器一秒會重新整理60次。
通過下面的方法,我們可以保證每一幀渲染之前,我們可以進行資料的計算以及調用渲染方法:
const loop = () => {
requestAnimationFrame(loop)
// 計算資料
// 繪制圖形
}
requestAnimationFrame(loop)
因為requestAnimationFrame方法隻會在下一次繪制前被調用,是以,我們需要在每次調用方法的時候去調用一次這個方法保證遊戲繼續運作,是以在loop方法裡面會重複調用這個方法。
一般情況下,我們會把這個方法放在函數的最前面,因為,如果在計算資料和繪制圖形的過程中報錯了,會導緻程式無法執行到這個方法,遊戲也就停掉了。
畫布
在 html 中,我們一般使用 canvas 标簽來繪制圖像,它本身沒有繪制能力,使用 getContext 擷取繪制上下文,調用上下文上面的方法進行繪制。
常用的繪制上下文有 Canvas API 和 WebGL,一般 CanvasAPI 來繪制2D圖像,WebGL 可繪制 2D 和 3D 圖像,他的性能更高。
canvas 提供了一些比較基礎的 API,但是在互動遊戲中的元素是比較複雜的,是以一般都會有渲染引擎和遊戲引擎來承接這些元素。
本文不會詳細講解 Canvas,可以到 MDN 等平台學習。
基礎渲染
接下來我會介紹一下,在2D遊戲化互動遊戲中,我們經常用到幾種渲染方案。
- 圖檔
- 文本
- 圖形
- 精靈
- 九宮格
- 遮罩
圖檔和文本就不用說了,是視圖開發中最常用到的。我們從圖形開始說
一般在開發中會經常使用一些簡單的圖形,圖檔不僅會用在直接展示内容,也會用在對渲染内容的遮罩,例如一張圖檔隻顯示圖形内的内容,也會用在按鈕區域判斷、實體引擎碰撞的形狀等等地方。
精靈圖也是我們在 CSS 中接觸的精靈圖,就是将多張圖檔合成在一張大圖中,在使用時渲染其中某個位置,通過精靈圖的方式,我們可以提高網絡加載效率以及渲染效率。一般精靈資源是由兩個檔案組成,一個是圖檔檔案,另外一個是位置資訊檔案。一般使用引擎進行渲染時,隻需要關心對應小圖的名稱。
我們經常會遇到一些尺寸不固定,但是周圍或四遍樣式不變形的圖檔,也就是 .9 圖,例如消息氣泡,如果直接設定寬高會将整個氣泡圖檔拉變形。
使用九宮格的原理進行解決:
一般渲染引擎也會提供友善的方式實作。
通過遮罩可以實作渲染内容的遮罩效果,是不是很像給div設定 overflow:hidden 呢
基礎動畫
過渡動畫
例如一個物體經過3秒,從100px的地方移動到500px。我們可以通過以下方法計算。
startTime 是動畫開始的時間。
如果一個物體向右做勻速運動,我們可以使用公式 s = v * t
一般情況下,我們都會使用現成的動畫庫,類似 Tween.js 實作,當然在實作複雜的動畫邏輯時,還可以使用一些工具,類似 Lottie,我們還是需要手寫動畫的。
逐幀動畫
骨骼動畫
骨骼動畫可以模拟實作一些比較複雜有一定關節邏輯的動畫,比起幀動畫而言,所使用的圖檔更少,占用記憶體更小。
骨骼動畫主要以下幾部分組成:
骨骼動畫貼圖
骨骼設計以及動畫
貼圖+骨骼+動畫
是以骨骼動畫資源一般由三個檔案組成,常用的骨骼動畫設計軟體是 Spine 和 Dragonbones,一般是由設計師或者動畫設計師進行設計。開發者隻需要使用軟體導出的資源即可。
項目實戰
了解到以上的内容,我們就可以開發互動項目了,工欲善其事,必先利其器,這裡我們推薦由淘系技術部開源的 Eva.js,它是專門給前端開發者提供的開發遊戲化互動項目所設計的。目前淘寶、天貓、支付寶、優酷、阿裡媽媽、AliExpress、Lazada、考拉等很多産品都在使用,2020年雙11養貓貓項目也是使用 Eva.js 實作的。
接下來我們拿一個最簡單的 Demo 來學習使用 Eva.js。
這是一顆心做左右移動動畫,點選後彈出一個alert。
Eva.js 的遊戲是由遊戲對象群組件構成,遊戲對象代表遊戲中的一個物體,元件代表物體的能力,在這個例子中,隻有一個物體,他的能力有三個:
- 顯示成一個心的圖檔
- 有一個左右的過渡動畫
- 點選事件
我們剛剛分析了這個 Demo 所需要的能力,接下來我們要做 Eva.js 開發遊戲的四步操作
Step1 添加資源&建立遊戲
import { resource, Game } from '@eva/eva.js'
import { RendererSystem } from '@eva/plugin-renderer'
import { ImgSystem } from '@eva/plugin-renderer-img'
import { EventSystem } from '@eva/plugin-renderer-event'
import { TransitionSystem } from '@eva/plugin-transition'
resource.addResource([
{
name: 'imageName',
type: RESOURCE_TYPE.IMAGE,
src: {
image: {
type: 'png',
url:
'//gw.alicdn.com/bao/uploaded/TB1lVHuaET1gK0jSZFhXXaAtVXa-200-200.png',
},
},
preload: true,
},
]);
const game = new Game({
systems: [
new RendererSystem({
canvas: document.querySelector('#canvas'),
width: 750,
height: 1000,
}),
new ImgSystem(),
new EventSystem(),
new TransitionSystem()
],
});
addResource 傳入了一個資源的裡面,這裡不一定隻有圖檔資源,還可以有幀動畫、骨骼動畫等等資源,這裡以圖檔資源舉例子。更多Demo可以進入 Eva.js 官網 中檢視。
在添加資源之後,我們也建立了一個遊戲執行個體,這是運作遊戲的主要運作時, 因為 Eva.js 隻有一個最核心的遊戲運作時,是以我們所有的功能都是要自己安裝的哦~是以我們要安裝這個遊戲所需要的系統,圖檔、事件、動畫。
- RendererSystem 是用來将遊戲渲染出來的系統,所有渲染的能力都依賴這個系統,裡面設定了寬高以及所要渲染的canvas對象。
- ImgSystem 是用來畫圖檔的系統
- EventSystem 是用來觸發點選事件的系統
- TransitionSystem 是用來做位移動畫的系統
Step2 建立對象,并設定定位
import { GameObject } from '@eva/eva.js'
const heart = new GameObject('heart', {
size: { width: 200, height: 200 },
position: {
x: 0,
y: 0,
},
origin: { x: 0, y: 0 },
anchor: {
x: 0,
y: 0,
},
});
GameObject 的第一個參數為對象的名稱,第二個參數為對象的位置資訊,其中 size 設定對象大小, position 設定位置,其他的可以後續參考文檔學習哦~
Step3 添加所需要的元件
剛剛我們在 new Game 的時候添加了實作視訊功能所需要的系統,這些系統是為了讀取元件上面的數值然後實作功能的,是以,我們需要給對象添加元件以後,才能夠讓對象實作對應的功能。
我們目前所需要的功能是圖檔渲染、點選事件、位移動畫,是以我們要添加三個元件
圖檔渲染
import { Img } from '@eva/plugin-renderer-img'
heart.addComponent(
new Img({
resource: 'imageName',
}),
);
調用 heart 的 addComponent 方法既可添加元件,這裡我們添加 Img 元件, Img 元件有個 resource 參數,該參數是圖檔資源的名稱,其實對應了 Step1 中添加的圖檔資源的名稱。當然雪碧圖、骨骼動畫也是同樣的原理,需要在 resource 中添加資源,在添加元件的時候使用。
import { Event } from '@eva/plugin-renderer-event'
const evt = heart.addComponent(new Event())
evt.on('tap', () => {
alert(1)
})
給遊戲對象添加一個 Event 元件,并通過 on 方法綁定 tap 事件, on 的第二個參數為 tap 事件所觸發的函數,當然,Event 元件還有其他事件,我們可以通過 Eva.js 文檔檢視。
位移動畫
import { Transition } from '@eva/plugin-transition'
const transition = heart.addComponent(new Transition())
transition.group = {
idle: [
{
name: 'position.x',
component: heart.transform,
values: [
{
time: 0,
value: 0,
tween: 'ease',
},
{
time: 1000,
value: 400,
tween: 'ease',
},
{
time: 2000,
value: 0
}
]
}
]
}
transition.play('idle', Infinity)
上面的代碼中,我們建立了一個動畫組,名字叫做 idle 目前動畫組裡面,我們對 heart.transform 元件的 position.x 屬性進行數值變化,0->1000ms,數值從0->400,1000ms->2000ms,數值從400->0,然後使用 Transition 元件的 play 方法,讓動畫執行 Infinity 次。
Step4 運作
一般遊戲都是自動運作的,是以做完以上工作後,遊戲會自動開始運作。
總結
未來會有越來越多的遊戲化産品,開發互動類遊戲将成為前端工程師的必備技能,通過本篇文章,我們可以了解到一些基礎的遊戲化互動技術,也通過 Eva.js 學習了如何實作一個最簡單的互動遊戲。
如果想對遊戲化、互動技術更加深入,我們需要去深入學習遊戲引擎、渲染原理、動畫、實體、音效等技術,對于互動業務開發來說 Eva.js 目前能滿足大部分需求。
前端領域中遊戲化方向剛剛起步, Eva.js 是專注于開發遊戲化項目的遊戲引擎,也處于剛剛起步的狀态,未來 Eva.js 會繼續專注于前端,專注于遊戲化項目,讓遊戲化項目開發更簡單。我們也希望大家能夠參與到前端遊戲化領域的建設中來,我們也會陸續分享相關的技術,輸出遊戲化項目開發能力。
CodeDay#7 北京站報名ing
7月17日,與移動開發行業技術大佬相聚中關村,探求前沿的移動端動态化跨端開發方案。