【Unity】学习记录【二】

2020年9月18日
DFA算法过滤文本的敏感词

  • Hashtable 的键值对可以是任何类型(均以object类为参数)
  • 相关部门网站上会有敏感词库提供
  • 完成XML模板建立后,打开Excel可以自动映射成表格,然后扩展完了之后

2020年9月19日
【事件•语法篇】如何声明自定义的事件以及事件的完整/简略声明格式..

  • 事件是基于委托的,但事件不是委托类型。事件的本质是委托字段的一个包装器。既然是包装器,那么就会在原有基础上有更多限制。(在事件的完整声明中就可以知道为什么了)

  • 为什么说事件是基于委托的?
    1.因为委托可以提供强制类型兼容的语法
    2.因为委托可以存储方法的引用

  • 事件在对委托的包装上做出了哪些事?
    1.事件只能使用+=、-=,不能使用= (在委托中使用=符号,叫做Reset Invocation List,重置调用列表)
    2.事件的调用(OnXX.Invoke(...))只能在事件拥有者自身的触发逻辑中调用,而委托可以在任意地方调用。
    *3.事件的简略声明格式中可以用==、!=来判断是否为空,但是完整声明格式里不行。

  • 事件的一般声明方式
    一般在使用事件的时候,都是用public event EventHandler OnXX; 来定义的,
    其中EventHanlder这个类型是一个约定,代表所有订阅OnXX的方法,都要满足EventHandler的形式。

  • EventHandler的约定是void EventHandler(object sender, EventArgs e);它是一个委托类型。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//1.事件拥有者
//2.事件
//3.事件响应者
//4.事件处理器
//5.订阅

//1.我(类)要有一个事件(成员)
//2.一群别的类关心、订阅我的事件(订阅的时候要符合约定)   custom.OnOrder += waitor.TakeAction;
//3.我的事件发生了! (一定是由事件拥有者的内部逻辑触发的)    OnOrder.Invoke(this,e);
//4. 关心的类们被一次性通知到
//5. 被通知到的人,拿着事件参数,做出相应      TakeAction(object _sender, EventArgs _e)

public class EventExample_Pro : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Custom custom = new Custom("Jimmy");
        Waitor waitor = new Waitor();
        //订阅
        custom.OnOrder += waitor.TakeAction;
        
        custom.Order("抹茶星冰乐",25,"大杯");
        custom.Order("焦糖玛奇朵", 28, "超大杯");
        custom.PayBill();     
    }
}

//事件拥有者
public class Custom
{
    public Custom(string name)
    {
        Name = name;
    } 
    public string Name { get; set; }
    public float Bill { get; set; }
    public event EventHandler OnOrder;
    
    public void Order(string coffeeName, float coffeeBasePrice, string cup)
    {
        if (OnOrder != null)   //在事件的完整声明中,是不可以通过!=符号来判断的,这里是个语法糖
        {
            OrderEventArgs e = new OrderEventArgs(coffeeName, coffeeBasePrice, cup);
            //事件触发
            OnOrder.Invoke(this, e);
        }
       
    }

    public void PayBill()
    {
        Debug.Log(Name + "一共需要支付" + Bill);
    }
}

//事件响应者
public class Waitor
{

    //事件处理器
    //按照自己的职责、任务做出特定处理
    internal void TakeAction(object _sender, EventArgs _e)
    {
        Custom custom = _sender as Custom;
        OrderEventArgs e = _e as OrderEventArgs;
        float bill = 0;
        switch (e.CoffeeCup)
        {
            case "中杯":
                bill = e.CoffeeBasePrice;
                break;
            case "大杯":
                bill = e.CoffeeBasePrice + 3;
                break;
            case "超大杯":
                bill = e.CoffeeBasePrice + 6;
                break;
            default:
                Debug.Log("只能在中杯、大杯、超大杯里挑选");
                break;
        }
        custom.Bill += bill;
        Debug.Log(custom.Name +"点了一杯"+ e.CoffeeName+",需要支付" + bill);
    }
}


//事件消息
public class OrderEventArgs : EventArgs
{
    public OrderEventArgs(string name, float basePrice, string cup)
    {
        CoffeeName = name;
        CoffeeBasePrice = basePrice;
        CoffeeCup = cup;
    }
    public string CoffeeName;
    public float CoffeeBasePrice;
    public string  CoffeeCup;
}
  • 事件的完整声明方式(机理)
    因为只在Custom中作了改变,所以只列出了Custom类。 事件的内部机理可以和属性的内部机理比对着来理解。
