-
package com.pbicv.ddpx.game; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.text.TextPaint; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import com.pbicv.ddpx.R; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class GameView extends View { private paint paint; private Paint textPaint; private CombatAircraft combatAircraft = null; private List<Sprite> sprites = new ArrayList<Sprite>(); private List<Sprite> spritesNeedAdded = new ArrayList<Sprite>(); //0:combatAircraft //1:explosion //2:yellowBullet //3:blueBullet //4:smallEnemyPlane //5:middleEnemyPlane //6:bigEnemyPlane //7:bombAward //8:bulletAward //9:pause1 //10:pause2 //11:bomb private List<Bitmap> bitmaps = new ArrayList<Bitmap>(); private float density = getResources().getDisplayMetrics().density;//Screen density public static final int STATUS_GAME_STARTED = 1;//The game starts public static final int STATUS_GAME_PAUSED = 2; //Game paused public static final int STATUS_GAME_OVER = 3;//The game is over public static final int STATUS_GAME_DESTROYED = 4;//The game is destroyed private int status = STATUS_GAME_DESTROYED;//initially destroyed state private long frame = 0;//Total number of frames drawn private long score = 0; //Total score private float fontSize = 12; //Default font size, used to draw text in the upper left corner private float fontSize2 = 20;//Used to draw text in Dialog when Game Over private float borderSize = 2;//The border of Game Over’s Dialog private Rect continueRect = new Rect();//Rect of the "Continue" and "Restart" buttons //Variables related to touch events private static final int TOUCH_MOVE = 1;//Move private static final int TOUCH_SINGLE_CLICK = 2;//Click private static final int TOUCH_DOUBLE_CLICK = 3; //Double click //A click event is composed of two events, DOWN and UP. Assuming that the interval from down to up is less than 200 milliseconds, we consider a click event to have occurred. private static final int singleClickDurationTime = 200; //A double-click event is composed of two click events. If the time between the two click events is less than 300 milliseconds, we consider a double-click event to have occurred. private static final int doubleClickDurationTime = 300; private long lastSingleClickTime = -1;//The last time a click occurred private long touchDownTime = -1;//The moment the contact is pressed private long touchUpTime = -1;//The moment when the contact pops up private float touchX = -1;//x coordinate of the touch point private float touchY = -1;//Y coordinate of the touch point public GameView(Context context) { super(context); init(null, 0); } public GameView(Context context, AttributeSet attrs) { super(context, attrs); init(attrs, 0); } public GameView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(attrs, defStyle); } private void init(AttributeSet attrs, int defStyle) { final TypedArray a = getContext().obtainStyledAttributes( attrs, R.styleable.GameView, defStyle, 0); a.recycle(); //Initialize paint paint = new Paint(); paint.setStyle(Paint.Style.FILL); //Set textPaint, set to anti-aliasing, and bold textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.FAKE_BOLD_TEXT_FLAG); textPaint.setColor(0xff000000); fontSize = textPaint.getTextSize(); fontSize *= density; fontSize2 *= density; textPaint.setTextSize(fontSize); borderSize *= density; } public void start(int[] bitmapIds){ destroy(); for(int bitmapId : bitmapIds){ Bitmap bitmap = BitmapFactory.decodeResource(getResources(), bitmapId); bitmaps.add(bitmap); } startWhenBitmapsReady(); } private void startWhenBitmapsReady(){ combatAircraft = new CombatAircraft(bitmaps.get(0)); //Set the game to start state status = STATUS_GAME_STARTED; postInvalidate(); } private void restart(){ destroyNotRecyleBitmaps(); startWhenBitmapsReady(); } public void pause(){ //Set the game to pause state status = STATUS_GAME_PAUSED; } private void resume(){ //Set the game to running state status = STATUS_GAME_STARTED; postInvalidate(); } private long getScore(){ //Get game score return score; } /*----------------------------------draw---------------- ---------------------*/ @Override protected void onDraw(Canvas canvas) { //We check at each frame whether the conditions for delayed triggering of the click event are met if(isSingleClick()){ onSingleClick(touchX, touchY); } super.onDraw(canvas); if(status == STATUS_GAME_STARTED){ drawGameStarted(canvas); }else if(status == STATUS_GAME_PAUSED){ drawGamePaused(canvas); }else if(status == STATUS_GAME_OVER){ drawGameOver(canvas); } } //Draw the game in running state private void drawGameStarted(Canvas canvas){ drawScoreAndBombs(canvas); //When drawing for the first time, move the fighter to the bottom of the Canvas, in the center of the horizontal direction if(frame == 0){ float centerX = canvas.getWidth() / 2; float centerY = canvas.getHeight() - combatAircraft.getHeight() / 2; combatAircraft.centerTo(centerX, centerY); } //Add spritesNeedAdded to sprites if(spritesNeedAdded.size() > 0){ sprites.addAll(spritesNeedAdded); spritesNeedAdded.clear(); } //Check the situation when the fighter runs in front of the bullet destroyBulletsFrontOfCombatAircraft(); //Remove the destroyed Sprite before drawing removeDestroyedSprites(); //Randomly add Sprite every 30 frames if(frame % 30 == 0){ createRandomSprites(canvas.getWidth()); } frame + + ; //Traverse sprites and draw enemy aircraft, bullets, rewards, and explosion effects Iterator<Sprite> iterator = sprites.iterator(); while (iterator.hasNext()){ Sprite s = iterator.next(); if(!s.isDestroyed()){ //The destroy method may be called in the draw method of Sprite s.draw(canvas, paint, this); } //Here we need to determine whether the Sprite was destroyed after executing the draw method. if(s.isDestroyed()){ //If the Sprite is destroyed, remove it from Sprites iterator.remove(); } } if(combatAircraft != null){ //Finally draw the fighter combatAircraft.draw(canvas, paint, this); if(combatAircraft.isDestroyed()){ //If the fighter is hit and destroyed, the game is over status = STATUS_GAME_OVER; } //By calling the postInvalidate() method, the View continues to render to achieve dynamic effects. postInvalidate(); } } //Draw the game in paused state private void drawGamePaused(Canvas canvas){ drawScoreAndBombs(canvas); //Call the Sprite's onDraw method instead of the draw method, so that the static Sprite can be rendered without letting the Sprite change its position. for(Sprites : sprites){ s.onDraw(canvas, paint, this); } if(combatAircraft != null){ combatAircraft.onDraw(canvas, paint, this); } //Draw Dialog and display score drawScoreDialog(canvas, "continue"); if(lastSingleClickTime > 0){ postInvalidate(); } } //Draw the end state of the game private void drawGameOver(Canvas canvas){ //After Game Over, only draw a pop-up window to display the final score drawScoreDialog(canvas, "restart"); if(lastSingleClickTime > 0){ postInvalidate(); } } private void drawScoreDialog(Canvas canvas, String operation){ int canvasWidth = canvas.getWidth(); int canvasHeight = canvas.getHeight(); //Storage original value float originalFontSize = textPaint.getTextSize(); Paint.Align originalFontAlign = textPaint.getTextAlign(); int originalColor = paint.getColor(); Paint.Style originalStyle = paint.getStyle(); /* W = 360 w1 = 20 w2 = 320 buttonWidth = 140 buttonHeight = 42 H = 558 h1 = 150 h2 = 60 h3 = 124 h4 = 76 */ int w1 = (int)(20.0 / 360.0 * canvasWidth); int w2 = canvasWidth - 2 * w1; int buttonWidth = (int)(140.0 / 360.0 * canvasWidth); int h1 = (int)(150.0 / 558.0 * canvasHeight); int h2 = (int)(60.0 / 558.0 * canvasHeight); int h3 = (int)(124.0 / 558.0 * canvasHeight); int h4 = (int)(76.0 / 558.0 * canvasHeight); int buttonHeight = (int)(42.0 / 558.0 * canvasHeight); canvas.translate(w1, h1); //Draw background color paint.setStyle(Paint.Style.FILL); paint.setColor(0xFFD7DDDE); Rect rect1 = new Rect(0, 0, w2, canvasHeight - 2 * h1); canvas.drawRect(rect1, paint); //Draw the border paint.setStyle(Paint.Style.STROKE); paint.setColor(0xFF515151); paint.setStrokeWidth(borderSize); //paint.setStrokeCap(Paint.Cap.ROUND); paint.setStrokeJoin(Paint.Join.ROUND); canvas.drawRect(rect1, paint); //Draw text "Airplane Battle Score" textPaint.setTextSize(fontSize2); textPaint.setTextAlign(Paint.Align.CENTER); canvas.drawText("Final score", w2 / 2, (h2 - fontSize2) / 2 + fontSize2, textPaint); //Draw the horizontal line under "Aircraft Battle Score" canvas.translate(0, h2); canvas.drawLine(0, 0, w2, 0, paint); //Draw the actual score String allScore = String.valueOf(getScore()); canvas.drawText(allScore, w2 / 2, (h3 - fontSize2) / 2 + fontSize2, textPaint); //Draw a horizontal line under the fraction canvas.translate(0, h3); canvas.drawLine(0, 0, w2, 0, paint); //Draw button border Rect rect2 = new Rect(); rect2.left = (w2 - buttonWidth) / 2; rect2.right = w2 - rect2.left; rect2.top = (h4 - buttonHeight) / 2; rect2.bottom = h4 - rect2.top; canvas.drawRect(rect2, paint); //Draw text "continue" or "restart" canvas.translate(0, rect2.top); canvas.drawText(operation, w2 / 2, (buttonHeight - fontSize2) / 2 + fontSize2, textPaint); continueRect = new Rect(rect2); continueRect.left = w1 + rect2.left; continueRect.right = continueRect.left + buttonWidth; continueRect.top = h1 + h2 + h3 + rect2.top; continueRect.bottom = continueRect.top + buttonHeight; //Reset textPaint.setTextSize(originalFontSize); textPaint.setTextAlign(originalFontAlign); paint.setColor(originalColor); paint.setStyle(originalStyle); } //Draw the score in the upper left corner and the number of bombs in the lower left corner private void drawScoreAndBombs(Canvas canvas){ //Draw the pause button in the upper left corner Bitmap pauseBitmap = status == STATUS_GAME_STARTED ? bitmaps.get(9) : bitmaps.get(10); RectF pauseBitmapDstRecF = getPauseBitmapDstRecF(); float pauseLeft = pauseBitmapDstRecF.left; float pauseTop = pauseBitmapDstRecF.top; canvas.drawBitmap(pauseBitmap, pauseLeft, pauseTop, paint); //Draw the total score in the upper left corner float scoreLeft = pauseLeft + pauseBitmap.getWidth() + 20 * density; float scoreTop = fontSize + pauseTop + pauseBitmap.getHeight() / 2 - fontSize / 2; canvas.drawText(score + "", scoreLeft, scoreTop, textPaint); //Draw the lower left corner if(combatAircraft != null & amp; & amp; !combatAircraft.isDestroyed()){ int bombCount = combatAircraft.getBombCount(); if(bombCount > 0){ //Draw the bomb in the lower left corner Bitmap bombBitmap = bitmaps.get(11); float bombTop = canvas.getHeight() - bombBitmap.getHeight(); canvas.drawBitmap(bombBitmap, 0, bombTop, paint); //Draw the number of bombs in the lower left corner float bombCountLeft = bombBitmap.getWidth() + 10 * density; float bombCountTop = fontSize + bombTop + bombBitmap.getHeight() / 2 - fontSize / 2; canvas.drawText("X " + bombCount, bombCountLeft, bombCountTop, textPaint); } } } //Check the situation when the fighter runs in front of the bullet private void destroyBulletsFrontOfCombatAircraft(){ if(combatAircraft != null){ float aircraftY = combatAircraft.getY(); List<Bullet> aliveBullets = getAliveBullets(); for(Bullet bullet : aliveBullets){ //If the fighter runs in front of the bullet, then destroy the bullet if(aircraftY <= bullet.getY()){ bullet.destroy(); } } } } //Remove the destroyed Sprite private void removeDestroyedSprites(){ Iterator<Sprite> iterator = sprites.iterator(); while (iterator.hasNext()){ Sprite s = iterator.next(); if(s.isDestroyed()){ iterator.remove(); } } } //Generate random Sprite private void createRandomSprites(int canvasWidth){ Sprite sprite = null; int speed = 2; //callTime represents the number of times the createRandomSprites method is called int callTime = Math.round(frame / 30); if((callTime + 1) % 25 == 0){ //Send props and prizes if((callTime + 1) % 50 == 0){ //Send bomb sprite = new BombAward(bitmaps.get(7)); } else{ //Send double bullets sprite = new BulletAward(bitmaps.get(8)); } } else{ //Send enemy aircraft int[] nums = {0,0,0,0,0,1,0,0,1,0,0,0,0,1,1,1,1,1,1,2}; int index = (int)Math.floor(nums.length*Math.random()); int type = nums[index]; if(type==0){ //Small enemy plane sprite = new SmallEnemyPlane(bitmaps.get(4)); } else if(type == 1){ //hit enemy aircraft sprite = new MiddleEnemyPlane(bitmaps.get(5)); } else if(type == 2){ //Big enemy aircraft sprite = new BigEnemyPlane(bitmaps.get(6)); } if(type != 2){ if(Math.random() < 0.33){ speed = 4; } } } if(sprite != null){ float spriteWidth = sprite.getWidth(); float spriteHeight = sprite.getHeight(); float x = (float)((canvasWidth - spriteWidth)*Math.random()); float y = -spriteHeight; sprite.setX(x); sprite.setY(y); if(sprite instanceof AutoSprite){ AutoSprite autoSprite = (AutoSprite)sprite; autoSprite.setSpeed(speed); } addSprite(sprite); } } /*----------------------------------touch---------------- ------------------*/ @Override public boolean onTouchEvent(MotionEvent event){ //Get the event type we want by calling the resolveTouchType method //It should be noted that the resolveTouchType method will not return the TOUCH_SINGLE_CLICK type //We will call the isSingleClick method to detect whether the click event is triggered every time the onDraw method is executed. int touchType = resolveTouchType(event); if(status == STATUS_GAME_STARTED){ if(touchType == TOUCH_MOVE){ if(combatAircraft != null){ combatAircraft.centerTo(touchX, touchY); } }else if(touchType == TOUCH_DOUBLE_CLICK){ if(status == STATUS_GAME_STARTED){ if(combatAircraft != null){ //Double click will cause the fighter to use bombs combatAircraft.bomb(this); } } } }else if(status == STATUS_GAME_PAUSED){ if(lastSingleClickTime > 0){ postInvalidate(); } }else if(status == STATUS_GAME_OVER){ if(lastSingleClickTime > 0){ postInvalidate(); } } return true; } //Synthesize the event type we want private int resolveTouchType(MotionEvent event){ int touchType = -1; int action = event.getAction(); touchX = event.getX(); touchY = event.getY(); if(action == MotionEvent.ACTION_MOVE){ long deltaTime = System.currentTimeMillis() - touchDownTime; if(deltaTime > singleClickDurationTime){ //contact movement touchType = TOUCH_MOVE; } }else if(action == MotionEvent.ACTION_DOWN){ //contact pressed touchDownTime = System.currentTimeMillis(); }else if(action == MotionEvent.ACTION_UP){ //Contact pops up touchUpTime = System.currentTimeMillis(); //Calculate the time difference between when the contact is pressed and when the contact pops up long downUpDurationTime = touchUpTime - touchDownTime; //If the time difference between pressing and lifting this contact is less than the time difference specified by a click event, //Then we think a click occurred if(downUpDurationTime <= singleClickDurationTime){ //Calculate the time difference between this click and the last click long twoClickDurationTime = touchUpTime - lastSingleClickTime; if(twoClickDurationTime <= doubleClickDurationTime){ //If the time difference between two clicks is less than the time difference between a double-click event execution, //Then we think that a double-click event occurred touchType = TOUCH_DOUBLE_CLICK; //reset variables lastSingleClickTime = -1; touchDownTime = -1; touchUpTime = -1; }else{ //If a click event is formed this time, but a double-click event is not formed, then we will not trigger the click event formed this time. //We should see if a second click event is formed again after doubleClickDurationTime milliseconds //If a second click event is formed at that time, then we will synthesize a double-click event with this click event. //Otherwise this click event will be triggered after doubleClickDurationTime milliseconds lastSingleClickTime = touchUpTime; } } } return touchType; } //Call this method in the onDraw method and check whether a click event occurs in each frame private boolean isSingleClick(){ boolean singleClick = false; //Let's check whether the last click event meets the conditions for triggering a click event after doubleClickDurationTime milliseconds. if(lastSingleClickTime > 0){ //Calculate the time difference between the current moment and the last click event long deltaTime = System.currentTimeMillis() - lastSingleClickTime; if(deltaTime >= doubleClickDurationTime){ //If the time difference exceeds the time difference required for a double-click event, //Then delay the click event that should have occurred before triggering at this moment singleClick = true; //reset variables lastSingleClickTime = -1; touchDownTime = -1; touchUpTime = -1; } } return singleClick; } private void onSingleClick(float x, float y){ if(status == STATUS_GAME_STARTED){ if(isClickPause(x, y)){ //pause button clicked pause(); } }else if(status == STATUS_GAME_PAUSED){ if(isClickContinueButton(x, y)){ //The "Continue" button was clicked resume(); } }else if(status == STATUS_GAME_OVER){ if(isClickRestartButton(x, y)){ //The "Restart" button was clicked restart(); } } } //Whether the pause button in the upper left corner is clicked private boolean isClickPause(float x, float y){ RectF pauseRecF = getPauseBitmapDstRecF(); return pauseRecF.contains(x, y); } //Whether you clicked "Continue" in the paused state private boolean isClickContinueButton(float x, float y){ return continueRect.contains((int)x, (int)y); } //Whether the "Restart" button in the GAME OVER state has been clicked private boolean isClickRestartButton(float x, float y){ return continueRect.contains((int)x, (int)y); } private RectF getPauseBitmapDstRecF(){ Bitmap pauseBitmap = status == STATUS_GAME_STARTED ? bitmaps.get(9) : bitmaps.get(10); RectF recF = new RectF(); recF.left = 15 * density; recF.top = 15 * density; recF.right = recF.left + pauseBitmap.getWidth(); recF.bottom = recF.top + pauseBitmap.getHeight(); return recF; } /*----------------------------------destroy---------------- ------------------*/ private void destroyNotRecyleBitmaps(){ //Set the game to destruction state status = STATUS_GAME_DESTROYED; //reset frame frame = 0; //reset score score = 0; //Destroy fighter if(combatAircraft != null){ combatAircraft.destroy(); } combatAircraft = null; //Destroy enemy aircraft, bullets, rewards, explosions for(Sprites : sprites){ s.destroy(); } sprites.clear(); } public void destroy(){ destroyNotRecyleBitmaps(); //Release Bitmap resources for(Bitmap bitmap : bitmaps){ bitmap.recycle(); } bitmaps.clear(); } /*----------------------------------public methods--------------- ------------------*/ //Add Sprite to Sprites public void addSprite(Sprite sprite){ spritesNeedAdded.add(sprite); } //Add score public void addScore(int value){ score + = value; } public int getStatus(){ return status; } public float getDensity(){ return density; } public Bitmap getYellowBulletBitmap(){ return bitmaps.get(2); } public Bitmap getBlueBulletBitmap(){ return bitmaps.get(3); } public Bitmap getExplosionBitmap(){ return bitmaps.get(1); } //Get active enemy aircraft public List<EnemyPlane> getAliveEnemyPlanes(){ List<EnemyPlane> enemyPlanes = new ArrayList<EnemyPlane>(); for(Sprites : sprites){ if(!s.isDestroyed() & amp; & amp; s instanceof EnemyPlane){ EnemyPlane sprite = (EnemyPlane)s; enemyPlanes.add(sprite); } } return enemyPlanes; } //Get the active bomb reward public List<BombAward> getAliveBombAwards(){ List<BombAward> bombAwards = new ArrayList<BombAward>(); for(Sprites : sprites){ if(!s.isDestroyed() & amp; & amp; s instanceof BombAward){ BombAward bombAward = (BombAward)s; bombAwards.add(bombAward); } } return bombAwards; } //Get the active bullet reward public List<BulletAward> getAliveBulletAwards(){ List<BulletAward> bulletAwards = new ArrayList<BulletAward>(); for(Sprites : sprites){ if(!s.isDestroyed() & amp; & amp; s instanceof BulletAward){ BulletAward bulletAward = (BulletAward)s; bulletAwards.add(bulletAward); } } return bulletAwards; } //Get the active bullet public List<Bullet> getAliveBullets(){ List<Bullet> bullets = new ArrayList<Bullet>(); for(Sprites : sprites){ if(!s.isDestroyed() & amp; & amp; s instanceof Bullet){ Bullet bullet = (Bullet)s; bullets.add(bullet); } } return bullets; } }
The above code uses Java code to complete various logics in the aircraft battle.
-
The following are some screenshots of the aircraft battle:
This is a very simple Android game. Friends in need can chat with me privately!