# 捉小猪
## 捉小猪是一款基于 SurfaceView 的 Android 休闲小游戏.
先上效果图:
![](https://www.writebug.com/myres/static/uploads/2021/10/25/3ab44bfe3ba8eaea90f2d81a91aeb561.writebug)![](https://www.writebug.com/myres/static/uploads/2021/10/25/e85ad2985715a342ac7312dbec871f9d.writebug)
## 实现思路
我们可以把每一个树桩, 小猪, 车厢都看成是一个 Drawable, 这个 Drawable 里面保存了 x, y 坐标, 我们的 SurfaceView 在 draw 的时候, 就把这些 Drawable draw 出来.
那可能有的小伙伴就会问了:
1. 那小猪是怎么让它跑起来, 并且腿部还不断地在动呢?
2. 还有小猪是怎么找到出路的呢?
刚刚我们讲过小猪是 Drawable, 其实我们自定义的这个 Drawable 就是一个帧动画, 它里面有一个 Bitmap 数组, 一个 currentIndex(这个用来记录当前帧), 我们在子线程里面不断更新这个 currentIndex, 当 Drawable 被调用 draw 的时候, 就根据 currentIndex 来从 Bitmap 数组里面取对应的 bitmap 出来. 刚刚还讲过 Drawable 里面保存了当前 x, y 坐标, 我们的路径动画在播放的时候, 就不断的更新里面的坐标, 另外, SurfaceView 那边也不断的调用这些 Drawable 的 draw 方法, 把他们画出来, 这样小猪就可以边移动, 边播放奔跑的动画了, 哈哈.
小猪找出路的话, 我们先看看这个:
![](https://www.writebug.com/myres/static/uploads/2021/10/25/19eecbd541d5161a0621d8adcbae73ce.writebug)
哈哈哈, 这样思路是不是清晰了好多.
其实我们的 SurfaceView 里面有一个 Rect 二维数组, 用来存放这些矩形, 小猪离开手指之后, 就开始从小猪当前所在的矩形,
用广度优先遍历, 找到一条最短的路径(比如: [5,5 5,4 5,3 5,2 5,1 5,0]这样的), 然后再根据这条路径在 Rect 数组中找到对应的矩形, 最后根据这些对应的矩形的坐标来确定出 Path.
哈哈, 有了 Path 小猪就可以跑了.
下面我们先来看看那个自定义的 Drawable 怎么写 (下面的那个 ThreadPool 类就是我们自己封装的一个单例的线程池):
```
/**
* 自定义的Drawable,类似于AnimationDrawable
*/
public class MyDrawable extends Drawable implements Cloneable {
private final int mDelay;//帧延时
private final byte[] mLock;//控制线程暂停的锁
private Semaphore mSemaphore;//来用控制线程更新问题
private Bitmap[] mBitmaps;//帧
private Paint mPaint;
private int mCurrentIndex;//当前帧索引
private float x, y;//当前坐标
private Future mTask;//帧动画播放的任务
private volatile boolean isPaused;//已暂停
public MyDrawable(int delay, Bitmap... bitmaps) {
mSemaphore = new Semaphore(1);
mBitmaps = bitmaps;
mDelay = delay;
mPaint = new Paint();
mPaint.setAntiAlias(true);
mLock = new byte[0];
}
public void start() {
stop();
mTask = ThreadPool.getInstance().execute(() -> {
while (true) {
synchronized (mLock) {
while (isPaused) {
try {
mLock.wait();
} catch (InterruptedException e) {
return;
}
}
}
try {
Thread.sleep(mDelay);
} catch (InterruptedException e) {
return;
}
try {
mSemaphore.acquire();
} catch (InterruptedException e) {
return;
}
mCurrentIndex++;
if (mCurrentIndex == mBitmaps.length) {
mCurrentIndex = 0;
}
mSemaphore.release();
}
});
}
void pause() {
isPaused = true;
}
void resume() {
isPaused = false;
synchronized (mLock) {
mLock.notifyAll();
}
}
private void stop() {
if (mTask != null) {
mTask.cancel(true);
mTask = null;
mCurrentIndex = 0;
}
}
@Override
public void draw(@NonNull Canvas canvas) {
try {
mSemaphore.acquire();
} catch (InterruptedException e) {
return;
}
canvas.drawBitmap(mBitmaps[mCurrentIndex], x, y, mPaint);
mSemaphore.release();
}
public void release() {
stop();
if (mBitmaps != null) {
for (Bitmap bitmap : mBitmaps) {
if (bitmap != null && !bitmap.isRecycled()) {
bitmap.recycle();
}
}
}
mBitmaps = null;
mPaint = null;
mTask = null;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
public Bitmap getBitmap() {
Bitmap result = null;
if (mBitmaps != null && mBitmaps.length > 0) {
result = mBitmaps[0];
}
return result;
}
@Override
public int getIntrinsicWidth() {
if (mBitmaps.length == 0) {
return 0;
}
return mBitmaps[0].getWidth();
}
@Override
public int getIntrinsicHeight() {
if (mBitmaps.length == 0) {
return 0;
}
return mBitmaps[0].getHeight();
}
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@SuppressWarnings("MethodDoesntCallSuperMethod")
public MyDrawable clone() {
return new MyDrawable(0, mBitmaps[0]);
}
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
```
start 方法大概就是开启一个子线程, 每次指定延时过后就更新 currentIndex, currentIndex 超出范围就置 0, 这样就可以一直循环播放下去了, 哈哈.
mSemaphore 是当执行 draw 的时候, 用来锁定 currentIndex 不让更新的.
好了, 现在有了 Drawable, 我们再来看看 Path 是怎么播放的:
我们可以先获取到 Path 上面的点, 有了这些点接下来就非常简单了.
获取 Path 上面的点坐标的方法大家应该也很熟悉了吧, 代码就不贴出来了,5.0 及以上系统用 Path 的 approximate 方法, 5.0 系统以下的用 PathMeasure 类. 具体代码在 SDK 里面也可以找到.
播放 Path 的话, 我们可以自定义一个 PathAnimation:
其实我们自定义的这个 PathAnimation 播放 Path 的逻辑也非常简单:当 start 方法执行的时候,记录一下开始时间,然后一个 while 循环,条件就是: 当前时间 - 开始时间 < 动画时长, 然后根据当前动画已经播放的时长和总动画时长计算出当前动画的播放进度, 然后我们就可以用这个 progress 来获取 Path 上对应的点,看看完整的代码:
```
public class PathAnimation {
private Keyframes mPathKeyframes;//关键帧
private long mAnimationDuration;//动画时长
private OnAnimationUpdateListener mOnAnimationUpdateListener;//动画更新监听
private AnimationListener mAnimationListener;//动画事件监听
private volatile boolean isAnimationRepeat, //反复播放的动画
isAnimationStopped,//已停止
isAnimationCanceled, //已取消 (
没有合适的资源?快使用搜索试试~ 我知道了~
基于 Android 使用 SurfaceView 开发《捉小猪》小游戏【100012842】
共335个文件
png:233个
xml:38个
java:38个
1.该资源内容由用户上传,如若侵权请联系客服进行举报
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
2.虚拟产品一经售出概不退款(资源遇到问题,请及时私信上传者)
版权申诉
0 下载量 82 浏览量
2023-06-26
16:16:34
上传
评论 1
收藏 16.75MB ZIP 举报
温馨提示
我们可以把每一个树桩, 小猪, 车厢都看成是一个 Drawable, 这个 Drawable 里面保存了 x, y 坐标, 我们的 SurfaceView 在 draw 的时候, 就把这些 Drawable draw 出来.
资源推荐
资源详情
资源评论
收起资源包目录
基于 Android 使用 SurfaceView 开发《捉小猪》小游戏【100012842】 (335个子文件)
70-16351324951302 1.7MB
gradlew.bat 2KB
70.gif 1.85MB
preview1.gif 1.85MB
preview5.gif 1.72MB
preview2.gif 1.7MB
preview3.gif 1.51MB
preview4.gif 564KB
.gitignore 754B
.gitignore 7B
build.gradle 2KB
build.gradle 711B
settings.gradle 15B
gradlew 5KB
gradle-wrapper.jar 52KB
PigstyMode.java 57KB
ClassicMode.java 39KB
LevelUtil.java 20KB
ComputeWayUtil.java 18KB
Pig.java 17KB
ClassicModeView.java 15KB
Application.java 12KB
MainActivity.java 10KB
RandomPiggies.java 10KB
LevelSelectView.java 8KB
PropOffsetHelper.java 7KB
Item.java 7KB
BaseActivity.java 6KB
PathAnimation.java 5KB
LevelSelect.java 5KB
MyValueAnimator.java 5KB
MyDrawable.java 4KB
LoadingView.java 3KB
PathKeyframes.java 3KB
HeartView.java 3KB
HomeView.java 3KB
AnimationButton.java 3KB
BitmapUtil.java 3KB
LogUtil.java 2KB
ShareUtil.java 2KB
PathKeyframesSupport.java 2KB
MyLayoutParams.java 1KB
Keyframes.java 1KB
ValueAnimatorUtil.java 1KB
ThreadPool.java 1KB
PropData.java 1KB
MyPath.java 934B
WayData.java 791B
MissionData.java 782B
ExampleInstrumentedTest.java 739B
WayData2.java 446B
ExampleUnitTest.java 397B
PositionData.java 332B
ic_guide_c3.jpg 124KB
ic_guide_c1.jpg 112KB
ic_guide_p3.jpg 110KB
ic_guide_c2.jpg 110KB
ic_guide_p2.jpg 77KB
LICENSE 34KB
README.md 23KB
background_music.ogg 371KB
ic_game_background.png 320KB
ic_game_background.png 190KB
ic_logo.png 161KB
ic_game_background.png 139KB
ic_game_background.png 110KB
ic_logo.png 99KB
ic_logo.png 71KB
ic_car_head_3.png 60KB
ic_logo.png 58KB
ic_car_head_2.png 58KB
ic_car_head_1.png 56KB
ic_car_head_0.png 56KB
ic_launcher.png 54KB
ic_board.9.png 49KB
ic_drop_right_2.png 44KB
ic_drop_left_2.png 44KB
ic_car_head_3.png 44KB
ic_drop_right_1.png 43KB
ic_drop_left_1.png 43KB
ic_guide_p1.png 43KB
ic_drop_left_0.png 43KB
ic_drop_right_0.png 42KB
ic_car_head_2.png 42KB
ic_car_head_1.png 41KB
ic_car_head_0.png 39KB
ic_occupied_right_0.png 39KB
ic_occupied_left_0.png 39KB
ic_launcher.png 38KB
ic_occupied_left_6.png 36KB
ic_occupied_right_6.png 36KB
ic_occupied_right_8.png 36KB
ic_occupied_left_7.png 36KB
ic_occupied_right_5.png 36KB
ic_occupied_left_8.png 36KB
ic_occupied_right_7.png 35KB
ic_occupied_right_1.png 35KB
ic_occupied_left_5.png 35KB
ic_occupied_right_2.png 35KB
ic_occupied_left_1.png 35KB
共 335 条
- 1
- 2
- 3
- 4
资源评论
神仙别闹
- 粉丝: 2668
- 资源: 7640
下载权益
C知道特权
VIP文章
课程特权
开通VIP
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功