Dota2 AI 开发 (二)定制AI阵容 配置英雄出装

2017年4月17日 没有评论

Dota2 AI 开发(一)环境配置 中介绍了如何搭建 Dota2 AI 的开发环境,在这篇文章中,主要介绍Dota2中AI的常规控制方式,并介绍如何在人机比赛中配置一个裸跳刀的Sven。

常用指令

  • 重新加载Lua脚本:dota_bot_reload_scripts
  • 加速游戏:host_timescale 4.0
  • 开启作弊:sv_cheats 1

控制选人

在bots目录下创建一个名为 hero_selection.lua的脚本,在其中输入如下内容:

function Think()


    if ( GetTeam() == TEAM_RADIANT )
    then
        print( "selecting radiant" );
        SelectHero( 0, "npc_dota_hero_antimage" );
        SelectHero( 1, "npc_dota_hero_lina" );
        SelectHero( 2, "npc_dota_hero_sven" );
        SelectHero( 3, "npc_dota_hero_bloodseeker" );
        SelectHero( 4, "npc_dota_hero_crystal_maiden" );
    elseif ( GetTeam() == TEAM_DIRE )
    then
        print( "selecting dire" );
        SelectHero( 5, "npc_dota_hero_drow_ranger" );
        SelectHero( 6, "npc_dota_hero_earthshaker" );
        SelectHero( 7, "npc_dota_hero_juggernaut" );
        SelectHero( 8, "npc_dota_hero_mirana" );
        SelectHero( 9, "npc_dota_hero_nevermore" );
    end

end

这就是自定义的英雄阵容了,己方阵容为:

  • 敌法
  • 火女
  • 斯文
  • 血魔
  • 冰女

敌方为:

  • 小黑
  • 小牛
  • 剑圣
  • 白虎
  • 影魔

阵容当然是可以换的,那么如何知道想要的英雄叫什么呢?

更改为英文

在游戏属性设置>设置启动项中添加 -language english即可将界面改成英文,这样就可以知道所有东西的名称了。

比如在上面的敌方阵营中没有T,我希望把影魔换掉,所以我找到DK,它的名字如下:

Dota2 AI 开发 (二)定制AI阵容 配置英雄出装

所以将上面函数选人的最后一行改为:

SelectHero( 9, "npc_dota_hero_abaddon" );

改好后保存,然后在控制台运行

dota_bot_reload_scripts

然后再次创建房间开游戏,就会发现预选的角色变化了

Dota2 AI 开发 (二)定制AI阵容 配置英雄出装

通过这样的方式,相信大家能找到一个合适的阵容,以提高游戏乐趣。当然这也是制作特定角色AI的基础。

控制购买装备

在 Dota2 ai 中,是可以制定不同英雄的出装套路的。只要按照命名规范创建item_purchase_xxxx.lua即可。不过我发现有的道具名字并不是显示的英文名。例如跳刀明明是:

Dota2 AI 开发 (二)定制AI阵容 配置英雄出装

结果到了配置里就是item_blink,这是怎么回事呢?我查了一下午,终于搞明白其中的缘由了。因为道具名称可能会随着道具描述更改,为了保持底层数据的稳定,官方制定了一个对照表。我在gamepedia上找到了一个版本。 应该是比较全的了,另外这个页面里也有英雄名哦。

我觉得有时候找起来实在是麻烦了,所以会直接通过指令给自己调装备测试,例如我刚刚的跳刀,我可以通过指令来尝试来找到名字,如果装备名正确,自己使用的角色身上就会获得它。

-item item_blink_dagger //wrong
-item item_blink //correct

弄清楚了道具名称,我们就可以开始着手制作自己的AI出装了。这里我以Sven为例。

实现

我希望Sven,买完吃喝之后,裸一个跳刀,然后出相位鞋、连击刀、bkb、撒旦、龙心。首先我先在bots目录下创建一个名为:item_purchase_sven.lua的文件,然后编辑如下内容:

local tableItemsToBuy = { 
        "item_tango",
        "item_clarity",
        "item_flask",
        ----------------------
        "item_blink",
        ---------------------
        "item_boots", 
        "item_blades_of_attack",
        "item_blades_of_attack",
        ----------------------
        "item_ogre_axe",
        "item_quarterstaff",
        "item_sobi_mask",
        "item_robe",
        ---------------------- 
        "item_ogre_axe",
        "item_mithril_hammer",
        "item_recipe_black_king_bar",
        -----------------------
        "item_lifesteal",
        "item_mithril_hammer",
        "item_reaver",
        ----------------------
        "item_reaver",
        "item_vitality_booster",
        "item_recipe_heart",
    };


