原视频:https://www.udemy.com/course/unity-2d-game-developer-course-farming-rpg/

bilibili课程地址:https://www.bilibili.com/video/BV1qi4y1k7ix

通用性的

  • GetComponent<>可以获取当前指定类型的组件,返回null代表没有
  • MonoBehaviour基础函数
    • Awake
    • Update
    • Start
    • OnEnable
    • OnDisable

单例类

防止多个玩家创建

 1using UnityEngine;
 2
 3public abstract class SingletonMonoBehavior<T> : MonoBehaviour where T : MonoBehaviour
 4{
 5    public static T Instance { get; private set; }
 6    protected virtual void Awake()
 7    {
 8        if (Instance == null)
 9        {
10            Instance = this as T;
11        } else {
12            Destroy(gameObject);
13        }
14    }
15}

多个部位动画绑定

使用订阅发布模式,利用delegate给所有的子动画部件发送触发动作,使用静态类处理发布订阅

 1public delegate void MovementDelegate(...);
 2
 3public static class EventHandler
 4{
 5    public static event MovementDelegate MovementEvent;
 6    public static void CallMovementEvent(...)
 7    {
 8        if (MovementEvent != null)
 9        {
10            MovementEvent(...);
11        }
12    }
13}

其他带有Animator的动画组件在OnEnable中使用EventHandler.MovementEvent += SetParameters;;在OnDisable中使用EventHandler.MovementEvent -= SetParameters;

动画测试脚本

手动调用EventHandlerCallMovementEvent,把变量设置好拖到玩家上

玩家移动

根据键盘输入、玩家速度和Time.deltaTime计算位移向量,调用Rigidbody2DMovePosition来实现,需要加原来的位置

层数高的会在上层,而且永远在层数低的上面

单个物体如果有多个子部件,比如SpriteRenderer,需要使用排序组来保证所有的子部件在一个层上

Tilemap

可以绘制贴图也可以存储碰撞关系,还可以存储一些根网格相关的关系,比如网格是否可以丢弃物品等

碰撞

有一方设置isTrigger后,函数private void OnTriggerEnter2D(Collider2D other)就会触发

相机

cinemachine相机可以有阻尼的跟随玩家

设置Confiner可以防止相机超过地图导致看到地图外的东西,需要添加碰撞体,设置LayerIgnore Raycast

预制件

可以把模板化的组件拖到资源里面生成预制件(Prefabs),预制件可以减少每次重新编辑的操作,类似于godot里面的scene

预制件参数:右键预制件创建预制件参数,在预制件的基础上生成新的预制件,只改变参数,节省空间,类似于设计模式里面的原型模式

字符串哈希化

使用静态的类保存int类型的字符串哈希,可以避免多次输入字符串的繁琐,用在Animator的动画属性里面

1xInput = Animator.StringToHash("xInput");

C#属性

1[System.Serializable]
2public class ItemDetails
3{
4    public int itemCode;
5    //...
6}

可以用来定义类、成员变量、方法等属性,已经用到的有:

TODO: 持续加入内容

1[System.Serializable]       // 可被从文件序列化反序列化
2[Range(10000, 100100)]      // 整型数据范围
3[SerializeField]            // unity脚本页面可以编辑该字段
4[HideInInspector]           // 使变量不显示在检查器中,而是序列化

可编程对象

添加到右键菜单创建可编程对象,提供图形化控件编辑数据

1using System.Collections.Generic;
2using UnityEngine;
3
4[CreateAssetMenu(fileName ="so_ItemList", menuName ="Scriptable Object/Item/Item List")]
5public class SO_ItemList : ScriptableObject
6{
7    [SerializeField]
8    public List<ItemDetails> itemDetails;
9}

fileName指定文件名,menuName指定右键菜单或者主菜单位置

自定义脚本属性

除了脚本变量属性外,可以定义额外的脚本属性来更方便的设置数据调试接口

在脚本字段上使用自定义属性

 1public class Item : MonoBehaviour
 2{
 3    [ItemCodeDescription] // 自定义属性
 4    [SerializeField]
 5    private int itemCode;
 6}
 7
 8// 属性来自
 9public class ItemCodeDescriptionAttribute : PropertyAttribute
10{
11}
12
13// 属性GUI渲染方法
14using UnityEditor;
15using UnityEngine;
16
17[CustomPropertyDrawer(typeof(ItemCodeDescriptionAttribute))]
18public class ItemCodeDescriptionDrawer : PropertyDrawer
19{
20    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
21    {
22        EditorGUI.BeginProperty(position, label, property);
23        if (property.propertyType != SerializedPropertyType.Integer) return;
24
25        EditorGUI.BeginChangeCheck();
26        // draw item code
27        var newValue = EditorGUI.IntField(
28            new Rect(position.x, position.y, position.width, position.height / 2),
29            label, property.intValue);
30
31        // draw item description
32        EditorGUI.LabelField(
33            new Rect(position.x, position.y + position.height / 2, position.width, position.height / 2),
34            "Item Description", GetItemDescription(property.intValue));
35
36        // if item code value changed, then set value to new value
37        if (EditorGUI.EndChangeCheck())
38        {
39            property.intValue = newValue;
40        }
41    }
42
43    // 查表渲染id对应的说明
44    private static string GetItemDescription(int itemCode)
45    {
46        var soItemList = AssetDatabase.LoadAssetAtPath("Assets/ScriptableObjectAssets/Item/so_ItemList.asset", typeof(SO_ItemList))
47            as SO_ItemList;
48        if (!soItemList) return "";
49        var itemDetailsList = soItemList.itemDetails;
50        var itemDetails = itemDetailsList.Find(x => x.itemCode == itemCode);
51        return itemDetails != null ? itemDetails.itemDescription : "";
52    }
53
54    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
55    {
56        return EditorGUI.GetPropertyHeight(property) * 2;
57    }
58}

物品管理拾取

使用静态类管理物品,物品类可以设置堆叠数量,物品和玩家发生碰撞触发玩家的pickup脚本,添加到物品管理类里面

物品栏UI

物品根据人物在世界坐标转换为viewport坐标的位置,然后在人物要被挡住时把物品栏移动到顶部

物品栏刷新

通过发布订阅从物品管理类中获取到物品变更函数,然后从里面刷新数据更新到UI上

弹窗描述

零散

  1. 在鼠标事件中,可以通过Raycast获取鼠标下面的物体
1eventData.pointerCurrentRaycast.gameObject
  1. 根据预制件创建对象的函数
1var obj = Instantiate(prefab, parentTransform);

未完待续💤