Commit c336f670 authored by Mahmoud Aglan's avatar Mahmoud Aglan

SAAAAADDDD START WORKING

parent 9833ad6e
This diff is collapsed.
...@@ -20,7 +20,7 @@ namespace com.al_arcade.cs ...@@ -20,7 +20,7 @@ namespace com.al_arcade.cs
var shader = Shader.Find("Universal Render Pipeline/Lit") var shader = Shader.Find("Universal Render Pipeline/Lit")
?? Shader.Find("Standard") ?? Shader.Find("Unlit/Color"); ?? Shader.Find("Standard") ?? Shader.Find("Unlit/Color");
// ── Body ─────────────────────────────────────────────────
_body = GameObject.CreatePrimitive(PrimitiveType.Capsule); _body = GameObject.CreatePrimitive(PrimitiveType.Capsule);
_body.name = "Body"; _body.name = "Body";
_body.transform.SetParent(transform); _body.transform.SetParent(transform);
...@@ -30,7 +30,7 @@ namespace com.al_arcade.cs ...@@ -30,7 +30,7 @@ namespace com.al_arcade.cs
_bodyMat = new Material(shader) { color = SSColorPalette.Primary }; _bodyMat = new Material(shader) { color = SSColorPalette.Primary };
_body.GetComponent<Renderer>().material = _bodyMat; _body.GetComponent<Renderer>().material = _bodyMat;
// ── Head ─────────────────────────────────────────────────
_head = GameObject.CreatePrimitive(PrimitiveType.Sphere); _head = GameObject.CreatePrimitive(PrimitiveType.Sphere);
_head.name = "Head"; _head.name = "Head";
_head.transform.SetParent(transform); _head.transform.SetParent(transform);
...@@ -40,7 +40,7 @@ namespace com.al_arcade.cs ...@@ -40,7 +40,7 @@ namespace com.al_arcade.cs
_headMat = new Material(shader) { color = SSColorPalette.Accent }; _headMat = new Material(shader) { color = SSColorPalette.Accent };
_head.GetComponent<Renderer>().material = _headMat; _head.GetComponent<Renderer>().material = _headMat;
// ── Eyes ─────────────────────────────────────────────────
var eyeMat = new Material(shader) { color = Color.white }; var eyeMat = new Material(shader) { color = Color.white };
var pupilMat = new Material(shader) { color = SSColorPalette.TextDark }; var pupilMat = new Material(shader) { color = SSColorPalette.TextDark };
...@@ -65,7 +65,7 @@ namespace com.al_arcade.cs ...@@ -65,7 +65,7 @@ namespace com.al_arcade.cs
pupil.GetComponent<Renderer>().material = new Material(pupilMat); pupil.GetComponent<Renderer>().material = new Material(pupilMat);
} }
// ── Mouth ────────────────────────────────────────────────
_mouth = GameObject.CreatePrimitive(PrimitiveType.Cube); _mouth = GameObject.CreatePrimitive(PrimitiveType.Cube);
_mouth.name = "Mouth"; _mouth.name = "Mouth";
_mouth.transform.SetParent(_head.transform); _mouth.transform.SetParent(_head.transform);
...@@ -75,7 +75,7 @@ namespace com.al_arcade.cs ...@@ -75,7 +75,7 @@ namespace com.al_arcade.cs
_mouthMat = new Material(shader) { color = SSColorPalette.Danger }; _mouthMat = new Material(shader) { color = SSColorPalette.Danger };
_mouth.GetComponent<Renderer>().material = _mouthMat; _mouth.GetComponent<Renderer>().material = _mouthMat;
// ── Arms ─────────────────────────────────────────────────
for (int side = -1; side <= 1; side += 2) for (int side = -1; side <= 1; side += 2)
{ {
var arm = GameObject.CreatePrimitive(PrimitiveType.Capsule); var arm = GameObject.CreatePrimitive(PrimitiveType.Capsule);
...@@ -89,7 +89,7 @@ namespace com.al_arcade.cs ...@@ -89,7 +89,7 @@ namespace com.al_arcade.cs
new Material(shader) { color = SSColorPalette.PrimaryDark }; new Material(shader) { color = SSColorPalette.PrimaryDark };
} }
// ── Feet ─────────────────────────────────────────────────
for (int side = -1; side <= 1; side += 2) for (int side = -1; side <= 1; side += 2)
{ {
var foot = GameObject.CreatePrimitive(PrimitiveType.Cube); var foot = GameObject.CreatePrimitive(PrimitiveType.Cube);
...@@ -160,7 +160,6 @@ namespace com.al_arcade.cs ...@@ -160,7 +160,6 @@ namespace com.al_arcade.cs
} }
} }
// ── Reactions ────────────────────────────────────────────────
public void PlayHappy() public void PlayHappy()
{ {
...@@ -183,7 +182,7 @@ namespace com.al_arcade.cs ...@@ -183,7 +182,7 @@ namespace com.al_arcade.cs
.OnComplete(() => _headMat.DOColor(SSColorPalette.Accent, 0.4f)); .OnComplete(() => _headMat.DOColor(SSColorPalette.Accent, 0.4f));
} }
// Reset mouth after delay
DOTween.Sequence().SetDelay(1f).OnComplete(() => DOTween.Sequence().SetDelay(1f).OnComplete(() =>
{ {
if (_mouth == null) return; if (_mouth == null) return;
......
...@@ -44,7 +44,7 @@ namespace com.al_arcade.cs ...@@ -44,7 +44,7 @@ namespace com.al_arcade.cs
Debug.Log(" Correct-the-Sentence Game"); Debug.Log(" Correct-the-Sentence Game");
Debug.Log("═══════════════════════════════════════════"); Debug.Log("═══════════════════════════════════════════");
// ── EventSystem ──────────────────────────────────────────
if (FindObjectOfType<EventSystem>() == null) if (FindObjectOfType<EventSystem>() == null)
{ {
var es = new GameObject("EventSystem"); var es = new GameObject("EventSystem");
...@@ -52,7 +52,7 @@ namespace com.al_arcade.cs ...@@ -52,7 +52,7 @@ namespace com.al_arcade.cs
es.AddComponent<StandaloneInputModule>(); es.AddComponent<StandaloneInputModule>();
} }
// ── Managers ─────────────────────────────────────────────
SSApiManager.EnsureInstance(); SSApiManager.EnsureInstance();
SSAudioManager.EnsureInstance(); SSAudioManager.EnsureInstance();
SSParticleManager.EnsureInstance(); SSParticleManager.EnsureInstance();
...@@ -64,49 +64,49 @@ namespace com.al_arcade.cs ...@@ -64,49 +64,49 @@ namespace com.al_arcade.cs
session.classCode = classCode; session.classCode = classCode;
yield return null; yield return null;
// ── Scene ────────────────────────────────────────────────
BuildScene(); BuildScene();
yield return null; yield return null;
// ── Camera ───────────────────────────────────────────────
SetupCamera(); SetupCamera();
yield return null; yield return null;
// ── Bot ──────────────────────────────────────────────────
var botObj = new GameObject("Bot"); var botObj = new GameObject("Bot");
botObj.transform.position = new Vector3(0, 0, 0); botObj.transform.position = new Vector3(0, 0, 0);
_bot = botObj.AddComponent<CsBotController>(); _bot = botObj.AddComponent<CsBotController>();
_bot.Build(); _bot.Build();
yield return null; yield return null;
// ── Word Container ───────────────────────────────────────
var wordContainer = new GameObject("WordContainer"); var wordContainer = new GameObject("WordContainer");
wordContainer.transform.position = Vector3.zero; wordContainer.transform.position = Vector3.zero;
yield return null; yield return null;
// ── UI ───────────────────────────────────────────────────
var uiObj = new GameObject("CsUI"); var uiObj = new GameObject("CsUI");
_uiManager = uiObj.AddComponent<CsUIManager>(); _uiManager = uiObj.AddComponent<CsUIManager>();
_uiManager.BuildUI(); _uiManager.BuildUI();
// ★ FIX: Initialize the UnityEvent if null
if (_uiManager.onRestartClicked == null) if (_uiManager.onRestartClicked == null)
_uiManager.onRestartClicked = new UnityEvent(); _uiManager.onRestartClicked = new UnityEvent();
yield return null; yield return null;
// ── Game Manager ─────────────────────────────────────────
var gmObj = new GameObject("CsGameManager"); var gmObj = new GameObject("CsGameManager");
_gm = gmObj.AddComponent<CsGameManager>(); _gm = gmObj.AddComponent<CsGameManager>();
_gm.bot = _bot; _gm.bot = _bot;
_gm.uiManager = _uiManager; _gm.uiManager = _uiManager;
_gm.wordContainer = wordContainer.transform; _gm.wordContainer = wordContainer.transform;
// ★ FIX: Safe listener via named method
_uiManager.onRestartClicked.AddListener(OnRestartClicked); _uiManager.onRestartClicked.AddListener(OnRestartClicked);
yield return null; yield return null;
// ── Lighting ─────────────────────────────────────────────
SetupLighting(); SetupLighting();
yield return null; yield return null;
...@@ -118,16 +118,13 @@ namespace com.al_arcade.cs ...@@ -118,16 +118,13 @@ namespace com.al_arcade.cs
yield return new WaitForSeconds(0.5f); yield return new WaitForSeconds(0.5f);
// ── Start ────────────────────────────────────────────────
if (useOfflineTestData) if (useOfflineTestData)
_gm.StartWithQuestions(GetTestQuestions()); _gm.StartWithQuestions(GetTestQuestions());
else else
_gm.StartGame(); _gm.StartGame();
} }
// ════════════════════════════════════════════════════════════
// RESTART HANDLER
// ════════════════════════════════════════════════════════════
private void OnRestartClicked() private void OnRestartClicked()
{ {
...@@ -135,9 +132,6 @@ namespace com.al_arcade.cs ...@@ -135,9 +132,6 @@ namespace com.al_arcade.cs
if (_gm != null) _gm.StartGame(); if (_gm != null) _gm.StartGame();
} }
// ════════════════════════════════════════════════════════════
// SCENE
// ════════════════════════════════════════════════════════════
private void BuildScene() private void BuildScene()
{ {
...@@ -146,7 +140,7 @@ namespace com.al_arcade.cs ...@@ -146,7 +140,7 @@ namespace com.al_arcade.cs
var shader = GetShader(); var shader = GetShader();
// Ground
var ground = GameObject.CreatePrimitive(PrimitiveType.Cube); var ground = GameObject.CreatePrimitive(PrimitiveType.Cube);
ground.name = "Ground"; ground.name = "Ground";
ground.transform.position = new Vector3(0, -0.05f, 0); ground.transform.position = new Vector3(0, -0.05f, 0);
...@@ -154,7 +148,7 @@ namespace com.al_arcade.cs ...@@ -154,7 +148,7 @@ namespace com.al_arcade.cs
ground.GetComponent<Renderer>().material = ground.GetComponent<Renderer>().material =
new Material(shader) { color = new Color32(40, 40, 65, 255) }; new Material(shader) { color = new Color32(40, 40, 65, 255) };
// Circular platform
var platform = GameObject.CreatePrimitive(PrimitiveType.Cylinder); var platform = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
platform.name = "Platform"; platform.name = "Platform";
platform.transform.position = new Vector3(0, 0.05f, 0); platform.transform.position = new Vector3(0, 0.05f, 0);
...@@ -163,7 +157,7 @@ namespace com.al_arcade.cs ...@@ -163,7 +157,7 @@ namespace com.al_arcade.cs
platform.GetComponent<Renderer>().material = platform.GetComponent<Renderer>().material =
new Material(shader) { color = SSColorPalette.WithAlpha(SSColorPalette.Primary, 0.6f) }; new Material(shader) { color = SSColorPalette.WithAlpha(SSColorPalette.Primary, 0.6f) };
// Outer ring with pulse
var ring = GameObject.CreatePrimitive(PrimitiveType.Cylinder); var ring = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
ring.name = "Ring"; ring.name = "Ring";
ring.transform.position = new Vector3(0, 0.04f, 0); ring.transform.position = new Vector3(0, 0.04f, 0);
...@@ -175,7 +169,7 @@ namespace com.al_arcade.cs ...@@ -175,7 +169,7 @@ namespace com.al_arcade.cs
ringMat.DOFade(0.1f, 1.5f).SetEase(Ease.InOutSine) ringMat.DOFade(0.1f, 1.5f).SetEase(Ease.InOutSine)
.SetLoops(-1, LoopType.Yoyo); .SetLoops(-1, LoopType.Yoyo);
// Background decorations
for (int i = 0; i < 20; i++) for (int i = 0; i < 20; i++)
{ {
var deco = GameObject.CreatePrimitive( var deco = GameObject.CreatePrimitive(
...@@ -213,7 +207,7 @@ namespace com.al_arcade.cs ...@@ -213,7 +207,7 @@ namespace com.al_arcade.cs
.SetLoops(-1, LoopType.Incremental).SetEase(Ease.Linear); .SetLoops(-1, LoopType.Incremental).SetEase(Ease.Linear);
} }
// Grid lines on ground
for (float x = -9; x <= 9; x += 3) for (float x = -9; x <= 9; x += 3)
{ {
var line = GameObject.CreatePrimitive(PrimitiveType.Cube); var line = GameObject.CreatePrimitive(PrimitiveType.Cube);
...@@ -235,7 +229,7 @@ namespace com.al_arcade.cs ...@@ -235,7 +229,7 @@ namespace com.al_arcade.cs
{ color = SSColorPalette.WithAlpha(SSColorPalette.Primary, 0.08f) }; { color = SSColorPalette.WithAlpha(SSColorPalette.Primary, 0.08f) };
} }
// Floor label
var floorText = new GameObject("FloorLabel"); var floorText = new GameObject("FloorLabel");
floorText.transform.position = new Vector3(0, 0.02f, -3); floorText.transform.position = new Vector3(0, 0.02f, -3);
floorText.transform.rotation = Quaternion.Euler(90, 0, 0); floorText.transform.rotation = Quaternion.Euler(90, 0, 0);
...@@ -276,7 +270,7 @@ namespace com.al_arcade.cs ...@@ -276,7 +270,7 @@ namespace com.al_arcade.cs
dirLight.intensity = 0.9f; dirLight.intensity = 0.9f;
dirLight.shadows = LightShadows.Soft; dirLight.shadows = LightShadows.Soft;
// Accent light on bot
var accent = new GameObject("AccentLight"); var accent = new GameObject("AccentLight");
var aLight = accent.AddComponent<Light>(); var aLight = accent.AddComponent<Light>();
aLight.type = LightType.Point; aLight.type = LightType.Point;
...@@ -285,7 +279,7 @@ namespace com.al_arcade.cs ...@@ -285,7 +279,7 @@ namespace com.al_arcade.cs
aLight.intensity = 0.8f; aLight.intensity = 0.8f;
aLight.color = SSColorPalette.Accent; aLight.color = SSColorPalette.Accent;
// Blue back light
var back = new GameObject("BackLight"); var back = new GameObject("BackLight");
var bLight = back.AddComponent<Light>(); var bLight = back.AddComponent<Light>();
bLight.type = LightType.Point; bLight.type = LightType.Point;
...@@ -298,9 +292,6 @@ namespace com.al_arcade.cs ...@@ -298,9 +292,6 @@ namespace com.al_arcade.cs
RenderSettings.ambientLight = new Color32(40, 42, 60, 255); RenderSettings.ambientLight = new Color32(40, 42, 60, 255);
} }
// ════════════════════════════════════════════════════════════
// TEST DATA
// ════════════════════════════════════════════════════════════
private CsQuestion[] GetTestQuestions() private CsQuestion[] GetTestQuestions()
{ {
...@@ -418,4 +409,4 @@ namespace com.al_arcade.cs ...@@ -418,4 +409,4 @@ namespace com.al_arcade.cs
?? Shader.Find("Unlit/Color"); ?? Shader.Find("Unlit/Color");
} }
} }
} }
\ No newline at end of file
...@@ -29,7 +29,7 @@ namespace com.al_arcade.cs ...@@ -29,7 +29,7 @@ namespace com.al_arcade.cs
public CsUIManager uiManager; public CsUIManager uiManager;
public Transform wordContainer; public Transform wordContainer;
// State
private CsGameState _state = CsGameState.Idle; private CsGameState _state = CsGameState.Idle;
private CsQuestion[] _questions; private CsQuestion[] _questions;
private int _currentIndex; private int _currentIndex;
...@@ -52,9 +52,6 @@ namespace com.al_arcade.cs ...@@ -52,9 +52,6 @@ namespace com.al_arcade.cs
Instance = this; Instance = this;
} }
// ═══════════════════════════════════════════════════════════
// PUBLIC API
// ═══════════════════════════════════════════════════════════
public void StartGame() => StartCoroutine(StartGameRoutine()); public void StartGame() => StartCoroutine(StartGameRoutine());
...@@ -112,9 +109,6 @@ namespace com.al_arcade.cs ...@@ -112,9 +109,6 @@ namespace com.al_arcade.cs
_onOptionDropped?.Invoke(isCorrectOption); _onOptionDropped?.Invoke(isCorrectOption);
} }
// ═══════════════════════════════════════════════════════════
// GAME FLOW
// ═══════════════════════════════════════════════════════════
private IEnumerator StartGameRoutine() private IEnumerator StartGameRoutine()
{ {
...@@ -277,9 +271,6 @@ namespace com.al_arcade.cs ...@@ -277,9 +271,6 @@ namespace com.al_arcade.cs
yield return new WaitForSeconds(0.8f); yield return new WaitForSeconds(0.8f);
} }
// ═══════════════════════════════════════════════════════════
// WORD SPAWNING — ★ Dynamic sizing, no overlaps
// ═══════════════════════════════════════════════════════════
private void SpawnWords(CsQuestion question) private void SpawnWords(CsQuestion question)
{ {
...@@ -288,23 +279,23 @@ namespace com.al_arcade.cs ...@@ -288,23 +279,23 @@ namespace com.al_arcade.cs
int wordCount = question.words.Length; int wordCount = question.words.Length;
// ★ Calculate total width needed based on actual text lengths
float padding = 0.6f; // extra padding per word card float padding = 0.6f;
float gapBetween = 0.25f; // gap between cards float gapBetween = 0.25f;
float charWidth = 0.32f; // approx width per character float charWidth = 0.32f;
float[] wordWidths = new float[wordCount]; float[] wordWidths = new float[wordCount];
float totalWidth = 0; float totalWidth = 0;
for (int i = 0; i < wordCount; i++) for (int i = 0; i < wordCount; i++)
{ {
float w = question.words[i].word_text.Length * charWidth + padding; float w = question.words[i].word_text.Length * charWidth + padding;
w = Mathf.Max(w, 1.0f); // minimum width w = Mathf.Max(w, 1.0f);
wordWidths[i] = w; wordWidths[i] = w;
totalWidth += w; totalWidth += w;
} }
totalWidth += (wordCount - 1) * gapBetween; totalWidth += (wordCount - 1) * gapBetween;
// ★ If total width exceeds max, scale everything down
float maxAllowedWidth = 14f; float maxAllowedWidth = 14f;
float scaleFactor = 1f; float scaleFactor = 1f;
if (totalWidth > maxAllowedWidth) if (totalWidth > maxAllowedWidth)
...@@ -315,12 +306,12 @@ namespace com.al_arcade.cs ...@@ -315,12 +306,12 @@ namespace com.al_arcade.cs
wordWidths[i] *= scaleFactor; wordWidths[i] *= scaleFactor;
} }
// Base position above bot
Vector3 basePos = bot != null Vector3 basePos = bot != null
? bot.transform.position + Vector3.up * 3.5f ? bot.transform.position + Vector3.up * 3.5f
: Vector3.up * 4f; : Vector3.up * 4f;
// ★ RTL layout: start from right edge
float startX = totalWidth / 2f; float startX = totalWidth / 2f;
float currentX = startX; float currentX = startX;
...@@ -339,7 +330,7 @@ namespace com.al_arcade.cs ...@@ -339,7 +330,7 @@ namespace com.al_arcade.cs
wb.Setup(word.word_text, word.is_wrong, i, wordWidths[i], scaleFactor); wb.Setup(word.word_text, word.is_wrong, i, wordWidths[i], scaleFactor);
_wordButtons.Add(wb); _wordButtons.Add(wb);
// Entrance animation
wordObj.transform.localScale = Vector3.zero; wordObj.transform.localScale = Vector3.zero;
wordObj.transform.DOScale(Vector3.one, 0.4f) wordObj.transform.DOScale(Vector3.one, 0.4f)
.SetDelay(i * 0.08f) .SetDelay(i * 0.08f)
...@@ -378,7 +369,6 @@ namespace com.al_arcade.cs ...@@ -378,7 +369,6 @@ namespace com.al_arcade.cs
_wordClickLocked = false; _wordClickLocked = false;
} }
// ── Victory ──────────────────────────────────────────────────
private IEnumerator VictorySequence() private IEnumerator VictorySequence()
{ {
......
...@@ -23,7 +23,7 @@ namespace com.al_arcade.cs ...@@ -23,7 +23,7 @@ namespace com.al_arcade.cs
private CsWordButton _targetWord; private CsWordButton _targetWord;
private CanvasGroup _canvasGroup; private CanvasGroup _canvasGroup;
// ★ Drag offset: distance from pointer to rect center at grab time
private Vector2 _dragOffset; private Vector2 _dragOffset;
private bool _originalPosCaptured; private bool _originalPosCaptured;
private Camera _canvasCamera; private Camera _canvasCamera;
...@@ -38,20 +38,20 @@ namespace com.al_arcade.cs ...@@ -38,20 +38,20 @@ namespace com.al_arcade.cs
_rect = GetComponent<RectTransform>(); _rect = GetComponent<RectTransform>();
if (_rect == null) _rect = gameObject.AddComponent<RectTransform>(); if (_rect == null) _rect = gameObject.AddComponent<RectTransform>();
// ★ CanvasGroup so we can make it not block raycasts while dragging
_canvasGroup = gameObject.AddComponent<CanvasGroup>(); _canvasGroup = gameObject.AddComponent<CanvasGroup>();
// Determine canvas camera (null for ScreenSpaceOverlay)
_canvasCamera = null; _canvasCamera = null;
if (_canvas != null && _canvas.renderMode != RenderMode.ScreenSpaceOverlay) if (_canvas != null && _canvas.renderMode != RenderMode.ScreenSpaceOverlay)
_canvasCamera = _canvas.worldCamera; _canvasCamera = _canvas.worldCamera;
// Background
_bg = gameObject.AddComponent<Image>(); _bg = gameObject.AddComponent<Image>();
_bg.color = SSColorPalette.Card; _bg.color = SSColorPalette.Card;
_bg.raycastTarget = true; _bg.raycastTarget = true;
// Text
var textObj = new GameObject("Text"); var textObj = new GameObject("Text");
textObj.transform.SetParent(transform, false); textObj.transform.SetParent(transform, false);
var textRect = textObj.AddComponent<RectTransform>(); var textRect = textObj.AddComponent<RectTransform>();
...@@ -72,41 +72,37 @@ namespace com.al_arcade.cs ...@@ -72,41 +72,37 @@ namespace com.al_arcade.cs
_text.fontSizeMax = 22; _text.fontSizeMax = 22;
SSFontManager.Apply(_text); SSFontManager.Apply(_text);
// Entrance animation
transform.localScale = Vector3.one * 0.5f; transform.localScale = Vector3.one * 0.5f;
transform.DOScale(Vector3.one, 0.3f).SetEase(Ease.OutBack); transform.DOScale(Vector3.one, 0.3f).SetEase(Ease.OutBack);
_originalPosCaptured = false; _originalPosCaptured = false;
} }
// ════════════════════════════════════════════════════════════
// DRAG HANDLERS — ★ Cursor-accurate positioning
// ════════════════════════════════════════════════════════════
public void OnBeginDrag(PointerEventData eventData) public void OnBeginDrag(PointerEventData eventData)
{ {
// ★ Capture the original anchored position for return-to-origin
_originalAnchoredPos = _rect.anchoredPosition; _originalAnchoredPos = _rect.anchoredPosition;
_originalPosCaptured = true; _originalPosCaptured = true;
// ★ Calculate offset: where the pointer is relative to the rect's center
// so the card doesn't jump — it stays exactly where you grabbed it
Vector2 localPointerPos; Vector2 localPointerPos;
RectTransformUtility.ScreenPointToLocalPointInRectangle( RectTransformUtility.ScreenPointToLocalPointInRectangle(
_rect.parent as RectTransform, // parent rect _rect.parent as RectTransform,
eventData.position, // screen pointer eventData.position,
_canvasCamera, // camera (null for overlay) _canvasCamera,
out localPointerPos); out localPointerPos);
_dragOffset = _rect.anchoredPosition - localPointerPos; _dragOffset = _rect.anchoredPosition - localPointerPos;
// Visual feedback
_bg.color = SSColorPalette.WithAlpha(SSColorPalette.Primary, 0.3f); _bg.color = SSColorPalette.WithAlpha(SSColorPalette.Primary, 0.3f);
DOTween.Kill(transform, "optScale"); DOTween.Kill(transform, "optScale");
transform.DOScale(Vector3.one * 1.08f, 0.12f).SetId("optScale"); transform.DOScale(Vector3.one * 1.08f, 0.12f).SetId("optScale");
transform.SetAsLastSibling(); transform.SetAsLastSibling();
// ★ Let raycasts pass through while dragging (optional, helps with drop detection)
_canvasGroup.blocksRaycasts = false; _canvasGroup.blocksRaycasts = false;
var audio = SSAudioManager.Instance; var audio = SSAudioManager.Instance;
...@@ -117,7 +113,7 @@ namespace com.al_arcade.cs ...@@ -117,7 +113,7 @@ namespace com.al_arcade.cs
{ {
if (_rect == null || _canvas == null) return; if (_rect == null || _canvas == null) return;
// ★ Convert screen pointer to local position in parent
Vector2 localPointerPos; Vector2 localPointerPos;
RectTransformUtility.ScreenPointToLocalPointInRectangle( RectTransformUtility.ScreenPointToLocalPointInRectangle(
_rect.parent as RectTransform, _rect.parent as RectTransform,
...@@ -125,16 +121,16 @@ namespace com.al_arcade.cs ...@@ -125,16 +121,16 @@ namespace com.al_arcade.cs
_canvasCamera, _canvasCamera,
out localPointerPos); out localPointerPos);
// ★ Apply with the initial offset so the card stays under the finger
_rect.anchoredPosition = localPointerPos + _dragOffset; _rect.anchoredPosition = localPointerPos + _dragOffset;
} }
public void OnEndDrag(PointerEventData eventData) public void OnEndDrag(PointerEventData eventData)
{ {
// Re-enable raycasts
_canvasGroup.blocksRaycasts = true; _canvasGroup.blocksRaycasts = true;
// ★ Check if dropped near the target word
bool droppedOnTarget = false; bool droppedOnTarget = false;
if (_targetWord != null && Camera.main != null) if (_targetWord != null && Camera.main != null)
{ {
...@@ -151,7 +147,7 @@ namespace com.al_arcade.cs ...@@ -151,7 +147,7 @@ namespace com.al_arcade.cs
if (IsCorrect) if (IsCorrect)
{ {
// Snap to target and disappear
DOTween.Kill(transform); DOTween.Kill(transform);
transform.DOScale(Vector3.zero, 0.3f) transform.DOScale(Vector3.zero, 0.3f)
.SetEase(Ease.InBack) .SetEase(Ease.InBack)
...@@ -163,14 +159,14 @@ namespace com.al_arcade.cs ...@@ -163,14 +159,14 @@ namespace com.al_arcade.cs
} }
else else
{ {
// Wrong — shake then return
DOTween.Kill(transform, "optShake"); DOTween.Kill(transform, "optShake");
transform.DOShakePosition(0.3f, 10f, 15).SetId("optShake"); transform.DOShakePosition(0.3f, 10f, 15).SetId("optShake");
} }
} }
} }
// ★ Return to original position (always, unless correct + destroyed)
ReturnToOrigin(); ReturnToOrigin();
} }
...@@ -188,9 +184,6 @@ namespace com.al_arcade.cs ...@@ -188,9 +184,6 @@ namespace com.al_arcade.cs
} }
} }
// ════════════════════════════════════════════════════════════
// HOVER
// ════════════════════════════════════════════════════════════
public void OnPointerEnter(PointerEventData eventData) public void OnPointerEnter(PointerEventData eventData)
{ {
...@@ -205,9 +198,6 @@ namespace com.al_arcade.cs ...@@ -205,9 +198,6 @@ namespace com.al_arcade.cs
_bg.DOColor(SSColorPalette.Card, 0.15f).SetId("optHover"); _bg.DOColor(SSColorPalette.Card, 0.15f).SetId("optHover");
} }
// ════════════════════════════════════════════════════════════
// CLEANUP
// ════════════════════════════════════════════════════════════
private void OnDestroy() private void OnDestroy()
{ {
...@@ -216,4 +206,4 @@ namespace com.al_arcade.cs ...@@ -216,4 +206,4 @@ namespace com.al_arcade.cs
if (_bg != null) DOTween.Kill(_bg); if (_bg != null) DOTween.Kill(_bg);
} }
} }
} }
\ No newline at end of file
...@@ -28,9 +28,6 @@ namespace com.al_arcade.cs ...@@ -28,9 +28,6 @@ namespace com.al_arcade.cs
[Header("Events")] [Header("Events")]
public UnityEvent onRestartClicked; public UnityEvent onRestartClicked;
// ════════════════════════════════════════════════════════════
// BUILD
// ════════════════════════════════════════════════════════════
public void BuildUI() public void BuildUI()
{ {
...@@ -61,14 +58,13 @@ namespace com.al_arcade.cs ...@@ -61,14 +58,13 @@ namespace com.al_arcade.cs
_feedbackGroup.alpha = 0; _feedbackGroup.alpha = 0;
} }
// ── Game HUD ─────────────────────────────────────────────────
private void BuildGameHUD(Transform parent) private void BuildGameHUD(Transform parent)
{ {
var go = MkFull(parent, "GameUI"); var go = MkFull(parent, "GameUI");
_gameUI = go.AddComponent<CanvasGroup>(); _gameUI = go.AddComponent<CanvasGroup>();
// Top bar
var topBar = new GameObject("TopBar"); var topBar = new GameObject("TopBar");
topBar.transform.SetParent(go.transform, false); topBar.transform.SetParent(go.transform, false);
var tbr = topBar.AddComponent<RectTransform>(); var tbr = topBar.AddComponent<RectTransform>();
...@@ -93,7 +89,7 @@ namespace com.al_arcade.cs ...@@ -93,7 +89,7 @@ namespace com.al_arcade.cs
_streakText.color = SSColorPalette.Warning; _streakText.color = SSColorPalette.Warning;
_streakText.alignment = TMPro.TextAlignmentOptions.Center; _streakText.alignment = TMPro.TextAlignmentOptions.Center;
// Progress bar
var pbg = MkFull(go.transform, "ProgressBg"); var pbg = MkFull(go.transform, "ProgressBg");
var pr = pbg.GetComponent<RectTransform>(); var pr = pbg.GetComponent<RectTransform>();
pr.anchorMin = new Vector2(0.25f, 1); pr.anchorMax = new Vector2(0.75f, 1); pr.anchorMin = new Vector2(0.25f, 1); pr.anchorMax = new Vector2(0.75f, 1);
...@@ -115,7 +111,7 @@ namespace com.al_arcade.cs ...@@ -115,7 +111,7 @@ namespace com.al_arcade.cs
_progressText.alignment = TMPro.TextAlignmentOptions.Center; _progressText.alignment = TMPro.TextAlignmentOptions.Center;
_progressText.color = SSColorPalette.WithAlpha(Color.white, 0.5f); _progressText.color = SSColorPalette.WithAlpha(Color.white, 0.5f);
// Hint
_hintText = MkTxt(go.transform, "Hint", "", 18, _hintText = MkTxt(go.transform, "Hint", "", 18,
new Vector2(0.5f, 0), new Vector2(0, 200), new Vector2(600, 40)); new Vector2(0.5f, 0), new Vector2(0, 200), new Vector2(600, 40));
_hintText.alignment = TMPro.TextAlignmentOptions.Center; _hintText.alignment = TMPro.TextAlignmentOptions.Center;
...@@ -123,7 +119,6 @@ namespace com.al_arcade.cs ...@@ -123,7 +119,6 @@ namespace com.al_arcade.cs
_hintText.fontStyle = TMPro.FontStyles.Italic; _hintText.fontStyle = TMPro.FontStyles.Italic;
} }
// ── Options Panel ────────────────────────────────────────────
private void BuildOptionsPanel(Transform parent) private void BuildOptionsPanel(Transform parent)
{ {
...@@ -155,7 +150,6 @@ namespace com.al_arcade.cs ...@@ -155,7 +150,6 @@ namespace com.al_arcade.cs
.color = SSColorPalette.WithAlpha(SSColorPalette.Accent, 0.6f); .color = SSColorPalette.WithAlpha(SSColorPalette.Accent, 0.6f);
} }
// ── Feedback Overlay ─────────────────────────────────────────
private void BuildFeedbackOverlay(Transform parent) private void BuildFeedbackOverlay(Transform parent)
{ {
...@@ -185,7 +179,6 @@ namespace com.al_arcade.cs ...@@ -185,7 +179,6 @@ namespace com.al_arcade.cs
_feedbackText.enableWordWrapping = true; _feedbackText.enableWordWrapping = true;
} }
// ── Loading ──────────────────────────────────────────────────
private void BuildLoadingPanel(Transform parent) private void BuildLoadingPanel(Transform parent)
{ {
...@@ -199,7 +192,6 @@ namespace com.al_arcade.cs ...@@ -199,7 +192,6 @@ namespace com.al_arcade.cs
_loadingText.color = Color.white; _loadingText.color = Color.white;
} }
// ── Error ────────────────────────────────────────────────────
private void BuildErrorPanel(Transform parent) private void BuildErrorPanel(Transform parent)
{ {
...@@ -214,7 +206,6 @@ namespace com.al_arcade.cs ...@@ -214,7 +206,6 @@ namespace com.al_arcade.cs
_errorText.enableWordWrapping = true; _errorText.enableWordWrapping = true;
} }
// ── Results ──────────────────────────────────────────────────
private void BuildResultsPanel(Transform parent) private void BuildResultsPanel(Transform parent)
{ {
...@@ -241,7 +232,7 @@ namespace com.al_arcade.cs ...@@ -241,7 +232,7 @@ namespace com.al_arcade.cs
_resultStats.color = SSColorPalette.WithAlpha(Color.white, 0.7f); _resultStats.color = SSColorPalette.WithAlpha(Color.white, 0.7f);
_resultStats.enableWordWrapping = true; _resultStats.enableWordWrapping = true;
// Restart button
var btn = new GameObject("RestartBtn"); var btn = new GameObject("RestartBtn");
btn.transform.SetParent(go.transform, false); btn.transform.SetParent(go.transform, false);
var br = btn.AddComponent<RectTransform>(); var br = btn.AddComponent<RectTransform>();
...@@ -264,9 +255,6 @@ namespace com.al_arcade.cs ...@@ -264,9 +255,6 @@ namespace com.al_arcade.cs
bt.fontStyle = TMPro.FontStyles.Bold; bt.fontStyle = TMPro.FontStyles.Bold;
} }
// ════════════════════════════════════════════════════════════
// PUBLIC METHODS
// ════════════════════════════════════════════════════════════
public void ShowGameUI() public void ShowGameUI()
{ _gameUI.gameObject.SetActive(true); _gameUI.DOFade(1f, 0.3f); } { _gameUI.gameObject.SetActive(true); _gameUI.DOFade(1f, 0.3f); }
...@@ -333,7 +321,6 @@ namespace com.al_arcade.cs ...@@ -333,7 +321,6 @@ namespace com.al_arcade.cs
} }
} }
// ── Options ──────────────────────────────────────────────────
public void ShowOptions(CsOption[] options, CsWordButton targetWord) public void ShowOptions(CsOption[] options, CsWordButton targetWord)
{ {
...@@ -387,7 +374,6 @@ namespace com.al_arcade.cs ...@@ -387,7 +374,6 @@ namespace com.al_arcade.cs
_activeOptions.Clear(); _activeOptions.Clear();
} }
// ── Loading / Error / Results ────────────────────────────────
public void ShowLoading(string msg) public void ShowLoading(string msg)
{ _loadingUI.gameObject.SetActive(true); if (_loadingText != null) _loadingText.arabicText = msg; _loadingUI.DOFade(1f, 0.3f); } { _loadingUI.gameObject.SetActive(true); if (_loadingText != null) _loadingText.arabicText = msg; _loadingUI.DOFade(1f, 0.3f); }
...@@ -440,7 +426,6 @@ namespace com.al_arcade.cs ...@@ -440,7 +426,6 @@ namespace com.al_arcade.cs
ClearOptions(); ClearOptions();
} }
// ── Helpers ──────────────────────────────────────────────────
private GameObject MkFull(Transform p, string n) private GameObject MkFull(Transform p, string n)
{ {
......
...@@ -22,9 +22,7 @@ namespace com.al_arcade.cs ...@@ -22,9 +22,7 @@ namespace com.al_arcade.cs
private float _cardWidth; private float _cardWidth;
private float _cardHeight = 0.8f; private float _cardHeight = 0.8f;
/// <summary>
/// Setup with explicit width and optional scale factor to prevent overlaps.
/// </summary>
public void Setup(string text, bool isWrong, int index, public void Setup(string text, bool isWrong, int index,
float cardWidth = -1f, float scaleFactor = 1f) float cardWidth = -1f, float scaleFactor = 1f)
{ {
...@@ -32,7 +30,7 @@ namespace com.al_arcade.cs ...@@ -32,7 +30,7 @@ namespace com.al_arcade.cs
IsWrong = isWrong; IsWrong = isWrong;
Index = index; Index = index;
// ★ If width not provided, calculate from text
if (cardWidth <= 0) if (cardWidth <= 0)
_cardWidth = text.Length * 0.32f + 0.6f; _cardWidth = text.Length * 0.32f + 0.6f;
else else
...@@ -49,7 +47,7 @@ namespace com.al_arcade.cs ...@@ -49,7 +47,7 @@ namespace com.al_arcade.cs
var shader = Shader.Find("Universal Render Pipeline/Lit") var shader = Shader.Find("Universal Render Pipeline/Lit")
?? Shader.Find("Standard") ?? Shader.Find("Unlit/Color"); ?? Shader.Find("Standard") ?? Shader.Find("Unlit/Color");
// ── Background card ──────────────────────────────────────
_background = GameObject.CreatePrimitive(PrimitiveType.Cube); _background = GameObject.CreatePrimitive(PrimitiveType.Cube);
_background.name = "WordBg"; _background.name = "WordBg";
_background.transform.SetParent(transform); _background.transform.SetParent(transform);
...@@ -60,11 +58,11 @@ namespace com.al_arcade.cs ...@@ -60,11 +58,11 @@ namespace com.al_arcade.cs
_bgMaterial = new Material(shader) { color = SSColorPalette.NeutralWord }; _bgMaterial = new Material(shader) { color = SSColorPalette.NeutralWord };
_background.GetComponent<Renderer>().material = _bgMaterial; _background.GetComponent<Renderer>().material = _bgMaterial;
// ── Text ─────────────────────────────────────────────────
var textObj = new GameObject("WordText"); var textObj = new GameObject("WordText");
textObj.transform.SetParent(transform); textObj.transform.SetParent(transform);
textObj.transform.localPosition = new Vector3(0, 0, -0.05f); textObj.transform.localPosition = new Vector3(0, 0, -0.05f);
// ★ Face camera (identity rotation — parent handles billboard in LateUpdate if needed)
textObj.transform.localRotation = Quaternion.identity; textObj.transform.localRotation = Quaternion.identity;
_text = textObj.AddComponent<ArabicTextMeshPro>(); _text = textObj.AddComponent<ArabicTextMeshPro>();
...@@ -81,11 +79,11 @@ namespace com.al_arcade.cs ...@@ -81,11 +79,11 @@ namespace com.al_arcade.cs
_text.overflowMode = TMPro.TextOverflowModes.Ellipsis; _text.overflowMode = TMPro.TextOverflowModes.Ellipsis;
SSFontManager.Apply(_text); SSFontManager.Apply(_text);
// ── Collider (matches card exactly) ──────────────────────
_collider = GetComponent<BoxCollider>(); _collider = GetComponent<BoxCollider>();
_collider.size = new Vector3(_cardWidth, _cardHeight, 0.2f); _collider.size = new Vector3(_cardWidth, _cardHeight, 0.2f);
// ── Shadow ───────────────────────────────────────────────
var shadow = GameObject.CreatePrimitive(PrimitiveType.Cube); var shadow = GameObject.CreatePrimitive(PrimitiveType.Cube);
shadow.name = "Shadow"; shadow.name = "Shadow";
shadow.transform.SetParent(transform); shadow.transform.SetParent(transform);
...@@ -120,7 +118,7 @@ namespace com.al_arcade.cs ...@@ -120,7 +118,7 @@ namespace com.al_arcade.cs
if (_bgMaterial != null) DOTween.Kill(_bgMaterial); if (_bgMaterial != null) DOTween.Kill(_bgMaterial);
} }
// ── Billboard towards camera ─────────────────────────────────
private void LateUpdate() private void LateUpdate()
{ {
if (Camera.main == null) return; if (Camera.main == null) return;
...@@ -130,7 +128,6 @@ namespace com.al_arcade.cs ...@@ -130,7 +128,6 @@ namespace com.al_arcade.cs
transform.rotation = Quaternion.LookRotation(awayFromCam); transform.rotation = Quaternion.LookRotation(awayFromCam);
} }
// ── Mouse Interaction ────────────────────────────────────────
private void OnMouseEnter() private void OnMouseEnter()
{ {
...@@ -158,7 +155,6 @@ namespace com.al_arcade.cs ...@@ -158,7 +155,6 @@ namespace com.al_arcade.cs
CsGameManager.Instance.OnWordClicked(this); CsGameManager.Instance.OnWordClicked(this);
} }
// ── Public Methods ───────────────────────────────────────────
public void Highlight(bool isWrongWord) public void Highlight(bool isWrongWord)
{ {
...@@ -173,7 +169,7 @@ namespace com.al_arcade.cs ...@@ -173,7 +169,7 @@ namespace com.al_arcade.cs
if (_text != null) _text.color = Color.white; if (_text != null) _text.color = Color.white;
transform.DOPunchScale(Vector3.one * 0.2f, 0.4f, 8, 0.3f); transform.DOPunchScale(Vector3.one * 0.2f, 0.4f, 8, 0.3f);
// Pulsing glow
DOTween.Sequence() DOTween.Sequence()
.Append(_bgMaterial.DOColor(Color.Lerp(targetColor, Color.white, 0.3f), 0.5f)) .Append(_bgMaterial.DOColor(Color.Lerp(targetColor, Color.white, 0.3f), 0.5f))
.Append(_bgMaterial.DOColor(targetColor, 0.5f)) .Append(_bgMaterial.DOColor(targetColor, 0.5f))
......
...@@ -46,7 +46,7 @@ namespace com.al_arcade.mcq ...@@ -46,7 +46,7 @@ namespace com.al_arcade.mcq
{ {
Debug.Log("══════ MCQ Runner Demo Builder ══════"); Debug.Log("══════ MCQ Runner Demo Builder ══════");
// ── EventSystem ──────────────────────────────────────────
if (FindObjectOfType<EventSystem>() == null) if (FindObjectOfType<EventSystem>() == null)
{ {
var es = new GameObject("EventSystem"); var es = new GameObject("EventSystem");
...@@ -54,7 +54,7 @@ namespace com.al_arcade.mcq ...@@ -54,7 +54,7 @@ namespace com.al_arcade.mcq
es.AddComponent<StandaloneInputModule>(); es.AddComponent<StandaloneInputModule>();
} }
// ── Managers ─────────────────────────────────────────────
SSApiManager.EnsureInstance(); SSApiManager.EnsureInstance();
SSAudioManager.EnsureInstance(); SSAudioManager.EnsureInstance();
SSParticleManager.EnsureInstance(); SSParticleManager.EnsureInstance();
...@@ -66,31 +66,31 @@ namespace com.al_arcade.mcq ...@@ -66,31 +66,31 @@ namespace com.al_arcade.mcq
session.classCode = classCode; session.classCode = classCode;
yield return null; yield return null;
// ── Scene ────────────────────────────────────────────────
BuildScene(); BuildScene();
yield return null; yield return null;
// ── Player ───────────────────────────────────────────────
BuildPlayer(); BuildPlayer();
yield return null; yield return null;
// ── Question Display ─────────────────────────────────────
BuildQuestionDisplay(); BuildQuestionDisplay();
yield return null; yield return null;
// ── UI ───────────────────────────────────────────────────
BuildUI(); BuildUI();
yield return null; yield return null;
// ── Game Manager ─────────────────────────────────────────
BuildGameManager(); BuildGameManager();
yield return null; yield return null;
// ── Camera ───────────────────────────────────────────────
SetupCamera(); SetupCamera();
yield return null; yield return null;
// ── Lighting ─────────────────────────────────────────────
SetupLighting(); SetupLighting();
yield return null; yield return null;
...@@ -98,16 +98,13 @@ namespace com.al_arcade.mcq ...@@ -98,16 +98,13 @@ namespace com.al_arcade.mcq
yield return new WaitForSeconds(0.3f); yield return new WaitForSeconds(0.3f);
// ── Start ────────────────────────────────────────────────
if (useOfflineTestData) if (useOfflineTestData)
_gm.StartWithQuestions(GetTestQuestions()); _gm.StartWithQuestions(GetTestQuestions());
else else
_gm.StartGame(); _gm.StartGame();
} }
// ════════════════════════════════════════════════════════════
// BUILD METHODS
// ════════════════════════════════════════════════════════════
private void BuildScene() private void BuildScene()
{ {
...@@ -116,7 +113,7 @@ namespace com.al_arcade.mcq ...@@ -116,7 +113,7 @@ namespace com.al_arcade.mcq
var shader = GetLitShader(); var shader = GetLitShader();
// Road (centered at x=0, extends along +Z)
var road = GameObject.CreatePrimitive(PrimitiveType.Cube); var road = GameObject.CreatePrimitive(PrimitiveType.Cube);
road.name = "Road"; road.name = "Road";
road.transform.position = new Vector3(0, -0.05f, 200); road.transform.position = new Vector3(0, -0.05f, 200);
...@@ -124,7 +121,7 @@ namespace com.al_arcade.mcq ...@@ -124,7 +121,7 @@ namespace com.al_arcade.mcq
road.GetComponent<Renderer>().material = new Material(shader) road.GetComponent<Renderer>().material = new Material(shader)
{ color = new Color32(60, 60, 80, 255) }; { color = new Color32(60, 60, 80, 255) };
// Lane dividers
for (int i = -1; i <= 1; i += 2) for (int i = -1; i <= 1; i += 2)
{ {
var line = GameObject.CreatePrimitive(PrimitiveType.Cube); var line = GameObject.CreatePrimitive(PrimitiveType.Cube);
...@@ -136,7 +133,7 @@ namespace com.al_arcade.mcq ...@@ -136,7 +133,7 @@ namespace com.al_arcade.mcq
{ color = SSColorPalette.WithAlpha(SSColorPalette.Accent, 0.4f) }; { color = SSColorPalette.WithAlpha(SSColorPalette.Accent, 0.4f) };
} }
// Center dashes
for (float z = 0; z < 400; z += 4) for (float z = 0; z < 400; z += 4)
{ {
var dash = GameObject.CreatePrimitive(PrimitiveType.Cube); var dash = GameObject.CreatePrimitive(PrimitiveType.Cube);
...@@ -148,7 +145,7 @@ namespace com.al_arcade.mcq ...@@ -148,7 +145,7 @@ namespace com.al_arcade.mcq
{ color = SSColorPalette.WithAlpha(Color.white, 0.2f) }; { color = SSColorPalette.WithAlpha(Color.white, 0.2f) };
} }
// Side decorations
for (int side = -1; side <= 1; side += 2) for (int side = -1; side <= 1; side += 2)
{ {
for (float z = 0; z < 400; z += 20) for (float z = 0; z < 400; z += 20)
...@@ -167,7 +164,7 @@ namespace com.al_arcade.mcq ...@@ -167,7 +164,7 @@ namespace com.al_arcade.mcq
} }
} }
// Horizon
var horizon = GameObject.CreatePrimitive(PrimitiveType.Cube); var horizon = GameObject.CreatePrimitive(PrimitiveType.Cube);
horizon.name = "Horizon"; horizon.name = "Horizon";
horizon.transform.position = new Vector3(0, 2f, 500); horizon.transform.position = new Vector3(0, 2f, 500);
...@@ -207,7 +204,7 @@ namespace com.al_arcade.mcq ...@@ -207,7 +204,7 @@ namespace com.al_arcade.mcq
_uiManager = obj.AddComponent<McqUIManager>(); _uiManager = obj.AddComponent<McqUIManager>();
_uiManager.BuildUI(); _uiManager.BuildUI();
// ★ FIX: Initialize the UnityEvent if null
if (_uiManager.onRestartClicked == null) if (_uiManager.onRestartClicked == null)
_uiManager.onRestartClicked = new UnityEvent(); _uiManager.onRestartClicked = new UnityEvent();
} }
...@@ -221,7 +218,7 @@ namespace com.al_arcade.mcq ...@@ -221,7 +218,7 @@ namespace com.al_arcade.mcq
_gm.uiManager = _uiManager; _gm.uiManager = _uiManager;
_gm.gateParent = new GameObject("Gates").transform; _gm.gateParent = new GameObject("Gates").transform;
// ★ FIX: Safe listener — all refs captured after they exist
_uiManager.onRestartClicked.AddListener(OnRestartClicked); _uiManager.onRestartClicked.AddListener(OnRestartClicked);
} }
...@@ -272,9 +269,6 @@ namespace com.al_arcade.mcq ...@@ -272,9 +269,6 @@ namespace com.al_arcade.mcq
RenderSettings.ambientMode = UnityEngine.Rendering.AmbientMode.Flat; RenderSettings.ambientMode = UnityEngine.Rendering.AmbientMode.Flat;
} }
// ════════════════════════════════════════════════════════════
// TEST DATA
// ════════════════════════════════════════════════════════════
private McqQuestion[] GetTestQuestions() private McqQuestion[] GetTestQuestions()
{ {
...@@ -306,9 +300,7 @@ namespace com.al_arcade.mcq ...@@ -306,9 +300,7 @@ namespace com.al_arcade.mcq
} }
} }
// ════════════════════════════════════════════════════════════════
// Camera follow — 3rd-person runner (behind + above)
// ════════════════════════════════════════════════════════════════
public class McqCameraFollow : MonoBehaviour public class McqCameraFollow : MonoBehaviour
{ {
public Transform target; public Transform target;
...@@ -328,4 +320,4 @@ namespace com.al_arcade.mcq ...@@ -328,4 +320,4 @@ namespace com.al_arcade.mcq
Time.deltaTime * smoothSpeed); Time.deltaTime * smoothSpeed);
} }
} }
} }
\ No newline at end of file
...@@ -62,7 +62,6 @@ namespace com.al_arcade.mcq ...@@ -62,7 +62,6 @@ namespace com.al_arcade.mcq
Instance = this; Instance = this;
} }
// ── Public API ───────────────────────────────────────────────
public void StartGame() => StartCoroutine(StartGameRoutine()); public void StartGame() => StartCoroutine(StartGameRoutine());
...@@ -90,7 +89,6 @@ namespace com.al_arcade.mcq ...@@ -90,7 +89,6 @@ namespace com.al_arcade.mcq
if (questionDisplay != null) questionDisplay.Hide(); if (questionDisplay != null) questionDisplay.Hide();
} }
// ── Game Flow ────────────────────────────────────────────────
private IEnumerator StartGameRoutine() private IEnumerator StartGameRoutine()
{ {
...@@ -202,7 +200,7 @@ namespace com.al_arcade.mcq ...@@ -202,7 +200,7 @@ namespace com.al_arcade.mcq
_state = McqGameState.AnswerFeedback; _state = McqGameState.AnswerFeedback;
yield return ProcessAnswer(answered && wasCorrect); yield return ProcessAnswer(answered && wasCorrect);
// Cleanup gates
foreach (var gate in _activeGates) foreach (var gate in _activeGates)
{ {
if (gate != null) if (gate != null)
...@@ -272,9 +270,6 @@ namespace com.al_arcade.mcq ...@@ -272,9 +270,6 @@ namespace com.al_arcade.mcq
yield return new WaitForSeconds(feedbackDisplayTime); yield return new WaitForSeconds(feedbackDisplayTime);
} }
// ══════════════════════════════════════════════════════════
// GATE SPAWNING — always road-center X=0
// ══════════════════════════════════════════════════════════
private void SpawnGates(McqQuestion question) private void SpawnGates(McqQuestion question)
{ {
...@@ -284,7 +279,7 @@ namespace com.al_arcade.mcq ...@@ -284,7 +279,7 @@ namespace com.al_arcade.mcq
float playerZ = player != null ? player.transform.position.z : 0f; float playerZ = player != null ? player.transform.position.z : 0f;
// ★ Gates spawn relative to ROAD center (x=0), NOT player x
Vector3 basePos = new Vector3(0f, 0f, playerZ + gateSpawnDistance); Vector3 basePos = new Vector3(0f, 0f, playerZ + gateSpawnDistance);
float totalWidth = (answers.Length - 1) * gateSpacing; float totalWidth = (answers.Length - 1) * gateSpacing;
...@@ -315,7 +310,6 @@ namespace com.al_arcade.mcq ...@@ -315,7 +310,6 @@ namespace com.al_arcade.mcq
return gate; return gate;
} }
// ── Feedback ─────────────────────────────────────────────────
private void ShowCorrectFeedback(int points) private void ShowCorrectFeedback(int points)
{ {
......
...@@ -42,11 +42,11 @@ namespace com.al_arcade.mcq ...@@ -42,11 +42,11 @@ namespace com.al_arcade.mcq
{ {
var shader = GetShader(); var shader = GetShader();
// ── Pillars ──────────────────────────────────────────────
CreatePillar(Vector3.left * (GateWidth / 2f), "LeftPillar", shader); CreatePillar(Vector3.left * (GateWidth / 2f), "LeftPillar", shader);
CreatePillar(Vector3.right * (GateWidth / 2f), "RightPillar", shader); CreatePillar(Vector3.right * (GateWidth / 2f), "RightPillar", shader);
// ── Top bar ──────────────────────────────────────────────
var topBar = GameObject.CreatePrimitive(PrimitiveType.Cube); var topBar = GameObject.CreatePrimitive(PrimitiveType.Cube);
topBar.name = "TopBar"; topBar.name = "TopBar";
topBar.transform.SetParent(transform); topBar.transform.SetParent(transform);
...@@ -56,7 +56,7 @@ namespace com.al_arcade.mcq ...@@ -56,7 +56,7 @@ namespace com.al_arcade.mcq
var topMat = new Material(shader) { color = SSColorPalette.Primary }; var topMat = new Material(shader) { color = SSColorPalette.Primary };
topBar.GetComponent<Renderer>().material = topMat; topBar.GetComponent<Renderer>().material = topMat;
// ── Answer panel — on the +Z side (back wall of gate archway) ──
_backPanel = GameObject.CreatePrimitive(PrimitiveType.Cube); _backPanel = GameObject.CreatePrimitive(PrimitiveType.Cube);
_backPanel.name = "AnswerPanel"; _backPanel.name = "AnswerPanel";
_backPanel.transform.SetParent(transform); _backPanel.transform.SetParent(transform);
...@@ -66,12 +66,12 @@ namespace com.al_arcade.mcq ...@@ -66,12 +66,12 @@ namespace com.al_arcade.mcq
_panelMaterial = new Material(shader) { color = SSColorPalette.GateDefault }; _panelMaterial = new Material(shader) { color = SSColorPalette.GateDefault };
_backPanel.GetComponent<Renderer>().material = _panelMaterial; _backPanel.GetComponent<Renderer>().material = _panelMaterial;
// ── Text label — identity rotation, readable from -Z (camera side) ──
var textObj = new GameObject("AnswerLabel"); var textObj = new GameObject("AnswerLabel");
textObj.transform.SetParent(transform); textObj.transform.SetParent(transform);
textObj.transform.localPosition = new Vector3(0, GateHeight / 2f, GateDepth / 2f - 0.08f); textObj.transform.localPosition = new Vector3(0, GateHeight / 2f, GateDepth / 2f - 0.08f);
// ★ NO Euler(0,180,0) — TMP 3D text is readable from -Z by default,
// camera is behind player at -Z. Identity rotation = correct.
textObj.transform.localRotation = Quaternion.identity; textObj.transform.localRotation = Quaternion.identity;
_label = textObj.AddComponent<ArabicTextMeshPro>(); _label = textObj.AddComponent<ArabicTextMeshPro>();
...@@ -88,13 +88,13 @@ namespace com.al_arcade.mcq ...@@ -88,13 +88,13 @@ namespace com.al_arcade.mcq
_label.overflowMode = TMPro.TextOverflowModes.Ellipsis; _label.overflowMode = TMPro.TextOverflowModes.Ellipsis;
SSFontManager.Apply(_label); SSFontManager.Apply(_label);
// ── Trigger collider ─────────────────────────────────────
_collider = gameObject.AddComponent<BoxCollider>(); _collider = gameObject.AddComponent<BoxCollider>();
_collider.isTrigger = true; _collider.isTrigger = true;
_collider.center = new Vector3(0, GateHeight / 2f, 0); _collider.center = new Vector3(0, GateHeight / 2f, 0);
_collider.size = new Vector3(GateWidth, GateHeight, GateDepth); _collider.size = new Vector3(GateWidth, GateHeight, GateDepth);
// ── Floor highlight ──────────────────────────────────────
var floor = GameObject.CreatePrimitive(PrimitiveType.Cube); var floor = GameObject.CreatePrimitive(PrimitiveType.Cube);
floor.name = "FloorHighlight"; floor.name = "FloorHighlight";
floor.transform.SetParent(transform); floor.transform.SetParent(transform);
...@@ -147,7 +147,6 @@ namespace com.al_arcade.mcq ...@@ -147,7 +147,6 @@ namespace com.al_arcade.mcq
if (_label != null) DOTween.Kill(_label.transform); if (_label != null) DOTween.Kill(_label.transform);
} }
// ── Collision ────────────────────────────────────────────────
private void OnTriggerEnter(Collider other) private void OnTriggerEnter(Collider other)
{ {
...@@ -157,7 +156,6 @@ namespace com.al_arcade.mcq ...@@ -157,7 +156,6 @@ namespace com.al_arcade.mcq
onPlayerEnter?.Invoke(GateIndex); onPlayerEnter?.Invoke(GateIndex);
} }
// ── Feedback Animations ──────────────────────────────────────
public void PlayCorrectAnimation() public void PlayCorrectAnimation()
{ {
......
...@@ -25,7 +25,7 @@ namespace com.al_arcade.mcq ...@@ -25,7 +25,7 @@ namespace com.al_arcade.mcq
var shader = Shader.Find("Universal Render Pipeline/Lit") ?? var shader = Shader.Find("Universal Render Pipeline/Lit") ??
Shader.Find("Standard") ?? Shader.Find("Unlit/Color"); Shader.Find("Standard") ?? Shader.Find("Unlit/Color");
// Background panel (Cube)
_panel = GameObject.CreatePrimitive(PrimitiveType.Cube); _panel = GameObject.CreatePrimitive(PrimitiveType.Cube);
_panel.name = "QuestionPanel"; _panel.name = "QuestionPanel";
_panel.transform.SetParent(transform); _panel.transform.SetParent(transform);
...@@ -36,7 +36,7 @@ namespace com.al_arcade.mcq ...@@ -36,7 +36,7 @@ namespace com.al_arcade.mcq
{ color = new Color(0.12f, 0.12f, 0.5f, 0.95f) }; { color = new Color(0.12f, 0.12f, 0.5f, 0.95f) };
_panel.GetComponent<Renderer>().material = panelMat; _panel.GetComponent<Renderer>().material = panelMat;
// Accent bar
var bar = GameObject.CreatePrimitive(PrimitiveType.Cube); var bar = GameObject.CreatePrimitive(PrimitiveType.Cube);
bar.name = "AccentBar"; bar.name = "AccentBar";
bar.transform.SetParent(_panel.transform); bar.transform.SetParent(_panel.transform);
...@@ -46,8 +46,7 @@ namespace com.al_arcade.mcq ...@@ -46,8 +46,7 @@ namespace com.al_arcade.mcq
bar.GetComponent<Renderer>().material = new Material(shader) bar.GetComponent<Renderer>().material = new Material(shader)
{ color = SSColorPalette.Accent }; { color = SSColorPalette.Accent };
// ★ Question text — identity local rotation
// Parent will billboard to face camera
var textObj = new GameObject("QuestionText"); var textObj = new GameObject("QuestionText");
textObj.transform.SetParent(transform); textObj.transform.SetParent(transform);
textObj.transform.localPosition = new Vector3(0, 0.2f, -0.06f); textObj.transform.localPosition = new Vector3(0, 0.2f, -0.06f);
...@@ -65,7 +64,7 @@ namespace com.al_arcade.mcq ...@@ -65,7 +64,7 @@ namespace com.al_arcade.mcq
_questionText.overflowMode = TMPro.TextOverflowModes.Ellipsis; _questionText.overflowMode = TMPro.TextOverflowModes.Ellipsis;
SSFontManager.Apply(_questionText); SSFontManager.Apply(_questionText);
// Source text
var srcObj = new GameObject("SourceText"); var srcObj = new GameObject("SourceText");
srcObj.transform.SetParent(transform); srcObj.transform.SetParent(transform);
srcObj.transform.localPosition = new Vector3(0, -0.85f, -0.06f); srcObj.transform.localPosition = new Vector3(0, -0.85f, -0.06f);
...@@ -104,14 +103,14 @@ namespace com.al_arcade.mcq ...@@ -104,14 +103,14 @@ namespace com.al_arcade.mcq
{ {
if (_playerTransform == null) return; if (_playerTransform == null) return;
// ★ Follow player, stay between player and gates
Vector3 targetPos = _playerTransform.position Vector3 targetPos = _playerTransform.position
+ Vector3.up * floatHeight + Vector3.up * floatHeight
+ Vector3.forward * forwardOffset; + Vector3.forward * forwardOffset;
transform.position = Vector3.Lerp(transform.position, targetPos, transform.position = Vector3.Lerp(transform.position, targetPos,
Time.deltaTime * followSpeed); Time.deltaTime * followSpeed);
// ★ Billboard: face away from camera so text (-Z face) faces camera
if (Camera.main != null) if (Camera.main != null)
{ {
Vector3 awayFromCam = transform.position - Camera.main.transform.position; Vector3 awayFromCam = transform.position - Camera.main.transform.position;
......
...@@ -27,9 +27,6 @@ namespace com.al_arcade.mcq ...@@ -27,9 +27,6 @@ namespace com.al_arcade.mcq
[Header("Events")] [Header("Events")]
public UnityEvent onRestartClicked; public UnityEvent onRestartClicked;
// ════════════════════════════════════════════════════════════
// BUILD
// ════════════════════════════════════════════════════════════
public void BuildUI() public void BuildUI()
{ {
...@@ -82,7 +79,7 @@ namespace com.al_arcade.mcq ...@@ -82,7 +79,7 @@ namespace com.al_arcade.mcq
new Vector2(200, -15), 24, TMPro.TextAlignmentOptions.TopLeft); new Vector2(200, -15), 24, TMPro.TextAlignmentOptions.TopLeft);
_streakText.color = SSColorPalette.Warning; _streakText.color = SSColorPalette.Warning;
// Hearts
_heartIcons = new Image[5]; _heartIcons = new Image[5];
for (int i = 0; i < 5; i++) for (int i = 0; i < 5; i++)
{ {
...@@ -98,7 +95,7 @@ namespace com.al_arcade.mcq ...@@ -98,7 +95,7 @@ namespace com.al_arcade.mcq
h.SetActive(false); h.SetActive(false);
} }
// Progress bar
var progBg = MakeImg(go.transform, "ProgressBg", var progBg = MakeImg(go.transform, "ProgressBg",
new Vector2(0, 0), new Vector2(1, 0), new Vector2(0, 0), new Vector2(1, 0),
SSColorPalette.WithAlpha(Color.white, 0.1f)); SSColorPalette.WithAlpha(Color.white, 0.1f));
...@@ -208,7 +205,7 @@ namespace com.al_arcade.mcq ...@@ -208,7 +205,7 @@ namespace com.al_arcade.mcq
new Vector2(200, 0), 24); new Vector2(200, 0), 24);
_resultStreak.color = SSColorPalette.Accent; _resultStreak.color = SSColorPalette.Accent;
// Restart button
var btnObj = new GameObject("RestartBtn"); var btnObj = new GameObject("RestartBtn");
btnObj.transform.SetParent(go.transform, false); btnObj.transform.SetParent(go.transform, false);
var br = btnObj.AddComponent<RectTransform>(); var br = btnObj.AddComponent<RectTransform>();
...@@ -229,9 +226,6 @@ namespace com.al_arcade.mcq ...@@ -229,9 +226,6 @@ namespace com.al_arcade.mcq
btnTxt.fontStyle = TMPro.FontStyles.Bold; btnTxt.fontStyle = TMPro.FontStyles.Bold;
} }
// ══════════════════════════════════════════════════════════
// PUBLIC
// ══════════════════════════════════════════════════════════
public void ShowGameUI() public void ShowGameUI()
{ {
...@@ -377,7 +371,6 @@ namespace com.al_arcade.mcq ...@@ -377,7 +371,6 @@ namespace com.al_arcade.mcq
_feedbackUI.alpha = 0; _feedbackUI.alpha = 0;
} }
// ── Helpers ──────────────────────────────────────────────────
private GameObject MakePanel(Transform p, string n, Vector2 amin, Vector2 amax) private GameObject MakePanel(Transform p, string n, Vector2 amin, Vector2 amax)
{ {
......
// ============================================================================
// Science Street — API Manager (Singleton)
// Handles all communication with the PHP backend
// ============================================================================
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
...@@ -21,7 +19,7 @@ namespace com.al_arcade.shared ...@@ -21,7 +19,7 @@ namespace com.al_arcade.shared
[SerializeField] private int timeoutSeconds = 15; [SerializeField] private int timeoutSeconds = 15;
// ── Singleton Setup ──────────────────────────────────────────
private void Awake() private void Awake()
{ {
if (Instance != null && Instance != this) if (Instance != null && Instance != this)
...@@ -33,7 +31,7 @@ namespace com.al_arcade.shared ...@@ -33,7 +31,7 @@ namespace com.al_arcade.shared
DontDestroyOnLoad(gameObject); DontDestroyOnLoad(gameObject);
} }
// ── Ensure Instance Exists ───────────────────────────────────
public static SSApiManager EnsureInstance() public static SSApiManager EnsureInstance()
{ {
if (Instance != null) return Instance; if (Instance != null) return Instance;
...@@ -43,9 +41,6 @@ namespace com.al_arcade.shared ...@@ -43,9 +41,6 @@ namespace com.al_arcade.shared
return Instance; return Instance;
} }
// ════════════════════════════════════════════════════════════
// GENERIC REQUEST
// ════════════════════════════════════════════════════════════
public IEnumerator GetRequest(string action, public IEnumerator GetRequest(string action,
Dictionary<string, string> parameters, Dictionary<string, string> parameters,
...@@ -79,9 +74,6 @@ namespace com.al_arcade.shared ...@@ -79,9 +74,6 @@ namespace com.al_arcade.shared
onSuccess?.Invoke(json); onSuccess?.Invoke(json);
} }
// ════════════════════════════════════════════════════════════
// PING
// ════════════════════════════════════════════════════════════
public IEnumerator Ping(Action<bool> onResult) public IEnumerator Ping(Action<bool> onResult)
{ {
...@@ -90,9 +82,6 @@ namespace com.al_arcade.shared ...@@ -90,9 +82,6 @@ namespace com.al_arcade.shared
err => onResult?.Invoke(false)); err => onResult?.Invoke(false));
} }
// ════════════════════════════════════════════════════════════
// VALIDATE CLASS CODE
// ════════════════════════════════════════════════════════════
public IEnumerator ValidateClassCode(string code, public IEnumerator ValidateClassCode(string code,
Action<ClassInfo> onSuccess, Action<ClassInfo> onSuccess,
...@@ -120,9 +109,6 @@ namespace com.al_arcade.shared ...@@ -120,9 +109,6 @@ namespace com.al_arcade.shared
onError); onError);
} }
// ════════════════════════════════════════════════════════════
// GET GRADES
// ════════════════════════════════════════════════════════════
public IEnumerator GetGrades(Action<GradeInfo[]> onSuccess, public IEnumerator GetGrades(Action<GradeInfo[]> onSuccess,
Action<string> onError) Action<string> onError)
...@@ -146,9 +132,6 @@ namespace com.al_arcade.shared ...@@ -146,9 +132,6 @@ namespace com.al_arcade.shared
onError); onError);
} }
// ════════════════════════════════════════════════════════════
// FETCH MCQ QUESTIONS
// ════════════════════════════════════════════════════════════
public IEnumerator FetchMcq(string buildType, string classCode, public IEnumerator FetchMcq(string buildType, string classCode,
int count, int gradeId, int count, int gradeId,
...@@ -183,9 +166,6 @@ namespace com.al_arcade.shared ...@@ -183,9 +166,6 @@ namespace com.al_arcade.shared
onError); onError);
} }
// ════════════════════════════════════════════════════════════
// FETCH T/F QUESTIONS
// ════════════════════════════════════════════════════════════
public IEnumerator FetchTf(string buildType, string classCode, public IEnumerator FetchTf(string buildType, string classCode,
int count, int gradeId, int count, int gradeId,
...@@ -220,9 +200,6 @@ namespace com.al_arcade.shared ...@@ -220,9 +200,6 @@ namespace com.al_arcade.shared
onError); onError);
} }
// ════════════════════════════════════════════════════════════
// FETCH CS QUESTIONS
// ════════════════════════════════════════════════════════════
public IEnumerator FetchCs(string buildType, string classCode, public IEnumerator FetchCs(string buildType, string classCode,
int count, int gradeId, int count, int gradeId,
......
// ============================================================================
// Science Street — Audio Manager
// Plays SFX with optional AudioClip references.
// Works silently if no clips are assigned.
// ============================================================================
using UnityEngine; using UnityEngine;
using System.Collections.Generic; using System.Collections.Generic;
...@@ -77,7 +74,6 @@ namespace com.al_arcade.shared ...@@ -77,7 +74,6 @@ namespace com.al_arcade.shared
return src; return src;
} }
// ── Play Methods ─────────────────────────────────────────────
public void Play(AudioClip clip, float volumeScale = 1f, float pitch = 1f) public void Play(AudioClip clip, float volumeScale = 1f, float pitch = 1f)
{ {
...@@ -104,10 +100,7 @@ namespace com.al_arcade.shared ...@@ -104,10 +100,7 @@ namespace com.al_arcade.shared
public void PlayCheer(float vol = 1f) => Play(sfxCheer, vol); public void PlayCheer(float vol = 1f) => Play(sfxCheer, vol);
public void PlayCountdown(float vol = 0.8f) => Play(sfxCountdown, vol); public void PlayCountdown(float vol = 0.8f) => Play(sfxCountdown, vol);
/// <summary>
/// Plays a generated procedural beep (no AudioClip needed).
/// Useful for demo/prototype mode.
/// </summary>
public void PlayProceduralBeep(float frequency = 440f, float duration = 0.1f, public void PlayProceduralBeep(float frequency = 440f, float duration = 0.1f,
float volume = 0.3f) float volume = 0.3f)
{ {
...@@ -119,7 +112,7 @@ namespace com.al_arcade.shared ...@@ -119,7 +112,7 @@ namespace com.al_arcade.shared
for (int i = 0; i < sampleCount; i++) for (int i = 0; i < sampleCount; i++)
{ {
float t = (float)i / sampleRate; float t = (float)i / sampleRate;
float envelope = 1f - ((float)i / sampleCount); // fade out float envelope = 1f - ((float)i / sampleCount);
data[i] = Mathf.Sin(2f * Mathf.PI * frequency * t) * envelope * volume; data[i] = Mathf.Sin(2f * Mathf.PI * frequency * t) * envelope * volume;
} }
...@@ -135,10 +128,10 @@ namespace com.al_arcade.shared ...@@ -135,10 +128,10 @@ namespace com.al_arcade.shared
public void PlayClickBeep() => PlayProceduralBeep(660f, 0.05f, 0.15f); public void PlayClickBeep() => PlayProceduralBeep(660f, 0.05f, 0.15f);
public void PlaySuccessJingle() public void PlaySuccessJingle()
{ {
// Three ascending tones
PlayProceduralBeep(523f, 0.12f, 0.2f); // C5 PlayProceduralBeep(523f, 0.12f, 0.2f);
StartCoroutine(DelayedBeep(660f, 0.12f, 0.1f)); // E5 StartCoroutine(DelayedBeep(660f, 0.12f, 0.1f));
StartCoroutine(DelayedBeep(784f, 0.2f, 0.2f)); // G5 StartCoroutine(DelayedBeep(784f, 0.2f, 0.2f));
} }
public void PlayFailBuzz() public void PlayFailBuzz()
......
// ============================================================================
// Science Street — Shared Color Palette
// AL-Arcade Game Dev Team
// ============================================================================
using UnityEngine; using UnityEngine;
namespace com.al_arcade.shared namespace com.al_arcade.shared
{ {
/// <summary>
/// Central color definitions matching the Science Street brand.
/// </summary>
public static class SSColorPalette public static class SSColorPalette
{ {
// ── Brand Colors ─────────────────────────────────────────────
public static readonly Color Primary = new Color32(48, 48, 208, 255); // #3030D0 public static readonly Color Primary = new Color32(48, 48, 208, 255);
public static readonly Color PrimaryDark = new Color32(32, 32, 168, 255); // #2020A8 public static readonly Color PrimaryDark = new Color32(32, 32, 168, 255);
public static readonly Color PrimaryLight = new Color32(80, 80, 220, 255); // #5050DC public static readonly Color PrimaryLight = new Color32(80, 80, 220, 255);
public static readonly Color Accent = new Color32(254, 215, 0, 255); // #FED700 public static readonly Color Accent = new Color32(254, 215, 0, 255);
public static readonly Color AccentDark = new Color32(212, 180, 0, 255); // #D4B400 public static readonly Color AccentDark = new Color32(212, 180, 0, 255);
// ── Feedback Colors ──────────────────────────────────────────
public static readonly Color Success = new Color32(16, 185, 129, 255); // #10B981 public static readonly Color Success = new Color32(16, 185, 129, 255);
public static readonly Color SuccessLight = new Color32(209, 250, 229, 255); // #D1FAE5 public static readonly Color SuccessLight = new Color32(209, 250, 229, 255);
public static readonly Color Danger = new Color32(239, 68, 68, 255); // #EF4444 public static readonly Color Danger = new Color32(239, 68, 68, 255);
public static readonly Color DangerLight = new Color32(254, 226, 226, 255); // #FEE2E2 public static readonly Color DangerLight = new Color32(254, 226, 226, 255);
public static readonly Color Warning = new Color32(245, 158, 11, 255); // #F59E0B public static readonly Color Warning = new Color32(245, 158, 11, 255);
public static readonly Color Info = new Color32(59, 130, 246, 255); // #3B82F6 public static readonly Color Info = new Color32(59, 130, 246, 255);
// ── UI Colors ────────────────────────────────────────────────
public static readonly Color Background = new Color32(238, 240, 248, 255); // #EEF0F8 public static readonly Color Background = new Color32(238, 240, 248, 255);
public static readonly Color Card = Color.white; public static readonly Color Card = Color.white;
public static readonly Color TextDark = new Color32(26, 26, 46, 255); // #1A1A2E public static readonly Color TextDark = new Color32(26, 26, 46, 255);
public static readonly Color TextMuted = new Color32(107, 114, 128, 255); // #6B7280 public static readonly Color TextMuted = new Color32(107, 114, 128, 255);
public static readonly Color Border = new Color32(229, 231, 235, 255); // #E5E7EB public static readonly Color Border = new Color32(229, 231, 235, 255);
// ── Game-specific ────────────────────────────────────────────
public static readonly Color GateCorrect = new Color32(16, 185, 129, 255); public static readonly Color GateCorrect = new Color32(16, 185, 129, 255);
public static readonly Color GateWrong = new Color32(239, 68, 68, 255); public static readonly Color GateWrong = new Color32(239, 68, 68, 255);
public static readonly Color GateDefault = new Color32(80, 80, 220, 255); public static readonly Color GateDefault = new Color32(80, 80, 220, 255);
public static readonly Color TrueGreen = new Color32(34, 197, 94, 255); // #22C55E public static readonly Color TrueGreen = new Color32(34, 197, 94, 255);
public static readonly Color FalseRed = new Color32(239, 68, 68, 255); // #EF4444 public static readonly Color FalseRed = new Color32(239, 68, 68, 255);
public static readonly Color WrongWord = new Color32(239, 68, 68, 255); public static readonly Color WrongWord = new Color32(239, 68, 68, 255);
public static readonly Color CorrectWord = new Color32(16, 185, 129, 255); public static readonly Color CorrectWord = new Color32(16, 185, 129, 255);
public static readonly Color NeutralWord = new Color32(248, 249, 252, 255); public static readonly Color NeutralWord = new Color32(248, 249, 252, 255);
// ── Helpers ──────────────────────────────────────────────────
public static Color WithAlpha(Color c, float a) public static Color WithAlpha(Color c, float a)
{ {
return new Color(c.r, c.g, c.b, a); return new Color(c.r, c.g, c.b, a);
......
// ============================================================================
// Science Street — Shared Data Models
// JSON deserialization targets for API responses
// ============================================================================
using System; using System;
using UnityEngine; using UnityEngine;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace com.al_arcade.shared namespace com.al_arcade.shared
{ {
// ── API Wrapper Responses ────────────────────────────────────────
[Serializable] [Serializable]
public class ApiResponse<T> public class ApiResponse<T>
...@@ -37,7 +35,6 @@ namespace com.al_arcade.shared ...@@ -37,7 +35,6 @@ namespace com.al_arcade.shared
public ClassInfo classInfo; public ClassInfo classInfo;
} }
// ── Core Data Types ──────────────────────────────────────────────
[Serializable] [Serializable]
public class GradeInfo public class GradeInfo
...@@ -58,30 +55,26 @@ namespace com.al_arcade.shared ...@@ -58,30 +55,26 @@ namespace com.al_arcade.shared
public string teacher_name; public string teacher_name;
} }
// ── MCQ ──────────────────────────────────────────────────────────
[Serializable] [Serializable]
public class McqQuestion public class McqQuestion
{ {
public string id; public string id;
public string question_text; public string question_text;
public string answer1; // always correct public string answer1;
public string answer2; public string answer2;
public string answer3; public string answer3;
public string answer4; public string answer4;
public string source; public string source;
public string grade_name; public string grade_name;
/// <summary>
/// Returns all 4 answers shuffled. Sets correctIndex to the
/// position of the correct answer in the shuffled array.
/// </summary>
public string[] GetShuffledAnswers(out int correctIndex) public string[] GetShuffledAnswers(out int correctIndex)
{ {
string[] arr = { answer1, answer2, answer3, answer4 }; string[] arr = { answer1, answer2, answer3, answer4 };
string correct = answer1; string correct = answer1;
// Fisher-Yates shuffle
for (int i = arr.Length - 1; i > 0; i--) for (int i = arr.Length - 1; i > 0; i--)
{ {
int j = UnityEngine.Random.Range(0, i + 1); int j = UnityEngine.Random.Range(0, i + 1);
...@@ -93,7 +86,6 @@ namespace com.al_arcade.shared ...@@ -93,7 +86,6 @@ namespace com.al_arcade.shared
} }
} }
// ── True/False ───────────────────────────────────────────────────
[Serializable] [Serializable]
public class TfQuestion public class TfQuestion
...@@ -105,7 +97,6 @@ namespace com.al_arcade.shared ...@@ -105,7 +97,6 @@ namespace com.al_arcade.shared
public string grade_name; public string grade_name;
} }
// ── Correct Sentence ─────────────────────────────────────────────
[Serializable] [Serializable]
public class CsQuestion public class CsQuestion
......
...@@ -2,10 +2,8 @@ using UnityEngine; ...@@ -2,10 +2,8 @@ using UnityEngine;
namespace com.al_arcade.shared namespace com.al_arcade.shared
{ {
/// <summary>
/// Global Arabic font manager. Set the font once from any DemoBuilder,
/// then all text components pick it up via Apply().
/// </summary>
public static class SSFontManager public static class SSFontManager
{ {
private static TMPro.TMP_FontAsset _font; private static TMPro.TMP_FontAsset _font;
...@@ -26,4 +24,4 @@ namespace com.al_arcade.shared ...@@ -26,4 +24,4 @@ namespace com.al_arcade.shared
if (_font != null && tmp != null) tmp.font = _font; if (_font != null && tmp != null) tmp.font = _font;
} }
} }
} }
\ No newline at end of file
// ============================================================================
// Science Street — Game Session Manager
// Holds current game session state (build type, class code, etc.)
// ============================================================================
using System; using System;
using System.Collections; using System.Collections;
using UnityEngine; using UnityEngine;
...@@ -23,14 +21,14 @@ namespace com.al_arcade.shared ...@@ -23,14 +21,14 @@ namespace com.al_arcade.shared
[Tooltip("Number of questions to fetch")] [Tooltip("Number of questions to fetch")]
public int questionCount = 10; public int questionCount = 10;
// ── Runtime State (Teacher Build) ────────────────────────────
[HideInInspector] public string classCode = ""; [HideInInspector] public string classCode = "";
[HideInInspector] public string className = ""; [HideInInspector] public string className = "";
[HideInInspector] public string teacherName = ""; [HideInInspector] public string teacherName = "";
[HideInInspector] public string gradeName = ""; [HideInInspector] public string gradeName = "";
[HideInInspector] public bool isClassValidated = false; [HideInInspector] public bool isClassValidated = false;
// ── Events ───────────────────────────────────────────────────
[Header("Events")] [Header("Events")]
public UnityEvent onSessionReady; public UnityEvent onSessionReady;
public UnityEvent<string> onSessionError; public UnityEvent<string> onSessionError;
...@@ -55,9 +53,7 @@ namespace com.al_arcade.shared ...@@ -55,9 +53,7 @@ namespace com.al_arcade.shared
return Instance; return Instance;
} }
/// <summary>
/// Validates a teacher class code. On success, stores all class info.
/// </summary>
public IEnumerator ValidateAndStartTeacherSession(string code, public IEnumerator ValidateAndStartTeacherSession(string code,
Action onSuccess, Action<string> onError) Action onSuccess, Action<string> onError)
{ {
...@@ -89,9 +85,7 @@ namespace com.al_arcade.shared ...@@ -89,9 +85,7 @@ namespace com.al_arcade.shared
}); });
} }
/// <summary>
/// Starts a Science Street (public) session.
/// </summary>
public void StartSciStreetSession(int grade = 0) public void StartSciStreetSession(int grade = 0)
{ {
buildType = "scistreet"; buildType = "scistreet";
...@@ -102,9 +96,7 @@ namespace com.al_arcade.shared ...@@ -102,9 +96,7 @@ namespace com.al_arcade.shared
onSessionReady?.Invoke(); onSessionReady?.Invoke();
} }
/// <summary>
/// Resets the session to defaults.
/// </summary>
public void ResetSession() public void ResetSession()
{ {
buildType = "scistreet"; buildType = "scistreet";
......
// ============================================================================
// Science Street — Particle Effect Manager
// Creates particle effects programmatically. Works without any prefabs.
// ============================================================================
using UnityEngine; using UnityEngine;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
...@@ -41,7 +39,6 @@ namespace com.al_arcade.shared ...@@ -41,7 +39,6 @@ namespace com.al_arcade.shared
return Instance; return Instance;
} }
// ── Public Play Methods ──────────────────────────────────────
public void PlayCorrectBurst(Vector3 position) public void PlayCorrectBurst(Vector3 position)
{ {
...@@ -105,7 +102,6 @@ namespace com.al_arcade.shared ...@@ -105,7 +102,6 @@ namespace com.al_arcade.shared
PlayConfetti(pos); PlayConfetti(pos);
} }
// ── Procedural Particle Creation ─────────────────────────────
private void PlayProceduralBurst(Vector3 pos, Color startColor, private void PlayProceduralBurst(Vector3 pos, Color startColor,
Color endColor, int count, float lifetime) Color endColor, int count, float lifetime)
...@@ -141,7 +137,7 @@ namespace com.al_arcade.shared ...@@ -141,7 +137,7 @@ namespace com.al_arcade.shared
new Keyframe(0f, 1f), new Keyframe(0f, 1f),
new Keyframe(1f, 0f))); new Keyframe(1f, 0f)));
// Use default material
var renderer = go.GetComponent<ParticleSystemRenderer>(); var renderer = go.GetComponent<ParticleSystemRenderer>();
renderer.material = GetParticleMaterial(); renderer.material = GetParticleMaterial();
...@@ -169,7 +165,7 @@ namespace com.al_arcade.shared ...@@ -169,7 +165,7 @@ namespace com.al_arcade.shared
main.loop = false; main.loop = false;
main.duration = 0.2f; main.duration = 0.2f;
// Random color from our palette
var gradient = new Gradient(); var gradient = new Gradient();
gradient.SetKeys( gradient.SetKeys(
new[] { new[] {
...@@ -208,7 +204,6 @@ namespace com.al_arcade.shared ...@@ -208,7 +204,6 @@ namespace com.al_arcade.shared
Destroy(go, 5f); Destroy(go, 5f);
} }
// ── Helpers ──────────────────────────────────────────────────
private void SpawnPrefab(ParticleSystem prefab, Vector3 pos) private void SpawnPrefab(ParticleSystem prefab, Vector3 pos)
{ {
...@@ -225,7 +220,7 @@ namespace com.al_arcade.shared ...@@ -225,7 +220,7 @@ namespace com.al_arcade.shared
{ {
if (_cachedMat != null) return _cachedMat; if (_cachedMat != null) return _cachedMat;
// Try to find the default particle shader
Shader shader = Shader.Find("Particles/Standard Unlit"); Shader shader = Shader.Find("Particles/Standard Unlit");
if (shader == null) shader = Shader.Find("Universal Render Pipeline/Particles/Unlit"); if (shader == null) shader = Shader.Find("Universal Render Pipeline/Particles/Unlit");
if (shader == null) shader = Shader.Find("Mobile/Particles/Alpha Blended"); if (shader == null) shader = Shader.Find("Mobile/Particles/Alpha Blended");
...@@ -233,7 +228,7 @@ namespace com.al_arcade.shared ...@@ -233,7 +228,7 @@ namespace com.al_arcade.shared
if (shader == null) shader = Shader.Find("Unlit/Color"); if (shader == null) shader = Shader.Find("Unlit/Color");
_cachedMat = new Material(shader); _cachedMat = new Material(shader);
_cachedMat.SetFloat("_Mode", 2); // Fade _cachedMat.SetFloat("_Mode", 2);
return _cachedMat; return _cachedMat;
} }
} }
......
...@@ -43,13 +43,13 @@ namespace com.al_arcade.tf ...@@ -43,13 +43,13 @@ namespace com.al_arcade.tf
float side = isLeft ? -1f : 1f; float side = isLeft ? -1f : 1f;
var shader = GetShader(); var shader = GetShader();
// ── Hand root ────────────────────────────────────────────
hand = new GameObject(isLeft ? "LeftHand" : "RightHand"); hand = new GameObject(isLeft ? "LeftHand" : "RightHand");
hand.transform.SetParent(transform); hand.transform.SetParent(transform);
hand.transform.localPosition = new Vector3(side * 0.35f, -0.3f, 0.55f); hand.transform.localPosition = new Vector3(side * 0.35f, -0.3f, 0.55f);
hand.transform.localRotation = Quaternion.Euler(12f, -side * 8f, side * 4f); hand.transform.localRotation = Quaternion.Euler(12f, -side * 8f, side * 4f);
// ── Arm (smaller, more proportional) ─────────────────────
var arm = GameObject.CreatePrimitive(PrimitiveType.Capsule); var arm = GameObject.CreatePrimitive(PrimitiveType.Capsule);
arm.name = "Arm"; arm.name = "Arm";
arm.transform.SetParent(hand.transform); arm.transform.SetParent(hand.transform);
...@@ -59,7 +59,7 @@ namespace com.al_arcade.tf ...@@ -59,7 +59,7 @@ namespace com.al_arcade.tf
arm.GetComponent<Renderer>().material = arm.GetComponent<Renderer>().material =
new Material(shader) { color = new Color32(255, 220, 180, 255) }; new Material(shader) { color = new Color32(255, 220, 180, 255) };
// ── Button (sized for close-up FPS view) ─────────────────
btn = GameObject.CreatePrimitive(PrimitiveType.Cylinder); btn = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
btn.name = isLeft ? "TrueButton" : "FalseButton"; btn.name = isLeft ? "TrueButton" : "FalseButton";
btn.transform.SetParent(hand.transform); btn.transform.SetParent(hand.transform);
...@@ -73,17 +73,17 @@ namespace com.al_arcade.tf ...@@ -73,17 +73,17 @@ namespace com.al_arcade.tf
}; };
btn.GetComponent<Renderer>().material = mat; btn.GetComponent<Renderer>().material = mat;
// ── Label — ★ small font, tight rect, on top of button ───
var labelObj = new GameObject("Label"); var labelObj = new GameObject("Label");
labelObj.transform.SetParent(btn.transform); labelObj.transform.SetParent(btn.transform);
labelObj.transform.localPosition = new Vector3(0, 0.8f, 0); labelObj.transform.localPosition = new Vector3(0, 0.8f, 0);
labelObj.transform.localRotation = Quaternion.Euler(90, 0, 0); labelObj.transform.localRotation = Quaternion.Euler(90, 0, 0);
// ★ Scale the label container down so the text is proportional
labelObj.transform.localScale = new Vector3(0.6f, 0.6f, 0.6f); labelObj.transform.localScale = new Vector3(0.6f, 0.6f, 0.6f);
label = labelObj.AddComponent<ArabicTextMeshPro>(); label = labelObj.AddComponent<ArabicTextMeshPro>();
label.arabicText = isLeft ? "صح ✓" : "خطأ ✗"; label.arabicText = isLeft ? "صح ✓" : "خطأ ✗";
// ★ fontSize 2 in a small world-space rect = readable not giant
label.fontSize = 2.2f; label.fontSize = 2.2f;
label.fontSizeMin = 1.5f; label.fontSizeMin = 1.5f;
label.fontSizeMax = 2.5f; label.fontSizeMax = 2.5f;
...@@ -91,12 +91,12 @@ namespace com.al_arcade.tf ...@@ -91,12 +91,12 @@ namespace com.al_arcade.tf
label.alignment = TMPro.TextAlignmentOptions.Center; label.alignment = TMPro.TextAlignmentOptions.Center;
label.color = Color.white; label.color = Color.white;
label.fontStyle = TMPro.FontStyles.Bold; label.fontStyle = TMPro.FontStyles.Bold;
// ★ Tight rect so text doesn't overflow
label.rectTransform.sizeDelta = new Vector2(1.2f, 0.5f); label.rectTransform.sizeDelta = new Vector2(1.2f, 0.5f);
label.overflowMode = TMPro.TextOverflowModes.Overflow; label.overflowMode = TMPro.TextOverflowModes.Overflow;
SSFontManager.Apply(label); SSFontManager.Apply(label);
// ── Glow ring ────────────────────────────────────────────
var ring = GameObject.CreatePrimitive(PrimitiveType.Cylinder); var ring = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
ring.name = "GlowRing"; ring.name = "GlowRing";
ring.transform.SetParent(btn.transform); ring.transform.SetParent(btn.transform);
...@@ -110,7 +110,6 @@ namespace com.al_arcade.tf ...@@ -110,7 +110,6 @@ namespace com.al_arcade.tf
}; };
} }
// ── Idle Animation ───────────────────────────────────────────
private void StartIdleAnimation() private void StartIdleAnimation()
{ {
...@@ -139,7 +138,6 @@ namespace com.al_arcade.tf ...@@ -139,7 +138,6 @@ namespace com.al_arcade.tf
_idleSeq = null; _idleSeq = null;
} }
// ── Input ────────────────────────────────────────────────────
public void SetReady(bool ready) => _isReady = ready; public void SetReady(bool ready) => _isReady = ready;
...@@ -161,14 +159,14 @@ namespace com.al_arcade.tf ...@@ -161,14 +159,14 @@ namespace com.al_arcade.tf
var restPos = isTrue ? _leftRestPos : _rightRestPos; var restPos = isTrue ? _leftRestPos : _rightRestPos;
var btnBaseScale = isTrue ? _leftBtnBaseScale : _rightBtnBaseScale; var btnBaseScale = isTrue ? _leftBtnBaseScale : _rightBtnBaseScale;
// ★ Kill ALL tweens, reset to known state
KillIdleTween(); KillIdleTween();
DOTween.Kill(hand.transform); DOTween.Kill(hand.transform);
DOTween.Kill(btn.transform); DOTween.Kill(btn.transform);
hand.transform.localPosition = restPos; hand.transform.localPosition = restPos;
btn.transform.localScale = btnBaseScale; btn.transform.localScale = btnBaseScale;
// ★ Press animation — always returns to restPos
var pressSeq = DOTween.Sequence(); var pressSeq = DOTween.Sequence();
pressSeq.Append(hand.transform pressSeq.Append(hand.transform
.DOLocalMoveY(restPos.y + 0.06f, 0.07f) .DOLocalMoveY(restPos.y + 0.06f, 0.07f)
...@@ -181,7 +179,7 @@ namespace com.al_arcade.tf ...@@ -181,7 +179,7 @@ namespace com.al_arcade.tf
.SetEase(Ease.OutBounce)); .SetEase(Ease.OutBounce));
pressSeq.OnComplete(() => StartIdleAnimation()); pressSeq.OnComplete(() => StartIdleAnimation());
// ★ Button squash (Y axis is thickness for cylinder)
btn.transform.DOScaleY(btnBaseScale.y * 0.3f, 0.05f) btn.transform.DOScaleY(btnBaseScale.y * 0.3f, 0.05f)
.SetEase(Ease.InQuad) .SetEase(Ease.InQuad)
.OnComplete(() => .OnComplete(() =>
...@@ -195,7 +193,6 @@ namespace com.al_arcade.tf ...@@ -195,7 +193,6 @@ namespace com.al_arcade.tf
TfGameManager.Instance.SubmitAnswer(isTrue); TfGameManager.Instance.SubmitAnswer(isTrue);
} }
// ── Feedback ─────────────────────────────────────────────────
public void PlayCorrectFeedback(bool wasTrue) public void PlayCorrectFeedback(bool wasTrue)
{ {
...@@ -270,4 +267,4 @@ namespace com.al_arcade.tf ...@@ -270,4 +267,4 @@ namespace com.al_arcade.tf
Shader.Find("Universal Render Pipeline/Lit") Shader.Find("Universal Render Pipeline/Lit")
?? Shader.Find("Standard") ?? Shader.Find("Unlit/Color"); ?? Shader.Find("Standard") ?? Shader.Find("Unlit/Color");
} }
} }
\ No newline at end of file
...@@ -12,9 +12,7 @@ namespace com.al_arcade.tf ...@@ -12,9 +12,7 @@ namespace com.al_arcade.tf
[AddComponentMenu("Science Street/TF Prefab Builder")] [AddComponentMenu("Science Street/TF Prefab Builder")]
public class TfPrefabBuilder : MonoBehaviour public class TfPrefabBuilder : MonoBehaviour
{ {
// ════════════════════════════════════════════════════════════
// SCENE
// ════════════════════════════════════════════════════════════
[Header("Scene Environment")] [Header("Scene Environment")]
[Tooltip("Your full factory environment prefab.")] [Tooltip("Your full factory environment prefab.")]
...@@ -26,18 +24,12 @@ namespace com.al_arcade.tf ...@@ -26,18 +24,12 @@ namespace com.al_arcade.tf
[SerializeField] private float cameraFOV = 55f; [SerializeField] private float cameraFOV = 55f;
[SerializeField] private Color cameraBgColor = new Color32(70, 80, 110, 255); [SerializeField] private Color cameraBgColor = new Color32(70, 80, 110, 255);
// ════════════════════════════════════════════════════════════
// LIGHTING
// ════════════════════════════════════════════════════════════
[Header("Lighting")] [Header("Lighting")]
[SerializeField] private GameObject directionalLightPrefab; [SerializeField] private GameObject directionalLightPrefab;
[SerializeField] private GameObject[] extraLightPrefabs; [SerializeField] private GameObject[] extraLightPrefabs;
[SerializeField] private Color ambientColor = new Color32(75, 78, 95, 255); [SerializeField] private Color ambientColor = new Color32(75, 78, 95, 255);
// ════════════════════════════════════════════════════════════
// PRODUCTION LINE
// ════════════════════════════════════════════════════════════
[Header("Production Line")] [Header("Production Line")]
[Tooltip("Your production line prefab. MUST have TfProductionLine.\n" + [Tooltip("Your production line prefab. MUST have TfProductionLine.\n" +
...@@ -45,9 +37,6 @@ namespace com.al_arcade.tf ...@@ -45,9 +37,6 @@ namespace com.al_arcade.tf
[SerializeField] private GameObject productionLinePrefab; [SerializeField] private GameObject productionLinePrefab;
[SerializeField] private Vector3 productionLinePosition = new Vector3(0, 0, 2); [SerializeField] private Vector3 productionLinePosition = new Vector3(0, 0, 2);
// ════════════════════════════════════════════════════════════
// QUESTION SCREEN
// ════════════════════════════════════════════════════════════
[Header("Question Screen")] [Header("Question Screen")]
[Tooltip("Your question screen prefab. MUST have TfQuestionScreen.\n" + [Tooltip("Your question screen prefab. MUST have TfQuestionScreen.\n" +
...@@ -55,27 +44,18 @@ namespace com.al_arcade.tf ...@@ -55,27 +44,18 @@ namespace com.al_arcade.tf
[SerializeField] private GameObject questionScreenPrefab; [SerializeField] private GameObject questionScreenPrefab;
[SerializeField] private Vector3 questionScreenPosition = new Vector3(0, 5f, 12f); [SerializeField] private Vector3 questionScreenPosition = new Vector3(0, 5f, 12f);
// ════════════════════════════════════════════════════════════
// HANDS
// ════════════════════════════════════════════════════════════
[Header("Hands / Buttons")] [Header("Hands / Buttons")]
[Tooltip("Your hands prefab. MUST have TfHandController.\n" + [Tooltip("Your hands prefab. MUST have TfHandController.\n" +
"If null, procedural hands are created.")] "If null, procedural hands are created.")]
[SerializeField] private GameObject handsPrefab; [SerializeField] private GameObject handsPrefab;
// ════════════════════════════════════════════════════════════
// UI
// ════════════════════════════════════════════════════════════
[Header("UI Canvas")] [Header("UI Canvas")]
[Tooltip("Your Canvas prefab. MUST have TfUIManager with all references wired.\n" + [Tooltip("Your Canvas prefab. MUST have TfUIManager with all references wired.\n" +
"If null, procedural UI is created.")] "If null, procedural UI is created.")]
[SerializeField] private GameObject canvasPrefab; [SerializeField] private GameObject canvasPrefab;
// ════════════════════════════════════════════════════════════
// AUDIO
// ════════════════════════════════════════════════════════════
[Header("Audio — SFX Clips")] [Header("Audio — SFX Clips")]
[SerializeField] private AudioClip sfxCorrect; [SerializeField] private AudioClip sfxCorrect;
...@@ -87,9 +67,6 @@ namespace com.al_arcade.tf ...@@ -87,9 +67,6 @@ namespace com.al_arcade.tf
[SerializeField] private AudioClip sfxPop; [SerializeField] private AudioClip sfxPop;
[SerializeField] private AudioClip sfxSlam; [SerializeField] private AudioClip sfxSlam;
// ════════════════════════════════════════════════════════════
// PARTICLES
// ════════════════════════════════════════════════════════════
[Header("Particles")] [Header("Particles")]
[SerializeField] private ParticleSystem correctBurstParticle; [SerializeField] private ParticleSystem correctBurstParticle;
...@@ -98,16 +75,10 @@ namespace com.al_arcade.tf ...@@ -98,16 +75,10 @@ namespace com.al_arcade.tf
[SerializeField] private ParticleSystem sparksParticle; [SerializeField] private ParticleSystem sparksParticle;
[SerializeField] private ParticleSystem starBurstParticle; [SerializeField] private ParticleSystem starBurstParticle;
// ════════════════════════════════════════════════════════════
// FONT
// ════════════════════════════════════════════════════════════
[Header("Arabic Font")] [Header("Arabic Font")]
[SerializeField] private TMP_FontAsset arabicFont; [SerializeField] private TMP_FontAsset arabicFont;
// ════════════════════════════════════════════════════════════
// GAME SETTINGS
// ════════════════════════════════════════════════════════════
[Header("Game Settings")] [Header("Game Settings")]
[SerializeField] private string buildType = "scistreet"; [SerializeField] private string buildType = "scistreet";
...@@ -120,9 +91,6 @@ namespace com.al_arcade.tf ...@@ -120,9 +91,6 @@ namespace com.al_arcade.tf
[Header("Debug")] [Header("Debug")]
[SerializeField] private bool useOfflineTestData = false; [SerializeField] private bool useOfflineTestData = false;
// ════════════════════════════════════════════════════════════
// RUNTIME
// ════════════════════════════════════════════════════════════
private TfGameManager _gm; private TfGameManager _gm;
private TfHandController _handController; private TfHandController _handController;
...@@ -159,20 +127,20 @@ namespace com.al_arcade.tf ...@@ -159,20 +127,20 @@ namespace com.al_arcade.tf
session.classCode = classCode; session.classCode = classCode;
yield return null; yield return null;
// ── Camera ───────────────────────────────────────────
SetupCamera(); SetupCamera();
yield return null; yield return null;
// ── Environment ──────────────────────────────────────
if (environmentPrefab != null) if (environmentPrefab != null)
Instantiate(environmentPrefab, Vector3.zero, Quaternion.identity); Instantiate(environmentPrefab, Vector3.zero, Quaternion.identity);
yield return null; yield return null;
// ── Lighting ─────────────────────────────────────────
SetupLighting(); SetupLighting();
yield return null; yield return null;
// ── Production Line ──────────────────────────────────
if (productionLinePrefab != null) if (productionLinePrefab != null)
{ {
var lineObj = Instantiate(productionLinePrefab, var lineObj = Instantiate(productionLinePrefab,
...@@ -194,7 +162,7 @@ namespace com.al_arcade.tf ...@@ -194,7 +162,7 @@ namespace com.al_arcade.tf
} }
yield return null; yield return null;
// ── Question Screen ──────────────────────────────────
if (questionScreenPrefab != null) if (questionScreenPrefab != null)
{ {
var screenObj = Instantiate(questionScreenPrefab, var screenObj = Instantiate(questionScreenPrefab,
...@@ -215,7 +183,7 @@ namespace com.al_arcade.tf ...@@ -215,7 +183,7 @@ namespace com.al_arcade.tf
} }
yield return null; yield return null;
// ── Hands ────────────────────────────────────────────
if (handsPrefab != null) if (handsPrefab != null)
{ {
var handsObj = Instantiate(handsPrefab); var handsObj = Instantiate(handsPrefab);
...@@ -235,7 +203,7 @@ namespace com.al_arcade.tf ...@@ -235,7 +203,7 @@ namespace com.al_arcade.tf
} }
yield return null; yield return null;
// ── UI ───────────────────────────────────────────────
if (canvasPrefab != null) if (canvasPrefab != null)
{ {
var canvasObj = Instantiate(canvasPrefab); var canvasObj = Instantiate(canvasPrefab);
...@@ -258,7 +226,7 @@ namespace com.al_arcade.tf ...@@ -258,7 +226,7 @@ namespace com.al_arcade.tf
_uiManager.onRestartClicked = new UnityEvent(); _uiManager.onRestartClicked = new UnityEvent();
yield return null; yield return null;
// ── Game Manager ─────────────────────────────────────
var gmObj = new GameObject("TfGameManager"); var gmObj = new GameObject("TfGameManager");
_gm = gmObj.AddComponent<TfGameManager>(); _gm = gmObj.AddComponent<TfGameManager>();
_gm.handController = _handController; _gm.handController = _handController;
...@@ -348,4 +316,4 @@ namespace com.al_arcade.tf ...@@ -348,4 +316,4 @@ namespace com.al_arcade.tf
}; };
} }
} }
} }
\ No newline at end of file
...@@ -21,7 +21,7 @@ namespace com.al_arcade.tf ...@@ -21,7 +21,7 @@ namespace com.al_arcade.tf
float totalLength = stepsToWin * stepDistance + 10f; float totalLength = stepsToWin * stepDistance + 10f;
var shader = GetShader(); var shader = GetShader();
// ── Belt surface ─────────────────────────────────────────
var belt = GameObject.CreatePrimitive(PrimitiveType.Cube); var belt = GameObject.CreatePrimitive(PrimitiveType.Cube);
belt.name = "Belt"; belt.name = "Belt";
belt.transform.SetParent(transform); belt.transform.SetParent(transform);
...@@ -31,7 +31,7 @@ namespace com.al_arcade.tf ...@@ -31,7 +31,7 @@ namespace com.al_arcade.tf
belt.GetComponent<Renderer>().material = belt.GetComponent<Renderer>().material =
new Material(shader) { color = new Color32(55, 55, 65, 255) }; new Material(shader) { color = new Color32(55, 55, 65, 255) };
// ── Belt top highlight ───────────────────────────────────
var beltTop = GameObject.CreatePrimitive(PrimitiveType.Cube); var beltTop = GameObject.CreatePrimitive(PrimitiveType.Cube);
beltTop.name = "BeltTop"; beltTop.name = "BeltTop";
beltTop.transform.SetParent(transform); beltTop.transform.SetParent(transform);
...@@ -41,7 +41,7 @@ namespace com.al_arcade.tf ...@@ -41,7 +41,7 @@ namespace com.al_arcade.tf
beltTop.GetComponent<Renderer>().material = beltTop.GetComponent<Renderer>().material =
new Material(shader) { color = new Color32(70, 70, 80, 255) }; new Material(shader) { color = new Color32(70, 70, 80, 255) };
// ── Rollers ──────────────────────────────────────────────
int rc = Mathf.CeilToInt(totalLength / 2f); int rc = Mathf.CeilToInt(totalLength / 2f);
_rollers = new GameObject[rc]; _rollers = new GameObject[rc];
for (int i = 0; i < rc; i++) for (int i = 0; i < rc; i++)
...@@ -58,7 +58,7 @@ namespace com.al_arcade.tf ...@@ -58,7 +58,7 @@ namespace com.al_arcade.tf
_rollers[i] = r; _rollers[i] = r;
} }
// ── Side rails ───────────────────────────────────────────
for (int s = -1; s <= 1; s += 2) for (int s = -1; s <= 1; s += 2)
{ {
var rail = GameObject.CreatePrimitive(PrimitiveType.Cube); var rail = GameObject.CreatePrimitive(PrimitiveType.Cube);
...@@ -71,7 +71,7 @@ namespace com.al_arcade.tf ...@@ -71,7 +71,7 @@ namespace com.al_arcade.tf
new Material(shader) { color = SSColorPalette.Primary }; new Material(shader) { color = SSColorPalette.Primary };
} }
// ── Legs ─────────────────────────────────────────────────
for (float z = 1; z < totalLength; z += 4f) for (float z = 1; z < totalLength; z += 4f)
{ {
for (int s = -1; s <= 1; s += 2) for (int s = -1; s <= 1; s += 2)
...@@ -87,12 +87,12 @@ namespace com.al_arcade.tf ...@@ -87,12 +87,12 @@ namespace com.al_arcade.tf
} }
} }
// ── Progress markers ─────────────────────────────────────
for (int i = 0; i < stepsToWin; i++) for (int i = 0; i < stepsToWin; i++)
{ {
float mz = (i + 1) * stepDistance; float mz = (i + 1) * stepDistance;
// Line marker
var marker = GameObject.CreatePrimitive(PrimitiveType.Cube); var marker = GameObject.CreatePrimitive(PrimitiveType.Cube);
marker.name = $"Marker_{i}"; marker.name = $"Marker_{i}";
marker.transform.SetParent(transform); marker.transform.SetParent(transform);
...@@ -105,13 +105,13 @@ namespace com.al_arcade.tf ...@@ -105,13 +105,13 @@ namespace com.al_arcade.tf
color = SSColorPalette.WithAlpha(SSColorPalette.Accent, 0.5f) color = SSColorPalette.WithAlpha(SSColorPalette.Accent, 0.5f)
}; };
// Step label — facing camera
var labelObj = new GameObject("StepLabel"); var labelObj = new GameObject("StepLabel");
labelObj.transform.SetParent(marker.transform); labelObj.transform.SetParent(marker.transform);
labelObj.transform.position = labelObj.transform.position =
marker.transform.position + new Vector3(1.6f, 0.5f, 0); marker.transform.position + new Vector3(1.6f, 0.5f, 0);
// Billboard toward camera
Vector3 away = labelObj.transform.position - cameraPos; Vector3 away = labelObj.transform.position - cameraPos;
away.y = 0; away.y = 0;
if (away.sqrMagnitude > 0.01f) if (away.sqrMagnitude > 0.01f)
...@@ -119,7 +119,7 @@ namespace com.al_arcade.tf ...@@ -119,7 +119,7 @@ namespace com.al_arcade.tf
var lbl = labelObj.AddComponent<ArabicTextMeshPro>(); var lbl = labelObj.AddComponent<ArabicTextMeshPro>();
lbl.arabicText = $"✦ {i + 1}"; lbl.arabicText = $"✦ {i + 1}";
// ★ Small readable size
lbl.fontSize = 2f; lbl.fontSize = 2f;
lbl.alignment = TMPro.TextAlignmentOptions.Center; lbl.alignment = TMPro.TextAlignmentOptions.Center;
lbl.color = SSColorPalette.WithAlpha(SSColorPalette.Accent, 0.7f); lbl.color = SSColorPalette.WithAlpha(SSColorPalette.Accent, 0.7f);
...@@ -127,7 +127,7 @@ namespace com.al_arcade.tf ...@@ -127,7 +127,7 @@ namespace com.al_arcade.tf
SSFontManager.Apply(lbl); SSFontManager.Apply(lbl);
} }
// ── End zone ─────────────────────────────────────────────
float endZ = stepsToWin * stepDistance + 2f; float endZ = stepsToWin * stepDistance + 2f;
var ez = GameObject.CreatePrimitive(PrimitiveType.Cube); var ez = GameObject.CreatePrimitive(PrimitiveType.Cube);
ez.name = "EndZone"; ez.name = "EndZone";
...@@ -138,7 +138,7 @@ namespace com.al_arcade.tf ...@@ -138,7 +138,7 @@ namespace com.al_arcade.tf
ez.GetComponent<Renderer>().material = ez.GetComponent<Renderer>().material =
new Material(shader) { color = SSColorPalette.WithAlpha(SSColorPalette.Success, 0.35f) }; new Material(shader) { color = SSColorPalette.WithAlpha(SSColorPalette.Success, 0.35f) };
// ── Product ──────────────────────────────────────────────
_product = new GameObject("Product"); _product = new GameObject("Product");
_product.transform.SetParent(transform); _product.transform.SetParent(transform);
_product.transform.localPosition = new Vector3(0, 0.7f, 0); _product.transform.localPosition = new Vector3(0, 0.7f, 0);
...@@ -152,7 +152,7 @@ namespace com.al_arcade.tf ...@@ -152,7 +152,7 @@ namespace com.al_arcade.tf
_productMat = new Material(shader) { color = SSColorPalette.Accent }; _productMat = new Material(shader) { color = SSColorPalette.Accent };
box.GetComponent<Renderer>().material = _productMat; box.GetComponent<Renderer>().material = _productMat;
// Stripe on box
var stripe = GameObject.CreatePrimitive(PrimitiveType.Cube); var stripe = GameObject.CreatePrimitive(PrimitiveType.Cube);
stripe.name = "Stripe"; stripe.name = "Stripe";
stripe.transform.SetParent(_product.transform); stripe.transform.SetParent(_product.transform);
...@@ -162,7 +162,7 @@ namespace com.al_arcade.tf ...@@ -162,7 +162,7 @@ namespace com.al_arcade.tf
stripe.GetComponent<Renderer>().material = stripe.GetComponent<Renderer>().material =
new Material(shader) { color = SSColorPalette.Primary }; new Material(shader) { color = SSColorPalette.Primary };
// Star on top
var star = GameObject.CreatePrimitive(PrimitiveType.Sphere); var star = GameObject.CreatePrimitive(PrimitiveType.Sphere);
star.name = "Star"; star.name = "Star";
star.transform.SetParent(_product.transform); star.transform.SetParent(_product.transform);
...@@ -175,7 +175,6 @@ namespace com.al_arcade.tf ...@@ -175,7 +175,6 @@ namespace com.al_arcade.tf
_currentZ = 0; _currentZ = 0;
} }
// ── Movement ─────────────────────────────────────────────────
public IEnumerator MoveForward(float dist) public IEnumerator MoveForward(float dist)
{ {
...@@ -265,4 +264,4 @@ namespace com.al_arcade.tf ...@@ -265,4 +264,4 @@ namespace com.al_arcade.tf
Shader.Find("Universal Render Pipeline/Lit") Shader.Find("Universal Render Pipeline/Lit")
?? Shader.Find("Standard") ?? Shader.Find("Unlit/Color"); ?? Shader.Find("Standard") ?? Shader.Find("Unlit/Color");
} }
} }
\ No newline at end of file
...@@ -15,15 +15,13 @@ namespace com.al_arcade.tf ...@@ -15,15 +15,13 @@ namespace com.al_arcade.tf
private Material _feedbackMat; private Material _feedbackMat;
private GameObject _feedbackPanel; private GameObject _feedbackPanel;
/// <summary>
/// Build screen at position, rotated to face cameraPos.
/// </summary>
public void Build(Vector3 position, Vector3 cameraPos) public void Build(Vector3 position, Vector3 cameraPos)
{ {
transform.position = position; transform.position = position;
var shader = GetShader(); var shader = GetShader();
// ── Frame ────────────────────────────────────────────────
var frame = GameObject.CreatePrimitive(PrimitiveType.Cube); var frame = GameObject.CreatePrimitive(PrimitiveType.Cube);
frame.name = "Frame"; frame.name = "Frame";
frame.transform.SetParent(transform); frame.transform.SetParent(transform);
...@@ -33,7 +31,7 @@ namespace com.al_arcade.tf ...@@ -33,7 +31,7 @@ namespace com.al_arcade.tf
frame.GetComponent<Renderer>().material = frame.GetComponent<Renderer>().material =
new Material(shader) { color = new Color32(25, 25, 35, 255) }; new Material(shader) { color = new Color32(25, 25, 35, 255) };
// ── Screen border highlight ──────────────────────────────
var borderGlow = GameObject.CreatePrimitive(PrimitiveType.Cube); var borderGlow = GameObject.CreatePrimitive(PrimitiveType.Cube);
borderGlow.name = "BorderGlow"; borderGlow.name = "BorderGlow";
borderGlow.transform.SetParent(transform); borderGlow.transform.SetParent(transform);
...@@ -43,7 +41,7 @@ namespace com.al_arcade.tf ...@@ -43,7 +41,7 @@ namespace com.al_arcade.tf
borderGlow.GetComponent<Renderer>().material = borderGlow.GetComponent<Renderer>().material =
new Material(shader) { color = SSColorPalette.WithAlpha(SSColorPalette.Primary, 0.3f) }; new Material(shader) { color = SSColorPalette.WithAlpha(SSColorPalette.Primary, 0.3f) };
// ── Screen surface ───────────────────────────────────────
var surface = GameObject.CreatePrimitive(PrimitiveType.Cube); var surface = GameObject.CreatePrimitive(PrimitiveType.Cube);
surface.name = "Surface"; surface.name = "Surface";
surface.transform.SetParent(transform); surface.transform.SetParent(transform);
...@@ -53,7 +51,7 @@ namespace com.al_arcade.tf ...@@ -53,7 +51,7 @@ namespace com.al_arcade.tf
_screenMat = new Material(shader) { color = new Color32(15, 15, 50, 255) }; _screenMat = new Material(shader) { color = new Color32(15, 15, 50, 255) };
surface.GetComponent<Renderer>().material = _screenMat; surface.GetComponent<Renderer>().material = _screenMat;
// ── Pole ─────────────────────────────────────────────────
var pole = GameObject.CreatePrimitive(PrimitiveType.Cylinder); var pole = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
pole.name = "Pole"; pole.name = "Pole";
pole.transform.SetParent(transform); pole.transform.SetParent(transform);
...@@ -63,7 +61,7 @@ namespace com.al_arcade.tf ...@@ -63,7 +61,7 @@ namespace com.al_arcade.tf
pole.GetComponent<Renderer>().material = pole.GetComponent<Renderer>().material =
new Material(shader) { color = new Color32(70, 70, 85, 255) }; new Material(shader) { color = new Color32(70, 70, 85, 255) };
// ── Pole base ────────────────────────────────────────────
var poleBase = GameObject.CreatePrimitive(PrimitiveType.Cylinder); var poleBase = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
poleBase.name = "PoleBase"; poleBase.name = "PoleBase";
poleBase.transform.SetParent(transform); poleBase.transform.SetParent(transform);
...@@ -73,7 +71,7 @@ namespace com.al_arcade.tf ...@@ -73,7 +71,7 @@ namespace com.al_arcade.tf
poleBase.GetComponent<Renderer>().material = poleBase.GetComponent<Renderer>().material =
new Material(shader) { color = new Color32(60, 60, 75, 255) }; new Material(shader) { color = new Color32(60, 60, 75, 255) };
// ── Question text ★ z = -0.25 (pushed out toward camera) ─
var textObj = new GameObject("QuestionText"); var textObj = new GameObject("QuestionText");
textObj.transform.SetParent(transform); textObj.transform.SetParent(transform);
textObj.transform.localPosition = new Vector3(0, 0.25f, -0.25f); textObj.transform.localPosition = new Vector3(0, 0.25f, -0.25f);
...@@ -84,7 +82,7 @@ namespace com.al_arcade.tf ...@@ -84,7 +82,7 @@ namespace com.al_arcade.tf
_questionText.color = Color.white; _questionText.color = Color.white;
_questionText.fontStyle = TMPro.FontStyles.Bold; _questionText.fontStyle = TMPro.FontStyles.Bold;
_questionText.enableAutoSizing = true; _questionText.enableAutoSizing = true;
// ★ Sensible font range for a 7-unit-wide screen at ~12m distance
_questionText.fontSizeMin = 1.8f; _questionText.fontSizeMin = 1.8f;
_questionText.fontSizeMax = 3.8f; _questionText.fontSizeMax = 3.8f;
_questionText.rectTransform.sizeDelta = new Vector2(6.2f, 2.2f); _questionText.rectTransform.sizeDelta = new Vector2(6.2f, 2.2f);
...@@ -92,7 +90,7 @@ namespace com.al_arcade.tf ...@@ -92,7 +90,7 @@ namespace com.al_arcade.tf
_questionText.overflowMode = TMPro.TextOverflowModes.Ellipsis; _questionText.overflowMode = TMPro.TextOverflowModes.Ellipsis;
SSFontManager.Apply(_questionText); SSFontManager.Apply(_questionText);
// ── Source text ──────────────────────────────────────────
var srcObj = new GameObject("SourceText"); var srcObj = new GameObject("SourceText");
srcObj.transform.SetParent(transform); srcObj.transform.SetParent(transform);
srcObj.transform.localPosition = new Vector3(0, -1.15f, -0.25f); srcObj.transform.localPosition = new Vector3(0, -1.15f, -0.25f);
...@@ -105,7 +103,7 @@ namespace com.al_arcade.tf ...@@ -105,7 +103,7 @@ namespace com.al_arcade.tf
_sourceText.rectTransform.sizeDelta = new Vector2(6f, 0.5f); _sourceText.rectTransform.sizeDelta = new Vector2(6f, 0.5f);
SSFontManager.Apply(_sourceText); SSFontManager.Apply(_sourceText);
// ── Feedback panel (overlays question) ───────────────────
_feedbackPanel = GameObject.CreatePrimitive(PrimitiveType.Cube); _feedbackPanel = GameObject.CreatePrimitive(PrimitiveType.Cube);
_feedbackPanel.name = "FeedbackPanel"; _feedbackPanel.name = "FeedbackPanel";
_feedbackPanel.transform.SetParent(transform); _feedbackPanel.transform.SetParent(transform);
...@@ -122,14 +120,14 @@ namespace com.al_arcade.tf ...@@ -122,14 +120,14 @@ namespace com.al_arcade.tf
fbObj.transform.SetParent(_feedbackPanel.transform); fbObj.transform.SetParent(_feedbackPanel.transform);
fbObj.transform.localPosition = new Vector3(0, 0, -0.15f); fbObj.transform.localPosition = new Vector3(0, 0, -0.15f);
fbObj.transform.localRotation = Quaternion.identity; fbObj.transform.localRotation = Quaternion.identity;
// ★ Scale container so feedback text isn't giant
// FeedbackPanel is 6.6 wide but we want text around 3 units
fbObj.transform.localScale = new Vector3(0.5f, 0.5f, 1f); fbObj.transform.localScale = new Vector3(0.5f, 0.5f, 1f);
_feedbackText = fbObj.AddComponent<ArabicTextMeshPro>(); _feedbackText = fbObj.AddComponent<ArabicTextMeshPro>();
_feedbackText.alignment = TMPro.TextAlignmentOptions.Center; _feedbackText.alignment = TMPro.TextAlignmentOptions.Center;
_feedbackText.color = Color.white; _feedbackText.color = Color.white;
// ★ Moderate feedback font size
_feedbackText.fontSize = 4f; _feedbackText.fontSize = 4f;
_feedbackText.fontSizeMin = 2.5f; _feedbackText.fontSizeMin = 2.5f;
_feedbackText.fontSizeMax = 5f; _feedbackText.fontSizeMax = 5f;
...@@ -140,7 +138,7 @@ namespace com.al_arcade.tf ...@@ -140,7 +138,7 @@ namespace com.al_arcade.tf
_feedbackText.arabicText = ""; _feedbackText.arabicText = "";
SSFontManager.Apply(_feedbackText); SSFontManager.Apply(_feedbackText);
// ── Accent strips (top + bottom) ─────────────────────────
var topStrip = GameObject.CreatePrimitive(PrimitiveType.Cube); var topStrip = GameObject.CreatePrimitive(PrimitiveType.Cube);
topStrip.name = "TopStrip"; topStrip.name = "TopStrip";
topStrip.transform.SetParent(transform); topStrip.transform.SetParent(transform);
...@@ -159,7 +157,7 @@ namespace com.al_arcade.tf ...@@ -159,7 +157,7 @@ namespace com.al_arcade.tf
botStrip.GetComponent<Renderer>().material = botStrip.GetComponent<Renderer>().material =
new Material(shader) { color = SSColorPalette.WithAlpha(SSColorPalette.Accent, 0.5f) }; new Material(shader) { color = SSColorPalette.WithAlpha(SSColorPalette.Accent, 0.5f) };
// ── Status indicator lights ──────────────────────────────
for (int i = -1; i <= 1; i += 2) for (int i = -1; i <= 1; i += 2)
{ {
var light = GameObject.CreatePrimitive(PrimitiveType.Sphere); var light = GameObject.CreatePrimitive(PrimitiveType.Sphere);
...@@ -174,18 +172,17 @@ namespace com.al_arcade.tf ...@@ -174,18 +172,17 @@ namespace com.al_arcade.tf
}; };
} }
// ★ Rotate entire screen so -Z faces camera
Vector3 awayFromCamera = transform.position - cameraPos; Vector3 awayFromCamera = transform.position - cameraPos;
awayFromCamera.y = 0; awayFromCamera.y = 0;
if (awayFromCamera.sqrMagnitude > 0.01f) if (awayFromCamera.sqrMagnitude > 0.01f)
transform.rotation = Quaternion.LookRotation(awayFromCamera); transform.rotation = Quaternion.LookRotation(awayFromCamera);
} }
// ── Show Question ────────────────────────────────────────────
public void ShowQuestion(string text, string source = "") public void ShowQuestion(string text, string source = "")
{ {
// Hide feedback
if (_feedbackMat != null) if (_feedbackMat != null)
{ {
DOTween.Kill(_feedbackMat, "fbMat"); DOTween.Kill(_feedbackMat, "fbMat");
...@@ -207,7 +204,7 @@ namespace com.al_arcade.tf ...@@ -207,7 +204,7 @@ namespace com.al_arcade.tf
if (_sourceText != null) if (_sourceText != null)
_sourceText.arabicText = !string.IsNullOrEmpty(source) ? $"📖 {source}" : ""; _sourceText.arabicText = !string.IsNullOrEmpty(source) ? $"📖 {source}" : "";
// Screen flash effect
if (_screenMat != null) if (_screenMat != null)
{ {
DOTween.Kill(_screenMat, "screenFlash"); DOTween.Kill(_screenMat, "screenFlash");
...@@ -219,7 +216,6 @@ namespace com.al_arcade.tf ...@@ -219,7 +216,6 @@ namespace com.al_arcade.tf
} }
} }
// ── Show Feedback ────────────────────────────────────────────
public void ShowFeedback(bool correct, int streak) public void ShowFeedback(bool correct, int streak)
{ {
...@@ -246,7 +242,6 @@ namespace com.al_arcade.tf ...@@ -246,7 +242,6 @@ namespace com.al_arcade.tf
} }
} }
// ── Clear ────────────────────────────────────────────────────
public void Clear() public void Clear()
{ {
...@@ -264,4 +259,4 @@ namespace com.al_arcade.tf ...@@ -264,4 +259,4 @@ namespace com.al_arcade.tf
Shader.Find("Universal Render Pipeline/Lit") Shader.Find("Universal Render Pipeline/Lit")
?? Shader.Find("Standard") ?? Shader.Find("Unlit/Color"); ?? Shader.Find("Standard") ?? Shader.Find("Unlit/Color");
} }
} }
\ No newline at end of file
...@@ -21,9 +21,6 @@ namespace com.al_arcade.tf ...@@ -21,9 +21,6 @@ namespace com.al_arcade.tf
[Header("Events")] [Header("Events")]
public UnityEvent onRestartClicked; public UnityEvent onRestartClicked;
// ════════════════════════════════════════════════════════════
// BUILD — all sizes tuned for 1920×1080 reference
// ════════════════════════════════════════════════════════════
public void BuildUI() public void BuildUI()
{ {
...@@ -50,14 +47,13 @@ namespace com.al_arcade.tf ...@@ -50,14 +47,13 @@ namespace com.al_arcade.tf
_resultsUI.alpha = 0; _resultsUI.gameObject.SetActive(false); _resultsUI.alpha = 0; _resultsUI.gameObject.SetActive(false);
} }
// ── Game HUD ─────────────────────────────────────────────────
private void BuildGameHUD(Transform parent) private void BuildGameHUD(Transform parent)
{ {
var go = MkPanel(parent, "GameUI"); var go = MkPanel(parent, "GameUI");
_gameUI = go.AddComponent<CanvasGroup>(); _gameUI = go.AddComponent<CanvasGroup>();
// ── Top bar ──────────────────────────────────────────────
var topBarObj = new GameObject("TopBar"); var topBarObj = new GameObject("TopBar");
topBarObj.transform.SetParent(go.transform, false); topBarObj.transform.SetParent(go.transform, false);
var tbr = topBarObj.AddComponent<RectTransform>(); var tbr = topBarObj.AddComponent<RectTransform>();
...@@ -69,26 +65,26 @@ namespace com.al_arcade.tf ...@@ -69,26 +65,26 @@ namespace com.al_arcade.tf
topBarObj.AddComponent<Image>().color = topBarObj.AddComponent<Image>().color =
SSColorPalette.WithAlpha(SSColorPalette.PrimaryDark, 0.82f); SSColorPalette.WithAlpha(SSColorPalette.PrimaryDark, 0.82f);
// Score — top-left
_scoreText = MkTxt(topBarObj.transform, "Score", "0", _scoreText = MkTxt(topBarObj.transform, "Score", "0",
32, new Vector2(0, 0.5f), new Vector2(36, 6), new Vector2(180, 44)); 32, new Vector2(0, 0.5f), new Vector2(36, 6), new Vector2(180, 44));
_scoreText.color = SSColorPalette.Accent; _scoreText.color = SSColorPalette.Accent;
_scoreText.fontStyle = TMPro.FontStyles.Bold; _scoreText.fontStyle = TMPro.FontStyles.Bold;
_scoreText.alignment = TMPro.TextAlignmentOptions.MidlineLeft; _scoreText.alignment = TMPro.TextAlignmentOptions.MidlineLeft;
// Score label
var scoreLbl = MkTxt(topBarObj.transform, "ScoreLbl", "النقاط", var scoreLbl = MkTxt(topBarObj.transform, "ScoreLbl", "النقاط",
14, new Vector2(0, 0.5f), new Vector2(36, -20), new Vector2(100, 22)); 14, new Vector2(0, 0.5f), new Vector2(36, -20), new Vector2(100, 22));
scoreLbl.color = SSColorPalette.WithAlpha(Color.white, 0.5f); scoreLbl.color = SSColorPalette.WithAlpha(Color.white, 0.5f);
scoreLbl.alignment = TMPro.TextAlignmentOptions.MidlineLeft; scoreLbl.alignment = TMPro.TextAlignmentOptions.MidlineLeft;
// Streak — center
_streakText = MkTxt(topBarObj.transform, "Streak", "", _streakText = MkTxt(topBarObj.transform, "Streak", "",
26, new Vector2(0.5f, 0.5f), new Vector2(0, 0), new Vector2(200, 44)); 26, new Vector2(0.5f, 0.5f), new Vector2(0, 0), new Vector2(200, 44));
_streakText.color = SSColorPalette.Warning; _streakText.color = SSColorPalette.Warning;
_streakText.alignment = TMPro.TextAlignmentOptions.Center; _streakText.alignment = TMPro.TextAlignmentOptions.Center;
// ── Progress bar ─────────────────────────────────────────
var progBg = new GameObject("ProgressBg"); var progBg = new GameObject("ProgressBg");
progBg.transform.SetParent(go.transform, false); progBg.transform.SetParent(go.transform, false);
var pr = progBg.AddComponent<RectTransform>(); var pr = progBg.AddComponent<RectTransform>();
...@@ -111,13 +107,13 @@ namespace com.al_arcade.tf ...@@ -111,13 +107,13 @@ namespace com.al_arcade.tf
_progressFill = fill.AddComponent<Image>(); _progressFill = fill.AddComponent<Image>();
_progressFill.color = SSColorPalette.Accent; _progressFill.color = SSColorPalette.Accent;
// Progress label
_progressLabel = MkTxt(go.transform, "ProgLbl", "0 / 5", _progressLabel = MkTxt(go.transform, "ProgLbl", "0 / 5",
16, new Vector2(0.5f, 1), new Vector2(0, -98), new Vector2(200, 22)); 16, new Vector2(0.5f, 1), new Vector2(0, -98), new Vector2(200, 22));
_progressLabel.alignment = TMPro.TextAlignmentOptions.Center; _progressLabel.alignment = TMPro.TextAlignmentOptions.Center;
_progressLabel.color = SSColorPalette.WithAlpha(Color.white, 0.55f); _progressLabel.color = SSColorPalette.WithAlpha(Color.white, 0.55f);
// ── Bottom hint ──────────────────────────────────────────
var hintObj = new GameObject("HintBg"); var hintObj = new GameObject("HintBg");
hintObj.transform.SetParent(go.transform, false); hintObj.transform.SetParent(go.transform, false);
var hbr = hintObj.AddComponent<RectTransform>(); var hbr = hintObj.AddComponent<RectTransform>();
...@@ -136,7 +132,6 @@ namespace com.al_arcade.tf ...@@ -136,7 +132,6 @@ namespace com.al_arcade.tf
hint.color = SSColorPalette.WithAlpha(Color.white, 0.5f); hint.color = SSColorPalette.WithAlpha(Color.white, 0.5f);
} }
// ── Loading ──────────────────────────────────────────────────
private void BuildLoadingPanel(Transform parent) private void BuildLoadingPanel(Transform parent)
{ {
...@@ -150,14 +145,13 @@ namespace com.al_arcade.tf ...@@ -150,14 +145,13 @@ namespace com.al_arcade.tf
_loadingText.alignment = TMPro.TextAlignmentOptions.Center; _loadingText.alignment = TMPro.TextAlignmentOptions.Center;
_loadingText.color = Color.white; _loadingText.color = Color.white;
// Spinner dots
var dots = MkTxt(go.transform, "Dots", "● ● ●", var dots = MkTxt(go.transform, "Dots", "● ● ●",
18, new Vector2(0.5f, 0.5f), new Vector2(0, -36), new Vector2(200, 30)); 18, new Vector2(0.5f, 0.5f), new Vector2(0, -36), new Vector2(200, 30));
dots.alignment = TMPro.TextAlignmentOptions.Center; dots.alignment = TMPro.TextAlignmentOptions.Center;
dots.color = SSColorPalette.Accent; dots.color = SSColorPalette.Accent;
} }
// ── Error ────────────────────────────────────────────────────
private void BuildErrorPanel(Transform parent) private void BuildErrorPanel(Transform parent)
{ {
...@@ -173,18 +167,17 @@ namespace com.al_arcade.tf ...@@ -173,18 +167,17 @@ namespace com.al_arcade.tf
_errorText.enableWordWrapping = true; _errorText.enableWordWrapping = true;
} }
// ── Results ──────────────────────────────────────────────────
private void BuildResultsPanel(Transform parent) private void BuildResultsPanel(Transform parent)
{ {
var go = MkPanel(parent, "ResultsUI"); var go = MkPanel(parent, "ResultsUI");
_resultsUI = go.AddComponent<CanvasGroup>(); _resultsUI = go.AddComponent<CanvasGroup>();
// Semi-transparent background
var bg = go.AddComponent<Image>(); var bg = go.AddComponent<Image>();
bg.color = SSColorPalette.WithAlpha(SSColorPalette.PrimaryDark, 0.94f); bg.color = SSColorPalette.WithAlpha(SSColorPalette.PrimaryDark, 0.94f);
// Card panel (centered box)
var card = new GameObject("Card"); var card = new GameObject("Card");
card.transform.SetParent(go.transform, false); card.transform.SetParent(go.transform, false);
var cr = card.AddComponent<RectTransform>(); var cr = card.AddComponent<RectTransform>();
...@@ -194,33 +187,33 @@ namespace com.al_arcade.tf ...@@ -194,33 +187,33 @@ namespace com.al_arcade.tf
var cardImg = card.AddComponent<Image>(); var cardImg = card.AddComponent<Image>();
cardImg.color = SSColorPalette.WithAlpha(new Color32(30, 30, 55, 255), 0.95f); cardImg.color = SSColorPalette.WithAlpha(new Color32(30, 30, 55, 255), 0.95f);
// Title
_resultTitle = MkTxt(card.transform, "Title", "تم التصنيع! 🎉", _resultTitle = MkTxt(card.transform, "Title", "تم التصنيع! 🎉",
42, new Vector2(0.5f, 1), new Vector2(0, -50), new Vector2(500, 55)); 42, new Vector2(0.5f, 1), new Vector2(0, -50), new Vector2(500, 55));
_resultTitle.alignment = TMPro.TextAlignmentOptions.Center; _resultTitle.alignment = TMPro.TextAlignmentOptions.Center;
_resultTitle.color = SSColorPalette.Accent; _resultTitle.color = SSColorPalette.Accent;
_resultTitle.fontStyle = TMPro.FontStyles.Bold; _resultTitle.fontStyle = TMPro.FontStyles.Bold;
// Score
_resultScore = MkTxt(card.transform, "Score", "0", _resultScore = MkTxt(card.transform, "Score", "0",
56, new Vector2(0.5f, 1), new Vector2(0, -130), new Vector2(300, 70)); 56, new Vector2(0.5f, 1), new Vector2(0, -130), new Vector2(300, 70));
_resultScore.alignment = TMPro.TextAlignmentOptions.Center; _resultScore.alignment = TMPro.TextAlignmentOptions.Center;
_resultScore.color = Color.white; _resultScore.color = Color.white;
_resultScore.fontStyle = TMPro.FontStyles.Bold; _resultScore.fontStyle = TMPro.FontStyles.Bold;
// Score label
MkTxt(card.transform, "ScoreLbl", "النقاط", MkTxt(card.transform, "ScoreLbl", "النقاط",
16, new Vector2(0.5f, 1), new Vector2(0, -185), new Vector2(100, 25)) 16, new Vector2(0.5f, 1), new Vector2(0, -185), new Vector2(100, 25))
.color = SSColorPalette.WithAlpha(Color.white, 0.4f); .color = SSColorPalette.WithAlpha(Color.white, 0.4f);
// Stats
_resultStats = MkTxt(card.transform, "Stats", "", _resultStats = MkTxt(card.transform, "Stats", "",
20, new Vector2(0.5f, 0.5f), new Vector2(0, 10), new Vector2(450, 60)); 20, new Vector2(0.5f, 0.5f), new Vector2(0, 10), new Vector2(450, 60));
_resultStats.alignment = TMPro.TextAlignmentOptions.Center; _resultStats.alignment = TMPro.TextAlignmentOptions.Center;
_resultStats.color = SSColorPalette.WithAlpha(Color.white, 0.65f); _resultStats.color = SSColorPalette.WithAlpha(Color.white, 0.65f);
_resultStats.enableWordWrapping = true; _resultStats.enableWordWrapping = true;
// Restart button
var btnObj = new GameObject("RestartBtn"); var btnObj = new GameObject("RestartBtn");
btnObj.transform.SetParent(card.transform, false); btnObj.transform.SetParent(card.transform, false);
var br = btnObj.AddComponent<RectTransform>(); var br = btnObj.AddComponent<RectTransform>();
...@@ -251,9 +244,6 @@ namespace com.al_arcade.tf ...@@ -251,9 +244,6 @@ namespace com.al_arcade.tf
btnTxt.fontStyle = TMPro.FontStyles.Bold; btnTxt.fontStyle = TMPro.FontStyles.Bold;
} }
// ════════════════════════════════════════════════════════════
// PUBLIC METHODS
// ════════════════════════════════════════════════════════════
public void ShowGameUI() public void ShowGameUI()
{ {
...@@ -325,7 +315,7 @@ namespace com.al_arcade.tf ...@@ -325,7 +315,7 @@ namespace com.al_arcade.tf
if (_resultStats != null) if (_resultStats != null)
_resultStats.arabicText = $"✓ صحيح: {correct} | ✗ خطأ: {wrong}"; _resultStats.arabicText = $"✓ صحيح: {correct} | ✗ خطأ: {wrong}";
// Animated entrance
var seq = DOTween.Sequence(); var seq = DOTween.Sequence();
seq.Append(_resultsUI.DOFade(1, 0.4f)); seq.Append(_resultsUI.DOFade(1, 0.4f));
if (_resultTitle != null) if (_resultTitle != null)
...@@ -340,7 +330,7 @@ namespace com.al_arcade.tf ...@@ -340,7 +330,7 @@ namespace com.al_arcade.tf
seq.Append(_resultScore.transform seq.Append(_resultScore.transform
.DOScale(1, 0.35f).SetEase(Ease.OutBack)); .DOScale(1, 0.35f).SetEase(Ease.OutBack));
// Count-up
int ds = 0; int ds = 0;
DOTween.To(() => ds, x => DOTween.To(() => ds, x =>
{ {
...@@ -358,9 +348,6 @@ namespace com.al_arcade.tf ...@@ -358,9 +348,6 @@ namespace com.al_arcade.tf
if (_resultsUI != null) _resultsUI.gameObject.SetActive(false); if (_resultsUI != null) _resultsUI.gameObject.SetActive(false);
} }
// ════════════════════════════════════════════════════════════
// HELPERS
// ════════════════════════════════════════════════════════════
private GameObject MkPanel(Transform p, string n) private GameObject MkPanel(Transform p, string n)
{ {
...@@ -391,4 +378,4 @@ namespace com.al_arcade.tf ...@@ -391,4 +378,4 @@ namespace com.al_arcade.tf
return t; return t;
} }
} }
} }
\ No newline at end of file
This diff is collapsed.
fileFormatVersion: 2 fileFormatVersion: 2
guid: 5fe070f9e16c540beb7eae4411def4a3 guid: 172cb1724b3354adbba5be5427c4ab94
folderAsset: yes
DefaultImporter: DefaultImporter:
externalObjects: {} externalObjects: {}
userData: userData:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment