Android game development-physical ball application

Source: Internet
Author: User

Game Development is different from application development. Everything is customized, so there is nothing to say about controls or things. The following is a simple example. It is actually in books and is being learned recently, by the way.
First, we need to develop the object. Movable. java
Package com. jj. ball;

Import android. graphics. Bitmap;
Import android. graphics. Canvas;

/***
* Ball objects
*
* @ Author zhangjia
*
*/
Public class Moveable {
Int startX = 0; // initial X coordinate
Int startY = 0; // initial Y coordinate
Int x; // time X,
Int y; // time Y

Float startVX = 0f; // The Initial vertical speed.
Float startVY = 0f; // The initial horizontal speed.

Float v_x = 0f; // time vertical speed
Float v_y = 0f; // time horizontal speed

Int r; // radius

Double timeX; // horizontal motion time
Double timeY; // vertical motion time

Bitmap bitmap = null; // small ball

BallThread bt = null;

Boolean bFall = false; // whether the ball has fallen from the board

Float impactFactor = 0.25f; // The loss coefficient of the speed after the ball hits the ground.

/***
* Constructor
*
* @ Param x
* @ Param y
* @ Param r
* @ Param bitmap
*/
Public Moveable (int x, int y, int r, Bitmap bitmap ){
Super ();
This. x = x;
This. startX = x;
This. startY = y;
This. y = y;
This. r = r;
This. bitmap = bitmap;
TimeX = System. nanoTime ();
This. v_x = BallView. V_MIN
+ (Int) (BallView. V_MAX-BallView. V_MIN) * Math. random (); // initial horizontal speed
Bt = new BallThread (this); // create and start BallThread
Bt. start ();
}

/***
* Painting
*/
Public void drawSelf (Canvas canvas ){
Canvas. drawBitmap (bitmap, x, y, null );
}

}
This is nothing more than the attribute of some small balls, involving the physical engine of BallThread. Next we will introduce it.
First, you must understand:
Subtle, nanosecond, and second conversion
Microsecond, time unit, symbol μs
1,000,000 microseconds = 1 second
Nanosecond, time unit, symbol ns
1,000,000,000 nanoseconds = 1 second
In addition, you need to understand some physical formulas: for example, how to calculate the vertical distance, the moving distance in the horizontal direction, and the horizontal and vertical speed during the moving process, these are the physical knowledge of our senior one. Although we haven't touched it for N years, we can still recall it. I would like to express my feelings: There are a wide variety of education courses in China. How many of them do we get in reality? NND: Ah.
Another point is that when the ball hits the ground and rises to the highest position, it is an extreme position. If it moves again, it is a new movement. however, I think everyone understands that the game is indeed much better than the application. At least the logic thinking is not comparable to the application.
Package com. jj. ball;

/***
* Ball physical Engine
*
* @ Author zhangjia
*
*/
Public class BallThread extends Thread {
Private Moveable moveable; // ball object
Private boolean flag = false; // thread ID
Private int sleepSpan = 30; // sleep time
Private float g = 200; // acceleration of gravity
Private double currentTime; // record the current event

/***
* Constructor
*
* @ Param moveable
*/
Public BallThread (Moveable moveable ){
Super ();
This. moveable = moveable;
This. flag = true;
}

@ Override
Public void run (){
While (flag ){
CurrentTime = System. nanoTime (); // obtain the current time, in nanoseconds
Double timeSpanX = (double) (currentTime-moveable. timeX)/1000/1000/1000); // gets the time from the player's start to the current horizontal direction
// Process horizontal motion
Moveable. x = (int) (moveable. startX + moveable. v_x * timeSpanX );
// Process the vertical motion
If (moveable. bFall ){
Double timeSpanY = (double) (currentTime-moveable. timeY)/1000/1000/1000 );
Moveable. y = (int) (moveable. startY + moveable. startVY
* TimeSpanY + timeSpanY * g/2 );
Moveable. v_y = (float) (moveable. startVY + g * timeSpanY );
// Determine whether the ball has reached the highest point
If (moveable. startVY <0
& Math. abs (moveable. v_y) <= BallView. UP_ZERO ){
Moveable. timeY = System. nanoTime (); // you can specify the start time in the vertical direction of the new motion phase.
Moveable. v_y = 0; // set the real-time speed in the vertical direction of the new motion phase
Moveable. startVY = 0; // sets the initial speed in the vertical direction of the new motion phase.
Moveable. startY = moveable. y; // you can specify the initial position in the vertical direction of the new motion phase.
}
// Determine whether the ball hits the ground
If (moveable. y + moveable. r * 2> = BallView. GROUND_LING
& Moveable. v_y> 0) {// determines the hit location condition.
// Speed of changing the horizontal direction
Moveable. v_x = moveable. v_x * (1-moveable. impactFactor); // attenuation speed in the horizontal direction
// Change the vertical speed
Moveable. v_y = 0-moveable. v_y
* (1-moveable. impactFactor); // decay the speed in the vertical direction and change the direction
If (Math. abs (moveable. v_y) <BallView. DOWN_ZERO) {// judge the speed of the hit location. if it is too small, stop
This. flag = false;
} Else {// after hitting the ground, the speed can also pop up to continue the next stage of motion
// Horizontal direction changes after hitting the ground
Moveable. startX = moveable. x; // you can specify the starting position of the horizontal direction of the new motion phase.
Moveable. timeX = System. nanoTime (); // sets the start time of the horizontal direction of the new motion phase.
// Changes in the vertical direction after hitting the ground
Moveable. startY = moveable. y; // you can specify the starting position in the vertical direction of the new motion phase.
Moveable. timeY = System. nanoTime (); // sets the start time of the vertical movement of the new motion phase.
Moveable. startVY = moveable. v_y; // sets the initial speed in the vertical direction of the new motion phase.
}
}
} Else if (moveable. x + moveable. r/2> = BallView. WOOD_EDGE) {// determines whether the ball has been removed from the baffle.
Moveable. timeY = System. nanoTime (); // records the start time of the ball in the vertical direction.
Moveable. bFall = true; // you can specify whether the flag starts to fall.
}
Try {
Thread. sleep (sleepSpan); // sleep for a period of time
} Catch (Exception e ){
E. printStackTrace ();
}
}
}

}
Here I want to add a little bit. Maybe everyone understands it, but I am confused here. The question is: Determine whether the ball hits moveable. y + moveable. r * 2> = BallView. why does GROUND_LING multiply the radius by 2 here? If you think so, you just put the center in the ball's heart, but the X involved in us, Y and so on are calculated based on the upper left corner of the ball. it is equivalent to wrapping a rectangle on the outermost side. in fact, this is the use of Collision Detection Technology (moment (cylindrical) detection ). in this way, we naturally understand why the radius is multiplied by 2. determine whether the ball is removed from the baffle moveable. x + moveable. r/2> = BallView. WOOD_EDGE. in reality, it is impossible for the ball to fall before it leaves the baffle. If so, it does not conform to the actual situation, so it starts to fall when the ball is still 1/4 away from the baffle, this is in line with the reality. (In fact, there seems no difference, but you need to understand ).
You can understand the code carefully. After all, it is not difficult to understand the code. The key is thinking. You have to have a trajectory model of ball movement in your mind.

After the development of the physical engine of the ball movement is complete, let the ball be displayed. Next we will develop the View.
Here I want to explain: in applications, we generally do not customize views, it is almost the transformation of the inherited control to achieve the desired effect (except in special cases), but the game is different, because the game interface is not a built-in android control, they are all drawn through Canvas. therefore, in the game, we generally directly inherit from the View and SurfaceView for drawing.
The core of the game is to constantly draw and refresh the interface. We have already drawn the figure through the onDraw method. Next we will analyze how to refresh the interface.


What is the difference between View and SurfaceView?
Simple Description:
In View, android provides us with two methods to update the UI: invalidate postInvalidate. However, we also need to update the UI through Handler, which is not required by postInvalidate and can be directly updated in the thread, surfaceView can be used to update a view in a custom thread.
Next let's take a look at BallView. java
Package com. jj. ball;

Import java. util. ArrayList;
Import java. util. Random;

Import android. content. Context;
Import android. content. res. Resources;
Import android. graphics. Bitmap;
Import android. graphics. BitmapFactory;
Import android. graphics. Canvas;
Import android. graphics. Color;
Import android. graphics. Paint;
Import android. graphics. RectF;
Import android. util. AttributeSet;
Import android. view. SurfaceHolder;
Import android. view. SurfaceView;

/***
* Custom View
*
* @ Author zhangjia
*
*/
Public class BallView extends SurfaceView implements SurfaceHolder. Callback {
Static final int V_MAX = 35; // maximum horizontal speed of the ball
Static final int V_MIN = 15; // Minimum horizontal speed of the ball

Public static final int WOOD_EDGE = 60; // X coordinate of the right side of the board

Static final int GROUND_LING = 730; // represents the coordinate of ground Y

Static final int UP_ZERO = 30; // when the ball is rising, if it is smaller than this number, it is 0,

Static final int DOWN_ZERO = 60; // when the ball falls, if the speed is less than this value, it is 0.

Bitmap bitmapArray [] = new Bitmap [6]; // Image array
Bitmap BMP back; // background image
Bitmap BMP wood; // template image background

String fps = "FPS: N/A"; // A String used to display the Frame Rate

Int ballNumber = 8; // Number of balls

ArrayList <Moveable> arrayList = new ArrayList <Moveable> (); // array of ball objects

Private SurfaceHolder surfaceHolder;
Private DrawThread drawThread;

Public BallView (Context context ){
Super (context );
SurfaceHolder = getHolder ();
SurfaceHolder. addCallback (this );
InitBitmaps (getResources ());
InitMovables ();
DrawThread = new DrawThread (this, surfaceHolder );
}

Public BallView (Context context, AttributeSet attrs ){
Super (context, attrs );
}

/***
* Initialize image data
*
* @ Param resources
*/
Void initBitmaps (Resources resources ){
BitmapArray [0] = BitmapFactory. decodeResource (resources,
R. drawable. ball_red_small); // red compared to small ball
BitmapArray [1] = BitmapFactory. decodeResource (resources,
R. drawable. ball_purple_small); // purple compared to small ball
BitmapArray [2] = BitmapFactory. decodeResource (resources,
R. drawable. ball_green_small); // relatively small green ball
BitmapArray [3] = BitmapFactory. decodeResource (resources,
R. drawable. ball_red); // large red ball
BitmapArray [4] = BitmapFactory. decodeResource (resources,
R. drawable. ball_purple); // large purple ball
BitmapArray [5] = BitmapFactory. decodeResource (resources,
R. drawable. ball_green); // large green ball
BMP back = BitmapFactory. decodeResource (resources, R. drawable. back); // specifies the background brick wall.
BMP wood = BitmapFactory. decodeResource (resources, R. drawable. wood); // wood
}

/***
* Initialize the ball
*/
Public void initMovables (){
Random random = new Random ();
For (int I = 0; I <ballNumber; I ++ ){
Int index = random. nextInt (32); // generates a random number.
Bitmap tempBitmap = null; // declare a Bitmap image reference
If (I <ballNumber/2 ){
TempBitmap = bitmapArray [3 + index % 3]; // If you initialize the previous hemisphere, you will randomly find
} Else {
TempBitmap = bitmapArray [index % 3]; // if it is the last hemisphere after initialization, randomly find
}
Moveable m = new Moveable (0, 70-tempBitmap. getHeight (),
TempBitmap. getWidth ()/2, tempBitmap); // create a Movable object
ArrayList. add (m); // add the newly created Movable object to the ArrayList
}
}

/***
* Draw Image Information
*/
Public void doDraw (Canvas canvas ){
// Draw full screen
RectF rectF = new RectF (0, 0, getWidth (), getHeight ());
Canvas. drawBitmap (BMP back, null, rectF, null); // draw the background
Canvas. drawBitmap (BMP wood, 0, 60, null); // draw a board
// Draw a series of small balls
For (Moveable moveable: arrayList ){
Moveable. drawSelf (canvas );
}
Paint paint = new Paint ();
Paint. setColor (Color. BLUE );
Paint. setTextSize (18 );
Paint. setAntiAlias (true );
Canvas. drawText (fps, 30, 30, paint); // draw text

}

@ Override
Public void surfaceCreated (SurfaceHolder holder ){
If (! DrawThread. isAlive ())
DrawThread. start ();
}

@ Override
Public void surfaceChanged (SurfaceHolder holder, int format, int width,
Int height ){

}

@ Override
Public void surfaceDestroyed (SurfaceHolder holder ){
DrawThread. flag = false;
DrawThread = null;

}

}
The comments have been quite clear and I believe everyone can understand them. Here we will only introduce them more. Next we will look at DrawThread for redrawing the screen and calculating the frame rate.

DrawThread. java
Package com. jj. ball;

Import android. graphics. Canvas;
Import android. view. SurfaceHolder;
Import android. view. SurfaceView;

/***
* Used for screen repainting and Frame Rate Calculation
*
* @ Author zhangjia
*
*/
Public class DrawThread extends Thread {
BallView ballView; // custom View
SurfaceHolder surfaceHolder;
Boolean flag = false; // thread ID
Int sleepSpan = 30; // thread sleep
Long start = System. nanoTime (); // actual time, used to calculate the Frame Rate
Int count = 0; // calculate the Frame Rate

Public DrawThread (BallView ballView, SurfaceHolder surfaceHolder ){
Super ();
This. ballView = ballView;
This. surfaceHolder = surfaceHolder;
This. flag = true;
}

@ Override
Public void run (){
Canvas canvas = null;
While (flag ){
Try {
Canvas = surfaceHolder. lockCanvas (); // Get canvas.
Synchronized (surfaceHolder ){
BallView. doDraw (canvas); // draw a ballView.

}
} Catch (Exception e ){
E. printStackTrace ();
} Finally {
If (canvas! = Null ){
SurfaceHolder. unlockCanvasAndPost (canvas); // unlock
}
}
This. count ++;
If (count = 20) {// if the count is 20 frames
Count = 0; // clear the counter
Long tempStamp = System. nanoTime (); // get the current time
Long span = tempStamp-start; // obtain the interval
Start = tempStamp; // assign a value to start
Double fps = Math. round (100000000000.0/span * 20)/100.0; // calculate the Frame Rate
BallView. fps = "FPS:" + fps; // set the calculated frame rate to the corresponding String object of BallView.
}
Try {
Thread. sleep (sleepSpan); // The Thread is sleeping for a period of time.
} Catch (Exception e ){
E. printStackTrace (); // capture and print exceptions
}
}
}

}
Here we get the surfaceHolder of BallView. We need to constantly draw the screen in this thread to achieve the animation effect. for surfaceHolder applications, as to when lockCanvas and unlockCanvasAndPost will be applied, you can refer to the article we just recommended.

Here I want to explain in detail a thing: frame rate.
This. count ++;
If (count = 20) {// if the count is 20 frames
Count = 0; // clear the counter
Long tempStamp = System. nanoTime (); // get the current time
Long span = tempStamp-start; // obtain the interval
Start = tempStamp; // assign a value to start
Double fps = Math. round (100000000000.0/span * 20)/100.0; // calculate the Frame Rate
BallView. fps = "FPS:" + fps; // set the calculated frame rate to the corresponding String object of BallView.
}
First, we get the time span consumed by 20 frames. then, calculate how many spans are included in the 100s. then, multiply by 20 to get the number of frames in 100 s, and divide by to get the number of frames and the frame rate (FPS) in 1 s ).

In this way, we have prepared the work for the ball, and finally we can call it in the Activity.
RequestWindowFeature (Window. FEATURE_NO_TITLE); // set not to display the title
GetWindow (). setFlags (
// Set to full screen mode
WindowManager. LayoutParams. FLAG_FULLSCREEN,
WindowManager. LayoutParams. FLAG_FULLSCREEN );
Window_Height = getWindowManager (). getdefadisplay display (). getHeight ();
Window_Width = getWindowManager (). getdefadisplay display (). getWidth ();

BallView = new BallView (this); // creates a BallView object
SetContentView (ballView); // set the screen to a BallView object
Finally, let's show you the effect. (To put it bluntly, if there is no graph, the interest will be less than half, which is not an exaggeration at all. I also see other people's blogs. If there is no graph, I may not see it. haha)
Example:

The effect looks good. Indeed, the game is fun !!!
Note: In Game Development, screen resolution is very demanding. For example, in this case, the book uses 320*480. and I am 480*800. it is difficult to run without processing.
Cause: the image is a whole. it is the walls and the ground, so I cannot determine the Y value of the ground. there is a way to get it. Calculate the proportion of the ground to the image in the image, but I just want to guess, because I just got started. if you are engaged in game development, we hope you will leave your solutions.
Leave a message if you have any questions.
In addition, if it is helpful to you, remember to like one.
Here: Thanks for you!

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.