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

上一节课中,我们讲述了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:[email protected]。谢谢。

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

时间: 2024-10-12 17:50:48

JavaFX战旗游戏开发 第七课 回合逻辑(完)的相关文章

JavaFX战旗类游戏开发 第六课 移动范围的获取

有一段时间没有写这个战旗游戏Demo的教程了.现在来继续. 战旗类游戏的范围获取其实并不复杂,主要是节点的遍历和权值的比较. 大家知道,在A*Star最短寻径算法里,权值是有个G值和H值的,G值是起点到当前点的移动量(通常相邻两格移动量在1),H值是当前点到目标点的移动量估算值. 当然,对于SLG游戏中寻找移动范围,并没有这么复杂,我们在这里只需一个G值,用于表示移动量. 主要算法原理: 1.有两个List----OpenList,CloseList. 2.将要移动的角色位置,添加到OpenLi

JavaFX战旗类游戏开发 第四课 属性框和菜单的创建

上一课中,我们创建了游戏角色.这节课中,我们将会创建菜单,以便后面使用. 由于只是Demo,我创建的是最简单的形式,如下图所示: 基于游戏开发中的UI控件通常需要有事件(比如图中的移动,攻击,待机,是有事件处理的),我们应该首先创建自己的文字控件. 文字控件代码如下: import com.sun.javafx.tk.FontMetrics; import com.sun.javafx.tk.Toolkit; import javafx.scene.canvas.GraphicsContext;

JavaFX战旗类游戏开发 第三课 创建游戏角色

在上一节课程中,我们学习了在JavaFX中绘制游戏地图.这一节课,我们将会创建我们的游戏角色. 首先,同样的,我们创建一个简单的基类. import javafx.scene.canvas.GraphicsContext; /** * 游戏物体基类 * @author Wing Mei */ public abstract class BaseObject { protected double x, y; protected double width,height; protected bool

MVC模式在游戏开发的应用

原地址: http://www.cocoachina.com/gamedev/2012/1129/5212.html MVC是三个单词的缩写,分别为:模型(Model).视图(View)和控制Controller).MVC是一个设计模式,它强制性地使应用程序的输入.处理和输出分开,将应用程序分成三个核心部件:模型.视图.控制器.它们各自处理自己的任务,关系如图所示: 模型是数据层,视图是表现层,控制器是逻辑层,也对应于程序运行中的数据输入,数据处理,数据输出基本三步骤.事实上,MVC模式开发也适

JavaFX战旗类游戏开发 第二课 游戏地图绘制

在上一节课中,我们对即将要完成的战旗Demo有了一个大概的了解,本节课当中,我们将会学习绘制游戏地图. 自从在JavaFX 2.2中增加了Canvas相关的功能,我们就可以使用Canvas来实现游戏绘制了. 游戏地图绘制主要用到GraphicsContext.drawImage方法. drawImage(Image image,double sx,double sy,double sw,double sh,double dx,double dy,double dw,double dh); 其中i

JavaFX横幅类游戏开发 教训 游戏贴图

上一节课,我们即将完成战旗Demo有了一个大概的了解.教训这,我们将学习绘制游戏地图. 由于JavaFX 2.2中添加了Canvas相关的功能,我们就能够使用Canvas来实现游戏绘制了. 游戏地图绘制主要用到GraphicsContext.drawImage方法. drawImage(Image image,double sx,double sy,double sw,double sh,double dx,double dy,double dw,double dh); 当中image 表示源图

【Cocos游戏实战】功夫小子第七课之游戏主功能场景逻辑功能和暂停功能场景的分析和实现

CSDN的markdown编辑器是吃屎了么! !.什么玩意.!写了一半写不了东西还全没了,搞个毛线! 本节课的视频教程地址是:第七课在此 假设本教程有帮助到您,希望您能点击进去观看一下,并且如今注冊成为极客学院的会员,能够免费领取30天学习时间,免费拿极客学院VIP,1000+高清视频.学习Android.HTML5.iOS各种开发技术,限时领取.手快的戳:http://e.jikexueyuan.com/invite/index.html?ZnJvbV9jb2RlPVkxblJUZSZ1bmF

cocos2d-x ios游戏开发初认识(七) 简单的动画

前面有一节说了帧动画,就是让精灵改变自己的位置.形状.大小来实现相应的动作,这讲主要是要通过一些方法来实现精灵的移动,产生各种炫丽的动画,也可能让你找到一点游戏场景. 下面具体根据代码分析: 为了清晰最好将前几节的代码注释掉. //根据前面的知识先创建一个菜单 CCMenuItemFont *item =CCMenuItemFont::create("开始游戏",this, menu_selector(MainScene::onMenuItem));//点击事件 //添加到菜单栏里面

Unity3D游戏开发从零单排(七) - NetworkView的Demo

提要 今天做了一个移动设备的网络通信demo,分两个部分,一个是网络连接,一个是数据通信. 需要两台Android设备A,B.A作客户端,B作服务端. 最终的效果是玩家控制设备A中的方块,B中的方块也一起动,同时在A的加速度传感器的信息在B中也实时更新. 网络连接 首先两台设备要联网,且IP在同一个网段,比如连接在同一个路由上,或者通过笔记本发出wifi信号,然后把设备连在上面. 在Unity3d中创建一个新工程,在场景中创建两个空物体,一个Client,一个Server. 在client创建一