Add TownSquare scene assets, resource production docs, and model pipeline updates.
Bring in plaza props/prefabs, ZhaoYun source GLB, design originals, and Hunyuan/Blender tooling used to produce them. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -67,11 +67,11 @@ namespace HotUpdate
|
||||
|
||||
_instance = Instantiate(prefab);
|
||||
_instance.name = "MaleExplorer_Runtime";
|
||||
var y = _instance.transform.position.y;
|
||||
_instance.transform.position = new Vector3(0f, y, 2.5f);
|
||||
var plaza = GameObject.Find("TownSquare_Runtime");
|
||||
var y = GroundSit.Height(Vector3.zero, plaza != null ? plaza.transform : null);
|
||||
_instance.transform.position = new Vector3(0f, y, 0f);
|
||||
DontDestroyOnLoad(_instance);
|
||||
|
||||
// 仅对概念图 Billboard 子节点做朝向;真 3D 网格不要转,否则会拧成纸片
|
||||
var billboardTf = _instance.transform.Find("Billboard");
|
||||
if (billboardTf != null && Camera.main != null)
|
||||
{
|
||||
@@ -82,8 +82,7 @@ namespace HotUpdate
|
||||
billboardTf.rotation = Quaternion.LookRotation(-dir.normalized);
|
||||
}
|
||||
|
||||
// 调整主相机看向角色(竖屏)
|
||||
if (Camera.main != null)
|
||||
if (GameObject.Find("TownSquare_Runtime") == null && Camera.main != null)
|
||||
{
|
||||
var cam = Camera.main;
|
||||
cam.transform.position = new Vector3(0f, 1.2f, -2.2f);
|
||||
|
||||
@@ -28,6 +28,14 @@ namespace HotUpdate.Gameplay
|
||||
WireNet();
|
||||
MaybeShowBootNotice();
|
||||
|
||||
var plaza = gameObject.AddComponent<TownSquareSpawner>();
|
||||
plaza.Bind(_ctx);
|
||||
StartCoroutine(SpawnWorld(plaza));
|
||||
}
|
||||
|
||||
System.Collections.IEnumerator SpawnWorld(TownSquareSpawner plaza)
|
||||
{
|
||||
yield return plaza.SpawnCo();
|
||||
var spawner = gameObject.AddComponent<CharacterSpawner>();
|
||||
spawner.Bind(_ctx);
|
||||
spawner.Spawn();
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace HotUpdate
|
||||
{
|
||||
/// <summary>
|
||||
/// 用地表 MeshCollider 的碰撞高度作为放置 Y。原点在底面中点的物件直接用该值。
|
||||
/// </summary>
|
||||
public static class GroundSit
|
||||
{
|
||||
const float RayStartY = 80f;
|
||||
const float RayLength = 160f;
|
||||
|
||||
public static float Height(Vector3 xz, Transform searchRoot = null, float fallback = 0f)
|
||||
{
|
||||
Physics.SyncTransforms();
|
||||
var ray = new Ray(new Vector3(xz.x, RayStartY, xz.z), Vector3.down);
|
||||
var bestDist = float.MaxValue;
|
||||
var y = fallback;
|
||||
var hitAny = false;
|
||||
|
||||
foreach (var col in Collect(searchRoot))
|
||||
{
|
||||
if (col == null || !col.enabled) continue;
|
||||
if (!IsGroundCollider(col)) continue;
|
||||
if (!col.Raycast(ray, out var hit, RayLength)) continue;
|
||||
if (hit.distance >= bestDist) continue;
|
||||
bestDist = hit.distance;
|
||||
y = hit.point.y;
|
||||
hitAny = true;
|
||||
}
|
||||
|
||||
if (hitAny) return y;
|
||||
|
||||
foreach (var col in Collect(searchRoot))
|
||||
{
|
||||
if (col == null || !col.enabled) continue;
|
||||
if (!col.Raycast(ray, out var hit, RayLength)) continue;
|
||||
if (hit.distance >= bestDist) continue;
|
||||
bestDist = hit.distance;
|
||||
y = hit.point.y;
|
||||
hitAny = true;
|
||||
}
|
||||
|
||||
return hitAny ? y : fallback;
|
||||
}
|
||||
|
||||
static MeshCollider[] Collect(Transform searchRoot)
|
||||
{
|
||||
if (searchRoot != null)
|
||||
return searchRoot.GetComponentsInChildren<MeshCollider>();
|
||||
return Object.FindObjectsByType<MeshCollider>(FindObjectsSortMode.None);
|
||||
}
|
||||
|
||||
static bool IsGroundCollider(MeshCollider col)
|
||||
{
|
||||
for (var t = col.transform; t != null; t = t.parent)
|
||||
{
|
||||
if (t.name.IndexOf("ground", System.StringComparison.OrdinalIgnoreCase) >= 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64eb3db5d5ad0e54e97d931d86a31bbb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,67 @@
|
||||
using System.Collections;
|
||||
using AOT.Boot;
|
||||
using UnityEngine;
|
||||
using YooAsset;
|
||||
|
||||
namespace HotUpdate
|
||||
{
|
||||
/// <summary>
|
||||
/// 加载小镇广场环境,供角色站在广场中央。
|
||||
/// </summary>
|
||||
public sealed class TownSquareSpawner : MonoBehaviour
|
||||
{
|
||||
public const string PrefabAddress = "TownSquare";
|
||||
public const string EditorPrefabPath = "Assets/Bundles/Scenes/Town/TownSquare/TownSquare.prefab";
|
||||
public const string ArtPackageName = "Art";
|
||||
|
||||
BootContext _ctx;
|
||||
GameObject _instance;
|
||||
|
||||
public void Bind(BootContext ctx) => _ctx = ctx;
|
||||
|
||||
public IEnumerator SpawnCo()
|
||||
{
|
||||
GameObject prefab = null;
|
||||
|
||||
if (YooAssets.IsInitialized && YooAssets.TryGetPackage(ArtPackageName, out var art) &&
|
||||
art.InitializeStatus == EOperationStatus.Succeeded)
|
||||
{
|
||||
var handle = art.LoadAssetAsync<GameObject>(PrefabAddress);
|
||||
yield return handle;
|
||||
if (handle.Status == EOperationStatus.Succeeded)
|
||||
prefab = handle.GetAssetObject<GameObject>();
|
||||
else
|
||||
handle.Release();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (prefab == null)
|
||||
prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(EditorPrefabPath);
|
||||
#endif
|
||||
|
||||
if (prefab == null)
|
||||
{
|
||||
Debug.LogError("[TownSquareSpawner] TownSquare prefab not found");
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (_instance != null)
|
||||
Destroy(_instance);
|
||||
|
||||
_instance = Instantiate(prefab);
|
||||
_instance.name = "TownSquare_Runtime";
|
||||
DontDestroyOnLoad(_instance);
|
||||
|
||||
if (Camera.main != null)
|
||||
{
|
||||
var cam = Camera.main;
|
||||
cam.transform.position = new Vector3(0f, 12f, -22f);
|
||||
cam.transform.rotation = Quaternion.Euler(28f, 0f, 0f);
|
||||
cam.fieldOfView = 40f;
|
||||
cam.backgroundColor = new Color(0.78f, 0.88f, 0.94f);
|
||||
}
|
||||
|
||||
Debug.Log($"[TownSquareSpawner] spawned ok res={_ctx?.ResVersion}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 561555ac8c9a26a498a4416023b2810e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user