Androng,一个针对Android的Pong克隆

  • 下载application from Android market
  • 下载source - 532 KB

内容 IntroductionAndroid游戏开发 活动视图绘图使用CanvasAnimationFrame速度独立动画 从Android获得高分辨率计时 碰撞检测 包围盒命中检测像素完美检测 声音managementInput managementNotifications 应用程序生命周期管理 活动、生命、生命、艺术线 安卓市场 源codeNext versionHistory 介绍 本文描述了我是如何开发和发布Androng的,它是运行在Android上的经典游戏Pong的克隆版本。我使用Java开发了这个游戏,它支持单人和双人模式。我想分享一下我是如何从头到尾开发这款游戏的。我所说的完成是指在Android市场上发行游戏。以上是两个游戏中的截图。 发出难闻的气味 “乒乓”最初是作为乒乓球的电子版本而开发的一种游戏。雅达利公司最早在1972年发明了这款游戏。目标是通过获得更高的分数来打败你的对手。你必须在比赛中守住球,希望你的对手失误。在我的实施中,谁先拿到10分,谁就赢。 安卓 Android是基于Linux 2.6内核的移动设备操作系统。Android Inc.开发了Android,谷歌于2005年收购了Android。虽然它是移动设备的操作系统,但操作系统本身很大。它由超过1200万行代码组成。Android支持多种不同的移动设备,并且有一个专门的Android版本(3.0蜂巢版)可以用于平板设备。 你可以用c++和。net开发Android应用程序,但是大多数Android开发是用Java完成的。我使用Java的原因是它接近于c#,我在白天的工作中使用的语言。要为Android开发Java应用程序,您需要Android SDK、Java SDK和集成开发环境(IDE),比如Eclipse或IntelliJ。 我使用的是IntelliJ的社区版,它是免费的。这是因为在我的日常工作中,我使用Visual Studio和Resharper,它们与IntelliJ有很多相似之处。Resharper和IntelliJ有很多相同的快捷键,它们都是由JetBrains公司开发的。注意,尽管可以使用Java进行开发,但并不是所有的Java库都可用,只有Android运行时支持的Java库可用。 安卓游戏开发 当你开始在Android上开发游戏时,你有三种可能的图形实现。您可以使用drawable包并使用视图或画布。另一方面,您可以使用OpenGL ES API,它是OpenGL规范用于嵌入式设备的特殊实现。OpenGL ES包括对3D图形的支持。对于Androng,我决定使用图形包并使用画布绘制。画布包足够快,以支持这个游戏所需的动画和运动。 活动 Android应用程序围绕活动和视图展开。活动是应用程序的一部分,它提供一个可交互的屏幕。一个应用程序可以包含多个活动。Android启动应用程序清单中指定的活动。这个清单很重要,它是一个配置文件,指定系统在启动应用程序之前需要的信息。例如,运行应用程序需要哪些权限。启动时,Android调用指定活动的onCreate方法。下面的源代码显示了Androng的主要活动。所有活动都应该从Activity类派生。 隐藏,复制Code

public class AndrongActivity extends Activity
{
  private AndrongSurfaceView pongSurfaceView;

  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    pongSurfaceView = (AndrongSurfaceView) findViewById(R.id.androng);
    pongSurfaceView.setTextView((TextView) findViewById(R.id.text));
  }

  ....
}

的观点 视图创建活动的用户界面,并从view类派生。一个总体视图可以由视图组和视图组成。视图组用于对视图进行分组。它们遵循复合设计模式。应用程序的用户界面可以由多个视图组成,即所谓的视图层次结构。您可以使用代码或资源文件来定义视图的布局。通过使用资源布局文件,用户界面的灵活性增加了。它变得更容易维护和包括对本地化的支持。布局资源文件是使用XML实现的。下面的资源文件显示了Androng的布局: 隐藏,收缩,复制Code

xmlversion="1.0"encoding="utf-8"?>
<FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent">
    <net.semantic.games.AndrongSurfaceViewandroid:id="@+id/androng"android:layout_width="match_parent"android:layout_height="match_parent"/>
    <RelativeLayoutandroid:layout_width="match_parent"android:layout_height="match_parent">
        <TextViewandroid:id="@+id/text"android:text="Androng"android:visibility="visible"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:layout_alignParentBottom="true"android:layout_marginBottom="10px"android:gravity="center_horizontal"android:textColor="#99FFFFFF"android:textSize="16sp"/>
     </RelativeLayout>
</FrameLayout>

所有布局类型都是viewgroup,用于创建层次模型。Androng使用一个FrameLayout,这是最简单的布局对象。FrameLayout是屏幕上的空白保留空间,可以用一个对象填充。所有的子对象都被固定在左上角。无法为子节点指定位置;每一个连续的子对象都被画在前面的对象上,这样可以模糊前面的对象。 在Androng的情况下,AndrongSurfaceView和TextView被绘制在彼此之上。在用户界面中加载XML是在activity setContentView(R.layout.main)的onCreate方法中完成;出来的地方。main标识资源中的XML文件。还可以使用其他布局类型。有航线布局、相对布局、表格布局和绝对布局。每种布局都有自己的优势。不建议为你的应用程序使用AbsoluteLayout,因为它在你的设备上看起来很好,但在其他设备上可能会有所不同。 现在我们有了一个用户界面,让我们看看能否将资源绘制到屏幕上。 图纸使用帆布 在您可以在画布上绘制资源之前,您需要有一个用于绘制的资源并获得一个用于绘制的画布。画布是使用一个SurfaceView获得的,这是一个位于活动视图窗口下的可绘制表面。对于Androng,我实现了一个名为AndrongSurfaceView的类,它扩展了SurfaceView,与布局文件中提到的视图相同。下面的源代码显示了AndrongSurfaceView类的一部分。 隐藏,复制Code

public class AndrongSurfaceView extends SurfaceView
       implements SurfaceHolder.Callback
{
  private AndrongThread androngThread;
  private TextView statusText;
  private SurfaceHolder holder;
  private Context context;
  
  public AndrongSurfaceView(Context context, AttributeSet attrs)
  {
    super(context, attrs);
    this.context = context;
    this.holder = getHolder();
    holder.addCallback(this);
    setFocusable(true);
  }
  
  ......
  
}

通过调用SurfaceHolder上的lockCanvas方法,我们可以获得Canvas类的一个实例,该实例可用于处理表面上的像素。可以使用AndrongSurfaceView类中的getHolder()来检索SurfaceHolder。 隐藏,复制Code

Canvas canvas = surfaceHolder.lockCanvas(null);

资源可以通过应用程序的上下文获得。例如,下面的源代码在每一帧中绘制游戏的背景。 隐藏,复制Code

Bitmap backgroundImage = BitmapFactory.decodeResource(resources, R.drawable.background2);
canvas.drawBitmap(backgroundImage, 0, 0, null);

动画 在《Androng》中,球是活泼的。动画由12帧组成,每一帧旋转球30度。当球击中球拍或侧面时,动画反转。这在游戏中创建了一个漂亮的球的动画效果。 Android内置了对动画的支持。它具有动画属性,您可以在其中设置对象属性的起始值和结束值。还有使用AnimationDrawable的帧动画。帧动画显示了一个序列的图像在其定义的顺序。AnimationDrawable的动画必须用XML定义。 这个帧动画的问题是,您可以在XML文件中设置动画的持续时间,但不能设置动画的速度。在Androng中,我想要在球碰到什么东西时反转动画。因此我决定自己来实现这个动画。 隐藏,复制Code

public class Sprite
{
   protected DrawableResourceCollection drawableResourceCollection;
   private int currentFrame;

   public void draw(Canvas canvas)
   {
      drawableResourceCollection
         .get(currentFrame)
         .setBounds((int) xPosition, 
                    (int) yPosition, 
                    (int) xPosition + getWidth(), 
                    (int) yPosition + getHeight());
      drawableResourceCollection.get(currentFrame).draw(canvas);
      currentFrame = GetNewFrame();
   }
   