-----------------------------------------------------------------------------

local secretShopThreshold = 100000;
local distanceBuyShop = 500;

function ItemPurchaseThink()

    local npcBot = GetBot();

    if ( #tableItemsToBuy == 0 )
    then
        npcBot:SetNextItemPurchaseValue( 0 );
        return;
    end

    local sNextItem = tableItemsToBuy[1];

    npcBot:SetNextItemPurchaseValue( GetItemCost( sNextItem ) );

    if ( npcBot:GetGold() >= GetItemCost( sNextItem ) )
    then
        if ( IsItemPurchasedFromSecretShop(sNextItem) and 
            npcBot:DistanceFromSecretShop() <= secretShopThreshold )
        then
            --print("Money is enough,Will Move to secret shop for: ",sNextItem);
            npcBot.secretShopMode = true;

            local shop_top = Vector(-4600, 1200);
            local shop_bot = Vector(4600,  -1200);

            local dist_top = GetUnitToLocationDistance( npcBot, shop_top );
            local dist_bot = GetUnitToLocationDistance( npcBot, shop_bot );

            if (dist_top < dist_bot) then
                npcBot:Action_MoveToLocation(shop_top);
            else
                npcBot:Action_MoveToLocation(shop_bot);
            end

            if ( npcBot:DistanceFromSecretShop() <= distanceBuyShop ) 
            then
                print("Will buy at secret shop : ",sNextItem," cost is:",
                    tostring(GetItemCost(sNextItem)));
                npcBot:ActionImmediate_PurchaseItem( sNextItem );
                table.remove( tableItemsToBuy, 1 );
                npcBot.secretShopMode = false;
            end
        else
            print("Money is enough,Will buy: ",sNextItem," cost is:",
                tostring(GetItemCost(sNextItem)));
            npcBot:ActionImmediate_PurchaseItem( sNextItem );
            table.remove( tableItemsToBuy, 1 );
        end
    end

end

-------------------------------------------------------------------------

需要注意的是不能买合成出的装备,需要自己按配方一个个配置。

配好后难道要等AI自己打出那么多钱来测试么?当然不,通过指令

dota_bot_give_gold 1000

来给所以AI加钱,直接就能看到结果。为了调试可以先把出门用的吃喝注释掉,防止占格子位置。

另外这套AI还有些瑕疵。我发现当角色不在神秘商店附近时是不可以买东西的,所以我写了个逻辑:如果需要买神秘商店的装备,就先将角色移动过去,然后再买。逻辑是这样的没错,不过角色移动的表现会比较傻缺。不管不顾的直接走也就罢了,有时候还会摩擦摩擦。搞了一天又累又饿,既然都能用,就先这样吧,以后有时间再优化,或者有大神路过帮忙看看也好。

总结

最后上个效果图

Dota2 AI 开发 (二)定制AI阵容 配置英雄出装

Dota2 AI 开发 (二)定制AI阵容 配置英雄出装

关注我的微信公众号,获取更多优质内容

分类: 未分类 标签:

OpenGL(八)使用 subroutine 切换可编程管线

2017年4月14日 没有评论

Subroutine 功能是在OpenGL 4.0 版本里才增加的,因此对于各种Android手机,这个功能基本跪了。如果你发现你的程序报错:ARB_shader_subroutine,那就说明当前显卡不支持。不过大体思路可以了解一下,因为思路类似的功能有其他的实现方式。

原理

在shader中声明一个函数变量,然后定义它的指针,并将其作为一个uniform变量公开出去。最后定义很多复写函数即可。

实现

由于版本限制,使用 subroutine 要注意在shader中加入版本的编译宏:

#version 400 core

在shader中编写:

subroutine vec4 SurfaceColor();
subroutine uniform SurfaceColor U_SurfaceColor;

subroutine (SurfaceColor) vec4 Ambient()
{
   //...
}

subroutine (SurfaceColor) vec4 Diffuse()
{
    //...
}

subroutine (SurfaceColor) vec4 Specular()
{
    //...
}

void main()
{
    gl_FragColor = U_SurfaceColor();
}

在shader中,每一个函数中的代码段代表一种处理函数。另一方面在GL指令中,绑定这个函数指针,并为其指定实现函数的索引值,即可实现效果的控制。

surfaceColorLocation = glGetSubroutineUniformLocation(program,GL_FRAGMENT_SHADER,"U_SurfaceColor");

GLuint ambientLightIndex = glGetSubroutineIndex(program,GL_FRAGMENT_SHADER,"Ambient");
GLuint diffuseLightIndex = glGetSubroutineIndex(program,GL_FRAGMENT_SHADER,"Diffuse");
GLuint specularLightIndex = glGetSubroutineIndex(program,GL_FRAGMENT_SHADER,"Specular");

//draw
glUniformMatrix4fv(MLocation, 1, GL_FALSE, glm::value_ptr(model));
glUniformSubroutinesuiv(GL_FRAGMENT_SHADER,1,&ambientLightIndex);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,ibo);
glDrawElements(GL_TRIANGLES,indexCount,GL_UNSIGNED_INT,0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

总结

本文介绍了OpenGL中的 subroutine 机制,通过它可以切换shader的内容。在Unity3d中使用Shader.maximumLOD技术可以达到类似的效果,相关内容可以参考官方文档

OpenGL(八)使用 subroutine 切换可编程管线

关注我的微信公众号,获取更多优质内容

分类: 未分类 标签:

OpenGL(七) GeometryInstancing 几何体实例化

2017年4月10日 没有评论

几何体实例化( GeometryInstancing ),是一种用于大批量重复物件渲染的GPU技术,以降低客户端和显卡端数据传输量,所谓的“一次提交,多次渲染”。简单说来就是合并DrawCall。

原理

通过

glDrawElementsInstanced(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0,3);

可以绘制多次几何体,这样就可以实现合并。然而不同几何体都重叠在一起了,这就需要传入一个差异性参数。然后通过glVertexAttribDivisor来指定分组赋值规则,这就相当于for循环最后的++i

实现

首先先设定一个变量数组,并将它存入VBO中

float posOffsets[] = {
    -1.0f,0.0f,0.0f,
    0.0f,0.0f,0.0f,
    1.0f,0.0f,0.0f
};
GLuint offsetVBO=CreateBufferObject(GL_ARRAY_BUFFER, sizeof(float) * 9, GL_STATIC_DRAW, posOffsets);

//get location index
offsetLocation = glGetAttribLocation(program, "offset");

绘制部分将这组VBO分组:

glBindBuffer(GL_ARRAY_BUFFER, offsetVBO);
glEnableVertexAttribArray(offsetLocation);
glVertexAttribPointer(offsetLocation, 3, GL_FLOAT, GL_FALSE, sizeof(float)*3, (void*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribDivisor(offsetLocation,1);

最后在shader中承接这组数据:

attribute vec3 pos;
attribute vec2 texcoord;
attribute vec3 normal;
attribute vec3 offset;

uniform mat4 M;
uniform mat4 V;
uniform mat4 P;
uniform mat4 NM;

varying vec3 V_Normal;
varying vec4 V_WorldPos;
varying vec2 V_Texcoord;

void main()
{
    V_Normal=mat3(NM)*normal;
    V_WorldPos=M*vec4(pos,1.0)+vec4(offset,1.0);
    V_Texcoord=texcoord;
    gl_Position=P*V*V_WorldPos;
}

这样就可以在3个不同的位置绘制相同的物体,且只占用一次DrawCall。

总结

通过 GeometryInstancing 可以在OpenGL中,实现合并Drawcall。

OpenGL(七) GeometryInstancing 几何体实例化

关注我的微信公众号,获取更多优质内容

分类: 未分类 标签:

[置顶] 房卡麻将分析之"防作弊处理"

2017年3月29日 没有评论

房卡麻将分析之"防作弊处理"

           

          棋牌游戏最重要的一个特点就是人与人对局,因为玩家各自的不可见,就存在着一些作弊的可能性和漏洞。对于手机房卡麻将游戏,大家最讨厌的问题就是作弊。如何防止玩家作弊,保证玩家的公平性,从技术上我们来讨论一些方案。



          首先,我先假设你的代码中服务器并不会犯一些低级错误,比如将所有玩家的手牌信息发给客户端。如果你没有保证好这一点,那么你的游戏将非常容易被辅助外挂进行”明牌化“处理。除去算牌记牌软件的因素,你的服务器只要不存在相关的漏洞就不用担心外挂。

                               [置顶]        房卡麻将分析之&quot;防作弊处理&quot;
          去掉服务器的因素,在客户端现在最经常出现的防作弊方案主要有两个方向:

一。玩家信息检测


第一种方向,主要是对于玩家的状态进行检测,目前也包括三个小的方法:


(一)玩家同IP提示。


          第一种方法它只是能够预防基于同一个局域网的玩家在一起游戏。处理也非常简单,因为服务器在接收手机联接时可以取得客户端IP地址,通过简单的转发,就可以让玩家得知同房间其它玩家的IP地址。如果房间里有相同IP的玩家,做一些提示即可。不过这种方案并不能够有效的预防作弊,基于4G信号来进行游戏的手机可以轻验跳过这样的验证。更多的时候只是一个心理预防。


(二)玩家GPS定位提示。


           第二种方法使用GPS定位来侦测玩家间距离相近或者定位一致。比第一种方案更真实的反映玩家是否在一起的情况。具体开发的时候,可以使用高德或百度的地图SDK,开发者到高德官网上申请账号,注册成为开发者后,可以创建应用,将包的信息提交,取得KEY,按照官网SDK接入游戏即可以方便的获取定位信息了。

[置顶]        房卡麻将分析之&quot;防作弊处理&quot;

           在服务器的数据库上,一般会有玩家上线信息的表,包括头像地址和GPS地址,玩家登录游戏后,通过高德或百度SDK取得当前定位并发送给服务器存储到数据库中,当玩家进入房间后,服务器也将其它玩家的相关信息发过来,在客户端进行对比并提示即可。

                        [置顶]        房卡麻将分析之&quot;防作弊处理&quot;

               在整个游戏过程中,可以根据需要进行定位更新处理。保证牌局对玩家进行实时检测。不过一般只需要游戏每局开局时做一些简单处理即可。


(三)玩家游戏过程视频在线。  


               第三种方法则可以在游戏过程中开启视频聊天,或用微信视频聊天,可以随时看到对方玩家在专注的游戏,也就避免玩家去进行作弊操作。不过这种方式相对比较耗电,技术上也相对较难一点。并不是太推荐。


               总体说来,在第一个方向上第二种方法比较可行,也较易操作。

二。牌局信息对比


第二种方向,主要是对于牌局的过程进行检查,目前也包括两个小的方法:


(一)牌局回放。

                牌局回放的意义在于玩家可以在游戏结束后对整个过程进行回顾,分析四个玩家的牌面进展整个过程,发现一些作弊操作。这个部分的技术方法我在公众号早期文章中进行了详细讲述,这里不再赘述,有兴趣的朋友可以查一下公众号历史文章。


                       [置顶]        房卡麻将分析之&quot;防作弊处理&quot;

(二)吃碰杠的供应记录。

              作弊的手段无非就是给牌,但一般游戏最容易的给牌就是吃和碰,在这方面做一些记录和显示,可以给玩家展现游戏过程中玩家是如何给牌的。比如我们的”大赢家“红中麻将,你可以清楚的看到哪个玩家给哪个玩家的碰牌。这种信息的处理非常重要和细致。

[置顶]        房卡麻将分析之&quot;防作弊处理&quot;

                 好了,基本上来说,并没有什么办法完全避免基于手机的房卡麻将,我们所做的手段,无非是降低作弊难度,提升公平性。如果有人说房卡麻将能百分之百防作弊,我觉得还是不要相信啦。最后我想说的是:游戏开心,远离赌博!

 

                  AR,VR,房卡棋牌技术,关注公众号:红孩儿的游戏开发之路

                                                   [置顶]        房卡麻将分析之&quot;防作弊处理&quot;

分类: 未分类 标签:

OpenGL(六)使用 VAO 打包指令 优化代码结构

2017年3月29日 没有评论

通常说来当创建好vbo的数据结构,还需要设置glVertexAttribPointer等一系列属性,在OpenGL中提供了 VAO (Vertex Array Object)它相当于同时记录了数据在哪里和数据是怎样分布。从实际应用的角度,它最重要的贡献就是简化了代码。

原理

通过接口函数生成一个VertexArray,然后在其中创建和设置VBO的相关参数,在最终绘制时,直接使用。

GLuint myVao;
glGenVertexArrays(1,&myVao);
glBindVertexArray(myVao);
//... some vbo operation
glBindVertexArray(0);

实现

通过C++的匿名函数,可以将vbo操作开放出去

#include <functional>

GLuint CreateVAOWithVBOSettings(std::function<void()> settings)
{
    GLuint vaoTemp;
    glGenVertexArrays(1,&vaoTemp);
    glBindVertexArray(vaoTemp);
    settings();
    glBindVertexArray(0);
    return vaoTemp;
}

调用时,将vbo过程作为参数传入:

struct VertexData
{
    float positon[3];
    float texcoord[2];
    float normal[3];
};

GLuint CreateBufferObject(GLenum bufferType,GLsizeiptr size,GLenum usage,void* data)
{
    GLuint object;
    glGenBuffers(1,&object);
    glBindBuffer(bufferType,object);
    glBufferData(bufferType,size,data,usage);
    glBindBuffer(bufferType,0);
    return object;
}

GLuint vao = CreateVAOWithVBOSettings([&]()->void
{
    GLuint vbo = CreateBufferObject(GL_ARRAY_BUFFER, sizeof(VertexData) * vertexCount, GL_STATIC_DRAW, vertexes);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glEnableVertexAttribArray(posLocation);
    glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)0);
    glEnableVertexAttribArray(texcoordLocation);
    glVertexAttribPointer(texcoordLocation, 2, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)(sizeof(float) * 3));
    glEnableVertexAttribArray(normalLocation);
    glVertexAttribPointer(normalLocation, 3, GL_FLOAT, GL_FALSE, sizeof(VertexData), (void*)(sizeof(float) * 5));
    glBindBuffer(GL_ARRAY_BUFFER, 0);
});


//draw
glBindVertexArray(vao);
//...
glBindVertexArray(0);

经过这样的改动,绘制部分变得非常简单。

总结

可以将 VAO 理解为一个存储指向VBO指针的数组,实际上数据还是储存在VBO中。它是将切分读取数据的过程打包在一起,避免了每次都编写繁琐的代码。

OpenGL(六)使用 VAO 打包指令 优化代码结构

关注我的微信公众号,获取更多优质内容

分类: 未分类 标签:

[置顶] 房卡麻将分析之"一键入局"

2017年3月28日 没有评论

  地方棋牌,VR,AR技术,请关注公众号:"红孩儿的游戏开发之路“

                  房卡麻将分析之"一键入局"


         房卡麻将通过微信邀请好友加入房间进行游戏,大大方便了玩家进行麻将游戏。这也凸显了微信对于游戏引流导量的强大平台作用。本次我们就给大家讲一下“一键入局”的技术原理。


          首先,我们先来疏理一下整个过程:

          玩家打开游戏,创建房间,并将房间信息(房间说明和房间号码)生成链接通过微信SDK的分享到好友或朋友圈。其它玩家看到这个链接并点击后,如果已经安装游戏,会提示打开游戏APP并直接加入房间。如果未完装游戏,则会转到游戏下载页。


          这是在微信中分享房间的链接:

              [置顶]        房卡麻将分析之&quot;一键入局&quot;
         [置顶]        房卡麻将分析之&quot;一键入局&quot;
           这里非常重要的一个技术是用到“魔窗”。它是实现通过微信链接打开App并进入房间的关键。

             [置顶]        房卡麻将分析之&quot;一键入局&quot;

           “魔窗”的主页是:http://www.magicwindow.cn,它有非常完整的集成文档,包括Android和IOS,Web等多个平台系统的接入说明,中外还有专业技术妹子的接入视频讲解,非常详尽。

             [置顶]        房卡麻将分析之&quot;一键入局&quot;
              这里主要使用的就是mLink,按照官方的文档一步步接入即可。在这一块,主要是需要生成一个短链接,这个短链接需要带上房间号信息,就像下面这样:

             [置顶]        房卡麻将分析之&quot;一键入局&quot;



             OK,配置好后台的信息并按文档集成好SDK后,在客户端逻辑部分要做的是当"魔窗" 唤醒App时所响应的函数中,将短链接房间号信息取出来。

AppActivity.java中的代码片段:

			MLink.getInstance(context).register("first",new MLinkCallback()
			{
				public void execute(Map<String, String>paramMap, Uri uri,Context context){
					String roomid = uri.getQueryParameter("roomid");
					if (roomid != null)
					{
						Native.WxAutoLoginSetInfo(Integer.parseInt("1"), roomid);
					}
				}
			});

JniCallback.cpp中的代码片段:


		JNIEXPORT void JNICALL Java_org_cocos2dx_cpp_Native_WxAutoLoginSetInfo(JNIEnv* env, jclass method, jint autoLogon, jstring roomId)
		{
			const char* data = env->GetStringUTFChars(roomId, 0);
			MissionWeiXin::m_nAutoLogin = autoLogon;
			MissionWeiXin::m_strRoomID = data;
		}

             有了房间号,设置给游戏中的变量,并在游戏中判断接收到的房间号是否有效,调用加入房间的函数进入房间,即可完成整个“一键入局”的处理了。


void SDHomeScence::update(float delta)
{
    //如果玩家自动进入游戏
	if (MissionWeiXin::m_nAutoLogin == 1 && isVisible())
    {
        int iActJoinNum = atoi(MissionWeiXin::m_strRoomID.c_str());
        int iServerID = iActJoinNum / 10000 - 10;
        SetJoinRoomIdInfo(iActJoinNum );
        
        CGameServerItem* pGameServer = GameManagerBase::InstanceBase().SearchGameServer(iServerID);
        if ( pGameServer )
        {
GameManagerBase::InstanceBase().connectGameServerByServerID(iServerID);
        }
        else
        {
            NoticeMsg::Instance().ShowTopMsgByScript("JoinRoomNumError");
        }
        MissionWeiXin::m_nAutoLogin = 0;
    }
}

        如此,整个过程就算基本完成了。好了,加入了微信”一键入局“,相信我们的房卡麻将游戏可以大大提升下载量和试玩量。


  地方棋牌,VR,AR技术,请关注公众号:"红孩儿的游戏开发之路“

分类: 未分类 标签:

OpenGL(五) 指令错误 检测的封装方法

2017年3月27日 没有评论

OpenGL的指令,返回值均为void,因此没法通过返回值来判断 指令错误 。为了能够第一时间发现问题,需要加入一个封装来监测是否有指令调用失败。

原理

当OpenGL调用出现错误时,会将错误的ID储存到一个GLenum中。这个值可以通过

glGetError();

获取到。

实现

通过一个宏可以封装出GL 指令错误 的调用检查接口。实现如下:

//.h
void CheckGLError(const char* file,int line);
#define  GL_CALL(x) do{x;CheckGLError(__FILE__,__LINE__);}while(0)

然后去实现这个检测函数:

//.cpp
void CheckGLError(const char* file,int line)
{
    GLenum error = glGetError();
    if(error != GL_NO_ERROR)
    {
        switch (error)
        {
        case GL_INVALID_ENUM:
            printf("GL Error: GL_INVALID_ENUM %s : %d /n",file,line);
            break;
        case GL_INVALID_VALUE:
            printf("GL Error: GL_INVALID_VALUE %s : %d /n",file,line);
            break;
        case GL_INVALID_OPERATION:
            printf("GL Error: GL_INVALID_OPERATION %s : %d /n",file,line);
            break;
        case GL_STACK_OVERFLOW:
            printf("GL Error: GL_STACK_OVERFLOW %s : %d /n",file,line);
            break;
        case GL_STACK_UNDERFLOW:
            printf("GL Error: GL_STACK_UNDERFLOW %s : %d /n",file,line);
            break;
        case GL_OUT_OF_MEMORY:
            printf("GL Error: GL_OUT_OF_MEMORY %s : %d /n",file,line);
            break;
        default:
            printf("GL Error: 0x%x %s : %d /n",error,file,line);
            break;
        }
    }
}

调用时可以这样写:

//call
GL_CALL(glEnable(GL_DEPTH_TEST));

如果GL指令出错,会在控制台中打印出行号。

总结

如果每次调用绘制指令都使用这个接口,就能在第一时间捕获 指令错误 。

另外可以设置项目属性来使win32程序运行时显示控制台状态。在项目->属性->配置属性->生成事件->后期生成事件中。在命令行上输入editbin /subsystem:console $(OutDir)$(ProjectName).exe。保存之后每次运行即可打开console

OpenGL(五) 指令错误 检测的封装方法

关注我的微信公众号,获取更多优质内容

分类: 未分类 标签:

Unity3d开发(二十) OnMouse_产生GC 的问题修复

2017年3月21日 没有评论

最近发现一个特别奇怪的现象,在游戏运行时,每帧 OnMove_产生GC ,大小是0.6KB的整数倍。具体Profiler效果如下图所示:

Unity3d开发(二十) OnMouse_产生GC 的问题修复

解决方法

经过比照发现这个问题与摄像机相关。由于是代码创建的摄像机组件,因此并未挂载GUILayer组件。解决这个问题的方法就是使用AddComponet<Camera>()的同时,调用AddComponent<GUILayout>()。在包含Camera的节点上添加这个组件即可解决 OnMove_产生GC 。

原理

由于没有Unity3d的源码只能通过经验推(xia)测(meng)。通过查询文档,发现这个组件主要是为了兼容之前版本的GUI。我觉得这里面的逻辑是,如果有这个组件,就会以引用的形式将这个组件储存起来,进而通过它找到GameObject以及Camera。否则,就需要使用GetComponent来查找。在每一帧调用GetComponent时就产生了GC。

总结

这…算个大坑吧。

Unity3d开发(二十) OnMouse_产生GC 的问题修复

关注我的微信公众号,获取更多优质内容

分类: 未分类 标签:

[置顶] 程序员三十岁之后要考虑什么?

2017年3月20日 没有评论

           程序员三十岁之后要考虑什么?

        周六受邀参加了触控科技CocoaChina开发者社区举办的"移动游戏人才培养和创业机会”的沙龙。其间被问到一个问题:“程序员三十岁之后该怎么办?”。


        “三十岁后该怎么办?”,好吧,虽然我一直都是二十八岁。。。,不过还是考虑一下吧。

              [置顶]        程序员三十岁之后要考虑什么?

        三十岁的程序员,其实正处于学习能力和项目经验的最佳平衡点。按理说是不存在什么问题的,不过人们常说:“三十而立”,进入三十岁,要面临买房讨老婆养家生娃的一系列压力山大阶段,的确是容易给当下高强度开发工作的程序员们一些困扰。我觉得我还是以个人经历给大家聊聊,如果能解解惑或做个参考也好。


       首先,年青时最好是有个中长期规划,不要过一天算一天,这个很重要。我当年上中学的时候,就因为爱上了编程,而立志要考大学计算机系,结果我如愿了。我来北京工作时,给自已订过一个目标,就是要在30岁前努力的成长,每年保持30%以上的薪水增长,30岁之后开始创业。虽然因为当时任引擎总监重任在肩创业晚了两年,但是大体也还是如愿了。还有就是2012年在CSDN开博客,我给自已订了目标,把Cocos2d-x的源码基本上给讲一遍。坚持了快一年,我的博客现在也百万访问量了。


        想想未来,给自已定个目标,容易让你走的更坚定,更有方向。之所以三十岁之后迷茫,是因为等你该进入到人生压力阶段时,你错过了最佳发展阶段时的努力,而导致这个的原因往往是漫无目的活。

        

        其次,要学会处理好事业和家庭的关系,游戏行业呆久了,见惯了大伙努力不要命的状态。有很多朋友为了工作,加班努力到半夜回家,甚至有的一周家人都见不到面,回到家里太晚,家人都睡了,第二天醒来时,老婆上班了,孩子上学了,自已又忙着往公司赶。这种状态我是觉得项目分阶段短期突击一下未尝不可,如果常态化,必然会影响家庭。这时候就需要好好想想,自已到底要什么?

                                 [置顶]        程序员三十岁之后要考虑什么?

         人,不是机器,钱是赚不完的,既然成家,就要学会平衡好对各方的责任和义务,才能走的更长远。


         最后,学会顺应天道。只要你没有停止学习和努力,那么经验和人脉的增长可以补足你在具体事情上精力不足的缺失。年龄本身并不是什么问题,主要还是心态和方法要变。人进入三十五岁关口后,身体会不如年青时那么能拼,要学会迎接长江后浪人材,年青人需要成长,需要拼的过程,而自已,也需要成长,但是化拙为巧,学会看清方向,学会省力,学会通过经验,人脉,市场分析等等引导干劲转化为成果,而不是和年青人拼体力。


        末了,祝各位过了三十的老将永远年青!

                           [置顶]        程序员三十岁之后要考虑什么?

分类: 未分类 标签:

[置顶] 房卡棋牌分析系列之"微信登录"

2017年3月20日 没有评论

”房卡“麻将研发技巧,尽在”红孩儿的游戏开发之路“,欢迎关注公众号!


                       房卡棋牌分析系列之"微信登录"

           房卡棋牌之所以火热,很大程度上取决于当下中国智能机的普及和微信作为基础通讯社交工具的广泛应用。在微信从青年人,到中年人,再到老年人的覆盖率不断扩张的现实中,基于微信登录的具有强大群众基础的棋牌游戏用户量大增。可以这么说,如果没有微信这样一款如此大影响力的通讯社交工具软件,就不会有今天房卡棋牌的火热。今天,我们就来分析一下如何在棋牌游工中加入“微信登录”


           要想应用“微信”的相关功能,首先要做的就是申请微信开发者资质。这需要到微信开放平台进行注册,成为一名微信开放平台的开发者。

[置顶]        房卡棋牌分析系列之&quot;微信登录&quot;

         目前要想成为开发者,除了按流程提交开发资质要求的证照之外,还需要交纳300元一年的费用。在这个过程中,客服会跟你电话沟通验证,并在通过后以挂号信形式将腾讯公司出具的发票寄给你。

[置顶]        房卡棋牌分析系列之&quot;微信登录&quot;


          在注册好账号后,可以在管理中心里创建一个新的移动应用(一个账号最多可创建不超过十个移动应用),填写好应用的基本信息,平台信息后就可以提交了。

[置顶]        房卡棋牌分析系列之&quot;微信登录&quot;


在这个过程中,要注意选择使用微信开放的功能。

[置顶]        房卡棋牌分析系列之&quot;微信登录&quot;

            应用创建成功之后,你将可以拿到AppIDAppSecret这两个重要信息。然后我们开始接入SDK的工作。


            首先,当然是下载微信SDK了,其实微信开发放平台提供了非常详细的下载说明和"接入指南",下载完之后,我们只需要仔细看一下,按着做就好了。

[置顶]        房卡棋牌分析系列之&quot;微信登录&quot;

               "移动应用"下的"微信登录功能"文档说明也非常详细,并有示例演示。"常见问题"中也提供一般性遇到的问题解答。相信对于大部分开发者来说并不是太大的问题。

[置顶]        房卡棋牌分析系列之&quot;微信登录&quot;

                  基本上来说,我们在游戏登录界面要做的就是通过点击"微信登录"按钮时调用如下代码:

[置顶]        房卡棋牌分析系列之&quot;微信登录&quot;

            在调用这个处理之后,手机会弹出微信要求验证的处理。

                             [置顶]        房卡棋牌分析系列之&quot;微信登录&quot;

           点击“确认登录”,会回调到WXApiDelegate 的onResp函数,得到所要的code并和appid,secret,grant_type组成一个HTTP RUL 字符串 send到https://api.weixin.qq.com/sns/oauth2/access_token? 之后程序就会收到微信账号的昵称,性别,头像图片地址,国家等等基本信息了。下一步,就是跳转到登录成功后的大厅界面,将将这些信息显示在玩家信息栏位了。

[置顶]        房卡棋牌分析系列之&quot;微信登录&quot;


           当然,如果登录过一次后,往往可以省去再次登录,这样就需要你在登录成功后将access_tokenopenid信息存储,并在下次启动游戏时加一个处理直接进行登录。

	std::string access_token = cocos2d::UserDefault::getInstance()->getStringForKey("access_token");
	std::string openid = cocos2d::UserDefault::getInstance()->getStringForKey("openid", "");
	if (access_token != "" && openid != "")
	{
		Req_UserInfo(access_token,openid);
	}
	else
	{
		JniFun::longinWX("","");
	}

       这样,房卡棋牌的"微信登录"基本就做好了。在"微信"已经成为手机最主要的社交软件的今天,基于"微信登录"的好友开房模式游戏必将进一步发展壮大,希望今天的分享对大家有帮助。


分类: 未分类 标签: