分类: 默认

  • 利用统一代理感知模型增强游戏体验

    利用统一代理感知模型增强游戏体验

    随着游戏设计开发技术的提升,加之游戏设备性能的上升,玩家越来越期待更逼真、更真实的感知。而代理感知模型就是一种简单的方法,具体而言包括了听觉模型和视觉模型。

    简单的感知模型容易让游戏本身无趣起来,最常见的例子就是利用离散距离来检查实现。这种情况下,玩家可以很容易的利用绝对盲区将AI逐一击破。

    这是一些简单的改进方法,基础但是可以提升AI的“聪明”程度。

    基本视觉模型

    先来看看基本的视觉模型。一般依次使用这三种计算,分别为视距、视锥和视线。

    视距是一个相当廉价的计算,一般情况计算平方从而避免平方根。

    举例而言,一个代理的视距最远为5个单位,而它的坐标为(0,0,0),当玩家位于(4,2,2)时计算结果为16+4+4=24<25,所以代理可以看到玩家。

    视锥测试是一个中度计算,因为需要一个向量归一化,然后进行点乘。如果结果大于0,那么玩家位于代理的180度视锥内;如果结果大于0.5,那么玩家位于代理的120度视锥内。

    最耗时的是视距计算,这需要从代理起点计算一个通往玩家的射线,这需要考虑到高度还有中间的任何障碍物。

    说明图样

    基本听觉模型

    听觉模型是一个很划算的东西,一点点简单的处理可以让游戏体验上升一个档次。

    最基本的听觉模型是基于位置的,随着代理和玩家距离的变化调整声音的音高和音量。

    其次就是基于障碍物的处理,比如代理位于封闭性障碍物附近,那么玩家听到的声音就会有回音和混响出现。这些的实现可能比较复杂,但是SoX项目可以简单做出很好的效果。

    椭圆视觉模型

    基本视觉模型简单,但是有些生硬和不合情理。比如代理不应该看到正右方的玩家,代理应该在近处有一个较大的视野,而远处应该不断的模糊。

    当然这个问题可以使用多个视锥来叠加,可以部分解决视野的问题。

    多个视锥

    一个小的大范围视锥,一个长度很长的小范围视锥。

    不过这样的计算会多一些,因为毕竟多了一个视锥,而且视锥与视锥之间有一个缺口。

    使用一个长型椭圆就可以解决问题了。

    长型椭圆

    椭圆不经可以增加真实度,还可以简化元素,因为判断一个我们只需要计算几个三角函数就可以解决绝大部分问题。计算焦点距离,和2a比较,不再需要平方。

    确定性的视觉增强

    真实的眼睛其实没有很明确的看得见与看不见,而是更多的出于周边视觉的情况下。

    人的眼睛有两只(废话…),大约有200度的视觉范围,其中110~120度是重叠区域。当查看身后的时候,杆体细胞和视锥细胞会产生颜色和运动的敏感问题,就是说你对眼色反应很少,但是移动的东西却很明显。

    简单的确定性模型

    这其中的数字代表百分之多少,当100%能够辨识时,代理应该采取完全行动,比如高精度射击;当70%能够辨识时,代理转向以获得更高的辨识或者采取低精度射击。

    当然这个区域划分很简陋,但是我一直使用这个。如果你愿意,代理的集中度也可以影响辨识度。

    确定性的听觉模型

    听觉的模拟可以引入举例,但是单纯的举例会让人困惑,特别是在室内环境中。

    听觉的确定性模型和视觉类似,当100%辨识度是玩家能够听到,当辨识度下降时音量等开始变化。

    辨识度本身可以和音源区域,障碍物质地大小等等相关。

    听觉模型的另外一个挑战是遮掩和淹没问题。

    当脚步声出现时它是否会被枪声所淹没,当枪声响起时它是否会被路过的汽车喇叭淹没。

    统一感知模型

    在我们拥有了两个确定性模型以后统一模型就简单了,辨识度可以取最大值,比如max(v,s)或者二者的和v+s

    如果听觉系统并不是游戏的重点,那么你可以考虑比例听觉模型,辨识度可以为…

  • 使用Robolectric模拟Http请求和响应

    习惯了用Gradle管理依赖以后突然回到Android开发,才发现Idea 12 对Android-Gradle项目支持很烂,只有手动下载依赖。

    Android App的开发比普通Java Web或者Java Application开发麻烦很多。主要是集成测试使用的Instrumented Test必须在模拟器或者真机上测试,挺耽误时间的。
    这样的测试通常粒度较大,测试的编写和维护较为困难,而最为重要的是,由于速度慢,如果使用TDD来进行开发,根本无法达到快速开发的要求。

    所以在Android开发中,尽可能多使用单元测试保证每个模块的正确性,来尽可能保证集成测试的成功率。

     

    基于Mockito的简单测试

    Android SDK也有测试框架,但是那个不是基于普通JVM的,运行起来还是需要模拟器或者真机。要基于普通JVM测试,就必须让模块尽可能的不依赖于Android的东西,需要依赖的也用Mockito等Mock掉。

    如果需要Mock的是自己的代码还好,如果是Android的东西,还需要处理各种状态和返回量。

    比如我们的APP需要调用WifiManager,如果只是检测Wifi是否开启还比较方便,只需要mock以后设定when就行了。

    WifiManager wifiManager = mock(WifiManager.class);
    when(wifiManager.isWifiEnabled()).thenReturn(true);

    如果我们需要扫描Wifi,然后检测Wifi状态等,工作就变动了。你需要仔细思考Android设备正常情况下的状态变动,还需要构造哦一个List<ScanResult>

    WifiManager wifiManager = mock(WifiManager.class);
    when(wifiManager.getScanResults()).thenReturn(Lists.<ScanResult>newArrayList());
    when(wifiManager.getWifiState()).thenReturn(WifiManager.WIFI_STATE_ENABLED).thenReturn(WifiManager.WIFI_STATE_DISABLED).thenReturn(WifiManager.WIFI_STATE_ENABLED);

    其实以上问题还好,因为我们mock出了WifiManager以后将它传入调用的类就行了。

    publicclass LoginService {
    private WifiManager wifimanager;
    publicLoginService(WifiManager 
  • 使用MiniCluster快速配置Hadoop开发环境

    使用MiniCluster快速配置Hadoop开发环境

    前年的时候用过Hadoop,那时候各种资料缺乏,各种摸索以后写出了能用的东西,然后打包仍服务器就再也没有管过。效率什么的谈不上,但是一直能用。

    当时花费了很多时间搭建一个环境,各种xml配置,然后引入依赖包,写好以后打包然后命令行测试一下,想想就耽误事儿。

    最近需要改动一下之前的程序,然后就纠结了。Hadoop已经2.3.0了,从MR换到了Yarn,虽然老版本兼容,不过还是随大流升级把。

    版本问题还好说,不过实在不想在本机装Hadoop了。

    在源码里面翻了翻,找到了MiniCluster,问题就解决了。

    Hadoop开发环境

    网上有很多安装Hadoop的文章,多是本地单机或者伪分布式模式。如果只是想学习一下,或者集群由专人维护,实在没有必要折腾。

    当然也可以下载做好的虚拟机文件,直接启动。

    对大部分人而言,只要有一个测试少量数据的方法就行了。

    而安装完成开发环境以后需要编写相关程序,然后打包运行。

    网络上也能找到不少测试专用的工具,不过始终没有真正的环境保险。

    从Hadoop的源码中能够找到一些测试,其中就有测试用的环境。然后在Jira找到了相关记录HADOOP-8009

    这个Improvement是将使用Hadoop Client和建立最小集群的包包括在一起,然后发布到maven库,分别命名为org.apache.hadoop:hadoop-clientorg.apache.hadoop:hadoop-minicluster

    主要的原因是因为Hadoop由几个块组成(common,hdfs,mapred),它们的版本可以有不同。其次是没有单独的客户端API。最主要的原因就是为了测试,特别是版本变化的时候。

    这意味着我们可以使用hadoop-minicluster去启动一个最小集群,并且它可以从maven仓库获取。

    从Gradle配置开始

    我使用Gradle管理项目,初始化模板如下:

    apply plugin:'java'
    apply plugin:'idea'
    
    repositories {
    mavenLocal()
    maven {
    url'http://maven.oschina.net/content/groups/public/'
    }
    }
    
    project.ext {
    junitVersion ='4.+'
    
  • 第三方库的更新和升级

    第三方库的更新和升级

    模块化、代码重用等思维的引领下,各种不同功能的库不断出现,基本涵盖了所有功能需求,极大加速了软件开发效率。

    另一方面,包管理工具的流行简化了引用过程。不再需要下载库文件,添加到项目目录。

    第三方库也是一种产品,一样存在生命周期,这样就会有升级的问题产生。

    为什么要升级

    第三方库也是人创造的,同样会有效率,代码质量等问题。伴随着升级会有各种修复、优化和新的特性。

    及时的升级可以排除潜在的bug,提升效率,如果有需要还可以根据API的升级简化代码并优化流程。

    对于一个成熟的、经过广泛验证的第三方库,比如Spring、Hibernate等等。它们的方方面面经过时间的考研,在一个稳定的版本范围内都是可信的。所谓的版本范围是指标准版本号

    如果第三方依赖库的管理是手动的,那么升级的步骤就很无趣了。你需要定期访问你所使用的库的主页,检查是否有升级,然后手动下载新版本,覆盖旧版本。

    如果使用包管理工具或者构建工具,如npm、gradle等,那么升级就比较简单了,直接修改配置本身然后执行更新命令就可以完成了。

    Gradle中的版本管理

    npm很方便,使用也简单,所以下面的例子都是以gradle为例的。

    在gradle中如果你需要引入一个第三方包,你只需要在配置中写道:

    apply plugin:'java'
    
    repositories {
    mavenCentral()
    }
    
    dependencies {
    compilegroup:'org.hibernate', name:'hibernate-core', version:'3.6.7.Final'
    testCompilegroup:'junit', name:'junit', version:'4.11'
    }

    这样gradle就可以自动下载相关的库,而当你需要升级的时候你只需要修改version配置即可。

    当然更多的人可能习惯偏好更紧凑的写法:

    dependencies {
    compile'org.hibernate:hibernate-core:3.6.7.Final'
    testCompile'junit:junit:4.11'
    }

    不过以上的修改都需要你在一大篇缩进不同的字符串中来回寻找,特别是以下情况的时候你可以就觉得有点不方便了:

    testCompile(
    "org.hamcrest:hamcrest-core:1.3",
    "org.hamcrest:hamcrest-library:1.3"
    )
  • 改善Hexo生成页面的描述信息

    改善Hexo生成页面的描述信息

    其实严格来讲这是针对具体的模板而言,不过其中涉及到Hexo的一些数据的调用,所以还是算作Hexo的一个优化吧。

    问题主要源于检查自己的博客被收录的情况,Google很给力,收录了200+,而百度就一页…

    如图所示,Google的很多搜索结果的描述部分是一样的,看着死活觉得不爽,所以来改进一下。

    原因分析

    首先看看这句话出自哪里,是在_config.yml的配置中出现的。根据这个配置这些文字又被写到html页面的head中。

    对于描述信息的具体细节可以参考Google站长帮助—标题和描述

    简单说我们要尽可能提供清晰的描述,这些描述要准确且不重复,可以手动也可以程序生成。当然在Hexo中我们肯定首推程序生成。

    修改

    不同的主题可能具体的文件不一样,不过原理是一致的。我以我使用的Pacman模板为例。

    需要修改的是head.ejs,先看看原始定义:

    <% } if (page.description){ %>
    <meta name="description" itemprop="description" content="<%= page.description %>">
    <% } else if (config.description&&(!is_post())){ %>
    <meta name="description" content="<%= config.description %>">
    <% } else if 
  • 使用Vagrant配置一个稳定的Hexo书写环境

    使用Vagrant配置一个稳定的Hexo书写环境

    Vagrant是一个工具,它可以让你轻松的配置虚拟机。利用它你可以快速的搭建环境,并共享环境。

    它本质上是使用了Virtual Box等虚拟机作为支撑,在此之上对配置分享等做简化。

    很早就知道了Vagrant的存在,但是一直不知道它的用处何在,直到我最近离开了我的笔记本一小段时间才真正享受了它的遍历。

    背景

    我的笔记本安装的是ubuntu,写博客用的hexo。在ubuntu下什么都很方便,特别是hexo的环境,ssh密钥等等都是配置好的。

    其实用ubuntu的原因真的很单纯,因为笔记本性能太差了。跑Window那简直是一卡一卡的,用ubuntu就可以有个很流畅的速度。

    但是最近我回家没有带笔记本,又想写写东西,就觉得各种不适应了。

    家里的系统是Windows的,各种纠结以后装上Virtual Box开始配置虚拟机,在下载镜像的时候放弃了,因为觉得麻烦。

    所以视线有回到Vagrant了。

    准备工作

    Vagrant有各个平台的安装包,Windows下100+M,加上Virtaul Box的100+M,总大小不大,我家小水管也可以接受。从Vagrant Cloud拖一个Ubuntu 12.04的Box下来,300M。

    然后就是安装并重启。

    不要忘记配置一下环境变量。

    创建并启动

    根据官方的说明,使用vagrant init hashicorp/precise32初始化一下,然后使用vagrant up启动。

    这里会自动下载对应的Box,但是速度很慢。

    拿出我们刚才下载好的Box,我用迅雷下的,速度完全不能比呀。

    随便放在哪里,然后执行命令

    vagrant box add precise32 file:///C:/Users/sdx/.vagrant.d/boxes/precise32.box

    再次声明,文件位置随意。我放在那个目录是因为我以为它可以自动识别。

    重新初始化

    vagrant init precise32
  • JDBC驱动加载和Class.forName

    我很少直接使用原生的JDBC,更多是使用mybatis或者hibernate。

    上周需要用一用,就到网上Copy了一段代码,一切运行正常。但是有一句话有点奇怪

    Class.forName("com.mysql.jdbc.Driver")

    这句话我原来也用过,一直是以为这是加载对应驱动用的,直到我删除了它,我才发现好像它不是必须的。那这东西是干嘛的?

    虽然网上的很多代码片段是抄来抄去的,但是我觉得这样写一定是有原因。

    Class.forName

    实现看看这句话本身的用处。

    在Java世界中,类只有在被使用的时候才本加载,更准确的是说在你寻求类的相关信息的时候才加载。

    比如有一个类名为SomeOne

    publicclass SomeOne {
    
    }

    然后在一个调用它的类名为SomeOneCaller

    publicclass SomeOneCaller {
    publicstatic SomeOne someOne =null;
    publicstatic Class<?> someOne = SomeOne.class;
    }

    第一句话并没有导致类的加载,而第二句话触发了加载。

    而Class.forName语句是加载对应名称的类,这里的加载是指读取了字节码,并执行static代码块。

    也就是在使用JDBC时调用这句话可以保证我们在执行DriverManager的getConnection
    方法时它已经被载入JVM中了。

    也就是说Class.forName本身和JDBC没有什么关系,它所做的只是加载对应名称的类。

    JDBC

    JDBC是一套标准API,用于数据库连接和SQL语句执行等等。
    既然是标准,那么一定是有相关规范的。

    要在getConnection时调用正确的类,一般的JDBC驱动都会调用registerDriver方法注册自己。

    比如在Mysql的驱动中:

    static 
  • 模块化的Grunt配置

    Grunt是Javascript任务运行工具,它可以让一切自动化。

    简而言之,它可以帮你完成代码压缩,代码编译,单元测试,代码规范验证等必须但高度重复的工作。

    极大程度简化你的工作,同时又保证你的代码质量。

    Grunt的同类工具很多,但是Grunt的一大特点就是生态庞大,有各种插件可以选择,而且都是可以通过npm安装的。

    GruntFile

    Gruntfile.js文件都是位于项目根目录中的一个JavaScript文件,并且它也应该与你的项目源文件一起提交。

    一个Gruntfile由下面几部分组成:

    • wrapper函数
    • 项目和任务配置
    • 加载的Grunt插件和任务
    • 自定义任务
    • 一个Gruntfile示例

    比如下面的例子,它从package.json中读取配置,再调用grunt-contrib-uglify插件来压缩源文件,同时根据读取的配置生成一个标语注释。同时注册默认任务为uglify任务。

    module.exports =function(grunt){
    
    grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    uglify: {
    options: {
    banner:'/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */n'
    },
    build: {
    src:'src/<%=pkg.name %>.js',
    dest:'build/<%= pkg.name %>.min.js'
    }
    }
    
  • 通过ssh使用git更新hexo内容

    Hexo每次新增一篇文章都会引起很多页面的变动。

    比如page页面,因为增加一篇文章,所以分页情况都改变了,这里会有将近10个页面的变化。

    其次是tag和categories目录,因为文章一般都会指定tag和categories,所以这里也会有几个页面的变动。

    最后就是文章页面本身了。

    这么算下来新增一篇文章会更新十多个页面,还有若干图片。

    每次使用ftp更新都很慢,虽然可以设置各种跳过规则,但是对比文件列表也是会花费相当的时间。加之hexo提供的ftp发布比一般的ftp客户端还慢(ftp发布工具是给予lftp的,只适用于linux)。

    我本来准备做一个版本管理工具,或者让hexo只生成最近几天的文章(issue链接)已减少更新量。

    不过折腾起来挺费劲的,最后作罢了。

    刚才突然想到我需要的其实就是一个git而已,加上主机本身支持ssh,所以决定使用在服务器搭建一个git服务器来更新网站。

    创建Git仓库

    其实创建Git仓库挺简单的,因为git自身就支持ssh方式的连接,我们只需要在服务端建立一个Git仓库,然后将页面生成到Git仓库中就行了。

    在目标目录执行命令:

    git init
    git add .
    git commit -m'init git repo with remote files'

    初始化仓库并将现有文件全部添加到其中。

    然后在本地执行命令将库拉去回来

    git clone username@host:~/path/to/www

    也可以直接在本地执行初始化一个Git仓库,然后添加一个remote

    git init
    git remote add web username@host:~/path/to/www
  • Hexo模板系统和pacman的修改

    Hexo模板系统和pacman的修改

    Hexo的原理是解析_posts中的md格式文件,然后根据模板的解析规则进行解析。

    我使用的风格修改自pacman,主要它看着比官方默认的主题要充实很多。

    但是毕竟是别人做好的,还是有很多不合自己意的,所以还是需要稍微修改一下。

    Hexo模板系统

    要做出修改,首先要看看Hexo的模板系统是怎么实现的。

    Hexo使用的是ejs,类似的东西就多了,比如Jade,swig,doT等等。

    ejs效率比较慢(相比其他的),这里有一个测评,也没有Jade有那么多特性。不过好处也是明显的,不需要太多的时间和精力就可以掌握。

    在Hexo中有post、page等不同的布局,而选用哪种布局是在md文件中声明的。

    Hexo首先解析md文件,然后根据layout.ejs判断布局类型,再转发给其他布局文件。在布局中可以引入其他文件,比如

    <%- partial('_partial/header')%>

    这样每一块内容都是单独的,方便二次使用,也可以几个不同布局引用一个代码片段。

    简要流程

    修改几处英文

    我没有从官方风格慢慢改,而是选用了pacman,修改了主题色以后感觉就不错了,不过有几处英文显示感觉不舒服。

    Hexo是支持多语言的,在_config.xml中配置就可以了,一般使用zh-CN。

    首先看看文章末尾的上一篇和下一篇

    language中没有对于这个的定义,首先我们补充nextpostprevpostzh-CN.yml

    nextpost:下一篇
    prevpost:上一篇

    然后修改post文件夹中的pagination.ejs,将PREVIOUS和NEXT替换掉

    <nav class="article-nav