大家快来玩转盘抽奖游戏(走在网页游戏开发的路上(七))

简介:



本文转自吴秦博客园博客,原文链接:http://www.cnblogs.com/skynet/archive/2011/06/15/2081106.html,如需转载请自行联系原作者

0. 前言

每逢过年过节各大游戏都会搞些小活动来刺激、吸引、黏住玩家,比如转盘抽奖活动,有的公司年底也喜欢搞抽奖活动。本文介绍如何设计一个flash转盘抽奖程序。先上效果,看如下Flash抽奖转盘:

注:转盘看上去比较丑,使出了吃奶劲才弄出这效果,毕竟不是专业美工人员,大家凑合着看。觉得还行鼓鼓掌,按按推荐;觉得不行,也不要拍砖,无视就行了。

预备知识:事件机制(可以参考走在网页游戏开发的路上(五))、flash动画原理(可以参考走在网页游戏开发的路上(六))、AS3文档类等。

1. 抽奖流程

其实我们的Flash只是一个显示作用,要转到哪个位置(中哪个奖品)是后台来完成的。而且每个奖品的概率是不同的,不是等概率的,我想没有转盘抽奖游戏是等概率的。从玩家点击“抽奖”开始到结束,与后台的交互如下:

clip_image002

转盘抽奖的大致流程是这样的:

F 玩家点击Flash中的“抽奖”按钮;

F Flash调用web页面中的Javascript函数,告诉它玩家开始抽奖了。当然Flash调用JS的时候是带了参数的,比如是谁在抽奖等详细信息; web页面中的JavaScript函数,通知后台(可以是C++PythonPHPJavaC#等等)玩家开始抽奖了。这里也是带了参数的!

F 后台返回结果,JavaScript函数通过调用Flash提供的接口,告诉抽奖结果;

F Flash拿到结果之后,就开始转到,最终转到指定位置。抽奖介绍!

JavaScriptflash和后台之间充当着桥梁的作用。Flash仅作展示的作用,概率和抽奖结果由后台控制。

2. AS3JavaScript之间通信

ExternalInterface 类是外部 PI,在ActionScriptFlash Player的容器之间实现直接通讯的应用程序编程接口,例如:含有JavaScriptHTML页。推荐对所有JavaScriptActionScript之间的通信使用ExternalInterface

HTML页中使用JavaScript,可以调用Flash Player中的ActionScript函数。ActionScript函数可以返回一个值,JavaScript会立即接收它作为该调用的返回值,反之亦然。此功能替代了较旧的fscommand()方法。

利用ActionScript,可以在HTML页上执行以下操作:

F 调用任何JavaScript 函数。

F 传递任意数量、具有任意名称的参数。

F 传递各种数据类型(BooleanNumberString 等等)。

F 接收来自JavaScript 函数的返回值。

通过在HTML页上使用JavaScript,可以:

F 调用ActionScript函数。

F 使用标准的函数调用表示法传递参数。

F 将值返回给JavaScript函数。

2.1. ExternalInterface.available属性

ExternalInterface.available属性指示当前的Flash Player是否位于提供外部接口的容器中。如果外部接口可用,则此属性为true;否则,为false。在使用ExternalInterface类中的任何其它功能之前,应始终进行检查以确保当前容器支持外部接口通信,如下所示:

if (ExternalInterface.available)

{

// 在此执行 ExternalInterface 方法调用。

}

注意ExternalInterface.available属性报告当前容器是否为支持ExternalInterface连接的容器类型。它不会报告当前浏览器中是否启用了JavaScript

通过使用ExternalInterface.objectID属性,您可以确定Flash Player实例的唯一标识符(具体来说,是指Internet Explorerobject标签的id属性,或者是指使用NPRuntime接口的浏览器中embed标签的name 属性)。这个唯一的ID代表浏览器中的当前SWF文档,并可用于对SWF文档进行引用,例如:在容器HTML页中调用JavaScript函数时进行引用。当Flash Player容器不是Web浏览器时,此属性为null

2.2. ActionScript中调用外部代码

ExternalInterface.call()方法执行容器应用程序中的代码。它至少需要一个参数,即包含容器应用程序中要调用函数的名称的字符串。传递给ExternalInterface.call()方法的其它任何参数均作为函数调用的参数传递给容器。

//调用外部函数"addNumbers"

//传递两个参数并将该函数的结果

//赋给变量"result"

var param1:uint = 3;

var param2:uint = 7;

var result:uint = ExternalInterface.call("addNumbers", param1, param2);

如果容器为HTML页,此方法将调用具有指定名称的JavaScript函数,必须在包含HTML页中的script元素中定义该函数。JavaScript函数的返回值被传递回ActionScript

<script language="JavaScript">

//加上两个数字,然后将结果发送回ActionScript

function addNumbers(num1, num2)

{

return (num1 + num2);

}

</script>

如果容器为其它的ActiveX容器,此方法将导致Flash PlayerActiveX 控件调度它的FlashCall事件。Flash Player将指定的函数名及所有参数序列化为一个XML字符串。容器可以在事件对象的request属性中访问该信息,并用它来确定如何执行它自己的代码。为了将值返回ActionScript,容器代码调用ActiveX对象的SetReturnValue()方法,并将结果(序列化为一个XML字符串)作为该方法的参数进行传递。

无论容器为Web浏览器还是为其它ActiveX容器,只要调用失败或容器方法没有指定返回值,都将返回null。如果包含环境属于调用代码无权访问的安全沙箱,ExternalInterface.call()方法将引发SecurityError 异常。可以通过在包含环境中为allowScriptAccess设置合适的值来解决此问题。例如,要在HTML页中更改allowScriptAccess 的值,请编辑objectembed标签中的相应属性。

2.3. 从容器中调用ActionScript代码

容器只能调用函数中的ActionScript代码,而不能调用任何其它ActionScript代码。要从容器应用程序调用ActionScript函数,必须执行两项操作:向ExternalInterface类注册函数,然后从容器的代码调用它。

首先,必须注册ActionScript函数,指示其应能够为容器所用。使用ExternalInterface.addCallback()方法,如下所示:

function callMe(name:String):String

{

return "busy signal";

}

ExternalInterface.addCallback("myFunction", callMe);

addCallback()方法采用两个参数。第一个参数为String类型的函数名,容器将籍此名称得知要调用的函数。第二个参数为容器调用定义的函数名时要执行的实际ActionScript函数。由于这些名称是截然不同的,因此可以指定将由容器使用的函数名,即使实际的ActionScript 函数具有不同的名称。这在函数名未知的情况下特别有用,例如:指定了匿名函数或需要在运行时确定要调用的函数。

一旦向ExternalInterface类注册了ActionScript函数,容器就可以实际调用该函数。完成该操作的具体方法依容器的类型而定。例如,在Web浏览器的JavaScript代码中,使用已注册的函数名调用ActionScript函数,就像它是Flash Player浏览器对象的方法(即,一个表示objectembed标签的JavaScript对象的方法)。也就是说,将传递参数并返回结果,就如同调用本地函数一样。

<script language="JavaScript">

// callResult gets the value "busy signal"

var callResult = flashObject.myFunction("my name");

</script>

...

<object id="flashObject"...>

...

<embed name="flashObject".../>

</object>

或者,在运行于计算机应用程序中的SWF文件中调用ActionScript函数时,必须将已注册的函数名及所有参数序列化为一个XML格式的字符串。然后,将该XML字符串作为一个参数来调用ActiveX控件的CallFunction()方法,以实际执行该调用。

不管是哪种情况,ActionScript函数的返回值都被传递回容器代码,当调用方为浏览器中的JavaScript代码时直接作为值返回,而当调用方为ActiveX容器时则会序列化为XML格式字符串。

3. 转盘旋转原理与实现

转盘旋转的设计和实现才是本文的重点,与服务器的交互将不再介绍,而且与服务器的交互也不止通过JavaScript的方法,还可以通过cgi的方式。接下来的内容,假设已经经历了FlashAS3àJavaScriptcgi等)à后台à JavaScriptcgi等),即Flash已经告诉后台玩家已经开始了抽奖,并且后台返回了结果——中奖物品(指针停止位置)。我们要做的工作就是,让指针旋转起来,并最终停留在指定位置。