public class Custom
{
    public Custom(string name)
    {
        Name = name;
    } 
    public string Name { get; set; }
    public float Bill { get; set; }
    
    //“事件是基于委托”这句话的核心↓↓↓
    public EventHandler orderEventHandler;  
    //orderEventHandler是一个委托类型的字段,而OnOrder事件是它的包装器
    //OnOrder是依赖于orderEventHandler的
    public event EventHandler OnOrder
    {
        add
        {
            orderEventHandler += value;
        }
        remove
        {
            orderEventHandler -= value;
        }
    }
    
    public void Order(string coffeeName, float coffeeBasePrice, string cup)
    {
        if (orderEventHandler != null)  
        {
            OrderEventArgs e = new OrderEventArgs(coffeeName, coffeeBasePrice, cup);
            //事件触发
            //完整声明中,只能通过事件内部的委托字段来执行
            orderEventHandler(this, e);
        }
    }

    public void PayBill()
    {
        Debug.Log(Name + "一共需要支付" + Bill);
    }
}
  • 另外EventHandler、Sender、EventArgs 都可以派生。

2020年9月20日
委托与事件
通过事件来改善脚本通信
本次例子,假设有以下三个类:

  • Player : 会死亡
  • Achievements(成就) :在死亡1000次的时候触发成就
  • UserInterface(用户界面)这三个 : 显示Player死亡信息

笨办法:

public class Player{
  void Die(){
    FindObjectOfType<Achievements>().OnPlayerDeath();
    FindObjectOfType<UIInterface>().OnPlayerDeath();
  }
}
public  class Achievements{
  public void OnPlayerDeath(){
  }
}
public  class UserInterface{
  public void OnPlayerDeath(){
  }
}

在这里相当于是Player做了一件事情,就去主动调用其他类。

  • 这样做会发生什么?
    后续当游戏系统复杂了,Player里头就会有一堆杂七杂八的引用。让Player的功能代码看起来不那么专一
    也会使得代码复用性变差,如果想在其他项目复用Player代码。首先得剔除所有无关引用,因为这些引用压根不在新项目中。

  • 那怎么做?
    让Player死亡时触发一个事件去通知所有关注它死亡的类。
    因为监听player是否死亡,实际上是其他想知道这个消息的类的职责

  • 因此正确的代码应该如下:

public class Player{
  public event Action deathEvent;   //通过事件来通知其他类
  void Die(){
    if( deathEvent != null){
      deathEvent();     //或者deathEvent.Invoke();
  }
}

public  class Achievements{
  void Start(){
    FindObjectOfType<Player>().deathEvent += OnPlayerDeath;  // 订阅
  }
  public void OnPlayerDeath(){
    // TODO Something ...
    // ....
    FindObjectOfType<Player>().deathEvent -= OnPlayerDeath; // 记得取消订阅
  }
}
public  class UserInterface{
  void Start(){
    FindObjectOfType<Player>().deathEvent += OnPlayerDeath;  // 订阅
  }
  public void OnPlayerDeath(){
    // TODO Something ...
    // ....
    FindObjectOfType<Player>().deathEvent -= OnPlayerDeath; // 取消订阅
  }
}
  • 另外:关于public event 后面跟的兼容类型,除了.Net给定的EventHandler,还可以用Action / Action<T> / Action<T1,T2> 、Func<ReturnT> / Func<T1,ReturnT>或是自定义的委托类型来约束。
  • 还有在唤醒事件的时候,一定要实现判空。

2020年9月23日
使用事件制作3D自动开关门(附:3D人物移动和旋转,out输出参数,3D搭建使用的快捷键..

  • 选中相机,Ctrl+Shift+F 相机角度自动变成Scene界面视角

  • 按住Ctrl拖拽会对其网格。网格与对齐捕捉设置:



  • 按住V顶点对齐

  • 在这期视频中找到了别的想要的,Tween系列插件——LeanTween

Tween 事实上就是一种数学库
能够让使用者更加方便的处理数学动画

  • 在AssetStore中下载免费版的之后,就可以用了。例如:
 LeanTween.moveLocalY( gameObject, to , time);

也可以用链式写法,设置运动过程中的速度曲线。例如:

 LeanTween.moveLocalY( gameObject, 3.0f , 1.0f).setEaseInSine();
  • 在OnDisable或者OnDestroy中记得取消订阅,不然会报错,而且导致内存过大。

  • 代码中用枚举类型定义一个字段的话,在Inspect界面会有下拉框。

  • Unity官方免费插件——ProGrids,方便搭建场景

  • Universal Render Pipeline

  • Unity官方免费插件——PostProcessing

2020年9月24日


2020年9月24日

排序、DotTween、LeanTween、十大Array用法、富文本、模板方法

  • 存入数据库的时间,要用国际协调时区DataTime.UtcNow

...我还是从第一集开始看吧
委托第一集


最近在研究NLP,还有浮岛星球的策划,还有大创的一些事情。
女朋友在准备研究生面试,明天就要开始了,默默支持一下,嘿嘿!


2020年9月28日


2020年10月16日
Unity 中制作虚拟摇杆


2020年10月18日


Model: 存数据,一般都是定义一些数据字段(比如:用户名称、设备缩放比、贴图材质、也可以有数据库增删改查操作这样的方法
View: 存放UI数据引用、事件监听
Control:实现业务逻辑功能,获取Model的数据,通知View层更新数据


// 以下说的都有点问题↓↓↓
// FIXUnityEvent中没有封装UnityAction字段

  • <封装了UnityAction委托字段的UnityEvent> 和 <C#中以关键字形式存在的event的区别>

UnityEvent中包含AddListener(UnityAction call)方法,Button.onClick就是UnityEvent类型的。
在AddListener中,会将参数call对应的函数引用,订阅到该UnityEvent对象中。

//目前还没找到AddListener内部机理。
// ↑↑↑


依赖倒置原则:高层次的模块不应该依赖于低层次的模块,两者都应该依赖于抽象接口。

题外话:
看到了评论区的争辩有点意思:


我觉得两个都说的挺对的,有时候我们需要事先预测一个需求的规模,也不是什么都要循规蹈矩。小的需求,做一个大的框架,确实要付出一些接口开发的成本。但是如果做的时候就考虑了后期要扩展这个功能,就需要想清楚了。
还有博主在这篇文章里说的最后一段,也挺有意思:

讲了这么多依赖倒置原则的优点,我们也来打击一下大家,在现实世界中确实存在着必须依赖细节的事物,比如法律,就必须依赖细节的定义。 “杀人偿命”在中国的法律中古今有之,那这里的杀人就是一个抽象的含义,怎么杀,杀什么人,为什么杀人,都没有定义,只要是杀人就统统得偿命,那这就是有问题了,好人杀了坏人,还要陪上自己的一条性命,这是不公正的,从这一点看,我们在实际的项目中使用依赖倒置原则时需要审时度势,不要抓住一个原则不放,每一个原则的优点都是有限度的,并不是放之四海而皆准的真理,所以别为了遵循一个原则而放弃了一个项目的终极目标:投产上线和盈利。作为一个项目经理或架构师,应该懂得技术只是实现目的的工具,惹恼了顶头上司,设计做得再漂亮,代码写得再完美,项目做得再符合标准,一旦项目亏本,产品投入大于产出,那整体就是扯淡!你自己也别想混得更好!


2020年10月19日

  • UnityEvent如果要带参数的话需要通过泛型的方式——UnityEvent<T0>,但这里有个坑,就是这个泛型的UnityEvent是抽象类,因此需要自己再定义一个类,去继承它。例如:
public class SelectContentEvent : UnityEvent<string>
 {
    //空
 }

还有一个和UnityEvent不一样的点——在声明事件中,需要new。例如:

public SelectContentEvent OnSelectContent = new SelectContentEvent();

2020年10月21日

  • 修改某些Unity组件的值的时候,要注意获取的对象是引用,还是拷贝。
    例如在Render类中有一个属性,它在get方法中做了处理,返回的是materials数组的一份拷贝
        public Material[] materials { get; set; }
  • 在Render组件的API中有如下说明:

Render.materials:

Note that like all arrays returned by Unity, this returns a copy of materials array. If you want to change some materials in it, get the value, change an entry and set materials back.

  • Unity封装的类里头,如果包含的属性是数组,都会返回一份拷贝。如果需要修改数组中的元素,只能通过改变数组的入口地址(为数组整体赋值)。

Unity Connect不做了

Hi JimmyZou,

Today we’re announcing that on February 4, 2021, Unity Connect, our dedicated talent and sharing marketplace, will shut down. We’re proud of the community that rallied around Connect and we are inspired by the great sharing and discovery that came from it.

We want to make this transition as smooth as possible for everyone who uses Connect. If you are an active user on the platform, click here to learn more about the alternatives that will be provided for some Connect features. If you would like to download any part of your profile, please feel free to do so before we delete your information on February 4, 2021.

Unity Connect就业信息网页:
https://connect.unity.com/jobs

posted @ 2020-09-08 21:35  JimmyZou  阅读(1)  评论(0编辑  收藏  举报  来源