所有前端都要看的2D游戲化互動入門基礎(chǔ)知識
背景
現(xiàn)在越來越多的公司和 App 開始使用游戲化的方式去做產(chǎn)品了,所謂游戲化,是指在非游戲環(huán)境中將游戲的思維和游戲的機(jī)制進(jìn)行整合運用,以引導(dǎo)用戶互動和使用的方法。
支付寶里面的螞蟻莊園、螞蟻森林,通過游戲和公益的結(jié)合實現(xiàn)用戶的留存和活躍。淘寶支付寶的芭芭農(nóng)場、京東的東東果園、拼多多的多多果園、美團(tuán)的小美果園...無一不是通過游戲化的方式去提升用戶留存的方案。
本篇文章,我們會列出一些游戲化互動類的游戲,然后對一個案例進(jìn)行拆分,帶大家學(xué)習(xí)一些2D互動最基礎(chǔ)的知識,讓大家能夠快速上手寫互動游戲。
能做什么
我們來看幾個2D互動項目,目前,大多數(shù)的互動都是以游戲的形式展現(xiàn),通過游戲的玩法和精致的效果,讓用戶有更好的互動體驗,我們通過養(yǎng)成,采集,塔防,抓娃娃等類似游戲的形式,結(jié)合業(yè)務(wù)屬性,達(dá)到更好的業(yè)務(wù)效果。
基礎(chǔ)學(xué)習(xí)
2D互動常用能力
首先,我們看一下2D互動游戲所用到的常用能力,第一部分是前端知識,主要包括渲染所需的繪制工具,游戲循環(huán),資源加載的能力。
然后是基礎(chǔ)的繪制和動畫能力,也就是前面提到的一些游戲基礎(chǔ)元素。在游戲開發(fā)中,會涉及到很多數(shù)學(xué)相關(guān)的知識,比如讓游戲中的物體模擬真實的物理效果,或者像一些游戲中的人機(jī)對戰(zhàn)中的機(jī)器人,是使用游戲 AI 來實現(xiàn)的,在本文中不會過多講解數(shù)學(xué)類知識。
互動游戲是如何運行起來的
互動游戲是如何運行起來的呢?
首先,我們知道,目前很多前端項目都是通過數(shù)據(jù)帶動視圖的,游戲也是這樣的,比如說,我們在游戲里面有一個飛機(jī),那么,我們需要定義飛機(jī)的尺寸,以及他在游戲中的位置,和他對應(yīng)的飛機(jī)圖片,這些屬于游戲數(shù)據(jù),我們將數(shù)據(jù)提交給渲染引擎,渲染引擎根據(jù)這些數(shù)據(jù)內(nèi)容將對應(yīng)的內(nèi)容渲染到畫布上。
游戲是動態(tài)運行的,為了實現(xiàn)一些動畫/真實物理效果效果,我們通過動畫,AI,物理引擎等工具控制數(shù)據(jù)的變化,然后通過循環(huán)來持續(xù)修改數(shù)據(jù),并且渲染到畫布實現(xiàn)游戲的運行。
循環(huán)
我們知道,通過循環(huán)來實現(xiàn)游戲的運行效果,接下來我們來看一下在前端瀏覽器環(huán)境下,游戲循環(huán)是如何實現(xiàn)的。
瀏覽器提供了 requestAnimationFrame 方法,要求瀏覽器在下一次繪制之前,調(diào)用制定的回調(diào)函數(shù),這個方法一般是用于更新動畫的。
瀏覽器在的每一次重繪我們叫做1幀,瀏覽器默認(rèn)的繪制頻率是60幀,也就是說,正常情況下,瀏覽器一秒會刷新60次。
通過下面的方法,我們可以保證每一幀渲染之前,我們可以進(jìn)行數(shù)據(jù)的計算以及調(diào)用渲染方法:
- const loop = () => { requestAnimationFrame(loop) // 計算數(shù)據(jù) // 繪制圖形}requestAnimationFrame(loop)
因為requestAnimationFrame方法只會在下一次繪制前被調(diào)用,所以,我們需要在每次調(diào)用方法的時候去調(diào)用一次這個方法保證游戲繼續(xù)運行,所以在loop方法里面會重復(fù)調(diào)用這個方法。
一般情況下,我們會把這個方法放在函數(shù)的最前面,因為,如果在計算數(shù)據(jù)和繪制圖形的過程中報錯了,會導(dǎo)致程序無法執(zhí)行到這個方法,游戲也就停掉了。
畫布
在 html 中,我們一般使用 canvas 標(biāo)簽來繪制圖像,它本身沒有繪制能力,使用 getContext 獲取繪制上下文,調(diào)用上下文上面的方法進(jìn)行繪制。
常用的繪制上下文有 Canvas API 和 WebGL,一般 CanvasAPI 來繪制2D圖像,WebGL 可繪制 2D 和 3D 圖像,他的性能更高。
canvas 提供了一些比較基礎(chǔ)的 API,但是在互動游戲中的元素是比較復(fù)雜的,所以一般都會有渲染引擎和游戲引擎來承接這些元素。
本文不會詳細(xì)講解 Canvas,可以到 MDN 等平臺學(xué)習(xí)。
基礎(chǔ)渲染
接下來我會介紹一下,在2D游戲化互動游戲中,我們經(jīng)常用到幾種渲染方案。
圖片
文本
圖形
精靈
九宮格
遮罩
圖片和文本就不用說了,是視圖開發(fā)中最常用到的。我們從圖形開始說
圖形
一般在開發(fā)中會經(jīng)常使用一些簡單的圖形,圖片不僅會用在直接展示內(nèi)容,也會用在對渲染內(nèi)容的遮罩,例如一張圖片只顯示圖形內(nèi)的內(nèi)容,也會用在按鈕區(qū)域判斷、物理引擎碰撞的形狀等等地方。
精靈
精靈圖也是我們在 CSS 中接觸的精靈圖,就是將多張圖片合成在一張大圖中,在使用時渲染其中某個位置,通過精靈圖的方式,我們可以提高網(wǎng)絡(luò)加載效率以及渲染效率。一般精靈資源是由兩個文件組成,一個是圖片文件,另外一個是位置信息文件。一般使用引擎進(jìn)行渲染時,只需要關(guān)心對應(yīng)小圖的名稱。
九宮格
我們經(jīng)常會遇到一些尺寸不固定,但是周圍或四遍樣式不變形的圖片,也就是 .9 圖,例如消息氣泡,如果直接設(shè)置寬高會將整個氣泡圖片拉變形。
使用九宮格的原理進(jìn)行解決:
一般渲染引擎也會提供方便的方式實現(xiàn)。
遮罩
通過遮罩可以實現(xiàn)渲染內(nèi)容的遮罩效果,是不是很像給div設(shè)置 overflow:hidden 呢
基礎(chǔ)動畫
過渡動畫
例如一個物體經(jīng)過3秒,從100px的地方移動到500px。我們可以通過以下方法計算。
startTime 是動畫開始的時間。
如果一個物體向右做勻速運動,我們可以使用公式 s = v * t
一般情況下,我們都會使用現(xiàn)成的動畫庫,類似 Tween.js 實現(xiàn),當(dāng)然在實現(xiàn)復(fù)雜的動畫邏輯時,還可以使用一些工具,類似 Lottie,我們還是需要手寫動畫的。
逐幀動畫
骨骼動畫
骨骼動畫可以模擬實現(xiàn)一些比較復(fù)雜有一定關(guān)節(jié)邏輯的動畫,比起幀動畫而言,所使用的圖片更少,占用內(nèi)存更小。
骨骼動畫主要以下幾部分組成:
骨骼動畫貼圖
骨骼設(shè)計以及動畫
貼圖+骨骼+動畫
所以骨骼動畫資源一般由三個文件組成,常用的骨骼動畫設(shè)計軟件是 Spine 和 Dragonbones,一般是由設(shè)計師或者動畫設(shè)計師進(jìn)行設(shè)計。開發(fā)者只需要使用軟件導(dǎo)出的資源即可。
項目實戰(zhàn)
了解到以上的內(nèi)容,我們就可以開發(fā)互動項目了,工欲善其事,必先利其器,這里我們推薦由淘系技術(shù)部開源的 Eva.js,它是專門給前端開發(fā)者提供的開發(fā)游戲化互動項目所設(shè)計的。目前淘寶、天貓、支付寶、優(yōu)酷、阿里媽媽、AliExpress、Lazada、考拉等很多產(chǎn)品都在使用,2020年雙11養(yǎng)貓貓項目也是使用 Eva.js 實現(xiàn)的。
接下來我們拿一個最簡單的 Demo 來學(xué)習(xí)使用 Eva.js。
這是一顆心做左右移動動畫,點擊后彈出一個alert。
Eva.js 的游戲是由游戲?qū)ο蠛徒M件構(gòu)成,游戲?qū)ο蟠碛螒蛑械囊粋€物體,組件代表物體的能力,在這個例子中,只有一個物體,他的能力有三個:
顯示成一個心的圖片
有一個左右的過渡動畫
點擊事件
我們剛剛分析了這個 Demo 所需要的能力,接下來我們要做 Eva.js 開發(fā)游戲的四步操作
Step1 添加資源&創(chuàng)建游戲
- 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可以進(jìn)入 Eva.js 官網(wǎng) 中查看。
在添加資源之后,我們也創(chuàng)建了一個游戲?qū)嵗@是運行游戲的主要運行時, 因為 Eva.js 只有一個最核心的游戲運行時,所以我們所有的功能都是要自己安裝的哦~所以我們要安裝這個游戲所需要的系統(tǒng),圖片、事件、動畫。
RendererSystem 是用來將游戲渲染出來的系統(tǒng),所有渲染的能力都依賴這個系統(tǒng),里面設(shè)置了寬高以及所要渲染的canvas對象。
ImgSystem 是用來畫圖片的系統(tǒng)
EventSystem 是用來觸發(fā)點擊事件的系統(tǒng)
TransitionSystem 是用來做位移動畫的系統(tǒng)
Step2 創(chuàng)建對象,并設(shè)置定位
- 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 的第一個參數(shù)為對象的名稱,第二個參數(shù)為對象的位置信息,其中 size 設(shè)置對象大小, position 設(shè)置位置,其他的可以后續(xù)參考文檔學(xué)習(xí)哦~
Step3 添加所需要的組件
剛剛我們在 new Game 的時候添加了實現(xiàn)視頻功能所需要的系統(tǒng),這些系統(tǒng)是為了讀取組件上面的數(shù)值然后實現(xiàn)功能的,所以,我們需要給對象添加組件以后,才能夠讓對象實現(xiàn)對應(yīng)的功能。
我們目前所需要的功能是圖片渲染、點擊事件、位移動畫,所以我們要添加三個組件
圖片渲染
- import { Img } from '@eva/plugin-renderer-img'heart.addComponent( new Img({ resource: 'imageName', }),);
調(diào)用 heart 的 addComponent 方法既可添加組件,這里我們添加 Img 組件, Img 組件有個 resource 參數(shù),該參數(shù)是圖片資源的名稱,其實對應(yīng)了 Step1 中添加的圖片資源的名稱。當(dāng)然雪碧圖、骨骼動畫也是同樣的原理,需要在 resource 中添加資源,在添加組件的時候使用。
點擊事件
- import { Event } from '@eva/plugin-renderer-event'const evt = heart.addComponent(new Event())evt.on('tap', () => { alert(1)})
給游戲?qū)ο筇砑右粋€ Event 組件,并通過 on 方法綁定 tap 事件, on 的第二個參數(shù)為 tap 事件所觸發(fā)的函數(shù),當(dāng)然,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)
上面的代碼中,我們創(chuàng)建了一個動畫組,名字叫做 idle 當(dāng)前動畫組里面,我們對 heart.transform 組件的 position.x 屬性進(jìn)行數(shù)值變化,0->1000ms,數(shù)值從0->400,1000ms->2000ms,數(shù)值從400->0,然后使用 Transition 組件的 play 方法,讓動畫執(zhí)行 Infinity 次。
Step4 運行
一般游戲都是自動運行的,所以做完以上工作后,游戲會自動開始運行。
總結(jié)
未來會有越來越多的游戲化產(chǎn)品,開發(fā)互動類游戲?qū)⒊蔀榍岸斯こ處煹谋貍浼寄埽ㄟ^本篇文章,我們可以了解到一些基礎(chǔ)的游戲化互動技術(shù),也通過 Eva.js 學(xué)習(xí)了如何實現(xiàn)一個最簡單的互動游戲。
如果想對游戲化、互動技術(shù)更加深入,我們需要去深入學(xué)習(xí)游戲引擎、渲染原理、動畫、物理、音效等技術(shù),對于互動業(yè)務(wù)開發(fā)來說 Eva.js 目前能滿足大部分需求。
前端領(lǐng)域中游戲化方向剛剛起步, Eva.js 是專注于開發(fā)游戲化項目的游戲引擎,也處于剛剛起步的狀態(tài),未來 Eva.js 會繼續(xù)專注于前端,專注于游戲化項目,讓游戲化項目開發(fā)更簡單。我們也希望大家能夠參與到前端游戲化領(lǐng)域的建設(shè)中來,我們也會陸續(xù)分享相關(guān)的技術(shù),輸出游戲化項目開發(fā)能力。