Unity基础知识

访问修饰符

  • public:可以在Inspector的组件中编辑和显示,简而言之就是可以在编辑器里面看到和编辑,并且可以跨脚本使用

  • private:只能在这个类里面使用,无法显示在编辑器上,且不能跨脚本使用

Awake()和Start()

  • Awake():初始化操作

  • Start():在Awake()后面进行,是在这个脚本调用时才会执行

注意:把脚本挂在一个GameObject上面,如果没有启用这个组件(脚本),只会调用Awake()函数

Update()和FixedUpdate()

两者的不同:FixUpdate()时间间隔是固定的,Update()可能每一帧的时间间隔不同

  • Update()常见使用例子:

  1. 移动非物理物体

  2. 简单的计时器

  3. 接受输入

  • FixedUpdate()常见使用例子:

  1. 任何影响刚体(即物理对象)的动作

  2. 使用力来定义移动

注意:ctrl+shift+M启动引导,可以快速调用自带的函数

矢量数学

注意:Z轴是朝里面的

暂时无法在飞书文档外展示此内容

注意:但是在二维空间中,z轴正方向指向的是屏幕外面

启用和禁用组件

public class EnableComponent:MonoBehavior
{
   private Light myLight;
   void start()
   {
      myLight=GetComponent<Light>();
   }
   void Update()
   {
      if(Input.GetKeyUp(KeyCode.space))
      {
         myLight.enabled=!myLight.enabled;  //按下空格键时如果组件正在启用就禁用,如果正在禁用就启用
      }
    }
 }

也可以启用和禁用脚本

激活游戏对象

父对象停止激活同样也会停用子对象,但是子对象依然处于活跃状态

public class ActiveObjects:MonoBehaviour
{
    void Start()
    {
        gameObject.setActive(false);
    }
 }

平移和旋转

public class TransfomFunctions:MonBehaviour
{
    public float movespeed=10f;
    public float turnSpeed=50f;
    void Update()
    {
//平移
       transform.Translate(vector3.forward*movespeed*Time.deltatime);//逐帧运动
       if(Input.GetKey(KeyCode.UpArrow))//按键进行移动
           transform.Translate(Vector3.forward*movement*Time.daltatime);
        transform.position += new Vector3(Input.GetAxis("Horizontal") * speed * Time.deltaTime, Input.GetAxis("Vertical") * speed * Time.deltaTime, 0);
//这种方法也可以实现按键进行移动
//旋转
        if(Input.GetKey(KeyCode.LeftArrow))
            transform.Rotate(Vector3.up,-turnSpeed*Time.deltatime);//第一个参数是确定绕着哪个轴

    }
 }
   

注意:

  • Vector3.forward:相对于世界坐标系的前方向,固定为 (0, 0, 1)

  • transform.forward:相对于物体自身的前方向,随物体旋转而改变。

LookAt

作用:将旋转朝向target

public class CameraLookAt:MonoBehaviour
{
   public Transform target;
   void Update()
   {
      transform.LookAt(target);
   }
}

比如说我想让摄像机始终对准一个物体,我就可以这样去做,我可以把场景中的一个GameObject直接拖到这个变量上面,unity会自动处理这种关系,Transform是GameObject上的一个组件,unity会自动找到这个GameObject上面的Transform组件,Rigid也是同理

Destroy

销毁自身:

Destroy(gameObject);
//这里的 gameObject 是指当前脚本所附加的那个对象。

销毁某个GameObject:

public class DestroyComponent:MonoBehaviour
{
   public GameObject other;
   void Update()
   {
       if(Input.GetKey(KeyCode.Space))
       {
           Destroy(other);
       }
    }
}

销毁组件:

public class DestroyComponent:MonoBehaviour
{
   void Update()
   {
      if(Input.GetKey(KeyCode.Space))
      {
         Destory(GetComponent<MeshRenderer>());
      }
    }
 }

延时销毁:

public class DestroyComponent:MonoBehaviour
{
   void Update()
   {
      if(Input.GetKey(KeyCode.Space))
      {
         Destory(gameObject,3f); //意思是延时3秒销毁自身
      }
    }
 }

gameobject的大小写问题:

  • 小写的 gameObject 是指当前脚本所在的对象实例,必须使用小写的 gameObject

  • 大写的 GameObject 是类名,不能直接传递给 Destroy() 函数。

GetButton和GetKey

1. Input.GetKey

  • 用途:检测特定键盘键是否被按下。

  • 检测方式:直接检测某个物理键(例如 "W"、"Space" 键等)的按下状态。

  • 使用方法

    if (Input.GetKey(KeyCode.Space))
    {
        // 检测到空格键被按下
    }
    • Input.GetKey(KeyCode):你需要传递一个 KeyCode 枚举值来检测某个键的按下状态。

    • 例子:

  • 常见场景:用于检测具体的键盘按键,比如方向键、字母键等。

2. Input.GetButton

  • 用途:检测由**输入管理器(Input Manager)**设置的按钮是否被按下。

  • 检测方式:通过输入管理器中定义的按钮名称检测输入,常用于处理虚拟按钮,例如游戏手柄的按钮、键盘按键等。

  • 使用方法

    if (Input.GetButton("Jump"))
    {
        // 检测到跳跃按钮被按下
    }

    • Input.GetButton("ButtonName"):你需要传递一个在输入管理器中设置的按钮名称字符串。

    • 例子:

  • 常见场景:适合处理复杂的输入,比如需要支持游戏手柄和键盘同时操作的情况下。通过输入管理器,你可以绑定多个物理按键到同一个虚拟按钮。例如,你可以将 "Jump" 映射到键盘的 "Space" 和游戏手柄的 "A" 按钮。

主要区别:

  • 输入方式

    • GetKey:直接检测物理键(通过 KeyCode)。

    • GetButton:检测由输入管理器配置的虚拟按钮名称,可以同时映射多个物理输入。

  • 灵活性

    • GetKey 更直接,用于具体的键检测。

    • GetButton 更灵活,可以处理跨设备的输入映射,适合复杂输入设置。

总结:

  • Input.GetKey 用于检测具体的键盘按键,比如直接检测“W”键是否被按下。

  • Input.GetButton 则通过输入管理器配置,用于检测虚拟按钮,适用于跨设备输入,例如同时支持键盘和手柄的跳跃操作。

如果你的游戏只需要简单的键盘输入,使用 GetKey 更为简单直接。如果你需要支持键盘、手柄等多种输入设备,GetButton 是更好的选择,因为它更具扩展性。

这个Name就是GetButton后面要写的东西

bbc70a2c5f7a48da8dc120241feaf1d4.png

GetButtonDown,GetButton和GetButtonUp的区别:

第一次按下,第一帧返回

随着帧数的增加

松开按钮那一帧

GetButtonDown

True

False

False

GetButton

True

True

False

GetButtonUp

False

False

True

GetAxis

Negative Button和Positive Button的区别:

Positive Button:用于表示轴的正向输入(+1),例如向右或向上。

Negative Button:用于表示轴的负向输入(-1),例如向左或向下。

Gravity和Sensitivity

Gravity:用于表示Axis值变为0的速度,这个值越大,速度越快

Sensitivity 与上面的相反,这个表示到达1或-1的速度,这个值越大,速度越快

GetAxis和GetAxisRaw的区别

  1. GetAxis

GetAxis 返回的是一个平滑过渡的值,就像是输入信号经过了“减震器”处理,让它变得更加柔和和平滑。它会在按下和松开按键时逐渐增加或减少输入值,因此适合需要流畅控制的情况。

  • 值范围-11,中间值会逐渐变化。

  • 平滑过渡:如果你按住按键(例如方向键),GetAxis 的值会从 0 逐渐增加到 1,松开时也会逐渐回到 0。这种平滑的变化适合像角色移动或者摄像机视角这样的场景,避免突然跳动。

举个例子: 假设你驾驶一辆汽车,当你踩油门时,GetAxis 的输入就像逐渐加速一样,你的速度会慢慢上升,而不是突然达到最大速度。同样,松开油门时,它会慢慢减速。

  1. GetAxisRaw

GetAxisRaw 则更直接,它返回的值没有经过平滑处理,是“生硬的”输入结果。按下按键时,它立即返回 -101,不会有中间值。适合需要精确和立即响应的场合,比如移动的方向控制。

  • 值范围:也在 -11 之间,但会立即跳到对应值。

  • 没有平滑过渡:按下按键时,GetAxisRaw 的值直接从 0 跳到 1,松开时直接跳回 0,没有渐变过程。这适合需要快速反应的场景,比如按键事件或者菜单导航

举个例子: 当你在玩一个经典的街机游戏时,按下方向键时角色会立即移动,不会有任何延迟或平滑过渡。这种“立刻反应”的效果就是 GetAxisRaw 的表现。

总结对比

  • GetAxis:值是平滑过渡的,按键响应是渐进的,适合需要控制速度或者平滑运动的情况。

  • GetAxisRaw:值是即时的,按键响应是立刻的,适合需要快速、直接响应的情况。

Dead和Snap

1. Dead(死区)

  • 作用Dead 是输入的死区,用来忽略轴输入的微小值,确保设备在微小偏移时不会被认为是有效输入。

  • 值范围:通常在 0 到 1 之间,代表了一个输入轴在这个范围内的值将被视为 0。

  • 用途:当使用模拟输入设备(如操纵杆或游戏手柄)时,由于它们可能会存在非常小的偏移或抖动,Dead 参数可以确保这些微小的无意输入不会触发动作。这非常有用,避免了角色因为设备敏感度太高而在游戏中产生不必要的移动。

例子: 如果你设置 Dead0.1,那么任何输入轴的值在 -0.10.1 之间的数值都会被视为 0,输入将不会影响物体的运动。

2. Snap(轴复位/快速切换)

  • 作用Snap 用于控制输入从正方向切换到负方向时,是否应立即将输入值重置为 0。它可以帮助处理按键方向的快速切换(例如从按下右方向键立即切换到左方向键)。

  • Snap 是一个布尔值(truefalse)。如果启用了 Snap(即设置为 true),当输入从一个方向(如正方向)突然切换到另一个方向(如负方向)时,输入轴的值会快速归零,而不会缓慢过渡。

例子: 假设你有一个水平轴,Positive Button 是“D”键(向右),Negative Button 是“A”键(向左):

  • 如果 Snaptrue,当你松开“D”键然后立即按下“A”键时,输入值会瞬间从 +1 切换到 -1,中间不会有过渡。

  • 如果 Snapfalse,输入值会平滑过渡,先从 +1 逐渐减小到 0,再从 0 变为 -1

结合例子理解

假设你有一个游戏角色的水平移动,通过 Input.GetAxis("Horizontal") 来控制它:

  • Dead:如果你设置了一个较大的 Dead 区(比如 0.2),那么轻微的输入(比如手柄轻微晃动)将不会导致角色移动,只有超出这个死区的输入才会生效。

  • Snap:如果你想让角色在从左移动立即切换到右移动时没有过渡效果,启用 Snap 可以让这种切换更加迅速。

但是需要如下代码:

void Update()
{
    float h = Input.GetAxis("Horizontal");
    float v = Input.GetAxis("Vertical"); 
}

OnMouseDown

OnMouseDown() 是 Unity 中的一种事件函数,它用于检测当用户在游戏对象上按下鼠标按钮时触发的行为。

具体来说,当用户用鼠标**点击(按下)**带有碰撞器(Collider)的游戏对象时,Unity 会自动调用这个方法,你可以在这个方法里定义相应的逻辑来响应鼠标点击事件。

使用场景:

OnMouseDown() 常用于实现一些点击互动效果,比如点击对象后改变它的颜色、销毁对象、打开菜单等。

基本用法:

public class Example : MonoBehaviour
{    // 当用户点击当前对象时会触发此方法
void OnMouseDown()
    {// 执行点击后的操作
        Debug.Log("Object clicked!");
    }
}

注意事项:

  1. 对象需要有碰撞器OnMouseDown() 只能在带有碰撞器(ColliderCollider2D)的游戏对象上起作用。碰撞器可以是立方体、球体、胶囊体等。

    1. 例如,如果你有一个 3D 对象,你需要给它添加一个 BoxColliderSphereCollider 等。如果是 2D 游戏对象,则需要添加 Collider2D

  2. 对象需要在摄像机视野内: 对象必须在相机的视野范围内,才能检测到鼠标点击。如果对象不可见或摄像机没有对准对象,它将无法被点击到。

  3. 相机需要启用物理光线投射(Raycasting): 默认情况下,摄像机的设置允许物理光线投射(Raycasting),这意味着 Unity 可以检测到鼠标点击的对象。如果你有多个摄像机,确保主摄像机启用了光线投射。

  4. 适用于所有鼠标按钮OnMouseDown() 响应的是任何鼠标按键的按下事件(左键、右键或中键)。但默认情况下,它通常响应鼠标左键的点击。

示例:点击对象后销毁它

public class DestroyOnClick : MonoBehaviour
{
    void OnMouseDown()
    {// 销毁当前对象
        Destroy(gameObject);
    }
}

在这个示例中,当用户点击附加了这个脚本的对象时,OnMouseDown() 会被调用,接着 Destroy(gameObject) 会销毁该对象。

示例:给点击的物体施加一个力

public void MouseClick:MonoBehaviour
{
   private RigitBody rb;
   private void Awake()
   {
      rb=GetComponent<RigitBody>();
   }
   void OnMouseDown()
   {
      rb.AddForce(-transform.forward*500f);
      rb.useGravity=true;
   }
}

OnMouseDown() 是 Unity 中处理鼠标点击事件的便捷方法,当用户点击一个带有碰撞器的对象时,会触发这个方法。在游戏开发中,它常用于创建交互,比如点击物体来触发动画、销毁物体、打开对话框等。

GetComponent

可以调用自身或其他GameObject的组件或附加的脚本

public class UsingOtherComponent:MonoBehaviour
{
   public GameObject otherGameObject;
   private AnotherScript anotherScript;   //以这个脚本名称为类型来命名
   private YetAnotherScript yetAnotherScript;
   void Awake()
   {
      anotherScript=GetComponent<AnotherScript>();
      yetAnotherScript=otherGameObject.GetComponent<YetAnother>();
      
   }
   void Start()
   {
      //
   }

 

需要注意:GetComponent本身会占用大量处理能力,所以最好在Awake()或者Start()中调用一次

DeltaTime

定义:

指的是两次更新或者固定更新函数调的间隔时长,但是每一帧的间隔时间是不同的,就像你打王者帧数是不一样的

 void Update()
 {
     if (!isDead) 
     {
             transform.position += new Vector3(Input.GetAxis("Horizontal") * speed * Time.deltaTime, Input.GetAxis("Vertical") * speed * Time.deltaTime, 0);

     }
 }

就像上面这样,

Time.deltaTime 是每帧之间经过的时间,它保证了你的代码在不同帧率下都能保持一致的速度。如果没有 Time.deltaTime,物体移动的距离会直接依赖于帧率,帧率高时物体会移动得更快,帧率低时移动得更慢。

使用 Time.deltaTime 可以让移动的速度与时间挂钩,而不是与帧数挂钩,保证了无论每秒渲染多少帧,物体的移动速度都保持一致,从而让运动变得平滑。

具体地说:

  • 如果一帧的时间较长,Time.deltaTime 会较大,乘以速度后弥补了由于帧率低导致的移动“跳跃”。

  • 如果一帧的时间较短,Time.deltaTime 会较小,物体只会移动很小的距离,这样即使帧率很高,物体也不会移动过快。

这样我们更改的就是每秒的值,而非每帧的值

具体例子:

transform.position += new Vector3(Input.GetAxis("Horizontal") * speed * Time.deltaTime, Input.GetAxis("Vertical") * speed * Time.deltaTime, 0);

Input.GetAxis("Horizontal") * speed:这里的 speed 是你设定的速度,它表示物体每秒应该移动的单位量。

Time.deltaTime:它将 speed 从“每秒移动量”转换为“每帧的移动量”。假设你的物体每秒应该移动 5 个单位,而每帧的时间间隔是 1/60 秒,那么这一帧物体的移动距离就是 5 * (1/60),即 0.0833 个单位。

这意味着,即使帧率不稳定,物体的总移动量仍然是基于每秒的时间,而不是每帧的次数。

数据类型:

Value:

只是复制操作

Reference:

是引用操作,对其的改变会实际影响

public class DatatypeScript:MonoBehaviour
{
   void Start()
   {
      Vector3 currentPosition=transform.position;
      currentPosition=new Vector3(0,2,0);
   }
 }   //不会更改实际的位置

public class DatatypeScript:MonoBehaviour
{
   void Start()
   {
      Transform tran=transform;
      tran.position=new vector(0,2,0);
   }
 }   //实际的位置也会更改

首字母大小写

在 Unity 中确实有这样一个规律,很多组件的类名与属性名是相关联的。通常,一个大写的类名代表 Unity 中的一个组件类型,而小写的属性名通常是对当前对象的那个组件实例的引用。这个规律帮助开发者更方便地访问对象上的常见组件。

具体规律解释:

  1. 大写的类名:通常用于表示某个组件的类型,即组件的类。例如:

    1. Transform:表示 Unity 中的 Transform 组件类型,负责管理对象的位置、旋转和缩放。

    2. Rigidbody:表示物理系统中的刚体组件,用于管理物体的物理行为。

    3. Collider:表示碰撞器组件,用于物体的碰撞检测。

  2. 小写的属性名:表示当前游戏对象上该类型组件的一个实例引用。这些属性是 MonoBehaviour 类的成员变量,直接指向游戏对象上的对应组件。常见的如:

    1. transform:表示当前游戏对象的 Transform 组件。

    2. rigidbody:表示当前游戏对象的 Rigidbody 组件。

    3. collider:表示当前游戏对象的 Collider 组件。

这些小写的属性让你可以直接在脚本中访问游戏对象的常用组件,而不需要手动通过 GetComponent<T>() 方法获取。

举例:

void Start()
{// 当前游戏对象的 Transform 组件
    transform.position = new Vector3(0, 0, 0);
// 当前游戏对象的 Rigidbody 组件
    Rigidbody rb = rigidbody;
    // 如果当前对象上没有刚体组件,这里可以手动获取
    Rigidbody rbManual = GetComponent<Rigidbody>();
}

在上面的例子中:

  • transform 直接访问当前对象的 Transform 组件。

  • rigidbody 直接访问当前对象的 Rigidbody 组件。

但有些情况例外:

  1. 不是所有组件都有默认的属性名:虽然 TransformRigidbody 等常见组件有小写的快捷属性,但是一些不常用的组件(如 AudioSourceLight 等)则需要使用 GetComponent<T>() 来手动获取。

4. AudioSource audioSource = GetComponent<AudioSource>();

  1. 属性是只读的:这些小写的属性通常是只读的,你不能直接给它们赋值,比如你不能直接用 transform = someOtherTransform;,只能操作它们的内部属性(如位置、旋转等)。

其他常见组件的类和属性:

  • Transformtransform

  • Rigidbodyrigidbody

  • Collidercollider

  • Rendererrenderer

  • Cameracamera

  • Lightlight

总结:

Unity 确实有这个规律:类名(大写)表示组件类型,属性名(小写)表示当前对象上的组件实例。这些内置的快捷访问属性让你可以更方便地操作常见组件,而不必通过 GetComponent<T>() 反复查找。

Instantiate函数

public class UsingInstantiate:MonoBehaviour
{
   public RigidBody projectile;
   public Transform barrelend;
   void Update()
   {
      if(Input.GetButtonDown("Fire"))
      {
         Instantiate(projectitle,barrelend.position,barrelend.rotation);
      }
   }
}

把场景中的对象拖到projectile和barrelend框框里面就可以获得这个实例的RigidBody和Transform

public class UsingInstantiate:MonoBehaviour
{
   public RigidBody projectile;
   public Transform barrelend;
   void Update()
   {
      if(Input.GetButtonDown("Fire"))
      {
         Rigidbody projectileInstance;
         projectileInstance=Instantiate(projectitle,barrelend.position,barrelend.rotation) as RigidBody;
         projectileInstance.AddForce(barrelend.up*350f);//给物体实现向上发射的效果
      }
   }
}

数组

public class Arrays:MonoBehaviour
{
   int[]myIntArray={12,76,8,947,903};
   //或者
   int[]myIntArray1=new int[5];
   void Start()
   {
      myIntArray[0]=12;
      myIntArray[1]=76;
      myIntArray[2]=8;
      myIntArray[3]=947;
      myIntArray[4]=903;
   }
}
public GameObject[]Players;
void Start()
{
   players=GameObject.FindGameObjectsWithTag("Player");
}

 

Invoke

Invoke("函数名称",延迟多少秒执行);

意思是延迟一段时间执行这个函数,只有返回值是void才能这么使用

public class InvokeScript:MonoBehaviour
{
   public GameObject target;
   void Start()
   {
      Invoke("SpawnObject",2);
   }
   void SpawnObject()
   {
      Instantiate(target,new Vector3(0,2,0),Quaternion.identity);
   }
}

InvokeRepeating

InvokeRepeating("MethodName", startDelay, repeatRate);
  • startDelay:第一次调用该方法的延迟时间。

