Implement simple user creation system

This commit is contained in:
Dries Van Schuylenbergh
2023-03-08 10:13:10 +00:00
committed by Victor Mylle
parent 7ed5a959e2
commit a351182aa1
31 changed files with 2503 additions and 140 deletions

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using UnityEngine;
[Serializable]
public class Progress
{
[Serializable]
// Helper class to serialize into byte[]
protected class DataEntry
{
public string key;
public List<byte> bytes = new List<byte>();
public DataEntry(string key, byte[] data)
{
this.key = key;
this.bytes = new List<byte>(data);
}
}
// TODO: use inheritance to create seperate MinigameProgress and CourseProgress
[Header("Course or Minigame")]
// TODO: change to course/minigame ScriptableObject reference
// Index of item in courses/minigame list object
public int index;
[SerializeField]
// values belonging to a certain key, in List (which can be serialized)
private List<DataEntry> entries = new List<DataEntry>();
// Add new `key` := `value`, returns `true` if successful
public bool Add<T>(string key, T data)
{
if (data == null)
return false;
// Search for already existing key
foreach (DataEntry entry in entries)
{
if (entry.key == key)
{
return false;
}
}
// Hacky serialization stuff
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
bf.Serialize(ms, data);
entries.Add(new DataEntry(key, ms.ToArray()));
return true;
}
}
// Get the value of type `T` belonging to `key`
public T Get<T>(string key)
{
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
// Find the correct key
foreach (DataEntry entry in entries)
{
if (entry.key == key)
{
// Hacky serialization stuff
byte[] data = entry.bytes.ToArray();
ms.Write(data, 0, data.Length);
ms.Seek(0, SeekOrigin.Begin);
return (T)bf.Deserialize(ms);
}
}
}
// Raise an exception when key is not found
throw new KeyNotFoundException();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 183eab19332f33a48a745b7b264611fc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(menuName = "Create new Scriptable/User/User")]
public class User : ScriptableObject
{
[Header("Personal data")]
// User nickname
public string username;
// User avatar
public Sprite avatar;
[Header("Personal settings")]
// TODO: set personal settings and preferences
[Header("Progress")]
// Total playtime
public double playtime;
[SerializeField]
// List of courses a user started/completed
public List<Progress> courses = new List<Progress>();
[SerializeField]
// List of minigames a user played
public List<Progress> minigames = new List<Progress>();
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3c6c5919d9f747143b377c2bc34cd28b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,101 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class UserCreationScreen : MonoBehaviour
{
// Max length of a username
private const int MAX_USERNAME_LENGTH = 12;
[Header("UI References")]
// Reference to the input text field for username
public TMP_InputField inputName;
// Reference to the avatar-list container
public Transform avatarsContainer;
[Header("Prefab")]
// Avatar prefab
public GameObject avatarPrefab;
// List of all sprites that are supported as avatars
public List<Sprite> sprites = new List<Sprite>();
[Header("Users List")]
// Reference to the UserList ScriptableObject
public UserList users;
[SerializeField]
// Current selected avatar
private int selectedAvatar = 0;
// List of references to avatar background sprites (so we can color them nicely)
private List<Image> avatars = new List<Image>();
void Start()
{
for (int i = 0; i < sprites.Count; i++)
{
// Create instance of prefab
GameObject instance = GameObject.Instantiate(avatarPrefab, avatarsContainer);
// Store value of i so we can use it the callback (else it would get the value of sprites.Count)
int x = i;
// Add onClick callback
instance.GetComponent<Button>().onClick.AddListener(() => UpdateAvatar(x));
// Store reference to image for fancy coloring
Image background = instance.GetComponent<Image>();
avatars.Add(background);
// Set background color
background.color = selectedAvatar == i ? Color.blue : Color.gray;
// Find correct component for setting the sprite
foreach (Image img in background.GetComponentsInChildren<Image>())
if (img != background)
{
img.sprite = sprites[i];
break;
}
}
}
// Update the current selected avatar
private void UpdateAvatar(int newAvatar)
{
avatars[selectedAvatar].color = Color.gray;
selectedAvatar = newAvatar;
avatars[selectedAvatar].color = Color.blue;
}
// Check if a given string is a correct username (using Regex)
static public bool IsValidUsername(string username)
{
return new Regex($@"^[abcdefghijklmnopqrstuvwxyz]{{1,{MAX_USERNAME_LENGTH}}}$").IsMatch(username);
}
// Create a new user (will be called by button)
public void CreateUser()
{
string username = inputName.text;
if (IsValidUsername(username))
{
if (!users.GetUserByUsername(username))
{
// Create a new entry in the UserList ScriptableObject
users.CreateAndAddNewUser(username, sprites[selectedAvatar]);
// TODO: change scene, for now just change to StartScreen
SceneManager.LoadScene("Common/Scenes/StartScreen");
}
// TODO: give more feedback to user
// Warn user that username already exists
else Debug.LogWarning($"Username '{username}' already exists!");
}
// TODO: give more feedback to user
// Warn user that username is invalid
else Debug.LogWarning($"Invalid username '{username}'!");
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fc3902e35c042b14f83b24498d31d587
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,45 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
[CreateAssetMenu(menuName = "Create new Scriptable/User/List")]
public class UserList : ScriptableObject
{
[Header("Template")]
// Reference to User template
public ScriptableObject userTemplate;
[Header("Users")]
// List of users
public List<User> users = new List<User>();
// Create a new User
public User CreateNewUser(string name, Sprite avatar)
{
User user = ScriptableObject.CreateInstance<User>();
user.username = name;
user.avatar = avatar;
return user;
}
// Create a new User and add to list
public User CreateAndAddNewUser(string name, Sprite avatar)
{
User user = CreateNewUser(name, avatar);
users.Add(user);
EditorUtility.SetDirty(this);
AssetDatabase.CreateAsset(user, $"Assets/Common/ScriptableObjects/Users/{name}.asset");
AssetDatabase.SaveAssets();
return user;
}
// Get user by username, returns `null` if no user can be found with such name
public User GetUserByUsername(string username)
{
foreach (User user in users)
if (user.username == username) return user;
return null;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3f3d6d68c3c3db64e91cf5ec9537ccda
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: