专栏首页林焕彬的专栏闪屏还可以这样玩
原创

闪屏还可以这样玩

前言

对于多数应用来说,在进入APP的时候使用短暂的闪屏广告来吸引用户是很常见的一个场景。但随着这种模式的频繁应用,越来越多的用户会感到审美疲劳,甚至不看就跳过闪屏了。那么,是否有形式比较新颖的闪屏,来改变这个现状呢?下面开始来介绍可互动闪屏。

一.什么是可互动闪屏

可互动闪屏对于传统广告闪屏的区别就是,在之前的基础上,补充了可交互的内容形式,增加了互动性和趣味性,可充分唤起用户的好奇心,从而提升整个广告或者某个模块的点击率。

举个例子:

在手Q游戏中心中,针对FIFA足球世界新游上线之际,我们尝试设计了一个可踢球互动的广告闪屏,引导用户下载游戏,具体如下:

视频内容

这个闪屏上线之后,数据非常可观,点击率是以往传统营销闪屏的3到4倍,进一步提升了游戏的下载转化。

二.关键技术点

这种可互动闪屏的形式,功能上跟目前市面上的H5小游戏很相似,但从技术实现的角度来看,在内容繁多、逻辑复杂的H5页面上增加一个小游戏框架来实现这种闪屏,是不可取的。一方面增加了文件资源大小,另一方面给页面渲染带来了更多的压力。那是不是就没有办法解决了?其实办法是有的,可以借鉴游戏框架的实现方式并进行简化。下面会围栏这个案例开始讲解。

1.设计总体互动框架

通过对多个游戏框架进行对比分析,以及接口文档研究,可以总结出以下处理模块

* 精灵图管理 * 预加载 * 物理引擎 * 动画 * 粒子效果 * 事件输入 * 声音管理 * 设备插件管理 * 基本图形绘制 * 网络模块 * ......

对于互动闪屏来说,并不需要太多的模块,经过对视频中的玩法分析,可以精简为以下模块进行开发,减少工作量。

* 精灵图 * 资源预加载 * 动画 * 事件输入 * 特效处理 * 生命周期

除了分析模块组成,还得设计一下总体流程框架图:

根据框架图,我们对整个互动闪屏的逻辑一目了然,可以开始编码整个互动闪屏的控制逻辑:

经过设计后的代码框架,大小有10kb,初步符合我们对框架简化的要求。

2.游戏元素设计

在这个互动闪屏中,有足球场,守门员,门框,足球,发射按钮,准心等元素。可以先设计一下这些元素的通用类属性和方法,并对其进行派生。

除此之外,闪屏中比较复杂的逻辑就是对足球的状态控制,涉及射击轨迹,守门员的状态变化等。

A.射击轨迹

一条射击轨迹一般会经过两个点,一个发射的起点和结束的终点。起点是固定的,关键点在于结束的终点,其实也就是准心的位置,可以获取准心的位置来确定:

Aim.prototype = {
......
        // 获取准心的位置,以及获胜的概率
        getInfo: function() {
            return {
                x: this.drawX + this.moveX + this.width / 2,
                y: this.drawY + this.height / 2
            }
        },
......
}

由于轨迹有可能是曲线的,可以通过二次贝赛尔曲线公式来计算得出。其中需要知道中间控制点的位置,把参数代入公式中就可以获得每个时间段的位置,把这些位置连起来就是这条曲线了。

//发射了,并还在运动时间内
if (this.isFired && this.t < this.duration) {
    //移动的时候根据时间缩小宽高
    this.width -= this.t * 15;
    this.height -= this.t * 15;
    //最终位置
    this.endpos[0] = this.finalpos[0] - this.width / 2;
    this.endpos[1] = this.finalpos[1] - this.height / 2;
    //发射位置
    this.startpos = [this.drawX, this.drawY];
    //计算控制点位置
    this.cp = [
        (this.startpos[0] + this.endpos[0]) / 2 - (this.startpos[1] - this.endpos[1]) * this.curveness,
        (this.startpos[1] + this.endpos[1]) / 2 - (this.endpos[0] - this.startpos[0]) * this.curveness
    ];
    //通过贝塞尔公式获得每个时间t的位置
    var x = quadraticBezier(this.startpos[0], this.cp[0], this.endpos[0], this.t);
    var y = quadraticBezier(this.startpos[1], this.cp[1], this.endpos[1], this.t);
    //时间t
    this.t += 0.2;
 
    this.drawX = Math.floor(x);
    this.drawY = Math.floor(y);
}

B.守门员状态

守门员有准备状态,守到球,守不到球的情况,可以通过雪碧图的方式,设定守门员的不同状态,并通过控制当前帧来确定守门员的状态。

利用canvas画图的API,恰好可以指定绘制原图片的某个区域大小,满足绘制控制精灵图不同状态的需求 相关代码如下:

Goalkeeper.prototype = {
    update: function() {
        // 预备阶段
        if (this.isReady) {
            this.srcY = this.height * this.curFrame;
            //控制准备状态的帧数
            if (this.curFrame < this.frameNum - 1) {
                this.curFrame++;
            } else {
                this.curFrame = 0;
            }
        } else {
            // 准备扑球,可进球可不进
            if (this.isKeepBall) {
                this.curFrame = 12
            } else {
                this.curFrame = 13
            }
            this.srcY = this.height * this.curFrame;
        }
    },
    draw: function(gamectx) {
        if (this.visible) {
            //绘制守门员
            gamectx.drawImage(SpriteTool.goalkeeperSprite, this.srcX, this.srcY, this.width, this.height, this.drawX, this.drawY, this.width, this.height);
        }
    },
}

