精华内容
下载资源
问答
  • 看过cocos2d-x 自带demo的朋友相信很多人都会发现demo中用了很多do{}while(0)和CC_BREAK_IF宏.他们是做什么用的呢? 其实这个是c++中的一个技巧. 我们时常会调用某个函数来创建一个对象,但由于内存不足或其他异常...
     
    


    看过cocos2d-x 自带demo的朋友相信很多人都会发现demo中用了很多do{}while(0)和CC_BREAK_IF宏.他们是做什么用的呢?

    其实这个是c++中的一个技巧.

    我们时常会调用某个函数来创建一个对象,但由于内存不足或其他异常情况发生时对象可能会创建失败,创建失败我们就要结束当前程序转到错误处理地方去处理错误或释放已生成的对象.

     

    	int* p1 = new int;
    	
    	CCObject* o1 = fun1();
    	if(!o1) goto catchcode;
    
    	CCObject* o2 = fun2();
    	if(!o2) goto catchcode;
    
    	//如果代码都执行成功
    	delete p;
    	p = NULL;
    
    //如果o1 o2有发生错误
    catchcode:
    	delete p;
    	p = NULL;
    

     

    我们可以看到,代码中用了很多goto语句.c++中虽然有goto,但却是极力不推崇这么做.因为goto一旦滥用会令程序的运行琢磨不定,给开发带来很多不必要的麻烦.这时do{}while(0) 就要出场了.

     

    int* p1 = new int;
    
    do
    {
    	CCObject* o1 = fun1();
    	if(!o1) break;
    
    	CCObject* o2 = fun2();
    	if(!o2) break;
    }while(0)
    
    delete p;
    p = NULL;
    	

    对比上一段代码,这段代码是不是显得简洁了许多.而宏CC_BREAK_IF的作用就是代替if(!o1) break;这样的代码,所以上面if(!o1) break;这样的代码完全可以替代为CC_BREAK_IF(!o1);

     

    这就是cocos2d-x中经常见到的do{}while(0)和CC_BREAK_IF的用法.


     

    展开全文
  • 1. 表明这个脚本是用sh来解析的,因为各种shell的语法还是有细微差别的,比如其他的shell还有bash 2.-x 是调试用的,加了这个,就会把脚本中的每条命令的执行情况打印出来 以下部分为转载: 我们在前面介绍的调试...

    #!/bin/sh -x

    1. 表明这个脚本是用sh来解析的,因为各种shell的语法还是有细微差别的,比如其他的shell还有bash
    2.-x 是调试用的,加了这个,就会把脚本中的每条命令的执行情况打印出来

    以下部分为转载:

    我们在前面介绍的调试手段是通过修改shell脚本的源代码,从其输出相关的调试信息来定位错误的,那有没有不修改源代码来调试shell脚本的方法呢?有的,那就是使用shell的执行选项,下面将介绍一些常用选项的用法:

    -n 只读取shell脚本,但不实际执行
    -x 进入跟踪方式,显示所执行的每一条命令
    -c "string" 从strings中读取命令

    "-n"可用于测试shell脚本是否存在语法错误,但不会实际执行命令。在shell脚本编写完成之后,实际执行之前,首先使用"-n"选项来测试脚本是否存在语法错误是一个很好的习惯。因为某些shell脚本在执行时会对系统环境产生影响,比如生成或移动文件等,如果在实际执行才发现语法错误,您不得不手工做一些系统环境的恢复工作才能继续测试这个脚本。

    "-c"选项使shell解释器从一个字符串中而不是从一个文件中读取并执行shell命令。当需要临时测试一小段脚本的执行结果时,可以使用这个选项,如下所示:
    sh -c 'a=1;b=2;let c=$a+$b;echo "c=$c"'

    "-x"选项可用来跟踪脚本的执行,是调试shell脚本的强有力工具。"-x"选项使shell在执行脚本的过程中把它实际执行的每一个命令行显示出来,并且在行首显示一个"+"号。 "+"号后面显示的是经过了变量替换之后的命令行的内容,有助于分析实际执行的是什么命令。 "-x"选项使用起来简单方便,可以轻松对付大多数的shell调试任务,应把其当作首选的调试手段。

    如果把本文前面所述的trap ‘command’ DEBUG机制与“-x”选项结合起来,我们就可以既输出实际执行的每一条命令,又逐行跟踪相关变量的值,对调试相当有帮助。

    我们以debug.sh脚本为例:

    # cat -n debug.sh  

    1    #!/bin/sh
    2    trap 'echo "before execute line:$LINENO,a=$a,b=$b,c=$c"' DEBUG
    3    a=1
    4    if [ "$a" -eq 1 ]
    5    then
    6    b=2
    7    else
    8    b=1
    9    fi
    10    c=3
    11    echo end
    现在对该脚本加上“-x”选项来执行它:

    当第2行被注释掉     当第2行没有被注释掉(即代码中有trap命令)
        
    在上面的结果中,前面有“+”号的行是shell脚本实际执行的命令,前面有“++”号的行是执行trap机制中指定的命令,其它的行则是输出信息。

    shell的执行选项除了可以在启动shell时指定外,亦可在脚本中用set命令来指定。 "set - 参数"表示启用某选项,"set +参数"表示关闭某选项。有时候我们并不需要在启动时用"-x"选项来跟踪所有的命令行,这时我们可以在脚本中使用set命令,如以下脚本片段所示:

    set -x#启动"-x"选项
    要跟踪的程序段
    set +x#关闭"-x"选项

    set命令同样可以使用前面介绍的调试钩子—DEBUG函数来调用,下面是DEBUG函数代码:
    #cat –n debugOth.sh 

    #!/bin/sh
    DEBUG()
    {
    if [ "DEBUG"="true" ];then
    $@
    fi
    }
    a=1
    DEBUG echo "a=$a"
    if [ "$a" -eq 1 ]
    then
    b=2
    else
    b=1
    fi
    DEBUG echo "b=$b"
    c=3
    DEBUG echo "c=$c"
    $@    与$*相同,但是使用时加引号,并在引号中返回每个参数。
    如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。
    这样可以避免脚本交付使用时删除这些调试语句的麻烦,如以下脚本片段所示:
    DEBUG set -x#启动"-x"选项
    要跟踪的程序段

    DEBUG set +x#关闭"-x"选项


    # sh debugOth.sh    # sh -x debugOth.sh
        
    四. 对"-x"选项的增强

    "-x"执行选项是目前最常用的跟踪和调试shell脚本的手段,但其输出的调试信息仅限于进行变量替换之后的每一条实际执行的命令以及行首的一个"+" 号提示符,居然连行号这样的重要信息都没有,对于复杂的shell脚本的调试来说,还是非常的不方便。幸运的是,我们可以巧妙地利用shell内置的一些环境变量来增强"-x"选项的输出信息,下面先介绍几个shell内置的环境变量:

    $LINENO
    代表shell脚本的当前行号,类似于C语言中的内置宏__LINE__

    $FUNCNAME
    函数的名字,类似于C语言中的内置宏__func__,但宏__func__只能代表当前所在的函数名,而$ FUNCNAME的功能更强大,它是一个数组变量,其中包含了整个调用链上所有的函数的名字,故变量${FUNCNAME[0]}代表shell脚本当前正在执行的函数的名字,而变量${FUNCNAME[1]}则代表调用函数${FUNCNAME[0]}的函数的名字,余者可以依此类推。

    $PS4
    主提示符变量$PS1和第二级提示符变量$PS2比较常见,但很少有人注意到第四级提示符变量$PS4的作用。我们知道使用“- x”执行选项将会显示shell脚本中每一条实际执行过的命令,而$PS4的值将被显示在“-x”选项输出的每一条命令的前面。在Bash Shell中,缺省的$PS4的值是"+"号。(现在知道为什么使用"-x"选项时,输出的命令前面有一个"+"号了吧?)。

    利用$PS4这一特性,通过使用一些内置变量来重定义$PS4的值,我们就可以增强"-x"选项的输出信息。例如先执行export PS4='+{$LINENO:${FUNCNAME[0]}} ', 然后再使用“-x”选项来执行脚本,就能在每一条实际执行的命令前面显示其行号以及所属的函数名。

    以下是一个存在bug的shell脚本的示例,本文将用此脚本来示范如何用“-n”以及增强的“-x”执行选项来调试shell脚本。这个脚本中定义了一个函数isRoot(),用于判断当前用户是不是root用户,如果不是,则中止脚本的执行。

    # cat -n debugOthO.sh

    1    #!/bin/sh
    2    isRoot()
    3    {
    4    if [ "$UID" -ne 0 ]
    5    return 1
    6    else
    7    return 0
    8    fi
    9    }
    10    isRoot
    11    if ["$?" -ne 0]
    12    then
    13    echo "Must be root to run this script"
    14    exit 1
    15    else
    16    echo "welcome root user"
    17    #do something
    18    fi
    首先执行# sh -n debugOthO.sh来进行语法检查,输出如下:
    # sh -n debugOthO.sh
    exp4.sh: line 6: syntax error near unexpected token `else'
    exp4.sh: line 6: ` else'

    发现了一个语法错误,通过仔细检查第6行前后的命令,我们发现是第4行的if语句缺少then关键字引起的(写惯了C程序的人很容易犯这个错误)。我们可以把第4行修改为if [ "$UID" -ne 0 ]; then来修正这个错误。再次运行# sh -n debugOthO.sh来进行语法检查,没有再报告错误。接下来就可以实际执行这个脚本了,执行结果如下:

    # sh -n debugOthO.sh
    exp2.sh: line 11: [1: command not found
    welcome root user

    尽管脚本没有语法错误了,在执行时却又报告了错误。错误信息还非常奇怪“[1: command not found”。现在我们可以试试定制$PS4的值,并使用“-x”选项来跟踪:

    $ export PS4='+{$LINENO:${FUNCNAME[0]}} '
    $ sh –x debugOthO.sh
    +{10:} isRoot
    +{4:isRoot} '[' 503 -ne 0 ']'
    +{5:isRoot} return 1
    +{11:} '[1' -ne 0 ']'
    exp4.sh: line 11: [1: command not found
    +{16:} echo 'welcome root user'
    welcome root user

    从输出结果中,我们可以看到脚本实际被执行的语句,该语句的行号以及所属的函数名也被打印出来,从中可以清楚的分析出脚本的执行轨迹以及所调用的函数的内部执行情况。由于执行时是第11行报错,这是一个if语句,我们对比分析一下同为if语句的第4行的跟踪结果:

    +{4:isRoot} '[' 503 -ne 0 ']'
    +{11:} '[1' -ne 0 ']'

    到这里我们就知道由于第11行的[号后面缺少了一个空格,导致[号与紧挨它的变量$?的值1被shell解释器看作了一个整体,并试着把这个整体视为一个命令来执行,故有“[1: command not found”这样的错误提示。只需在[号后面插入一个空格就一切正常了。

    shell中还有其它一些对调试有帮助的内置变量,比如在Bash Shell中还有BASH_SOURCE, BASH_SUBSHELL等一批对调试有帮助的内置变量,您可以通过man sh或man bash来查看,然后根据您的调试目的,使用这些内置变量来定制$PS4,从而达到增强“-x”选项的输出信息的目的。

    五. 总结

    现在让我们来总结一下调试shell脚本的过程:
    首先使用“-n”选项检查语法错误,然后使用“-x”选项跟踪脚本的执行,使用“-x”选项之前,别忘了先定制PS4变量的值来增强“-x”选项的输出信息,至少应该令其输出行号信息(先执行export PS4='+[$LINENO]',更一劳永逸的办法是将这条语句加到您用户主目录的.bash_profile文件中去),这将使你的调试之旅更轻松。也可以利用trap,调试钩子等手段输出关键调试信息,快速缩小排查错误的范围,并在脚本中使用“set -x”及“set +x”对某些代码块进行重点跟踪。这样多种手段齐下,相信您已经可以比较轻松地抓出您的shell脚本中的臭虫了。
    如果您的脚本足够复杂,还需要更强的调试能力,可以使用shell调试器bashdb,这是一个类似于GDB的调试工具,可以完成对shell脚本的断点设置,单步执行,变量观察等许多功能,使用bashdb对阅读和理解复杂的shell脚本也会大有裨益。关于bashdb的安装和使用,不属于本文范围,您可参阅http://bashdb.sourceforge.net/上的文档并下载试用
    --------------------- 
    作者:杰儿__er 
    来源:CSDN 
    原文:https://blog.csdn.net/weixin_42167759/article/details/80700719 
    版权声明:本文为博主原创文章,转载请附上博文链接!

    展开全文
  • Cocos2d-x-Lua示例项目HelloLua

    万次阅读 2014-06-25 20:45:08
    Cocos2d-x-Lua示例项目HelloLua 本篇博客介绍Cocos2d-x中Lua的实例项目,就是使用Cocos2d-x创建的初始项目运行所呈现... 注:本示例项目在Mac系统下创建 首先我们来创建一个Cocos2d-x Lua项目,在命令敲入类似以下命令

    Cocos2d-x-Lua示例项目HelloLua


    本篇博客介绍Cocos2d-x中Lua的实例项目,就是使用Cocos2d-x创建的初始项目运行所呈现的农场,这里笔者取名为HelloLua。本篇博客会详细在代码中解析Cocos2d-x 3.1.1创建的Lua项目中实例,一些API的使用。

    注:本示例项目在Mac系统下创建

    首先我们来创建一个Cocos2d-x Lua项目,在命令敲入类似以下命令

    cocos new HelloLua -p com.wwj.hellolua -l lua -d ~/Cocos2dxProj

    这样我们就在Cocos2dxProj目录下创建了一个名叫HelloLua的Lua项目

    进入我们runtime-src目录下打开proj.ios_mac目录,双击使用Xcode打开我们的项目:

    使用Xcode对我们的Lua项目进行编译并运行,就会呈现一个以下效果的示例游戏:

       

    看完效果图之后,来看看我们XCode里面的项目文件结构,如下图:

    以上画圈的有main.cpp、AppDelegate.h、AppDelegate.cpp、main.lua、hello2.lua

    我们下面一个一个来看:

    首先来看main.cpp文件,这个文件就是我们程序的入口文件,程序的运行时从这里开始的

    》》main.cpp

    #include "AppDelegate.h"
    #include "cocos2d.h"
    
    USING_NS_CC;
    
    int main(int argc, char *argv[])
    {
        AppDelegate app;
        return Application::getInstance()->run();
    }
    

    以上代码我们可以看到,在main.cpp里,通过#include引入了两个头文件,一个是AppDelegate.h、一个是cocos2d.h。

    定义了我们程序的入口方法main,通过执行Application::getInstance()->run()方法来运行我们的程序。


    接着我们来看AppDelegate.h和AppDelegate.cpp,这里两个文件用于 控制整个游戏的生命周期

    >>>AppDelegate.h

    #ifndef __APP_DELEGATE_H__
    #define __APP_DELEGATE_H__
    
    #include "cocos2d.h"
    
    /**
    @brief    The cocos2d Application.
    
    The reason for implement as private inheritance is to hide some interface call by Director.
    */
    class  AppDelegate : private cocos2d::Application
    {
    public:
        AppDelegate();
        virtual ~AppDelegate();
    
        /**
        @brief    Implement Director and Scene init code here.
        @return true    Initialize success, app continue.
        @return false   Initialize failed, app terminate.
        */
        virtual bool applicationDidFinishLaunching();
    
        /**
        @brief  The function be called when the application enter background
        @param  the pointer of the application
        */
        virtual void applicationDidEnterBackground();
    
        /**
        @brief  The function be called when the application enter foreground
        @param  the pointer of the application
        */
        virtual void applicationWillEnterForeground();
    };
    
    #endif  // __APP_DELEGATE_H__
    

    >>>AppDelegate.cpp

    #include "AppDelegate.h"
    #include "CCLuaEngine.h"
    #include "SimpleAudioEngine.h"
    #include "cocos2d.h"
    
    using namespace CocosDenshion;
    
    USING_NS_CC;
    using namespace std;
    
    AppDelegate::AppDelegate()
    {
    }
    
    AppDelegate::~AppDelegate()
    {
        SimpleAudioEngine::end();
    }
    
    bool AppDelegate::applicationDidFinishLaunching()
    {
        // initialize director 获得导演类实例
        auto director = Director::getInstance();
        // 获得OpenGL实例
    	auto glview = director->getOpenGLView();
    	if(!glview) {
            // 指定窗口大小
    		glview = GLView::createWithRect("HelloLua", Rect(0,0,900,640));
            // 设置OpenGL视图
    		director->setOpenGLView(glview);
    	}
        // 设置分辨率大小为480*320
        glview->setDesignResolutionSize(480, 320, ResolutionPolicy::NO_BORDER);
    
        // turn on display FPS 打开帧频,屏幕左下角哪一串数据
        // 启用FPS 显示,当前 FPS 会在游戏的左下角显示。FPS也就是屏幕每秒重绘的次数。即每秒帧速率。在游戏开发阶段,可以方便地确定游戏运行是否流畅。
        director->setDisplayStats(true);
    
        // set FPS. the default value is 1.0/60 if you don't call this
        // 设置绘制间隔
        director->setAnimationInterval(1.0 / 60);
    
        // 获得Lua引擎实例
        auto engine = LuaEngine::getInstance();
        // 设置脚本引擎
        ScriptEngineManager::getInstance()->setScriptEngine(engine);
        // 执行main.lua脚本
        if (engine->executeScriptFile("src/main.lua")) {
            return false;
        }
    
        return true;
    }
    
    // This function will be called when the app is inactive. When comes a phone call,it's be invoked too
    // 当应用程序将要进入后台时,会调用这个方法
    void AppDelegate::applicationDidEnterBackground()
    {
        Director::getInstance()->stopAnimation();
    
        SimpleAudioEngine::getInstance()->pauseBackgroundMusic();
    }
    
    // this function will be called when the app is active again
    // 该方法与applicationDidEnterBackground() 成对出现,在应用程序回到前台时被调用
    void AppDelegate::applicationWillEnterForeground()
    {
        Director::getInstance()->startAnimation();
    
        SimpleAudioEngine::getInstance()->resumeBackgroundMusic();
    }
    

    我们在AppDelegate类当中可以找到执行我们Lua脚本的方法,下面来看一下main.lua这个文件,我们屏幕显示的逻辑实现全部在这个文件中可以看到:

    >>>main.lua

    require "Cocos2d"
    require "Cocos2dConstants"
    
    -- cclog
    cclog = function(...)
        print(string.format(...))
    end
    
    -- for CCLuaEngine traceback 输出绑定执行函数发生错误的信息
    function __G__TRACKBACK__(msg)
        cclog("----------------------------------------")
        cclog("LUA ERROR: " .. tostring(msg) .. "\n")
        cclog(debug.traceback())
        cclog("----------------------------------------")
        return msg
    end
    
    local function main()
        collectgarbage("collect")
        -- avoid memory leak 这是脚本回收参数,避免内存泄漏
        collectgarbage("setpause", 100)
        collectgarbage("setstepmul", 5000)
    	-- 追加资源的搜索顺序
    	cc.FileUtils:getInstance():addSearchResolutionsOrder("src");
    	cc.FileUtils:getInstance():addSearchResolutionsOrder("res");
    	local schedulerID = 0
        --support debug 获取目标平台
        local targetPlatform = cc.Application:getInstance():getTargetPlatform()
        if (cc.PLATFORM_OS_IPHONE == targetPlatform) or (cc.PLATFORM_OS_IPAD == targetPlatform) or 
           (cc.PLATFORM_OS_ANDROID == targetPlatform) or (cc.PLATFORM_OS_WINDOWS == targetPlatform) or
           (cc.PLATFORM_OS_MAC == targetPlatform) then
            cclog("result is ")
    		--require('debugger')()
            
        end
    	-- 类似c++的include,引入文件,会检查是否重复引入
        require "hello2"
    	-- 调用外部函数,在hello2.lua中
        cclog("result is " .. myadd(1, 1))
    
        ---------------
    	-- 获取可视区域
        local visibleSize = cc.Director:getInstance():getVisibleSize()
    	-- 可视原点坐标 OpenGL坐标系,左下角为原点
        local origin = cc.Director:getInstance():getVisibleOrigin()
    
        -- add the moving dog 添加移动的小松鼠
        local function creatDog()
    		-- 每一帧尺寸设置,local表示局部变量
            local frameWidth = 105
            local frameHeight = 95
    
            -- create dog animate 加载动画资源并创建精灵帧
    		-- 加载精灵动画所在纹理
            local textureDog = cc.Director:getInstance():getTextureCache():addImage("dog.png")
    		-- 设置第一帧帧区域
            local rect = cc.rect(0, 0, frameWidth, frameHeight)
    		-- 创建第一帧精灵Frame
            local frame0 = cc.SpriteFrame:createWithTexture(textureDog, rect)
    		-- 设置第二帧帧区域
            rect = cc.rect(frameWidth, 0, frameWidth, frameHeight)
    		-- c创建第二帧精灵Frame
            local frame1 = cc.SpriteFrame:createWithTexture(textureDog, rect)
    		-- 基于使用第一帧Frame创建Sprite对象
            local spriteDog = cc.Sprite:createWithSpriteFrame(frame0)
            spriteDog.isPaused = false
            spriteDog:setPosition(origin.x, origin.y + visibleSize.height / 4 * 3)
    --[[
            local animFrames = CCArray:create()
    
            animFrames:addObject(frame0)
            animFrames:addObject(frame1)
    ]]--
    		-- 根据帧序列数组创建一个动画animation。帧间隔时间delay等于0.5秒
            local animation = cc.Animation:createWithSpriteFrames({frame0,frame1}, 0.5)
    		-- 根据动画animation创建动作实例
            local animate = cc.Animate:create(animation);
    		-- 松鼠精灵执行该动作
            spriteDog:runAction(cc.RepeatForever:create(animate))
    
            -- moving dog at every frame 用来更新松鼠的位置,后面会调用该函数
            local function tick()
                if spriteDog.isPaused then return end
                local x, y = spriteDog:getPosition()
                if x > origin.x + visibleSize.width then
                    x = origin.x
                else
                    x = x + 1
                end
    
                spriteDog:setPositionX(x)
            end
    		-- 生成一个scheule,每帧执行tick函数
            schedulerID = cc.Director:getInstance():getScheduler():scheduleScriptFunc(tick, 0, false)
    
            return spriteDog
        end
    
        -- create farm 创建地面的农场
        local function createLayerFarm()
    		-- 创建一个新的Lyaer用作农场管理
            local layerFarm = cc.Layer:create()
    
            -- add in farm background 添加农场管理
            local bg = cc.Sprite:create("farm.jpg")
            bg:setPosition(origin.x + visibleSize.width / 2 + 80, origin.y + visibleSize.height / 2)
            layerFarm:addChild(bg)
    
            -- add land sprite 添加地面砖块
            for i = 0, 3 do
                for j = 0, 1 do
                    local spriteLand = cc.Sprite:create("land.png")、
    				-- 设定每一块砖块位置
                    spriteLand:setPosition(200 + j * 180 - i % 2 * 90, 10 + i * 95 / 2)
                    layerFarm:addChild(spriteLand)
                end
            end
    
            -- add crop 添加庄稼,注意crop.png是多张图的合成贴图,所以只取了里面的部分贴图
            local frameCrop = cc.SpriteFrame:create("crop.png", cc.rect(0, 0, 105, 95))
            for i = 0, 3 do
                for j = 0, 1 do
                    local spriteCrop = cc.Sprite:createWithSpriteFrame(frameCrop);
                    spriteCrop:setPosition(10 + 200 + j * 180 - i % 2 * 90, 30 + 10 + i * 95 / 2)
                    layerFarm:addChild(spriteCrop)
                end
            end
    
            -- add moving dog 调用上面的createDog()方面,创建一个移动的松鼠
            local spriteDog = creatDog()
            layerFarm:addChild(spriteDog)
    
            -- handing touch events 手指触摸事件处理
            local touchBeginPoint = nil
    		-- 手指点击开始
            local function onTouchBegan(touch, event)
                local location = touch:getLocation()
                cclog("onTouchBegan: %0.2f, %0.2f", location.x, location.y)
                touchBeginPoint = {x = location.x, y = location.y} -- 保存点击位置
                spriteDog.isPaused = true -- 将松鼠暂停移动
                -- CCTOUCHBEGAN event must return true
                return true
            end
    
    		-- 手指按住移动
            local function onTouchMoved(touch, event)
                local location = touch:getLocation()
                cclog("onTouchMoved: %0.2f, %0.2f", location.x, location.y)
                if touchBeginPoint then
    				-- 将整个农村层拖动,因为之前已经将农场里面所有对象加入layerFarm
                    local cx, cy = layerFarm:getPosition()
                    layerFarm:setPosition(cx + location.x - touchBeginPoint.x,
                                          cy + location.y - touchBeginPoint.y)
                    touchBeginPoint = {x = location.x, y = location.y}
                end
            end
    
    		-- 手指离开
            local function onTouchEnded(touch, event)
                local location = touch:getLocation()
                cclog("onTouchEnded: %0.2f, %0.2f", location.x, location.y)
                touchBeginPoint = nil	-- 点击位置数据清空
                spriteDog.isPaused = false	-- 恢复松鼠移动
            end
    
    		-- 创建触摸事件监听器
            local listener = cc.EventListenerTouchOneByOne:create()
    		-- 注册touch事件
            listener:registerScriptHandler(onTouchBegan,cc.Handler.EVENT_TOUCH_BEGAN )
            listener:registerScriptHandler(onTouchMoved,cc.Handler.EVENT_TOUCH_MOVED )
            listener:registerScriptHandler(onTouchEnded,cc.Handler.EVENT_TOUCH_ENDED )
            local eventDispatcher = layerFarm:getEventDispatcher()
    		-- 添加场景图优先级事件监听
            eventDispatcher:addEventListenerWithSceneGraphPriority(listener, layerFarm)
            
            local function onNodeEvent(event)
               if "exit" == event then
                   cc.Director:getInstance():getScheduler():unscheduleScriptEntry(schedulerID)
               end
            end
            layerFarm:registerScriptHandler(onNodeEvent)
    
            return layerFarm
        end
    
    
        -- create menu 创建界面菜单
        local function createLayerMenu()
    		-- 创建一个新的Layer管理所有菜单
            local layerMenu = cc.Layer:create()
    
            local menuPopup, menuTools, effectID
    		
    		-- 点击菜单回调函数
            local function menuCallbackClosePopup()
                -- stop test sound effect 关闭音效
                cc.SimpleAudioEngine:getInstance():stopEffect(effectID)
                menuPopup:setVisible(false) -- 隐藏菜单
            end
    
    		-- 点击菜单回调函数
            local function menuCallbackOpenPopup()
                -- loop test sound effect 打开营销
                local effectPath = cc.FileUtils:getInstance():fullPathForFilename("effect1.wav")
                effectID = cc.SimpleAudioEngine:getInstance():playEffect(effectPath)
                menuPopup:setVisible(true)
            end
    
            -- add a popup menu 创建弹出的菜单面板
            local menuPopupItem = cc.MenuItemImage:create("menu2.png", "menu2.png")
            menuPopupItem:setPosition(0, 0)
            menuPopupItem:registerScriptTapHandler(menuCallbackClosePopup)
            menuPopup = cc.Menu:create(menuPopupItem)
            menuPopup:setPosition(origin.x + visibleSize.width / 2, origin.y + visibleSize.height / 2)
            menuPopup:setVisible(false)
            layerMenu:addChild(menuPopup)
            
            -- add the left-bottom "tools" menu to invoke menuPopup
    		-- 添加左下角的工具按钮,用来弹出菜单面板
            local menuToolsItem = cc.MenuItemImage:create("menu1.png", "menu1.png")
            menuToolsItem:setPosition(0, 0)
    		-- 注册点击回调地址
            menuToolsItem:registerScriptTapHandler(menuCallbackOpenPopup)
            menuTools = cc.Menu:create(menuToolsItem)
            local itemWidth = menuToolsItem:getContentSize().width
            local itemHeight = menuToolsItem:getContentSize().height
            menuTools:setPosition(origin.x + itemWidth/2, origin.y + itemHeight/2)
            layerMenu:addChild(menuTools)
    
            return layerMenu
        end
    
        -- play background music, preload effect
    
        -- uncomment below for the BlackBerry version
        local bgMusicPath = nil 
        if (cc.PLATFORM_OS_IPHONE == targetPlatform) or (cc.PLATFORM_OS_IPAD == targetPlatform) then
            bgMusicPath = cc.FileUtils:getInstance():fullPathForFilename("res/background.caf")
        else
            bgMusicPath = cc.FileUtils:getInstance():fullPathForFilename("res/background.mp3")
        end
        cc.SimpleAudioEngine:getInstance():playMusic(bgMusicPath, true)
        local effectPath = cc.FileUtils:getInstance():fullPathForFilename("effect1.wav")
    	-- 预加载音效
        cc.SimpleAudioEngine:getInstance():preloadEffect(effectPath)
    
        -- run
        local sceneGame = cc.Scene:create() -- 创建场景
        sceneGame:addChild(createLayerFarm())	-- 将农场层加入场景
        sceneGame:addChild(createLayerMenu())	-- 将菜单界面层加入场景
    	
    	-- 判断是否有运行的场景
    	if cc.Director:getInstance():getRunningScene() then
    		cc.Director:getInstance():replaceScene(sceneGame) -- 替换场景
    	else
    		cc.Director:getInstance():runWithScene(sceneGame)
    	end
    
    end
    
    --[[
    xpcall( 调用函数, 错误捕获函数 );
    lua提供了xpcall来捕获异常
    xpcall接受两个参数:调用函数、错误处理函数。
    当错误发生时,Lua会在栈释放以前调用错误处理函数,因此可以使用debug库收集错误相关信息。
    两个常用的debug处理函数:debug.debug和debug.traceback
    前者给出Lua的提示符,你可以自己动手察看错误发生时的情况;
    后者通过traceback创建更多的错误信息,也是控制台解释器用来构建错误信息的函数。
    --]]
    local status, msg = xpcall(main, __G__TRACKBACK__)
    if not status then
        error(msg)
    end
    


    这是Cocos2d-x 3.1.1所使用的代码,代码中的注释已经很详细了,笔者在这里就不多说,希望大家能认真阅读,跟笔者一起尽快入门Lua在Cocos2dx中的使用。










    展开全文
  • Vert.x-Web的讲解和使用(一)

    千次阅读 2015-07-14 14:00:39
    Vert.x-Web Vert.x-Web是vert.x的web构建模块。 你可以使用它构建现代的可扩展的web...Vert.x-Web基于Vert.x-Core为更容易的构建真正的web应用程序提供了丰富的功能。 Vert.x-Web的设计灵感来自Node.js和Ruby,并且成


    Vert.x-Web

    Vert.x-Web是vert.x的web构建模块。

    你可以使用它构建现代的可扩展的web应用程序。

    Vert.x提供了一些相当底层的方式处理HTTP请求,但是对于大多数的应用程序这些也就够用了。

    Vert.x-Web基于Vert.x-Core为更容易的构建真正的web应用程序提供了丰富的功能。

    Vert.x-Web的设计灵感来自Node.js和Ruby,并且成功的继承了Vert.x.2.x版本。

    Vert.x-Web是强大的,灵活以及可嵌入的。你可以使用其中的部分功能或者完全抛弃它来构建自己的应用程序。还有Vert.x不是一个容器。

    你可以使用Vert.x来构建经典的WEB应用程序,基于restfull的Web应用程序,实时通信的web应用程序或者任何类型的web应用程序。Vert.x完全可以胜任这些工作。创建什么类型的WEB应用程序完全是有你来决定,Vert.x仅仅提供支持而已。

    Vert.x-Web是一个极好的编写RESTful服务的,但是我们不强迫你写这样的程序。

    关于一些Vert.x的关键特征:

    路由(基于方法、路径等)

    正则表达式模式匹配的路径

    从路径提取参数

    内容协商

    请求主体处理

    身体大小限制

    Cookie解析和处理

    多表单上传

    多文件上传

    子路由器支持

    会话支持——本地和集群

    交叉起源资源共享支持

    错误页面处理程序
    基本身份验证
    基于重定向的身份验证
    授权处理程序
    JWT基于授权
    用户/角色/权限授权
    标识处理

    服务器端呈现的模板支持,包括支持以下模板引擎的:
    Handlebars
    Jade
    MVEL
    Thymeleaf

    响应时间处理程序
    静态文件服务,包括缓存逻辑和目录清单。
    请求超时的支持

    SockJS支持
    事件总线桥

    大多数的Vert.x的特性被实现为处理事件(Handler),这样你就可以自己实现它。随着时间的推移我们将加入更多特性。

    下面我们将讨论这些特性

    使用Vert.x-Web

    如果你想要使用Vert.x可以在你的构建配置文件中添加如下依赖关系:

    Maven(在你的pom.xml):

    <dependency>

    <groupId>{maven-groupId}</groupId>

    <artifactId>{maven-artifactId}</artifactId>

    <version>{maven-version}</version>

    </dependency>

    Gradle(在你的build.gradle):

    compile {maven-groupId}:{maven-artifactId}:{maven-version}

    Re-cap 基于 Vert-x核心HTTP 服务器

     Vert.x-Web基于Vert.x核心的公开API.所以在你使用Vert.x-Web的时候你应该对vert.x的核心API有必要的理解。

    下面我们将带你熟悉一下Vert.x的核心HTTP文档。

    这里有一个使用Vert.x核心API编写的《hello world》的web服务器。在这个例子中我们没有使用Vert.x-Web的相关特性:

    HttpServer server = vertx.createHttpServer();

    server.requestHandler(request -> {

    // This handler gets called for each request that arrives on the server

    HttpServerResponse response = request.response();

    response.putHeader("content-type", "text/plain");

    // Write to the response and end it

    response.end("Hello World!");

    });

    server.listen(8080);

    我们创建了一个HTTPServer的实例,并且设置了一个请求处理事件,当一个请求到达服务器的时候,事件将被激发并作出应答。

    当请求到达服务器时,我们将设置content-type为Text/plain并且输出Hello World!的文字。这样一次请求就结束了。

    最后我们将服务绑定到8080端口。

    然后你可以在浏览器中访问http://localhost:8080,如果出现Hello World! 说明我们的Web服务启动成功!!

    基本Vert.x-Web概念

    Router 是Vert.x的核心概念之一。它的一个对象可以维护零个或多个路径。

    Router获得一个HTTP请求然后找到与这个请求相匹配的路径,并将这个请求转交到这个路径上。

    路径上可以关联处理事件,路径接收的请求后,触发处理事件,处理完成后结束或者转到下一个程序处理。

    下面是一个简单的Router的例子:

    HttpServer server = vertx.createHttpServer();

    Router router = Router.router(vertx);

    router.route().handler(routingContext -> {

    // This handler will be called for every request

    HttpServerResponse response = routingContext.response();

    response.putHeader("content-type", "text/plain");

    // Write to the response and end it

    response.end("Hello World from Vert.x-Web!");

    });

    server.requestHandler(router::accept).listen(8080);

    这个例子是我们上一章所讲的Hello World。只不过这次使用Vert.x-Web实现。

    我们先创建了一个HTTPServer,然后创建了一个Router。这次我们创建了一个简单的没有任何匹配规则的Router,这意味着它将匹配所有到达服务器的请求。

    然后我们为Router创建处理程序。处理程序将响应所有到达服务器的请求。

    这个Router将进入标准的RoutingContext处理程序(包含标准的HttpServerRequest和HttpServerResponse)而其他的东西则使Vert.x-Web工作更简单。

    每一个请求都是一个路径,每一个路径都有相应的路径的上下文实例,这些请求将被相同的上下文实例的处理程序处理。

    一旦我们设置了处理程序后,所有到达服务起的请求将被处理程序响应。

    处理请求和调用下一个处理程序

    当Vert.x-Web决定由一个路径的处理程序请求另一个路径的处理程序时,你只需要调用下一个路径的处理程序并将RoutingContext对象传入处理程序。

    如果在当前处理程序中你不想结束请求,你还可以将请求转入到另一个相匹配的处理程序中。

    你不是必须马上调用下一个处理程序。你可以为下个处理程序调用设置延时,让其在多长时间之后运行:

    	Route route1 = router.route("/some/path/").handler(routingContext -> {
    
      	HttpServerResponse response = routingContext.response();
      	// enable chunked responses because we will be adding data as
      	// we execute over other handlers. This is only required once and
      	// only if several handlers do output.
      	response.setChunked(true);
    
      	response.write("route1\n");
    
      	// Call the next matching route after a 5 second delay
      	routingContext.vertx().setTimer(5000, tid -> routingContext.next());
    	});
    
    	Route route2 = router.route("/some/path/").handler(routingContext -> {
    
      	HttpServerResponse response = routingContext.response();
      	response.write("route2\n");
    
      	// Call the next matching route after a 5 second delay
      	routingContext.vertx().setTimer(5000, tid ->  routingContext.next());
    	});
    
    	Route route3 = router.route("/some/path/").handler(routingContext -> {
    
     	 HttpServerResponse response = routingContext.response();
      	response.write("route3");
    
      	// Now end the response
      	routingContext.response().end();
    	});

    在上面的例子中:route1 写入响应 等待5秒后 ,调用route2 , 然后在等待5秒后 route3运行 最后响应结束。

    注意:所有这一切发生的时候 没有线程处于阻塞状态。

    使用阻塞处理程序

    有时候,你需要做一些花费时间的事情,需要处理程序循环一段时间。比如:调用遗留阻塞API或者做一些密集的计算。

    使用平常的处理程序你不能做这样的事情,所以我们提供了可阻塞的处理程序。

    可阻塞的处理程序看起来就像一个普通的处理程序,但是在Vert.x中它使用的是一个工作池里的线程,而不是通常使用的循环。

    你可以为一个路径(route)添加一个blockingHandler。比如这样:

    	router.route().blockingHandler(routingContext -> {
    
      	// Do something that might take some time synchronously
      	service.doSomethingThatBlocks();
    
      	// Now call the next handler
      	routingContext.next();
    
    		});

    默认情况下:所有阻塞程序运行相同的上下文时,这意味着在当前处理程序执行完成之前,下一个将不被执行。如果你不关心执行顺序和不在意你的阻塞处理程序并行执行,你可以设置特殊的指令屏蔽它。

    没看明白附上原文:

    By default, any blocking handlers executed on the same context (e.g. the same verticle instance) are ordered - this means the next one won’t be executed until the previous one has completed. If you don’t care about orderering and don’t mind your blocking handlers executing in parallel you can set the blocking ha ndler specifying ordered as false using blockingHandler.

    路经的设置

    一个Route可以设置请求的URI匹配路径,在本例中,他将匹配任何相同的请求路径。

    在接下来的例子中这个处理程序将会响应/some/path/路径的请求。Route将会忽略斜杠(/)所以它也将响应 /some/path 和 /some/path//:

    	Route route = router.route().path("/some/path/");
    
    	route.handler(routingContext -> {
      	// This handler will be called for the following request paths:
    
     	 // `/some/path`
      	// `/some/path/`
     	 // `/some/path//`
     	 //
      	// but not:
     	 // `/some/path/subdir`
    	});

    Route路经的开始

    经常我们需要访问特定的开头的路径,要做到这一点,我们可以使用一个表达式。有一个更简单的方法就是在定义路径的时候使用星号(*)作为路径的结束。

    如下的例子中 这个处理程序将响应所有以/some/path/开头的所有请求:

    比如:/some/path/foo.html 和 /some/path/otherdir/blah.css 都匹配

    	Route route = router.route().path("/some/path/*");
    
    	route.handler(routingContext -> {
      		// This handler will be called for any path that starts with
      		// `/some/path/`, e.g.
    
      		// `/some/path`
      		// `/some/path/`
      		// `/some/path/subdir`
      		// `/some/path/subdir/blah.html`
      		//
     		 // but not:
      		// `/some/bath`
    });

    任何Route都可以在创建时指定路径

    	Route route = router.route("/some/path/*");
    
    	route.handler(routingContext -> {
      		// This handler will be called same as previous example
    	});

    Route捕获路径参数

    我们可以使用占位符来匹配路径用来捕获路径参数。

    比如这样:

    	Route route = router.route(HttpMethod.POST, "/catalogue/products/:productype/:productid/");
    
    	route.handler(routingContext -> {
    
      		String productType = routingContext.request().getParam("producttype");
      		String productID = routingContext.request().getParam("productid");
    
      		// Do something with them...
    	});	

    占位符的格式是《:参数名称》。其中参数名称包括任何字母字符,数字字符或下划线。

    在上面的例子中:如果post请求的地址是:/catalogue/products/tools/drill123/ 然后Route会匹配为:productType的值为 tools和productID的值为drill123.

    Route使用正则表达式

    Route可以使用正则表达式。

    	Route route = router.route().pathRegex(".*foo");
    
    	route.handler(routingContext -> {
    
      		// This handler will be called for:
    
      		// /some/path/foo
     		 // /foo
      		// /foo/bar/wibble/foo
      		// /foo/bar
    
      		// But not:
      		// /bar/wibble
    	});

    另外在创建Route的时候也可以指定正则表达式:

    	Route route = router.routeWithRegex(".*foo");
    
    	route.handler(routingContext -> {
    
      		// This handler will be called same as previous example
    
    	});

    Route使用正则表达式捕获路径参数

    你可以使用正则表达式捕获路径参数。这里有个例子:

    	Route route = router.routeWithRegex(".*foo");
    
    	// This regular expression matches paths that start with something like:
    	// "/foo/bar" - where the "foo" is captured into param0 and the "bar" is captured into
    	// param1
    	route.pathRegex("\\/([^\\/]+)\\/([^\\/]+)").handler(routingContext -> {
    
     	 	String productType = routingContext.request().getParam("param0");
      		String productID = routingContext.request().getParam("param1");
    
     		 // Do something with them...
    });


    在上面的例子中, 如果请求的路径是:/tools/drill123/,然后这个路径会匹配为:productType的值被设置为 tools ,productID的值会被设置为drill123.

    抓取和抓取组都使用正则表达式。

    捕获指定提交方法的路径

    默认情况下 Route会匹配所有提交的HTTP请求,不论是get提交,还是post提交。

    如果你想让Route匹配特殊的HTTP提交方式,你可以使用 method()方法:

    	Route route = router.route().method(HttpMethod.POST);
    
    	route.handler(routingContext -> {
    
      		// This handler will be called for any POST request
    
    	});

    或者你可以在创建Route的时候指定:

    	Route route = router.route(HttpMethod.POST, "/some/path/");
    
    	route.handler(routingContext -> {
    
      		// This handler will be called for any POST request to a URI path starting with /some/path/
    
    	});

    如果你想让 Route匹配特定的HTTP提交方式,你还可以使用其他的方法 比如: get() post() put()。像下面的例子那样:

    	router.get().handler(routingContext -> {
    
     	 // Will be called for any GET request
    
    	});
    
    	router.get("/some/path/").handler(routingContext -> {
    
     	 // Will be called for any GET request to a path
      	// starting with /some/path
    
    	});
    
    	router.getWithRegex(".*foo").handler(routingContext -> {
    
     	 // Will be called for any GET request to a path
      	// ending with `foo`
    
    	});
    如果你想匹配多个提交方式的话,你可以多次调用method()方法:

    	Route route = router.route().method(HttpMethod.POST).method(HttpMethod.PUT);
    
    	route.handler(routingContext -> {
    
      		// This handler will be called for any POST or PUT request
    
    	});

    Route的顺序

    Route的默认顺序就是你添加时的顺序。

    当一个请求到达的时候,Router会遍历每个Route并且检查是否匹配,如果匹配的话对应的处理程序将被唤醒,用来处理请求。

    如果处理程序随后调用next()方法的话,那么下一个匹配的处理程序将被调用处理。

    这里有一个例子用来说明这件事情:

    	Route route1 = router.route("/some/path/").handler(routingContext -> {
    
     	 HttpServerResponse response = routingContext.response();
     	 // enable chunked responses because we will be adding data as
     	 // we execute over other handlers. This is only required once and
      	// only if several handlers do output.
      	response.setChunked(true);
    
     	 response.write("route1\n");
    
      	// Now call the next matching route
     	 routingContext.next();
    	});
    
    	Route route2 = router.route("/some/path/").handler(routingContext -> {
    
      	HttpServerResponse response = routingContext.response();
      	response.write("route2\n");
    
      	// Now call the next matching route
      	routingContext.next();
    	});
    
    	Route route3 = router.route("/some/path/").handler(routingContext -> {
    
      	HttpServerResponse response = routingContext.response();
      	response.write("route3");
    	
      	// Now end the response
      	routingContext.response().end();
    	});
    上面的例子的响应是:

    route1

    route2

    route3

    这些Route按照添加的顺序来响应来自/some/path的请求。

    如果你想改变默认的排序,你仅需要为Route的order()方法指定一个特殊的整数值。

    Route在创建的时候被分配一个序号,并且按照这个序号的前后顺序被添加到Router。第一个的编号为0 第二个编号为1 。。。。

    通过指定Route的顺序,你可以改变Route的默认顺序。如果你想让一个Route在0之前执行的话,序号可以是一个负数。

    让我们改变默认的顺序,让route2在route1前运行:

    	Route route1 = router.route("/some/path/").handler(routingContext -> {
    
     	 HttpServerResponse response = routingContext.response();
      	response.write("route1\n");
    
     	 // Now call the next matching route
      	routingContext.next();
    	});
    
    	Route route2 = router.route("/some/path/").handler(routingContext -> {
    
      	HttpServerResponse response = routingContext.response();
      	// enable chunked responses because we will be adding data as
      	// we execute over other handlers. This is only required once and
      	// only if several handlers do output.
     	 response.setChunked(true);
    
      	response.write("route2\n");
    
      	// Now call the next matching route
      	routingContext.next();
    	});
    
    	Route route3 = router.route("/some/path/").handler(routingContext -> {
    
      	HttpServerResponse response = routingContext.response();
     	 response.write("route3");
    
      	// Now end the response
      	routingContext.response().end();
    	});
    
    	// Change the order of route2 so it runs before route1
    	route2.order(-1);

    现在的响应内容将变成这样的:

    route2

    route1

    route3

    如果两个相匹配的Route拥有相同的序号的时候,他们的执行顺序将按照添加的顺序来执行(即默认顺序)。

    如果你想要一个Route在最后执行的话,你只需要调用last()方法。

    Route设置请求的MIME类型

    你可以使用consumes()方法设置一个Route指定匹配的request的MIME类型。

    在这种情况下,RquestBody中将包含一个Content-type用于指定请求的MiME的类型,MIME类型的值将被comsumes()设置的值匹配。

    基本上。consumes定义了处理程序可以匹配处理的MIME类型。

    我们可以精确的匹配MIME类型:

    	router.route().consumes("text/html").handler(routingContext -> {
    
      	// This handler will be called for any request with
      	// content-type header set to `text/html`
    
    	});
    也可以匹配多个MIME类型:

    	router.route().consumes("text/html").consumes("text/plain").handler(routingContext -> {
    
     	 // This handler will be called for any request with
     	 // content-type header set to `text/html` or `text/plain`.
    
    	});
    同样可以支持通配符:

    	router.route().consumes("text/*").handler(routingContext -> {
    
     	 // This handler will be called for any request with top level type `text`
     	 // e.g. content-type header set to `text/html` or `text/plain` will both match
    
    	});
    还可以匹配指定结尾的MIME类型:

    	router.route().consumes("*/json").handler(routingContext -> {
    
      	// This handler will be called for any request with sub-type json
      	// e.g. content-type header set to `text/json` or `application/json` will both match
    
    	});

    如果你不指定带/的类型,它会认为你使用自定义的类型。


    组合使用Route匹配

    你可以使用不同的方法组合我们上面讲过的Route的方法。
    例如:

    	Route route = router.route(HttpMethod.PUT, "myapi/orders")
                        .consumes("application/json")
                        .produces("application/json");
    
    	route.handler(routingContext -> {
    
      	// This would be match for any PUT method to paths starting with "myapi/orders" with a
      	// content-type of "application/json"
      	// and an accept header matching "application/json"
    
    	});

    禁用和启用Route

    你可以使用route的disable()方法禁用Route,一个禁用的Route在匹配的时候将会被忽略。
    同样你可以使用Route的enable()方法启用它。


    上下文数据

    如果你想要在一次请求的两个处理程序之间传输数据的话,你可以将数据保存在RoutingContext中。
    这里有一个例子,我们在一个处理程序中将一点数据放到上下文中存储并且在下一个处理程序中再次拿到它。
    你可以放进上下文中任何对象,并且能从上下文中把它取出来。
    请求 /some/path/other将会匹配如下两个Route。

    	router.get("/some/path").handler(routingContext -> {
    
      	routingContext.put("foo", "bar");
      	routingContext.next();
    
    	});
    
    	router.get("/some/path/other").handler(routingContext -> {
    
      	String bar = routingContext.get("foo");
      	// Do something with bar
      	routingContext.response().end();
    
    	});

    另一种选择是,你可以通过data()方法获得RoutingContext的上下文的Map集合。

    Router挂载

    有时你把很多的处理程序有意义的分割到多个Router中.如果你想在不同的应用程序或者不同的跟路径下使用他们的话,下面的内容就相当有用了。

    你可以将一个Router挂载到另一个Router下,这样就可以做到Router的重复利用了。这个挂载在别的Router下的Router就被叫为子Router。

    如果你需要的话子Router下还可以挂载其他的子Router,这样你就可以有几个级别的Router了。

    让我们看一个将子Router挂载到其他Router的例子:

    这个子Router维护了一些简单的REST的API的处理程序,我们将它挂载到另一个Router下。在此我们就不实现这个REST API了。

    这是子Router:

    	Router restAPI = Router.router(vertx);
    
    	restAPI.get("/products/:productID").handler(rc -> {
    
      	// TODO Handle the lookup of the product....
      	rc.response().write(productJSON);
    
    	});
    
    	restAPI.put("/products/:productID").handler(rc -> {
    
      	// TODO Add a new product...
      	rc.response().end();
    
    	});
    
    	restAPI.delete("/products/:productID").handler(rc -> {
    
      	// TODO delete the product...
      	rc.response().end();
    
    	});
    如果这个Router作为一个顶级的Router,那么GET/PUT/DELETE请求就会被响应。 就像/products/product1234会成功调用API。

    比如说,我们有一个被另一个Router描述的网站。

    	Router mainRouter = Router.router(vertx);
    
    	// Handle static resources
    	mainRouter.route("/static/*").handler(myStaticHandler);
    
    	mainRouter.route(".*\\.templ").handler(myTemplateHandler);

    我们可以定义一个挂载点/productsAPI用于挂载子Router到这个主Router下。

    	mainRouter.mountSubRouter("/productsAPI", restAPI);

    这意味着REST API的可访问路径编程了/productsAPI/products/product1234.

    默认的404处理程序

    如果没有相匹配的Route.Vert.x将会抛出404响应。

    我们可以定义自己的错误处理程序,或者提供可用的错误增强程序,如果没有私有的错误处理程序,Vert.x将会抛出404(Not Found)响应。



    本翻译只为抛砖引玉,有什么不到位的地方希望大家能够海涵(小弟英语水平菜鸟一枚),有什么错误还请诸位大大不吝赐教,小弟将不胜感激。

    本翻译只为抛砖引玉,有什么不到位的地方希望大家能够海涵(小弟英语水平菜鸟一枚)

    展开全文
  • x265-1.8版本-common/threading.h注释

    万次阅读 2016-02-15 19:51:45
    x265-1.8版本-common/threading.h注释
  • x265-1.7版本-common/threading.h注释

    万次阅读 2016-01-22 22:55:39
    x265-1.7版本-common/threading.h注释
  • x265-1.7版本-encoder/slicetype.cpp注释

    万次阅读 2016-01-23 14:24:01
    x265-1.7版本-encoder/slicetype.cpp注释
  • x265-1.8版本-encoder/slicetype.cpp注释

    万次阅读 2016-02-15 20:26:54
    x265-1.8版本-encoder/slicetype.cpp注释
  • Cocos2d-x-2.2.1最新版各个平台搭建

    千次阅读 2014-02-11 18:05:37
    声明:本教程在参考了以下博文,并经过自己的摸索后实际操作得出,本教程系本人原创,由于升级后的cocos2d-x有了一些变化,目前的博文还没有关于Cocos2d-x2.2.1最新版搭建Android交叉编译环境组建的说明,所以我这几...
  • xorg-xserver相关完全解析

    千次阅读 2016-04-29 17:34:51
    本文主要是从以下几个方面介绍xorg-xserver 相关的知识  1.linux系统图形界面框架 2.xserver 和x client启动过程 3.图形2d,3d加速原理简介 4.xserver主分支代码解析。 5.xserver,xclient协议简介 6.一个基于...
  • 原创文章,欢迎转载,转载请注明:文章来自[寒江孤叶丶的Cocos2d-x之旅系列] 博客地址:http://blog.csdn.net/qq446569365 在Coco2d-x3.2版本中,对LuaSocket进行了集成,我们可以直接在程序中调用luaSocket进行...
  • shell脚本——调试(-n / -x /-c)

    万次阅读 2018-06-15 10:28:28
    有的,那就是使用shell的执行选项,下面将介绍一些常用选项的用法:-n 只读取shell脚本,但不实际执行-x 进入跟踪方式,显示所执行的每一条命令-c "string" 从strings中读取命令"-n"可用于测试...
  • Vert.x 3学习笔记---14( Web篇 )

    万次阅读 2016-03-07 10:43:49
    之前的文章介绍的vertx-core的内容。我们可以看到core中的东西就像是一个异步的工具集。我们可以在这个工具集的基础上面进行很多的操作。比如:异步的文件操作、异步的socket等等。...Vert.x-WebVert
  • 时间下载了cocos2d-x 3.1,按照官网的教程,配置环境,编译打包,走了一遍,感觉不错,顺便发现其中用了很多python的脚本文件,比如今天要说的android-build.py.这个文件把编译,打包的功能全部整合到了一起.也就是传说...
  • 【Cocos2d-x 状态机篇】第04章--事件驱动,你想象不到的强大   笨木头花心贡献,啥?花心?不呢,是用心~ 转载请注明,原文地址: http://blog.csdn.net/musicvs/article/details/8349314   正文:     到...
  • 学习cocos2d-x自带的SimpleGame

    千次阅读 2014-01-27 09:36:20
    cocos2d-x文件目录里面自带了很多例子,其中就有一个SimpleGame。它属于射击类的小小小游戏。 开始学吧 首先新建个cocos2d-x项目,打开终端进入project-creator目录,输入: ./create_project.py -project ...
  • VL53L0X为2m测距版本,VL53L1X为4m版本。目前测试发现,这两颗芯片并不只是性能区别,**官方给出的是两套库,不能相互通用**。仔细阅读芯片手册甚至调用API的配置过程也是不同的,需要特别注意一下手中的芯片是哪个...
  • 声明:本文在参考了以下博客,经过自己的摸索后实际操作得出,本教程系本人原创,由于升级后的cocos2d-x有了一些变化,目前的博客还没有关于Cocos2d-x 2.2.1最新版搭建Android交叉编译环境组建的说明,所以我这几天...
  • C语言程序设计---读书笔记汇总(整理中)

    万次阅读 多人点赞 2018-05-11 23:34:01
    1、书籍信息 作者:(美)Brian W. Kernighan / (美)Dennis M. Ritchie 原作名: The C Programming Language 2、简单叙述 1978 : The C Programming Language ,第1版 1983 : ANSI 建立 X3J11 委员会 (标准...
  • 1、空语句在编译的时候会出现警告,所以有必要用#define FOO do{ } while(0) 2、给定一个基本块,可以在里面定义局部变量 3、为了能够在条件语句中使用复杂的宏定义。例如下面这代码: #define FOO(x) \
  • 看过cocos2d-x 自带demo的朋友相信很多人都会发现demo中用了很多do{}while(0)和CC_BREAK_IF宏.他们是做什么用的呢?  其实这个是c++中的一个技巧.  我们时常会调用某个函数来创建一个对象,但由于内存不足或其他...
  • 下面程序段中带下划线的语句的执行次数的数量级是( nlog2nnlog_2nnlog2​n )。 i:=1; WHILE i<n BEGIN FOR j:=1 TO n DO x:=x+1; i:=i*2; END 分析: i:=1; WHILE i<n BEGIN FOR j:=1 TO n DO x:=x+1; ...
  • cocos2d-x里有两个物理引擎分别是box2d和chipmunk(金花鼠,不知道为什么叫这名字……),如果想看看基础的常识及这两个物理引擎的区别请到以下网址:http://bsr1983.iteye.com/blog/1672032 其实本人的物理知识也...
  • 相应的源代码位于该指南代码库的step-1目录下 我们将从第一次迭代开始,最简单的代码可能是使用Vert.x编写一个Wiki。而下一次迭代将在代码库中引入更多的简洁以及适当的测试,我们将看到基于Vert.x的快速原型是一...
  • 1.使用POST发送数据   以POST方式发送数据主要是为了向服务器发送较大量的客户端的数据,它不受URL的长度限制。POST请求将 数据以URL编码的形式放在HTTP正文中,字段形式为fieldname=value,用&分隔每个字段。...
  • cocos2d-x 3.x封装socket方法

    千次阅读 2016-08-01 22:11:51
    在Coco2d-x3.2版本中,对LuaSocket进行了集成,我们可以直接在程序中调用luaSocket进行方便的TCP/UDP/FTP/HTTP等等通讯,非常方便。 下边先上一代码:   view sourceprint? 01.local ...
  • Java语言程序设计---期末复习试题

    千次阅读 2020-12-26 19:43:14
    1、关于Java语言叙述错误的是:( C) A.Java语言具有跨平台性 B.Java是一种面向对象的语言 C.Java语言中的类可以多继承 D.Java的垃圾收集机制自动回收程序已不再使用的对象 2、以下叙述正确的是:( B)的确台a...
  • 這個錯誤是在一開始訓練Keras(使用TensorFlow當backend)模型的時候就出現。Python版本:3.5.2 Keras版本:2.1.2 ...試著將這些程序關掉,然後再重新運行剛剛那代碼(不必重啟kernel),問題即可成功解決!
  • memset:作用是在一内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法 。 */ memset(&_quad, 0, sizeof(_quad)); // Atlas: Color /* // 顶点坐标,纹理坐标和颜色信息 // ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 156,823
精华内容 62,729
关键字:

以下程序段x=-1;do{x=x*x}