Android SurfaceView practices: flabby bird (lower) and surfaceviewflabby

Source: Internet
Author: User
Tags gety

Android SurfaceView practices: flabby bird (lower) and surfaceviewflabby

Reprinted please indicate the source: bytes]

1. Overview

In the Android SurfaceView practice, we have drawn all the elements required by the game in flabby bird (on, includes Bird, Floor, Pipe, background image, and score.

This blog will basically continue to lead everyone towards our goal in the previous article. The question is, our goal is:


That's it.

First, let's clarify, of course, the gap between our status and:

1. There is only one MPs queue, which needs to be dynamically generated and removed;

2. Our birds are still at rest. Now we need to drop by default. Press the screen to rise for a distance;

3. Determine the connection between the bird and the pipeline and the ground while flying, and determine whether the bird is Gameover;

4. Now, when a bird passes through a pipe, we can score + 1;


OK. The target is defined ~~~

So first, based on the above, we find that what is missing in our game?

Well, it's status. We need to know when the game is running, when it is ready to run, and when it is GameOver. Therefore, an enumeration variable is introduced:

/*** Game status ** @ author zhy **/private enum GameStatus {WAITING, RUNNING, OVER}

1. By default, it is in the WAITING status, and the screen is static. The above is a static bird ~~

2. When you touch the screen: The game starts to interact with each other based on the user's touch;

3. When the bird touches the pipe or falls to the ground, it enters the GAMEOVER state. If it is OVER, it immediately switches to the WAITING state after the bird falls to the ground.

Okay. In this way, we can complete the three statuses ~~ Is this like a game ~


With the status, we will consider how to deal with user interaction. Our interaction is mainly touch, so we can rewrite the onTouchEvent method of View.

In onTouchEvent, the changed variables are reflected in the rendering based on the user's touch game status and some variables. In this way, the interaction effect is formed.

What then? For the sake of code clarity, we originally had only one draw () method in the thread. Now we add a method: logic (); process the calculation of scores during the game and the generation and removal of pipelines.

In this way, the logic can be separated. logic focuses on some logical tasks, and draw only draws;

After talking about this, if you can digest it, it's okay. Now we are starting to enter the code stage ~~


2. dynamically generate and remove Pipelines

First, we add the mStatus variable private GameStatus mStatus = GameStatus. WAITING;

Then add the logic method and the onTouchEvent method.

Code after screening:

Public class CopyOfGameFlabbyBird extends SurfaceView implements Callback, Runnable {// some codes are omitted private enum GameStatus {WAITTING, RUNNING, STOP ;} /*** record the game status */private GameStatus mStatus = GameStatus. WAITTING;/*** the distance from which the touch rises. As it is rising, it is a negative value */private static final int TOUCH_UP_SIZE =-16; /*** converts the rising distance to px. One variable is stored here, and the variable is calculated in run **/private final int mBirdUpDis = Util. dp2px (getContext (), TOUCH_UP_SIZE); private int mTmpBirdDis;/*** distance from automatic bird fall */private final int mAutoDownSpeed = Util. dp2px (getContext (), 2);/*** process some logical calculations */private void logic () {switch (mStatus) {case RUNNING: // pipeline movement for (Pipe pipe: mPipes) {pipe. setX (pipe. getX ()-mSpeed);} // update the x coordinate of the floor drawn by us, and move the floor to mFloor. setX (mFloor. getX ()-mSpeed); break; case STOP: // bird drops break; default: break; }}@ Overridepublic boolean onTouchEvent (MotionEvent event) {int action = event. getAction (); if (action = MotionEvent. ACTION_DOWN) {switch (mStatus) {case WAITTING: mStatus = GameStatus. RUNNING; break; case RUNNING: mTmpBirdDis = mBirdUpDis; break ;}return true ;}@ Overridepublic void run () {while (isRunning) {// some code logic (); draw (); // some code is omitted }}}

We can see that the logic () method is added in the run. In onTouch, the user goes DOWN, the status is changed, or the mTmpBirdDis is set to the distance that the bird rises when each user clicks, next we will implement it.

Another point is that we extract the x coordinates of the update pipeline from the drawFloor, and extract the x coordinates of the update mFloor from the draw to the logic (). Currently, the draw only needs to be drawn, no matter what you do.

Now, after the game is started, the screen is still, and the user starts to move after the touch;

Of course, it is still a pipeline. Next, we will dynamically add pipelines:

MPs queue addition:

For MPs queue addition, I want to generate an MPS queue every DP;

When the pipeline moves out of the screen, we remove it from the List to avoid unnecessary painting;

How can this problem be solved?

/*** Distance between two pipelines */private final int PIPE_DIS_BETWEEN_TWO = Util. dp2px (getContext (), 300);/*** records the moving distance. When PIPE_DIS_BETWEEN_TWO is reached, a pipeline is generated */private int mTmpMoveDistance; /*** process some logical calculations */private void logic () {switch (mStatus) {case RUNNING: // MPs queue mTmpMoveDistance + = mSpeed; // generate a pipeline if (mTmpMoveDistance> = PIPE_DIS_BETWEEN_TWO) {Pipe pipe = new Pipe (getContext (), getWidth (), getHeight (), mPipeTop, mPipeBottom); mPipes. add (pipe); mTmpMoveDistance = 0;} break; case STOP: // bird drops break; default: break ;}}

It is easy to add two variables. In logic, if it is in the RUNNING state, record the moving distance to reach our preset value of 300dp and add one.

Now our results (remember to delete the pipeline we added in onSizeChanged, no need, we have generated it dynamically)


As you can see, the initial status is WAITTING. When we click, the floor starts to move, and the pipeline starts to dynamically add and move ~~~

Now, there is a problem: Our pipeline is now dynamically added. with the running of the game, our pipeline will certainly be infinite. Of course, I am a waste of over 10 points, this problem does not occur.

There are no limits. Even if it doesn't collapse, it's estimated that it will be stuck, so many pipelines won't be seen. Why do we plot it?

So how can we remove these pipelines that are not on the screen?

MPs queue removal:

It's easy to look at the Code:

/*** Record the MPs queue to be removed */private List <Pipe> mNeedRemovePipe = new ArrayList <Pipe> (); /*** handle some logical calculations */private void logic () {switch (mStatus) {case RUNNING: // update the x coordinates drawn by the floor and move the floor to mFloor. setX (mFloor. getX ()-mSpeed); // pipeline movement for (Pipe pipe: mPipes) {if (pipe. getX () <-mPipeWidth) {mNeedRemovePipe. add (pipe); continue;} pipe. setX (pipe. getX ()-mSpeed);} // remove the MPs queue mPipes. removeAll (mNeedRemovePipe); Log. e ("TAG", "Number of existing pipelines:" + mPipes. size (); // pipeline mTmpMoveDistance + = mSpeed; // generate a pipeline if (mTmpMoveDistance> = PIPE_DIS_BETWEEN_TWO) {Pipe pipe = new Pipe (getContext (), getWidth (), getHeight (), mPipeTop, mPipeBottom); mPipes. add (pipe); mTmpMoveDistance = 0;} break; case STOP: // bird drops break; default: break ;}}

In fact, several lines of code are added. For better understanding, more code is added. We have added the variable mNeedRemovePipe. When traversing Pipes, if the left side of x is smaller than-mPipeWidth, it indicates that the image cannot be seen, so the image is protected against mNeedRemovePipe;

Finally, mNeedRemovePipe is removed.

Some people may say, why do we need to create more mNeedRemovePipe? You can remove the for loop ~ Well, this will not work, and an error will be reported;

Some people say that I know it will report an error, but you can use a secure List like CopyOnWriteArrayList to remove it for a loop ~~ Well, this is acceptable, but in this type of List methods, for security purposes, various clones will inevitably cause slow running speeds ~~ We are a game here. We must avoid unnecessary speed loss ~~~

Okay ~~ Now there is no problem with pipelines ~~~

Next, our birds should be moved ~~


3. Let the birds fly

In fact, it is very simple. I am embarrassed to separate the two lines of code ~~

Private void logic () {switch (mStatus) {case RUNNING: // update the x coordinate of the floor drawn, and move the floor to mFloor. setX (mFloor. getX ()-mSpeed); logicPipe (); // default whereabouts. when clicked, mTmpBirdDis + = mAutoDownSpeed; mBird. setY (mBird. getY () + mTmpBirdDis); break; case STOP: // birds drop break; default: break ;}}

Just add two lines in logic ~~

MTmpBirdDis + = mAutoDownSpeed;
MBird. setY (mBird. getY () + mTmpBirdDis );

By default, the system drops faster and faster. When a user clicks, the distance increases instantly, and the user continues to drop down ~

I have extracted the relevant pipelines ~~

Current results:


Now, we can see that our birds can finally interact with each other ~~ The animation above is faster ~~ Sorry, it is said that the image can be smaller ~

Now we have implemented most of the functions, with only one GameOver and scoring left ~~~ Let's go!


4. GameOver or Grades ++

I think it's easy to judge the failure. When traversing the pipeline, I can determine whether the pipe and the bird are touched ~~ Or does the y coordinate of the bird touch the ground ~~

Call checkGameOver () in logic ()

Private void checkGameOver () {// if you touch the floor, ggif (mBird. getY ()> mFloor. getY ()-mBird. getHeight () {mStatus = GameStatus. STOP;} // if you hit the Pipe for (Pipe wall: mPipes) {// if (wall. getX () + mPipeWidth <mBird. getX () {continue;} if (wall. touchBird (mBird) {mStatus = GameStatus. STOP; break ;}}}

If gg is on the ground, gg is on the pipeline;

Public class Pipe {//... /*** determine whether to touch or not * @ param mBird * @ return */public boolean touchBird (Bird mBird) {/*** if the bird has touched the pipe */if (mBird. getX () + mBird. getWidth ()> x & (mBird. getY () We added touchBird to the pipeline for judgment ~~ It is very simple. If the bird is in the MPs queue range, it is true if it is not in the gap of the MPs queue ~~

Now, run the code and find that our bird will be OVER if it hits the pipe or lands ~~ However, after OVER, it will never change again ~~

What about visa? Because after OVER, our status is STOP, and we didn't do any processing ~~

We should judge in the STOP. If it is not implemented, let the bird land, and then switch to WAITTING ~

Private void logic () {switch (mStatus) {case RUNNING: case STOP: // bird falls // if the bird is still in the air, let it fall if (mBird. getY () <mFloor. getY ()-mBird. getWidth () {mTmpBirdDis + = mAutoDownSpeed; mBird. setY (mBird. getY () + mTmpBirdDis);} else {mStatus = GameStatus. WAITTING; initPos ();} break; default: break;}/*** reset the bird location and other data */private void initPos () {mPipes. clear (); mNeedRemovePipe. clear (); // reset the position of the bird mBird. setY (mHeight * 2/3); // reset the fall speed mTmpBirdDis = 0; mTmpMoveDistance = 0 ;}

If you STOP, let the bird continue to fall, switch to WAITING directly when it reaches the ground, and remember to reset some necessary data;

Finally, the score is calculated ~~~

Even ~~

Score calculation:

Fortunately, the score calculation is relatively simple ~~~

/*** Process some logical calculations */private void logic () {switch (mStatus) {case RUNNING: mGrade = 0; // update the x coordinates drawn on the floor, floor mobile mFloor. setX (mFloor. getX ()-mSpeed); logicPipe (); // default whereabouts. when clicked, mTmpBirdDis + = mAutoDownSpeed; mBird. setY (mBird. getY () + mTmpBirdDis); // calculate the score mGrade + = mRemovedPipe; for (Pipe pipe: mPipes) {if (pipe. getX () + mPipeWidth <mBird. getX () {mGrade ++ ;}} checkGameOver (); break; case STOP: // bird fall }}

We can see that several lines of code are added to calculate scores ~ Our score is set to 0 each time, and then the number of pipelines that are invisible (none can be seen, it must be passed) is added, and the number of pipelines on the screen on the left of the bird is added ~

This is your score ~~

So remember to use the mRemovedPipe

Private int mRemovedPipe = 0; private void logicPipe () {// pipeline movement for (Pipe pipe: mPipes) {if (pipe. getX () <-mPipeWidth) {mNeedRemovePipe. add (pipe); mRemovedPipe ++; continue;} pipe. setX (pipe. getX ()-mSpeed );}
In addition, after OVER, set mRemovedPipe to 0 in initPos. After restarting, The mRemovedPipe starts from 0 ~~~

Okay, now it's over ~~ As for Constants on the distance between pipelines, pipe width, and bird descent rate, if you feel uncomfortable, modify them by yourself ~~~

Please allow me to paste the final result again ~ :


While looking at the results, let's summarize:

In fact, the game is not difficult in general. Although I have written two articles so long, you can stay away and watch them. In fact, we still use our original SurfaceViewTemplate. There is nothing more than a method to rewrite onTouchEvent and logic, onTouchEvent must be written for interaction. In fact, there is no code ~ Calculation required for repainting using the logic method ~~ While draw is doing it with peace of mind ~~ That is to say, this game is actually no different from creating a bird and clicking up ~~~

Congratulations, our SurfaceView has covered all the knowledge points after these three blogs (and a turntable). Maybe someday, if you come up with a simple, abusive game, you will be rich ~~



Download source code



I have created a QQ Group for your convenience. Group Number:423372824

Bytes ----------------------------------------------------------------------------------------------------------

Some of the bloggers have already been online. If you do not like boring text, stamp it (for initial record, we look forward to your support ):

Video directory address: My recorded video tutorial










Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.