頁遊《火影忍者》角色和背景遮擋半透明效果的實現

這裏講的是關於2D遊戲的角色和背景以及物體之間的遮擋關係,用半透明角色處理的討論和實現方式。這裏主要是討論關於頁遊《火影忍者》裏對於角色和背景物體之間的遮擋處理方式。同時也實現了和他一樣的效果(可能)。具體是沒分析火影的代碼,只是猜測了它的原理然後進行實現的。其他的幾種實現方式則簡單的討論,一筆帶過(如果有人有興趣,可以留言,我再單獨寫篇Blog和相關例子來)
實現語言:ActionScript3.0(傳統的顯示處理,Stage3D可以做到有些不一樣,所以不在討論範圍)
心急的讀者可以直接從第三點開始看我們今天的主題實現內容。

一、需要實現遮擋關係的原因

個人推測和分析,不妥之處請之處討論。
1. 因爲AS3傳統顯示2D圖像的時候,沒有zbuff緩衝這個概念,所以顯示圖像的層次關係,是按照addChild的順序來的.
2. 爲了實現最大的渲染效率,一般美術會把遊戲場景渲染成大的一張圖片,然後再切割成小塊進行加載顯示。整個場景的所有元件,包括背景、樹、建築、河流等等,都是渲染出一張圖。
那麼問題來了,建築和背景樹等物體都渲染地圖上,那人物走過來的時候,這個時候的遮擋關係怎麼處理呢?所以就會衍生出各種處理這種遮擋關係的技術了。

二、角色物體遮擋的幾種做法

首先簡單討論幾種實現方式。
1. 和角色同層,根據深度重新設置addChild
這是比較消耗性能的做法,也是早期很多遊戲的做法,表現效果也很好。同時也是一些3D遊戲場景的做法(3D有zbuff緩衝)。出一個地圖編輯器,然後把導入背景,把各種元件(樹、建築等等)拖到場景去,最後導出。在遊戲中人物就和元件參與深度排序,處理遮擋關係。
現在一般是採用後面的兩種做法了,在一些特別需要表現力的時候,纔會單獨做成元件,不然都是合在背景裏了。
這裏寫圖片描述
2. 地圖編輯器打格子表明走到的角色半透
這個是頁遊紅極一時的做法,大部分rpg和arpg都是這麼做的。比如《凡人修真2》和雄霸頁遊天下的《傳奇霸業》,下面傳奇霸業的表現效果(剛截的圖)。
這裏寫圖片描述
具體的實現方式大概走格子走到這個建築的時候,會檢測格子的屬性,如果帶有半透效果的參數,則設置角色的alpha值。
3. 背建築遮擋的部分才半透,效果高大尚。
讀者:這個是3D做法吧!
當然不是了,通過2D圖像混合一樣可以做到。(原理估計也是3D那一套。就是提供的遮罩圖來擦出下面的的圖片)。這個算是比較新穎的做法,目前我就看騰訊的《火影忍者》,其他的就暫時沒去查看了(我自己做的遊戲是採用這個最新的做法,潮流嘛)。
開始還擔心性能會比較大損耗,做了簡單測試,發現沒什麼影響。先看火影裏的表現效果吧
這裏寫圖片描述
仔細看紅框裏的那個角色,可以發現角色被那個柱子擋住的一半是半透明效果,沒擋住的就是原來的顏色。簡直酷斃了!
立馬把火影的緩存搞下來,發現它是加了背景遮罩圖,然後類似3D混合圖形的做法實現的。後來會詳細地講。(囉嗦了這麼久,終於開始正題了)

三、火影的角色遮擋半透實現分析

仔細分析了火影的新手村場景,從緩存了除了提取出背景之外,還有另外一些黑色的古怪圖片。背景還是跟其他遊戲的做法,古怪的是那個黑色怪圖。發現是背景裏的一些建築物的黑色圖。下面是縮略圖。
這裏寫圖片描述
剛開始還以爲是逐像素匹配,自己手動實現的(可以做到的),但是效率會殘不忍堵。另外想到的就是3D裏
常用的圖像混合處理了。查下AS3的Bitmap的API,還真查找到了。這裏只列出會用到的API。

DisplayObject.blendMode屬性
BlendMode 類中的一個值,用於指定要使用的混合模式。