C.进球逻辑判断

在用户点击发射按钮后,根据准心的位置可以判定是否进球。若是在球框外,那是肯定进不了的;若是在球框内,那有一定的进球概率。可以设定进球概率为60%,通过设置随机数的方式进行判断,代码如下:

var getRandomPercent = function(probability) {
    var probability = probability * 100 || 1;
    var odds = Math.floor(Math.random() * 100);
    if (probability === 1) { return true };
    if (odds < probability) {
        return true;
    } else {
        return false;
    }
}

确定了进球概率后,接下来就是设定守门员的状态变化了,可以根据球的位置移动,以及终点的位置后确定守门员的状态

//判断是否进球
var info = this.aim.getInfo();
// 球停止了,且被扑中了
if (this.ball.isStop && info.result == false) {

    this.keeper.catchball();
    this.keeper.keepBall(info.x);
    this.ball.hide();
    //球停止了,且进球了
} else if (this.ball.isStop && info.result) {
    this.keeper.catchball();
    this.ball.hide();
    // 进球的时候,球在门框的位置
    if (this.aim.moveX > 0) {
        this.ballstage.drawBallInRight();
    } else {
        this.ballstage.drawBallInLeft();
    }
}

3.特效应用

在互动结束后,可以看到整个闪屏以螺旋扭曲的形式缩小到新游运营位,这种炫酷的形式,其核心是应用了WebGL来动态改变图片的展现形式。 可以想象扭曲一张纸,通过确定了扭曲的中心点,扭曲的角度和扭曲的半径,就可以实现。在WebGL中,是通过这3个变量以及扭曲算法来改变图片的顶点着色器,控制螺旋特效的展现情况。

//控制变量
uniform float radius; //螺旋半径
uniform float angle;  //螺旋角度
uniform vec2 center;  //螺旋中心点
uniform vec2 texSize; //螺旋画布大小
uniform sampler2D texture; //纹理素材
varying vec2 texCoord; //坐标轴

void main() {
   //更新坐标轴xy
    vec2 coord = texCoord * texSize;
    coord -= center;
    float distance = length(coord);
    if (distance < radius) {
        float percent = (radius - distance) / radius;
        float theta = percent * percent * angle;
        float s = sin(theta);
        float c = cos(theta);
        
        coord = vec2(
            coord.x * c - coord.y * s,
            coord.x * s + coord.y * c
        );
    }
    coord += center;
    //改变顶点
    gl_FragColor = texture2D(texture, coord / texSize);
    vec2 clampedCoord = clamp(coord, vec2(0.0), texSize);
    if (coord != clampedCoord) {
        /* fade to transparent if we are outside the image */
        gl_FragColor.a *= max(0.0, 1.0 - length(coord - clampedCoord));
    }
}

这里涉及GLSL的着色器代码,不懂的同学可以阅读其他资料来了解,这里就不赘述了。

三,结尾

整体来说,借鉴其他游戏框架并输出一个简洁有力的微互动框架,一方面可以满足产品方面对互动闪屏的需求,另一方面也会后续的互动闪屏开发奠定了基础,以后面对这样的需求开发就更加省心省力了。文章如有不正确的地方,欢迎指正。

参考文章

http://phaser.io/docs/2.6.2/index https://github.com/hujiulong/blog/issues/1 https://learnopengl-cn.github.io/01%20Getting%20started/05%20Shaders/ http://evanw.github.io/glfx.js/

都看到最下面了,关注一下呗?

关注DeepOcean公众号,不定时给你推荐一些技术分享,内容也许不新但一定是对你有帮助

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • iPhone X 适配手Q H5 页面通用解决方案

    对于手Q 中的各业务来说,受 Phone X 影响的 H5 页面挺多,应该采取什么快速有效的办法来应对呢?

    林焕彬
  • tcplayer 源码改造第二弹 -> 加入倍速播放

    由腾讯视频的官方文档可以知道,currentTime方法是暴露给用户,用于获取/设置当前时间的方法,同理,加入获取当前倍速的方法currentRate:

    大洼X
  • 寿司开卖:实现寿司制作特效和音响特效

    本节我们将继续上一节完成若干个小功能。首先要完成的是,当客户动画在主页面出现时,它左上角会冒泡,显示它想购买何种寿司,此时玩家可以点击左下角面板中各种元素,组合...

    望月从良
  • Druid学习笔记(二) - 数据连接的获取

    我们在分析mybatis执行sql的时候,最终定位到数据库连接池上。当时分析到mybatis通过数据库连接池获取到链接,然后通过连接执行sql。

    程序员_备忘录
  • Java基础:五、this关键字、static含义(4)

    如果只有一个peel()方法,如何知道是被a还是b所调用的呢?因为编译器会把“所操作对象的引用”作为第一次参数传递给peel()。所以上述两个方法的调用就变成了...

    桑鱼
  • Flutter 入门指北之基础部件

    原文:https://www.jianshu.com/p/8ddb16902ce6

    陈宇明
  • VUE+WebPack游戏设计:欲望都市城市图层的设计

    望月从良
  • Element Tabs 标签页 展示Echart 并随窗口变化自适应

    1、如何在一个div中展示不同的图表  (点击tab时为Chart组件赋值,并传给子组件,子组件监听数据变化并在 $nextTick 中重新绘制表格)

    tianyawhl
  • HTML5 Canvas炫酷的火焰风暴动画

    越陌度阡
  • .glb格式的模型怎么在three.js中展示

    3D软件中导出的格式一般有.obj 和.glb ,下面是blender 2.8.2 生成模型并在three.js中展示的流程

    tianyawhl

扫码关注云+社区

领取腾讯云代金券