大家快来玩转盘抽奖游戏(走在网页游戏开发的路上(七))-阿里云开发者社区

开发者社区> 开发与运维> 正文

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

简介:

.  抽奖流程

其实我们的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导师的指导!要休息了,明天还要上班,不然明天要挂了。如果大家觉得还不错,就请推荐。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章