mthoutai

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

近期刚好有做2D游戏的点光源效果,然后就扩展一下。研究了一下战争迷雾的效果。主要是想实现相似魔兽争霸那种人物走动,然后黑色的战争迷雾随着人物的移动渐渐打开的效果。使用具有渐变透明图片作为光源来使得战争迷雾呈现出平滑的效果。

本文后面介绍了两个简单的实现方法。效果有细微的区别。有兴趣的同学能够分别研究。最后也有完整展示代码和提供样例下载。

一、常见的战争迷雾效果

早期的红警的战争迷雾大家应该也比較熟悉,只是看起来没那么平滑,应该是採用图块拼出来。能够明显看得出一些方方块块。
这里写图片描写叙述
可见早期魔兽争霸2也是没那么平滑的。
后面出的一些有些的战争迷雾就会有比較平滑的过渡效果了。比方英雄联盟,魔兽3(这两个是3D的)。可是也有2D也做得比較平滑(看图的最后一个截图)
这里写图片描写叙述

二、实现的简单原理解说

由于是做2D游戏,所以思路基本上是2D,可是同一时候也有3D思想,比如图层混合方式处理等。主要原理就是依据图片的alpha值来进行反向擦除(alpha为1时全然擦除,alpha越小则越不透明。实现渐变过程效果)。最后用了两种比較简单的方法来实现了这样的效果。两者有微弱的区别。这里採用的是AS3实现的,源代码也提供了几种渐变图片,能够作为点光源来擦亮迷雾。能够替换上去看各种效果的。
这里写图片描写叙述

  1. 遮罩擦除做法
    最先想到的原理。是基于之前实现相似点光源效果的做法。通过一层带有alpha值遮罩图来擦掉相应的战争迷雾,就是移动版增大的点光源效果。首先人物背景。然后一层战争迷雾在最顶层,人物带了个点光源。然后人物移动的时候,不会把那个点光源层进行绘制,那么光源层就会原来越大。那个迷雾自然就会越来越大了。
    下图:刚開始是一个圆圈,然后随着人物移动,圆圈会扩大。


    这里写图片描写叙述
    得到这个图之后。就是对黑色战争迷雾层依据alpha进行擦了。

//一个专门做点光源的顶层容器
var topContainer:Sprite = new Sprite();
//强制为该显示对象创建一个透明度组
topContainer.blendMode = BlendMode.LAYER;
//依据显示对象的 Alpha 值擦除迷雾
openFogBitmap.blendMode = BlendMode.ERASE;

合成遮罩图来去除迷雾的代码是,最后一个參数true是合并alpha:

bitmapData.copyPixels(bitmapData,pointRect,new Point(role.x,role.y),null,null,true);

这样尾随人物移动不断地把遮罩扩大。除了最開始的合成遮罩图,后面的处理跟之前讲的新手引导遮罩和点光源实现机制一样。后面会给出相关的代码。

只是这样的实现是有点不好的是合并alpha,这样会导致范围突然变亮(由于alpha相加大于1,就所有擦了,大部分亮了,也就是会有个逐渐变亮的效果,使得战争迷雾开启效果没那么真实)。

终于表现效果例如以下图:
这里写图片描写叙述
2. 直接擦出战争迷雾方法(橡皮擦功能)
实际測试了下,对遮罩擦除做法这个效果不太惬意。于是再研究了一下,想到了橡皮效果,直接用点光源图片把战争迷雾一点点擦掉又怎样呢?赶紧细致看了相关api,还真有相似的实现效果。

主要还是bitmapData的draw方法。重点是这种方法的第四个參数,

source:IBitmapDrawable — 要绘制到 BitmapData 对象的显示对象或 BitmapData 对象 
matrix:Matrix (default = null) — 一个 Matrix 对象。用于缩放、旋转位图或转换位图的坐标。

。 colorTransform:flash.geom:ColorTransform (default = null) — 一个 ColorTransform 对象 blendMode:String (default = null) — 指定要应用于所生成位图的混合模式。

所以我们每次在战争迷雾这个层次这里每次依据玩家移动,调用draw方法把角色带的点光源图片给draw进入战争迷雾的BitmapData中,然后设置为依据alpha的參数来擦出,露出终于的背景即可了。

BlendMode.ERASE //提供混合模式可视效果的常量值的类。

//设置须要draw的坐标位置 var matrix:Matrix = new Matrix(1,0,0,1,role.x,role.y); fogBitmapData.draw(pointBitmap,matrix,null,BlendMode.ERASE);

终于效果图:
这里写图片描写叙述

代码实现

代码已经有比較详细的凝视了,这里不做解释。详细自己看代码了。能够执行两个样例来比較。
代码样例源代码下载:2D游戏战争迷雾的实现样例(AS3版本号)
1. 遮罩擦除做法代码。FogLightTest.as

/**
     * 战争迷雾遮罩灯效果測试样例
     * @author sodaChen
     * Date:2017-2-16
     */
    [SWF(width="1274",height="768")]
    public class FogLightTest extends Sprite
    {
        /** 背景 **/
        [Embed(source = "res/alpha/bg.jpg")]
        private var bgClass:Class;
        //点光源图片
        [Embed(source = "res/alpha/light4.png")]
        private var shadowClass:Class;
        /** 打开的迷雾图像源,用来擦出迷雾 **/
        private var openFogBitmap:Bitmap;
        /** 原始点光源图片。用来确定一个角色视野范围的 **/
        private var pointBitmap:Bitmap;
        /** 存放已经开启的迷雾图片 **/
        private var pointBitmapDatas:Vector.<uint>;
        /** 点光源的大小范围 **/
        private var pointRect:Rectangle;
        private var role:Sprite;
        /** 光源的移动 **/
        private var speed:int = 10;


        public function FogLightTest()
        {
            addEventListener(Event.ADDED_TO_STAGE,onStage);
        }
        private function onStage(evt:Event):void
        {
            //加入背景
            addChild(new bgClass());
            /////////////////////////////文本的正式測试代码啦/////////////////////////////
            //新建一个专门做点光源的顶层容器
            var topContainer:Sprite = new Sprite();
            topContainer.mouseEnabled = false;
            //强制为该显示对象创建一个透明度组
            topContainer.blendMode = BlendMode.LAYER;
            addChild(topContainer);
            //创建黑色的迷雾
            var mask:Shape = new Shape();
            mask.graphics.beginFill(0x000000);
            mask.graphics.drawRect(0,0,1274,768);
            mask.graphics.endFill();
            topContainer.addChild(mask);

            //制作点光源,用来擦亮迷雾,详细的擦出在以下的鼠标事件那里
            pointBitmap = new shadowClass();
            //创建擦亮迷雾后的视野图片
            pointRect = new Rectangle(0,0,pointBitmap.bitmapData.width,pointBitmap.bitmapData.height);
            openFogBitmap = new Bitmap(new BitmapData(1274,768,true,0));
            //复制最開始的位置
            openFogBitmap.bitmapData.copyPixels(pointBitmap.bitmapData,pointRect,new Point(450,300));
            //依据显示对象的 Alpha 值擦除迷雾
            openFogBitmap.blendMode = BlendMode.ERASE;
            topContainer.addChild(openFogBitmap);

            /** 移动中的主角 **/
            role = new Sprite();
            role.x = 450; role.y = 300;
            topContainer.addChild(role);
            stage.addEventListener(KeyboardEvent.KEY_DOWN,onKeyDown);
        }

        private function onKeyDown(evt:KeyboardEvent):void
        {
            //4个方向键的控制
            if(evt.keyCode == Keyboard.DOWN) role.y += speed;
            else if(evt.keyCode == Keyboard.UP) role.y -= speed;
            else if(evt.keyCode == Keyboard.LEFT) role.x -= speed;
            else if(evt.keyCode == Keyboard.RIGHT) role.x += speed;
            //临时不考虑性能,不停地写新的图像数据
    openFogBitmap.bitmapData.copyPixels(pointBitmap.bitmapData,pointRect,new Point(role.x,role.y),null,null,true);
        }
  1. 直接擦出战争迷雾方法(橡皮擦功能)代码,EraserFogTest.as
   /**
     * 迷雾战争擦除效果
     * @author sodaChen
     * Date:2017-2-16
     */
    [SWF(width="1274",height="768")]
    public class EraserFogTest extends Sprite
    {
        /** 背景 **/
        [Embed(source = "res/alpha/bg.jpg")]
        private var bgClass:Class;
        //点光源图片
        [Embed(source = "res/alpha/light4.png")]
        private var shadowClass:Class;
        /** 打开的迷雾图像源。用来擦出迷雾 **/
        private var openFogBitmap:Bitmap;
        /** 光源,擦亮迷雾的范围 **/
        private var pointBitmap:Bitmap;
        private var role:Sprite;
        /** 光源的移动 **/
        private var speed:int = 10;
        /** 迷雾的图像数据源 **/
        private var fogBitmapData:BitmapData;

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

            /////////////////////////////文本的正式測试代码啦/////////////////////////////
            //新建一个专门做点光源的顶层容器
            var topContainer:Sprite = new Sprite();
            topContainer.mouseEnabled = false;
            //强制为该显示对象创建一个透明度组
            topContainer.blendMode = BlendMode.LAYER;
            addChild(topContainer);
            //创建黑色的迷雾
            var mask:Shape = new Shape();
            //颜色能够选自己喜欢的
            mask.graphics.beginFill(0x000000);
            mask.graphics.drawRect(0,0,1274,768);
            mask.graphics.endFill();
            fogBitmapData = new BitmapData(1274,768);
            fogBitmapData.draw(mask);
            topContainer.addChild(new Bitmap(fogBitmapData));

            //制作点光源。用来擦亮迷雾,详细的擦出在以下的鼠标事件那里
            pointBitmap = new shadowClass();

            /** 移动中的主角 **/
            role = new Sprite();
            role.x = 450; role.y = 300;
            topContainer.addChild(role);
            //默认位置
            openFog();
            stage.addEventListener(KeyboardEvent.KEY_DOWN,onKeyDown);
        }

        private function onKeyDown(evt:KeyboardEvent):void
        {
            //4个方向键的控制
            if(evt.keyCode == Keyboard.DOWN) role.y += speed;
            else if(evt.keyCode == Keyboard.UP) role.y -= speed;
            else if(evt.keyCode == Keyboard.LEFT) role.x -= speed;
            else if(evt.keyCode == Keyboard.RIGHT) role.x += speed;
            openFog();
        }
        private function openFog():void
        {
            //设置须要draw的坐标位置
            var matrix:Matrix = new Matrix(1,0,0,1,role.x,role.y);
            //正常的daw方法,主要參数是后面的BlendMode.ERASE。

依据pointBitmap的透明度来擦除fogBitmapData fogBitmapData.draw(pointBitmap,matrix,null,BlendMode.ERASE); }

posted on 2017-08-01 19:47  mthoutai  阅读(766)  评论(0编辑  收藏  举报