您的位置:首页 > 编程语言 > Java开发

JavaFX战旗游戏开发 第七课 回合逻辑(完)

2014-10-28 18:07 295 查看
  上一节课中,我们讲述了SLG中获取移动范围的算法(获取攻击范围也是同理),相对如自动寻径来说,简单不少。由于个人时间问题,这一节课将会把内容讲完,将这个系列完结,并给出示例下载地址。

  项目下载地址:JavaFX战旗类游戏开发示例

  注意:该项目为e(fx)clipse项目

  在战旗游戏开发中,最基本的回合逻辑就是敌方回合和我方回合。当然,在如今的SLG游戏中,往往是根据我方角色和敌方角色的某些数值计算(例如速度之类的),来排列角色操控的列表,而且某些技能还能中断某个角色的操作,将他往操作列表的后面移动(例如著名的SLG游戏《英雄传说》)。

  当然,在这里我们只是讲述最简单的回合逻辑。

  下面是我们需要定义的一些简单枚举:

// 游戏状态
enum Status {
NONE, SHOW_ENEMY_PROPERTY, SHOW_MENU, PREPARE_MOVE, MOVE, PREPARE_ATTACK, ATTACK, WAIT, GAME_WIN, GAME_OVER
}

enum GameTurn {
PLAYER, ENEMY
}

// 当前游戏状态
private Status nowStatus = Status.NONE;
// 当前游戏回合
private GameTurn nowTurn = GameTurn.PLAYER;


  我们在这里定义了游戏状态。游戏状态是在游戏开发中很重要的一个参数,我们需要根据不同的游戏状态,来显示不同的游戏界面,并直接影响到当前的角色能够进行哪些操作,正在进行的是哪些操作。

  我们这里的Status里包含游戏状态:显示敌方属性,显示菜单,准备移动,移动,准备攻击,攻击,等待,游戏胜利,游戏失败。

  另外,定义了一个回合枚举,分为我方回合和敌方回合。当然,有的SLG中阵营分类不止两个,例如以前的FC上的《第四次机器人大战》,里面有我方,敌方和中立阵营(游戏中的名字叫联邦,自盟和什么来着,忘了。这个游戏是外星科技山寨改版的)。

  接下来,上一课中,我们创建的定时器就要派上用场了。

moveTimer = WTimer.createWTimer(50, new WTimer.OnTimerListener() {

@Override
public void onTimerRunning(WTimer mTimer) {
if (nowStatus == Status.MOVE) {
int nowPlayerX = (int) (nowControllPlayer.getX() / tileWidth);
int nowPlayerY = (int) (nowControllPlayer.getY() / tileHeight);
if (nowPlayerX != moveToX) {
nowControllPlayer.moveX(nowPlayerX > moveToX ? -tileWidth : tileWidth);
} else {
if (nowPlayerY != moveToY) {
nowControllPlayer.moveY(nowPlayerY > moveToY ? -tileHeight : tileHeight);
} else {
nowControllPlayer.setWaitToAttack(false);
nowControllPlayer.setWaitToMove(false);
nowControllPlayer.setCanMove(false);
nowControllPlayer = null;
nowStatus = Status.NONE;
moveTimer.stop();
}
}
}
}
});

