`

android游戏角色的速度控制

阅读更多
在游戏中要控制角色的移动速度是个比较麻烦的事情,至少对于新人来说,确实是这样的。比如拿贪吃蛇来说,要控制蛇的移动速度,甚至随着关数的增加,还需要加快蛇的移动速度,这个东西怎么来实现的呢?大体想法还是这样,在while循环里判断当前时间是否已经达到角色移动一步所需要的时间,如果达到则更新角色坐标,再更新画面。
如果这样的话,我也就不用单独写博客了,这里主要是以《beginning android games》的贪吃蛇为例,最近发现这本书确实写得很好。作者将游戏的模块封装得很好,很值得学习与借鉴。
关于游戏角色的画面更新有一个很重要的类AndroidFastRenderView:
public class AndroidFastRenderView extends SurfaceView implements Runnable {
    AndroidGame game;
    Bitmap framebuffer;
    Thread renderThread = null;
    SurfaceHolder holder;
    volatile boolean running = false;
    
    public AndroidFastRenderView(AndroidGame game, Bitmap framebuffer) {
        super(game);
        this.game = game;
        this.framebuffer = framebuffer;
        this.holder = getHolder();
    }

    public void resume() { 
        running = true;
        renderThread = new Thread(this);
        renderThread.start();         
    }      
    
    public void run() {
        Rect dstRect = new Rect();
        long startTime = System.nanoTime();
        while(running) {  
            if(!holder.getSurface().isValid())
                continue;           
            
            float deltaTime = (System.nanoTime()-startTime) / 1000000000.0f;
            startTime = System.nanoTime();

            game.getCurrentScreen().update(deltaTime);
            game.getCurrentScreen().present(deltaTime);
            
            Canvas canvas = holder.lockCanvas();
            canvas.getClipBounds(dstRect);
            canvas.drawBitmap(framebuffer, null, dstRect, null);                           
            holder.unlockCanvasAndPost(canvas);
        }
    }

    public void pause() {                        
        running = false;                        
        while(true) {
            try {
                renderThread.join();
                break;
            } catch (InterruptedException e) {
                // retry
            }
        }
    }        
}

这个类里面有很多值得学习或者注意的东西。
1.这个类继承了SurfaceView类,这个SurfaceView也是一个View类,主要作用是在开启另外一个线程,并通过GPU等硬件加速功能将视图渲染显示出来。基本上所有的UI组件显示最好的方法最是单独开启线程显示,这样就不会因独占主线程而导致界面不流畅。
   由于AndroidFastRenderView本身继承了Runnable接口,所以它是一个线程,其最重要的逻辑放在run方法内。
2.要使用画图功能时,需要用到Canvas,这个Canvas不是像平时那样直接通过new Canvas()而得到,而是通过SurfaceHolder的lockCanvas方法而来,而得到SurfaceHolder的实例是通过父类的getHolder()方法。在画图时,先由holder.lockCanvas()得到Canvas实例,将所有图画完这后再由holder.unlockCanvasAndPost(canvas)统一提交,这时才开始所有的绘图工作。这样一次性处理既可以利用GPU加速,也可以使用缓冲技术,以此达到性能优化的目的。
3.还有一点需要注意,那就是running字段,这个字段的作用是判断游戏当前是否在运行,需要用volatile关键字申明。这样能保证running的值每次都从主存中读取而不是来自本地内存中。

上面这个类非常关键,但它只保证不停的执行update与present方法,并不能达到精确控制角色移动速度的目的。而控制速度的方法是由World类的update完成的。
。。。
public void update(float deltaTime) {
        if (gameOver)
            return;

        tickTime += deltaTime;

        while (tickTime > tick) {
            tickTime -= tick;
            snake.advance();
            if (snake.checkBitten()) {
                gameOver = true;
                return;
            }

            SnakePart head = snake.parts.get(0);
            //头部接触到stain时
            if (head.x == stain.x && head.y == stain.y) {
                score += SCORE_INCREMENT;
                snake.eat();
                if (snake.parts.size() == WORLD_WIDTH * WORLD_HEIGHT) {
                    gameOver = true;
                    return;
                } else {
                    placeStain();
                }

                //增加移动速度
                if (score % 100 == 0 && tick - TICK_DECREMENT > 0) {
                    tick -= TICK_DECREMENT;
                }
            }
        }
    }
。。。


上面方法中,deltaTime表示AndroidFastRenderView执行每次循环所需要的时间,tickTime用于存储移动一步所需要累计的时间,如果达到累计时间tickTime达到移动一步所需要的时间tick,则将蛇的坐标更新一次,然后等待下次达到再更新,这样一直循环下去。

总结,虽然控制角色移动本身是件很简单的事,但要用于实战之中,做到模块的分化,性能的优化等还是很有难度的。在此把贪吃蛇的源码也放上来吧。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics