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
d4ba32ba
Commit
d4ba32ba
authored
Apr 10, 2026
by
Yousef Sameh
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
New QuestionBank Api and schema
parent
ada9ac1c
Changes
36
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
36 changed files
with
1331 additions
and
1720 deletions
+1331
-1720
AppRouter.cs
My project/Assets/App/Infrastructure/Core/AppRouter.cs
+15
-9
UserService.cs
My project/Assets/App/Infrastructure/User/UserService.cs
+119
-17
UserModel.cs
My project/Assets/App/Models/UserModel.cs
+5
-19
HomeController.cs
My project/Assets/App/UI/HomeController.cs
+2
-0
LeaderboardController.cs
My project/Assets/App/UI/LeaderboardController.cs
+1
-0
LoginController.cs
My project/Assets/App/UI/LoginController.cs
+5
-7
ProfileController.cs
My project/Assets/App/UI/ProfileController.cs
+5
-0
Login.uxml
My project/Assets/AppUI/NewAppUI/Login.uxml
+4
-4
Canvas.prefab
My project/Assets/Prefabs/MCQ/Canvas.prefab
+20
-20
TfUI.prefab
My project/Assets/Prefabs/TF/Phone Art/TfUI.prefab
+2
-2
CsDemoBuilder.cs
My project/Assets/ScienceStreet/CS/Scripts/CsDemoBuilder.cs
+4
-105
CsGameManager.cs
My project/Assets/ScienceStreet/CS/Scripts/CsGameManager.cs
+9
-3
CsPrefabBuilder.cs
...roject/Assets/ScienceStreet/CS/Scripts/CsPrefabBuilder.cs
+1
-38
CsSentence.cs
My project/Assets/ScienceStreet/CS/Scripts/CsSentence.cs
+26
-29
McqDemoBuilder.cs
...roject/Assets/ScienceStreet/MCQ/Scripts/McqDemoBuilder.cs
+6
-20
McqGameManager.cs
...roject/Assets/ScienceStreet/MCQ/Scripts/McqGameManager.cs
+13
-6
McqPrefabBuilder.cs
...ject/Assets/ScienceStreet/MCQ/Scripts/McqPrefabBuilder.cs
+1
-9
New.meta
My project/Assets/ScienceStreet/MCQ/Scripts/New.meta
+0
-8
AnswerButtonUI.cs
...ct/Assets/ScienceStreet/MCQ/Scripts/New/AnswerButtonUI.cs
+0
-228
NewMCQGameManger.cs
.../Assets/ScienceStreet/MCQ/Scripts/New/NewMCQGameManger.cs
+0
-223
NewMCQUIManager.cs
...t/Assets/ScienceStreet/MCQ/Scripts/New/NewMCQUIManager.cs
+0
-266
NewMCQUIManager.cs.meta
...ets/ScienceStreet/MCQ/Scripts/New/NewMCQUIManager.cs.meta
+0
-2
QuestionUi.cs
...roject/Assets/ScienceStreet/MCQ/Scripts/New/QuestionUi.cs
+0
-376
AttemptData.cs
...roject/Assets/ScienceStreet/Shared/Scripts/AttemptData.cs
+58
-0
AttemptData.cs.meta
...t/Assets/ScienceStreet/Shared/Scripts/AttemptData.cs.meta
+2
-0
MCQHelper.cs
My project/Assets/ScienceStreet/Shared/Scripts/MCQHelper.cs
+65
-0
MCQHelper.cs.meta
...ect/Assets/ScienceStreet/Shared/Scripts/MCQHelper.cs.meta
+2
-0
QuestionFilter.cs
...ect/Assets/ScienceStreet/Shared/Scripts/QuestionFilter.cs
+110
-0
QuestionFilter.cs.meta
...ssets/ScienceStreet/Shared/Scripts/QuestionFilter.cs.meta
+2
-0
SSApiManager.cs
...oject/Assets/ScienceStreet/Shared/Scripts/SSApiManager.cs
+408
-118
SSDataModels.cs
...oject/Assets/ScienceStreet/Shared/Scripts/SSDataModels.cs
+409
-157
SSGameSession.cs
...ject/Assets/ScienceStreet/Shared/Scripts/SSGameSession.cs
+25
-24
TfDemoBuilder.cs
My project/Assets/ScienceStreet/TF/Scripts/TfDemoBuilder.cs
+1
-16
TfGameManager.cs
My project/Assets/ScienceStreet/TF/Scripts/TfGameManager.cs
+8
-2
TfPrefabBuilder.cs
...roject/Assets/ScienceStreet/TF/Scripts/TfPrefabBuilder.cs
+1
-10
ProjectSettings.asset
My project/ProjectSettings/ProjectSettings.asset
+2
-2
No files found.
My project/Assets/App/Infrastructure/Core/AppRouter.cs
View file @
d4ba32ba
...
...
@@ -38,33 +38,38 @@ public class AppRouter : MonoBehaviour
private
async
UniTask
Boot
()
{
// 1. Init Supabase
// 1. Try cache first — instant
// var cached = UserService.Instance.LoadFromCache();
// 2. Init Supabase
bool
ready
=
await
SupabaseManager
.
Instance
.
Initialize
();
if
(!
ready
)
{
ShowError
(
"Failed to connect to server"
);
// Offline but have cache — go to home
// if (cached != null) { GoToHome(); return; }
ShowError
(
"Failed to connect"
);
return
;
}
// 2. Listen for unexpected sign-outs
SupabaseManager
.
Instance
.
AddAuthStateListener
(
OnAuthStateChanged
);
// 3. Ensure session
var
authResult
=
await
SupabaseAuthentication
.
Instance
.
EnsureSession
();
if
(
authResult
.
IsT1
)
{
// if (cached != null) { GoToHome(); return; }
GoToLogin
();
return
;
}
// 4.
Try load profile
// 4.
Refresh from network
var
profileResult
=
await
UserService
.
Instance
.
GetCurrentUser
();
profileResult
.
Switch
(
success
=>
GoToHome
(),
error
=>
GoToLogin
()
error
=>
{
// if (cached != null) GoToHome(); // stale cache, still usable
GoToLogin
();
}
);
_booted
=
true
;
}
// ─── Auth State Listener (Safety Net Only) ───────────────────────
...
...
@@ -101,6 +106,7 @@ public class AppRouter : MonoBehaviour
{
if
(
SceneManager
.
GetActiveScene
().
name
!=
"MainMenu"
)
await
SceneManager
.
LoadSceneAsync
(
"MainMenu"
);
UserService
.
Instance
.
StartListening
();
HideSplash
();
}
...
...
My project/Assets/App/Infrastructure/User/UserService.cs
View file @
d4ba32ba
using
System
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Cysharp.Threading.Tasks
;
using
Newtonsoft.Json
;
using
OneOf
;
using
Supabase.Realtime
;
using
Supabase.Realtime.PostgresChanges
;
using
UnityEngine
;
public
class
UserService
...
...
@@ -8,11 +13,98 @@ public class UserService
private
static
UserService
_instance
;
public
static
UserService
Instance
=>
_instance
??=
new
UserService
();
private
const
string
CACHE_KEY
=
"cached_user_profile"
;
public
User
CurrentUser
{
get
;
private
set
;
}
public
bool
HasProfile
=>
CurrentUser
!=
null
;
public
bool
IsFromCache
{
get
;
private
set
;
}
public
event
Action
<
User
>
OnUserChanged
;
public
event
Action
<
string
,
string
>
OnRankChanged
;
// oldRank, newRank
public
event
Action
<
int
,
int
>
OnPointsChanged
;
// oldPoints, newPoints
private
RealtimeChannel
_channel
;
// ─── Realtime ────────────────────────────────────────────────────
public
async
Task
StartListening
()
{
var
client
=
SupabaseManager
.
Instance
.
Supabase
();
var
authUser
=
client
?.
Auth
.
CurrentUser
;
if
(
authUser
==
null
)
return
;
if
(
_channel
!=
null
)
return
;
private
UserService
()
{
}
await
client
.
Realtime
.
ConnectAsync
();
Debug
.
Log
(
"Current UserID"
+
authUser
.
Id
);
_channel
=
client
.
Realtime
.
Channel
(
"user_profile"
);
_channel
.
Register
(
new
PostgresChangesOptions
(
"public"
,
"users"
,
PostgresChangesOptions
.
ListenType
.
All
,
filter
:
$"id=eq.
{
authUser
.
Id
}
"
));
_channel
.
AddPostgresChangeHandler
(
PostgresChangesOptions
.
ListenType
.
All
,
(
sender
,
change
)
=>
{
// Run on main thread to avoid Unity threading issues
UniTask
.
Post
(()
=>
{
var
newUser
=
change
.
Model
<
User
>();
CurrentUser
=
newUser
;
IsFromCache
=
false
;
OnUserChanged
?.
Invoke
(
CurrentUser
);
SaveToCache
(
CurrentUser
);
});
}
);
await
_channel
.
Subscribe
();
}
public
void
StopListening
()
{
if
(
_channel
==
null
)
return
;
_channel
.
Unsubscribe
();
_channel
=
null
;
}
// ─── Cache ───────────────────────────────────────────────────────
private
void
SaveToCache
(
User
user
)
{
if
(
user
==
null
)
{
PlayerPrefs
.
DeleteKey
(
CACHE_KEY
);
PlayerPrefs
.
Save
();
return
;
}
var
json
=
JsonConvert
.
SerializeObject
(
user
);
PlayerPrefs
.
SetString
(
CACHE_KEY
,
json
);
PlayerPrefs
.
Save
();
}
public
User
LoadFromCache
()
{
var
json
=
PlayerPrefs
.
GetString
(
CACHE_KEY
,
null
);
if
(
string
.
IsNullOrEmpty
(
json
))
return
null
;
try
{
var
user
=
JsonConvert
.
DeserializeObject
<
User
>(
json
);
CurrentUser
=
user
;
IsFromCache
=
true
;
OnUserChanged
?.
Invoke
(
CurrentUser
);
return
user
;
}
catch
{
PlayerPrefs
.
DeleteKey
(
CACHE_KEY
);
return
null
;
}
}
// ─── Network ─────────────────────────────────────────────────────
public
async
UniTask
<
OneOf
<
UserResult
,
ErrorResult
>>
GetCurrentUser
()
{
...
...
@@ -32,7 +124,17 @@ public class UserService
if
(
response
?.
Models
==
null
||
response
.
Models
.
Count
==
0
)
return
new
ErrorResult
(
"Profile not found"
);
var
oldUser
=
CurrentUser
;
CurrentUser
=
response
.
Models
[
0
];
IsFromCache
=
false
;
SaveToCache
(
CurrentUser
);
if
(
oldUser
!=
null
&&
oldUser
.
Rank
!=
CurrentUser
.
Rank
)
OnRankChanged
?.
Invoke
(
oldUser
.
Rank
,
CurrentUser
.
Rank
);
if
(
oldUser
!=
null
&&
oldUser
.
Points
!=
CurrentUser
.
Points
)
OnPointsChanged
?.
Invoke
(
oldUser
.
Points
,
CurrentUser
.
Points
);
OnUserChanged
?.
Invoke
(
CurrentUser
);
return
new
UserResult
(
CurrentUser
);
}
...
...
@@ -45,10 +147,10 @@ public class UserService
public
async
UniTask
<
OneOf
<
UserResult
,
ErrorResult
>>
CreateProfile
(
string
username
,
string
school
=
null
,
string
grade
=
null
,
string
sex
=
null
,
string
age
=
null
)
string
curriculum
=
null
,
string
term
=
null
)
{
try
{
...
...
@@ -59,21 +161,16 @@ public class UserService
var
user
=
new
User
{
Username
=
username
,
School
=
school
,
Grade
=
grade
,
Sex
=
sex
,
Age
=
age
,
Curriculum
=
curriculum
,
Term
=
term
,
Rank
=
"normal"
,
Points
=
0
,
CreatedAt
=
DateTime
.
UtcNow
,
UpdatedAt
=
DateTime
.
UtcNow
Points
=
0
};
await
client
.
From
<
User
>().
Insert
(
user
);
// Re-fetch to get server-set fields (id, timestamps)
var
fetchResult
=
await
GetCurrentUser
();
return
fetchResult
;
return
await
GetCurrentUser
();
}
catch
(
Exception
ex
)
{
...
...
@@ -84,10 +181,10 @@ public class UserService
public
async
UniTask
<
OneOf
<
UserResult
,
ErrorResult
>>
UpdateProfile
(
string
username
=
null
,
string
school
=
null
,
string
grade
=
null
,
string
sex
=
null
,
string
age
=
null
)
string
curriculum
=
null
,
string
term
=
null
)
{
try
{
...
...
@@ -98,17 +195,16 @@ public class UserService
var
update
=
new
User
();
if
(
username
!=
null
)
update
.
Username
=
username
;
if
(
school
!=
null
)
update
.
School
=
school
;
if
(
grade
!=
null
)
update
.
Grade
=
grade
;
if
(
sex
!=
null
)
update
.
Sex
=
sex
;
if
(
age
!=
null
)
update
.
Age
=
age
;
if
(
curriculum
!=
null
)
update
.
Curriculum
=
curriculum
;
if
(
term
!=
null
)
update
.
Term
=
term
;
await
client
.
From
<
User
>()
.
Where
(
x
=>
x
.
Id
==
authUser
.
Id
)
.
Update
(
update
);
// Re-fetch
return
await
GetCurrentUser
();
}
catch
(
Exception
ex
)
...
...
@@ -118,9 +214,15 @@ public class UserService
}
}
// ─── Clear ───────────────────────────────────────────────────────
public
void
ClearUser
()
{
StopListening
();
CurrentUser
=
null
;
IsFromCache
=
false
;
PlayerPrefs
.
DeleteKey
(
CACHE_KEY
);
PlayerPrefs
.
Save
();
OnUserChanged
?.
Invoke
(
null
);
}
}
\ No newline at end of file
My project/Assets/App/Models/UserModel.cs
View file @
d4ba32ba
...
...
@@ -2,50 +2,36 @@ using System;
using
Supabase.Postgrest.Attributes
;
using
Supabase.Postgrest.Models
;
using
Newtonsoft.Json
;
[Table("users")]
public
class
User
:
BaseModel
{
[
PrimaryKey
(
"id"
)]
[
JsonProperty
(
"id"
)]
public
string
Id
{
get
;
set
;
}
[
Column
(
"user_name"
)]
[
JsonProperty
(
"user_name"
)]
public
string
Username
{
get
;
set
;
}
[
Column
(
"rank"
)]
[
JsonProperty
(
"rank"
)]
public
string
Rank
{
get
;
set
;
}
[
Column
(
"points"
)]
[
JsonProperty
(
"points"
)]
public
int
Points
{
get
;
set
;
}
=
0
;
[
Column
(
"created_at"
)]
[
JsonProperty
(
"created_at"
)]
public
DateTime
CreatedAt
{
get
;
set
;
}
[
Column
(
"updated_at"
)]
[
JsonProperty
(
"updated_at"
)]
public
DateTime
UpdatedAt
{
get
;
set
;
}
[
Column
(
"school"
)]
[
JsonProperty
(
"school"
)]
public
string
School
{
get
;
set
;
}
[
Column
(
"grade"
)]
[
JsonProperty
(
"grade"
)]
public
string
Grade
{
get
;
set
;
}
[
Column
(
"sex"
)]
[
JsonProperty
(
"sex"
)]
public
string
Sex
{
get
;
set
;
}
[
Column
(
"age"
)]
[
JsonProperty
(
"age"
)]
public
string
Age
{
get
;
set
;
}
}
[
Column
(
"curriculum"
)]
public
string
Curriculum
{
get
;
set
;
}
[
Column
(
"term"
)]
public
string
Term
{
get
;
set
;
}
}
\ No newline at end of file
My project/Assets/App/UI/HomeController.cs
View file @
d4ba32ba
...
...
@@ -47,6 +47,8 @@ public class HomeController : MonoBehaviour
private
void
OnUserChange
(
User
user
)
{
print
(
$"[HomeController] Updating user info:
{
user
?.
Username
}
, Rank:
{
user
?.
Rank
}
, Points:
{
user
?.
Points
}
"
);
username
.
text
=
user
.
Username
;
xp
.
text
=
user
.
Points
.
ToString
();
rank
.
text
=
AppUtils
.
RankToArabic
(
user
.
Rank
);
...
...
My project/Assets/App/UI/LeaderboardController.cs
View file @
d4ba32ba
...
...
@@ -14,6 +14,7 @@ public class LeaderboardController : MonoBehaviour
var
root
=
leaderboardDocument
.
rootVisualElement
;
leaderboardScrollView
=
root
.
Q
<
ScrollView
>(
"Leaderboard"
);
UserService
.
Instance
.
OnUserChanged
+=
_
=>
LoadLeaderboard
();
LoadLeaderboard
().
Forget
();
}
...
...
My project/Assets/App/UI/LoginController.cs
View file @
d4ba32ba
...
...
@@ -7,9 +7,9 @@ public class LoginController : MonoBehaviour
private
TextField
username
;
private
DropdownField
grade
;
private
DropdownField
school
;
private
DropdownField
sex
;
private
TextField
age
;
private
DropdownField
term
;
private
DropdownField
curriculum
;
private
Button
register
;
...
...
@@ -18,12 +18,10 @@ public class LoginController : MonoBehaviour
var
root
=
uIDocument
.
rootVisualElement
;
username
=
root
.
Q
<
TextField
>(
"Username"
);
username
.
languageDirection
=
LanguageDirection
.
RTL
;
grade
=
root
.
Q
<
DropdownField
>(
"Grade"
);
school
=
root
.
Q
<
DropdownField
>(
"School"
);
sex
=
root
.
Q
<
DropdownField
>(
"Sex"
);
age
=
root
.
Q
<
TextField
>(
"Age"
);
term
=
root
.
Q
<
DropdownField
>(
"Term"
);
curriculum
=
root
.
Q
<
DropdownField
>(
"Curriculum"
);
register
=
root
.
Q
<
Button
>(
"Register"
);
register
.
clicked
+=
RegisterAnon
;
...
...
@@ -44,7 +42,7 @@ public class LoginController : MonoBehaviour
return
;
}
var
signUp
=
await
UserService
.
Instance
.
CreateProfile
(
username
.
text
,
grade
.
value
,
school
.
value
,
sex
.
value
,
age
.
text
);
var
signUp
=
await
UserService
.
Instance
.
CreateProfile
(
username
.
text
,
curriculum
.
value
,
grade
.
value
,
sex
.
value
,
term
.
value
);
signUp
.
Switch
(
user
=>
{
AppRouter
.
GoToHome
();
...
...
My project/Assets/App/UI/ProfileController.cs
View file @
d4ba32ba
...
...
@@ -23,4 +23,9 @@ public class ProfileController : MonoBehaviour
{
name
.
text
=
user
.
Username
;
}
private
void
OnDestroy
()
{
UserService
.
Instance
.
OnUserChanged
-=
OnUserChange
;
}
}
My project/Assets/AppUI/NewAppUI/Login.uxml
View file @
d4ba32ba
...
...
@@ -11,7 +11,7 @@
<ui:Label text="👤" name="TextFieldLabel" language-direction="RTL" class="emoji" style="color: rgb(117, 117, 117); margin-bottom: 20px; font-size: 40px; -unity-text-align: middle-right; margin-left: 5px;"/>
<ui:Label text="اسم المستخدم" name="TextFieldLabel" language-direction="RTL" class="base-text-light" style="color: rgb(117, 117, 117); margin-bottom: 20px; font-size: 35px; -unity-font-definition: url("project://database/Assets/ALArcade/Hakwaty%20Font/TSHakwaty-DemiBold.otf?fileID=12800000&guid=566b773a07b3d064aa1f4c6ef7b6f6fa&type=3#TSHakwaty-DemiBold");"/>
</ui:VisualElement>
<ui:TextField label="" placeholder-text="" name="Username" value="" language-direction="RTL" class="textField" style="flex-direction: row-reverse; color: rgb(0, 0, 0); -unity-font-definition: url("project://database/Assets/UI%20Toolkit/UnityThemes/UnityDefaultRuntimeTheme.tss?fileID=2230732570650464555&guid=901fb73b2529c134f9cf372789759383&type=3#NotInter-Regular"); -unity-text-align: upper-right; -unity-text-generator: advanced; justify-content: space-between; align-content: flex-start;">
<ui:TextField label="" placeholder-text="" name="Username" value="" language-direction="RTL" class="textField" style="flex-direction: row-reverse; color: rgb(0, 0, 0); -unity-font-definition: url("project://database/Assets/UI%20Toolkit/UnityThemes/UnityDefaultRuntimeTheme.tss?fileID=2230732570650464555&guid=901fb73b2529c134f9cf372789759383&type=3#NotInter-Regular"); -unity-text-align: upper-right; -unity-text-generator: advanced; justify-content: space-between; align-content: flex-start;
white-space: nowrap; text-overflow: clip;
">
<ui:Image source="project://database/Assets/Art/export/mail@3x.png?fileID=2800000&guid=0d76662a81af3a7408ca3c2975f08b8f&type=3#mail@3x" tint-color="rgb(158, 158, 158)" style="margin-left: 25px; width: 51px; display: none;"/>
</ui:TextField>
</ui:VisualElement>
...
...
@@ -34,8 +34,8 @@
<ui:Label text="النظام التعليمي" name="TextFieldLabel" language-direction="RTL" class="base-text-light" style="color: rgb(117, 117, 117); margin-bottom: 20px; font-size: 35px; -unity-font-definition: url("project://database/Assets/ALArcade/Hakwaty%20Font/TSHakwaty-DemiBold.otf?fileID=12800000&guid=566b773a07b3d064aa1f4c6ef7b6f6fa&type=3#TSHakwaty-DemiBold");"/>
</ui:VisualElement>
<ui:VisualElement name="Menu" class="padding" style="flex-grow: 0; background-color: rgb(255, 255, 255); height: auto; flex-shrink: 10; border-top-width: 0; border-right-width: 0; border-bottom-width: 0; border-left-width: 0; border-top-left-radius: 50px; border-top-right-radius: 50px; border-bottom-right-radius: 50px; border-bottom-left-radius: 50px; padding-top: 25px; padding-right: 25px; padding-bottom: 25px; padding-left: 25px; justify-content: space-between; border-left-color: rgba(0, 0, 0, 0.25); border-right-color: rgba(0, 0, 0, 0.25); border-top-color: rgba(0, 0, 0, 0.25); border-bottom-color: rgba(0, 0, 0, 0.25); display: flex; margin-bottom: 0;">
<ui:Button text="" name="
Grade
" class="row-btn" style="height: 80px; -unity-text-align: middle-right;">
<ui:DropdownField label="" choices="اختر المدرسة...,مصري عربي,مصري لغات" index="0" language-direction="RTL" name="
School
" style="margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0; position: absolute; flex-grow: 0; font-size: 35px; -unity-font-definition: url("project://database/Assets/ALArcade/Hakwaty%20Font/TSHakwaty-DemiBold.otf?fileID=12800000&guid=566b773a07b3d064aa1f4c6ef7b6f6fa&type=3#TSHakwaty-DemiBold"); -unity-text-generator: advanced; color: rgb(66, 66, 66); -unity-text-align: upper-right; flex-direction: row;"/>
<ui:Button text="" name="" class="row-btn" style="height: 80px; -unity-text-align: middle-right;">
<ui:DropdownField label="" choices="اختر المدرسة...,مصري عربي,مصري لغات" index="0" language-direction="RTL" name="
Curriculum
" style="margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0; position: absolute; flex-grow: 0; font-size: 35px; -unity-font-definition: url("project://database/Assets/ALArcade/Hakwaty%20Font/TSHakwaty-DemiBold.otf?fileID=12800000&guid=566b773a07b3d064aa1f4c6ef7b6f6fa&type=3#TSHakwaty-DemiBold"); -unity-text-generator: advanced; color: rgb(66, 66, 66); -unity-text-align: upper-right; flex-direction: row;"/>
</ui:Button>
</ui:VisualElement>
</ui:VisualElement>
...
...
@@ -59,7 +59,7 @@
</ui:VisualElement>
<ui:VisualElement name="Menu" class="padding" style="flex-grow: 0; background-color: rgb(255, 255, 255); height: auto; flex-shrink: 10; border-top-width: 0; border-right-width: 0; border-bottom-width: 0; border-left-width: 0; border-top-left-radius: 50px; border-top-right-radius: 50px; border-bottom-right-radius: 50px; border-bottom-left-radius: 50px; padding-top: 25px; padding-right: 25px; padding-bottom: 25px; padding-left: 25px; justify-content: space-between; border-left-color: rgba(0, 0, 0, 0.25); border-right-color: rgba(0, 0, 0, 0.25); border-top-color: rgba(0, 0, 0, 0.25); border-bottom-color: rgba(0, 0, 0, 0.25); display: flex; margin-bottom: 0;">
<ui:Button text="" name="Level" class="row-btn" style="height: 80px; -unity-text-align: middle-right;">
<ui:DropdownField label="" choices="اختر فصل دراسي...,الفصل الدراسي الاول,الفصل الدراسي الثاني" index="0" language-direction="RTL" name="
Grade
" style="margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0; position: absolute; flex-grow: 0; font-size: 35px; -unity-font-definition: url("project://database/Assets/ALArcade/Hakwaty%20Font/TSHakwaty-DemiBold.otf?fileID=12800000&guid=566b773a07b3d064aa1f4c6ef7b6f6fa&type=3#TSHakwaty-DemiBold"); -unity-text-generator: advanced; color: rgb(66, 66, 66); -unity-text-align: upper-right; flex-direction: row;"/>
<ui:DropdownField label="" choices="اختر فصل دراسي...,الفصل الدراسي الاول,الفصل الدراسي الثاني" index="0" language-direction="RTL" name="
Term
" style="margin-top: 0; margin-right: 0; margin-bottom: 0; margin-left: 0; position: absolute; flex-grow: 0; font-size: 35px; -unity-font-definition: url("project://database/Assets/ALArcade/Hakwaty%20Font/TSHakwaty-DemiBold.otf?fileID=12800000&guid=566b773a07b3d064aa1f4c6ef7b6f6fa&type=3#TSHakwaty-DemiBold"); -unity-text-generator: advanced; color: rgb(66, 66, 66); -unity-text-align: upper-right; flex-direction: row;"/>
</ui:Button>
</ui:VisualElement>
</ui:VisualElement>
...
...
My project/Assets/Prefabs/MCQ/Canvas.prefab
View file @
d4ba32ba
...
...
@@ -1116,10 +1116,10 @@ RectTransform:
m_Children
:
[]
m_Father
:
{
fileID
:
5665338920870028329
}
m_LocalEulerAnglesHint
:
{
x
:
0
,
y
:
0
,
z
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
0
}
m_AnchorMax
:
{
x
:
0
,
y
:
0
}
m_AnchoredPosition
:
{
x
:
22.572838
,
y
:
0
}
m_SizeDelta
:
{
x
:
51.9337
,
y
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
1
}
m_AnchorMax
:
{
x
:
0
,
y
:
1
}
m_AnchoredPosition
:
{
x
:
22.572838
,
y
:
-25.96685
}
m_SizeDelta
:
{
x
:
0
,
y
:
51.9337
}
m_Pivot
:
{
x
:
0.5
,
y
:
0.5
}
---
!u!222
&1367268893384981932
CanvasRenderer
:
...
...
@@ -3222,10 +3222,10 @@ RectTransform:
m_Children
:
[]
m_Father
:
{
fileID
:
5665338920870028329
}
m_LocalEulerAnglesHint
:
{
x
:
0
,
y
:
0
,
z
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
0
}
m_AnchorMax
:
{
x
:
0
,
y
:
0
}
m_AnchoredPosition
:
{
x
:
83.71851
,
y
:
0
}
m_SizeDelta
:
{
x
:
51.9337
,
y
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
1
}
m_AnchorMax
:
{
x
:
0
,
y
:
1
}
m_AnchoredPosition
:
{
x
:
83.71851
,
y
:
-25.96685
}
m_SizeDelta
:
{
x
:
0
,
y
:
51.9337
}
m_Pivot
:
{
x
:
0.5
,
y
:
0.5
}
---
!u!222
&6044436298841298018
CanvasRenderer
:
...
...
@@ -3406,10 +3406,10 @@ RectTransform:
m_Children
:
[]
m_Father
:
{
fileID
:
5665338920870028329
}
m_LocalEulerAnglesHint
:
{
x
:
0
,
y
:
0
,
z
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
0
}
m_AnchorMax
:
{
x
:
0
,
y
:
0
}
m_AnchoredPosition
:
{
x
:
267.15555
,
y
:
0
}
m_SizeDelta
:
{
x
:
51.9337
,
y
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
1
}
m_AnchorMax
:
{
x
:
0
,
y
:
1
}
m_AnchoredPosition
:
{
x
:
267.15555
,
y
:
-25.96685
}
m_SizeDelta
:
{
x
:
0
,
y
:
51.9337
}
m_Pivot
:
{
x
:
0.5
,
y
:
0.5
}
---
!u!222
&7681831561278972478
CanvasRenderer
:
...
...
@@ -3793,10 +3793,10 @@ RectTransform:
m_Children
:
[]
m_Father
:
{
fileID
:
5665338920870028329
}
m_LocalEulerAnglesHint
:
{
x
:
0
,
y
:
0
,
z
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
0
}
m_AnchorMax
:
{
x
:
0
,
y
:
0
}
m_AnchoredPosition
:
{
x
:
206.00986
,
y
:
0
}
m_SizeDelta
:
{
x
:
51.9337
,
y
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
1
}
m_AnchorMax
:
{
x
:
0
,
y
:
1
}
m_AnchoredPosition
:
{
x
:
206.00986
,
y
:
-25.96685
}
m_SizeDelta
:
{
x
:
0
,
y
:
51.9337
}
m_Pivot
:
{
x
:
0.5
,
y
:
0.5
}
---
!u!222
&8749602556167845090
CanvasRenderer
:
...
...
@@ -4052,10 +4052,10 @@ RectTransform:
m_Children
:
[]
m_Father
:
{
fileID
:
5665338920870028329
}
m_LocalEulerAnglesHint
:
{
x
:
0
,
y
:
0
,
z
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
0
}
m_AnchorMax
:
{
x
:
0
,
y
:
0
}
m_AnchoredPosition
:
{
x
:
144.8642
,
y
:
0
}
m_SizeDelta
:
{
x
:
51.9337
,
y
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
1
}
m_AnchorMax
:
{
x
:
0
,
y
:
1
}
m_AnchoredPosition
:
{
x
:
144.8642
,
y
:
-25.96685
}
m_SizeDelta
:
{
x
:
0
,
y
:
51.9337
}
m_Pivot
:
{
x
:
0.5
,
y
:
0.5
}
---
!u!222
&8187631058268110602
CanvasRenderer
:
...
...
My project/Assets/Prefabs/TF/Phone Art/TfUI.prefab
View file @
d4ba32ba
...
...
@@ -2374,8 +2374,8 @@ MonoBehaviour:
m_OnCullStateChanged
:
m_PersistentCalls
:
m_Calls
:
[]
text
:
"
\u0646\u06
2C\u062D\u062A
\u0627\u0644\u0645\u0647\u0645\u0629
"
fontStack
:
{
fileID
:
11400000
,
guid
:
0029e5efb4c7a12f1ac9136de794e6dc
,
type
:
2
}
text
:
"
\u0646\u06
42\u0627\u0637\u0643
"
fontStack
:
{
fileID
:
11400000
,
guid
:
657d8db1dabff4325ae70686887b629b
,
type
:
2
}
appearance
:
{
fileID
:
11400000
,
guid
:
3a559cf5d653f05ea807e1be5655df92
,
type
:
2
}
fontSize
:
72
baseDirection
:
2
...
...
My project/Assets/ScienceStreet/CS/Scripts/CsDemoBuilder.cs
View file @
d4ba32ba
...
...
@@ -296,108 +296,7 @@ namespace com.al_arcade.cs
{
return
new
CsQuestion
[]
{
new
()
{
id
=
"1"
,
source
=
"علوم - الوحدة 3"
,
wrong_word
=
"القمر"
,
correct_answer
=
"الشمس"
,
words
=
new
CsWord
[]
{
new
()
{
word_text
=
"القمر"
,
position
=
0
,
is_wrong
=
true
},
new
()
{
word_text
=
"مصدر"
,
position
=
1
,
is_wrong
=
false
},
new
()
{
word_text
=
"الضوء"
,
position
=
2
,
is_wrong
=
false
},
new
()
{
word_text
=
"والحرارة"
,
position
=
3
,
is_wrong
=
false
},
new
()
{
word_text
=
"للأرض"
,
position
=
4
,
is_wrong
=
false
},
},
options
=
new
CsOption
[]
{
new
()
{
option_text
=
"الشمس"
,
is_correct
=
true
},
new
()
{
option_text
=
"النجوم"
,
is_correct
=
false
},
new
()
{
option_text
=
"الكواكب"
,
is_correct
=
false
},
new
()
{
option_text
=
"المذنبات"
,
is_correct
=
false
},
}
},
new
()
{
id
=
"2"
,
source
=
"كيمياء"
,
wrong_word
=
"الأكسجين"
,
correct_answer
=
"النيتروجين"
,
words
=
new
CsWord
[]
{
new
()
{
word_text
=
"يتكون"
,
position
=
0
,
is_wrong
=
false
},
new
()
{
word_text
=
"معظم"
,
position
=
1
,
is_wrong
=
false
},
new
()
{
word_text
=
"الغلاف"
,
position
=
2
,
is_wrong
=
false
},
new
()
{
word_text
=
"الجوي"
,
position
=
3
,
is_wrong
=
false
},
new
()
{
word_text
=
"من"
,
position
=
4
,
is_wrong
=
false
},
new
()
{
word_text
=
"الأكسجين"
,
position
=
5
,
is_wrong
=
true
},
},
options
=
new
CsOption
[]
{
new
()
{
option_text
=
"النيتروجين"
,
is_correct
=
true
},
new
()
{
option_text
=
"ثاني أكسيد الكربون"
,
is_correct
=
false
},
new
()
{
option_text
=
"الهيليوم"
,
is_correct
=
false
},
}
},
new
()
{
id
=
"3"
,
source
=
"أحياء"
,
wrong_word
=
"الجذر"
,
correct_answer
=
"الورقة"
,
words
=
new
CsWord
[]
{
new
()
{
word_text
=
"الجذر"
,
position
=
0
,
is_wrong
=
true
},
new
()
{
word_text
=
"هي"
,
position
=
1
,
is_wrong
=
false
},
new
()
{
word_text
=
"الجزء"
,
position
=
2
,
is_wrong
=
false
},
new
()
{
word_text
=
"المسؤول"
,
position
=
3
,
is_wrong
=
false
},
new
()
{
word_text
=
"عن"
,
position
=
4
,
is_wrong
=
false
},
new
()
{
word_text
=
"البناء"
,
position
=
5
,
is_wrong
=
false
},
new
()
{
word_text
=
"الضوئي"
,
position
=
6
,
is_wrong
=
false
},
},
options
=
new
CsOption
[]
{
new
()
{
option_text
=
"الورقة"
,
is_correct
=
true
},
new
()
{
option_text
=
"الساق"
,
is_correct
=
false
},
new
()
{
option_text
=
"الزهرة"
,
is_correct
=
false
},
new
()
{
option_text
=
"البذرة"
,
is_correct
=
false
},
}
},
new
()
{
id
=
"4"
,
source
=
"فيزياء"
,
wrong_word
=
"تنخفض"
,
correct_answer
=
"ترتفع"
,
words
=
new
CsWord
[]
{
new
()
{
word_text
=
"تنخفض"
,
position
=
0
,
is_wrong
=
true
},
new
()
{
word_text
=
"درجة"
,
position
=
1
,
is_wrong
=
false
},
new
()
{
word_text
=
"غليان"
,
position
=
2
,
is_wrong
=
false
},
new
()
{
word_text
=
"الماء"
,
position
=
3
,
is_wrong
=
false
},
new
()
{
word_text
=
"عند"
,
position
=
4
,
is_wrong
=
false
},
new
()
{
word_text
=
"مئة"
,
position
=
5
,
is_wrong
=
false
},
new
()
{
word_text
=
"سلسيوس"
,
position
=
6
,
is_wrong
=
false
},
},
options
=
new
CsOption
[]
{
new
()
{
option_text
=
"ترتفع"
,
is_correct
=
true
},
new
()
{
option_text
=
"تثبت"
,
is_correct
=
false
},
new
()
{
option_text
=
"تتذبذب"
,
is_correct
=
false
},
}
},
new
()
{
id
=
"5"
,
source
=
"جغرافيا"
,
wrong_word
=
"خمس"
,
correct_answer
=
"سبع"
,
words
=
new
CsWord
[]
{
new
()
{
word_text
=
"يتكون"
,
position
=
0
,
is_wrong
=
false
},
new
()
{
word_text
=
"العالم"
,
position
=
1
,
is_wrong
=
false
},
new
()
{
word_text
=
"من"
,
position
=
2
,
is_wrong
=
false
},
new
()
{
word_text
=
"خمس"
,
position
=
3
,
is_wrong
=
true
},
new
()
{
word_text
=
"قارات"
,
position
=
4
,
is_wrong
=
false
},
},
options
=
new
CsOption
[]
{
new
()
{
option_text
=
"ست"
,
is_correct
=
false
},
new
()
{
option_text
=
"سبع"
,
is_correct
=
true
},
new
()
{
option_text
=
"ثمان"
,
is_correct
=
false
},
}
},
};
}
...
...
My project/Assets/ScienceStreet/CS/Scripts/CsGameManager.cs
View file @
d4ba32ba
...
...
@@ -83,9 +83,15 @@ namespace com.al_arcade.cs
if
(
uiManager
!=
null
&&
uiManager
.
isMusicOn
)
SSAudioManager
.
EnsureInstance
().
PlayMusic
();
var
filter
=
new
QuestionFilter
()
.
CurriculumId
(
0
)
.
SubjectId
(
0
)
.
GradeId
(
session
.
gradeId
)
.
Count
(
session
.
questionCount
)
.
Shuffle
(
true
);
yield
return
api
.
FetchCs
(
session
.
buildType
,
session
.
classCode
,
session
.
questionCount
,
session
.
gradeId
,
filter
,
qs
=>
_questions
=
qs
,
err
=>
onError
(
err
)
);
...
...
@@ -445,7 +451,7 @@ namespace com.al_arcade.cs
_targetGroup
.
RemoveMember
(
_targetGroup
.
Targets
[
1
].
Object
);
}
_groupFraming
.
CenterOffset
=
new
Vector2
(
0f
,
-
0.
7
f
);
_groupFraming
.
CenterOffset
=
new
Vector2
(
0f
,
-
0.
8
f
);
}
private
IEnumerator
UnlockAfterCooldown
()
...
...
My project/Assets/ScienceStreet/CS/Scripts/CsPrefabBuilder.cs
View file @
d4ba32ba
...
...
@@ -308,44 +308,7 @@ namespace com.al_arcade.cs
{
return
new
CsQuestion
[]
{
new
()
{
id
=
"1"
,
source
=
"علوم"
,
wrong_word
=
"القمر"
,
correct_answer
=
"الشمس"
,
words
=
new
CsWord
[]
{
new
()
{
word_text
=
"القمر"
,
position
=
0
,
is_wrong
=
true
},
new
()
{
word_text
=
"مصدر"
,
position
=
1
,
is_wrong
=
false
},
new
()
{
word_text
=
"الضوء"
,
position
=
2
,
is_wrong
=
false
},
new
()
{
word_text
=
"والحرارة"
,
position
=
3
,
is_wrong
=
false
},
new
()
{
word_text
=
"للأرض"
,
position
=
4
,
is_wrong
=
false
},
},
options
=
new
CsOption
[]
{
new
()
{
option_text
=
"الشمس"
,
is_correct
=
true
},
new
()
{
option_text
=
"النجوم"
,
is_correct
=
false
},
new
()
{
option_text
=
"الكواكب"
,
is_correct
=
false
},
}
},
new
()
{
id
=
"2"
,
source
=
"جغرافيا"
,
wrong_word
=
"خمس"
,
correct_answer
=
"سبع"
,
words
=
new
CsWord
[]
{
new
()
{
word_text
=
"يتكون"
,
position
=
0
,
is_wrong
=
false
},
new
()
{
word_text
=
"العالم"
,
position
=
1
,
is_wrong
=
false
},
new
()
{
word_text
=
"من"
,
position
=
2
,
is_wrong
=
false
},
new
()
{
word_text
=
"خمس"
,
position
=
3
,
is_wrong
=
true
},
new
()
{
word_text
=
"قارات"
,
position
=
4
,
is_wrong
=
false
},
},
options
=
new
CsOption
[]
{
new
()
{
option_text
=
"ست"
,
is_correct
=
false
},
new
()
{
option_text
=
"سبع"
,
is_correct
=
true
},
new
()
{
option_text
=
"ثمان"
,
is_correct
=
false
},
}
},
};
}
}
...
...
My project/Assets/ScienceStreet/CS/Scripts/CsSentence.cs
View file @
d4ba32ba
...
...
@@ -207,7 +207,7 @@ public class CsSentence : MonoBehaviour
{
csWord
=
rect
.
gameObject
.
AddComponent
<
CsWordButton
>();
}
csWord
.
Setup
(
_question
.
words
[
i
].
word_text
,
_question
.
words
[
i
].
is_
wrong
,
i
,
elementWidth
,
1f
);
csWord
.
Setup
(
_question
.
words
[
i
].
word_text
,
_question
.
words
[
i
].
is_
distractor
,
i
,
elementWidth
,
1f
);
// Animate to the locally offset position
rect
.
DOLocalMove
(
targetPos
,
0.3f
).
SetEase
(
Ease
.
OutCubic
).
OnComplete
(()
=>
...
...
@@ -224,32 +224,6 @@ public class CsSentence : MonoBehaviour
}
}
_targetGroup
.
RemoveMember
(
_targetGroup
.
Targets
[
1
].
Object
);
// Update Camera Group (top + left + right)
// Top
_targetGroup
.
AddMember
(
_wordTexts
[
0
].
transform
.
parent
,
1f
,
_wordTexts
[
0
].
transform
.
parent
.
GetComponent
<
RectTransform
>().
rect
.
height
*
0.02f
);
var
widestRect
=
widestWord
.
transform
.
parent
.
GetComponent
<
RectTransform
>();
var
offsetFromCenter
=
Vector3
.
right
*
(
widestRect
.
sizeDelta
.
x
/
2
);
var
right
=
new
GameObject
(
"Right"
);
var
rightRect
=
right
.
AddComponent
<
RectTransform
>();
rightRect
.
SetParent
(
widestRect
);
rightRect
.
localPosition
=
offsetFromCenter
;
rightRect
.
SetParent
(
widestRect
.
parent
);
var
left
=
new
GameObject
(
"left"
);
var
leftRect
=
left
.
AddComponent
<
RectTransform
>();
leftRect
.
SetParent
(
widestRect
);
leftRect
.
localPosition
=
-
offsetFromCenter
;
leftRect
.
SetParent
(
widestRect
.
parent
);
_targetGroup
.
AddMember
(
rightRect
,
1f
,
_wordTexts
[
0
].
transform
.
parent
.
GetComponent
<
RectTransform
>().
rect
.
height
*
0.02f
);
_targetGroup
.
AddMember
(
leftRect
,
1f
,
_wordTexts
[
0
].
transform
.
parent
.
GetComponent
<
RectTransform
>().
rect
.
height
*
0.02f
);
if
(
_background
!=
null
)
Destroy
(
_background
);
}
...
...
@@ -365,8 +339,6 @@ public class CsSentence : MonoBehaviour
cnvRt
.
offsetMax
=
Vector2
.
zero
;
cnvRt
.
offsetMin
=
Vector2
.
zero
;
_targetGroup
.
AddMember
(
cnvRt
,
1f
,
2f
);
canvasObj
.
transform
.
SetLocalPositionAndRotation
(
Vector3
.
zero
,
Quaternion
.
identity
);
var
_backgroundImage
=
backGO
.
AddComponent
<
Image
>();
_backgroundImage
.
type
=
Image
.
Type
.
Sliced
;
...
...
@@ -378,6 +350,31 @@ public class CsSentence : MonoBehaviour
cnvRt
.
DOLocalMoveY
(
0f
,
0.3f
).
SetEase
(
Ease
.
OutCubic
);
_backgroundImage
.
DOFade
(
1f
,
0.3f
).
SetEase
(
Ease
.
OutCubic
).
From
(
0f
);
StartCoroutine
(
AnimateTextIn
());
// Update Camera Group (top + left + right)
// Top
_targetGroup
.
AddMember
(
_wordTexts
[
0
].
transform
.
parent
,
1f
,
_wordTexts
[
0
].
transform
.
parent
.
GetComponent
<
RectTransform
>().
rect
.
height
*
0.02f
);
var
widestRect
=
widestWord
.
transform
.
parent
.
GetComponent
<
RectTransform
>();
var
offsetFromCenter
=
Vector3
.
right
*
(
widestRect
.
sizeDelta
.
x
/
2
);
var
right
=
new
GameObject
(
"Right"
);
var
rightRect
=
right
.
AddComponent
<
RectTransform
>();
rightRect
.
SetParent
(
widestRect
);
rightRect
.
localPosition
=
offsetFromCenter
;
rightRect
.
SetParent
(
widestRect
.
parent
);
var
left
=
new
GameObject
(
"left"
);
var
leftRect
=
left
.
AddComponent
<
RectTransform
>();
leftRect
.
SetParent
(
widestRect
);
leftRect
.
localPosition
=
-
offsetFromCenter
;
leftRect
.
SetParent
(
widestRect
.
parent
);
_targetGroup
.
AddMember
(
rightRect
,
1f
,
_wordTexts
[
0
].
transform
.
parent
.
GetComponent
<
RectTransform
>().
rect
.
height
*
0.02f
);
_targetGroup
.
AddMember
(
leftRect
,
1f
,
_wordTexts
[
0
].
transform
.
parent
.
GetComponent
<
RectTransform
>().
rect
.
height
*
0.02f
);
}
}
My project/Assets/ScienceStreet/MCQ/Scripts/McqDemoBuilder.cs
View file @
d4ba32ba
...
...
@@ -274,21 +274,7 @@ namespace com.al_arcade.mcq
{
return
new
McqQuestion
[]
{
new
()
{
id
=
"1"
,
question_text
=
"ما هي عاصمة مصر؟"
,
answer1
=
"القاهرة"
,
answer2
=
"الإسكندرية"
,
answer3
=
"أسوان"
,
answer4
=
"المنصورة"
,
source
=
"جغرافيا"
},
new
()
{
id
=
"2"
,
question_text
=
"كم عدد كواكب المجموعة الشمسية؟"
,
answer1
=
"8"
,
answer2
=
"9"
,
answer3
=
"7"
,
answer4
=
"10"
,
source
=
"علوم"
},
new
()
{
id
=
"3"
,
question_text
=
"ما هو أكبر محيط في العالم؟"
,
answer1
=
"المحيط الهادي"
,
answer2
=
"المحيط الأطلسي"
,
answer3
=
"المحيط الهندي"
,
answer4
=
"المحيط المتجمد"
,
source
=
"جغرافيا"
},
new
()
{
id
=
"4"
,
question_text
=
"ما هو الغاز الأكثر وفرة في الغلاف الجوي؟"
,
answer1
=
"النيتروجين"
,
answer2
=
"الأكسجين"
,
answer3
=
"ثاني أكسيد الكربون"
,
answer4
=
"الهيليوم"
,
source
=
"كيمياء"
},
new
()
{
id
=
"5"
,
question_text
=
"من اخترع المصباح الكهربائي؟"
,
answer1
=
"توماس إديسون"
,
answer2
=
"نيكولا تسلا"
,
answer3
=
"ألبرت آينشتاين"
,
answer4
=
"إسحاق نيوتن"
,
source
=
"تاريخ العلوم"
},
};
}
...
...
My project/Assets/ScienceStreet/MCQ/Scripts/McqGameManager.cs
View file @
d4ba32ba
...
...
@@ -73,9 +73,15 @@ namespace com.al_arcade.mcq
var
session
=
SSGameSession
.
EnsureInstance
();
var
api
=
SSApiManager
.
EnsureInstance
();
var
filter
=
new
QuestionFilter
()
.
CurriculumId
(
0
)
.
SubjectId
(
0
)
.
GradeId
(
session
.
gradeId
)
.
Count
(
session
.
questionCount
)
.
Shuffle
(
true
);
yield
return
api
.
FetchMcq
(
session
.
buildType
,
session
.
classCode
,
session
.
questionCount
,
session
.
gradeId
,
filter
,
qs
=>
_questions
=
qs
,
err
=>
onError
(
err
)
);
...
...
@@ -153,7 +159,7 @@ namespace com.al_arcade.mcq
BeginGameplay
();
}
public
void
ResetGame
()
public
override
void
ResetGame
()
{
ResetBaseState
();
_state
=
McqGameState
.
Idle
;
...
...
@@ -311,8 +317,9 @@ namespace com.al_arcade.mcq
// ─── Gate Spawning ───────────────────────────────────────────────────
private
void
SpawnGates
(
McqQuestion
question
)
{
string
[]
answers
=
question
.
GetShuffledAnswers
(
out
int
correctIdx
);
_correctGateIndex
=
correctIdx
;
var
shuffled
=
McqHelper
.
Shuffle
(
question
);
string
[]
answers
=
shuffled
.
answers
;
_correctGateIndex
=
shuffled
.
correctIndex
;
_activeGates
.
Clear
();
if
(
questionDisplay
!=
null
)
...
...
@@ -327,7 +334,7 @@ namespace com.al_arcade.mcq
for
(
int
i
=
0
;
i
<
answers
.
Length
;
i
++)
{
Vector3
gatePos
=
basePos
+
Vector3
.
right
*
(
startX
+
i
*
gateSpacing
);
var
gate
=
CreateGate
(
gatePos
,
i
,
answers
[
i
],
i
==
correctId
x
);
var
gate
=
CreateGate
(
gatePos
,
i
,
answers
[
i
],
i
==
_correctGateInde
x
);
_activeGates
.
Add
(
gate
);
gate
.
transform
.
DOScale
(
Vector3
.
one
,
0.5f
)
...
...
My project/Assets/ScienceStreet/MCQ/Scripts/McqPrefabBuilder.cs
View file @
d4ba32ba
...
...
@@ -328,15 +328,7 @@ namespace com.al_arcade.mcq
{
return
new
McqQuestion
[]
{
new
()
{
id
=
"1"
,
question_text
=
"ما هي عاصمة مصر؟"
,
answer1
=
"القاهرة"
,
answer2
=
"الإسكندرية"
,
answer3
=
"أسوان"
,
answer4
=
"المنصورة"
,
source
=
"جغرافيا"
},
new
()
{
id
=
"2"
,
question_text
=
"كم عدد كواكب المجموعة الشمسية؟"
,
answer1
=
"8"
,
answer2
=
"9"
,
answer3
=
"7"
,
answer4
=
"10"
,
source
=
"علوم"
},
new
()
{
id
=
"3"
,
question_text
=
"ما هو أكبر محيط في العالم؟"
,
answer1
=
"المحيط الهادي"
,
answer2
=
"المحيط الأطلسي"
,
answer3
=
"المحيط الهندي"
,
answer4
=
"المحيط المتجمد"
,
source
=
"جغرافيا"
},
};
}
}
...
...
My project/Assets/ScienceStreet/MCQ/Scripts/New.meta
deleted
100644 → 0
View file @
ada9ac1c
fileFormatVersion: 2
guid: 20ba488acbcf34d438c13bfb24840820
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
My project/Assets/ScienceStreet/MCQ/Scripts/New/AnswerButtonUI.cs
deleted
100644 → 0
View file @
ada9ac1c
using
UnityEngine
;
using
UnityEngine.UI
;
using
TMPro
;
using
System
;
using
LightSide
;
using
DG.Tweening
;
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
);
[
SerializeField
]
private
Color
disabledColor
=
new
Color
(
0.7f
,
0.7f
,
0.7f
,
1f
);
private
int
answerIndex
;
private
Action
<
int
>
onClicked
;
private
bool
isInteractable
=
true
;
private
CanvasGroup
canvasGroup
;
private
RectTransform
rectTransform
;
private
void
OnEnable
()
{
if
(
button
!=
null
)
{
button
.
onClick
.
RemoveAllListeners
();
}
}
private
void
OnDisable
()
{
DOTween
.
Kill
(
this
);
}
/// <summary>
/// Setup answer button with text and optional image
/// </summary>
public
void
Setup
(
string
answerText
,
Sprite
answerImage
,
int
index
,
Action
<
int
>
callback
)
{
if
(
button
!=
null
)
button
.
Select
();
answerIndex
=
index
;
onClicked
=
callback
;
isInteractable
=
true
;
// Cache components
if
(
rectTransform
==
null
)
rectTransform
=
GetComponent
<
RectTransform
>();
if
(
canvasGroup
==
null
)
canvasGroup
=
GetComponent
<
CanvasGroup
>();
if
(
canvasGroup
==
null
)
canvasGroup
=
gameObject
.
AddComponent
<
CanvasGroup
>();
// 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
)
{
DOTween
.
Kill
(
buttonBackgroundImage
,
true
);
buttonBackgroundImage
.
DOColor
(
isCorrect
?
selectedCorrectColor
:
selectedIncorrectColor
,
0.3f
).
SetEase
(
Ease
.
OutQuad
);
}
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
)
{
DOTween
.
Kill
(
buttonBackgroundImage
,
true
);
buttonBackgroundImage
.
DOColor
(
correctAnswerColor
,
0.3f
)
.
SetEase
(
Ease
.
OutQuad
);
}
if
(
button
!=
null
)
{
button
.
interactable
=
false
;
}
}
/// <summary>
/// Disable this button without highlighting
/// </summary>
public
void
SetDisabled
()
{
isInteractable
=
false
;
if
(
buttonBackgroundImage
!=
null
)
{
DOTween
.
Kill
(
buttonBackgroundImage
,
true
);
buttonBackgroundImage
.
DOColor
(
disabledColor
,
0.3f
)
.
SetEase
(
Ease
.
OutQuad
);
}
if
(
button
!=
null
)
{
button
.
interactable
=
false
;
}
}
/// <summary>
/// Reset button to normal state
/// </summary>
public
void
Reset
()
{
isInteractable
=
true
;
if
(
buttonBackgroundImage
!=
null
)
{
DOTween
.
Kill
(
buttonBackgroundImage
,
true
);
buttonBackgroundImage
.
color
=
normalColor
;
}
if
(
answerTextComponent
!=
null
)
{
answerTextComponent
.
color
=
normalTextColor
;
}
if
(
canvasGroup
!=
null
)
{
canvasGroup
.
alpha
=
1f
;
}
if
(
rectTransform
!=
null
)
{
rectTransform
.
localScale
=
Vector3
.
one
;
}
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/NewMCQGameManger.cs
deleted
100644 → 0
View file @
ada9ac1c
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
;
using
System
;
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
DateTime
gameStartTime
;
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
();
gameStartTime
=
DateTime
.
Now
;
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
;
NewMCQUIManager
.
Instance
.
SetScore
(
_score
);
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
.
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
);
GameHistoryService
.
Instance
.
AddGame
(
"mcq"
,
_score
,
gameStartTime
,
DateTime
.
Now
);
}
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
)
{
Debug
.
Log
(
"shake"
);
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/NewMCQUIManager.cs
deleted
100644 → 0
View file @
ada9ac1c
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
deleted
100644 → 0
View file @
ada9ac1c
fileFormatVersion: 2
guid: 168f088242b9231408ef684c9094c66e
\ No newline at end of file
My project/Assets/ScienceStreet/MCQ/Scripts/New/QuestionUi.cs
deleted
100644 → 0
View file @
ada9ac1c
This diff is collapsed.
Click to expand it.
My project/Assets/ScienceStreet/Shared/Scripts/AttemptData.cs
0 → 100644
View file @
d4ba32ba
using
System
;
using
Newtonsoft.Json
;
namespace
com.al_arcade.shared
{
/// <summary>
/// Data object for reporting a single question attempt to the server.
/// </summary>
[
Serializable
]
public
class
AttemptData
{
// Required
[
JsonProperty
(
"question_type"
)]
public
string
questionType
;
[
JsonProperty
(
"question_id"
)]
public
int
questionId
;
[
JsonProperty
(
"session_id"
)]
public
string
sessionId
;
[
JsonProperty
(
"is_correct"
)]
public
int
isCorrect
;
[
JsonProperty
(
"time_taken_ms"
)]
public
int
timeTakenMs
;
// Optional
[
JsonProperty
(
"is_skipped"
)]
public
int
isSkipped
;
[
JsonProperty
(
"is_timeout"
)]
public
int
isTimeout
;
[
JsonProperty
(
"selected_answer"
)]
public
string
selectedAnswer
;
[
JsonProperty
(
"correct_answer"
)]
public
string
correctAnswer
;
[
JsonProperty
(
"room_id"
)]
public
string
roomId
;
[
JsonProperty
(
"player_id"
)]
public
string
playerId
;
[
JsonProperty
(
"round_in_game"
)]
public
int
?
roundInGame
;
[
JsonProperty
(
"total_rounds"
)]
public
int
?
totalRounds
;
[
JsonProperty
(
"player_streak"
)]
public
int
playerStreak
;
[
JsonProperty
(
"player_score_at_time"
)]
public
int
playerScoreAtTime
;
[
JsonProperty
(
"players_in_room"
)]
public
int
?
playersInRoom
;
[
JsonProperty
(
"answer_rank"
)]
public
int
?
answerRank
;
[
JsonProperty
(
"hint_used"
)]
public
int
hintUsed
;
[
JsonProperty
(
"power_up_used"
)]
public
string
powerUpUsed
;
[
JsonProperty
(
"device_type"
)]
public
string
deviceType
=
"unknown"
;
[
JsonProperty
(
"os"
)]
public
string
os
;
[
JsonProperty
(
"client_version"
)]
public
string
clientVersion
;
[
JsonProperty
(
"screen_width"
)]
public
int
?
screenWidth
;
[
JsonProperty
(
"screen_height"
)]
public
int
?
screenHeight
;
public
AttemptData
()
{
}
public
AttemptData
(
string
type
,
int
qId
,
string
session
,
bool
correct
,
int
timeMs
)
{
questionType
=
type
;
questionId
=
qId
;
sessionId
=
session
;
isCorrect
=
correct
?
1
:
0
;
timeTakenMs
=
timeMs
;
}
}
[
Serializable
]
public
class
BatchAttemptPayload
{
public
AttemptData
[]
attempts
;
}
}
\ No newline at end of file
My project/Assets/ScienceStreet/
MCQ/Scripts/New/QuestionUi
.cs.meta
→
My project/Assets/ScienceStreet/
Shared/Scripts/AttemptData
.cs.meta
View file @
d4ba32ba
fileFormatVersion: 2
guid: 68aee830395e108478a9643b20de1689
\ No newline at end of file
guid: 8b3bc4d6f06a2d94baff2c7e3214a202
\ No newline at end of file
My project/Assets/ScienceStreet/Shared/Scripts/MCQHelper.cs
0 → 100644
View file @
d4ba32ba
using
System.Collections.Generic
;
using
UnityEngine
;
namespace
com.al_arcade.shared
{
/// <summary>
/// Shuffled MCQ answers container.
/// answer1 from the API is ALWAYS correct — this shuffles
/// them for display and tracks which index is correct.
/// </summary>
public
struct
ShuffledMcq
{
/// <summary>The 4 answers in shuffled order.</summary>
public
string
[]
answers
;
/// <summary>Index (0-3) of the correct answer in the shuffled array.</summary>
public
int
correctIndex
;
/// <summary>Check if player's chosen index is correct.</summary>
public
bool
IsCorrect
(
int
chosenIndex
)
=>
chosenIndex
==
correctIndex
;
/// <summary>The correct answer text.</summary>
public
string
CorrectText
=>
answers
[
correctIndex
];
}
public
static
class
McqHelper
{
/// <summary>
/// Shuffles the 4 answers and returns which index is now correct.
/// </summary>
public
static
ShuffledMcq
Shuffle
(
McqQuestion
q
)
{
// Build list with original indices
var
list
=
new
List
<(
string
text
,
bool
isCorrect
)>
{
(
q
.
answer1
,
true
),
// answer1 = ALWAYS correct
(
q
.
answer2
,
false
),
(
q
.
answer3
,
false
),
(
q
.
answer4
,
false
),
};
// Fisher-Yates shuffle
for
(
int
i
=
list
.
Count
-
1
;
i
>
0
;
i
--)
{
int
j
=
Random
.
Range
(
0
,
i
+
1
);
(
list
[
i
],
list
[
j
])
=
(
list
[
j
],
list
[
i
]);
}
var
result
=
new
ShuffledMcq
{
answers
=
new
string
[
4
],
correctIndex
=
-
1
};
for
(
int
i
=
0
;
i
<
4
;
i
++)
{
result
.
answers
[
i
]
=
list
[
i
].
text
;
if
(
list
[
i
].
isCorrect
)
result
.
correctIndex
=
i
;
}
return
result
;
}
}
}
\ No newline at end of file
My project/Assets/ScienceStreet/
MCQ/Scripts/New/NewMCQGameMang
er.cs.meta
→
My project/Assets/ScienceStreet/
Shared/Scripts/MCQHelp
er.cs.meta
View file @
d4ba32ba
fileFormatVersion: 2
guid: 8b4109c5dc3e965418f041d490b09d96
\ No newline at end of file
guid: 1bacf1883f436a74caa55a658a8746e3
\ No newline at end of file
My project/Assets/ScienceStreet/Shared/Scripts/QuestionFilter.cs
0 → 100644
View file @
d4ba32ba
using
System.Collections.Generic
;
namespace
com.al_arcade.shared
{
/// <summary>
/// Fluent builder for question filter parameters.
/// Used by GetMcq, GetTf, GetCs, GetMixed.
/// </summary>
public
class
QuestionFilter
{
private
readonly
Dictionary
<
string
,
string
>
_params
=
new
();
public
QuestionFilter
CurriculumId
(
int
id
)
{
_params
[
"curriculum_id"
]
=
id
.
ToString
();
return
this
;
}
public
QuestionFilter
SubjectId
(
int
id
)
{
_params
[
"subject_id"
]
=
id
.
ToString
();
return
this
;
}
public
QuestionFilter
GradeId
(
int
id
)
{
_params
[
"grade_id"
]
=
id
.
ToString
();
return
this
;
}
public
QuestionFilter
TermId
(
int
id
)
{
_params
[
"term_id"
]
=
id
.
ToString
();
return
this
;
}
public
QuestionFilter
ChapterId
(
int
id
)
{
_params
[
"chapter_id"
]
=
id
.
ToString
();
return
this
;
}
public
QuestionFilter
Difficulty
(
string
diff
)
{
if
(!
string
.
IsNullOrEmpty
(
diff
))
_params
[
"difficulty"
]
=
diff
;
return
this
;
}
public
QuestionFilter
BloomLevel
(
string
bloom
)
{
if
(!
string
.
IsNullOrEmpty
(
bloom
))
_params
[
"bloom_level"
]
=
bloom
;
return
this
;
}
public
QuestionFilter
VerifiedOnly
(
bool
v
=
true
)
{
if
(
v
)
_params
[
"verified_only"
]
=
"1"
;
return
this
;
}
public
QuestionFilter
Count
(
int
n
)
{
if
(
n
>
0
)
_params
[
"count"
]
=
n
.
ToString
();
return
this
;
}
public
QuestionFilter
Shuffle
(
bool
s
=
true
)
{
_params
[
"shuffle"
]
=
s
?
"1"
:
"0"
;
return
this
;
}
// Mixed-specific counts
public
QuestionFilter
McqCount
(
int
n
)
{
if
(
n
>=
0
)
_params
[
"mcq_count"
]
=
n
.
ToString
();
return
this
;
}
public
QuestionFilter
TfCount
(
int
n
)
{
if
(
n
>=
0
)
_params
[
"tf_count"
]
=
n
.
ToString
();
return
this
;
}
public
QuestionFilter
CsCount
(
int
n
)
{
if
(
n
>=
0
)
_params
[
"cs_count"
]
=
n
.
ToString
();
return
this
;
}
public
Dictionary
<
string
,
string
>
Build
()
=>
new
(
_params
);
/// <summary>Quick shortcut: filter by chapter only.</summary>
public
static
QuestionFilter
ForChapter
(
int
chapterId
,
int
count
=
10
)
{
return
new
QuestionFilter
().
ChapterId
(
chapterId
).
Count
(
count
);
}
/// <summary>Quick shortcut: filter by subject + grade + term.</summary>
public
static
QuestionFilter
ForSubject
(
int
subjectId
,
int
gradeId
,
int
termId
,
int
count
=
10
)
{
return
new
QuestionFilter
()
.
SubjectId
(
subjectId
)
.
GradeId
(
gradeId
)
.
TermId
(
termId
)
.
Count
(
count
);
}
}
}
\ No newline at end of file
My project/Assets/ScienceStreet/
MCQ/Scripts/New/AnswerButtonUI
.cs.meta
→
My project/Assets/ScienceStreet/
Shared/Scripts/QuestionFilter
.cs.meta
View file @
d4ba32ba
fileFormatVersion: 2
guid: cb8a50d45c5491e45a8c75803f8df92c
\ No newline at end of file
guid: 52e062c4425a8ca4ea8b284d19376b38
\ No newline at end of file
My project/Assets/ScienceStreet/Shared/Scripts/SSApiManager.cs
View file @
d4ba32ba
This diff is collapsed.
Click to expand it.
My project/Assets/ScienceStreet/Shared/Scripts/SSDataModels.cs
View file @
d4ba32ba
This diff is collapsed.
Click to expand it.
My project/Assets/ScienceStreet/Shared/Scripts/SSGameSession.cs
View file @
d4ba32ba
...
...
@@ -59,30 +59,31 @@ namespace com.al_arcade.shared
{
var
api
=
SSApiManager
.
EnsureInstance
();
yield
return
api
.
ValidateClassCode
(
code
,
cls
=>
{
classCode
=
cls
.
class_code
;
className
=
cls
.
class_name
;
teacherName
=
cls
.
teacher_name
;
gradeName
=
cls
.
grade_name
;
buildType
=
"teacher"
;
isClassValidated
=
true
;
if
(
int
.
TryParse
(
cls
.
grade_id
,
out
int
gid
))
gradeId
=
gid
;
Debug
.
Log
(
$"[SSSession] Teacher session started:
{
className
}
by
{
teacherName
}
"
);
onSuccess
?.
Invoke
();
onSessionReady
?.
Invoke
();
},
err
=>
{
isClassValidated
=
false
;
Debug
.
LogWarning
(
$"[SSSession] Validation failed:
{
err
}
"
);
onError
?.
Invoke
(
err
);
onSessionError
?.
Invoke
(
err
);
});
// yield return api.ValidateClassCode(code,
// cls =>
// {
// classCode = cls.class_code;
// className = cls.class_name;
// teacherName = cls.teacher_name;
// gradeName = cls.grade_name;
// buildType = "teacher";
// isClassValidated = true;
// if (int.TryParse(cls.grade_id, out int gid))
// gradeId = gid;
// Debug.Log($"[SSSession] Teacher session started: {className} by {teacherName}");
// onSuccess?.Invoke();
// onSessionReady?.Invoke();
// },
// err =>
// {
// isClassValidated = false;
// Debug.LogWarning($"[SSSession] Validation failed: {err}");
// onError?.Invoke(err);
// onSessionError?.Invoke(err);
// });
yield
break
;
}
...
...
My project/Assets/ScienceStreet/TF/Scripts/TfDemoBuilder.cs
View file @
d4ba32ba
...
...
@@ -396,22 +396,7 @@ namespace com.al_arcade.tf
{
return
new
TfQuestion
[]
{
new
()
{
id
=
"1"
,
question_text
=
"الشمس تدور حول الأرض"
,
is_true
=
false
,
source
=
"علوم"
},
new
()
{
id
=
"2"
,
question_text
=
"الماء يتكون من الهيدروجين والأكسجين"
,
is_true
=
true
,
source
=
"كيمياء"
},
new
()
{
id
=
"3"
,
question_text
=
"القمر ينير بذاته"
,
is_true
=
false
,
source
=
"فلك"
},
new
()
{
id
=
"4"
,
question_text
=
"النباتات تحتاج ضوء الشمس لعملية البناء الضوئي"
,
is_true
=
true
,
source
=
"أحياء"
},
new
()
{
id
=
"5"
,
question_text
=
"عدد قارات العالم خمس قارات"
,
is_true
=
false
,
source
=
"جغرافيا"
},
new
()
{
id
=
"6"
,
question_text
=
"الحديد يتمدد بالحرارة"
,
is_true
=
true
,
source
=
"فيزياء"
},
new
()
{
id
=
"7"
,
question_text
=
"المحيط الأطلسي هو أكبر محيطات العالم"
,
is_true
=
false
,
source
=
"جغرافيا"
},
new
()
{
id
=
"8"
,
question_text
=
"جسم الإنسان يحتوي على 206 عظمة"
,
is_true
=
true
,
source
=
"أحياء"
},
};
}
...
...
My project/Assets/ScienceStreet/TF/Scripts/TfGameManager.cs
View file @
d4ba32ba
...
...
@@ -54,9 +54,15 @@ namespace com.al_arcade.tf
{
var
session
=
SSGameSession
.
EnsureInstance
();
var
api
=
SSApiManager
.
EnsureInstance
();
var
filter
=
new
QuestionFilter
()
.
CurriculumId
(
0
)
.
SubjectId
(
0
)
.
GradeId
(
session
.
gradeId
)
.
Count
(
session
.
questionCount
);
yield
return
api
.
FetchTf
(
session
.
buildType
,
session
.
classCode
,
session
.
questionCount
,
session
.
gradeId
,
filter
,
qs
=>
_questions
=
qs
,
err
=>
onError
(
err
)
);
...
...
My project/Assets/ScienceStreet/TF/Scripts/TfPrefabBuilder.cs
View file @
d4ba32ba
...
...
@@ -320,16 +320,7 @@ namespace com.al_arcade.tf
{
return
new
TfQuestion
[]
{
new
()
{
id
=
"1"
,
question_text
=
"الشمس تدور حول الأرض"
,
is_true
=
false
,
source
=
"علوم"
},
new
()
{
id
=
"2"
,
question_text
=
"الماء يتكون من الهيدروجين والأكسجين"
,
is_true
=
true
,
source
=
"كيمياء"
},
new
()
{
id
=
"3"
,
question_text
=
"القمر ينير بذاته"
,
is_true
=
false
,
source
=
"فلك"
},
new
()
{
id
=
"4"
,
question_text
=
"النباتات تحتاج ضوء الشمس لعملية البناء الضوئي"
,
is_true
=
true
,
source
=
"أحياء"
},
new
()
{
id
=
"5"
,
question_text
=
"الحديد يتمدد بالحرارة"
,
is_true
=
true
,
source
=
"فيزياء"
},
};
}
}
...
...
My project/ProjectSettings/ProjectSettings.asset
View file @
d4ba32ba
...
...
@@ -13,7 +13,7 @@ PlayerSettings:
useOnDemandResources
:
0
accelerometerFrequency
:
60
companyName
:
DefaultCompany
productName
:
Correct The Sentence
productName
:
SSBookMinigames
defaultCursor
:
{
fileID
:
0
}
cursorHotspot
:
{
x
:
0
,
y
:
0
}
m_SplashScreenBackgroundColor
:
{
r
:
0.13725491
,
g
:
0.12156863
,
b
:
0.1254902
,
a
:
1
}
...
...
@@ -170,7 +170,7 @@ PlayerSettings:
androidMaxAspectRatio
:
2.4
androidMinAspectRatio
:
1
applicationIdentifier
:
Android
:
com.DefaultCompany.
CorrectTheSentence
Android
:
com.DefaultCompany.
SSBookMinigames
Standalone
:
com.Unity-Technologies.com.unity.template.urp-blank
iPhone
:
com.Unity-Technologies.com.unity.template.urp-blank
buildNumber
:
...
...
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