在cocos2d-x里面手势识别

在本教程中,即将带来的是在cocos2d-x里面使用手势识别,然后写一个带有手势识别的游戏。该游戏的功能是,当手势是↑,↓的时候,忍者就跳跃,当手势是↓,↓,↑的时候,忍者发射一颗子弹,当手势是↑,↓,↓的时候,忍者发射一个飞镖。呵呵,什么乱七八糟的动作,不过,手势识别并入游戏还是很不错的感觉。
  如果你对cocos2d-x编程不了解,可以到我博客上找相关的文章。本教程假定你已经学过前面的《用cocos2d-x做一个简单的windows phone 7游戏》系列教程,或者拥有同等的经验。
  现在新建一个工程,命名为cocos2dGestureDemo,和前面的教程一样,openXLive不需要,并且也添加了需要的Lib和修复之。下载:http://dl.dbank.com/c0c4orbdka,并且添加所以图片到Image文件夹。


  现在先添加一个类GestureScene到Classes。并且让之继承于CCScene。修改代码如下:


   class GestureScene:CCScene
    {
        public GestureScene()
        {
            this.addChild(GestureLayer.node());
        }
    }


    class GestureLayer : CCLayer
    {
        public override bool init()
        {
            if (!base.init())
                return false;
            return true;
        }


        public static new GestureLayer node()
        {
            GestureLayer layer = new GestureLayer();
            if (layer.init())
                return layer;
            return null;
        }
    }

并且修改AppDelegate的Launch方法。

            //CCScene pScene = cocos2dGestureDemoScene.scene();
            GestureScene pScene = new GestureScene();
            //run
            pDirector.runWithScene(pScene);

现在,基本工作就做好了。

  现在来说下手势识别的问题吧。在cocos2d-x,没有封装关于手势识别的东西,没办法,我们只有用基础的XNA的手势识别了。关于XNA的手势识别,我也不想多讲,直接看台湾的MSDN的文章吧。这里讲得不错。不过是繁体字,应该都能看懂的吧。http://msdn.microsoft.com/zh-tw/windowsphone/gg490792.aspx

那么,现在来添加手势识别吧。在GestureLayer类中添加一个声明:
        
CCLabelTTF title;

接着在init方法添加代码:

            title = CCLabelTTF.labelWithString("gesture", "Arial", 32);
            title.position = new CCPoint(400, 400);
            this.addChild(title);
            TouchPanel.EnabledGestures = GestureType.Hold | GestureType.Tap | GestureType.DoubleTap 
                | GestureType.FreeDrag | GestureType.Flick
             | GestureType.Pinch | GestureType.HorizontalDrag | GestureType.VerticalDrag;
            this.schedule(gestureRecognize);

添加一个方法:
   
     void gestureRecognize(float dt)
        {
            while (TouchPanel.IsGestureAvailable)
            {
                GestureSample gesture = TouchPanel.ReadGesture();
                switch (gesture.GestureType)
                {
                    case GestureType.DoubleTap:
                        {
                            title.setString("DoubleTap");
                            break;
                        }
                    case GestureType.DragComplete:
                        {
                            title.setString("DoubleTap");
                            break;
                        }
                    case GestureType.Flick:
                        {
                            title.setString("DoubleTap");
                            break;
                        }
                    case GestureType.FreeDrag:
                        {
                            title.setString("FreeDrag");
                            break;
                        }
                    case GestureType.Hold:
                        {
                            title.setString("Hold");
                            break;
                        }
                    case GestureType.HorizontalDrag:
                        {
                            title.setString("HorizontalDrag");
                            break;
                        }
                    case GestureType.None:
                        {
                            title.setString("None");
                            break;
                        }
                    case GestureType.Pinch:
                        {
                            title.setString("Pinch");
                            break;
                        }
                    case GestureType.PinchComplete:
                        {
                            title.setString("PinchComplete");
                            break;
                        }
                    case GestureType.Tap:
                        {
                            title.setString("Tap");
                            break;
                        }
                    case GestureType.VerticalDrag:
                        {
                            title.setString("VerticalDrag");
                            break;
                        }
                    default:
                        {
                            title.setString("default");
                            break;
                        }
                }
            }
        }

上面我们做了什么呢,如果看了上述提到的MSDN的文章应该都懂了,不过也来解释下吧。在上面,注册了TouchPanel的手势识别时间,在周期执行的gestureRecognize里面,先判断是否有手势没处理,如果有,就进行处理。
编译运行,你就能看到label的改变了。