用到了BlendMode類的兩個屬性

LAYER : String = "layer"
[靜態] 強制爲該顯示對象創建一個透明度組。
ERASE : String = "erase"
[靜態] 根據顯示對象的 Alpha 值擦除背景。

其他的API也有趣,有興趣的同學可以試試。

四、角色遮擋半透的實現的例子資源

整理了一下相關使用到的資源。實際上問過美術,美術說出這個遮罩圖非常容易。我們也實際出了相關的資源。
1. 背景圖
把火影的背景圖給拼起來,合成一張1274 * 768的大背景圖方便測試
2. 透明背景遮罩圖
同樣把透明遮罩剪切出和背景對應部分截取出來,然後對好位置。這個圖的透明度決定了角色的顯示效果
3. 角色資源圖
這裏寫圖片描述

五、簡單的代碼實現原理過程

  1. 背景層獨立出來,不參與圖像混合
  2. 角色和透明遮罩單獨一個容器層,但是這兩種都在同一個容器A中
  3. 容器A的blendMode屬性設置爲:
//強制爲該顯示對象創建一個透明度組
blendMode = BlendMode.LAYER;
  1. 角色容器不用設置blendMode,因爲在容器A中設置就可以了
  2. 透明遮罩這個Bitmap需要設置
blendMode = BlendMode.ERASE;

確保透明遮罩Bitmap在角色容器的上層就可以了。下面是具體的代碼實現過程。

六、詳細的代碼

看代碼實現起來非常簡單,代碼也非常簡潔

package
{
    import flash.display.Bitmap;
    import flash.display.BlendMode;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;

    /**
     * 地圖透明角色測試例子
     * @author sodaChen
     * Date:2017-2-16
     */
    [SWF(width="1274",height="768")]
    public class AlphaMapTest extends Sprite
    {
        /** 背景 **/
        [Embed(source = "res/alpha/bg.jpg")]
        private var bgClass:Class;
        /** 透明遮罩背景 **/
        [Embed(source = "res/alpha/alphaBg.png")]
        private var alphaBgClass:Class;
        /** 角色 **/
        [Embed(source = "res/alpha/role.png")]
        private var roleClass:Class;

        /** 角色容器 ,用來存放角色和透明圖像的**/
        private var roleContainer:Sprite;
        /** 角色層,只放角色 **/
        private var roleLayer:Sprite;


        public function AlphaMapTest()
        {
            super();
            addEventListener(Event.ADDED_TO_STAGE,onStage);
        }
        private function onStage(evt:Event):void
        {
            //添加背景
            addChild(new bgClass());

            //添加角色容器
            roleContainer = new Sprite();
            //強制爲該顯示對象創建一個透明度組
            roleContainer.blendMode = BlendMode.LAYER;
            addChild(roleContainer);

            //創建角色層,其實角色可以不用單獨容器,但是必須保證alphaBg在所有角色的最上面
            roleLayer = new Sprite();
            roleContainer.addChild(roleLayer);

            //創建角色並添加到角色容器中
            createRole(300,120);
            createRole(230,550);
            //不會被遮擋的角色
            createRole(400,200);

            //根據顯示對象的 Alpha 值擦除背景.這個透明圖像必須在最頂層,確保下面的角色會被擦出
            var alphaBg:Bitmap = new alphaBgClass();
            alphaBg.blendMode = BlendMode.ERASE;
            roleContainer.addChild(alphaBg);
        }
        //創建角色
        private function createRole(roleX:int,roleY:int):void
        {
            var role:Sprite = new Sprite();
            var roleBitmap:Bitmap = new roleClass();
            role.x = roleX;
            role.y = roleY;
            role.addChild(roleBitmap);
            roleLayer.addChild(role);
            role.addEventListener(MouseEvent.MOUSE_DOWN,onMouse);
            role.addEventListener(MouseEvent.MOUSE_UP,onMouse);
        }
        private function onMouse(evt:MouseEvent):void
        {
            var role:Sprite = evt.currentTarget as Sprite;
            if(evt.type == MouseEvent.MOUSE_DOWN)
                role.startDrag();
            else
                role.stopDrag();
        }
    }
}

最終實現的效果圖:
這裏寫圖片描述
例子代碼下載:
2DAS3遊戲地圖角色遮罩

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章