unity3d 开发笔记

news2024/11/25 22:48:31

unity 3d

Unity是一个游戏引擎,包含渲染引擎,物理引擎,碰撞检测,音效,动画效果,场景管理等系统。它的开发效率高、脚本使用C#开发、简单易用、跨平台(可以导出各个平台的程序),别的游戏引擎比如虚幻引擎俗称UE (Unreal Engine,使用C++开发脚本,3A大作首选)。下边放一个Unity软件的大图。

在这里插入图片描述

介绍

  • project面板放游戏资源,hierarcy放游戏对象,inspector显示当前游戏对象和属性信息
  • component组件代表游戏对象的某个功能,所有游戏对象都有transform组件
  • 目录下的assets是最重要的,其他可以不要
  • 直接安装unity就可以,不用安装unity hub
  • ProjectSettings\ProjectVersion.txt 查看项目的unity版本
  • unity package file, unity可以直接导入,类似zip文件,可以右键export package生成,主要使用的unity版本尽量一致

操作

  • 最右上角,面板 layout,选择 2 by 3
  • project面板 右键 one column layout
  • Q + 鼠标左键(按鼠标滚轮),可以移动scene
  • alt + 右键,缩放视图
  • alt + 左(同只按右键),旋转视图
  • 右键 + w a s d q e,场景漫游(不同方向)
  • 选择物体,然后按F,视图以物体为中心来居中,或者在hierarcy双击游戏对象也可以
  • 选择游戏对象,ctrl + d 可以快速复制一个
  • z轴是正对人的放心,x轴是水平方向,y轴是垂直方向
  • 游戏运行时改的值,可以copy component,然后结束调试paste值即可。

视图模式

在这里插入图片描述

视图有两种模式,ISO正交模式(2d效果)和 Pers透视模式(近大远小效果)。

坐标系

  • 世界坐标,全局不变的坐标(只有唯一的原点)
  • 本地坐标,物体自身坐标,随着旋转而变化
    unit使用左手坐标系(左手做成数字8,中指指向自己即可)
    在这里插入图片描述

场景

场景 scene,是相关联的一组游戏对象的集合,比如一个地图或者游戏的某一关,在scenes文件下,后缀名是 .unity 双击此文件可打开项目。新建的游戏对象,默认在当前视图的场景中间。场景默认自带Main Camera,和Directional Light两个东西。

GameObject

游戏对象,hierachy中的每个对象都是GameObject的子类,都有transform属性。Plane只有正面没有反面,quad相似,只不过是竖着的。任何物体都是三角形拼出来的。

PreFab 预制件

如果在Hierachy面板中,要把一个游戏对象变成模板,直接拖到 Project面板即可,修改PreFab的属性就会影响所有的游戏对象。如果要批量创建模型,最好放到预制件里。
在这里插入图片描述

Scripts

创建脚本,不用的方法,最好删除。

mesh

网格,mesh filter可以改变物体的形状。
在这里插入图片描述
Mesh Renderer 渲染物体,可以改变材质,两个组合在一起才能显示物体。可以创建空物体,自行添加,这两个组件,让物体显示。
在这里插入图片描述

群组父子结构

如果要组合两个对象,在hierachy面板,一个拖到另一个就可以。也可以创建几个空模型作为父对象,其他的拖进来。这时候子模型的坐标就是相对坐标了,相对父模型的坐标,可以使用transform组件的reset功能重置,会更好。如果后续模型更新,如果把组件都挂在模型上,就要重新替换,所以建议新建一个空物体作为父模型,把所有行为都给父模型即可。

材质 material

物体的质地,比如色彩、纹理、透明度等,实际就是shader的实例。
纹理(texture)就是附加到物体表面的贴图。材质要给到 Mesh Renderer
在Assets目录下新建Textures目录,放置贴图图片,可以直接拖到game object上,unity会自动生成一个材质。
在这里插入图片描述
Albedo
基础贴图,决定物体纹理和颜色

Rendering mode

材质的渲染模式 ( Rendering mode ) 默认是 opaque(不透明的),fade(渐变,淡入淡出)

cutput (镂空,透明通道去掉,只剩下不透明的)
在这里插入图片描述

transparent(透明的,需要设置albedo的颜色的a值)
在这里插入图片描述

Metallic
使用金属模拟外观

Specular
镜面反射

Smothless
光滑度,设置物体表面光滑程度

Normal Map
法线贴图,物体表面凹凸程度

Emission
自发光,控制物体表面自发光颜色和贴图。

shader

材质的本质是shader的实例,shader是专门用来渲染3d图形的技术,可以使纹理以某种方式展现,就是一种嵌入到渲染管线的程序,控制GPU运行效果的算法。材质的属性和设置,是由shader决定的。

在这里插入图片描述

场景下的绘图模式

Shaded着色模式(默认模式)所有游戏对象的贴图都正常显示
Wireframe网格线框显示模式以网格线框形式显示所有对象
Shaded Wireframe着色模式线框以贴图加网格线框形式显示对象
Shadow Cascades阴影级联以阴影方式显示对象
Render Paths渲染路径显示模式以渲染路径的形式显示
Alpha ChannelAlpha通道显示以灰度图的方式显示所有对象
Overdraw以半透明方式显示以半透明的方式显示所有对象
MipmapsMIP映射图显示以MIP映射图方式显示所有对象

光照

平行光,像太阳,从一个方向过来照亮全景
点光,一个点发射光线,并减弱
聚光,某个方向照射,有范围限制
范围光,一个区域内的光,没有方向
实时光照和烘焙光照,后者就是开发时把光照计算好,在游戏运行时,直接使用图片产物,提升性能。
一般场景为了真实,会有几个光源,因为有漫反射的存在,没有绝对黑的物体的面。
很多游戏没有阴影,因为实时渲染影响非常消耗资源。
比如范围灯光,看不出效果,可以烘焙看一下,windows -> rendering -> lighting
在这里插入图片描述

粒子系统

可以实现硝烟,火焰,雪花,水汽,爆炸效果。右键Effects -> Particle System来创建。也可以从网上下载现成的使用。

脚本开发

  • Unity是一个Component-Based的引擎,所有物体都是GameObject。
  • Time,Input,Physics都是Unity中的全局变量。
  • MonoBehaviour 是 Unity 中所有脚本的基类。

