我一直在写一个像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;
}
答案 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,更新方法和绘制方法的实体。然后添加扩展先前对象的其他对象。从实体中你可以得到一个专门的外星人并添加一些信息。从那里你可以创建不同的特殊类型的外星人具有不同的运动模式。
祝你好运,并始终努力使用您的代码尽可能高效:)。