具有手势识别的游戏

  接下来做点有趣的事情吧。做我们想要的拥有手势识别功能的游戏。曾记得,NDS的游戏就是如此手势识别,什么上上下干什么动作,下下上做什么动作,。。。。那么来实现下吧。

我们所有的操作都在GestureLayer这个层上面。

先添加些声明:

        Dictionary<string, List<string>> gestureAction;
        List<List<string>> matchGesture;
        float intervalTime = 0;//动作间隔时间
        int gestureTime = 0;//第几次动作
        float recognizeTime = 0.2f;//识别时间
        CCSprite player;

上面定义了些变量,我们怎么做手势识别呢,Dictionary定义了所有动作的集合,matchGesture来保存匹配的手势。intervalTime来判断上一动作和下一动作间隔的时间,如果时间超过0.8秒。那么就执行系列动作需要产生的效果。recognizeTime是一个识别动作间隔时间的常量,这个使动作间隔稳定些,使动作更容易判断。

 因为我们只需要竖直方向的移动的手势识别。那么添加并且修改init方法:

            initGestureLib();
            TouchPanel.EnabledGestures = GestureType.VerticalDrag;
            player = CCSprite.spriteWithFile(@"images/Player");
            player.position = new CCPoint(100, 200);
            this.addChild(player);
            this.schedule(gestureRecognize);

上面我们添加了一个player,并且修改手势注册只有竖直方向的移动。

修改gestureRecognize方法并且添加一个方法:

        void gestureRecognize(float dt)
        {
           while (TouchPanel.IsGestureAvailable)
            {
                GestureSample gesture = TouchPanel.ReadGesture();
                switch (gesture.GestureType)
                {
                    case GestureType.VerticalDrag:
                        {
                            break;
                        }
                    default:
                        break;
                }
            }
        }


void initGestureLib()
        {
            gestureAction = new Dictionary<string, List<string>>(); ;
            matchGesture = new List<List<string>>();
            gestureAction["projectile"] = new List<string>() { "↑", "↓", "↓" };
            gestureAction["jump"] = new List<string>() { "↑", "↓" };
            gestureAction["bullet"] = new List<string>() { "↓", "↓", "↑" };
        }

上面,我们初始化了手势动作的集合并且修改了gestureRecognize方法只能识别竖直移动。


下面,我们要对手势来进行识别,要怎么做呢,首先,应该识别手势是上还是下的。然后先判断如果是第一个动作,从gestureAction里面找匹配的并且添加到matchGesture里面,不是第一个动作的话,就从matchGesture里面寻找list中该位置是否和这个动作一致,不一致就从matchGesture中移除。简单来说,动作的识别就是一遍遍遍历matchGesture,不一致的移除,剩下的就是和动作匹配的啦。接下来添加一些方法。


 
       /// <summary>
        /// 收集动作并且做动作的筛选
        /// </summary>
        /// <param name="gesture"></param>
        void collectGesture(GestureSample gesture)
        {
            string gestureName;
            if (gesture.Delta.Y > 0)
                gestureName = "↓";
            else
                gestureName = "↑";
            if (gestureTime == 0 && matchGesture.Count != 0)
            {
                matchGesture.Clear();
            }
            Debug.WriteLine(String.Format("gestureName:{0},GestureTime:{1},gesture.y:{2},x:{3}",
                gestureName, gestureTime, gesture.Delta.Y, gesture.Delta.X));
            if (gestureTime == 0)
            {
                foreach (var item in gestureAction.Values)
                {
                    if (item[0].Equals(gestureName))
                    {
                        matchGesture.Add(item);
                    }
                }
            }
            else
            {
                if (matchGesture.Count <= 0)
                {
                    gestureTime = 0;
                    intervalTime = 0;
                    return;
                }
                List<List<string>> toDelete = new List<List<string>>();
                foreach (var item in matchGesture)
                {
                    if (item.Count <= gestureTime)
                    {
                        toDelete.Add(item);
                        continue;
                    }
                    if (!item[gestureTime].Equals(gestureName))
                    {
                        toDelete.Add(item);
                    }
                }
                foreach (var item in toDelete)
                {
                    matchGesture.Remove(item);
                }
            }
            gestureTime++;
            intervalTime = 0;


        }


        /// <summary>
        /// 执行相应的动作
        /// </summary>
        void gestureDoAction()
        {
            if (matchGesture.Count <= 0)
            {
                intervalTime = 0;
                gestureTime = 0;
                return;
            }
            foreach (var item in matchGesture)
            {
                if (item.Count == gestureTime)
                {
                    foreach (var key in gestureAction.Keys)
                    {
                        if (gestureAction[key] == item)
                        {
                            playerDoAction(key);
                            break;
                        }
                    }
                    break;
                }
            }
            gestureTime = 0;
            matchGesture.Clear();
        }


        void playerDoAction(string action)
        {
            if (action == "projectile")
            {
                CCSprite projectile = CCSprite.spriteWithFile(@"images/Projectile");
                projectileFly(projectile);
            }
            else if (action == "jump")
            {
                player.runAction(CCJumpBy.actionWithDuration(0.5f, new CCPoint(0, 0), 40, 3));
            }
            else if (action == "bullet")
            {
                CCSprite bullet = CCSprite.spriteWithFile(@"images/bullet");
                projectileFly(bullet);
            }
        }


        void projectileFly(CCSprite projectile)
        {
            projectile.position = new CCPoint(player.position.x, player.position.y);
            projectile.runAction(CCSequence.actions(CCMoveTo.actionWithDuration(0.7f, new CCPoint(CCDirector.sharedDirector().getWinSize().width, player.position.y)),
                CCCallFuncN.actionWithTarget(this, spriteMoveDone)));
            this.addChild(projectile);
        }




        void spriteMoveDone(object sender)
        {
            CCSprite sprite = sender as CCSprite;
            this.removeChild(sprite, true);
        }



