android_Android游戏开发–基本游戏循环

android

android

在到目前为止的系列之后,您将对游戏架构有所了解。 即使只是简短地讲,但我们知道我们需要以某种形式输入信息,更新游戏的内部状态,最后将其渲染到屏幕上,并产生一些声音和/或振动。 此外,我们为第一个游戏创建了一个示例Android项目。 在本文中,我们将讨论并实现基本的游戏循环。

让我们保持简单。 检查下图。

基本的游戏循环

我们处理输入,更新内部对象的状态并呈现当前状态。 更新渲染按逻辑分组。 它们绑在一起,往往一个接一个地执行。

Android中的任何事情都发生在Activity中。 该活动将创建一个View视图就是一切发生的地方。 在此进行触摸并显示生成的图像。 可以将“活动”想象成一张拥有一张纸的表格(“视图” ),使我们能够绘制一些东西。 我们将用铅笔在纸上画一些东西。 这将是我们的触碰,实际的化学React会在纸上发生,因此我们与View交互的结果将产生图像。 与ActivityView相同。 如下图所示:

Android游戏循环

让我们从项目中打开DroidzActivity.java 。 我们看到线

setContentView(R.layout.main);

这无非就是在创建活动时将默认(R)视图分配给活动。 在我们的情况下,它发生在启动时。

让我们创建一个将要使用的新视图View是一个简单的类,为我们提供事件处理(如onTouch)和可绘制的可见矩形空间。 最简单的方法是扩展Android自己的SurfaceView。 我们还将实现SurfaceHolder.Callback来访问表面变化,例如在表面变化或设备方向发生变化时。

MainGamePanel.java

package net.obviam.droidz;

import android.content.Context;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MainGamePanel extends SurfaceView implements
  SurfaceHolder.Callback {

 public MainGamePanel(Context context) {
  super(context);
  // adding the callback (this) to the surface holder to intercept events
  getHolder().addCallback(this);
  // make the GamePanel focusable so it can handle events
  setFocusable(true);
 }

 @Override
 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
 }

 @Override
 public void surfaceCreated(SurfaceHolder holder) {
 }

 @Override
 public void surfaceDestroyed(SurfaceHolder holder) {
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  return super.onTouchEvent(event);
 }

 @Override
 protected void onDraw(Canvas canvas) {
 }
}

上面的代码是一个普通类,它覆盖了我们感兴趣的方法。除了第15和17行外,没有什么特别的。

getHolder().addCallback(this);

此行将当前类( MainGamePanel )设置为实际表面上发生的事件的处理程序。

setFocusable(true);

上一行使我们的游戏面板更具针对性,这意味着它可以获得焦点,从而可以处理事件。 我们添加了回调并将其集中在构造函数中,这样我们就不会错过。

越过的方法(第20行及以后)将全部使用,但当前将其保留为空。

让我们创建将成为我们实际游戏循环的线程。

MainThread.java

package net.obviam.droidz;

public class MainThread extends Thread {

 // flag to hold game state
 private boolean running;
 public void setRunning(boolean running) {
  this.running = running;
 }

 @Override
 public void run() {
  while (running) {
   // update game state
   // render state to the screen
  }
 }
}

如您所见,这并没有太大作用。 它会覆盖run()方法,并且将运行标志设置为true时,它将执行无限循环。

当前线程尚未实例化,因此让我们在屏幕加载时启动它。让我们看一下修改后的MainGamePanel类。

package net.obviam.droidz;

import android.content.Context;
import android.graphics.Canvas;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MainGamePanel extends SurfaceView implements
  SurfaceHolder.Callback {

 private MainThread thread;

 public MainGamePanel(Context context) {
  super(context);
  getHolder().addCallback(this);

  // create the game loop thread
  thread = new MainThread();

  setFocusable(true);
 }

 @Override
 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
 }

 @Override
 public void surfaceCreated(SurfaceHolder holder) {
  thread.setRunning(true);
  thread.start();
 }

 @Override
 public void surfaceDestroyed(SurfaceHolder holder) {
  boolean retry = true;
  while (retry) {
   try {
    thread.join();
    retry = false;
   } catch (InterruptedException e) {
    // try again shutting down the thread
   }
  }
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  return super.onTouchEvent(event);
 }

 @Override
 protected void onDraw(Canvas canvas) {
 }
}

我们添加了以下几行:第12行将线程声明为私有属性。

private MainThread thread;

在第19行,我们实例化线程。

thread = new MainThread();

surfaceCreated方法中,将运行标志设置为true,然后启动线程(第30和31行)。 到此方法被称为曲面时,就已经创建好了,可以安全地开始游戏循环了。

看一下surfaceDestroyed方法。

public void surfaceDestroyed(SurfaceHolder holder) {
 // tell the thread to shut down and wait for it to finish
 // this is a clean shutdown
 boolean retry = true;
 while (retry) {
  try {
   thread.join();
   retry = false;
  } catch (InterruptedException e) {
   // try again shutting down the thread
  }
 }
}

在破坏表面之前直接调用此方法。 这里不是设置运行标志的地方,但是我们放入的代码可确保线程干净地关闭。 我们只是阻塞线程并等待其死亡。

如果现在在模拟器中运行项目,将看不到太多内容,但是我们将使用一些日志记录对其进行测试。 不用担心,因为我将在下一章中介绍日志记录。您可以在Android网站上找到更多信息。

添加与屏幕的交互

当我们触摸屏幕下部时,我们将退出该应用程序。 如果我们在其他任何地方触摸它,我们只会记录坐标。

MainThread类中,添加以下行:

private SurfaceHolder surfaceHolder;
private MainGamePanel gamePanel;

public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
 super();
 this.surfaceHolder = surfaceHolder;
 this.gamePanel = gamePanel;
}

我们声明了gamePanelsurfaceHolder变量,以及一个以实例为参数的构造函数。重要的是要同时拥有它们和不仅仅是gamePanel,因为我们在绘制时需要锁定表面,而这只能通过surfaceHolder来完成。

将int实例化线程的MainGamePanel的构造函数更改为

thread = new MainThread(getHolder(), this);

我们正在将当前的holder和面板传递给它的新构造函数,以便线程可以访问它们。 我们将在游戏面板中创建游戏更新方法,并从线程中触发它,但目前仍保持原样。

TAG常量添加到MainThread类。 每个类都有自己的String常量TAG 。 常量的值将是包含该常量的类的名称。 我们使用的是Android自己的日志记录框架,该框架具有两个参数。 firs是标签,它只是一个字符串,用于标识日志消息的来源,第二个是我们要记录的消息。 将类的名称用作标记是一个好习惯,因为这样可以轻松查找日志。

日志记录

要打开日志查看器,请转到Windows-> Show View-> Other …,然后在对话框中选择Android-> LogCat

显示视图-> LogCat

现在您应该看到LogCat视图。 这不过是一个控制台,您可以在其中跟踪Android的日志。 这是一个很棒的工具,因为您可以过滤包含特定文本的日志或具有特定标签的日志,这非常有用。

让我们回到我们的代码。 MainThread.java类如下所示:

package net.obviam.droidz;

import android.util.Log;
import android.view.SurfaceHolder;

public class MainThread extends Thread {

 private static final String TAG = MainThread.class.getSimpleName();

 private SurfaceHolder surfaceHolder;
 private MainGamePanel gamePanel;
 private boolean running;
 public void setRunning(boolean running) {
  this.running = running;
 }

 public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
  super();
  this.surfaceHolder = surfaceHolder;
  this.gamePanel = gamePanel;
 }

 @Override
 public void run() {
  long tickCount = 0L;
  Log.d(TAG, "Starting game loop");
  while (running) {
   tickCount++;
   // update game state
   // render state to the screen
  }
  Log.d(TAG, "Game loop executed " + tickCount + " times");
 }
}

在第08行中,我们定义了用于记录的标签。在run()方法中,我们定义tickCount ,每次执行while循环(游戏循环)时,它都会增加一次。我们记录结果。

让我们回到MainGamePanel.java类,在其中我们修改了onTouchEvent方法,以便处理屏幕上的触摸。

public boolean onTouchEvent(MotionEvent event) {
 if (event.getAction() == MotionEvent.ACTION_DOWN) {
  if (event.getY() > getHeight() - 50) {
   thread.setRunning(false);
   ((Activity)getContext()).finish();
  } else {
   Log.d(TAG, "Coords: x=" + event.getX() + ",y=" + event.getY());
  }
 }
 return super.onTouchEvent(event);
}

02行我们检查屏幕上的事件是否为按下手势的开始( MotionEvent.ACTION_DOWN )。 如果是这样,我们检查触摸是否发生在屏幕下部。 也就是说,手势的Y坐标位于屏幕的下部50像素。 如果是这样,我们将线程的运行状态设置为false,并在主要退出应用程序的主活动上调用finish()

注意:屏幕是一个矩形,其左上角坐标为(0,0),右下角坐标为( getWidth()getHeight() )。

我还修改了DroidzActivity.java类,因此我们记录了它的生命周期。

package net.obviam.droidz;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;

public class DroidzActivity extends Activity {
    /** Called when the activity is first created. */

 private static final String TAG = DroidzActivity.class.getSimpleName();

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // requesting to turn the title OFF
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        // making it full screen
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        // set our MainGamePanel as the View
        setContentView(new MainGamePanel(this));
        Log.d(TAG, "View added");
    }

 @Override
 protected void onDestroy() {
  Log.d(TAG, "Destroying...");
  super.onDestroy();
 }

 @Override
 protected void onStop() {
  Log.d(TAG, "Stopping...");
  super.onStop();
 }
}

20行将显示变为全屏显示。仅重写onDestroy()onStop()方法以记录活动的生命周期。

通过右键单击项目来运行应用程序,然后选择运行方式-> Android应用程序您应该会看到黑屏。 如果您在上半部分单击几下,然后单击模拟器屏幕的底部,则应用程序应退出。在此阶段,值得检查日志。

LogCat

突出显示的行是最有趣的,就像您匹配代码中的日志一样,您将确切地看到方法调用的顺序。 您还应该看到线程的while循环执行了多少次。 这是一个非常高的数字,但是下一次我们将在介绍FPS和UPS时对周期进行更周到的考虑。 即每秒帧数每秒更新数。 我们将创建一个游戏循环,该循环实际上会在屏幕上绘制某些内容,并且每秒将执行指定次数的次数。

到目前为止,我们所做的事情:

  • 创建全屏应用程序
  • 有一个单独的线程控制应用程序
  • 拦截基本手势,例如按下手势
  • 慷慨地关闭应用程序

此处下载源代码。

将其导入eclipse中,它应该立即起作用。

参考:来自我们的JCG合作伙伴Tamas Jano的“ Basic Game Loop ”(“对抗谷物”博客)。

不要忘记查看我们的新Android游戏ArkDroid (以下屏幕截图) 。 您的反馈将大有帮助!
相关文章:

翻译自: https://www.javacodegeeks.com/2011/07/android-game-development-basic-game_05.html

android

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值