使用线程在Android中运行游戏引擎的问题
我目前正在为Android从头开始构建一个游戏(一个RPG),并且在使用Android集成逻辑时遇到了问题 我有一个游戏引擎的工作版本,我正在Eclipse中的一个单独的Java程序中工作。然而,当我尝试在Android(使用Android Studio构建)中运行引擎时,我被Android的怪癖所困扰,无法正常运行 问题在于Android是事件驱动的,需要将计算转移到工作线程。不幸的是,这意味着我必须使用特殊的方法来使用Android的UI进行视觉响应。(我知道这不是线程安全的。)我没有问题将其与“香草”版本进行比较,因为它非常直接,而是在打印语句和扫描仪上运行 因此,我从一个可运行的(从活动)启动引擎,如下所示: EngineRunnable是另一个类,它保存测试引擎的特定实例(正在攻击的单元)的信息并调用测试引擎 SetButtonsVisible()方法(也在“活动”中)使四个编程定义的按钮显示在屏幕上,使用RunOnUIThread设置其可见性,以便按下按钮。我有一个SetButtonsInvisible()方法将这些按钮设置为View.INVISIBLE和View.GONE runnable最终调用这个“Battle”方法,它位于GameEngine类中使用线程在Android中运行游戏引擎的问题,android,multithreading,user-interface,game-engine,runnable,Android,Multithreading,User Interface,Game Engine,Runnable,我目前正在为Android从头开始构建一个游戏(一个RPG),并且在使用Android集成逻辑时遇到了问题 我有一个游戏引擎的工作版本,我正在Eclipse中的一个单独的Java程序中工作。然而,当我尝试在Android(使用Android Studio构建)中运行引擎时,我被Android的怪癖所困扰,无法正常运行 问题在于Android是事件驱动的,需要将计算转移到工作线程。不幸的是,这意味着我必须使用特殊的方法来使用Android的UI进行视觉响应。(我知道这不是线程安全的。)我没有问题将
public void battle(BattleUnit unitA, BattleUnit unitB) {
unitA.setName("Player 1");
unitB.setName("Player 2");
activity.setTVText(unitA.getName() + " starts at " + unitA.getLocX() + " , " + unitA.getLocY() + " .");
activity.setTVText(unitB.getName() + " starts at " + unitB.getLocX() + " , " + unitB.getLocY() + " .");
Log.i("Sequence","1");
while (unitA.isActive() && unitB.isActive()) {
decideTurn(unitA, unitB);
}
declareWinner(unitA, unitB);
}
这个问题在这些所谓的方法中发挥了作用
public void decideTurn (BattleUnit unit1, BattleUnit unit2){
//Unit 1 is faster and moves first
if(unit1.getSpeed() >= unit2.getSpeed() && unit1.hasMoved() == false){
activity.setTVText(unit1.getName()+"'s turn!");
action(unit1, unit2);
Log.i("Sequence", "2");
}
//Unit 2 is faster and moves first OR Unit 1 is faster and Unit 2 moves second
else if (unit2.hasMoved() == false){
activity.setTVText(unit2.getName()+"'s turn!");
action(unit2, unit1);
Log.i("Sequence", "2");
}
//Unit 2 is faster and Unit 1 moves second
else if (unit2.hasMoved() == true && unit1.hasMoved() == false) {
activity.setTVText(unit1.getName() + "'s turn!");
action(unit1, unit2);
Log.i("Sequence","2");}
//Both units moved
else{
unit1.newTurn();
unit2.newTurn();
Log.i("Sequence", "New Turn");
}
}
它在此处调用操作方法:
private void action(BattleUnit unit1, BattleUnit unit2){
activity.activateButtons(unit1, unit2, 1);
Log.i("Sequence", "3");
}
这就是改变用户界面的方法。我将最后一个方法移到屏幕上方的文本视图。其中有四个。四个消息的底部被放置为“当前”消息,如下所示:
public synchronized void setTVText (CharSequence charSequence){
final CharSequence sequence = charSequence;
//textHandler = new Handler();
//textHandler.post(new Runnable() {
runOnUiThread(new Runnable() {
@Override
public void run() {
tv1.setText(tv2.getText());
tv2.setText(tv3.getText());
tv3.setText(tv4.getText());
tv4.setText(sequence);
}
});
}
因此,当我在手机上测试程序时,无论使用什么按钮,程序都会打印出玩家的轮到时间。在我的示例中,始终是播放器2。所以它会打印四次
我记录了代码,如图所示。当我运行代码时,日志的顺序在2和3之间交替(或者在决定转弯和打开按钮以显示动作之间跳跃)。此外,当我通过选择移动来更改按钮时(如我如何更改文本视图),程序会落后(我认为它必须用引擎启动的内容覆盖更改,或者程序在第2点和第3点之间进行了太多更改,以致无法按下按钮)
我完全不明白为什么。我对Android开发相对较新,尤其是多线程。它是否多次调用引擎?或者是我的while语句代码的本质让游戏继续
任何帮助或建议都将不胜感激。您是否尝试过实现回调/接口?因为一个动作触发了一个响应,所以创建一个接口并将其解析到引擎,它可以回调到主活动并更新活动UI 接口/回调
public interface EngineCallback {
void actionComplete();
void changePlayerTurnTV(String text);
void doAction(BattleUnit unit1, BattleUnit unit2);
}
在游戏活动中实现此接口,然后将该接口传递给引擎
发动机示例
public class GameEngine {
private mEngineCallback;
public GameEngine(EngineCallback callback) {
mEngineCallback = callback;
}
public void decideTurn (BattleUnit unit1, BattleUnit unit2){
//Unit 1 is faster and moves first
if(unit1.getSpeed() >= unit2.getSpeed() && unit1.hasMoved() == false){
mEngineCallback.changePlayerTurnTV(unit1.getName()+"'s turn!");
action(unit1, unit2);
Log.i("Sequence", "2");
}
//Unit 2 is faster and moves first OR Unit 1 is faster and Unit 2 moves second
else if (unit2.hasMoved() == false){
mEngineCallback.changePlayerTurnTV(unit2.getName()+"'s turn!");
action(unit2, unit1);
Log.i("Sequence", "2");
}
//Unit 2 is faster and Unit 1 moves second
else if (unit2.hasMoved() == true && unit1.hasMoved() == false) {
mEngineCallback.changePlayerTurnTV(unit1.getName() + "'s turn!");
action(unit1, unit2);
Log.i("Sequence","2");}
//Both units moved
else{
unit1.newTurn();
unit2.newTurn();
Log.i("Sequence", "New Turn");
}
}
private void action(BattleUnit unit1, BattleUnit unit2){
mEngineCallback.doAction(unit1, unit2);
}
public void performExampleAction() {
// proccessing code
// processing code
mEngineCallback.actionComplete();
}
}
配子活动
public class GameActivity extends AppCompatActivity implements EngineCallback {
...
@Override
public void actionComplete() {
// update UI code
}
@Override
public void changePlayerTurnTV(String text) {
tv1.setText(tv2.getText());
tv2.setText(tv3.getText());
tv3.setText(tv4.getText());
tv4.setText(text);
}
@Override
public void doAction(BattleUnit unit1, BattleUnit unit2) {
activateButtons(unit1, unit2, 1);
}
}
当操作将触发响应时,始终使用回调/接口,并始终将业务层与表示层分离
编辑:将部分代码重构到实现中我还没有尝试过这一点。非常感谢你的建议。不幸的是,我在实现它时会遇到困难,因为Java不支持多继承。(我已经在活动中实现了View.OnClickListener来处理按钮单击。)要处理按钮单击的活动,但按钮单击会触发GameEngine中的一个函数,因此GameEngine将进行处理并通知活动处理已完成,因此活动可以更新ui。老实说,我被你的意思弄糊涂了。您是说在我完成任何操作(其计算是在单个方法中设置的)后调用此接口,以便它仅在计算完成时更新UI吗?并在活动中处理按钮单击,而不是在程序开始时将View.OnClickListener作为一个实现?这会通过强制UI等待来解决我的问题吗?我只关心在我的代码区域之间来回的日志结果(不向后调用),而不是正常地向前移动“处理活动中的按钮单击,而不是在程序开始时将View.OnClickListener作为实现"? 是的,您使用游戏引擎进行计算,并使用界面将其发送到UI。我假设上面列出的除了setTVText之外的所有方法都是在TestEngine类中实现的。我的意思是UI似乎更新得如此之快,以至于它跳过了我希望用户看到的帧。我的意思是,我当前创建的类表示它实现了View.OnClickListener。您建议的解决方案将使用回调/接口来替换它,因此我必须为活动中的各个按钮分别创建侦听器。谢谢你的回答。我在仔细检查。除了setTVText之外,上面唯一的一个是使用my runnable实例声明线程,该实例也在活动中。我应该把它移到引擎本身吗?
public class GameActivity extends AppCompatActivity implements EngineCallback {
...
@Override
public void actionComplete() {
// update UI code
}
@Override
public void changePlayerTurnTV(String text) {
tv1.setText(tv2.getText());
tv2.setText(tv3.getText());
tv3.setText(tv4.getText());
tv4.setText(text);
}
@Override
public void doAction(BattleUnit unit1, BattleUnit unit2) {
activateButtons(unit1, unit2, 1);
}
}