Android SurfaceView实战 带你玩转flabby bird

import android.graphics.RectF;

public class Bird

{

/**

  • 鸟在屏幕高度的2/3位置

*/

private static final float RADIO_POS_HEIGHT = 2 / 3F;

/**

  • 鸟的宽度 30dp

*/

private static final int BIRD_SIZE = 30;

/**

  • 鸟的横坐标

*/

private int x;

/**

  • 鸟的纵坐标

*/

private int y;

/**

  • 鸟的宽度

*/

private int mWidth;

/**

  • 鸟的高度

*/

private int mHeight;

/**

  • 鸟的bitmap

*/

private Bitmap bitmap;

/**

  • 鸟绘制的范围

*/

private RectF rect = new RectF();

public Bird(Context context, int gameWith, int gameHeight, Bitmap bitmap)

{

this.bitmap = bitmap;

//鸟的位置

x = gameWith / 2 - bitmap.getWidth() / 2;

y = (int) (gameHeight * RADIO_POS_HEIGHT);

// 计算鸟的宽度和高度

mWidth = Util.dp2px(context, BIRD_SIZE);

mHeight = (int) (mWidth * 1.0f / bitmap.getWidth() * bitmap.getHeight());

}

/**

  • 绘制自己

  • @param canvas

*/

public void draw(Canvas canvas)

{

rect.set(x, y, x + mWidth, y + mHeight);

canvas.drawBitmap(bitmap, null, rect, null);

}

public int getY()

{

return y;

}

public void setY(int y)

{

this.y = y;

}

public int getWidth()

{

return mWidth;

}

public int getHeight()

{

return mHeight;

}

}

定义了一个类,代表我们的鸟,以及一堆成员变量,并且提供一个draw方法对外;

在GameFlabbyBird中,只需要,初始化我们的Bird,在draw里面调用bird.draw即可;

部分筛检后代码:

public class CopyOfGameFlabbyBird extends SurfaceView implements Callback,

Runnable

{

/**

  • 鸟相关*************

*/

private Bird mBird;

private Bitmap mBirdBitmap;

/**

  • 初始化图片

*/

private void initBitmaps()

{

mBg = loadImageByResId(R.drawable.bg1);

mBirdBitmap = loadImageByResId(R.drawable.b1);

}

private void draw()

{

// drawSomething…

drawBg();

drawBird();

}

private void drawBird()

{

mBird.draw(mCanvas);

}

/**

  • 初始化尺寸相关

*/

@Override

protected void onSizeChanged(int w, int h, int oldw, int oldh)

{

// 初始化mBird

mBird = new Bird(getContext(), mWidth, mHeight, mBirdBitmap);

}

}

是不是很简单,下面看下此时效果图:

Activity里面这么调用即可:

package com.zhy.surfaceViewDemo;

import com.zhy.view.GameFlabbyBird;

import android.app.Activity;

import android.os.Bundle;

import android.view.Window;

import android.view.WindowManager;

public class MainActivity extends Activity

{

GameFlabbyBird mGame;

@Override

protected void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,

WindowManager.LayoutParams.FLAG_FULLSCREEN);

requestWindowFeature(Window.FEATURE_NO_TITLE);

mGame = new GameFlabbyBird(this);

setContentView(mGame);

}

}

效果图:

不管咋样,我们的鸟已经在指定的位置了~有木有一点小激动

下面开始添加地板;

3、绘制地板


绘制地板相比来说会难一点,因为我们需要考虑怎么让地板运动,起初我截取了两个大图,希望通过两张图不断变化,产生动画效果,but,动画的太卡,有跳跃感;

于是,我忽然想到了一个东西可以做,我就把基础图变成了这样:

很小的一块图,先不考虑运动,如何填充成我们目标效果呢?

还记得有个类叫做BitmapShader么?我们可以利用它进行填充。

相关知识可以参考:Android BitmapShader 实战 实现圆形、圆角图片

首先我们依旧是定义一个地板类:Floor

package com.zhy.view;

import java.util.concurrent.TimeUnit;

import com.zhy.surfaceViewDemo.Config;

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.BitmapShader;

import android.graphics.Canvas;

import android.graphics.Paint;

import android.graphics.Paint.Style;

import android.graphics.Shader.TileMode;

public class Floor

{

/*

  • 地板位置游戏面板高度的4/5到底部

*/

private static final float FLOOR_Y_POS_RADIO = 4 / 5F; // height of 4/5

/**

  • x坐标

*/

private int x;

/**

  • y坐标

*/

private int y;

/**

  • 填充物

*/

private BitmapShader mFloorShader;

private int mGameWidth;

private int mGameHeight;

public Floor(int gameWidth, int gameHeight, Bitmap floorBg)

{

mGameWidth = gameWidth;

mGameHeight = gameHeight;

y = (int) (gameHeight * FLOOR_Y_POS_RADIO);

mFloorShader = new BitmapShader(floorBg, TileMode.REPEAT,

TileMode.CLAMP);

}

/**

  • 绘制自己

  • @param mCanvas

  • @param mPaint

*/

public void draw(Canvas mCanvas, Paint mPaint)

{

if (-x > mGameWidth)

{

x = x % mGameWidth;

}

mCanvas.save(Canvas.MATRIX_SAVE_FLAG);

//移动到指定的位置

mCanvas.translate(x, y);

mPaint.setShader(mFloorShader);

mCanvas.drawRect(x, 0, -x + mGameWidth, mGameHeight - y, mPaint);

mCanvas.restore();

mPaint.setShader(null);

}

public int getX()

{

return x;

}

public void setX(int x)

{

this.x = x;

}

}

定义了一堆成员变量,核心就在于,我们传入地板背景的填充物,然后初始化我们的mFloorShader,横向重复,纵向拉伸(这里的拉伸是指,纵向的最后一个像素不断重复)。

我们对外公布了draw方法,传入Canvas,我们首先调用canvas.save(),然后将canvas移动到指定的位置,然后绘制我们的矩形,矩形的填充就是我们的地板了~~;

这里,注意一下,我们这里使用了一个变量x,而不是0;为什么呢?因为我们的地板需要利用这个x运动。

那么现在我们如何才能动呢?

首先我们在GameFlabbyBird定义一个变量,表示移动速度mSpeed,然后在draw中不断更新mFloor的x坐标为:mFloor.setX(mFloor.getX() - mSpeed);

这样的画,每次绘制我们floor的起点,会向左移动mSpeed个位置,就形成了运行的效果;但是呢?不能一直减下去,不然最终我们的x岂不是负无穷了,那得绘制多大?

所以我们:

if (-x > mGameWidth)

{

x = x % mGameWidth;

}

如果x的正值大于宽度了,我们取余一下~~~

最终我们的绘制范围是:

mCanvas.drawRect(x, 0, -x + mGameWidth, mGameHeight - y, mPaint);

ok,贴下筛检后GameFlabbyBird代码:

package com.zhy.view;

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.graphics.Canvas;

import android.graphics.Paint;

import android.graphics.PixelFormat;

import android.graphics.RectF;

import android.util.AttributeSet;

import android.util.Log;

import android.view.SurfaceHolder;

import android.view.SurfaceHolder.Callback;

import android.view.SurfaceView;

import com.zhy.surfaceViewDemo.R;

public class CopyOfGameFlabbyBird extends SurfaceView implements Callback,

Runnable

{

private Paint mPaint;

/**

  • 地板

*/

private Floor mFloor;

private Bitmap mFloorBg;

private int mSpeed;

public CopyOfGameFlabbyBird(Context context, AttributeSet attrs)

{

super(context, attrs);

mPaint = new Paint();

mPaint.setAntiAlias(true);

mPaint.setDither(true);

initBitmaps();

// 初始化速度

mSpeed = Util.dp2px(getContext(), 2);

}

/**

  • 初始化图片

*/

private void initBitmaps()

{

mFloorBg = loadImageByResId(R.drawable.floor_bg2);

}

private void draw()

{

// drawSomething…

drawBg();

drawBird();

drawFloor();

// 更新我们地板绘制的x坐标

mFloor.setX(mFloor.getX() - mSpeed);

}

private void drawFloor()

{

mFloor.draw(mCanvas, mPaint);

}

@Override

protected void onSizeChanged(int w, int h, int oldw, int oldh)

{

// 初始化地板

mFloor = new Floor(mWidth, mHeight, mFloorBg);

}

}

其实很简单,就是声明几个变量,初始化一下;记得在draw中更新mFloor的x即可。

现在的效果:

好了,最后剩下个管道了~~~

4、绘制管道


自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

【延伸Android必备知识点】

【Android部分高级架构视频学习资源】

**Android精讲视频学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水!

**任何市场都是优胜略汰适者生存,只要你技术过硬,到哪里都不存在饱和不饱和的问题,所以重要的还是提升自己。懂得多是自己的加分项 而不是必须项。门槛高了只能证明这个市场在不断成熟化!**另外一千个读者就有一千个哈姆雷特,所以以上只是自己的关键,不喜勿喷!

如果你是卡在缺少学习资源的瓶颈上,那么刚刚好我能帮到你。欢迎关注会持续更新和分享的。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

[外链图片转存中…(img-DrO5c8aD-1712392303989)]

【Android部分高级架构视频学习资源】

**Android精讲视频学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水!

**任何市场都是优胜略汰适者生存,只要你技术过硬,到哪里都不存在饱和不饱和的问题,所以重要的还是提升自己。懂得多是自己的加分项 而不是必须项。门槛高了只能证明这个市场在不断成熟化!**另外一千个读者就有一千个哈姆雷特,所以以上只是自己的关键,不喜勿喷!

如果你是卡在缺少学习资源的瓶颈上,那么刚刚好我能帮到你。欢迎关注会持续更新和分享的。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!

AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算

  • 51
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值