package com.scwang.smart.refresh.header; import android.content.Context; import android.graphics.Canvas; import android.graphics.Point; import android.graphics.RectF; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.util.SparseArray; import android.view.View; import com.scwang.smart.refresh.header.fungame.FunGameView; import com.scwang.smart.refresh.layout.api.RefreshKernel; import com.scwang.smart.refresh.layout.util.SmartUtil; import java.util.LinkedList; import java.util.Queue; import java.util.Random; /** * Created by scwang on 2018/3/09. * from https://github.com/Hitomis/FunGameRefresh */ public class FunGameBattleCityHeader extends FunGameView { // /** * 轨道数量 */ protected static int TANK_ROW_NUM = 3; /** * 炮管尺寸所在tank尺寸的比率 */ protected static final float TANK_BARREL_RATIO = 1/3.f; /** * 默认子弹之间空隙间距 */ protected static final int DEFAULT_BULLET_NUM_SPACING = 360; /** * 默认敌方坦克之间间距 */ protected static final int DEFAULT_ENEMY_TANK_NUM_SPACING = 60; /** * 表示运行漏掉的敌方坦克总数量 和 升级后消灭坦克总数量的增量 */ protected static final int DEFAULT_TANK_MAGIC_TOTAL_NUM = 8; /** * 所有轨道上敌方坦克矩阵集合 */ protected SparseArray> eTankSparseArray; /** * 屏幕上所有子弹坐标点集合 */ protected Queue mBulletList; /** * 击中敌方坦克的子弹坐标点 */ protected Point usedBullet; /** * 用于随机定位一个轨道下标值 */ protected Random random; /** * 子弹半径 */ protected float bulletRadius; /** * 敌方坦克间距、子弹间距 */ protected int enemyTankSpace, bulletSpace; /** * 炮筒尺寸 */ protected int barrelSize; /** * 敌方坦克速度、子弹速度 */ protected int enemySpeed = 1, bulletSpeed = 4; /** * 当前前一辆敌方坦克和后一辆已经存在的间距值 * 用于确定是否要派出新的一辆敌方坦克 */ protected int offsetETankX; /** * 当前前一颗子弹和后一颗子弹的间距值 * 用于确定是否要发射新的一颗子弹 */ protected int offsetMBulletX; /** * 当前漏掉的坦克数量 */ protected int overstepNum; /** * 当前难度等级需要消灭坦克数量 */ protected int levelNum; /** * 当前难度等级内消灭的敌方坦克数量 */ protected int wipeOutNum; /** * 表示第一次标示值,用于添加第一辆敌方坦克逻辑 */ protected boolean once = true; // // public FunGameBattleCityHeader(Context context) { this(context, null); } public FunGameBattleCityHeader(Context context, AttributeSet attrs) { super(context, attrs, 0); random = new Random(); } @Override public void onInitialized(@NonNull RefreshKernel kernel, int height, int maxDragHeight) { controllerSize = height / TANK_ROW_NUM; barrelSize = (int) Math.floor(controllerSize * TANK_BARREL_RATIO + .5f); bulletRadius = (barrelSize - 2 * DIVIDING_LINE_SIZE) * .5f; super.onInitialized(kernel, height, maxDragHeight); } // // protected void resetConfigParams() { status = FunGameView.STATUS_GAME_PREPARE; controllerPosition = DIVIDING_LINE_SIZE; enemySpeed = SmartUtil.dp2px(1); bulletSpeed = SmartUtil.dp2px(4); levelNum = DEFAULT_TANK_MAGIC_TOTAL_NUM; wipeOutNum = 0; once = true; enemyTankSpace = controllerSize + barrelSize + DEFAULT_ENEMY_TANK_NUM_SPACING; bulletSpace = DEFAULT_BULLET_NUM_SPACING; eTankSparseArray = new SparseArray<>(); for (int i = 0; i < TANK_ROW_NUM; i++) { Queue rectFQueue = new LinkedList<>(); eTankSparseArray.put(i, rectFQueue); } mBulletList = new LinkedList<>(); } /** * 由index轨道下标从左边起始位置生成一个用于绘制敌方坦克的Rect * @param index 轨道下标 * @return 敌方坦克矩阵 */ protected RectF generateEnemyTank(int index) { float left = - (controllerSize + barrelSize); float top = index * (controllerSize) + DIVIDING_LINE_SIZE; return new RectF(left, top, left + barrelSize * 2.5f, top + controllerSize); } /** * 由Y坐标获取该坐标所在轨道的下标 * @param y 坐标Y值 * @return 轨道下标 */ protected int getTrackIndex(int y) { int index = y / (mHeaderHeight / TANK_ROW_NUM); index = index >= TANK_ROW_NUM ? TANK_ROW_NUM - 1 : index; index = index < 0 ? 0 : index; return index; } /** * 判断是否消灭敌方坦克 * @param point 单签子弹坐标点 * @return 消灭:true, 反之:false */ protected boolean checkWipeOutETank(Point point) { boolean beHit = false; int trackIndex = getTrackIndex(point.y); RectF rectF = eTankSparseArray.get(trackIndex).peek(); if (rectF != null && rectF.contains(point.x, point.y)) { // 击中 if (++wipeOutNum == levelNum) { upLevel(); } eTankSparseArray.get(trackIndex).poll(); beHit = true; } return beHit; } /** * 难度升级 */ protected void upLevel() { levelNum += DEFAULT_TANK_MAGIC_TOTAL_NUM; enemySpeed += SmartUtil.dp2px(1); bulletSpeed += SmartUtil.dp2px(1); wipeOutNum = 0; if (enemyTankSpace > 12) enemyTankSpace -= 12; if (bulletSpace > 30) bulletSpace -= 30; } /** * 判断我方坦克是否与敌方坦克相撞 * @param index 轨道下标 * @param selfX 我方坦克所在坐标X值 * @param selfY 我方坦克矩阵的top 或者 bottom 值 * @return true:相撞,反之:false */ protected boolean checkTankCrash(int index, float selfX, float selfY) { boolean isCrash = false; RectF rectF = eTankSparseArray.get(index).peek(); if (rectF != null && rectF.contains(selfX, selfY)) { isCrash = true; } return isCrash; } /** * 随机定位一个轨道下标值 * @return 轨道下标 */ protected int appearanceOption() { return random.nextInt(TANK_ROW_NUM); } // // @Override protected void drawGame(Canvas canvas, int width, int height) { drawSelfTank(canvas,width); if (status == STATUS_GAME_PLAY || status == STATUS_GAME_FINISHED || status == STATUS_GAME_FAIL) { drawEnemyTank(canvas,width); drawBulletPath(canvas,width); } final View thisView = this; if (thisView.isInEditMode()) { drawTank(canvas, new RectF(controllerSize, 0, controllerSize * 2, controllerSize)); drawTank(canvas, new RectF(0, controllerSize, controllerSize, controllerSize*2)); drawTank(canvas, new RectF(controllerSize * 3, controllerSize * 2, controllerSize * 4, controllerSize*3)); } } /** * 绘制子弹路径 * @param width 华埠宽度 * @param canvas 默认画布 */ protected void drawBulletPath(Canvas canvas, int width) { mPaint.setColor(mModelColor); offsetMBulletX += bulletSpeed; if (offsetMBulletX / bulletSpace == 1) { offsetMBulletX = 0; } if (offsetMBulletX == 0) { Point bulletPoint = new Point(); bulletPoint.x = width - controllerSize - barrelSize; bulletPoint.y = (int) (controllerPosition + controllerSize * .5f); mBulletList.offer(bulletPoint); } boolean isOverStep = false; for (Point point : mBulletList) { if (checkWipeOutETank(point)) { usedBullet = point; continue; } if (point.x + bulletRadius <= 0) { isOverStep = true; } drawBullet(canvas, point); } if (isOverStep) { mBulletList.poll(); } mBulletList.remove(usedBullet); usedBullet = null; } /** * 绘制子弹 * @param canvas 默认画布 * @param point 子弹圆心坐标点 */ protected void drawBullet(Canvas canvas, Point point) { point.x -= bulletSpeed; canvas.drawCircle(point.x, point.y, bulletRadius, mPaint); } /** * 绘制我方坦克 * @param width 华埠宽度 * @param canvas 默认画布 */ protected void drawSelfTank(Canvas canvas, int width) { mPaint.setColor(rModelColor); boolean isAboveCrash = checkTankCrash(getTrackIndex((int) controllerPosition), width - controllerSize, controllerPosition); boolean isBelowCrash = checkTankCrash(getTrackIndex((int) (controllerPosition + controllerSize)), width - controllerSize, controllerPosition + controllerSize); if (isAboveCrash || isBelowCrash) { status = STATUS_GAME_OVER; } canvas.drawRect(width - controllerSize, controllerPosition + DIVIDING_LINE_SIZE, width, controllerPosition + controllerSize + DIVIDING_LINE_SIZE, mPaint); canvas.drawRect(width - controllerSize - barrelSize, controllerPosition + (controllerSize - barrelSize) * .5f, width - controllerSize, controllerPosition + (controllerSize - barrelSize) * .5f + barrelSize, mPaint); } /** * 绘制三条轨道上的敌方坦克 * @param canvas 默认画布 * @param width 华埠宽度 */ protected void drawEnemyTank(Canvas canvas, int width) { mPaint.setColor(lModelColor); offsetETankX += enemySpeed; if (offsetETankX / enemyTankSpace == 1 || once) { offsetETankX = 0; once = false; } boolean isOverstep = false; int option = appearanceOption(); for (int i = 0; i < TANK_ROW_NUM; i++) { Queue rectFQueue = eTankSparseArray.get(i); if (offsetETankX == 0 && i == option) { rectFQueue.offer(generateEnemyTank(i)); } for (RectF rectF : rectFQueue) { if (rectF.left >= width) { isOverstep = true; if (++overstepNum >= DEFAULT_TANK_MAGIC_TOTAL_NUM) { status = STATUS_GAME_OVER; break; } continue; } drawTank(canvas, rectF); } if (status == STATUS_GAME_OVER) break; if (isOverstep) { rectFQueue.poll(); isOverstep = false; } } final View thisView = this; thisView.invalidate(); } /** * 绘制一辆敌方坦克 * @param canvas 默认画布 * @param rectF 坦克矩阵 */ protected void drawTank(Canvas canvas, RectF rectF) { rectF.set(rectF.left + enemySpeed, rectF.top, rectF.right + enemySpeed, rectF.bottom); canvas.drawRect(rectF, mPaint); float barrelTop = rectF.top + (controllerSize - barrelSize) * .5f; canvas.drawRect(rectF.right, barrelTop, rectF.right + barrelSize, barrelTop + barrelSize, mPaint); } // }