Nowpaper 十五英寸的世界

Rich Games Developer

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

不知道有多少朋友了解动画和电影的制作手法,他们都是一个场景一个场景的拍摄录制,然后剪辑成为一部完整的片子,而游戏是非常类似,你可以理解要制作一个世界,然后让玩家其中按照世界的规则进展,而屏幕就是最好的观察者,可是好的片子要有一个“指挥”世界运转,让主角按照预定的路线行进,这在cocos2d中对应的是导演类(CCDirector),在http://www.cnblogs.com/hielvis/archive/2012/06/05/2534706.html中诠释的非常清晰,如想深入建议好好读读。

现在拿出之前的设计材料,好好想想游戏该有什么场景,在本篇游戏中,将会主要在如下的四个场景切换:

st010101

从上图中可以看出是开始,然后到选择战役,再到游戏中,最后为结束,游戏的主体的流程就这么搞定。

 

  开始 第一个场景

之前的代码只是进了一个Test场景,现在咱们做一个新的场景,先将上面的开始界面完成,把图片的素材准备好,将界面拆分主要有背景图、按钮、标志,将图片保存兵添加到Content里:

QQ截图20120912092832

下面打开Scenes里的SceneStart.cs,把类写成下面这样:

public class SceneStart : CCScene
{
    public SceneStart()
    {
        base.init();
        //取得屏幕大小
        CCSize size = CCDirector.sharedDirector().getWinSize();
        //背景图
        CCSprite background = CCSprite.spriteWithFile("bg_start") ;
        this.addChild(background);
        //Logo图
        CCSprite logo = CCSprite.spriteWithFile("logo");
        //设置到界面中间偏上
        logo.position = new CCPoint(size.width / 2, size.height / 2 + 120);
        this.addChild(logo);
        //两个按钮
        CCMenuItemImage btn_start = CCMenuItemImage.itemFromNormalImage("btn_start1", "btn_start2", this, click_start);
        CCMenuItemImage btn_setting = CCMenuItemImage.itemFromNormalImage("btn_setting1", "btn_setting2", this, click_setting);
        //MenuItem需要通过CCMenu组合
        CCMenu menu = CCMenu.menuWithItems(btn_start, btn_setting);
        //一个垂直间隔排列
        menu.alignItemsVerticallyWithPadding(10);
        //设置到界面中间偏下
        menu.position = new CCPoint(size.width / 2, size.height / 2 - 120);
        this.addChild(menu);
    }
    private void click_start(CCObject sender)
    {
    }
    private void click_setting(CCObject sender)
    {
    }
}

初步写完之后可能会需要引用using cocos2d;然后在AppDelegate.cs里面做如下修改:

public class AppDelegate : CCApplication
{
    public AppDelegate(Game game, GraphicsDeviceManager graphics)
        : base(game, graphics)
    {
        CCApplication.sm_pSharedApplication = this;
    }
    public override bool applicationDidFinishLaunching()
    {
        //初始化CCDirector
        CCDirector pDirector = CCDirector.sharedDirector();
        pDirector.setOpenGLView();

        //是否显示FPS(每秒帧速率)
        pDirector.DisplayFPS = true;
        // 在这里设置Updata的间隔
        pDirector.animationInterval = 1.0 / 60;

        // 创建一个场景
        CCScene pScene = new Scenes.SceneStart();

        // 运行这个场景
        pDirector.runWithScene(pScene);

        return true;
    }
}

你会发现:CCScene pScene = new Scenes.SceneStart();之后通过pDirector.runWithScene(pScene)进入了这个场景,pDirector就是导演,他说:现在要拍摄开始界面的场景了。

当然里面有很多的布景道具什么的,在上面的代码中就已经完成,现在运行一下看看:

QQ截图20120912093630

