人机交互实验:Android开发之人物移动、地图滑动、传感器、触屏的应用

Android开发,人物移动与地图滑动,传感器、触屏的应用

1.程序介绍

这是我们人机交互课的实验,老师对我们的要求如下:

  • 1.能绘制出二维卷轴地图,及人物可在地图上各方向移动
  • 2.在第1次的基础上加入交互,用手指控制人物的移动和地图的缩放等,实现一指交互、二指交互。
  • 3.在之前的基础上,实现使用手机上2种感应器在程序中的作用,例如重力感应器、GPS、加速度感应器等
    先给个程序的截图吧~

2.人物移动和地图滑动

参考该博客Android游戏开发之主角的移动和地图的平滑滚动
我程序的框架是参考该博客中的程序,它已经实现了地图的绘制、用键盘控制人物的移动,所以其它的功能我是在该程序的基础上实现的。
其余的不说了,先说说原来程序的不足之处

2.1 源程序引用数值较多

这是我很不喜欢源程序的地方,因为很多地方用的直接是数值,一方面很难让人知道该数值的含义,另一方面也使得若换不同的地图、不同屏幕像素的手机等,得重新改动,比如UpdateHero方法中,如何判断接下来是人物移动还是地图滑动,在if中用了很多数值,一开始看的话,你根本不知道这些数值代表什么!
在我的程序中,我多定义了些变量,由变量算出这些数值,这样换地图的话只要更改地图的宽高,换手机的话就什么都不用改。给出前后代码的对比。

//原来的人物往下移动的处理
if (mHeroScreenY >= 320) {
    if (mHeroIndexY >= 10 && mHeroIndexY <= 20) {
        mMapPosY -= HERO_STEP;
    } else {
        mHeroScreenY += HERO_STEP;
    }
} else {
    mHeroScreenY += HERO_STEP;
}

/*
 * 我对人物往下移动的处理
 * 先对代码做一些解释,当然,数值是来源于原来代码中的。
 * 地图上下移动有个范围,地图上界即屏幕上界~地图下界即屏幕下界 
 * 地图最上端为屏幕最上端时,320/32=10
 * 地图最下端为屏幕最下端时,(800-160)/32=20
 * 但若<=20,则地图最下面会有一小部分重复往上动,走到那里会出现多个人 改成17后,就没事了。
 * 这是因为有的手机像素高度比较大(大于800),所以会出现最下面地图重复移动的情况
 */
if (mHeroScreenY >= h * 2) {
    if (mHeroIndexY >= h * 2 / 32
        && mHeroIndexY <= (SCENCE_HEIGHT - h) / 32) {
    //mMapPosY:地图的坐标,即人物不动,地图往上移动,也就是地图的Y坐标减小
        mMapPosY -= HERO_STEP;
    } else {
        mHeroScreenY += HERO_STEP;
    }
} else {
    // 人就在屏幕上往下移动
    mHeroScreenY += HERO_STEP;
}

2.2 源程序对碰撞处理不是很好

我参考的源程序对碰撞进行了处理,但是处理考虑不全面!在控制人物移动过程中,刚开始,嗯,不错,碰撞处理挺好的。可是当你人物继续移动,直到人物在屏幕上位置不变,而地图开始移动的时候,这是若碰到物体,你会发现竟然穿过去了!!!我通过用Log.v()方法输出想要的内容,调试了好久,终于被我发现了错误。

先看看对碰撞的处理:

if (mCollision[mHeroIndexY][mHeroIndexX] == -1) {
    mHeroPosX = mBackHeroPosX;
    mHeroPosY = mBackHeroPosY;
    mHeroScreenY = mBackHeroScreenY;
    mHeroScreenX = mBackHeroScreenX;
    //下面是我增加的
    mMapPosX = mBackmMapPosX;
    mMapPosY = mBackmMapPosY;
    isAcotrCollision = true;
} else {
    mBackHeroPosX = mHeroPosX;
    mBackHeroPosY = mHeroPosY;
    mBackHeroScreenX = mHeroScreenX;
    mBackHeroScreenY = mHeroScreenY;
    //下面是我增加的
    mBackmMapPosX = mMapPosX;
    mBackmMapPosY = mMapPosY;
    isAcotrCollision = false;
}

若发生碰撞,人物在屏幕、地图中的x、y坐标进行回退,即回退到前一次的。细心的童鞋可以发现,它还缺少了一种可能。那就是当人物过了屏幕宽或高的三分之二,实际上是地图在滑动了。针对该种情形下发生碰撞,源代码中并没对地图的x,y坐标进行回退!所以导致地图在滑动的时候,即使发生碰撞,地图还在滑动,这就导致人物穿墙的问题!

3.触屏控制

3.1 一指触屏控制人物移动

原先是通过键盘控制的,若要增加触屏控制人物移动,其实很好办,因为原先的代码都已经把框架给写好了,原文中通过下面四个变量,来判断人物是往哪里移动。

    /** 按键下 **/
    private boolean mIskeyDown = false;
    /** 按键左 **/
    private boolean mIskeyLeft = false;
    /** 按键右 **/
    private boolean mIskeyRight = false;
    /** 按键上 **/
    private boolean mIskeyUp = false;  

所以在触屏的代码中,我们只要根据手指不同的位置,通过原来代码中的下面方法:

public void setKeyState(int keyCode, boolean state) {
        switch (keyCode) {
        case KeyEvent.KEYCODE_DPAD_DOWN:
            mIskeyDown = state;
            break;
        case KeyEvent.KEYCODE_DPAD_UP:
            mIskeyUp = state;
            break;
        case KeyEvent.KEYCODE_DPAD_LEFT:
            mIskeyLeft = state;
            break;
        case KeyEvent.KEYCODE_DPAD_RIGHT:
            mIskeyRight = state;
            break;
        }
        mAllkeyDown = state;
}

用setKeyState()将对应方向的布尔变量设置为true,人自然而然就会往对应方向移动了。
一指触控的代码是实现了onTouchEvent()方法,该方法属于Activity,所以凡是继承了Activity的类,都可以覆盖该方法。
关于该方法,可以见Android开发教程: 触摸屏模拟实现方向键
怎么控制人物移动呢,我是通过获取手指的x,y坐标与人物在屏幕中的x,y坐标的差的绝对值,哪个大就在哪个方向上移动。具体见代码:

public boolean onTouchEvent(MotionEvent event) {
    //这是两指触控实现缩放的,在这里先注释掉
    //if (event.getPointerCount() == 2) {
    //  setMultiTouch(event);
    //}
    float x = event.getX();
    float y = event.getY();
    float dx, dy;
    float mX = mAnimView.mHeroScreenX, mY = mAnimView.mHeroScreenY;
    switch (event.getAction()) {
    // 触摸结束
    case MotionEvent.ACTION_UP:
        isFirst = true;
        oldRate = rate;
    // 手指离开屏幕,则人物不动,下面是我增加的方法,使所有方向都为false
        mAnimView.setKeyStateFalse();  
        return false;
    case MotionEvent.ACTION_DOWN:
        // mAnimView.setKeyStateFalse();
        dx = x - mX;
        dy = y - mY;
        int i,j;
        float adx=Math.abs(dx);
        float ady=Math.abs(dy);
        if (adx >= ady) { // move from left -> right
            //这里为什么>35,是因为我还要用<35的那些范围用于其它的功能实现。
            //后面的48,也是同样道理。
            if (dx > 35.0f) {
                mAnimView.setKeyState(KeyEvent.KEYCODE_DPAD_RIGHT, true);
            } else if (dx<-35.0f){
                mAnimView.setKeyState(KeyEvent.KEYCODE_DPAD_LEFT, true);
            }
        } else { // move from top -> bottom or bottom -> top
            if (dy > 48.0f) {
                mAnimView.setKeyState(KeyEvent.KEYCODE_DPAD_DOWN, true);
            } else if(dy<-48.0f){
                mAnimView.setKeyState(KeyEvent.KEYCODE_DPAD_UP, true);
            }
        }
        return true;
        /**
         * MOVE是介于MotionEvent.ACTION_DOWN和MotionEvent.ACTION_UP之间的,
         * 所以通过MOVE来控制人物移动 如果用MotionEvent.ACTION_DOWN的话,
         * 则至少在我的手机上,只能每次按一下走一步,太不方便了
         */
    case MotionEvent.ACTION_MOVE:
        dx = x - mX;
        dy = y - mY;
        if (Math.abs(dx) >= Math.abs(dy)) { // move from left -> right
            // or right -> left
            if (dx > 0.0f) {
                mAnimView.setKeyState(KeyEvent.KEYCODE_DPAD_RIGHT, true);
            } else {
                mAnimView.setKeyState(KeyEvent.KEYCODE_DPAD_LEFT, true);
            }
        } else { // move from top -> bottom or bottom -> top
            if (dy > 0.0f) {
                mAnimView.setKeyState(KeyEvent.KEYCODE_DPAD_DOWN, true);
            } else {
                mAnimView.setKeyState(KeyEvent.KEYCODE_DPAD_UP, true);
            }
        }
        return true;
    }
    return super.onTouchEvent(event);
}

3.2 二指实现地图缩放

《Android游戏编程之从零开始》里面有讲到,参考这个链接吧多触点实现图片缩放。我是通过上面中注释掉的setMultiTouch(event)方法来实现地图缩放的,详情的话就参加我的代码,本文最后会给出下载链接。 这里遇到一个问题,那就是当地图不断缩小时,屏幕上剩余部分会残留之前刷新的图片,所以每次缩小之前,得先清屏一下。android开发--Canvas清屏只需三句话

