Android - 为乒乓球游戏创建定时器循环

标签 android loops timer android-asynctask pong

我有一个用 Java 编写的 Pong 游戏,我正在尝试将它移植到 Android。 (这是我的第一款 Android 游戏;)。

在 Java 中,我使用了一个 Timer 对象,它基本上更新了游戏值(球/ Racket 位置),然后重新绘制屏幕。

我正尝试在 Android 中实现相同的功能,但遇到了一些错误。

我的程序由作为游戏视觉部分的 PongView 类和使用 PongView 作为其 View 的 PongDriverActivity 类组成.如果我有一个使 PongView 无效的循环线程,我会得到一个错误,因为该线程无法触及在另一个线程上生成的 View 。

我假设我需要执行某种AsyncTask,但我不确定如何循环它。

关于什么是最佳实现方式有什么建议吗?

最佳答案

有很多方法可以做到这一点。 Android 确实支持普通的 Java Thread 对象,您可以使用它们。您可以搜索 (SO) android game loop 并可能找到很多示例。但是,如果您想尝试 AsyncTask,这里有一个示例实现:

public class PongView extends View {

    public PongView(Context context) {
        super(context);
    }    
    public void setBallPosition(int x, int y) {
        // relocate the ball graphic
    }    
}

然后,您的 Activity :

public class PongDriverActivity extends Activity implements OnSeekBarChangeListener {

    private enum GameSpeed { SLOW, MEDIUM, FAST };

    private PongView mGameView;
    private PongDriverTask mWorker;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mGameView = new PongView(this);
        mWorker = new PongDriverTask(GameSpeed.MEDIUM);

        // if you need to pass some data to the worker:
        mWorker.execute("one", "two", "three");  
        // else, declare the AsyncTask's first generic param as Void, and do:
        //mWorker.execute();

        setContentView(mGameView);
    }

    // you would connect this up to a button, to allow the user to stop  
    public void onStopClicked(View sender) {
        if (mWorker.isRunning()) {
            mWorker.stopRunning();
        }
    }

    // you could connect this up to a slider, to allow the user to control the paddle
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        // you may need to convert progress to a y-coordinate
        mWorker.setPaddlePosition(progress);
    }

然后(可能在内部类 PongDriverActivity.java 中):

private class PongDriverTask extends AsyncTask<String, Integer, Integer> {

    private int mDelay;
    private boolean mRunning = true;
    private int mPaddleY = 0;

    public PongDriverTask(GameSpeed speed) {
        if (speed == GameSpeed.SLOW) {
            mDelay = 100;
        } else if (speed == GameSpeed.MEDIUM) {
            mDelay = 50;
        } else if (speed == GameSpeed.FAST) {
            mDelay = 10;
        }
    }

    public synchronized boolean isRunning() {
        return mRunning;
    }
    public synchronized void stopRunning() {
        mRunning = false;
    }
    public synchronized void setPaddlePosition(int y) {
        mPaddleY = 0;
    }
    private synchronized int getPaddlePosition() {
        return mPaddleY;
    }

    @Override
    protected void onPostExecute(Integer score) {
        super.onPostExecute(score);
        // here you could show a UI that shows the final score
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();

        mGameView.setBallPosition(0, 0);
        // here you could throw up some UI that shows just
        //  before the game really starts
    }

    @Override
    protected void onProgressUpdate(Integer... params) {
        super.onProgressUpdate(params);
        // retrieve updated game coordinates from params
        mGameView.setBallPosition(params[0], params[1]);  // x,y
    }

    @Override
    protected Integer doInBackground(String... args) {
        // If you have some information to pass to the background worker, 
        //  it would be passed in args[0], args[1], etc.
        // It doesn't have to be String data.  you could change the generic 
        //  to take something other than String as the first param, in which
        //  case, doInBackground() would take a variable length list of that
        //  other data type.
        int ballX = 0;
        int ballY = 0;
        int score = 0;

        while (isRunning()) {
            // use your game engine to recalculate the ball position
            //  on this background thread
            int paddleY = getPaddlePosition();
            ballX += 1;
            ballY += 2;
            score++;

            // now, update the UI, which must happen on the UI thread
            publishProgress(ballX, ballY);

            try {
                Thread.sleep(mDelay);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return score;
    }
}

任务的 doInBackground() 方法将在后台线程上运行,因此您不能直接从那里修改 UI。但是,我上面覆盖的所有其他 AsyncTask 方法都是在 UI 线程上调用的,因此在其中任何一个中执行 UI 工作都是安全的。

此代码假定桨由搜索栏控制,您可以将您的 Activity 设置为其更改监听器。还有很多其他方法可以做到这一点。

我刚刚实现了一个粗略的游戏停止机制,您可以将其连接到一个按钮。如果你想允许用户暂停和恢复,你可以搜索暂停/恢复线程的实现,这应该也适用于这里。例如:

How to Pause and Resume a Thread in Java from another Thread

更多阅读...

See this writeup for some discussion of game loop speeds ...

and maybe take a look at this discussion, too

关于Android - 为乒乓球游戏创建定时器循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16095557/

相关文章:

java - 在没有收集的情况下在 Java 中迭代 Spark DataFrame

php - 如何在 PHP 中的数字之间插入连字符?

c# - 在计时器经过回调时调用没有 Monitor.Exit 的 Monitor.TryEnter 显示意外行为

linux - 解释 shell 脚本代码 "for loop"

Android googlemap权限检查,无法解析符号

java - 从 CustomArray 适配器调用 onActivityResult

Android:同时播放多个mp3,具有精确同步和独立音量控制

c++ - 由于英国夏令时, boost 计时器停止工作

timer - MSP 430G2553 使用 Timer_A 在比较模式下进行采样

android - 如何获取android Honeycomb系统的屏幕宽度和高度?