Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
SSBookMinigames
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Administrator
SSBookMinigames
Commits
0ed084a6
Commit
0ed084a6
authored
Apr 02, 2026
by
mohamed20047
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
complete new mcq game
parent
90197342
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
4123 additions
and
5 deletions
+4123
-5
New MSQ.unity
My project/Assets/Scenes/MCQ/New MSQ.unity
+3126
-0
New MSQ.unity.meta
My project/Assets/Scenes/MCQ/New MSQ.unity.meta
+7
-0
New.meta
My project/Assets/ScienceStreet/MCQ/Scripts/New.meta
+8
-0
AnswerButtonUI.cs
...ct/Assets/ScienceStreet/MCQ/Scripts/New/AnswerButtonUI.cs
+189
-0
AnswerButtonUI.cs.meta
...sets/ScienceStreet/MCQ/Scripts/New/AnswerButtonUI.cs.meta
+2
-0
NewMCQGameManger.cs
.../Assets/ScienceStreet/MCQ/Scripts/New/NewMCQGameManger.cs
+216
-0
NewMCQGameManger.cs.meta
...ts/ScienceStreet/MCQ/Scripts/New/NewMCQGameManger.cs.meta
+2
-0
NewMCQUIManager.cs
...t/Assets/ScienceStreet/MCQ/Scripts/New/NewMCQUIManager.cs
+266
-0
NewMCQUIManager.cs.meta
...ets/ScienceStreet/MCQ/Scripts/New/NewMCQUIManager.cs.meta
+2
-0
QuestionUi.cs
...roject/Assets/ScienceStreet/MCQ/Scripts/New/QuestionUi.cs
+206
-0
QuestionUi.cs.meta
...t/Assets/ScienceStreet/MCQ/Scripts/New/QuestionUi.cs.meta
+2
-0
SSDataModels.cs
...oject/Assets/ScienceStreet/Shared/Scripts/SSDataModels.cs
+97
-5
No files found.
My project/Assets/Scenes/MCQ/New MSQ.unity
0 → 100644
View file @
0ed084a6
This source diff could not be displayed because it is too large. You can
view the blob
instead.
My project/Assets/Scenes/MCQ/New MSQ.unity.meta
0 → 100644
View file @
0ed084a6
fileFormatVersion: 2
guid: dc77d4f7977b6514e96035771e5e547f
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
My project/Assets/ScienceStreet/MCQ/Scripts/New.meta
0 → 100644
View file @
0ed084a6
fileFormatVersion: 2
guid: 20ba488acbcf34d438c13bfb24840820
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
My project/Assets/ScienceStreet/MCQ/Scripts/New/AnswerButtonUI.cs
0 → 100644
View file @
0ed084a6
using
UnityEngine
;
using
UnityEngine.UI
;
using
TMPro
;
using
System
;
using
LightSide
;
public
class
AnswerButtonUI
:
MonoBehaviour
{
[
SerializeField
]
private
Button
button
;
[
SerializeField
]
private
Image
answerImageComponent
;
[
SerializeField
]
private
UniText
answerTextComponent
;
[
SerializeField
]
private
LayoutGroup
answerLayout
;
[
SerializeField
]
private
Image
buttonBackgroundImage
;
[
SerializeField
]
private
Color
normalColor
=
Color
.
white
;
[
SerializeField
]
private
Color
normalTextColor
=
Color
.
black
;
[
SerializeField
]
private
Color
selectedCorrectColor
=
Color
.
green
;
[
SerializeField
]
private
Color
selectedIncorrectColor
=
Color
.
red
;
[
SerializeField
]
private
Color
correctAnswerColor
=
new
Color
(
0.2f
,
0.8f
,
0.2f
,
1f
);
// Light green
[
SerializeField
]
private
Color
disabledColor
=
new
Color
(
0.7f
,
0.7f
,
0.7f
,
1f
);
// Gray
private
int
answerIndex
;
private
Action
<
int
>
onClicked
;
private
bool
isInteractable
=
true
;
private
void
OnEnable
()
{
if
(
button
!=
null
)
{
button
.
onClick
.
RemoveAllListeners
();
}
}
/// <summary>
/// Setup answer button with text and optional image
/// </summary>
public
void
Setup
(
string
answerText
,
Sprite
answerImage
,
int
index
,
Action
<
int
>
callback
)
{
button
.
Select
();
answerIndex
=
index
;
onClicked
=
callback
;
isInteractable
=
true
;
// Setup text
if
(
answerTextComponent
!=
null
)
{
answerTextComponent
.
Text
=
answerText
;
answerTextComponent
.
color
=
normalTextColor
;
}
// Setup image
if
(
answerImageComponent
!=
null
)
{
if
(
answerImage
!=
null
)
{
answerImageComponent
.
sprite
=
answerImage
;
answerImageComponent
.
gameObject
.
SetActive
(
true
);
}
else
{
answerImageComponent
.
gameObject
.
SetActive
(
false
);
}
}
// Setup button
if
(
button
!=
null
)
{
button
.
onClick
.
RemoveAllListeners
();
button
.
onClick
.
AddListener
(()
=>
HandleClick
());
button
.
interactable
=
true
;
}
// Reset background color
if
(
buttonBackgroundImage
!=
null
)
{
buttonBackgroundImage
.
color
=
normalColor
;
}
// Rebuild layout
if
(
answerLayout
!=
null
)
{
LayoutRebuilder
.
ForceRebuildLayoutImmediate
(
answerLayout
.
transform
as
RectTransform
);
}
}
/// <summary>
/// Handle button click
/// </summary>
private
void
HandleClick
()
{
if
(!
isInteractable
||
button
==
null
||
!
button
.
interactable
)
return
;
onClicked
?.
Invoke
(
answerIndex
);
}
/// <summary>
/// Mark this answer as selected and correct
/// </summary>
public
void
SetSelected
(
bool
isCorrect
)
{
isInteractable
=
false
;
if
(
buttonBackgroundImage
!=
null
)
{
buttonBackgroundImage
.
color
=
isCorrect
?
selectedCorrectColor
:
selectedIncorrectColor
;
}
if
(
button
!=
null
)
{
button
.
interactable
=
false
;
}
}
/// <summary>
/// Show this as the correct answer (when user selected wrong)
/// </summary>
public
void
SetCorrect
()
{
isInteractable
=
false
;
if
(
buttonBackgroundImage
!=
null
)
{
buttonBackgroundImage
.
color
=
correctAnswerColor
;
}
if
(
button
!=
null
)
{
button
.
interactable
=
false
;
}
}
/// <summary>
/// Disable this button without highlighting
/// </summary>
public
void
SetDisabled
()
{
isInteractable
=
false
;
if
(
buttonBackgroundImage
!=
null
)
{
buttonBackgroundImage
.
color
=
disabledColor
;
}
if
(
button
!=
null
)
{
button
.
interactable
=
false
;
}
}
/// <summary>
/// Reset button to normal state
/// </summary>
public
void
Reset
()
{
isInteractable
=
true
;
if
(
buttonBackgroundImage
!=
null
)
{
buttonBackgroundImage
.
color
=
normalColor
;
}
if
(
answerTextComponent
!=
null
)
{
answerTextComponent
.
color
=
normalTextColor
;
}
if
(
button
!=
null
)
{
button
.
interactable
=
true
;
}
}
/// <summary>
/// Get answer index
/// </summary>
public
int
GetAnswerIndex
()
{
return
answerIndex
;
}
/// <summary>
/// Check if button is interactable
/// </summary>
public
bool
IsInteractable
()
{
return
isInteractable
;
}
}
\ No newline at end of file
My project/Assets/ScienceStreet/MCQ/Scripts/New/AnswerButtonUI.cs.meta
0 → 100644
View file @
0ed084a6
fileFormatVersion: 2
guid: cb8a50d45c5491e45a8c75803f8df92c
\ No newline at end of file
My project/Assets/ScienceStreet/MCQ/Scripts/New/NewMCQGameManger.cs
0 → 100644
View file @
0ed084a6
using
UnityEngine
;
using
System.Collections
;
using
System.Collections.Generic
;
using
UnityEngine.Events
;
using
DG.Tweening
;
using
com.al_arcade.shared
;
using
com.al_arcade.mcq
;
namespace
com.al_arcade.mcq
{
public
class
NewMCQGameManger
:
MonoBehaviour
{
[
SerializeField
]
private
int
totalLives
=
5
;
[
SerializeField
]
private
float
questionTime
=
5
;
[
SerializeField
]
private
float
streakBonusThreshold
=
3
;
[
Header
(
"Audio — SFX Clips"
)]
[
SerializeField
]
private
AudioClip
sfxCorrect
;
[
SerializeField
]
private
AudioClip
sfxWrong
;
[
SerializeField
]
private
AudioClip
sfxClick
;
[
SerializeField
]
private
AudioClip
sfxVictory
;
[
SerializeField
]
private
AudioClip
sfxDefeat
;
[
SerializeField
]
private
AudioClip
sfxWhoosh
;
[
SerializeField
]
private
AudioClip
sfxPop
;
[
SerializeField
]
private
AudioClip
sfxCheer
;
[
SerializeField
]
private
AudioClip
sfxCountdown
;
private
McqQuestion
[]
_questions
;
private
McqQuestion
currentQuestion
;
private
int
currentQuestionIndex
;
private
int
_score
,
_streak
,
_bestStreak
,
_lives
;
private
int
_correctCount
,
_wrongCount
;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void
Start
()
{
SetupAudioManager
();
_lives
=
totalLives
;
if
(
NewMCQUIManager
.
Instance
!=
null
)
{
NewMCQUIManager
.
Instance
.
SetLives
(
totalLives
,
totalLives
);
NewMCQUIManager
.
Instance
.
SetScore
(
0
);
NewMCQUIManager
.
Instance
.
ShowGameUI
();
}
StartCoroutine
(
StartGame
());
}
private
IEnumerator
StartGame
()
{
NewMCQUIManager
.
Instance
.
ShowLoading
(
"جاري تحميل الأسئلة..."
);
var
session
=
SSGameSession
.
EnsureInstance
();
var
api
=
SSApiManager
.
EnsureInstance
();
string
error
=
null
;
yield
return
api
.
FetchMcq
(
session
.
buildType
,
session
.
classCode
,
session
.
questionCount
,
session
.
gradeId
,
qs
=>
_questions
=
qs
,
err
=>
error
=
err
);
if
(
error
!=
null
||
_questions
==
null
||
_questions
.
Length
==
0
)
{
NewMCQUIManager
.
Instance
.
ShowError
(
error
??
"لا توجد أسئلة متاحة"
);
yield
break
;
}
NewMCQUIManager
.
Instance
.
HideLoading
();
ShowNextQuestion
();
}
private
void
ShowNextQuestion
()
{
NewMCQUIManager
.
Instance
.
SetProgress
(
currentQuestionIndex
,
_questions
.
Length
);
if
(
currentQuestionIndex
>
_questions
.
Length
)
{
StartCoroutine
(
VictorySequence
());
return
;
}
McqQuestion
question
=
_questions
[
currentQuestionIndex
];
QuestionUi
.
Instance
.
DisplayQuestion
(
question
,
OnAnswerSubmitted
);
}
private
void
OnAnswerSubmitted
(
int
selectedIndex
,
bool
isCorrect
)
{
Debug
.
Log
(
$"Answer Index:
{
selectedIndex
}
, Correct:
{
isCorrect
}
"
);
if
(
isCorrect
)
{
_correctCount
++;
_streak
++;
if
(
_bestStreak
<
_streak
)
{
_bestStreak
=
_streak
;
}
int
points
=
100
;
if
(
_streak
>=
streakBonusThreshold
)
points
+=
(
_streak
-
(
int
)
streakBonusThreshold
+
1
)
*
25
;
_score
+=
points
;
ShowCorrectFeedback
(
points
);
var
audio
=
SSAudioManager
.
Instance
;
if
(
audio
!=
null
)
{
if
(
audio
.
sfxCorrect
!=
null
)
audio
.
PlayCorrect
();
else
audio
.
PlayCorrectBeep
();
}
}
else
{
_streak
=
0
;
_wrongCount
++;
_lives
--;
ShowWrongFeedback
();
NewMCQUIManager
.
Instance
.
SetScore
(
_score
);
NewMCQUIManager
.
Instance
.
SetLives
(
_lives
,
totalLives
);
if
(
_lives
<=
0
)
{
StartCoroutine
(
GameOverSequence
());
return
;
}
var
audio
=
SSAudioManager
.
Instance
;
if
(
audio
!=
null
)
{
if
(
audio
.
sfxWrong
!=
null
)
audio
.
PlayWrong
();
else
audio
.
PlayWrongBeep
();
}
}
Invoke
(
nameof
(
GoToNextQuestion
),
1f
);
}
private
void
GoToNextQuestion
()
{
currentQuestionIndex
++;
QuestionUi
.
Instance
.
ResetForNextQuestion
();
ShowNextQuestion
();
}
private
IEnumerator
VictorySequence
()
{
var
audio
=
SSAudioManager
.
Instance
;
if
(
audio
!=
null
)
{
if
(
audio
.
sfxVictory
!=
null
)
audio
.
PlayVictory
();
else
audio
.
PlaySuccessJingle
();
}
var
particles
=
SSParticleManager
.
Instance
;
if
(
particles
!=
null
)
particles
.
PlayScreenConfetti
();
yield
return
new
WaitForSeconds
(
0.5f
);
NewMCQUIManager
.
Instance
.
ShowResults
(
_score
,
_correctCount
,
_wrongCount
,
_bestStreak
,
_questions
.
Length
,
true
);
}
private
IEnumerator
GameOverSequence
()
{
var
audio
=
SSAudioManager
.
Instance
;
if
(
audio
!=
null
)
{
if
(
audio
.
sfxDefeat
!=
null
)
audio
.
PlayDefeat
();
else
audio
.
PlayFailBuzz
();
}
yield
return
new
WaitForSeconds
(
0.5f
);
NewMCQUIManager
.
Instance
.
ShowResults
(
_score
,
_correctCount
,
_wrongCount
,
_bestStreak
,
_questions
.
Length
,
false
);
}
private
void
ShowCorrectFeedback
(
int
points
)
{
if
(
NewMCQUIManager
.
Instance
!=
null
)
{
string
msg
=
_streak
>=
streakBonusThreshold
?
$"ممتاز! +
{
points
}
(سلسلة
{
_streak
}
×)"
:
$"صحيح! +
{
points
}
"
;
NewMCQUIManager
.
Instance
.
ShowFeedback
(
msg
,
true
);
}
}
private
void
ShowWrongFeedback
()
{
if
(
NewMCQUIManager
.
Instance
!=
null
)
NewMCQUIManager
.
Instance
.
ShowFeedback
(
"خطأ!"
,
false
);
if
(
Camera
.
main
!=
null
)
{
DOTween
.
Kill
(
Camera
.
main
.
transform
,
"camShake"
);
Camera
.
main
.
transform
.
DOShakePosition
(
0.4f
,
0.3f
,
15
,
90f
,
false
,
true
)
.
SetEase
(
Ease
.
OutQuad
).
SetId
(
"camShake"
);
}
}
private
void
SetupAudioManager
()
{
var
a
=
SSAudioManager
.
EnsureInstance
();
if
(
sfxCorrect
!=
null
)
a
.
sfxCorrect
=
sfxCorrect
;
if
(
sfxWrong
!=
null
)
a
.
sfxWrong
=
sfxWrong
;
if
(
sfxClick
!=
null
)
a
.
sfxClick
=
sfxClick
;
if
(
sfxVictory
!=
null
)
a
.
sfxVictory
=
sfxVictory
;
if
(
sfxDefeat
!=
null
)
a
.
sfxDefeat
=
sfxDefeat
;
if
(
sfxWhoosh
!=
null
)
a
.
sfxWhoosh
=
sfxWhoosh
;
if
(
sfxPop
!=
null
)
a
.
sfxPop
=
sfxPop
;
if
(
sfxCheer
!=
null
)
a
.
sfxCheer
=
sfxCheer
;
if
(
sfxCountdown
!=
null
)
a
.
sfxCountdown
=
sfxCountdown
;
}
}
}
My project/Assets/ScienceStreet/MCQ/Scripts/New/NewMCQGameManger.cs.meta
0 → 100644
View file @
0ed084a6
fileFormatVersion: 2
guid: 8b4109c5dc3e965418f041d490b09d96
\ No newline at end of file
My project/Assets/ScienceStreet/MCQ/Scripts/New/NewMCQUIManager.cs
0 → 100644
View file @
0ed084a6
using
UnityEngine
;
using
UnityEngine.UI
;
using
UnityEngine.Events
;
// using ALArcade.ArabicTMP;
namespace
com.al_arcade.mcq
{
using
DG.Tweening
;
using
LightSide
;
using
shared
;
using
UnityEngine.SceneManagement
;
public
class
NewMCQUIManager
:
MonoBehaviour
{
public
static
NewMCQUIManager
Instance
;
[
Header
(
"CanvasGroups"
)]
[
SerializeField
]
private
CanvasGroup
_gameUI
;
[
SerializeField
]
private
CanvasGroup
_loadingUI
;
[
SerializeField
]
private
CanvasGroup
_errorUI
;
[
SerializeField
]
private
CanvasGroup
_resultsUI
;
[
SerializeField
]
private
CanvasGroup
_feedbackUI
;
[
Header
(
"Game UI"
)]
[
SerializeField
]
private
UniText
_scoreText
;
[
SerializeField
]
private
UniText
_streakText
;
[
SerializeField
]
private
UniText
_progressText
;
[
SerializeField
]
private
UniText
_loadingText
;
[
SerializeField
]
private
UniText
_errorText
;
[
SerializeField
]
private
UniText
_feedbackText
;
[
SerializeField
]
private
Image
_feedbackBg
;
[
SerializeField
]
private
Image
[]
_heartIcons
;
[
Header
(
"Results UI"
)]
[
SerializeField
]
private
UniText
_resultTitle
;
[
SerializeField
]
private
UniText
_resultScore
;
[
SerializeField
]
private
UniText
_resultCorrect
;
[
SerializeField
]
private
UniText
_resultWrong
;
[
SerializeField
]
private
UniText
_resultStreak
;
[
SerializeField
]
private
Button
_resultRestartBtn
;
[
SerializeField
]
private
Slider
_progressSlider
;
[
SerializeField
]
private
GameObject
_winIcon
;
[
SerializeField
]
private
GameObject
_loseIcon
;
private
Image
_progressFill
;
[
Header
(
"Events"
)]
public
UnityEvent
onRestartClicked
;
private
void
Awake
()
{
if
(
_gameUI
!=
null
)
{
_gameUI
.
alpha
=
0
;
_gameUI
.
gameObject
.
SetActive
(
false
);
}
if
(
_loadingUI
!=
null
)
{
_loadingUI
.
alpha
=
0
;
_loadingUI
.
gameObject
.
SetActive
(
false
);
}
if
(
_errorUI
!=
null
)
{
_errorUI
.
alpha
=
0
;
_errorUI
.
gameObject
.
SetActive
(
false
);
}
if
(
_resultsUI
!=
null
)
{
_resultsUI
.
alpha
=
0
;
_resultsUI
.
gameObject
.
SetActive
(
false
);
}
if
(
_feedbackUI
!=
null
)
{
_feedbackUI
.
alpha
=
0
;
_feedbackUI
.
gameObject
.
SetActive
(
true
);
}
if
(
_winIcon
!=
null
)
_winIcon
.
SetActive
(
false
);
if
(
_loseIcon
!=
null
)
_loseIcon
.
SetActive
(
false
);
if
(
Instance
!=
null
)
{
Debug
.
LogError
(
"there is two ui manager"
);
}
else
{
Instance
=
this
;
}
}
private
void
Start
()
{
_resultRestartBtn
.
onClick
.
AddListener
(()
=>
{
SceneManager
.
LoadScene
(
SceneManager
.
GetActiveScene
().
name
);
});
}
public
void
ShowGameUI
()
{
_gameUI
.
gameObject
.
SetActive
(
true
);
_gameUI
.
DOFade
(
1f
,
0.5f
);
}
public
void
SetScore
(
int
score
)
{
if
(
_scoreText
==
null
)
return
;
_scoreText
.
Text
=
score
.
ToString
(
"N0"
);
DOTween
.
Kill
(
_scoreText
.
transform
,
"scorePunch"
);
_scoreText
.
transform
.
DOPunchScale
(
Vector3
.
one
*
0.2f
,
0.3f
,
6
,
0.3f
)
.
SetId
(
"scorePunch"
);
}
public
void
SetStreak
(
int
streak
)
{
if
(
_streakText
==
null
)
return
;
_streakText
.
Text
=
streak
>
1
?
$"🔥 ×
{
streak
}
"
:
""
;
if
(
streak
>
1
)
{
DOTween
.
Kill
(
_streakText
.
transform
,
"streakPop"
);
_streakText
.
transform
.
localScale
=
Vector3
.
one
*
1.3f
;
_streakText
.
transform
.
DOScale
(
Vector3
.
one
,
0.4f
)
.
SetEase
(
Ease
.
OutElastic
).
SetId
(
"streakPop"
);
}
}
public
void
SetLives
(
int
lives
,
int
maxLives
)
{
for
(
int
i
=
0
;
i
<
_heartIcons
.
Length
;
i
++)
{
bool
active
=
i
<
maxLives
;
_heartIcons
[
i
].
gameObject
.
SetActive
(
active
);
if
(
active
)
{
bool
alive
=
i
<
lives
;
_heartIcons
[
i
].
color
=
alive
?
SSColorPalette
.
Danger
:
SSColorPalette
.
WithAlpha
(
SSColorPalette
.
Danger
,
0.2f
);
if
(!
alive
&&
i
==
lives
)
{
DOTween
.
Kill
(
_heartIcons
[
i
].
transform
);
_heartIcons
[
i
].
transform
.
DOPunchScale
(
Vector3
.
one
*
0.5f
,
0.3f
);
}
}
}
}
public
void
SetProgress
(
int
current
,
int
total
)
{
if
(
_progressText
!=
null
)
_progressText
.
Text
=
$"
{
current
}
/
{
total
}
"
;
if
(
_progressFill
!=
null
&&
total
>
0
)
{
float
t
=
(
float
)
current
/
total
;
DOTween
.
Kill
(
_progressFill
.
rectTransform
,
"progFill"
);
_progressFill
.
rectTransform
.
DOAnchorMax
(
new
Vector2
(
t
,
1f
),
0.5f
)
.
SetEase
(
Ease
.
OutQuad
).
SetId
(
"progFill"
);
}
if
(
_progressSlider
!=
null
)
{
DOTween
.
Kill
(
"progSlider"
);
DOVirtual
.
Float
(
_progressSlider
.
value
,
(
float
)
current
,
0.5f
,
score
=>
{
_progressSlider
.
value
=
score
;
}).
SetEase
(
Ease
.
OutQuad
).
SetId
(
"progSlider"
);
}
}
public
void
ShowFeedback
(
string
message
,
bool
isCorrect
)
{
if
(
_feedbackText
!=
null
)
_feedbackText
.
Text
=
message
;
if
(
_feedbackBg
!=
null
)
_feedbackBg
.
color
=
SSColorPalette
.
WithAlpha
(
isCorrect
?
SSColorPalette
.
Success
:
SSColorPalette
.
Danger
,
0.92f
);
DOTween
.
Kill
(
_feedbackUI
);
_feedbackUI
.
alpha
=
0
;
var
bgRect
=
_feedbackBg
?.
rectTransform
;
if
(
bgRect
!=
null
)
{
print
(
"Showing Feedback"
);
bgRect
.
localScale
=
new
Vector3
(
0.5f
,
0f
,
1f
);
var
seq
=
DOTween
.
Sequence
();
seq
.
Append
(
_feedbackUI
.
DOFade
(
1f
,
0.15f
));
seq
.
Join
(
bgRect
.
DOScaleX
(
1f
,
0.25f
).
SetEase
(
Ease
.
OutBack
));
seq
.
Join
(
bgRect
.
DOScaleY
(
1f
,
0.2f
).
SetEase
(
Ease
.
OutBack
).
SetDelay
(
0.05f
));
seq
.
AppendInterval
(
1.2f
);
seq
.
Append
(
_feedbackUI
.
DOFade
(
0f
,
0.3f
));
}
}
public
void
ShowLoading
(
string
msg
)
{
_loadingUI
.
gameObject
.
SetActive
(
true
);
if
(
_loadingText
!=
null
)
_loadingText
.
Text
=
msg
;
_loadingUI
.
DOFade
(
1f
,
0.3f
);
}
public
void
HideLoading
()
{
_loadingUI
.
DOFade
(
0f
,
0.3f
)
.
OnComplete
(()
=>
_loadingUI
.
gameObject
.
SetActive
(
false
));
}
public
void
ShowError
(
string
msg
)
{
_errorUI
.
gameObject
.
SetActive
(
true
);
if
(
_errorText
!=
null
)
_errorText
.
Text
=
msg
;
_errorUI
.
DOFade
(
1f
,
0.3f
);
}
public
void
ShowResults
(
int
score
,
int
correct
,
int
wrong
,
int
bestStreak
,
int
total
,
bool
won
)
{
_resultsUI
.
gameObject
.
SetActive
(
true
);
_resultsUI
.
alpha
=
0
;
if
(
_resultTitle
!=
null
)
_resultTitle
.
Text
=
won
?
"أحسنت!"
:
"حظ أوفر!"
;
if
(
_resultScore
!=
null
)
_resultScore
.
Text
=
score
.
ToString
(
"N0"
);
if
(
_resultCorrect
!=
null
)
_resultCorrect
.
Text
=
$"صحيح:
{
correct
}
"
;
if
(
_resultWrong
!=
null
)
_resultWrong
.
Text
=
$"خطأ:
{
wrong
}
"
;
if
(
_resultStreak
!=
null
)
_resultStreak
.
Text
=
$"أعلى سلسلة:
{
bestStreak
}
"
;
if
(
_winIcon
!=
null
)
_winIcon
.
SetActive
(
won
);
if
(
_loseIcon
!=
null
)
_loseIcon
.
SetActive
(!
won
);
var
seq
=
DOTween
.
Sequence
();
seq
.
Append
(
_resultsUI
.
DOFade
(
1f
,
0.5f
));
if
(
_resultTitle
!=
null
)
{
_resultTitle
.
transform
.
localScale
=
Vector3
.
zero
;
seq
.
Append
(
_resultTitle
.
transform
.
DOScale
(
1f
,
0.5f
).
SetEase
(
Ease
.
OutBack
));
}
if
(
_winIcon
!=
null
)
{
_winIcon
.
transform
.
localScale
=
Vector3
.
zero
;
seq
.
Append
(
_winIcon
.
transform
.
DOScale
(
1f
,
0.5f
).
SetEase
(
Ease
.
OutBack
));
}
if
(
_loseIcon
!=
null
)
{
_loseIcon
.
transform
.
localScale
=
Vector3
.
zero
;
seq
.
Append
(
_loseIcon
.
transform
.
DOScale
(
1f
,
0.5f
).
SetEase
(
Ease
.
OutBack
));
}
if
(
_resultScore
!=
null
)
{
_resultScore
.
transform
.
localScale
=
Vector3
.
zero
;
seq
.
Append
(
_resultScore
.
transform
.
DOScale
(
1f
,
0.4f
).
SetEase
(
Ease
.
OutBack
));
}
}
public
void
HideResults
()
{
_resultsUI
.
DOFade
(
0f
,
0.3f
)
.
OnComplete
(()
=>
_resultsUI
.
gameObject
.
SetActive
(
false
));
}
public
void
RestartButtonFunction
()
{
HideResults
();
onRestartClicked
?.
Invoke
();
McqGameManager
.
Instance
.
ResetGame
();
McqGameManager
.
Instance
.
StartGame
();
}
public
void
ResetUI
()
{
_gameUI
.
gameObject
.
SetActive
(
false
);
_loadingUI
.
gameObject
.
SetActive
(
false
);
_errorUI
.
gameObject
.
SetActive
(
false
);
_resultsUI
.
gameObject
.
SetActive
(
false
);
_feedbackUI
.
alpha
=
0
;
}
}
}
My project/Assets/ScienceStreet/MCQ/Scripts/New/NewMCQUIManager.cs.meta
0 → 100644
View file @
0ed084a6
fileFormatVersion: 2
guid: 168f088242b9231408ef684c9094c66e
\ No newline at end of file
My project/Assets/ScienceStreet/MCQ/Scripts/New/QuestionUi.cs
0 → 100644
View file @
0ed084a6
using
UnityEngine
;
using
UnityEngine.UI
;
using
TMPro
;
using
System
;
using
com.al_arcade.shared
;
using
LightSide
;
public
class
QuestionUi
:
MonoBehaviour
{
public
static
QuestionUi
Instance
;
[
SerializeField
]
private
Image
questionImageComponent
;
[
SerializeField
]
private
UniText
questionTextComponent
;
[
SerializeField
]
private
Transform
answersGrid
;
[
SerializeField
]
private
AnswerButtonUI
[]
answerButtons
=
new
AnswerButtonUI
[
4
];
[
SerializeField
]
private
LayoutGroup
questionContainer
;
[
SerializeField
]
private
LayoutGroup
answersContainer
;
private
McqQuestion
currentQuestion
;
private
int
correctAnswerIndex
=
-
1
;
private
bool
hasAnswered
=
false
;
private
Action
<
int
,
bool
>
onAnswerSelected
;
private
void
Awake
()
{
if
(
Instance
==
null
)
{
Instance
=
this
;
}
else
{
Debug
.
LogError
(
"there is two questionUI"
);
}
}
private
void
OnEnable
()
{
if
(
answerButtons
.
Length
!=
4
)
{
Debug
.
LogError
(
"MultiChoiceQuestionUI: Must have exactly 4 answer buttons!"
);
}
}
/// <summary>
/// Display a multiple choice question with all its data
/// </summary>
public
void
DisplayQuestion
(
McqQuestion
question
,
Action
<
int
,
bool
>
onAnswerCallback
=
null
)
{
if
(
question
==
null
)
{
Debug
.
LogError
(
"MultiChoiceQuestionUI: Question is null!"
);
return
;
}
currentQuestion
=
question
;
onAnswerSelected
=
onAnswerCallback
;
hasAnswered
=
false
;
correctAnswerIndex
=
-
1
;
// Reset previous shuffle
question
.
ResetShuffle
();
// Setup question display
SetupQuestion
(
question
);
// Get shuffled answers WITH images
question
.
GetShuffledAnswersWithImages
(
out
string
[]
shuffledAnswers
,
out
Sprite
[]
shuffledImages
,
out
int
correctIndex
);
correctAnswerIndex
=
correctIndex
;
// Setup answer buttons with shuffled data
SetupAnswerButtons
(
shuffledAnswers
,
shuffledImages
);
//RectTransform example = answerButtons[1].GetComponent<RectTransform>();
//(answersContainer as GridLayoutGroup).cellSize = new Vector2(example.rect.width, example.rect.height);
// Rebuild layout hierarchy
LayoutRebuilder
.
ForceRebuildLayoutImmediate
(
questionContainer
.
GetComponent
<
RectTransform
>());
LayoutRebuilder
.
ForceRebuildLayoutImmediate
(
answersContainer
.
GetComponent
<
RectTransform
>());
}
/// <summary>
/// Setup the question text and image
/// </summary>
private
void
SetupQuestion
(
McqQuestion
question
)
{
// Setup question text
questionTextComponent
.
Text
=
question
.
question_text
;
// Setup question image (if exists)
if
(
question
.
questionImage
!=
null
)
{
questionImageComponent
.
sprite
=
question
.
questionImage
;
questionImageComponent
.
gameObject
.
SetActive
(
true
);
}
else
{
questionImageComponent
.
gameObject
.
SetActive
(
false
);
}
}
/// <summary>
/// Setup all 4 answer buttons with text and images
/// </summary>
private
void
SetupAnswerButtons
(
string
[]
answers
,
Sprite
[]
images
)
{
if
(
answers
.
Length
!=
4
||
images
.
Length
!=
4
)
{
Debug
.
LogError
(
"MultiChoiceQuestionUI: Answer arrays must have exactly 4 elements!"
);
return
;
}
for
(
int
i
=
0
;
i
<
4
;
i
++)
{
answerButtons
[
i
].
Setup
(
answerText
:
answers
[
i
],
answerImage
:
images
[
i
],
index
:
i
,
callback
:
OnAnswerClicked
);
}
}
/// <summary>
/// Called when an answer button is clicked
/// </summary>
private
void
OnAnswerClicked
(
int
selectedIndex
)
{
Debug
.
Log
(
"pressed"
);
if
(
hasAnswered
)
return
;
hasAnswered
=
true
;
bool
isCorrect
=
selectedIndex
==
correctAnswerIndex
;
// Show visual feedback
ShowAnswerFeedback
(
selectedIndex
,
isCorrect
);
// Invoke callback
onAnswerSelected
?.
Invoke
(
selectedIndex
,
isCorrect
);
}
/// <summary>
/// Show visual feedback for selected answer
/// </summary>
private
void
ShowAnswerFeedback
(
int
selectedIndex
,
bool
isCorrect
)
{
for
(
int
i
=
0
;
i
<
4
;
i
++)
{
if
(
i
==
selectedIndex
)
{
answerButtons
[
i
].
SetSelected
(
isCorrect
);
}
else
if
(
i
==
correctAnswerIndex
&&
!
isCorrect
)
{
answerButtons
[
i
].
SetCorrect
();
}
else
{
answerButtons
[
i
].
SetDisabled
();
}
}
}
/// <summary>
/// Reset all buttons for next question
/// </summary>
public
void
ResetForNextQuestion
()
{
hasAnswered
=
false
;
correctAnswerIndex
=
-
1
;
foreach
(
var
button
in
answerButtons
)
{
button
.
Reset
();
}
}
/// <summary>
/// Get current question
/// </summary>
public
McqQuestion
GetCurrentQuestion
()
{
return
currentQuestion
;
}
/// <summary>
/// Get the correct answer index for current question
/// </summary>
public
int
GetCorrectAnswerIndex
()
{
return
correctAnswerIndex
;
}
/// <summary>
/// Check if user has answered
/// </summary>
public
bool
HasAnswered
()
{
return
hasAnswered
;
}
}
\ No newline at end of file
My project/Assets/ScienceStreet/MCQ/Scripts/New/QuestionUi.cs.meta
0 → 100644
View file @
0ed084a6
fileFormatVersion: 2
guid: 68aee830395e108478a9643b20de1689
\ No newline at end of file
My project/Assets/ScienceStreet/Shared/Scripts/SSDataModels.cs
View file @
0ed084a6
...
@@ -61,28 +61,120 @@ namespace com.al_arcade.shared
...
@@ -61,28 +61,120 @@ namespace com.al_arcade.shared
{
{
public
string
id
;
public
string
id
;
public
string
question_text
;
public
string
question_text
;
public
Sprite
questionImage
;
public
string
answer1
;
public
string
answer1
;
public
Sprite
answer1Image
;
public
string
answer2
;
public
string
answer2
;
public
Sprite
answer2Image
;
public
string
answer3
;
public
string
answer3
;
public
Sprite
answer3Image
;
public
string
answer4
;
public
string
answer4
;
public
Sprite
answer4Image
;
public
string
source
;
public
string
source
;
public
string
grade_name
;
public
string
grade_name
;
// Private shuffle state (keeps track of current shuffle)
private
int
[]
_shuffleIndices
;
private
int
_cachedCorrectIndex
=
-
1
;
/// <summary>
/// Original method - unchanged for backwards compatibility
/// </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
;
// Generate and cache shuffle indices
for
(
int
i
=
arr
.
Length
-
1
;
i
>
0
;
i
--)
_shuffleIndices
=
new
int
[]
{
0
,
1
,
2
,
3
};
for
(
int
i
=
_shuffleIndices
.
Length
-
1
;
i
>
0
;
i
--)
{
{
int
j
=
UnityEngine
.
Random
.
Range
(
0
,
i
+
1
);
int
j
=
UnityEngine
.
Random
.
Range
(
0
,
i
+
1
);
(
arr
[
i
],
arr
[
j
])
=
(
arr
[
j
],
arr
[
i
]);
(
_shuffleIndices
[
i
],
_shuffleIndices
[
j
])
=
(
_shuffleIndices
[
j
],
_shuffleIndices
[
i
]);
}
// Apply shuffle
string
[]
shuffled
=
new
string
[
4
];
for
(
int
i
=
0
;
i
<
4
;
i
++)
{
shuffled
[
i
]
=
arr
[
_shuffleIndices
[
i
]];
}
correctIndex
=
Array
.
IndexOf
(
shuffled
,
correct
);
_cachedCorrectIndex
=
correctIndex
;
return
shuffled
;
}
/// <summary>
/// NEW: Get shuffled answers WITH images
/// Returns answer text + images in the same shuffled order
/// </summary>
public
void
GetShuffledAnswersWithImages
(
out
string
[]
answers
,
out
Sprite
[]
images
,
out
int
correctIndex
)
{
string
[]
textArr
=
{
answer1
,
answer2
,
answer3
,
answer4
};
Sprite
[]
imageArr
=
{
answer1Image
,
answer2Image
,
answer3Image
,
answer4Image
};
string
correct
=
answer1
;
// Generate shuffle indices if not already done
if
(
_shuffleIndices
==
null
||
_shuffleIndices
.
Length
==
0
)
{
_shuffleIndices
=
new
int
[]
{
0
,
1
,
2
,
3
};
for
(
int
i
=
_shuffleIndices
.
Length
-
1
;
i
>
0
;
i
--)
{
int
j
=
UnityEngine
.
Random
.
Range
(
0
,
i
+
1
);
(
_shuffleIndices
[
i
],
_shuffleIndices
[
j
])
=
(
_shuffleIndices
[
j
],
_shuffleIndices
[
i
]);
}
}
// Apply shuffle to both text and images
string
[]
shuffledText
=
new
string
[
4
];
Sprite
[]
shuffledImages
=
new
Sprite
[
4
];
for
(
int
i
=
0
;
i
<
4
;
i
++)
{
int
originalIndex
=
_shuffleIndices
[
i
];
shuffledText
[
i
]
=
textArr
[
originalIndex
];
shuffledImages
[
i
]
=
imageArr
[
originalIndex
];
}
}
correctIndex
=
Array
.
IndexOf
(
arr
,
correct
);
answers
=
shuffledText
;
return
arr
;
images
=
shuffledImages
;
correctIndex
=
Array
.
IndexOf
(
shuffledText
,
correct
);
_cachedCorrectIndex
=
correctIndex
;
}
/// <summary>
/// HELPER: Get image for a specific answer index (after shuffle)
/// Useful if you only have the index
/// </summary>
public
Sprite
GetAnswerImageByIndex
(
int
shuffledIndex
)
{
if
(
_shuffleIndices
==
null
||
_shuffleIndices
.
Length
==
0
)
return
null
;
int
originalIndex
=
_shuffleIndices
[
shuffledIndex
];
Sprite
[]
allImages
=
{
answer1Image
,
answer2Image
,
answer3Image
,
answer4Image
};
return
allImages
[
originalIndex
];
}
/// <summary>
/// HELPER: Get the cached correct index from last shuffle
/// </summary>
public
int
GetCachedCorrectIndex
()
{
return
_cachedCorrectIndex
;
}
/// <summary>
/// HELPER: Reset shuffle state (call before new question)
/// </summary>
public
void
ResetShuffle
()
{
_shuffleIndices
=
null
;
_cachedCorrectIndex
=
-
1
;
}
}
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment