在经历总结和调研后,我们提出了基于 Android、iOS 各自 native 平台打造自研引擎对图形化应用进行统一处理和封装的方案,以及在此基础之上的整个弹幕流程的重新设计与实现。
其中 Android 基于 libGDX 以 Java/Kotlin 为语言,体积小巧,灵活可配置,围绕 ECS 框架,手动控制计算、渲染等主要流程。
// 顶层 build.gradle
repositories {
mavenCentral()
}
// build.gradle
dependencies {
implementation 'com.kuaishou:akdanmaku:1.0.3'
}
proguard-rules.pro
中添加反混淆
-dontwarn com.badlogic.gdx.backends.android.AndroidFragmentApplication
-dontwarn com.badlogic.gdx.utils.GdxBuild
-dontwarn com.badlogic.gdx.jnigen.BuildTarget*
-dontwarn com.badlogic.gdx.graphics.g2d.freetype.FreetypeBuild
# Required if using Gdx-Controllers extension
-keep class com.badlogic.gdx.controllers.android.AndroidControllers
val danmakuPlayer = DanmakuPlayer(
DefaultDanmakuRenderer(applicationContext)
)
danmakuPlayer.release()
当前弹幕使用方式类似于视频播放器,分为 DanmakuView
与 DanmakuPlayer
,当需要跨场景使用时,复用 DanmakuPlayer
即可。
val danmakuView = findViewById<DanmakuView>(R.id.danmakuView)
danmakuPlayer.bindView(danmakuView)
// config 见后文
danmakuPlayer.start(config)
danmakuPlayer.pause()
danmakuPlayer.stop()
danmakuPlayer.seekTo(positionMills)
AkDanmaku 定义了标准的弹幕数据类型 DanmakuItemData
,需要转换成此类型进行添加
// 批量更新数据
val data: List<DanmakuItemData> = parseDanmakuJson() // 数据解析
danmakuPlayer.update(data)
// 单个添加,多用于发送
val danmaku = DanmakuItemData().apply {
// init fields
}
danmakuPlayer.send(danmaku)
除去基础的播放功能外,其他功能均通过配置项来更改,更改配置时,需要创建一个新的 DanmakuConfig
对象
// 调整可见性
danmakuConfig = danmakuConfig.copy(visibility = false)
danmakuPlayer.updateConfig(danmakuConfig)
屏蔽等操作下放给业务方,除了默认的 Filter,也可以自定义新的弹幕过滤,过滤分为两种
- 数据过滤
DataFilter
,过滤后除非更新DanmakuConfig#dataFilter
并调用updateFilter
,被过滤的数据全程不会再参与任何绘制流程。 - 布局过滤
LayoutFilter
,过滤后弹幕不会进入布局、绘制的流程
自定义的过滤器需要同时自定义一个 ID,请不要与 DanmakuFilters
中的预制常量冲突。同样通过对应的 ID 可以找到对应的过滤器。
常用的集合过滤型过滤器有一个抽象类
SimpleDanmakuFilter
, 用户过滤、内容过滤、色彩过滤等均通过其实现
ID 为 DanmakuFilters.FILTER_TYPE_USER_ID
,不再支持 UserHash 过滤,对应类为 UserIdFilter
(dataFilters[DanmakuFilters.FILTER_TYPE_USER_ID] as? UserIdFilter)?.let { filter ->
filter.clear()
userIds.forEach { filter.addFilter(it) }
}
ID 为 DanmakuFilters.FILTER_TYPE_BLOCKED_TEXT
,对应类为 BlockedTextFilter
(dataFilters[DanmakuFilters.FILTER_TYPE_BLOCKED_TEXT] as? BlockedTextFilter)?.let { filter ->
filter.clear()
userIds.forEach { filter.addFilter(it) }
}
danmakuConfig = danmakuConfig.copy(
textSizeScale = scaleSize
)
danmakuPlayer.updatePlaySpeed(3f)
弹幕库不检测和拦截任何的触摸事件,需要在 DanmakuView
上处理触摸事件时请将它视作一个普通的 View。
val danmakus: List<DanmakuItem>? = danmakuPlayer.getDanmakusAtPoint(Point(x, y))
其中 DanmakuItem
中包含了弹幕数据结构和点击时的位置与区域
// 悬停,若之前存在有已经悬停的弹幕会被释放
danmakuPlayer.hold(danmaku)
// 释放,悬停的弹幕继续运动
danmakuPlayer.hold(null)
除去正常的弹幕展示以外,弹幕还可以添加任意的动画效果。
val danmaku = DanmakuItemData(
Random.nextLong(),
danmakuPlayer.getCurrentTimeMs() + 500,
"这是我自己发送的内容(*^▽^*)😄",
DanmakuItemData.DANMAKU_MODE_ROLLING,
textSize = 25,
textColor = Color.WHITE,
score = 9,
danmakuStyle = DanmakuItemData.DANMAKU_STYLE_ICON_UP,
rank = 9
)
val item = danmakuPlayer.obtainItem(danmaku)
val sequenceAction = Actions.sequence(
Actions.rotateBy(360f, 1000L),
Actions.scaleTo(1.5f, 1.5f, 500L),
Actions.scaleTo(0.8f, 0.8f, 300L)
)
item.addAction(
Actions.moveBy(0f, 300f, 1735L),
sequenceAction,
Actions.sequence(Actions.fadeOut(500L), Actions.fadeIn(300L))
)
danmakuPlayer.send(item)
目标弹幕的渲染流程为
- Data
- Layout
- Render
这三个模块部分均可以通过扩展对应的类来自定义,他们分别是
- DanmakkuItem:单个弹幕的数据,其中一定包含 DanmakuItemData,其他业务相关数据可以在自定义时加入
- DataSource:弹幕的数据源,提供更灵活的数据提供方式
- DanmakuLayouter:对弹幕进行布局的类
- DanmakuRenderer:绘制弹幕,默认实现了一个带描边的纯文字绘制弹幕渲染器,如果需要更多样式,可以扩展此类来实现