基础

  • window -> general 显示 console 控制台
  • 关于注释, /// 会被编译,// 不会。所以使用///会减慢编译的速度(但不会影响执行速度)且在其它的人调用你的代码时提供智能感知。
  • #region 是 C# 预处理器指令。就是将复杂的代码块折叠,是界面看起来整洁一些。

GameObject和Component

GameObject是游戏场景中真实存在的,而且有位置的一个物件。Component附属于GameObject,控制GameObject的各种属性。GameObject是由Component组合成的,Component的生命周期和GameObject息息相关。调用此GameObject的Destroy方法,它的子对象和对应的所有Component都会被销毁,但也可以一次只销毁一个Component。常用的组件如下表所示

Component作用
RigidBody 刚体使物体能在物理控制下运动
Collider 碰撞器和RigidBody刚体一起使碰撞发生,没有Collider,两个碰撞的刚体会相互穿透
Renderer 渲染器使物体显示在屏幕上
AudioSource 音频源使物体在scence场景播放音频
Animation 动画
Animator 动画控制器

脚本也是组件,因此能附到游戏对象上。常用的组件可以通过简单的成员变量获取,附在游戏对象上的组件或脚本可以通过GetComponent获取。

using UnityEngine;
using System.Collections;

public class example : MonoBehaviour {
    void Awake() {
        transform.Translate(0, 1, 0);
        GetComponent<Transform>().Translate(0, 1, 0);
    }
}

语法结构

  • 文件名必须和类型相同,首字母大写
  • 脚本必须附加到物体上才可以执行
  • 附加到物体上的类必须继承 MonoBehaviour
  • 删除类中没用的空方法
public class WeatherParticle : MonoBehaviour

下边是一个脚本的demo

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Ball : MonoBehaviour
{
    // 游戏开始运行的时候执行,适合做初始化
    void Start()
    {
        Debug.Log("组件执行开始!");
        transform.position = new Vector3(1, 2.5f, 3);
    }
 
    // 每帧都会执行,不同设备的频率不一样
    void Update()
    {
        Debug.Log("当前游戏进行时间:" + Time.time);
        // 每次帧移动 1.5 
        transform.Translate(1.5f, 0, 0)
        // 如果要保证每秒移动距离相同,Time.deltaTime是两个帧的间隔
   		transform.Translate(1.5 * Time.deltaTime, 0, 0)
    }
}

获取位置

// 获取纵轴输入和横轴输入
// wsad和上下左右键的输入
float v = Input.GetAxis("Vertical");
float h = Input.GetAxis("Horizontal");
Debug.Log("纵轴输入"+v+"横轴输入" + h);
transform.Translate(0, h * 3 * Time.deltaTime,  v * 3 * Time.deltaTime);

触发和碰撞

// 1. 增加一个立方体,在 box collider上勾选 is trigger
// 2. 给球体增加一个刚体组件,(add component -> pyhsicis -> rigidbody)勾选 
// 3. 使用前边的小球撞立方体,给立方体挂一个脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Coin : MonoBehaviour
{
    //触发开始事件OnTriggerEnter
    private void OnTriggerEnter(Collider other)
    {
        Debug.Log(other.name + "碰到了");
    }
    private void OnTriggerStay(Collider other)
    {
        Debug.Log(other.name + "碰撞持续中");
    }
    private void OnTriggerExit(Collider other)
    {
        Debug.Log(other.name + "碰撞结束");
    }
}

编译过程

源代码 - CLS - 中间语言 - Mono Runtime - 机器码

修改默认cs模板

打开编辑器的目录,比如我的是
D:\apps\unity-editor\2021.3.29f1c1\Editor\Data\Resources\ScriptTemplates
打开 81-C# Script-NewBehaviourScript.cs.txt 修改即可

脚本生命周期

也叫必然事件/消息,脚本从唤醒到销毁的过程。附加到游戏对象上,控制游戏行为。
在class中声明的可见的成员变量(private不可见),在unity软件中可见也可以修改,并且这个优先级高,以为在unitu中这是一个对象。不要在脚本中写构造函数

public class Demo1 : MonoBehaviour
{ 
	// 加上这个,虽然是public在unity也不可见
    [HideInInspector]
    public float speed = 10;
    
    // 虽然私有,但是在unity可见
    [SerializeField]
    private float speed1 = 10;

	// 字段定义数值范围 
	[Range(0, 100)]
    public float speed2 = 10;

  	// 执行时机,渲染帧执行,每次渲染时执行,执行间隔不固定和设备性能和渲染量有关
    // 适用性: 处理游戏逻辑
    void Update()
    {
        float v = Input.GetAxis("Vertical");
        float h = Input.GetAxis("Horizontal");
        transform.Translate(h * speed * Time.deltaTime, 0, v * speed * Time.deltaTime);
    }


    // 固定时间更新,
    // 适用性:适用游戏对象做物理操作,比如移动等,默认为0.02s,不会受到渲染影响
    private void FixedUpdate()
    {


    }

    // 延迟更新,适用于跟随逻辑
    private void LateUpdate()
    {

    }



    // 开始时被调用
    // 创建游戏物体,立即执行
    private void Awake()
    {
        Debug.Log("awake" + Time.time);
        print("lll");
    }

    // Awake之后执行
    // 创建游戏物体,脚本启用才执行
    // 如果此脚本被挂在多个游戏对象上,先执行所有的awake,再执行所有的start
    // this.name 游戏对象的名字
    private void Start()
    {

        print(this);
        speed = 10;
        Debug.Log("Start" + Time.time + this.name);

        // unity内置随机数
        int b = Random.Range(0, 100);

        int a = 0;

        while(a < -1){
            //我们将obj1初始化为一个Cube立方体,当然我们也可以初始化为其他的形状
            GameObject obj1 = GameObject.CreatePrimitive(PrimitiveType.Cube);
            //设置物体的位置Vector3三个参数分别代表x,y,z的坐标数
            obj1.transform.position = new Vector3((float)(1 +  a * 1.3),1,1);
            //给这个创建出来的对象起个名字
            obj1.name = ("dujia" + a);
            a++;
        }


		//设置物体的tag值,在赋值之前要在Inspector面板中注册一个tag值
		//注册tag值得方法,用鼠标选中摄像机对象在Inspector面板中找到tag,选addtag
		// obj1.tag = "shui";
		//设置物体贴图要图片文件放在(Resources)文件夹下,没有自己创建
		// obj1.renderer.material.mainTexture = (Texture)Resources.Load("psb20");

    }

