干货!教教大家如何自己制作作品!



  • 首先,我终于在忙完其它任务的情况下,抽出时间,写一篇帖子,教教大家如何制作自己的作品了。过去的一周里,我非常期待有一天自己能够写一篇这样的技术文,教教大家如何制作自己的作品。现在终于抽了个周六的晚上,把这个时间赶紧利用起来!

    首先,我要确定一下大家使用的操作系统是什么。这里推荐大家使用Windows XP或者以上的操作系统,Linux的话可能不行了,Mac电脑的话,可以安装Windows操作系统或者安装Windows虚拟机来解决。

    然后呢,需要安装的软件:
    1、MikuMikuDance,下载地址在这里 (提取码:0bc6)。我现在使用的是8.04。在安装这款软件的时候,需要检测一下是否安装了显卡驱动,因为这款软件依赖DirectX 9.0c以上。一般来说是没有问题的。

    这里呢,我们对作品的定义是集合模型、动作、语音以及人工智能的智能体。所以呢,大家可以将自己喜欢的模型、动作、语音以及人工智能按照自己的意愿结合起来,然后打包成作品,这样就能够在我这款软件中使用了!

    那么大家就有疑问了,我们上哪儿去得到模型呢?其实11区里有一个非常强大的资源集合网站,叫做bowlroll.net。大家进入这个网站,注册用户,然后就可以在资源上传者允许的情况下下载模型了!(看不懂日文?全程开谷歌翻译吧)。

    好了,当模型下载完毕后,大家会得到一个安装包,里面有很多很多纹理图片还有一个pmd文件(你的那个是pmx文件?抱歉,我们目前只做pmd,以后会逐渐扩展到pmx的),嗯,那你下载对了!

    有了模型,那么动作如何来呢?对了,我们使用刚刚下载的软件MikuMikuDance编辑动作了!MikuMikuDance上手还是很快的,不过要精通还是有点难的。下面是我使用MikuMikuDance来编辑角色“徵羽摩可”动作的截图:
    0_1448715908565_合适的机会.png
    按照网络上的一些知名的教程,就很方便地学会制作动作啦!

    接下来就是语音了。作品对语音部分没有太多的要求,只要支持wav或mp3格式的可以了。大家可以自己录音来为角色配音,也可以使用Vocaloid这样的声音合成软件来合成声音。

    最后介绍的就是人工智能了。人工智能这里使用的就是我们说的qml文件来实现。qml是一种脚本文件,主要用在Qt程序中。而我们这款应用呢,支持动态载入qml文件,也就是说,你的脚本挂在网上、Qt资源系统或者是本地,我们都可以顺利地将其载入。作品呢,将qml文件连同模型、动作、人工智能都一同打包到了zip文件中。也就是说,引擎呢,在读取的时候,对zip解压,并且读取其中的qml文件作为人工智能。目前呢,一个作品暂时只能支持一个qml格式的人工智能。

    人工智能的部分,包括了对场景的设置,以及角色的智能部分。智能的部分使用的算法是有限状态机,这种算法是目前AVG游戏常用的流程算法。其实呢,大家完全可以把这款软件当作一个开放平台的AVG软件,大家的作品就是角色,可以设置人工智能,将角色变得非常智能,TA可以在与玩家互动的同时,给玩家带来无限惊喜。

    那么,我们该如何编写人工智能文件呢?目前可能比较方便的编辑软件就是Qt Creator了。Qt Creator对QML的语法支持非常完善。然后大家就是分析文件的结构了。这里我拿一个初音未来-夕立的测试人工智能文件讲解:

    // DesignedConf.qml
    import QtQml 2.2
    import QtQml.StateMachine 1.0
    import QtQuick.Window 2.0
    import QtDream.AI.StateMachine 0.2
    import QtDream.Render 1.0
    import QtDream.Render 2.0
    import QtMultimedia 5.5
    
    QtObject
    {
        property MMDScene scene: MMDScene
        {
            color: "papayawhip"// 场景的背景颜色,可以为合法的WEB字符串或者是#开头的16进制数字
            light// MMDScene的只读成员(但是它的成员是可以写的),表示光照
            {
                enabled: true// 是否启用
                type: MMDLight.MMDStyleLight// 这里可选的有:NoLight(无光源)、SimpleLight(简单光源(聚光灯))、MMDStyleLight(MMD风格的光源(方向光源))
                lookAt: camera.lookAt// 看的位置点(建议不要修改)
                up: camera.up// 向上的向量(建议不要修改)
    
                direction: Qt.vector3d( -0.5, -1, -0.5 )// 光源方向
                ambient: Qt.rgba( 1, 1, 1 )// 环境光
                diffuse: Qt.rgba( 0.5, 0.5, 0.5 )// 漫反射
                specular: Qt.rgba( 0.6, 0.6, 0.6 )// 镜面反射
            }
    
            shadow// MMDScene的只读成员(但是它的成员是可以写的),表示阴影
            {
                enabled: false// 是否启用
                textureSize: Qt.size( 1024, 1024 )// 阴影映射(shadow map)的纹理大小(不要设置过大)
                type: MMDShadow.PCFShadow// 这里可选的有:NoShadow(无阴影)、SimpleShadow(简单阴影)、SimpleShadowDynamicBias(动态偏移的简单阴影)、PCFShadow(基于百分比平摊的阴影)
            }
    
            physics// MMDScene的只读成员(但是它的成员是可以写的),表示物理
            {
                enabled: false// 是否启用
                gravity: Qt.vector3d( 0, -9.81, 0 )// 重力向量
                drawType: MMDPhysics.DrawNone// 这里可选的有:DrawNone(不绘制)、DrawRigidBody(绘制刚体)、DrawJoint(绘制关节)、DrawAll(绘制全部)
            }
    
            option// MMDScene的只读成员(但是它的成员是可以写的),表示其它选项
            {
                boneVisible: false// 是否显示骨骼
            }
    
            Axis// 绘制坐标轴
            {
                length: 50// 长度
            }
    
            MMDModel// MMD的模型
            {
                id: hatsuneMiku
                objectName: "初音未来"// 这个会显示在面板上
                pmdSource: "../model/Miku_Hatsune_Ver2.pmd"// PMD格式的路径(写相对本文件的路径)
                edgeWidth: 0.03// 描边的宽度(默认0.03)
                property string word
    
                transforms: [ Translate { value: Qt.vector3d( -4, 0, 0 ) } ]// 位置的变换
            }
    
            MMDModel
            {
                id: yuuDachi
                objectName: "夕立"
                pmdSource: "../model/Yuu_ver1.10.pmd"
                edgeWidth: 0.03
                property string word
    
                transforms: [ Translate { value: Qt.vector3d( 4, 0, 0 ) } ]
            }
    
            camera: MouseCamera
            {
                objectName: "mouseCamera"
                position: Qt.vector3d( 0, 10, 35 )
                lookAt: Qt.vector3d( 0, 10, 0 )
                up: Qt.vector3d( 0, 1, 0 )
                fieldOfView: 45
                aspectRatio: 270 / 480
                nearPlane: 1
                farPlane: 1000
            }
        }
    
        property Audio audio: Audio { }
        property StateMachine machine: StateMachine
        {
            id: machine
            running: true
            initialState: s1
    
            StateSettings// 这里最好不要修改
            {
                id: settings
                outputWord: modelWord( hatsuneMiku ) + modelWord( yuuDachi )
            }
    
            Condition// 状态的转换
            {
                id: c2
                objectName: "c2"
                when: inputWord.indexOf( "我喜欢你" ) != -1// 触发条件
                targetState: s2// 转换对象
            }
    
            Condition
            {
                id: c3
                objectName: "c3"
                when: inputWord.indexOf( "我喜欢你" ) == -1 &&
                      inputWord.indexOf( "讨厌" ) == -1
                targetState: s3
            }
    
            Condition
            {
                id: c4
                objectName: "c4"
                when: inputWord.indexOf( "我喜欢你" ) == -1 &&
                      inputWord.indexOf( "讨厌" ) != -1
                targetState: s4
            }
    
            AIState// 状态
            {
                id: s1
                objectName: "AI:s1"
                conditions: [ c2, c3, c4 ]// 该状态可能拥有哪些转换
                onEntered:// 当进入该状态的时候作出哪些响应
                {
    
                    playAudio( audio, "../voice/succeed.mp3" );
                    playMotion( hatsuneMiku, "../motion/test_1.vmd" );
                    playMotion( yuuDachi, "../motion/test_1.vmd" );
                    hatsuneMiku.word = "你好,欢迎来到人工智能测试平台。";
                    yuuDachi.word = "你好,欢迎来到人工智能测试平台。";
                }
            }
    
            AIState
            {
                id: s2
                objectName: "AI:s2"
                conditions: [ c2, c3, c4 ]
                onEntered:
                {
                    playAudio( audio, "../voice/succeed.mp3" );
                    playMotion( hatsuneMiku, "../motion/test_1.vmd" );
                    playMotion( yuuDachi, "../motion/test_1.vmd" );
                    hatsuneMiku.word = "我也喜欢你。";
                    yuuDachi.word = "我也喜欢你。";
                }
            }
    
            AIState
            {
                id: s3
                objectName: "AI:s3"
                conditions: [ c2, c3, c4 ]
                onEntered:
                {
                    playAudio( audio, "../voice/failed.wav" );
                    playMotion( hatsuneMiku, "../motion/test_2.vmd" );
                    playMotion( yuuDachi, "../motion/test_2.vmd" );
                    hatsuneMiku.word = "我刚来到这个世界,还不太懂人类的语言,能够教教我吗?";
                    yuuDachi.word = "我刚来到这个世界,还不太懂人类的语言,能够教教我吗?";
                }
            }
    
            AIState
            {
                id: s4
                objectName: "AI:s4"
                conditions: [ c2, c3, c4 ]
                onEntered:
                {
                    playAudio( audio, "../voice/failed.wav" );
                    playMotion( hatsuneMiku, "../motion/test_2.vmd" );
                    playMotion( yuuDachi, "../motion/test_2.vmd" );
                    hatsuneMiku.word = "你好像很讨厌我。";
                    yuuDachi.word = "你好像很讨厌我。";
                }
            }
        }
        property alias inputWord: settings.inputWord
        property alias outputWord: settings.outputWord
    
        ///////////////////////////////////////////////////////////////////////////
        //
        //  一些辅助的函数
        function playAudio( audio, source, loopCount )
        {
            if ( !loopCount ) loopCount = 1;
    
            audio.stop( );
            audio.source = source;
            audio.loops = loopCount;
            audio.play( );
        }
    
        function playMotion( model, vmdSource, loopCount )
        {
            if ( !loopCount ) loopCount = 1;
    
            model.stopAnimation( );
            var modelStatusChanged = function ( )
            {
                if ( model.status === MMDModel.Ready )
                {
                    model.playAnimation( 30, loopCount );
                    model.statusChanged.disconnect( modelStatusChanged );
                }
            }
            model.statusChanged.connect( modelStatusChanged );
            model.forceLoadVMD( Qt.resolvedUrl( vmdSource ) );
        }
    
        function modelWord( model )
        {
            if ( model.word )
            {
                return "<h2>" + model.objectName + "</h2>" + model.word;
            }
            else return "";
        }
    }
    

    上面的例子我尽量将所有的都作了注释,而且,如果大家对于有限状态机的人工智能还是不太理解的话,我在CSDN上也写了一篇博客,大家可以参考一下:这里

    最后不要忘了!还有一个很重要的文件,它就是product.json。这个文件是json文件,它规定了引擎该如何读取这些文件,以及一些模型的信息。这会显示在模型的信息中。一个典型的json文件的内容如下:

    {
        "AI": [
            "AI/DesignedConf.qml"
        ],
        "comment": "这是一个最初的测试作品集合哦。",
        "model": [
            "model/Miku_Hatsune_Ver2.pmd",
            "model/Yuu_ver1.10.pmd"
        ],
        "motion": [
            "motion/test_1.vmd",
            "motion/test_2.vmd"
        ],
        "name": "最初的测试",
        "version": 100,
        "voice": [
            "voice/failed.wav",
            "voice/succeed.mp3"
        ]
    }
    

    好了,当这些大家都准备好了的话,就可以组装了!大家可以右键,然后选择“发送->压缩的文件夹”,这样稍微一压缩,一个作品就做好啦!
    0_1448718652519_打包.png

    有了作品又该如何使用呢?在我们软件的作品中心中,可以很方便地下载到需要的作品,如果大家先暂时在本地测试一下,那么右上角也有安装本地的作品这一功能。这样大家可以很方便地对自己的作品进行测试了。如果大家想要发布,那么可以在我们论坛的“作品中心”发表自己的作品,当然了,发表的作品和我们的应用共通,这样的话,可以很方便地在应用中下载到自己喜爱的作品。大家把自己的得意之作都分享一下吧!
    0_1448719046846_Screenshot_2015-11-28-21-55-53.png
    最后,我希望能够借这个教程,抛砖引玉,大家能够充分地调动创作的激情,制作出让玩家满意的作品!



  • @jiangcaiyang
    我什么时候丢了个作品了?应该是最后回复吧。。。



  • @qyvlik 这个在第三版中已经修正。



  • 小米二进不去啊,在登陆界面点击登陆,然后就闪退了



  • @药师 那可能是因为你的Android系统没有升级,我这个在Android 4.4.4 KitKat是没有问题的。支持的Android API是21。



  • @药师 我用Android API-19编译一个版本,然后单独发给你试试看。



  • @jiangcaiyang 收到,回家试试看


登录后回复
 

与 萌梦社区 的连接断开,我们正在尝试重连,请耐心等待