開発環境
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をコピーする
4)LiveWallpaperService.javaを修正(修正箇所はAdd Codeとコメント入れました)
/**
*
* 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アプリが作れるかと思います。
/**
*
* 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();
}
}
}
あとはassets配下に違う画像を入れて、LAppDefine.javaのBACK_IMAGE_NAMEの変更すれば背景画像を変えられます。
おまけ
ちなみにSimpleプロジェクトの方だとソースは以下のように修正すると背景が表示されます。
(SampleApp1からコピーしてくるものは同じ)
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 ) ;
}
}
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();
}
}
}