Commit c59801ad authored by Mahmoud Aglan's avatar Mahmoud Aglan

Fixes Some performance headaches in the mcq game

parent e94cf53a
{"TestSuite":"","Date":0,"Player":{"Development":false,"ScreenWidth":0,"ScreenHeight":0,"ScreenRefreshRate":0,"Fullscreen":false,"Vsync":0,"AntiAliasing":0,"Batchmode":false,"RenderThreadingMode":"MultiThreaded","MtRendering":false,"GraphicsJobs":false,"GpuSkinning":true,"Platform":"","ColorSpace":"","AnisotropicFiltering":"","BlendWeights":"","GraphicsApi":"","ScriptingBackend":"IL2CPP","AndroidTargetSdkVersion":"AndroidApiLevelAuto","AndroidBuildSystem":"Gradle","BuildTarget":"Android","StereoRenderingPath":"MultiPass"},"Hardware":{"OperatingSystem":"","DeviceModel":"","DeviceName":"","ProcessorType":"","ProcessorCount":0,"GraphicsDeviceName":"","SystemMemorySizeMB":0},"Editor":{"Version":"6000.3.9f1","Branch":"6000.3/staging","Changeset":"7a9955a4f2fa","Date":1770746648},"Dependencies":["com.cysharp.unitask@2.5.10","com.github-glitchenzo.nugetforunity@4.5.0","com.unity.2d.sprite@1.0.0","com.unity.ai.navigation@2.0.11","com.unity.cinemachine@3.1.6","com.unity.collab-proxy@2.11.4","com.unity.ide.rider@3.0.39","com.unity.ide.visualstudio@2.0.26","com.unity.inputsystem@1.19.0","com.unity.multiplayer.center@1.0.1","com.unity.nuget.newtonsoft-json@3.2.2","com.unity.postprocessing@3.5.4","com.unity.recorder@5.1.6","com.unity.render-pipelines.universal@17.3.0","com.unity.shadergraph@17.3.0","com.unity.test-framework@1.6.0","com.unity.timeline@1.8.11","com.unity.ugui@2.0.0","com.unity.visualeffectgraph@17.3.0","com.unity.visualscripting@1.9.11","media.lightside.unitext@1.0.0","com.unity.modules.accessibility@1.0.0","com.unity.modules.adaptiveperformance@1.0.0","com.unity.modules.ai@1.0.0","com.unity.modules.androidjni@1.0.0","com.unity.modules.animation@1.0.0","com.unity.modules.assetbundle@1.0.0","com.unity.modules.audio@1.0.0","com.unity.modules.cloth@1.0.0","com.unity.modules.director@1.0.0","com.unity.modules.imageconversion@1.0.0","com.unity.modules.imgui@1.0.0","com.unity.modules.jsonserialize@1.0.0","com.unity.modules.particlesystem@1.0.0","com.unity.modules.physics@1.0.0","com.unity.modules.physics2d@1.0.0","com.unity.modules.screencapture@1.0.0","com.unity.modules.terrain@1.0.0","com.unity.modules.terrainphysics@1.0.0","com.unity.modules.tilemap@1.0.0","com.unity.modules.ui@1.0.0","com.unity.modules.uielements@1.0.0","com.unity.modules.umbra@1.0.0","com.unity.modules.unityanalytics@1.0.0","com.unity.modules.unitywebrequest@1.0.0","com.unity.modules.unitywebrequestassetbundle@1.0.0","com.unity.modules.unitywebrequestaudio@1.0.0","com.unity.modules.unitywebrequesttexture@1.0.0","com.unity.modules.unitywebrequestwww@1.0.0","com.unity.modules.vectorgraphics@1.0.0","com.unity.modules.vehicles@1.0.0","com.unity.modules.video@1.0.0","com.unity.modules.vr@1.0.0","com.unity.modules.wind@1.0.0","com.unity.modules.xr@1.0.0","com.unity.modules.subsystems@1.0.0","com.unity.modules.hierarchycore@1.0.0","com.unity.render-pipelines.core@17.3.0","com.unity.ext.nunit@2.0.5","com.unity.searcher@4.9.4","com.unity.render-pipelines.universal-config@17.0.3","com.unity.collections@2.6.2","com.unity.bindings.openimageio@1.0.2","com.unity.splines@2.8.2","com.unity.burst@1.8.28","com.unity.mathematics@1.3.3","com.unity.nuget.mono-cecil@1.11.6","com.unity.test-framework.performance@3.2.0","com.unity.settings-manager@2.1.1"],"Results":[]}
\ No newline at end of file
{"MeasurementCount":-1}
\ No newline at end of file
using System.Collections; using System.Collections;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityEngine.Events; using UnityEngine.Events;
...@@ -134,36 +135,70 @@ namespace com.al_arcade.mcq ...@@ -134,36 +135,70 @@ namespace com.al_arcade.mcq
} }
// Combined dashes into a single mesh (100 objects → 1)
{
var tempCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
var cubeMesh = tempCube.GetComponent<MeshFilter>().sharedMesh;
Destroy(tempCube);
var combines = new List<CombineInstance>();
for (float z = 0; z < 400; z += 4) for (float z = 0; z < 400; z += 4)
{ {
var dash = GameObject.CreatePrimitive(PrimitiveType.Cube); combines.Add(new CombineInstance
dash.name = "Dash"; {
dash.transform.position = new Vector3(0, 0.01f, z); mesh = cubeMesh,
dash.transform.localScale = new Vector3(0.05f, 0.01f, 2f); transform = Matrix4x4.TRS(
Destroy(dash.GetComponent<Collider>()); new Vector3(0, 0.01f, z),
dash.GetComponent<Renderer>().material = new Material(shader) Quaternion.identity,
new Vector3(0.05f, 0.01f, 2f))
});
}
var dashObj = new GameObject("RoadDashes");
var mf = dashObj.AddComponent<MeshFilter>();
var mr = dashObj.AddComponent<MeshRenderer>();
var combined = new Mesh();
combined.CombineMeshes(combines.ToArray(), true, true);
mf.mesh = combined;
mr.material = new Material(shader)
{ color = SSColorPalette.WithAlpha(Color.white, 0.2f) }; { color = SSColorPalette.WithAlpha(Color.white, 0.2f) };
} }
// Combined side blocks into a single mesh (40 objects → 1)
{
var tempCube = GameObject.CreatePrimitive(PrimitiveType.Cube);
var cubeMesh = tempCube.GetComponent<MeshFilter>().sharedMesh;
Destroy(tempCube);
var combines = new List<CombineInstance>();
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)
{ {
var block = GameObject.CreatePrimitive(PrimitiveType.Cube);
block.name = "SideBlock";
float h = Random.Range(2f, 5f); float h = Random.Range(2f, 5f);
block.transform.position = new Vector3(side * 12f, h / 2f, z); combines.Add(new CombineInstance
block.transform.localScale = new Vector3( {
Random.Range(2f, 4f), h, Random.Range(3f, 8f)); mesh = cubeMesh,
Destroy(block.GetComponent<Collider>()); transform = Matrix4x4.TRS(
var c = Color.Lerp(SSColorPalette.Primary, new Vector3(side * 12f, h / 2f, z),
SSColorPalette.PrimaryLight, Random.value); Quaternion.identity,
block.GetComponent<Renderer>().material = new Material(shader) new Vector3(Random.Range(2f, 4f), h, Random.Range(3f, 8f)))
{ color = SSColorPalette.WithAlpha(c, 0.4f) }; });
} }
} }
var blocksObj = new GameObject("SideBlocks");
var mf = blocksObj.AddComponent<MeshFilter>();
var mr = blocksObj.AddComponent<MeshRenderer>();
var combined = new Mesh();
combined.CombineMeshes(combines.ToArray(), true, true);
mf.mesh = combined;
var midColor = Color.Lerp(SSColorPalette.Primary, SSColorPalette.PrimaryLight, 0.5f);
mr.material = new Material(shader)
{ color = SSColorPalette.WithAlpha(midColor, 0.4f) };
}
var horizon = GameObject.CreatePrimitive(PrimitiveType.Cube); var horizon = GameObject.CreatePrimitive(PrimitiveType.Cube);
horizon.name = "Horizon"; horizon.name = "Horizon";
......
...@@ -50,6 +50,10 @@ namespace com.al_arcade.mcq ...@@ -50,6 +50,10 @@ namespace com.al_arcade.mcq
private Camera _mainCamera; private Camera _mainCamera;
private bool _isTicking; private bool _isTicking;
// Gate object pool — reuse instead of Instantiate/Destroy per question
private List<McqGateController> _gatePool = new();
private const int GatePoolSize = 6;
// ✅ NEW: CS-style progress counter // ✅ NEW: CS-style progress counter
// goes +1 on correct, -1 on wrong, clamped to [0, pointsToWin] // goes +1 on correct, -1 on wrong, clamped to [0, pointsToWin]
private int _deltaChangeInSize; private int _deltaChangeInSize;
...@@ -176,6 +180,7 @@ namespace com.al_arcade.mcq ...@@ -176,6 +180,7 @@ namespace com.al_arcade.mcq
// ─── BeginGameplay ─────────────────────────────────────────────────── // ─── BeginGameplay ───────────────────────────────────────────────────
protected override void BeginGameplay() protected override void BeginGameplay()
{ {
InitGatePool();
_currentIndex = _score = _streak = _correctCount = _wrongCount = 0; _currentIndex = _score = _streak = _correctCount = _wrongCount = 0;
_bestStreak = 0; _bestStreak = 0;
...@@ -327,7 +332,7 @@ namespace com.al_arcade.mcq ...@@ -327,7 +332,7 @@ namespace com.al_arcade.mcq
DOTween.Kill(gate.transform); DOTween.Kill(gate.transform);
gate.transform.DOScale(Vector3.zero, 0.3f) gate.transform.DOScale(Vector3.zero, 0.3f)
.SetEase(Ease.InBack) .SetEase(Ease.InBack)
.OnComplete(() => { if (gate != null) Destroy(gate.gameObject); }); .OnComplete(() => { if (gate != null) gate.Recycle(); });
} }
} }
_activeGates.Clear(); _activeGates.Clear();
...@@ -455,12 +460,82 @@ namespace com.al_arcade.mcq ...@@ -455,12 +460,82 @@ namespace com.al_arcade.mcq
if (g != null) if (g != null)
{ {
DOTween.Kill(g.transform); DOTween.Kill(g.transform);
Destroy(g.gameObject); g.Recycle();
} }
} }
_activeGates.Clear(); _activeGates.Clear();
} }
// ─── Gate Pool ──────────────────────────────────────────────────────
private void InitGatePool()
{
if (_gatePool.Count > 0) return;
GameObject prefab = prefabBuilder != null ? prefabBuilder.GetGatePrefab() : null;
for (int i = 0; i < GatePoolSize; i++)
{
GameObject go;
McqGateController gate;
if (prefab == null)
{
go = new GameObject($"Gate_Pool_{i}");
gate = go.AddComponent<McqGateController>();
}
else
{
go = Instantiate(prefab);
gate = go.GetComponent<McqGateController>();
}
if (gateParent != null) go.transform.SetParent(gateParent);
go.SetActive(false);
_gatePool.Add(gate);
}
}
private McqGateController GetGateFromPool(Vector3 position, int index,
string answerText, bool isCorrect)
{
McqGateController gate = null;
for (int i = 0; i < _gatePool.Count; i++)
{
if (!_gatePool[i].gameObject.activeSelf)
{
gate = _gatePool[i];
break;
}
}
// Pool exhausted — expand
if (gate == null)
{
GameObject prefab = prefabBuilder != null ? prefabBuilder.GetGatePrefab() : null;
GameObject go;
if (prefab == null)
{
go = new GameObject($"Gate_Pool_{_gatePool.Count}");
gate = go.AddComponent<McqGateController>();
}
else
{
go = Instantiate(prefab);
gate = go.GetComponent<McqGateController>();
}
if (gateParent != null) go.transform.SetParent(gateParent);
_gatePool.Add(gate);
}
gate.transform.position = position;
gate.gameObject.SetActive(true);
gate.Setup(index, answerText, isCorrect);
return gate;
}
// ─── Gate Spawning ─────────────────────────────────────────────────── // ─── Gate Spawning ───────────────────────────────────────────────────
private void SpawnGates(McqQuestion question) private void SpawnGates(McqQuestion question)
{ {
...@@ -481,7 +556,7 @@ namespace com.al_arcade.mcq ...@@ -481,7 +556,7 @@ namespace com.al_arcade.mcq
for (int i = 0; i < answers.Length; i++) for (int i = 0; i < answers.Length; i++)
{ {
Vector3 gatePos = basePos + Vector3.right * (startX + i * gateSpacing); Vector3 gatePos = basePos + Vector3.right * (startX + i * gateSpacing);
var gate = CreateGate(gatePos, i, answers[i], i == _correctGateIndex); var gate = GetGateFromPool(gatePos, i, answers[i], i == _correctGateIndex);
_activeGates.Add(gate); _activeGates.Add(gate);
gate.transform.DOScale(Vector3.one, 0.5f) gate.transform.DOScale(Vector3.one, 0.5f)
...@@ -491,34 +566,6 @@ namespace com.al_arcade.mcq ...@@ -491,34 +566,6 @@ namespace com.al_arcade.mcq
} }
} }
private McqGateController CreateGate(Vector3 position, int index,
string answerText, bool isCorrect)
{
GameObject gatePrefab = prefabBuilder != null ? prefabBuilder.GetGatePrefab() : null;
GameObject go;
if (gatePrefab == null)
{
go = new GameObject($"Gate_{index}");
go.transform.position = position;
}
else
{
go = Instantiate(gatePrefab, position, Quaternion.identity);
}
if (gateParent != null) go.transform.SetParent(gateParent);
McqGateController gate;
if (gatePrefab == null)
gate = go.AddComponent<McqGateController>();
else
gate = go.GetComponent<McqGateController>();
gate.Setup(index, answerText, isCorrect);
return gate;
}
// ─── Feedback ──────────────────────────────────────────────────────── // ─── Feedback ────────────────────────────────────────────────────────
private void ShowCorrectFeedback(int points) private void ShowCorrectFeedback(int points)
{ {
......
...@@ -24,6 +24,7 @@ namespace com.al_arcade.mcq ...@@ -24,6 +24,7 @@ namespace com.al_arcade.mcq
private MaterialPropertyBlock _mpb; private MaterialPropertyBlock _mpb;
private Sequence _idleAnim; private Sequence _idleAnim;
private Sequence _glowAnim; private Sequence _glowAnim;
private bool _built;
private const float GateWidth = 2.5f; private const float GateWidth = 2.5f;
private const float GateHeight = 3.5f; private const float GateHeight = 3.5f;
...@@ -48,12 +49,33 @@ namespace com.al_arcade.mcq ...@@ -48,12 +49,33 @@ namespace com.al_arcade.mcq
IsCorrect = correct; IsCorrect = correct;
WasEntered = false; WasEntered = false;
EnsureMaterial();
if (_built)
UpdateContent();
else
{
BuildVisuals(); BuildVisuals();
_built = true;
}
if (_mpb != null)
SetPanelColor(SSColorPalette.GateDefault);
StartIdleAnimation(); StartIdleAnimation();
} }
public void Start() public void Start()
{ {
EnsureMaterial();
if (_mpb != null)
SetPanelColor(SSColorPalette.GateDefault);
}
private void EnsureMaterial()
{
if (_panelRenderer == null || _mpb != null) return;
if (_sharedGateMaterial == null) if (_sharedGateMaterial == null)
{ {
_sharedGateMaterial = new Material(_panelRenderer.material.shader); _sharedGateMaterial = new Material(_panelRenderer.material.shader);
...@@ -61,9 +83,7 @@ namespace com.al_arcade.mcq ...@@ -61,9 +83,7 @@ namespace com.al_arcade.mcq
} }
_panelRenderer.sharedMaterial = _sharedGateMaterial; _panelRenderer.sharedMaterial = _sharedGateMaterial;
_panelMaterial = _sharedGateMaterial; _panelMaterial = _sharedGateMaterial;
_mpb = new MaterialPropertyBlock(); _mpb = new MaterialPropertyBlock();
SetPanelColor(SSColorPalette.GateDefault);
} }
private void SetPanelColor(Color color) private void SetPanelColor(Color color)
...@@ -73,6 +93,30 @@ namespace com.al_arcade.mcq ...@@ -73,6 +93,30 @@ namespace com.al_arcade.mcq
_panelRenderer.SetPropertyBlock(_mpb); _panelRenderer.SetPropertyBlock(_mpb);
} }
private void UpdateContent()
{
if (_answerText != null)
{
_answerText.Text = AnswerText;
_answerTextShadow.Text = AnswerText;
}
else if (_label != null)
{
_label.arabicText = AnswerText;
}
}
public void Recycle()
{
KillAllTweens();
WasEntered = false;
onPlayerEnter = null;
transform.localScale = Vector3.one;
if (_mpb != null)
SetPanelColor(SSColorPalette.GateDefault);
gameObject.SetActive(false);
}
private void BuildVisuals() private void BuildVisuals()
{ {
var shader = GetShader(); var shader = GetShader();
......
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