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
Hide 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
...
@@ -38,33 +38,38 @@ public class AppRouter : MonoBehaviour
private
async
UniTask
Boot
()
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
();
bool
ready
=
await
SupabaseManager
.
Instance
.
Initialize
();
if
(!
ready
)
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
;
return
;
}
}
// 2. Listen for unexpected sign-outs
SupabaseManager
.
Instance
.
AddAuthStateListener
(
OnAuthStateChanged
);
// 3. Ensure session
// 3. Ensure session
var
authResult
=
await
SupabaseAuthentication
.
Instance
.
EnsureSession
();
var
authResult
=
await
SupabaseAuthentication
.
Instance
.
EnsureSession
();
if
(
authResult
.
IsT1
)
if
(
authResult
.
IsT1
)
{
{
// if (cached != null) { GoToHome(); return; }
GoToLogin
();
GoToLogin
();
return
;
return
;
}
}
// 4.
Try load profile
// 4.
Refresh from network
var
profileResult
=
await
UserService
.
Instance
.
GetCurrentUser
();
var
profileResult
=
await
UserService
.
Instance
.
GetCurrentUser
();
profileResult
.
Switch
(
profileResult
.
Switch
(
success
=>
GoToHome
(),
success
=>
GoToHome
(),
error
=>
GoToLogin
()
error
=>
{
// if (cached != null) GoToHome(); // stale cache, still usable
GoToLogin
();
}
);
);
_booted
=
true
;
}
}
// ─── Auth State Listener (Safety Net Only) ───────────────────────
// ─── Auth State Listener (Safety Net Only) ───────────────────────
...
@@ -101,6 +106,7 @@ public class AppRouter : MonoBehaviour
...
@@ -101,6 +106,7 @@ public class AppRouter : MonoBehaviour
{
{
if
(
SceneManager
.
GetActiveScene
().
name
!=
"MainMenu"
)
if
(
SceneManager
.
GetActiveScene
().
name
!=
"MainMenu"
)
await
SceneManager
.
LoadSceneAsync
(
"MainMenu"
);
await
SceneManager
.
LoadSceneAsync
(
"MainMenu"
);
UserService
.
Instance
.
StartListening
();
HideSplash
();
HideSplash
();
}
}
...
...
My project/Assets/App/Infrastructure/User/UserService.cs
View file @
d4ba32ba
using
System
;
using
System
;
using
System.Threading
;
using
System.Threading.Tasks
;
using
Cysharp.Threading.Tasks
;
using
Cysharp.Threading.Tasks
;
using
Newtonsoft.Json
;
using
OneOf
;
using
OneOf
;
using
Supabase.Realtime
;
using
Supabase.Realtime.PostgresChanges
;
using
UnityEngine
;
using
UnityEngine
;
public
class
UserService
public
class
UserService
...
@@ -8,11 +13,98 @@ public class UserService
...
@@ -8,11 +13,98 @@ public class UserService
private
static
UserService
_instance
;
private
static
UserService
_instance
;
public
static
UserService
Instance
=>
_instance
??=
new
UserService
();
public
static
UserService
Instance
=>
_instance
??=
new
UserService
();
private
const
string
CACHE_KEY
=
"cached_user_profile"
;
public
User
CurrentUser
{
get
;
private
set
;
}
public
User
CurrentUser
{
get
;
private
set
;
}
public
bool
HasProfile
=>
CurrentUser
!=
null
;
public
bool
HasProfile
=>
CurrentUser
!=
null
;
public
bool
IsFromCache
{
get
;
private
set
;
}
public
event
Action
<
User
>
OnUserChanged
;
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
;
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
;
private
UserService
()
{
}
_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
()
public
async
UniTask
<
OneOf
<
UserResult
,
ErrorResult
>>
GetCurrentUser
()
{
{
...
@@ -32,7 +124,17 @@ public class UserService
...
@@ -32,7 +124,17 @@ public class UserService
if
(
response
?.
Models
==
null
||
response
.
Models
.
Count
==
0
)
if
(
response
?.
Models
==
null
||
response
.
Models
.
Count
==
0
)
return
new
ErrorResult
(
"Profile not found"
);
return
new
ErrorResult
(
"Profile not found"
);
var
oldUser
=
CurrentUser
;
CurrentUser
=
response
.
Models
[
0
];
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
);
OnUserChanged
?.
Invoke
(
CurrentUser
);
return
new
UserResult
(
CurrentUser
);
return
new
UserResult
(
CurrentUser
);
}
}
...
@@ -45,10 +147,10 @@ public class UserService
...
@@ -45,10 +147,10 @@ public class UserService
public
async
UniTask
<
OneOf
<
UserResult
,
ErrorResult
>>
CreateProfile
(
public
async
UniTask
<
OneOf
<
UserResult
,
ErrorResult
>>
CreateProfile
(
string
username
,
string
username
,
string
school
=
null
,
string
grade
=
null
,
string
grade
=
null
,
string
sex
=
null
,
string
sex
=
null
,
string
age
=
null
)
string
curriculum
=
null
,
string
term
=
null
)
{
{
try
try
{
{
...
@@ -59,21 +161,16 @@ public class UserService
...
@@ -59,21 +161,16 @@ public class UserService
var
user
=
new
User
var
user
=
new
User
{
{
Username
=
username
,
Username
=
username
,
School
=
school
,
Grade
=
grade
,
Grade
=
grade
,
Sex
=
sex
,
Sex
=
sex
,
Age
=
age
,
Curriculum
=
curriculum
,
Term
=
term
,
Rank
=
"normal"
,
Rank
=
"normal"
,
Points
=
0
,
Points
=
0
CreatedAt
=
DateTime
.
UtcNow
,
UpdatedAt
=
DateTime
.
UtcNow
};
};
await
client
.
From
<
User
>().
Insert
(
user
);
await
client
.
From
<
User
>().
Insert
(
user
);
return
await
GetCurrentUser
();
// Re-fetch to get server-set fields (id, timestamps)
var
fetchResult
=
await
GetCurrentUser
();
return
fetchResult
;
}
}
catch
(
Exception
ex
)
catch
(
Exception
ex
)
{
{
...
@@ -84,10 +181,10 @@ public class UserService
...
@@ -84,10 +181,10 @@ public class UserService
public
async
UniTask
<
OneOf
<
UserResult
,
ErrorResult
>>
UpdateProfile
(
public
async
UniTask
<
OneOf
<
UserResult
,
ErrorResult
>>
UpdateProfile
(
string
username
=
null
,
string
username
=
null
,
string
school
=
null
,
string
grade
=
null
,
string
grade
=
null
,
string
sex
=
null
,
string
sex
=
null
,
string
age
=
null
)
string
curriculum
=
null
,
string
term
=
null
)
{
{
try
try
{
{
...
@@ -98,17 +195,16 @@ public class UserService
...
@@ -98,17 +195,16 @@ public class UserService
var
update
=
new
User
();
var
update
=
new
User
();
if
(
username
!=
null
)
update
.
Username
=
username
;
if
(
username
!=
null
)
update
.
Username
=
username
;
if
(
school
!=
null
)
update
.
School
=
school
;
if
(
grade
!=
null
)
update
.
Grade
=
grade
;
if
(
grade
!=
null
)
update
.
Grade
=
grade
;
if
(
sex
!=
null
)
update
.
Sex
=
sex
;
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
await
client
.
From
<
User
>()
.
From
<
User
>()
.
Where
(
x
=>
x
.
Id
==
authUser
.
Id
)
.
Where
(
x
=>
x
.
Id
==
authUser
.
Id
)
.
Update
(
update
);
.
Update
(
update
);
// Re-fetch
return
await
GetCurrentUser
();
return
await
GetCurrentUser
();
}
}
catch
(
Exception
ex
)
catch
(
Exception
ex
)
...
@@ -118,9 +214,15 @@ public class UserService
...
@@ -118,9 +214,15 @@ public class UserService
}
}
}
}
// ─── Clear ───────────────────────────────────────────────────────
public
void
ClearUser
()
public
void
ClearUser
()
{
{
StopListening
();
CurrentUser
=
null
;
CurrentUser
=
null
;
IsFromCache
=
false
;
PlayerPrefs
.
DeleteKey
(
CACHE_KEY
);
PlayerPrefs
.
Save
();
OnUserChanged
?.
Invoke
(
null
);
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;
...
@@ -2,50 +2,36 @@ using System;
using
Supabase.Postgrest.Attributes
;
using
Supabase.Postgrest.Attributes
;
using
Supabase.Postgrest.Models
;
using
Supabase.Postgrest.Models
;
using
Newtonsoft.Json
;
[Table("users")]
[Table("users")]
public
class
User
:
BaseModel
public
class
User
:
BaseModel
{
{
[
PrimaryKey
(
"id"
)]
[
PrimaryKey
(
"id"
)]
[
JsonProperty
(
"id"
)]
public
string
Id
{
get
;
set
;
}
public
string
Id
{
get
;
set
;
}
[
Column
(
"user_name"
)]
[
Column
(
"user_name"
)]
[
JsonProperty
(
"user_name"
)]
public
string
Username
{
get
;
set
;
}
public
string
Username
{
get
;
set
;
}
[
Column
(
"rank"
)]
[
Column
(
"rank"
)]
[
JsonProperty
(
"rank"
)]
public
string
Rank
{
get
;
set
;
}
public
string
Rank
{
get
;
set
;
}
[
Column
(
"points"
)]
[
Column
(
"points"
)]
[
JsonProperty
(
"points"
)]
public
int
Points
{
get
;
set
;
}
=
0
;
public
int
Points
{
get
;
set
;
}
=
0
;
[
Column
(
"created_at"
)]
[
Column
(
"created_at"
)]
[
JsonProperty
(
"created_at"
)]
public
DateTime
CreatedAt
{
get
;
set
;
}
public
DateTime
CreatedAt
{
get
;
set
;
}
[
Column
(
"updated_at"
)]
[
Column
(
"updated_at"
)]
[
JsonProperty
(
"updated_at"
)]
public
DateTime
UpdatedAt
{
get
;
set
;
}
public
DateTime
UpdatedAt
{
get
;
set
;
}
[
Column
(
"school"
)]
[
JsonProperty
(
"school"
)]
public
string
School
{
get
;
set
;
}
[
Column
(
"grade"
)]
[
Column
(
"grade"
)]
[
JsonProperty
(
"grade"
)]
public
string
Grade
{
get
;
set
;
}
public
string
Grade
{
get
;
set
;
}
[
Column
(
"sex"
)]
[
Column
(
"sex"
)]
[
JsonProperty
(
"sex"
)]
public
string
Sex
{
get
;
set
;
}
public
string
Sex
{
get
;
set
;
}
[
Column
(
"age"
)]
[
Column
(
"curriculum"
)]
[
JsonProperty
(
"age"
)]
public
string
Curriculum
{
get
;
set
;
}
public
string
Age
{
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
...
@@ -47,6 +47,8 @@ public class HomeController : MonoBehaviour
private
void
OnUserChange
(
User
user
)
private
void
OnUserChange
(
User
user
)
{
{
print
(
$"[HomeController] Updating user info:
{
user
?.
Username
}
, Rank:
{
user
?.
Rank
}
, Points:
{
user
?.
Points
}
"
);
username
.
text
=
user
.
Username
;
username
.
text
=
user
.
Username
;
xp
.
text
=
user
.
Points
.
ToString
();
xp
.
text
=
user
.
Points
.
ToString
();
rank
.
text
=
AppUtils
.
RankToArabic
(
user
.
Rank
);
rank
.
text
=
AppUtils
.
RankToArabic
(
user
.
Rank
);
...
...
My project/Assets/App/UI/LeaderboardController.cs
View file @
d4ba32ba
...
@@ -14,6 +14,7 @@ public class LeaderboardController : MonoBehaviour
...
@@ -14,6 +14,7 @@ public class LeaderboardController : MonoBehaviour
var
root
=
leaderboardDocument
.
rootVisualElement
;
var
root
=
leaderboardDocument
.
rootVisualElement
;
leaderboardScrollView
=
root
.
Q
<
ScrollView
>(
"Leaderboard"
);
leaderboardScrollView
=
root
.
Q
<
ScrollView
>(
"Leaderboard"
);
UserService
.
Instance
.
OnUserChanged
+=
_
=>
LoadLeaderboard
();
LoadLeaderboard
().
Forget
();
LoadLeaderboard
().
Forget
();
}
}
...
...
My project/Assets/App/UI/LoginController.cs
View file @
d4ba32ba
...
@@ -7,9 +7,9 @@ public class LoginController : MonoBehaviour
...
@@ -7,9 +7,9 @@ public class LoginController : MonoBehaviour
private
TextField
username
;
private
TextField
username
;
private
DropdownField
grade
;
private
DropdownField
grade
;
private
DropdownField
school
;
private
DropdownField
sex
;
private
DropdownField
sex
;
private
TextField
age
;
private
DropdownField
term
;
private
DropdownField
curriculum
;
private
Button
register
;
private
Button
register
;
...
@@ -18,12 +18,10 @@ public class LoginController : MonoBehaviour
...
@@ -18,12 +18,10 @@ public class LoginController : MonoBehaviour
var
root
=
uIDocument
.
rootVisualElement
;
var
root
=
uIDocument
.
rootVisualElement
;
username
=
root
.
Q
<
TextField
>(
"Username"
);
username
=
root
.
Q
<
TextField
>(
"Username"
);
username
.
languageDirection
=
LanguageDirection
.
RTL
;
grade
=
root
.
Q
<
DropdownField
>(
"Grade"
);
grade
=
root
.
Q
<
DropdownField
>(
"Grade"
);
school
=
root
.
Q
<
DropdownField
>(
"School"
);
sex
=
root
.
Q
<
DropdownField
>(
"Sex"
);
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
=
root
.
Q
<
Button
>(
"Register"
);
register
.
clicked
+=
RegisterAnon
;
register
.
clicked
+=
RegisterAnon
;
...
@@ -44,7 +42,7 @@ public class LoginController : MonoBehaviour
...
@@ -44,7 +42,7 @@ public class LoginController : MonoBehaviour
return
;
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
=>
signUp
.
Switch
(
user
=>
{
{
AppRouter
.
GoToHome
();
AppRouter
.
GoToHome
();
...
...
My project/Assets/App/UI/ProfileController.cs
View file @
d4ba32ba
...
@@ -23,4 +23,9 @@ public class ProfileController : MonoBehaviour
...
@@ -23,4 +23,9 @@ public class ProfileController : MonoBehaviour
{
{
name
.
text
=
user
.
Username
;
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 @@
...
@@ -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="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: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>
<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: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:TextField>
</ui:VisualElement>
</ui:VisualElement>
...
@@ -34,8 +34,8 @@
...
@@ -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: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>
<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: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:Button text="" name="" 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: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:Button>
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
...
@@ -59,7 +59,7 @@
...
@@ -59,7 +59,7 @@
</ui:VisualElement>
</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: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: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:Button>
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
</ui:VisualElement>
...
...
My project/Assets/Prefabs/MCQ/Canvas.prefab
View file @
d4ba32ba
...
@@ -1116,10 +1116,10 @@ RectTransform:
...
@@ -1116,10 +1116,10 @@ RectTransform:
m_Children
:
[]
m_Children
:
[]
m_Father
:
{
fileID
:
5665338920870028329
}
m_Father
:
{
fileID
:
5665338920870028329
}
m_LocalEulerAnglesHint
:
{
x
:
0
,
y
:
0
,
z
:
0
}
m_LocalEulerAnglesHint
:
{
x
:
0
,
y
:
0
,
z
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
1
}
m_AnchorMax
:
{
x
:
0
,
y
:
0
}
m_AnchorMax
:
{
x
:
0
,
y
:
1
}
m_AnchoredPosition
:
{
x
:
22.572838
,
y
:
0
}
m_AnchoredPosition
:
{
x
:
22.572838
,
y
:
-25.96685
}
m_SizeDelta
:
{
x
:
51.9337
,
y
:
0
}
m_SizeDelta
:
{
x
:
0
,
y
:
51.9337
}
m_Pivot
:
{
x
:
0.5
,
y
:
0.5
}
m_Pivot
:
{
x
:
0.5
,
y
:
0.5
}
---
!u!222
&1367268893384981932
---
!u!222
&1367268893384981932
CanvasRenderer
:
CanvasRenderer
:
...
@@ -3222,10 +3222,10 @@ RectTransform:
...
@@ -3222,10 +3222,10 @@ RectTransform:
m_Children
:
[]
m_Children
:
[]
m_Father
:
{
fileID
:
5665338920870028329
}
m_Father
:
{
fileID
:
5665338920870028329
}
m_LocalEulerAnglesHint
:
{
x
:
0
,
y
:
0
,
z
:
0
}
m_LocalEulerAnglesHint
:
{
x
:
0
,
y
:
0
,
z
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
1
}
m_AnchorMax
:
{
x
:
0
,
y
:
0
}
m_AnchorMax
:
{
x
:
0
,
y
:
1
}
m_AnchoredPosition
:
{
x
:
83.71851
,
y
:
0
}
m_AnchoredPosition
:
{
x
:
83.71851
,
y
:
-25.96685
}
m_SizeDelta
:
{
x
:
51.9337
,
y
:
0
}
m_SizeDelta
:
{
x
:
0
,
y
:
51.9337
}
m_Pivot
:
{
x
:
0.5
,
y
:
0.5
}
m_Pivot
:
{
x
:
0.5
,
y
:
0.5
}
---
!u!222
&6044436298841298018
---
!u!222
&6044436298841298018
CanvasRenderer
:
CanvasRenderer
:
...
@@ -3406,10 +3406,10 @@ RectTransform:
...
@@ -3406,10 +3406,10 @@ RectTransform:
m_Children
:
[]
m_Children
:
[]
m_Father
:
{
fileID
:
5665338920870028329
}
m_Father
:
{
fileID
:
5665338920870028329
}
m_LocalEulerAnglesHint
:
{
x
:
0
,
y
:
0
,
z
:
0
}
m_LocalEulerAnglesHint
:
{
x
:
0
,
y
:
0
,
z
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
1
}
m_AnchorMax
:
{
x
:
0
,
y
:
0
}
m_AnchorMax
:
{
x
:
0
,
y
:
1
}
m_AnchoredPosition
:
{
x
:
267.15555
,
y
:
0
}
m_AnchoredPosition
:
{
x
:
267.15555
,
y
:
-25.96685
}
m_SizeDelta
:
{
x
:
51.9337
,
y
:
0
}
m_SizeDelta
:
{
x
:
0
,
y
:
51.9337
}
m_Pivot
:
{
x
:
0.5
,
y
:
0.5
}
m_Pivot
:
{
x
:
0.5
,
y
:
0.5
}
---
!u!222
&7681831561278972478
---
!u!222
&7681831561278972478
CanvasRenderer
:
CanvasRenderer
:
...
@@ -3793,10 +3793,10 @@ RectTransform:
...
@@ -3793,10 +3793,10 @@ RectTransform:
m_Children
:
[]
m_Children
:
[]
m_Father
:
{
fileID
:
5665338920870028329
}
m_Father
:
{
fileID
:
5665338920870028329
}
m_LocalEulerAnglesHint
:
{
x
:
0
,
y
:
0
,
z
:
0
}
m_LocalEulerAnglesHint
:
{
x
:
0
,
y
:
0
,
z
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
1
}
m_AnchorMax
:
{
x
:
0
,
y
:
0
}
m_AnchorMax
:
{
x
:
0
,
y
:
1
}
m_AnchoredPosition
:
{
x
:
206.00986
,
y
:
0
}
m_AnchoredPosition
:
{
x
:
206.00986
,
y
:
-25.96685
}
m_SizeDelta
:
{
x
:
51.9337
,
y
:
0
}
m_SizeDelta
:
{
x
:
0
,
y
:
51.9337
}
m_Pivot
:
{
x
:
0.5
,
y
:
0.5
}
m_Pivot
:
{
x
:
0.5
,
y
:
0.5
}
---
!u!222
&8749602556167845090
---
!u!222
&8749602556167845090
CanvasRenderer
:
CanvasRenderer
:
...
@@ -4052,10 +4052,10 @@ RectTransform:
...
@@ -4052,10 +4052,10 @@ RectTransform:
m_Children
:
[]
m_Children
:
[]
m_Father
:
{
fileID
:
5665338920870028329
}
m_Father
:
{
fileID
:
5665338920870028329
}
m_LocalEulerAnglesHint
:
{
x
:
0
,
y
:
0
,
z
:
0
}
m_LocalEulerAnglesHint
:
{
x
:
0
,
y
:
0
,
z
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
0
}
m_AnchorMin
:
{
x
:
0
,
y
:
1
}
m_AnchorMax
:
{
x
:
0
,
y
:
0
}
m_AnchorMax
:
{
x
:
0
,
y
:
1
}
m_AnchoredPosition
:
{
x
:
144.8642
,
y
:
0
}
m_AnchoredPosition
:
{
x
:
144.8642
,
y
:
-25.96685
}
m_SizeDelta
:
{
x
:
51.9337
,
y
:
0
}
m_SizeDelta
:
{
x
:
0
,
y
:
51.9337
}
m_Pivot
:
{
x
:
0.5
,
y
:
0.5
}
m_Pivot
:
{
x
:
0.5
,
y
:
0.5
}
---
!u!222
&8187631058268110602
---
!u!222
&8187631058268110602
CanvasRenderer
:
CanvasRenderer
:
...
...
My project/Assets/Prefabs/TF/Phone Art/TfUI.prefab
View file @
d4ba32ba
...
@@ -2374,8 +2374,8 @@ MonoBehaviour:
...
@@ -2374,8 +2374,8 @@ MonoBehaviour:
m_OnCullStateChanged
:
m_OnCullStateChanged
:
m_PersistentCalls
:
m_PersistentCalls
:
m_Calls
:
[]
m_Calls
:
[]
text
:
"
\u0646\u06
2C\u062D\u062A
\u0627\u0644\u0645\u0647\u0645\u0629
"
text
:
"
\u0646\u06
42\u0627\u0637\u0643
"
fontStack
:
{
fileID
:
11400000
,
guid
:
0029e5efb4c7a12f1ac9136de794e6dc
,
type
:
2
}
fontStack
:
{
fileID
:
11400000
,
guid
:
657d8db1dabff4325ae70686887b629b
,
type
:
2
}
appearance
:
{
fileID
:
11400000
,
guid
:
3a559cf5d653f05ea807e1be5655df92
,
type
:
2
}
appearance
:
{
fileID
:
11400000
,
guid
:
3a559cf5d653f05ea807e1be5655df92
,
type
:
2
}
fontSize
:
72
fontSize
:
72
baseDirection
:
2
baseDirection
:
2
...
...
My project/Assets/ScienceStreet/CS/Scripts/CsDemoBuilder.cs
View file @
d4ba32ba
...
@@ -163,7 +163,7 @@ namespace com.al_arcade.cs
...
@@ -163,7 +163,7 @@ namespace com.al_arcade.cs
ring
.
transform
.
localScale
=
new
Vector3
(
3.5f
,
0.08f
,
3.5f
);
ring
.
transform
.
localScale
=
new
Vector3
(
3.5f
,
0.08f
,
3.5f
);
Destroy
(
ring
.
GetComponent
<
Collider
>());
Destroy
(
ring
.
GetComponent
<
Collider
>());
var
ringMat
=
new
Material
(
shader
)
var
ringMat
=
new
Material
(
shader
)
{
color
=
SSColorPalette
.
WithAlpha
(
SSColorPalette
.
Accent
,
0.3f
)
};
{
color
=
SSColorPalette
.
WithAlpha
(
SSColorPalette
.
Accent
,
0.3f
)
};
ring
.
GetComponent
<
Renderer
>().
material
=
ringMat
;
ring
.
GetComponent
<
Renderer
>().
material
=
ringMat
;
ringMat
.
DOFade
(
0.1f
,
1.5f
).
SetEase
(
Ease
.
InOutSine
)
ringMat
.
DOFade
(
0.1f
,
1.5f
).
SetEase
(
Ease
.
InOutSine
)
.
SetLoops
(-
1
,
LoopType
.
Yoyo
);
.
SetLoops
(-
1
,
LoopType
.
Yoyo
);
...
@@ -215,7 +215,7 @@ namespace com.al_arcade.cs
...
@@ -215,7 +215,7 @@ namespace com.al_arcade.cs
line
.
transform
.
localScale
=
new
Vector3
(
0.02f
,
0.01f
,
15
);
line
.
transform
.
localScale
=
new
Vector3
(
0.02f
,
0.01f
,
15
);
Destroy
(
line
.
GetComponent
<
Collider
>());
Destroy
(
line
.
GetComponent
<
Collider
>());
line
.
GetComponent
<
Renderer
>().
material
=
new
Material
(
shader
)
line
.
GetComponent
<
Renderer
>().
material
=
new
Material
(
shader
)
{
color
=
SSColorPalette
.
WithAlpha
(
SSColorPalette
.
Primary
,
0.08f
)
};
{
color
=
SSColorPalette
.
WithAlpha
(
SSColorPalette
.
Primary
,
0.08f
)
};
}
}
for
(
float
z
=
-
2
;
z
<=
12
;
z
+=
3
)
for
(
float
z
=
-
2
;
z
<=
12
;
z
+=
3
)
{
{
...
@@ -225,7 +225,7 @@ namespace com.al_arcade.cs
...
@@ -225,7 +225,7 @@ namespace com.al_arcade.cs
line
.
transform
.
localScale
=
new
Vector3
(
20
,
0.01f
,
0.02f
);
line
.
transform
.
localScale
=
new
Vector3
(
20
,
0.01f
,
0.02f
);
Destroy
(
line
.
GetComponent
<
Collider
>());
Destroy
(
line
.
GetComponent
<
Collider
>());
line
.
GetComponent
<
Renderer
>().
material
=
new
Material
(
shader
)
line
.
GetComponent
<
Renderer
>().
material
=
new
Material
(
shader
)
{
color
=
SSColorPalette
.
WithAlpha
(
SSColorPalette
.
Primary
,
0.08f
)
};
{
color
=
SSColorPalette
.
WithAlpha
(
SSColorPalette
.
Primary
,
0.08f
)
};
}
}
...
@@ -296,108 +296,7 @@ namespace com.al_arcade.cs
...
@@ -296,108 +296,7 @@ namespace com.al_arcade.cs
{
{
return
new
CsQuestion
[]
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
...
@@ -83,9 +83,15 @@ namespace com.al_arcade.cs
if
(
uiManager
!=
null
&&
uiManager
.
isMusicOn
)
if
(
uiManager
!=
null
&&
uiManager
.
isMusicOn
)
SSAudioManager
.
EnsureInstance
().
PlayMusic
();
SSAudioManager
.
EnsureInstance
().
PlayMusic
();
var
filter
=
new
QuestionFilter
()
.
CurriculumId
(
0
)
.
SubjectId
(
0
)
.
GradeId
(
session
.
gradeId
)
.
Count
(
session
.
questionCount
)
.
Shuffle
(
true
);
yield
return
api
.
FetchCs
(
yield
return
api
.
FetchCs
(
session
.
buildType
,
session
.
classCode
,
filter
,
session
.
questionCount
,
session
.
gradeId
,
qs
=>
_questions
=
qs
,
qs
=>
_questions
=
qs
,
err
=>
onError
(
err
)
err
=>
onError
(
err
)
);
);
...
@@ -445,7 +451,7 @@ namespace com.al_arcade.cs
...
@@ -445,7 +451,7 @@ namespace com.al_arcade.cs
_targetGroup
.
RemoveMember
(
_targetGroup
.
Targets
[
1
].
Object
);
_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
()
private
IEnumerator
UnlockAfterCooldown
()
...
...
My project/Assets/ScienceStreet/CS/Scripts/CsPrefabBuilder.cs
View file @
d4ba32ba
...
@@ -308,44 +308,7 @@ namespace com.al_arcade.cs
...
@@ -308,44 +308,7 @@ namespace com.al_arcade.cs
{
{
return
new
CsQuestion
[]
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
...
@@ -207,7 +207,7 @@ public class CsSentence : MonoBehaviour
{
{
csWord
=
rect
.
gameObject
.
AddComponent
<
CsWordButton
>();
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
// Animate to the locally offset position
rect
.
DOLocalMove
(
targetPos
,
0.3f
).
SetEase
(
Ease
.
OutCubic
).
OnComplete
(()
=>
rect
.
DOLocalMove
(
targetPos
,
0.3f
).
SetEase
(
Ease
.
OutCubic
).
OnComplete
(()
=>
...
@@ -224,32 +224,6 @@ public class CsSentence : MonoBehaviour
...
@@ -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
);
if
(
_background
!=
null
)
Destroy
(
_background
);
}
}
...
@@ -365,8 +339,6 @@ public class CsSentence : MonoBehaviour
...
@@ -365,8 +339,6 @@ public class CsSentence : MonoBehaviour
cnvRt
.
offsetMax
=
Vector2
.
zero
;
cnvRt
.
offsetMax
=
Vector2
.
zero
;
cnvRt
.
offsetMin
=
Vector2
.
zero
;
cnvRt
.
offsetMin
=
Vector2
.
zero
;
_targetGroup
.
AddMember
(
cnvRt
,
1f
,
2f
);
canvasObj
.
transform
.
SetLocalPositionAndRotation
(
Vector3
.
zero
,
Quaternion
.
identity
);
canvasObj
.
transform
.
SetLocalPositionAndRotation
(
Vector3
.
zero
,
Quaternion
.
identity
);
var
_backgroundImage
=
backGO
.
AddComponent
<
Image
>();
var
_backgroundImage
=
backGO
.
AddComponent
<
Image
>();
_backgroundImage
.
type
=
Image
.
Type
.
Sliced
;
_backgroundImage
.
type
=
Image
.
Type
.
Sliced
;
...
@@ -378,6 +350,31 @@ public class CsSentence : MonoBehaviour
...
@@ -378,6 +350,31 @@ public class CsSentence : MonoBehaviour
cnvRt
.
DOLocalMoveY
(
0f
,
0.3f
).
SetEase
(
Ease
.
OutCubic
);
cnvRt
.
DOLocalMoveY
(
0f
,
0.3f
).
SetEase
(
Ease
.
OutCubic
);
_backgroundImage
.
DOFade
(
1f
,
0.3f
).
SetEase
(
Ease
.
OutCubic
).
From
(
0f
);
_backgroundImage
.
DOFade
(
1f
,
0.3f
).
SetEase
(
Ease
.
OutCubic
).
From
(
0f
);
StartCoroutine
(
AnimateTextIn
());
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
...
@@ -119,7 +119,7 @@ namespace com.al_arcade.mcq
...
@@ -119,7 +119,7 @@ namespace com.al_arcade.mcq
road
.
transform
.
position
=
new
Vector3
(
0
,
-
0.05f
,
200
);
road
.
transform
.
position
=
new
Vector3
(
0
,
-
0.05f
,
200
);
road
.
transform
.
localScale
=
new
Vector3
(
16
,
0.1f
,
500
);
road
.
transform
.
localScale
=
new
Vector3
(
16
,
0.1f
,
500
);
road
.
GetComponent
<
Renderer
>().
material
=
new
Material
(
shader
)
road
.
GetComponent
<
Renderer
>().
material
=
new
Material
(
shader
)
{
color
=
new
Color32
(
60
,
60
,
80
,
255
)
};
{
color
=
new
Color32
(
60
,
60
,
80
,
255
)
};
for
(
int
i
=
-
1
;
i
<=
1
;
i
+=
2
)
for
(
int
i
=
-
1
;
i
<=
1
;
i
+=
2
)
...
@@ -130,7 +130,7 @@ namespace com.al_arcade.mcq
...
@@ -130,7 +130,7 @@ namespace com.al_arcade.mcq
line
.
transform
.
localScale
=
new
Vector3
(
0.08f
,
0.01f
,
500
);
line
.
transform
.
localScale
=
new
Vector3
(
0.08f
,
0.01f
,
500
);
Destroy
(
line
.
GetComponent
<
Collider
>());
Destroy
(
line
.
GetComponent
<
Collider
>());
line
.
GetComponent
<
Renderer
>().
material
=
new
Material
(
shader
)
line
.
GetComponent
<
Renderer
>().
material
=
new
Material
(
shader
)
{
color
=
SSColorPalette
.
WithAlpha
(
SSColorPalette
.
Accent
,
0.4f
)
};
{
color
=
SSColorPalette
.
WithAlpha
(
SSColorPalette
.
Accent
,
0.4f
)
};
}
}
...
@@ -142,7 +142,7 @@ namespace com.al_arcade.mcq
...
@@ -142,7 +142,7 @@ namespace com.al_arcade.mcq
dash
.
transform
.
localScale
=
new
Vector3
(
0.05f
,
0.01f
,
2f
);
dash
.
transform
.
localScale
=
new
Vector3
(
0.05f
,
0.01f
,
2f
);
Destroy
(
dash
.
GetComponent
<
Collider
>());
Destroy
(
dash
.
GetComponent
<
Collider
>());
dash
.
GetComponent
<
Renderer
>().
material
=
new
Material
(
shader
)
dash
.
GetComponent
<
Renderer
>().
material
=
new
Material
(
shader
)
{
color
=
SSColorPalette
.
WithAlpha
(
Color
.
white
,
0.2f
)
};
{
color
=
SSColorPalette
.
WithAlpha
(
Color
.
white
,
0.2f
)
};
}
}
...
@@ -160,7 +160,7 @@ namespace com.al_arcade.mcq
...
@@ -160,7 +160,7 @@ namespace com.al_arcade.mcq
var
c
=
Color
.
Lerp
(
SSColorPalette
.
Primary
,
var
c
=
Color
.
Lerp
(
SSColorPalette
.
Primary
,
SSColorPalette
.
PrimaryLight
,
Random
.
value
);
SSColorPalette
.
PrimaryLight
,
Random
.
value
);
block
.
GetComponent
<
Renderer
>().
material
=
new
Material
(
shader
)
block
.
GetComponent
<
Renderer
>().
material
=
new
Material
(
shader
)
{
color
=
SSColorPalette
.
WithAlpha
(
c
,
0.4f
)
};
{
color
=
SSColorPalette
.
WithAlpha
(
c
,
0.4f
)
};
}
}
}
}
...
@@ -171,7 +171,7 @@ namespace com.al_arcade.mcq
...
@@ -171,7 +171,7 @@ namespace com.al_arcade.mcq
horizon
.
transform
.
localScale
=
new
Vector3
(
200
,
40
,
1
);
horizon
.
transform
.
localScale
=
new
Vector3
(
200
,
40
,
1
);
Destroy
(
horizon
.
GetComponent
<
Collider
>());
Destroy
(
horizon
.
GetComponent
<
Collider
>());
horizon
.
GetComponent
<
Renderer
>().
material
=
new
Material
(
shader
)
horizon
.
GetComponent
<
Renderer
>().
material
=
new
Material
(
shader
)
{
color
=
new
Color32
(
100
,
120
,
180
,
255
)
};
{
color
=
new
Color32
(
100
,
120
,
180
,
255
)
};
}
}
private
void
BuildPlayer
()
private
void
BuildPlayer
()
...
@@ -274,21 +274,7 @@ namespace com.al_arcade.mcq
...
@@ -274,21 +274,7 @@ namespace com.al_arcade.mcq
{
{
return
new
McqQuestion
[]
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
...
@@ -73,9 +73,15 @@ namespace com.al_arcade.mcq
var
session
=
SSGameSession
.
EnsureInstance
();
var
session
=
SSGameSession
.
EnsureInstance
();
var
api
=
SSApiManager
.
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
(
yield
return
api
.
FetchMcq
(
session
.
buildType
,
session
.
classCode
,
filter
,
session
.
questionCount
,
session
.
gradeId
,
qs
=>
_questions
=
qs
,
qs
=>
_questions
=
qs
,
err
=>
onError
(
err
)
err
=>
onError
(
err
)
);
);
...
@@ -153,7 +159,7 @@ namespace com.al_arcade.mcq
...
@@ -153,7 +159,7 @@ namespace com.al_arcade.mcq
BeginGameplay
();
BeginGameplay
();
}
}
public
void
ResetGame
()
public
override
void
ResetGame
()
{
{
ResetBaseState
();
ResetBaseState
();
_state
=
McqGameState
.
Idle
;
_state
=
McqGameState
.
Idle
;
...
@@ -311,8 +317,9 @@ namespace com.al_arcade.mcq
...
@@ -311,8 +317,9 @@ namespace com.al_arcade.mcq
// ─── Gate Spawning ───────────────────────────────────────────────────
// ─── Gate Spawning ───────────────────────────────────────────────────
private
void
SpawnGates
(
McqQuestion
question
)
private
void
SpawnGates
(
McqQuestion
question
)
{
{
string
[]
answers
=
question
.
GetShuffledAnswers
(
out
int
correctIdx
);
var
shuffled
=
McqHelper
.
Shuffle
(
question
);
_correctGateIndex
=
correctIdx
;
string
[]
answers
=
shuffled
.
answers
;
_correctGateIndex
=
shuffled
.
correctIndex
;
_activeGates
.
Clear
();
_activeGates
.
Clear
();
if
(
questionDisplay
!=
null
)
if
(
questionDisplay
!=
null
)
...
@@ -327,7 +334,7 @@ namespace com.al_arcade.mcq
...
@@ -327,7 +334,7 @@ namespace com.al_arcade.mcq
for
(
int
i
=
0
;
i
<
answers
.
Length
;
i
++)
for
(
int
i
=
0
;
i
<
answers
.
Length
;
i
++)
{
{
Vector3
gatePos
=
basePos
+
Vector3
.
right
*
(
startX
+
i
*
gateSpacing
);
Vector3
gatePos
=
basePos
+
Vector3
.
right
*
(
startX
+
i
*
gateSpacing
);
var
gate
=
CreateGate
(
gatePos
,
i
,
answers
[
i
],
i
==
correctId
x
);
var
gate
=
CreateGate
(
gatePos
,
i
,
answers
[
i
],
i
==
_correctGateInde
x
);
_activeGates
.
Add
(
gate
);
_activeGates
.
Add
(
gate
);
gate
.
transform
.
DOScale
(
Vector3
.
one
,
0.5f
)
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
...
@@ -328,15 +328,7 @@ namespace com.al_arcade.mcq
{
{
return
new
McqQuestion
[]
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
using
UnityEngine
;
using
UnityEngine.UI
;
using
TMPro
;
using
System
;
using
com.al_arcade.shared
;
using
LightSide
;
using
DG.Tweening
;
public
class
QuestionUi
:
MonoBehaviour
{
public
static
QuestionUi
Instance
;
[
SerializeField
]
private
CanvasGroup
questionPanelCanvasGroup
;
[
SerializeField
]
private
LayoutGroup
questionContainer
;
[
SerializeField
]
private
Image
questionImageComponent
;
[
SerializeField
]
private
UniText
questionTextComponent
;
[
SerializeField
]
private
Transform
answersGrid
;
[
SerializeField
]
private
AnswerButtonUI
[]
answerButtons
=
new
AnswerButtonUI
[
4
];
[
SerializeField
]
private
LayoutGroup
answersContainer
;
[
SerializeField
]
private
CanvasGroup
gameUICanvasGroup
;
[
Header
(
"Animation Settings"
)]
[
SerializeField
]
private
float
questionEntranceDuration
=
0.6f
;
[
SerializeField
]
private
float
answerStaggerDelay
=
0.1f
;
[
SerializeField
]
private
float
answerEntranceDuration
=
0.4f
;
[
SerializeField
]
private
float
wrongAnswerShakeDuration
=
0.4f
;
[
SerializeField
]
private
float
wrongAnswerShakeStrength
=
15f
;
private
McqQuestion
currentQuestion
;
private
int
correctAnswerIndex
=
-
1
;
private
bool
hasAnswered
=
false
;
private
Action
<
int
,
bool
>
onAnswerSelected
;
private
Sequence
questionSequence
;
private
Sequence
[]
answerSequences
=
new
Sequence
[
4
];
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!"
);
}
// Auto-find CanvasGroup if not assigned
if
(
questionPanelCanvasGroup
==
null
)
{
questionPanelCanvasGroup
=
GetComponent
<
CanvasGroup
>();
}
if
(
gameUICanvasGroup
==
null
&&
answersContainer
!=
null
)
{
gameUICanvasGroup
=
answersContainer
.
GetComponentInParent
<
CanvasGroup
>();
}
}
/// <summary>
/// Display a multiple choice question with all its data + animations
/// </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
;
// Kill previous animations
DOTween
.
Kill
(
questionSequence
);
for
(
int
i
=
0
;
i
<
4
;
i
++)
{
DOTween
.
Kill
(
answerSequences
[
i
]);
}
// 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
);
// Rebuild layout hierarchy
LayoutRebuilder
.
ForceRebuildLayoutImmediate
(
questionContainer
.
GetComponent
<
RectTransform
>());
LayoutRebuilder
.
ForceRebuildLayoutImmediate
(
answersContainer
.
GetComponent
<
RectTransform
>());
// Play entrance animations
// AnimateQuestionEntrance();
AnimateAnswersEntrance
();
}
/// <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
);
}
}
/// Animate question entrance (fade only, no position change)
/// </summary>
private
void
AnimateQuestionEntrance
()
{
if
(
questionPanelCanvasGroup
==
null
)
return
;
// Reset state
questionPanelCanvasGroup
.
alpha
=
0
;
questionSequence
=
DOTween
.
Sequence
();
questionSequence
.
Append
(
questionPanelCanvasGroup
.
DOFade
(
1f
,
questionEntranceDuration
));
}
/// <summary>
/// Animate answers entrance (staggered, spring pop)
/// </summary>
private
void
AnimateAnswersEntrance
()
{
for
(
int
i
=
0
;
i
<
4
;
i
++)
{
AnimateAnswerButtonEntrance
(
i
);
}
}
private
void
AnimateAnswerButtonEntrance
(
int
index
)
{
RectTransform
answerRect
=
answerButtons
[
index
].
GetComponent
<
RectTransform
>();
CanvasGroup
answerCanvasGroup
=
answerButtons
[
index
].
GetComponent
<
CanvasGroup
>();
if
(
answerCanvasGroup
==
null
)
{
answerCanvasGroup
=
answerButtons
[
index
].
gameObject
.
AddComponent
<
CanvasGroup
>();
}
// Reset state
answerCanvasGroup
.
alpha
=
0
;
answerRect
.
localScale
=
Vector3
.
zero
;
float
delay
=
index
*
answerStaggerDelay
;
answerSequences
[
index
]
=
DOTween
.
Sequence
();
answerSequences
[
index
].
AppendInterval
(
delay
);
answerSequences
[
index
].
Append
(
answerCanvasGroup
.
DOFade
(
1f
,
answerEntranceDuration
*
0.5f
));
answerSequences
[
index
].
Join
(
answerRect
.
DOScale
(
1f
,
answerEntranceDuration
)
.
SetEase
(
Ease
.
OutBack
));
}
/// <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
);
AnimateAnswerSelection
(
i
,
isCorrect
);
}
else
if
(
i
==
correctAnswerIndex
&&
!
isCorrect
)
{
answerButtons
[
i
].
SetCorrect
();
AnimateCorrectAnswer
(
i
);
}
else
{
answerButtons
[
i
].
SetDisabled
();
AnimateDisabledAnswer
(
i
);
}
}
// Wrong answer screen shake
if
(!
isCorrect
)
{
ShakeGameUI
();
}
}
/// <summary>
/// Animate selected answer (pop + scale)
/// </summary>
private
void
AnimateAnswerSelection
(
int
index
,
bool
isCorrect
)
{
RectTransform
answerRect
=
answerButtons
[
index
].
GetComponent
<
RectTransform
>();
DOTween
.
Sequence
()
.
Append
(
answerRect
.
DOScale
(
1.15f
,
0.15f
).
SetEase
(
Ease
.
OutBack
))
.
Append
(
answerRect
.
DOScale
(
1f
,
0.1f
).
SetEase
(
Ease
.
InQuad
));
}
/// <summary>
/// Animate correct answer highlight (glow + pulse)
/// </summary>
private
void
AnimateCorrectAnswer
(
int
index
)
{
RectTransform
answerRect
=
answerButtons
[
index
].
GetComponent
<
RectTransform
>();
DOTween
.
Sequence
()
.
Append
(
answerRect
.
DOScale
(
1.05f
,
0.2f
).
SetEase
(
Ease
.
OutBack
))
.
SetLoops
(-
1
,
LoopType
.
Yoyo
)
.
SetId
(
$"correctAnswer_
{
index
}
"
);
}
/// <summary>
/// Animate disabled answer (fade out slightly)
/// </summary>
private
void
AnimateDisabledAnswer
(
int
index
)
{
CanvasGroup
answerCanvasGroup
=
answerButtons
[
index
].
GetComponent
<
CanvasGroup
>();
if
(
answerCanvasGroup
==
null
)
return
;
answerCanvasGroup
.
DOFade
(
0.5f
,
0.3f
).
SetEase
(
Ease
.
InQuad
);
}
/// <summary>
/// Screen shake for wrong answer (shake GameUI)
/// </summary>
private
void
ShakeGameUI
()
{
if
(
gameUICanvasGroup
==
null
)
return
;
RectTransform
gameUIRect
=
gameUICanvasGroup
.
GetComponent
<
RectTransform
>();
gameUIRect
.
DOShakeAnchorPos
(
wrongAnswerShakeDuration
,
new
Vector2
(
wrongAnswerShakeStrength
,
wrongAnswerShakeStrength
*
0.5f
),
vibrato
:
12
,
randomness
:
90f
,
snapping
:
false
).
SetEase
(
Ease
.
OutQuad
);
}
/// <summary>
/// Reset all buttons for next question
/// </summary>
public
void
ResetForNextQuestion
()
{
hasAnswered
=
false
;
correctAnswerIndex
=
-
1
;
// Kill pulsing animations
for
(
int
i
=
0
;
i
<
4
;
i
++)
{
DOTween
.
Kill
(
$"correctAnswer_
{
i
}
"
);
answerButtons
[
i
].
Reset
();
}
// Fade out question
//AnimateQuestionExit();
}
/// <summary>
/// Animate question exit (fade out only, reset position)
/// </summary>
private
void
AnimateQuestionExit
()
{
if
(
questionPanelCanvasGroup
==
null
)
return
;
RectTransform
questionRect
=
questionContainer
.
GetComponent
<
RectTransform
>();
DOTween
.
Sequence
()
.
Append
(
questionPanelCanvasGroup
.
DOFade
(
0f
,
0.3f
))
.
OnComplete
(()
=>
{
// Reset position back to original after fade
if
(
questionRect
!=
null
)
questionRect
.
anchoredPosition
=
Vector2
.
zero
;
});
}
/// <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/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
fileFormatVersion: 2
guid: 68aee830395e108478a9643b20de1689
guid: 8b3bc4d6f06a2d94baff2c7e3214a202
\ No newline at end of file
\ 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
fileFormatVersion: 2
guid: 8b4109c5dc3e965418f041d490b09d96
guid: 1bacf1883f436a74caa55a658a8746e3
\ No newline at end of file
\ 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
fileFormatVersion: 2
guid: cb8a50d45c5491e45a8c75803f8df92c
guid: 52e062c4425a8ca4ea8b284d19376b38
\ No newline at end of file
\ No newline at end of file
My project/Assets/ScienceStreet/Shared/Scripts/SSApiManager.cs
View file @
d4ba32ba
using
System
;
using
System
;
using
System.Collections
;
using
System.Collections
;
using
System.Collections.Generic
;
using
System.Collections.Generic
;
using
System.Text
;
using
UnityEngine
;
using
UnityEngine
;
using
UnityEngine.Networking
;
using
UnityEngine.Networking
;
using
Newtonsoft.Json
;
using
Newtonsoft.Json
;
namespace
com.al_arcade.shared
namespace
com.al_arcade.shared
{
{
/// <summary>
/// Science Street Question Bank API v3.0 Client.
/// Singleton MonoBehaviour — communicates with api.php.
/// </summary>
public
class
SSApiManager
:
MonoBehaviour
public
class
SSApiManager
:
MonoBehaviour
{
{
public
static
SSApiManager
Instance
{
get
;
private
set
;
}
public
static
SSApiManager
Instance
{
get
;
private
set
;
}
[
Header
(
"API Configuration"
)]
[
Header
(
"API Configuration"
)]
[
SerializeField
]
private
string
baseUrl
=
[
SerializeField
]
private
string
baseUrl
=
"https://phpserver.caprover.al-arcade.com/ssbook/api.php"
;
"https://phpserver.caprover.al-arcade.com/ssbook/api.php"
;
[
SerializeField
]
private
int
timeoutSeconds
=
15
;
[
SerializeField
]
private
int
timeoutSeconds
=
15
;
// ═══════════════════════════════════════════════════
// LIFECYCLE
// ═══════════════════════════════════════════════════
private
void
Awake
()
private
void
Awake
()
{
{
if
(
Instance
!=
null
&&
Instance
!=
this
)
if
(
Instance
!=
null
&&
Instance
!=
this
)
...
@@ -31,7 +37,6 @@ namespace com.al_arcade.shared
...
@@ -31,7 +37,6 @@ namespace com.al_arcade.shared
DontDestroyOnLoad
(
gameObject
);
DontDestroyOnLoad
(
gameObject
);
}
}
public
static
SSApiManager
EnsureInstance
()
public
static
SSApiManager
EnsureInstance
()
{
{
if
(
Instance
!=
null
)
return
Instance
;
if
(
Instance
!=
null
)
return
Instance
;
...
@@ -41,8 +46,10 @@ namespace com.al_arcade.shared
...
@@ -41,8 +46,10 @@ namespace com.al_arcade.shared
return
Instance
;
return
Instance
;
}
}
// ═══════════════════════════════════════════════════
public
IEnumerator
GetRequest
(
string
action
,
// LOW-LEVEL HTTP HELPERS
// ═══════════════════════════════════════════════════
private
IEnumerator
GetRequest
(
string
action
,
Dictionary
<
string
,
string
>
parameters
,
Dictionary
<
string
,
string
>
parameters
,
Action
<
string
>
onSuccess
,
Action
<
string
>
onSuccess
,
Action
<
string
>
onError
)
Action
<
string
>
onError
)
...
@@ -65,173 +72,456 @@ namespace com.al_arcade.shared
...
@@ -65,173 +72,456 @@ namespace com.al_arcade.shared
if
(
req
.
result
!=
UnityWebRequest
.
Result
.
Success
)
if
(
req
.
result
!=
UnityWebRequest
.
Result
.
Success
)
{
{
string
errorMsg
=
req
.
error
??
"Network error"
;
string
errorMsg
=
req
.
error
??
"Network error"
;
Debug
.
LogWarning
(
$"[SSApi]
Request failed:
{
errorMsg
}
"
);
Debug
.
LogWarning
(
$"[SSApi]
GET failed:
{
errorMsg
}
\n URL:
{
url
}
"
);
onError
?.
Invoke
(
errorMsg
);
onError
?.
Invoke
(
errorMsg
);
yield
break
;
yield
break
;
}
}
string
json
=
req
.
downloadHandler
.
text
;
onSuccess
?.
Invoke
(
req
.
downloadHandler
.
text
);
onSuccess
?.
Invoke
(
json
);
}
}
private
IEnumerator
PostForm
(
string
action
,
Dictionary
<
string
,
string
>
fields
,
Action
<
string
>
onSuccess
,
Action
<
string
>
onError
)
{
var
form
=
new
WWWForm
();
form
.
AddField
(
"action"
,
action
);
if
(
fields
!=
null
)
{
foreach
(
var
kv
in
fields
)
form
.
AddField
(
kv
.
Key
,
kv
.
Value
);
}
using
var
req
=
UnityWebRequest
.
Post
(
baseUrl
,
form
);
req
.
timeout
=
timeoutSeconds
;
public
IEnumerator
Ping
(
Action
<
bool
>
onResult
)
yield
return
req
.
SendWebRequest
();
if
(
req
.
result
!=
UnityWebRequest
.
Result
.
Success
)
{
Debug
.
LogWarning
(
$"[SSApi] POST form failed:
{
req
.
error
}
"
);
onError
?.
Invoke
(
req
.
error
??
"Network error"
);
yield
break
;
}
onSuccess
?.
Invoke
(
req
.
downloadHandler
.
text
);
}
private
IEnumerator
PostJson
(
string
action
,
string
jsonBody
,
Action
<
string
>
onSuccess
,
Action
<
string
>
onError
)
{
{
yield
return
GetRequest
(
"ping"
,
null
,
string
url
=
baseUrl
+
"?action="
+
UnityWebRequest
.
EscapeURL
(
action
);
json
=>
onResult
?.
Invoke
(
true
),
byte
[]
bodyRaw
=
Encoding
.
UTF8
.
GetBytes
(
jsonBody
);
err
=>
onResult
?.
Invoke
(
false
));
using
var
req
=
new
UnityWebRequest
(
url
,
"POST"
);
req
.
uploadHandler
=
new
UploadHandlerRaw
(
bodyRaw
);
req
.
downloadHandler
=
new
DownloadHandlerBuffer
();
req
.
SetRequestHeader
(
"Content-Type"
,
"application/json"
);
req
.
timeout
=
timeoutSeconds
;
yield
return
req
.
SendWebRequest
();
if
(
req
.
result
!=
UnityWebRequest
.
Result
.
Success
)
{
Debug
.
LogWarning
(
$"[SSApi] POST json failed:
{
req
.
error
}
"
);
onError
?.
Invoke
(
req
.
error
??
"Network error"
);
yield
break
;
}
onSuccess
?.
Invoke
(
req
.
downloadHandler
.
text
);
}
}
private
T
Parse
<
T
>(
string
json
)
where
T
:
class
{
return
JsonConvert
.
DeserializeObject
<
T
>(
json
);
}
public
IEnumerator
ValidateClassCode
(
string
code
,
// ═══════════════════════════════════════════════════
Action
<
ClassInfo
>
onSuccess
,
// PING
Action
<
string
>
onError
)
// ═══════════════════════════════════════════════════
public
IEnumerator
Ping
(
Action
<
PingResponse
>
onSuccess
,
Action
<
string
>
onError
=
null
)
{
yield
return
GetRequest
(
"ping"
,
null
,
json
=>
{
var
r
=
Parse
<
PingResponse
>(
json
);
if
(
r
!=
null
&&
r
.
success
)
onSuccess
?.
Invoke
(
r
);
else
onError
?.
Invoke
(
r
?.
error
??
"Ping failed"
);
},
err
=>
onError
?.
Invoke
(
err
));
}
/// <summary>Simple bool ping — backwards compatible.</summary>
public
IEnumerator
Ping
(
Action
<
bool
>
onResult
)
{
{
var
p
=
new
Dictionary
<
string
,
string
>
{
{
"class_code"
,
code
}
};
yield
return
Ping
(
_
=>
onResult
?.
Invoke
(
true
),
_
=>
onResult
?.
Invoke
(
false
));
}
yield
return
GetRequest
(
"validate_class"
,
p
,
// ═══════════════════════════════════════════════════
// TAXONOMY: Curricula
// ═══════════════════════════════════════════════════
public
IEnumerator
GetCurricula
(
Action
<
CurriculumInfo
[
]>
onSuccess
,
Action
<
string
>
onError
)
{
yield
return
GetRequest
(
"get_curricula"
,
null
,
json
=>
json
=>
{
{
try
var
r
=
Parse
<
CurriculaResponse
>(
json
);
{
if
(
r
is
{
success
:
true
})
var
resp
=
JsonConvert
.
DeserializeObject
<
ValidateClassResponse
>(
json
);
onSuccess
?.
Invoke
(
r
.
curricula
??
Array
.
Empty
<
CurriculumInfo
>());
if
(
resp
.
success
&&
resp
.
classInfo
!=
null
)
else
onSuccess
?.
Invoke
(
resp
.
classInfo
);
onError
?.
Invoke
(
r
?.
error
??
"Failed to load curricula"
);
else
onError
?.
Invoke
(
resp
.
error
??
"Validation failed"
);
}
catch
(
Exception
e
)
{
Debug
.
LogError
(
$"[SSApi] Parse error:
{
e
.
Message
}
"
);
onError
?.
Invoke
(
"Failed to parse server response"
);
}
},
},
onError
);
onError
);
}
}
// ═══════════════════════════════════════════════════
// TAXONOMY: Grades
// ═══════════════════════════════════════════════════
public
IEnumerator
GetGrades
(
Action
<
GradeInfo
[
]>
onSuccess
,
public
IEnumerator
GetGrades
(
Action
<
GradeInfo
[
]>
onSuccess
,
Action
<
string
>
onError
)
Action
<
string
>
onError
)
{
{
yield
return
GetRequest
(
"get_grades"
,
null
,
yield
return
GetRequest
(
"get_grades"
,
null
,
json
=>
json
=>
{
{
try
var
r
=
Parse
<
GradesResponse
>(
json
);
{
if
(
r
is
{
success
:
true
})
var
resp
=
JsonConvert
.
DeserializeObject
<
GradesResponse
>(
json
);
onSuccess
?.
Invoke
(
r
.
grades
??
Array
.
Empty
<
GradeInfo
>());
if
(
resp
.
success
)
else
onSuccess
?.
Invoke
(
resp
.
grades
??
Array
.
Empty
<
GradeInfo
>());
onError
?.
Invoke
(
r
?.
error
??
"Failed to load grades"
);
else
onError
?.
Invoke
(
resp
.
error
??
"Failed to load grades"
);
}
catch
(
Exception
e
)
{
onError
?.
Invoke
(
"Parse error: "
+
e
.
Message
);
}
},
},
onError
);
onError
);
}
}
// ═══════════════════════════════════════════════════
// TAXONOMY: Terms
// ═══════════════════════════════════════════════════
public
IEnumerator
GetTerms
(
Action
<
TermInfo
[
]>
onSuccess
,
Action
<
string
>
onError
)
{
yield
return
GetRequest
(
"get_terms"
,
null
,
json
=>
{
var
r
=
Parse
<
TermsResponse
>(
json
);
if
(
r
is
{
success
:
true
})
onSuccess
?.
Invoke
(
r
.
terms
??
Array
.
Empty
<
TermInfo
>());
else
onError
?.
Invoke
(
r
?.
error
??
"Failed to load terms"
);
},
onError
);
}
public
IEnumerator
FetchMcq
(
string
buildType
,
string
classCode
,
// ═══════════════════════════════════════════════════
int
count
,
int
gradeId
,
// TAXONOMY: Subjects
Action
<
McqQuestion
[
]>
onSuccess
,
// ═══════════════════════════════════════════════════
Action
<
string
>
onError
)
public
IEnumerator
GetSubjects
(
Action
<
SubjectInfo
[
]>
onSuccess
,
Action
<
string
>
onError
,
int
curriculumId
=
0
)
{
{
var
p
=
new
Dictionary
<
string
,
string
>
var
p
=
new
Dictionary
<
string
,
string
>();
{
if
(
curriculumId
>
0
)
{
"build_type"
,
buildType
},
p
[
"curriculum_id"
]
=
curriculumId
.
ToString
();
{
"count"
,
count
.
ToString
()
}
};
yield
return
GetRequest
(
"get_subjects"
,
p
,
json
=>
{
var
r
=
Parse
<
SubjectsResponse
>(
json
);
if
(
r
is
{
success
:
true
})
onSuccess
?.
Invoke
(
r
.
subjects
??
Array
.
Empty
<
SubjectInfo
>());
else
onError
?.
Invoke
(
r
?.
error
??
"Failed to load subjects"
);
},
onError
);
}
// ═══════════════════════════════════════════════════
// TAXONOMY: Chapters
// ═══════════════════════════════════════════════════
public
IEnumerator
GetChapters
(
Action
<
ChapterInfo
[
]>
onSuccess
,
Action
<
string
>
onError
,
int
subjectId
=
0
,
int
gradeId
=
0
,
int
termId
=
0
)
{
var
p
=
new
Dictionary
<
string
,
string
>();
if
(
subjectId
>
0
)
p
[
"subject_id"
]
=
subjectId
.
ToString
();
if
(
gradeId
>
0
)
p
[
"grade_id"
]
=
gradeId
.
ToString
();
if
(
gradeId
>
0
)
p
[
"grade_id"
]
=
gradeId
.
ToString
();
if
(
buildType
==
"teacher"
&&
!
string
.
IsNullOrEmpty
(
classCode
))
if
(
termId
>
0
)
p
[
"term_id"
]
=
termId
.
ToString
();
p
[
"class_code"
]
=
classCode
;
yield
return
GetRequest
(
"get_
mcq
"
,
p
,
yield
return
GetRequest
(
"get_
chapters
"
,
p
,
json
=>
json
=>
{
{
try
var
r
=
Parse
<
ChaptersResponse
>(
json
);
{
if
(
r
is
{
success
:
true
})
var
resp
=
JsonConvert
.
DeserializeObject
<
ApiResponse
<
McqQuestion
>>(
json
);
onSuccess
?.
Invoke
(
r
.
chapters
??
Array
.
Empty
<
ChapterInfo
>());
if
(
resp
.
success
&&
resp
.
questions
!=
null
)
else
onSuccess
?.
Invoke
(
resp
.
questions
);
onError
?.
Invoke
(
r
?.
error
??
"Failed to load chapters"
);
else
onError
?.
Invoke
(
resp
.
error
??
"No questions returned"
);
}
catch
(
Exception
e
)
{
onError
?.
Invoke
(
"Parse error: "
+
e
.
Message
);
}
},
},
onError
);
onError
);
}
}
// ═══════════════════════════════════════════════════
// TAXONOMY: Full Tree
// ═══════════════════════════════════════════════════
public
IEnumerator
GetTaxonomyTree
(
Action
<
TreeCurriculum
[
]>
onSuccess
,
Action
<
string
>
onError
,
int
curriculumId
=
0
)
{
var
p
=
new
Dictionary
<
string
,
string
>();
if
(
curriculumId
>
0
)
p
[
"curriculum_id"
]
=
curriculumId
.
ToString
();
public
IEnumerator
FetchTf
(
string
buildType
,
string
classCode
,
yield
return
GetRequest
(
"get_taxonomy_tree"
,
p
,
int
count
,
int
gradeId
,
json
=>
Action
<
TfQuestion
[
]>
onSuccess
,
{
Action
<
string
>
onError
)
var
r
=
Parse
<
TaxonomyTreeResponse
>(
json
);
if
(
r
is
{
success
:
true
})
onSuccess
?.
Invoke
(
r
.
tree
??
Array
.
Empty
<
TreeCurriculum
>());
else
onError
?.
Invoke
(
r
?.
error
??
"Failed to load taxonomy tree"
);
},
onError
);
}
// ═══════════════════════════════════════════════════
// QUESTIONS: MCQ
// ═══════════════════════════════════════════════════
public
IEnumerator
FetchMcq
(
QuestionFilter
filter
,
Action
<
McqQuestion
[
]>
onSuccess
,
Action
<
string
>
onError
)
{
{
var
p
=
new
Dictionary
<
string
,
string
>
yield
return
GetRequest
(
"get_mcq"
,
filter
.
Build
(),
{
json
=>
{
"build_type"
,
buildType
},
{
{
"count"
,
count
.
ToString
()
}
var
r
=
Parse
<
McqResponse
>(
json
);
};
if
(
r
is
{
success
:
true
}
&&
r
.
questions
!=
null
)
if
(
gradeId
>
0
)
p
[
"grade_id"
]
=
gradeId
.
ToString
();
onSuccess
?.
Invoke
(
r
.
questions
);
if
(
buildType
==
"teacher"
&&
!
string
.
IsNullOrEmpty
(
classCode
))
else
p
[
"class_code"
]
=
classCode
;
onError
?.
Invoke
(
r
?.
error
??
"No MCQ questions returned"
);
},
onError
);
}
/// <summary>Quick overload: fetch MCQ by chapter id.</summary>
public
IEnumerator
FetchMcq
(
int
chapterId
,
int
count
,
Action
<
McqQuestion
[
]>
onSuccess
,
Action
<
string
>
onError
)
{
yield
return
FetchMcq
(
QuestionFilter
.
ForChapter
(
chapterId
,
count
),
onSuccess
,
onError
);
}
yield
return
GetRequest
(
"get_tf"
,
p
,
// ═══════════════════════════════════════════════════
// QUESTIONS: True/False
// ═══════════════════════════════════════════════════
public
IEnumerator
FetchTf
(
QuestionFilter
filter
,
Action
<
TfQuestion
[
]>
onSuccess
,
Action
<
string
>
onError
)
{
yield
return
GetRequest
(
"get_tf"
,
filter
.
Build
(),
json
=>
json
=>
{
{
try
var
r
=
Parse
<
TfResponse
>(
json
);
{
if
(
r
is
{
success
:
true
}
&&
r
.
questions
!=
null
)
var
resp
=
JsonConvert
.
DeserializeObject
<
ApiResponse
<
TfQuestion
>>(
json
);
onSuccess
?.
Invoke
(
r
.
questions
);
if
(
resp
.
success
&&
resp
.
questions
!=
null
)
else
onSuccess
?.
Invoke
(
resp
.
questions
);
onError
?.
Invoke
(
r
?.
error
??
"No TF questions returned"
);
else
onError
?.
Invoke
(
resp
.
error
??
"No questions returned"
);
}
catch
(
Exception
e
)
{
onError
?.
Invoke
(
"Parse error: "
+
e
.
Message
);
}
},
},
onError
);
onError
);
}
}
/// <summary>Quick overload: fetch TF by chapter id.</summary>
public
IEnumerator
FetchTf
(
int
chapterId
,
int
count
,
Action
<
TfQuestion
[
]>
onSuccess
,
Action
<
string
>
onError
)
{
yield
return
FetchTf
(
QuestionFilter
.
ForChapter
(
chapterId
,
count
),
onSuccess
,
onError
);
}
public
IEnumerator
FetchCs
(
string
buildType
,
string
classCode
,
// ═══════════════════════════════════════════════════
int
count
,
int
gradeId
,
// QUESTIONS: Correct the Sentence
Action
<
CsQuestion
[
]>
onSuccess
,
// ═══════════════════════════════════════════════════
Action
<
string
>
onError
)
public
IEnumerator
FetchCs
(
QuestionFilter
filter
,
Action
<
CsQuestion
[
]>
onSuccess
,
Action
<
string
>
onError
)
{
yield
return
GetRequest
(
"get_cs"
,
filter
.
Build
(),
json
=>
{
var
r
=
Parse
<
CsResponse
>(
json
);
if
(
r
is
{
success
:
true
}
&&
r
.
questions
!=
null
)
onSuccess
?.
Invoke
(
r
.
questions
);
else
onError
?.
Invoke
(
r
?.
error
??
"No CS questions returned"
);
},
onError
);
}
/// <summary>Quick overload: fetch CS by chapter id.</summary>
public
IEnumerator
FetchCs
(
int
chapterId
,
int
count
,
Action
<
CsQuestion
[
]>
onSuccess
,
Action
<
string
>
onError
)
{
yield
return
FetchCs
(
QuestionFilter
.
ForChapter
(
chapterId
,
count
),
onSuccess
,
onError
);
}
// ═══════════════════════════════════════════════════
// QUESTIONS: Mixed (MCQ + TF + CS interleaved)
// ═══════════════════════════════════════════════════
public
IEnumerator
FetchMixed
(
QuestionFilter
filter
,
Action
<
MixedResponse
>
onSuccess
,
Action
<
string
>
onError
)
{
yield
return
GetRequest
(
"get_mixed"
,
filter
.
Build
(),
json
=>
{
var
r
=
Parse
<
MixedResponse
>(
json
);
if
(
r
is
{
success
:
true
})
onSuccess
?.
Invoke
(
r
);
else
onError
?.
Invoke
(
r
?.
error
??
"No mixed questions returned"
);
},
onError
);
}
/// <summary>Quick overload for mixed by chapter.</summary>
public
IEnumerator
FetchMixed
(
int
chapterId
,
int
mcqCount
,
int
tfCount
,
int
csCount
,
Action
<
MixedResponse
>
onSuccess
,
Action
<
string
>
onError
)
{
var
filter
=
new
QuestionFilter
()
.
ChapterId
(
chapterId
)
.
McqCount
(
mcqCount
)
.
TfCount
(
tfCount
)
.
CsCount
(
csCount
);
yield
return
FetchMixed
(
filter
,
onSuccess
,
onError
);
}
// ═══════════════════════════════════════════════════
// ANALYTICS: Report Single Attempt
// ═══════════════════════════════════════════════════
public
IEnumerator
ReportAttempt
(
AttemptData
attempt
,
Action
<
ReportAttemptResponse
>
onSuccess
=
null
,
Action
<
string
>
onError
=
null
)
{
string
json
=
JsonConvert
.
SerializeObject
(
attempt
,
new
JsonSerializerSettings
{
NullValueHandling
=
NullValueHandling
.
Ignore
});
yield
return
PostJson
(
"report_attempt"
,
json
,
text
=>
{
var
r
=
Parse
<
ReportAttemptResponse
>(
text
);
if
(
r
is
{
success
:
true
})
onSuccess
?.
Invoke
(
r
);
else
onError
?.
Invoke
(
r
?.
error
??
"Failed to report attempt"
);
},
onError
);
}
/// <summary>Quick overload: report with minimal data.</summary>
public
IEnumerator
ReportAttempt
(
string
questionType
,
int
questionId
,
string
sessionId
,
bool
isCorrect
,
int
timeTakenMs
,
Action
<
ReportAttemptResponse
>
onSuccess
=
null
,
Action
<
string
>
onError
=
null
)
{
var
attempt
=
new
AttemptData
(
questionType
,
questionId
,
sessionId
,
isCorrect
,
timeTakenMs
);
yield
return
ReportAttempt
(
attempt
,
onSuccess
,
onError
);
}
// ═══════════════════════════════════════════════════
// ANALYTICS: Report Batch of Attempts
// ═══════════════════════════════════════════════════
public
IEnumerator
ReportBatch
(
AttemptData
[]
attempts
,
Action
<
ReportBatchResponse
>
onSuccess
=
null
,
Action
<
string
>
onError
=
null
)
{
var
payload
=
new
BatchAttemptPayload
{
attempts
=
attempts
};
string
json
=
JsonConvert
.
SerializeObject
(
payload
,
new
JsonSerializerSettings
{
NullValueHandling
=
NullValueHandling
.
Ignore
});
yield
return
PostJson
(
"report_batch"
,
json
,
text
=>
{
var
r
=
Parse
<
ReportBatchResponse
>(
text
);
if
(
r
is
{
success
:
true
})
onSuccess
?.
Invoke
(
r
);
else
onError
?.
Invoke
(
r
?.
error
??
"Failed to report batch"
);
},
onError
);
}
// ═══════════════════════════════════════════════════
// ANALYTICS: Flag / Report a Question
// ═══════════════════════════════════════════════════
public
IEnumerator
ReportQuestion
(
string
questionType
,
int
questionId
,
string
reason
,
string
detail
=
null
,
string
sessionId
=
null
,
string
reporterId
=
null
,
Action
<
ReportQuestionResponse
>
onSuccess
=
null
,
Action
<
string
>
onError
=
null
)
{
var
fields
=
new
Dictionary
<
string
,
string
>
{
{
"question_type"
,
questionType
},
{
"question_id"
,
questionId
.
ToString
()
},
{
"reason"
,
reason
}
};
if
(!
string
.
IsNullOrEmpty
(
detail
))
fields
[
"detail"
]
=
detail
;
if
(!
string
.
IsNullOrEmpty
(
sessionId
))
fields
[
"session_id"
]
=
sessionId
;
if
(!
string
.
IsNullOrEmpty
(
reporterId
))
fields
[
"reporter_id"
]
=
reporterId
;
yield
return
PostForm
(
"report_question"
,
fields
,
text
=>
{
var
r
=
Parse
<
ReportQuestionResponse
>(
text
);
if
(
r
is
{
success
:
true
})
onSuccess
?.
Invoke
(
r
);
else
onError
?.
Invoke
(
r
?.
error
??
"Failed to report question"
);
},
onError
);
}
// ═══════════════════════════════════════════════════
// ANALYTICS: Get Question Stats
// ═══════════════════════════════════════════════════
public
IEnumerator
GetQuestionStats
(
string
questionType
,
int
questionId
,
Action
<
QuestionStatsResponse
>
onSuccess
,
Action
<
string
>
onError
)
{
{
var
p
=
new
Dictionary
<
string
,
string
>
var
p
=
new
Dictionary
<
string
,
string
>
{
{
{
"
build_type"
,
build
Type
},
{
"
question_type"
,
question
Type
},
{
"
count"
,
count
.
ToString
()
}
{
"
question_id"
,
questionId
.
ToString
()
}
};
};
if
(
gradeId
>
0
)
p
[
"grade_id"
]
=
gradeId
.
ToString
();
if
(
buildType
==
"teacher"
&&
!
string
.
IsNullOrEmpty
(
classCode
))
p
[
"class_code"
]
=
classCode
;
yield
return
GetRequest
(
"get_
c
s"
,
p
,
yield
return
GetRequest
(
"get_
question_stat
s"
,
p
,
json
=>
json
=>
{
{
try
var
r
=
Parse
<
QuestionStatsResponse
>(
json
);
{
if
(
r
is
{
success
:
true
})
var
resp
=
JsonConvert
.
DeserializeObject
<
ApiResponse
<
CsQuestion
>>(
json
);
onSuccess
?.
Invoke
(
r
);
if
(
resp
.
success
&&
resp
.
questions
!=
null
)
else
onSuccess
?.
Invoke
(
resp
.
questions
);
onError
?.
Invoke
(
r
?.
error
??
"Failed to load stats"
);
else
onError
?.
Invoke
(
resp
.
error
??
"No questions returned"
);
}
catch
(
Exception
e
)
{
onError
?.
Invoke
(
"Parse error: "
+
e
.
Message
);
}
},
},
onError
);
onError
);
}
}
}
}
}
}
\ No newline at end of file
My project/Assets/ScienceStreet/Shared/Scripts/SSDataModels.cs
View file @
d4ba32ba
using
System
;
using
System
;
using
UnityEngine
;
using
Newtonsoft.Json
;
using
Newtonsoft.Json
;
namespace
com.al_arcade.shared
namespace
com.al_arcade.shared
{
{
// ═══════════════════════════════════════════════════════
// BASE RESPONSE
// ═══════════════════════════════════════════════════════
[
Serializable
]
[
Serializable
]
public
class
Api
Response
<
T
>
public
class
Api
BaseResponse
{
{
public
bool
success
;
public
bool
success
;
public
string
error
;
public
string
error
;
public
string
api_version
;
}
// ═══════════════════════════════════════════════════════
// PING
// ═══════════════════════════════════════════════════════
[
Serializable
]
public
class
PingResponse
:
ApiBaseResponse
{
public
string
message
;
public
string
version
;
public
string
db
;
public
string
time
;
}
// ═══════════════════════════════════════════════════════
// TAXONOMY: Curriculum
// ═══════════════════════════════════════════════════════
[
Serializable
]
public
class
CurriculumInfo
{
public
int
id
;
public
string
code
;
public
string
name_ar
;
public
string
name_en
;
public
string
question_lang
;
public
int
subject_count
;
}
[
Serializable
]
public
class
CurriculaResponse
:
ApiBaseResponse
{
public
int
count
;
public
int
count
;
public
T
[]
questions
;
public
CurriculumInfo
[]
curricula
;
}
}
// ═══════════════════════════════════════════════════════
// TAXONOMY: Grade
// ═══════════════════════════════════════════════════════
[
Serializable
]
[
Serializable
]
public
class
Grade
sResponse
public
class
Grade
Info
{
{
public
bool
success
;
public
int
id
;
public
string
error
;
public
string
name_ar
;
public
string
name_en
;
public
int
sort_order
;
}
[
Serializable
]
public
class
GradesResponse
:
ApiBaseResponse
{
public
int
count
;
public
GradeInfo
[]
grades
;
public
GradeInfo
[]
grades
;
}
}
// ═══════════════════════════════════════════════════════
// TAXONOMY: Term
// ═══════════════════════════════════════════════════════
[
Serializable
]
[
Serializable
]
public
class
ValidateClassResponse
public
class
TermInfo
{
{
public
bool
success
;
public
int
id
;
public
string
error
;
public
string
name_ar
;
public
string
name_en
;
public
int
sort_order
;
}
[
JsonProperty
(
"class"
)]
[
Serializable
]
public
ClassInfo
classInfo
;
public
class
TermsResponse
:
ApiBaseResponse
{
public
int
count
;
public
TermInfo
[]
terms
;
}
}
// ═══════════════════════════════════════════════════════
// TAXONOMY: Subject
// ═══════════════════════════════════════════════════════
[
Serializable
]
public
class
SubjectInfo
{
public
int
id
;
public
string
name_ar
;
public
string
name_en
;
public
string
icon
;
public
string
color_hex
;
public
int
curriculum_id
;
public
string
curriculum_code
;
public
string
curriculum_ar
;
public
string
curriculum_en
;
public
string
question_lang
;
}
[
Serializable
]
[
Serializable
]
public
class
GradeInfo
public
class
SubjectsResponse
:
ApiBaseResponse
{
public
int
count
;
public
SubjectInfo
[]
subjects
;
}
// ═══════════════════════════════════════════════════════
// TAXONOMY: Chapter
// ═══════════════════════════════════════════════════════
[
Serializable
]
public
class
ChapterInfo
{
public
int
id
;
public
string
name_ar
;
public
string
name_en
;
public
int
subject_id
;
public
string
subject_ar
;
public
string
subject_en
;
public
int
grade_id
;
public
string
grade_ar
;
public
string
grade_en
;
public
int
term_id
;
public
string
term_ar
;
public
string
term_en
;
public
int
curriculum_id
;
public
string
curriculum_code
;
public
string
question_lang
;
public
int
mcq_count
;
public
int
tf_count
;
public
int
cs_count
;
public
int
total_questions
;
}
[
Serializable
]
public
class
ChaptersResponse
:
ApiBaseResponse
{
public
int
count
;
public
ChapterInfo
[]
chapters
;
}
// ═══════════════════════════════════════════════════════
// TAXONOMY: Full Tree
// ═══════════════════════════════════════════════════════
[
Serializable
]
public
class
TreeChapter
{
{
public
string
id
;
public
int
id
;
public
string
name
;
public
string
name_ar
;
public
string
name_en
;
public
int
grade_id
;
public
int
term_id
;
public
string
grade_ar
;
public
string
grade_en
;
public
string
term_ar
;
public
string
term_en
;
public
int
mcq
;
public
int
tf
;
public
int
cs
;
public
int
total
;
}
}
[
Serializable
]
[
Serializable
]
public
class
ClassInfo
public
class
TreeSubject
{
{
public
string
id
;
public
int
id
;
public
string
class_name
;
public
string
name_ar
;
public
string
class_code
;
public
string
name_en
;
public
string
i
s_active
;
public
string
i
con
;
public
string
grade_id
;
public
string
color_hex
;
public
string
grade_name
;
public
TreeChapter
[]
chapters
;
public
string
teacher_name
;
public
int
total_questions
;
}
}
[
Serializable
]
public
class
TreeCurriculum
{
public
int
id
;
public
string
code
;
public
string
name_ar
;
public
string
name_en
;
public
string
question_lang
;
public
TreeSubject
[]
subjects
;
public
int
total_questions
;
}
[
Serializable
]
[
Serializable
]
public
class
McqQuestion
public
class
TaxonomyTreeResponse
:
ApiBaseResponse
{
{
public
string
id
;
public
TreeCurriculum
[]
tree
;
}
// ═══════════════════════════════════════════════════════
// SHARED: Taxonomy context embedded in questions
// ═══════════════════════════════════════════════════════
[
Serializable
]
public
class
QuestionTaxonomy
{
public
int
chapter_id
;
public
string
chapter_ar
;
public
string
chapter_en
;
public
int
subject_id
;
public
string
subject_ar
;
public
string
subject_en
;
public
int
grade_id
;
public
string
grade_ar
;
public
string
grade_en
;
public
int
term_id
;
public
string
term_ar
;
public
string
term_en
;
public
int
curriculum_id
;
public
string
curriculum_code
;
public
string
question_lang
;
}
// ═══════════════════════════════════════════════════════
// MCQ Question
// ═══════════════════════════════════════════════════════
[
Serializable
]
public
class
McqQuestion
:
QuestionTaxonomy
{
public
int
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
explanation
;
public
string
difficulty
;
public
string
bloom_level
;
public
int
?
time_limit_s
;
public
string
question_image
;
public
string
answer1_image
;
public
string
answer2_image
;
public
string
answer3_image
;
public
string
answer4_image
;
public
string
explanation_image
;
public
string
question_audio
;
public
string
source
;
public
string
source
;
public
string
grade_name
;
public
float
?
accuracy_rate
;
public
int
?
avg_time_ms
;
// Private shuffle state (keeps track of current shuffle)
public
float
?
auto_difficulty
;
private
int
[]
_shuffleIndices
;
}
private
int
_cachedCorrectIndex
=
-
1
;
[
Serializable
]
/// <summary>
public
class
McqResponse
:
ApiBaseResponse
/// Original method - unchanged for backwards compatibility
{
/// </summary>
public
string
type
;
public
string
[]
GetShuffledAnswers
(
out
int
correctIndex
)
public
int
count
;
{
public
McqQuestion
[]
questions
;
string
[]
arr
=
{
answer1
,
answer2
,
answer3
,
answer4
};
}
string
correct
=
answer1
;
// ═══════════════════════════════════════════════════════
// Generate and cache shuffle indices
// TF Question
_shuffleIndices
=
new
int
[]
{
0
,
1
,
2
,
3
};
// ═══════════════════════════════════════════════════════
for
(
int
i
=
_shuffleIndices
.
Length
-
1
;
i
>
0
;
i
--)
[
Serializable
]
{
public
class
TfQuestion
:
QuestionTaxonomy
int
j
=
UnityEngine
.
Random
.
Range
(
0
,
i
+
1
);
{
(
_shuffleIndices
[
i
],
_shuffleIndices
[
j
])
=
(
_shuffleIndices
[
j
],
_shuffleIndices
[
i
]);
public
int
id
;
}
// 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
];
}
answers
=
shuffledText
;
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
;
}
}
[
Serializable
]
public
class
TfQuestion
{
public
string
id
;
public
string
question_text
;
public
string
question_text
;
public
bool
is_true
;
public
bool
is_true
;
public
string
explanation
;
public
string
difficulty
;
public
string
bloom_level
;
public
int
?
time_limit_s
;
public
string
question_image
;
public
string
question_audio
;
public
string
explanation_image
;
public
string
source
;
public
string
source
;
public
string
grade_name
;
public
float
?
accuracy_rate
;
public
int
?
avg_time_ms
;
public
float
?
auto_difficulty
;
}
}
[
Serializable
]
[
Serializable
]
public
class
CsQuestion
public
class
TfResponse
:
ApiBaseResponse
{
{
public
string
id
;
public
string
type
;
public
string
source
;
public
int
count
;
public
string
grade_name
;
public
TfQuestion
[]
questions
;
public
string
wrong_word
;
public
string
correct_answer
;
public
CsWord
[]
words
;
public
CsOption
[]
options
;
}
}
// ═══════════════════════════════════════════════════════
// CS (Correct the Sentence) Question
// ═══════════════════════════════════════════════════════
[
Serializable
]
[
Serializable
]
public
class
CsWord
public
class
CsWord
{
{
public
string
word_text
;
public
string
word_text
;
public
int
position
;
public
int
position
;
public
bool
is_
wrong
;
public
bool
is_
distractor
;
}
}
[
Serializable
]
[
Serializable
]
...
@@ -216,4 +302,170 @@ namespace com.al_arcade.shared
...
@@ -216,4 +302,170 @@ namespace com.al_arcade.shared
public
string
option_text
;
public
string
option_text
;
public
bool
is_correct
;
public
bool
is_correct
;
}
}
}
[
Serializable
]
public
class
CsQuestion
:
QuestionTaxonomy
{
public
int
id
;
public
string
instruction_text
;
public
string
explanation
;
public
string
difficulty
;
public
string
bloom_level
;
public
int
?
time_limit_s
;
public
string
question_image
;
public
string
explanation_image
;
public
string
source
;
public
float
?
accuracy_rate
;
public
int
?
avg_time_ms
;
public
float
?
auto_difficulty
;
public
CsWord
[]
words
;
public
CsOption
[]
options
;
public
string
distractor_word
;
public
string
correct_answer
;
public
string
sentence
;
}
[
Serializable
]
public
class
CsResponse
:
ApiBaseResponse
{
public
string
type
;
public
int
count
;
public
CsQuestion
[]
questions
;
}
// ═══════════════════════════════════════════════════════
// MIXED Question (interleaved)
// ═══════════════════════════════════════════════════════
[
Serializable
]
public
class
MixedByType
{
public
McqQuestion
[]
mcq
;
public
TfQuestion
[]
tf
;
public
CsQuestion
[]
cs
;
}
/// <summary>
/// A single item in the interleaved array.
/// Use _type to determine which fields are populated.
/// </summary>
[
Serializable
]
public
class
MixedQuestion
:
QuestionTaxonomy
{
public
string
_type
;
// "mcq", "tf", "cs"
public
int
id
;
// MCQ fields
public
string
question_text
;
public
string
answer1
;
public
string
answer2
;
public
string
answer3
;
public
string
answer4
;
public
string
question_image
;
public
string
answer1_image
;
public
string
answer2_image
;
public
string
answer3_image
;
public
string
answer4_image
;
// TF fields
public
bool
is_true
;
// CS fields
public
string
instruction_text
;
public
CsWord
[]
words
;
public
CsOption
[]
options
;
public
string
sentence
;
public
string
distractor_word
;
public
string
correct_answer
;
// Shared
public
string
explanation
;
public
string
difficulty
;
public
int
?
time_limit_s
;
}
[
Serializable
]
public
class
MixedResponse
:
ApiBaseResponse
{
public
int
total
;
public
MixedByType
by_type
;
public
MixedQuestion
[]
interleaved
;
}
// ═══════════════════════════════════════════════════════
// ANALYTICS: Report Attempt Response
// ═══════════════════════════════════════════════════════
[
Serializable
]
public
class
ReportAttemptResponse
:
ApiBaseResponse
{
public
int
attempt_id
;
public
bool
recorded
;
}
[
Serializable
]
public
class
ReportBatchResponse
:
ApiBaseResponse
{
public
int
recorded
;
public
int
total
;
public
string
[]
errors
;
}
// ═══════════════════════════════════════════════════════
// ANALYTICS: Report Question (flag) Response
// ═══════════════════════════════════════════════════════
[
Serializable
]
public
class
ReportQuestionResponse
:
ApiBaseResponse
{
public
int
report_id
;
public
string
message
;
}
// ═══════════════════════════════════════════════════════
// ANALYTICS: Question Stats
// ═══════════════════════════════════════════════════════
[
Serializable
]
public
class
QuestionStats
{
public
int
times_served
;
public
int
times_correct
;
public
int
times_wrong
;
public
int
times_skipped
;
public
int
times_timeout
;
public
int
?
avg_time_ms
;
public
float
?
accuracy_rate
;
public
float
?
completion_rate
;
public
float
?
difficulty_rating
;
public
float
?
skip_rate
;
public
float
?
timeout_rate
;
public
string
first_served_at
;
public
string
last_served_at
;
}
[
Serializable
]
public
class
AnswerDistribution
{
public
int
answer_index
;
public
int
times_selected
;
public
int
?
avg_time_ms
;
public
int
selected_by_top_quartile
;
public
int
selected_by_bottom_quartile
;
}
[
Serializable
]
public
class
DailyTrend
{
public
string
stat_date
;
public
int
times_served
;
public
int
times_correct
;
public
int
times_wrong
;
public
int
?
avg_time_ms
;
}
[
Serializable
]
public
class
QuestionStatsResponse
:
ApiBaseResponse
{
public
QuestionStats
stats
;
public
DailyTrend
[]
daily_trend
;
public
AnswerDistribution
[]
answer_distribution
;
public
string
message
;
}
}
\ No newline at end of file
My project/Assets/ScienceStreet/Shared/Scripts/SSGameSession.cs
View file @
d4ba32ba
...
@@ -59,30 +59,31 @@ namespace com.al_arcade.shared
...
@@ -59,30 +59,31 @@ namespace com.al_arcade.shared
{
{
var
api
=
SSApiManager
.
EnsureInstance
();
var
api
=
SSApiManager
.
EnsureInstance
();
yield
return
api
.
ValidateClassCode
(
code
,
// yield return api.ValidateClassCode(code,
cls
=>
// cls =>
{
// {
classCode
=
cls
.
class_code
;
// classCode = cls.class_code;
className
=
cls
.
class_name
;
// className = cls.class_name;
teacherName
=
cls
.
teacher_name
;
// teacherName = cls.teacher_name;
gradeName
=
cls
.
grade_name
;
// gradeName = cls.grade_name;
buildType
=
"teacher"
;
// buildType = "teacher";
isClassValidated
=
true
;
// isClassValidated = true;
if
(
int
.
TryParse
(
cls
.
grade_id
,
out
int
gid
))
// if (int.TryParse(cls.grade_id, out int gid))
gradeId
=
gid
;
// gradeId = gid;
Debug
.
Log
(
$"[SSSession] Teacher session started:
{
className
}
by
{
teacherName
}
"
);
// Debug.Log($"[SSSession] Teacher session started: {className} by {teacherName}");
onSuccess
?.
Invoke
();
// onSuccess?.Invoke();
onSessionReady
?.
Invoke
();
// onSessionReady?.Invoke();
},
// },
err
=>
// err =>
{
// {
isClassValidated
=
false
;
// isClassValidated = false;
Debug
.
LogWarning
(
$"[SSSession] Validation failed:
{
err
}
"
);
// Debug.LogWarning($"[SSSession] Validation failed: {err}");
onError
?.
Invoke
(
err
);
// onError?.Invoke(err);
onSessionError
?.
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
...
@@ -396,22 +396,7 @@ namespace com.al_arcade.tf
{
{
return
new
TfQuestion
[]
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
...
@@ -54,9 +54,15 @@ namespace com.al_arcade.tf
{
{
var
session
=
SSGameSession
.
EnsureInstance
();
var
session
=
SSGameSession
.
EnsureInstance
();
var
api
=
SSApiManager
.
EnsureInstance
();
var
api
=
SSApiManager
.
EnsureInstance
();
var
filter
=
new
QuestionFilter
()
.
CurriculumId
(
0
)
.
SubjectId
(
0
)
.
GradeId
(
session
.
gradeId
)
.
Count
(
session
.
questionCount
);
yield
return
api
.
FetchTf
(
yield
return
api
.
FetchTf
(
session
.
buildType
,
session
.
classCode
,
filter
,
session
.
questionCount
,
session
.
gradeId
,
qs
=>
_questions
=
qs
,
qs
=>
_questions
=
qs
,
err
=>
onError
(
err
)
err
=>
onError
(
err
)
);
);
...
...
My project/Assets/ScienceStreet/TF/Scripts/TfPrefabBuilder.cs
View file @
d4ba32ba
...
@@ -320,16 +320,7 @@ namespace com.al_arcade.tf
...
@@ -320,16 +320,7 @@ namespace com.al_arcade.tf
{
{
return
new
TfQuestion
[]
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:
...
@@ -13,7 +13,7 @@ PlayerSettings:
useOnDemandResources
:
0
useOnDemandResources
:
0
accelerometerFrequency
:
60
accelerometerFrequency
:
60
companyName
:
DefaultCompany
companyName
:
DefaultCompany
productName
:
Correct The Sentence
productName
:
SSBookMinigames
defaultCursor
:
{
fileID
:
0
}
defaultCursor
:
{
fileID
:
0
}
cursorHotspot
:
{
x
:
0
,
y
:
0
}
cursorHotspot
:
{
x
:
0
,
y
:
0
}
m_SplashScreenBackgroundColor
:
{
r
:
0.13725491
,
g
:
0.12156863
,
b
:
0.1254902
,
a
:
1
}
m_SplashScreenBackgroundColor
:
{
r
:
0.13725491
,
g
:
0.12156863
,
b
:
0.1254902
,
a
:
1
}
...
@@ -170,7 +170,7 @@ PlayerSettings:
...
@@ -170,7 +170,7 @@ PlayerSettings:
androidMaxAspectRatio
:
2.4
androidMaxAspectRatio
:
2.4
androidMinAspectRatio
:
1
androidMinAspectRatio
:
1
applicationIdentifier
:
applicationIdentifier
:
Android
:
com.DefaultCompany.
CorrectTheSentence
Android
:
com.DefaultCompany.
SSBookMinigames
Standalone
:
com.Unity-Technologies.com.unity.template.urp-blank
Standalone
:
com.Unity-Technologies.com.unity.template.urp-blank
iPhone
:
com.Unity-Technologies.com.unity.template.urp-blank
iPhone
:
com.Unity-Technologies.com.unity.template.urp-blank
buildNumber
:
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