原视频: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

单例类

防止多个玩家创建

using UnityEngine;

public abstract class SingletonMonoBehavior<T> : MonoBehaviour where T : MonoBehaviour
{
    public static T Instance { get; private set; }
    protected virtual void Awake()
    {
        if (Instance == null)
        {
            Instance = this as T;
        } else {
            Destroy(gameObject);
        }
    }
}

多个部位动画绑定

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

public delegate void MovementDelegate(...);

public static class EventHandler
{
    public static event MovementDelegate MovementEvent;
    public static void CallMovementEvent(...)
    {
        if (MovementEvent != null)
        {
            MovementEvent(...);
        }
    }
}

其他带有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的动画属性里面

xInput = Animator.StringToHash("xInput");

C#属性

[System.Serializable]
public class ItemDetails
{
    public int itemCode;
    //...
}

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

TODO: 持续加入内容

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

可编程对象

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

using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName ="so_ItemList", menuName ="Scriptable Object/Item/Item List")]
public class SO_ItemList : ScriptableObject
{
    [SerializeField]
    public List<ItemDetails> itemDetails;
}

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

自定义脚本属性

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

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

public class Item : MonoBehaviour
{
    [ItemCodeDescription] // 自定义属性
    [SerializeField]
    private int itemCode;
}

// 属性来自
public class ItemCodeDescriptionAttribute : PropertyAttribute
{
}

// 属性GUI渲染方法
using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(ItemCodeDescriptionAttribute))]
public class ItemCodeDescriptionDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        EditorGUI.BeginProperty(position, label, property);
        if (property.propertyType != SerializedPropertyType.Integer) return;

        EditorGUI.BeginChangeCheck();
        // draw item code
        var newValue = EditorGUI.IntField(
            new Rect(position.x, position.y, position.width, position.height / 2),
            label, property.intValue);

        // draw item description
        EditorGUI.LabelField(
            new Rect(position.x, position.y + position.height / 2, position.width, position.height / 2),
            "Item Description", GetItemDescription(property.intValue));

        // if item code value changed, then set value to new value
        if (EditorGUI.EndChangeCheck())
        {
            property.intValue = newValue;
        }
    }

    // 查表渲染id对应的说明
    private static string GetItemDescription(int itemCode)
    {
        var soItemList = AssetDatabase.LoadAssetAtPath("Assets/ScriptableObjectAssets/Item/so_ItemList.asset", typeof(SO_ItemList))
            as SO_ItemList;
        if (!soItemList) return "";
        var itemDetailsList = soItemList.itemDetails;
        var itemDetails = itemDetailsList.Find(x => x.itemCode == itemCode);
        return itemDetails != null ? itemDetails.itemDescription : "";
    }

    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return EditorGUI.GetPropertyHeight(property) * 2;
    }
}

物品管理拾取

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

物品栏UI

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

物品栏刷新

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

弹窗描述

零散

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

未完待续💤