0

我一直在为 Android 编写像游戏这样的太空入侵者,我有点担心我的代码中有多少重复(将会出现)。在游戏中将会有 EDIT: 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;
        }
4

3 回答 3

3

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

于 2013-07-29T18:23:42.037 回答
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),但编写的代码越多,您就会掌握它。


一个错误

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

这会创建一个只有 8 个而不是 9 个的数组。数组比您认为的数字少 1 的地方是当您访问元素时:因为有一个 0 元素,所以它是 8 piguy[0]piguy[7]

为了使下面的代码安全,不要硬编码那里的数字

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 循环(for-each 循环)语法,因为它看起来更清晰,并且您不会在索引变量或长度上出错:

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

是相同的

for (BasicEnemy enemy : piguys)
{
    enemy.update();
}
于 2013-07-29T20:26:19.880 回答
0

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

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

由于有很多特定的数字,将项目添加到数组中可能仍然有点混乱。

如果您知道要使用多少个项目,我推荐一个数组。否则 ArrayList 非常方便!我推荐数组而不是链表,因为您将根据波数进行访问(我假设)。

此外,您应该真正考虑创建对象的层次结构。在我的第一个射击游戏中,我发现这是了解这个主题的一种非常有用的方式。例如,您的基础对象是具有 x、ay、update 方法和 draw 方法的实体。然后添加扩展先前对象的附加对象。从实体你可以得到一个专门化的外星人并添加一点信息。从那里您可以创建具有不同运动模式的不同专业类型的外星人。

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

于 2013-07-29T18:28:17.990 回答