OOP h06
某一款游戏,其主要角色如下:
游戏中每个对象有当前 x
,y
坐标,坐标值取值范围为整数
非建筑物可以通过 move(dx,dy)
来移动坐标值,dx
,dy
表示 x
轴,y
轴增量,取值范围为整数
对象 A 攻击 B 的时候,要满足两个对象之间直线距离小于等于 A 的攻击范围,否则攻击无效(被攻击方不减健康值)
任何对象有 getHealth()
方法,返回当前生命值,如果已经死亡则返回 <=0
的一个数字
任何对象有 isDestroyed()
方法,如果生命值 <=0
则 true
,否则 false
类 |
初始生命值 |
攻击力 |
攻击范围 |
初始坐标值 |
GameBase 玩家基地 |
500 |
- |
- |
创建时指定 |
WarFactroy 兵工厂 |
100 |
- |
- |
创建时指定 |
Barrack 兵营 |
100 |
- |
- |
创建时指定 |
HeavyTank 重型坦克 |
200 |
20 |
10 |
兵工厂 |
MediumTank 轻型坦克 |
100 |
10 |
10 |
兵工厂 |
RifleSoldier 步枪兵 |
50 |
5 |
5 |
兵营 |
RPGSoldier 火箭兵 |
50 |
10 |
10 |
兵营 |
Dog 军犬 |
50 |
5 |
5 |
兵营 |
其中
Barrack
兵营可以训练出步枪兵、RPG 兵、军犬
RifleSoldier
步枪兵对战军犬可以一次击毙军犬
Dog
军犬对战人类时候一口毙命
此外还要能通过 Soldier.getLivingSoldierCount
/getDeadedSoldierCount
统计现在活着的和死去的士兵数量
请遵循以上游戏规则,并根据如下测试代码(见文末)设计代码
作业批改的时候,方法可能不同组合
例如让一个物体移动,超出或者进入攻击范围,判断是否攻击有效
让一个物体制造出来以后,不移动,然后另一个物体攻击,判断该物体是否使用了默认的 x
,y
未来可能分玩家,例如玩家 A,玩家 B,玩家 C
未来可能玩家 A 战斗单元不能攻击自己的战斗单元
未来玩家之间可能要结盟、取消结盟
未来坦克攻击以后,造成的杀伤可能不是点杀伤,而是一个杀伤范围
……
0x00 分析
和 h05 相比,h06 增加的特性主要为角色的坐标和攻击范围。这两个属性和生命值、攻击力一样,需要添加到基本类中,后称为 GameObject
。需要改动的是 attack
方法,攻击前需要判断二者距离,并和攻击者的攻击范围作比较。
0x01 EnumObjectType
&Param
根据 Test
的调用,EnumObjectType
需要添加 barrack
和 warFactory
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package com.huawei.classroom.student.h06;
public enum EnumObjectType { heavyTank, mediumTank, rifleSoldier, RPGSoldier, dog, barrack, warFactory }
|
可以新建 Param
类,存放所有变量的值,便于管理和更改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| package com.huawei.classroom.student.h06;
public class Param {
public static final int BASE_HEALTH=500; public static final int BASE_STRENGTH=0; public static final double BASE_ATTACKRANGE=0;
public static final int DOG_HEALTH=50; public static final int DOG_STRENGTH=5; public static final double DOG_ATTACKRANGE=5.0;
public static final int SOLDIER_HEALTH=50; public static final int SOLDIER_RPG_STRENGTH=10; public static final double SOLDIER_RPG_ATTACKRANGE=10.0; public static final int SOLDIER_RIFLE_STRENGTH=5; public static final double SOLDIER_RIFLE_ATACKRANGE=5.0;
public static final int TANK_HEAVY_HEALTH=200; public static final int TANK_HEAVY_STRENGTH=20; public static final double TANK_ATTACKRANGE=10.0;
public static final int TANK_MEDIUM_HEALTH=100; public static final int TANK_MEDIUM_STRENGTH=10;
public static final int WAR_FACTORY_HEALTH=100; public static final int WAR_FACTORY_STRENGTH=0; public static final double WAR_FACTORY_ATTACKRANGE=0;
public static final int BARRACK_HEALTH=100; public static final int BARRACK_STRENGTH=0; public static final double BARRACK_ATTACKRANGE=0;
}
|
0x02 GameObject
此类的作用相当于 h05 中提到的 Base
类,但做了改进。
属性
根据分析,需要增加新的属性,包括攻击范围和坐标值。
所有的属性均为 private
,修改操作只在此类进行。
1 2 3 4
| private int lifeValue = 0; private final int attackPower; private final double attackRange; private int x, y;
|
构造方法
构造方法传五个参数,对应进行赋值。
1 2 3 4 5 6 7
| public GameObject(int lv, int ap, double ar, int x, int y) { this.lifeValue = lv; this.attackPower = ap; this.attackRange = ar; this.x = x; this.y = y; }
|
Setter
包括 changeHealth
,传入参数为某攻击力,当对象生命值为正(活着)时才可以更改其生命值,为减去攻击力后的值:
1 2 3 4 5 6 7 8 9
| public void changeHealth(int strength) { if (this.isDestroyed()) { return; } this.lifeValue -= strength; if (this.lifeValue <= 0) { this.dead(); } }
|
生命值更改后,需要判断其是否已经死亡,若死亡则调用 dead
方法。
dead
方法在此类中为空,供子类重写,便于一些角色在死亡后需要进一步操作,如士兵。
按照以前的思路,士兵死亡需要额外记录数量,自然想到对 changeHealth
方法直接重写。但私有属性不应在子类中修改,所以改进了重写的方法,即另写 dead
。
Getter
getAttackPower
Dog
和 rifleSoldier
都有特殊的攻击效果,以前是在它们的类中重写了 attack
,本次按照上述分析做出了改进。
特殊的攻击是因为它们攻击特定角色时可以导致其直接死亡,可以想象为它们遇到特定角色时攻击力增强。所以此类中新建此方法,默认返回 -1
,Dog
和 rifleSoldier
重写为更大的值,具体用法见下文 attack
。
1 2 3
| public int getAttackPower(GameObject obj) { return -1; }
|
其他
包括 getHealth
、getPosX
、getPosY
和 getDistance
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public int getHealth() { return lifeValue; }
public int getPosX() { return this.x; }
public int getPosY() { return this.y; }
public double getDistance(GameObject obj) { return Math.sqrt(Math.pow(this.x - obj.x, 2) + Math.pow(this.y - obj.y, 2)); }
|
move
移动角色,即更改其坐标:
1 2 3 4
| public void move(int dx, int dy) { this.x += dx; this.y += dy; }
|
attack
被攻击者为参数,首先判断其是否死亡,死亡的角色不能再次被攻击。
然后获取其 getAttackPower
,若小于零(默认为 -1
)则正常,否则进一步判断攻击距离。
满足攻击距离在攻击范围内,则 changeHealth
改变的就是 getAttackPower
值。
1 2 3 4 5 6 7 8 9 10 11 12
| public void attack(GameObject obj) { if (obj.isDestroyed()) { return; } int ap = getAttackPower(obj); if (ap < 0) { ap = this.attackPower; } if (this.attackRange >= this.getDistance(obj)) { obj.changeHealth(ap); } }
|
isDestroyed
是否死亡:
1 2 3
| public boolean isDestroyed() { return lifeValue <= 0; }
|
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| package com.huawei.classroom.student.h06;
public abstract class GameObject {
private int lifeValue = 0; private final int attackPower; private final double attackRange; private int x, y;
public GameObject(int lv, int ap, double ar, int x, int y) { this.lifeValue = lv; this.attackPower = ap; this.attackRange = ar; this.x = x; this.y = y; }
public void changeHealth(int strength) { if (this.isDestroyed()) { return; } this.lifeValue -= strength; if (this.lifeValue <= 0) { this.dead(); } }
public void dead() {
}
public int getHealth() { return lifeValue; }
public int getPosX() { return this.x; }
public int getPosY() { return this.y; }
public double getDistance(GameObject obj) { return Math.sqrt(Math.pow(this.x - obj.x, 2) + Math.pow(this.y - obj.y, 2)); }
public void move(int dx, int dy) { this.x += dx; this.y += dy; }
public void attack(GameObject obj) { if (obj.isDestroyed()) { return; } int ap = getAttackPower(obj); if (ap < 0) { ap = this.attackPower; } if (this.attackRange >= this.getDistance(obj)) { obj.changeHealth(ap); } }
public boolean isDestroyed() { return lifeValue <= 0; }
public int getAttackPower(GameObject obj) { return -1; } }
|
0x03 GameBase
继承 GameObject
。
构造方法需要传参坐标,其中需要调用 GameObject
的五参数构造方法。
通过 Test
知需要静态方法 createGameBase
来创建一个游戏基地。
游戏基地可以建造建筑,参数为要建造的建筑类型和坐标。
对应建筑的创建均需要传参坐标。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.huawei.classroom.student.h06;
public class GameBase extends GameObject{ public GameBase(int x, int y) { super(Param.BASE_HEALTH, Param.BASE_STRENGTH, Param.BASE_ATTACKRANGE, x, y); } public static GameBase createGameBase(int x, int y) { return new GameBase(x, y); } public Building building(EnumObjectType enumObjectType, int x, int y) { if (enumObjectType == EnumObjectType.barrack) { return new Barrack(x, y); } else if (enumObjectType == EnumObjectType.warFactory) { return new WarFactory(x, y); } else { return null; } } }
|
0x04 Building
建筑类,继承 GameObject
,不被创建,子类包括兵营和军工厂。但兵营和军工厂的创建不通过 Building
而是通过 GameBase
。
建筑不可移动,故重写 move
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package com.huawei.classroom.student.h06;
public class Building extends GameObject {
public Building(int health, int strength, double attackRange, int x, int y) { super(health, strength, attackRange, x, y); }
@Override public void move(int dx, int dy) { }
}
|
0x05 Barrack
&WarFactory
兵营和军工厂,继承 Building
,由 GameBase
创建。构造方法传参坐标(创建时),调用父类构造方法(继承)。
兵营可训练出步枪兵、火箭兵和军犬,它们的坐标就是兵营的坐标(传参):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.huawei.classroom.student.h06;
public class Barrack extends Building {
public Barrack(int x, int y) { super( Param.BARRACK_LV,Param.BARRACK_AP, Param.BARRACK_AR, x, y); }
public GameObject traing(EnumObjectType type) { if (type == EnumObjectType.rifleSoldier) { return new RifleSoldier(this.getPosX(), this.getPosY()); }else if (type == EnumObjectType.RPGSoldier) { return new RPGSoldier(this.getPosX(), this.getPosY()); }else if (type == EnumObjectType.dog) { return new Dog(this.getPosX(), this.getPosY()); } return null; }
}
|
军工厂可建造重坦和轻坦,它们的坐标就是军工厂的坐标(传参):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.huawei.classroom.student.h06;
public class WarFactory extends Building {
public WarFactory(int x, int y) { super(Param.WAR_FACTORY_LV, Param.WAR_FACTORY_AP, Param.WAR_FACTORY_AR, x, y); }
public Tank building(EnumObjectType type) {
if (type == EnumObjectType.mediumTank) { return new MediumTank(this.getPosX(), this.getPosY()); } else if (type == EnumObjectType.heavyTank) { return new HeavyTank(this.getPosX(), this.getPosY()); } return null;
} }
|
0x06 Soldier
士兵类,继承 GameObject
,不被创建,子类包括步枪兵和火箭兵。但步枪兵和火箭兵的创建不通过 Soldier
而是通过 Barrack
。
最重要的就是记录生存和死亡的士兵数量,静态变量。重写 dead
以当士兵死亡时改变生存和死亡数量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package com.huawei.classroom.student.h06;
public abstract class Soldier extends GameObject {
private static int livingSoldierCount=0; private static int deadedSoldierCount=0;
public Soldier(int lv,int ap, double ar, int x, int y) { super(lv, ap, ar, x, y); livingSoldierCount++; } public static int getLivingSoldierCount() { return livingSoldierCount; } public static int getDeadedSoldierCount() { return deadedSoldierCount; }
@Override public void dead() { livingSoldierCount--; deadedSoldierCount++; }
}
|
0x07 Tank
坦克类,继承 GameObject
,不被创建,子类包括重坦和轻坦。但重坦和轻坦的创建不通过 Tank
而是通过 WarFactory
:
1 2 3 4 5 6 7 8 9 10 11 12
| package com.huawei.classroom.student.h06;
public class Tank extends GameObject {
public Tank(int lv, int ap, double ar, int x, int y) { super(lv, ap, ar, x, y); }
}
|
0x08 RifleSoldier
&RPGSoldier
&Dog
步枪兵类,继承 Soldier
,由 Barrack
创建,构造方法传参坐标(创建时),调用父类构造方法(继承)。需要重写 getAttackPower
针对 Dog
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.huawei.classroom.student.h06;
public class RifleSoldier extends Soldier {
public RifleSoldier(int x, int y) { super(Param.SOLDIER_LV, Param.SOLDIER_RIFLE_AP, Param.SOLDIER_RIFLE_AR, x, y); }
@Override public int getAttackPower(GameObject obj) { if(obj instanceof Dog) { return 1000; } return -1 ; }
}
|
火箭兵类,继承 Soldier
,由 Barrack
创建,构造方法传参坐标(创建时),调用父类构造方法(继承):
1 2 3 4 5 6 7 8 9 10 11 12
| package com.huawei.classroom.student.h06;
public class RPGSoldier extends Soldier {
public RPGSoldier(int x, int y) { super(Param.SOLDIER_LV, Param.SOLDIER_RPG_AP, Param.SOLDIER_RPG_AR, x, y); }
}
|
军犬类,继承 GameBase
,由 Barrack
创建,构造方法传参坐标(创建时),调用父类构造方法(继承)。需要重写 getAttackPower
针对 Soldier
类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.huawei.classroom.student.h06;
public class Dog extends GameObject {
public Dog(int x, int y) { super(Param.DOG_LV, Param.DOG_AP, Param.DOG_AR, x, y); }
@Override public int getAttackPower(GameObject obj) { if(obj instanceof Soldier) { return 1000; } return -1; }
}
|
0x09 HeavyTank
&MediumTank
重坦和轻坦类,继承 Tank
,由 WarFactory
创建,构造方法传参坐标(创建时),调用父类构造方法(继承):
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.huawei.classroom.student.h06;
public class HeavyTank extends Tank {
public HeavyTank(int x, int y) { super(Param.TANK_HEAVY_LV, Param.TANK_HEAVY_AP, Param.TANK_AR, x, y); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12
| package com.huawei.classroom.student.h06;
public class MediumTank extends Tank {
public MediumTank(int x, int y) { super(Param.TANK_MEDIUM_LV, Param.TANK_MEDIUM_AP, Param.TANK_AR, x, y); }
}
|
0x0A Test
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| package com.huawei.classroom.student.h06;
public class Test {
public Test() { }
public static void main(String[] args) {
GameBase gameBase=GameBase.createGameBase(10,10); if(gameBase.getHealth()==500) { System.out.println("ok1"); } Barrack barrack=(Barrack)gameBase.building(EnumObjectType.barrack, 20, 20) ; if(barrack.getHealth()==100) { System.out.println("ok2"); } RifleSoldier rifleSoldier1=(RifleSoldier)barrack.traing(EnumObjectType.rifleSoldier); if(rifleSoldier1.getHealth()==50) { System.out.println("ok3"); } RPGSoldier rPGSoldier1=(RPGSoldier)barrack.traing(EnumObjectType.RPGSoldier ); if(rPGSoldier1.getHealth()==50) { System.out.println("ok4"); } Dog dog1=(Dog)barrack.traing(EnumObjectType.dog ); if(dog1.getHealth()==50) { System.out.println("ok5"); } WarFactory warFactory=(WarFactory)gameBase.building(EnumObjectType.warFactory, 30, 30) ; if(warFactory.getHealth()==100) { System.out.println("ok6"); } Tank mediumTank1=(MediumTank)warFactory.building(EnumObjectType.mediumTank); if(mediumTank1.getHealth()==100) { System.out.println("ok7"); }
Tank heavyTank1=(HeavyTank)warFactory.building(EnumObjectType.heavyTank ); if(heavyTank1.getHealth()==200) { System.out.println("ok8"); }
heavyTank1.move(10, 10); rifleSoldier1.move(5, 5); dog1.move(0, 0); mediumTank1.attack( heavyTank1);
if(heavyTank1.getHealth()==200){ System.out.println("ok9"); }
mediumTank1.attack(rifleSoldier1); if(rifleSoldier1.getHealth()==40 ) { System.out.println("ok10"); } mediumTank1.attack(rifleSoldier1); if(rifleSoldier1.getHealth()==30 ) { System.out.println("ok12"); } if( Soldier.getLivingSoldierCount()==2 ) { System.out.println("ok11"); }
}
}
|