减少Android游戏中的重复次数

时间:2013-07-29 18:18:33

标签: java android

我一直在写一个像Android游戏这样的太空入侵者,我有点担心我的代码会有多少重复(将会是这样)。在游戏中将有编辑:45个敌人,每个都有一个命中框和射击功能。我从一开始就想过,我应该创造一系列利用少数方法但从未制造过的敌人。我怎样才能减少重复?或者,我应该继续这条路吗?

我不是要求所有的答案 - 这不好玩。我只需要解决主要问题的想法。

以下代码将清楚地解决问题:

import android.graphics.Rect;
import java.util.ArrayList;

public class Enemy 
{
    private int maxHealth, currentHealth, power, speedX, speedY, centerX, centerY;
    private Background bg = GameScreen.getBg1();
    private Ship ship = GameScreen.getShip();
    public  static Rect xguy1Rect = new Rect(0,0,0,0);
    public static Rect xguy2Rect = new Rect(0,0,0,0);
    public static Rect xguy3Rect = new Rect(0,0,0,0);
    public static Rect xguy4Rect = new Rect(0,0,0,0);
    public static Rect xguy5Rect = new Rect(0,0,0,0);
    public static Rect xguy6Rect = new Rect(0,0,0,0);
    public static Rect xguy7Rect = new Rect(0,0,0,0);

    private boolean isMovingRight = true;
    private boolean isMovingLeft = false;
    private boolean xguy1IsShooting = false;
    private boolean xguy2IsShooting = false;
    private boolean xguy3IsShooting = false;

    private ArrayList<EnemyProjectile> eProjectiles = new ArrayList<EnemyProjectile>();

    //Behavioral Methods
    public void update() {
    //centerX += speedY;
    //moveRight();
    //moveLeft();
    //changeMovement();
    autoFire();
    speedY = bg.getSpeedY();
    speedX = 1;
    //setBounds??? 
    xguy1Rect.set(GameScreen.xguy1.getCenterX() +22, GameScreen.xguy1.getCenterY()-1 , GameScreen.xguy1.getCenterX() + 4+22, GameScreen.xguy1.getCenterY() );
    xguy2Rect.set(GameScreen.xguy2.getCenterX()+22, GameScreen.xguy2.getCenterY() -1, GameScreen.xguy2.getCenterX() +4+22, GameScreen.xguy2.getCenterY() ); 
    xguy3Rect.set(GameScreen.xguy3.getCenterX() +22 , GameScreen.xguy3.getCenterY()-1, GameScreen.xguy3.getCenterX() +4+22, GameScreen.xguy3.getCenterY() );
    xguy4Rect.set(GameScreen.xguy4.getCenterX() +22, GameScreen.xguy4.getCenterY()-1 , GameScreen.xguy4.getCenterX() + 4+22, GameScreen.xguy4.getCenterY() );
    xguy5Rect.set(GameScreen.xguy5.getCenterX()+22, GameScreen.xguy5.getCenterY() -1, GameScreen.xguy5.getCenterX() +4+22, GameScreen.xguy5.getCenterY() ); 
    xguy6Rect.set(GameScreen.xguy6.getCenterX() +22 , GameScreen.xguy6.getCenterY()-1, GameScreen.xguy6.getCenterX() +4+22, GameScreen.xguy6.getCenterY() );
    xguy7Rect.set(GameScreen.xguy7.getCenterX() +22 , GameScreen.xguy7.getCenterY()-1, GameScreen.xguy7.getCenterX() +4+22, GameScreen.xguy7.getCenterY() );
    }

