LoginSignup
5
7

More than 5 years have passed since last update.

Live2DWallpaperに背景を表示する

Last updated at Posted at 2015-10-01

開発環境

Live2D Android SDK 2.0.05_1のliveWallpaperプロジェクト
MacでEclipse4.2

Androidの壁紙の背景設定

「Live2D Android SDKで壁紙の設定方法がわからない」と困っている人がいたのでちょっとやり方を調べてみました。
 → How to set image as background in Live Wallpaper?

SurfaceViewを使っているので背景表示もOpenGLでやる必要があります。
ちなみにLive2DのAndroid版はOpenGL1.0を使ってました(OpenGL2.0を使ってないのは、古い端末に対応するための名残り?)

SampleApp1プロジェクトからコピーして、比較的簡単に実装する方法がわかったのでメモしておきます。
liveWallpaperでのやり方を書きますが、SDK内のsimpleプロジェクトも同様な手順で背景表示できます。

liveWallpaperの修正手順

1)sample/sampleApp1からsrc/jp/live2d/utils/androidをコピーする

2)sample/sampleApp1からsrc/jp/live2d/sample/LAppDefine.javaをコピーする

3)sample/sampleApp1からassets/image/back_class_normal.pngをコピーする

img_packageexplorer.png

4)LiveWallpaperService.javaを修正(修正箇所はAdd Codeとコメント入れました)

LiveWallpaperService.java
/**
 *
 *  You can modify and use this source freely
 *  only for the development of application related Live2D.
 *
 *  (c) Live2D Inc. All rights reserved.
 */
package jp.live2d.sample;


import jp.live2d.utils.android.FileManager;
import android.view.MotionEvent;
import net.rbgrn.android.glwallpaperservice.*;

public class LiveWallpaperService extends GLWallpaperService {

    public LiveWallpaperService() {
        super();
    }

    public Engine onCreateEngine() {
        MyEngine engine = new MyEngine();
        return engine;
    }

    class MyEngine extends GLEngine {
        Live2DRenderer renderer;

        public MyEngine() {
            super();
            // handle prefs, other initialization
            renderer = new Live2DRenderer(getApplicationContext());
            setRenderer(renderer);
            setRenderMode(RENDERMODE_CONTINUOUSLY);
            // Add Code Start
            FileManager.init(getApplicationContext());
            // Add Code End
        }

        @Override
        public void onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_UP:
                renderer.resetDrag();
                break;
            case MotionEvent.ACTION_MOVE:
                renderer.drag(event.getX(), event.getY());
                break;
            case MotionEvent.ACTION_CANCEL:
                break;
            }
        }

        public void onDestroy() {
            super.onDestroy();
            if (renderer != null) {
                renderer.release();
            }
            renderer = null;
        }
    }
}

5)Live2DRenderer.javaを修正(修正箇所はAdd Codeとコメント入れました)
 gl.glOrthof()を2回使い無理やりな気がするのでOpenGL的には正しくないかもですが、一応これでAndroidアプリが作れるかと思います。

Live2DRenderer.java
/**
 *
 *  You can modify and use this source freely
 *  only for the development of application related Live2D.
 *
 *  (c) Live2D Inc. All rights reserved.
 */
 package jp.live2d.sample;

import java.io.IOException;
import java.io.InputStream;

import jp.live2d.android.Live2DModelAndroid;
import jp.live2d.android.UtOpenGL;
import jp.live2d.framework.L2DPhysics;
import jp.live2d.framework.L2DStandardID;
import jp.live2d.framework.L2DTargetPoint;
import jp.live2d.motion.Live2DMotion;
import jp.live2d.motion.MotionQueueManager;
import jp.live2d.utils.android.FileManager;
import jp.live2d.utils.android.SimpleImage;
import android.content.Context;
import android.content.res.AssetManager;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import net.rbgrn.android.glwallpaperservice.*;

public class Live2DRenderer implements GLWallpaperService.Renderer
{
    Context con;

    Live2DModelAndroid  live2DModel ;
    Live2DMotion motion;
    MotionQueueManager motionMgr;
    L2DTargetPoint dragMgr;
    L2DPhysics physics;

    final String MODEL_PATH = "epsilon/Epsilon.moc" ;
    final String TEXTURE_PATHS[] =
        {
            "epsilon/Epsilon.1024/texture_00.png" ,
            "epsilon/Epsilon.1024/texture_01.png" ,
            "epsilon/Epsilon.1024/texture_02.png"
        } ;
    final String MOTION_PATH="epsilon/motions/Epsilon_idle_01.mtn";
    final String PHYSICS_PATH="epsilon/Epsilon.physics.json";

    float glWidth=0;
    float glHeight=0;

    // Add Code Start
    private SimpleImage bg;// BackGround Image
    private float modelWidth = 0;
    private float aspect = 0;
    // Add Code End

    public Live2DRenderer(Context context)
    {
        con = context;
        dragMgr=new L2DTargetPoint();
        motionMgr=new MotionQueueManager();
    }