奇怪,这是为什么会变成这样呢,这里说一下CCSprite,CCSprite一般叫精灵类,它的主要作用是呈现图片,在cocos2dxna中没有专门Image类,一切的图片就是通过CCSprite来显示和操作的,CCSprite载入图片后按照(0.5,0.5)的中心点(也叫锚点anchorPoint)为准,也就是说所在的xy的位置实际为其承载图片的中心位置,所以我们需要做一下坐标修改或者将锚点设置到(0,0)的位置上。比如说:

anchorPoint = new CCPoint(0, 0);

下面简单说一下代码,cocos2dxna的坐标周是在屏幕的左下,这一点和cocos2d其他版本是一致的,主要是为了统一标准,当然这也带来了理解上的麻烦,当然了,万能的导演(CCDirector)也提供了转换方法,但是我们在这里不需要太多,为了统一性,建议就按照左下的坐标开发比较好。

CCSprite的CCSprite.spriteWithFile静态方法可以很方便的将我们所需图片生成,不必自己在写很多的代码了,只需要一行,spriteWithFile所取得是对应在Content资源工程里的图片文件,CCSprite也可以控制其坐标位置,开始场景中,就用了position来控制位置,将Logo固定到了中间偏上的位置。

按钮类CCMenu单独是没有作用的,需要通过多个子项目来初始化自己,所以就有了两个菜单的代码,CCMenuItemImage也有静态方法帮助开发者写很少的代码完成按钮,CCMenuItemImage是CCMenuItem的派生类,由CCMenuItem派生的还有CCMenuItemLabel、CCMenuItemSprite等,它们的作用跟名字一样,只是看如何方便使用了。

去掉FPS

这个时候会说左下角的帧数显示实在太难受了,能不能去掉,当然可以,直接把导演的DisplayFPS设置成为false就可以,但是cocos2dxna有个bug,去掉之后运行是下面这个样子的:

QQ截图20120912094010

这个bug的解决方法,也很简单,就是在AppDelegate里初始化一下Font就行了,这是因为内部的处理机制造成的。

CCLabelTTF a = CCLabelTTF.labelWithString("0", "Arial", 0);

加入上面的代码就可以,不用添加进去,现在运行看看:

QQ截图20120912094032

是不是帅多了?也许现在迫不及待的赶紧弄下一个场景,先打住,现在聊聊更加重要的话题:

庞大的资源  纹理图片优化 plist

现在打开你的工程目录找到bin下面所编译的程序,看看Content里(Content是输出工程的内容集合,你所见的都将会打包到XAP中)输出的XNB文件吧:

QQ截图20120912094321

现在,你能忍吗?其实原图不打包才400多KB,为什么转换之后就变成这么夸张了呢,因为XNA对资源内容都会自行编译输出,这是为了方便自己,同时将不规格的资源内容编译成规格的,这方面如果说多了就会涉及到图像渲染的细节,这么说吧,对于图像渲染显示而言,它们其实只认2的N次幂长宽的图像,无论是程序还是硬件,它的输入和输出都遵循这个规则,所以,你的精美小图片不管用什么存储最终都会变成统一规格。

一种方法是通过图像纹理的模式修正,将其变成2的N次幂长宽的图像,请看下图:

image

你的所有的图像都会默认为color格式,所以很悲催的事实就是将面临超大的图像素材体积(内部还给你扩展成了2的N次幂),DxtCompressed格式则可以大大的减小图像体积,但是DxtCompressed对图像有一定的要求,最基本的要求是:2的N次幂宽高

对于图像资源,很难有正好的符合,刻意的制作就会代码N多代码的修正,我们利用cocos2d的特性来解决,在cocos2d有一种将很多图片保存成为一张大图片,然后独取出来单独使用,这个方法就是plist的图片帧文件,它能极大的减小图片体积,最重要的是大图就能符合2的N次幂宽高的要求了。

TexturePackerGUI是专门制作cocos2d plist文件的软件,这个软件最好的优势是有Windows版本的(有很多优秀的工具都在IOS运行),下面是简单的使用方法:

