using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
public class DialogueManager : MonoBehaviour
{
public Image backgroundPanel;
public Image avatarImage;
public Text nameText;
public Text messageText;
public Transform optionsPanel;
public Button continueButton;
public GameObject optionButtonPrefab;
private Dictionary<int, DialogueNode> nodeDict = new Dictionary<int, DialogueNode>();
private DialogueNode currentNode;
private bool isTyping = false;
private string fullMessage;
private string currentDialoguePath;
public delegate void DialogueEvent(int nodeId);
public event DialogueEvent OnNodeStart;
public event DialogueEvent OnDialogueEnd;
void Start()
{
continueButton.onClick.RemoveAllListeners();
continueButton.onClick.AddListener(OnContinueClicked);
if (continueButton == null)
{
Debug.LogError("继续按钮未绑定!");
}
}
public void OpenDialogue(string dialoguePath, int startId = 1)
{
currentDialoguePath = dialoguePath;
LoadDialogue(dialoguePath);
gameObject.SetActive(true);
StartCoroutine(StartDialogueRoutine(startId));
}
IEnumerator StartDialogueRoutine(int startId)
{
yield return new WaitForEndOfFrame(); // 确保UI已激活
StartDialogueNode(startId);
}
void LoadDialogue(string path)
{
nodeDict.Clear();
TextAsset jsonFile = Resources.Load<TextAsset>(path);
if (jsonFile == null)
{
Debug.LogError("对话文件未找到: " + path);
return;
}
DialogueTree tree = JsonUtility.FromJson<DialogueTree>(
"{ \"nodes\": " + jsonFile.text + "}"
);
foreach (var node in tree.nodes)
{
nodeDict.Add(node.id, node);
}
}
void StartDialogueNode(int nodeId)
{
if (!nodeDict.ContainsKey(nodeId))
{
EndDialogue();
return;
}
currentNode = nodeDict[nodeId];
OnNodeStart?.Invoke(nodeId);
UpdateUI();
}
void UpdateUI()
{
// 清空选项区域
if (optionsPanel != null)
{
for (int i = optionsPanel.childCount - 1; i >= 0; i--)
{
Destroy(optionsPanel.GetChild(i).gameObject);
}
}
foreach (Transform child in optionsPanel)
{
Destroy(child.gameObject);
}
nameText.text = string.IsNullOrEmpty(currentNode.name) ? "" : currentNode.name;
fullMessage = currentNode.msg;
if (!string.IsNullOrEmpty(currentNode.avatar))
{
Sprite avatarSprite = Resources.Load<Sprite>(currentNode.avatar);
if (avatarSprite != null)
{
avatarImage.sprite = avatarSprite;
avatarImage.gameObject.SetActive(true);
}
else
{
Debug.LogWarning("头像未找到: " + currentNode.avatar);
avatarImage.gameObject.SetActive(false);
}
}
else
{
avatarImage.gameObject.SetActive(false);
}
StartCoroutine(TypewriterEffect());
if (currentNode.opts != null && currentNode.opts.Length > 0)
{
continueButton.gameObject.SetActive(false);
for (int i = 0; i < currentNode.opts.Length; i++)
{
if (currentNode.opts[i] != null) // 空检查
{
CreateOptionButton(currentNode.opts[i]);
}
}
}
else if (currentNode.nextid > 0)
{
continueButton.gameObject.SetActive(true);
continueButton.GetComponentInChildren<Text>().text = "继续";
}
else
{
continueButton.gameObject.SetActive(true);
continueButton.GetComponentInChildren<Text>().text = "结束";
}
}
IEnumerator TypewriterEffect()
{
isTyping = true;
messageText.text = "";
foreach (char c in fullMessage.ToCharArray())
{
messageText.text += c;
yield return new WaitForSeconds(0.03f); // 调整显示速度
}
isTyping = false;
}
void CreateOptionButton(DialogueOption option)
{
if (optionButtonPrefab == null)
{
Debug.LogError("选项按钮预制体未分配!");
return;
}
GameObject buttonObj = Instantiate(optionButtonPrefab, optionsPanel);
buttonObj.SetActive(true);
Button primaryButton = buttonObj.GetComponentInChildren<Button>(true);
Text primaryText = buttonObj.GetComponentInChildren<Text>(true);
primaryText.text = option.msg;
primaryButton.onClick.RemoveAllListeners();
primaryButton.onClick.AddListener(() => HandleOptionSelection(option));
}
void HandleOptionSelection(DialogueOption option)
{
int targetId = option.toId != 0 ? option.toId : option.nextid;
if (nodeDict.ContainsKey(targetId))
{
StartDialogueNode(targetId);
}
else if (targetId > 0)
{
Debug.LogWarning($"目标节点不存在: {targetId}");
EndDialogue();
}
else
{
EndDialogue(); // 结束对话
}
}
void OnContinueClicked()
{
if (currentNode == null) // 添加空值检查
{
CloseDialogue();
return;
}
if (isTyping)
{
messageText.text = fullMessage;
isTyping = false;
return;
}
if (currentNode.nextid > 0 && nodeDict.ContainsKey(currentNode.nextid))
{
StartDialogueNode(currentNode.nextid);
}
else
{
EndDialogue();
}
}
public void CloseDialogue()
{
continueButton.onClick.RemoveAllListeners();
foreach (Transform child in optionsPanel)
{
Button btn = child.GetComponent<Button>();
if (btn != null) btn.onClick.RemoveAllListeners();
}
gameObject.SetActive(false);
OnDialogueEnd?.Invoke(currentNode != null ? currentNode.id : -1);
}
void EndDialogue()
{
CloseDialogue();
OnDialogueEnd?.Invoke(currentNode.id);
Debug.Log("对话结束");
}
public void ChangeBackground(string bgPath)
{
Sprite bgSprite = Resources.Load<Sprite>(bgPath);
if (bgSprite != null)
{
backgroundPanel.sprite = bgSprite;
}
else
{
Debug.LogWarning("背景未找到: " + bgPath);
}
}
public void ChangeAvatar(string avatarPath)
{
Sprite avatarSprite = Resources.Load<Sprite>(avatarPath);
if (avatarSprite != null)
{
avatarImage.sprite = avatarSprite;
}
else
{
Debug.LogWarning("立绘未找到: " + avatarPath);
}
}
}
using System;
using UnityEngine;
using UnityEngine.Serialization;
namespace UnityEngine.EventSystems
{
[AddComponentMenu("Event/Standalone Input Module")]
/// <summary>
/// A BaseInputModule designed for mouse / keyboard / controller input.
/// </summary>
/// <remarks>
/// Input module for working with, mouse, keyboard, or controller.
/// </remarks>
public class StandaloneInputModule : PointerInputModule
{
private float m_PrevActionTime;
private Vector2 m_LastMoveVector;
private int m_ConsecutiveMoveCount = 0;
private Vector2 m_LastMousePosition;
private Vector2 m_MousePosition;
private GameObject m_CurrentFocusedGameObject;
private PointerEventData m_InputPointerEvent;
private const float doubleClickTime = 0.3f;
protected StandaloneInputModule()
{
}
[Obsolete("Mode is no longer needed on input module as it handles both mouse and keyboard simultaneously.", false)]
public enum InputMode
{
Mouse,
Buttons
}
[Obsolete("Mode is no longer needed on input module as it handles both mouse and keyboard simultaneously.", false)]
public InputMode inputMode
{
get { return InputMode.Mouse; }
}
[SerializeField]
private string m_HorizontalAxis = "Horizontal";
/// <summary>
/// Name of the vertical axis for movement (if axis events are used).
/// </summary>
[SerializeField]
private string m_VerticalAxis = "Vertical";
/// <summary>
/// Name of the submit button.
/// </summary>
[SerializeField]
private string m_SubmitButton = "Submit";
/// <summary>
/// Name of the submit button.
/// </summary>
[SerializeField]
private string m_CancelButton = "Cancel";
[SerializeField]
private float m_InputActionsPerSecond = 10;
[SerializeField]
private float m_RepeatDelay = 0.5f;
[SerializeField]
[FormerlySerializedAs("m_AllowActivationOnMobileDevice")]
[HideInInspector]
private bool m_ForceModuleActive;
[Obsolete("allowActivationOnMobileDevice has been deprecated. Use forceModuleActive instead (UnityUpgradable) -> forceModuleActive")]
public bool allowActivationOnMobileDevice
{
get { return m_ForceModuleActive; }
set { m_ForceModuleActive = value; }
}
/// <summary>
/// Force this module to be active.
/// </summary>
/// <remarks>
/// If there is no module active with higher priority (ordered in the inspector) this module will be forced active even if valid enabling conditions are not met.
/// </remarks>
[Obsolete("forceModuleActive has been deprecated. There is no need to force the module awake as StandaloneInputModule works for all platforms")]
public bool forceModuleActive
{
get { return m_ForceModuleActive; }
set { m_ForceModuleActive = value; }
}
/// <summary>
/// Number of keyboard / controller inputs allowed per second.
/// </summary>
public float inputActionsPerSecond
{
get { return m_InputActionsPerSecond; }
set { m_InputActionsPerSecond = value; }
}
/// <summary>
/// Delay in seconds before the input actions per second repeat rate takes effect.
/// </summary>
/// <remarks>
/// If the same direction is sustained, the inputActionsPerSecond property can be used to control the rate at which events are fired. However, it can be desirable that the first repetition is delayed, so the user doesn't get repeated actions by accident.
/// </remarks>
public float repeatDelay
{
get { return m_RepeatDelay; }
set { m_RepeatDelay = value; }
}
/// <summary>
/// Name of the horizontal axis for movement (if axis events are used).
/// </summary>
public string horizontalAxis
{
get { return m_HorizontalAxis; }
set { m_HorizontalAxis = value; }
}
/// <summary>
/// Name of the vertical axis for movement (if axis events are used).
/// </summary>
public string verticalAxis
{
get { return m_VerticalAxis; }
set { m_VerticalAxis = value; }
}
/// <summary>
/// Maximum number of input events handled per second.
/// </summary>
public string submitButton
{
get { return m_SubmitButton; }
set { m_SubmitButton = value; }
}
/// <summary>
/// Input manager name for the 'cancel' button.
/// </summary>
public string cancelButton
{
get { return m_CancelButton; }
set { m_CancelButton = value; }
}
private bool ShouldIgnoreEventsOnNoFocus()
{
#if UNITY_EDITOR
return !UnityEditor.EditorApplication.isRemoteConnected;
#else
return true;
#endif
}
public override void UpdateModule()
{
if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus())
{
if (m_InputPointerEvent != null && m_InputPointerEvent.pointerDrag != null && m_InputPointerEvent.dragging)
{
ReleaseMouse(m_InputPointerEvent, m_InputPointerEvent.pointerCurrentRaycast.gameObject);
}
m_InputPointerEvent = null;
return;
}
m_LastMousePosition = m_MousePosition;
m_MousePosition = input.mousePosition;
}
private void ReleaseMouse(PointerEventData pointerEvent, GameObject currentOverGo)
{
ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler);
var pointerClickHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
// PointerClick and Drop events
if (pointerEvent.pointerClick == pointerClickHandler && pointerEvent.eligibleForClick)
{
ExecuteEvents.Execute(pointerEvent.pointerClick, pointerEvent, ExecuteEvents.pointerClickHandler);
}
if (pointerEvent.pointerDrag != null && pointerEvent.dragging)
{
ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.dropHandler);
}
pointerEvent.eligibleForClick = false;
pointerEvent.pointerPress = null;
pointerEvent.rawPointerPress = null;
pointerEvent.pointerClick = null;
if (pointerEvent.pointerDrag != null && pointerEvent.dragging)
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.endDragHandler);
pointerEvent.dragging = false;
pointerEvent.pointerDrag = null;
// redo pointer enter / exit to refresh state
// so that if we moused over something that ignored it before
// due to having pressed on something else
// it now gets it.
if (currentOverGo != pointerEvent.pointerEnter)
{
HandlePointerExitAndEnter(pointerEvent, null);
HandlePointerExitAndEnter(pointerEvent, currentOverGo);
}
m_InputPointerEvent = pointerEvent;
}
public override bool ShouldActivateModule()
{
if (!base.ShouldActivateModule())
return false;
var shouldActivate = m_ForceModuleActive;
shouldActivate |= input.GetButtonDown(m_SubmitButton);
shouldActivate |= input.GetButtonDown(m_CancelButton);
shouldActivate |= !Mathf.Approximately(input.GetAxisRaw(m_HorizontalAxis), 0.0f);
shouldActivate |= !Mathf.Approximately(input.GetAxisRaw(m_VerticalAxis), 0.0f);
shouldActivate |= (m_MousePosition - m_LastMousePosition).sqrMagnitude > 0.0f;
shouldActivate |= input.GetMouseButtonDown(0);
if (input.touchCount > 0)
shouldActivate = true;
return shouldActivate;
}
/// <summary>
/// See BaseInputModule.
/// </summary>
public override void ActivateModule()
{
if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus())
return;
base.ActivateModule();
m_MousePosition = input.mousePosition;
m_LastMousePosition = input.mousePosition;
var toSelect = eventSystem.currentSelectedGameObject;
if (toSelect == null)
toSelect = eventSystem.firstSelectedGameObject;
eventSystem.SetSelectedGameObject(toSelect, GetBaseEventData());
}
/// <summary>
/// See BaseInputModule.
/// </summary>
public override void DeactivateModule()
{
base.DeactivateModule();
ClearSelection();
}
public override void Process()
{
if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus())
return;
bool usedEvent = SendUpdateEventToSelectedObject();
// case 1004066 - touch / mouse events should be processed before navigation events in case
// they change the current selected gameobject and the submit button is a touch / mouse button.
// touch needs to take precedence because of the mouse emulation layer
if (!ProcessTouchEvents() && input.mousePresent)
ProcessMouseEvent();
if (eventSystem.sendNavigationEvents)
{
if (!usedEvent)
usedEvent |= SendMoveEventToSelectedObject();
if (!usedEvent)
SendSubmitEventToSelectedObject();
}
}
private bool ProcessTouchEvents()
{
for (int i = 0; i < input.touchCount; ++i)
{
Touch touch = input.GetTouch(i);
if (touch.type == TouchType.Indirect)
continue;
bool released;
bool pressed;
var pointer = GetTouchPointerEventData(touch, out pressed, out released);
ProcessTouchPress(pointer, pressed, released);
if (!released)
{
ProcessMove(pointer);
ProcessDrag(pointer);
}
else
RemovePointerData(pointer);
}
return input.touchCount > 0;
}
/// <summary>
/// This method is called by Unity whenever a touch event is processed. Override this method with a custom implementation to process touch events yourself.
/// </summary>
/// <param name="pointerEvent">Event data relating to the touch event, such as position and ID to be passed to the touch event destination object.</param>
/// <param name="pressed">This is true for the first frame of a touch event, and false thereafter. This can therefore be used to determine the instant a touch event occurred.</param>
/// <param name="released">This is true only for the last frame of a touch event.</param>
/// <remarks>
/// This method can be overridden in derived classes to change how touch press events are handled.
/// </remarks>
protected void ProcessTouchPress(PointerEventData pointerEvent, bool pressed, bool released)
{
var currentOverGo = pointerEvent.pointerCurrentRaycast.gameObject;
// PointerDown notification
if (pressed)
{
pointerEvent.eligibleForClick = true;
pointerEvent.delta = Vector2.zero;
pointerEvent.dragging = false;
pointerEvent.useDragThreshold = true;
pointerEvent.pressPosition = pointerEvent.position;
pointerEvent.pointerPressRaycast = pointerEvent.pointerCurrentRaycast;
DeselectIfSelectionChanged(currentOverGo, pointerEvent);
if (pointerEvent.pointerEnter != currentOverGo)
{
// send a pointer enter to the touched element if it isn't the one to select...
HandlePointerExitAndEnter(pointerEvent, currentOverGo);
pointerEvent.pointerEnter = currentOverGo;
}
var resetDiffTime = Time.unscaledTime - pointerEvent.clickTime;
if (resetDiffTime >= doubleClickTime)
{
pointerEvent.clickCount = 0;
}
// search for the control that will receive the press
// if we can't find a press handler set the press
// handler to be what would receive a click.
var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.pointerDownHandler);
var newClick = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
// didnt find a press handler... search for a click handler
if (newPressed == null)
newPressed = newClick;
// Debug.Log("Pressed: " + newPressed);
float time = Time.unscaledTime;
if (newPressed == pointerEvent.lastPress)
{
var diffTime = time - pointerEvent.clickTime;
if (diffTime < doubleClickTime)
++pointerEvent.clickCount;
else
pointerEvent.clickCount = 1;
pointerEvent.clickTime = time;
}
else
{
pointerEvent.clickCount = 1;
}
pointerEvent.pointerPress = newPressed;
pointerEvent.rawPointerPress = currentOverGo;
pointerEvent.pointerClick = newClick;
pointerEvent.clickTime = time;
// Save the drag handler as well
pointerEvent.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(currentOverGo);
if (pointerEvent.pointerDrag != null)
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.initializePotentialDrag);
}
// PointerUp notification
if (released)
{
// Debug.Log("Executing pressup on: " + pointer.pointerPress);
ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler);
// Debug.Log("KeyCode: " + pointer.eventData.keyCode);
// see if we mouse up on the same element that we clicked on...
var pointerClickHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
// PointerClick and Drop events
if (pointerEvent.pointerClick == pointerClickHandler && pointerEvent.eligibleForClick)
{
ExecuteEvents.Execute(pointerEvent.pointerClick, pointerEvent, ExecuteEvents.pointerClickHandler);
}
if (pointerEvent.pointerDrag != null && pointerEvent.dragging)
{
ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.dropHandler);
}
pointerEvent.eligibleForClick = false;
pointerEvent.pointerPress = null;
pointerEvent.rawPointerPress = null;
pointerEvent.pointerClick = null;
if (pointerEvent.pointerDrag != null && pointerEvent.dragging)
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.endDragHandler);
pointerEvent.dragging = false;
pointerEvent.pointerDrag = null;
// send exit events as we need to simulate this on touch up on touch device
ExecuteEvents.ExecuteHierarchy(pointerEvent.pointerEnter, pointerEvent, ExecuteEvents.pointerExitHandler);
pointerEvent.pointerEnter = null;
}
m_InputPointerEvent = pointerEvent;
}
/// <summary>
/// Calculate and send a submit event to the current selected object.
/// </summary>
/// <returns>If the submit event was used by the selected object.</returns>
protected bool SendSubmitEventToSelectedObject()
{
if (eventSystem.currentSelectedGameObject == null)
return false;
var data = GetBaseEventData();
if (input.GetButtonDown(m_SubmitButton))
ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, data, ExecuteEvents.submitHandler);
if (input.GetButtonDown(m_CancelButton))
ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, data, ExecuteEvents.cancelHandler);
return data.used;
}
private Vector2 GetRawMoveVector()
{
Vector2 move = Vector2.zero;
move.x = input.GetAxisRaw(m_HorizontalAxis);
move.y = input.GetAxisRaw(m_VerticalAxis);
if (input.GetButtonDown(m_HorizontalAxis))
{
if (move.x < 0)
move.x = -1f;
if (move.x > 0)
move.x = 1f;
}
if (input.GetButtonDown(m_VerticalAxis))
{
if (move.y < 0)
move.y = -1f;
if (move.y > 0)
move.y = 1f;
}
return move;
}
/// <summary>
/// Calculate and send a move event to the current selected object.
/// </summary>
/// <returns>If the move event was used by the selected object.</returns>
protected bool SendMoveEventToSelectedObject()
{
float time = Time.unscaledTime;
Vector2 movement = GetRawMoveVector();
if (Mathf.Approximately(movement.x, 0f) && Mathf.Approximately(movement.y, 0f))
{
m_ConsecutiveMoveCount = 0;
return false;
}
bool similarDir = (Vector2.Dot(movement, m_LastMoveVector) > 0);
// If direction didn't change at least 90 degrees, wait for delay before allowing consequtive event.
if (similarDir && m_ConsecutiveMoveCount == 1)
{
if (time <= m_PrevActionTime + m_RepeatDelay)
return false;
}
// If direction changed at least 90 degree, or we already had the delay, repeat at repeat rate.
else
{
if (time <= m_PrevActionTime + 1f / m_InputActionsPerSecond)
return false;
}
var axisEventData = GetAxisEventData(movement.x, movement.y, 0.6f);
if (axisEventData.moveDir != MoveDirection.None)
{
ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, axisEventData, ExecuteEvents.moveHandler);
if (!similarDir)
m_ConsecutiveMoveCount = 0;
m_ConsecutiveMoveCount++;
m_PrevActionTime = time;
m_LastMoveVector = movement;
}
else
{
m_ConsecutiveMoveCount = 0;
}
return axisEventData.used;
}
protected void ProcessMouseEvent()
{
ProcessMouseEvent(0);
}
[Obsolete("This method is no longer checked, overriding it with return true does nothing!")]
protected virtual bool ForceAutoSelect()
{
return false;
}
/// <summary>
/// Process all mouse events.
/// </summary>
protected void ProcessMouseEvent(int id)
{
var mouseData = GetMousePointerEventData(id);
var leftButtonData = mouseData.GetButtonState(PointerEventData.InputButton.Left).eventData;
m_CurrentFocusedGameObject = leftButtonData.buttonData.pointerCurrentRaycast.gameObject;
// Process the first mouse button fully
ProcessMousePress(leftButtonData);
ProcessMove(leftButtonData.buttonData);
ProcessDrag(leftButtonData.buttonData);
// Now process right / middle clicks
ProcessMousePress(mouseData.GetButtonState(PointerEventData.InputButton.Right).eventData);
ProcessDrag(mouseData.GetButtonState(PointerEventData.InputButton.Right).eventData.buttonData);
ProcessMousePress(mouseData.GetButtonState(PointerEventData.InputButton.Middle).eventData);
ProcessDrag(mouseData.GetButtonState(PointerEventData.InputButton.Middle).eventData.buttonData);
if (!Mathf.Approximately(leftButtonData.buttonData.scrollDelta.sqrMagnitude, 0.0f))
{
var scrollHandler = ExecuteEvents.GetEventHandler<IScrollHandler>(leftButtonData.buttonData.pointerCurrentRaycast.gameObject);
ExecuteEvents.ExecuteHierarchy(scrollHandler, leftButtonData.buttonData, ExecuteEvents.scrollHandler);
}
}
protected bool SendUpdateEventToSelectedObject()
{
if (eventSystem.currentSelectedGameObject == null)
return false;
var data = GetBaseEventData();
ExecuteEvents.Execute(eventSystem.currentSelectedGameObject, data, ExecuteEvents.updateSelectedHandler);
return data.used;
}
/// <summary>
/// Calculate and process any mouse button state changes.
/// </summary>
protected void ProcessMousePress(MouseButtonEventData data)
{
var pointerEvent = data.buttonData;
var currentOverGo = pointerEvent.pointerCurrentRaycast.gameObject;
// PointerDown notification
if (data.PressedThisFrame())
{
pointerEvent.eligibleForClick = true;
pointerEvent.delta = Vector2.zero;
pointerEvent.dragging = false;
pointerEvent.useDragThreshold = true;
pointerEvent.pressPosition = pointerEvent.position;
pointerEvent.pointerPressRaycast = pointerEvent.pointerCurrentRaycast;
DeselectIfSelectionChanged(currentOverGo, pointerEvent);
var resetDiffTime = Time.unscaledTime - pointerEvent.clickTime;
if (resetDiffTime >= doubleClickTime)
{
pointerEvent.clickCount = 0;
}
// search for the control that will receive the press
// if we can't find a press handler set the press
// handler to be what would receive a click.
var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.pointerDownHandler);
var newClick = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
// didnt find a press handler... search for a click handler
if (newPressed == null)
newPressed = newClick;
// Debug.Log("Pressed: " + newPressed);
float time = Time.unscaledTime;
if (newPressed == pointerEvent.lastPress)
{
var diffTime = time - pointerEvent.clickTime;
if (diffTime < doubleClickTime)
++pointerEvent.clickCount;
else
pointerEvent.clickCount = 1;
pointerEvent.clickTime = time;
}
else
{
pointerEvent.clickCount = 1;
}
pointerEvent.pointerPress = newPressed;
pointerEvent.rawPointerPress = currentOverGo;
pointerEvent.pointerClick = newClick;
pointerEvent.clickTime = time;
// Save the drag handler as well
pointerEvent.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(currentOverGo);
if (pointerEvent.pointerDrag != null)
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.initializePotentialDrag);
m_InputPointerEvent = pointerEvent;
}
// PointerUp notification
if (data.ReleasedThisFrame())
{
ReleaseMouse(pointerEvent, currentOverGo);
}
}
protected GameObject GetCurrentFocusedGameObject()
{
return m_CurrentFocusedGameObject;
}
}
}
[
{ "id":1,
"msg":"帝帝帝大王正指挥瓦多迪建造'苹果能量塔',突然发现核心能源——星光苹果被偷。",
"nextid":2
},
{
"id": 2,
"name": "DDD",
"avatar": "Avatars/DDD",
"msg": "(暴跳如雷)我的超级苹果!没有它能量塔就是废铁!等等…这粉色绒毛!(捡起沙滩上的绒毛)卡比——!!",
"nextid":3
},
{ "id":3,
"msg": "(星之卡比从礁石后探头,抱着发光的苹果啃得正欢)",
"nextid":4
},
{
"id": 4,
"name": "Kriby",
"avatar": "Avatars/Kirby",
"msg": " Poyo?(无辜眨眼,苹果汁滴在沙子上泛起星光)",
"opts":
[
{
"msg": "主动归还苹果,但要求交换条件",
"toId": 8
},
{
"msg": "转身逃跑引发追逐战",
"toId": 5
}
]
},
{
"id": 5,
"name": "DDD",
"avatar": "Avatars/DDD",
"msg": " 可恶的粉球,给我站住不许跑!!!",
"nextid":6
},
{
"id": 6,
"name": "Kriby",
"avatar": "Avatars/Kirby",
"msg": "Poyo!Poyo!",
"nextid":7
},
{
"id": 7,
"msg": "就这样两人一直追逐下去........但是最终还是选择和解",
"nextid":8
},
{
"id": 8,
"name": "Kriby",
"avatar": "Avatars/Kirby",
"msg": " Poyo-poyo!(指向远方迷雾海域)",
"nextid":9
},
{
"id": 9,
"name": "DDD",
"avatar": "Avatars/DDD",
"msg": " 什么?你说海妖偷走了其他苹果?(狐疑)哼!本大王才不会被骗…(突然巨浪袭来)",
"nextid":10
},
{
"id": 10,
"msg": " 巨型海妖现身,触手卷走苹果能量塔的核心部件,两人被困在腐朽的船舱,海水不断渗入。",
"nextid":11
},
{
"id": 11,
"name": "DDD",
"avatar": "Avatars/DDD",
"msg": " 锤子卡在木梁里)可恶!粉球球,现在我们是一根绳上的蚂蚱了!",
"nextid":12
},
{
"id": 12,
"name": "Kriby",
"avatar": "Avatars/Kirby",
"msg": "(变身潜水卡比,照亮黑暗)Poyo! ",
"nextid":13
},
{
"id": 13,
"msg": " 逃生途中发现古代石板,记载着苹果能量的秘密。",
"nextid":14
},
{
"id": 14,
"name": "MetaKnight",
"avatar": "Avatars/MetaKnight",
"msg": "(突然从天而降)停下!那石板会唤醒古代灾厄!(剑指帝帝帝)你又在谋划什么? ",
"nextid":15
},
{
"id": 15,
"name": "DDD",
"avatar": "Avatars/DDD",
"msg": "( 本大王需要苹果救妹妹蒂芙!她的石化病…(罕见露出脆弱) ",
"nextid":16
},
{
"id":16,
"name":"Kriby",
"msg":"Poyo?(那我现在应该怎么做呢?)",
"opts": [
{
"msg":"卡比调和冲突,提议三方合作",
"nextid":17
},
{
"msg":"卡比独自吞下石板引发能量暴走",
"nextid":22
}
]
},
{
"id":17,
"msg":"三人抵达星云神殿,发现苹果能量可治愈石化病,但会毁灭梦幻岛生态。",
"nextid":18
},
{
"id":18,
"name":"DDD",
"avatar":"Avatars/DDD",
"msg":"(颤抖握锤)蒂芙还是岛屿…靠你了(为卡比提供能量)",
"nextid":19
},
{
"id":19,
"name":"MetaKnight",
"avatar":"Avatars/MetaKnight",
"msg":"(收剑入鞘)或许是时候该发力了(为卡比提供能量)",
"nextid":20
},
{
"id":20,
"name":"Kriby",
"avatar":"Avatars/Kirby",
"msg":"(跳到祭坛中央,全身发光)POYO——!",
"nextid":21
},
{
"id":21,
"msg":"能量不断被吸入卡比体内,在短暂光芒之后,卡比稳定下来后让帝帝帝将苹果带回,蒂芙苏醒,帝帝帝含泪道谢"
},
{
"id":22,
"msg":"卡比吸收过量能量裂变成暗黑卡比,引发新危机"
}
]仔细检查代码是否有问题为什么会是NULL
最新发布