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移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
【延伸Android必备知识点】
【Android部分高级架构视频学习资源】
**Android精讲视频学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶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中高级、架构师对你更是如鱼得水!
**任何市场都是优胜略汰适者生存,只要你技术过硬,到哪里都不存在饱和不饱和的问题,所以重要的还是提升自己。懂得多是自己的加分项 而不是必须项。门槛高了只能证明这个市场在不断成熟化!**另外一千个读者就有一千个哈姆雷特,所以以上只是自己的关键,不喜勿喷!
如果你是卡在缺少学习资源的瓶颈上,那么刚刚好我能帮到你。欢迎关注会持续更新和分享的。
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算