先打开TexturePackerGUI,将前面的开始界面素材拖入到窗口中。

QQ截图20120912223502

建议将DataFormat改成cocos2d-0.99.4,然后点击Publish按钮,保存为GameUI01.plist,浏览位置就会发现多出一个GameUI01.png,它们之间就是对应的,现在我们在程序里使用它们,将其添加到Content工程里,然后做如下两个修改:

1、修改一下GameUI01.png的Content Processor内Textrue Format属性为DxtCompressed。

2、修改GameUI01.plist的输入和输出管线,因为plist需要cocos2dxna的独特的内容管线,所以先添加引用:

image

然后选择GameUI.plist属性,将输入和输出的管线修改:

image

现在编译一下看看,呵呵,你会发现一个错,这个错是关于重复名称的,所以你需要将生成的plist文件和png文件分开,比如说建立一个images目录,推荐在plist所在的文件目录下建立Images目录将对应的png加入,因为在代码中会自动处理并寻找对应的图像文件,好了,现在再来一次编译吧。

现在再看GameUI01所生成的两个xna文件,两个加起来只有1MB,已经大大的减少了体积,在png图像里还有很多的空位,也许完全能够留给其他的UI界面。

如何使用plist

下一步就是如何在代码中使用plist文件,打开SceneStart.cs代码我们做一下改造。

public class SceneStart : CCScene
{
    public SceneStart()
    {
        base.init();
        CCSpriteFrameCache.sharedSpriteFrameCache().addSpriteFramesWithFile("GameUI01", "images/GameUI01");
        //取得屏幕大小
        CCSize size = CCDirector.sharedDirector().getWinSize();
        //背景图
        CCSprite background = CCSprite.spriteWithSpriteFrameName("bg_start.png") ;
        background.anchorPoint = new CCPoint(0, 0);
        this.addChild(background);
        //Logo图
        CCSprite logo = CCSprite.spriteWithSpriteFrameName("logo.png");
        //设置到界面中间偏上
        logo.position = new CCPoint(size.width / 2, size.height / 2 + 120);
        this.addChild(logo);
        //两个按钮
        CCMenuItemSprite btn_start = CCMenuItemSprite.itemFromNormalSprite(
            CCSprite.spriteWithSpriteFrameName("btn_start1.png"),
            CCSprite.spriteWithSpriteFrameName("btn_start2.png"), 
            this, click_start);
        CCMenuItemSprite btn_setting = CCMenuItemSprite.itemFromNormalSprite(
            CCSprite.spriteWithSpriteFrameName("btn_setting1.png"),
            CCSprite.spriteWithSpriteFrameName("btn_setting2.png"),
            this, click_start);
        //MenuItem需要通过CCMenu组合
        CCMenu menu = CCMenu.menuWithItems(btn_start, btn_setting);
        //一个垂直间隔排列
        menu.alignItemsVerticallyWithPadding(10);
        //设置到界面中间偏下
        menu.position = new CCPoint(size.width / 2, size.height / 2 - 120);
        this.addChild(menu);
    }
    private void click_start(CCObject sender)
    {
    }
    private void click_setting(CCObject sender)
    {
    }
}

这段代码中使用CCSpriteFrameCache载入了plist文件和png文件,用它们来提前所需要的图片帧,之后在程序中使用,CCSpriteFrameCache是一个仓库,使用文件名来标识所有的内容资源,CCSprite.spriteWithSpriteFrameName能够直接依据文件名从中取出使用,关于CCSpriteFrameCache将应用在动画等图像处理方面,这次仅仅是一个简单的图片应用示例。

本次代码仍然在:https://github.com/Nowpaper/SanguoCommander_cocos2dxna_Sample

关于TexturePackerGUI软件,可以在网上搜索到使用。

本想在本次写完切换的,看了看内容已经够多了,还是下一篇完成吧:)

posted on 2012-09-13 19:12  nowpaper  阅读(5204)  评论(4编辑  收藏  举报