2021SC@SDUSC
Catalog
Override onTouchEvent() method
1. Unit conversion: TypedValue class
2. Canvas save() and restore()
3. Constructor for BitmapShader
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.