Cocos 2.x 坐標系

坐標系和節點變換屬性

一、概念
1.錨點Anchor

錨點由 anchorX 和 anchorY 兩個值表示,他們是通過節點尺寸計算錨點位置的乘數因子,范圍都是 0 ~ 1 之間。(0.5, 0.5) 表示錨點位于節點長度乘 0.5 和寬度乘 0.5 的地方,即節點的中心。


image.png

錨點屬性設為 (0, 0) 時,錨點位于節點本地坐標系的初始原點位置,也就是節點約束框的左下角。


image.png
2.坐標軸方向

x軸都是向右的,y軸是向上的。


image.png
3.坐標軸原點

這里比較有特色了。

先說世界坐標系。世界坐標系也叫做絕對坐標系,在 Cocos Creator 游戲開發中表示場景空間內的統一坐標體系,「世界」就用來表示我們的游戲場景。世界坐標系的原點在屏幕的左下角。

然后就是本地坐標系。本地坐標系也叫相對坐標系,是和節點相關聯的坐標系。每個節點都有獨立的坐標系,當節點移動或改變方向時,和該節點關聯的坐標系將隨之移動或改變方向。

所有子節點就會以 錨點所在位置 作為坐標系原點,注意這個行為和 cocos2d-x 引擎中的默認行為不同,是 Cocos Creator 坐標系的特色!

二、驗證

image.png

創建了一個ppp的scene,然后自動創建了一個Canvas,Canvas是處于世界坐標系當中的。

這時候再拖進去一張圖片,設置Anchor為0.5,0.5,坐標為0,0。因為這張圖也處于世界坐標系中,所以就會顯示在左下角。并且是圖片的中心點處于左下角,這也說明Anchor決定了一張圖片以哪個像素點做為自己的位置參考點。比如,把Anchor改為0,0。此時這張圖片的左下角會和世界坐標系的左下角對齊。

1.canvas
image.png

繼續來做驗證,Canvas寬高設置為960,640。Anchor設置為0.5,0.5。這表明,當我們設置它的位置時,實際上它的參考點是中心點,也就是480,320那個像素。Canvas本身處于世界坐標系,如果想讓它居中,那么Position應該設置多少呢?很明顯,相對于左下角的坐標原點,正是480,320。

2.canvas中的子節點

再拖進去一個圖片,設置Anchor為0.5,0.5。然后設置Position為0,0。此時,特色系統發揮作用了,canvas的坐標原點在其Anchor位置,所以新拖的圖片會被顯示在正中心。

這個時候,用肉眼確認一下這張新圖片,在世界坐標系中的坐標值,很顯然是480,320。然后掛個腳本組件,把這張圖片命名為pic1,使用API再驗證一下:

cc.log(this.pic1.convertToWorldSpaceAR(cc.v2(0,0)));

convertToWorldSpaceAR的作用是,將節點坐標系下的一個點轉換到世界空間坐標系。

如果此時,把pic1的Anchor改為0,0。在顯示上確實不一樣了,圖片會偏向右上方。但是convertToWorldSpaceAR打印的,仍然是480,320。如果此時把pic1的Position改為300,0。圖片會向右方移動,convertToWorldSpaceAR打印的,會變成780,320。

3.再嵌入一個子節點

把pic1里面,再嵌入一個小星星圖片叫star。當設置pic1的Anchor為0,0。設置star的Anchor為0,0;Postion為0,0時,發現star確實是在pic1的左下角。這驗證了,子節點以 錨點所在位置 作為坐標系原點。這時同樣可以驗證star在世界坐標系的位置:cc.log(this.pic1.getChildByName("star").convertToWorldSpaceAR(cc.v2(0,0)));

三、為什么搞成這樣子

參考位置系統對程序員來說極度不友好,摘抄一部分過來:

Q:原來寫一個需要居中的節點node.setPosition(parent.width/2,parent.height/2),現在需要寫的 node.setPosition(parent.width*(0.5-parent.anchorX),parent.height*(0.5-parent.anchorY))。現在的很容易忘記寫現在的這種寫法 一旦父節點的錨點不在中心點,位置就不會居中。

比如有一組卡牌,有規律排列, 原來通過獲取坐標很容易得出是第幾張, 現在這是不容易的, 如果不考慮父節點錨點就會出錯. 而錨點除了運動外是常被忽略的, 如果后期不經意變動,那就會出問題.

