根据系列图片绘制动画总结
一、采用系统提供的Animation类,用自带的方法
其中的animation.xml文件如下:
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false" >
<item
android:drawable="@drawable/a"
android:duration="100"/>
<item
android:drawable="@drawable/b"
android:duration="100"/>
<item
android:drawable="@drawable/c"
android:duration="100"/>
<item
android:drawable="@drawable/d"
android:duration="100"/>
<item
android:drawable="@drawable/e"
android:duration="100"/>
<item
android:drawable="@drawable/f"
android:duration="100"/>
<item
android:drawable="@drawable/g"
android:duration="100"/>
<item
android:drawable="@drawable/h"
android:duration="100"/>
<item
android:drawable="@drawable/i"
android:duration="100"/>
<item
android:drawable="@drawable/j"
android:duration="100"/>
</animation-list>
/** 拿到ImageView对象 **/
imageView = (ImageView) findViewById(R.id.imageView);
/** 通过ImageView对象拿到背景显示的AnimationDrawable **/
animationDrawable = (AnimationDrawable) imageView.getBackground();
animationDrawable.setOneShot(true);
/** 开始播放动画 **/
button0 = (Button) findViewById(R.id.button0);
button0.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
/** 播放动画 **/
if (animationDrawable != null && !animationDrawable.isRunning()) {
animationDrawable.start();
}
}
});
其中://设置单次播放animationDrawable.setOneShot(true);
二、提供了很多帧动画的图片,利用android绘图,可以绘制出人物走动的动画。
如上图,这种情况下,可以按照如下步骤,绘制动画:
1、根据人物上下左右行走,可以分为四段动画,可以定义一个长度为4的动画数组;
2、根据键盘上下左右键事件分别触发不同的动画。
主要绘制的语句如下:
mHeroAnim[ANIM_DOWN] = new Animation(context,new int []{R.drawable.hero_down_a,R.drawable.hero_down_b,R.drawable.hero_down_c,R.drawable.hero_down_d},true);
mHeroAnim[ANIM_LEFT] = new Animation(context,new int []{R.drawable.hero_left_a,R.drawable.hero_left_b,R.drawable.hero_left_c,R.drawable.hero_left_d},true);
mHeroAnim[ANIM_RIGHT]= new Animation(context,new int []{R.drawable.hero_right_a,R.drawable.hero_right_b,R.drawable.hero_right_c,R.drawable.hero_right_d},true);
mHeroAnim[ANIM_UP] = new Animation(context,new int []{R.drawable.hero_up_a,R.drawable.hero_up_b,R.drawable.hero_up_c,R.drawable.hero_up_d},true);
三、只提供一张人物图片,就要采用程序来切割该图片,得到动画所需要的帧图像。
如上图,这种情况下,可以按照如下步骤,绘制动画:
1、通过该图片的宽度和高度,和定义的Tile的宽度和高度,可以切割出12张所需要的帧图像;
2、根据人物上下左右行走,可以分为四段动画,可以定义一个长度为4的动画数组;
3、根据键盘上下左右键事件分别触发不同的动画;
切割图片,以及绘制动画的主要代码如下:
// 利用程序来切割图片
Bitmap testmap = BitmapFactory.decodeResource(getResources(),
R.drawable.enemy);
Bitmap[][] bitmap = new Bitmap[ANIM_COUNT][ANIM_COUNT];
int tileWidth = testmap.getWidth() / ANIM_COUNT;
int tileHeight = testmap.getHeight() / ANIM_COUNT;
int i = 0, x = 0, y = 0;
for (i = 0; i < ANIM_COUNT; i++) {
y = 0;
bitmap[ANIM_DOWN][i] = BitmapClipBitmap(testmap, x, y, tileWidth,
tileHeight);
y += tileHeight;
bitmap[ANIM_LEFT][i] = BitmapClipBitmap(testmap, x, y, tileWidth,
tileHeight);
y += tileHeight;
bitmap[ANIM_RIGHT][i] = BitmapClipBitmap(testmap, x, y, tileWidth,
tileHeight);
y += tileHeight;
bitmap[ANIM_UP][i] = BitmapClipBitmap(testmap, x, y, tileWidth,
tileHeight);
x += tileWidth;
}
mTestAnim[ANIM_DOWN] = new Animation(this, bitmap[ANIM_DOWN], true);
mTestAnim[ANIM_LEFT] = new Animation(this, bitmap[ANIM_LEFT], true);
mTestAnim[ANIM_RIGHT] = new Animation(this, bitmap[ANIM_RIGHT], true);
mTestAnim[ANIM_UP] = new Animation(this, bitmap[ANIM_UP], true);
注意:以上后面两种方式分别采用了不同的绘制方法
第一种的绘制构造方法为:Animation(Contextcontext, int [] frameBitmapID, boolean isloop);
第二种的绘制构造方法为:Animation(Contextcontext, Bitmap [] frameBitmap, boolean isloop);
有了这些准备条件后,我们可以开始真正的绘制:
public void DrawAnimation(Canvas Canvas, Paint paint, int x, int y) {
// 如果没有播放结束则继续播放
if (!mIsend) {
Canvas.drawBitmap(mframeBitmap[mPlayID], x, y, paint);
long time = System.currentTimeMillis();
if (time - mLastPlayTime > ANIM_TIME) {
mPlayID++;
mLastPlayTime = time;
if (mPlayID >= mFrameCount) {
// 标志动画播放结束
mIsend = true;
if (mIsLoop) {
// 设置循环播放
mIsend = false;
mPlayID = 0;
}
}
}
}
}
这里采用了两个标志位来判断动画的状态,mIsend判断动画是否播放,true结束播放,false为播放动画;mIsloop判断动画是否循 环,true为循环,false为不循环;当然,动画循环的时候,动画肯定是播放的,于是mIsloop为true,则mIsend为false,由于每一组动画都为四张图片,所以mPlayID最多为4,当人物一组动作也就是一个动画完成后,表示4张图片都绘制了一遍,则要讲mPlayID置为0,表示 重新开始需要绘制四张图片代表一个动画。
我们在主类中如何通过按键来触发动画呢?需要通过实现onDraw()方法,并且不断的重绘,主要代码如下:
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mMapImage, 0, 0, mPaint);
canvas.save();
canvas.clipRect(0, 0, 320, 30);
mPaint.setColor(Color.WHITE);
canvas.drawRect(0, 0, 480, 30, mPaint);
mPaint.setColor(Color.RED);
canvas.restore();
/** 根据按键更新显示动画 **/
if (mAllkeyDown) {
if (mIskeyDown) {
mAnimationState = ANIM_DOWN;
canvas.drawText("按下下键,开始播放向下动画开始", 0, 20, mPaint);
} else if (mIskeyLeft) {
mAnimationState = ANIM_LEFT;
canvas.drawText("按下左键,开始播放向左动画开始", 0, 20, mPaint);
} else if (mIskeyRight) {
mAnimationState = ANIM_RIGHT;
canvas.drawText("按下右键,开始播放向右动画开始", 0, 20, mPaint);
} else if (mIskeyUp) {
mAnimationState = ANIM_UP;
canvas.drawText("按下上键,开始播放向上动画开始", 0, 20, mPaint);
}
/** 绘制主角动画 **/
mHeroAnim[mAnimationState].DrawAnimation(canvas, mPaint, 20, 100);
mTestAnim[mAnimationState].DrawAnimation(canvas, mPaint, 100, 100);
} else {
/** 按键抬起后人物停止动画 **/
mHeroAnim[mAnimationState].DrawFrame(canvas, mPaint, 20, 100, 0);
mTestAnim[mAnimationState].DrawFrame(canvas, mPaint, 100, 100, 0);
canvas.drawText("按键已经抬起动画停止", 0, 20, mPaint);
}
super.onDraw(canvas);
invalidate();
}
这样,我们的动画绘制时时刻刻都在进行着。
我们可以通过控制上下左右按键,来控制标志:
public void setKeyState(int keyCode, boolean state) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_DOWN:
mIskeyDown = state;
break;
case KeyEvent.KEYCODE_DPAD_UP:
mIskeyUp = state;
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
mIskeyLeft = state;
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
mIskeyRight = state;
break;
}
mAllkeyDown = state;
}
地图绘制理解
地图绘制可以借助工具,得到相应的地图数组数据;有了地图数组数据,则可以从提供的地图库文件根据ID得到相应的地图块,然后画在屏幕上;
因为地图数组数据比较容易得到,再次不过多阐述,分析关键的绘图原理机制:
1、准备一张地图库文件(其中包含了所有地图上需要的图形元素,如图所示),所有的地图元素都从这里面切割出去,然后绘制成想要的东西;
2、可以由工具得到想要的地图数组数据,如下:
//第一层游戏View地图数组
public int [][]mMapView = {
{ 1, 1, 1, 1, 137, 137, 137, 1, 1, 1 },
{ 1, 1, 1, 1, 137, 137, 137, 1, 1, 1 },
{ 1, 1, 1, 1, 137, 137, 137, 1, 1, 1 },
{ 137, 137, 137, 137, 137, 137, 137, 137, 137, 137 },
{ 137, 137, 137, 137, 137, 137, 137, 137, 137, 137 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 137, 137 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 137, 137 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 137, 137 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 137, 137 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 137, 137 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 137, 137 },
{ 137, 137, 137, 137, 137, 137, 137, 137, 137, 137 },
{ 137, 137, 137, 137, 137, 137, 137, 137, 137, 137 },
{ 1, 1, 1, 1, 1, 137, 137, 137, 1, 1 },
{ 1, 1, 1, 1, 1, 137, 137, 137, 1, 1 }
};
//第二层游戏实体actor数组
public int [][]mMapAcotor = {
{ 102, 103, 103, 104, 0, 0, 0, 165, 166, 167 },
{ 110, 111, 111, ......
......
3、通过2中的地图数组数据,我们可以代码中动态的切割上面的地图库文件来进行绘制;
private void DrawMap(Canvas canvas, Paint paint, Bitmap bitmap) {
int i, j;
for (i = 0; i < TILE_HEIGHT_COUNT; i++) {
for (j = 0; j < TILE_WIDTH_COUNT; j++) {
int ViewID = mMapView[i][j];
int ActorID = mMapAcotor[i][j];
// 绘制地图第一层
if (ViewID > TILE_NULL) {
DrawMapTile(ViewID, canvas, paint, bitmap, j * TILE_WIDTH,
i * TILE_HEIGHT);
}
// 绘制地图第二层
if (ActorID > TILE_NULL) {
DrawMapTile(ActorID, canvas, paint, bitmap, j * TILE_WIDTH,
i * TILE_HEIGHT);
}
}
}
}
此处TILE_HEIGH_COUNT为纵向tile的数量,TILE_WIDTH_COUNT为横向tile的数量,viewId代表在地图库文件中的具体位置,actorId也是一样,那么通过一个id是如何找到在地图库文件中的位置的呢?我们来看看DrawMapTile()方法是如何实现的:
private void DrawMapTile(int id, Canvas canvas, Paint paint, Bitmap bitmap,
int x, int y) {
// 根据数组中的ID算出在地图资源中的XY 坐标
// 因为编辑器默认0 所以第一张tile的ID不是0而是1 所以这里 -1
id--;
int count = id / mWidthTileCount;
int bitmapX = (id - (count * mWidthTileCount)) * TILE_WIDTH;
int bitmapY = count * TILE_HEIGHT;
DrawClipImage(canvas, paint, bitmap, x, y, bitmapX, bitmapY,
TILE_WIDTH, TILE_HEIGHT);
}
由此处代码可以看出,tile的id在地图库文件中是按横向顺序排列的方式定义的,假如横排10个,竖排15个,现在有一个ID为66的tile,则应该在第7行的第7个位置。按照上面的算法,id-- = 65;int count = 65/10=6;
Int bitmapX =(66-(6*10))*32 = 6*32,int bitmapY = 6*32,于是可以根据这个tile的x和y的具体值可以找到地图库文件中的一个矩形区域,然后接下来,变为想到的一个tile,于是整个地图便是由一个一个这样的tile得到而来。