if (isShrink) {
    Paint p = new Paint();
    p.setXfermode(new PorterDuffXfermode(Mode.CLEAR));
    mCanvas.drawPaint(p);
    p.setXfermode(new PorterDuffXfermode(Mode.SRC));
}
//接下来可以通过mCanvas.drawColor(Color.WHITE)来改变背景颜色

4. 传感器的应用

4.1 加速度感应器的应用

我通过加速度感应器,实现了两个功能:1.控制人物移动,2.通过摇一摇换人物主角(牧场物语矿石镇的伙伴男女主人公)
参考下面两个文章:
Android游戏开发之小球重力感应实现
Android之摇一摇功能

4.2 光线传感器的使用

没办法, 老师要求要用两个传感器,可是发现可以用的传感器除了加速度感应,其它的实在没什么。。。所以勉强用了个光线传感器,若光线暗的话,就把背景从白色亮的变为灰色(保护眼睛)。。。真的是为了用而用。。。

两个传感器实现代码如下:

/*传感器相关变量
 *private SensorManager mSensorMgr = null;
 *Sensor mSensor = null;
 *Sensor mSensor2 = null;
 *mSensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
 *mSensor = mSensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
 *mSensor2 = mSensorMgr.getDefaultSensor(Sensor.TYPE_LIGHT);

 *mSensorMgr.registerListener(this, mSensor,SensorManager.SENSOR_DELAY_GAME);
 *mSensorMgr.registerListener(this, mSensor2,SensorManager。SENSOR_DELAY_GAME);
 */
public void onSensorChanged(SensorEvent event) {
    // 加速度传感器
    if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {  
        mGX = event.values[0];
        mGY = event.values[1];
        mGZ = event.values[2];
        setKeyStateFalse();
        if (Math.abs(mGX) >= Math.abs(mGY) && Math.abs(mGX) >= 1.0) {
            if (mGX >= 0) { 
                // 向左走
                setKeyState(KeyEvent.KEYCODE_DPAD_LEFT, true);
            } else {
                // 向右走
                setKeyState(KeyEvent.KEYCODE_DPAD_RIGHT, true);
            }
        } else if (Math.abs(mGX) < Math.abs(mGY) && Math.abs(mGY) >= 1.0) {
            // 这里之所以设置成一个大于6.0,一个小于3.0,是因为人拿手机的时候,手机基本上就是倾斜的,mGY肯定大于0.
            if (1.0 <mGY && mGY< 4.0) // 向上走
                setKeyState(KeyEvent.KEYCODE_DPAD_UP, true);
            else if (mGY >= 7.0) { // 向下走
                setKeyState(KeyEvent.KEYCODE_DPAD_DOWN, true);
            }
        }
        //摇一摇换主角    
        int minValue=19;
        if (Math.abs(mGX) > minValue || Math.abs(mGY) > minValue || Math.abs(mGZ) > minValue) {
            roleId=(roleId+1)%2;
        }   
    }
    //光线传感器
    else if(event.sensor.getType() == Sensor.TYPE_LIGHT){
        float lv=event.values[0];
        if(lv>10.0f){
            isNight=false;
        }
        else{
            isNight=true;
        }
    }
}

5.增加种植、收割功能

之前在3.1触屏控制人物移动的代码中提到,选取abs|dx|>35或者abs|dy|>48,才允许人物朝相应的方向移动。之所以留出人物周围一部分的范围,是用于种植一些农产品。就是当人物在农田中时,手指触摸人物一小部分范围内,可以种上东西。对种上东西的地方再触摸一次,就收割农产品。(说简单点,其实就是画上一个图片、消除一个图片。。。)
给个图:

这里用到了mMapAcotor数组,若值为-1,表示没有什么。若为0~5,对应6种不同的农产品。根据不同的索引值,在地图位置画上对应农产品的位图。在OnTouchEvent方法中增加的部分代码:

if(adx<=32.0f){
    //先算出手指触摸的地方在地图中对应的数组索引
    i=(-mAnimView.mMapPosX+(int)x)/mAnimView.TILE_WIDTH;
    j=(-mAnimView.mMapPosY+(int)y)/mAnimView.TILE_HEIGHT;
    if(mAnimView.mMapAcotor[j][i]==-1){
        //随机一个农产品
        int tmp=rand.nextInt(mAnimView.totOfProds);
        mAnimView.mMapAcotor[j][i]=tmp;
        //mAnimView.numOfProds[tmp]++;
    }
    else{
        mAnimView.isHarvest=true; //起不了作用,屏幕没有显示收割。。。
        //mAnimView.numOfProds[mAnimView.mMapAcotor[j][i]]--;
        mAnimView.mMapAcotor[j][i]=-1;
    }
}

6. 增加背景音乐设置

详细就见代码,不说了,给个截图吧

        


7. 源代码下载

猜你喜欢

转载自blog.csdn.net/sunny_xsc1994/article/details/47001855