Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
隨著HTML5的火熱,越來越多的人投入到HTML5開發中了,canvas作為HTML5中比較重要的一個元素,在很多官網的主頁面中被使用到。今天我們一起來看看如何使用canvas畫出一個夢幻的星空背景,還會有流星運動。
本文的代碼已經放到Github上了,感興趣的可以自取,Github地址如下。
https://github.com/zhouxiongking/article-pages/blob/master/articles/starry/starry.html
HTML5
首先我們來看看通過canvas實現的星空效果圖,如下所示。
效果圖
接下來我們看看這個效果是如何通過代碼一步步實現的。
首先來看看頁面上的HTML代碼,只有一個Div元素。
HTML代碼
首先我們需要定義一些常量,比如畫布的寬和高,星星數量,流星個數。在這個星空中流星其實是星星的一個,只是添加了動態效果。
頁面初始化
然后是設定一個定時器,通過一段隨機時間生成一個流星的索引號。
流星索引號
緊接著來看看生成一個星星的方法,該方法返回一個星星的各項參數,包括x,y軸坐標,透明度,x,y軸偏移量。
生成星星的參數
然后是最重要的render方法,通過該方法可以將星星渲染至畫布上,我們將這個方法拆開看,首先是對流星的繪制,流星索引號通過上面metor方法獲得。
畫流星
然后是對于星星各項參數的處理,比如有的星星生成的點坐標超出了屏幕寬高,有的透明度是負數,都要將其處理成正常參數。
各項參數判斷
最后是在畫布中進行繪制。
畫布繪制
至此,這個畫面效果的講解完畢,如果代碼正確的話,就可以看到文中出現的效果圖。
今天這篇文章主要是借助HTML5中的canvas畫出了一個夢幻星空的效果,你學會了嗎?
、前言
在瀏覽一些圖片網站的時候,經常會看到很多的漂亮的星空圖,比如,下面的圖片。其實這種星星圖片的效果,也可以通過html+css樣式和js的方式來實現。今天教大家如何實現星星圖的效果。
軟件:Dreamweaver
每次刷新產生隨機的星星個數。顯示畫布上。
<body>
<canvas id='canvas'></canvas>
</body>
給canva 畫布加上邊框,方便觀察。
<style type="text/css">
canvas{
border:2px solid #f00;
}
</style>
<script type="text/javascript">
var _canvas=document.getElementById("canvas")
_canvas.width=500;
_canvas.height=500;
var r,g ,b,a;
</script>
for (var j = 0; j < 150; j++) {
arc.x=Math.floor(Math.random()*_canvas.width);
arc.y=Math.floor(Math.random()*_canvas.height);
arc.r=Math.floor(Math.random()*31+10);
r=Math.ceil(Math.random()*256);
g=Math.ceil(Math.random()*256);
b=Math.ceil(Math.random()*256);
a=Math.random();
darw();
}
如何畫星星?(公式解析)(圖片來源百度)
星星有內切圓和外切圓,每兩個點之間的角度是固定的,因此可得到星星的每個點的坐標,畫出星星。
/* 隨機產生星星*/
for (var i = 0; i < 5; i++) {
_ctx.lineTo(Math.cos((18+72*i)/180*Math.PI)*arc.r+arc.x, -Math.sin((18+72*i)/180*Math.PI)*arc.r+arc.y);
_ctx.lineTo(Math.cos((54+72*i)/180*Math.PI)*arc.r/2+arc.x, -Math.sin((54+72*i)/180*Math.PI)*arc.r/2+arc.y);
}
Math函數隨機產生0-225的RGB值。
/* 隨機顏色*/
_ctx.fillStyle="rgba(" + r + "," + g + "," + b + "," + a + ")";
_ctx.fill();
_ctx.strokeStyle="rgba(" + r + "," + g + "," + b + "," + a + ")";
_ctx.stroke();
}
darw();
1、點擊f12運行到瀏覽器
2、每次刷新網頁,隨機產生不一樣的星星和隨機顏色。
想學習更多前端、Python爬蟲、大數據等計算機知識,請前往:http://pdcfighting.com/
ocos Creator 開發游戲的一個核心理念就是讓內容生產和功能開發可以流暢的并行協作,我們在上個部分著重于處理美術內容,而接下來就是通過編寫腳本來開發功能的流程,之后我們還會看到寫好的程序腳本可以很容易的被內容生產者使用。
如果您從沒寫過程序也不用擔心,我們會在教程中提供所有需要的代碼,只要復制粘貼到正確的位置就可以了,之后這部分工作可以找您的程序員小伙伴來解決。下面讓我們開始創建驅動主角行動的腳本吧。
創建腳本
注意: Cocos Creator 中腳本名稱就是組件的名稱,這個命名是大小寫敏感的!如果組件名稱的大小寫不正確,將無法正確通過名稱使用組件!
編寫組件屬性
在打開的 Player 腳本里已經有了預先設置好的一些代碼塊,如下所示:
cc.Class({ extends: cc.Component, properties: { // foo: { // // ATTRIBUTES: // default: null, // The default value will be used only when the component attaching // // to a node for the first time // type: cc.SpriteFrame, // optional, default is typeof default // serializable: true, // optional, default is true // }, // bar: { // get () { // return this._bar; // }, // set (value) { // this._bar = value; // } // }, }, // LIFE-CYCLE CALLBACKS: // onLoad () {}, start () { }, // update (dt) {}, });
我們來大概了解一下這些代碼的作用。首先我們可以看到一個全局的 cc.Class() 方法,什么是 cc呢?cc 是 Cocos 的簡稱,Cocos 引擎的主要命名空間,引擎代碼中所有的類、函數、屬性和常量都在這個命名空間中定義。而 Class() 就是 cc 模塊下的一個方法,這個方法用于聲明 Cocos Creator 中的類。為了方便區分,我們把使用 cc.Class 聲明的類叫做 CCClass。Class() 方法的參數是一個原型對象,在原型對象中以鍵值對的形式設定所需的類型參數,就能創建出所需要的類。
例如:
var Sprite = cc.Class({ name: "sprite" });
以上代碼用 cc.Class() 方法創建了一個類型,并且賦給了 Sprite 變量。同時還將類名設為 sprite。類名用于序列化,一般可以省略。
對于 cc.Class 的詳細學習可以參考 使用 cc.Class 聲明類型。
現在我們回到腳本編輯器看回之前的代碼,這些代碼就是編寫一個組件(腳本)所需的結構。具有這樣結構的腳本就是 Cocos Creator 中的 組件(Component),他們能夠掛載到場景中的節點上,提供控制節點的各種功能。我們先來設置一些屬性,然后看看怎樣在場景中調整他們。
找到 Player 腳本里的 properties 部分,將其改為以下內容并保存:
// Player.js //... properties: { // 主角跳躍高度 jumpHeight: 0, // 主角跳躍持續時間 jumpDuration: 0, // 最大移動速度 maxMoveSpeed: 0, // 加速度 accel: 0, }, //...
Cocos Creator 規定一個節點具有的屬性都需要寫在 properties 代碼塊中,這些屬性將規定主角的移動方式,在代碼中我們不需要關心這些數值是多少,因為我們之后會直接在 屬性檢查器 中設置這些數值。以后在游戲制作過程中,我們可以將需要隨時調整的屬性都放在 properties 中。
現在我們可以把 Player 組件添加到主角節點上。在 層級管理器 中選中 Player 節點,然后在 屬性檢查器 中點擊 添加組件 按鈕,選擇 添加用戶腳本組件 -> Player,為主角節點添加 Player 組件。
現在我們可以在 屬性檢查器 中(需要選中 Player 節點)看到剛添加的 Player 組件了,按照下圖將主角跳躍和移動的相關屬性設置好:
這些數值除了 jumpDuration 的單位是秒之外,其他的數值都是以像素為單位的,根據我們現在對 Player組件的設置:我們的主角將能夠跳躍 200 像素的高度,起跳到最高點所需的時間是 0.3 秒,最大水平方向移動速度是 400 像素每秒,水平加速度是 350 像素每秒。
這些數值都是建議,一會等游戲運行起來后,您完全可以按照自己的喜好隨時在 屬性檢查器 中修改這些數值,不需要改動任何代碼。
編寫跳躍和移動代碼
下面我們添加一個方法,來讓主角跳躍起來,在 properties: {...}, 代碼塊的下面,添加叫做 setJumpAction 的方法:
// Player.js properties: { //... }, setJumpAction: function () { // 跳躍上升 var jumpUp = cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)).easing(cc.easeCubicActionOut()); // 下落 var jumpDown = cc.moveBy(this.jumpDuration, cc.v2(0, -this.jumpHeight)).easing(cc.easeCubicActionIn()); // 不斷重復 return cc.repeatForever(cc.sequence(jumpUp, jumpDown)); },
這里就需要了解一下 Cocos Creator 的 動作(Action)系統 了。由于動作系統比較復雜,這里就簡單的介紹一下。
在 Cocos Creator 中,動作 簡單來說就是 節點的位移、縮放和旋轉。
例如在上面的代碼中,moveBy() 方法的作用是在規定的時間內移動指定的一段距離,第一個參數就是我們之前定義主角屬性中的跳躍時間,第二個參數是一個 Vec2(表示 2D 向量和坐標)類型的對象,為了更好的理解,我們可以看看官方給的函數說明:
/** * !#en * Moves a Node object x,y pixels by modifying its position property. <br/> * x and y are relative to the position of the object. <br/> * Several MoveBy actions can be concurrently called, and the resulting <br/> * movement will be the sum of individual movements. * !#zh 移動指定的距離。 * @method moveBy * @param {Number} duration duration in seconds * @param {Vec2|Number} deltaPos * @param {Number} [deltaY] * @return {ActionInterval} * @example * // example * var actionTo = cc.moveBy(2, cc.v2(windowSize.width - 40, windowSize.height - 40)); */ cc.moveBy = function (duration, deltaPos, deltaY) { return new cc.MoveBy(duration, deltaPos, deltaY); };
可以看到,方法 moveBy 一共可以傳入三個參數,前兩個參數我們已經知道,第三個參數是 Number 類型的 Y 坐標,我們可以發現第二個參數是可以傳入兩種類型的,第一種是 Number 類型,第二種才是 Vec2類型,如果我們在這里傳入的是 Number 類型,那么默認這個參數就是 X 坐標,此時就要填第三個參數,為 Y 坐標。上面的例子中 cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)) 第二個參數傳入的是使用 cc.v2 方法構建的 Vec2 類型對象,這個類型表示的是一個坐標,即有 X 坐標也有 Y 坐標,因為不需要再傳入第三個參數!同時注意官方的一段話 x and y are relative to the position of the object.,這句話的意思是傳入的 X、Y 坐標都是相對于節點當前的坐標位置,而不是整個坐標系的絕對坐標。
了解了參數的含義之后,我們再來關注 moveBy() 方法的返回值,看官方說明可以知道,這個方法返回的是一個 ActionInterval 類型的對象,ActionInterval 在 Cocos 中是一個表示時間間隔動作的類,這種動作在一定時間內完成。到這里我們就可以理解代碼 cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)).easing(cc.easeCubicActionOut()) 前一部分 的意思了,它的意思就是構造一個 ActionInterval 類型的對象,這個對象表示在 jumpDuration 的時間內,移動到相對于當前節點的 (0,this.jumpHeight) 的坐標位置,簡單來說,就是一個向上跳躍的動作。
那么 后半部分 easing(cc.easeCubicActionOut()) 的作用是什么呢?easing 是 ActionInterval 類下的一個方法,這個方法可以讓時間間隔動作呈現為一種緩動運動,傳入的參數是一個緩動對象,返回一個 ActionInterval 類型對象,這里傳入的是使用 easeCubicActionInOut 方法構建的緩動對象,EaseCubicInOut 是按三次函數緩動進入并退出的動作,具體曲線可參考下圖:
詳細內容可參考 API。
接下來在 onLoad 方法里調用剛添加的 setJumpAction 方法,然后執行 runAction 來開始動作:
// Player.js onLoad: function () { // 初始化跳躍動作 this.jumpAction = this.setJumpAction(); this.node.runAction(this.jumpAction); },
onLoad 方法會在場景加載后立刻執行,所以我們會把初始化相關的操作和邏輯都放在這里面。我們首先將循環跳躍的動作傳給了 jumpAction 變量,之后調用這個組件掛載的節點下的 runAction 方法,傳入循環跳躍的 Action 從而讓節點(主角)一直跳躍。保存腳本,然后我們就可以開始第一次運行游戲了!
點擊 Cocos Creator 編輯器上方正中的 預覽游戲 按鈕
,Cocos Creator 會自動打開您的默認瀏覽器并開始在里面運行游戲,現在應該可以看到我們的主角——紫色小怪獸在場景中間活潑的蹦個不停了。
移動控制
只能在原地傻蹦的主角可沒前途,讓我們為主角添加鍵盤輸入,用 A 和 D 來控制他的跳躍方向。在 setJumpAction 方法的下面添加鍵盤事件響應函數:
// Player.js setJumpAction: function () { //... }, onKeyDown (event) { // set a flag when key pressed switch(event.keyCode) { case cc.macro.KEY.a: this.accLeft = true; break; case cc.macro.KEY.d: this.accRight = true; break; } }, onKeyUp (event) { // unset a flag when key released switch(event.keyCode) { case cc.macro.KEY.a: this.accLeft = false; break; case cc.macro.KEY.d: this.accRight = false; break; } },
然后修改 onLoad 方法,在其中加入向左和向右加速的開關,以及主角當前在水平方向的速度。最后再調用 cc.systemEvent,在場景加載后就開始監聽鍵盤輸入:
// Player.js onLoad: function () { // 初始化跳躍動作 this.jumpAction = this.setJumpAction(); this.node.runAction(this.jumpAction); // 加速度方向開關 this.accLeft = false; this.accRight = false; // 主角當前水平方向速度 this.xSpeed = 0; // 初始化鍵盤輸入監聽 cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this); cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this); }, onDestroy () { // 取消鍵盤輸入監聽 cc.systemEvent.off(cc.SystemEvent.EventType.KEY_DOWN, this.onKeyDown, this); cc.systemEvent.off(cc.SystemEvent.EventType.KEY_UP, this.onKeyUp, this); },
有 Android 開發經驗的同學比較好理解,這里的監聽器實質上就和 Android 里的 OnClickListener 差不多,在 cocos 中通過 systemEvent 來監聽系統 全局 事件。(鼠標、觸摸和自定義事件的監聽和派發的詳細內容請參考 監聽和發射事件。)這里通過向 systemEvent 注冊了一個鍵盤響應函數,在函數中通過 switch 判斷鍵盤上的 A 和 D 是否被按下或松開,若按下就執行對應的操作。
最后修改 update 方法的內容,添加加速度、速度和主角當前位置的設置:
// Player.js update: function (dt) { // 根據當前加速度方向每幀更新速度 if (this.accLeft) { this.xSpeed -= this.accel * dt; } else if (this.accRight) { this.xSpeed += this.accel * dt; } // 限制主角的速度不能超過最大值 if ( Math.abs(this.xSpeed) > this.maxMoveSpeed ) { // if speed reach limit, use max speed with current direction this.xSpeed = this.maxMoveSpeed * this.xSpeed / Math.abs(this.xSpeed); } // 根據當前速度更新主角的位置 this.node.x += this.xSpeed * dt; },
update 在場景加載后就會每幀調用一次,我們一般把需要經常計算或及時更新的邏輯內容放在這里。在我們的游戲中,根據鍵盤輸入獲得加速度方向后,就需要每幀在 update 中計算主角的速度和位置。
保存腳本后,點擊 預覽游戲 來看看我們最新的成果。在瀏覽器打開預覽后,用鼠標點擊一下游戲畫面(這是瀏覽器的限制,要點擊游戲畫面才能接受鍵盤輸入),然后就可以按 A 和 D 鍵來控制主角左右移動了!
感覺移動起來有點遲緩?主角跳的不夠高?希望跳躍時間長一些?沒問題,這些都可以隨時調整。只要為 Player 組件設置不同的屬性值,就可以按照您的想法調整游戲。這里有一組設置可供參考:
Jump Height: 150 Jump Duration: 0.3 Max Move Speed: 400 Accel: 1000
這組屬性設置會讓主角變得靈活無比,至于如何選擇,就看您想做一個什么風格的游戲了。
制作星星
主角現在可以跳來跳去了,我們要給玩家一個目標,也就是會不斷出現在場景中的星星,玩家需要引導小怪獸碰觸星星來收集分數。被主角碰到的星星會消失,然后馬上在隨機位置重新生成一個。
制作 Prefab
對于需要重復生成的節點,我們可以將他保存成 Prefab(預制) 資源,作為我們動態生成節點時使用的模板。關于 Prefab 的更多信息,請閱讀 預制資源(Prefab)。
首先從 資源管理器 中拖拽 assets/textures/star 圖片到場景中,位置隨意,我們只是需要借助場景作為我們制作 Prefab 的工作臺,制作完成后會我們把這個節點從場景中刪除。
我們不需要修改星星的位置或渲染屬性,但要讓星星能夠被主角碰觸后消失,我們需要為星星也添加一個專門的組件。按照和添加 Player 腳本相同的方法,添加名叫 Star 的 JavaScript 腳本到 assets/scripts/中。
接下來雙擊這個腳本開始編輯,星星組件只需要一個屬性用來規定主角距離星星多近時就可以完成收集,修改 properties,加入以下內容并保存腳本。
// Star.js properties: { // 星星和主角之間的距離小于這個數值時,就會完成收集 pickRadius: 0, },
將這個腳本添加到剛創建的 star 節點上,在 層級管理器 中選中 star 節點,然后在 屬性檢查器 中點擊 添加組件 按鈕,選擇 添加用戶腳本組件 -> Star,該腳本便會添加到剛創建的 star 節點上。然后在 屬性檢查器中把 Pick Radius 屬性值設為 60:
Star Prefab 需要的設置就完成了,現在從 層級管理器 中將 star 節點拖拽到 資源管理器 中的 assets 文件夾下,就生成了名叫 star 的 Prefab 資源。
現在可以從場景中刪除 star 節點了,后續可以直接雙擊這個 star Prefab 資源進行編輯。
接下去我們會在腳本中動態使用星星的 Prefab 資源生成星星。
添加游戲控制腳本
星星的生成是游戲主邏輯的一部分,所以我們要添加一個叫做 Game 的腳本作為游戲主邏輯腳本,這個腳本之后還會添加計分、游戲失敗和重新開始的相關邏輯。
添加 Game 腳本到 assets/scripts 文件夾下,雙擊打開腳本。首先添加生成星星需要的屬性:
// Game.js properties: { // 這個屬性引用了星星預制資源 starPrefab: { default: null, type: cc.Prefab }, // 星星產生后消失時間的隨機范圍 maxStarDuration: 0, minStarDuration: 0, // 地面節點,用于確定星星生成的高度 ground: { default: null, type: cc.Node }, // player 節點,用于獲取主角彈跳的高度,和控制主角行動開關 player: { default: null, type: cc.Node } },
這里初學者可能會疑惑,為什么像 starPrefab 這樣的屬性會用 {} 括起來,括號里面還有新的 “屬性” 呢?其實這是屬性的一種完整聲明,之前我們的屬性聲明都是不完整的,有些情況下,我們需要為屬性聲明添加參數,這些參數控制了屬性在 屬性檢查器 中的顯示方式,以及屬性在場景序列化過程中的行為。例如:
properties: { score: { default: 0, displayName: "Score (player)", tooltip: "The score of player", } }
以上代碼為 score 屬性設置了三個參數 default、 displayName 和 tooltip。這幾個參數分別指定了 score的默認值(default)為 0,在 屬性檢查器 里,其屬性名(displayName)將顯示為 Score (player),并且當鼠標移到參數上時,顯示對應的 tooltip。
下面是常用參數:
default:設置屬性的默認值,這個默認值僅在組件第一次添加到節點上時才會用到
type:限定屬性的數據類型,詳見 CCClass 進階參考:type 參數
visible:設為 false 則不在屬性檢查器面板中顯示該屬性
serializable: 設為 false 則不序列化(保存)該屬性
displayName:在屬性檢查器面板中顯示成指定名字
tooltip:在屬性檢查器面板中添加屬性的 tooltip
所以上面的代碼:
starPrefab: { default: null, type: cc.Prefab },
就容易理解了,首先在 Game 組件下聲明了 starPrefab 屬性,這個屬性默認值為 null,能傳入的類型必須是 Prefab 預制資源類型。這樣之后的 ground、player 屬性也可以理解了。
保存腳本后將 Game 組件添加到 層級管理器 中的 Canvas 節點上(選中 Canvas 節點后,拖拽腳本到 屬性檢查器 上,或者點擊 屬性檢查器 的 添加組件 按鈕,并從 添加用戶腳本組件 中選擇 Game。)
接下來從 資源管理器 中拖拽 star 的 Prefab 資源到 Game 組件的 Star Prefab 屬性中。這是我們第一次為屬性設置引用,只有在屬性聲明時規定 type 為引用類型時(比如我們這里寫的 cc.Prefab 類型),才能夠將資源或節點拖拽到該屬性上。
接著從 層級管理器 中拖拽 ground 和 Player 節點到 Canvas 節點 Game 組件中相對應名字的屬性上,完成節點引用。
然后設置 Min Star Duration 和 Max Star Duration 屬性的值為 3 和 5,之后我們生成星星時,會在這兩個之間隨機取值,就是星星消失前經過的時間。
在隨機位置生成星星
接下來我們繼續修改 Game 腳本,在 onLoad 方法 后面 添加生成星星的邏輯:
// Game.js onLoad: function () { // 獲取地平面的 y 軸坐標 this.groundY = this.ground.y + this.ground.height/2; // 生成一個新的星星 this.spawnNewStar(); }, spawnNewStar: function() { // 使用給定的模板在場景中生成一個新節點 var newStar = cc.instantiate(this.starPrefab); // 將新增的節點添加到 Canvas 節點下面 this.node.addChild(newStar); // 為星星設置一個隨機位置 newStar.setPosition(this.getNewStarPosition()); }, getNewStarPosition: function () { var randX = 0; // 根據地平面位置和主角跳躍高度,隨機得到一個星星的 y 坐標 var randY = this.groundY + Math.random() * this.player.getComponent('Player').jumpHeight + 50; // 根據屏幕寬度,隨機得到一個星星 x 坐標 var maxX = this.node.width/2; randX = (Math.random() - 0.5) * 2 * maxX; // 返回星星坐標 return cc.v2(randX, randY); },
這里需要注意幾個問題:
保存腳本以后點擊 預覽游戲 按鈕,在瀏覽器中可以看到,游戲開始后動態生成了一顆星星!用同樣的方法,您可以在游戲中動態生成任何預先設置好的以 Prefab 為模板的節點。
添加主角碰觸收集星星的行為
現在要添加主角收集星星的行為邏輯了,這里的重點在于,星星要隨時可以獲得主角節點的位置,才能判斷他們之間的距離是否小于可收集距離,如何獲得主角節點的引用呢?別忘了我們前面做過的兩件事:
所以我們只要在 Game 腳本生成 Star 節點實例時,將 Game 組件的實例傳入星星并保存起來就好了,之后我們可以隨時通過 game.player 來訪問到主角節點。讓我們打開 Game 腳本,在 spawnNewStar 方法最后面添加一句 newStar.getComponent('Star').game = this;,如下所示:
// Game.js spawnNewStar: function() { // ... // 在星星組件上暫存 Game 對象的引用 newStar.getComponent('Star').game = this; },
保存后打開 Star 腳本,現在我們可以利用 Game 組件中引用的 player 節點來判斷距離了,在 onLoad 方法后面添加名為 getPlayerDistance 和 onPicked 的方法:
// Star.js getPlayerDistance: function () { // 根據 player 節點位置判斷距離 var playerPos = this.game.player.getPosition(); // 根據兩點位置計算兩點之間距離 var dist = this.node.position.sub(playerPos).mag(); return dist; }, onPicked: function() { // 當星星被收集時,調用 Game 腳本中的接口,生成一個新的星星 this.game.spawnNewStar(); // 然后銷毀當前星星節點 this.node.destroy(); },
Node 下的 getPosition() 方法 返回的是節點在父節點坐標系中的位置(x, y),即一個 Vec2 類型對象。同時注意調用 Node 下的 destroy() 方法 就可以銷毀節點。
然后在 update 方法中添加每幀判斷距離,如果距離小于 pickRadius 屬性規定的收集距離,就執行收集行為:
// Star.js update: function (dt) { // 每幀判斷和主角之間的距離是否小于收集距離 if (this.getPlayerDistance() < this.pickRadius) { // 調用收集行為 this.onPicked(); return; } },
保存腳本,再次預覽測試,通過按 A 和 D 鍵來控制主角左右移動,就可以看到控制主角靠近星星時,星星就會消失掉,然后在隨機位置生成了新的星星!
添加得分
小怪獸辛辛苦苦的收集星星,沒有獎勵怎么行?現在讓我們來添加在收集星星時增加得分獎勵的邏輯和顯示。
添加分數文字(Label)
游戲開始時得分從 0 開始,每收集一個星星分數就會加 1。要顯示得分,首先要創建一個 Label 節點。在 層級管理器 中選中 Canvas 節點,右鍵點擊并選擇菜單中的 創建新節點 -> 創建渲染節點 -> Label(文字),一個新的 Label 節點會被創建在 Canvas 節點下面,而且順序在最下面。接下來我們要用如下的步驟配置這個 Label 節點:
注意: Score: 0 的文字建議使用英文冒號,因為 Label 組件的 String 屬性加了位圖字體后,會無法識別中文的冒號。
完成后效果如下圖所示:
在 Game 腳本中添加得分邏輯
我們將會把計分和更新分數顯示的邏輯放在 Game 腳本里,打開 Game 腳本開始編輯,首先在 properties 區塊的 最后 添加分數顯示 Label 的引用屬性:
// Game.js properties: { // ... // score label 的引用 scoreDisplay: { default: null, type: cc.Label } },
接下來在 onLoad 方法 里面 添加計分用的變量的初始化:
// Game.js onLoad: function () { // ... // 初始化計分 this.score = 0; },
然后在 update 方法 后面 添加名叫 gainScore 的新方法:
// Game.js gainScore: function () { this.score += 1; // 更新 scoreDisplay Label 的文字 this.scoreDisplay.string = 'Score: ' + this.score; },
保存 Game 腳本后,回到 層級管理器,選中 Canvas 節點,然后把前面添加好的 score 節點拖拽到 屬性檢查器 里 Game 組件的 Score Display 屬性中。
在 Star 腳本中調用 Game 中的得分邏輯
下面打開 Star 腳本,在 onPicked 方法中加入 gainScore 的調用:
// Star.js onPicked: function() { // 當星星被收集時,調用 Game 腳本中的接口,生成一個新的星星 this.game.spawnNewStar(); // 調用 Game 腳本的得分方法 this.game.gainScore(); // 然后銷毀當前星星節點 this.node.destroy(); },
保存后預覽,可以看到現在收集星星時屏幕正上方顯示的分數會增加了!
失敗判定和重新開始
現在我們的游戲已經初具規模,但得分再多,不可能失敗的游戲也不會給人成就感?,F在讓我們加入星星定時消失的行為,而且讓星星消失時就判定為游戲失敗。也就是說,玩家需要在每顆星星消失之前完成收集,并不斷重復這個過程完成玩法的循環。
為星星加入計時消失的邏輯
打開 Game 腳本,在 onLoad 方法的 spawnNewStar 調用 之前 加入計時需要的變量聲明:
// Game.js onLoad: function () { // ... // 初始化計時器 this.timer = 0; this.starDuration = 0; // 生成一個新的星星 this.spawnNewStar(); // 初始化計分 this.score = 0; },
然后在 spawnNewStar 方法最后加入重置計時器的邏輯,其中 this.minStarDuration 和 this.maxStarDuration 是我們一開始聲明的 Game 組件屬性,用來規定星星消失時間的隨機范圍:
// Game.js spawnNewStar: function() { // ... // 重置計時器,根據消失時間范圍隨機取一個值 this.starDuration = this.minStarDuration + Math.random() * (this.maxStarDuration - this.minStarDuration); this.timer = 0; },
在 update 方法中加入計時器更新和判斷超過時限的邏輯:
// Game.js update: function (dt) { // 每幀更新計時器,超過限度還沒有生成新的星星 // 就會調用游戲失敗邏輯 if (this.timer > this.starDuration) { this.gameOver(); return; } this.timer += dt; },
最后,在 gainScore 方法后面加入 gameOver 方法,游戲失敗時重新加載場景。
// Game.js gameOver: function () { this.player.stopAllActions(); //停止 player 節點的跳躍動作 cc.director.loadScene('game'); }
這里需要初學者了解的是,cc.director 是一個管理你的游戲邏輯流程的單例對象。由于 cc.director 是一個單例,你不需要調用任何構造函數或創建函數,使用它的標準方法是通過調用 cc.director.methodName(),例如這里的 cc.director.loadScene('game') 就是重新加載游戲場景 game,也就是游戲重新開始。而節點下的 stopAllActions 方法就顯而易見了,這個方法會讓節點上的所有 Action 都失效。
以上,對 Game 腳本的修改就完成了,保存腳本,然后打開 Star 腳本,我們需要為即將消失的星星加入簡單的視覺提示效果,在 update 方法最后加入以下代碼:
// Star.js update: function() { // ... // 根據 Game 腳本中的計時器更新星星的透明度 var opacityRatio = 1 - this.game.timer/this.game.starDuration; var minOpacity = 50; this.node.opacity = minOpacity + Math.floor(opacityRatio * (255 - minOpacity)); }
保存 Star 腳本,我們的游戲玩法邏輯就全部完成了!現在點擊 預覽游戲 按鈕,我們在瀏覽器看到的就是一個有核心玩法、激勵機制、失敗機制的合格游戲了。
加入音效
盡管很多人玩手游的時候會無視聲音,我們為了教程展示的工作流程盡量完整,還是要補全加入音效的任務。
跳躍音效
首先加入跳躍音效,打開 Player 腳本,添加引用聲音文件資源的 jumpAudio 屬性:
// Player.js properties: { // ... // 跳躍音效資源 jumpAudio: { default: null, type: cc.AudioClip }, },
然后改寫 setJumpAction 方法,插入播放音效的回調,并通過添加 playJumpSound 方法來播放聲音:
// Player.js setJumpAction: function () { // 跳躍上升 var jumpUp = cc.moveBy(this.jumpDuration, cc.v2(0, this.jumpHeight)).easing(cc.easeCubicActionOut()); // 下落 var jumpDown = cc.moveBy(this.jumpDuration, cc.v2(0, -this.jumpHeight)).easing(cc.easeCubicActionIn()); // 添加一個回調函數,用于在動作結束時調用我們定義的其他方法 var callback = cc.callFunc(this.playJumpSound, this); // 不斷重復,而且每次完成落地動作后調用回調來播放聲音 return cc.repeatForever(cc.sequence(jumpUp, jumpDown, callback)); }, playJumpSound: function () { // 調用聲音引擎播放聲音 cc.audioEngine.playEffect(this.jumpAudio, false); },
這里需要強調的是回調函數的作用,我們首先來看官方對 callFunc() 方法的定義:
/** * !#en Creates the action with the callback. * !#zh 執行回調函數。 * @method callFunc * @param {function} selector * @param {object} [selectorTarget=null] * @param {*} [data=null] - data for function, it accepts all data types. * @return {ActionInstant} * @example * // example * // CallFunc without data * var finish = cc.callFunc(this.removeSprite, this); * * // CallFunc with data * var finish = cc.callFunc(this.removeFromParentAndCleanup, this._grossini, true); */ cc.callFunc = function (selector, selectorTarget, data) { return new cc.CallFunc(selector, selectorTarget, data); };
我們可以看到 callFunc 方法可以傳入三個參數,第一個參數是方法的 selector,我們可以理解為方法名。第二個參數是 Object 類型,一般填入 this。第三個參數為帶回的數據,可以是所有的數據類型,可以不填。我們再注意到這個方法的返回值 —— ActionInstant,這是一個瞬間執行的動作類。到這里我們就可以理解了,使用 callFunc 調用回調函數可以讓函數轉變為 cc 中的 Action(動作),這一用法在 cc 的動作系統里非常實用!例如在上面我們將播放聲音的函數傳入 callFunc 賦值給 callback,讓 callback 成為了一個播放聲音的動作 Action,那么我們之后就能通過 cc.sequence 將跳躍和播放聲音的動作組合起來,實現每跳一次就能播放音效的功能!
得分音效
保存 Player 腳本以后打開 Game 腳本,來添加得分音效,首先仍然是在 properties 中添加一個屬性來引用聲音文件資源:
// Game.js properties: { // ... // 得分音效資源 scoreAudio: { default: null, type: cc.AudioClip } },
然后在 gainScore 方法里插入播放聲音的代碼:
// Game.js gainScore: function () { this.score += 1; // 更新 scoreDisplay Label 的文字 this.scoreDisplay.string = 'Score: ' + this.score.toString(); // 播放得分音效 cc.audioEngine.playEffect(this.scoreAudio, false); },
保存腳本,回到 層級管理器 ,選中 Player 節點,然后從 資源管理器 里拖拽 assets/audio/jump 資源到 Player 組件的 Jump Audio 屬性上。
然后選中 Canvas 節點,把 assets/audio/score 資源拖拽到 Game 組件的 Score Audio 屬性上。
這樣就大功告成了!完成形態的場景層級和各個關鍵組件的屬性如下:
現在我們可以盡情享受剛制作完成的游戲了,您能打到多少分呢?別忘了您可以隨時修改 Player 和 Game 組件里的移動控制和星星持續時間等游戲參數,來快速調節游戲的難度。修改組件屬性之后需要保存場景,修改后的數值才會被記錄下來。
總結
恭喜您完成了用 Cocos Creator 制作的第一個游戲!希望這篇快速入門教程能幫助您了解 Cocos Creator 游戲開發流程中的基本概念和工作流程。如果您對編寫和學習腳本編程不感興趣,也可以直接從完成版的項目中把寫好的腳本復制過來使用。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。