  • repeatRate:之后重复调用该方法的时间间隔。

示例:

public class Example : MonoBehaviour
{void Start()
    {// 延迟 2 秒后开始,每隔 1 秒调用一次 PrintMessage 方法
        InvokeRepeating("PrintMessage", 2f, 1f);
    }
void PrintMessage()
    {
        Debug.Log("This message is displayed repeatedly every second.");
    }
}

在这个例子中,PrintMessage 方法将在 2 秒后第一次被调用,然后每 1 秒重复调用一次,直到取消该操作或对象销毁。

取消调用:

你可以使用 CancelInvoke() 方法来取消已经计划的 Invoke()InvokeRepeating() 调用。

CancelInvoke("MethodName");

 

枚举

enum Direction{North,East,South,West};   //默认North是0,East是1,依次往后推
enum Direction{North=10,East=11,South=13,West=27}
void Start()
{
   Direction myDirection;
   myDirection=Direction.North;
}
void ReserveDirection(Direction dir)
{
   if(dir==Direction.North)
      dir=Direction.South;
   //以此类推
   .....
 }

属性

属性(Property) 是一种在类或对象中用于封装字段(Field)的机制。属性允许你通过类似访问字段的方式,来读取或修改对象的值,同时可以控制对这些值的访问、验证和操作。属性不仅仅是简单的字段,它们通常带有特定的逻辑,可以在读取和设置值时进行额外的操作

属性(Property)是什么?

属性是一个带有getset访问器的特殊成员,它提供了一种更加安全和灵活的方式来访问对象的内部数据。属性可以理解为字段的“外包装”,它允许你在获取或设置值时加入自定义的逻辑。

形象解释

可以把属性看作是一个**“智能字段”**,它不仅仅是一个单纯的变量,更像是带有保护和规则的门卫,你可以通过这个门卫来获取和设置值,但他可以根据需要控制、检查或者修改你获取或设置的内容。

访问器(Accessor)是什么?

访问器 是属性中的组成部分,它们是用于读取或设置属性值的特定方法。访问器包括两种类型:

  1. Getter(get 访问器):用于读取属性的值

  2. Setter(set 访问器):用于设置属性的值

通过访问器,你可以在属性被访问或修改时执行额外的逻辑,比如验证输入数据、记录日志或者限制权限。

代码示例:

这个创建的公共属性通常是私有属性的首字母大写

public class Person
{
    // 这是一个私有字段,只能在类内部访问
    private string name;

    // 定义一个公共属性来访问私有字段
    public string Name
    {
        // get 访问器,用于读取属性值
        get
        {
            return name;
        }

        // set 访问器,用于设置属性值
        set
        {
                name = value;
        }
    }
}
public class Game:MonBehaviour
{
   void Start()
   {
      Person myPerson=new Person();
      myPerson.Name="hehe";
      string Text01=myPerson.Name;
   }
}

解释代码:

  • name 是一个私有字段,只能在类内部访问。

  • Name 是一个公共属性,通过 getset 访问器来控制对 name 字段的访问。

    • get 访问器 允许你读取 name 的值。

    • set 访问器 允许你设置 name 的值.

为什么要使用属性和访问器?

  1. 封装数据:通过属性,你可以保护类的内部数据结构,不让它们直接暴露给外部代码,从而减少出错的可能性。

  2. 数据验证:在 set 访问器中可以添加验证逻辑,确保属性的值在被设置时符合要求。

  3. 灵活性:你可以在 getset 访问器中添加额外的逻辑,而不改变外部访问的方式。这样在代码维护时可以保持接口的一致性。

  4. 只读或只写:可以通过定义只有 getset 的访问器来创建只读只写的属性。例如,去掉 set 访问器就会创建一个只读属性。

  5. 可以在里面调用函数

public class Player
{
   private int level;
   public int Level
   {
      get
      {
         return experience/1000;
      }
      set
      {
         experience=value*1000;
      }
   }
   public int health{get;set;}
}

访问器的用法

  • get 访问器:通常用于计算、返回或直接访问内部字段。它允许你在获取值时执行一些额外操作,而外部代码却不需要知道这些操作的细节。

  • set 访问器:用于控制对属性值的更改。你可以在 set 中进行数据验证、触发事件或者执行额外的操作。

总结

  • 属性 是一种可以通过 getset 访问器来封装和控制字段的方式。

  • 访问器 是属性中用于读取和修改值的特殊 方法,get 访问器用于读取值,set 访问器用于设置值。

  • 属性可以让你更安全地访问对象的内部数据,同时还能保持灵活性和可维护性。

三元运算符

message=health>0?"Player is Alive":"Player is Dead";

静态

类的每个对象都共享同一个值

//比如说·我想知道我创建了多少Enemy
public class Enemy
{
   public static int enemyCount=0;
   public Enemy()
   {
      enemyCount++;
   }
}
public class Game
{
   void Start()
   {  
      Enemy enemy1=new Enemy();
      Enemy enemy2=new Enemy();
      Enemy enemy3=new Enemy();
      int x=Enemy.enemyCount;
   }
}

静态方法无需创建类即可直接使用

  • 静态类无法实例化

  • 静态类中所有成员都必须是静态的

就如Input.GetButtonDown一样,不需要创建实例就可以访问

public static class Utilties
{
   public static int Add(int num1,int num2)
   {
      return num1+num2;
   }
}
public class UtitliesExample:MonoBehaviour
{
   void Start()
   {
      int x=Utilties.Add(5,6);
   }

静态方法属于类,非静态方法属于实例

静态方法属于类,非静态方法属于实例”这句话的意思是,静态方法是直接与类本身相关联的,而非静态方法则与类的具体对象(实例)**相关联。下面通过详细解释和例子,帮助你理解这一点。

  1. 静态方法属于类

  • 静态方法是用 static 关键字修饰的,它与类本身关联,不依赖于任何对象实例。这意味着你可以直接通过类名来调用静态方法,而不需要创建类的对象。

  • 调用方式类名.静态方法()

示例:静态方法

public class MathUtility
{
    // 静态方法
    public static int Add(int a, int b)
    {
        return a + b;
    }
}

// 调用静态方法
int result = MathUtility.Add(3, 5); // 输出 8

解释

  • MathUtility.Add(3, 5) 直接通过 MathUtility 类名调用 Add 方法,不需要创建 MathUtility 的实例(即不需要 new MathUtility())。

  • 这表明 Add 方法属于类本身,与任何实例无关。

  1. 非静态方法属于实例

  • 非静态方法没有 static 修饰,它与**类的具体实例(对象)**相关联。要调用非静态方法,必须先创建类的实例,方法会作用于这个实例的状态或数据。

  • 调用方式对象.非静态方法()

示例:非静态方法

 
public class Person
{
    public string Name;

    // 非静态方法
    public void SayHello()
    {
        Console.WriteLine($"Hello, my name is {Name}.");
    }
}

// 创建实例并调用非静态方法
Person person = new Person();
person.Name = "Alice";
person.SayHello(); // 输出: Hello, my name is Alice.

解释

  • person.SayHello() 调用的是 Person 类的非静态方法,但这里必须先通过 new Person() 创建 person 实例。

  • SayHello 方法依赖于 person 对象的状态(Name 字段),因此它与 person 的实例相关。

总结静态方法与非静态方法的区别

1. 静态方法

  • 属于类:不依赖任何实例,直接通过类名调用。

  • 访问限制:静态方法只能访问其他静态成员(静态字段或静态方法),不能访问类的非静态成员,因为它们没有实例。

  • 例子

public class MyClass
{
    public static void StaticMethod()
    {
        Console.WriteLine("This is a static method.");
    }
}

MyClass.StaticMethod(); // 通过类名调用

2. 非静态方法

  • 属于实例:必须通过类的具体对象调用,方法通常依赖实例的状态(字段或属性)。

  • 访问权限:非静态方法可以访问类的所有成员,包括静态和非静态成员,因为每个实例都能访问类中的所有资源。

  • 例子

public class MyClass
{
    public string Name;

    public void NonStaticMethod()
    {
        Console.WriteLine($"This is a non-static method. Name is {Name}");
    }
}

MyClass myObject = new MyClass();
myObject.Name = "Alice";
myObject.NonStaticMethod(); // 通过实例调用

进一步理解:类和实例的关系

  1. 类(Class) 是一种模板或蓝图,定义了对象的结构和行为,但类本身并不是实际存在的“物体”。

  2. 实例(Instance) 是类的具体化,当你使用 new 关键字创建对象时,你就得到一个类的实例,每个实例都有自己独立的数据和状态。

类与实例的关系可以类比为:

  • 是一个建筑的设计图纸,它描述了建筑的结构(字段)和功能(方法),但图纸本身并不是建筑。

  • 实例 是根据图纸建造的具体建筑。每个建筑(实例)都有自己独特的特征,比如颜色、大小等(即每个对象的属性值不同)。

静态方法属于“图纸”,非静态方法属于“建筑”

  • 静态方法属于图纸(类),因为它定义的是图纸本身的功能,不依赖于任何具体的建筑。

  • 非静态方法属于建筑(实例),因为它需要依赖具体建筑的特征来执行,比如建筑的具体位置或大小。

总结

  • 静态方法属于类:它与类相关联,独立于任何对象,可以通过类名直接调用。静态方法无法访问实例的状态或非静态成员。

  • 非静态方法属于实例:它依赖于类的具体对象(实例),通过实例调用。非静态方法可以访问实例的状态和非静态成员。

静态方法适用于不依赖于实例的工具类或全局逻辑,而非静态方法适合处理与具体对象相关的操作和状态。

泛型

public class SomeClass
{
   public T GenericMethod<T>(T param)
   {
      return param;
   }
}
public class SomeOtherClass:MonoBehaviour
{
   void Start()
   {
      Someclass myClass=new SomeClass();
      myClass.GenericMethod<int>(5);
   }
}
public class GenericClass<T>
{
   T item;
   public void UpdateItem(T newItem)
   {
      item=newItem;
   }
}
public class GenericClassExample:MonoBehaviour
{
   void Start()
   {
      GenericClass<int>myClass=new GenericClass<int>();
      myClass.UpdateItem(5);
   }
}

 

协程

协程的概念

在Unity中,协程是一种可以暂停执行并在某个条件满足后再继续的代码块。它允许我们“分步”执行一系列操作,不需要一次性完成,并且能够在两个步骤之间设置延时。

类比解释

想象你在厨房准备一顿大餐。为了节省时间,你希望在等待水煮开的过程中,去切蔬菜、调配酱汁、甚至摆盘,而不是干站着等水开。这时候,你心里会安排一个“等待水开”的任务。这个任务不会影响你去完成其他工作,等到水开时,你再回来处理它——这就是协程的作用。

在游戏开发中,比如在角色逐步恢复生命值、等待一定时间生成新的敌人等,协程能帮你实现这些“分布式”的操作,避免一次性卡住游戏主线程。

协程的实际例子

假设你在制作一个塔防游戏,每隔5秒就要生成一个敌人,而不是一开局就一下子生成所有的敌人。

  1. 传统方法的问题:如果你用一个普通循环,可能会这样写:

void GenerateEnemies()
{
    for (int i = 0; i < 10; i++)
    {
        SpawnEnemy();
        // 等待5秒(理论上)
    }
}

  1. 这里的问题是,代码会停在等待的地方,整个游戏都卡住,5秒后才继续下一个敌人生成。玩家体验会非常差,因为游戏“卡住了”。

  2. 协程的解决方法:用协程来处理,效果会更流畅,因为协程可以暂停、延时并继续,而不会打断游戏。

IEnumerator GenerateEnemiesCoroutine()
{
    for (int i = 0; i < 10; i++)
    {
        SpawnEnemy();                   // 生成敌人
        yield return new WaitForSeconds(5.0f);  // 等待5秒,不影响其他代码执行
    }
}

  1. 在这种情况下,yield return new WaitForSeconds(5.0f); 就是让协程暂停5秒,而不会暂停主线程。游戏依然正常运行,你的其他代码(比如角色的移动、界面更新)不受影响,而协程5秒后会自动恢复,生成下一个敌人。

什么时候用协程?

协程非常适合用在游戏中需要等待、分阶段执行的场景,比如:

  • 每隔一段时间生成一个道具、敌人或奖励。

  • 逐步更新一个值,比如角色的生命值、体力等。

  • 实现简单的动画或镜头效果,比如缓慢淡入、淡出、移动摄像头。

总结

协程就是一种“聪明的助手”,能帮你把大任务拆解成小任务,在每个任务间隙中“休息一下”,而不让主任务停下。这样可以确保游戏流畅运行,避免等待、延迟等操作导致的卡顿。

具体例子:

 public override IEnumerator DamageCharacter(int damage, float interval)   //damage是对角色造成的伤害,interval是相邻两次伤害之间间隔的时间
 {
     while(true)
     {
         hitPoints-=damage;
         if(hitPoints<=float.Epsilon)
         {
             KillCharacter();
             break;
         }
         if(hitPoints>float.Epsilon)
         {
             yield return new WaitForSeconds(interval);
         }
         else
         {
             break;
         }
     }
 }

Yield return 表示“返回一个等待条件,并暂停协程,直到条件满足后继续”

OnEnable()函数

和start()函数有点像,但是不同的是这个函数是在每次对象或脚本启用时调用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值