2D游戏平滑的迷雾战争效果

最近刚好有做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);
        }
时间: 2024-10-26 02:33:00

2D游戏平滑的迷雾战争效果的相关文章

?Unity 2D游戏开发教程之2D游戏的运行效果

Unity 2D游戏开发教程之2D游戏的运行效果 2D游戏的运行效果 本章前前后后使用了很多节的篇幅,到底实现了怎样的一个游戏运行效果呢?或者说,游戏中的精灵会不会如我们所想的那样运行呢?关于这些疑问,会在本节集中揭晓. (1)单击Unity上方,工具栏里的播放按钮,开始运行当前的游戏,默认精灵当前进入的是Idle动画状态,如图1-34所示. 图1-34  Idle状态 (2)当读者按下键盘上的左.右方向键,或者A.D键的时候,精灵会进入Walking动画状态,并且会向左或者向右移动,如图1-3

IOS 2D游戏开发框架 SpriteKit

最近发现Xcode自带的2D游戏开发框架SpriteKit可以直接引入到APP中进行混合开发,这就是说可以开发出既带业务应用又带游戏的苹果APP,咋怎么觉得这是一个自己的小发现....呵呵.....,查了下其实人家早有人这样做了........发现这功能我当然很开了,所以下了两个案例准备学学.以前业余时间也学过一下cocos2d-x这样的跨平台游戏框架,也做过小案例,所以感觉这个框架并不麻烦,而且比cocos2d-x简单.并且这框架我不应该像C2D-X那样学了就扔哪里不管了,因为IOS开发正是我

【2D游戏引擎】WIP反思

WIP(Working In Progress)是我初学游戏引擎开发时候开发的一个2D游戏引擎,当时计划为它实现类似Unity一样的编辑器,具有和Unity相似的工作流,但是由于水平不够,走了很多弯路,闭门造车,做了很多错误的设计,导致很多地方反人类和难以维护,加之时间有限,所以已经停止了对它的继续开发.由于停止了开发,又不想把所有资料都全部搞丢,所以把在开发中学到的东西,和一些自己的思考都记录一下.以便后可以参考. 他最后可以实现这些功能,不过实现起来远比成熟的引擎蛋疼得多: 脚本游戏编程 基

UWP简单示例(三):快速开发2D游戏引擎

准备 IDE:VisualStudio 2015 Language:VB.NET/C# 图形API:Win2D MSDN教程:UWP游戏开发 写在前面的话 没有什么比重复造轮子更让人心碎的事情了. (如果有,那就是造了两遍) 是否有必要开发游戏引擎? 现在市面上有很多成熟的跨平台游戏引擎,对个人开发者也较为友好 若你是一名C#开发者,可以选择CocosSharp或Unity3D 尤其Unity3D,用它开发2D和3D游戏一样出色 当然,亲自编写一款简单的2D游戏引擎也是一件让人自信感满满的事情

学习虚幻4需要储备的知识(2D游戏开发者向)

这些都是入门可读的资料,所有英文资料都有中译本. 2D游戏引擎可认为是3D游戏引擎的子集+优化(可选).简单的2D游戏引擎可以基于画布(不用节点)和继承树(不用组件),甚至没多少继承(直接堆Class),直接搞了.3D游戏引擎则要复杂一些. 各种效果术语扫盲 如果只是术语扫盲,官方文档好多是机翻,而且写的很详细(复杂)(各种流程,原理,例子,gif,视频混在一起),完全不如那些游戏网站,或者卖硬件的写的简单清楚,简单几句话就写完了(毕竟人家是要卖的),比如这个: 玩得更明白 显卡帝揭秘3D游戏画

unity3d 2d游戏制作的模式

  经过了4个月不懈的努力,我和图灵教育合作的这本3D游戏开发书预计下个月就要出版了.这里MOMO先打一下广告,图灵的出版社编辑成员都非常给力,尤其是编辑小花为这本书付出了很大的努力,还有杨海玲老师,不然我也无法完成这本书的编写.等这本书出版了大家记得买喔,哇咔咔- 下面,这篇文章是MOMO 3D游戏开发书籍中的一小段章节的修改版本,本篇文章我们将探讨一下Unity3D中如何来制作2D游戏.目前市面上已经有非常成熟的2D游戏引擎,比如cocos2d 或cocos2d-x等,并且都是免费的开发者可

(十一)Unity5新特性----实战2D游戏

孙广东  2015.7.11 在本教程中,将了解到U5新功能.你通过本教程.您将了解下面内容: Changes in Component Access Physics Effectors Adding Constant Force Audio Mixer 你能够先下载空的资源项目:起始项目.将其解压缩,然后在 Unity 中打开StarterProject\Assets\Scenes\Main.unity 场景. 您应该看到例如以下所看到的内容: 是一个繁星满天的夜晚背景,相机已经设置了.前景包

变态版大鱼吃小鱼-基于pixi.js 2D游戏引擎

之前用CSS3画了一条??,那还是一年前的事情了,这次我用一天的时间去研究了一下pixi.js,一个基于webgl的2D游戏引擎,然后用它做了一个demo出来,变态版大鱼吃小鱼. 试玩地址:变态版大鱼吃小鱼 这是一个h5游戏排名,我最看好pixi,刚开始也试过create.js,不过它不支持webgl,这是一个硬伤,然后又了解了一下Phaser,基于pixi开发的,功能肯定比pixi强大,不过我觉得它太重了,好像是500多K,不适合做小游戏,而pixi只有90K,并且支持webgl,所以就选它了

Unity 2D游戏开发教程之使用脚本实现游戏逻辑

Unity 2D游戏开发教程之使用脚本实现游戏逻辑 使用脚本实现游戏逻辑 通过上一节的操作,我们不仅创建了精灵的动画,还设置了动画的过渡条件,最终使得精灵得以按照我们的意愿,进入我们所指定的动画状态.但是这其中还有一些问题.例如,我们无法使用键盘控制精灵当前要进入的动画状态,而且精灵也只是在原地播放动画而已.但我们希望精灵在进入到PlayerWalkingAnimation状态时,位置应该发生改变. 要解决这些问题,就需要编写脚本.也就是说,要使用脚本来实现动画的播放控制,以及其它一些游戏的逻辑