技术人扯技术Android开发Android技术知识

Android自定义View--螺旋丸

2018-10-22  本文已影响33人  a49f87ef5d4f

0.前言

最近在做游戏,所以博客就改成双周更了。诚恳的地说,我不是一个火影迷,初中的时候看了几集,高中的时候看了几集,然后就拉倒了。我觉得嘴遁鸣人的螺旋丸挺不错的,今天准备做一个9.9元包邮版螺旋丸。看效果图


image

1.分析

1.1这个球是个立体的

立体的话这里采用opengl es进行实现

1.2球分三层

最里面是白色,碗面浅蓝,再者更蓝,最后是白色线段框架

1.3球要旋转

螺旋丸螺旋丸,自然得转圈了

2.代码

2.1绘制球体

HelicalCircle是主体,包括program和shader的创建,球坐标的创建,最后的绘制

2.1.1准备球体坐标

 private fun preparePosList() {
        var x0 = 0f
        var y0 = 0f
        var z0 = radius
        var x1 = 0f
        var y1 = 0f
        var z1 = radius

        val h = 360 / precision

        val v = 360 / precision

        for (i in 0 until h) {
            val arc0 = Math.PI / 180 * i * precision
            val arc1 = Math.PI / 180 * (i + 1) * precision
            y0 = (radius * Math.cos(arc0)).toFloat()
            y1 = (radius * Math.cos(arc1)).toFloat()
            for (j in 0 until v) {
                val arc = Math.PI / 180 * j * precision
                x0 = (radius * Math.sin(arc0) * sin(arc)).toFloat()
                z0 = (radius * Math.sin(arc0) * cos(arc)).toFloat()
                x1 = (radius * Math.sin(arc1) * sin(arc)).toFloat()
                z1 = (radius * Math.sin(arc1) * cos(arc)).toFloat()

                posList.add(x0)
                posList.add(y0)
                posList.add(z0)
                posList.add(x1)
                posList.add(y1)
                posList.add(z1)
            }
        }

        pos= posList.toFloatArray()
    }

这段代码生成了一个float数组,就是每个球体上点的坐标。这里采用的是球坐标,原理很简单,你可以把球体横向按照precision为梯度进行横切,然后每个切面也按照precision进行竖切,每个点的坐标计算出来后添加进一个List然后转换为float数组。

2.1.2准备program和shader


 private val vertexSlgl="#version 300 es\n" +
            "\n" +
            "layout(location=0) in vec3 pos;\n" +
            "uniform mat4 model;\n" +
            "uniform mat4 view;\n" +
            "uniform mat4 projection;\n" +
            "void main()\n" +
            "{\n" +
            "   gl_Position=projection*view*model*vec4(pos,1.0);\n" +
            "}"

    private val fragmentSlgl="#version 300 es\n" +
            "precision mediump float;\n" +
            "uniform vec4 aColor;\n" +
            "out vec4 fragColor;\n" +
            "void main()\n" +
            "{\n" +
            "   fragColor=aColor;\n" +
            "}"

 private fun prepareProgram()
    {
        programId = createProgram(vertexSlgl, fragmentSlgl)
        val vaoArray = IntArray(1)
        GLES30.glGenVertexArrays(1, vaoArray, 0)
        VAO = vaoArray[0]
        GLES30.glBindVertexArray(VAO)
        val vboArray = IntArray(1)
        GLES30.glGenBuffers(1, vboArray, 0)
        VBO = vboArray[0]
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, VBO)
        val posBuffer = ByteBuffer.allocateDirect(4 * pos.size).order(ByteOrder.nativeOrder()).asFloatBuffer()
        posBuffer.put(pos)
        posBuffer.position(0)
        GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, 4 * pos.size, posBuffer, GLES30.GL_STATIC_DRAW)
        GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 3 * 4, 0)
        GLES30.glEnableVertexAttribArray(0)
    }

    private fun createProgram(vertexSource: String, fragmentSource: String): Int {
        val vertexShader = createShader(GLES30.GL_VERTEX_SHADER, vertexSource)
        val fragmentShader = createShader(GLES30.GL_FRAGMENT_SHADER, fragmentSource)
        return createProgram(vertexShader, fragmentShader)
    }


    private fun createShader(type: Int, source: String): Int {
        val shaderId = GLES30.glCreateShader(type)
        if (shaderId <= 0) {
            Log.d(TAG, "create shader failed")
        }
        GLES30.glShaderSource(shaderId, source)
        GLES30.glCompileShader(shaderId)
        val status = IntArray(1)
        GLES30.glGetShaderiv(shaderId, GLES30.GL_COMPILE_STATUS, status, 0)
        if (status[0] <= 0) {
            Log.d(TAG, "compile shader failed")
            val infoLog = GLES30.glGetShaderInfoLog(shaderId)
            Log.d(TAG, infoLog)
            GLES30.glDeleteShader(shaderId)
        }
        return shaderId
    }

    private fun createProgram(vertexShader: Int, fragmentShader: Int): Int {
        val programId = GLES30.glCreateProgram()
        if (programId <= 0) {
            Log.d(TAG, "create program failed")
        }
        GLES30.glAttachShader(programId, vertexShader)
        GLES30.glAttachShader(programId, fragmentShader)
        GLES30.glLinkProgram(programId)
        val status = IntArray(1)
        GLES30.glGetProgramiv(programId, GLES30.GL_LINK_STATUS, status, 0)
        if (status[0] <= 0) {
            Log.d(TAG, "link program failed")
            val infoLog = GLES30.glGetProgramInfoLog(programId)
            Log.d(TAG, infoLog)
        }
        return programId
    }

这段代码就是创建shader和program,很简单,没什么可说的。

2.1.2绘制

    fun draw(aspect:Float,r:Float=0.0f,g:Float=0.0f,b:Float=1.0f,a:Float=0.5f,scale:Float=1.0f)
    {
        degree+=10
        GLES30.glUseProgram(programId)
        val colorLocation=GLES30.glGetUniformLocation(programId,"aColor")
        GLES30.glUniform4f(colorLocation,r,g,b,a)
        val modelMatrixLocation=GLES30.glGetUniformLocation(programId,"model")
        Matrix.setIdentityM(modelMatrix,0)
        Matrix.scaleM(modelMatrix,0,scale,scale,scale)
        Matrix.rotateM(modelMatrix,0, Math.toRadians(degree).toFloat(),0f,1f,0f)
        GLES30.glUniformMatrix4fv(modelMatrixLocation,1,false,modelMatrix,0)
        val viewMatrixLocation=GLES30.glGetUniformLocation(programId,"view")
        Matrix.setLookAtM(viewMatrix,0,0f,0f,10f,0f,0f,0f,0f,1f,0f)
        GLES30.glUniformMatrix4fv(viewMatrixLocation,1,false,viewMatrix,0)
        val projectionMatrixLocation=GLES30.glGetUniformLocation(programId,"projection")
        Matrix.perspectiveM(projectionMatrix,0,45.0f, aspect,0.1f,100f)
        GLES30.glUniformMatrix4fv(projectionMatrixLocation,1,false,projectionMatrix,0)
        GLES30.glDrawArrays(model,0,pos.size / 3)
    }

}

绘制球体,设置球体颜色和缩放程度

2.HelicalCircleRender

override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {

        helicalCircle.prepare()

    }

    override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
        GLES30.glViewport(0, 0, width, height)
        screenWidth = width.toFloat()
        screenHeight = height.toFloat()
    }

    override fun onDrawFrame(gl: GL10?) {
        GLES30.glEnable(GLES30.GL_BLEND)
        GLES30.glClearColor(0f,0f,0f,1.0f)
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT or GLES30.GL_DEPTH_BUFFER_BIT)
        val aspect=screenWidth / screenHeight
        GLES30.glDisable(GLES30.GL_DEPTH_TEST)
        helicalCircle.model=GLES30.GL_TRIANGLE_STRIP
        helicalCircle.draw(aspect,r=0.67f,g=0.85f,b=0.98f,a=0.5f,scale = 0.99f*scale)
        helicalCircle.draw(aspect,r=0.85f,g=0.96f,b=0.99f,a=0.5f,scale= 0.5f*scale)
        helicalCircle.draw(aspect,r=0.97f,g=1.0f,b=0.99f,a=0.5f,scale= 0.3f*scale)
        GLES30.glEnable(GLES30.GL_DEPTH_TEST)
        helicalCircle.model=GLES30.GL_LINE_STRIP
        helicalCircle.draw(aspect,r=1.0f,g=1.0f,b=1.0f,a=0.2f,scale = 0.998f*scale)
    }

onSurfaceCreated里进行球坐标的计算和program以及shader的准备,在onDrawFrame里绘制三个球

2.3旋转

 override fun onTouchEvent(event: MotionEvent?): Boolean {
        when(event?.action)
        {
            MotionEvent.ACTION_DOWN->{

                touchX=event.x

            }

            MotionEvent.ACTION_MOVE->{

                val scaleOffset=(event.x-touchX)/width
                render.scale+=scaleOffset
                touchX=event.x
            }
        }

        return true
    }

复写HelicalCircleView的onTouchEvent方法,通过修改HelicalCircleRender的scale来对球体进行缩放。

3源码

github

image

关注我的公众号

上一篇下一篇

猜你喜欢

热点阅读