3.1. 数学知识

这里可以说是用上了高中的数学知识了,指针从一个位置旋转到另一个位置,相当箭头的顶点绕着中心做圆周运动,运动过程中顶点的坐标变化公式如下:

F x += radius * sin

F y += radius * cos

其中radius为半径(即指针长度)、为指针旋转的角度。

clip_image004

我们要做的就是不断的改变旋转角度,直到达到指定的位置,将旋转位置连续起来达到运动的效果(使用Event.ENTER_FRAMETimerEvent.TIMER,参见走在网页游戏开发的路上(六))。

3.2. 指针运动

以开始给出的转盘为例,转盘中有8个物品,指针角度为0时指向物品1,角度为45时指向物品2…,以360/8=45为间隔。现在假设指针其实为值为物品1,中奖结果为物品5,即指针要旋转180度。为了设计动画效果,我们监听Event.ENTER_FRAME事件,每帧使指针旋转一个小角度,比如5,直到角度为180度停止,这样就可以达到旋转的效果。

那我们现在是每帧根据上面的公式,改变指针顶点的位置吗?不是,在ActionScript中,显示对象有个rotation属性,顾名思义旋转角度的意思。这样每帧改变这个属性值就可以,在Event.ENTER_FRAME事件处理函数中,使指针的rotation += 45,直到rotation等于180。(rotation的取值范围是:(-180, 180]

说明:如果是一个小球绕着某个点,做圆周运动,改变rotation属性显然是不行的,这时必须得通过上面介绍的公式改变小球的xy坐标达到运动的效果,这也是介绍上面公式的原因。

似乎这样做已经满足了基本要求,但是为了让用户感觉更真实,往往会要求指针至少转几圈,不会在一圈之内就到达指定位置停止了。那么这时指针的指针需要旋转度数:旋转圈数 * 360 + (指针当前位置与指定位置的偏差值)。

实现效果如下:

3.3. 缓动效果

很显然,上面实现的动画效果比较生硬,需要加入缓动效果。现实实际效果也是如此,高速转到的指针不可能突然停止,其中有个减速的效果,当速度减到0时,指针才会停止。这其实也好办,我们在Event.ENTER_FRAME事件处理函数中,调整指针rotation的增加值从大到小变动,直到这个增量为0停止,这里我就不实现了。下面我要介绍一个非常有用的缓动效果库——TweenMax,如果做flash webgame开发的话,肯定会用用到这个库

3.3.1TweenMax库简介

TweenMax库包含了很多缓动效果,它是建立在TweenLite核心类及TweenFilterLite基础之上(它们同样包含了一些缓动效果)。但是TweenMax新增功能:

F 进行贝塞尔缓动

F 连续的缓动(序列化的缓动)

F 对对象数组中的对象进行同意的缓动使用allTo()allFrom()

F 缓动中的暂停/继续功能,使用pause()resume()方法,或“paused”属性

F 跳转至缓动的任何时段,使用“progress”属性。输入一个0~1之间的数值

F 16进制的颜色进行缓动,使用hexColors属性

F 获取缓动效果的实例数组,该数组中包括了加在一个指定目标对象上的所有的缓动效果的实例,TweenMax.getTweensOf(mc);如果该mc应用了多个缓动效果,则返回一个数组,数组中是不同的缓动效果的实例

F 获取TweenMaxTweenLiteTweenFilterLite的实例数组,使用静态函数getAllTweens()

F 终止所有的缓动

F 暂停/继续全部的缓动

3.3.2常用方法

l 构造函数:public function TweenMax(target:Object, duration:Number, vars:Object)

l target:目标对象,即需要缓动效果的对象,我们的例子中就是转盘的指针

l duration:持续的时间(单位:秒);

l vars:包含想要缓动的的属性值,缓动的常用属性包括

n alpha:Number:目标对象在缓动结束时的alpha

n delay:Number:延迟缓动

n ease:Function:缓动函数

n easeParames:Array:缓动函数中的参数

n autoAlpha:Number:用来代替alpha属性,可获得一些附加小伙,实现透明度缓动效果

n volume:Number:改变MovieClip或者SoundChannel的音量,将缓动结束时的音量值调整为指定的值

n tint:Number:改变可显示对象的色调/颜色

n frame:Number:将MovieClip缓动到指定的帧频

n bezier:ArrayBezier缓动,允许你以非线醒的方式进行缓动

n bezierThrough:Array:贝赛尔曲线要经过的位置点

n orientToBezier:Array:使MovieClip自动调整自身的方向,使之符合贝塞尔路径[x,y,rotation,ang](rotation:旋转属性,ang:旋转的度数

n hexColors:Object:缓动指定对象中相应颜色属性的值(TweenMax.to(my_obj,{hexColors:{mycolor:0Xff0000}})

n onStart:Function:在缓动开始时想要执行的某个函数

n onStartParams:Array:缓动开始时要执行函数的参数

n onUpdate:Function:缓动过程中,每次更新属性值时,要执行的函数

n onUpdateParams:Array:同上

n onComplete:Function:缓动结束时要执行的函数

n onCompleteParams:Array:同上

n renderOnStart:Boolean:阻止缓动的渲染效果直到缓动真正开始

n overwrite:Boolean:缓动效果是否可以被覆盖

n blurFilter:Object:应用模糊滤镜,需要传递一个具有下列属性的对象作为参数:blurX(横向的模糊度)blurY(纵向的模糊度),quality(品质,默认值为2)

n glowFilter:Object:应用发光滤镜,需要传递一个带有以下属性的对象:alpha,blurX,blurY,color,strength(强度),quality,inner(内侧发光),knockout(挖空)

n colorMatrixFilter:Object:应用颜色矩阵滤镜,需要传递一个带有以下属性的对象:colorize(色调),amount(总量),contrast(对比度),brightness(亮度),saturation(饱和度),hue(色相),threshold(阀值),relative(相关性),matrix(颜色矩阵)

n dropShadowFilter:Object:应用阴影滤镜,需要传递一个带有以下属性的对象:alpha,angle(角度),blurX,blurY,color,distance(距离),strength,quality

n bevelFilter:Object:应用斜角滤镜,需要传递一个带有以下属性的对象:angle,blurX,blurY,distance,hightlightAlpha(高亮区的透明度),highlightColor(高亮区的颜色),shadowAlpha(阴影区的透明度),shadowColor(阴影区的颜色),strength(强度),quality

n progress:Number:缓动进程0~1

n paused:Boolean:是否停止缓动

l 函数:allTo(targets:Array, duration:Number, vars:Object):Array

返回的是一个数组保存了创建的所有TweenMax Object

l 函数:allFrom(targets:Array, duration:Number, vars:Object):Array

allTo一样,只是定义的是运动对象的初始状态,运动到当前状态。

l 函数:complete(skipRender:Boolean = false, suppressEvents:Boolean = false):void

强制TweenMax到最后结束部分。如果第一个参数设为true,则不会渲染,TweenMax将停在调用那一刻。如果第二个参数设为true则不会触发onCompelte,onUpdate等事件。

l 函数:delayedCall(delay:Number, onComplete:Function, onCompleteParams:Array = null, useFrames:Boolean = false):TweenMax

延迟执行函数

l 函数:getTweensOf(target:Object):Array

返回运动物体正在运行的的TweenMax Object

l 函数:isTweening(target:Object):Boolean

判断是否正在缓动

l 函数:updateTo(vars:Object, resetDuration:Boolean = false):void

可以在运行中新增或改变原有的属性变化值。第二个参数设为false时将不重播缓动,而继续缓动到新的值;设为true将中断并重播缓动。

4. 最终代码实现

前面介绍了转盘的原理和TweenMax库,下面看最终实现的代码。不废话,上关键代码:

package  
{
    import flash.display.Sprite;
    import com.greensock.TweenMax;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.utils.getDefinitionByName;
    /**
     * ...
     * @author ...
     */
    public class DialUI extends Sprite 
    {
        private var _tween:TweenMax;
        private var _view:*;
    
        //物品个数;
        private var _count:int = 8;
        //角度;
        private var _angle:Number = 360/_count;
        //最少旋转圈数;
        private var _rotateNum:int = 2;
        
        public function DialUI() 
        {
            initView();
        }
        
        private function initView():void
        {
            var cls:Class = getDefinitionByName("Dial") as Class;
            if (cls != null)
            {                
                _view = new cls();
                _view.pointer.rotation = 0;
                _view.btnStart.addEventListener(MouseEvent.CLICK, onClickHandler);                
                addChild(_view);
                
                _tween = new TweenMax(_view.pointer, 2, {onComplete: onCompleteHandler});
            }
        }
        
        private function onClickHandler(evt:MouseEvent):void
        {
            _view.btnStart.mouseEnabled = false;
            var temp:uint = Math.floor(8 * Math.random());
            trace(temp);
            var rt = _angle * temp + (360 * _rotateNum);
            _tween.updateTo({rotation: rt}, true)
        }
        
        private function onCompleteHandler():void
        {
            _view.btnStart.mouseEnabled = true;
        }
    }
 
}

完整代码下载

写在最后

不知直觉,写到这么晚了,效率啊!最后大概浏览了一下,还是有很多传达的东西没有表现出来,只有大家意会了。声明:本文是我在公司半个月前所做东西的总结,但并不涉及泄漏公司机密。感觉heaton导师的指导!要休息了,明天还要上班,不然明天要挂了。如果大家觉得还不错,就请推荐。

参考文献:

  • FLASH抽奖程序中的细节问题[上]、[下]
  • TweenMax官方文档
  • Flash帮助文档
相关文章
|
4月前
|
索引
羊了个羊游戏开发实战教程(2):随机生成卡牌
羊了个羊游戏开发实战教程(2):随机生成卡牌
30 0
|
9月前
扫雷小游戏 万字全网最详细(可展开一片空白)下
扫雷小游戏 万字全网最详细(可展开一片空白)
36 0
|
9月前
|
小程序 算法 数据可视化
扫雷小游戏 万字全网最详细(可展开一片空白)上
扫雷小游戏 万字全网最详细(可展开一片空白)
60 0
|
11月前
|
Python
通过游戏学Python系列之小兔要上天---手把手教你使用Pygame开发平台跳跃类游戏06之死亡后游戏重新开始
通过游戏学Python系列之小兔要上天---手把手教你使用Pygame开发平台跳跃类游戏06之死亡后游戏重新开始
139 0
|
11月前
|
Java
手把手一步一步教你使用Java开发一个大型街机动作闯关类游戏13之英雄不要走出屏幕
手把手一步一步教你使用Java开发一个大型街机动作闯关类游戏13之英雄不要走出屏幕
106 0
|
11月前
|
Python
通过游戏学Python系列之小兔要上天---手把手教你使用Pygame开发平台跳跃类游戏04之跳跃
通过游戏学Python系列之小兔要上天---手把手教你使用Pygame开发平台跳跃类游戏04之跳跃
106 0
|
小程序 开发者
闯关答题小程序产品
闯关答题小程序产品
闯关答题小程序产品
想过七夕?->女朋友陪你玩游戏(C)(下)
七夕到了,没有女朋友怎么办?没事~ 写一个小游戏,让女朋友陪你玩扫雷~
想过七夕?->女朋友陪你玩游戏(C)(下)
想过七夕?->女朋友陪你玩游戏(C)(上)
三子棋 七夕到了,没有女朋友怎么办?没事~ 写一个小游戏,让女朋友陪你玩三子棋~
想过七夕?->女朋友陪你玩游戏(C)(上)