精华内容
下载资源
问答
  • 从 倔强青铜 到 荣耀王者!只差这篇让你学会Unity中最重要的部分——脚本组件✨。本篇内容对Unity脚本做了一个超级详细的介绍,从开发工具到具体实例,API等都有介绍。

    📢前言

    • 如果你点进来了,那么恭喜你看到了一篇关于Unity中的脚本超级详细的全面介绍🔔
    • 内容干不干,往下看就知道了!🎄
    • 本篇博客来简单介绍一下Unity组件中很重要的一种——脚本组件的介绍 🎅
    • 几乎覆盖了关于脚本内容的大部分,希望感兴趣的小伙伴可以认真看完哦🎉
    • 如果看完学不会请顺着网线来咬我🙊!

    在这里插入图片描述


    ❤️脚本

    • 首先让我们回顾一下上一篇文章中对Unity中的脚本的简单介绍

    • 脚本在Unity中也是一种组件,在之前的组件介绍文章中已经说过

    • 脚本必须要继承MonoBehaviour,而且文件名要和类名一致(如果在Unity里更改了C#文件的名称,在这个脚本里也要改类名),不然脚本挂不上游戏对象。

    在这里插入图片描述


    🧡脚本概念

    • 脚本是附加在游戏物体上用于定义游戏对象行为的指令代码
    • 脚本脚本就是Unity中写代码的东西!(🙉小白提醒~)
    • Unity支持三种高级编程语言:
      C#JavaScript Boo Script(unity4以前支持的)

    💛开发工具

    开发工具指的是需要用来写代码,写脚本的东西!(🙈小白提醒~)

    MonoDevelop

    • unity自带的脚本编辑器,创建Mono应用程序,适用于Linux、Mac OSX和Windows的集成开发环境,支持C#和JavaScript等
    • 这款编辑器应该是Unity5.0或者是之前的老版本使用的,目前是被淘汰了用不到

    Visual Studio

    • 微软公司的开发工具包,包括了整个软件生命周期中需要的大部分工具,如团队开发工具,集成开发环境等等。
    • 在Unity中通过菜单设置修改默认的脚本编辑器,Visual Studio 官网下载链接在这

    在这里插入图片描述

    JetBrains Rider

    • JetBrains Rider 是一款基于 IntelliJ 平台和 ReSharper 的跨平台 .NET IDE。
    • Rider 支持 .NET 框架、新的跨平台框架 .NET Core 和基于 Mono 的项目。
    • 这使您可以开发广泛的应用程序,包括:.NET 桌面应用程序、服务和库、Unity 游戏、Xamarin 应用、ASP.NET 和 ASP.NET Core web 应用程序。
    • JetBrains Rider官网下载链接在这,感兴趣的可以下载试试,跟VS开发差不多

    💚脚本结构介绍

    • 在这里说一下Monobehaviour的继承关系
    • 脚本—>Monobehaviour—>Behaviour—>Component—>Object
    • 下面是一个普通脚本的结构介绍
    using 命名空间;
    public class 类名:MonoBehaviour
    {
        void 方法名()
        {
            Debug.Log("调试显示信息");
            print("本质就是Debug.Log方法");
        }
    }
    
    • 下面是新建一个脚本打开的样子,Start() 方法和 Update() 方法是Unity中新建一个脚本时自动就有的方法
    • Start() 方法在程序开启的第一帧执行
    • Update() 方法在程序的每一帧都会执行
    • Unity中每一帧是0.02秒

    在这里插入图片描述
    创建脚本有好几种方法,这里简单介绍两种

    1.Project面板右键-> Create-> C# Script
    在这里插入图片描述

    2.在游戏对象上点击Add Component,直接输入想要创建的脚本名称就可以了
    这样脚本会自动创建完成并且挂载到刚才点击的游戏对象上面
    在这里插入图片描述


    💙脚本的生命周期

    • 说道脚本,自然要谈一谈Unity中的脚本生命周期啦。

    • 因为我们所写的代码都要有一个执行顺序,才能有条不紊的执行我们需要让程序执行的事情,听起来还有些绕口~

    • 类似Android中的Activity也有自己的生命周期

    • 先来看一张挺长的图,这张图几乎涵盖了脚本的所有方法执行的顺序啦~

    在这里插入图片描述
    看完了图是不是感觉有点慌,有的小伙伴就要说啦:这个图的方法那么多,看着就头大啦,看不明白~

    那不要担心,我们今天主要讲的是Unity脚本生命周期的九大回调,也是脚本中最常用到的方法了
    想让回调函数执行有个前提,就是 脚本必须以组件的方式挂载到场景中某一个游戏对象身上

    • 九大回调说的就是就是下面图中这九个方法啦
      在这里插入图片描述
    • 三种Update都是每帧执行一次
    • FixedUpdate一般用于表现物理效果的时候使用会比Update效果好
    • LateUpdate在相机脚本上使用较好,因为相机可以在Update执行之后运行,不会出现画面Bug的现象

    其中他们的执行顺序:
    Awake——>OnEnable——>Start——>FixedUpdate——>Update——>LateUpdate——>OnGUI——>OnDisable——>OnDestroy

    • 如果有多个脚本中同时在Awake()或Start()中写了方法,哪一个脚本中的先执行呢?

    这个问题需要考虑,不然的话代码中会出现一个时效出错,空引用之类的错误

    这里告诉大家一个办法,就是下面这套流程。
    可以在下图面板上将脚本的先后顺序添加上,程序就会按照我们设置的顺序来执行啦
    如果没有设置这个,那不同脚本先后执行速度就是随机的了,不过AwakeStart也都是在第一帧执行的

    • Edit->Project Settings->Script Execution Order
      在这里插入图片描述
      在这里插入图片描述

    💜调试方法

    Unity编辑器调试

    在脚本代码中用打印方法调试
    Debug.Log()或者print()进行打印log调试

    使用Visual Studio调试

    调试步骤:

    1. 在可能出错的行添加断点
    2. 启动调试
    3. 在Unity中Play场景
    4. 在vs中按F11逐条调试
    5. 调试完毕按F5退出调试

    在这里插入图片描述

    也可以在调试时右键–>快速监视,在快速监视面板便捷的调试和查看数据。
    还可以在即时窗口输入代码进行调试

    因为Update和其他方法不同,它是逐帧运行的,所以在调试时需要单帧调试

    • 步骤:启动调试->运行场景->暂停游戏->加断点->单帧执行->结束

    在这里插入图片描述


    🖤Unity脚本常用的API

    阅读编程资料时经常会看到API这个名词,网上各种高大上的解释估计放倒了一批初学者。
    在这里插入图片描述

    API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。——百度百科

    网上关于API的介绍有一大堆,大多数还举了例子介绍
    其实把API看成一个使用文档就得了~哪来这么多事

    既然介绍完了脚本,那就顺带介绍几种脚本常用的API吧,可以先来看一下Unity的核心类图(图片上网上我能找到的最清晰的了,虽然还是有些模糊~)
    在这里插入图片描述

    GameObject(游戏对象)

    GameObject游戏对象:GameObject提供了添加、设置游戏对象,查找、销毁游戏对象的功能
    下图是GameObject的属性参数。
    在这里插入图片描述
    下面使用代码示例来学习一下

    void Start()
    {
    Debug.Log(gameObject.activeSelf);//当前游戏对象的激活状态
    gameObject.SetActive(false);//设置当前对象的激活状态
    gameObject.SetActive(!gameObject.activeSelf);//设置当前游戏对象的激活状态为当前状态的反向状态
    
    Debug.Log(gameObject.name);//获取当前对象名字
    Debug.Log(gameObject.tag);//获取当前对象标签
    Debug.Log(gameObject.layer);//获取当前对象层
    
    Light mylight = gameObject.GetComponent(type:"Light") as Light;
    mylight = gameObject.GetComponent(typeof(Light))as Light;
    
    mylight = gameObject.GetComponent<Light>();//获取当前游戏对象的组件
    mylight = gameObject.AddComponent<Light>();//添加一个组件到游戏对象身上,并返回这个组件
    ---
    GameObject lt = GameObject.Find("Directional Light");//通过名字找到单个游戏对象
    GameObject com = GameObject.FindWithTag("Player");//通过标签找到单个游戏对象
    com = GameObect.FindGameObjectWithTag("MainCanmer");
    
    GameObject[] coms = GameObject.FindGameObjectWithTag(MainCanmer);//通过标签找到多个游戏对象
    }
    

    特别要注意的是不同通过Find找到处于未激活状态的对象
    Inspector面板上这里未勾选就是未激活的意思,相应的在Hierarcht面板上的游戏对象就会变成灰色
    在这里插入图片描述

    Component(组件)

    Component就是组件,在上篇博客已经介绍了,这里再重新介绍下脚本API的使用吧
    添加、获取和销毁组件的方法
    添加组件

    GameObject Cube= GameObject.CreatePrimitive(PrimitiveType.Cube);//创建一个方格
    Cube.AddComponent<BoxCollider>();//添加盒形碰撞器组件
    Cube.AddComponent<Rigidbody>();//添加刚体组件
    Cube.AddComponent<Test>();//添加Test脚本
    

    获取组件

    BoxCollider boxCollider = Cube.GetComponent<BoxCollider>();//获取盒形碰撞器组件
    Rigidbody rigidbody = Cube.GetComponent<Rigidbody>();//获取刚体组件
    Test test= Cube.GetComponent<Test>();//获取某个Test脚本
    
    GetComponent
    GetComponents
    
    GetComponentInChildren
    GetComponentsInChildren
    
    GetComponentInParent
    GetComponentsInParent
    
    

    销毁组件

    //()中的参数为创建相应组件时的组件名称
    Destroy(boxCollider );//销毁盒形碰撞器组件
    Destroy(rigidbody);//销毁刚体组件
    
    

    Transform

    Transform组件在上篇介绍组件的博客也说过了,这里在提一下在脚本使用的API
    在这里插入图片描述
    下面来用代码示例学一下Transform的使用

    void Start(){
    //1.控制游戏对象的变换
    Debug.Log(transform.position);//世界坐标
    Debug.Log(ransform.localPostion);//本地坐标
    
    Debug.Log(transform.eulerAngles);//世界欧拉角
    Debug.Log(transform.localEulerAngles);//本地欧拉角
    
    Debug.Log(transform.rotation);//世界旋转(四元数)
    Debug.Log(transform.localRotation);//本地旋转(四元数)
    
    //本地缩放
     Debug.Log(transform.localScale);
     //方向
    //自身前方的方向向量
    Debug.Log(transform.forward);
     //自身右方的方向向量
    Debug.Log(transform.right);
    //自身上方的方向向量
     Debug.Log(transform.up);
    
    //描述游戏对象的层级关系
     //父对象
    Debug.Log(transform.parent);
       //根对象
    Debug.Log(transform.root);
    
    //设置父物体
    transform.parent = lookAtTarget;
    //==>
    transform.SetParent(lookAtTarget);
    
     //遍历所有的子对象
     foreach (Transform tra in transform)
     {
     Debug.Log(tra);
      }
    
    //遍历所有的子对象
    for (int i = 0; i < transform.childCount; i++)
    {
     Debug.Log(transform.GetChild(i));
     }
    
     //找当前对象的子对象
    Transform sph = transform.Find("Cylinder/Sphere");
     Debug.Log(sph);
    }
    
    private void Update()
        {
            //角色朝自身前方移动
            // transform.position += transform.forward * 0.05f;
            // transform.Translate(transform.forward * 0.05f);
     //自转
     transform.Rotate(new Vector3(0,1,0),2,Space.World)
    // ==>
     transform.eulerAngles += new Vector3(0,1,0);
    
    transform.Rotate(new Vector3(0,1,0),2,Space.Self);
    //==>
    transform.localEulerAngles += new Vector3(0,1,0);
    
    //绕某个点沿某个轴,旋转
    transform.RotateAround(new Vector3(0, 0, 0), new Vector3(0, 1, 0), 5);
    
    transform.LookAt(new Vector3(0,0,0));
    transform.LookAt(lookAtTarget);//看向某个对象
    

    Vector3(三维向量)

    Vector3向量,在三维坐标系中带有方向和大小的数据,下图介绍了Vector3的属性参数
    在这里插入图片描述
    下面来用代码示例学一下 Vector3的使用

    //创建一个三维向量
            Vector3 dir = new Vector3(1,2,3);
            //创建一个二维向量
            Vector2 dir2 = new Vector2(3,3);
            //创建一个四维向量
            Vector4 dir4 = new Vector4(1,2,3,4);
            
    //获取一个向量的单位向量
    Vector3 normalDir = dir.normalized;
    //将当前向量变成单位向量
            dir.Normalize();
            //向量的长度【模】
            float mag = dir.magnitude;
            //模的平方【用来做向量长度的对比】
            float sqrMag = dir.sqrMagnitude;
     // Vector3.forward
            // Vector3.back
            // Vector3.left
            // Vector3.right
            // Vector3.up
            // Vector3.down
            // Vector3.zero
            // Vector3.one       
    
            Vector3 pointA = Vector3.forward;
            Vector3 pointB = Vector3.right;
            //求两个坐标的距离
            float dis = Vector3.Distance(pointA, pointB);
     
            Vector3 dirA = Vector3.one;
            Vector3 dirB = Vector3.right;
    
            //求两个向量的夹角
            float angle = Vector3.Angle(dirB, dirA);
            float newAngle = Vector3.Angle(new Vector3(1, 1, 0), Vector3.zero);
    
          //求两个向量的点乘
            float dot = Vector3.Dot(dirA, dirB);
    
          //求两个向量的叉乘【求两个向量的法向量】
            Vector3 normal = Vector3.Cross(dirA, dirB);
    
          //求一个向量在某个方向上的投影向量
            Vector3 pro = Vector3.Project(new Vector3(1, 1, 0), Vector3.right);
    
       //求两个向量中间的插值坐标
            Vector3.Lerp(new Vector3(1, 0, 0), new Vector3(5, 0, 0), 0.5f);
    
    //由快到慢运动过去
            transform.position = Vector3.Lerp(transform.position, new Vector3(5, 0, 0), 0.03f);
     
    

    Quaternion(四元数旋转)

    Quaternion四元数本质上是一种高阶复数,是一个四维空间,相对于复数的二维空间。
    在Unity里,tranform组件有一个变量名为Rotation,它的类型就是四元数。用于在Unity中控制旋转的一种方式
    在这里插入图片描述

    下面来用代码示例学一下 Quaternion的使用

    public class ReadQuaternion : MonoBehaviour
    {
        
        public float turnSpeed = 3f;
        public Transform target;
        
      void Start()
        {
            //空旋转【相当于欧拉角的(0,0,0)】
            Debug.Log(Quaternion.identity);
            //将当前角色的旋转设置空旋转
            transform.rotation = Quaternion.identity;
        }
        void Update()
        {
            // transform.LookAt(target);
            //插值旋转
            // transform.rotation 起点四元数
    
            //玩家指向敌人的方向向量
            Vector3 dir = target.position - transform.position;
            //目标四元数
            Quaternion targetQua = Quaternion.LookRotation(dir);
            
            // transform.position = Vector3.Lerp(transform.position,Vector3.right*5,0.02f);
    
            //插值转身
            transform.rotation = Quaternion.Lerp(transform.rotation, targetQua, 0.02f * turnSpeed);
        }
    }
    

    Time(时间)

    首先介绍两个概念:现实时间游戏时间
    大多数Time类都是依赖于游戏时间的。
    现实时间也就是不依赖于程序内部,就算程序暂停也会继续计算的真实时间,而游戏时间是基于程序内部的,可以自行调整。
    我们这里用的都是游戏时间
    在这里插入图片描述
    下面来用代码示例学一下 Time 类的使用

    public class ReadTimeAndMathf : MonoBehaviour
    {
    
        //时间缩放
        public float timeScale = 1f;
    
        public float moveSpeed = 2;
    
        private void FixedUpdate()
        {
            Debug.Log(Time.fixedDeltaTime);
            transform.position += Vector3.forward * Time.fixedDeltaTime * moveSpeed;
        }
    
        void Update()
        {
            // Debug.Log(Time.time);//游戏过来多长时间
            Debug.Log(Time.deltaTime);//每帧的时间间隔
    
            //调整时间缩放
            Time.timeScale = timeScale;
    
            // transform.position += Vector3.forward * Time.deltaTime * moveSpeed;
            // transform.position = Vector3.Lerp(transform.position,Vector3.forward * 5 ,Time.deltaTime * moveSpeed);
        }
    }
    

    Input键盘输入方法

    在游戏中我们经常要用到按某个键来执行某件事,就比如按A键开炮,空格键跳跃等等。下面就来简单介绍一下怎样使用 键盘输入方法

    public class SimplePlayerMove : MonoBehaviour
    {
        [Header("炮弹")]
        public GameObject bullet;
        
        public float moveSpeed = 3f;
        public float turnSpeed = 3f;
    
        private float hor, ver;
        
    void OldUpdate(){
    bool downA = Input.GetKeyDown(KeyCode.A);
    
            if (Input.GetKeyDown(KeyCode.A))
            {
                Debug.Log("按下了A键");
            }
    
            if (Input.GetKeyUp(KeyCode.A))
            {
                Debug.Log("松开了A键");
            }
    
            if (Input.GetKey(KeyCode.Space))
            {
                Debug.Log("按住了空格键");
            }
            
    if(Input.GetKey(KeyCode.W))//前进
    {
    transform.position +=transform.forward * Time.deltaTime * moveSpeed;
    }
    if(Input.GetKey(KeyCode.S))//后退
    {
    transform.position -=transform.forward * Time.deltaTime * moveSpeed;
    }
     if (Input.GetKey(KeyCode.A))//左转
            {
                transform.eulerAngles -= Vector3.up * turnSpeed;
            }
      if (Input.GetKey(KeyCode.D))//右转
            {
                transform.eulerAngles += Vector3.up * turnSpeed;
            }
    }
    ----
    void Update()
      {
        hor = Input.GetAxis("Horizontal");
        ver = Input.GetAxis("Vertical");
    //transform.position += new Vector3(hor, 0, ver) *Time.deltaTime * moveSpeed;   
           //前后移动
            transform.position += ver * transform.forward * Time.deltaTime * moveSpeed;
            //左右转身
            transform.eulerAngles += hor * Vector3.up * turnSpeed;
      }
    }
    
    

    Input鼠标输入方法

    说完了键盘输入,自然还有鼠标输入啦,那下面就来介绍一下 鼠标输入方法

    
    void KeyUpdate()
    {
    
            if (Input.GetMouseButtonDown(0))
            {
                Debug.Log("按下了鼠标左键");
            }
            
            if (Input.GetMouseButtonUp(0))
            {
                Debug.Log("松开了鼠标左键");
            }
            
            if (Input.GetMouseButton(0))
            {
                Debug.Log("按住了鼠标左键");
            }
    }
    ---
    void Update()
    {
    float hor = Input.GetAxis("Horizontal");
    //Debug。Log( if (Input.GetButtonDown("Fire"));
            {
                Debug.Log("按住了开火键");
    
                // GameObject crtPlayer = Instantiate(playerPrefab);
                GameObject crtPlayer = Instantiate(playerPrefab, Vector3.forward, Quaternion.identity);
            })
     if (Input.GetButtonDown("Fire"))
            {
                Debug.Log("按住了开火键");
    
                // GameObject crtPlayer = Instantiate(playerPrefab);
                GameObject crtPlayer = Instantiate(playerPrefab, Vector3.forward, Quaternion.identity);
            }
    }
    
    
    

    💞Unity实现功能的三部曲

    1. 找到要操作的游戏对象或他的组件
    2. 找到功能所对应的组件
    3. 设置组件的属性,或调用组件提供的方法,从而实现功能

    注意

    1. 当程序出现异常,程序会自动暂停,后边的代码就不会执行了
    2. 游戏运行时,所有的操作在停止游戏后,都会恢复
    3. 要真的去做某项操作时,不要在游戏运行时操作

    在这里插入图片描述


    👥总结

    • 好了,关于脚本方面呢我们已经全部介绍完了,最后还简单提了一下Unity实现功能的三部曲
      大家都学会了吧,不用顺着网线来咬我了~
    • 其实整个游戏程序实现功能的思路就是这样,先找到这个游戏对象,然后利用脚本写代码让他做相应的事情。
    • 所有的游戏对象有条不紊的执行他们该干的事情,那么一个游戏程序也就自然而然的运行起来了
    • 如果你是从之前写的几篇博客完整看到这里,那么我之前写的那篇坦克大战的小游戏也就可以自己做了

    Unity中从倔强青铜上荣耀王者,看完这篇是不是有手就行~

    • 其实做一个小游戏也没有那么难嘛~嘿嘿
    • 不过关于Unity中UI方面还没有介绍,大家可以在网上搜一下大佬的文章看看呀,后续有时间会更UI方面的知识点的~

    下面将几篇关于Unity的使用文章挂在这里啦,对Unity有兴趣的小伙伴可以再看一看哦,可以自己动手做一个小游戏玩~



    如果上面这几篇博客内容都差不多了,就可以自己动手做下面小游戏试试啦~🎅下面文章教程和源码都在,可以简单参考下哦


    心动不如行动,还不赶紧一键三连,然后收藏夹吃灰?

    祝大家早日上荣耀王者!

    下篇再见啦~さようなら
    在这里插入图片描述

    展开全文
  • Lotus Notes中最重要的部分

    千次阅读 2008-10-15 09:48:00
    LotusNotes中最重要的部分Evaluate()在脚本中执行一个lotus公式语言,返回该公式语言返回的值。3. (@Modified, @Date, @Weekday, @Today, @Adjust, @Yesterday)。该样例在上例的基础上做些修改,在指定“Yesterday”...
     
    

     

    LotusNotes中最重要的部分
    Evaluate()在脚本中执行一个lotus公式语言,返回该公式语言返回的值。
    3. (@Modified, @Date, @Weekday, @Today, @Adjust, @Yesterday)。该样例在上例的基础上做些修改,在指定“Yesterday”时要跳过周末。如果今天是星期一,则 y 被设置为今天的日期再减去 3 天;否则设置为昨天的日期。用 y 代替 @Yesterday 用于测试 @Modified date。
    d := @Date(@Modified);
    y := @If(@Weekday(@Today) = 2; @Adjust(@Today; 0; 0; -3; 0; 0; 0); @Yesterday);
    FIELD ViewStatus := @If(d = @Today; "Today"; d = y; "Yesterday"; "Old");
    SELECT @All
    怎样判断一个RTF为空值
    Function IsRTFNull(rtfield As String) As Integer
    On Error Goto Errhandle
    Dim workspace As New NotesUIWorkspace
    Dim uidoc As NotesUIDocument
    Set uidoc = workspace.CurrentDocument
    currentfield = uidoc.CurrentField
    Call uidoc.GotoField(rtfield)
    Call uidoc.SelectAll
    Call uidoc.DeselectAll
    If currentfield <> "" Then
    Call uidoc.GotoField(currentfield)
    End If
    IsRTFNull = False
    Exit Function
    Errhandle:
    Select Case Err
    Case 4407
    'the DeselectAll line generated an error message, indicating that the rich text field does     not contain anything
    If currentfield <> "" Then
    Call uidoc.GotoField(currentfield)
    End If
    IsRTFNull = True
    Exit Function
    Case Else
    'For any other error, force the same error to cause LotusScript to do the error handling
    Error Err
    End Select
    End Function
    怎样返回一个数据的类型
    Declarations
    Class ReturnObj
    Private m_stName As String
    Private m_stType As String
    Property Get NameVal As String
    NameVal = m_stName$
    End Property
    Property Get TypeVal As String
    TypeVal = m_stType$
    End Property
    Sub new( arg_stName$, arg_stType$ )
    m_stName = arg_stName$
    m_stType = arg_stType
    End Sub
    End Class
    Function Test() As ReturnObj
    Set Test = New ReturnObj( "Name", "Type" )
    End Function
    Initialize
    Dim var
    Set var = Test()
    Msgbox( var.NameVal )
    怎样判断一个文件目录是否存在
    If Dir$(dirName, ATTR_DIRECTORY) = ""
    Then     'Directory does not exist
    Else
    'Directory does exist
    End If
    怎样在lotusScript中运行代理
    Set s = CreateObject("Notes.NotesSession"
    Set db = s.GETDATABASE("", "db.nsf"
    Set a = db.GETAGENT("SomeAgent"
    Call s.SETENVIRONMENTVAR("AgentDocID", "ABCD"
    Call a.RUN
    怎样才能得到当前数据库的文件路径
    Public Function
    GetDatabasePath( db As Notesdatabase ) As String
    Dim position As Integer
    position = Instr( db.FilePath, db.FileName )
    GetDatabasePath = Left( db.FilePath , position - 1 )
    End Function
    怎样比较两个日期型的域
    mdate1V = document.DateField1(0)
    mdate2V = document.DateField2(0)
    If mdate1V < mdate2V Then
    MsgBox "DATE 1 LESS THEN DATE 2"
    Else
    MsgBox "DATE 2 LESS THEN OR EQUAL TO DATE 1"
    End If
    在Script中做到@mailsend
    Function SendMailMemo(sendTo As String, _
    cc As String, _
    bcc As String, _
    subject As String, _
    body As String, _
    linkTo As NotesDocument) As Integer
    On Error Goto ErrorHandler
    Dim mailDb As New NotesDatabase("", ""
    Dim mailDoc As NotesDocument
    Dim rtItem As NotesRichTextItem
    Call mailDb.OpenMail
    If (mailDb.IsOpen = False) Then Call mailDb.Open("", ""
    Set mailDoc = mailDb.CreateDocument
    mailDoc.Form = "Memo"
    mailDoc.SendTo = sendTo
    mailDoc.CC = cc
    mailDoc.BCC = bcc
    mailDoc.Subject = subject
    Set rtItem = mailDoc.CreateRichTextItem("Body"
    Call rtItem.AppendText(body)
    If Not(linkTo Is Nothing) Then
    Call rtItem.AddNewLine(2)
    Call rtItem.AppendDocLink(linkTo, "Double-click to open document"
    End If
    Call mailDoc.Send(False)
    SendMailMemo = True
    Exit Function
    ErrorHandler:
    Print "Error " & Str$(Err) & ": " & Error$
    Resume TheEnd
    TheEnd:
    SendMailMemo = False
    End Function
    怎样用lotusScript启动附件
    首先使用EmbeddedObjects类将附件拆离到一个临时文件夹里,然后用shell命令语句运行它
    怎样在lotusScript中创建一个姓名域、读者域、作者域
    创建一个"specialType"姓名域
    Dim variableName As New NotesItem( notesDocument, name$, value [,specialType%])
    创建一个"Author"作者域
    Dim TAuthor As New NotesItem(doc, "Author", Auths, AUTHORS)
    TAuthor.IsSummary = True
    修改了主文档后,怎样自动修改答复文档
    Sub QuerySave(Source As Notesuidocument, Continue As Variant)
    Dim Collection As NotesDocumentCollection
    Dim Doc As NotesDocument
    Dim Form, ParentStatus, Status As String
    Set Doc = Source.Document
    Set Collection = Doc.Responses
    Set Doc = Collection.GetFirstDocument
    ParentStatus = Source.FieldGetText("STATUS"
    While Not ( Doc Is Nothing )
    Form = Doc.GetItemValue("Form"(0)
    Status = Doc.GetItemValue("Status"(0)
    If (Form = "TASK"And (Status <> ParentStatus) Then
    Call Doc.ReplaceItemValue( "STATUS", ParentStatus )
    Call Doc.Save (True, False)
    End If
    Set Doc = Collection.GetNextDocument(Doc)
    WendEnd
    Sub
    怎样及时取到服务器的时间
    dim doc as notesdocument
    set doc = Serverdb.Createdocument
    createDate = doc.Created
    ............
    最后不要让该文档存盘
    lotusScripts中怎样在字符串里加回车?
    "aaa"+chr(10)+"bbb"
    怎样屏蔽用户用Delete键删除文件
    在数据库Script中的Querydocumentdelete中使用下列语句。
    Continue=False
    LotusScript 是完全面向对象的编程语言。它通过预定义的类与 Domino 接口。Domino 监控用户代码的编译和加载,并且自动包含 Domino 的类定义。
    访问现有的对象最好使用 LotusScript,例如:根据其他文档的值来更改一个文档中的值。LotusScript 提供了一些公式没有的功能,例如:操作数据库存取控制列表 (ACL) 的能力。
    写script关键是取对象,查看对象的属性,所以你要学会看notes提供的Script帮助。下面是我收集的一些script例子。一般是比较技巧的程序,要学习一般script编写,请下载lotusScript学习库!
    LotusScript 是完全面向对象的编程语言。它通过预定义的类与 Domino 接口。Domino 监控用户代码的编译和加载,并且自动包含 Domino 的类定义。
    访问现有的对象最好使用 LotusScript,例如:根据其他文档的值来更改一个文档中的值。LotusScript 提供了一些公式没有的功能,例如:操作数据库存取控制列表 (ACL) 的能力。
    写script关键是取对象,查看对象的属性,所以你要学会看notes提供的Script帮助。下面是我收集的一些script例子。一般是比较技巧的程序,要学习一般script编写,请下载lotusScript学习库!
    如何辨别域中数据的类型?
    Dim Item as NotesItem
    set item=doc.getfirstitem("item"
    if item.type=NUMBERS then
    end if
    怎样判断视图中没有文档?
    set doc = vw.getfirstdocument()
    if doc is nothing then
    end if
    如何将查询结果放到一个文件夹里?
    下面是将搜索结果放到名叫newfolder的文件夹中,并跳转到该文件夹上
    Sub Click(Source As Button)
    Dim docs As notesdocumentcollection
    Dim doc As notesdocument
    ...........
    q=doc.query(0)
    Set docs = db.ftsearch(q, 0)
    Call docs.PutAllInFolder( "newfolder" )
    Call w.OpenDatabase( "","","newfolder"
    End Sub
    如何删掉数据库中所有私有视图?
    Dim session As New notessession
    Dim db As notesdatabase
    Dim doc As notesdocument
    Set db=session.currentdatabase
    Forall i In db.views
    Set doc=db.getDocumentByUNID(v.universalID)
    ' 这个地方视图当作文档来处理,以便取到视图的一些属性。
    viewflag=doc.getItemvalue("$flags"
    If viewflag(0)="pYV" Then
    ' 视图属性中$flags为"pYV"的是私有视图。
    Call i.remove
    End If
    End Forall
    如何在Notes中调用ODBC数据源中的进程?
    下面是一个利用ODBC调用access数据库(资料库)的script代码
    Dim session As New NotesSession
    Dim con As New ODBCConnection
    Dim qry As New ODBCQuery
    Dim result As New ODBCResultSet
    Set qry.Connection = con
    Set result.Query = qry
    con.ConnectTo("资料库"
    qry.SQL = "SELECT * FROM 资料库"
    result.Execute
    If result.IsResultSetAvailable Then
    Do
    result.NextRow
    id=result.GetValue("ID",id)
    Loop Until result.IsEndOfData
    result.Close(DB_CLOSE)
    Else
    Messagebox "Cannot get result set for AssetData"
    Exit Sub
    End If
    con.Disconnect
    End Sub
    从后台刷新当前文档?
    将当前文档先关闭后再打开
    set doc=uidoc.document
    ......
    call uidoc.save()
    call uidoc.close()
    set uidoc=ws.editdocument(doc)
    获得当前视图中选择了的文档?
    可以用 Notesdatabase 的 Unprocesseddocuments 属性。
    Dim session As New notessession
    Dim db As notesdatabase
    Dim collection As notesdocumentcollection
    Set db = session.currentdatabase
    Set collection = db.UnprocessedDocuments
    Unprocesseddocuments 其实很有用的
    notes和Excel交换数据
    Dim session As New NotesSession
    Dim db As NotesDatabase
    Dim view As NotesView
    Dim doc As NotesDocument
    Dim excelApplication As Variant
    Dim excelWorkbook As Variant
    Dim excelSheet As Variant
    Dim i As Integer
    Set excelApplication = CreateObject("Excel.Application"
    excelApplication.Visible = True
    Set excelWorkbook = excelApplication.Workbooks.Add
    Set excelSheet = excelWorkbook.Worksheets("Sheet1"
    excelSheet.Cells(1,1).Value = "姓名"
    excelSheet.Cells(1,2).Value = "年龄"
    i = 1
    Set db = session.CurrentDatabase
    Set view = db.GetView("abc"
    Set doc = view.GetFirstDocument
    While Not(doc Is Nothing)
    i = i + 1
    excelSheet.Cells(i,1).Value = doc.ClassCategories(0)
    excelSheet.Cells(i,2).Value = doc.Subject(0)
    Set doc = view.GetNextDocument(doc)
    Wend
    excelSheet.Columns("A:B".Select
    excelSheet.Columns("A:B".EntireColumn.AutoFit
    excelWorkbook.SaveAs("Script 内容"
    excelApplication.Quit
    Set excelApplication = Nothing
    在视图中怎样历遍所有的文档?
    Dim db As New NotesDatabase( "Ankara", "current/projects.nsf" )
    Dim view As NotesView
    Dim doc As NotesDocument
    Set view = db.GetView( "Open/By Due Date" )
    Set doc = view.GetFirstDocument
    While Not ( doc Is Nothing )
    ....................
    Set doc = view.GetNextDocument( doc )
    Wend
    在scipt中如何调用公式
    例如我们想要取服务器名的普通名,在script中用@name() ,假设server变量以取到服务器名称在script中用Evaluate可以运行公式,如:servername=Evaluate("@name([CN];server)"
    怎样用script代理取到CGI变量
    Dim session As New NotesSession
    Dim doc As NotesDocument
    Set doc = session.DocumentContext
    Messagebox "User = " + doc.Remote_User(0)
    如何使用Win32API隐藏菜单呢?
    1. Declarations :
    Declare Function GetActiveWindow Lib "user32.dll" () As Long
    Declare Function SetMenu Lib "user32.dll" ( Byval hmenu As Long, Byval newmenu As Long ) As Integer
    2.
    Sub HiddenMenu()
    Dim hwnd As Long
    hwnd = GetActiveWindow()
    Call SetMenu(hwnd,0)
    End Sub
    展开全文
  • reducer就是实现(state, action) => newState纯函数,也就是真正处理state地方。值得注意是,Redux并不希望你修改老state,而且通过直接返回新state方式去修改。 在讲如何设计reducer之前,先介绍几个...

    reducer就是实现(state, action) => newState的纯函数,也就是真正处理state的地方。值得注意的是,Redux并不希望你修改老的state,而且通过直接返回新state的方式去修改。

    在讲如何设计reducer之前,先介绍几个术语:
    ✦ reducer:实现(state, action) -> newState的纯函数,可以根据场景分为以下好几种
    ✦ root reducer:根reducer,作为createStore的第一个参数
    ✦ slice reducer:分片reducer,相对根reducer来说的。用来操作state的一部分数据。多个分片reducer可以合并成一个根reducer
    ✦ higher-order reducer:高阶reducer,接受reducer作为参数的函数/返回reducer作为返回值的函数。
    ✦ case function:功能函数,接受指定action后的更新逻辑,可以是简单的reducer函数,也可以接受其他参数。

    reducer的最佳实践主要分为以下几个部分
    ✦ 抽离工具函数,以便复用。
    ✦ 抽离功能函数(case function),精简reducer声明部分的代码。
    ✦ 根据数据类别拆分,维护多个独立的slice reducer。
    ✦ 合并slice reducer。
    ✦ 通过crossReducer在多个slice reducer中共享数据。
    ✦ 减少reducer的模板代码。

    接下来,我们详细的介绍每个部分

    如何抽离工具函数?

    抽离工具函数,几乎在任何一个项目中都需要。要抽离的函数需要满足以下条件:
    ✦ 纯净,和业务逻辑不耦合
    ✦ 功能单一,一个函数只实现一个功能
    由于reducer都是对state的增删改查,所以会有较多的重复的基础逻辑,针对reducer来抽离工具函数,简直恰到好处。

    // 比如对象更新,浅拷贝
    export const updateObject = (oldObj, newObj) => {
        return assign({}, oldObj, newObj);
    }
    // 比如对象更新,深拷贝
    export const deepUpdateObject = (oldObj, newObj) => {
        return deepAssign({}, oldObj, newObj);
    }

    工具函数抽离出来,建议放到单独的文件中保存。

    如何抽离 case function 功能函数?

    不要被什么case function吓到,直接给你看看代码你就清楚了,也是体力活,目的是为了让reducer的分支判断更清晰。

    // 抽离前,所有代码都揉到slice reducer中,不够清晰
    function appreducer(state = initialState, action) {
        switch (action.type) {
            case 'ADD_TODO':
                ...
                ...
                return newState;
            case 'TOGGLE_TODO':
                ...
                ...
                return newState;
            default:
                return state;
        }
    }
    
    // 抽离后,将所有的state处理逻辑放到单独的函数中,reducer的逻辑格外清楚
    function addTodo(state, action) {
        ...
        ...
        return newState;
    }
    function toggleTodo(state, action) {
        ...
        ...
        return newState;
    }
    function appreducer(state = initialState, action) {
        switch (action.type) {
            case 'ADD_TODO':
                return addTodo(state, action);
            case 'TOGGLE_TODO':
                return toggleTodo(state, action);
            default:
                return state;
        }
    }

    case function就是指定action的处理函数,是最小粒度的reducer。
    抽离case function,可以让slice reducer的代码保持结构上的精简。

    如何设计slice reducer?

    上一篇 关于state的博客 已经提过,我们需要对state进行拆分处理,然后用对应的slice reducer去处理对应的数据,比如article相关的数据用articlesReducer去处理,paper相关的数据用papersReducer去处理。
    这样可以保证数据之间解耦,并且让每个slice reducer保持代码清晰并且相对独立。
    比如好奇心日报有articles、papers两个类别的数据,我们拆分state并扁平化改造

    {
        // 扁平化
        entities: {
            articles: {},
            papers: {}
        },
    
        // 按类别拆分数据
        articles: {
            list: []
        },
        papers: {
            list: []
        }
    }

    为了对state.articles和state.papers分别进行管理,我们设计两个slice reducer,分别是articlesReducer和papersReducer

    // ------------------------------------
    // Action Handlers
    // ------------------------------------
    const ACTION_HANDLERS = {
        [UPDATE_ARTICLES_LIST]: updateArticelsList(articles, action)
    }
    // ------------------------------------
    // reducer
    // ------------------------------------
    // !!!值得注意的是,对于articlesReducer来说,它并不知道state的存在,它只知道state.articles!!!
    // 所以articlesReducer完成的工作是(articles, action) => newArticles
    export function articlesReducer(articles = {
        list: []
    }, action) {
        const handler = ACTION_HANDLERS[action.type]
    
        return handler ? handler(articles, action) : articles
    }
    
    // papersReducer类似,就不贴代码了。

    由于我们的state进行了扁平化改造,所以我们需要在case function中进行normalizr化。

    根据state的拆分,设计出对应的slice reducer,让他们对自己的数据分别管理,这样后代码更便于维护,但也引出了两个问题。
    ✦ 拆分多个slice reducer,但createStore只能接受一个reducer作为参数,所以我们怎么合并这些slice reducer呢?
    ✦ 每个slice reducer只负责管理自身的数据,对state并不知情。那么articlesReducer怎么去改变state.entities的数据呢?
    这两个问题,分别引出了两部分内容,分别是:slice reducer合并、slice reducer数据共享。

    如何合并多个slice reducer?

    redux提供了combineReducer方法,可以用来合并多个slice reducer,返回root reducer传递给createStore使用。直接上代码,非常简单。

    combineReducers({
        entities: entitiesreducer,
    
        // 对于articlesReducer来说,他接受(state, action) => newState,
        // 其中的state,是articles,也就是state.articles
        // 它并不能获取到state的数据,更不能获取到state.papers的数据
        articles: articlesReducer,
        papers: papersReducer
    })

    传递给combineReducer的是key-value 键值对,其中键表示传递到对应reducer的数据,也就是说:slice reducer中的state并不是全局state,而是state.articles/state.papers等数据。

    如果解决多个slice reducer间共享数据的问题?

    slice reducer本质上是为了实现专门数据专门管理,让数据管理更清晰。那么slice reducer间如何共享数据呢?

    举个例子,我们异步获取article的时候,会附带将comments也带过来,那么我们在articlesReducer中怎么去维护这份comments数据?

    // 不好的方法
    // 我们通过两次dispatch来分别更新comments和article
    // 缺点是:slice reducer之间严重耦合,代码不容易维护
    dispatch(updateComments(comments));
    dispatch(updateArticle(article)));

    那么有什么更好的办法呢?我们能不能在articlesReducer处理之后,将action透传给commentsReducers呢?看看如下代码

    // 定义一个crossReducer
    function crossReducer(state, action) {
        switch (action.type) {
            // 处理指定的action
            case UPDATE_COMMENTS:
                return Object.assign({}, state, {
                    // 这儿是关键,相当于透传到commentsReducer,然后让commentsReducer去处理对应的逻辑。
                    // 这样的话
                    // crossReducer不关心commentsReducer的逻辑
                    // articlesReducer也不用去关心commentsReducer的逻辑
                    comments: commentsReducer(state.comments, action)
                });
            default:
                return state;
        }
    }
    
    let combinedReducer = combineReducers({
        entities: entitiesreducer,
        articles: articlesReducer,
        papers: papersReducer
    });
    
    // 在其他reducer处理完成后,在进行crossReducer的操作
    function rootReducer(state, action) {
        let tempstate = combinedReducer(state, action),
            finalstate = crossReducer(tempstate, action);
    
        return finalstate;
    }

    当然,我们可以使用reduce-reducers这个插件来简化上面的rootReducer。

    import reduceReducers from 'reduce-reducers';
    
    export const rootReducer = reduceReducers(
        combineReducers({
            entities: entitiesreducer,
    
            articles: articlesReducer,
            comments: commentsReducer
        }),
        crossReducer
    );

    原理很简单,先执行某些slice reducer,执行完成后,再去执行crossReducer,而crossReducer本身不做任何的工作,只负责调用关联reducer,并且把数据传到关联reducer中。

    如何减少reducer的样板代码?

    每次写action/action creator/reducer,都会写很多相似度很高的代码,我们是否可以通过一定封装,来减少这些样板代码呢?
    比如我们定义一个createReducer的函数,用来创建slice reducer。如下所示:

    function createReducer(initialState, handlers) {
        return function reducer(state = initialState, action) {
            if (handlers.hasOwnProperty(action.type)) {
                return handlers[action.type](state, action)
            } else {
                return state
            }
        }
    }
    
    const todosreducer = createReducer([], {
        'ADD_TODO': addTodo,
        'TOGGLE_TODO': toggleTodo,
        'EDIT_TODO': editTodo
    });

    也可以使用现成的比较好的方案,比如:redux-actions。给个简单的示例,更多的可以查看官方文档。

    // 定义action及action creator
    const {
        increment,
        descrement
    } = createActions({
        INCREMENT: (val) => val,
        DECREMENT: (val) => val
    });
    
    // 定义reducer
    const reducer = handleActions({
        INCREMENT: (state, action) => ({
            counter: state.counter + action.payload
        }),
    
        DECREMENT: (state, action) => ({
            counter: state.counter - action.payload
        })
    }, { counter: 0 });

    减少样板代码之后,代码一下就变得清晰多了。

    总结说点啥?

    reducer的设计相对于state和action来说要复杂很多,他涉及拆分、合并、数据共享的问题。
    本文介绍了怎样最佳实践的去设计reducer,按照上面的步骤下来,可以让你的reducer保持结构简单。

    ✦ 抽离工具函数,这个不用多说。
    ✦ 抽离case function,让slice reducer看起来更简洁。其中case function是最小粒度的reducer,是action的处理函数。
    ✦ 拆分slice reducer,这个是和state拆分匹配的,拆分slice reducer是为了实现专门数据专门管理,并且让slice reducer更加便于维护。
    ✦ 合并slice reducer,createStore只能接受一个reducer作为参数,所以我们用combineReducer将拆分后的slice reducer合并起来。先拆分再合并其实更多是为了工程上的便利。
    ✦ 使用crossReducer类似的功能,可以实现slice reducer间数据共享。
    ✦ 减少reducer的样板代码,这个不多说,使用redux-actions就挺好,但不建议新人这样做。

    实际开发中,我个人更喜欢将action和reducer写在一个文件中,并且将redux相关的代码全部放到统一的目录中。
    结合上一篇博客讲的 state设计,Redux基本的架构雏形就出来了,当然可以继续深入,比如结合按需加载、路由、数据持久化等等。

    展开全文
  • 本章着重分析Graphic源码部分,接下来章节会重点分析Graphic继承链下MaskableGraphic与衍生出来组件们。 GraphicRegistry 管理同Canvas下所有Graphic对象 Dictionary> m_Graphics Graphic 初始化时(Enable...

    系列

    UGUI源码分析系列总览

    Graphic

    Related Class: Graphic、MaskableGraphic、GraphicRegistry、CanvasUpdateRegistry、VertexHelper

    Related Interface: ICanvasElement、IMeshModifier、IClippable、IMaskable、IMaterialModifier

    Intro: 图形组件的基类,基础中的基础组件

    • ICanvasElement: Canvas元素(重建接口),当Canvas发生更新时重建(void Rebuild)
    • IMeshModifier:网格处理接口
    • IClippable:裁剪相关处理接口
    • IMaskable:遮罩处理接口
    • IMaterialModifier:材质处理接口

    Graphic 作为图像组件的基类,主要实现了网格与图像的生成/刷新方法。
    在生命周期Enable阶段、Editor模式下的OnValidate中、层级/颜色/材质改变时都会进行相应的刷新(重建)。
    重建过程主要通过 CanvasUpdateSystem 最终被Canvas所重新渲染。
    详情请见:CanvasUpdateSystem源码剖析

    重建主要分为两个部分:顶点重建(UpdateGeometry)与 材质重建(UpdateMaterial)

    更新完成的结果会设置进CanvasRenderer,从而被渲染形成图像。

    在这里插入图片描述

    本章着重分析Graphic源码部分,接下来的章节会重点分析Graphic继承链下的MaskableGraphic与衍生出来的组件们。


    GraphicRegistry

    管理同Canvas下的所有Graphic对象

    Dictionary<Canvas, IndexedSet<Graphic>> m_Graphics

    Graphic 初始化时(Enable)会寻找其最近根节点的Canvas组件,并以此为key存储在GraphicRegistry中。


    Rebuild

    public virtual void Rebuild(CanvasUpdate update)
    {
        if (canvasRenderer.cull)
            return;
        switch (update)
        {
            case CanvasUpdate.PreRender:
                if (m_VertsDirty)
                {
                    UpdateGeometry();//网格更新
                    m_VertsDirty = false;
                }
                if (m_MaterialDirty)
                {
                    UpdateMaterial();//材质纹理更新
                    m_MaterialDirty = false;
                }
                break;
        }
    }
    

    UpdateGeometry

    Graphic 顶点(网格)更新与生成,发生顶点重建时会被调用。

    过程:

    • 更新VertexHelper数据
    • 遍历身上的IMeshModifier组件(MeshEffect组件,实现网格的一些特效,例如Shadow、Outline),更新VertexHelper数据
    • 将最终的顶点数据设置给 workerMesh,并将workerMesh设置进canvasRenderer中,进行渲染。
    private void DoMeshGeneration()
    {
        if (rectTransform != null && rectTransform.rect.width >= 0 && rectTransform.rect.height >= 0)
            OnPopulateMesh(s_VertexHelper);//更新顶点信息
        else
            s_VertexHelper.Clear(); // clear the vertex helper so invalid graphics dont draw.
    
        var components = ListPool<Component>.Get();
        GetComponents(typeof(IMeshModifier), components);
    
        for (var i = 0; i < components.Count; i++)
            ((IMeshModifier)components[i]).ModifyMesh(s_VertexHelper);//若由网格特效,则由特效继续更新顶点信息
    
        ListPool<Component>.Release(components);
    
        s_VertexHelper.FillMesh(workerMesh);
        canvasRenderer.SetMesh(workerMesh);//设置当canvasRenderer中
    }
    

    基础的网格由 4 个顶点 2 个三角面构成

    在这里插入图片描述

    VertexHelper : 临时存储有关顶点的所有信息,辅助生成网格

    - List<Vector3> m_Positions : 顶点位置

    - List<Color32> m_Colors :顶点颜色

    - List<Vector2> m_Uv0S :第1个顶点UV坐标

    - List<Vector2> m_Uv1 :第2个顶点UV坐标

    - List<Vector2> m_Uv2S :第3个顶点UV坐标

    - List<Vector2> m_Uv3S :第4个顶点UV坐标

    - List<Vector3> m_Normals :法线向量

    - List<Vector4> m_Tangents : 切线向量

    - List<int> m_Indices : 三角面顶点索引

    BaseMeshEffect

    • PositionAsUV1: 根据顶点坐标设置UV1坐标(一般为法线贴图,不加此组件时UV1坐标默认是Vector2.zero
    • Shadow:在顶点数基础上增加了一倍的顶点数,并根据偏移(effectDistance)设置新顶点的坐标,实现阴影效果。
    • Outline:继承自Shadow,原理就是分别在四个角(根据effectDistance换算)上实现了四个Shadow,即增加了4倍的顶点数。

    在这里插入图片描述


    UpdateMaterial

    Graphic 材质更新,发生材质重建时会被调用。

    过程:

    • 获取自身材质material,遍历身上的IMaterialModifier组件(材质处理组件,实现材质特效,例如Mask),更新 materialForRendering
    • 将最终的材质数据materialForRendering与纹理mainTexture设置进canvasRenderer中,进行渲染。
    protected virtual void UpdateMaterial()
    {
        if (!IsActive())
            return;
        canvasRenderer.materialCount = 1;
        canvasRenderer.SetMaterial(materialForRendering, 0);
        canvasRenderer.SetTexture(mainTexture);
    }
    
    public virtual Material materialForRendering
    {
        get
        {
            var components = ListPool<Component>.Get();
            GetComponents(typeof(IMaterialModifier), components);
    
            var currentMat = material;
            for (var i = 0; i < components.Count; i++)
                currentMat = (components[i] as IMaterialModifier).GetModifiedMaterial(currentMat);//这里由IMaterialModifier组件对currentMat进行特效化处理,得到最终展示的材质
            ListPool<Component>.Release(components);
            return currentMat;
        }
    }
    

    .
    .
    .
    .
    .


    嗨,我是作者Vin129,逐儿时之梦正在游戏制作的技术海洋中漂泊。知道的越多,不知道的也越多。希望我的文章对你有所帮助:)


    展开全文
  • 【运筹学】企业最重要的部分-预测和决策

    千次阅读 热门讨论 2015-04-12 18:57:12
    运筹学(operations Research)是一门研究如何有效组织和管理人机系统科学。在管理领域,对管理决策工作进行决策计量方法。运筹帷幄之中,决胜千里之外,企业领导主要职责就是运用运筹学理论知识,结合...
  • http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html
  • 现如今还是菜菜,如若有错误地方还望指正! 如若转载,请注明源地址:http://blog.csdn.net/shinilaobababa/article/details/8453434 首先还是来一句OC中内存管理黄金法则比较好  如果对一个对象使用了...
  • 不写代码:程序员最重要的技能 [英文版]

    千次阅读 多人点赞 2019-05-27 23:30:07
    作为一个程序员,写代码是你工作中最重要的部分。在你的编程生涯中,你需要跟各种各样的代码需求打交道。每次需求都会迫使你做出艰难的决定。这都没有问题。作为一个程序员,这是所有人对你的期待:写代码。然而,...
  • 对计算机误解 很多人认为计算机嘛,顾名思义主要是计算机器,于是有一种错觉,既然是计算,那自然联想到小学初中学数学加减乘除运算.困惑自然而然产生,通过加减乘除这么简单不能再简单数学运算计算机能实现...
  • 不知道为什么那些网文作者都说socks代理比http代理复杂,http代理和socks代理我都做了,...我做那个http代理可谓很失败,用着用着就cpu100%占用了,应该就是解析http协议头做不好,出现死循环了或者别,那么多htt
  • 今天首先看看String类的部分方法,后续我会继续抽时间添加其他方法。 1、indexOf方法 indexOf (String str)是用于检测对象字符串是否包含参数字符串的方法。 indexOf 方法返回一个整数值,指出 String 对象内子...
  • 学IT最重要的东西是什么?

    千次阅读 2018-07-27 16:58:32
    学IT最重要的东西是什么? (大哥的心得) 第一、 学程序最重要的东西是什么? 我从大一开始真正接触程序,大部分的程序语言和工具我都是自学的,有的人说“这都是怎么学的?为什么我就学着感觉那么难?”。针对这...
  • LINUX组成部分

    千次阅读 2018-05-05 15:02:32
    内核是所有组成部分中最为基础、最重要的部分。1.linux内核内核(Kernal)是整个操作系统的核心,管理着整个计算机的软硬件资源。内核控制整个计算机的运行,提供相应的硬件驱动程序、网络接口程序,并管理所有程序...
  • 网站最重要的SEO关键

    2009-04-11 23:32:00
    2、网站被连结的数量,是指访客从别人的网站内容里点选连结到你的网站,这部分占了第2重要的影响。你想想当访客从别的网站被你的广告所吸引而点击连结到你的网站时,你想想你的网站内容是不是具备着一定比例的人气和...
  • 下面对数据库中部分的重要字段进行说明。 Interfaceid interfaceid hostid main type useip ip dns port ...
  • 创业最重要的是什么?

    千次阅读 2020-11-12 09:08:39
    其中绝大部分人都是想着要创业,然后跟你说了一大堆创业时候该如何如何,甚至创业成功之后如何如何? 但是过了一段时间你会发现,其实之前是怎么样,现在他还是怎么玩?正是印证了那句老话,晚上睡觉前想了千万条路...
  • 基础系统和东西是最重要的

    千次阅读 2011-04-24 10:35:00
    类似观点在此blog已经表达过了,继续换个视角和能达到...   我想表达东西其实就是,一些会让人眼前一亮东西(像扣篮)大部分情况就是扮演噱头角色,真正决定系统能力还是基础部分(跳投
  • C++程序员最重要的能力

    千次阅读 2009-03-14 13:45:00
    人需要机器来帮助来解决一些问题,往往大部分人不想去为此而学习如何与机器沟通,所以需要一小部分人掌握这样能力来为大部分人服务,这一小部分人就是程序员。 和普通翻译官一样,程序言需要懂得多门“语言”,...
  •  作为内核网络协议部分最重要的数据结构SKB,有很多值得仔细推敲的问题。   SKB这种说法实际包含了两部分,即skb描述符和skb数据。 Skb描述符即内核中的skbuff结构体,里面含有大量的指针变量,运用指针的好处是...
  • 最重要的事只有一件>>读后感

    千次阅读 2017-02-20 13:09:11
    然后根据目标找到关键问题,再根据关键问题找出最重要的事,最后全力以赴的完成. 完成了最重要的一件事就解决了关键问题,解决了关键问题就实现了目标. 想实现目标,有几要和几不要. 要找到人生导师. 要研究成功人士
  • 这段时间做Android项目,其中感觉头疼一点就是界面制作。面对设计师发过来效果图,想要做出与之完全相符效果,真是头疼万分。 因为Android不像iphone那样,只需要面对一两种屏幕,所以大部分图片可直接...
  • 影响视频质量和大小的重要参数

    千次阅读 2019-12-03 15:43:57
    码流(Data Rate)是指视频文件在单位时间内使用的数据流量,也叫码率,是视频编码中画面质量控制中最重要的部分。同样分辨率下,视频文件的码流越大,压缩比就越小,画面质量就越好。 比特率 数字信道传送数字信号...
  • iMatrix平台的系统元数据管理是维护各系统定义的模型数据,包括数据表、表单、列表...系统元数据管理中最重要的是列表管理和表单管理。列表管理可以通过配置列表页面是否分页、是否启用查询、合并当前页、合并所有页、
  • Linux四个组成部分

    千次阅读 2011-09-22 10:05:02
    Linux主要分为4个部分:内核(kernel)、shell、文件结构和实用工具。 一、内核 内核主要作用是运行程序和管理硬件。 内核包括几个主要部分:进程管理、内存管理、...内核最重要的部分是内存管理和进程管理。 二、
  • 作者 |刘洪善华为云产品经理 ​苹果隐私用户体验 用户体验是个很大词。...在我8年安全职业生涯中,苹果可以说是我见过的最注重安全隐私产品(特性)用户体验公司。仅我们考察10年间WWD..
  • 现在人工智能的发展可谓是如火如荼,从而引起了很多人学习人工...很多关于人工智能的文献以及报告都不约而同的偏重于关注机器学习算法,将其视为最重要的部分。主流媒体似乎把算法与人脑等同了。他们似乎在传达着这...
  • 解释机器学习模型并展示结果 机器学习模型经常被批评为黑盒子:我们将数据放在一边,并...我们将讨论机器学习项目中最重要的部分:记录我们的工作并展示结果。 该系列的第一部分包括数据清理,探索性数据分析,特...
  • 1、国内对CO.CC域名申请太多,也造成了部分已经申请域名被河蟹,不过可以通过再申请国内域名做转发比如dtdns等。 2、通过访问www.co.cc即可申请,我刚申请免费域名是clsql.co.cc暂时做了域名转向,因为有24...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 34,835
精华内容 13,934
关键字:

最重要的部分