    public void onDrawFrame(GL10 gl) {
        // Your rendering code goes here
        gl.glMatrixMode(GL10.GL_MODELVIEW ) ;
        gl.glLoadIdentity() ;
        gl.glClear( GL10.GL_COLOR_BUFFER_BIT ) ;
        // Add Code Start
        bg.draw(gl);    // background image draw
        // Live2D model adjust
        gl.glScalef(2.4f, 2.4f, 2.4f); // scale(x, y, z)
        gl.glTranslatef(0.0f, -0.3f, 0.0f); // position(x, y, z)
        gl.glOrthof(0 , modelWidth , modelWidth / aspect , 0 , 0.5f , -0.5f ) ;
        // Add Code End

        live2DModel.loadParam();

        if(motionMgr.isFinished())
        {
            motionMgr.startMotion(motion, false);
        }
        else
        {
            motionMgr.updateParam(live2DModel);
        }

        live2DModel.saveParam();

        dragMgr.update();

        float dragX=dragMgr.getX();
        float dragY=dragMgr.getY();
        live2DModel.addToParamFloat(L2DStandardID.PARAM_ANGLE_X, dragX*30);
        live2DModel.addToParamFloat(L2DStandardID.PARAM_ANGLE_Y, dragY*30);
        live2DModel.addToParamFloat(L2DStandardID.PARAM_BODY_ANGLE_X, dragX*10);

        physics.updateParam(live2DModel);

        live2DModel.setGL( gl ) ;

        live2DModel.update() ;
        live2DModel.draw() ;

    }

    public void onSurfaceChanged(GL10 gl, int width, int height) {
        gl.glViewport( 0 , 0 , width , height ) ;

        gl.glMatrixMode( GL10.GL_PROJECTION ) ;
        gl.glLoadIdentity() ;

        // Add Code Start
        modelWidth = live2DModel.getCanvasWidth();
        aspect = (float)width/height;
//      gl.glOrthof(
//              0 ,
//              modelWidth ,
//              modelWidth * height / width,
//              0 ,
//              0.5f ,  -0.5f
//              ) ;
//      gl.glOrthof(0, modelWidth, modelWidth * height / width, 0, 0.5f, -0.5f);
        // background image adjust
        gl.glOrthof(-2.0f , 2.0f , -2.0f ,2.0f , 0.5f , -0.5f ) ;
        // Add Code End

        glWidth=width;
        glHeight=height;
    }

    public void onSurfaceCreated(GL10 gl, EGLConfig config)
    {
        AssetManager mngr = con.getAssets();
        try
        {
            InputStream in = mngr.open( MODEL_PATH ) ;
            live2DModel = Live2DModelAndroid.loadModel( in ) ;
            in.close() ;
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

        try
        {
            // Add Code Start
            setupBackground(gl);
            // Add Code End

            //texture
            for (int i = 0 ; i < TEXTURE_PATHS.length ; i++ )
            {
                InputStream in = mngr.open( TEXTURE_PATHS[i] ) ;
                int texNo = UtOpenGL.loadTexture(gl , in , true ) ;
                live2DModel.setTexture( i , texNo ) ;
                in.close();
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

        try
        {
            InputStream in = mngr.open( MOTION_PATH ) ;
            motion = Live2DMotion.loadMotion( in ) ;
            in.close() ;

            in=mngr.open(PHYSICS_PATH);
            physics=L2DPhysics.load(in);
            in.close();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * Called when the engine is destroyed. Do any necessary clean up because
     * at this point your renderer instance is now done for.
     */
    public void release() {
    }

    public void resetDrag()
    {
        dragMgr.set(0, 0);
    }


    public void drag(float x,float y)
    {
        float screenX=x/glWidth*2-1;
        float screenY=-y/glHeight*2+1;

//      Log.i("", "x:"+screenX+" y:"+screenY);

        dragMgr.set(screenX,screenY);
    }

    /*
     * BackGround Image Setting
     * @param context
     */
    private void setupBackground(GL10 context) {
        try {
            InputStream in = FileManager.open(LAppDefine.BACK_IMAGE_NAME);
            bg=new SimpleImage(context,in);
            bg.setDrawRect(
                    LAppDefine.VIEW_LOGICAL_MAX_LEFT,
                    LAppDefine.VIEW_LOGICAL_MAX_RIGHT,
                    LAppDefine.VIEW_LOGICAL_MAX_BOTTOM,
                    LAppDefine.VIEW_LOGICAL_MAX_TOP);

            // uv area
            bg.setUVRect(0.0f,1.0f,0.0f,1.0f);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

img_wallpaper.png

あとはassets配下に違う画像を入れて、LAppDefine.javaのBACK_IMAGE_NAMEの変更すれば背景画像を変えられます。

おまけ

ちなみにSimpleプロジェクトの方だとソースは以下のように修正すると背景が表示されます。
(SampleApp1からコピーしてくるものは同じ)

SampleActivity.java
package jp.live2d.sample;

import jp.live2d.Live2D;
import jp.live2d.utils.android.FileManager;
import android.app.Activity;
import android.os.Bundle;

public class SampleActivity extends Activity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        Live2D.init();

        FileManager.init(this.getApplicationContext());

        SampleGLSurfaceView     view = new SampleGLSurfaceView(this) ;
        setContentView( view ) ;
    }

}
SampleGLSurfaceView.java
package jp.live2d.sample;

import java.io.IOException;
import java.io.InputStream;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import jp.live2d.android.Live2DModelAndroid;
import jp.live2d.android.UtOpenGL;
import jp.live2d.util.UtSystem;
import jp.live2d.utils.android.FileManager;
import jp.live2d.utils.android.SimpleImage;
import android.content.Context;
import android.opengl.GLSurfaceView;

public class SampleGLSurfaceView extends GLSurfaceView
{
    private SampleGLRenderer        renderer ;
    private SimpleImage bg;// 背景の描画
    private float modelWidth = 0;
    private float aspect = 0;

    public SampleGLSurfaceView(Context context )
    {
        super(context);

        renderer = new SampleGLRenderer() ;
        setRenderer( renderer ) ;
    }


    class SampleGLRenderer implements Renderer
    {
        private Live2DModelAndroid  live2DModel ;
        private final String MODEL_PATH = "haru/haru.moc" ;
        private final String TEXTURE_PATHS[] =
            {
                "haru/haru.1024/texture_00.png" ,
                "haru/haru.1024/texture_01.png" ,
                "haru/haru.1024/texture_02.png"
            } ;

        @Override
        public void onDrawFrame(GL10 gl)
        {
            gl.glMatrixMode(GL10.GL_MODELVIEW ) ;
            gl.glLoadIdentity() ;
            gl.glClear( GL10.GL_COLOR_BUFFER_BIT ) ;
            // background draw
            bg.draw(gl);

            // Live2Dモデル調整
            gl.glScalef(2.8f, 2.8f, 2.8f);
            gl.glTranslatef(0.0f, -0.3f, 0.0f);
            gl.glOrthof(0 , modelWidth , modelWidth / aspect , 0 , 0.5f , -0.5f ) ;

            double t = (UtSystem.getUserTimeMSec()/1000.0) * 2 * Math.PI  ;// 1秒ごとに2π(1周期)増える
            double cycle=3.0;// パラメータが一周する時間(秒)
            double sin=Math.sin( t/cycle );// -1から1の間を周期ごとに変化する
            live2DModel.setParamFloat( "PARAM_ANGLE_X" , (float) (30 * sin) ) ;// PARAM_ANGLE_Xのパラメータが[cycle]秒ごとに-30から30まで変化する

            live2DModel.setGL( gl ) ;

            live2DModel.update() ;
            live2DModel.draw() ;
        }


        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height)
        {
            // ビューポートはデバイスの幅と合わせる。画面全体に表示される。
            gl.glViewport( 0 , 0 , width , height ) ;

            // 簡易的にプロジェクション行列一つですべての変換を行う。
            gl.glMatrixMode( GL10.GL_PROJECTION ) ;
            gl.glLoadIdentity() ;

            modelWidth = live2DModel.getCanvasWidth();// モデラーで設定したキャンバス幅
            aspect = (float)width/height;
            //modelWidth=2400.0, aspect=0.6642066, modelWidth /aspect = 3613.33356218
            // 描画範囲の設定 引数はleft,right,bottom,topの順
//          gl.glOrthof(0 , modelWidth , modelWidth / aspect , 0 , 0.5f , -0.5f ) ;
            // background 用に調整
            gl.glOrthof(-2.0f , 2.0f , -2.0f ,2.0f , 0.5f , -0.5f ) ;
        }


        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config)
        {
            //  モデルの初期化
            try
            {
                // 背景の作成
                setupBackground(gl);


                InputStream in = getContext().getAssets().open( MODEL_PATH ) ;
                live2DModel = Live2DModelAndroid.loadModel( in ) ;
                in.close() ;

                for (int i = 0 ; i < TEXTURE_PATHS.length ; i++ )
                {
                    InputStream tin = getContext().getAssets().open( TEXTURE_PATHS[i] ) ;
                    int texNo = UtOpenGL.loadTexture(gl , tin , true ) ;
                    live2DModel.setTexture( i , texNo ) ;
                }
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }

        /*
         * 背景の設定
         * @param context
         */
        private void setupBackground(GL10 context) {
            try {
                InputStream in = FileManager.open(LAppDefine.BACK_IMAGE_NAME);
//              InputStream in = getContext().getAssets().open( LAppDefine.BACK_IMAGE_NAME ) ;
                bg=new SimpleImage(context,in);
                // 描画範囲。画面の最大表示範囲に合わせる
                bg.setDrawRect(
                        LAppDefine.VIEW_LOGICAL_MAX_LEFT,
                        LAppDefine.VIEW_LOGICAL_MAX_RIGHT,
                        LAppDefine.VIEW_LOGICAL_MAX_BOTTOM,
                        LAppDefine.VIEW_LOGICAL_MAX_TOP);

                // 画像を使用する範囲(uv)
                bg.setUVRect(0.0f,1.0f,0.0f,1.0f);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


5
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7