using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Linq; using TMPro; using UnityEngine.InputSystem; // WAJIB: Karena kamu pakai Input System baru public class Manager : MonoBehaviour { [Header("Data References")] public Atom[] atoms; public Molecule[] molecules; [Header("UI Reference")] public GameObject panelInfo; public TextMeshProUGUI textNamaMolekul; [Header("Integration")] public ARInteractionManager interactionManager; [Header("Glitch Prevention")] public float minAtomDistance = 0.05f; // Variabel Private Internal private GameObject activeModelObject; private Molecule activeMolecule; private Atom activeAtom; // Variabel Baru untuk Fitur Tap Nama private Atom selectedAtom; private int totalAtomsTracked = 0; void Start() { // Urutkan resep agar prioritas molekul kompleks (O3) lebih tinggi dari sederhana (O2) molecules = molecules.OrderByDescending(m => m.requirements.Sum(r => r.count)).ToArray(); foreach (var m in molecules) if(m.model) m.model.SetActive(false); if(panelInfo) panelInfo.SetActive(false); } void Update() { var trackedAtoms = GetTrackedAtomsFiltered(); // Hitung total atom yang muncul di layar saat ini totalAtomsTracked = 0; foreach (var list in trackedAtoms.Values) totalAtomsTracked += list.Count; foreach (var typeList in trackedAtoms.Values) { foreach (var atom in typeList) { if (!atom.visual.activeSelf) atom.visual.SetActive(true); } } // Jalankan logika deteksi Tap setiap frame HandleSelectionInput(); Molecule matched = null; foreach (var m in molecules) { if (Match(m, trackedAtoms)) { matched = m; break; } } if (matched != null) { if (matched != activeMolecule) { HideAllMolecules(); Vector3 startPos = CalculateSmartCenter(matched, trackedAtoms); Quaternion startRot = GetSmartRotation(trackedAtoms); matched.model.transform.position = startPos; matched.model.transform.rotation = startRot; matched.model.SetActive(true); matched.model.transform.localScale = Vector3.one; StartCoroutine(AnimatePop(matched.model.transform)); } activeModelObject = matched.model; activeMolecule = matched; activeAtom = null; Vector3 center = CalculateSmartCenter(activeMolecule, trackedAtoms); activeMolecule.model.transform.position = Vector3.Lerp(activeMolecule.model.transform.position, center, Time.deltaTime * 10f); if (interactionManager != null && !interactionManager.IsUserInteracting()) { Quaternion targetRot = GetSmartRotation(trackedAtoms); activeMolecule.model.transform.rotation = Quaternion.Lerp(activeMolecule.model.transform.rotation, targetRot, Time.deltaTime * 10f); } HideAtomVisuals(activeMolecule, trackedAtoms); } else { HideAllMolecules(); activeMolecule = null; Atom foundAtom = GetFirstTrackedAtom(trackedAtoms); if (foundAtom != null) { activeModelObject = foundAtom.visual; activeAtom = foundAtom; } else { activeModelObject = null; activeAtom = null; } } UpdateUI(); } // --- LOGIKA BARU: DETEKSI TAP PADA ATOM --- void HandleSelectionInput() { // Cek apakah user menekan layar if (Pointer.current != null && Pointer.current.press.wasPressedThisFrame) { Vector2 touchPos = Pointer.current.position.ReadValue(); Ray ray = Camera.main.ScreenPointToRay(touchPos); RaycastHit hit; // Jika raycast mengenai sesuatu yang punya Collider if (Physics.Raycast(ray, out hit)) { // Cari apakah objek tersebut adalah Atom (atau bagian dari prefab Atom) Atom atom = hit.collider.GetComponentInParent(); if (atom != null && atom.isTracked) { selectedAtom = atom; // Simpan atom yang dipilih } else { selectedAtom = null; // Tap objek lain, hilangkan seleksi } } else { selectedAtom = null; // Tap area kosong, hilangkan seleksi } } // Reset seleksi otomatis jika marker atom tersebut hilang dari kamera if (selectedAtom != null && !selectedAtom.isTracked) { selectedAtom = null; } } // --- REVISI LOGIKA UI PANEL --- void UpdateUI() { if (panelInfo == null) return; // 1. CEK DULU: Apakah ada molekul dari BUKU yang lagi fokus?[cite: 21] bool isBookFocused = false; if (interactionManager != null) { // Kita cek ke Interaction Manager, apakah dia lagi pegang model dari buku foreach (var b in interactionManager.bookManagers) { if (b != null && b.GetFocusedMolecule() != null) { isBookFocused = true; break; } } } // 2. LOGIKA TAMPILAN if (activeMolecule != null) // Prioritas Kartu: Molekul { panelInfo.SetActive(true); textNamaMolekul.text = activeMolecule.moleculeName; } else if (totalAtomsTracked == 1 && activeAtom != null) // Prioritas Kartu: 1 Atom { panelInfo.SetActive(true); textNamaMolekul.text = GetAtomDisplayName(activeAtom.atomType); } else if (totalAtomsTracked > 1 && selectedAtom != null) // Prioritas Kartu: Tap Atom[cite: 19] { panelInfo.SetActive(true); textNamaMolekul.text = GetAtomDisplayName(selectedAtom.atomType); } else if (isBookFocused) { // JANGAN DIMATIKAN: Biarkan BookPageManager yang mengontrol teksnya panelInfo.SetActive(true); } else { // Matikan HANYA jika tidak ada kartu DAN tidak ada buku yang fokus[cite: 19] panelInfo.SetActive(false); } } // --- FUNGSI HELPER (TETAP SAMA) --- Quaternion GetSmartRotation(Dictionary> atoms) { foreach(var list in atoms.Values) if (list.Count > 0) return list[0].transform.rotation; return Quaternion.identity; } Dictionary> GetTrackedAtomsFiltered() { var rawDict = new Dictionary>(); foreach (var atom in atoms) { if (atom.isTracked) { if (!rawDict.ContainsKey(atom.atomType)) rawDict[atom.atomType] = new List(); rawDict[atom.atomType].Add(atom); } } var cleanDict = new Dictionary>(); foreach (var kvp in rawDict) { List cleanList = new List(); foreach (var atom in kvp.Value) { bool isGhost = false; foreach (var cleanAtom in cleanList) if (Vector3.Distance(atom.transform.position, cleanAtom.transform.position) < minAtomDistance) { isGhost = true; break; } if (!isGhost) cleanList.Add(atom); else atom.visual.SetActive(false); } cleanDict[kvp.Key] = cleanList; } return cleanDict; } bool Match(Molecule m, Dictionary> a) { foreach (var r in m.requirements) if (!a.ContainsKey(r.atomType) || a[r.atomType].Count < r.count) return false; return true; } Vector3 CalculateSmartCenter(Molecule m, Dictionary> a) { Vector3 c = Vector3.zero; int t = 0; foreach (var r in m.requirements) if (a.ContainsKey(r.atomType)) for(int i=0; i> a) { Vector3 c = CalculateSmartCenter(m, a); foreach (var r in m.requirements) if (a.ContainsKey(r.atomType)) { var s = a[r.atomType].OrderBy(x => Vector3.Distance(x.transform.position, c)).ToList(); for (int i=0; i> d) { foreach(var l in d.Values) if (l.Count > 0) return l[0]; return null; } IEnumerator AnimatePop(Transform t) { float timer = 0; float dur = 0.6f; while (timer < dur) { timer += Time.deltaTime; float x = timer / dur; float scale = 1 + (1.70158f + 1) * Mathf.Pow(x - 1, 3) + 1.70158f * Mathf.Pow(x - 1, 2); t.localScale = Vector3.one * scale; yield return null; } t.localScale = Vector3.one; } string GetAtomDisplayName(AtomType type) { switch (type) { case AtomType.H: return "Hidrogen"; case AtomType.O: return "Oksigen"; case AtomType.C: return "Karbon"; case AtomType.N: return "Nitrogen"; case AtomType.Na: return "Natrium"; case AtomType.S: return "Sulfur"; case AtomType.Cl: return "Klorin"; default: return type.ToString(); } } public GameObject GetActiveModel() { return activeModelObject; } }