`

Starling实现的硬皮翻书效果

 
阅读更多

作者:郭少瑞

———————————————————————————————————————–

更新(2012-12-31):

在今年的最后一天,这个效果终于更新为软纸翻页的版本(封面和封底仍然是硬纸),演示地址:
http://www.todoair.com/demo/book/StarlingBook2.html

硬纸到软纸的最大困难,在于拖拽中的几何算法和纹理拼接(因为Starling还未提供遮罩,所以不能按照遮罩的思路做,只能根据顶点拼接纹理)。几何算法是从天地会的这篇帖子中获取的,感谢这位作者。拼接则相对简单,显示中如果遇到五边形,则用一个三角形和一个四边形拼接。通过合理控制每个多边形的顶点坐标和纹理UV坐标,可以模拟实现原来遮罩才能做的效果。

在iPad一代上进行性能测试,稳定在60FPS左右,视频演示:
http://v.youku.com/v_show/id_XNDk1NzI2MTM2.html

源码下载:

您可以从这里下载完整项目和源码:点击这里下载

———————————————————————————————————————–

Flash的翻书效果想必大家都看到过很多,不过基于Stage3D的版本似乎还很难找到(也可能是我孤陋寡闻了,如果您知道的话欢迎补充)。现在很多项目已经开始使用Stage3D(或基于Stage3D的衍生框架比如Starling)来制作了,有时候也需要将原有传统Flash的效果移植到Stage3D层面来实现,这样融合更方便,性能上也可能更强大一些。

下面是一个基于Starling的翻书效果实现,当然还很简陋,只实现了硬皮翻页,软纸翻页还没有实现。不过我觉得在这个例子上进行一下扩展,用图片纹理坐标和顶点控制的方式,是可以实现出原来遮罩才能实现的效果的(传统Flash翻书效果大都使用遮罩),所以这个例子权作抛砖引玉,期待能有更为完善的例子出来。

效果演示

您可以点击下面的地址,查看这个Demo的实现效果:
http://www.todoair.com/demo/book/StarlingBook.html

Sorry,因为没写Loading,文件尺寸较大,需要您耐心等待一会儿。

实现过程

为了尽可能利用Starling的批处理优势来提升性能,所以素材采用了TextureAtlas的方式,将所有页的图片集中到一张大图上(当然这个地方也有缺点,如果一张图放不下的话,就要分几张图存放,并修改获取素材的方式,以便从不同的源中获得纹理)。在渲染处理上,使用QuadBatch来代替普通的显示对象叠加,来尽量提升性能。在笔者的电脑上,这个例子可以稳定在60FPS运行。

核心是PageFlipContainer这个类,这个类使用一个QuadBatch实例来更新显示,并侦听Touch事件来启动翻页过程。具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package test.pf
{
    import flash.display.Bitmap;
    import flash.geom.Point;
  
    import starling.display.Image;
    import starling.display.QuadBatch;
    import starling.display.Sprite;
    import starling.events.Event;
    import starling.events.Touch;
    import starling.events.TouchEvent;
    import starling.events.TouchPhase;
    import starling.textures.Texture;
    import starling.textures.TextureAtlas;
  
    import test.TrangleImage;
  
    /**
     * 基于Starling的翻页组件
     * @author shaorui
     */
    public class PageFlipContainer extends Sprite
    {
        /**包含内页的图集*/
        private var altas:TextureAtlas;
        /**书的宽度*/
        private var bookWidth:Number;
        /**书的高度*/
        private var bookHeight:Number;
        /**书的总页数*/
        private var bookCount:Number;
        /**批处理显示*/
        private var quadBatch:QuadBatch;
        /**左侧显示页面页码*/
        private var leftPageNum:int = -1;
        /**右侧显示页面页码*/
        private var rightPageNum:int = 0;
        /**翻动中的页面编码(正面,反面为+1)*/
        private var flipingPageNum:int = -1;
        /**正在翻页的位置(-1到1),由程序控制,外部无须调用*/
        public var flipingPageLocation:Number = -1;
        /**是否需要更新*/
        private var needUpdate:Boolean = true;
  
        /**@private*/
        public function PageFlipContainer(altas:TextureAtlas,bookWidth:Number,bookHeight:Number,bookCount:Number)
        {
            super();
            this.altas = altas;
            this.bookWidth = bookWidth;
            this.bookHeight = bookHeight;
            this.bookCount = bookCount;
            initPage();
        }
        /**初始化页*/
        private function initPage():void
        {
            quadBatch = new QuadBatch();
            addChild(quadBatch);
            textures = altas.getTextures();
            cacheImage = new Image(textures[0]);
            flipImage = new ImagePage(textures[0]);
            addEventListener(Event.ENTER_FRAME,enterFrameHandler);
            addEventListener(Event.ADDED_TO_STAGE,firstFrameInit);
            addEventListener(TouchEvent.TOUCH,onTouchHandler);
        }
        /**显示的时候初始化第一个画面*/
        private function firstFrameInit():void
        {
            removeEventListener(Event.ADDED_TO_STAGE,firstFrameInit);
            enterFrameHandler();
            needUpdate = false;
        }
        /**用于缓存纹理的图片*/
        private var cacheImage:Image;
        /**翻动的图片*/
        private var flipImage:ImagePage;
        /**缓存的纹理数组*/
        private var textures:Vector.;
        /**每帧调用*/
        private function enterFrameHandler(event:Event=null):void
        {
            if(stage == null || !needUpdate)
                return;
            quadBatch.reset();
            if(flipingPageNum >= 0)
            {
                leftPageNum = flipingPageNum - 1;
                rightPageNum = flipingPageNum + 2;
            }
            //选择左侧的页面
            if(validatePageNumber(leftPageNum))
            {
                cacheImage.x = 0;
                cacheImage.texture = textures[leftPageNum];
                quadBatch.addImage(cacheImage);
            }
            //渲染右侧的页面
            if(validatePageNumber(rightPageNum))
            {
                cacheImage.x = bookWidth/2;
                cacheImage.texture = textures[rightPageNum];
                quadBatch.addImage(cacheImage);
            }
            //渲染正在翻转的页面
            if(validatePageNumber(flipingPageNum))
            {
                if(flipingPageLocation>=0)
                    flipImage = new ImagePage(textures[flipingPageNum]);
                else
                    flipImage = new ImagePage(textures[flipingPageNum+1]);
                flipImage.setLocation(flipingPageLocation);
                quadBatch.addImage(flipImage);
                flipImage.dispose();
            }
        }
        /**是否处于拖动状态*/
        private var isDraging:Boolean = false;
        /**触碰处理*/
        private function onTouchHandler(event:TouchEvent):void
        {
            var touch:Touch = event.getTouch(this);
            if(touch != null && (touch.phase == TouchPhase.BEGAN || touch.phase == TouchPhase.MOVED || touch.phase == TouchPhase.ENDED))
            {
                var point:Point = touch.getLocation(this);
                var imgWidth:Number = bookWidth/2;
                if(touch.phase == TouchPhase.BEGAN)
                {
                    isDraging = true;
                    if(point.x >= imgWidth)
                    {
                        if(validatePageNumber(rightPageNum))
                        {
                            flipingPageNum = rightPageNum;
                        }
                    }
                    else
                    {
                        if(validatePageNumber(leftPageNum))
                        {
                            flipingPageNum = leftPageNum-1;
                        }
                    }
                }
                else if(touch.phase == TouchPhase.MOVED)
                {
                    if(isDraging)
                    {
                        flipingPageLocation = (point.x-imgWidth)/imgWidth;
                        if(flipingPageLocation > 1)
                            flipingPageLocation = 1;
                        if(flipingPageLocation < -1)                             flipingPageLocation = -1;                       validateNow();                  }               }               else                {                   isDraging = false;                  finishTouchByMotion(point.x);               }           }           else            {               needUpdate = false;             }       }       /**触控结束后,完成翻页过程*/       private function finishTouchByMotion(endX:Number):void      {           var imgWidth:Number = bookWidth/2;          needUpdate = true;          touchable = false;          addEventListener(Event.ENTER_FRAME,executeMotion);          function executeMotion(event:Event):void            {               if(endX >= imgWidth)
                {
                    flipingPageLocation += 0.04;
                    if(flipingPageLocation >= 1)
                    {
                        flipingPageLocation = 1;
                        removeEventListener(Event.ENTER_FRAME,executeMotion);
                        tweenCompleteHandler();
                    }
                }
                else
                {
                    flipingPageLocation -= 0.04;
                    if(flipingPageLocation = 0 && pageNum < bookCount)               return true;            else                return false;       }       /**当前页码*/       public function get pageNumber():int        {           if(leftPageNum >= 0)
                return leftPageNum;
            else
                return rightPageNum;
        }
        /**强制更新一次显示*/
        public function validateNow():void
        {
            needUpdate = true;
            enterFrameHandler();
            needUpdate = false;
        }
        /**跳页*/
        public function gotoPage(pn:int):void
        {
            if(pn < 0)               pn = 0;             if(pn >= bookCount)
                pn = bookCount-1;
            if(pn == 0)
            {
                leftPageNum = -1;
                rightPageNum = 0;
            }
            else if(pn == bookCount-1)
            {
                leftPageNum = pn;
                rightPageNum = -1;
            }
            else
            {
                if(pn%2==0)
                    pn = pn - 1;
                leftPageNum = pn;
                rightPageNum = pn+1;
            }
            flipingPageNum = -1;
            validateNow();
        }
    }
}

使用方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**初始化*/
private function initGame(event:Event):void
{
    /*----------------------翻页组件-----------------------*/
    //把图片合集到一起,减少DRW值
    var bookImgs:Bitmap = new bookImgClass();
    var xml:XML = XML(new bookXml());
    //这个工具可以给图片加上阴影,提升显示效果
    ShadowUtil.addShadow(bookImgs,xml);
    var texture:Texture = Texture.fromBitmap(bookImgs);
    var atlas:TextureAtlas = new TextureAtlas(texture,xml);
    //创建一个翻页容器,设置纹理,书的尺寸和总页数
    pageFlipContainer = new PageFlipContainer(atlas,800,480,8);
    pageFlipContainer.x = 100;
    pageFlipContainer.y = 100;
    addChild(pageFlipContainer);
    //创建一个按钮控制翻页
    var btn:Button = new Button(Texture.fromBitmap(new btnImgClass() as Bitmap),"下一页");
    btn.x = 100;
    btn.y = 600;
    btn.addEventListener(TouchEvent.TOUCH,btnTouchHandler);
    addChild(btn);
}
/**翻页*/
private function btnTouchHandler(event:TouchEvent):void
{
    var touch:Touch = event.getTouch(event.target as DisplayObject);
    if(touch != null && touch.phase == TouchPhase.ENDED)
    {
        var pn:int = pageFlipContainer.pageNumber+1;
        if( pn%2==0 )
            pn+=1;
        if( pn >= 8 )
            pn = 0;
        pageFlipContainer.gotoPage(pn);
    }
}

下载

您可以从这里下载完整项目和源码:点击这里下载

参考:

http://pageflip.hu/

分享到:
评论

相关推荐

    Starling实现的图标拖拽

    在本文中,我们将深入探讨如何使用Starling框架在Adobe AIR应用程序中实现图标拖放功能。Starling是一个由Daniel Slopov开发的高性能2D游戏开发库,它利用硬件加速的Stage3D API来提供流畅的动画体验。在Air环境中,...

    Starling Graphics extension 绘图API 扩展

    Starling是一个高效、跨平台的ActionScript 3库,专门设计用于游戏开发,它利用硬件加速来提供流畅的动画效果。Graphics扩展则进一步提升了Starling的功能,为开发者提供了更多高级的绘图选项。 首先,让我们深入...

    基于starling的游戏

    1. **硬件加速**:Starling利用Stage3D API实现硬件加速,显著提升了2D图形的渲染速度,使游戏画面更加流畅。 2. **轻量级**:Starling的API设计简洁,易于理解和使用,开发者可以快速上手并专注于游戏逻辑。 3. *...

    Starling1.8源码(包含粒子系统扩展包)

    通过研究这个源码,开发者不仅可以了解Starling框架的内部工作原理,还能学习到如何高效地实现粒子效果。例如,如何使用星型或圆形粒子,如何控制粒子的发射速度和方向,以及如何实现粒子的生命周期管理(出生、运动...

    starling 任意形状遮罩

    Starling 任意形状遮罩是一种在AS3(ActionScript 3)环境中,使用Starling框架实现的高级图形处理技术。Starling是一个针对Adobe Flash Player和Adobe AIR的2D游戏开发框架,它允许开发者利用硬件加速来提升游戏...

    adobe starling as3 flash 3d Starling演讲PPT以及附件

    内容包括AS3 项目源文件两个 包含PDF中提到的例子 以及运行效果SWF10个 在Stage3D出现之前,Flash3D引擎( Papervision3D, Away3D,…)都是软解 CPU是通用处理器,没有为渲染三角形而优化过 Stage3D是一个新的Flash...

    Starling开发的微信飞机大战源码

    在Starling框架下,游戏的逻辑处理、碰撞检测、动画效果等都可以得到优化。Starling支持矢量图形,这意味着游戏中的飞机、子弹、敌人等元素可以实现高质量的缩放,同时保持流畅的帧率。 “Starling”是基于AS3的跨...

    Starling开发的游戏

    5. **物理引擎集成**:可与Box2D等物理引擎无缝集成,实现真实的物理效果。 6. **动画系统**:提供强大的时间轴和动作管理,简化动画制作过程。 7. **触摸和手势支持**:对于移动设备,Starling提供了触摸事件和手势...

    框架starling

    1. **硬件加速**:Starling通过使用Stage3D API实现了硬件加速,这使得开发者能够在2D图形处理中充分利用现代GPU的能力,提高渲染速度和帧率,尤其是在处理大量动态图形时。 2. **简单API**:尽管Stage3D底层API...

    air starling 图片旋转

    本篇文章将深入探讨如何在Starling中实现图片旋转,并讨论如何消除旋转过程中可能出现的锯齿现象,特别是在桌面应用中。 首先,我们需要理解Starling中的基本概念。Starling的核心是`DisplayObject`类,它是所有可...

    Starling1.5.1

    6. **物理引擎集成**:Starling与Box2D等物理引擎有良好的兼容性,可轻松添加物理效果到游戏中。 7. **事件系统**:遵循ActionScript 3的事件模型,让事件处理变得直观和简单。 8. **声音管理**:提供了音频播放...

    starling版消灭星星

    在开发过程中,开发者可能利用了Starling的特性来创建动画效果,如星星的出现、碰撞检测以及消除时的爆炸特效,这些都需要对图形渲染和事件处理有深入理解。 首先,我们要理解Starling框架的基本结构。它使用了...

    Starling9宫格类库

    通过这样的9宫格类库,开发者可以轻松地在Starling项目中实现高质量的缩放效果,无论是在移动设备还是桌面平台上,都能提供良好的用户体验。在实际应用中,9宫格缩放常用于按钮、背景、窗口等元素,使得UI元素在不同...

    Starling Feathers:Starling专属UI框架

    Starling本身是一个跨平台的游戏开发框架,基于ActionScript 3.0,利用硬件加速来实现高效的2D图形渲染,尤其适合在移动设备上使用。Feathers的出现弥补了Starling在UI组件上的不足,提供了丰富的控件和布局选项,...

    starling中文API

    Shader允许你编写GLSL代码来实现复杂的图形效果。 7. **Event Handling**:Starling提供了一套事件处理机制,类似于Flash的事件系统,包括触摸、鼠标和键盘事件。 8. **Animation**:Starling通过Timeline和Tween...

    <CitrusEngine系列教程二:结合starling和Box2D开发游戏>示例源码

    在本教程中,Starling将用于绘制游戏场景和角色,实现流畅的图形效果。 3. **Box2D**:Box2D是一个流行的开源2D物理引擎,用于模拟现实世界的物理行为。在ActionScript3中,Box2D被广泛应用于游戏开发,因为它可以...

    PrimaryFeather-Starling-Framework-v1.3-175-g09eebe4.zip

    - 源代码:Starling框架的实现,供学习和自定义。 - 资源文件:如纹理、音频或其他媒体,用于演示或测试。 - 文档:关于如何使用框架的指南或API文档。 总的来说,Starling Framework为2D游戏开发者提供了强大的...

    Starling,Features和StarlingMVC第三方库

    Starling 是一个强大的2D游戏开发框架,它基于Adobe Flash Player和Adobe AIR,为开发者提供了在这些平台上实现高性能图形渲染的能力。Starling 使用Stage3D技术,能够将2D渲染优化到接近原生代码的速度,这使得它...

    starling中文版粒子

    starling中文版粒子下载

    starling.swc

    Starling是一个基于Adobe Flash Player和Adobe AIR的2D游戏开发框架,由Daniel Sperl创建,其设计目的是为了提供高效、高性能的2D图形渲染能力,同时保持ActionScript 3.0的简单性和易用性。在Flash开发环境中,...

Global site tag (gtag.js) - Google Analytics