191 lines
7.6 KiB
C#
191 lines
7.6 KiB
C#
using System.Collections;
|
|
using HotUpdate.Gameplay;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
namespace HotUpdate.UI
|
|
{
|
|
/// <summary>Walks the camp guide over the camp painting and speaks only after a tap.</summary>
|
|
public sealed class HomeExplorerPresenter : MonoBehaviour
|
|
{
|
|
const int Layer = 31;
|
|
static readonly string[] Lines =
|
|
{
|
|
"喷泉今天的水声很好听。",
|
|
"芽芽他们在营地里等你。",
|
|
"想再去弹珠台转转吗?",
|
|
"这条石路是我每天散步的地方。",
|
|
"花开得正好,要不要一起看看?",
|
|
"下一关的路上好像有新朋友。",
|
|
"训练之后,大家都会更有精神。",
|
|
"我在这儿看着营地,你去冒险吧。"
|
|
};
|
|
|
|
UiPrefabLoader _loader;
|
|
GameObject _stage;
|
|
Camera _camera;
|
|
RenderTexture _texture;
|
|
RawImage _image;
|
|
bool _booting;
|
|
Transform _actor;
|
|
RectTransform _button;
|
|
RectTransform _bubble;
|
|
TextMeshProUGUI _line;
|
|
float _hideAt;
|
|
int _last = -1;
|
|
bool _hooked;
|
|
Vector3 _wander;
|
|
|
|
public bool Ready => _actor != null;
|
|
public bool Talking => _bubble != null && _bubble.gameObject.activeSelf;
|
|
public string Line => _line != null ? _line.text : "";
|
|
|
|
void OnEnable()
|
|
{
|
|
_image = GetComponent<RawImage>();
|
|
if (_image != null) _image.enabled = _camera != null;
|
|
if (_camera != null) _camera.enabled = true;
|
|
else if (!_booting) StartCoroutine(Boot());
|
|
}
|
|
|
|
void OnDisable()
|
|
{
|
|
if (_camera != null) _camera.enabled = false;
|
|
if (_image != null) _image.enabled = false;
|
|
if (_bubble != null) _bubble.gameObject.SetActive(false);
|
|
}
|
|
|
|
void OnDestroy()
|
|
{
|
|
if (_button != null) _button.GetComponent<Button>().onClick.RemoveListener(Speak);
|
|
if (_image != null) _image.texture = null;
|
|
if (_camera != null) _camera.targetTexture = null;
|
|
if (_texture != null)
|
|
{
|
|
_texture.Release();
|
|
Destroy(_texture);
|
|
}
|
|
if (_stage != null) Destroy(_stage);
|
|
_loader?.Dispose();
|
|
}
|
|
|
|
IEnumerator Boot()
|
|
{
|
|
_booting = true;
|
|
try { yield return BootCore(); }
|
|
finally { _booting = false; }
|
|
}
|
|
|
|
IEnumerator BootCore()
|
|
{
|
|
// The serialized material makes the shader a real AssetBundle dependency.
|
|
// Never display the opaque camera clear color when that dependency is unavailable.
|
|
if (_image == null || _image.material == null || _image.material.shader == null ||
|
|
_image.material.shader.name != "AIGame/HomeExplorerCutout" || !_image.material.shader.isSupported)
|
|
{
|
|
Debug.LogWarning("[HomeExplorer] Preview material unavailable; keeping the camp background visible.");
|
|
yield break;
|
|
}
|
|
var root = transform.parent;
|
|
_button = root.Find("Btn_Explorer") as RectTransform;
|
|
_bubble = root.Find("Panel_ExplorerTalk") as RectTransform;
|
|
_line = root.Find("Panel_ExplorerTalk/Txt_ExplorerTalk").GetComponent<TextMeshProUGUI>();
|
|
_bubble.gameObject.SetActive(false);
|
|
yield return null;
|
|
|
|
GameObject prefab = null;
|
|
_loader = new UiPrefabLoader(null, "Assets/Bundles/Characters/Human/CampGuideStudio");
|
|
yield return _loader.LoadAsync("CampGuideStudio_Rigged", loaded => prefab = loaded);
|
|
if (prefab == null || _stage != null || !isActiveAndEnabled) yield break;
|
|
|
|
_stage = new GameObject("HomeExplorerStage");
|
|
_stage.transform.position = new Vector3(40f, 0f, 0f);
|
|
_actor = Instantiate(prefab, _stage.transform).transform;
|
|
_actor.localPosition = Vector3.zero;
|
|
_actor.localRotation = Quaternion.Euler(0f, 180f, 0f);
|
|
SetLayer(_actor);
|
|
|
|
var lightGo = new GameObject("Light");
|
|
lightGo.transform.SetParent(_stage.transform, false);
|
|
var light = lightGo.AddComponent<Light>();
|
|
light.type = LightType.Directional;
|
|
light.intensity = 1.15f;
|
|
light.cullingMask = 1 << Layer;
|
|
light.transform.rotation = Quaternion.Euler(38f, -28f, 0f);
|
|
SetLayer(lightGo.transform);
|
|
|
|
var camGo = new GameObject("Camera");
|
|
camGo.transform.SetParent(_stage.transform, false);
|
|
_camera = camGo.AddComponent<Camera>();
|
|
_camera.clearFlags = CameraClearFlags.SolidColor;
|
|
// Read the shipped material: a hot-update DLL may run with an older Art bundle.
|
|
_camera.backgroundColor = _image.material.GetColor("_Key");
|
|
_camera.cullingMask = 1 << Layer;
|
|
_camera.orthographic = true;
|
|
_camera.orthographicSize = 1.45f;
|
|
_camera.nearClipPlane = 0.05f;
|
|
_camera.farClipPlane = 30f;
|
|
_camera.allowHDR = false;
|
|
_camera.depth = -50;
|
|
_camera.transform.localPosition = new Vector3(0.15f, 1.7f, -3.6f);
|
|
_camera.transform.LookAt(_stage.transform.TransformPoint(0f, 0.7f, 0f));
|
|
_texture = new RenderTexture(512, 512, 16, RenderTextureFormat.ARGB32);
|
|
_texture.Create();
|
|
_camera.targetTexture = _texture;
|
|
_image.texture = _texture;
|
|
_image.enabled = true;
|
|
_bubble.gameObject.SetActive(false);
|
|
if (!_hooked && _button != null)
|
|
{
|
|
_button.GetComponent<Button>().onClick.AddListener(Speak);
|
|
_hooked = true;
|
|
}
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if (_actor == null) return;
|
|
float t = Time.time * 0.45f;
|
|
Vector3 next = new Vector3(Mathf.Sin(t) * 1.05f, 0f, Mathf.Sin(t * 0.5f) * 0.28f);
|
|
Vector3 dir = next - _wander;
|
|
_wander = next;
|
|
_actor.localPosition = next;
|
|
if (dir.sqrMagnitude > 0.000001f)
|
|
_actor.localRotation = Quaternion.LookRotation(new Vector3(dir.x, 0f, dir.z)) * Quaternion.Euler(0f, 180f, 0f);
|
|
}
|
|
|
|
void LateUpdate()
|
|
{
|
|
if (_actor == null || _camera == null || _button == null) return;
|
|
var stage = (RectTransform)transform;
|
|
Vector3 view = _camera.WorldToViewportPoint(_actor.position + Vector3.up * 0.75f);
|
|
if (view.z < 0f) return;
|
|
Vector3 world = stage.TransformPoint(new Vector3((view.x - 0.5f) * stage.rect.width, (view.y - 0.5f) * stage.rect.height, 0f));
|
|
_button.position = world;
|
|
if (_bubble.gameObject.activeSelf)
|
|
{
|
|
_bubble.position = _button.position + new Vector3(0f, 150f * _button.lossyScale.y, 0f);
|
|
if (Time.unscaledTime >= _hideAt) _bubble.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
|
|
public void Speak()
|
|
{
|
|
if (_line == null || _bubble == null) return;
|
|
int index = Random.Range(0, Lines.Length);
|
|
if (Lines.Length > 1 && index == _last) index = (index + 1) % Lines.Length;
|
|
_last = index;
|
|
_line.text = Lines[index];
|
|
_bubble.gameObject.SetActive(true);
|
|
_hideAt = Time.unscaledTime + 3.5f;
|
|
}
|
|
|
|
static void SetLayer(Transform root)
|
|
{
|
|
root.gameObject.layer = Layer;
|
|
foreach (Transform child in root) SetLayer(child);
|
|
}
|
|
}
|
|
}
|