精华内容
下载资源
问答
  • 麒麟子在开发中搞出来的框架,都是遵守“大道至简,实用至上”这两个基本原则。 接触一个引擎的第一件事,就是搞出一个实用的框架,方便在此基础上做开发。 由于目前的引擎已经是对象+组件模式,所以在场景对象...

    前言

    麒麟子在开发中搞出来的框架,都是遵守“大道至简,实用至上”这两个基本原则。

    接触一个引擎的第一件事,就是搞出一个实用的框架,方便在此基础上做开发。

    由于目前的引擎已经是对象+组件模式,所以在场景对象管理上,不需要花太多功夫了。我们主要集中在界面管理这块。

    一、常见的几种游戏类型。

    既然我们的框架想要满足日常开发,就不得不满足星辰大海般的需求。从客户端的角度,我们可以把游戏分为三类。

    1.1、纯界面玩法

    像一些SLG、卡牌、象棋等可以视作纯界面玩法。(也有某些大作要求3D表现效果的,我们不作讨论),比如下面的这类游戏。

    动物餐厅

     

    1.2、某些纯界面+战斗场景

    王者荣耀

     

    1.3、从头到尾都是3D场景

    魔兽世界

     

    1.4、小结

    仔细分析以后我们可以发现,假如我们的框架始终支持 2D/3D场景 + 界面 这样的能力。就可以了。 

     

    二、单场景 vs. 多场景

    单场景+Prefab 和 多场景 从Cocos 2d-x提供了replaceScene函数开始,就一直有人在争论这个话题。只要引擎提供了“场景”这个定义的,都会遇上决策问题。

    麒麟子看得很明白,所谓的引擎场景管理,就是给了你一次无脑销毁所有结点的机会。

    如果你是在两个截然不同的逻辑之间切换,那其实是可以使用引擎的场景切换功能的。 如果逻辑相差不大,我建议依然采用单场景方式。

    而麒麟子更推荐的是单场景+Prefab方式,这样会迫使你自己更注重场景节点和资源的管理。

     

    三、框架内容

    3.1、程序启动入口

    程序启动入口包含两个部分。

    3.1.1、最小场景

    最小场景如果不出意外,我们只需要一个Canvas节点,一个MainCamera节点,一个MainLight节点就好了。

    3.1.2、App.ts

    麒麟子喜欢以App.ts作为程序入口,将这个App.ts挂在Canvas节点上即可。App.ts的内容并不多,如下所示

    import { _decorator, Component, Node } from 'cc';
    import { UIMgr, UILayer } from './UIMgr';
    import { HUD } from './HUD';
    const { ccclass, property } = _decorator;
    
    @ccclass('AppTs')
    export class AppTs extends Component {
        start () {
            UIMgr.inst.setup(UILayer.NUM);
            UIMgr.inst.showUI(HUD);
        }
    }

    我们可以清晰地看到,在这里,麒麟子只启动了UIMgr。如果还有其他游戏管理器需要一开始就初始化,那么把它们放在这里就好了。 

     

    3.2、界面管理器

    • 界面管理器至少要包含几个功能
    • 动态加载和销毁界面
    • 界面层级管理
    • 界面事件自动管理机制
    • 界面与游戏业务逻辑通信机制

    上面说的这些,框架里都自带了。

    四、分辨率自适应

    很多小伙伴一定还在纠结是用fitWidth还是fitHeight吧。

    如果使用fitWidth,遇上更细长的设备,界面上面部分可能被裁剪。

    如果使用fitHeight,遇上更短的设备,界面左右部分可能被裁剪。

    其实我们想要的只有一句话:任何时候,都不要裁剪

    基于这个目标,我们制定出的策略就是。

    a、在比设计分辨率更细长的设备上,我们使用fitHeight,这样一来,他长由他长,我们只需要保证背景左右能够填充就行。

    b、在比设计分辨率更短的设备上,我们使用fitWidth,这样一来,他高由他高,我们只需要保证背景上下能够填充就行。上面的适配机制,已经被麒麟子写成了一个函数。

    public resize() {
                    //根据屏幕大小决定适配策略
            //想明白原理,请阅读本文 https://blog.csdn.net/qq_36720848/article/details/89742451
    
            let dr = view.getDesignResolutionSize();
            var s = cc.view.getFrameSize();
            var rw = s.width;
            var rh = s.height;
            var finalW = rw;
            var finalH = rh;
     
            if((rw/rh) > (dr.width / dr.height)){
                //!#zh: 是否优先将设计分辨率高度撑满视图高度。 */
                //cvs.fitHeight = true;
                
                //如果更长,则用定高
                finalH = dr.height;
                finalW = finalH * rw/rh;
            }
            else{
                /*!#zh: 是否优先将设计分辨率宽度撑满视图宽度。 */
                //cvs.fitWidth = true;
                //如果更短,则用定宽
                finalW = dr.width;
                finalH = rh/rw * finalW;
            }
    
            view.setDesignResolutionSize(finalW,finalH,ResolutionPolicy.UNKNOWN);
            let cvs = find('Canvas').getComponent(UITransformComponent);
            cvs.node.width = finalW;
            cvs.node.height = finalH;
        }

    关于这个函数,有两个地方要注意

    注意1:在设置面板里面,fitWidthfitHeight都得去掉,一个都不要勾,否则可能出现设置失效。

    注意2:麒麟子在调用view.setDesignResolutionSize函数的时候,最后一个参数传的是ResolutionPolicy.UNKNOWN。这样一来,这个函数是可以重复调用且生效的。当我们处于微信浏览器的时候,横竖屏旋转会导致宽高比不一致,这就需要再次调用这个函数来重新调整布局。

     

    五、游戏逻辑与界面通信机制

    麒麟子这里没有MVC,也没有MVVM,只有以下几个套路

    1、数据、数据更新方法由逻辑管理器提供

    2、数据变更,想让界面产生改变,则通过事件传递

    3、界面可以直接调用逻辑管理器

    4、两个界面之间,只能通过事件传递(需要两个界面联动的情况非常少,一般情况下界面之间的逻辑都是互不干扰的)

    也就是说,逻辑管理器不知道界面的存在,但界面是知道逻辑管理器的存在的。

    我举一个关于个人信息的例子。在这个例子中,我们会涉及到三个文件 MyInfoMgr.ts(用户信息逻辑管理器 M) 、UIMyInfoController.ts(用户信息界面控制器 C) 、MyInfo.prefab(用户信息界面布局 V)。

    如果非要用MVC来对应的话,就按我后面标记的字母来对应吧。

    当用户点击个人按钮时,UIMgr会实例化UIMyInfoController,UIMyInfoController类会加载MyInfo.prefab。

    当加载成功后,UIMyInfoController会有一个onCreated回调,我们可以在回调里初始化我们的显示信息。

    UIMyInfoController需要监听用户信息改变相关的事件,当收到事件的时候,需要对应地做显示更新

    MyInfoMgr持有用户数据并提供访问接口以及数据修改接口,当数据产生修改时,会抛出修改事件。它不关心这个事件是否有人要用。无脑抛出就行。


    六、总结

    与其说是框架,不如说是麒麟子的惯用套路。这个套路没有出色的地方,也不满足很多学术性的依赖解耦标准。但这个套路陪着我走过了大大小小很多项目。

    如果要给它下一个定义的话,我觉得就是两个词:“简单、实用”。

     

    哦对了,只能用Cocos Creator 3D 1.1.1打开。

    源码地址:https://gitee.com/qilinzi/creator3ddemos/GFW/

     

    麒麟子会持续更新这个系列,想及时收到推送的朋友,可以关注我的公众号。

     

    展开全文
  • 麒麟子在五一劳动节这一天,为大家准备了一个DEMO。 DEMO演示了,大厅,背景,以及像 3D麻将,斗地主等桌面的适配方案。使大家可以用一套资源,适配从iPhoneX(目前最长的移动设备) 到 iPad(目前最短的移动设备 )的...


    麒麟子在五一劳动节这一天,为大家准备了一个DEMO。 DEMO演示了,大厅,背景,以及像 3D麻将,斗地主等桌面的适配方案。使大家可以用一套资源,适配从iPhoneX(目前最长的移动设备) 到 iPad(目前最短的移动设备 )的分辨率。

    PS:黑莓那种1:1也可以的,只是我人为的把它忽略了。

    本DEMO在线演示: https://qilinzi.ukylin.net/?lesson=09

    本DEMO源代码:https://gitee.com/qilinzi/qlz_ccc_tips


    不谋万世者,不足谋一时。不谋全局者,不足谋一域。

    麒麟子对这句话的理解就是,早睡早起,好好学习,天天向上。哈哈哈哈。正文开始之前,先瞎**几句,是麒麟子的风格,大家久了就习惯了。如果不想看的,直接到正文即可。 这段的主要目的,是想缓解一下大家的压力。毕竟大部分看这个文章的朋友,都在996,或者在去996的路上。

    麒麟子不只一次说过,不要总是学人有跟热点,不要总幻想自己是站在风口的猪。即便你飞起来了,那万一风突然停了呢。选择一个自己看好的领域进行深耕,不断积累。

    而选择什么领域,技术只是基础。用户需求是否可持续,才是你能否持续深耕的关键。

    麒麟子有云:棋牌之路漫漫其修远兮,吾将上下而求索(翻译:棋牌领域是棵常青树,是一个可以深耕的领域)

    后面的文章,大部分是基于棋牌来讲解知识点,但对于其他游戏也同样适用


    一、不同适配策略的表现效果

    Cocos Creator的Canvas提供了fitWidth和fitHeight两个开关,可以实现4种效果

    1、fitWidth = false, fitHeight = false

        对应的是自动选择定宽还是定高,当用户手机比设计分辨率更宽时,使用定宽。 当用户手机比设计分辨率更短时,使用定高。 带来的就是裁剪效果

    2、fitWidth = true, fitHeight = false

        对应的是定宽效果

    3、fitHeight = false,fitHeight = true

    对应的是定高效果

    4、fitWidth = true,fitHeight = true

    对应的是show_all效果

    在设计分辨率不变的情况下,不同的适配策略,在不同的设备上的表现效果,如下图

    二、幼麟棋牌的适配模式

    1、适配效果介绍

    棋牌游戏,卡牌游戏等项目的适配,比RPG要难一点。特别是牌桌和全屏界面。对于RPG来说,只需要做靠主界面的停靠即可。而棋牌游戏,桌子本来空间就不足,简单的停靠是满足不了需求的。幼麟棋牌团队在经历了十几个项目之后总结出一条铁律:不允许任意分辨率对内容裁剪。这条铁律沿用至今,暂时没有发现满足不了的情况。

    看到上面黑字的时候,我们肯定第一时间想到全显的适配策略。 

    心细的朋友应该能够发现,全显和幼麟棋牌,都是保证内容能够全部显示。但为什么麒麟子在画图的时候,要把全显的背景弄成黑色呢。 你没看错,这不是麒麟子脑袋抽风,这是因为,二者有区别。

    有兴趣的朋友可以拿ShowAll和幼麟棋牌的适配方案做一个测试,你会发现,核心差异就在Widget是否能够正确停靠的问题。 使用ShowAll方案,Widget会按绿色边缘作为停靠。 而使用幼麟棋牌方案,Widget会使用屏幕宽高作为停靠。

    2、全分辨率适配代码实现

    这里要注意,CocosCreator场景中,一定要去掉 fitWidth和fitHeight。 一个都不能留,否则是有问题的。下面的代码是从Demo代码中的Utils.ts文件中截取的。

        public static resize() {
            
            var cvs = cc.find('Canvas').getComponent(cc.Canvas);
            //保存原始设计分辨率,供屏幕大小变化时使用
            if(!this.curDR){
                this.curDR = cvs.designResolution;
            }
            var dr = this.curDR;
            var s = cc.view.getFrameSize();
            var rw = s.width;
            var rh = s.height;
            var finalW = rw;
            var finalH = rh;
    
            if((rw/rh) > (dr.width / dr.height)){
                //!#zh: 是否优先将设计分辨率高度撑满视图高度。 */
                //cvs.fitHeight = true;
                
                //如果更长,则用定高
                finalH = dr.height;
                finalW = finalH * rw/rh;
            }
            else{
                /*!#zh: 是否优先将设计分辨率宽度撑满视图宽度。 */
                //cvs.fitWidth = true;
                //如果更短,则用定宽
                finalW = dr.width;
                finalH = rh/rw * finalW;
            }
            cvs.designResolution = cc.size(finalW, finalH);
            cvs.node.width = finalW;
            cvs.node.height = finalH;
            cvs.node.emit('resize');
        }

    麒麟子保证,用上面的代码,配上fitWidth = false, fitHeight = false. 你将再也不用因为分辨率适配而掉头发 (因为刘海屏适配掉的头发不算在这个里面)

    不要太浪哦!!!!

    三、背景适配

    按理说,我们弄一张足够大的背景,总是能撑满所有分辨率的。但这样一来,美术的设计成本就高了。 特别是不能复用一些现成(别人)的资源。

    麒麟子弄了一个bg_scaler.ts来满足大家,大家可以通过点击大厅的 居中,自由缩放,裁剪 来观察不同的效果。

    关键代码如下:

    resize () {
    
            this.node.width = this.originWidth;
            this.node.height = this.originHeight;
    
            //0、居中(居中其实不需要挂这个脚本,浪费效率)
            //1、宽高都根据高度拉伸
            //2、长边充满
            var cvs = cc.find('Canvas').getComponent(cc.Canvas);
            var size = cc.view.getFrameSize();
            
            var dr = Utils.curDR;
            var scaleMethod = this.mode;
    
            //
            var fitWidth = true;
            //如果更宽,则使用定高
            if((size.width/size.height) > (dr.width / dr.height)){
                fitWidth = false;
            }
    
            //自由缩放撑满
            if(scaleMethod == 1){
                if(fitWidth){
                    this.node.height = this.node.width/size.width * size.height;
                }
                else{
                    this.node.width = this.node.height/size.height * size.width;
                }
            }
            //保持等比缩放撑满
            else if(scaleMethod == 2){
                if(fitWidth){
                    //定宽表示设备更高了,则以高的缩放为准
                    var oldHeight = this.node.height;
                    this.node.height = this.node.width/size.width * size.height;
                    var scale = this.node.height/oldHeight;
                    this.node.width = scale * this.node.width;
                }
                else{
                    //定高表示设备更宽的,以宽的缩放为准
                    var oldWidth = this.node.width;
                    this.node.width = this.node.height/size.height * size.width;
                    var scale = this.node.width / oldWidth;
                    this.node.height = scale * this.node.height;
                }
            }
            else{
                //默认处理,有黑边
            }
        }

    四、麻将桌子适配

    麒麟子的DEMO里面,处理了一个假3D的麻将桌子。因为这个比较特殊,所以放在这里作为了案例。此代码在DEMO代码的 09_3dmjtable.ts文件中。它实现的效果是,如果屏幕比设计分辨率高,比如 iPad这样的设备,那么将会多显示出一些墙壁上的内容。如果比设计分辨率更长,比如iPhoneX,那么将会做一定的拉伸,确保背景充满。

        start () {
            cc.find('Canvas').on('resize',this.resize.bind(this));
            this.resize();
        }
    
        resize(){
            var canvas = cc.find('Canvas');
            if(canvas.width > Utils.curDR.width){
                this.node.width = canvas.width;
            }
        }

    五、斗地主桌子适配

    半弧的桌子适配是最容易的,只需要一个Widget无脑充满即可

    六、总结

    本文章和DEMO只是演示了一些基本的适配技巧。 每一个项目,面对的需求都大相径庭,没有哪一个方案是可以一劳永逸的,需要大家根据自身项目的情况,酌情调整。

    本DEMO在线演示: https://qilinzi.ukylin.net/?lesson=09

    本DEMO源代码:https://gitee.com/qilinzi/qlz_ccc_tips

    展开全文
  • HELLO,大家好,我是麒麟子。作为Cocos社区高产用户,今天又给大家带来了一个看起来很酷,但实际上大多数人用不到的DEMO。 不知道大家是否记得梦幻西游、问道、英雄无敌、仙剑奇侠传、神仙道、神曲OL。 不知道大家...

    HELLO,大家好,我是麒麟子。作为Cocos社区高产用户,今天又给大家带来了一个看起来很酷,但实际上大多数人用不到的DEMO。

    不知道大家是否记得梦幻西游、问道、英雄无敌、仙剑奇侠传、神仙道、神曲OL。

    不知道大家是否最近在玩自走棋(哎哟,不错哟,最近特别火)

    然而,他们从技术上讲,没有本质的区别,他们的战斗都是回合制。 

    回合制的游戏就像棋牌一样,每一个回合,同一时间,只有一个人能操作。 等这个人表演完。再交给下一个。


    DEMO内容:

    这个DEMO展示了一个战斗场景,玩家可以控制一个英雄去攻击目标。 英雄有6个技能,当玩家点击某个技能的时候,英雄会冲到目标跟前,翻云覆云一番,再退回自己的位置。

    有在线演示可以看哦:https://qilinzi.ukylin.net/?lesson=08

    当然,还有,源码:https://gitee.com/qilinzi/qlz_ccc_tips


    一、动作与特效

    在这个例子中,英雄的动作和特效是在同一张图上的。显然,这样的方式不适合像传奇这样的MMORPG。 但是对于不换装的游戏,是完全没有问题的。还能省下不少DrawCall。

    值得注意的是,我们在这个例子中,并没有使用Cocos Creator中的Animation来编辑英雄动画。 因为一个游戏有上百上千种动画,如果一个个手工编辑的话,是要死人的。 SO。。。 我们自己手写了一个。 请看大屏幕。

    //动画信息配置
    var AnimConfig = { }
    AnimConfig['0001'] = {
        'attack':{frames:8,fps:8},
        'attacked':{frames:1,fps:8},
        'combat_idle':{frames:4,fps:8},
        'idle':{frames:4,fps:8},
        'ride_idle':{frames:4,fps:8},
        'ride_run':{frames:8,fps:8},
        'run':{frames:8,fps:8},
        'rush':{frames:1,fps:8},
        'spell1':{frames:8,fps:8},
        'spell2':{frames:8,fps:8},
        'spell3':{frames:14,fps:8},
        'spell4':{frames:8,fps:8},
        'spell5':{frames:4,fps:8},
        'spell6':{frames:10,fps:8},
    }
    
    AnimConfig['0003'] = {
        'attack':{frames:8,fps:8},
        'attacked':{frames:1,fps:8},
        'combat_idle':{frames:4,fps:8},
        'idle':{frames:4,fps:8},
        'ride_idle':{frames:4,fps:8},
        'ride_run':{frames:8,fps:8},
        'run':{frames:8,fps:8},
        'rush':{frames:1,fps:8},
        'spell1':{frames:8,fps:8},
        'spell2':{frames:8,fps:8},
        'spell3':{frames:14,fps:8},
        'spell4':{frames:8,fps:8},
        'spell5':{frames:4,fps:8},
        'spell6':{frames:10,fps:8},
    }
    
    export default class NewClass {
        public static getRoleInfo(roleId){
            return AnimConfig[roleId];
        }
    
    }

    上面的类用于配置我们对应角色的动画,每一个动画,有动画名,帧数,帧率 三个属性。 每一个角色都有一个编码,如0001,0003。

    import AnimConfig from './08_anim_config';
    
    const {ccclass, property} = cc._decorator;
    
    @ccclass
    export default class NewClass extends cc.Component {
    
        @property
        roleId:string = '0001';
    
        @property
        defaultAnim:string = 'idle';
        // LIFE-CYCLE CALLBACKS:
    
        private _spriteFrames:Array<cc.SpriteFrame> = [];
        private _lastStartTime = 0;
    
        private getAnimInfo(animName:string):any{
            var info = AnimConfig.getRoleInfo(this.roleId);
            return info[animName];
        }
    
        onLoad () {
        }
    
        playAnim(animName:string){
            var aniInfo = this.getAnimInfo(animName);
            if(!aniInfo){
                return null;
            }
            this.defaultAnim = animName;
            this._lastStartTime = Date.now();
    
            var folder =  'roles/' + this.roleId + '/';
            var arr = [];
            for(var i = 0; i < aniInfo.frames; ++i){
                var url = folder + animName + '/frame' + i;
                arr.push(url);
            }
            cc.loader.loadResArray(arr,cc.SpriteFrame,function(err,arr){
                this._spriteFrames = arr;
            }.bind(this));
        }
    
        start () {
            this.playAnim(this.defaultAnim);
        }
    
    
    
        update (dt) {
            if(!this._lastStartTime || !this._spriteFrames.length){
                return;
            }
    
            var fps = this.getAnimInfo(this.defaultAnim).fps;
    
            var index = Math.floor((Date.now() - this._lastStartTime) / 1000 * fps);
            if(index > this._spriteFrames.length){
                this.playAnim('combat_idle');
                return;
            }
            index %= this._spriteFrames.length;
            this.node.getComponent(cc.Sprite).spriteFrame = this._spriteFrames[index];
        }
    }
    

    在上面的代码中,playAnim被调用的时候,我们首先获取到动画的信息,然后使用cc.loader.loadResArray来加载动画所需要使用到的图片。待加载完毕后,放入对象变量中缓存。

    update里,我们根据时间计算出当前帧。 

    通过这样的方式,我们就可以基于配置和文件命名规则来实现大量的角色动画和NPC动画。 减少动画编辑的工作量。

    二、攻击过程

    回合制游戏,最大的特点就是玩家不需要控制角色位置。 所以,近身攻击是需要系统主动移动位置到目标跟前的。 攻击完毕后,又要移回来。 我们在这里,使用了cc.Action组合来做。先欣赏一下代码。

        onCastSpell(event){
            var animName = event.target.__meta;
            if(this._isSpelling){
                return;
            }
            this._isSpelling = true;
            var arr = [];
            //切换成冲刺动画,并移动到目标跟前  
     arr.push(cc.spawn(cc.moveTo(0.3,this.target.node.position.sub(cc.v2(120,0))),cc.callFunc(function(){
                this.hero.playAnim('rush');
            },this)) );
            //播放攻击动画
            arr.push(cc.callFunc(function(){
                this.hero.playAnim(animName);
            },this));
            var animInfo = AnimConfig.getRoleInfo(this.hero.roleId)[animName];
            var playTime = animInfo.frames / animInfo.fps;
            //等待攻击完成
            arr.push(cc.delayTime(0.5 + playTime));
            //移回原来位置
            arr.push(cc.moveTo(0.1,this.hero.node.position));
            arr.push(cc.callFunc(function(){
                this._isSpelling = false;
            },this));
    
            var act = cc.sequence(arr);
            this.hero.node.runAction(act);
        }

    1、冲刺到目标面前

    为了实现冲刺到目标面前,我们需要在切换动画的同时,移动角色位置。 Cocos Creator提供了cc.spawn,构建出能够同时执行多个Action的组合Action。cc.callFunc我们理解为自定义Action,你可以在回调函数里编写你期望的逻辑。

    2、攻击

    我们只需要简单的使用cc.callFunc写一个自定义Action,进行攻击动作的播放即可。

    3、等待播放完成

    由于我们自己写的动画播放类,还未处理播放完成事件,所以在这里,我们使用了一个cc.delayTime来做等待,等待的时间,是根据动画帧率*帧数 + 一个固定值

    4、回到原来位置

    这个用cc.moveTo即可实现

    5、把它们串起来

    我们把所有的Action放入一个数组中,再使用cc.sequence,即可以构建出一个按顺序执行的cc.Action。 并把这个Action交给目标节点执行即可

    6、其它

    大家会看到一个this._isSpelling的变量,和这个相关的代码,均是为了防止用户在攻击过程中,多次点击出现BUG。

    三、技能图标与池

    技能图标的实现相当简单,就是把他们放到了一个Layout里面,只不过,我们是动态添加的节点。 在这里,麒麟子使用了一套常用的辅助函数

        public static removeItemToPool(listRoot){
            for(var i = 0; i < listRoot.childrenCount; ++i){
                listRoot.children[i].active = false;
            }
        };
        
        public static addItemFromPool(listRoot){
            for(var i = 0; i < listRoot.childrenCount; ++i){
                var child = listRoot.children[i];
                if(child.active == false){
                    child.active = true;
                    return child;
                }
            }
        
            var newChild = cc.instantiate(listRoot.children[0]);
            listRoot.addChild(newChild);
            return newChild;
        };

    removeItemToPool 会把所有的layout子节点标记为active = false; 

    addItemFromPool 会从layout子节点中选择一个active为false的节点,若没有可用的,则复制0号节点来用。 

    上面这个套路,麒麟子用了很久了,既简单明了,又能够控制对象池。 大家在做背包,或者其它大量子元素界面的时候可以试试。可以很任性的随意刷新,完全不用担心效率和内存问题。


    四、结束语

    有在线演示:https://qilinzi.ukylin.net/?lesson=08

    源码:https://gitee.com/qilinzi/qlz_ccc_tips

    大家朋什么不明白的,或者想了解但麒麟子没有写的。在本博客中留言即可,也可以直接在交流群里@麒麟子,或者私聊麒麟子。 谢谢大家的支持!

    展开全文
  • 关注公众号:麒麟子随笔,走着瞧) 这篇文章其实是我即将发布的文章《Cocos Creator 3.x后期效果框架源码剖析》的序。 但这个序太长了,甚至写着写着就跑题了,面对这思如泉涌的结晶又舍不得删。 怎么办呢?于是开了...

    一、啊,这夜

    又是一个静谧的夜,用来写作是再好不过的了。

    说来也奇怪,C姐催我稿子的时候,我绞尽脑汁,也想不出来写什么好。现在轮到自己自由发挥了,却发现保底可以写上十多篇。(不信?关注公众号:麒麟子随笔,走着瞧)

    这篇文章其实是我即将发布的文章《Cocos Creator 3.x后期效果框架源码剖析》的序。

    但这个序太长了,甚至写着写着就跑题了,面对这思如泉涌的结晶又舍不得删。

    怎么办呢?于是开了一个新贴发出来,顺便回答一些关于2D开发者如何快速掌握3D游戏开发非Cocos引擎的开发者如何快速掌握Cocos等方面的问题。

    大家最想知道的问题:怎么才能在3D的领域如鱼得水?我放在了最后,本文5000多个字,想跳过中间内容的,可以直接拖到最后去看。

    二、最近

    熟悉麒麟子的人都知道,各媒体断更了一年的麒麟子,突然迎来了这一波猛烈的输出。

    明白人一眼就能看出,这波输出绝非偶然,肯定是经过了深思熟虑的。

    麒麟子觉得:如果不写点东西,那做这个布道师还有什么意思。就像C姐的工位上写着:“如果没有奶茶,还来这人间干什么”。(再一次Q到了C姐,真不是故意的)

    最近麒麟子在Cocos Store上发了两个工具类的源码,KylinsPostEffectsKylinsGraphicsDebugger

    KylinsPostEffects是一个不污染场景结点树不依赖Prefab不修改引擎渲染管线使用简单极易扩展的3D后期效果处理方案。内置了GlowBloom空间扰动LUT等效果。

    KylinsGraphicsDebugger是一个3D图形渲染调试器,提供了Mipmap分辨率检查材质实例检查Overdraw检查等工具,可以快速定位项目中的内存问题效率问题。同时计划添加UV检查真实分辨率像素检查等功能。

    可以在store.cocos.com的搜索框内输入kylins,即可列出麒麟子所有的作品。

    作品相关的文章教程会尽快写出来,争取做到让用的人用得爽让学的人学得会

    三、关于Cocos布道师

    有人就问了,这一年,麒麟子都做什么去了,为什么作布道师,反而输出少了。

    答案非常简单,我在寻找我作为布道师的 —

    我花了较长时间去理解Cocos引擎要挑战的目标,去领悟Cocos公司(厦门雅基软件)的使命与愿景,去感受开发者的反馈与诉求,去摸索作为一个Cocos布道师到底应该做哪些的事。

    最后我总结出来,Cocos布道师的职责就是促进信息流通与资源共享,减少开发者与Cocos官方之间的信息差,减少开发者与开发者之间的信息差

    所以在接下来的日子里,我会保持更新。

    那已经消逝的一年光阴是无法追回了,能做的只有尽力弥补。

    在这里,希望愿意交流的朋友,主动与布道师团队任何一个成员联系,因为你们宝贵的反馈与诉求,是可以让Cocos做得越来越好的重要因素。只有听取了大量开发者的建议,满足了普遍开发者的诉求,才能成为世界一流3D图形引擎。

    四、行情

    如今的小游戏已经不能用来定义了。

    早期的小游戏制作中,一些团队通过比光速还快的核心玩法迭代加比腰椎尖盘还要突出的广告点击策略,配合流量矩阵,能够创造出属于自己的流量池,通过热门品类和长尾品类的优化配置,可以做到很好的ROI,不少人因此走上了人生巅峰。

    但随着越来越多的厂商入局,越来越多的玩家已经被洗过,导致了越来越高的玩家期待和逐步攀升的买量成本。这一套打法也逐步失去了优势,红利已然消失怠尽。

    互联网的产品竞争几乎符合产品竞争的四个阶段,小游戏也不例外

    1.人无我有

    这种竞争格局一般出现在市场初期,红利阶段,谁快谁就胜利了。

    这也就是小游戏的第一个阶段。在这个阶段的小游戏产品,不管是获得发行权,还是获得玩家,成本都是极低的。

    2.人有我优

    这种竞争格局一般出现在市场中期,竞争进入相对紧张阶段。

    市场从拼速度到了拼质量阶段。

    在这个阶段的小游戏产品,发行开始挑了,玩家也开始挑了。

    因为各个品类都已经被铺满了,发行和玩家都开始货比三家.

    3.人优我廉

    这种竞争格局一般出现在市场中后期,竞争进入了白热化阶段。

    产品本身的提升已经到了瓶颈,或者是产品本身的提升很难带来溢价了。

    那怎么办呢,只好进入价格战。

    小游戏对于玩家端,已经是免费模式了。因此,一些厂商与发行合作,免费置入其流量矩阵,形成了典型的买方市场(发行说了算)

    4.人廉我转

    这是一个最容易改变竞争格局的策略,当然也是一个九死一生的策略。一但一个行业进入价格战,唯一能打破格局的,就是那些愿意创新的人。

    很多商业模式成熟的公司不会去转型,或者很难转型。 是因为他肯定会把核心资源投入到当前最赚钱的模式上,只留一小部分去创新。

    这也是为什么,一些后来居上的创新者,往往都是前面的产品做得不成功的。因为他们没有包袱,反而更愿意接受改变。

    如今的小游戏,早已不是当年的小游戏。今年已经有不少的中度3D游戏出现,再到明年,就会有更多更重的游戏出现。

    硬件的升级、网络带宽的提升、流量资费的下降、技术的发展、玩家的口味变高,都会推进着这个历史进程的发展,不会因为什么而终结。曾经发生在端游上的、页游上的、手游上的游戏品类,都会在小游戏上重演一次。

    所以,通过其他平台的历史发展轨迹,我们不难看出。游戏是会越来越重的,3D占比是会越来越高的。

    另一个方面,Cocos Creator3.x版本开始,已经将2D3D合二为一,不管是在小游戏领域,还是在原生领域,不管是在2D领域还是在3D领域,都已趋于成熟形态。通过Cocos Creator做的2D3D原生小游戏都已经陆续在各大平台出现。在麒麟子接触的一些项目中,什么经营建造,SLG,飞行·跑酷都不在少数,甚至有一些打通了小游戏和原生的3DMMORPG3D赛车3D Moba类产品。

    经常有人问麒麟子,Cocos Creator的3D重度产品在哪里啊?感觉没人用来做大项目。那我反问一个问题,你觉得一个重度产品的开发周期是多久?今天立项,下周上线?如今一个中度小游戏产品的周期都拉长到了3~6个月,更何况是原生重度3D产品,请大家耐心等待。

    五、社区常见3D学习相关问题

    麒麟子经常会面临一些开发者的咨询,诸如一些关于2D开发者如何快速掌握3D游戏开发或者非Cocos引擎的开发者如何快速掌握Cocos之类的问题,在这里麒麟子挑了一些典型的问题来回答,希望这些回答可以解决掉80%的开发者疑惑。

    1、为什么精通Cocos Creator 2.x,但用Cocos Creator 3.x还是很吃力

    Cocos Creator 3.x与Cocos Creator 2.x最大的区别就是,界面上多了许多3D相关的东西,并且相关API因为要支持3D而做了不少调整。

    我举一些简单的例子:

    Sprite

    在2.x是可以直接设置Alpha Blend相关参数的,但在3.x中,如果要修改Sprite的渲染属性,你必须新建一个材质,然后将这个材质拖到Sprite的material属性上。

    为了建这个材质,你需要了解材质和Sprite的引用关系,了解Effect和材质的关系。 对于一个只开发2D游戏的人来说,增加了一些需要理解的东西。

    图片素材

    在2.x中默认是SpriteFrame,但在3.x中,默认是Texture2D,对于只开发2D游戏的人来说,会觉得多了额外工作。

    positionrotationscale

    在2.x中,我们通过this.node.x|y|zthis.node.rotationXthis.node.scaleX等就可以直接设置一个节点的位置旋转缩放

    但在3.x中,设置位置的几种方式如下

    //方式一
    //注意:this.node.position是只读的,不能直接修改
    let pos = this.node.position; 
    this.node.setPosition(pos.x,pos.y,pos.z+10.0);
    
    //方式二
    //注意:这种方式最不推荐使用,特别是在update里面
    let pos = this.node.position.clone();
    this.node.getPosition(pos);
    pos.z += 10;
    //注意:一定要设置回去,才能触法节点的世界矩阵更新
    this.node.position = pos;
    
    
    //方式三
    //如果pos更新较频繁,建议将pos声明为成员变量,可避免每次都申请临时变量
    let pos = v3();
    this.node.getPosition(pos);
    pos.z += 10;
    //注意:一定要设置回去,才能触法节点的世界矩阵更新
    this.node.position = pos;
    

    很多人就不解,为什么最后一定还要this.node.position = pos,这里我也解释过非常多次了,由于语言特性,以及我们兼顾我们最想要的执行效率。在一些初始化代码,不需要保存的地方,我一般采用方式一,在一些操作频率较高的地方,我一般采用方式三。 因为在游戏开发中,我们需要避免分配较多较小的临时对象,容易造成GC问题。

    在3.x中的rotation就更让人不解了,因为rotation直接变成了四元数。直接设置其x,y,z,w是不可能的。甚至有人一直问w代表的是什么意思。
    如果想要采用欧拉角设置,3.x提供的方式如下

    //取得欧拉角 注:this.node.eulerAngles也是只读的。
    let rot = this.node.eulerAngles;
    //绕Z旋转45度
    this.node.setRotationFromEuler(rot.x,rot.y,rot.z+45);
    
    //取得欧拉角 注:this.node.eulerAngles也是只读的。
    //如果是在update中想要保存,且又避免临时对象分配的话。
    //用下面的方法
    let rot = v3();
    rot.set(this.node.eulerAngles);
    //绕Z旋转45度
    rot.z += 45;
    this.node.setRotationFromEuler(rot.x,rot.y,rot.z);
    

    以上只是从大家常见的节点操作上来说明,至于UITransform以及Canvas属性变更,这些属于常见的引擎操作,并不难理解。

    这里麒麟子要表达的是,我们不能去局限于引擎的API长是什么样,而应该去理解,引擎的API是为了完成什么样的功能,这样设计的目的是什么。这样即使他变了样子,也依然可以快速掌握精要。

    爱它,就应该爱它的内在。

    2、为什么用某某3D,但用Cocos Creator 3.x做3D还是很吃力

    如果一个小伙子,使用一个引擎很多年,拥有非常多的3D项目经验,但转到Cocos Creator来,发现很难做出效果,我想核心原因可能是以下几点:

    1、是否太过于依赖引擎API本身,换个名字就对应不上了。

    2、是否太过于依赖插件扩展完成项目,当找不到对应插件的时候,就不知如何是好了。

    3、是否缺乏最基本的图形管线3D渲染3D数学基础。

    如果上面三个问题都是,麻烦联系一下麒麟子,一定会给你一个满意的答复。

    3、引擎工程师是做什么的,需要掌握哪些知识?

    引擎工程师顾名思义,就是开发引擎的工程师。但开发引擎的工程师有多少岗位,你知道吗? 其实非常简单,打开Cocos的招聘信息就知道有哪些岗位了,几乎每个岗位都在招。

    常见的有渲染组框架组原生组编辑器组物理组动画组等等。

    渲染组就是负责渲染流水线设计与实现、材质系统设计与实现、内置渲染效果设计与实现等等与画画和性能相关的工作。

    框架组就是负责引擎上层与用户接口,比如我们用的TS接口、Component组件,就是框架组需要设计并实现的工作。

    原生组就是负责各个平台和系统的对接与适配,不同平台采用的图形接口不同,文件接口不同,系统接口不同,原生组都需要将其统一,供上层使用。

    编辑器组主要负责编辑器的实现啦,这个组其实是非常NB的,它决定了引擎的生产效率。

    物理组和动画组就更容易解释了,物理系统和动画系统就是他们负责的。

    如果想要知道不同的组需要掌握哪些知识,来给自己制定学习计划的话,可以去招聘信息网站搜索Cocos引擎官方的招聘信息,就能略知一二了。

    4、什么是引擎中台,需要掌握哪些知识

    在中台这个词出现以前,其实是没有引擎中台的,早期的称呼是为项目服务的引擎工程师
    简单来说就是,一个项目组中的人,不可能所有人都去研究引擎。

    引擎中台的职责,就是研究引擎特性,并将引擎特性结合项目需求,提供最适合的引擎使用方案。屏蔽引擎版本差异,降低项目组中对引擎知识的门槛。

    比如,利用引擎接口实现项目中需要的场景管理方案,利用引擎接口实现项目中的特效制作和管理方案利用引擎接口实现项目中的动画管理方案等等。

    即项目开发人员,会使用引擎中台提供的接口来做功能。

    引擎中台需要掌握一定的引擎开发知识,但可以不用太深入,引擎中台更多需要的是项目制作经验,因为只有经验充足,才能制定出符合项目需求的引擎使用方案。

    常见的至少熟悉一种图形API,如WEBGL,3D图形管线原理向量、矩阵、四元数骨骼动画原理,材质系统,光照算法,Shader编写物理系统 等等。

    如果项目组中的引擎中台只有你一个,那你可能都得学会相关技能。

    由于不是让你去开发一个系统 ,所以学习的深度可以从使用着手,再到了解基本原理即可。

    若精力充沛,则可以根据个人兴趣深入研究某个方向。

    5、什么是TA,需要掌握哪些知识

    TA(技术美术)是最近些年兴起的热门岗位,一个3D项目的效果和美术工作效率很大一部分来自于TA。

    早期项目对于TA的定位很简单:定制美术规范、写Shader、写3dsMax插件、协调美术与程序之间的术语代沟

    而现在的TA,则从各个维度进行了分类。由于我不是美术出生,甚至有些分类我看都看不懂。

    但作为TA,大部分都是从最基本的 工具自定义、Shader自定义开始的。

    早期这部分工作,是交给项目组中的3D引擎工程师来做的。

    因此,可以理解为: 技术路线的TA -> 引擎中台美术路线的TA-> TA

    也就是说,不管你是何种岗位入行的,只要你朝中间靠拢,都能成为TA。

    6、如何学习3D编程,就是学Shader吗?

    很多人把学3D编程写Shader划上了等号。然而实际上是, 学Shader可以不用学3D编程,但学3D编程必须学Shader。
    Shader的编写只是3D编程的一个知识点。

    Shader大部分关注的是顶点像素的处理,你只需要理解图形管线,然后去学习光照算法图像处理手法等,就可以写出非常不错的Shader。
    但如果你学习3D编程,那你至少需要学会以下内容:

    1、本地坐标系->世界坐标系->观察坐标系->裁剪坐标系->NDC->屏幕坐标系

    2、顶点处理->图元组装->裁剪->光栅化->背面剔除->像素处理->模板测试->深度测试->ALPHA混合

    3、熟悉其中一种图形API,如OPENGL(ES)、WEBGL、D3D、Metal、Vulkan

    4、左右手法则,向量、点乘、叉乘,缩放矩阵、平移矩阵、旋转矩阵、观察矩阵、投影矩阵,四元数,法线,平面

    5、Alpha测试,Alpha混合

    6、常见着色算法,PBR流程

    7、综合应用:熟悉一个架构良好的、符合商用标准的3D引擎的渲染流水线,比如Cocos的源代码

    8、大量的练习和项目实践

    这里列出来的,是3D编程中躲不掉术语关键字学习过程。还有更多细节,因人的目标与学习偏好不同而不同。

    另外,上面的内容看起来多,其实要学会并不难,快则一年,多则三年,你就会成就一个不一样的自己。

    6、怎么才能在3D的领域如鱼得水?

    由于有了上面第5个问题,这个问题就很容易了。

    • 1、完成上面第5个问题中的3D编程学习
    • 2、参与3D项目进行经验积累
    • 3、选择一个工作内容方向深耕多年
    • 4、起一个响亮、个性、好记的昵称
    • 5、多分享,发文章,写稿子,多参与各类社区活动

    六、终于写完了

    本以为可以12点前完成这篇稿子,还是高估了自己。现在已是1:41了。

    写作很爽,但也蛮花时间。

    只希望这篇文章能给想在3D方向发展的朋友们一些帮助。

    展开全文
  • 麒麟子Cocos Creator实用技巧七:方向与角度转换

    千次阅读 热门讨论 2019-04-28 21:12:09
    麒麟子做了一个Demo给大家,向大家演示了方向转角度,角度转方向的应用。 Demo中有两个坦克,中间的坦克锁定了另一个坦克,始终把自己的炮口对准它,并且会不停地发朝另一个坦克发射炮弹。 发出来的炮弹会追踪另一...
  • 麒麟子创建了一个节点,添加了一个cc.Graphics组件。 最后发现,cc.Graphics的arc函数,并不能绘制出我想要的效果。 既然没有直接支持的函数,想必Cocos Creator并不推荐这样的操作。 放弃了。   思索了...
  • 零前言麒麟子在开发中搞出来的框架,都是遵守“大道至简,实用至上”这两个基本原则。接触一个引擎的第一件事,就是搞出一个实用的框架,方便在此基础上做开发。由于目前的引擎已经是对象+组件模式,...
  • 麒麟子Cocos Creator实用技巧

    千次阅读 2019-04-23 23:24:00
    麒麟子Cocos Creator实用技巧 大家好,我是麒麟子,开源棋牌《幼麟棋牌-四川麻将》(泄漏版叫 《达达麻将》)作者,成都幼麟科技创始人。 自09年进入游戏行业以来,不知不觉已经度过了十个春秋。 ...
  • 前段时间有幸被「幼麟棋牌-麒麟子」 邀请加入幼麟游戏研发群,讨论学习游戏开发技术、行业动态等相关内容。在群中麒麟子透露,出一个重磅消息:“幼麟棋牌全部版本将开源!”在这里...
  • [访客必读]麒麟子的故事

    千次阅读 2019-05-06 20:53:15
    大家好,我是麒麟子。 一、初识计算机 1997年香港回归不久后的一个下午,在隔壁堂哥的稻草棚里捡到了一本《Windows入门基础》(当时觉得很神奇,现在看来,那只不过是一本教大家如何操作Windows系统的书籍),便...
  • 麒麟子Cocos Creator 3D研究笔记之材质IBL与天空盒动态切换
  • 当然麒麟子是没有出门的,看到朋友圈那种堵成狗的照片,感觉有点傻。哈哈哈。 不知道大家还记不记得上一节课的作业,输出车子,房子,老婆,当然你也可以输出孩子 console.log('车子'); console.log('房子'); ...
  • 每个人心中有一个属于自己的游戏世界,麒麟子也不例外。拿到Cocos Creator 3D第一时间,就想撸点人物角色相关的。像换装、角色控制、血条、武器挂接之类的。 然而当我按照套路把角色手中的大棒放到对应的骨骼下的...
  • 麒麟电子显示屏驱动麒麟电子显示屏驱动麒麟电子显示屏驱动麒麟电子显示屏驱动
  • 比如,麒麟子本人就梦想自己有一天能做一个魔兽世界,因为我是魔兽粉。至今还时不时的登录WoW,只为怀念艾泽拉斯的大好河山。那是青春的回忆,是少年时的天堂! 我希望你们想学游戏开发,不是...
  • 麒麟子做了一个DEMO给大家,这个DEMO很简单,大家可以按下鼠标,或者在手机上按住不放。拖拽背景,背景会根据拖拽移动。同时会保证背景边缘不会越过父节点的上下左右边界 在线演示:...
  • 如果你是从大纲那一篇开始看过来的,你在点开这个文章的连接之前,肯定期望说,麒麟子又会在讲正式内容之前BB点什么,这样的话,看起教程来轻松不累还实惠。没错,你猜对了,这就是我的风格。 为什么会把编程教学...
  • 大家好,我是麒麟子。 最近两天老有人说,哎呀,麒麟子老师,你的博客都停更了好几天了,最后写的那一篇还是一篇不知道写什么的文章。感觉是在抒发情怀,又仿佛在卖弄文采。 我哪有抒发情怀,哪有卖弄文采,明明是...
  • 麒麟子在2016年的时候,开启了自己的Cocos Creator之门,并发展出了自己的公司。 在Cocos Creator 3D来临之际,麒麟子再一次选择了提前跟进。 凭借着微弱的商业嗅觉,麒麟子觉得Cocos Creator 3D未来可期。 ...
  • 夜深了,咱就直接进入正题吧,容麒麟子先上张图。 截自cc.d.ts 我们主要用到的就是这个CameraComponent中的converToUINode函数。它这个注释有点问题。 第一个参数 wpos:传入的是世界坐标点,比如人物头顶某个...
  • 在我们日常游戏开发中,经常会面临将玩家名字截断的需求。 假如玩家是在我们游戏中创建的名字,那么可以简单粗暴地禁止玩家使用手机表情输入即可。 ... 因此,为了配合界面的显示,...举一个例子,假如我的名字是麒麟子
  • 大家在做棋牌App或者一些特定需求的时候,需要截取当前游戏屏幕内容保存。 我们一般是采用cc.RenderTexture来截图并保存到游戏的可写目录 有时候会遇上,截出来的图片是白屏,或者部分白屏。 ...
  • 麒麟子Javascript游戏编程零基础教程大纲

    千次阅读 多人点赞 2019-04-27 23:14:02
    大家好,我是麒麟子 自09年进入游戏行业以来,不知不觉已经...
  • 二、换装实现 本DEMO框架采用《麒麟子惯用的框架》 可以先行了解。 1、资源 主要模型资源在assets/resources/Prefabs下面,assets/resources/Textures下面是对应的贴图。 1、Avatar 麒麟子在场景上留了一个Avatar...
  • 有人就说啦,唉麒麟子,你怎么老爱吹牛*,动不动就两百多瓶,你真当自己是牛么。 大家不要急嘛,之所以说两百多瓶,就是想证明我昨晚确实喝醉了,醉得来都记不起喝了多少了。 还有什么能比这个不靠谱的数字更能...
  • 看着群里的小伙伴们都很热衷于Shader的编写,麒麟子刚好做到了这个需求:焦点对象高亮、受击变色、霸体、特殊技能角色周身氛围增强,所以在此分享一下Cocos Creator 3D版本下,Shader的编写。 在此也提醒一下各位小...
  • 小伙子,能够走到这里,你已经不错了。 什么?你是妹子? 放心吧,入了这行,妹子也会被“汉化”的。 游戏程序员分工,以及对应的技能需求。...负责处理客户端相关开发工作,就是需要下载到玩家机器上的那一部分。...
  • 麒麟子公司最近有一款斗地主上微信小游戏,另外还做了两款休闲类小游戏。 当我们觉得差不多大功告成的时候,有用户反馈说,背景音乐突然就没了。要返回大厅再进游戏场景才有。 我当时第一反应就是,正在播放的音乐...
  • 虽然麒麟子秀发依旧,但我不能保证,你的秀发,不会在10年之后,弃你而去。 麒麟子有必要提前科普一下,程序界的梗。 毛不易代言的霸王防脱,也防止不了某些人脱发。虽然这个要掉发的人,不做程序员没也会掉,但是...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,174
精华内容 2,069
关键字:

麒麟子