    private void OnMouseEnter() {
          Debug.Log("OnMouseEnter");
    }

    private void OnMouseExit() {
          Debug.Log("OnMouseExit");
    }

    private void OnMouseDown() {
        // print(this.activeSelf);
        print("OnMouseDown");
    }
 
    private void OnMouseUp() {
          Debug.Log("OnMouseUp");
    }

    // 当物体在相机不可见时
    private void OnBecameInvisible() {
        
    }

    // 当物体在相机可见时
    private void OnBecameVisible() {
        
    }

    // 销毁时
    private void OnDestroy() {
        
    }

    // 程序结束时
    private void OnApplicationQuit() {
        
    }
}

在这里插入图片描述

debug

  • 这两种log是等价的,在继承monohavior的情况下,测试环境可以加,生产环境需要删除。
  private void Awake()
   {
       Debug.Log("awake" + Time.time);
       print("123");
   }
  • 使用类的成员变量,在inspector面板可以实时看到这个数据的变化。

重要的类

在这里插入图片描述

Component

public class ComDemo : MonoBehaviour
{
    // OnGUI是Unity中通过代码驱动的GUI系统
    // 主要用来创建调试工具、创建自定义属性面板、创建新的Editor窗口和工具达到扩展编辑器效果
    private void OnGUI() {
        // 左上角创建一个按钮
        if(GUILayout.Button("click")){
            // 点击按钮的行为
            print("ok");
            this.GetComponent<MeshRenderer>().material.color = Color.blue;
            // 自定义颜色
            // this.GetComponent<MeshRenderer>().material.color = new Color(0.3f, 0.4f, 0.6f, 0.3f);

            // 获取所有的组件
            var all = this.GetComponents<Component>();

            foreach (var item in all)
            {
                print(item.GetType());
            }

            // 深度优先搜索所有的子组件
            // var allAndChildren = this.GetComponentsInChildren<MeshRenderer>();

        }
    }
}

Transform

public class TransformDemo : MonoBehaviour
{
    void OnGUI()
    {
        if (GUILayout.Button("transform"))
        {
            // 每个子物体的变化组件
             foreach (Transform c in transform)
             {
                 print(c.name);
             }


            // 世界坐标系坐标位置
            print(this.transform.position);
            // 相对于父轴心点的位置
            print(this.transform.localPosition);
            // 相对于父的缩放
            print(this.transform.localScale);

            /**
             移动
             */
            // 自身坐标系z轴每次移动1米
            this.transform.Translate(0, 0, 1);
            // 世界坐标系
            this.transform.Translate(0, 0, 1, Space.World);

            /**
             旋转
             */
             this.transform.rotation = Quaternion.Euler(3.0f, 1.2f, 1.0f);

            // 自身坐标系的y轴旋转10度
             this.transform.Rotate(0,10,0);
             this.transform.Rotate(0,10,0, Space.World)

            // 围绕世界坐标点旋转,y轴,1度
            this.transform.RotateAround(Vector3.zero, Vector3.up, 1);

            // 获取父物体变换组件
            this.transform.parent
            // 是最高层级的transform
			this.transform.root
			
            // 设置父物体变换组件
            this.transfrom.setParent(this.transform.root, true)

            // 根据名称查找子物体的transform对象
            Transform tf1 = this.transform.Find("trans1");

            // 查找孙子路径
            Transform tf11 = this.transform.Find("trans1/transf2");

            int count = this.transform.childCount;
            print(count);
            for(int i=0;i < count; i++){
                print(transform.GetChild(i));
            }
        }
    }
}

gameObject的激活状态

在这里插入图片描述
勾上就是激活,不勾上就是不激活状态,可以用setActivate来设置,不激活就是不在场景显示了。this.activeSelf是自己的激活状态,但是如果父物体不是,那也不是。所以使用 activeInHierarchy来判断最终状态。

public class GameObject1 : MonoBehaviour
{
    void OnGUI()
    {
        if (GUILayout.Button("GameObject1")) 
        {
            print(this.gameObject.activeSelf);
            print(this.gameObject.activeInHierarchy);
            
            // this.gameObject.SetActive(false);

            // 所有的组件不能new
            Light light = this.gameObject.AddComponent<Light>();
            light.color = Color.red;
            light.type = LightType.Point;

            // 使用标签或者游戏物体列表
            GameObject[] gbs = GameObject.FindGameObjectsWithTag("Player");
            GameObject gb = GameObject.FindWithTag("Player");
            print(this.gameObject.name);
            
            // 5s后销毁游戏对象
			Destroy(this.gameObject, 5);

        }
    }
}

InputManager(旧输入系统)

可以获取用户的输入事件

using UnityEngine;

// 这个就会在 add component下显示一个Demo2222的文件夹,名字是input111
// 用于定义在unity中的别名
// 这种写法是C#的特性
[AddComponentMenu("Demo2222/input111")]
public class InputDemo : MonoBehaviour {
  void Start() {
    
  }

  void Update() {
    if (Input.GetKey(KeyCode.A)) {
      print("Pressed A.");     
    }
    
    if(Input.GetKeyDown(KeyCode.W)){
      print("Pressed Down w.");
    }
    
    if (Input.GetMouseButton(0)){
      print("Pressed left click.");
    }

    if (Input.GetMouseButton(1)){
      print("Pressed right click.");
    }

    if (Input.GetMouseButton(2)){
      print("Pressed middle click.");
    }
    // Fire1的定义如下所示
    if (Input.GetButton("Fire1")) {
       print("fire1");
    }
    // 这种down的执行的次数少
    if (Input.GetButtonDown("Fire1")) {
      print("fire1 down");
    }
    
    // 参数值也是标准输入选的,在setting里
    // print(Input.GetAxis("Horizontal"));
    // 没有中间过程的值,比如按下 w使得 0 - 1 上边方法可能有小数,这个要么0要么1
    // print(Input.GetAxisRaw("Horizontal"));
    
    // 1 
    print(Input.GetAxis("Fire1"));

  }
}

Edit -> Project Settings -> Input Manager 有一些输入的自定义设置,
在这里插入图片描述
在这里插入图片描述

InputSystem(新输入系统)