   ...
}

Sprite是用于所有可动画对象的基类,如球和拍子。每个精灵都有一个DrawableResourceCollection,它由一个可绘制资源列表组成。 隐藏,复制Code

public class DrawableResourceCollection extends LinkedList

当调用Sprite的draw方法时,从DrawableResourceCollection中检索一个可绘制资源并将其绘制在画布上。GetNewFrame()决定了资源中下一个帧的索引。GetNewFrame()方法使用一个布尔型animationForward,顾名思义,它决定动画是向前还是向后移动。通过这种方式,我们可以很容易地通过将布尔值从真改变为假来逆转动画的方向,反之亦然。 帧速无关动画 Android运行在许多设备上,每个设备都有自己的硬件规范。这意味着运行您的应用程序的设备的处理速度将有所不同。这意味着精灵的动画速度,比如Androng游戏中的球,会因设备而不同。这是一个不希望出现的情况,游戏玩法可能因设备而异。因此,我们想让游戏的动画独立于设备的处理能力。我们通过将时间合并到应用程序中并指定像素移动或每时间的动画速度来实现这一点。 从Android获得高分辨率计时 在安卓系统中有三种不同的获取时间的方法: elapsedRealtime upTimeMillis currentTimeMillis () () () 第一个是System.currentTimeMillis(),表示自纪元以来的毫秒数。基于Unix系统的纪元是1970年1月1日。这显然取决于设备的当前时间;当时间切换由于电话网络同步或由于用户的行动,这个数字跳回或回。 第二个是System.upTimeMillis(),是自设备启动以来的毫秒数。这个时钟在设备进入睡眠模式时停止,但不受时间变化的影响。 第三个也是最后一个选项是elapsedRealtime(),这也是自设备启动以来的毫秒数。与第二种选择的不同之处在于,当设备进入睡眠模式时,它会继续运行。 对于我们的帧速独立动画,我选择了upTimeMillis(),因为第一个选项可能会向前或向后跳跃,不利于计算帧速,而第三个选项会在游戏暂停时继续运行,这也是计算帧速的麻烦。下面的代码使用第二个选项计算每秒帧数。 隐藏,复制Code

while (isRunning)
{
  currentTimeInMillis = System.upTimeMillis();
  double timeNeededToDrawFrame = 
        (currentTimeInMillis - previousTimeInMillis) / 1000;
  previousTimeInMillis = currentTimeInMillis;
  DrawFrame(time);
  UpdatePhysics(timeNeededToDrawFrame);
}

结果,变量timeNeededToDrawFrame被发送给构成游戏屏幕的所有对象。例如,球收到这个球就有一个speed每秒两个水平像素。通过将它与绘制这个框架所需的时间相乘,我们就得到了球应该移动的像素数。垂直速度也是如此。这使帧速度独立动画。 碰撞检测 大多数(如果不是所有的话)游戏都需要碰撞检测。有很多方法可以检测两个游戏物体是否碰撞。和rong结合了边界盒和像素方法。 包围盒碰撞检测 下面的图可以很容易地说明边界框法: 下面的算法检测每个精灵周围的虚拟盒子是否重叠: 隐藏,复制Code

if (bottom1 < top2)
  return false;
if (top1 > bottom2)
  return false;
if (right1 < left2)
  return false;
if (left1 > right2)
  return false;

//bounding box do overlap

边界盒碰撞检测算法是检测碰撞的一种快速方法,但是如果形状不是矩形的,比如球,我们可能会得到误报。例如,在下面的情况下,边界框检测会检测到一个碰撞,而实际上并没有碰撞。 像素完美的检测 我们可以通过为球使用一个边界圆来解决这个问题,但是我想使用一个更通用的方法来解决这个问题,使用精灵的像素。因此,当边界盒算法检测到一个碰撞时,我们扫描两个精灵的重叠像素。如果两个精灵的相同位置包含一个像素(颜色!= 0),就会发生碰撞。 算法确定重叠框的宽度和高度,以及该框在每个精灵中的位置。该算法扫描盒子中的每个像素来寻找碰撞。可以通过调用Drawable上的getBitmap()方法从位图中读取像素。有关完整的碰撞检测例程,请参阅源代码中sprite类的collideswith方法。 健全的管理 当球与球拍、墙壁碰撞,或者球员得分时,他就会发出声音。用Android播放声音很简单。媒体播放器或声音池类可以播放声音。我使用声音池类是因为它们提供了更多的灵活性。使用声音池类播放声音涉及到AudioManager;AudioManager是一个所谓的Android系统服务。下面的源代码显示了如何获得AudioManager系统服务和播放媒体文件“hit”。击球是当球击中球拍或侧面时进行的。 隐藏,复制Code

