android游戏开发的技术实现分析

       在android开发中,你可能会有想要开发一个小游戏的冲动,那么用android来开发游戏如何实现呢?幸运的是,google提供了一些已经开发好的游戏实例.我们从他的两个游戏实例入手来探究探究.

      对于轻量级的小游戏,其游戏的核心显示内容,我们可以写一个自己的view来实现!然后以一定的频率刷新这个view,我们调用view的invalidate()来实现.具体的我们来看看一个大家常见的游戏:Snake(贪吃蛇),下面来分析一些实现这个游戏的关键代码.

      和其他app一样,Snake也由一个Activity开始的,同样的通过setContentView(R.layout.snake_layout);这行代码来确定显示内容的布局,这里有必要看看这个布局文件的内容(如下):

<merge xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res/com.example.android.snake">

  <com.example.android.snake.BackgroundView
    android:id="@+id/background"

    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:colorSegmentOne="@color/muted_red"
    app:colorSegmentTwo="@color/muted_yellow"
    app:colorSegmentThree="@color/muted_blue"
    app:colorSegmentFour="@color/muted_green"
     />

  <com.example.android.snake.SnakeView
    android:id="@+id/snake"
    android:background="@android:color/transparent"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:tileSize="24dp" />

  <TextView android:id="@+id/text"
  android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center"
    android:gravity="center"
    android:textColor="@color/text_violet"
    android:textSize="24sp"
    android:visibility="visible" />

  <RelativeLayout android:id="@+id/arrowContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:visibility="gone">

    <ImageView android:id="@+id/imageUp"
    android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignParentTop="true"
      android:layout_centerHorizontal="true"
      android:padding="20dp"
      android:src="@drawable/dpad_up" />

    <ImageView android:id="@+id/imageLeft"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignParentLeft="true"
      android:layout_centerVertical="true"
      android:padding="20dp"
      android:src="@drawable/dpad_left" />

    <ImageView android:id="@+id/imageRight"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignParentRight="true"
      android:layout_centerVertical="true"
      android:padding="20dp"
      android:src="@drawable/dpad_right" />

    <ImageView android:id="@+id/imageDown"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignParentBottom="true"
      android:layout_centerHorizontal="true"
      android:padding="20dp"
      android:src="@drawable/dpad_down" />
  </RelativeLayout>

</merge>
        上面的代码,我们首先需要注意的是这个布局文件采用了merge来限定:因为我们的布局是framelayout,所以这里就采用了merge,因为任何加入activity的view的父groupview都是framelayout.

         这里我们重点关注的时SnakeView,这个view就是实现这个游戏的核心.这里需要注意这一行代码:app:tileSize="24dp".这是SnakeView自己定义的一个属性,用于定义贪吃蛇的大小.能够这样使用,你需要先在attrs.xml里面为SnakeView定义这样的属性,看看这里时如何定义的:

  <declare-styleable name="TileView">
    <attr name="tileSize" format="dimension" />
  </declare-styleable>
          你肯定会奇怪,怎么是TileView呢? 其实SnakeView是TileView的子类,而TileView是view的子类.

        所以我们重点来看看SnakeView的实现.(贪吃蛇的具体实现就不设计,这里只是介绍一个游戏开发的实现核心).其实游戏的动画效果就是反复的调用invalidate来促使view重现绘制,通过重写View的onDraw(Canvas canvas)来实现.这样以来,当调用invalidate(),就会触发view自己回调onDraw(Canvas canvas),而游戏每一次需要显示的内容都由onDraw来绘制.

         下面是定时刷新view的代码:

    class RefreshHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            SnakeView.this.update();
            SnakeView.this.invalidate();
        }

        public void sleep(long delayMillis) {
            this.removeMessages(0);
            sendMessageDelayed(obtainMessage(0), delayMillis);
        }
    };

       上面定义的是一个handle,该handle起到一个定时器的作用,每隔delayMillis这么长的时间,来更新update()需要下次绘制的内容,然后invalidate()来刷新.

        下面是他的onDraw的实现:

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int x = 0; x < mXTileCount; x += 1) {
            for (int y = 0; y < mYTileCount; y += 1) {
                if (mTileGrid[x][y] > 0) {
                    canvas.drawBitmap(mTileArray[mTileGrid[x][y]], mXOffset + x * mTileSize,
                            mYOffset + y * mTileSize, mPaint);
                }
            }
        }

    }
        通过上面代码可以知道,其实就绘制了一些图片,这些图片按照一定的坐标就组成来一条蛇.所以每次只要mTileGrid里面的信息准确其绘制出的图像就是准确的.

         对于像贪吃蛇/象棋等这样的轻量级游戏我们可以这么做,不过对于一些格斗类的游戏就不太实用了,当要绘制大量的图片资源/铃声资源的时候就满足不了需要.这个时候我们需要用到一个特殊的view:SurfaceView

           还好,google给我提供了一个实例:JetBoy.下面我们来看看如何使用SurfaceView.

           (1)首先,你写的SurfaceView需要实现这个接口SurfaceHolder.Callback, 其目的是我们需要获取我们的SurfaceView的状态变化(创建成功/大小变化/销毁).如下代码:

public class JetBoyView extends SurfaceView implements SurfaceHolder.Callback {
          (2)  我们需要得到  SurfaceView的SurfaceHolder,并且使用SurfaceHolder.Callback,你需要在构造方法里面完成,如下代码:

    public JetBoyView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // register our interest in hearing about changes to our surface
        SurfaceHolder holder = getHolder();
        holder.addCallback(this);

            (3)由于,我们需要绘制大量的内容,所以我们一般需要开启一个线程来做这些工作.下面来详细介绍其中缘由: 我们通过上面获得的SurfaceHolder就可以获得这个SurfaceView的Canvas,所以只要我们用这个Canvas来绘制我们需要的东西即可,这些工作我们都是在我们定义的线程里面来完成的,当然这一切都应该在SurfaceView创建完成以后(surfaceCreated(SurfaceHolder arg0) 回调以后)才能完成.下面来看thread里面的run里面核心代码:

        public void run() {
            // while running do stuff in this loop...bzzz!
            while (mRun) {
                Canvas c = null;

                ...
				...
				...

                try {
                    c = mSurfaceHolder.lockCanvas(null);
                    // synchronized (mSurfaceHolder) {
                    doDraw(c);
                    // }
                } finally {
                    // do this in a finally so that if an exception is thrown
                    // during the above, we don't leave the Surface in an
                    // inconsistent state
                    if (c != null) {
                        mSurfaceHolder.unlockCanvasAndPost(c);
                    }
                }// end finally block
            }// end while mrun block
        }

           上面代码通过mSurfaceHolder.lockCanvas(null);来获取我们SurfaceView的canvas, 然后绘制内容,绘制完成指向这行代码mSurfaceHolder.unlockCanvasAndPost(c);

          (4)所以我们需要在surfaceCreated的时候开始我们的游戏:

    public void surfaceCreated(SurfaceHolder arg0) {
        // start the thread here so that we don't busy-wait in run()
        // waiting for the surface to be created
        thread.setRunning(true);
        thread.start();
    }
          在surfaceDestroyed的时候结束我们的线程:

    public void surfaceDestroyed(SurfaceHolder arg0) {
        boolean retry = true;
        thread.setRunning(false);
        while (retry) {
            try {
                thread.join();
                retry = false;

            } catch (InterruptedException e) {
            }
        }
    }

其实都很简单的大家使用一下就明白的.

         

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。