2019年推出的系统,设备和动作分离,比老板复杂,分为Input Action、input signal bindings、Devices。
在这里插入图片描述
这里设置为新输入系统。
在assets目录新建settings目录,再新建 Input Actions
在这里插入图片描述
点击 edit asset
在这里插入图片描述
创建一个移动的action,配置 action type是value,control type是 vector2,并添加WASD的键盘绑定。
在这里插入图片描述

创建 control schema
在这里插入图片描述
并在这里勾选
在这里插入图片描述
也可以让unity自动生成配置,在add component的列表中
在这里插入图片描述
这里Behavior可以选择 Invoke Unity Events,可以绑定脚本事件
在这里插入图片描述
也可以输入代码方式,调用 PlayerInput
在这里插入图片描述

using UnityEngine;
using UnityEngine.InputSystem;

public class InputDemo : MonoBehaviour {
  
  // PlayerControl 是自动生成的脚本的名字
  public PlayerControl inputControl;

  public Vector2 dir;
  
  void Awake() {
    inputControl = new PlayerControl();
  }

  private void OnEnable() {
    inputControl.Enable();
  }

  private void OnDisable() {
    inputControl.Disable();
  }

  void Update() {
    dir = inputControl.Player.Move.ReadValue<Vector2>();
    print(dir.x);
  }


  public void Move1() {
    print("moving....");
  }
}

时间

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

// Time.deltaTime -> Time.unscaledDeltaTime 
// Time.time -> Time.unscaledTime === Time.realtimeSinceStartup (游戏真实运行时间)
public class TimeDemo : MonoBehaviour
{

    // Update不受timeScale影响,但是加上了Time.deltaTime就被影响了
    // unscaledDeltaTime不受缩放影响的每帧间隔
    private void Update()
    {
       // this.transform.Rotate(0, 100 * Time.deltaTime, 0);
       // this.transform.Rotate(0, 100 * Time.unscaledDeltaTime, 0);
    }


    private void FixedUpdate() {
        // this.transform.Rotate(0, 100, 1);
    }

    private void OnGUI()
    {
        if (GUILayout.Button("Time"))
        {
            // 游戏开始的秒数
            print(Time.time);

            // 每帧的间隔时间  0.004
            print(Time.deltaTime);

            // 可以保证在机器性能不一样的情况下,保存转速恒定
            // 机器好的情况下,渲染速度快 Time.deltaTime小,反之,渲染速度慢,Time.deltaTime大
            this.transform.Rotate(0, 100 * Time.deltaTime, 1);

        }

        if (GUILayout.Button("暂停游戏"))
        {
            Time.timeScale = 0;
        }
        if (GUILayout.Button("继续游戏"))
        {
            Time.timeScale = 1;
        }
    }
}


资源加载

资源要放到Assets/Resources目录, 使用 Resources.Load 方法加载,如果是加载FBX模型是GameObject对象,但是不会再Hierachy面板出现,需要Instantiate复制一个实例才能显示出来。

public class ResourcesLoad : MonoBehaviour
{
    // 按下w播放音乐
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.W))
        {
            // ,不需要写扩展名
            var music = Resources.Load("Audios/bg");
            AudioSource.PlayClipAtPoint(music as AudioClip, Camera.main.transform.position);
        }
    }
}

场景切换

新建两个场景 Scene1 和 Scene2,拖拽到BuildSetting面板中,注意,这个顺序就显示的顺序
在这里插入图片描述

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

public class ToggleScene : MonoBehaviour
{
    public void Scene1(){
		// 新版切换场景方法
        SceneManager.LoadScene("Scene1");
        // 老版切换场景方法,已废弃
        Application.LoadLevel("Scene1");
    }

    public void Scene2(){
        SceneManager.LoadScene("Scene2");
    }

}

音频

一般都是 MP3和WAV文件,AudioSource播放组件,AudioListener收听音乐,摄像机默认都有一个AudioListener
在Camera新增一个AudioSource,并添加音乐到 AudioClip中。
在这里插入图片描述


public class AudioControl : MonoBehaviour
{

    private AudioSource a;

    void Start()
    {
        this.a = GetComponent<AudioSource>();
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Q))
        {
            // this.a.Play();
            // 剪辑,位置,音量
            AudioSource.PlayClipAtPoint(this.a.clip, Camera.main.transform.position, 0.1f);
        }
    }
}

动画 Animation

  • unity的老版本只有Animation组件,在4.6版本以后则增添了Animator。
  • 只是控制一个动画的播放则用Animaton组件,如果是很多动画之间相互转换则使用Animator组件,两者的区别就是Animator有一个动画控制器(俗称动画状态机),使用它来进行动画切换是非常方便的,但缺点是占用内存比Animaton组件大。
  • 动画结束后,可以添加事件(挂载的游戏对象的脚本),可以通过这个特性,使得很多动画循环播放

动画的使用方式如下。

  1. 新建一个游戏物体
  2. 在游戏物体添加Animation组件
    在这里插入图片描述
  3. 打开Animation面板 window - Animation
  4. 选中游戏对象,在游戏面板中,点击add property,0:10代表1秒10帧(60帧为1秒)。
    在这里插入图片描述
    点击红点,红点背景是红色而且右边有红条,代表录制中
    在这里插入图片描述

也可以点击curves,直接操作曲线
在这里插入图片描述

选中右侧的点,录制开始和结束状态,就可以播放动画了,左键+alt可以拖动动画面板

在这里插入图片描述
录制好之后,在inspector面板 Animation组件的Animation属性,选中这个动画就可以播放了。
在这里插入图片描述
新建脚本


public class DoorControl : MonoBehaviour{

    public bool open = false;

    public Animation animation1;

    public string animationName = "Door";

    void Start(){
      this.animation1 = this.GetComponent<Animation>();
    }

    void OnMouseDown(){
      if(!this.animation1.isPlaying){
        if(open){
            // 倒着播放
            this.animation1[animationName].speed = -1;
            // 没有这句话,就是 0 - 0了,门瞬间关闭了,从最后开始播放
            this.animation1[animationName].time =this.animation1[animationName].length;
         }else {
            this.animation1[animationName].speed = 1;
         }
         this.animation1.Play(animationName);
         open = !open;
      }
    }
}

动画播放模式

loop就是循环播放,once一次,pingpang就是来回回来播放,clapforver固定播放到最后一帧(一直播放中)
在这里插入图片描述

