说到flash,大家一定马上想到的就是各种酷炫的动画。最初我们的flash动画都是在工具上直接用矢量图制作完成的.flash对动画的渲染会非常消耗CPU。动画虽然酷炫,但是动画一旦多起来flash的渲染压力是会非常大的,严重的话直接掉帧,使整个画面非常的不流畅,给 用户的体验也是非常糟糕的.如果动画少了又很影响观赏性,很多美好的设想都成为泡影。但是如果我们合理的利用位图,把位图做成动画,不但减轻了CPU的渲染压力,还能添加更多的动画。让游戏充满观赏性。
显示位图不会额外消耗CPU,只会占用一定的内存,我们把一组位图按一定的顺序不停的切换,就形成了动画,和制作动画片的原理是同样的道理,可以简称为序列帧。 用位图序列帧动画取代flash动画简单的来说就是用 ‘内存换CPU’。我们玩游戏的时候通常会看到很多相同的角色,这些角色可以共用同一套bitmapData,其实我们说的切换位图,就是用一个bitmap不停的把新的bitmapData传给bitmap,bitmapData才是真正占用内存的东西,所以我们很多相同的动画共用同一个bitmapData就可以非常节省内存的开销了。并且要注意的是,当我们不再需要某个bitmapData的时候 一定要把它dispose();这样才会把一个位图数据从内存中释放。
我们先准备好素材,就是一帧帧的图片,这种序列帧通常可以从3Dmax中直接导出,在游戏公司,通常都是3D的人做好然后把素材给你,然后你再做成动画。
准备好图片之后把图片都按顺序命好名
然后我们把这些图片都加载进来,新建一个项目叫做SimpleBitmapAnimation(简单的位图动画) 然后在bin中新建一个play的文件夹,把鱼游动的动画的序列帧图片都放进去
再新建一个effect的文件夹,把特效的动画的序列帧图片放进去。
我们的思路是这样的,先把动画所需的位图都加载进来,然后定义一个单例来保存位图的bitmapData数据。 最后再在ENTER_FRAME的函数内通过每帧的播放时间(毫秒)还有当前播放了多少时间(毫秒)来确定当前播放到第几帧,然后显示对应帧数上的图片就可以了。
我们先定义一个单例类叫做ResourcesMananger用来当做资源管理器。里面有一个Dictionary(字典)用来保存加载过的位图数据
public class ResourcesMananger
{
private var _bitmapDataDict:Dictionary = new Dictionary();//用来存位图数据的字典
private static var _instance:ResourcesMananger = new ResourcesMananger();//单例接口
public function ResourcesMananger()
{
}
static public function get instance():ResourcesMananger
{
return _instance;
}
public function get bitmapDataDict():Dictionary
{
return _bitmapDataDict;
}
public function set bitmapDataDict(value:Dictionary):void
{
_bitmapDataDict = value;
}
}
定义一个类叫BitmapAnimation用来实现位图动画。
public class BitmapAnimation extends Sprite
{
private var bitmap:Bitmap = new Bitmap();//位图
private var _curFrame:Number;//当前帧
private var _preFrame:Number;//上一帧
private var _runTime:Number;//运行时间(毫秒)
private var _data:Array;//序列帧名称数据
private var ifPlayer:Boolean = false;//是否开始播放动画
private var sameTime:int;//播放的速度(毫秒)
public function BitmapAnimation(bitmapNameArr:Array,playSpeed:int=80)
{
//先把序列帧的数据和播放速度通过构造函数传进来。
//_data里的名称数据要确保可以获取到资源管理器中的bitmapData
_data = bitmapNameArr;
sameTime = playSpeed;
this.addChild(bitmap);//把一开始就定义好的位图添加到舞台上
this.mouseChildren = false;
this.mouseEnabled = false;
}
//开始播放
public function play():void
{
ifPlayer = true;
_curFrame = 0;
_preFrame = 0;
_runTime = 0;
//确定播放的时候定义帧事件并且显示第一帧的图片
this.addEventListener(Event.ENTER_FRAME,onEnterFrame);
setFrame(_curFrame);
}
//停止播放并且移除掉位图和帧事件
public function stop():void
{
ifPlayer = true;
this.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
if (bitmap && bitmap.parent)
{
bitmap.parent.removeChild(bitmap);
}
}
private function onEnterFrame(e:Event):void
{
runTime = _runTime + 33;//每秒30帧1帧就是33毫秒所以把播放时间累加
}
public function set runTime(value:Number):void
{
_runTime = value;
_curFrame = getCurFrame(_preFrame);//获取当前帧
if (_curFrame != _preFrame)
{
setFrame(_curFrame);//显示当前帧
_preFrame = _curFrame;
}
}
private function getCurFrame(v:uint):uint
{
//计算出当前帧
var cf:uint = v + uint(_runTime / sameTime);
_runTime = _runTime % sameTime;
return cf;
}
public function setFrame(v:Number):void
{
var frame:int = v % _data.length;//通过帧的总数计算出当前的帧
var curFrameName:String = _data[frame];
var bitmapData:BitmapData = ResourcesMananger.instance.bitmapDataDict[curFrameName];
bitmap.bitmapData = bitmapData;//替换掉当前舞台上的bitmap的bitmapData
}
}
回到Main我们先把需要的一些属性定义好。
private var playloaderNameArr:Array = [];//装鱼游动序列帧名字的数组
private var effectloaderNameArr:Array = [];//装特效序列帧名字的数组
private var loaderNum:int;//加载成功的次数
private var loader:Loader;//定义一个加载类
然后在init函数中调用initArr函数,把序列帧依次加进数组,并且多线程加载序列帧图片。
private function initArr():void
{
var filePath:String;//位图;路径
for (var i:int = 1; i < 16; i++)
{
var id:String = i > 9?i.toString():"0" + i;
filePath = "play/" + "fish_1008_" + id + ".png";
playloaderNameArr.push("play/fish_1008_" + id);
createLoader(filePath);
}
for (i = 0; i < 9; i++)
{
filePath = "effect/" + "0000" + i + ".png";//拼接位图路径
effectloaderNameArr.push("effect/0000" + i);//把对应图片的名字加进数组
createLoader(filePath);//加载这个路径的位图
}
}
接下来是createLoader方法,和我们之前学过的加载一样。
private function createLoader(filePath:String):void
{
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.INIT, initialize);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS, progressHandler);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, OnNetERROR);
loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, OnNetERROR);
loader.load(new URLRequest(filePath));
}
然后我们利用Event.INIT来缓存加载成功的位图和判断是否所有资源都加载完成。
private function initialize(e:Event):void
{
trace("加载完成。在init事件之前");
//移除一系列监听过的事件
e.currentTarget.loader.contentLoaderInfo.removeEventListener(Event.INIT, initialize);
e.currentTarget.loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, completeHandler);
e.currentTarget.loader.contentLoaderInfo.removeEventListener(ProgressEvent.PROGRESS, progressHandler);
e.currentTarget.loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, OnNetERROR);
e.currentTarget.loader.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, OnNetERROR);
var num:int = 0;
var len:int = playloaderNameArr.length;
for (var i:int = 0; i < len; i++)
{
var loaderName:String = playloaderNameArr[i];
//获取到当前加载资源的路径并且判断是否包含数组中的名字,如果包含说明是数组中对应的图片
if (String(e.currentTarget.url).indexOf(loaderName) != -1)
{
num = 1;//标记一下当前的位图已经存起来了
ResourcesMananger.instance.bitmapDataDict[loaderName] = (e.currentTarget.loader.content as Bitmap).bitmapData;
break;//缓存过之后跳出循环
}
}
if (num!=1)//已经缓存过的位图不需要再验证
{
len = effectloaderNameArr.length;
for (i = 0; i < len; i++)
{
loaderName = effectloaderNameArr[i];
if (String(e.currentTarget.url).indexOf(loaderName) != -1)
{
ResourcesMananger.instance.bitmapDataDict[loaderName] = (e.currentTarget.loader.content as Bitmap).bitmapData;
break;
}
}
}
loaderNum += 1;
//确保所有的位图都加载好了就调用LoadComplete();
if (loaderNum>=(playloaderNameArr.length + effectloaderNameArr.length))
{
LoadComplete();
}
}
最后再将动画初始化并且加进舞台
private function LoadComplete():void
{
//定义特效位图动画并且播放动画
var effect:BitmapAnimation = new BitmapAnimation(effectloaderNameArr);
effect.play();
this.addChild(effect);
//定义鱼的位图动画并且播放动画
var play:BitmapAnimation = new BitmapAnimation(playloaderNameArr);
play.play();
this.addChild(play);
}
最后动画乖乖的被我们用位图轮播的方式显示在了舞台上。
登录发表评论 登录 注册