A:居中的節點請讓美術直接在子節點上加 Widget,設置對齊到 horizontal center 或 vertical center 就可以了,不需要再寫代碼了(而且你的代碼只能對齊一次,Widget 可以在運行時保持對齊狀態)

忘記需要考慮錨點位置是改變規則以后的正常現象,請多一點耐心,轉換一下思路,相信很快就能看到新規則帶來的各種好處(尤其是編輯器里拼場景時)。

關于你說的通過位置獲取索引的問題,我理解你從以前經驗中產生的需求是「隨手設置一個可以讓父節點旋轉的點作為錨點,子節點排列不受錨點影響」,但現在整個工作流都重新設計過了,比如卡牌索引的問題,應該是動態創建的時候就保存好索引,這樣根本不用去算位置;而需要旋轉父節點時,只要知道子節點會圍繞父節點的錨點旋轉,再根據需要重新設置好錨點位置就可以了吧?

以編輯器為核心搭建 UI 的工作流程和以前用代碼寫 UI 的流程是完全不同的,所以需要修改一些系統來更好的匹配現在的工作流程。對于舊項目如何移植,我們之后會推出更多案例給大家參考。

我再總結一下錨點的作用。
錨點的作用無非兩個,標識美術關鍵元素和方便策劃布局。

  • 標識美術關鍵點:
    這種錨點是由美術在場景里設置的,就像我前面舉例的,十字準心和箭頭。
    此時父節點旋轉,子物體必須繞著父物體錨點動 (現在滿足,原先滿足)。
    父節點錨點位置如果變化,子物體應該自動跟著移動 (現在滿足,原先不滿足)。

  • 方便策劃布局:
    如果是卡牌背景圖,按鈕,圖標這些 UI 元素,美術是無所謂錨點在哪的,一般都是策劃在編輯器里根據布局需要設置好的。
    我們現在鼓勵的是策劃使用 widget 來做定位,策劃其實不再需要通過 anchor 來做布局了。
    就算要用 anchor 布局,如果 anchor 需要變,一般是由于 UI 有了新的布局需求,本來就不是改一個 anchor 能解決的事情,更不會有策劃手賤去改一個本來設得好好的 anchor。
    就算改錯了,策劃也不可能不預覽結果,有錯誤他自己會及時在編輯器下處理,不需要經過程序解決。

所以你帖子標題說的“位置系統對程序員來說極度不友好”應該是不成立的。

四、converToWorldSpaceAR和converToWorldSpace區別

參考 使用convertToNodeSpace()的一個問題

node.convertToWorldSpace(cc.p(0,0))是將node的0,0(忽略錨點,也就是node的左下角)這個位置轉換為世界坐標,所以得到的位置應該是node的左下角相對于世界坐標系原點的偏移量。如果你要獲取node在世界坐標系的位置,應該使用Canvas.converToWorldSpaceAR(node),這樣才能獲取node在世界坐標系的position

關于這一點,看一下源碼,更清楚:

//CCNode.js:

 * !#zh 將一個相對于節點左下角的坐標位置轉換到世界空間坐標系。
 * 這個 API 的設計是為了和 cocos2d-x 中行為一致,
 * 更多情況下你可能需要使用 convertToWorldSpaceAR
 * @method convertToWorldSpace
 * @param {Vec2} nodePoint
 * @return {Vec2}
 * @example
 * var newVec2 = node.convertToWorldSpace(cc.v2(100, 100));
 */
convertToWorldSpace (nodePoint) {
    this._updateWorldMatrix();
    let out = new cc.Vec2(
        nodePoint.x - this._anchorPoint.x * this._contentSize.width,
        nodePoint.y - this._anchorPoint.y * this._contentSize.height
    );
    return vec2.transformMat4(out, out, this._worldMatrix);
},

 * !#zh
 * 將節點坐標系下的一個點轉換到世界空間坐標系。
 * @method convertToWorldSpaceAR
 * @param {Vec2} nodePoint
 * @return {Vec2}
 * @example
 * var newVec2 = node.convertToWorldSpaceAR(cc.v2(100, 100));
 */
convertToWorldSpaceAR (nodePoint) {
    this._updateWorldMatrix();
    let out = new cc.Vec2();
    return vec2.transformMat4(out, nodePoint, this._worldMatrix);
},
五、實例

參考快速上手:制作第一個游戲 摘星星,這里可以下載最終完成的項目

1.Game.js
//Game.js