actioTimer = WTimer.createWTimer(50, new WTimer.OnTimerListener() {
@Override
public void onTimerRunning(WTimer mTimer) {
//敌方回合
if (nowTurn == GameTurn.ENEMY) {
if (nowStatus == Status.NONE) {
//当前敌方角色索引
if (nowActionIndex < enemys.size()) {
//选择操作的敌方角色
nowControllPlayer = enemys.get(nowActionIndex);
nowControllPlayer.setChoose(true);
// 没有就近角色
if (!nowControllPlayer.isHasNearBP(players)) {
//如果可以移动
if (nowControllPlayer.isCanMove()) {
//获取最近的一个角色(这里可以更改规则,比如HP最低的等等)
BasePlayer player = nowControllPlayer.getNearestBP(players);
path.clear();
// path.map_sprite = createMapSprite();
//搜索移动范围
LinkedList<WNode> nodeList = path.SearchMoveScan(
new Point2D(nowControllPlayer.getX() / tileWidth, nowControllPlayer.getY()
/ tileHeight), nowControllPlayer.getMove());
// 删选当前可移动范围内 如果有角色,则把该移动点删除
List<WNode> deleteList = new ArrayList<>();
for (WNode node : nodeList) {
if (isPointHasPlayer((int) node.getPoint().getX(), (int) node.getPoint().getY())) {
deleteList.add(node);
}
}
for (WNode node : deleteList) {
nodeList.remove(node);
}
nowControllPlayer.nodeList = nodeList;
//获取可移动范围里距离 最近角色的最近的点
Point2D point = player.getNearestNode(nodeList);
moveToX = (int) (point.getX());
moveToY = (int) (point.getY());
//状态更改为移动
nowStatus = Status.MOVE;
moveTimer.start();
} else {
//状态更改为准备攻击
nowStatus = Status.PREPARE_ATTACK;
}
} else {
//状态更改为准备攻击
nowStatus = Status.PREPARE_ATTACK;
}
} else {
//当操作角色的索引大于 敌方角色集合的大小时,回合结束 并重置所有敌方角色状态
nowStatus = Status.NONE;
nowTurn = GameTurn.PLAYER;
nowActionIndex = 0;
for (BasePlayer enemy : enemys) {
enemy.reset();
}
}
} else if (nowStatus == Status.PREPARE_ATTACK) {
// 敌人周边四个方格是否有角色
if (nowControllPlayer.isHasNearBP(players)) {
//敌人获取最近的我方角色,并攻击
BasePlayer bp = nowControllPlayer.getNearestBP(players);
nowControllPlayer.attack(bp);
nowBeAttackedPlayer = bp;
bp.setFlash(true);
nowStatus = Status.ATTACK;
} else {
//没有攻击对象,则操作下一个敌方角色
nowControllPlayer.setCanAction(false);
nowActionIndex++;
nowControllPlayer.setChoose(false);
nowStatus = Status.NONE;
}
} else if (nowStatus == Status.ATTACK) {
//角色死亡
if (!nowBeAttackedPlayer.isFlash()) {
if (nowBeAttackedPlayer.getHp() <= 0) {
players.remove(nowBeAttackedPlayer);
}
nowControllPlayer.setCanAction(false);
nowActionIndex++;
nowControllPlayer.setChoose(false);
nowStatus = Status.NONE;
//当没有我方角色时,游戏结束
if (players.size() == 0) {
nowStatus = Status.GAME_OVER;
nowTurn = GameTurn.PLAYER;
}
}
}
} else if (nowTurn == GameTurn.PLAYER) {
if (nowStatus == Status.ATTACK) {
if (!nowBeAttackedPlayer.isFlash()) {
//敌人死亡
if (nowBeAttackedPlayer.getHp() <= 0) {
nowControllPlayer.getExp(nowBeAttackedPlayer.getExp());
enemys.remove(nowBeAttackedPlayer);
}
waitToNextPlayer();
//当没有敌方角色时,游戏胜利
if (enemys.size() == 0) {
nowStatus = Status.GAME_WIN;
}
}
}
}
}
});
actioTimer.start();


  这一段代码比较的多,主要是创建了两个定时器,MoveTimer和ActionTimer。

  MoveTimer的作用是控制角色的移动(根据时间间隔一个单元格一个单元格的移动)。

  ActionTimer的作用是敌方回合或者我方回合的一些操作。

  由于我方回合主要是菜单的鼠标事件和简单的判断,这里我描述一下敌方回合的步骤。

  敌方回合

  1.当前敌方操作角色索引小于敌方角色链表大小,如果CanMove,则获取最近的我方角色(也可以根据HP等来判断),并获取敌方角色可移动的范围。

  2.从敌方角色可移动范围中,获取距离“上个步骤中得到的我方最角色”最近的一个点,并将状态改为Move,启动MoveTimer开始移动。

  3.如果!CanMove(就是已经移动过了),则将状态更改为PREPARE_ATTACK,准备攻击。

  4.如果状态为PREPARE_ATTACK,则判断敌方周围四个单元格有无可攻击对象,如果有,则获取对象并将状态更改为ATTACK并攻击,如果无,则操作下一个敌方角色。

  5.如果状态为ATTACK,则判断被攻击的对象是否死亡,死亡则移出链表。当我方角色链表为空,则游戏结束。



  以上为敌方回合的逻辑步骤,代码中的注释比较多,就不做过多说明。可以对照项目代码来看。

  然后,在鼠标事件和绘制中,也是通过状态来管理的,这里列出绘制代码来说明。