动画 Animator

动画机含有多个动画的片段,并使用controller通过parameter控制。可以在Project面板右键Animator Controller来创建。
右键 Make transition可以连到下一个动画,点击线可以添加切换的条件,连线可以选中点击delete删除,条件的值可以在脚本设置。

  • 选中游戏对象,添加组件,添加Animation,会自动创建 Animator的
  • 选中游戏对象,菜单window,Animation,会自动打开,这个游戏对象上的动画
    在这里插入图片描述

   

PlayerPrefs本地存储

类似浏览器的LocalStorage,但是不存在文件系统,只有游戏卸载了才不存在。

public class PlayerPrefsTest : MonoBehaviour
{
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Z))
        {
            PlayerPrefs.SetInt("level", 11);
            PlayerPrefs.SetFloat("level1", 11f);
            PlayerPrefs.SetString("level11", "11");
        }
    }

    private void OnMouseDown()
    {
        print(PlayerPrefs.GetInt("level"));
    }
}


协同 Coroutine

当前代码,在等待某些资源条件好的时候,在未来执行。

public class CoRoutineTest : MonoBehaviour
{
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.L)){
           StartCoroutine(play1());
        }
    }
	
	// 2秒后再print(2)
    IEnumerator play1(){
      print(1);
      yield return new WaitForSeconds(2);
      print(2);
    }
}


www类

基于HTTP的网络传输功能,需要结合协同使用。

public class WWWDemo : MonoBehaviour
{

    private Texture2D pic;

    void Start(){
        
    }

    void Update() {
      if(Input.GetKeyDown(KeyCode.O)){
         StartCoroutine(DrawPic());
      }
    }

    void OnGUI(){
        if(pic != null){
          GUI.DrawTexture(new Rect(0,0,100,100), pic);
        }
    }

    IEnumerator DrawPic(){
      WWW w3 = new WWW("https://img0.baidu.com/it/u=3111329697,2164934529&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=313");
      yield return w3;
      pic = w3.texture;
    }
}


UnityWebRequest

可以用WWW或者HttpWebRequest来实现文件的下载。因为WWW不存在设置timeout属性,因此当我们网络不好请求超时的时候,无法简单的做出判断。当网络极差的时候,游戏下载将会停止(即一直在等待yield return www)当时间较长时网络恢复将无法继续下载,也没有提示,需要重启才能重新下载。Unity早在5.4版本的时候就出了新的API UnityWebRequest用于替代WWW。

using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

public class HttpTest : MonoBehaviour
{
  private string jsonUrl = "http://localhost:8888/demo.json";

  private void Start()
  {
    StartCoroutine(Get());
  }

  IEnumerator Get()
  {
    UnityWebRequest request = UnityWebRequest.Get(jsonUrl);
    yield return request.SendWebRequest();
    if(request.isHttpError || request.isNetworkError)
    {
      Debug.LogError(request.error);
    }
    else
    {
      string receiveContent = request.downloadHandler.text;
      Debug.Log(receiveContent);
    }
  }
}

人称设置

第一人称,把camera直接拖到物体内部并且position一致。
第三人称,把camera直接拖到物体内部而且位置不一样,可以看到自己。
如下,实现了前后左右移动的效果

public class CubeMove : MonoBehaviour
{

    private float speed;

    private float angleSpeed;

    void Start(){
        speed = 1f;
        angleSpeed = 5f;
    }

    void Update() {
        transform.Translate(Vector3.forward * speed * Time.deltaTime * Input.GetAxisRaw("Vertical"));
        transform.Rotate(Vector3.up, Input.GetAxisRaw("Horizontal") * angleSpeed * Time.deltaTime );
    }
}

Streaming Assets

Unity 中的大多数资源在构建时都会合并到项目中。但是,将文件放入目标计算机上的普通文件系统以使其可通过路径名访问有时会很有用。

物理引擎

模拟真实事件的物体碰撞,跌落,使用了 nvida 的physX。新建cube,add component,选择 Physics给cube添加一个刚体,
Rigidbody刚体表示受力的作用,Collider表示使用碰撞检测。

using UnityEngine;

namespace Scene4 {
  public class RigidControl : MonoBehaviour {


    private Rigidbody obj;
    
    private void Start() {
      obj = GetComponent<Rigidbody>();
    }
    
    private void Update() {
      if (Input.GetKeyDown(KeyCode.A)) {
        // 给物体添加一个右上方的力, Impulse是冲击力,瞬间爆发
        obj.AddForce(new Vector3(1, 1, 0) * 10, ForceMode.Impulse);
      }
      
      if (Input.GetKeyDown(KeyCode.Q)) {
        // 给物体添加一个右上方的力, 牵引力,不停的按下才可以
        obj.AddForce(new Vector3(1, 1, 0) * 100, ForceMode.Acceleration);
      }

      if (Input.GetKeyDown(KeyCode.W)) {
        // 直接给物体一个速度
        obj.velocity = new Vector3(1, 1, 0) * 3;
      }
    }
    
  }
}


碰撞检测

地板Plane都有默认的 Mesh Collider,发生碰撞的条件是两个物体之间都有Collider并且至少一方有刚体。Box Collider也有材质但是和MeshRenderer的材质(渲染材质)不一样,这个叫做物理材质(不同的物理属性)。可以使用右键 Physics Material 创建物理材质。

在这里插入图片描述
面板中的属性依次是动摩擦系数、静摩擦系数、弹性(物体落下弹起的效果)、摩擦系数合并(比如取最大值)

在这里插入图片描述
可以调整碰撞体的大小(不接触物体但是能碰到的效果)。

is Trigger 勾选了就是trigger触发器,没有力的作用(比如小球和地面,勾选了直接就穿过了地面)。

碰撞有2种消息(trigger的和非trigger的),3种状态(Enter、Stay、Exit)。


// 被碰到的物体的名字
private void OnCollisionEnter(Collision c) {
  print("OnCollisionEnter" + c.gameObject.name);
}

private void OnCollisionStay() {
  print("OnCollisionStay");
}

private void OnCollisionExit() {
  print("OnCollisionExit");
}

private void OnTriggerEnter() {
  print("OnTriggerEnter");
}
    

射线

射线是一个点向另一个点发射一条线,一旦和其他的模型发生碰撞,就停止发射。射线是摄像机发出的。
鼠标点击一个点,摄像机和这个点连线,之后和哪个物体发生了碰撞就是点击了哪个物体。

using UnityEngine;

public class RayTest : MonoBehaviour {
    
    // 游戏对象 添加组件 LineRenderer
    private LineRenderer line;

    private void Start() {
        line = GetComponent<LineRenderer>();
    }

    private void Update() {
        if (Input.GetMouseButtonDown(0)) {
            if (Camera.main) {
                print(Input.mousePosition);
                Ray r = Camera.main.ScreenPointToRay(Input.mousePosition);
                RaycastHit hit;
                // 如果和某物体发生了碰撞, 100内,第4个参数是layer,默认所有层,这个层是二进制 1 << 8 代表第8层
                if (Physics.Raycast(r, out hit, 100f)) {
                    // 碰撞的点
                    print(hit.point);
                    // 碰到的物体
                    print(hit.collider.gameObject);
                    line.enabled = true;
                    line.SetPosition(1, hit.point);
                    // 销毁碰到的物体
                    Destroy(hit.collider.gameObject);
                }
                else {
                    line.enabled = false;
                    print("没碰撞");
                }
            }
        }
    }
}



角色控制器

主要应用到会动的游戏物体上,不会动的就用刚体就好了,也不需要Collider来碰撞检测了。
在这里插入图片描述
前两项是坡度(超过这个度数爬不了)和最小位移,角色控制器默认给加胶囊体。


using UnityEngine;

public class MoveMove : MonoBehaviour {

    private CharacterController controller;

    private Camera main;

    private float speed;
    void Start() {
        controller = GetComponent<CharacterController>();
        speed = 10000f;
        main = Camera.main;
    }

    void Update() {
        // SimpleMove 有重力的作用,move没有
        // controller.SimpleMove()
   
        // 这个是世界坐标系
        // controller.SimpleMove(Vector3.forward * speed *  Time.deltaTime * Input.GetAxisRaw("Horizontal"));
        
        // 推荐使用本地坐标系,往自己的前方走
        controller.SimpleMove(transform.forward * speed *  Time.deltaTime * Input.GetAxisRaw("Horizontal"));
    }

    private void LateUpdate() {
        // 摄像机跟随
        // main.transform.position = transform.position + new Vector3(0, 0, -1f);
    }
}

Layer层

在这里插入图片描述
在这里插入图片描述
最多32个层,内置0-7改不了,是二进制表示 1 << 8代表第8层。

地形

GameObject Terrain,可以创建一些自定义的山啊什么的,一般不使用,一般使用自定义地形。

Gizmos 辅助线框类

在这里插入图片描述
点击可以切换是否显示,点箭头可以展开更多选项
在这里插入图片描述

导航 navigation

导航就是任务行走路线,也可以自动避开一些障碍物。菜单windows -> AI -> navigation打开面板。
在这里插入图片描述
创建一个这样的地形,选中所有的物体,然后
在这里插入图片描述

在这里插入图片描述
给运动的物体,添加一个导航组件
在这里插入图片描述
给物体绑定脚本

using UnityEngine;
using UnityEngine.AI;

public class NavController : MonoBehaviour {

    private NavMeshAgent a;
    
    void Start() {
      a = GetComponent<NavMeshAgent>();
    }

    void Update() {
      if (Input.GetMouseButtonDown(0)) {
        var r = Camera.main.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;
        if (Physics.Raycast(r, out hit)) {
          var p = hit.point;
          a.SetDestination(p);
          
        }
      }
    }
}


动态障碍物

给中间的门去掉 static navigation,并且添加 Nav Mesh Obstacle组件
在这里插入图片描述

网格链接(就是导航区域可链接起来)
比如可以从高台跳下去
首先,选中可以跳的物体勾选上
在这里插入图片描述
然后设置高度(下边选项是跳跃距离),并重新烘焙即可
在这里插入图片描述

如果有两个特定地方,需要链接,首先在场景创建两个地点cube。然后添加 Off Mesh Link 组件

在这里插入图片描述

练习 demo

循环创建几个Cube

int a = 0;

while(a < -1){
   //我们将obj1初始化为一个Cube立方体,当然我们也可以初始化为其他的形状
   GameObject obj1 = GameObject.CreatePrimitive(PrimitiveType.Cube);
   //设置物体的位置Vector3三个参数分别代表x,y,z的坐标数
   obj1.transform.position = new Vector3((float)(1 +  a * 1.3),1,1);
   //给这个创建出来的对象起个名字
   obj1.name = ("dujia" + a);
   a++;
}

获取其他游戏对象脚本变量的值

  
GameObject gb = GameObject.FindWithTag("Enemy");
print(gb.name);
print(gb.GetComponent<Enemy>().money);

查找所有敌人的血量

Enemy .cs

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

public class Enemy : MonoBehaviour
{
    public int money = 100;
}


新建两个 Enemy0和Enemy1的游戏对象,并绑定上述脚本

public class GameObject1 : MonoBehaviour
{
    void OnGUI()
    {
        if (GUILayout.Button("GameObject1"))
        {

            // 单个敌人
            GameObject gb = GameObject.FindWithTag("Enemy");
            print(gb.name)
            print(gb.GetComponent<Enemy>().money);

            // 如果有多个
            GameObject[] gbs = GameObject.FindGameObjectsWithTag("Enemy");
            for (int i = 0; i < gbs.Length; i++)
            {
                print(gbs[i].GetComponent<Enemy>().money);
            }

            Enemy[] all = Object.FindObjectsOfType<Enemy>();
            for (int i = 0; i < all.Length; i++)
            {
               if(all[i].money < 60){
                  all[i].GetComponent<MeshRenderer>().material.color = Color.red;
               }
            }

        }
    }
}

层级未知查找子物体

public class TransFormHelper
{
    // 层级未知查找子物体
    public static Transform Getchild(Transform parent, string kidName)
    {
        Transform childTransForm = parent.Find(kidName);
        if (childTransForm != null)
        {
            return childTransForm;
        }
        int count = parent.childCount;
        for (int i = 0; i < count; i++)
        {
            Transform ct = Getchild(parent.GetChild(i), kidName);
              if (ct != null)
            {
                return ct;
            }

        }
        return null;
    }

}

// 使用
Transform tf = TransFormHelper.Getchild(this.transform, "trans1");
tf.GetComponent<MeshRenderer>().material.color = Color.red;

// 判断两个物体的距离
print(Vector3.Distance(this.transform.position, tf.position));

实现倒计时

在Hierachy面板,右键UI选择Text,新建文本。注意,TextMeshPro 是 Unity 的最终文本解决方案。它是 Unity UI Text 和旧版 Text Mesh 的完美替代方案。

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

public class Counter : MonoBehaviour{

  public int second = 120;

  private float nextTime = 1;

  private float totalTime = 0;

  private TMPro.TextMeshProUGUI text1;

  void Start(){
    this.text1 = this.GetComponent<TMPro.TextMeshProUGUI>();
    // 从1s开始重复调用,间隔也是1s
    InvokeRepeating("TimerFn3", 1, 1);

    // 3s后执行
    // Invoke("TimerFn3", 3);
  }


  void TimerFn3() {
    if (second > 0){
        second--;
        var res = string.Format("{0:d2}:{1:d2}", second / 60, second % 60);
        this.text1.text = res;
        if (second < 10){
          this.text1.color = Color.red;
        }
      }else {
        CancelInvoke("TimerFn3")
      }
  }

	// 适合发射子弹
  void TimerFn() {
    if (Time.time > nextTime) {
      if (second > 0){
        second--;
        var res = string.Format("{0:d2}:{1:d2}", second / 60, second % 60);
        this.text1.text = res;
        nextTime = Time.time + 1;
        if (second < 10){
          this.text1.color = Color.red;
        }
      }
    }
  }

  void TimerFn2(){
    totalTime += Time.deltaTime;
    if (totalTime >= 1) {
      totalTime = 0;
      if (second > 0){
        second--;
        var res = string.Format("{0:d2}:{1:d2}", second / 60, second % 60);
        this.text1.text = res;
        if (second < 10){
          this.text1.color = Color.red;
        }
      }
    }
  }

  void Update(){
    // this.TimerFn2();
  }
}

打箱子游戏

新建9个箱子,1个子弹,都做成预制体

新建一个空的游戏对象,加下面的脚本

using UnityEngine;

public class HitBox : MonoBehaviour {

    // 定义为public,可以在软件中看到,并且可以直接预制体拖过去就可以
    public GameObject Box;
    
    public GameObject Bullet;

    public Texture2D pointer;

    private float FireTime1, FireTime2;
    
    private void Start() {
        for (int i = 0; i < 5; i++) {
            for (int j = 0; j < 5; j++) {
                var box = Instantiate(Box);
                box.transform.position = new Vector3(-2+i, 0.5f+j, 4.5f);
            }
        }

        FireTime1 = 0.5f;
        FireTime2 = 0;
        Cursor.SetCursor(pointer, new Vector2(pointer.width / 2,pointer.height / 2), CursorMode.Auto);
    }
    
    private void Update() {
        if (Input.GetButton("Fire1") && Camera.main) {
            FireTime2 += Time.deltaTime;
            if (FireTime2 >= FireTime1) {
                FireTime2 = 0;
                var bullet = Instantiate(Bullet);
                bullet.transform.position = Camera.main.transform.position;
                var r = Camera.main.ScreenPointToRay(Input.mousePosition);
                RaycastHit hit;
                // 这样会出来很多小球
                if (Physics.Raycast(r, out hit, 50)) {
                    bullet.GetComponent<Rigidbody>().velocity = (hit.point - bullet.transform.position) * 10;
                }
                else {
                    print("没碰撞");
                }
            }
        }
    }
}


当子弹和箱子不可见时,需要销毁,所以给它们附加如下的脚本

using UnityEngine;

public class DistroyOBj : MonoBehaviour
{
    private void OnBecameInvisible() {
        Destroy(this.gameObject);
    }
}

可以设置摄像机的远方距离,做一下优化
在这里插入图片描述
下载一个瞄准的图片素材,变成cursor
在这里插入图片描述

参考资料

  • 【Unity自制手册】unity常用API大全——一篇文章足以(万字详解)
  • Unity 引擎渲染技术学习极简路线图
  • 视频教程
  • 详解Unity入门之GameObject
  • 详解Unity中的ShaderGraph入门使用教程
  • Unity3D开发教程:愤怒的小鸟
  • Unity性能优化 - 脚本篇
  • 第一天 初识Unity脚本
  • unity 发送消息与web前端交互
  • unity预处理器指令
  • Unity Input System 新输入系统的功能及用法介绍
  • Unity神奇的Gizmos 辅助线框
  • 通过Mixamo生成人物动画并导入Unity实现资源可用的方法
  • transform.LookAt参数详解
  • Unity随记(一)LookAt和LookRotation的使用
  • Unity 基础 之 Vector3介绍
  • Transform.Forward和Vector3.Forward的正确使用方法
  • unity与vue交互(无第三方插件)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1169752.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

abbyy15有哪些新功能更新改进?

可以说优化文档过程&#xff1a;ABBYY在一个工作流中对各种类型的文档进行数字化、检索、编辑、保护、共享和协作。不得不承认FineReader 15最大特色是采用了ABBYY最新推出的基于AI的OCR技术&#xff0c;能够更快速的在同一工作流程中对各种文档进行数字化、检索、编辑、加密、…

【原创】java+swing+mysql汽车租赁管理系统设计与实现

摘要&#xff1a; 汽车租赁管理系统是一个综合性的系统&#xff0c;旨在实现汽车租赁过程的自动化和优化。它涵盖了从客户预订、车辆管理&#xff0c;通过设计和实现汽车租赁管理系统&#xff0c;可以提高汽车租赁公司的运营效率和服务质量&#xff0c;降低运营成本&#xff0…

45基于matlab的ARIMA:AutoregressiveIntegratedMovingAverage model。

基于matlab的ARIMA&#xff1a;AutoregressiveIntegratedMovingAverage model。自回归差分移动平均模型(p,d,q)&#xff0c;AR自回归模型&#xff0c;MA移动平均模型&#xff0c;时间序列模型步骤包括&#xff1a;1. 数据平稳性检验&#xff1b;2. 确定模型参数&#xff1b;3. …

快速了解相似检索方法

一、相似检索方法总体分析 相似检索方法是一种用于从大量数据中找到与查询数据相似的数据项的技术。这种方法通常用于信息检索、推荐系统、图像处理、自然语言处理等领域。相似检索主要方法可以总体分为以下几类&#xff1a; 基于距离度量的方法&#xff1a; 余弦相似度&…

国产手机纷自研操作系统,“不作恶”的谷歌自食恶果

在中国第一家手机企业自研操作系统并取得成功之后&#xff0c;日前再有两家国产手机宣布自研操作系统&#xff0c;由此将导致安卓系统的基座开始被撼动&#xff0c;垄断很可能将就此终结。 谷歌曾声称自己“不作恶”&#xff0c;不过在安卓系统发展壮大之后&#xff0c;谷歌开始…

[C++]关键字,类与对象等——喵喵要吃C嘎嘎2

希望你开心&#xff0c;希望你健康&#xff0c;希望你幸福&#xff0c;希望你点赞&#xff01; 最后的最后&#xff0c;关注喵&#xff0c;关注喵&#xff0c;关注喵&#xff0c;大大会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我真的…

小米手机刷EU版ROM教程

第一件事 拿到手机第一件事是登录账号&#xff0c;然后开启开发者模式&#xff0c;设置中搜索&#xff1a;手机解锁状态 绑定解锁账号&#xff0c;等168小时候才能解锁 提前下载软件包 官方解锁工具 官方Fastboot工具 recovery包&#xff1a;twrp ROM包 其他: EU rom论坛页…

Proteus仿真--数码管随机模拟显示乘法口诀(仿真文件+程序)

本文主要介绍基于51单片机的数码管随机模拟显示乘法口诀实验&#xff08;完整仿真源文件及代码见文末链接&#xff09; 仿真图如下 其中上方主要是6位显示数码管&#xff0c;使用方法点击左下方的按键开关实现不同的数字切换&#xff0c;按照乘法表进行 仿真运行视频 Prote…

Python入门教程——Python变量定义的简单使用介绍

文章目录 前言一、变量的定义二、变量的简单使用三、类型转换关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Python小游戏源码五、面试资料六、Python兼职渠道 前…

接口测试用例设计详解

1.接口测试用例设计简介 我们对系统的需求分析完成之后&#xff0c;即可设计对应的接口测试用例&#xff0c;然后用接口测试用例进行接口测试。接口测试用例的设计也需要用到黑盒测试方法&#xff0c;其与功能测试用例设计的方法类似&#xff0c;接口测试用例设计中还需要增加…

MATLAB和S7-1200PLC OPC通信(激活S7-1200PLC OPCUA服务器)

MATLAB和SMART PLC OPC通信请参考下面文章博客: MATLAB和西门子SMART PLC OPC通信-CSDN博客文章浏览阅读123次。西门子S7-200SMART PLC OPC软件的下载和使用,请查看下面文章Smart 200PLC PC Access SMART OPC通信_基于pc access smart的opc通信_RXXW_Dor的博客-CSDN博客OPC是…

软考网络规划师复习第六章:基础网络实验

正文共&#xff1a;10086 字 120 图&#xff0c;预估阅读时间&#xff1a;11 分钟 目录 软考网络规划师复习第一章&#xff1a;Windows系统中的网络 0、序言 1、Windows系统中的网络1.1、桌面中的网卡1.2、命令行中的网卡1.3、路由表1.4、家用路由器 软考网络规划师复习第二章&…

Python词云生成工具3:定制更多参数

文章目录 添加整型参数布尔型参数背景颜色词云生成逻辑源代码 Python打造一个词云软件显示分词结果 添加整型参数 我们所有的设置都放在了wcDct中&#xff0c;所以若想用更多的参数来定制词云&#xff0c;那么只需在wcDct中添加内容&#xff0c;例如下面这些整型参数 其次&a…

【Linux】进程概念III --fork函数解析

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法…感兴趣就关注我吧&#xff01;你定不会失望。 本篇导航 0. 创建进程1. 认识fork函数2.使用Fork函数3.关于fork的为什么3.1 一个函数如何返回两次?fork究竟在干什么?3.2 为什么要给子…

0-1矩阵列互斥问题——回溯法 Python实现

三、 0-1 矩阵的列集互斥问题。给定一个 m n m \times n mn 的 0-1 矩阵 A \mathrm{A} A 。定义列互斥为: 对于矩阵 A A A 中的任意两列 i i i 和 j j j, 如果在对应的每一行上, i i i 和 j j j 不存在同时为 1 的情况, 则称列 i \mathrm{i} i 和 j \mathrm{j} j 互斥…

Redis-命令操作Redis->redis简介,redis的安装(Linux版本windows版本),redis的命令

redis简介redis的安装&#xff08;Linux版本&windows版本&#xff09;redis的命令 1.redis简介 Redis是一个开源&#xff08;BSD许可&#xff09;&#xff0c;内存存储的数据结构服务器&#xff0c;可用作数据库&#xff0c;高速缓存和消息队列代理。 它支持字符串、哈…

零基础入门Python,主要该学些什么?一文详解。

文章目录 前言一、Python开发基础二、Python高级编程和数据库开发三、前端开发四、WEB框架开发五、爬虫开发六、全栈项目实战七、数据分析八、人工智能九、自动化运维&开发十、高并发语言GO开发关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精…

Nginx的location优先级和重定向

Nginx的location有优先级级和匹配方式&#xff1a; 在http模块有server,在server模块才有location,location匹配的是uri /test /image 在一个server当中有多个location,如何来确定匹配哪个location。 Nginx的正则表达式&#xff1a; ^:字符串的起始位置 $:字符串的结束位…

推荐彩虹知识商城源码

彩虹知识商城7.0.3小森升级版新增供货商开心学习版&#xff0c;新增邮件提醒功能&#xff0c;支持给用户发订单、结算等邮件通知&#xff0c;支持给管理员发送提现、域名审核等邮件通知&#xff0c;支持设置手续费最低扣除金额&#xff0c;修复了其他一些已知问题。 演示地址&…

工作数字化的中国历程 | 从 OA 到 BPM 到数字流程自动化

业务流程是由“活动”&#xff08;或称“工作任务”&#xff09;构成的&#xff0c;在企业里的所有工作是不是都叫流程&#xff0c;或者属于流程的一部分&#xff0c;这个概念很绕&#xff0c;我觉得没有必要去做学究气的辨析。我曾经提出过一个从工作的两个特性&#xff08;产…