Mobile Internet Development Teaching Case Study-2

Posted by r_honey on Sat, 16 Oct 2021 18:06:15 +0200

2021SC@SDUSC

Catalog

Case - Flappybird

1. Analysis of Code Ideas

Gamebird class

Override onTouchEvent() method

logic() method

initPos() method

checkGameOver() method

2. Detail Analysis

1. Unit conversion: TypedValue class

2. Canvas save() and restore()

3. Constructor for BitmapShader

4.Android Log class

Case - Flappybird

1. Analysis of Code Ideas

Gamebird class

(1) First you need to draw the background, birds, pipes, floors. What you need to do here is to use the related methods in the Bird, Floor, Pipe classes. The specific steps were analyzed in the previous blog.

(2) Setting and recording of game state variables: WAITING,RUNNING,OVER;

//Game Status Enumeration Variables
    private enum GameStatus
    {
        WAITING, RUNNING, OVER
    }
    //Record the status of the game*/
    private GameStatus gameStatus = GameStatus.WAITING;

(3) Sound

//voice
    private AudioRecordDemo audioRecordDemo=new AudioRecordDemo();

(4) During the game, the bird's touch rises and falls automatically, and the pipe moves and removes.

Related variables:

//The distance at which the touch rises is negative because it rises
    private static final int TOUCH_UP_SIZE = -16;
    //Convert the rising distance to px; here you store one more variable, which is calculated in run
    private final int mBirdUPDirs = Util.dp2px(getContext(), TOUCH_UP_SIZE);
    //Variables for calculation
    private int mTmpBirdDis;
    // Distance at which birds fall automatically
    private final int mAutoDownSpeed = Util.dp2px(getContext(), 2);
    // Record pipelines that need to be removed
    private List<Pipe> mNeedRemovePipe=new ArrayList<Pipe>();
    private int mRemovedPipe = 0;
    // Distance between two pipes
    private final int PIPE_DIS_BETWEEN_TWO=Util.dp2px(getContext(),100);
    //Distance of pipeline movement
    private int mTmpMoveDistance;

Override onTouchEvent() method

If the state of the game is WAITING at the touch screen, it means that the user has not officially started the game, which is equivalent to being in the preparation phase. At this time, the pipeline is not moving. After the touch screen, it changes to RUNNING state, the pipeline, background, floor start moving, and sound loading.

If RUNNING is in the state of the touch screen, it will raise the bird's height, which is stated in the above variable.

 @Override
    public boolean onTouchEvent(MotionEvent event){
        int action=event.getAction();

        if(action==MotionEvent.ACTION_DOWN){
            switch (gameStatus){
                //Convert WAITTING to RUNNING when touching screen
                case WAITING:
                    //Sound begins to load
                    gameStatus= GameStatus.RUNNING;
                    audioRecordDemo=new AudioRecordDemo();
                    audioRecordDemo.getNoiseLevel();
                    break;
                //If RUNNING raises the height of the bird when touching the screen
                case RUNNING:
                    mTmpBirdDis=mBirdUPDirs;
                    break;
            }
        }
        return true;
    }

logic() method

Is used for some logical calculations

1) In the RUNNING state:

1>Pipes need to be removed when they are removed from the screen by adding them to the List where the pipes are removed, while letting the number of pipes removed + 1;

2>When the distance between pipelines >=the distance between pipelines, a new pipeline is generated (instantiate the new pipeline and add it to the pipeline List);

3>Height handling of the bird and check the time to end the game.

2) In the OVER state:

1>If a bird is in the air, let it fall, plus the distance it falls automatically;

2>Otherwise change the game status to WAITING.

    private void logic(){
        switch (gameStatus){
            case RUNNING:

                for(Pipe pipe:mPipes){
                    //Remove the pipe when it is removed from the screen
                    if(pipe.getX()<-mPipeWidth){
                        mNeedRemovePipe.add(pipe);
                        mRemovedPipe++;
                        continue;
                    }
                    //Pipeline movement
                    pipe.setX(pipe.getX()-mSpeed);
                }
                //Pipes that can be deleted out of existing pipes
                mPipes.removeAll(mNeedRemovePipe);
                Log.e("TAG","Number of existing pipes"+mPipes.size());

                //Distance of pipeline movement
                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;
                }
                //Handle bird height here
                int volume=audioRecordDemo.getMvolume();
//                mTmpBirdDis+=mAutoDownSpeed;
                bird.setY(bird.getY()-(volume-40)+mTmpBirdDis);
                Log.e("TAG",volume+"");
                //Default drop, instant rise on Click
//                mTmpBirdDis+=mAutoDownSpeed;
//                bird.setY(bird.getY()+mTmpBirdDis);
                checkGameOver();
                break;
            case OVER:
                //Birds fall
                //If the bird is in the air, let it fall
                if (bird.getY() < mHeight)
                {
                    mTmpBirdDis += mAutoDownSpeed;
                    bird.setY(bird.getY() + mTmpBirdDis);
                } else
                {
                    gameStatus = GameStatus.WAITING;
                    initPos();
                }
                break;
            default:
                break;
        }
    }

(5) Data for resetting the location of birds, etc.

