2021SC@SDUSC
目录
案例——Flappybird
一、代码思路分析
Gamebird类
(1)首先需要绘制背景、鸟、管道、地板。这里需要做的就是用Bird、Floor、Pipe类中的相关方法实现。具体步骤在上一个博客中分析过。
(2)游戏状态变量的设置和记录:WAITING,RUNNING,OVER;
//游戏状态枚举变量
private enum GameStatus
{
WAITING, RUNNING, OVER
}
//记录游戏的状态*/
private GameStatus gameStatus = GameStatus.WAITING;
(3)声音
//声音
private AudioRecordDemo audioRecordDemo=new AudioRecordDemo();
(4)在游戏过程中,鸟的触摸上升、自动下落 ,管道的移动、移除。
相关变量:
//触摸上升的距离,因为是上升,所以为负值
private static final int TOUCH_UP_SIZE = -16;
//将上升的距离转化为px;这里多存储一个变量,变量在run中计算
private final int mBirdUPDirs = Util.dp2px(getContext(), TOUCH_UP_SIZE);
//用于计算的变量
private int mTmpBirdDis;
// 鸟自动下落的距离
private final int mAutoDownSpeed = Util.dp2px(getContext(), 2);
// 记录需要移除的管道
private List<Pipe> mNeedRemovePipe=new ArrayList<Pipe>();
private int mRemovedPipe = 0;
// 两个管道间的距离
private final int PIPE_DIS_BETWEEN_TWO=Util.dp2px(getContext(),100);
//管道移动的距离
private int mTmpMoveDistance;
重写onTouchEvent()方法
在触屏时如果游戏状态是WAITING的话,说明用户还没有正式开始游戏,相当于处于准备阶段,这个时候管道是不移动的,触屏后改为RUNNING状态,管道、背景、地板开始移动,声音加载。
如果触屏时的状态时RUNNING,则会让鸟的高度升高,升高的值在上面变量中声明。
@Override
public boolean onTouchEvent(MotionEvent event){
int action=event.getAction();
if(action==MotionEvent.ACTION_DOWN){
switch (gameStatus){
//触屏时如果是WAITTING转为RUNNING
case WAITING:
//声音开始加载
gameStatus= GameStatus.RUNNING;
audioRecordDemo=new AudioRecordDemo();
audioRecordDemo.getNoiseLevel();
break;
//触屏时如果是RUNNING让鸟的高度升高
case RUNNING:
mTmpBirdDis=mBirdUPDirs;
break;
}
}
return true;
}
logic()方法
是用于一些逻辑上的计算
1)在RUNNING状态下:
1>管道在移出屏幕时需要被移除,操作是将该管道加入到移出管道的List中, 同时让移除的管道数+1;
2>当管道移动距离>=管道间距离的时候,生成一个新管道(实例化新管道,并将其加入到管道List中);
3>鸟的高度处理,并检查时候结束游戏。
2)在OVER状态下:
1>如果鸟在空中,就让它掉下来,加上它自动下落的距离;
2>否则更改游戏状态为WAITING。
private void logic(){
switch (gameStatus){
case RUNNING:
for(Pipe pipe:mPipes){
//当管道移出屏幕时,移除管道
if(pipe.getX()<-mPipeWidth){
mNeedRemovePipe.add(pipe);
mRemovedPipe++;
continue;
}
//管道移动
pipe.setX(pipe.getX()-mSpeed);
}
//现存管道中出去可以删除的管道
mPipes.removeAll(mNeedRemovePipe);
Log.e("TAG","现存管道数量"+mPipes.size());
//管道移动的距离
mTmpMoveDistance+=mSpeed;
//生成一个管道
if(mTmpMoveDistance>=PIPE_DIS_BETWEEN_TWO){
Pipe pipe=new Pipe(getContext(),getWidth(),getHeight(),
mPipeTop,mPipeBottom);
mPipes.add(pipe);
mTmpMoveDistance=0;
}
//在这里处理鸟的高度
int volume=audioRecordDemo.getMvolume();
// mTmpBirdDis+=mAutoDownSpeed;
bird.setY(bird.getY()-(volume-40)+mTmpBirdDis);
Log.e("TAG",volume+"");
//默认下落,点击时瞬间上升
// mTmpBirdDis+=mAutoDownSpeed;
// bird.setY(bird.getY()+mTmpBirdDis);
checkGameOver();
break;
case OVER:
//鸟落下
//如果鸟在空中,让他掉下来
if (bird.getY() < mHeight)
{
mTmpBirdDis += mAutoDownSpeed;
bird.setY(bird.getY() + mTmpBirdDis);
} else
{
gameStatus = GameStatus.WAITING;
initPos();
}
break;
default:
break;
}
}
(5)用于重置鸟的位置等数据:
initPos()方法
private void initPos()
{
mPipes.clear();
mNeedRemovePipe.clear();
//重置鸟的位置
bird.setY(mHeight * 1 / 3);
//重置下落速度
mTmpBirdDis = 0;
mTmpMoveDistance = 0 ;
mRemovedPipe=0;
audioRecordDemo=null;
}
(6)用于检验游戏是否结束:
checkGameOver()方法
如果鸟触碰到地板,或者撞到管道,游戏均结束;顺利穿过则游戏继续。
碰到地板:bird.getY()>mHeight;
撞到管道:wall.touchBird(bird),用了Pipe类中的touchBird()方法;
顺利通过:wall.getX()+mPipeWidth < bird.getX();
private void checkGameOver()
{
// 如果触碰地板,gg
if (bird.getY() > mHeight)
{
gameStatus = GameStatus.OVER;
}
// 如果撞到管道
for (Pipe wall : mPipes)
{
//已经穿过的
if (wall.getX() + mPipeWidth < bird.getX())
{
continue;
}
if (wall.touchBird(bird))
{
gameStatus = GameStatus.OVER;
break;
}
}
}
(7)重写surfaceCreated(SurfaceHolder holder)方法,开启线程;
重写 surfaceDestoryed(SurfaceHolder holder)方法通知关闭线程;
run()方法控制线程sleep的时间
(8) draw()方法调用drawBg(),drawBird(),drawPipes()方法来绘制背景、鸟、管道;
重写onSizeChanged(int w,int h,int oldw,int oldh)方法用于改变尺寸大小。
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh){
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
mGamePanelRect.set(0, 0, w, h);
// 初始化mBird
bird = new Bird(getContext(), mWidth, mHeight, mBird_bitmap);
// 初始化速度
mSpeed = Util.dp2px(getContext(), 2);
// 初始化管道范围
mPipeRect = new RectF(0, 0, mPipeWidth, mHeight);
// Pipe pipe = new Pipe(getContext(), w, h, mPipeTop, mPipeBottom);
// mPipes.add(pipe);
}
管道和鸟的运行演示: