Java實現(xiàn)warcraft?java版游戲的示例代碼
前言
致敬經(jīng)典的warcraft,《warcraft java版》是一款即時戰(zhàn)略題材單機游戲,采用魔獸原味風格和機制。收集資源,建造防御工事,消滅所有敵軍。
人類:洛丹倫人類聯(lián)盟自獸人首次穿過黑暗之門時便告成立。他們堅韌不拔,勇敢無畏,身穿堅甲,手握利刃,英勇迎敵。
獸人:獸人是一個粗獷而堅韌的種族,他們身穿簡單的皮毛和帶有尖刺的皮甲,以肆意兇狠的戰(zhàn)斗風格而聞名。
用java語言實現(xiàn),采用了swing技術(shù)進行了界面化處理,設(shè)計思路用了面向?qū)ο笏枷搿?/p>
主要需求
1、玩家可以自己選擇陣營:人類(Human)和獸人(Orc)兩個陣營可以挑。
2、主要資源:
黃金:黃金在Warcraft 中是主要的資源。黃金被用來建造新的建筑物,訓練單位和研究升級。黃金在中立的建筑物也被用來購買雇傭兵,英雄物品,或啟用特殊的服務。
木材:木材和黃金類似,也是主要活動的消耗資源之一。所有種族都使用木材生產(chǎn)戰(zhàn)爭所需的許多不同的結(jié)構(gòu)的武器和機器。
3、建筑系統(tǒng):
不同建筑的建造成本、時間和目的各不相同。城鎮(zhèn)大廳可以訓練工人和存放資源,有些建筑可以訓練戰(zhàn)斗單位,還有的可以讓玩家完成科技升級或解鎖不同類型的單位。
4、操作系統(tǒng):
擇和移動:使用鼠標左鍵點擊一個單位或建筑,就可以查看相應的狀態(tài)以及可以下達的指令。選擇單位之后,玩家可以通過點擊鼠標右鍵下達移動指令,或者點擊界面底部指令面板上的按鈕(或按下相應的快捷鍵)來指揮該單位。
按住鼠標左鍵并拖拽即可拉出一個矩形的方框,玩家可以通過這種方式選擇多個單位,這也被稱之為“框選”。選擇多個單位之后,玩家可以一次性向所有選中的單位下達指令。玩家還可以按下Tab鍵來循環(huán)切換查看各個單位的指令面板。
編隊:選擇多個單位或建筑后,玩家可以按下Ctrl+任意數(shù)字鍵,以此將選中的單位編為一隊。編隊之后,玩家只需要按下該數(shù)字鍵就可以再次選中相應的編隊。
功能截圖
啟動游戲:
配置:
啟動游戲界面:
游戲主界面:
開始一個新游戲:
開局一個人,開啟種田模式
森林和金礦:
建設(shè)防御工事:
屬性:
代碼實現(xiàn)
啟動入口
public class Main { public static final String PROGRAM = "Warcraft java版"; public static final String VERSION = "1.0.0"; private static final long serialVersionUID = 1L; private Main() { } public static Initializer initialize(boolean jar) { if (jar) { Media.loadFromJar(Main.class); } Engine.start(PROGRAM, VERSION, "ressources", true, Theme.SYSTEM); return ENGINE.createInitializer(320, 200, 32, 60); } public static void main(String[] args) { boolean jar = false; try { jar = Boolean.parseBoolean(args[0]); } catch (Exception e) { } Launcher launcher = new Launcher(null, initialize(jar)); launcher.start(); } }
ModelAttacker類
/** * Main abstraction representing any unit with the ability of attacking. */ public abstract class ModelAttacker extends ModelUnit implements AttackerAbility<Tile, ModelSkill, Attributes> { private final List<AbstractEntry<Tile, ModelSkill, Attributes>> guards; private int orderX, orderY; private boolean assault, riposte; private boolean defend; private long guardTimer; public ModelAttacker(Map map, RessourcesHandler rsch, MediaRessource<BufferedImage> rsc) { super(map, rsch, rsc.file, rsc.ressource); this.guards = new ArrayList<AbstractEntry<Tile, ModelSkill, Attributes>>(1); this.damages.setMin(this.getDataInt("DMG_MIN")); this.damages.setMax(this.getDataInt("DMG_MAX")); this.riposte = true; this.assault = false; this.defend = false; this.orderX = -1; this.orderY = -1; } public void setDamages(int min, int max) { this.damages.setMin(min); this.damages.setMax(max); } @Override public void update(Keyboard keyboard, Mouse mouse, float extrp) { super.update(keyboard, mouse, extrp); this.updateAttack(extrp); if (this.isAlive() && (!this.hasTarget() || this.isMoving())) { // Reset defense state when its over if (this.isDefending() && !this.isAttacking()) { this.defend = false; this.assault = false; } // Check guard area when not defending & attacking or assault if (!this.isDefending() && (this.assault || (!this.isAttacking() && !this.isMoving()))) { if (Maths.time() - this.guardTimer > 500) { if (this.player instanceof AI) { this.guard(); } else { if (!this.isMoving()) { this.guard(); } } this.guardTimer = Maths.time(); } } } } @Override public boolean assignDestination(int tx, int ty) { boolean found = super.assignDestination(tx, ty); if (this.orderX == -1 && this.assault) { this.orderX = tx; this.orderY = ty; } return found; } public void reAssignDestination() { if (this.orderX != -1 && this.orderY != -1) { this.stopAttack(); this.setTarget(null); super.assignDestination(this.orderX, this.orderY); } else { this.stopAttack(); this.stopMoves(); } } @Override public void stop() { this.stopAttack(); super.stop(); } protected void guard() { int fov = this.getFieldOfView() - 1; for (int v = this.getYInTile() - fov; v <= this.getYInTile() + fov; v++) { for (int h = this.getXInTile() - fov; h <= this.getXInTile() + fov; h++) { try { int eid = this.map.getRef(v, h); if (eid > 0 && eid != this.id) { AbstractEntry<Tile, ModelSkill, Attributes> e = ModelUnit.get(eid); if (e == null) { e = ModelBuilding.get(eid); } if (e.isAlive() && e.isVisible() && e.getOwnerID() != this.getOwnerID() && e.getOwnerID() > 0 && e.isActive()) { this.guards.add(e); } } } catch (ArrayIndexOutOfBoundsException e) { continue; } } } int min = Integer.MAX_VALUE; AbstractEntry<Tile, ModelSkill, Attributes> closest = null; for (AbstractEntry<Tile, ModelSkill, Attributes> e : this.guards) { int dist = this.getDistance(e); // Priority to unit if (closest instanceof AbstractBuilding && e instanceof AbstractUnit) { min = dist; closest = e; } else if (!(closest instanceof AbstractUnit && e instanceof AbstractBuilding) || closest == null) { if (dist < min) { min = dist; closest = e; } } } this.guards.clear(); if (closest != null) { this.guardAction(closest); } } protected void guardAction(AbstractEntry<Tile, ModelSkill, Attributes> e) { // Priority to attacker model if (this.getTarget() instanceof ModelAttacker && !(e instanceof ModelAttacker)) { return; } this.attack(e); } @Override public void onHit(AbstractEntry<Tile, ModelSkill, Attributes> attacker) { super.onHit(attacker); if (this.isAlive() && this.riposte) { // AI gives priority to unit riposte if (attacker instanceof AbstractUnit && this.getTarget() instanceof AbstractBuilding && this.player instanceof AI) { this.attack(attacker); return; } // Keep closest target only boolean closest = false; if (this.hasTarget()) { closest = this.getDistance(attacker) < this.getDistance(this.getTarget()); } if ((this.hasTarget() || closest) && this.getOwnerID() != attacker.getOwnerID()) { this.attack(attacker); } } } @Override public void onKilled(AbstractEntry<Tile, ModelSkill, Attributes> attacker) { if (this.assault) { this.reAssignDestination(); } } public void setRiposte(boolean state) { this.riposte = state; } public void setAssault(boolean state) { this.assault = state; } public boolean getAssault() { return this.assault; } public void setDefend(boolean state) { this.defend = state; } public boolean isDefending() { return this.defend; } @Override public boolean isPassive() { return super.isPassive() && !this.isAttacking(); } public boolean hasTarget() { return this.getTarget() != null; } }
ModelUnit類
public abstract class ModelUnit extends AbstractUnit<Tile, ModelSkill, Attributes> { private static final TreeMap<Integer, ModelUnit> ENTRYS = new TreeMap<Integer, ModelUnit>(); public static ModelUnit get(int id) { return ENTRYS.get(id); } public static void clear() { ENTRYS.clear(); } public static List<ModelUnit> getByOwner(int ownerID) { List<ModelUnit> list = new ArrayList<ModelUnit>(1); Collection<ModelUnit> c = ENTRYS.values(); for (ModelUnit u : c) { if (u.getOwnerID() == ownerID) { list.add(u); } } return list; } private void manage() { ENTRYS.put(this.id, this); } private static final int CORPSE_TIME = 5000; private static final int CORPSE_NUMBER = 3; private static final int CORPSE_OFFSET = 8; private static final Orientation[] orientations = Orientation.values(); public final Map map; public final UnitType type; public final Race faction; protected Player player; private boolean isOnScreen; private TiledSprite corpse; private long deadTimer, angleTimer, nextAngleTimer; private boolean dead; private int deadIndex, deadOffset; public ModelUnit(Map map, RessourcesHandler rsch, String data, BufferedImage surface) { super(data, map, surface, new Attributes()); this.map = map; this.type = UnitType.valueOf(this.getDataString("TYPE").toUpperCase()); this.setFieldOfView(this.getDataInt("FOV")); this.setFrame(this.getDataInt("DEFAULT_FRAME")); this.setSkipLastFrameOnReverse(true); this.faction = Race.valueOf(this.getDataString("FACTION").toLowerCase()); this.life.setMax(this.getDataInt("MAX_LIFE")); this.life.set(this.life.getMax()); this.addSkill(new Move(0, this)); this.addSkill(new Stop(1, this)); this.setSpeed(1.5f, 1.5f); this.setLayer(2); this.corpse = Drawable.DRAWABLE.loadTiledSprite(rsch.get("CORPSE").ressource, 32, 32); this.corpse.load(false); this.deadTimer = -1L; this.dead = false; this.deadIndex = 0; if (this.faction == Race.orcs) { this.deadOffset = 8; } else { this.deadOffset = 0; } this.map.fogOfWar.updateEntryFOV(this); this.angleTimer = Maths.time(); this.nextAngleTimer = Maths.random(0, 2000) + 5000L; this.manage(); } @Override public void place(int tx, int ty) { super.place(tx, ty); this.map.fogOfWar.updateEntryFOV(this); } @Override public void update(Keyboard keyboard, Mouse mouse, float extrp) { int otx = this.getXInTile(); int oty = this.getYInTile(); super.update(keyboard, mouse, extrp); // Apply mirror depending of the orientation Orientation o = this.getOrientation(); if (o.ordinal() > 4) { if (!this.getMirror()) { this.mirror(true); } } else { if (this.getMirror()) { this.mirror(false); } } if (!this.isAlive()) { // Handle dead corps effect if (!this.dead) { if (this.deadTimer == -1L) { this.deadTimer = Maths.time(); } if (Maths.time() - this.deadTimer > CORPSE_TIME) { this.setVisibility(false); this.dead = true; this.deadIndex = 0; this.deadTimer = Maths.time(); } } else { if (this.deadIndex <= CORPSE_NUMBER && Maths.time() - this.deadTimer > CORPSE_TIME) { this.deadIndex++; this.deadTimer = Maths.time(); } } if (this.deadIndex > CORPSE_NUMBER) { this.remove(); } } else { // Update fog when unit moved if (otx != this.getXInTile() || oty != this.getYInTile()) { this.map.fogOfWar.updateEntryFOV(this); } // Apply a random angle unit entry is still idle too much time if (this.isPassive() && Maths.time() - this.angleTimer > this.nextAngleTimer) { this.setAnimation("IDLE"); this.setOrientation(orientations[Maths.random(0, orientations.length - 1)]); this.angleTimer = Maths.time(); this.nextAngleTimer = Maths.random(0, 2000) + 5000L; } } if (this.animName != null) { CollisionArea area = this.getCollArea(this.animName); this.updateCollision(area.getX(), area.getY(), area.getWidth(), area.getHeight()); } else { this.updateCollision(16, 16, 0, 0); } } @Override public void render(Graphics2D g, Camera camera) { super.render(g, camera); if (this.dead && this.deadIndex <= CORPSE_NUMBER) { int o = 0; if (this.getOrientation().ordinal() > 0) { o = 4; } this.corpse.render(g, this.deadIndex + this.deadOffset + o, this.getX() - camera.getX() - CORPSE_OFFSET, this.getY() - camera.getY() - CORPSE_OFFSET); } if (this.getX() >= camera.getX() && this.getX() <= camera.getX() + 320 && this.getY() >= camera.getY() && this.getY() <= camera.getY() + 200) { this.isOnScreen = true; } else { this.isOnScreen = false; } } @Override public void setOwnerID(int id) { super.setOwnerID(id); if (id > 0) { this.player = (Player) AbstractPlayer.get(id); } } public Player player() { return this.player; } @Override public void stop() { super.stop(); this.clearIgnoredID(); this.angleTimer = Maths.time(); } @Override public void onStartMove() { this.setAnimation("MOVE"); } @Override public void onMove() { if (!this.animName.equals("MOVE")) { this.setAnimation("MOVE"); } } @Override public void onArrived() { this.setAnimation("IDLE"); this.angleTimer = Maths.time(); } @Override public void onDied() { if (this.getOrientation().ordinal() < 4) { this.setOrientation(Orientation.NORTH); } else { this.setOrientation(Orientation.NORTH_EAST); } this.setAnimation("DIE"); this.player.removeUnit(this); if (this.isOnScreen()) { ControlPanel.playSfx(0, this.faction, SFX.die); } } @Override public void onSelection() { ControlPanel.playSfx(this.getOwnerID(), this.faction, SFX.select); } @Override public void onOrderedFail(ModelSkill skill) { } @Override public void onKilled(AbstractEntry<Tile, ModelSkill, Attributes> attacker) { } public boolean isPassive() { return !this.isMoving(); } public boolean isOnScreen() { return this.isOnScreen; } }
總結(jié)
通過此次的《warcraft java版》游戲?qū)崿F(xiàn),讓我對swing的相關(guān)知識有了進一步的了解,對java這門語言也有了比以前更深刻的認識。
java的一些基本語法,比如數(shù)據(jù)類型、運算符、程序流程控制和數(shù)組等,理解更加透徹。java最核心的核心就是面向?qū)ο笏枷?,對于這一個概念,終于悟到了一些。
以上就是Java實現(xiàn)warcraft java版游戲的示例代碼的詳細內(nèi)容,更多關(guān)于Java warcraft游戲的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot使用Spring?Cache高效處理緩存數(shù)據(jù)
Spring?Cache是一個框架,只要簡單加一個注解,就能實現(xiàn)緩存功能,本文主要介紹了SpringBoot使用Spring?Cache高效處理緩存數(shù)據(jù),感興趣的可以了解一下2023-11-11解決Mybatis中mapper.xml文件update,delete及insert返回值問題
這篇文章主要介紹了解決Mybatis中mapper.xml文件update,delete及insert返回值問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11IDEA提示內(nèi)存不足 low memory的完美解決方法(親測好用)
這篇文章主要介紹了IDEA提示內(nèi)存不足 low memory的完美解決方法(親測好用),這里以IDEA2022版本為例,在IDE中 幫助(help)–>change memory setting(改變內(nèi)存設(shè)置),具體設(shè)置辦法文中給大家詳細講解,需要的朋友可以參考下2023-01-01后端報TypeError:Cannot?read?properties?of?null?(reading?‘
這篇文章主要給大家介紹了關(guān)于后端報TypeError:Cannot?read?properties?of?null?(reading?‘xxx‘)錯誤的解決辦法,這個錯誤是開發(fā)中常見的錯誤之一,需要的朋友可以參考下2023-05-05Hibernate連接三種數(shù)據(jù)庫的配置文件
今天小編就為大家分享一篇關(guān)于Hibernate連接三種數(shù)據(jù)庫的配置文件,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03