对于多数应用来说,在进入APP的时候使用短暂的闪屏广告来吸引用户是很常见的一个场景。但随着这种模式的频繁应用,越来越多的用户会感到审美疲劳,甚至不看就跳过闪屏了。那么,是否有形式比较新颖的闪屏,来改变这个现状呢?下面开始来介绍可互动闪屏。
可互动闪屏对于传统广告闪屏的区别就是,在之前的基础上,补充了可交互的内容形式,增加了互动性和趣味性,可充分唤起用户的好奇心,从而提升整个广告或者某个模块的点击率。
举个例子:
在手Q游戏中心中,针对FIFA足球世界新游上线之际,我们尝试设计了一个可踢球互动的广告闪屏,引导用户下载游戏,具体如下:
这个闪屏上线之后,数据非常可观,点击率是以往传统营销闪屏的3到4倍,进一步提升了游戏的下载转化。
这种可互动闪屏的形式,功能上跟目前市面上的H5小游戏很相似,但从技术实现的角度来看,在内容繁多、逻辑复杂的H5页面上增加一个小游戏框架来实现这种闪屏,是不可取的。一方面增加了文件资源大小,另一方面给页面渲染带来了更多的压力。那是不是就没有办法解决了?其实办法是有的,可以借鉴游戏框架的实现方式并进行简化。下面会围栏这个案例开始讲解。
通过对多个游戏框架进行对比分析,以及接口文档研究,可以总结出以下处理模块
* 精灵图管理 * 预加载 * 物理引擎 * 动画 * 粒子效果 * 事件输入 * 声音管理 * 设备插件管理 * 基本图形绘制 * 网络模块 * ......
对于互动闪屏来说,并不需要太多的模块,经过对视频中的玩法分析,可以精简为以下模块进行开发,减少工作量。
* 精灵图 * 资源预加载 * 动画 * 事件输入 * 特效处理 * 生命周期
除了分析模块组成,还得设计一下总体流程框架图:
根据框架图,我们对整个互动闪屏的逻辑一目了然,可以开始编码整个互动闪屏的控制逻辑:
经过设计后的代码框架,大小有10kb,初步符合我们对框架简化的要求。
在这个互动闪屏中,有足球场,守门员,门框,足球,发射按钮,准心等元素。可以先设计一下这些元素的通用类属性和方法,并对其进行派生。
除此之外,闪屏中比较复杂的逻辑就是对足球的状态控制,涉及射击轨迹,守门员的状态变化等。
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();
}
}
在互动结束后,可以看到整个闪屏以螺旋扭曲的形式缩小到新游运营位,这种炫酷的形式,其核心是应用了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公众号,不定时给你推荐一些技术分享,内容也许不新但一定是对你有帮助
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。