public void draw() {
gameMap.drawMap(gContext);
drawPlayer();
switch (nowStatus) {
case SHOW_MENU:
if (nowControllPlayer != null && nowControllPlayer.isCanAction()) {
actionMenu.draw(gContext);
}
propertyMenu.draw(gContext);
break;
case SHOW_ENEMY_PROPERTY:
propertyMenu.draw(gContext);
break;
case PREPARE_MOVE:

break;
case GAME_WIN:
gContext.setFont(Font.font(18));
gContext.setFill(Color.WHITE);
gContext.fillText("游戏胜利!", 250, 150);
break;
case GAME_OVER:
gContext.setFont(Font.font(18));
gContext.setFill(Color.RED);
gContext.fillText("游戏失败!", 250, 150);
break;
default:
break;
}
gContext.save();
gContext.setFont(Font.font(18));
switch (nowTurn) {
case PLAYER:
gContext.setFill(Color.WHITE);
gContext.fillText("我方回合", 15, getHeight() - 15);
break;
case ENEMY:
gContext.setFill(Color.RED);
gContext.fillText("敌方回合", 15, getHeight() - 15);
break;
}
gContext.restore();
}
   

  在绘制中,我们根据不同的状态来绘制不同的内容。

   鼠标事件原理是如此,如下所示:

// 事件处理
setOnMouseClicked(e -> {
if (e.getButton() == MouseButton.PRIMARY) {
switch (nowStatus) {
case NONE:
for (BasePlayer player : players) {
//判断点击的角色,显示属性框和操作菜单
if (player.isCollisionWith(e.getX(), e.getY())) {
actionMenu.setLocation(player.getX() + tileWidth, player.getY());
actionMenu.getTextObjects()[0].setColor(player.isCanMove() != true ? Color.DARKGRAY
: Color.WHITE);
actionMenu.getTextObjects()[1].setColor(player.isCanAttack() != true ? Color.DARKGRAY
: Color.WHITE);
propertyMenu.initPlayer(player);
nowControllPlayer = player;
nowControllPlayer.setChoose(true);
nowStatus = Status.SHOW_MENU;
}
}
//如果点击的是敌方角色,则只显示敌方属性框
for (BasePlayer enemy : enemys) {
if (enemy.isCollisionWith(e.getX(), e.getY())) {
propertyMenu.initPlayer(enemy);
nowStatus = Status.SHOW_ENEMY_PROPERTY;
}
}
break;
case SHOW_MENU:
actionMenu.onMousePressed(e);
break;
case PREPARE_MOVE:
moveToX = (int) (e.getX() / tileWidth);
moveToY = (int) (e.getY() / tileHeight);
isCanMove = false;
// 判断点击的是否是可移动的范围
if (!isPointHasPlayer(moveToX, moveToY)) {
for (WNode node : nowControllPlayer.nodeList) {
if (((int) node.getPoint().getX()) == moveToX && ((int) node.getPoint().getY()) == moveToY) {
isCanMove = true;
}
}
//如果可以移动,则启动移动定时器
if (isCanMove) {
nowStatus = Status.MOVE;
moveTimer.start();
nowControllPlayer.setChoose(false);
}
}
break;
case PREPARE_ATTACK:
//准备攻击状态时,点击要攻击的角色
for (BasePlayer enemy : enemys) {
if (enemy.isCollisionWith(e.getX(), e.getY())) {
nowControllPlayer.attack(enemy);
nowControllPlayer.setWaitToAttack(false);
enemy.setFlash(true);
nowBeAttackedPlayer = enemy;
nowStatus = Status.ATTACK;
}
}
break;
default:
break;
}
} else if (e.getButton() == MouseButton.SECONDARY) {
//右键状态还原
if (nowControllPlayer != null) {
nowControllPlayer.setChoose(false);
nowControllPlayer.setWaitToAttack(false);
nowControllPlayer.setWaitToMove(false);
}
nowStatus = Status.NONE;
nowControllPlayer = null;
}
});
  

  具体的大家可以研究项目代码。

  截图如下:





    本文章为个人原创,版权所有,转载请注明出处:http://blog.csdn.net/ml3947。另外我的个人博客:http://www.wjfxgame.com

--------------------------------------------------------------------------------------------------------------------------------------------------------------------

   其实这个示例已经完成很久了,之前是一个CSDN的朋友说想看看JavaFX战旗类游戏开发的示例。于是业余花了一点点时间写了这个例子。随后开始写了这个《JavaFX战旗类游戏开发》的系列博文,可惜,中间间间断断时不时写一篇,已经过去了好几个月了,汗,感觉自己变得有点懒散了,于是总共七课的课程算是完结了,之前的系列文章有很多坑,不知道何时会想到去填。。今年下半年算是不平凡的半年,考了驾照,然后结婚,老婆家酒已经办了,我家还没办。之后还要度蜜月,空余的时间也没多少心思写着写那,无奈。

  另外,很多朋友发私信加QQ。但是现在本人QQ非特殊情况基本不加技术讨论的。毕竟有时候还是想休息之类的。如果有任何问题发我的邮件Gmail:wingfourever@gmail.com。谢谢。

--------------------------------------------------------------------------------------------------------------

  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  JavaFX 战旗 游戏开发