Unity脚本API笔记

Unity之脚本API笔记一(Transform详解及使用方法) 一、什么是Transform? 场景中的每一个物体都有一个Transform,用于存储和操作对象的位置、旋转和缩放,存在层级关系(父级和子级)。 二、常用变量与属性(位置) 1.位置 position:世界坐标 localposition:相对坐标(父物体就是世界,相对于父物体的坐标) private void OnGUI() { //世界坐标 GUILayout.Label(string.Format("Transform's position:{. 阅读详情

关于GameObject.Find()

找的范围是场景内任何GameObject,包括子物体,不一定是根物体。

所以如果某管理器想给一类对象弄根物体,用这个可能找到一个非根物体的物体。不要用这个。直接在Awake里创建。

Debug DrawRay()看不到射线

原因:Gizmos没开。

Screen.width/Screen.height得到0

打印屏幕的长宽比:

输出是0:

原因:这两个值是int,要计算它们的比需要转换成float。

在脚本里判断一个引用的对象是在Scene里的实例还是预制体

脚本里开放的对象引用在Inspector里显示为接口,可以把Scene里的物体实例拖进去,也可以把Assets里的预制体拖进去,但是预制体不实例化是不能用的。脚本需要能判断它是实例还预制体。

判断gameObject.scene.name==null,Assets里的预制体这个值是Null,场景里的是场景的名字。

Invoke()

只能调用没有参数的方法,即使有默认值的参数也不行。

UnityEvent

UnityEvent引用在Inspector里是这样的:

可以在这个列表里添加某对象的某方法,列表里的方法叫“监听器”(Listener)。手拖进去的叫持久性监听器。takeAction.AddListener()可以在脚本里添加监听,叫非持久性监听器,不显示在Inspector里。

执行takeAction.Invoke()会执行这个列表里的所有方法。

背包系统关于List Remove()的报错

这个错误是测试背包系统,拿起物品时出现的,是偶发的。点击可拾取物品里的物品时,会从背包的可拾取物品List里删除相应的元素,此时有概率报错:ObjectDisposedException: SerializedProperty itemsInReach.Array.data[3] has disappeared!

但是可拾取物品的List的元素被正常删掉了:

拿起最后一个物品时有概率报错:InvalidOperationException: The operation is not possible when moved past all properties (Next returned false)。此时最后一个物品在可拾取物品List里的元素不会被删掉,它指向的物品已经删掉了,所以这个元素变成一个空引用。

新发现:测试的时候如果Inspector没有看着背包脚本或背包脚本折叠起来就从来不会报这个错。我又看到报错是关于UnityEditor的,应该是脚本的接口更新显示相关的报错。不是我的脚本的错误,所以这个报错我不打算管了。

定义一个脚本后报错The namespace '<global namespace.' already contains a definition for 'xxx'

脚本里的类和脚本重名就报错,不重名反而没问题。

原因:我已经定义了这个名字的类,但是我忘了。

写了ContextMenu,但是Inspector脚本的菜单里没有

原因:加ContextMenu的方法都不能有参数!否则不出现!

NotSupportedException: Specified method is not supported.

这个报错可能对应不止一种具体错误。。

我出问题的那一行是这样:

经过检查是因为这个Resources.Load()路径的资源不存在。但是报的这个错我完全看不懂。我决定不在静态类里用字符串记录预制体路径了,还是往检查器里拖。

Destroy()和DestroyImmediate()

Destroy()是把物体放在一个缓冲区,下一帧删除,所以只能在Play Mode使用,在编辑状态要使用DestroyImmediate()。因为这个区别,删除所有子物体时GetChild()的效果不一样,Play Mode在一个方法里执行Destroy()前后GetChild()返回的值没有变化,可以用GetChild(i),

编辑模式执行DestroyImmediate()后后面的子物体向前进一位,循环删除应该用GetChild(0).

充分利用动画状态机的逻辑

这个问题在射击游戏笔记里说过,一些代码的执行时机其实刚好就是动画状态机的一些状态切换时。

比如人物跑步、换弹、趴下、站起时不允许瞄准、射击,与其在玩家输入这些动作时判断,其实这些刚好就是人物离开持枪状态的去向,直接写在持枪状态的OnStateExit()即可。

再比如更新枪和子弹的信息,需要执行的时机有很多:从身上取枪时、交换枪时、拿起手里枪对应的弹匣时、换弹时、射击时、放下手里的枪时、放下手里枪的弹匣时、改变自动方式时。其中除了射击、改变自动方式,其他要更新的时机都不在持枪状态,那么就可以写在持枪状态的OnStateEnter()。

用lambda表达式给按钮传递带参数的回调方法onClick.AddListener(()=>...)

如果lambda表达式用到了循环变量,一定要临时声明一个局部变量记录它。否则lambda表达式将使用循环结束后的循环变量。

rigidBody.velocity和animator.velocity的关系

二者共同决定了物体的速度,二者没什么关系。使用animator的Root Motion驱动角色移动时,刚体的velocity是0,除了角色下坡时,刚体的重力起作用,会有一点向下的速度;角色上坡被地面顶向上时,刚体并没有向上的速度。

动画事件方法和有参数方法的矛盾

动画事件方法不能有参数,但是想让一个方法既当动画事件又被其他地方调用,且其他地方有参数会很方便。

可以写一个无参的方法作动画事件,它调用有参的方法,参数写死。

void GrabRifleAnimEvent(){//动画事件里要用这个方法,没法传自定义类型参数
        MoveGunInHand(rifleScript);
    }
    void GrabPistolAnimEvent(){
        MoveGunInHand(pistolScript);
    }
    void MoveGunInHand(Item item){
        item.transform.SetParent(rightHand);
        item.transform.localPosition=Vector3.zero;
        item.transform.localEulerAngles=gunEulerInHand;
    }

特性相关方法

查询有没有特性:

type.IsDefined(typeof(自定义特性类型),bool 是否查找继承链)

得到特性类:

type.GetCustomAttributes(bool 是否查找继承链)

异步加载场景在Hierarchy出现好几个场景名(Is loading)

代码:

public Slider loadSceneProgress;
    void Start()
    {
        DontDestroyOnLoad(gameObject);
        // DontDestroyOnLoad(loadSceneProgress.gameObject);
    }
    public string sceneName;
    AsyncOperation asyncOperation=null;
    public void StartLoadAsync(){
        asyncOperation=SceneManager.LoadSceneAsync(sceneName);
        StartCoroutine(MyLoadSceneAsync(null));
    }
    IEnumerator MyLoadSceneAsync(UnityAction Preset){
        while(!asyncOperation.isDone){
            loadSceneProgress.value=asyncOperation.progress;
            Debug.Log(asyncOperation.progress);
            yield return null;
        }
        loadSceneProgress.value=1;
        Preset();
    }
void OnTriggerEnter(){
        Instance.StartLoadAsync();
    }
}

原因:异步加载是通过OnTriggerEnter()触发的,加载中每一帧都执行一次OnTriggerEnter()。

缓冲池

看的教程里缓冲池包含一个字典,键是字符串,值是存放缓冲物体的父物体。

Dictionary<string,GameObject> bufferDict=new Dictionary<string,GameObject>();

觉得这样写字符串容易打错,不如用枚举。

延迟放入缓冲池使用协程,yield return new WaitForSeconds()后面没有执行

public IEnumerator EnpoolLater(GameObject instance,BufferType bufferType,float delay){
        yield return new WaitForSeconds(delay);
        if(bufferType==BufferType.impact){
            Debug.Log("击中效果进入");
        }
        Enpool(instance,bufferType);
    }

原因:放入的是击中效果,由子弹触发,但是子弹在执行Enpool()前被销毁了。又把子弹的销毁改成不活动,也不能执行延迟入池。

所以延迟入池的代码还是要放在被入池物体的OnEnable()里,不要让其他物体触发。其他物体可能提前就被销毁或失活了。

射击游戏弹头缓冲池的问题

射击游戏的弹头、击中效果、弹壳都使用缓冲池存储,出现了击中效果不在瞄准位置的问题。

[RequireComponent(typeof(CapsuleCollider))]
[RequireComponent(typeof(Rigidbody))]
public class MyBullet : MonoBehaviour
{
    public LayerMask bulletLayerMask;
    int groundLayer=8;
    public WeaponData weaponDamageData;
    Vector3 lastFramePosition;
    float lifeTime=1;
    void Start(){

    }
    void OnEnable(){
        lastFramePosition = transform.position;
        StartCoroutine(BufferPoolBase.Instance.EnpoolLater(gameObject,BufferPoolBase.BufferType.bullet,lifeTime));
    }
    RaycastHit raycastHit;
    BodyTrigger bodyTrigger;
    ImpactEffectRecorder myImpactEffect;
    void Update(){
        if(Physics.Linecast(lastFramePosition,transform.position,out raycastHit,bulletLayerMask,QueryTriggerInteraction.UseGlobal)){
            if(raycastHit.collider.gameObject.layer==groundLayer){//打到地
                if(raycastHit.transform.TryGetComponent(out myImpactEffect)){
                    GameObject effectInstance;
                    effectInstance=BufferPoolBase.Instance.Depool(myImpactEffect.impactEffectPrefab.gameObject,BufferPoolBase.BufferType.impact);//缓冲池出池
                    effectInstance.transform.position=raycastHit.point;
                    effectInstance.transform.LookAt(raycastHit.point+raycastHit.normal);
                }
                else{
                    Debug.Log(raycastHit.transform.name+"没有击中效果!");
                }
            }
            else{
                if(raycastHit.collider.TryGetComponent(out bodyTrigger)){
                    bodyTrigger.GetHurt(weaponDamageData);
                }
            }
            BufferPoolBase.Instance.Enpool(gameObject,BufferPoolBase.BufferType.bullet);
        }
    }
}

弹头脚本有一个Vector3记录上一帧的位置,每一帧的位置和上一帧的位置使用Physics.Linecast()检测击中。在OnEnable()里记录了一次当前位置,但是这个代码是在把弹头放到枪口之前的,导致击中检测的连线是从上一次子弹入池的位置到枪口。

解决方法:给Depool()加一个参数Vector3 position,先设置位置再激活。理论上所有物体都应该这样,但是其他物体影响不大,弹头必须这样。修改后的Depool():

Dictionary<BufferType,Transform> bufferDict=new Dictionary<BufferType,Transform>();
    //可能的情况包括没缓冲池、有缓冲池没物体(一般不会有,但理论上可能)、有缓冲池有物体
    public GameObject Depool(GameObject prefab,BufferType bufferType,Vector3 position){
        GameObject instance;
        if(bufferDict.ContainsKey(bufferType)){//缓冲池已建立
            if(bufferDict[bufferType].childCount>0){//缓冲池里有物体
                instance=bufferDict[bufferType].GetChild(0).gameObject;//取出
                instance.transform.SetParent(null);//解绑
            }
            else{//有缓冲池没物体(曾经放入过物体,又拿出了)
                instance=GameObject.Instantiate(prefab);
            }
        }
        else{//没有缓冲池
            Transform bufferTransform=new GameObject(bufferType.ToString()).transform;
            bufferDict.Add(bufferType, bufferTransform);
            instance=GameObject.Instantiate(prefab);
        }
        instance.transform.position = position;
        instance.SetActive(true);//激活
        return instance;
    }

这样写第一个弹头还是有问题,第一个弹头的OnEnable()在Instantiate()时执行,在设置位置之前,导致记录的上是帧位置是(0,0,0)。所以又在需要Instantiate()的参数里加上了position和一个Quaternion.identity。

总结:缓冲池的严谨写法是在SetActive()之前就设置好位置,如果是实例化,则在Instantiate()参数里加上位置。对大部分物体这可能只是锦上添花,但是对弹头实体,需要记录上一帧位置,通过Physics.Linecast()判断击中时,必须这样写。

NullReferenceException: SerializedObject of SerializedProperty has been Disposed.

背景:写了两个带[ExecuteAlways]的脚本,开始运行后无限报错,停止运行也报错。把一个脚本删掉,执行,不报错,再撤销删除脚本,再执行,也不报错。

DontDestroyOnLoad的单例物体在返回一个场景时怎么防止出现两个

这是一个跨场景的单例,可以在Start()里判断:

1.单例是空,把自己写入;

2.单例非空&&单例不是自己,把自己销毁;

3.它不能继承泛型单例基类,因为判断单例为空不能使用Instance,否则就像其他类调用它一样触发“要么新建要么报错”的自动操作,需要使用instance,那么这个私有instance必须写在自己里;

public class MySceneManager : MonoBehaviour
{
    private static MySceneManager instance;
    public static MySceneManager Instance{
        get{
            if(!instance){
                #if UNITY_EDITOR
                Debug.Log($"场景里没有MySceneManager实例!");
                if (UnityEditor.EditorApplication.isPlaying)
                {
                    UnityEditor.EditorApplication.isPlaying = false;
                }
                #endif
            }
            return instance;
        }
    }
    void Start()
    {
        if(!instance){
            instance=this;
        }
        else if(Instance!=this){
            Destroy(gameObject);
        }
        DontDestroyOnLoad(gameObject);
    }
}

异步加载场景从第二个场景回第一个场景后IEnumerator的一些代码没有执行,且新场景的动画没有播放,物理引擎无效

原因:从游戏场景加载回来是经过了暂停界面,执行了Time.timeScale=0;没复原。

为什么只有匿名函数、lambda表达式会出现闭包

匿名函数、lambda表达式支持在一个函数内定义,在这个函数外执行。而C#虽然允许函数内定义函数,却不允许函数内定义,函数外执行。委托装匿名函数、lambda表达式是个例外。

玩家人物对界面的修改代码应该放在哪里

游戏界面有武器信息、交互动作等信息。玩家人物在某些情况下会修改这些信息,修改的代码可以防止人物脚本里(游戏里不管是不是玩家人物都有人物脚本),人物脚本知道该修改信息的时间,但不知道这个人物是不是玩家需要判断一下;也可以放在某个管理器单例里,这样不用判断某个人物是不是玩家,但又不知道该修改信息的时间,只能在Update()里一直监视。

通过反射判断数据类型使用相应的PlayerPrefs读取数据失败

object LoadValue(Type fieldType,string keyName){
        if(fieldType==typeof(int)){
            return PlayerPrefs.GetInt(keyName);
        }else if(fieldType==typeof(float)){
            return PlayerPrefs.GetFloat(keyName);
        }else if(fieldType==typeof(string)){
            return PlayerPrefs.GetString(keyName);
        }else if(fieldType==typeof(bool)){
            return PlayerPrefs.GetString(keyName)=="T"?true:false;
        }
        return null;
    }

原因:调用上面的函数时传入了fieldInfo.GetType(),应该传fieldInfo.FieldType。

static readonly的坑

static readonly会在构造函数之后执行。如果构造函数里用到了,应该改用const。

public class GameDataManager
{
    static GameDataManager instance=new GameDataManager();
    public static GameDataManager Instance{get=>instance;}
    static readonly string audioDataKey="AudioData";
    public AudioData audioData;
    GameDataManager(){
        audioData=PlayerPrefsDataManager.Instance.Load(typeof(AudioData),audioDataKey) as AudioData;
        if((!audioData.notFirst)){
            audioData.notFirst=true;
            audioData.soundVolume=1;
            audioData.musicVolume=1;
            audioData.musicOn=true;
            audioData.soundOn=true;
            PlayerPrefsDataManager.Instance.Save(audioData,audioDataKey);
            Debug.Log(audioDataKey);
        }
}

让编辑器停止播放

#if UNITY_EDITOR
    if (UnityEditor.EditorApplication.isPlaying){
        UnityEditor.EditorApplication.isPlaying = false;
    }
#endif

List自带的排序方法的输入方法怎么记

返回1代表排后面,-1代表排前面。

rankData.Sort((a,b)=>{
            if(a.passTime>b.passTime){
                return 1;
            }
            return -1;
        });

ScriptableObject实例检查器显示Script是None

原因:这个脚本里写了两个类,一个继承ScriptableObject,还有另一个。但是脚本名字和继承ScriptableObject的类不同名。

代码刷新Asset文件夹

AssetDataBase.Refresh();

玩家重生时把玩家摆到重生位置的代码没有生效

public void RespawnPlayer(){
        // firstPersonCam.transform.parent=null;
        // aimCam.transform.parent=null;
        Cursor.lockState=CursorLockMode.Locked;
        player.transform.position=respawnPosition.position;
        player.transform.eulerAngles=new Vector3(0,player.transform.eulerAngles.y,0);
        if(player is Character1){
            (player as Character1).rigidbody=player.GetComponent<Rigidbody>();
            (player as Character1).rigidbody.constraints=
            RigidbodyConstraints.FreezeRotationY|
            RigidbodyConstraints.FreezeRotationZ|
            RigidbodyConstraints.FreezeRotationX;
        }
        player.animator.Rebind();
        if(playerInitialLife<=0){
            playerInitialLife=100;
        }
        player.life=playerInitialLife;
        PanelGame.Instance.UpdateLifeBar(player.life);
        InteractionManagerOverlap.Instance.Enable();
    }
public void Revive(){//游戏结束界面按重生执行的
        MyGameManager.Instance.RespawnPlayer();
        Destroy(gameObject);
    }

 把RespawnPlayer用ContextMenu开放出来执行有效。把重生按钮的回调改成只修改玩家位置,也无效。人物死后把CharacterController移除,再点重生,问题没有了。又是这玩意搞的鬼。所以发现有CharacterController时,通过UGUI按钮回调改变人物位置会无效。

脚本里开放的UnityEvent字段后来删除了但是还留在检查器面板?

类的静态变量写入后,没有读取一下直接用用的就是默认值?

登录时写入了当前用户名。读取数据时还是打印读取admin的数据。写入用户名时打印一下,然后就读取登录的用户的数据了?

public static string nowUser="admin";
DataManager.nowUser=inputFieldName.text;
// Debug.Log(DataManager.nowUser);
DataManager.Instance.ReadUserData();
public void ReadUserData(){
        #if UNITY_EDITOR
        Debug.Log(string.Concat("读取用户",nowUser,"的数据"));
        #endif
}

编译预处理#define的名字是否跨脚本存在?

在一个脚本#define了一个名字,另一个脚本判断不存在。全局存在的名字需要在Project settings-Player里加:

不继承MonoBehavior的单例在编辑器里什么时候销毁

每次改完脚本回到编辑器重新编译时销毁。

Resources.Load()

输入的路径不能有后缀名。

Instantiate()和Start()的时间关系

在一个函数里实例化一个对象A,这个对象的Start()里实例化一个对象B,同一个函数里后面执行Init(),有对B的身上的Text的写入,报空。实例化后Start()好像在实例化所在的代码块之后执行。总之把Start()的内容也放进Init()吧。

基类的一个虚函数的代码,子类需要的代码大部分和base一样,中间小部分不一样,怎么设计?

可以直接打破虚函数-重写的设计,把子类和基类相同的代码另放一个函数A,基类原来的虚函数叫B,子类重写的叫C,基类不一样的代码是D,子类的是E。

基类:

B(){
    D();
    A();
}

子类:

C(){
    E();
    A();
}

模块代码分布的问题

一些模块的功能需要巨量代码,比如人物,需要移动旋转、动画控制、地面检测、交互检测(拾取、对话、开门、上载具)、拥有的物品管理(拾取、放下、使用)、对武器的管理……

这巨量代码的分布,有3种方案:

  1. 写在一个脚本,形成一个巨大的脚本;
  2. 分成不同类,放在不同脚本,有一个中心类,有[RequireComponent()]特性把需要的脚本自动挂载。子模块互相访问要借助中心类。中心类可以引用子模块来控制子模块的初始化顺序,也可以让子模块都弄单例。
  3. 用partial class,类是一个类,代码在不同脚本;

1方案最差,先排除。

2:如果是中心类引用子模块,子模块经由中心模块相互访问,调用链比较长。

3:显得没有2那么模块化,类还是一个,只是脚本模块化了。模块间访问更方便。还有一个问题就是点进类的脚本时无法一步到位。

类的初始化时成员类的初始化问题

对于一个类里的引用型成员,在定义里写上new,可以在new这个类时把里面的引用型成员也都new了,避免报空。

[Serializable]
    public class UserPropsData{
        public List<int> ownedCharacters=new List<int>();
        public int characterIndex;
        public List<int> ownedRifles=new List<int>();
        public int rifleIndex=-1;
        public List<int> ownedHandguns=new List<int>();
        public int handgunIndex=-1;
        public ItemsInPack inventoryProps=new ItemsInPack();
        public ItemsInPack packProps=new ItemsInPack();
        public int money;
    }
userPropsData=new UserPropsData();

字段初始化值修改后在Unity编辑器的实例无效的问题

字段初始化在Unity里创建场景实例、创建预制体时就会生效,再改初始化值,初始化不会再执行,也不会生效。

检查器的成员class何时为null?

定义一个class A,加上[Serializable],作为一个MonoBehavior的成员,加到游戏对象上,打印A,发现不为空。如果打印前设null,则为空。而且在检查器上看空对象和全部值为默认值的对象是一样的。

[ContextMenu("为空???")]
        void P()
        {
            // otherPack = null;
            Debug.Log(otherPack);
            Debug.Log(otherPack == null);
        }

如果把设null放在另一个函数D,打印放在另一个函数P,先执行D,再执行P,也不为空。只能在打印前一行设null才打印出来空,好像在编辑器里游戏对象上依附的组件的成员类如果为空都会被立即分配对象。

[ContextMenu("设空")]
        void D()
        {
            otherPack = null;
        }
        [ContextMenu("为空???")]
        void P()
        {
            // otherPack = null;
            Debug.Log(otherPack);
            Debug.Log(otherPack == null);
        }

总之不要通过MonoBehavior成员类是否为空判断任何信息,永远当它不为空。

Debug.Log打印一个实例打出Null和null有什么区别???

背景

我写了一个检查单例如果已经存在,则把正在初始化的单例销毁

protected virtual void Awake(){
                Debug.Log(instance);
                Debug.Log(instance==null);
        if (instance == null)
            {
                instance = this as T;
            }
            else if (instance != this as T)
            {
#if UNITY_EDITOR
                Debug.Log(typeof(T) + "已存在!");
#endif
                Destroy(gameObject);
            }
    }

然后发现打印instance有Null和null,其中打印null时instance==null会判false。这里它把我的任务面板销毁了,其他单例面板都没有销毁。我想知道任务面板单例是怎么变成null的。

退出关卡回到主菜单后打印任务面板单例和HUD面板单例:

这里我能想到的任务面板的特殊就是它有一个协程更新任务目的地标志,在OnDestroy里停止了这个协程。然后我把停止协程的代码放到OnDisable,问题就解决了。

然后发现单例基类写了OnDestroy,里面会把单例置空。

void OnDestroy(){//直接销毁一个单例的父物体时,它也会被销毁,防止出现instance指向已销毁对象
        if(instance==this as T){//多余的单例自我销毁时,不能置空单例
            instance=null;
        }
    }

如果子类直接也写OnDestroy,就执行子类的,不会执行这个置空。

我的实验

发现Monobehavior变量在检查器是None时打印Null,是Missing时打印null。可以用来区分变量是被赋值过null(打印Null)还是只把指向的对象销毁了(打印null)。

游戏对象变量是None时也打印null,不管是None还是Missing都打印null。

2025.10.15今天做实验的结论是:一个字段只有开放到检查器后未赋值前打印为Null,赋值后再不管置空还是对象销毁都打印null,总之就是Null很难打印出来。想再看见Null只能把这个字段删掉,重编译,再加上,再重编译。

实现在检查器能配置的多选枚举

1. 定义带Flags特性的枚举‌

[System.Flags]
public enum MyEnum
{
    None = 0,
    Option1 = 1 << 0,  // 1
    Option2 = 1 << 1,  // 2
    Option3 = 1 << 2,  // 4
    Option4 = 1 << 3   // 8
}

‌2. 在Inspector面板显示多选‌
创建自定义属性绘制器:

using UnityEditor;
using UnityEngine;

public class EnumMultiAttribute : PropertyAttribute { }

[CustomPropertyDrawer(typeof(EnumMultiAttribute))]
public class EnumMultiAttributeDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        property.intValue = EditorGUI.MaskField(position, label, property.intValue, property.enumNames);
    }
}

‌3. 在MonoBehaviour中使用‌

public class TestScript : MonoBehaviour
{
    [EnumMultiAttribute]
    public MyEnum myEnum;
    
    // 判断是否选中某个枚举值
    public bool IsSelected(MyEnum type)
    {
        int index = 1 << (int)type;
        return ((int)myEnum & index) == index;
    }
}

关键要点

  • ‌[Flags] 特性‌:允许枚举进行位运算,支持多选功能1
  • ‌2的幂次赋值‌:每个枚举值赋予2的幂次数值,避免值冲突1
  • ‌位运算检查‌:使用按位与运算判断是否选中特定选项

build版本中某个函数的输入参数对象有概率从某一行开始变成null

