背景:
在过去的几个月里,我一直在用Java开发一个回合制RPG。我编程才一年左右,所以我的专业知识主要局限于命令行应用程序和面向对象编程原理,这些都是我在初级CS课程中学到的。
开发进展顺利,直到我开始将游戏从命令行输入调整为使用我开始使用Swing构建的定制UI。这在很大程度上是因为基于命令行的输入可以让我停止正在做的一切,等待来自扫描仪或类似的东西的输入,但从我在这里阅读的线程来看(在Java Swing中等待多个按钮输入,等待按钮被按下(,Swing的事件驱动特性实际上不允许您在不中断EDT和冻结GUI的情况下等待任何事情,这对我来说是件坏事。
问题:
虽然Swing对我来说很新颖,但我已经在主菜单上使用了它,并了解它如何容易地应用于数据相对集中在几个对象之间的游戏,这些对象利用用户的简单命令来移动或攻击或攻击你。我的问题是,我的RPG在一个战斗场景中有2到16个活跃角色,可以是用户控制的,也可以是人工智能控制的,可以使用任何数量的不同命令(包括大约16种不同的技能(,玩家角色必须能够访问所有角色之间通用的库存。可以说,我的游戏主要是菜单驱动的。例如,我将发布一个从我在战斗中运行的循环中调用的方法:
//initializes a turn; written as a function to cut down on duplicate code
public battleData initializeTurn(battleData toInitialize){
if(toInitialize.initializeSkill()) {
if(toInitialize.initializeTarget(playerParty, playerMinions, 1) == 0)
return initializeTurn(toInitialize); //recursive call if the user cancelled
} //the skill they selected.
else {
if(toInitialize.initializeTarget(enemyParty, enemyMinions, -1) == 0)
return initializeTurn(toInitialize); //see above
}
return toInitialize;
}
该方法调用gameCharacter类中编写的两个函数initializeTarget和initializeSkill,从两个已传递目标数组中选择一个目标,并从角色的两个技能列表中选择一项技能。从gameCharacter类派生出playerCharacter和Monster类,它们分别根据用户输入和基本AI计算方法的返回值。由于我有很多关于技能和项目的数据存储在不同的类中,用户输入的行为也不同,这取决于哪个角色正在接受输入,他们现在是在选择目标还是在选择技能,以及他们在技能选择菜单的范围内,我发现很难制定一种算法,让我把整个事情变成事件驱动的,因为事件可能意味着许多不同的东西,这取决于当前正在查看菜单的哪个部分。
解决方案:
我读过一些东西,比如Swing工作人员被用来处理后台任务,状态被用来向不同的地方提供输入,定时器被用来定期更新图形组件,但我对Swing还不够熟悉,不知道在这一点上应该采取什么样的最佳途径。我更想找到一个通用的算法来实现,而不是一个神奇的解决方案,它可以让我在不更改太多代码的情况下解决问题。我非常愿意重新塑造我迄今为止写的很多东西,所以不要害怕告诉我,如果你认为这会让程序变得更好,我将不得不重做很多。
谢谢你对我的宽容;如果你需要更多的上下文,我很乐意解释或发布更多的代码。我本来会先发布更多,但我提到的大多数菜单都是1-200行,解释起来比实际复制起来更容易。
首先,您可以将GUI视为在更新屏幕时不做任何事情的东西,而不是在等待输入时命令行什么都不做,从而使问题稍微简单一些。由于这是一个基于回合的游戏,所以必须对回合何时可以进行计算提出要求。通常情况下,当用户完成输入时会发生这种情况。在你的情况下,根据我收集的信息,输入基本上是对不同游戏角色的命令。因此,每次用户命令做某事(点击游戏角色的命令按钮(时,都必须有类似于isReadyToCompute()
的检查。当方法返回true时,可以调用computeTurn()
。以下是代码中的粗略表示。你需要添加一些与游戏相关的检查,但它应该能让你知道从哪里开始
skillButton1.addActionListener(e -> {
// change cursor to "select" type cursor
skill1 = true;
});
public class Mouse implements MouseListener {
public void mouseClicked(MouseEvent e) {
// get mouse x y
// find target game character at x y or if none then return
// remember the character as target
// of skill1 of selected character
// and count this as order complete for selected character
skill1 = false;
// change cursor to normal
if (isReadyToCompute()) computeTurn();
}
}
总体想法是,您不需要循环,因为GUI已经有了自己的内部循环,并且它在EDT上运行。EDT捕获和分派由动作和鼠标侦听器处理的事件。当使用命令行时,您等待侦听输入事件的扫描仪,类似地,在GUI中,您等待前面提到的侦听器,后者依次侦听按钮按下或鼠标事件。有关如何编写监听器的更多信息,请参阅Oracle官方教程:
https://docs.oracle.com/javase/tutorial/uiswing/events/actionlistener.html
https://docs.oracle.com/javase/tutorial/uiswing/events/mouselistener.html
如果对你来说一切都一样,我建议你改用JavaFX,这是一个更丰富的图形框架,是游戏的更好选择,也是Swing的继任者。http://docs.oracle.com/javase/8/javafx/get-started-tutorial/jfx-overview.htm#JFXST784