• <cocos2dx for wp7>使用cocos2dx制作基于Tile地图的游戏(一)


    本教程基于子龙山人翻译的cocos2d的IPHONE教程,用cocos2d-x for XNA引擎重写,加上我一些加工制作。教程中大多数文字图片都是原作者和翻译作者子龙山人,还有不少是我自己的理解和加工。感谢原作者的教程和子龙山人的翻译。本教程仅供学习交流之用,切勿进行商业传播。

     子龙山人翻译的Iphone教程地址:http://www.cnblogs.com/zilongshanren/archive/2011/04/11/2012852.html

    Iphone教程原文地址:http://www.raywenderlich.com/1163/how-to-make-a-tile-based-game-with-cocos2d

    程序截图:

      在这个分为几部分教程中(我还没想好是几部分⊙﹏⊙b汗),我将会教大家如何使用cocos2d-x来做一个基于tile地图的游戏,当然还有Tiled地图编辑器。(我们小时候玩的小霸王小学机里面的游戏,大部分都是基于tile地图的游戏,如重装机兵,坦克大战、冒险岛、吞食天地等)我们将会创建一个忍者在沙漠中找东西吃的小游戏,并且被怪物追赶的游戏-_-

      在这第一部分教程中,我将教大家如何使用Tile来创建地图,怎样把地图加到游戏中,怎么让地图跟随玩家滚动,以及怎样使用对象层。

      如果你还没有准备好的话,你可能需要先从用cocos2d-x做一个简单的windows phone 7游戏》系列教程开始学起,因为我们这个教程使用了大量的基本概念,而这些概念都可以从上面的教程中获取。

      好了,让我们玩一玩tile地图吧!

     

    创建工程

    打开VS2010,新建一个cocos2d-x项目。命名为cocos2dTIleMapGameDemo,同样的,openxLIve这个服务我们也用不到,所以那个勾同样去掉。同样的,添加和修复引用也是必要的。

    接下来,下载游戏资源文件(http://dl.dbank.com/c0p7fmgba7),这个资源文件包里包含了以下内容:

    • 玩家sprite。这个图片和用cocos2d-x做一个简单的windows phone 7游戏》差不多。
    • 一些音效。
    • 一些背景音乐。
    • 我们将会使用的tile集合--它实际上会和tile地图编辑器一块儿使用,但是,我想把它放在这里,余下的事情会变得更容易。
    • 一些额外的“特殊”的tile,我将会在后面加以说明。

      一旦你获得了这些资源,解压并把它添加到你的Content工程的“Resources”目录下面。

      如果一切顺利,所有的文件应该都在你的工程里了。是时候制作我们的地图了!

     

     

    使用Tile来制作地图

      cocos2d-x支持使用Tile地图编辑器(http://www.mapeditor.org/)创建的TMX格式的地图。

      下载完之后,直接双击运行安装。点击文件\新文件,然后会出现以下对话框:

    在 地图方向部分,你可以选择正常。

      接下来,设置地图的大小。记住,这个大小是以tile为单位的,而不是以像素为单位。我们将创建一个尽量小的地图,因此选择32×32(现在发现50*50的地图WP7不能加载全,最大32*32。不过在test工程里面的示例再怎么样的地图都行。不过人家用的摄影机,不是我这种的设置位置的方法,现在还没有研究为啥)

      最后,你指定每个地图的宽度和高度。你这里选择的宽度和高度要根据你的实际的tile图片的尺寸来做。这个教程使用的样例tile的尺寸是32×32,所以在上面的选项中选择32×32.

      接下来,我们把制作地图所需要的tile集合导入进来。点击菜单栏上面的“地图”菜单,“新图块.”,然后会出现下面的窗口:

      为了获得图片,点击Browse按钮,然后定位到你的Resource文件夹,选择 tmw_desert_spacing.png文件,然后加到工程中去。它会基于文件名自动填充名字。点击确定即可。

    将会看到图块窗口中显示了一些tiles。现在,你可以制作地图了!点击一个Tile。你就可以在任何设置图块了。这个就像画图工具,什么画刷也能用,具体看工具栏,这样可以快速制作地图。

    充分发挥你的聪明才智!确保增加至少一对建筑物在地图上,因为后面我们需要一些东西来做碰撞。

    一旦你完成了地图的绘制工作,在Layers选项卡的层上面双击(现在可以说是“图层1”),然后重命名为“background”。然后点击“文件\保存”并且保存文件到你的工程的Resource文件夹中,并且命名为“TileMap.tmx”。

    把tile地图添加到cocos2d的场景中

    现在新建一个类,命名为TileMapScene。来作为我们游戏场景。并且里面的代码修改为:

        class TileMapScene:CCScene
        {
            public TileMapScene()
            {
                this.addChild(TileMapLayer.node());
            }
        }
    
        class TileMapLayer : CCLayer
        {
            public override bool init()
            {
                if (!base.init())
                    return false;;
                return true;
            }
    
            public static new TileMapLayer node()
            {
                TileMapLayer layer = new TileMapLayer();
                if (layer.init())
                    return layer;
                return null;
            }
        }

    并且修改导演类里面的applicationDidFinishLaunching。修改如下:

     

                //CCScene pScene = cocos2dTIleMapGameDemoScene.scene();
                CCScene pScene = new TileMapScene();
                //run

     

    现在需要添加两个引用。ICSharpCode.SharpZipLib.DLL和zlib.net.DLL。这两个DLL可以在cocos2d-x的安装包的Test工程的bin目录下面找到。然后在解决方案资源管理器里面选择TileMap.tmx文件,选择属性,确认Content Importer和Content Processor如下面所选,如果没找到下面选项,确认Content工程的引用中的cocos2d.Content.Pipeline.Importers正确引用


    现在打开TileMap.tmx文件。确认image 的source路径为相对路径,相对Tmx文件的相对路径。

     做完上面的工作后,现在再添加一些声明到TileMapLayer类中。

            CCTMXTiledMap tileMap = null;
            CCTMXLayer background = null;

    并且添加以下代码到Init中。

                tileMap = CCTMXTiledMap.tiledMapWithTMXFile("Resources/TileMap");
                background = tileMap.layerNamed("background");
                addChild(tileMap, -1);

      这里,我们调用CCTMXTiledMap类的一些方法,把我们刚刚创建的地图文件加载进去。

      一些简明的CCTMXTiledMap的背景知识。它是一个CCNode,你可以设置它的位置和比例等。这个地图的孩子是一些层,而且提供了一个帮助函数可以让你通过层的名字得到层对象--我们上面就是通过这种方面获得地图背景的。每一个层都是一个CCSpriteSheet的子类,这里考虑了性能的原因--但是这也意味着每一个层只能有一个tile集。

      因此,我们这里做的所有这些,就是指向一个tile地图,然后保存背景层的引用,并且把tile地图加到TileMapLayer层中。

      好了,就这么多!编译并运行工程,你将会看到地图的左下角出现在模拟器中。

      还不错!但是,这还不是一个游戏!我们还需要三个东西:a)游戏主角,b)主角初使位置和c)能够移动视图,这样就好像是第一视角了。

      好了,接下来让我们来解决这些问题。

    tiled对象层和设置tile地图位置

      tiled支持两类层--tile层(就是我们目前使用的层),还有对象层。

      对象层允许你在地图上圈出一些区域,来指定一些事件的发生。比如,你可能想制作一个区域,在那里怪物将会跳出来,或者是一个区域,只要进入就会死掉。这我们这个例子中,我们将创建一个区域来显示我们的游戏主角。

      因此,找到Tiled的菜单,点击” 图层\添加对象层”,命名为“Objects”,然后按回车键。

      我们只想要选择一个tile来让主角显示。先在工具栏上面选择添加对象。因此,在你的地图上选择一个tile。这个区域的大小实际上并没有关系,因为我们仅仅使用x、y坐标。

     然后,上面的灰蓝色对象框上面点右键,选择“对象属性”, 取名为“SpawnPoint",然后选择确定:

    现在,保存TMX文件,并且记得更新到Resource文件夹。

    然后往TileMapLayer里面添加一个声明:

            CCSprite player = null;

    同时修改Init方法(在return true前面添加):

                CCTMXObjectGroup objects = tileMap.objectGroupNamed("Objects");
                int x, y;
                if (objects != null)
                {
                    //Debug.WriteLine(objects.Properties.ToString());
                    foreach (var item in objects.Objects)
                    {
                        if (item.ContainsValue("SpawnPoint"))
                        {
                            x = Convert.ToInt32(item["x"]);
                            y = Convert.ToInt32(item["y"]);
                            player = CCSprite.spriteWithFile(@"Resources/Player");
                            player.position = new CCPoint(x, y);
                            this.addChild(player);
                        }
                    }
                }
                this.setViewpointCenter(player.position);
     

      好了,让我们先歇会儿,来解释一下对象层和对象组。首先,注意你通过CCTMXTiledMap对象的objectGroupNamed方法来获得对象层(而不是layerNamed方法)。它返回一个特殊的CCTMXObjectGroup对象。

      我们然后调用CCTMXObjectGroup类的Objects是一个List<IDictionary<string,string>>对象,其有一个name的key的value就是我们定义的SpawnPoint,这个字典包含了关于对象的大量信息,包括x和y坐标值,宽度和高度。在这个例子中,我们只关心x和y坐标,因此,我们提取出这两个信息,并且设置player的位置。

    PS:在这里随意提下调试技巧吧。本来我也不懂这个CCTMXObjectGroup类是怎么回事,也不懂里面的数据是怎么保存的。虽然知道是一个字典的List,但是不知道我需要的信息在哪,那么就在上面的for循环前面添加一个断点。然后运行到断点处对objects.Objects对象进行快速监视,在快速监视窗口查找需要的信息。这个方法对于不认识的对象可以很好了解其情况。

      最后,我想设置这个视图为玩家所在的位置。因此,添加下面一个新方法到文件中:

            void setViewpointCenter(CCPoint position)
            {
                CCSize winSize = CCDirector.sharedDirector().getWinSize();
                float x = Math.Max(position.x, winSize.width / 2);
                float y = Math.Max(position.y, winSize.height / 2);
                x = Math.Min(x, (tileMap.MapSize.width * tileMap.TileSize.width) - winSize.width / 2);
                y = Math.Min(y, tileMap.MapSize.height * tileMap.TileSize.height - winSize.height / 2);
                CCPoint acturalPosition = new CCPoint(x, y);
    
                CCPoint centerView = new CCPoint(winSize.width / 2, winSize.height / 2);
                CCPoint viewPoint = CCPointExtension.ccpSub(centerView, acturalPosition);
                this.position = viewPoint;
            }

      好了,让我解释一下。假设这个函数是设置camera的中心。我们允许用户传入地图上任何x、y坐标值--但是如果你仔细想一下,有些东西我们并不想让它显示出来--比如,我们不想让屏幕超过地图的边界(那些区域仅仅是一个空白区域!)

      比如,看看下面这幅图:

      看一下,什么时候camera的中心会小于winSize.width/2或者winSize.height/2,部分视图将会在屏幕之外?类似的,我们需要检查上面的界限区间,也和我们这里的情形一样。

      因此,我们把这个函数看作是设置camera的视角中心点。然而。。。那不完全是我们想要的。在cocos2d里面有一种方式可以直接操作一个CCNode的camera,但是那会使事情变得更复杂。我们需要另一种替代方法,那就是移动整个层。

      看看下面的图:

      想像一个大的地图,我们查看从0到winSize.height/width的坐标。我们的视图的中心点是centerOfView,而且我们知道我们要把这个中心设置到哪里(actualPositon)。因此,为了使实际的位置和视图中心相吻合,我们只需要把地图往左下角移动即可!

      这个可以通过使实际的位置减去视图的中心位置来实现,然后设置TileMapLayer层到那个点。

      唉!太多理论了--让我们看点实际的吧!编译并运行项目,如果一切顺利,你将会看到忍者在场景当中,然而视角也移过来了。


    似乎上面的忍者放置太靠左下角了。。。。不过也可以看出来视觉移过来了

    使忍者移动

      我们已经有一个好的开端了,但是我们的忍者只是站在那儿不动!这可不像真正的忍者!

      我们使忍者动起来吧,只需要让忍者移动到用户点击的地方就行了。在TileMapLayer类中的init增加以下代码

    this.isTouchEnabled = true;

    然后往类中添加添加以下方法:

            public override void registerWithTouchDispatcher()
            {
                CCTouchDispatcher.sharedDispatcher().addTargetedDelegate(this, 0, true);
            }
    
            void setPlayerPosition(CCPoint position)
            {
                player.position = position;
            }
    
            public override bool ccTouchBegan(CCTouch touch, CCEvent event_)
            {
                return true;
            }
    
            public override void ccTouchEnded(CCTouch touch, CCEvent event_)
            {
                CCPoint touchLocation = touch.locationInView(touch.view());
                touchLocation = CCDirector.sharedDirector().convertToGL(touchLocation);
                touchLocation = this.convertToNodeSpace(touchLocation);
    
                CCPoint playerPos = new CCPoint(player.position.x, player.position.y);
                CCPoint diff = CCPointExtension.ccpSub(touchLocation, playerPos);
                if (Math.Abs(diff.x) > Math.Abs(diff.y))
                {
                    if (diff.x > 0)
                    {
                        playerPos.x += tileMap.TileSize.width;
                    }
                    else
                        playerPos.x -= tileMap.TileSize.width;
                }
                else
                {
                    if (diff.y > 0)
                    {
                        playerPos.y += tileMap.TileSize.height;
                    }
                    else
                        playerPos.y -= tileMap.TileSize.width;
                }
    
                if (playerPos.x <= tileMap.MapSize.width * tileMap.TileSize.width &&
                    playerPos.y <= tileMap.MapSize.height * tileMap.TileSize.height &&
                    playerPos.x >= 0 &&
                    playerPos.y >= 0)
                {
                    this.setPlayerPosition(playerPos);
                }
                this.setViewpointCenter(player.position);
                
            }

       首先,在init方法中设置层能够接收touch事件。如果我们覆盖registerWithTouchDispatcher方法,来使这个层能够处理目标touch事件。这样会导致ccTouchBegan和ccTouchEnded方法被调用(注意是单数形式,而不是复数形式的ccTouchesBegan和ccTouchesEnded方法)

      你可能会问,为什么我要讲这个,因为我们在 用cocos2d-x做一个简单的windows phone 7游戏》里面使用的是ccTouchesBegan和ccTouchesEnded方法。那两个方法可以,在这个教程里用两种方法都可以。但是,我想向大家介绍一个新方法,因为它有两个优点:

    • 划分UITouch集合并调度的工作全部由cocos2d-x框架来完成。每一次方法调用,你只获得了一个UITouch。“
    • “你可以在ccTouchBegan中返回true,这样当前的层就可以接收touch事件回调。而且,只有当你返回yes的时候,才会响应move/ended/cancelled回调. 这个就使你从一些复杂的多触摸判断中解放出来了。

      不管怎么说,在我们的ccTouchEnded里面,我们转换屏幕touch坐标为局部view坐标,然后再转换成GL的坐标。这两个步骤,只需要一步完成,即调用 [self convertToNodeSpace:touchLocation].就可以了。

      这是因为,touch位置只是告诉我们屏幕视口的坐标(比如100,100)。但是,我们我们滚动了地图,这个位置实际可能对应地图的(800,800)。因此,调用这个方法基于我们当前层的位置来决定touch的偏移。

      接下来,计算出touch点和player的位置之差。我们必须基于touch位置选择一个方向,因此,首先,我们需要计算出是上下移动还是左右移动。然后,我们比较正负值,决定具体的方向。

      相应的,我们再调整player的位置,并且设置player的位置为视口的中心位置,这个在上一节中已经用到了。

     

      编译并运行!你现在可以点击鼠标,想让尽者移到哪,它就移到哪儿!

    何去何从?

      这只是这个教程的一部分。此时,你应该了解一些创建tile地图的基础了,而且知道如何把它导入到游戏当中。

      这里有我们目前为止用的完整源代码(http://dl.dbank.com/c0ffx1qcib

      接下,继续学习第二部分教程吧!在那里,我将教大家如何在地图中添加碰撞检测,如果使我们的忍者沿着墙壁快乐的奔跑!

    专注移动开发。本博客教程采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可
  • 相关阅读:
    MySQL索引背后的数据结构及算法原理 [转]
    5.5下对DDL操作提速的测试
    由浅入深理解索引的实现(2) [转]
    由浅入深理解索引的实现(1) [转]
    两个比较有用的字符串函数
    在慢查询里保留注释部分
    想在Innodb表上做OPTIMIZE操作?先等等看再说!
    Win CE和smartphone和pocket pc和windows mobile比较(zt)
    学习笔记(配置SQL Server 2005允许远程连接)
    配置程序集的版本策略(zt)
  • 原文地址:https://www.cnblogs.com/fengyun1989/p/2478674.html
Copyright © 2020-2023  润新知