下面从if (!chasingMission){}后面mission在build版本有概率变成null。这个函数是通过事件中心触发事件执行的,vs附加到进程发现调用时它所属的对象已经是null。

public void OpenMission(MissionBase mission){
    if (missionList.Contains(mission))
    {
        return;
    }
    missionList.Add(mission);
    UpdateMissionList();
    ShowMissionOpen(mission);
    if (!chasingMission)
    {
        chasingMission=mission;
    }
    Debug.Log("任务是" +mission);
    Debug.Log("任务对象是" + mission.gameObject);
    mission.gameObject.SetActive(true);
    missionMarker.gameObject.SetActive(true);
}

原因:它的类写了OnDestroy()方法,覆盖了基类的OnDestroy(),导致移除监听的代码没有执行。第二次进这个关卡时执行的还是上一次关卡添加的监听,它的对象已被销毁。对此我们可以总结一下观察者模式忘记移除监听的问题的特征:对象报空,且不是第一次进行某过程时出现,而是重复进行某过程(一般是重复进入某场景)时出现。

游戏切后台时自动暂停

用到了游戏切后台的生命周期函数OnApplicationFocus。

还要排除已经有关卡结算面板、游戏结束面板的情况,因为这些情况不能暂停。如果弹出暂停面板,会把光标隐藏,点不了结算面板。

private void OnApplicationFocus(bool focus)
{
    if (!focus)
    {
        //如果已经显示鼠标(关卡结算、游戏结束界面)就不要再暂停了!
        if (Cursor.lockState == CursorLockMode.Locked)
        {
            Pause();
        }
    }
}

Debug.LogError后面的代码会不会执行

会。

