有锯齿的圆
解决方案是 smoothstep
。它根据到 texture
与背景的变换起始点的距离平滑的从0
到1
变化。因此距离 0
到 0.49
时 texture
的透明度为 1
,大于等于 0.5
时为 0
,0.49
和 0.5
之间时平滑变化,如此圆的边就平滑了。
无锯齿圆
OpenGL
中如何使用 texture
显示图像和文本?在动画中圆有两种状态 —— 普通和选中。在普通状态下圆的 texture
包含文字和颜色,在选中状态下同时包含图像。因此我需要为每个圆创建两个不同的 texture
。
我使用 Bitmap
实例来创建 texture
,绘制所有元素。
fun bindTextures(textureIds: IntArray, index: Int) {
texture = bindTexture(textureIds, index * 2, false)
imageTexture = bindTexture(textureIds, index * 2 + 1, true)
}
private fun bindTexture(textureIds: IntArray, index: Int, withImage: Boolean): Int {
glGenTextures(1, textureIds, index)
createBitmap(withImage).toTexture(textureIds[index])
return textureIds[index]
}
private fun createBitmap(withImage: Boolean): Bitmap {
var bitmap = Bitmap.createBitmap(bitmapSize.toInt(), bitmapSize.toInt(), Bitmap.Config.ARGB_4444)
val bitmapConfig: Bitmap.Config = bitmap.config ?: Bitmap.Config.ARGB_8888
bitmap = bitmap.copy(bitmapConfig, true)
val canvas = Canvas(bitmap)
if (withImage) drawImage(canvas)
drawBackground(canvas, withImage)
drawText(canvas)
return bitmap
}
private fun drawBackground(canvas: Canvas, withImage: Boolean) {
…
}
private fun drawText(canvas: Canvas) {
…
}
private fun drawImage(canvas: Canvas) {
…
}
之后我将 texture
单元赋值给 u_Text
变量。我使用 texture2()
方法获取片段的真实颜色,texture2()
接收 texture
单元和片段顶点的位置两个参数。
关于动画的物理特性十分的简单。主要的对象是 World
实例,所有的实体创建都需要它。
class CircleBody(world: World, var position: Vec2, var radius: Float, var increasedRadius: Float) {
val decreasedRadius: Float = radius
val increasedDensity = 0.035f
val decreasedDensity = 0.045f
var isIncreasing = false
var isDecreasing = false
var physicalBody: Body
var increased = false
private val shape: CircleShape
get() = CircleShape().apply {
m_radius = radius + 0.01f
m_p.set(Vec2(0f, 0f))
}
private val fixture: FixtureDef
get() = FixtureDef().apply {
this.shape = this@CircleBody.shape
density = if (radius > decreasedRadius) decreasedDensity else increasedDensity
}
private val bodyDef: BodyDef
get() = BodyDef().apply {
type = BodyType.DYNAMIC
this.position = this@CircleBody.position
}
init {
physicalBody = world.createBody(bodyDef)
physicalBody.createFixture(fixture)
}
}
如你所见创建实体很简单:需要指定实体的类型(例如:动态、静态、运动学)、位置、半径、形状、密度以及运动。
每次画面绘制,都需要调用 World
的 step()
方法移动所有的实体。之后你可以在图形的新位置进行绘制。
我遇到的问题是 World
的重力只能是一个方向,而不能是一个点。JBox2D
不支持轨道重力。因此将圆移动到屏幕中心是无法实现的,所以我只能自己来实现引力。
private val currentGravity: Float
get() = if (touch) increasedGravity else gravity
private fun move(body: CircleBody) {
body.physicalBody.apply {
val direction = gravityCenter.sub(position)
val distance = direction.length()
val gravity = if (body.increased) 1.3f * currentGravity else currentGravity
if (distance > step * 200) {
applyForce(direction.mul(gravity / distance.sqr()), position)
}
}
}
引力挑战
每次发生移动时,我计算出力的大小并作用于每个实体,看上去就像圆受引力作用在移动。
GLSurfaceView
和其它的 Android view
一样可以响应用户的点击事件。
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
startX = event.x
startY = event.y
previousX = event.x
previousY = event.y
}
MotionEvent.ACTION_UP -> {
if (isClick(event)) renderer.resize(event.x, event.y)
renderer.release()
}
MotionEvent.ACTION_MOVE -> {
if (isSwipe(event)) {
renderer.swipe(event.x, event.y)
previousX = event.x
previousY = event.y
} else {
release()
}
}
else -> release()
}
return true
}
private fun release() = postDelayed({ renderer.release() }, 1000)
private fun isClick(event: MotionEvent) = Math.abs(event.x - startX) < 20 && Math.abs(event.y - startY) < 20
private fun isSwipe(event: MotionEvent) = Math.abs(event.x - previousX) > 20 && Math.abs(event.y - previousY) > 20
最后
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。
同时我经过多年的收藏目前也算收集到了一套完整的学习资料以及高清详细的Android架构进阶学习导图及笔记免费分享给大家,希望对想成为架构师的朋友有一定的参考和帮助。
下面是部分资料截图,诚意满满:特别适合有开发经验的Android程序员们学习。
资料免费领取方式:点击我的GitHub~
不论遇到什么困难,都不应该成为我们放弃的理由!
和帮助。
下面是部分资料截图,诚意满满:特别适合有开发经验的Android程序员们学习。
[外链图片转存中…(img-FjtAZiWQ-1643776884601)]
资料免费领取方式:点击我的GitHub~
不论遇到什么困难,都不应该成为我们放弃的理由!
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。