Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/unity3d/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用线程在Android中运行游戏引擎的问题_Android_Multithreading_User Interface_Game Engine_Runnable - Fatal编程技术网

使用线程在Android中运行游戏引擎的问题

使用线程在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进行视觉响应。(我知道这不是线程安全的。)我没有问题将

我目前正在为Android从头开始构建一个游戏(一个RPG),并且在使用Android集成逻辑时遇到了问题

我有一个游戏引擎的工作版本,我正在Eclipse中的一个单独的Java程序中工作。然而,当我尝试在Android(使用Android Studio构建)中运行引擎时,我被Android的怪癖所困扰,无法正常运行

问题在于Android是事件驱动的,需要将计算转移到工作线程。不幸的是,这意味着我必须使用特殊的方法来使用Android的UI进行视觉响应。(我知道这不是线程安全的。)我没有问题将其与“香草”版本进行比较,因为它非常直接,而是在打印语句和扫描仪上运行

因此,我从一个可运行的(从活动)启动引擎,如下所示:

EngineRunnable是另一个类,它保存测试引擎的特定实例(正在攻击的单元)的信息并调用测试引擎

SetButtonsVisible()方法(也在“活动”中)使四个编程定义的按钮显示在屏幕上,使用RunOnUIThread设置其可见性,以便按下按钮。我有一个SetButtonsInvisible()方法将这些按钮设置为View.INVISIBLE和View.GONE

runnable最终调用这个“Battle”方法,它位于GameEngine类中

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);
}
}