if (GUILayout.Button("校验资源"))
{
    Debug.LogError("错误!!!");
    Debug.Log("哈哈哈哈");
}

    OnDrawGizmos Gizmos.DrawSphere没生效

    测试了一会搞出来个这个:

    private void OnDrawGizmos()
    {
        Gizmos.color = gizmosColor;
        Gizmos.DrawSphere(worldOri, gizmosRadius);
        Gizmos.DrawSphere(worldUL, gizmosRadius); 
        Gizmos.DrawSphere(worldLL, gizmosRadius);
        Gizmos.DrawRay(worldOri, Vector3.down * 2);
    }

    是颜色不透明度被设置0了。

    协程:StartCoroutine=分头行动;yield return=原地等待

    unity(3)笔记09-Unity脚本:脚本介绍,*脚本生命周期&常用事件*,调试,常用API,Component,Transform,GameObject,Object,Time,预制件 av28779788?p=112视频学习笔记 当前为-->第三部分:Unity脚本 脚本介绍 脚本 开发工具 几个常用脚本标签注意:unity中的构造函数会执行2次而不是1次!因此我们不用构造函数来初始化. *脚本生命周期&常用事件* 调试 Unity脚本生命周期示意图 常用API Compone... 阅读详情

    相关推荐

    unity笔记

    unity笔记。

    Yamoonn的博客 2455

    Unity中的GUIStyle错误:SerializedObject of SerializedProperty has been Disposed.

    GUIStyle错误:SerializedObject of SerializedProperty has been Disposed.

    qingxiu3733的博客 4074

    Unity入门笔记体系构建:从碎片化学习到系统化知识库

    在游戏开发与计算机图形学领域,系统化知识管理是提升学习效率与工程实践能力的关键。其核心原理在于通过结构化笔记方法,将孤立的技术概念、API原理与项目实践串联成可复用工作流,从而解决碎片化学习导致的认知断层问题。这一方法的技术价值在于构建个人化、可检索的知识引擎,显著降低开发中的重复试错成本,并加速从理论到产出的转化过程。在Unity引擎学习这一具体应用场景中,开发者常面临预制体、协程、物理系统等概念理解与实操脱节的挑战。通过建立基于问题驱动的三维度笔记架构——涵盖概念本质、操作步骤与项目决策——并借助Obs

    weixin_30542979的博客 314

    Unity Editor 编辑器拓展 11——SerializedProperty 和 SerializedObject 的使用

    在自定Inspector面板的时候,经常会使用到SerializedProperty 和 SerializedObject。使用这两个类可以十分方便的进行数据的显示与处理。他们自带撤销记录。使用的Unity内置的设定序列化数据。与别的GUI控件搭配十分相称。 SerializedProperty的坑 其在调用DeleteArrayElementAtIndex()的时候不会自动调整列表,而是会在index处留下null。 所以要想删除列表中的某个元素,需要对同一个Index...

    qq_14812585的博客 4443

    Unity脚本介绍和常用API

    脚本介绍 1.脚本 介绍 脚本是附加在游戏物体上用于定义游戏对象行为的指令代码 Unity支持三种高级编程语言: C#、JavaScript 和 Boo Script(unity4以前支持的) 语法结构 using 命名空间; public class 类名:MonoBehaviour { void 方法名() { Debug.Log("调试显示信息"); print("本质就是Debug.Log方法"); } } 文件名与类名必须一致(如果

    weixin_52804684的博客 2836

    Unity零基础到入门 ☀️| Unity从青铜 到 王者!只差这篇让你学会Unity中最重要的部分——脚本组件✨

    从 倔强青铜 到 荣耀王者!只差这篇让你学会Unity中最重要的部分——脚本组件✨。本篇内容对Unity脚本做了一个超级详细的介绍,从开发工具到具体实例,API等都有介绍。

    努力前行,总会成为自己心中的那道光 9万+

    unity中的SerializedObject和SerializedProperty类

           SerializedObject和SerializedProperty是提供通用的方式编辑对象属性的类,可自动处理预制件和UI。01         构造函数接受Object对象或者数组。另一种额外接受一个Object对象。          isEditingMultipleObject() 是否可以序列化多对象。maxArraySizeForMultiEditing() ...

    Tong1993222的博客 1万+

    一个步骤跳过 Unity 启动Logo | 多平台适用 | 官方API支持

    众所周知,使用Unity引擎打包的工程在启动时都带有Unity的默认启动Logo。 这个问题可以通过购买Unity专业版以及零元购解决,但是对于多数人来说一般不会使用这种方法。 那本篇文章就来使用一种更简单的方法来直接去掉启动Logo,只需要一个脚本即可完成。

    努力前行,总会成为自己心中的那道光 8万+

    Unity SerializedProperty

    using System; using UnityEditor; using UnityEngine; [CustomEditor(typeof(WeaponTest))] public class WeaponEditor : Editor { public override void OnInspectorGUI() { base.OnInspectorGUI(); SerializedProperty etValue = serializedObjec

    游戏人生 1598

    骁龙X Elite笔记本重装Windows 11 Arm64系统完整指南与避坑手册

    UEFI安全启动和Arm64架构是理解现代计算设备系统部署的基础。UEFI取代传统BIOS,提供了更安全的启动环境和模块化固件接口,而Arm64架构则以其高能效特性,在移动和新兴PC领域日益重要。其技术价值在于为设备带来更快的启动速度、更强的安全防护以及更优的功耗表现。在应用场景上,这直接关系到搭载高通骁龙X Elite等Arm处理器的笔记本电脑的系统安装与维护。对于这类设备,重装系统远非插入通用安装盘那么简单,核心挑战在于必须使用专为Arm64编译的Windows镜像,并需提前集成芯片组、网络等硬件的专属

    weixin_33428613的博客 485

    一种解决STM32多串口同时收发的方法

    在做项目中,遇到了同时调用串口通信时程序崩溃的问题,在项目中,串口1用作调试串口,串口2用作MQTT通信串口,串口3用作下位机通信串口, 串口1重定向以后,用库函数自带的printf函数打印字符串 串口2使用自己写的u2_printf函数,即va_list这套变参宏定义后使用vsprintf函数打印字符串 串口3刚开始使用串口2一样的打印方式,程序在调试调用到串口3打印函数时直接崩溃,后续将串口3改用dma转存输出,仍然无法正常调用 多次研究以后,采用三个串口都使用重定向后的库函数自带的printf

    Zxp0605的博客 8796

    SerializedProperty类

    一、SerializedProperty类属性及函数 首先提供官方参考文档:API-Reference-SerializedProperty 概述:命名空间在UnityEditor内;SerializedProperty类通常和SerialedObjec以及Editor类一起使用;用于修改属性的内容; 属性 boolValue / floatValue / doubleValue / i...

    mozhi 6115

    RK3588+TRL8367s 四网口千兆交换机配置与优化实战

    本文详细介绍了基于RK3588高性能SoC与TRL8367s千兆交换芯片构建四网口交换机的完整实战流程。内容涵盖硬件连接、设备树配置、内核驱动加载、性能优化及常见问题排查,重点解析了RGMII时序调试、DSA架构配置等关键步骤,为嵌入式网络设备开发提供实用指南。

    weixin_29266647的博客 674

    Unity学习笔记:unity脚本常用API

    给我去查官方文档!! 哈哈开玩笑的,看到这篇文章的想必都是学习Unity脚本的新手,想通过网络资料来了解Unity引擎的常用类及类的一些功能和使用方法。我写这篇文章就是为了告诉各位半路出家的朋友们,学习和使用脚本编程开发最有效的资料就是官方文档!很多新手都没有这个习惯,去网上查资料看的一脸懵逼。 编程开发就像是厨师做饭,你只需要学会 煎炒烹炸、焖溜熬炖 的方法,然后看着菜谱就能...

    自闭的饭盒 857

    (10)Proteus 8.9上的STM32仿真系列(HAL)---freertos

    实验设计:通过cubemxide生成freertos工程,然后创建一个 pc13 led灯间隔200ms闪烁的任务。非常遗憾的是proteus8.9仿真失败,实物上没有问题,但还是把流程过一遍。 一、原理图 实物是淘宝上stm32f103 c8t6的核心板,proteus上没有这个芯片,这里图上大概表示一下,用的是核心板自带的pc13引脚的led灯。 二、cubemxide配置 1.RCC如之前工程一样配置 2、配置SYS timebase Source不要使用systick,自己选择一个定时器,我这

    qq_35442550的博客 7344

    PSF(点扩散函数)与去卷积算法消除图像模糊

    本文旨在总结图像处理中使用去卷积的传统方法来进行图像去模糊的相关知识。

    m0_53607446的博客 6015

    Unity之脚本API笔记一(Resources详解及使用方法,一点关于transform)

    目录 一、Resources (资源) 一、Resources (资源) Resources类允许你按照它们的路径名查找并加载物体。在编辑器中,Resources.FindObjectsOfTypeAll可用于查找assets和Scene对象。所有位于Assets文件夹下名为"Resources"的文件夹中的资源,都可以Resources.Load 函数访问。可能存在多个“资源”文件夹,并且在加载对象时将检查每个文件夹。 在Unity中通常不需要使用路径名来访问资源,相反...

    Anglepoppy的博客 2433

    SAP 更改物料基本计量单位

    创建订单单位后,新创建的采购申请、采购合同、采购信息记录均会使用新的订单单位

    matinal 當冬夜漸暖 。公众号:matinal 6162

    Unity学习笔记 —— c#脚本基础

    作为一个游戏初学者学习的用作记忆的笔记,瞎写的#

    2401_89101588的博客 427

    【华为OD机试】真题A卷-Linux发行版的数量(Python)

    【华为OD机试】真题A卷-Linux发行版的数量(Python):Linux操作系统有多个发行版,distrowatch.com提供了各个发行版的资料。这些发行版互相存在关联,例如Ubuntu基于Debian开发,而Mint又基于Ubuntu开发,那么我们认为Mint同Debian也存在关联。 发行版集是一个或多个相关存在关联的操作系统发行版,集合内不包含没有关联的发行版。给你一个nxn的矩阵isConnected,其中isConnected[i][i]=1表示第i个发行版和第j个发行版直接

    u014481728的博客 944

    看懂 SAP S/4HANA 产品主数据里的 GTIN:一串号码背后的全球商品识别与业务协同

    SAP S/4HANA中的GTIN管理是企业供应链运营的关键环节。本文深入解析GTIN的业务内涵及其在SAP系统中的实现方式:1) GTIN是全球通用的商品标识码,不同于企业内部物料编码;2) SAP主数据中GTIN与计量单位强绑定,需为不同包装层级维护独立GTIN;3) 同一产品可能因渠道、供应商差异存在多个GTIN;4) GTIN贯穿零售POS、仓储管理、EDI订单等核心业务流程;5) 通过典型案例说明错误维护GTIN将导致收银异常、仓库作业低效等运营问题。文章强调GTIN管理需结合包装层级、计量单位等

    2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1433

    unity脚本api_Unity 5:API更改和脚本自动更新

    unity脚本apiAs the breadth of the Unity feature set keeps on growing, and as we build it with more and more engineers at the same time, the necessity for being able to split up the product in separate m...

    culiao6493的博客 1583

    蓝牙模块HM-10手册

    蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

    怎样去查询Unity的API文档

    自从开通了公众号之后,有更多的朋友加我qq来交流问题。这是一个好的事情。在交流的过程中,我发现很多朋友都比较缺乏程序的基础,但又急着拿Unity来做一些作品之类的。所以经常会遇到一些很入门的问题没有办法解决。这些问题基本都几种在写代码的过程中。然后回顾一下之前的文章,感觉都是从美术资源的优化角度去介绍Unity的。所以接下来我打算写一些关于程序方面的事情。 前两篇都是针对程序基础比较薄弱...

    阿赵的博客 1万+

    温度PID控制PLC程序

    温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

    jre9下载 jdk9

    (Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

    笔记12 unity常用的API

    Unity组件生命周期里的方法构造刷新GPU控制的每帧调用一次的刷新CPU控制的固定时间刷新激活一个旧方法销毁新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片 组件 生命周期里的方法 构造 //构造 private void Awake() { Debug.Log("Awake"); } ...

    weixin_45131451的博客 2508

    Unity脚本api网址

    Unity脚本api网址 可用于查询在unity3d开发过程中可能会用到的api。

    云海泛舟 1567

    Unity学习日志_Unity脚本

    Unity学习日志_Unity脚本 1. unity脚本基础介绍 1. unity脚本中需要注意的事项: 一个脚本一个功能。 脚本是附加在游戏对象上用于定义游戏对象行为的指令代码。 文件名必须与类名一致。 写好的脚本必须附加到物体上才可以执行。 附加到游戏对象上的脚本必须继承MonoBehaviour类(单纯的工具类不需要继承此类)。 unity脚本本质是一个c#类。 将unity脚本绑定到游戏对象(也是对象,GameObject类)上本质是创建了一个对象,并将对象的引用存储在游戏对象的“组件管理数组”中

    BraveRunTo的博客 1711
    上一篇: Unity模型笔记
    下一篇: Unity组件笔记
    WarPigs
    博客等级 码龄7年 249粉丝 175原创
    评论
    成就一亿技术人!
    拼手气红包6.0元
    还能输入1000个字符
     
     条评论被折叠 查看
    添加红包

    请填写红包祝福语或标题

    个

    红包个数最小为10个

    元

    红包金额最低5元

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

    抵扣说明:

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

    余额充值