initPos() method

  private void initPos()
    {
        mPipes.clear();
        mNeedRemovePipe.clear();
        //Reset the position of the bird
        bird.setY(mHeight * 1 / 3);
        //Reset Drop Speed
        mTmpBirdDis = 0;
        mTmpMoveDistance = 0 ;
        mRemovedPipe=0;
        audioRecordDemo=null;

    }

(6) Used to check if the game is over:

checkGameOver() method

If a bird touches the floor or hits a pipe, the game ends; if it passes smoothly, the game continues.

Touch the floor: bird.getY()>mHeight;

Crashed into a pipe: wall.touchBird(bird), using the touchBird() method in the Pipe class;

Successfully passed: wall.getX()+mPipeWidth < bird.getX();

 private void checkGameOver()
    {

        // If touching the floor, gg
        if (bird.getY() > mHeight)
        {
            gameStatus = GameStatus.OVER;
        }
        // If you hit a pipe
        for (Pipe wall : mPipes)
        {
            //Passed through
            if (wall.getX() + mPipeWidth < bird.getX())
            {
                continue;
            }
            if (wall.touchBird(bird))
            {
                gameStatus = GameStatus.OVER;
                break;
            }
        }
    }

(7) Override the surfaceCreated(SurfaceHolder holder) method to open a thread;

Override the surfaceDestoryed(SurfaceHolder holder) method to notify the thread to close;

The run() method controls the time of the thread sleep

(8) draw() method calls drawBg(),drawBird(),drawPipes() method to draw background, bird, pipe;

Override the onSizeChanged(int w,int h,int oldw,int oldh) method to change size.

@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);
        // Initialize mBird
        bird = new Bird(getContext(), mWidth, mHeight, mBird_bitmap);
        // Initialization speed
        mSpeed = Util.dp2px(getContext(), 2);
        // Initialize Pipeline Range
        mPipeRect = new RectF(0, 0, mPipeWidth, mHeight);
//        Pipe pipe = new Pipe(getContext(), w, h, mPipeTop, mPipeBottom);
//        mPipes.add(pipe);
    }

Pipeline and bird demonstrations:

2. Detail Analysis

1. Unit conversion: TypedValue class

TypedValue: This class is a tool class that acts as a dynamic container for some data values, mainly values in the resource. The TypedValue class converts the corresponding values into point values on the actual screen, that is, pixel values.

The specific usage is as follows:

TypedValue.applyDimension(int unit, float value, DisplayMetrics metrics)

Among them, the first parameter is the unit you want, the second parameter is the value of the unit you want, and the third parameter encapsulates the various attribute values of the display area.

For example, if I want to get a 25sp, I'll use

TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 25,context.getResources().getDisplayMetrics())

That is, I want a 25 (second parameter) SP (first parameter) corresponding to the px value on the current device.

Be careful:

COMPLEX_UNIT_DIP: Multiply the display density(dpi/160).

TypedValue.COMPLEX_UNIT_SP: Multiply pixel density scaledDensity(=density).

No matter which way we use to achieve unit conversion, we are all adapting for the screen.

2. Canvas save() and restore()

 mCanvas.save();

Both save() and restore() are used to save and restore the Canvas state without parameters. The state of the Canvas is a snapshot of all the styles and variations applied to the current picture.

The Canvas state is stored on the stack, and whenever the save() method is called, the current state is pushed to the stack to be saved. A painting state includes:

1. Deformations currently applied (i.e., movement, rotation, and scaling)

2. Values of strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY shadowBlur, shadowColor, globalComposite Operations

3. Current clipping path

You can call the save method any number of times.

Each time the restore method is called, the last saved state pops up from the stack and all settings are restored.

3. Constructor for BitmapShader

public BitmapShader(@NonNull Bitmap bitmap, TileMode tileX, TileMode tileY)

Three parameters: bitmap refers to the picture to be textured, tileX refers to the drawing mode of texture in the X direction, and tileY refers to the drawing mode in the Y direction.

TileMode is an enumeration type with three possible values:

1. CLMP expands the color of the bitmap boundary if the size of the content to be filled exceeds the bitmap size.

2. REPEAT repeats and repeats bitmap s to fill. Texture pictures will repeat in this area if the drawing area is larger than texture pictures.

3. Decomplete the MIRROR image. If the area drawn is larger than the texture image, the texture image will repeat as a mirror.

4.Android Log class

There are five common methods for android.util.Log: Log.v() Log.d() Log.i() Log.w() and Log.e(). They correspond to VERBOSE, DEBUG,INFO, WARN, ERROR according to the initial letter.

1. Log.v's debug color is black, any message will be output, where V stands for verbose's verbose meaning, commonly used as Log.v(""");

2. The output color of Log.d is blue, only the meaning of debug debugging is output, but he will output the upper information, which can be filtered and selected through the Logcat tag of DDMS.

3. Log.i's output is green, general informative message information. It does not output the information of Log.v and Log.d, but displays the information of i, w and e.

4. Log.w means orange. It can be seen as a warning warning. We need to pay attention to optimizing the Android code and output the information of Log.e when it is selected.

5. Log.e is red, you can think of error errors. Only red error information is shown here. These errors need to be carefully analyzed to see the stack information.

Note: Different printing methods are used with a String tag (String msg) parameter, tag represents the label of the printed information, MSG represents the information that needs to be printed.

Topics: Android Android Studio