AudioManager mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
SoundPool mSoundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
mSoundPool.play(R.raw.hit, streamVolume, streamVolume, 1, 0, 1);

游戏的所有声音都被预先加载到一个SoundPoolMap中,并从这个SoundPoolMap中播放。所有的声音管理方法被归为一个类SoundManager;请参阅该类的源代码。这个类基于Stephen Flockton提供的一个例子,他写了一个关于Android开发的博客。 输入管理 游戏可以通过Android设备的触摸屏来控制,这被称为触摸模式。如果你用手指触摸屏幕上的一个按钮,就会激活触摸模式。处理触摸模式事件就像覆盖表面视图的onTouchEvent一样简单。onTouchEvent的签名如下所示: 隐藏,复制Code

public boolean onTouchEvent(MotionEvent event)

当您用一个或多个手指触摸屏幕时,该事件将被抛出。MotionEvent参数有一个名为getPointerCount()的方法,它返回放置在屏幕上的手指的数量。不过,这实际上取决于设备的性能和Android版本。Androng有两个玩家模式,两个玩家可以用他们的手指在同一设备上对抗对方。在此模式中,使用getPointerCount()方法。如果有两根手指触摸设备屏幕,食指= 0表示食指,食指= 1表示食指。在事件上使用getX()或getY()方法中的索引,可以确定手指的位置。 隐藏,复制Code

float xPosition1 = event.getX(pointerIndex);
float yPosition1 = event.getY(pointerIndex);

xPosition1和yPosition1用于将球拍放置在屏幕上。 通知 Androng使用Toast通知来告诉用户某些事件,比如如何开始游戏以及哪个玩家赢了游戏。吐司通知是弹出在窗口表面的消息。消息会自动淡入和淡出,并在屏幕上停留预定的时间。下面的代码在Androng游戏屏幕上放置了一个toast通知。 隐藏,复制Code

Toast toast = Toast.makeText(context, "Select Menu for a new game.", Toast.LENGTH_LONG);
toast.show();

常数吐司。根据Android文档,LENGTH_LONG会告诉Android文本通知应该长时间显示。默认的,面包。LENGHT_LONG对应于3.5s。Show()将在屏幕上实际显示文本。 应用程序生命周期管理 由于Android是针对移动设备的操作系统,因此需要特别注意对这类设备稀缺资源的管理。每个Android应用程序都运行在自己的进程中,并能够执行特定的任务。一个任务可以由多个活动组成。每个Android应用程序都应该管理应用程序的生命周期。例如,Android操作系统可能会在需要时决定暂停或销毁应用程序额外的资源。因此,您的应用程序应该能够在需要时保存并恢复其状态。 活动的生命周期 如前所述,Android应用程序由活动组成;在这些活动中,您应该管理应用程序的生命周期。启动或重新启动应用程序有三种可能的场景。 oncreateonrestart& nbsp; onstartostartonresumeonresume 所有这些“on*”方法都是活动的一部分。当您正常启动应用程序时,会出现重新启动场景。重新启动的场景发生在Android停止你的活动之后,就在它重新开始之前。最后一种情况,即从暂停中重启,发生在系统准备开始恢复之前的活动时。当另一个应用程序到达前台时,您的活动将暂停。这显示了应用程序生命周期的总体图形概述。 注意,当您更改Android设备的方向时,您的应用程序将重新启动。 对于Androng的第一个版本,我决定在游戏被Android操作系统破坏时不保存游戏状态。当游戏暂停并重新启动时,我只是重新启动Androng游戏线程。 重新启动线程 游戏画面的绘制和物理计算在单独的线程上运行。这个AndrongThread来源于Thread。 隐藏,复制Code

public class AndrongThread extends Thread
{  
  @Override
  public void run()
  {
    long startTime = SystemClock.uptimeMillis();
    while (isRunning)
    {    
      ...
    }
  }
  
  ...
}

线程在一个循环中连续运行,该循环由一个布尔正在运行。当程序停止时,isRunning布尔值设置为false,线程停止运行。在由Android OS调用的方法surfaceDestroyed中,我使用了Join()语句等待。 隐藏,复制Code

public void surfaceDestroyed(SurfaceHolder surfaceHolder)
{
  androngThread.setRunning(false);
  boolean retry = true;
  while (retry)
  {
    try
    {
      androngThread.join();
      retry = false;
    }
    catch (InterruptedException e)
    {
    }
  }
}

该方法使用setRunning方法将布尔值isRunning设置为false,这将停止线程的运行。接下来,代码调用androngThread.join(),根据文档,它阻塞当前线程,直到接收方完成执行并死亡。 这正是我想要的行为。然而,在重新启动或恢复应用程序期间,我得到错误“线程已经开始”,而试图(重新)启动线程。join语句似乎成功了,但没有停止线程。在thread类上还有其他可用的方法,比如stop()和destroy(),但是根据文档,它们都不推荐使用,不应该使用。我决定在创建线程时解决这个问题。 下面的代码显示了我的解决方案;它不优雅,但很有用。 隐藏,复制Code

public void surfaceCreated(SurfaceHolder surfaceHolder)
{
  androngThread.setRunning(true);
  try
  {
    androngThread.start();
  }
  catch (Exception error)
  {
    androngThread = CreateNewAndrongThread();
    androngThread.start();
    androngThread.setRunning(true);
  }
}

该方法尝试启动线程;当它失败时,异常处理程序创建一个新线程并启动新创建的线程。 安卓市场 我想在Android市场上发布Androng。Android市场是Android应用程序的开放发行平台。开放意味着你的申请不受监管,没有审批流程。应用程序的可见性取决于客户对您的评价。 Android Market并不是Android应用程序的唯一发行平台;另一个出版渠道是亚马逊。目前,亚马逊市场只对来自美国的顾客开放。 在你可以在Android市场上发布你的游戏之前,你需要一次性支付25美元的注册费用。除此之外,对于在Android市场上销售的每一个应用程序,谷歌都会收取30%的费用。这在我看来是合理的与其他出版渠道。下面的图片显示了Androng的publisher屏幕。Androng是免费的,可以从Android市场下载。 IntelliJ的社区版提供了打包应用程序的机会。在发布应用程序之前,必须使用公钥组合对应用程序进行签名。应用程序的更新必须使用相同的密钥进行签名。这个带有.apk扩展名的包可以在Android市场上发布。你必须在发布前填写一些字段,如描述和一些截图,徽标等。如果你有Android手机,你可以在这里通过Android market下载游戏。 Android market的好处是你可以深入了解你的应用程序的用户。它显示用户是否得到任何错误,他们使用的Android版本,以及他们拥有的设备类型。例如,下面的屏幕截图显示了已经下载并运行的设备的类型。 源代码 下面是游戏的源代码;如果您使用IntelliJ的社区版本,您可以打开这个项目文件。否则,您可以打开单独的源代码或Java文件。 下一个版本 对于下一个版本的Androng,我计划了以下特性,没有特别的顺序。 使用一个真正的物理引擎,如Box2d或和enginehigh score list,与中央存储在Google-guice的webUsage依赖注入增加了移动蝙蝠的自由,支持Android版本1.6创建一个推广视频 历史 1日2011年5月 最初的帖子。 本文转载于:http://www.diyabc.com/frontweb/news30723.html

你可能感兴趣的:(Androng,一个针对Android的Pong克隆)