onLoad: function () {
    // 獲取地平面的 y 軸坐標
    this.groundY = this.ground.y + this.ground.height/2;
    ...

spawnNewStar: function() {
    var newStar = null;
    // 使用給定的模板在場景中生成一個新節點
    if (this.starPool.size() > 0) {
    // this will be passed to Star's reuse method
        newStar = this.starPool.get(this);
    } else {
        newStar = cc.instantiate(this.starPrefab);
    }
    // 將新增的節點添加到 Canvas 節點下面
    this.node.addChild(newStar);
    // 為星星設置一個隨機位置
    newStar.setPosition(this.getNewStarPosition());
    ...

getNewStarPosition: function () {
    // if there's no star, set a random x pos
    if (!this.currentStar) {
        this.currentStarX = (Math.random() - 0.5) * 2 * this.node.width/2;
    }
    var randX = 0;
    // 根據地平面位置和主角跳躍高度,隨機得到一個星星的 y 坐標
    var randY = this.groundY + Math.random() * this.player.jumpHeight + 50;
    // 根據屏幕寬度和上一個星星的 x 坐標,隨機得到一個新生成星星 x 坐標
    var maxX = this.node.width/2;
    if (this.currentStarX >= 0) {
        randX = -Math.random() * maxX;
    } else {
        randX = Math.random() * maxX;
    }
    this.currentStarX = randX;
    // 返回星星坐標
    return cc.v2(randX, randY);
},

這個Game.js腳本,是掛在Canvas上的。也就是說this.node.addChild添加星星時,實際上添加到了Canvas坐標系中。

  • 地平面groundY計算時,也就是找地面圖片最頂端的坐標,因為上面說了,Anchor決定了一張圖片以哪個像素點做為自己的位置參考點,這里ground的Anchor設置了0.5,0.5。所以this.groundY = this.ground.y + this.ground.height/2

  • randY就是地平面加上,跳躍高度最大值,中間的一個隨機值。后面還有個+50是為什么呢?可以測試一下,添加randY = this.groundY;,也就是Math.random()取0時,如果不加50,randY就是地平面的位置。此時因為添加的星星star的Anchor設置了0.5,0.5,將會出現星星有一半埋在地下線下面,顯然不合理。

  • maxX 這里Canvas的坐標原點就在屏幕中心,所以randX是分正負的,也就是var maxX = this.node.width/2;randX = -Math.random() * maxX;當然,為了游戲更合理,randX總是會出現在上一次的位置坐標完全相反的半軸,這是通過currentStarX 這個變量來標記的。

2.Player.js
// called every frame
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;

    // limit player position inside screen
    if ( this.node.x > this.node.parent.width/2) {
        this.node.x = this.node.parent.width/2;
        this.xSpeed = 0;
    } else if (this.node.x < -this.node.parent.width/2) {
        this.node.x = -this.node.parent.width/2;
        this.xSpeed = 0;
    }
},

Player.js也是掛在Canvas上的。這里有邊緣檢測,如何判斷橫向移動出界:this.node.x > this.node.parent.width/2

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,327評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,996評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,316評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,406評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,128評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,524評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,576評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,759評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,310評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,065評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,249評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,821評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,479評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,909評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,140評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,984評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,228評論 2 375

推薦閱讀更多精彩內容

  • (二)坐標系和節點變換屬性 標簽(空格分隔): 內容創作工作流程 在 場景編輯器 和 節點和組件 文檔中,我們介紹...
    菜鳥_一枚閱讀 1,160評論 0 0
  • 無論是2d還是3d游戲開發,圖形學都是基礎,解析幾何的基本思想是將幾何圖形抽象成點的運動軌跡,從而點可以作為組成圖...
    tmgg閱讀 12,855評論 2 4
  • 筆者在上篇文章中對ARKit進行了簡單介紹,在本篇文章中主要介紹ARKit中經常用到的API及使用方法。了解這些A...
    未明一二閱讀 1,219評論 1 7
  • 游戲開發中,有了坐標系才有了整個世界,坐標系是游戲世界的標尺,讓游戲元素正確有序得顯示在屏幕上而不會分崩離析。 標...
    JunChow520閱讀 1,811評論 0 3
  • 沒有人會拒絕陽光和溫暖。 勒妮和帕洛瑪在眾人看來就像一只刺猬,不太平易近人,甚至有點尖酸刻薄,沒有人會真正肯去擁抱...
    桔子汽水醬閱讀 237評論 0 0