  public void autoFire()
  {
      int num = 1 + (int)(Math.random() * ((250 - 1) + 1));
      //System.out.println(num);
      if(num == 4 || num == 6 || num == 8 && xguy1IsShooting == false)
      {
          if(GameScreen.xguy1IsAlive == true)
          {
            xguy1Attack();
          }
      }
      if(num == 1 || num == 3 || num == 5 && xguy2IsShooting == false)
      {
          if(GameScreen.xguy2IsAlive == true)
          {
              xguy2Attack();
          }
      }
      if(num == 12 || num == 15 || num == 17 && xguy3IsShooting == false)
      {
          if(GameScreen.xguy3IsAlive == true)
          {
              xguy3Attack();
          }
      }
  }

    public void moveRight()
    {
        if(isMovingRight == true)
        {
            centerX += speedX;
            if(centerX >= 630)
            {
                isMovingRight = false;
                isMovingLeft = true;
            }
        }
    }
    public void moveLeft()
    {
        if(isMovingLeft == true)
        {
            centerX -= speedX;
            if(centerX <= 10)
            {
                isMovingLeft = false;
                isMovingRight = true;
            }
        }
    }
    public void changeMovement()
    {
        //causes delayed death - xguys only die after going right
        if(centerX >= 630)
        {
            isMovingRight = false;
        }
        if(isMovingRight == false)
        {
            isMovingLeft = true;
        }
    }
    public void die() 
    {

    }
  public void xguy1Attack() 
  {
      EnemyProjectile e = new EnemyProjectile(GameScreen.xguy1.getCenterX()-6, GameScreen.xguy1.getCenterY());
      eProjectiles.add(e);
      xguy1IsShooting = false;
  }
  public void xguy2Attack()
  {
      EnemyProjectile e = new EnemyProjectile(GameScreen.xguy2.getCenterX()-6, GameScreen.xguy2.getCenterY());
      eProjectiles.add(e);
      xguy2IsShooting = false;
  }

  public void xguy3Attack()
  {
      EnemyProjectile e = new EnemyProjectile(GameScreen.xguy3.getCenterX()-6, GameScreen.xguy3.getCenterY() );
      eProjectiles.add(e);
      xguy3IsShooting = false;
  }

    public int getMaxHealth() {
        return maxHealth;
    }

    public int getCurrentHealth() {
        return currentHealth;
    }

    public int getPower() {
        return power;
    }

    public int getSpeedY() {
        return speedY;
    }

    public int getCenterX() {
        return centerX;
    }

    public int getCenterY() {
        return centerY;
    }

    public Background getBg() {
        return bg;
    }

    public void setMaxHealth(int maxHealth) {
        this.maxHealth = maxHealth;
    }

    public void setCurrentHealth(int currentHealth) {
        this.currentHealth = currentHealth;
    }

    public void setPower(int power) {
        this.power = power;
    }

    public void setSpeedX(int speedX) {
        this.speedY = speedX;
    }

    public void setCenterX(int centerX) {
        this.centerX = centerX;
    }

    public void setCenterY(int centerY) {
        this.centerY = centerY;
    }

    public void setBg(Background bg) {
        this.bg = bg;
    }

  public ArrayList getEProjectiles() {
      return eProjectiles;
      }
}

............................................... ...................

import android.graphics.Rect;

public class Projectile {

    private int x, y, speedY;
    private boolean visible;

    private Rect r;

    public Projectile(int startX, int startY) 
    {
        x = startX;
        y = startY;
        speedY = 7;
        visible = true;

        r = new Rect(0,0,0,0);
    }

    public void update()
    {
        y -= speedY;
        r.set(x, y, x + 4, y + 10);
        if (y < -10) {
           visible = false;
           r=null;
        }
        if (visible)
        {
            checkCollision();
        }
    }

    private void checkCollision()
    {
        if(Rect.intersects(r, Enemy.xguy1Rect) && GameScreen.xguy1IsAlive == true)
        {
        visible = false;
        GameScreen.score += 10;
        GameScreen.xguy1IsAlive = false;
        }
        else if(Rect.intersects(r, Enemy.xguy2Rect) && GameScreen.xguy2IsAlive == true)
        {
            visible = false;
            GameScreen.score += 10;
            GameScreen.xguy2IsAlive = false;
        }
        else if(Rect.intersects(r, Enemy.xguy3Rect) && GameScreen.xguy3IsAlive == true)
        {
            visible = false;
            GameScreen.score += 10;
            GameScreen.xguy3IsAlive = false;
        }
        else if(Rect.intersects(r, Enemy.xguy4Rect) && GameScreen.xguy4IsAlive == true)
        {
            visible = false;
            GameScreen.score += 10;
            GameScreen.xguy4IsAlive = false;
        }
        else if(Rect.intersects(r, Enemy.xguy5Rect) && GameScreen.xguy5IsAlive == true)
        {
            visible = false;
            GameScreen.score += 10;
            GameScreen.xguy5IsAlive = false;
        }
        else if(Rect.intersects(r, Enemy.xguy6Rect) && GameScreen.xguy6IsAlive == true)
        {
            visible = false;
            GameScreen.score += 10;
            GameScreen.xguy6IsAlive = false;
        }
        else if(Rect.intersects(r, Enemy.xguy7Rect) && GameScreen.xguy7IsAlive == true)
        {
            visible = false;
            GameScreen.score += 10;
            GameScreen.xguy7IsAlive = false;
        }

        if(GameScreen.xguy1IsAlive == false && GameScreen.xguy2IsAlive == false 
                && GameScreen.xguy3IsAlive == false)
        {
            GameScreen.allEnemiesAreDead = true;
        }

//      if(r.intersect(GameScreen.saucer.sRect))
//      {
//          visible = false;
//          GameScreen.score += 100;
//          GameScreen.saucerIsAlive = false;
//          System.out.println("you hit the alien!");
//      }

    }


    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int getSpeedY() {
        return speedY;
    }

    public boolean isVisible() {
        return visible;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    public void setSpeedY(int speedY) {
        this.speedY = speedY;
    }

    public void setVisible(boolean visible) {
        this.visible = visible;
    }

}

我认为如果我继续这条路径并分别初始化,设置和检查28个矩形上的碰撞,那将是非常低效的。此外,创造28个射击功能将是一件麻烦事。

游戏的主要类别也有重复。句点行分隔块。以下是片段:

public static BasicEnemy xguy1, xguy2, xguy3, xguy4, xguy5, xguy6, xguy7;
...........................................................................
    static boolean xguy1IsAlive = true;
    static boolean xguy2IsAlive = true;
    static boolean xguy3IsAlive = true;
    static boolean xguy4IsAlive = true;
    static boolean xguy5IsAlive = true;
    static boolean xguy6IsAlive = true;
    static boolean xguy7IsAlive = true;
...........................................................................
            xguy1 = new BasicEnemy(420, 100);
        xguy2 = new BasicEnemy(480, 100);
        xguy3 = new BasicEnemy(360, 100);
        xguy4 = new BasicEnemy(300, 100);
        xguy5 = new BasicEnemy(240, 100);
        xguy6 = new BasicEnemy(540, 100);
        xguy7 = new BasicEnemy(600, 100);
...........................................................................
            xguy1.update();
        xguy2.update();
        xguy3.update();
        xguy4.update();
        xguy5.update();
        xguy6.update();
        xguy7.update();
..........................................................................
if(xguy1IsAlive == true)
        {
            g.drawImage(xanim.getImage(), xguy1.getCenterX()-16, xguy1.getCenterY()-12);
        }
        if(xguy2IsAlive == true)
        {
            g.drawImage(xanim.getImage(), xguy2.getCenterX()-16, xguy2.getCenterY()-12);
        }
        if(xguy3IsAlive == true)
        {
            g.drawImage(xanim.getImage(), xguy3.getCenterX()-16, xguy3.getCenterY()-12);
        }
        if(xguy4IsAlive == true)
        {
            g.drawImage(xanim.getImage(), xguy4.getCenterX()-16, xguy4.getCenterY()-12);
        }
        if(xguy5IsAlive == true)
        {
            g.drawImage(xanim.getImage(), xguy5.getCenterX()-16, xguy5.getCenterY()-12);
        }
        if(xguy6IsAlive == true)
        {
            g.drawImage(xanim.getImage(), xguy6.getCenterX()-16, xguy6.getCenterY()-12);
        }
        if(xguy7IsAlive == true)
        {
            g.drawImage(xanim.getImage(), xguy7.getCenterX()-16, xguy7.getCenterY()-12);
        }
...............................................................................
            xguy1 = null;
        xguy2 = null;
        xguy3 = null;
        xguy4 = null;
        xguy5 = null;
        xguy6 = null;
        xguy7 = null;

尝试使用阵列减少重复(主要类别中的所有代码): (对于第三种类型的敌人)

//make an array of 9 'piguys' that take act as basic enemies
public static BasicEnemy piguys[] = new BasicEnemy[8];

//create and set the positions of each piguy
for(int i = 0; i < 9; i ++)
        {
            piguys[num] = new BasicEnemy(180 + num*55, 200);
            num +=1;
        }
//short way of calling the update method in the Enemy class for each piguy 
for(int x = 0; x < 9; x++)
        {
            piguys[num2].update();
            num2 +=1;
        }
//short way to draw each piguy to the screen 
for(int f = 0; f < 9; f++)
        {
            g.drawImage(panim.getImage(), piguys[num3].getCenterX()-12, piguys[num3].getCenterY()-12);
            num3 += 1; 
        }

            //short way of nullifying each object so they can be created in the constructor
        for(int s = 0; s < 9; s++)
        {
            piguys[num3] = null;
            num +=1;
        }

3 个答案:

答案 0 :(得分:3)

你想的没错。您需要使用某种集合对象(如数组或列表)来减少代码中的重复,我赞赏您寻求此code smell的解决方案。最后,当您继续开发游戏时,您可能想要更好的方法来配置您现在在代码中执行的操作。一种常见的解决方案是创建一个文件格式,其中包含每个敌人所需的所有配置(同样的文件通常也包含“map”数据)。您的代码将能够读取文件并从配置中创建敌人(然后将它们放在之前的集合中)。在创建原型时做你正在做的事情并不是一个坏主意,但在决定制作生产代码之前,你需要重构一堆。

答案 1 :(得分:1)

每当你有像

这样的代码时
xguy1IsAlive...
xguy2IsAlive...

使用数组或List。即使只有2个。

所有与“人”相关的代码也应该在一个单独的类中。

public  static Rect xguy1Rect = new Rect(0,0,0,0);
private boolean xguy1IsShooting = false;
static boolean xguy1IsAlive = true;

所有这些属性属于一个人,但它目前遍布各处。现在有多个地方负责跟踪一个人的状态。如果每个人都跟踪它自己的状态,那就更好了。

一些代码,用于演示如何运作:

class Projectile {
    // position, direction, speed, ...
    public void updatePosition() { /* based on speed & direction */ }
}

class Enemy {
    public boolean isHitBy(Projectile p) { /* check internal Rect if hit */ }
    public void updateHealthAfterHitOf(Projectile p) { /* decrease health */ }
    public boolean isAlive() { /* health > 0 ? */ }
    public boolean wantsToShoot() { /* maybe random */}
    public Projectile shootProjectile() { /* create a projectile at current position */ }
    public void updatePosition() { /* move in some direction */ }
    // move etc...
}

// the game, update() is called on each step
class Game {
    private static final int INITIAL_ENEMIES = 6;

    private final List<Enemy> enemies = new ArrayList<Enemy>(INITIAL_ENEMIES);
    private final List<Projectile> projectiles = new ArrayList<Projectile>();

    public Game() {
        for (int i = 0; i < INITIAL_ENEMIES; i++)
            enemies.add(new Enemy());
    }

    public void update() {

        // handling classes on a very generic / abstract level here.
        // I don't want to know if there is a Rect in enemy or something else
        // -> not my responsibility here.

        // first move all projectiles
        for (Projectile p : projectiles) {
            p.updatePosition();
        }

        // update all enemies
        for (Iterator<Enemy> enemyIterator = enemies.iterator(); enemyIterator.hasNext();) {
            Enemy enemy = enemyIterator.next();

            // 1. check if hit, if so remove projectile
            for (Iterator<Projectile> projectilesIterator = projectiles.iterator(); projectilesIterator.hasNext();) {
                Projectile p = projectilesIterator.next();
                if (enemy.isHitBy(p)) {
                    enemy.updateHealthAfterHitOf(p);
                    // remove projectile from list
                    projectilesIterator.remove();
                }
            }

            // check if enemy survived, if not remove enemy, if yes it may shoot & move
            if (enemy.isAlive()) {
                enemy.updatePosition();
                if (enemy.wantsToShoot()) {
                    Projectile projectile = enemy.shootProjectile();
                    projectiles.add(projectile);
                }
            } else {
                enemyIterator.remove();
            }
        }

    }
}

设计好的面向对象的代码需要很多经验,而背后的相当抽象的原则很难理解(SOLID),但是你会得到更多的代码。< / p>


一个错误

//make an array of 9 'piguys' that take act as basic enemies
public static BasicEnemy piguys[] = new BasicEnemy[8];

这会创建一个只有8而不是9的数组。数组的位置比你访问元素时的数字少1:piguy[0]piguy[7]因为那里有8是一个0元素。

要使下面的代码安全,请不要对其中的数字进行硬编码

for (int i = 0; i < 9; i++)
{
    piguys[num] = new BasicEnemy(180 + num * 55, 200);
    num += 1;
}

而是使用该数组的实际长度

    for (int i = 0; i < piguys.length; i++)
    {
        piguys[i] = new BasicEnemy(180 + i * 55, 200);
    }

这也允许您更改数组大小而无需更改代码中的硬编码数字。访问数组时,请使用循环中使用的索引变量(此处为i)。如果num具有除i之外的其他值

    for (int i = 0; i < piguys.length; i++)
    {
        piguys[i] = new BasicEnemy(180 + num * 55, 200);
        num += 1;
    }

但继续使用piguys[i]

使用其他变量名称,例如

for(int i...) {
}
for(int j...) {
}
for(int k...) {
}

是不必要的,可能导致混淆。每个循环使用i都可以。除非它们像

那样嵌套
for(int i...) {
    for(int j...) {
    }
}

最后一件事:如果你只是读取你为循环(for-each循环)语法增强的数组值,因为它看起来更清晰,你不能用索引变量或长度犯错:

for (int i = 0; i < piguys.length; i++)
{
    piguys[i].update();
}

相同
for (BasicEnemy enemy : piguys)
{
    enemy.update();
}

答案 2 :(得分:0)

是的,重复代码通常被忽视,除非它导致显着的性能提升。

我强烈建议将它们全部添加到数组或数组列表中。然后,您可以使用for循环遍历此数组并执行碰撞,更新和绘制对象。

由于存在大量特定数字,因此将数据添加到数组可能仍然有点混乱。

如果你知道要使用多少项,我推荐一个数组。否则,ArrayList非常方便!我建议使用链接列表上的数组,因为你将根据波数计算访问(我假设)。

另外,您应该真正考虑创建hierarchy of objects。在我的第一个射手中,我发现这是了解这个主题的一种非常有用的方法。例如,您的基础对象是具有x,y,更新方法和绘制方法的实体。然后添加扩展先前对象的其他对象。从实体中你可以得到一个专门的外星人并添加一些信息。从那里你可以创建不同的特殊类型的外星人具有不同的运动模式。

祝你好运,并始终努力使用您的代码尽可能高效:)。