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

2024-02-12 09:08

本文主要是介绍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的透明度来擦除fogBitmapDatafogBitmapData.draw(pointBitmap,matrix,null,BlendMode.ERASE);}

这篇关于2D游戏平滑的迷雾战争效果的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/702237

相关文章

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部

Kotlin Compose Button 实现长按监听并实现动画效果(完整代码)

《KotlinComposeButton实现长按监听并实现动画效果(完整代码)》想要实现长按按钮开始录音,松开发送的功能,因此为了实现这些功能就需要自己写一个Button来解决问题,下面小编给大... 目录Button 实现原理1. Surface 的作用(关键)2. InteractionSource3.

使用WPF实现窗口抖动动画效果

《使用WPF实现窗口抖动动画效果》在用户界面设计中,适当的动画反馈可以提升用户体验,尤其是在错误提示、操作失败等场景下,窗口抖动作为一种常见且直观的视觉反馈方式,常用于提醒用户注意当前状态,本文将详细... 目录前言实现思路概述核心代码实现1、 获取目标窗口2、初始化基础位置值3、创建抖动动画4、动画完成后

uniapp小程序中实现无缝衔接滚动效果代码示例

《uniapp小程序中实现无缝衔接滚动效果代码示例》:本文主要介绍uniapp小程序中实现无缝衔接滚动效果的相关资料,该方法可以实现滚动内容中字的不同的颜色更改,并且可以根据需要进行艺术化更改和自... 组件滚动通知只能实现简单的滚动效果,不能实现滚动内容中的字进行不同颜色的更改,下面实现一个无缝衔接的滚动

Java实现图片淡入淡出效果

《Java实现图片淡入淡出效果》在现代图形用户界面和游戏开发中,**图片淡入淡出(FadeIn/Out)**是一种常见且实用的视觉过渡效果,它可以用于启动画面、场景切换、轮播图、提示框弹出等场景,通过... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细

使用animation.css库快速实现CSS3旋转动画效果

《使用animation.css库快速实现CSS3旋转动画效果》随着Web技术的不断发展,动画效果已经成为了网页设计中不可或缺的一部分,本文将深入探讨animation.css的工作原理,如何使用以及... 目录1. css3动画技术简介2. animation.css库介绍2.1 animation.cs

Flutter实现文字镂空效果的详细步骤

《Flutter实现文字镂空效果的详细步骤》:本文主要介绍如何使用Flutter实现文字镂空效果,包括创建基础应用结构、实现自定义绘制器、构建UI界面以及实现颜色选择按钮等步骤,并详细解析了混合模... 目录引言实现原理开始实现步骤1:创建基础应用结构步骤2:创建主屏幕步骤3:实现自定义绘制器步骤4:构建U

Python开发文字版随机事件游戏的项目实例

《Python开发文字版随机事件游戏的项目实例》随机事件游戏是一种通过生成不可预测的事件来增强游戏体验的类型,在这篇博文中,我们将使用Python开发一款文字版随机事件游戏,通过这个项目,读者不仅能够... 目录项目概述2.1 游戏概念2.2 游戏特色2.3 目标玩家群体技术选择与环境准备3.1 开发环境3

Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)

《Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)》文章介绍了如何使用dhtmlx-gantt组件来实现公司的甘特图需求,并提供了一个简单的Vue组件示例,文章还分享了一... 目录一、首先 npm 安装插件二、创建一个vue组件三、业务页面内 引用自定义组件:四、dhtmlx

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2