上面的方法做了些什么呢,如果仔细看,应该能看出,收集动作并且做动作筛选,主要做法就是遍历和匹配,每次收集动作结束的时候,要把intervalTime置为0,并且把当前动作个数加1.

当要完成动作效果gestureDoAction的时候,就要先判断matchGesture里面的东西了,是否为空,或者没有最匹配的(也就是里面的list的count没有和gestureTime相同,就没有最合适的动作序列)。

如果有,就执行动作,先从Dictionary里面取出相对应的key。然后开始执行相应的动作,下面的方法应该都很熟悉了,就是执行了一些动作。
下面,主要的来了。修改gestureRecognize如下:
 
       void gestureRecognize(float dt)
        {
            intervalTime += dt;
            if (intervalTime >= 0.8f)
            {
                intervalTime = 0;
                gestureDoAction();


            }
            while (TouchPanel.IsGestureAvailable)
            {
                GestureSample gesture = TouchPanel.ReadGesture();
                switch (gesture.GestureType)
                {
                    case GestureType.VerticalDrag:
                        {
                            if (gesture.Delta.Y != 0 && intervalTime > recognizeTime)
                            {
                                collectGesture(gesture);
                            }
                            break;
                        }
                    default:
                        break;
                }
            }
        }

上面做了些什么呢,先判断intervalTime,也就是距离上次动作的时间,如果大于0.8秒,那么就执行相应系列动作的效果,不然,如果gesture的Delta值的Y值不等于0(我经过检测,发现即使在竖直移动,Y值也可能等于0),并且intervalTime大于一定值,就收集动作,为什么呢,因为我发现,如果一开始就搜集手势,手势就多而且有可能不是需要的,因为基本第一触点的效果不好,而且大家都不太在乎。

那么现在可以来运行下了。在模拟器上,用鼠标代替手指划,是不是已经达到想要的效果了呢,如果没有,请观察Debug窗口,那里应该输出了你的手势动作的相关信息,查看是否是想要的手势。

现在在模拟器已经能不错的识别手势动作了。但是在真机上呢,在我的T8788上面,手势动作的识别效果不太好,毕竟手和鼠标的动作效果不太一样。鼠标的效果还不错主要是我多次尝试调节intervalTime,recognizeTime数值的效果,我想,要在真机上很好的识别,也要多次尝试调节这两个数值,或者增加更好的手势的获取和判断方法。

图片就不上了,毕竟动作难以以图片捕捉。

本次示例代码下载:http://dl.dbank.com/c0yla46khj


 何去何从:
  • 增加更有趣的动作。
  • 增加更复杂的动作
  • 使手势收集和筛选的算法更强大更快。
  • 。。。。。
 


 


 


 


 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章