Skip to content
Go back

Cocos Creator学习之旅

Updated:  at  11:34 PM

文章目录

前向渲染管线和延迟渲染管线

开发Cocos迷失岛和死亡地牢 回顾收获

  1. 统一管理数据,使用全局单例的数据中心,来管理游戏数据,使用get/set方法访问数据,数据更新时触发事件,然后通知视图层进行渲染更新。

  2. 事件驱动视图更新,事件驱动,on/off/emit/once等事件方法,简化视图层渲染更新逻辑。

  3. 用户操作类事件,推荐使用代码进行绑定,而非在编辑器中绑定。

  4. property装饰器,简化属性绑定,方便代码逻辑优化。

  5. getCompoent获取节点组件,诸如Button,Sprite,UITransform等,设置对应组件属性(Butto.interactable),调用方法。

  6. prefab预制体的使用,简化节点复用 instaniate 方法实例化

  7. 获取父子节点,兄弟节点,find/getChildByName/getChildByPath

  8. resources.load方法加载资源,异步加载,回调中获取资源,统一使用promise封装异步加载

  9. 重复利用资源放入节点池,避免反复创建节点

  10. 动画组件animationClip,动画状态机,动画事件回调,可使用director.loadScene方法进行场景切换

  11. 构建随机棋盘,计算棋盘网格线,计算移动路径,计算是否成功

  12. 摄像头设置震动状态,利用sin函数计算震动幅度,在update函数中更新摄像机位置

Cocos编辑器相关

SkeletalAnimation动画组件,专门用于控制动画的播放和设置。

模型节点:

Body模型节点拥有一个蒙皮网格渲染器组件 SkinnedMeshRenderer:

骨骼动画组件 SkeletalAnimation 组件:

可在FBX模型上进行动画剪辑:
const anim = this.node.getComponent(SkeletalAnimation);
anim包含一系列可用于操作动画的方法,也只是订阅对应的事件。

绑定碰撞模块节点到骨骼:

摄像机设置

天空盒:在场景节点中打开天空盒开关同时,还需将摄像机的 ClearFlags 设为 SKYBOX

摄像机属性:

光照设置及阴影设置

光照需要注意对于设置了受光材质的物体才有效:

阴影的生成:

  1. 打开场景节点的Shadows Enabled开关。
  2. 接受阴影的物体,要打开接受阴影开关。
  3. 被光照的物体,要打开投射阴影开关。
  4. 光照的视锥体影响范围也要注意,超出范围,也可能导致阴影消失。

字体缓冲模式

Label文本的缓冲模式:

分辨率

UI系统中分辨率分为:

适配各类屏幕分辨率,项目设置中设置固定高度,使用脚本获取屏幕宽度,然后对组件宽度进行缩放,或设置锚点,始终定义在屏幕的左右两侧。

Layout组件和Widget组件

Layout组件:

Widget组件:

Cocos3D场景DrawCall优化

Cocos2D的DrawCall优化

ts脚本定义枚举值属性:

import {Enum} from 'cc'

const Position = Enum({
    left: "left",
    right: "right",
    center: "center"
})

@property({ type: Position })
public alignType = Position.left; // 对齐类型,默认为左对齐

3D项目开发2D游戏时需要注意

在制作卡牌游戏时,操作卡牌的多个节点时,在图层z属性相差不多时,需要注意操作scale属性时,不要忘记同时操作z维度,这个问题导致卡牌的图层出现了一些细微的错位,因为我只设置了x和y,但是其实我也有使用z来控制卡牌的多层级结构。

基于asset bundle的加载

项目打包

  1. 安卓打包
  1. 微信小游戏打包

着色器

顶点着色器

顶点着色器之后,并不会直接传递给像素着色器,而是会先把顶点着色器输出的东西进行插值、像素化。

这个过程有一个术语叫:光栅化,三角形经过光栅化后,变成了一个个像素

除了顶点位置信息顶点法线颜色纹理坐标等都会先经过光栅化,再传递给像素着色器。

由于所有vert输出的值都会被光栅化,所以顶点着色器传递到像素着色器的法线向量,在使用的时候,记得先normalize,否则会有意想不到的效果。

宝石特效

片元着色器使用varying变量的时候要保证,与顶点着色器要一一对应,有时候不同的pass的顶点着色器,可能对应同一个片元着色器函数。

// Effect Syntax Guide: https://docs.cocos.com/creator/manual/zh/shader/index.html

CCEffect %{
  techniques:
  - name: opaque
    passes:
    - vert: unlit-vs:vert
      frag: unlit-fs:frag
      properties: &props
        mainTexture:    { value: white }
        mainColor:      { value: [1, 1, 1, 1], editor: { type: color } }
        power:          { value: 0.5}
  - name: transparent
    passes:
    - vert: unlit-vs:vert # builtin header
      frag: unlit-fs:frag
      blendState:
        targets:
        - blend: true
          blendSrc: src_alpha
          blendDst: one_minus_src_alpha
          blendSrcAlpha: src_alpha
          blendDstAlpha: one_minus_src_alpha
      properties: *props
}%

CCProgram unlit-vs %{
  precision highp float;
  #include <legacy/input-standard>
  #include <builtin/uniforms/cc-global>
  #include <legacy/local-batch>
  #include <legacy/input-standard>
  #include <legacy/fog-vs>
  #include <legacy/shadow-map-vs>

  in vec4 a_color;

  #if HAS_SECOND_UV
    in vec2 a_texCoord1;
  #endif

  out vec3 v_position;
  out vec3 v_normal;
  out vec3 v_dir;
  out vec3 v_tangent;
  out vec3 v_bitangent;
  out vec2 v_uv;
  out vec2 v_uv1;
  out vec4 v_color;

  vec4 vert () {
    StandardVertInput In;
    CCVertInput(In);

    mat4 matWorld, matWorldIT;
    CCGetWorldMatrixFull(matWorld, matWorldIT);

    vec4 pos = matWorld * In.position;
    v_dir = normalize((cc_matView * pos).xyz);
    v_position = pos.xyz;
    v_normal = normalize((matWorldIT * vec4(In.normal, 0.0)).xyz);
    v_tangent = normalize((matWorld * vec4(In.tangent.xyz, 0.0)).xyz);
    v_bitangent = cross(v_normal, v_tangent) * In.tangent.w; // note the cross order

    v_uv = a_texCoord;
    #if HAS_SECOND_UV
      v_uv1 = a_texCoord1;
    #endif
    v_color = a_color;

    CC_TRANSFER_FOG(pos);
    CC_TRANSFER_SHADOW(pos);

    return cc_matProj * (cc_matView * matWorld) * In.position;
  }
}%

CCProgram unlit-fs %{
  precision highp float;
  #include <legacy/output>
  #include <legacy/fog-fs>

  in vec2 v_uv;
  in vec3 v_position;
  in vec3 v_normal;
  in vec3 v_dir;

  uniform sampler2D mainTexture;

  uniform Constant {
    vec4 mainColor;
    float power;
  };

  vec4 frag () {
    vec4 col = mainColor;
    float stren = dot(v_dir, v_normal);     // 通过单位视向量与单位法向量获取夹角cos值
    col *= pow(abs(stren), power);          // abs取绝对值,通过可控参数,控制强度
    return CCFragOutput(col);
  }
}%

Cocos uniform在shader中定义的规范

Cocos Shader规定,所有非 sampler 类型的 uniform 都应以 UBO(Uniform Buffer Object/Uniform Block)形式声明。

所有成员实际的偏移量(即成员在整体结构中的位置偏离起始位置的距离),是以自身所占用字节数为单位进行对齐的。例如,若一个成员占 4 个字节,那么它在 UBO 中的偏移量会是 4 的倍数,这样做通常是为了提高数据访问效率和内存管理的合理性,让硬件在读取数据时能更高效地定位和处理各个成员。 对于 32 位处理器,字长为 8 字节。如果一个 8字节的变量未对齐存储,可能需要两次内存访问才能读取完整的变量值,而对齐存储时只需一次内存访问。

UBO对内存管理的优化:


uniform IncorrectUBOOrder {
  float f1_1;     // offset 0, length 4 (aligned to 4 bytes)
  vec2 v2;        // offset 8, length 8 (aligned to 8 bytes) [IMPLICIT PADDING!]
  float f1_2;     // offset 16, length 4 (aligned to 4 bytes)
};                // total of 32 bytes

uniform CorrectUBOOrder {
  float f1_1;     // offset 0, length 4 (aligned to 4 bytes)
  float f1_2;     // offset 4, length 4 (aligned to 4 bytes)
  vec2 v2;        // offset 8, length 8 (aligned to 8 bytes)
};                // total of 16 bytes

aligned to 4 bytes 意味着数据项的起始地址必须是 4 的倍数,aligned to 8 bytes 则表示起始地址必须是 8 的倍数。vec2 v2前面是两个四字节数,刚好是从8开始的,所以后一种定义方式更好

f1_1 和 f1_2 对齐到相同位置的原因

Shader练习题

要求横向分三块,0-0.25为粉色,0.25-0.75为黑色,0.75-1为粉色。代码如下,注释代码是我写的,标准答案更简洁:

uniform vec2 iResolution;
void main() {
  // Normalized pixel coordinates (from 0 to 1)
  vec2 uv = gl_FragCoord.xy / iResolution.xy;
  vec3 color = vec3(1.0, 0.3, 0.3);
  // vec3 color = vec3(step(0.25, abs(uv.x-0.5)), step(0.25, abs(uv.x-0.5)), step(0.25, abs(uv.x-0.5)));
  // color = vec3(min(color.x, 1.0),min(color.x, 0.3),min(color.x, 0.3));
  float t1 = 1.0 - step(0.25, uv.x);
  float t2 = step(0.75, uv.x);
  gl_FragColor = vec4(color * max(t1, t2), 1.0);
}


Previous Post
Three.js 汹涌的海(六)
Next Post
Nodejs之process