c# - NullReferenceException:对象引用未设置为对象的实例(在我的 PlayerDeath 脚本上)
问题描述
嘿,我对所有东西都是新手,很抱歉,如果这个问题得到了解决,但我在编辑 Unity 的 Platformer 2D 游戏的脚本时遇到了问题。有一个错误,玩家不能死,错误 NullReferenceException:对象引用未设置为对象的实例。但是解决方案。
NullReferenceException: Object reference not set to an instance of an object
Platformer.Gameplay.PlayerDeath.Execute () (at Assets/Scripts/Gameplay/PlayerDeath.cs:20)
Platformer.Core.Simulation+Event`1[T].ExecuteEvent () (at Assets/Scripts/Core/Simulation.Event.cs:57)
Platformer.Core.Simulation.Tick () (at Assets/Scripts/Core/Simulation.cs:114)
Platformer.Mechanics.GameController.Update () (at Assets/Scripts/Mechanics/GameController.cs:40)
这是我的 PlayerDeath
using System.Collections;
using System.Collections.Generic;
using Platformer.Core;
using Platformer.Model;
using UnityEngine;
namespace Platformer.Gameplay
{
/// <summary>
/// Fired when the player has died.
/// </summary>
/// <typeparam name="PlayerDeath"></typeparam>
public class PlayerDeath : Simulation.Event<PlayerDeath>
{
PlatformerModel model = Simulation.GetModel<PlatformerModel>();
public override void Execute()
{
var player = model.player;
if (player.health.IsAlive)
{
player.health.Die();
model.virtualCamera.m_Follow = null;
model.virtualCamera.m_LookAt = null;
// player.collider.enabled = false;
player.controlEnabled = false;
if (player.audioSource && player.ouchAudio)
player.audioSource.PlayOneShot(player.ouchAudio);
player.animator.SetTrigger("hurt");
player.animator.SetBool("dead", true);
Simulation.Schedule<PlayerSpawn>(2);
}
}
}
}
这是我的 Simulation.Event
using System.Collections.Generic;
namespace Platformer.Core
{
public static partial class Simulation
{
/// <summary>
/// An event is something that happens at a point in time in a simulation.
/// The Precondition method is used to check if the event should be executed,
/// as conditions may have changed in the simulation since the event was
/// originally scheduled.
/// </summary>
/// <typeparam name="Event"></typeparam>
public abstract class Event : System.IComparable<Event>
{
internal float tick;
public int CompareTo(Event other)
{
return tick.CompareTo(other.tick);
}
public abstract void Execute();
public virtual bool Precondition() => true;
internal virtual void ExecuteEvent()
{
if (Precondition())
Execute();
}
/// <summary>
/// This method is generally used to set references to null when required.
/// It is automatically called by the Simulation when an event has completed.
/// </summary>
internal virtual void Cleanup()
{
}
}
/// <summary>
/// Event<T> adds the ability to hook into the OnExecute callback
/// whenever the event is executed. Use this class to allow functionality
/// to be plugged into your application with minimal or zero configuration.
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class Event<T> : Event where T : Event<T>
{
public static System.Action<T> OnExecute;
internal override void ExecuteEvent()
{
if (Precondition())
{
Execute();
OnExecute?.Invoke((T)this);
}
}
}
}
}
这是我的模拟
using System;
using System.Collections.Generic;
using UnityEngine;
namespace Platformer.Core
{
/// <summary>
/// The Simulation class implements the discrete event simulator pattern.
/// Events are pooled, with a default capacity of 4 instances.
/// </summary>
public static partial class Simulation
{
static HeapQueue<Event> eventQueue = new HeapQueue<Event>();
static Dictionary<System.Type, Stack<Event>> eventPools = new Dictionary<System.Type, Stack<Event>>();
/// <summary>
/// Create a new event of type T and return it, but do not schedule it.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
static public T New<T>() where T : Event, new()
{
Stack<Event> pool;
if (!eventPools.TryGetValue(typeof(T), out pool))
{
pool = new Stack<Event>(4);
pool.Push(new T());
eventPools[typeof(T)] = pool;
}
if (pool.Count > 0)
return (T)pool.Pop();
else
return new T();
}
/// <summary>
/// Clear all pending events and reset the tick to 0.
/// </summary>
public static void Clear()
{
eventQueue.Clear();
}
/// <summary>
/// Schedule an event for a future tick, and return it.
/// </summary>
/// <returns>The event.</returns>
/// <param name="tick">Tick.</param>
/// <typeparam name="T">The event type parameter.</typeparam>
static public T Schedule<T>(float tick = 0) where T : Event, new()
{
var ev = New<T>();
ev.tick = Time.time + tick;
eventQueue.Push(ev);
return ev;
}
/// <summary>
/// Reschedule an existing event for a future tick, and return it.
/// </summary>
/// <returns>The event.</returns>
/// <param name="tick">Tick.</param>
/// <typeparam name="T">The event type parameter.</typeparam>
static public T Reschedule<T>(T ev, float tick) where T : Event, new()
{
ev.tick = Time.time + tick;
eventQueue.Push(ev);
return ev;
}
/// <summary>
/// Return the simulation model instance for a class.
/// </summary>
/// <typeparam name="T"></typeparam>
static public T GetModel<T>() where T : class, new()
{
return InstanceRegister<T>.instance;
}
/// <summary>
/// Set a simulation model instance for a class.
/// </summary>
/// <typeparam name="T"></typeparam>
static public void SetModel<T>(T instance) where T : class, new()
{
InstanceRegister<T>.instance = instance;
}
/// <summary>
/// Destroy the simulation model instance for a class.
/// </summary>
/// <typeparam name="T"></typeparam>
static public void DestroyModel<T>() where T : class, new()
{
InstanceRegister<T>.instance = null;
}
/// <summary>
/// Tick the simulation. Returns the count of remaining events.
/// If remaining events is zero, the simulation is finished unless events are
/// injected from an external system via a Schedule() call.
/// </summary>
/// <returns></returns>
static public int Tick()
{
var time = Time.time;
var executedEventCount = 0;
while (eventQueue.Count > 0 && eventQueue.Peek().tick <= time)
{
var ev = eventQueue.Pop();
var tick = ev.tick;
ev.ExecuteEvent();
if (ev.tick > tick)
{
//event was rescheduled, so do not return it to the pool.
}
else
{
// Debug.Log($"<color=green>{ev.tick} {ev.GetType().Name}</color>");
ev.Cleanup();
try
{
eventPools[ev.GetType()].Push(ev);
}
catch (KeyNotFoundException)
{
//This really should never happen inside a production build.
Debug.LogError($"No Pool for: {ev.GetType()}");
}
}
executedEventCount++;
}
return eventQueue.Count;
}
}
}
这是我的游戏控制器
using Platformer.Core;
using Platformer.Model;
using UnityEngine;
namespace Platformer.Mechanics
{
/// <summary>
/// This class exposes the the game model in the inspector, and ticks the
/// simulation.
/// </summary>
public class GameController : MonoBehaviour
{
public static GameController Instance { get; private set; }
//This model field is public and can be therefore be modified in the
//inspector.
//The reference actually comes from the InstanceRegister, and is shared
//through the simulation and events. Unity will deserialize over this
//shared reference when the scene loads, allowing the model to be
//conveniently configured inside the inspector.
public PlatformerModel model = Simulation.GetModel<PlatformerModel>();
void Start()
{
Screen.SetResolution(1280, 720, true);
}
void OnEnable()
{
Instance = this;
}
void OnDisable()
{
if (Instance == this) Instance = null;
}
void Update()
{
if (Instance == this) Simulation.Tick();
}
}
}
解决方案
我正在处理同样的问题。当我们编写自定义播放器而不是使用默认播放器时,问题通常开始。
至少对于 2D Platformer 微游戏,您需要做的是打开名为“GameController”的 GameObject(应该在顶部)并将您的玩家*拖到 GameController 组件下的 Player 字段中。
*具体来说,具有您的 PlayerController 组件的 GameObject。
(我真的很喜欢 Unity 潜入像 Qt 这样的完整事件系统,但是这个 Simulation 课程对我来说似乎非常复杂和不优雅。随着我的进步,我们会看到我的想法;但我怀疑它是否比它更有效缓存的 GameObject.Find 调用。)
那应该可以解决您的问题!
推荐阅读
- oracle - 常用的删除表代码对我不起作用
- wordpress - 汉堡切换按钮在本地工作,但在部署时不工作
- ruby - 如何使用页面对象获取基于表中其他元素的元素列表?
- html - 通过css更改默认文本不起作用
- c++ - 本例中的“别名本地 shared_ptr”是什么
- laravel - 存储到一对多多态关系中的特定 id
- python-3.7 - 如何计算python中一个句子中有多少个特定字母?
- amazon-web-services - 即使删除的消息等于收到的消息,SQS 大小也会增加
- java - JComboBox.setSelectedItem 的行为与空值不同
- angular - 如何在不同的地方更改组件的样式?