correct spawn into parent

Multiplayer
Sascha Woitschetzki 2023-03-30 10:40:21 +07:00
parent 9dd19781e8
commit 38b9ff3127
582 changed files with 13617 additions and 8330 deletions

@ -1,19 +1,14 @@
// This should be editor only // This should be editor only
#if UNITY_EDITOR #if UNITY_EDITOR
using System.Collections;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
namespace ParrelSync.Example namespace ParrelSync.Example {
{ public class CustomArgumentExample : MonoBehaviour {
public class CustomArgumentExample : MonoBehaviour
{
// Start is called before the first frame update // Start is called before the first frame update
void Start() private void Start() {
{
// Is this editor instance running a clone project? // Is this editor instance running a clone project?
if (ClonesManager.IsClone()) if (ClonesManager.IsClone()) {
{
Debug.Log("This is a clone project."); Debug.Log("This is a clone project.");
//Argument can be set from the clones manager window. //Argument can be set from the clones manager window.
@ -21,8 +16,7 @@ namespace ParrelSync.Example
Debug.Log("The custom argument of this clone project is: " + customArgument); Debug.Log("The custom argument of this clone project is: " + customArgument);
// Do what ever you need with the argument string. // Do what ever you need with the argument string.
} }
else else {
{
Debug.Log("This is the original project."); Debug.Log("This is the original project.");
} }
} }

@ -1,22 +1,20 @@
using UnityEditor; using UnityEditor;
namespace ParrelSync
{ namespace ParrelSync {
[InitializeOnLoad] [InitializeOnLoad]
public class EditorQuit public class EditorQuit {
{ static EditorQuit() {
IsQuiting = false;
EditorApplication.quitting += Quit;
}
/// <summary> /// <summary>
/// Is editor being closed /// Is editor being closed
/// </summary> /// </summary>
static public bool IsQuiting { get; private set; } public static bool IsQuiting { get; private set; }
static void Quit()
{
IsQuiting = true;
}
static EditorQuit() private static void Quit() {
{ IsQuiting = true;
IsQuiting = false;
EditorApplication.quitting += Quit;
} }
} }
} }

@ -1,18 +1,14 @@
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
namespace ParrelSync
{ namespace ParrelSync {
/// <summary> /// <summary>
/// For preventing assets being modified from the clone instance. /// For preventing assets being modified from the clone instance.
/// </summary> /// </summary>
public class ParrelSyncAssetModificationProcessor : UnityEditor.AssetModificationProcessor public class ParrelSyncAssetModificationProcessor : AssetModificationProcessor {
{ public static string[] OnWillSaveAssets(string[] paths) {
public static string[] OnWillSaveAssets(string[] paths) if (ClonesManager.IsClone() && Preferences.AssetModPref.Value) {
{ if (paths != null && paths.Length > 0 && !EditorQuit.IsQuiting) {
if (ClonesManager.IsClone() && Preferences.AssetModPref.Value)
{
if (paths != null && paths.Length > 0 && !EditorQuit.IsQuiting)
{
EditorUtility.DisplayDialog( EditorUtility.DisplayDialog(
ClonesManager.ProjectName + ": Asset modifications saving detected and blocked", ClonesManager.ProjectName + ": Asset modifications saving detected and blocked",
"Asset modifications saving are blocked in the clone instance. \n\n" + "Asset modifications saving are blocked in the clone instance. \n\n" +
@ -21,13 +17,12 @@ namespace ParrelSync
"Please use the original editor window if you want to make changes to the project files.", "Please use the original editor window if you want to make changes to the project files.",
"ok" "ok"
); );
foreach (var path in paths) foreach (string path in paths) Debug.Log("Attempting to save " + path + " are blocked.");
{
Debug.Log("Attempting to save " + path + " are blocked.");
}
} }
return new string[0] { };
return new string[0]{ };
} }
return paths; return paths;
} }
} }

@ -1,138 +1,131 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.IO; using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug; using Debug = UnityEngine.Debug;
namespace ParrelSync namespace ParrelSync {
{
/// <summary> /// <summary>
/// Contains all required methods for creating a linked clone of the Unity project. /// Contains all required methods for creating a linked clone of the Unity project.
/// </summary> /// </summary>
public class ClonesManager public class ClonesManager {
{
/// <summary> /// <summary>
/// Name used for an identifying file created in the clone project directory. /// Name used for an identifying file created in the clone project directory.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// (!) Do not change this after the clone was created, because then connection will be lost. /// (!) Do not change this after the clone was created, because then connection will be lost.
/// </remarks> /// </remarks>
public const string CloneFileName = ".clone"; public const string CloneFileName = ".clone";
/// <summary> /// <summary>
/// Suffix added to the end of the project clone name when it is created. /// Suffix added to the end of the project clone name when it is created.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// (!) Do not change this after the clone was created, because then connection will be lost. /// (!) Do not change this after the clone was created, because then connection will be lost.
/// </remarks> /// </remarks>
public const string CloneNameSuffix = "_clone"; public const string CloneNameSuffix = "_clone";
public const string ProjectName = "ParrelSync"; public const string ProjectName = "ParrelSync";
/// <summary> /// <summary>
/// The maximum number of clones /// The maximum number of clones
/// </summary> /// </summary>
public const int MaxCloneProjectCount = 10; public const int MaxCloneProjectCount = 10;
/// <summary> /// <summary>
/// Name of the file for storing clone's argument. /// Name of the file for storing clone's argument.
/// </summary> /// </summary>
public const string ArgumentFileName = ".parrelsyncarg"; public const string ArgumentFileName = ".parrelsyncarg";
/// <summary> /// <summary>
/// Default argument of the new clone /// Default argument of the new clone
/// </summary> /// </summary>
public const string DefaultArgument = "client"; public const string DefaultArgument = "client";
#region Managing clones #region Managing clones
/// <summary> /// <summary>
/// Creates clone from the project currently open in Unity Editor. /// Creates clone from the project currently open in Unity Editor.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static Project CreateCloneFromCurrent() public static Project CreateCloneFromCurrent() {
{ if (IsClone()) {
if (IsClone())
{
Debug.LogError("This project is already a clone. Cannot clone it."); Debug.LogError("This project is already a clone. Cannot clone it.");
return null; return null;
} }
string currentProjectPath = ClonesManager.GetCurrentProjectPath(); string currentProjectPath = GetCurrentProjectPath();
return ClonesManager.CreateCloneFromPath(currentProjectPath); return CreateCloneFromPath(currentProjectPath);
} }
/// <summary> /// <summary>
/// Creates clone of the project located at the given path. /// Creates clone of the project located at the given path.
/// </summary> /// </summary>
/// <param name="sourceProjectPath"></param> /// <param name="sourceProjectPath"></param>
/// <returns></returns> /// <returns></returns>
public static Project CreateCloneFromPath(string sourceProjectPath) public static Project CreateCloneFromPath(string sourceProjectPath) {
{ Project sourceProject = new(sourceProjectPath);
Project sourceProject = new Project(sourceProjectPath);
string cloneProjectPath = null; string cloneProjectPath = null;
//Find available clone suffix id //Find available clone suffix id
for (int i = 0; i < MaxCloneProjectCount; i++) for (int i = 0; i < MaxCloneProjectCount; i++) {
{ string originalProjectPath = GetCurrentProject().projectPath;
string originalProjectPath = ClonesManager.GetCurrentProject().projectPath; string possibleCloneProjectPath = originalProjectPath + CloneNameSuffix + "_" + i;
string possibleCloneProjectPath = originalProjectPath + ClonesManager.CloneNameSuffix + "_" + i;
if (!Directory.Exists(possibleCloneProjectPath)) if (!Directory.Exists(possibleCloneProjectPath)) {
{
cloneProjectPath = possibleCloneProjectPath; cloneProjectPath = possibleCloneProjectPath;
break; break;
} }
} }
if (string.IsNullOrEmpty(cloneProjectPath)) if (string.IsNullOrEmpty(cloneProjectPath)) {
{
Debug.LogError("The number of cloned projects has reach its limit. Limit: " + MaxCloneProjectCount); Debug.LogError("The number of cloned projects has reach its limit. Limit: " + MaxCloneProjectCount);
return null; return null;
} }
Project cloneProject = new Project(cloneProjectPath); Project cloneProject = new(cloneProjectPath);
Debug.Log("Start cloning project, original project: " + sourceProject + ", clone project: " + cloneProject); Debug.Log("Start cloning project, original project: " + sourceProject + ", clone project: " + cloneProject);
ClonesManager.CreateProjectFolder(cloneProject); CreateProjectFolder(cloneProject);
//Copy Folders //Copy Folders
Debug.Log("Library copy: " + cloneProject.libraryPath); Debug.Log("Library copy: " + cloneProject.libraryPath);
ClonesManager.CopyDirectoryWithProgressBar(sourceProject.libraryPath, cloneProject.libraryPath, CopyDirectoryWithProgressBar(sourceProject.libraryPath, cloneProject.libraryPath,
"Cloning Project Library '" + sourceProject.name + "'. "); "Cloning Project Library '" + sourceProject.name + "'. ");
Debug.Log("Packages copy: " + cloneProject.libraryPath); Debug.Log("Packages copy: " + cloneProject.libraryPath);
ClonesManager.CopyDirectoryWithProgressBar(sourceProject.packagesPath, cloneProject.packagesPath, CopyDirectoryWithProgressBar(sourceProject.packagesPath, cloneProject.packagesPath,
"Cloning Project Packages '" + sourceProject.name + "'. "); "Cloning Project Packages '" + sourceProject.name + "'. ");
//Link Folders //Link Folders
ClonesManager.LinkFolders(sourceProject.assetPath, cloneProject.assetPath); LinkFolders(sourceProject.assetPath, cloneProject.assetPath);
ClonesManager.LinkFolders(sourceProject.projectSettingsPath, cloneProject.projectSettingsPath); LinkFolders(sourceProject.projectSettingsPath, cloneProject.projectSettingsPath);
ClonesManager.LinkFolders(sourceProject.autoBuildPath, cloneProject.autoBuildPath); LinkFolders(sourceProject.autoBuildPath, cloneProject.autoBuildPath);
ClonesManager.LinkFolders(sourceProject.localPackages, cloneProject.localPackages); LinkFolders(sourceProject.localPackages, cloneProject.localPackages);
ClonesManager.RegisterClone(cloneProject); RegisterClone(cloneProject);
return cloneProject; return cloneProject;
} }
/// <summary> /// <summary>
/// Registers a clone by placing an identifying ".clone" file in its root directory. /// Registers a clone by placing an identifying ".clone" file in its root directory.
/// </summary> /// </summary>
/// <param name="cloneProject"></param> /// <param name="cloneProject"></param>
private static void RegisterClone(Project cloneProject) private static void RegisterClone(Project cloneProject) {
{
/// Add clone identifier file. /// Add clone identifier file.
string identifierFile = Path.Combine(cloneProject.projectPath, ClonesManager.CloneFileName); string identifierFile = Path.Combine(cloneProject.projectPath, CloneFileName);
File.Create(identifierFile).Dispose(); File.Create(identifierFile).Dispose();
//Add argument file with default argument //Add argument file with default argument
string argumentFilePath = Path.Combine(cloneProject.projectPath, ClonesManager.ArgumentFileName); string argumentFilePath = Path.Combine(cloneProject.projectPath, ArgumentFileName);
File.WriteAllText(argumentFilePath, DefaultArgument, System.Text.Encoding.UTF8); File.WriteAllText(argumentFilePath, DefaultArgument, Encoding.UTF8);
/// Add collabignore.txt to stop the clone from messing with Unity Collaborate if it's enabled. Just in case. /// Add collabignore.txt to stop the clone from messing with Unity Collaborate if it's enabled. Just in case.
string collabignoreFile = Path.Combine(cloneProject.projectPath, "collabignore.txt"); string collabignoreFile = Path.Combine(cloneProject.projectPath, "collabignore.txt");
@ -140,19 +133,16 @@ namespace ParrelSync
} }
/// <summary> /// <summary>
/// Opens a project located at the given path (if one exists). /// Opens a project located at the given path (if one exists).
/// </summary> /// </summary>
/// <param name="projectPath"></param> /// <param name="projectPath"></param>
public static void OpenProject(string projectPath) public static void OpenProject(string projectPath) {
{ if (!Directory.Exists(projectPath)) {
if (!Directory.Exists(projectPath))
{
Debug.LogError("Cannot open the project - provided folder (" + projectPath + ") does not exist."); Debug.LogError("Cannot open the project - provided folder (" + projectPath + ") does not exist.");
return; return;
} }
if (projectPath == ClonesManager.GetCurrentProjectPath()) if (projectPath == GetCurrentProjectPath()) {
{
Debug.LogError("Cannot open the project - it is already open."); Debug.LogError("Cannot open the project - it is already open.");
return; return;
} }
@ -164,13 +154,11 @@ namespace ParrelSync
string fileName = GetApplicationPath(); string fileName = GetApplicationPath();
string args = "-projectPath \"" + projectPath + "\""; string args = "-projectPath \"" + projectPath + "\"";
Debug.Log("Opening project \"" + fileName + " " + args + "\""); Debug.Log("Opening project \"" + fileName + " " + args + "\"");
ClonesManager.StartHiddenConsoleProcess(fileName, args); StartHiddenConsoleProcess(fileName, args);
} }
private static string GetApplicationPath() private static string GetApplicationPath() {
{ switch (Application.platform) {
switch (Application.platform)
{
case RuntimePlatform.WindowsEditor: case RuntimePlatform.WindowsEditor:
return EditorApplication.applicationPath; return EditorApplication.applicationPath;
case RuntimePlatform.OSXEditor: case RuntimePlatform.OSXEditor:
@ -178,87 +166,82 @@ namespace ParrelSync
case RuntimePlatform.LinuxEditor: case RuntimePlatform.LinuxEditor:
return EditorApplication.applicationPath; return EditorApplication.applicationPath;
default: default:
throw new System.NotImplementedException("Platform has not supported yet ;("); throw new NotImplementedException("Platform has not supported yet ;(");
} }
} }
/// <summary> /// <summary>
/// Is this project being opened by an Unity editor? /// Is this project being opened by an Unity editor?
/// </summary> /// </summary>
/// <param name="projectPath"></param> /// <param name="projectPath"></param>
/// <returns></returns> /// <returns></returns>
public static bool IsCloneProjectRunning(string projectPath) public static bool IsCloneProjectRunning(string projectPath) {
{
//Determine whether it is opened in another instance by checking the UnityLockFile //Determine whether it is opened in another instance by checking the UnityLockFile
string UnityLockFilePath = new string[] { projectPath, "Temp", "UnityLockfile" } string UnityLockFilePath = new[]{ projectPath, "Temp", "UnityLockfile" }
.Aggregate(Path.Combine); .Aggregate(Path.Combine);
switch (Application.platform) switch (Application.platform) {
{ case RuntimePlatform.WindowsEditor:
case (RuntimePlatform.WindowsEditor):
//Windows editor will lock "UnityLockfile" file when project is being opened. //Windows editor will lock "UnityLockfile" file when project is being opened.
//Sometime, for instance: windows editor crash, the "UnityLockfile" will not be deleted even the project //Sometime, for instance: windows editor crash, the "UnityLockfile" will not be deleted even the project
//isn't being opened, so a check to the "UnityLockfile" lock status may be necessary. //isn't being opened, so a check to the "UnityLockfile" lock status may be necessary.
if (Preferences.AlsoCheckUnityLockFileStaPref.Value) if (Preferences.AlsoCheckUnityLockFileStaPref.Value)
return File.Exists(UnityLockFilePath) && FileUtilities.IsFileLocked(UnityLockFilePath); return File.Exists(UnityLockFilePath) && FileUtilities.IsFileLocked(UnityLockFilePath);
else return File.Exists(UnityLockFilePath);
return File.Exists(UnityLockFilePath); case RuntimePlatform.OSXEditor:
case (RuntimePlatform.OSXEditor):
//Mac editor won't lock "UnityLockfile" file when project is being opened //Mac editor won't lock "UnityLockfile" file when project is being opened
return File.Exists(UnityLockFilePath); return File.Exists(UnityLockFilePath);
case (RuntimePlatform.LinuxEditor): case RuntimePlatform.LinuxEditor:
return File.Exists(UnityLockFilePath); return File.Exists(UnityLockFilePath);
default: default:
throw new System.NotImplementedException("IsCloneProjectRunning: Unsupport Platfrom: " + Application.platform); throw new NotImplementedException("IsCloneProjectRunning: Unsupport Platfrom: " +
Application.platform);
} }
} }
/// <summary> /// <summary>
/// Deletes the clone of the currently open project, if such exists. /// Deletes the clone of the currently open project, if such exists.
/// </summary> /// </summary>
public static void DeleteClone(string cloneProjectPath) public static void DeleteClone(string cloneProjectPath) {
{
/// Clone won't be able to delete itself. /// Clone won't be able to delete itself.
if (ClonesManager.IsClone()) return; if (IsClone()) return;
///Extra precautions. ///Extra precautions.
if (cloneProjectPath == string.Empty) return; if (cloneProjectPath == string.Empty) return;
if (cloneProjectPath == ClonesManager.GetOriginalProjectPath()) return; if (cloneProjectPath == GetOriginalProjectPath()) return;
//Check what OS is //Check what OS is
string identifierFile; string identifierFile;
string args; string args;
switch (Application.platform) switch (Application.platform) {
{ case RuntimePlatform.WindowsEditor:
case (RuntimePlatform.WindowsEditor):
Debug.Log("Attempting to delete folder \"" + cloneProjectPath + "\""); Debug.Log("Attempting to delete folder \"" + cloneProjectPath + "\"");
//The argument file will be deleted first at the beginning of the project deletion process //The argument file will be deleted first at the beginning of the project deletion process
//to prevent any further reading and writing to it(There's a File.Exist() check at the (file)editor windows.) //to prevent any further reading and writing to it(There's a File.Exist() check at the (file)editor windows.)
//If there's any file in the directory being write/read during the deletion process, the directory can't be fully removed. //If there's any file in the directory being write/read during the deletion process, the directory can't be fully removed.
identifierFile = Path.Combine(cloneProjectPath, ClonesManager.ArgumentFileName); identifierFile = Path.Combine(cloneProjectPath, ArgumentFileName);
File.Delete(identifierFile); File.Delete(identifierFile);
args = "/c " + @"rmdir /s/q " + string.Format("\"{0}\"", cloneProjectPath); args = "/c " + @"rmdir /s/q " + string.Format("\"{0}\"", cloneProjectPath);
StartHiddenConsoleProcess("cmd.exe", args); StartHiddenConsoleProcess("cmd.exe", args);
break; break;
case (RuntimePlatform.OSXEditor): case RuntimePlatform.OSXEditor:
Debug.Log("Attempting to delete folder \"" + cloneProjectPath + "\""); Debug.Log("Attempting to delete folder \"" + cloneProjectPath + "\"");
//The argument file will be deleted first at the beginning of the project deletion process //The argument file will be deleted first at the beginning of the project deletion process
//to prevent any further reading and writing to it(There's a File.Exist() check at the (file)editor windows.) //to prevent any further reading and writing to it(There's a File.Exist() check at the (file)editor windows.)
//If there's any file in the directory being write/read during the deletion process, the directory can't be fully removed. //If there's any file in the directory being write/read during the deletion process, the directory can't be fully removed.
identifierFile = Path.Combine(cloneProjectPath, ClonesManager.ArgumentFileName); identifierFile = Path.Combine(cloneProjectPath, ArgumentFileName);
File.Delete(identifierFile); File.Delete(identifierFile);
FileUtil.DeleteFileOrDirectory(cloneProjectPath); FileUtil.DeleteFileOrDirectory(cloneProjectPath);
break; break;
case (RuntimePlatform.LinuxEditor): case RuntimePlatform.LinuxEditor:
Debug.Log("Attempting to delete folder \"" + cloneProjectPath + "\""); Debug.Log("Attempting to delete folder \"" + cloneProjectPath + "\"");
identifierFile = Path.Combine(cloneProjectPath, ClonesManager.ArgumentFileName); identifierFile = Path.Combine(cloneProjectPath, ArgumentFileName);
File.Delete(identifierFile); File.Delete(identifierFile);
FileUtil.DeleteFileOrDirectory(cloneProjectPath); FileUtil.DeleteFileOrDirectory(cloneProjectPath);
@ -275,32 +258,30 @@ namespace ParrelSync
#region Creating project folders #region Creating project folders
/// <summary> /// <summary>
/// Creates an empty folder using data in the given Project object /// Creates an empty folder using data in the given Project object
/// </summary> /// </summary>
/// <param name="project"></param> /// <param name="project"></param>
public static void CreateProjectFolder(Project project) public static void CreateProjectFolder(Project project) {
{
string path = project.projectPath; string path = project.projectPath;
Debug.Log("Creating new empty folder at: " + path); Debug.Log("Creating new empty folder at: " + path);
Directory.CreateDirectory(path); Directory.CreateDirectory(path);
} }
/// <summary> /// <summary>
/// Copies the full contents of the unity library. We want to do this to avoid the lengthy re-serialization of the whole project when it opens up the clone. /// Copies the full contents of the unity library. We want to do this to avoid the lengthy re-serialization of the
/// whole project when it opens up the clone.
/// </summary> /// </summary>
/// <param name="sourceProject"></param> /// <param name="sourceProject"></param>
/// <param name="destinationProject"></param> /// <param name="destinationProject"></param>
[System.Obsolete] [Obsolete]
public static void CopyLibraryFolder(Project sourceProject, Project destinationProject) public static void CopyLibraryFolder(Project sourceProject, Project destinationProject) {
{ if (Directory.Exists(destinationProject.libraryPath)) {
if (Directory.Exists(destinationProject.libraryPath))
{
Debug.LogWarning("Library copy: destination path already exists! "); Debug.LogWarning("Library copy: destination path already exists! ");
return; return;
} }
Debug.Log("Library copy: " + destinationProject.libraryPath); Debug.Log("Library copy: " + destinationProject.libraryPath);
ClonesManager.CopyDirectoryWithProgressBar(sourceProject.libraryPath, destinationProject.libraryPath, CopyDirectoryWithProgressBar(sourceProject.libraryPath, destinationProject.libraryPath,
"Cloning project '" + sourceProject.name + "'. "); "Cloning project '" + sourceProject.name + "'. ");
} }
@ -309,47 +290,44 @@ namespace ParrelSync
#region Creating symlinks #region Creating symlinks
/// <summary> /// <summary>
/// Creates a symlink between destinationPath and sourcePath (Mac version). /// Creates a symlink between destinationPath and sourcePath (Mac version).
/// </summary> /// </summary>
/// <param name="sourcePath"></param> /// <param name="sourcePath"></param>
/// <param name="destinationPath"></param> /// <param name="destinationPath"></param>
private static void CreateLinkMac(string sourcePath, string destinationPath) private static void CreateLinkMac(string sourcePath, string destinationPath) {
{
sourcePath = sourcePath.Replace(" ", "\\ "); sourcePath = sourcePath.Replace(" ", "\\ ");
destinationPath = destinationPath.Replace(" ", "\\ "); destinationPath = destinationPath.Replace(" ", "\\ ");
var command = string.Format("ln -s {0} {1}", sourcePath, destinationPath); string command = string.Format("ln -s {0} {1}", sourcePath, destinationPath);
Debug.Log("Mac hard link " + command); Debug.Log("Mac hard link " + command);
ClonesManager.ExecuteBashCommand(command); ExecuteBashCommand(command);
} }
/// <summary> /// <summary>
/// Creates a symlink between destinationPath and sourcePath (Linux version). /// Creates a symlink between destinationPath and sourcePath (Linux version).
/// </summary> /// </summary>
/// <param name="sourcePath"></param> /// <param name="sourcePath"></param>
/// <param name="destinationPath"></param> /// <param name="destinationPath"></param>
private static void CreateLinkLinux(string sourcePath, string destinationPath) private static void CreateLinkLinux(string sourcePath, string destinationPath) {
{
sourcePath = sourcePath.Replace(" ", "\\ "); sourcePath = sourcePath.Replace(" ", "\\ ");
destinationPath = destinationPath.Replace(" ", "\\ "); destinationPath = destinationPath.Replace(" ", "\\ ");
var command = string.Format("ln -s {0} {1}", sourcePath, destinationPath); string command = string.Format("ln -s {0} {1}", sourcePath, destinationPath);
Debug.Log("Linux Symlink " + command); Debug.Log("Linux Symlink " + command);
ClonesManager.ExecuteBashCommand(command); ExecuteBashCommand(command);
} }
/// <summary> /// <summary>
/// Creates a symlink between destinationPath and sourcePath (Windows version). /// Creates a symlink between destinationPath and sourcePath (Windows version).
/// </summary> /// </summary>
/// <param name="sourcePath"></param> /// <param name="sourcePath"></param>
/// <param name="destinationPath"></param> /// <param name="destinationPath"></param>
private static void CreateLinkWin(string sourcePath, string destinationPath) private static void CreateLinkWin(string sourcePath, string destinationPath) {
{
string cmd = "/C mklink /J " + string.Format("\"{0}\" \"{1}\"", destinationPath, sourcePath); string cmd = "/C mklink /J " + string.Format("\"{0}\" \"{1}\"", destinationPath, sourcePath);
Debug.Log("Windows junction: " + cmd); Debug.Log("Windows junction: " + cmd);
ClonesManager.StartHiddenConsoleProcess("cmd.exe", cmd); StartHiddenConsoleProcess("cmd.exe", cmd);
} }
//TODO(?) avoid terminal calls and use proper api stuff. See below for windows! //TODO(?) avoid terminal calls and use proper api stuff. See below for windows!
@ -361,52 +339,44 @@ namespace ParrelSync
// out int pBytesReturned, System.IntPtr lpOverlapped); // out int pBytesReturned, System.IntPtr lpOverlapped);
/// <summary> /// <summary>
/// Create a link / junction from the original project to it's clone. /// Create a link / junction from the original project to it's clone.
/// </summary> /// </summary>
/// <param name="sourcePath"></param> /// <param name="sourcePath"></param>
/// <param name="destinationPath"></param> /// <param name="destinationPath"></param>
public static void LinkFolders(string sourcePath, string destinationPath) public static void LinkFolders(string sourcePath, string destinationPath) {
{ if (Directory.Exists(destinationPath) == false && Directory.Exists(sourcePath))
if ((Directory.Exists(destinationPath) == false) && (Directory.Exists(sourcePath) == true)) switch (Application.platform) {
{ case RuntimePlatform.WindowsEditor:
switch (Application.platform)
{
case (RuntimePlatform.WindowsEditor):
CreateLinkWin(sourcePath, destinationPath); CreateLinkWin(sourcePath, destinationPath);
break; break;
case (RuntimePlatform.OSXEditor): case RuntimePlatform.OSXEditor:
CreateLinkMac(sourcePath, destinationPath); CreateLinkMac(sourcePath, destinationPath);
break; break;
case (RuntimePlatform.LinuxEditor): case RuntimePlatform.LinuxEditor:
CreateLinkLinux(sourcePath, destinationPath); CreateLinkLinux(sourcePath, destinationPath);
break; break;
default: default:
Debug.LogWarning("Not in a known editor. Application.platform: " + Application.platform); Debug.LogWarning("Not in a known editor. Application.platform: " + Application.platform);
break; break;
} }
}
else else
{
Debug.LogWarning("Skipping Asset link, it already exists: " + destinationPath); Debug.LogWarning("Skipping Asset link, it already exists: " + destinationPath);
}
} }
#endregion #endregion
#region Utility methods #region Utility methods
private static bool? isCloneFileExistCache = null; private static bool? isCloneFileExistCache;
/// <summary> /// <summary>
/// Returns true if the project currently open in Unity Editor is a clone. /// Returns true if the project currently open in Unity Editor is a clone.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static bool IsClone() public static bool IsClone() {
{ if (isCloneFileExistCache == null) {
if (isCloneFileExistCache == null)
{
/// The project is a clone if its root directory contains an empty file named ".clone". /// The project is a clone if its root directory contains an empty file named ".clone".
string cloneFilePath = Path.Combine(ClonesManager.GetCurrentProjectPath(), ClonesManager.CloneFileName); string cloneFilePath = Path.Combine(GetCurrentProjectPath(), CloneFileName);
isCloneFileExistCache = File.Exists(cloneFilePath); isCloneFileExistCache = File.Exists(cloneFilePath);
} }
@ -414,85 +384,71 @@ namespace ParrelSync
} }
/// <summary> /// <summary>
/// Get the path to the current unityEditor project folder's info /// Get the path to the current unityEditor project folder's info
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static string GetCurrentProjectPath() public static string GetCurrentProjectPath() {
{
return Application.dataPath.Replace("/Assets", ""); return Application.dataPath.Replace("/Assets", "");
} }
/// <summary> /// <summary>
/// Return a project object that describes all the paths we need to clone it. /// Return a project object that describes all the paths we need to clone it.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static Project GetCurrentProject() public static Project GetCurrentProject() {
{ string pathString = GetCurrentProjectPath();
string pathString = ClonesManager.GetCurrentProjectPath();
return new Project(pathString); return new Project(pathString);
} }
/// <summary> /// <summary>
/// Get the argument of this clone project. /// Get the argument of this clone project.
/// If this is the original project, will return an empty string. /// If this is the original project, will return an empty string.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static string GetArgument() public static string GetArgument() {
{
string argument = ""; string argument = "";
if (IsClone()) if (IsClone()) {
{ string argumentFilePath = Path.Combine(GetCurrentProjectPath(), ArgumentFileName);
string argumentFilePath = Path.Combine(GetCurrentProjectPath(), ClonesManager.ArgumentFileName); if (File.Exists(argumentFilePath)) argument = File.ReadAllText(argumentFilePath, Encoding.UTF8);
if (File.Exists(argumentFilePath))
{
argument = File.ReadAllText(argumentFilePath, System.Text.Encoding.UTF8);
}
} }
return argument; return argument;
} }
/// <summary> /// <summary>
/// Returns the path to the original project. /// Returns the path to the original project.
/// If currently open project is the original, returns its own path. /// If currently open project is the original, returns its own path.
/// If the original project folder cannot be found, retuns an empty string. /// If the original project folder cannot be found, retuns an empty string.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static string GetOriginalProjectPath() public static string GetOriginalProjectPath() {
{ if (IsClone()) {
if (IsClone())
{
/// If this is a clone... /// If this is a clone...
/// Original project path can be deduced by removing the suffix from the clone's path. /// Original project path can be deduced by removing the suffix from the clone's path.
string cloneProjectPath = ClonesManager.GetCurrentProject().projectPath; string cloneProjectPath = GetCurrentProject().projectPath;
int index = cloneProjectPath.LastIndexOf(ClonesManager.CloneNameSuffix); int index = cloneProjectPath.LastIndexOf(CloneNameSuffix);
if (index > 0) if (index > 0) {
{
string originalProjectPath = cloneProjectPath.Substring(0, index); string originalProjectPath = cloneProjectPath.Substring(0, index);
if (Directory.Exists(originalProjectPath)) return originalProjectPath; if (Directory.Exists(originalProjectPath)) return originalProjectPath;
} }
return string.Empty; return string.Empty;
} }
else
{ /// If this is the original, we return its own path.
/// If this is the original, we return its own path. return GetCurrentProjectPath();
return ClonesManager.GetCurrentProjectPath();
}
} }
/// <summary> /// <summary>
/// Returns all clone projects path. /// Returns all clone projects path.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public static List<string> GetCloneProjectsPath() public static List<string> GetCloneProjectsPath() {
{ List<string> projectsPath = new();
List<string> projectsPath = new List<string>(); for (int i = 0; i < MaxCloneProjectCount; i++) {
for (int i = 0; i < MaxCloneProjectCount; i++) string originalProjectPath = GetCurrentProject().projectPath;
{ string cloneProjectPath = originalProjectPath + CloneNameSuffix + "_" + i;
string originalProjectPath = ClonesManager.GetCurrentProject().projectPath;
string cloneProjectPath = originalProjectPath + ClonesManager.CloneNameSuffix + "_" + i;
if (Directory.Exists(cloneProjectPath)) if (Directory.Exists(cloneProjectPath))
projectsPath.Add(cloneProjectPath); projectsPath.Add(cloneProjectPath);
@ -502,28 +458,27 @@ namespace ParrelSync
} }
/// <summary> /// <summary>
/// Copies directory located at sourcePath to destinationPath. Displays a progress bar. /// Copies directory located at sourcePath to destinationPath. Displays a progress bar.
/// </summary> /// </summary>
/// <param name="source">Directory to be copied.</param> /// <param name="source">Directory to be copied.</param>
/// <param name="destination">Destination directory (created automatically if needed).</param> /// <param name="destination">Destination directory (created automatically if needed).</param>
/// <param name="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param> /// <param name="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param>
public static void CopyDirectoryWithProgressBar(string sourcePath, string destinationPath, public static void CopyDirectoryWithProgressBar(string sourcePath, string destinationPath,
string progressBarPrefix = "") string progressBarPrefix = "") {
{ DirectoryInfo source = new(sourcePath);
var source = new DirectoryInfo(sourcePath); DirectoryInfo destination = new(destinationPath);
var destination = new DirectoryInfo(destinationPath);
long totalBytes = 0; long totalBytes = 0;
long copiedBytes = 0; long copiedBytes = 0;
ClonesManager.CopyDirectoryWithProgressBarRecursive(source, destination, ref totalBytes, ref copiedBytes, CopyDirectoryWithProgressBarRecursive(source, destination, ref totalBytes, ref copiedBytes,
progressBarPrefix); progressBarPrefix);
EditorUtility.ClearProgressBar(); EditorUtility.ClearProgressBar();
} }
/// <summary> /// <summary>
/// Copies directory located at sourcePath to destinationPath. Displays a progress bar. /// Copies directory located at sourcePath to destinationPath. Displays a progress bar.
/// Same as the previous method, but uses recursion to copy all nested folders as well. /// Same as the previous method, but uses recursion to copy all nested folders as well.
/// </summary> /// </summary>
/// <param name="source">Directory to be copied.</param> /// <param name="source">Directory to be copied.</param>
/// <param name="destination">Destination directory (created automatically if needed).</param> /// <param name="destination">Destination directory (created automatically if needed).</param>
@ -531,36 +486,25 @@ namespace ParrelSync
/// <param name="copiedBytes">To track already copied bytes. Calculated automatically, initialize at 0.</param> /// <param name="copiedBytes">To track already copied bytes. Calculated automatically, initialize at 0.</param>
/// <param name="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param> /// <param name="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param>
private static void CopyDirectoryWithProgressBarRecursive(DirectoryInfo source, DirectoryInfo destination, private static void CopyDirectoryWithProgressBarRecursive(DirectoryInfo source, DirectoryInfo destination,
ref long totalBytes, ref long copiedBytes, string progressBarPrefix = "") ref long totalBytes, ref long copiedBytes, string progressBarPrefix = "") {
{
/// Directory cannot be copied into itself. /// Directory cannot be copied into itself.
if (source.FullName.ToLower() == destination.FullName.ToLower()) if (source.FullName.ToLower() == destination.FullName.ToLower()) {
{
Debug.LogError("Cannot copy directory into itself."); Debug.LogError("Cannot copy directory into itself.");
return; return;
} }
/// Calculate total bytes, if required. /// Calculate total bytes, if required.
if (totalBytes == 0) if (totalBytes == 0) totalBytes = GetDirectorySize(source, true, progressBarPrefix);
{
totalBytes = ClonesManager.GetDirectorySize(source, true, progressBarPrefix);
}
/// Create destination directory, if required. /// Create destination directory, if required.
if (!Directory.Exists(destination.FullName)) if (!Directory.Exists(destination.FullName)) Directory.CreateDirectory(destination.FullName);
{
Directory.CreateDirectory(destination.FullName);
}
/// Copy all files from the source. /// Copy all files from the source.
foreach (FileInfo file in source.GetFiles()) foreach (FileInfo file in source.GetFiles()) {
{ try {
try
{
file.CopyTo(Path.Combine(destination.ToString(), file.Name), true); file.CopyTo(Path.Combine(destination.ToString(), file.Name), true);
} }
catch (IOException) catch (IOException) {
{
/// Some files may throw IOException if they are currently open in Unity editor. /// Some files may throw IOException if they are currently open in Unity editor.
/// Just ignore them in such case. /// Just ignore them in such case.
} }
@ -569,7 +513,7 @@ namespace ParrelSync
copiedBytes += file.Length; copiedBytes += file.Length;
/// Display the progress bar. /// Display the progress bar.
float progress = (float)copiedBytes / (float)totalBytes; float progress = copiedBytes / (float)totalBytes;
bool cancelCopy = EditorUtility.DisplayCancelableProgressBar( bool cancelCopy = EditorUtility.DisplayCancelableProgressBar(
progressBarPrefix + "Copying '" + source.FullName + "' to '" + destination.FullName + "'...", progressBarPrefix + "Copying '" + source.FullName + "' to '" + destination.FullName + "'...",
"(" + (progress * 100f).ToString("F2") + "%) Copying file '" + file.Name + "'...", "(" + (progress * 100f).ToString("F2") + "%) Copying file '" + file.Name + "'...",
@ -578,66 +522,57 @@ namespace ParrelSync
} }
/// Copy all nested directories from the source. /// Copy all nested directories from the source.
foreach (DirectoryInfo sourceNestedDir in source.GetDirectories()) foreach (DirectoryInfo sourceNestedDir in source.GetDirectories()) {
{
DirectoryInfo nextDestingationNestedDir = destination.CreateSubdirectory(sourceNestedDir.Name); DirectoryInfo nextDestingationNestedDir = destination.CreateSubdirectory(sourceNestedDir.Name);
ClonesManager.CopyDirectoryWithProgressBarRecursive(sourceNestedDir, nextDestingationNestedDir, CopyDirectoryWithProgressBarRecursive(sourceNestedDir, nextDestingationNestedDir,
ref totalBytes, ref copiedBytes, progressBarPrefix); ref totalBytes, ref copiedBytes, progressBarPrefix);
} }
} }
/// <summary> /// <summary>
/// Calculates the size of the given directory. Displays a progress bar. /// Calculates the size of the given directory. Displays a progress bar.
/// </summary> /// </summary>
/// <param name="directory">Directory, which size has to be calculated.</param> /// <param name="directory">Directory, which size has to be calculated.</param>
/// <param name="includeNested">If true, size will include all nested directories.</param> /// <param name="includeNested">If true, size will include all nested directories.</param>
/// <param name="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param> /// <param name="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param>
/// <returns>Size of the directory in bytes.</returns> /// <returns>Size of the directory in bytes.</returns>
private static long GetDirectorySize(DirectoryInfo directory, bool includeNested = false, private static long GetDirectorySize(DirectoryInfo directory, bool includeNested = false,
string progressBarPrefix = "") string progressBarPrefix = "") {
{
EditorUtility.DisplayProgressBar(progressBarPrefix + "Calculating size of directories...", EditorUtility.DisplayProgressBar(progressBarPrefix + "Calculating size of directories...",
"Scanning '" + directory.FullName + "'...", 0f); "Scanning '" + directory.FullName + "'...", 0f);
/// Calculate size of all files in directory. /// Calculate size of all files in directory.
long filesSize = directory.GetFiles().Sum((FileInfo file) => file.Length); long filesSize = directory.GetFiles().Sum(file => file.Length);
/// Calculate size of all nested directories. /// Calculate size of all nested directories.
long directoriesSize = 0; long directoriesSize = 0;
if (includeNested) if (includeNested) {
{
IEnumerable<DirectoryInfo> nestedDirectories = directory.GetDirectories(); IEnumerable<DirectoryInfo> nestedDirectories = directory.GetDirectories();
foreach (DirectoryInfo nestedDir in nestedDirectories) foreach (DirectoryInfo nestedDir in nestedDirectories)
{ directoriesSize += GetDirectorySize(nestedDir, true, progressBarPrefix);
directoriesSize += ClonesManager.GetDirectorySize(nestedDir, true, progressBarPrefix);
}
} }
return filesSize + directoriesSize; return filesSize + directoriesSize;
} }
/// <summary> /// <summary>
/// Starts process in the system console, taking the given fileName and args. /// Starts process in the system console, taking the given fileName and args.
/// </summary> /// </summary>
/// <param name="fileName"></param> /// <param name="fileName"></param>
/// <param name="args"></param> /// <param name="args"></param>
private static void StartHiddenConsoleProcess(string fileName, string args) private static void StartHiddenConsoleProcess(string fileName, string args) {
{ Process.Start(fileName, args);
System.Diagnostics.Process.Start(fileName, args);
} }
/// <summary> /// <summary>
/// Thanks to https://github.com/karl-/unity-symlink-utility/blob/master/SymlinkUtility.cs /// Thanks to https://github.com/karl-/unity-symlink-utility/blob/master/SymlinkUtility.cs
/// </summary> /// </summary>
/// <param name="command"></param> /// <param name="command"></param>
private static void ExecuteBashCommand(string command) private static void ExecuteBashCommand(string command) {
{
command = command.Replace("\"", "\"\""); command = command.Replace("\"", "\"\"");
var proc = new Process() Process proc = new(){
{ StartInfo = new ProcessStartInfo{
StartInfo = new ProcessStartInfo
{
FileName = "/bin/bash", FileName = "/bin/bash",
Arguments = "-c \"" + command + "\"", Arguments = "-c \"" + command + "\"",
UseShellExecute = false, UseShellExecute = false,
@ -647,22 +582,18 @@ namespace ParrelSync
} }
}; };
using (proc) using (proc) {
{
proc.Start(); proc.Start();
proc.WaitForExit(); proc.WaitForExit();
if (!proc.StandardError.EndOfStream) if (!proc.StandardError.EndOfStream) Debug.LogError(proc.StandardError.ReadToEnd());
{
UnityEngine.Debug.LogError(proc.StandardError.ReadToEnd());
}
} }
} }
public static void OpenProjectInFileExplorer(string path) public static void OpenProjectInFileExplorer(string path) {
{ Process.Start(path);
System.Diagnostics.Process.Start(@path);
} }
#endregion #endregion
} }
} }

@ -1,106 +1,84 @@
using UnityEngine; using System.Collections.Generic;
using UnityEditor;
using System.IO; using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
namespace ParrelSync namespace ParrelSync {
{
/// <summary> /// <summary>
///Clones manager Unity editor window /// Clones manager Unity editor window
/// </summary> /// </summary>
public class ClonesManagerWindow : EditorWindow public class ClonesManagerWindow : EditorWindow {
{
/// <summary> /// <summary>
/// Returns true if project clone exists. /// For storing the scroll position of clones list
/// </summary> /// </summary>
public bool isCloneCreated private Vector2 clonesScrollPos;
{
get { return ClonesManager.GetCloneProjectsPath().Count >= 1; }
}
[MenuItem("ParrelSync/Clones Manager", priority = 0)]
private static void InitWindow()
{
ClonesManagerWindow window = (ClonesManagerWindow)EditorWindow.GetWindow(typeof(ClonesManagerWindow));
window.titleContent = new GUIContent("Clones Manager");
window.Show();
}
/// <summary> /// <summary>
/// For storing the scroll position of clones list /// Returns true if project clone exists.
/// </summary> /// </summary>
Vector2 clonesScrollPos; public bool isCloneCreated => ClonesManager.GetCloneProjectsPath().Count >= 1;
private void OnGUI() private void OnGUI() {
{
/// If it is a clone project... /// If it is a clone project...
if (ClonesManager.IsClone()) if (ClonesManager.IsClone()) {
{
//Find out the original project name and show the help box //Find out the original project name and show the help box
string originalProjectPath = ClonesManager.GetOriginalProjectPath(); string originalProjectPath = ClonesManager.GetOriginalProjectPath();
if (originalProjectPath == string.Empty) if (originalProjectPath == string.Empty)
{
/// If original project cannot be found, display warning message. /// If original project cannot be found, display warning message.
EditorGUILayout.HelpBox( EditorGUILayout.HelpBox(
"This project is a clone, but the link to the original seems lost.\nYou have to manually open the original and create a new clone instead of this one.\n", "This project is a clone, but the link to the original seems lost.\nYou have to manually open the original and create a new clone instead of this one.\n",
MessageType.Warning); MessageType.Warning);
}
else else
{
/// If original project is present, display some usage info. /// If original project is present, display some usage info.
EditorGUILayout.HelpBox( EditorGUILayout.HelpBox(
"This project is a clone of the project '" + Path.GetFileName(originalProjectPath) + "'.\nIf you want to make changes the project files or manage clones, please open the original project through Unity Hub.", "This project is a clone of the project '" + Path.GetFileName(originalProjectPath) +
"'.\nIf you want to make changes the project files or manage clones, please open the original project through Unity Hub.",
MessageType.Info); MessageType.Info);
}
//Clone project custom argument. //Clone project custom argument.
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Arguments", GUILayout.Width(70)); EditorGUILayout.LabelField("Arguments", GUILayout.Width(70));
if (GUILayout.Button("?", GUILayout.Width(20))) if (GUILayout.Button("?", GUILayout.Width(20)))
{
Application.OpenURL(ExternalLinks.CustomArgumentHelpLink); Application.OpenURL(ExternalLinks.CustomArgumentHelpLink);
}
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
string argumentFilePath = Path.Combine(ClonesManager.GetCurrentProjectPath(), ClonesManager.ArgumentFileName); string argumentFilePath =
Path.Combine(ClonesManager.GetCurrentProjectPath(), ClonesManager.ArgumentFileName);
//Need to be careful with file reading / writing since it will effect the deletion of //Need to be careful with file reading / writing since it will effect the deletion of
// the clone project(The directory won't be fully deleted if there's still file inside being read or write). // the clone project(The directory won't be fully deleted if there's still file inside being read or write).
//The argument file will be deleted first at the beginning of the project deletion process //The argument file will be deleted first at the beginning of the project deletion process
//to prevent any further being read and write. //to prevent any further being read and write.
//Will need to take some extra cautious if want to change the design of how file editing is handled. //Will need to take some extra cautious if want to change the design of how file editing is handled.
if (File.Exists(argumentFilePath)) if (File.Exists(argumentFilePath)) {
{ string argument = File.ReadAllText(argumentFilePath, Encoding.UTF8);
string argument = File.ReadAllText(argumentFilePath, System.Text.Encoding.UTF8);
string argumentTextAreaInput = EditorGUILayout.TextArea(argument, string argumentTextAreaInput = EditorGUILayout.TextArea(argument,
GUILayout.Height(50), GUILayout.Height(50),
GUILayout.MaxWidth(300) GUILayout.MaxWidth(300)
); );
File.WriteAllText(argumentFilePath, argumentTextAreaInput, System.Text.Encoding.UTF8); File.WriteAllText(argumentFilePath, argumentTextAreaInput, Encoding.UTF8);
} }
else else {
{
EditorGUILayout.LabelField("No argument file found."); EditorGUILayout.LabelField("No argument file found.");
} }
} }
else// If it is an original project... else // If it is an original project...
{ {
if (isCloneCreated) if (isCloneCreated) {
{
GUILayout.BeginVertical("HelpBox"); GUILayout.BeginVertical("HelpBox");
GUILayout.Label("Clones of this Project"); GUILayout.Label("Clones of this Project");
//List all clones //List all clones
clonesScrollPos = clonesScrollPos =
EditorGUILayout.BeginScrollView(clonesScrollPos); EditorGUILayout.BeginScrollView(clonesScrollPos);
var cloneProjectsPath = ClonesManager.GetCloneProjectsPath(); List<string> cloneProjectsPath = ClonesManager.GetCloneProjectsPath();
for (int i = 0; i < cloneProjectsPath.Count; i++) for (int i = 0; i < cloneProjectsPath.Count; i++) {
{
GUILayout.BeginVertical("GroupBox"); GUILayout.BeginVertical("GroupBox");
string cloneProjectPath = cloneProjectsPath[i]; string cloneProjectPath = cloneProjectsPath[i];
bool isOpenInAnotherInstance = ClonesManager.IsCloneProjectRunning(cloneProjectPath); bool isOpenInAnotherInstance = ClonesManager.IsCloneProjectRunning(cloneProjectPath);
if (isOpenInAnotherInstance == true) if (isOpenInAnotherInstance)
EditorGUILayout.LabelField("Clone " + i + " (Running)", EditorStyles.boldLabel); EditorGUILayout.LabelField("Clone " + i + " (Running)", EditorStyles.boldLabel);
else else
EditorGUILayout.LabelField("Clone " + i); EditorGUILayout.LabelField("Clone " + i);
@ -109,17 +87,13 @@ namespace ParrelSync
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
EditorGUILayout.TextField("Clone project path", cloneProjectPath, EditorStyles.textField); EditorGUILayout.TextField("Clone project path", cloneProjectPath, EditorStyles.textField);
if (GUILayout.Button("View Folder", GUILayout.Width(80))) if (GUILayout.Button("View Folder", GUILayout.Width(80)))
{
ClonesManager.OpenProjectInFileExplorer(cloneProjectPath); ClonesManager.OpenProjectInFileExplorer(cloneProjectPath);
}
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Arguments", GUILayout.Width(70)); EditorGUILayout.LabelField("Arguments", GUILayout.Width(70));
if (GUILayout.Button("?", GUILayout.Width(20))) if (GUILayout.Button("?", GUILayout.Width(20)))
{
Application.OpenURL(ExternalLinks.CustomArgumentHelpLink); Application.OpenURL(ExternalLinks.CustomArgumentHelpLink);
}
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
string argumentFilePath = Path.Combine(cloneProjectPath, ClonesManager.ArgumentFileName); string argumentFilePath = Path.Combine(cloneProjectPath, ClonesManager.ArgumentFileName);
@ -128,17 +102,15 @@ namespace ParrelSync
//The argument file will be deleted first at the beginning of the project deletion process //The argument file will be deleted first at the beginning of the project deletion process
//to prevent any further being read and write. //to prevent any further being read and write.
//Will need to take some extra cautious if want to change the design of how file editing is handled. //Will need to take some extra cautious if want to change the design of how file editing is handled.
if (File.Exists(argumentFilePath)) if (File.Exists(argumentFilePath)) {
{ string argument = File.ReadAllText(argumentFilePath, Encoding.UTF8);
string argument = File.ReadAllText(argumentFilePath, System.Text.Encoding.UTF8);
string argumentTextAreaInput = EditorGUILayout.TextArea(argument, string argumentTextAreaInput = EditorGUILayout.TextArea(argument,
GUILayout.Height(50), GUILayout.Height(50),
GUILayout.MaxWidth(300) GUILayout.MaxWidth(300)
); );
File.WriteAllText(argumentFilePath, argumentTextAreaInput, System.Text.Encoding.UTF8); File.WriteAllText(argumentFilePath, argumentTextAreaInput, Encoding.UTF8);
} }
else else {
{
EditorGUILayout.LabelField("No argument file found."); EditorGUILayout.LabelField("No argument file found.");
} }
@ -149,50 +121,44 @@ namespace ParrelSync
EditorGUI.BeginDisabledGroup(isOpenInAnotherInstance); EditorGUI.BeginDisabledGroup(isOpenInAnotherInstance);
if (GUILayout.Button("Open in New Editor")) if (GUILayout.Button("Open in New Editor")) ClonesManager.OpenProject(cloneProjectPath);
{
ClonesManager.OpenProject(cloneProjectPath);
}
GUILayout.BeginHorizontal(); GUILayout.BeginHorizontal();
if (GUILayout.Button("Delete")) if (GUILayout.Button("Delete")) {
{
bool delete = EditorUtility.DisplayDialog( bool delete = EditorUtility.DisplayDialog(
"Delete the clone?", "Delete the clone?",
"Are you sure you want to delete the clone project '" + ClonesManager.GetCurrentProject().name + "_clone'?", "Are you sure you want to delete the clone project '" +
ClonesManager.GetCurrentProject().name + "_clone'?",
"Delete", "Delete",
"Cancel"); "Cancel");
if (delete) if (delete) ClonesManager.DeleteClone(cloneProjectPath);
{
ClonesManager.DeleteClone(cloneProjectPath);
}
} }
GUILayout.EndHorizontal(); GUILayout.EndHorizontal();
EditorGUI.EndDisabledGroup(); EditorGUI.EndDisabledGroup();
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
EditorGUILayout.EndScrollView(); EditorGUILayout.EndScrollView();
if (GUILayout.Button("Add new clone")) if (GUILayout.Button("Add new clone")) ClonesManager.CreateCloneFromCurrent();
{
ClonesManager.CreateCloneFromCurrent();
}
GUILayout.EndVertical(); GUILayout.EndVertical();
GUILayout.FlexibleSpace(); GUILayout.FlexibleSpace();
} }
else else {
{
/// If no clone created yet, we must create it. /// If no clone created yet, we must create it.
EditorGUILayout.HelpBox("No project clones found. Create a new one!", MessageType.Info); EditorGUILayout.HelpBox("No project clones found. Create a new one!", MessageType.Info);
if (GUILayout.Button("Create new clone")) if (GUILayout.Button("Create new clone")) ClonesManager.CreateCloneFromCurrent();
{
ClonesManager.CreateCloneFromCurrent();
}
} }
} }
} }
[MenuItem("ParrelSync/Clones Manager", priority = 0)]
private static void InitWindow() {
ClonesManagerWindow window = (ClonesManagerWindow)GetWindow(typeof(ClonesManagerWindow));
window.titleContent = new GUIContent("Clones Manager");
window.Show();
}
} }
} }

@ -1,8 +1,8 @@
namespace ParrelSync namespace ParrelSync {
{ public class ExternalLinks {
public class ExternalLinks public const string RemoteVersionURL =
{ "https://raw.githubusercontent.com/VeriorPies/ParrelSync/master/VERSION.txt";
public const string RemoteVersionURL = "https://raw.githubusercontent.com/VeriorPies/ParrelSync/master/VERSION.txt";
public const string Releases = "https://github.com/VeriorPies/ParrelSync/releases"; public const string Releases = "https://github.com/VeriorPies/ParrelSync/releases";
public const string CustomArgumentHelpLink = "https://github.com/VeriorPies/ParrelSync/wiki/Argument"; public const string CustomArgumentHelpLink = "https://github.com/VeriorPies/ParrelSync/wiki/Argument";

@ -1,22 +1,16 @@
using System.IO; using System.IO;
using UnityEngine; using UnityEngine;
namespace ParrelSync namespace ParrelSync {
{ public class FileUtilities : MonoBehaviour {
public class FileUtilities : MonoBehaviour public static bool IsFileLocked(string path) {
{ FileInfo file = new(path);
public static bool IsFileLocked(string path) try {
{ using (FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None)) {
FileInfo file = new FileInfo(path);
try
{
using (FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None))
{
stream.Close(); stream.Close();
} }
} }
catch (IOException) catch (IOException) {
{
//the file is unavailable because it is: //the file is unavailable because it is:
//still being written to //still being written to
//or being processed by another thread //or being processed by another thread

@ -1,45 +1,38 @@
namespace ParrelSync.NonCore using UnityEditor;
{ using UnityEngine;
using UnityEditor;
using UnityEngine;
namespace ParrelSync.NonCore {
/// <summary> /// <summary>
/// A simple script to display feedback/star dialog after certain time of project being opened/re-compiled. /// A simple script to display feedback/star dialog after certain time of project being opened/re-compiled.
/// Will only pop-up once unless "Remind me next time" are chosen. /// Will only pop-up once unless "Remind me next time" are chosen.
/// Removing this file from project wont effect any other functions. /// Removing this file from project wont effect any other functions.
/// </summary> /// </summary>
[InitializeOnLoad] [InitializeOnLoad]
public class AskFeedbackDialog public class AskFeedbackDialog {
{ private const string InitializeOnLoadCountKey = "ParrelSync_InitOnLoadCount",
const string InitializeOnLoadCountKey = "ParrelSync_InitOnLoadCount", StopShowingKey = "ParrelSync_StopShowFeedBack"; StopShowingKey = "ParrelSync_StopShowFeedBack";
static AskFeedbackDialog()
{ static AskFeedbackDialog() {
if (EditorPrefs.HasKey(StopShowingKey)) { return; } if (EditorPrefs.HasKey(StopShowingKey)) return;
int InitializeOnLoadCount = EditorPrefs.GetInt(InitializeOnLoadCountKey, 0); int InitializeOnLoadCount = EditorPrefs.GetInt(InitializeOnLoadCountKey, 0);
if (InitializeOnLoadCount > 20) if (InitializeOnLoadCount > 20)
{
ShowDialog(); ShowDialog();
}
else else
{
EditorPrefs.SetInt(InitializeOnLoadCountKey, InitializeOnLoadCount + 1); EditorPrefs.SetInt(InitializeOnLoadCountKey, InitializeOnLoadCount + 1);
}
} }
//[MenuItem("ParrelSync/(Debug)Show AskFeedbackDialog ")] //[MenuItem("ParrelSync/(Debug)Show AskFeedbackDialog ")]
private static void ShowDialog() private static void ShowDialog() {
{ int option = EditorUtility.DisplayDialogComplex("Do you like " + ClonesManager.ProjectName + "?",
int option = EditorUtility.DisplayDialogComplex("Do you like " + ParrelSync.ClonesManager.ProjectName + "?", "Do you like " + ClonesManager.ProjectName + "?\n" +
"Do you like " + ParrelSync.ClonesManager.ProjectName + "?\n" + "If so, please don't hesitate to star it on GitHub and contribute to the project!",
"If so, please don't hesitate to star it on GitHub and contribute to the project!", "Star on GitHub",
"Star on GitHub", "Close",
"Close", "Remind me next time"
"Remind me next time" );
);
switch (option) switch (option) {
{
// First parameter. // First parameter.
case 0: case 0:
Debug.Log("AskFeedbackDialog: Star on GitHub selected"); Debug.Log("AskFeedbackDialog: Star on GitHub selected");
@ -58,9 +51,6 @@
Debug.Log("AskFeedbackDialog: Remind me next time"); Debug.Log("AskFeedbackDialog: Remind me next time");
EditorPrefs.SetInt(InitializeOnLoadCountKey, 0); EditorPrefs.SetInt(InitializeOnLoadCountKey, 0);
break; break;
default:
//Debug.Log("Close windows.");
break;
} }
} }

@ -1,25 +1,20 @@
namespace ParrelSync.NonCore using UnityEditor;
{ using UnityEngine;
using UnityEditor;
using UnityEngine;
public class OtherMenuItem namespace ParrelSync.NonCore {
{ public class OtherMenuItem {
[MenuItem("ParrelSync/GitHub/View this project on GitHub", priority = 10)] [MenuItem("ParrelSync/GitHub/View this project on GitHub", priority = 10)]
private static void OpenGitHub() private static void OpenGitHub() {
{
Application.OpenURL(ExternalLinks.GitHubHome); Application.OpenURL(ExternalLinks.GitHubHome);
} }
[MenuItem("ParrelSync/GitHub/View FAQ", priority = 11)] [MenuItem("ParrelSync/GitHub/View FAQ", priority = 11)]
private static void OpenFAQ() private static void OpenFAQ() {
{
Application.OpenURL(ExternalLinks.FAQ); Application.OpenURL(ExternalLinks.FAQ);
} }
[MenuItem("ParrelSync/GitHub/View Issues", priority = 12)] [MenuItem("ParrelSync/GitHub/View Issues", priority = 12)]
private static void OpenGitHubIssues() private static void OpenGitHubIssues() {
{
Application.OpenURL(ExternalLinks.GitHubIssue); Application.OpenURL(ExternalLinks.GitHubIssue);
} }
} }

@ -1,34 +1,29 @@
using UnityEngine; using UnityEditor;
using UnityEditor; using UnityEngine;
namespace ParrelSync namespace ParrelSync {
{
/// <summary> /// <summary>
/// To add value caching for <see cref="EditorPrefs"/> functions /// To add value caching for <see cref="EditorPrefs" /> functions
/// </summary> /// </summary>
public class BoolPreference public class BoolPreference {
{ private bool? valueCache;
public string key { get; private set; }
public bool defaultValue { get; private set; } public BoolPreference(string key, bool defaultValue) {
public BoolPreference(string key, bool defaultValue)
{
this.key = key; this.key = key;
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
} }
private bool? valueCache = null; public string key { get; }
public bool defaultValue { get; }
public bool Value public bool Value {
{ get {
get
{
if (valueCache == null) if (valueCache == null)
valueCache = EditorPrefs.GetBool(key, defaultValue); valueCache = EditorPrefs.GetBool(key, defaultValue);
return (bool)valueCache; return (bool)valueCache;
} }
set set {
{
if (valueCache == value) if (valueCache == value)
return; return;
@ -38,41 +33,30 @@ namespace ParrelSync
} }
} }
public void ClearValue() public void ClearValue() {
{
EditorPrefs.DeleteKey(key); EditorPrefs.DeleteKey(key);
valueCache = null; valueCache = null;
} }
} }
public class Preferences : EditorWindow public class Preferences : EditorWindow {
{
[MenuItem("ParrelSync/Preferences", priority = 1)]
private static void InitWindow()
{
Preferences window = (Preferences)EditorWindow.GetWindow(typeof(Preferences));
window.titleContent = new GUIContent(ClonesManager.ProjectName + " Preferences");
window.Show();
}
/// <summary> /// <summary>
/// Disable asset saving in clone editors? /// Disable asset saving in clone editors?
/// </summary> /// </summary>
public static BoolPreference AssetModPref = new BoolPreference("ParrelSync_DisableClonesAssetSaving", true); public static BoolPreference AssetModPref = new("ParrelSync_DisableClonesAssetSaving", true);
/// <summary> /// <summary>
/// In addition of checking the existence of UnityLockFile, /// In addition of checking the existence of UnityLockFile,
/// also check is the is the UnityLockFile being opened. /// also check is the is the UnityLockFile being opened.
/// </summary> /// </summary>
public static BoolPreference AlsoCheckUnityLockFileStaPref = new BoolPreference("ParrelSync_CheckUnityLockFileOpenStatus", true); public static BoolPreference AlsoCheckUnityLockFileStaPref =
new("ParrelSync_CheckUnityLockFileOpenStatus", true);
private void OnGUI() private void OnGUI() {
{ if (ClonesManager.IsClone()) {
if (ClonesManager.IsClone())
{
EditorGUILayout.HelpBox( EditorGUILayout.HelpBox(
"This is a clone project. Please use the original project editor to change preferences.", "This is a clone project. Please use the original project editor to change preferences.",
MessageType.Info); MessageType.Info);
return; return;
} }
@ -88,7 +72,6 @@ namespace ParrelSync
AssetModPref.Value); AssetModPref.Value);
if (Application.platform == RuntimePlatform.WindowsEditor) if (Application.platform == RuntimePlatform.WindowsEditor)
{
AlsoCheckUnityLockFileStaPref.Value = EditorGUILayout.ToggleLeft( AlsoCheckUnityLockFileStaPref.Value = EditorGUILayout.ToggleLeft(
new GUIContent( new GUIContent(
"Also check UnityLockFile lock status while checking clone projects running status", "Also check UnityLockFile lock status while checking clone projects running status",
@ -96,15 +79,21 @@ namespace ParrelSync
"(the Clones Manager window show the clone project is still running even it's not) if the clone editor crashed" "(the Clones Manager window show the clone project is still running even it's not) if the clone editor crashed"
), ),
AlsoCheckUnityLockFileStaPref.Value); AlsoCheckUnityLockFileStaPref.Value);
}
GUILayout.EndVertical(); GUILayout.EndVertical();
if (GUILayout.Button("Reset to default")) if (GUILayout.Button("Reset to default")) {
{
AssetModPref.ClearValue(); AssetModPref.ClearValue();
AlsoCheckUnityLockFileStaPref.ClearValue(); AlsoCheckUnityLockFileStaPref.ClearValue();
Debug.Log("Editor preferences cleared"); Debug.Log("Editor preferences cleared");
} }
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
[MenuItem("ParrelSync/Preferences", priority = 1)]
private static void InitWindow() {
Preferences window = (Preferences)GetWindow(typeof(Preferences));
window.titleContent = new GUIContent(ClonesManager.ProjectName + " Preferences");
window.Show();
}
} }
} }

@ -1,49 +1,44 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace ParrelSync namespace ParrelSync {
{ public class Project : ICloneable {
public class Project : System.ICloneable
{
public string name;
public string projectPath;
string rootPath;
public string assetPath; public string assetPath;
public string projectSettingsPath;
public string libraryPath;
public string packagesPath;
public string autoBuildPath; public string autoBuildPath;
public string libraryPath;
public string localPackages; public string localPackages;
public string name;
public string packagesPath;
public string projectPath;
public string projectSettingsPath;
private string rootPath;
char[] separator = new char[1] { '/' }; private char[] separator = new char[1]{ '/' };
/// <summary> /// <summary>
/// Default constructor /// Default constructor
/// </summary> /// </summary>
public Project() public Project() { }
{
}
/// <summary> /// <summary>
/// Initialize the project object by parsing its full path returned by Unity into a bunch of individual folder names and paths. /// Initialize the project object by parsing its full path returned by Unity into a bunch of individual folder names
/// and paths.
/// </summary> /// </summary>
/// <param name="path"></param> /// <param name="path"></param>
public Project(string path) public Project(string path) {
{
ParsePath(path); ParsePath(path);
} }
/// <summary> /// <summary>
/// Create a new object with the same settings /// Create a new object with the same settings
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public object Clone() public object Clone() {
{ Project newProject = new();
Project newProject = new Project();
newProject.rootPath = rootPath; newProject.rootPath = rootPath;
newProject.projectPath = projectPath; newProject.projectPath = projectPath;
newProject.assetPath = assetPath; newProject.assetPath = assetPath;
@ -61,22 +56,21 @@ namespace ParrelSync
/// <summary> /// <summary>
/// Update the project object by renaming and reparsing it. Pass in the new name of a project, and it'll update the other member variables to match. /// Update the project object by renaming and reparsing it. Pass in the new name of a project, and it'll update the
/// other member variables to match.
/// </summary> /// </summary>
/// <param name="name"></param> /// <param name="name"></param>
public void updateNewName(string newName) public void updateNewName(string newName) {
{
name = newName; name = newName;
ParsePath(rootPath + "/" + name + "/Assets"); ParsePath(rootPath + "/" + name + "/Assets");
} }
/// <summary> /// <summary>
/// Debug override so we can quickly print out the project info. /// Debug override so we can quickly print out the project info.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public override string ToString() public override string ToString() {
{
string printString = name + "\n" + string printString = name + "\n" +
rootPath + "\n" + rootPath + "\n" +
projectPath + "\n" + projectPath + "\n" +
@ -86,16 +80,15 @@ namespace ParrelSync
autoBuildPath + "\n" + autoBuildPath + "\n" +
localPackages + "\n" + localPackages + "\n" +
libraryPath; libraryPath;
return (printString); return printString;
} }
private void ParsePath(string path) private void ParsePath(string path) {
{
//Unity's Application functions return the Assets path in the Editor. //Unity's Application functions return the Assets path in the Editor.
projectPath = path; projectPath = path;
//pop off the last part of the path for the project name, keep the rest for the root path //pop off the last part of the path for the project name, keep the rest for the root path
List<string> pathArray = projectPath.Split(separator).ToList<string>(); List<string> pathArray = projectPath.Split(separator).ToList();
name = pathArray.Last(); name = pathArray.Last();
pathArray.RemoveAt(pathArray.Count() - 1); pathArray.RemoveAt(pathArray.Count() - 1);

@ -1,22 +1,20 @@
using System; using System;
using System.Net;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
namespace ParrelSync.Update
{ namespace ParrelSync.Update {
/// <summary> /// <summary>
/// A simple update checker /// A simple update checker
/// </summary> /// </summary>
public class UpdateChecker public class UpdateChecker {
{
//const string LocalVersionFilePath = "Assets/ParrelSync/VERSION.txt"; //const string LocalVersionFilePath = "Assets/ParrelSync/VERSION.txt";
public const string LocalVersion = "1.5.1"; public const string LocalVersion = "1.5.1";
[MenuItem("ParrelSync/Check for update", priority = 20)] [MenuItem("ParrelSync/Check for update", priority = 20)]
static void CheckForUpdate() private static void CheckForUpdate() {
{ using (WebClient client = new()) {
using (System.Net.WebClient client = new System.Net.WebClient()) try {
{
try
{
//This won't work with UPM packages //This won't work with UPM packages
//string localVersionText = AssetDatabase.LoadAssetAtPath<TextAsset>(LocalVersionFilePath).text; //string localVersionText = AssetDatabase.LoadAssetAtPath<TextAsset>(LocalVersionFilePath).text;
@ -25,36 +23,31 @@ namespace ParrelSync.Update
string latesteVersionText = client.DownloadString(ExternalLinks.RemoteVersionURL); string latesteVersionText = client.DownloadString(ExternalLinks.RemoteVersionURL);
Debug.Log("latest version text got: " + latesteVersionText); Debug.Log("latest version text got: " + latesteVersionText);
string messageBody = "Current Version: " + localVersionText +"\n" string messageBody = "Current Version: " + localVersionText + "\n"
+"Latest Version: " + latesteVersionText + "\n"; + "Latest Version: " + latesteVersionText + "\n";
var latestVersion = new Version(latesteVersionText); Version latestVersion = new Version(latesteVersionText);
var localVersion = new Version(localVersionText); Version localVersion = new Version(localVersionText);
if (latestVersion > localVersion) if (latestVersion > localVersion) {
{
Debug.Log("There's a newer version"); Debug.Log("There's a newer version");
messageBody += "There's a newer version available"; messageBody += "There's a newer version available";
if(EditorUtility.DisplayDialog("Check for update.", messageBody, "Get latest release", "Close")) if (EditorUtility.DisplayDialog("Check for update.", messageBody, "Get latest release",
{ "Close")) Application.OpenURL(ExternalLinks.Releases);
Application.OpenURL(ExternalLinks.Releases);
}
} }
else else {
{
Debug.Log("Current version is up-to-date."); Debug.Log("Current version is up-to-date.");
messageBody += "Current version is up-to-date."; messageBody += "Current version is up-to-date.";
EditorUtility.DisplayDialog("Check for update.", messageBody,"OK"); EditorUtility.DisplayDialog("Check for update.", messageBody, "OK");
} }
} }
catch (Exception exp) catch (Exception exp) {
{
Debug.LogError("Error with checking update. Exception: " + exp); Debug.LogError("Error with checking update. Exception: " + exp);
EditorUtility.DisplayDialog("Update Error","Error with checking update. \nSee console for more details.", EditorUtility.DisplayDialog("Update Error",
"OK" "Error with checking update. \nSee console for more details.",
"OK"
); );
} }
} }
} }
} }
} }

@ -1,57 +1,52 @@
namespace ParrelSync using System;
{ using System.Collections.Generic;
using UnityEditor; using System.IO;
using UnityEngine; using System.Linq;
using System; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Security.Cryptography; using UnityEditor;
using System.IO; using UnityEngine;
using System.Linq;
namespace ParrelSync {
[InitializeOnLoad] [InitializeOnLoad]
public class ValidateCopiedFoldersIntegrity public class ValidateCopiedFoldersIntegrity {
{ private const string SessionStateKey = "ValidateCopiedFoldersIntegrity_Init";
const string SessionStateKey = "ValidateCopiedFoldersIntegrity_Init";
/// <summary> /// <summary>
/// Called once on editor startup. /// Called once on editor startup.
/// Validate copied folders integrity in clone project /// Validate copied folders integrity in clone project
/// </summary> /// </summary>
static ValidateCopiedFoldersIntegrity() static ValidateCopiedFoldersIntegrity() {
{ if (!SessionState.GetBool(SessionStateKey, false)) {
if (!SessionState.GetBool(SessionStateKey, false))
{
SessionState.SetBool(SessionStateKey, true); SessionState.SetBool(SessionStateKey, true);
if (!ClonesManager.IsClone()) { return; } if (!ClonesManager.IsClone()) return;
ValidateFolder(ClonesManager.GetCurrentProjectPath(), ClonesManager.GetOriginalProjectPath(), "Packages"); ValidateFolder(ClonesManager.GetCurrentProjectPath(), ClonesManager.GetOriginalProjectPath(),
"Packages");
} }
} }
public static void ValidateFolder(string targetRoot, string originalRoot, string folderName) public static void ValidateFolder(string targetRoot, string originalRoot, string folderName) {
{ string targetFolderPath = Path.Combine(targetRoot, folderName);
var targetFolderPath = Path.Combine(targetRoot, folderName); string targetFolderHash = CreateMd5ForFolder(targetFolderPath);
var targetFolderHash = CreateMd5ForFolder(targetFolderPath);
var originalFolderPath = Path.Combine(originalRoot, folderName); string originalFolderPath = Path.Combine(originalRoot, folderName);
var originalFolderHash = CreateMd5ForFolder(originalFolderPath); string originalFolderHash = CreateMd5ForFolder(originalFolderPath);
if (targetFolderHash != originalFolderHash) if (targetFolderHash != originalFolderHash) {
{
Debug.Log("ParrelSync: Detected changes in '" + folderName + "' directory. Updating cloned project..."); Debug.Log("ParrelSync: Detected changes in '" + folderName + "' directory. Updating cloned project...");
FileUtil.ReplaceDirectory(originalFolderPath, targetFolderPath); FileUtil.ReplaceDirectory(originalFolderPath, targetFolderPath);
} }
} }
static string CreateMd5ForFolder(string path) private static string CreateMd5ForFolder(string path) {
{
// assuming you want to include nested folders // assuming you want to include nested folders
var files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories) List<string> files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)
.OrderBy(p => p).ToList(); .OrderBy(p => p).ToList();
MD5 md5 = MD5.Create(); MD5 md5 = MD5.Create();
for (int i = 0; i < files.Count; i++) for (int i = 0; i < files.Count; i++) {
{
string file = files[i]; string file = files[i];
// hash path // hash path

@ -1,10 +1,15 @@
{ {
"name": "com.veriorpies.parrelsync", "name": "com.veriorpies.parrelsync",
"displayName": "ParrelSync", "displayName": "ParrelSync",
"version": "1.5.1", "version": "1.5.1",
"unity": "2018.4", "unity": "2018.4",
"description": "ParrelSync is a Unity editor extension that allows users to test multiplayer gameplay without building the project by having another Unity editor window opened and mirror the changes from the original project.", "description": "ParrelSync is a Unity editor extension that allows users to test multiplayer gameplay without building the project by having another Unity editor window opened and mirror the changes from the original project.",
"license": "MIT", "license": "MIT",
"keywords": [ "Networking", "Utils", "Editor", "Extensions" ], "keywords": [
"dependencies": {} "Networking",
"Utils",
"Editor",
"Extensions"
],
"dependencies": {}
} }

@ -1,15 +1,15 @@
{ {
"name": "ParrelSync", "name": "ParrelSync",
"references": [], "references": [],
"includePlatforms": [ "includePlatforms": [
"Editor" "Editor"
], ],
"excludePlatforms": [], "excludePlatforms": [],
"allowUnsafeCode": false, "allowUnsafeCode": false,
"overrideReferences": false, "overrideReferences": false,
"precompiledReferences": [], "precompiledReferences": [],
"autoReferenced": true, "autoReferenced": true,
"defineConstraints": [], "defineConstraints": [],
"versionDefines": [], "versionDefines": [],
"noEngineReferences": false "noEngineReferences": false
} }

@ -1,7 +1,7 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// <auto-generated> // <auto-generated>
// This code was auto-generated by com.unity.inputsystem:InputActionCodeGenerator // This code was auto-generated by com.unity.inputsystem:InputActionCodeGenerator
// version 1.5.0 // version 1.5.1
// from Assets/PlayerInputActions.inputactions // from Assets/PlayerInputActions.inputactions
// //
// Changes to this file may cause incorrect behavior and will be lost if // Changes to this file may cause incorrect behavior and will be lost if

@ -534,7 +534,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 684533fb770d426797cb0bf29a6bcdf3, type: 3} m_Script: {fileID: 11500000, guid: 684533fb770d426797cb0bf29a6bcdf3, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
cuttingRecipeSOArray: CuttingRecipeSOArray:
- {fileID: 11400000, guid: 7099fe20df5fcf646869c078799be4e6, type: 2} - {fileID: 11400000, guid: 7099fe20df5fcf646869c078799be4e6, type: 2}
- {fileID: 11400000, guid: 15b9c81d8c4442547ab5bc3aa788ecc8, type: 2}
- {fileID: 11400000, guid: 1f51b2ff38b5b7b498f16d7ea781b45a, type: 2} - {fileID: 11400000, guid: 1f51b2ff38b5b7b498f16d7ea781b45a, type: 2}
- {fileID: 11400000, guid: 15b9c81d8c4442547ab5bc3aa788ecc8, type: 2}

@ -1,21 +1,20 @@
using Unity.Netcode.Components; using Unity.Netcode.Components;
using UnityEngine; using UnityEngine;
namespace Unity.Multiplayer.Samples.Utilities.ClientAuthority namespace Unity.Multiplayer.Samples.Utilities.ClientAuthority {
{
/// <summary> /// <summary>
/// Used for syncing a transform with client side changes. This includes host. Pure server as owner isn't supported by this. Please use NetworkTransform /// Used for syncing a transform with client side changes. This includes host. Pure server as owner isn't supported by
/// for transforms that'll always be owned by the server. /// this. Please use NetworkTransform
/// for transforms that'll always be owned by the server.
/// </summary> /// </summary>
[DisallowMultipleComponent] [DisallowMultipleComponent]
public class ClientNetworkTransform : NetworkTransform public class ClientNetworkTransform : NetworkTransform {
{
/// <summary> /// <summary>
/// Used to determine who can write to this transform. Owner client only. /// Used to determine who can write to this transform. Owner client only.
/// This imposes state to the server. This is putting trust on your clients. Make sure no security-sensitive features use this transform. /// This imposes state to the server. This is putting trust on your clients. Make sure no security-sensitive features
/// use this transform.
/// </summary> /// </summary>
protected override bool OnIsServerAuthoritative() protected override bool OnIsServerAuthoritative() {
{
return false; return false;
} }
} }

@ -3,33 +3,28 @@ using Unity.Netcode;
using UnityEngine; using UnityEngine;
public class BaseCounter : NetworkBehaviour, IKitchenObjectParent { public class BaseCounter : NetworkBehaviour, IKitchenObjectParent {
public static event EventHandler OnAnyObjectPlacedHere; public KitchenObject kitchenObject;
public Transform CounterTopPoint { get; set; } private Transform CounterTopPoint { get; set; }
public Transform KitchenObjectHoldPoint { get; set; }
private KitchenObject kitchenObject; public Transform GetKitchenObjectFollowTransform() => CounterTopPoint;
public void Awake() {
CounterTopPoint = transform.Find("CounterTopPoint");
}
public KitchenObject KitchenObject { public KitchenObject KitchenObject {
get => kitchenObject; get => kitchenObject;
set { set {
kitchenObject = value; kitchenObject = value;
if (value != null) { if (value is not null) OnAnyObjectPlacedHere?.Invoke(this, EventArgs.Empty);
OnAnyObjectPlacedHere?.Invoke(this, EventArgs.Empty);
}
} }
} }
public void Awake() { public NetworkObject GetNetworkObject() => NetworkObject;
CounterTopPoint = transform.Find("CounterTopPoint");
KitchenObjectHoldPoint = CounterTopPoint;
}
public virtual void Interact(Player player) { public static event EventHandler OnAnyObjectPlacedHere;
Debug.LogError("Something went wrong, Interaction with BaseCounter");
}
public virtual void InteractAlternate(Player player) { public virtual void Interact(Player player) => Debug.LogError("Something went wrong, Interaction with BaseCounter");
Debug.LogError("Something went wrong, Interaction with BaseCounter");
}
public NetworkObject GetNetworkObject() => NetworkObject; public virtual void InteractAlternate(Player player) => Debug.LogError("Something went wrong, Interaction with BaseCounter");
} }

@ -1,57 +1,40 @@
using UnityEngine; using UnityEngine;
public class ClearCounter : BaseCounter, IKitchenObjectParent public class ClearCounter : BaseCounter, IKitchenObjectParent {
{ public KitchenObjectSO KitchenObjectSO { get; set; }
public KitchenObjectSO KitchenObjectSO { get; set; }
public override void Interact(Player player) public override void Interact(Player player) {
{ if (KitchenObject == null) {
if (KitchenObject == null) // Debug.Log("There is no KitchenObject here");
{ if (player.KitchenObject != null) {
// Debug.Log("There is no KitchenObject here"); //Debug.Log($"Player is putting {player.KitchenObject.KitchenObjectSO.objectName} to ClearCounter");
if (player.KitchenObject != null) player.KitchenObject.SetKitchenObjectParent(this);
{ player.KitchenObject = null;
//Debug.Log($"Player is putting {player.KitchenObject.KitchenObjectSO.objectName} to ClearCounter"); }
player.KitchenObject.SetKitchenObjectParent(this); else {
player.KitchenObject = null; Debug.Log("Player not carrying anything");
} }
else }
{ else {
Debug.Log("Player not carrying anything"); // Debug.Log("There is a KitchenObject");
} if (player.KitchenObject != null) {
} if (player.KitchenObject.TryGetPlate(out PlateKitchenObject plateKitchenObject)) {
else // Debug.Log("Player is holding a plate");
{ if (plateKitchenObject.TryAddIngredient(KitchenObject.KitchenObjectSO)) KitchenObject.DestroySelf();
// Debug.Log("There is a KitchenObject"); }
if (player.KitchenObject != null) else {
{ // Debug.Log("Player is carrying something but not a plate");
if (player.KitchenObject.TryGetPlate(out PlateKitchenObject plateKitchenObject)) if (KitchenObject.TryGetPlate(out plateKitchenObject))
{ // Debug.Log("ClearCounter is holding a plate");
// Debug.Log("Player is holding a plate"); if (plateKitchenObject.TryAddIngredient(player.KitchenObject.KitchenObjectSO))
if (plateKitchenObject.TryAddIngredient(KitchenObject.KitchenObjectSO)) player.KitchenObject.DestroySelf();
{ }
KitchenObject.DestroySelf(); }
} else {
} Debug.Log("Player is taking KitchenObject from ClearCounter");
else KitchenObject.SetKitchenObjectParent(player);
{ KitchenObject = null;
// Debug.Log("Player is carrying something but not a plate"); }
if (KitchenObject.TryGetPlate(out plateKitchenObject)) }
{ }
// Debug.Log("ClearCounter is holding a plate");
if (plateKitchenObject.TryAddIngredient(player.KitchenObject.KitchenObjectSO))
{
player.KitchenObject.DestroySelf();
}
}
}
}
else
{
Debug.Log("Player is taking KitchenObject from ClearCounter");
KitchenObject.SetKitchenObjectParent(player);
KitchenObject = null;
}
}
}
} }

@ -2,24 +2,24 @@ using System;
using Unity.Netcode; using Unity.Netcode;
using UnityEngine; using UnityEngine;
public class ContainerCounter : BaseCounter {
public class ContainerCounter : BaseCounter
{
[SerializeField] private KitchenObjectSO kitchenObjectSO; [SerializeField] private KitchenObjectSO kitchenObjectSO;
public event EventHandler OnPlayerGrabbedObject; public event EventHandler OnPlayerGrabbedObject;
public override void Interact(Player player) public override void Interact(Player player) {
{ if (kitchenObjectSO != null && player.KitchenObject == null) {
if (kitchenObjectSO != null && player.KitchenObject == null)
{
KitchenObject.SpawnKitchenObject(kitchenObjectSO, player); KitchenObject.SpawnKitchenObject(kitchenObjectSO, player);
InteractLogicServerRpc(); InteractLogicServerRpc();
} }
} }
[ServerRpc(RequireOwnership = false)] [ServerRpc(RequireOwnership = false)]
private void InteractLogicServerRpc() => InteractLogicClientRpc(); public void InteractLogicServerRpc() {
InteractLogicClientRpc();
}
[ClientRpc] [ClientRpc]
private void InteractLogicClientRpc() => OnPlayerGrabbedObject?.Invoke(this, EventArgs.Empty); public void InteractLogicClientRpc() {
} OnPlayerGrabbedObject?.Invoke(this, EventArgs.Empty);
}
}

@ -1,22 +1,20 @@
using System; using System;
using UnityEngine; using UnityEngine;
public class ContainerCounterVisual : MonoBehaviour {
public class ContainerCounterVisual : MonoBehaviour
{
private static readonly int openClose = Animator.StringToHash("OpenClose"); private static readonly int openClose = Animator.StringToHash("OpenClose");
[SerializeField] private ContainerCounter containerCounter; [SerializeField] private ContainerCounter containerCounter;
private Animator animator; private Animator animator;
private void Awake() private void Awake() {
{
animator = GetComponent<Animator>(); animator = GetComponent<Animator>();
} }
private void Start() private void Start() {
{
containerCounter.OnPlayerGrabbedObject += ContainerCounter_OnPlayerGrabbedObject; containerCounter.OnPlayerGrabbedObject += ContainerCounter_OnPlayerGrabbedObject;
} }
private void ContainerCounter_OnPlayerGrabbedObject(object sender, System.EventArgs e) => animator.SetTrigger(openClose); private void ContainerCounter_OnPlayerGrabbedObject(object sender, EventArgs e) {
} animator.SetTrigger(openClose);
}
}

@ -3,40 +3,31 @@ using System.Linq;
using Unity.Netcode; using Unity.Netcode;
using UnityEngine; using UnityEngine;
public class CuttingCounter : BaseCounter, IHasProgress, IKitchenObjectParent public class CuttingCounter : BaseCounter, IHasProgress {
{ [SerializeField] private CuttingRecipeSO[] CuttingRecipeSOArray;
[SerializeField] private CuttingRecipeSO[] cuttingRecipeSOArray;
private int cuttingProgress; private int cuttingProgress;
public static event EventHandler OnAnyCut; public static event EventHandler OnAnyCut;
public event EventHandler<IHasProgress.ProgressChangedEventArgs> OnProgressChanged; public event EventHandler<IHasProgress.ProgressChangedEventArgs> OnProgressChanged;
public event EventHandler OnCut; public event EventHandler OnCut;
public override void Interact(Player player) public override void Interact(Player player) {
{ if (KitchenObject == null) {
if (KitchenObject == null) if (player.KitchenObject is null) return;
{ if (!HasRecipeWithInput(player.KitchenObject.KitchenObjectSO)) return;
if (player.KitchenObject == null) return;
if (HasRecipeWithInput(player.KitchenObject.KitchenObjectSO)) KitchenObject kitchenObject = player.KitchenObject;
{ kitchenObject.SetKitchenObjectParent(this);
KitchenObject kitchenObject = player.KitchenObject; player.KitchenObject = null;
kitchenObject.SetKitchenObjectParent(this); InteractLogicPlaceObjectOnCounterServerRpc();
player.KitchenObject = null;
InteractLogicPlaceObjectOnCounterServerRpc();
}
} }
else else {
{
if (player.KitchenObject == null) return; if (player.KitchenObject == null) return;
if (player.KitchenObject.TryGetPlate(out PlateKitchenObject plateKitchenObject)) if (player.KitchenObject.TryGetPlate(out PlateKitchenObject plateKitchenObject)) {
{
if (plateKitchenObject.TryAddIngredient(KitchenObject.KitchenObjectSO)) KitchenObject.DestroySelf(); if (plateKitchenObject.TryAddIngredient(KitchenObject.KitchenObjectSO)) KitchenObject.DestroySelf();
} }
else else {
{
KitchenObject.SetKitchenObjectParent(player); KitchenObject.SetKitchenObjectParent(player);
KitchenObject = null; KitchenObject = null;
} }
@ -44,70 +35,59 @@ public class CuttingCounter : BaseCounter, IHasProgress, IKitchenObjectParent
} }
[ServerRpc(RequireOwnership = false)] [ServerRpc(RequireOwnership = false)]
private void InteractLogicPlaceObjectOnCounterServerRpc() => InteractLogicPlaceObjectOnCounterClientRpc(); public void InteractLogicPlaceObjectOnCounterServerRpc() => InteractLogicPlaceObjectOnCounterClientRpc();
[ClientRpc] [ClientRpc]
private void InteractLogicPlaceObjectOnCounterClientRpc() public void InteractLogicPlaceObjectOnCounterClientRpc() {
{
cuttingProgress = 0; cuttingProgress = 0;
OnProgressChanged?.Invoke(this, new() { ProgressNormalized = 0f }); OnProgressChanged?.Invoke(this, new IHasProgress.ProgressChangedEventArgs{ ProgressNormalized = 0f });
} }
public override void InteractAlternate(Player player) public override void InteractAlternate(Player player) {
{
if (KitchenObject == null) return; if (KitchenObject == null) return;
if (!HasRecipeWithInput(KitchenObject.KitchenObjectSO)) return;
if (HasRecipeWithInput(KitchenObject.KitchenObjectSO)) CutObjectServerRpc();
{ TestCuttingProgressDoneServerRpc();
CutObjectServerRpc();
TestCuttingProgressDoneServerRpc();
}
} }
[ServerRpc(RequireOwnership = false)] [ServerRpc(RequireOwnership = false)]
private void CutObjectServerRpc() => CutObjectClientRpc(); public void CutObjectServerRpc() => CutObjectClientRpc();
[ClientRpc] [ClientRpc]
private void CutObjectClientRpc() public void CutObjectClientRpc() {
{
cuttingProgress++; cuttingProgress++;
OnCut?.Invoke(this, EventArgs.Empty); OnCut?.Invoke(this, EventArgs.Empty);
OnAnyCut?.Invoke(this, EventArgs.Empty); OnAnyCut?.Invoke(this, EventArgs.Empty);
CuttingRecipeSO cuttingRecipeSO = GetCuttingRecipeSOWithInput(KitchenObject.KitchenObjectSO); CuttingRecipeSO cuttingRecipeSO = GetCuttingRecipeSOWithInput(KitchenObject.KitchenObjectSO);
OnProgressChanged?.Invoke(this, new() { ProgressNormalized = (float)cuttingProgress / cuttingRecipeSO.cuttingProgressMax }); OnProgressChanged?.Invoke(this, new IHasProgress.ProgressChangedEventArgs
{ ProgressNormalized = (float)cuttingProgress / cuttingRecipeSO.cuttingProgressMax });
} }
[ServerRpc(RequireOwnership = false)] [ServerRpc(RequireOwnership = false)]
private void TestCuttingProgressDoneServerRpc() public void TestCuttingProgressDoneServerRpc() {
{
CuttingRecipeSO cuttingRecipeSO = GetCuttingRecipeSOWithInput(KitchenObject.KitchenObjectSO); CuttingRecipeSO cuttingRecipeSO = GetCuttingRecipeSOWithInput(KitchenObject.KitchenObjectSO);
if (cuttingRecipeSO is null) return;
if (cuttingProgress < cuttingRecipeSO.cuttingProgressMax) return; if (cuttingProgress < cuttingRecipeSO.cuttingProgressMax) return;
KitchenObjectSO outputKitchenObjectSO = GetOutputForInput(KitchenObject.KitchenObjectSO); KitchenObjectSO outputKitchenObjectSO = GetOutputForInput(KitchenObject.KitchenObjectSO);
if (outputKitchenObjectSO == null) return; if (outputKitchenObjectSO is null) return;
KitchenObject.DestroyKitchenObject(KitchenObject); KitchenObject.DestroyKitchenObject(KitchenObject);
KitchenObject = null;
KitchenObject.SpawnKitchenObject(outputKitchenObjectSO, this); KitchenObject.SpawnKitchenObject(outputKitchenObjectSO, this);
} }
private bool HasRecipeWithInput(KitchenObjectSO inputKitchenObjectSO) private bool HasRecipeWithInput(KitchenObjectSO inputKitchenObjectSO) => GetCuttingRecipeSOWithInput(inputKitchenObjectSO) is not null;
{
return GetCuttingRecipeSOWithInput(inputKitchenObjectSO) != null;
}
private KitchenObjectSO GetOutputForInput(KitchenObjectSO inputKitchenObjectSO) private KitchenObjectSO GetOutputForInput(KitchenObjectSO inputKitchenObjectSO) {
{
CuttingRecipeSO cuttingRecipeSO = GetCuttingRecipeSOWithInput(inputKitchenObjectSO); CuttingRecipeSO cuttingRecipeSO = GetCuttingRecipeSOWithInput(inputKitchenObjectSO);
return cuttingRecipeSO ? cuttingRecipeSO.output : null; return cuttingRecipeSO ? cuttingRecipeSO.output : null;
} }
private CuttingRecipeSO GetCuttingRecipeSOWithInput(KitchenObjectSO inputKitchenObjectSO) private CuttingRecipeSO GetCuttingRecipeSOWithInput(KitchenObjectSO inputKitchenObjectSO)
{ => CuttingRecipeSOArray.FirstOrDefault(cuttingRecipeSO => cuttingRecipeSO.input == inputKitchenObjectSO);
return cuttingRecipeSOArray.FirstOrDefault(cuttingRecipeSO => cuttingRecipeSO.input == inputKitchenObjectSO);
} public static void ResetStaticData() => OnAnyCut = null;
} }

@ -1,24 +1,20 @@
using System; using System;
using UnityEngine; using UnityEngine;
public class CuttingCounterVisual : MonoBehaviour public class CuttingCounterVisual : MonoBehaviour {
{ private static readonly int cut = Animator.StringToHash("Cut");
private static readonly int cut = Animator.StringToHash("Cut"); [SerializeField] private CuttingCounter cuttingCounter;
[SerializeField] private CuttingCounter cuttingCounter; private Animator animator;
private Animator animator;
private void Awake() private void Awake() {
{ animator = GetComponent<Animator>();
animator = GetComponent<Animator>(); }
}
private void Start() private void Start() {
{ cuttingCounter.OnCut += CuttingCounter_OnCut;
cuttingCounter.OnCut += CuttingCounter_OnCut; }
}
private void CuttingCounter_OnCut(object sender, System.EventArgs e) private void CuttingCounter_OnCut(object sender, EventArgs e) {
{ animator.SetTrigger(cut);
animator.SetTrigger(cut); }
}
} }

@ -6,11 +6,10 @@ public class DeliveryCounter : BaseCounter {
} }
public override void Interact(Player player) { public override void Interact(Player player) {
if (player.KitchenObject != null) { if (player.KitchenObject != null)
if (player.KitchenObject.TryGetPlate(out PlateKitchenObject plateKitchenObject)) { if (player.KitchenObject.TryGetPlate(out PlateKitchenObject plateKitchenObject)) {
DeliveryManager.Instance.DeliverRecipe(plateKitchenObject); DeliveryManager.Instance.DeliverRecipe(plateKitchenObject);
KitchenObject.DestroyKitchenObject(player.KitchenObject); KitchenObject.DestroyKitchenObject(player.KitchenObject);
} }
}
} }
} }

@ -2,44 +2,37 @@ using System;
using Unity.Netcode; using Unity.Netcode;
using UnityEngine; using UnityEngine;
public class PlatesCounter : BaseCounter public class PlatesCounter : BaseCounter {
{
[SerializeField] private float spawnPlateTimerMax = 10f; [SerializeField] private float spawnPlateTimerMax = 10f;
[SerializeField] private int platesSpawnedAmountMax = 6; [SerializeField] private int platesSpawnedAmountMax = 6;
[SerializeField] private KitchenObjectSO plateKitchenObjectSO; [SerializeField] private KitchenObjectSO plateKitchenObjectSO;
private int platesSpawnedAmount; private int platesSpawnedAmount;
private float spawnPlateTimer; private float spawnPlateTimer;
private void Update() private void Update() {
{
if (!IsServer) return; if (!IsServer) return;
spawnPlateTimer += Time.deltaTime; spawnPlateTimer += Time.deltaTime;
if (spawnPlateTimer > spawnPlateTimerMax) if (spawnPlateTimer > spawnPlateTimerMax) {
{
spawnPlateTimer = 0f; spawnPlateTimer = 0f;
if (KitchenGameManager.Instance.IsGamePlaying() && platesSpawnedAmount < platesSpawnedAmountMax) if (KitchenGameManager.Instance.IsGamePlaying() && platesSpawnedAmount < platesSpawnedAmountMax)
{
SpawnPlateServerRpc(); SpawnPlateServerRpc();
}
} }
} }
[ServerRpc] [ServerRpc]
private void SpawnPlateServerRpc() => SpawnPlateClientRpc(); public void SpawnPlateServerRpc() => SpawnPlateClientRpc();
[ClientRpc] [ClientRpc]
private void SpawnPlateClientRpc() public void SpawnPlateClientRpc() {
{
platesSpawnedAmount++; platesSpawnedAmount++;
OnPlateSpawned?.Invoke(this, System.EventArgs.Empty); OnPlateSpawned?.Invoke(this, EventArgs.Empty);
} }
public event EventHandler OnPlateSpawned; public event EventHandler OnPlateSpawned;
public event EventHandler OnPlateRemoved; public event EventHandler OnPlateRemoved;
public override void Interact(Player player) public override void Interact(Player player) {
{
if (player.KitchenObject != null) return; if (player.KitchenObject != null) return;
if (platesSpawnedAmount == 0) return; if (platesSpawnedAmount == 0) return;
KitchenObject.SpawnKitchenObject(plateKitchenObjectSO, player); KitchenObject.SpawnKitchenObject(plateKitchenObjectSO, player);
@ -48,12 +41,13 @@ public class PlatesCounter : BaseCounter
[ServerRpc(RequireOwnership = false)] [ServerRpc(RequireOwnership = false)]
private void InteractLogicServerRpc() => InteractLogicClientRpc(); public void InteractLogicServerRpc() {
InteractLogicClientRpc();
}
[ClientRpc] [ClientRpc]
private void InteractLogicClientRpc() public void InteractLogicClientRpc() {
{
platesSpawnedAmount--; platesSpawnedAmount--;
OnPlateRemoved?.Invoke(this, System.EventArgs.Empty); OnPlateRemoved?.Invoke(this, EventArgs.Empty);
} }
} }

@ -2,37 +2,32 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
public class PlatesCounterVisual : MonoBehaviour public class PlatesCounterVisual : MonoBehaviour {
{ [SerializeField] private PlatesCounter platesCounter;
[SerializeField] private PlatesCounter platesCounter; [SerializeField] private Transform counterTopPoint;
[SerializeField] private Transform counterTopPoint; [SerializeField] private Transform plateVisualPrefab;
[SerializeField] private Transform plateVisualPrefab;
private List<GameObject> plateVisualGameObjectList; private List<GameObject> plateVisualGameObjectList;
private void Awake() private void Awake() {
{ plateVisualGameObjectList = new List<GameObject>();
plateVisualGameObjectList = new(); }
}
private void Start() private void Start() {
{ platesCounter.OnPlateSpawned += PlatesCounter_OnPlateSpawned;
platesCounter.OnPlateSpawned += PlatesCounter_OnPlateSpawned; platesCounter.OnPlateRemoved += PlatesCounter_OnPlateRemoved;
platesCounter.OnPlateRemoved += PlatesCounter_OnPlateRemoved; }
}
private void PlatesCounter_OnPlateRemoved(object sender, System.EventArgs e) private void PlatesCounter_OnPlateRemoved(object sender, EventArgs e) {
{ GameObject plateGameObject = plateVisualGameObjectList[^1];
GameObject plateGameObject = plateVisualGameObjectList[^1]; _ = plateVisualGameObjectList.Remove(plateGameObject);
_ = plateVisualGameObjectList.Remove(plateGameObject); Destroy(plateGameObject);
Destroy(plateGameObject); }
}
private void PlatesCounter_OnPlateSpawned(object sender, System.EventArgs e) private void PlatesCounter_OnPlateSpawned(object sender, EventArgs e) {
{ Transform plateVisualTransform = Instantiate(plateVisualPrefab, counterTopPoint);
Transform plateVisualTransform = Instantiate(plateVisualPrefab, counterTopPoint); const float plateOffsetY = .1f;
const float plateOffsetY = .1f; plateVisualTransform.localPosition = new Vector3(0, plateOffsetY * plateVisualGameObjectList.Count, 0);
plateVisualTransform.localPosition = new(0, plateOffsetY * plateVisualGameObjectList.Count, 0); plateVisualGameObjectList.Add(plateVisualTransform.gameObject);
plateVisualGameObjectList.Add(plateVisualTransform.gameObject); }
}
} }

@ -2,10 +2,8 @@ using System;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
public class StoveCounter : BaseCounter, IHasProgress, IKitchenObjectParent public class StoveCounter : BaseCounter, IHasProgress, IKitchenObjectParent {
{ public enum State {
public enum State
{
Idle, Idle,
Frying, Frying,
Fried, Fried,
@ -21,24 +19,19 @@ public class StoveCounter : BaseCounter, IHasProgress, IKitchenObjectParent
private State state; private State state;
private void Start() private void Start() {
{
SetStateAndFireEvent(State.Idle); SetStateAndFireEvent(State.Idle);
} }
private void Update() private void Update() {
{
if (KitchenObject != null) if (KitchenObject != null)
{ switch (state) {
switch (state)
{
case State.Idle: case State.Idle:
break; break;
case State.Frying: case State.Frying:
fryingTimer += Time.deltaTime; fryingTimer += Time.deltaTime;
FireProgressEvent(fryingTimer / fryingRecipeSO.fryingTimerMax); FireProgressEvent(fryingTimer / fryingRecipeSO.fryingTimerMax);
if (fryingTimer > fryingRecipeSO.fryingTimerMax) if (fryingTimer > fryingRecipeSO.fryingTimerMax) {
{
KitchenObject.DestroySelf(); KitchenObject.DestroySelf();
KitchenObject.SpawnKitchenObject(fryingRecipeSO.output, this); KitchenObject.SpawnKitchenObject(fryingRecipeSO.output, this);
// Debug.Log("Object fried"); // Debug.Log("Object fried");
@ -51,8 +44,7 @@ public class StoveCounter : BaseCounter, IHasProgress, IKitchenObjectParent
case State.Fried: case State.Fried:
burningTimer += Time.deltaTime; burningTimer += Time.deltaTime;
FireProgressEvent(burningTimer / burningRecipeSO.burningTimerMax); FireProgressEvent(burningTimer / burningRecipeSO.burningTimerMax);
if (burningTimer > burningRecipeSO.burningTimerMax) if (burningTimer > burningRecipeSO.burningTimerMax) {
{
KitchenObject.DestroySelf(); KitchenObject.DestroySelf();
KitchenObject.SpawnKitchenObject(burningRecipeSO.output, this); KitchenObject.SpawnKitchenObject(burningRecipeSO.output, this);
// Debug.Log("Object burned"); // Debug.Log("Object burned");
@ -63,37 +55,28 @@ public class StoveCounter : BaseCounter, IHasProgress, IKitchenObjectParent
break; break;
case State.Burned: case State.Burned:
break; break;
default:
break;
} }
}
} }
public event EventHandler<IHasProgress.ProgressChangedEventArgs> OnProgressChanged; public event EventHandler<IHasProgress.ProgressChangedEventArgs> OnProgressChanged;
public event EventHandler<StateChangedEventArgs> OnStateChanged; public event EventHandler<StateChangedEventArgs> OnStateChanged;
private void SetStateAndFireEvent(State newState) private void SetStateAndFireEvent(State newState) {
{
state = newState; state = newState;
OnStateChanged?.Invoke(this, new() { State = newState }); OnStateChanged?.Invoke(this, new StateChangedEventArgs{ State = newState });
} }
private void FireProgressEvent(float progress) private void FireProgressEvent(float progress) {
{ OnProgressChanged?.Invoke(this, new IHasProgress.ProgressChangedEventArgs{
OnProgressChanged?.Invoke(this, new()
{
ProgressNormalized = progress, ProgressNormalized = progress,
State = state State = state
}); });
} }
public override void Interact(Player player) public override void Interact(Player player) {
{ if (KitchenObject == null) {
if (KitchenObject == null)
{
Debug.Log("There is no KitchenObject here"); Debug.Log("There is no KitchenObject here");
if (player.KitchenObject != null && HasRecipeWithInput(player.KitchenObject.KitchenObjectSO)) if (player.KitchenObject != null && HasRecipeWithInput(player.KitchenObject.KitchenObjectSO)) {
{
Debug.Log($"Player is putting {player.KitchenObject.KitchenObjectSO.objectName} to StoveCounter"); Debug.Log($"Player is putting {player.KitchenObject.KitchenObjectSO.objectName} to StoveCounter");
player.KitchenObject.SetKitchenObjectParent(this); player.KitchenObject.SetKitchenObjectParent(this);
player.KitchenObject = null; player.KitchenObject = null;
@ -101,38 +84,33 @@ public class StoveCounter : BaseCounter, IHasProgress, IKitchenObjectParent
SetStateAndFireEvent(State.Frying); SetStateAndFireEvent(State.Frying);
fryingTimer = 0f; fryingTimer = 0f;
} }
else else {
{
Debug.Log("Player not carrying anything for the StoveCounter!"); Debug.Log("Player not carrying anything for the StoveCounter!");
} }
} }
else else {
{
Debug.Log($"There is a {KitchenObject.KitchenObjectSO.objectName} on the StoveCounter"); Debug.Log($"There is a {KitchenObject.KitchenObjectSO.objectName} on the StoveCounter");
if (player.KitchenObject != null) if (player.KitchenObject != null) {
{ if (player.KitchenObject.TryGetPlate(out PlateKitchenObject plateKitchenObject)) {
if (player.KitchenObject.TryGetPlate(out PlateKitchenObject plateKitchenObject)) Debug.Log(
{ $"Player is carrying a {player.KitchenObject.KitchenObjectSO.objectName}, try to put Ingredient onto it...");
Debug.Log($"Player is carrying a {player.KitchenObject.KitchenObjectSO.objectName}, try to put Ingredient onto it..."); if (plateKitchenObject.TryAddIngredient(KitchenObject.KitchenObjectSO)) {
if (plateKitchenObject.TryAddIngredient(KitchenObject.KitchenObjectSO)) Debug.Log(
{ $"Player is taking {KitchenObject.KitchenObjectSO.objectName} from StoveCounter to plate");
Debug.Log($"Player is taking {KitchenObject.KitchenObjectSO.objectName} from StoveCounter to plate");
KitchenObject.DestroySelf(); KitchenObject.DestroySelf();
SetStateAndFireEvent(State.Idle); SetStateAndFireEvent(State.Idle);
FireProgressEvent(0f); FireProgressEvent(0f);
} }
else else {
{ Debug.Log(
Debug.Log($"Ingredient {player.KitchenObject.KitchenObjectSO.objectName} does not fit anymore!"); $"Ingredient {player.KitchenObject.KitchenObjectSO.objectName} does not fit anymore!");
} }
} }
else else {
{
Debug.Log($"Player is already carrying a {player.KitchenObject.KitchenObjectSO.objectName}!"); Debug.Log($"Player is already carrying a {player.KitchenObject.KitchenObjectSO.objectName}!");
} }
} }
else else {
{
Debug.Log($"Player is taking {KitchenObject.KitchenObjectSO.objectName} from StoveCounter to hand"); Debug.Log($"Player is taking {KitchenObject.KitchenObjectSO.objectName} from StoveCounter to hand");
KitchenObject.SetKitchenObjectParent(player); KitchenObject.SetKitchenObjectParent(player);
KitchenObject = null; KitchenObject = null;
@ -142,48 +120,39 @@ public class StoveCounter : BaseCounter, IHasProgress, IKitchenObjectParent
} }
} }
public override void InteractAlternate(Player player) public override void InteractAlternate(Player player) {
{
Debug.Log("Nothing alternate to do here!"); Debug.Log("Nothing alternate to do here!");
} }
private bool HasRecipeWithInput(KitchenObjectSO inputKitchenObjectSO) private bool HasRecipeWithInput(KitchenObjectSO inputKitchenObjectSO) {
{
return GetFryingRecipeSOWithInput(inputKitchenObjectSO); return GetFryingRecipeSOWithInput(inputKitchenObjectSO);
} }
private KitchenObjectSO GetOutputForInput(KitchenObjectSO inputKitchenObjectSO) private KitchenObjectSO GetOutputForInput(KitchenObjectSO inputKitchenObjectSO) {
{
FryingRecipeSO fRSO = GetFryingRecipeSOWithInput(inputKitchenObjectSO); FryingRecipeSO fRSO = GetFryingRecipeSOWithInput(inputKitchenObjectSO);
return fRSO ? fRSO.output : null; return fRSO ? fRSO.output : null;
} }
private FryingRecipeSO GetFryingRecipeSOWithInput(KitchenObjectSO inputKitchenObjectSO) private FryingRecipeSO GetFryingRecipeSOWithInput(KitchenObjectSO inputKitchenObjectSO) {
{
return fryingRecipeSOArray.FirstOrDefault(fRSO => fRSO.input == inputKitchenObjectSO); return fryingRecipeSOArray.FirstOrDefault(fRSO => fRSO.input == inputKitchenObjectSO);
} }
private BurningRecipeSO GetBurningRecipeSOWithInput(KitchenObjectSO inputKitchenObjectSO) private BurningRecipeSO GetBurningRecipeSOWithInput(KitchenObjectSO inputKitchenObjectSO) {
{ BurningRecipeSO burningRecipeSO =
BurningRecipeSO burningRecipeSO = burningRecipeSOArray.FirstOrDefault(fRSO => fRSO.input == inputKitchenObjectSO); burningRecipeSOArray.FirstOrDefault(fRSO => fRSO.input == inputKitchenObjectSO);
if (burningRecipeSO == null) if (burningRecipeSO == null) {
{
Debug.LogError($"No BurningRecipe found for {inputKitchenObjectSO.objectName}"); Debug.LogError($"No BurningRecipe found for {inputKitchenObjectSO.objectName}");
return null; return null;
} }
else
{ return burningRecipeSO;
return burningRecipeSO;
}
} }
public bool IsFrying() public bool IsFrying() {
{
return state == State.Frying; return state == State.Frying;
} }
public bool IsFried() public bool IsFried() {
{
return state == State.Fried; return state == State.Fried;
} }
} }

@ -1,53 +1,41 @@
using UnityEngine; using UnityEngine;
public class StoveCounterSound : MonoBehaviour public class StoveCounterSound : MonoBehaviour {
{ private const float warningSoundTimerMax = .5f;
[SerializeField] private StoveCounter stoveCounter; [SerializeField] private StoveCounter stoveCounter;
private AudioSource audioSource; private AudioSource audioSource;
private float warningSoundTimer; private bool playWarningSound;
private bool playWarningSound; private float warningSoundTimer;
private const float warningSoundTimerMax = .5f;
private void Awake() private void Awake() {
{ audioSource = GetComponent<AudioSource>();
audioSource = GetComponent<AudioSource>(); }
}
private void Start() private void Start() {
{ stoveCounter.OnStateChanged += StoveCounter_OnStateChanged;
stoveCounter.OnStateChanged += StoveCounter_OnStateChanged; stoveCounter.OnProgressChanged += StoveCounter_OnProgressChanged;
stoveCounter.OnProgressChanged += StoveCounter_OnProgressChanged; }
}
private void StoveCounter_OnStateChanged(object sender, StateChangedEventArgs e) private void Update() {
{ if (playWarningSound) {
if (e.State is StoveCounter.State.Frying or StoveCounter.State.Fried) warningSoundTimer -= Time.deltaTime;
{ if (warningSoundTimer <= 0f) {
audioSource.Play(); warningSoundTimer = warningSoundTimerMax;
} SoundManager.Instance.PlayWarningSound(stoveCounter.transform.position);
else }
{ }
audioSource.Pause(); }
}
}
private void StoveCounter_OnProgressChanged(object sender, IHasProgress.ProgressChangedEventArgs e) private void StoveCounter_OnStateChanged(object sender, StateChangedEventArgs e) {
{ if (e.State is StoveCounter.State.Frying or StoveCounter.State.Fried)
float burnShowProgressAmount = .5f; audioSource.Play();
playWarningSound = stoveCounter.IsFried() && e.ProgressNormalized >= burnShowProgressAmount; else
} audioSource.Pause();
}
private void Update() private void StoveCounter_OnProgressChanged(object sender, IHasProgress.ProgressChangedEventArgs e) {
{ float burnShowProgressAmount = .5f;
if (playWarningSound) playWarningSound = stoveCounter.IsFried() && e.ProgressNormalized >= burnShowProgressAmount;
{ }
warningSoundTimer -= Time.deltaTime;
if (warningSoundTimer <= 0f)
{
warningSoundTimer = warningSoundTimerMax;
SoundManager.Instance.PlayWarningSound(stoveCounter.transform.position);
}
}
}
} }

@ -1,20 +1,17 @@
using UnityEngine; using UnityEngine;
public class StoveCounterVisual : MonoBehaviour public class StoveCounterVisual : MonoBehaviour {
{ [SerializeField] private StoveCounter stoveCounter;
[SerializeField] private StoveCounter stoveCounter; [SerializeField] private GameObject stoveOnGameObject;
[SerializeField] private GameObject stoveOnGameObject; [SerializeField] private GameObject particlesGameObject;
[SerializeField] private GameObject particlesGameObject;
private void Start() private void Start() {
{ stoveCounter.OnStateChanged += StoveCounter_OnStateChanged;
stoveCounter.OnStateChanged += StoveCounter_OnStateChanged; }
}
private void StoveCounter_OnStateChanged(object sender, StateChangedEventArgs e) private void StoveCounter_OnStateChanged(object sender, StateChangedEventArgs e) {
{ bool showVisual = e.State is StoveCounter.State.Frying or StoveCounter.State.Fried;
bool showVisual = e.State is StoveCounter.State.Frying or StoveCounter.State.Fried; stoveOnGameObject.SetActive(showVisual);
stoveOnGameObject.SetActive(showVisual); particlesGameObject.SetActive(showVisual);
particlesGameObject.SetActive(showVisual); }
}
} }

@ -17,8 +17,12 @@ public class TrashCounter : BaseCounter {
} }
[ServerRpc(RequireOwnership = false)] [ServerRpc(RequireOwnership = false)]
private void InteractLogicServerRpc() => InteractLogicClientRpc(); public void InteractLogicServerRpc() {
InteractLogicClientRpc();
}
[ClientRpc] [ClientRpc]
private void InteractLogicClientRpc() => OnAnyObjectTrashed?.Invoke(this, EventArgs.Empty); public void InteractLogicClientRpc() {
OnAnyObjectTrashed?.Invoke(this, EventArgs.Empty);
}
} }

@ -4,31 +4,23 @@ using Unity.Netcode;
using UnityEngine; using UnityEngine;
using Random = UnityEngine.Random; using Random = UnityEngine.Random;
public class DeliveryManager : NetworkBehaviour public class DeliveryManager : NetworkBehaviour {
{ private const float spawnRecipeTimerMax = 3f;
private const int waitingRecipesMax = 5;
[SerializeField] private RecipeListSO recipeListSO;
private float spawnRecipeTimer = 3f;
public static DeliveryManager Instance { get; private set; } public static DeliveryManager Instance { get; private set; }
public List<RecipeSO> WaitingRecipeSOList { get; private set; } public List<RecipeSO> WaitingRecipeSOList { get; private set; }
public int SuccessfulRecipes { get; private set; } public int SuccessfulRecipes { get; private set; }
public int Points { get; private set; } public int Points { get; private set; }
public event EventHandler OnRecipeSpawned; private void Awake() {
public event EventHandler OnRecipeCompleted;
public event EventHandler<RecipeEventArgs> OnRecipeSuccess;
public event EventHandler OnRecipeFailed;
[SerializeField] private RecipeListSO recipeListSO;
private float spawnRecipeTimer = 3f;
private const float spawnRecipeTimerMax = 3f;
private const int waitingRecipesMax = 5;
private void Awake()
{
Instance = this; Instance = this;
WaitingRecipeSOList = new(); WaitingRecipeSOList = new List<RecipeSO>();
} }
private void Update() private void Update() {
{
if (!IsServer) return; if (!IsServer) return;
spawnRecipeTimer -= Time.deltaTime; spawnRecipeTimer -= Time.deltaTime;
@ -37,32 +29,32 @@ public class DeliveryManager : NetworkBehaviour
spawnRecipeTimer = spawnRecipeTimerMax; spawnRecipeTimer = spawnRecipeTimerMax;
if (WaitingRecipeSOList.Count >= waitingRecipesMax) return; if (WaitingRecipeSOList.Count >= waitingRecipesMax) return;
if (KitchenGameManager.Instance.IsGamePlaying()) if (KitchenGameManager.Instance.IsGamePlaying()) {
{
int waitingRecipeSOIndex = Random.Range(0, recipeListSO.recipeSOList.Count); int waitingRecipeSOIndex = Random.Range(0, recipeListSO.recipeSOList.Count);
SpawnNewWaitingRecipeClientRpc(waitingRecipeSOIndex); SpawnNewWaitingRecipeClientRpc(waitingRecipeSOIndex);
} }
} }
public event EventHandler OnRecipeSpawned;
public event EventHandler OnRecipeCompleted;
public event EventHandler<RecipeEventArgs> OnRecipeSuccess;
public event EventHandler OnRecipeFailed;
[ClientRpc] [ClientRpc]
private void SpawnNewWaitingRecipeClientRpc(int waitingRecipeSOIndex) public void SpawnNewWaitingRecipeClientRpc(int waitingRecipeSOIndex) {
{
WaitingRecipeSOList.Add(recipeListSO.recipeSOList[waitingRecipeSOIndex]); WaitingRecipeSOList.Add(recipeListSO.recipeSOList[waitingRecipeSOIndex]);
OnRecipeSpawned?.Invoke(this, EventArgs.Empty); OnRecipeSpawned?.Invoke(this, EventArgs.Empty);
} }
public void DeliverRecipe(PlateKitchenObject plateKitchenObject) public void DeliverRecipe(PlateKitchenObject plateKitchenObject) {
{ for (int i = 0; i < WaitingRecipeSOList.Count; i++) {
for (int i = 0; i < WaitingRecipeSOList.Count; i++) if (WaitingRecipeSOList[i].KitchenObjectSOList.Count !=
{ plateKitchenObject.GetKitchenObjectSOList().Count) continue;
if (WaitingRecipeSOList[i].KitchenObjectSOList.Count != plateKitchenObject.GetKitchenObjectSOList().Count) continue;
bool plateContentsMatchesRecipe = true; bool plateContentsMatchesRecipe = true;
foreach (KitchenObjectSO recipeKitchenObjectSO in WaitingRecipeSOList[i].KitchenObjectSOList) foreach (KitchenObjectSO recipeKitchenObjectSO in WaitingRecipeSOList[i].KitchenObjectSOList) {
{
bool ingredientFound = false; bool ingredientFound = false;
foreach (KitchenObjectSO plateKitchenObjectSO in plateKitchenObject.GetKitchenObjectSOList()) foreach (KitchenObjectSO plateKitchenObjectSO in plateKitchenObject.GetKitchenObjectSOList()) {
{
if (plateKitchenObjectSO != recipeKitchenObjectSO) continue; if (plateKitchenObjectSO != recipeKitchenObjectSO) continue;
ingredientFound = true; ingredientFound = true;
break; break;
@ -72,8 +64,7 @@ public class DeliveryManager : NetworkBehaviour
plateContentsMatchesRecipe = false; plateContentsMatchesRecipe = false;
} }
if (!plateContentsMatchesRecipe) if (!plateContentsMatchesRecipe) {
{
DeliverIncorrectRecipeServerRpc(); DeliverIncorrectRecipeServerRpc();
continue; continue;
} }
@ -84,26 +75,28 @@ public class DeliveryManager : NetworkBehaviour
} }
[ServerRpc(RequireOwnership = false)] [ServerRpc(RequireOwnership = false)]
private void DeliverIncorrectRecipeServerRpc() => DeliverIncorrectRecipeClientRpc(); public void DeliverIncorrectRecipeServerRpc() {
DeliverIncorrectRecipeClientRpc();
}
[ClientRpc] [ClientRpc]
private void DeliverIncorrectRecipeClientRpc() public void DeliverIncorrectRecipeClientRpc() {
{
Points -= 1; Points -= 1;
OnRecipeFailed?.Invoke(this, EventArgs.Empty); OnRecipeFailed?.Invoke(this, EventArgs.Empty);
} }
[ServerRpc(RequireOwnership = false)] [ServerRpc(RequireOwnership = false)]
private void DeliverCorrectRecipeServerRpc(int waitingRecipeSOListIndex) => DeliverCorrectRecipeClientRpc(waitingRecipeSOListIndex); public void DeliverCorrectRecipeServerRpc(int waitingRecipeSOListIndex) {
DeliverCorrectRecipeClientRpc(waitingRecipeSOListIndex);
}
[ClientRpc] [ClientRpc]
private void DeliverCorrectRecipeClientRpc(int waitingRecipeSOListIndex) public void DeliverCorrectRecipeClientRpc(int waitingRecipeSOListIndex) {
{
Debug.Log($"Player delievered a correct {WaitingRecipeSOList[waitingRecipeSOListIndex].RecipeName}."); Debug.Log($"Player delievered a correct {WaitingRecipeSOList[waitingRecipeSOListIndex].RecipeName}.");
SuccessfulRecipes++; SuccessfulRecipes++;
Points += WaitingRecipeSOList[waitingRecipeSOListIndex].Points; Points += WaitingRecipeSOList[waitingRecipeSOListIndex].Points;
WaitingRecipeSOList.RemoveAt(waitingRecipeSOListIndex); WaitingRecipeSOList.RemoveAt(waitingRecipeSOListIndex);
OnRecipeCompleted?.Invoke(this, EventArgs.Empty); OnRecipeCompleted?.Invoke(this, EventArgs.Empty);
OnRecipeSuccess?.Invoke(this, new RecipeEventArgs() { RecipeSO = WaitingRecipeSOList[waitingRecipeSOListIndex] }); OnRecipeSuccess?.Invoke(this, new RecipeEventArgs{ RecipeSO = WaitingRecipeSOList[waitingRecipeSOListIndex] });
} }
} }

@ -1,17 +1,14 @@
using System; using System;
using static StoveCounter; using static StoveCounter;
public class RecipeEventArgs : EventArgs public class RecipeEventArgs : EventArgs {
{ public RecipeSO RecipeSO;
public RecipeSO RecipeSO;
} }
public class StateChangedEventArgs : EventArgs public class StateChangedEventArgs : EventArgs {
{ public State State;
public State State;
} }
public class SelectedCounterChangedEventArgs : EventArgs public class SelectedCounterChangedEventArgs : EventArgs {
{ public BaseCounter SelectedCounter;
public BaseCounter SelectedCounter;
} }

@ -1,18 +1,12 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine; using UnityEngine;
public class FollowTransform : MonoBehaviour public class FollowTransform : MonoBehaviour {
{ public Transform TargetTransform { get; set; }
private Transform targetTransform;
public void SetTargetTransform(Transform _targetTransform) => targetTransform = _targetTransform; private void LateUpdate() {
if (TargetTransform is null) return;
private void LateUpdate() transform.position = TargetTransform.position;
{ transform.rotation = TargetTransform.rotation;
if (targetTransform == null) return;
transform.position = targetTransform.position;
transform.rotation = targetTransform.rotation;
} }
} }

@ -2,12 +2,8 @@ using System;
using UnityEngine; using UnityEngine;
using UnityEngine.InputSystem; using UnityEngine.InputSystem;
public class GameInput : MonoBehaviour public class GameInput : MonoBehaviour {
{ public enum Binding {
public static GameInput Instance { get; private set; }
public enum Binding
{
MoveUp, MoveUp,
MoveDown, MoveDown,
MoveLeft, MoveLeft,
@ -15,19 +11,19 @@ public class GameInput : MonoBehaviour
Interact, Interact,
InteractAlternate, InteractAlternate,
Pause, Pause,
Gamepad_Interact, GamepadInteract,
Gamepad_InteractAlternate, GamepadInteractAlternate,
Gamepad_Pause GamepadPause
} }
private const string PlayerPrefsBindings = "InputBindings"; private const string PlayerPrefsBindings = "InputBindings";
private static PlayerInputActions playerInputActions; private static PlayerInputActions playerInputActions;
public static GameInput Instance { get; private set; }
private void Awake() private void Awake() {
{
Instance = this; Instance = this;
playerInputActions = new(); playerInputActions = new PlayerInputActions();
if (PlayerPrefs.HasKey(PlayerPrefsBindings)) if (PlayerPrefs.HasKey(PlayerPrefsBindings))
playerInputActions.LoadBindingOverridesFromJson( playerInputActions.LoadBindingOverridesFromJson(
PlayerPrefs.GetString(PlayerPrefsBindings) PlayerPrefs.GetString(PlayerPrefsBindings)
@ -44,25 +40,19 @@ public class GameInput : MonoBehaviour
public event EventHandler OnPauseAction; public event EventHandler OnPauseAction;
public event EventHandler OnBindingRebind; public event EventHandler OnBindingRebind;
private void InteractAlternate_performed(InputAction.CallbackContext obj) => private void InteractAlternate_performed(InputAction.CallbackContext obj) => OnInteractAlternateAction?.Invoke(this, EventArgs.Empty);
OnInteractAlternateAction?.Invoke(this, System.EventArgs.Empty);
private void InteractPerformed(InputAction.CallbackContext obj) => private void InteractPerformed(InputAction.CallbackContext obj) => OnInteractAction?.Invoke(this, EventArgs.Empty);
OnInteractAction?.Invoke(this, System.EventArgs.Empty);
private void Pause_performed(InputAction.CallbackContext obj) => private void Pause_performed(InputAction.CallbackContext obj) => OnPauseAction?.Invoke(this, EventArgs.Empty);
OnPauseAction?.Invoke(this, System.EventArgs.Empty);
public Vector2 GetMovementVectorNormalized() public static Vector2 GetMovementVectorNormalized() {
{
Vector2 inputVector = playerInputActions.Player.Move.ReadValue<Vector2>(); Vector2 inputVector = playerInputActions.Player.Move.ReadValue<Vector2>();
return inputVector.normalized; return inputVector.normalized;
} }
public string GetBindingText(Binding binding) public static string GetBindingText(Binding binding) {
{ return binding switch{
return binding switch
{
Binding.MoveUp => playerInputActions.Player.Move.bindings[1].ToDisplayString(), Binding.MoveUp => playerInputActions.Player.Move.bindings[1].ToDisplayString(),
Binding.MoveDown => playerInputActions.Player.Move.bindings[2].ToDisplayString(), Binding.MoveDown => playerInputActions.Player.Move.bindings[2].ToDisplayString(),
Binding.MoveLeft => playerInputActions.Player.Move.bindings[3].ToDisplayString(), Binding.MoveLeft => playerInputActions.Player.Move.bindings[3].ToDisplayString(),
@ -71,24 +61,22 @@ public class GameInput : MonoBehaviour
Binding.InteractAlternate Binding.InteractAlternate
=> playerInputActions.Player.InteractAlternate.bindings[0].ToDisplayString(), => playerInputActions.Player.InteractAlternate.bindings[0].ToDisplayString(),
Binding.Pause => playerInputActions.Player.Pause.bindings[0].ToDisplayString(), Binding.Pause => playerInputActions.Player.Pause.bindings[0].ToDisplayString(),
Binding.Gamepad_Interact Binding.GamepadInteract
=> playerInputActions.Player.Interact.bindings[1].ToDisplayString(), => playerInputActions.Player.Interact.bindings[1].ToDisplayString(),
Binding.Gamepad_InteractAlternate Binding.GamepadInteractAlternate
=> playerInputActions.Player.InteractAlternate.bindings[1].ToDisplayString(), => playerInputActions.Player.InteractAlternate.bindings[1].ToDisplayString(),
Binding.Gamepad_Pause => playerInputActions.Player.Pause.bindings[1].ToDisplayString(), Binding.GamepadPause => playerInputActions.Player.Pause.bindings[1].ToDisplayString(),
_ => throw new NotImplementedException(), _ => throw new NotImplementedException()
}; };
} }
public void RebindBinding(Binding binding, Action onActionRebound) public void RebindBinding(Binding binding, Action onActionRebound) {
{
playerInputActions.Player.Disable(); playerInputActions.Player.Disable();
InputAction inputAction; InputAction inputAction;
int bindingIndex; int bindingIndex;
switch (binding) switch (binding) {
{
default: default:
case Binding.MoveUp: case Binding.MoveUp:
inputAction = playerInputActions.Player.Move; inputAction = playerInputActions.Player.Move;
@ -118,15 +106,15 @@ public class GameInput : MonoBehaviour
inputAction = playerInputActions.Player.Pause; inputAction = playerInputActions.Player.Pause;
bindingIndex = 0; bindingIndex = 0;
break; break;
case Binding.Gamepad_Interact: case Binding.GamepadInteract:
inputAction = playerInputActions.Player.Interact; inputAction = playerInputActions.Player.Interact;
bindingIndex = 1; bindingIndex = 1;
break; break;
case Binding.Gamepad_InteractAlternate: case Binding.GamepadInteractAlternate:
inputAction = playerInputActions.Player.InteractAlternate; inputAction = playerInputActions.Player.InteractAlternate;
bindingIndex = 1; bindingIndex = 1;
break; break;
case Binding.Gamepad_Pause: case Binding.GamepadPause:
inputAction = playerInputActions.Player.Interact; inputAction = playerInputActions.Player.Interact;
bindingIndex = 1; bindingIndex = 1;
break; break;
@ -134,8 +122,7 @@ public class GameInput : MonoBehaviour
inputAction inputAction
.PerformInteractiveRebinding(bindingIndex) .PerformInteractiveRebinding(bindingIndex)
.OnComplete(callback => .OnComplete(callback => {
{
playerInputActions.Player.Enable(); playerInputActions.Player.Enable();
onActionRebound(); onActionRebound();
PlayerPrefs.SetString( PlayerPrefs.SetString(
@ -143,8 +130,8 @@ public class GameInput : MonoBehaviour
playerInputActions.SaveBindingOverridesAsJson() playerInputActions.SaveBindingOverridesAsJson()
); );
PlayerPrefs.Save(); PlayerPrefs.Save();
OnBindingRebind?.Invoke(this, System.EventArgs.Empty); OnBindingRebind?.Invoke(this, EventArgs.Empty);
}) })
.Start(); .Start();
} }
} }

@ -1,13 +1,11 @@
using System; using System;
using static StoveCounter; using static StoveCounter;
public interface IHasProgress public interface IHasProgress {
{ public event EventHandler<ProgressChangedEventArgs> OnProgressChanged;
public event EventHandler<ProgressChangedEventArgs> OnProgressChanged;
public class ProgressChangedEventArgs : System.EventArgs public class ProgressChangedEventArgs : EventArgs {
{ public float ProgressNormalized;
public State State; public State State;
public float ProgressNormalized; }
}
} }

@ -1,10 +1,10 @@
using Unity.Netcode; using Unity.Netcode;
using UnityEngine; using UnityEngine;
public interface IKitchenObjectParent public interface IKitchenObjectParent {
{
public KitchenObject KitchenObject { get; set; } public KitchenObject KitchenObject { get; set; }
public Transform KitchenObjectHoldPoint { get; set; } public Transform GetKitchenObjectFollowTransform();
//public Transform GetKitchenObjectHoldPoint(); //public Transform GetKitchenObjectHoldPoint();
//public void SetKitchenObject(KitchenObject kitchenObject); //public void SetKitchenObject(KitchenObject kitchenObject);
//public KitchenObject GetKitchenObject(); //public KitchenObject GetKitchenObject();

@ -1,99 +1,48 @@
using System; using System;
using UnityEngine; using UnityEngine;
public class KitchenGameManager : MonoBehaviour public class KitchenGameManager : MonoBehaviour {
{
public static KitchenGameManager Instance { get; private set; }
public event EventHandler OnStateChanged;
public event EventHandler OnGamePaused;
public event EventHandler OnGameUnpaused;
[SerializeField] private const float gamePlayingTimerMax = 300f; [SerializeField] private const float gamePlayingTimerMax = 300f;
private bool isGamePaused;
private enum State
{
WaitingToStart,
CountdownToStart,
GamePlaying,
GameOver
}
private State state; private State state;
public static KitchenGameManager Instance { get; private set; }
public float CountdownToStartTimer { get; private set; } = 1f; public float CountdownToStartTimer { get; private set; } = 1f;
public float GamePlayingTimer { get; private set; } public float GamePlayingTimer { get; private set; }
private bool isGamePaused;
private void Awake() private void Awake() {
{
Instance = this; Instance = this;
state = State.WaitingToStart; state = State.WaitingToStart;
GamePlayingTimer = gamePlayingTimerMax; GamePlayingTimer = gamePlayingTimerMax;
} }
private void Start() private void Start() {
{
GameInput.Instance.OnPauseAction += GameInput_OnPauseAction; GameInput.Instance.OnPauseAction += GameInput_OnPauseAction;
GameInput.Instance.OnInteractAction += GameInput_OnInteractAction; GameInput.Instance.OnInteractAction += GameInput_OnInteractAction;
//DEBUG trigger game start automatically //DEBUG trigger game start automatically
state = State.CountdownToStart; state = State.CountdownToStart;
OnStateChanged?.Invoke(this, System.EventArgs.Empty); OnStateChanged?.Invoke(this, EventArgs.Empty);
} }
private void GameInput_OnPauseAction(object sender, System.EventArgs e) private void Update() {
{ switch (state) {
TogglePauseGame();
}
private void GameInput_OnInteractAction(object sender, System.EventArgs e)
{
if (state == State.WaitingToStart)
{
state = State.CountdownToStart;
OnStateChanged?.Invoke(this, System.EventArgs.Empty);
}
}
public void TogglePauseGame()
{
isGamePaused = !isGamePaused;
if (isGamePaused)
{
Time.timeScale = 0f;
OnGamePaused?.Invoke(this, System.EventArgs.Empty);
}
else
{
Time.timeScale = 1f;
OnGameUnpaused?.Invoke(this, System.EventArgs.Empty);
}
}
private void Update()
{
switch (state)
{
case State.WaitingToStart: case State.WaitingToStart:
break; break;
case State.CountdownToStart: case State.CountdownToStart:
CountdownToStartTimer -= Time.deltaTime; CountdownToStartTimer -= Time.deltaTime;
if (CountdownToStartTimer > 0f) if (CountdownToStartTimer > 0f) return;
{
return;
}
state = State.GamePlaying; state = State.GamePlaying;
OnStateChanged?.Invoke(this, System.EventArgs.Empty); OnStateChanged?.Invoke(this, EventArgs.Empty);
break; break;
case State.GamePlaying: case State.GamePlaying:
GamePlayingTimer -= Time.deltaTime; GamePlayingTimer -= Time.deltaTime;
if (GamePlayingTimer > 0f) if (GamePlayingTimer > 0f) return;
{
return;
}
state = State.GameOver; state = State.GameOver;
GamePlayingTimer = gamePlayingTimerMax; GamePlayingTimer = gamePlayingTimerMax;
OnStateChanged?.Invoke(this, System.EventArgs.Empty); OnStateChanged?.Invoke(this, EventArgs.Empty);
break; break;
case State.GameOver: case State.GameOver:
break; break;
@ -102,23 +51,53 @@ public class KitchenGameManager : MonoBehaviour
// Debug.Log(state); // Debug.Log(state);
} }
public bool IsGamePlaying() public event EventHandler OnStateChanged;
{ public event EventHandler OnGamePaused;
public event EventHandler OnGameUnpaused;
private void GameInput_OnPauseAction(object sender, EventArgs e) {
TogglePauseGame();
}
private void GameInput_OnInteractAction(object sender, EventArgs e) {
if (state == State.WaitingToStart) {
state = State.CountdownToStart;
OnStateChanged?.Invoke(this, EventArgs.Empty);
}
}
public void TogglePauseGame() {
isGamePaused = !isGamePaused;
if (isGamePaused) {
Time.timeScale = 0f;
OnGamePaused?.Invoke(this, EventArgs.Empty);
}
else {
Time.timeScale = 1f;
OnGameUnpaused?.Invoke(this, EventArgs.Empty);
}
}
public bool IsGamePlaying() {
return state == State.GamePlaying; return state == State.GamePlaying;
} }
public bool IsCountToStartActive() public bool IsCountToStartActive() {
{
return state == State.CountdownToStart; return state == State.CountdownToStart;
} }
public bool IsGameOver() public bool IsGameOver() {
{
return state == State.GameOver; return state == State.GameOver;
} }
public float GetGamePlayingTimerNormalized() public float GetGamePlayingTimerNormalized() {
{ return 1 - GamePlayingTimer / gamePlayingTimerMax;
return 1 - (GamePlayingTimer / gamePlayingTimerMax); }
private enum State {
WaitingToStart,
CountdownToStart,
GamePlaying,
GameOver
} }
} }

@ -1,19 +1,20 @@
using Unity.Netcode; using Unity.Netcode;
using UnityEngine; using UnityEngine;
public class KitchenGameMultiplayer : NetworkBehaviour public class KitchenGameMultiplayer : NetworkBehaviour {
{
public static KitchenGameMultiplayer Instance { get; private set; }
[SerializeField] private KitchenObjectListSO kitchenObjectListSO; [SerializeField] private KitchenObjectListSO kitchenObjectListSO;
public static KitchenGameMultiplayer Instance { get; private set; }
private void Awake() => Instance = this; private void Awake() {
Instance = this;
}
public void SpawnKitchenObject(KitchenObjectSO kitchenObjectSO, IKitchenObjectParent kitchenObjectParent) => SpawnKitchenObjectServerRpc(GetKitchenObjectSOIndex(kitchenObjectSO), kitchenObjectParent.GetNetworkObject()); public void SpawnKitchenObject(KitchenObjectSO kitchenObjectSO, IKitchenObjectParent kitchenObjectParent) {
SpawnKitchenObjectServerRpc(GetKitchenObjectSOIndex(kitchenObjectSO), kitchenObjectParent.GetNetworkObject());
}
[ServerRpc(RequireOwnership = false)] [ServerRpc(RequireOwnership = false)]
private void SpawnKitchenObjectServerRpc(int kitchenObjectSOIndex, NetworkObjectReference kitchenObjectParentNetworkObjectReference) public void SpawnKitchenObjectServerRpc(int kitchenObjectSOIndex, NetworkObjectReference kitchenObjectParentNetworkObjectReference) {
{
KitchenObjectSO kitchenObjectSO = GetKitchenObjectSOFromIndex(kitchenObjectSOIndex); KitchenObjectSO kitchenObjectSO = GetKitchenObjectSOFromIndex(kitchenObjectSOIndex);
Transform kitchenObjectTransform = Instantiate(kitchenObjectSO.prefab); Transform kitchenObjectTransform = Instantiate(kitchenObjectSO.prefab);
@ -21,23 +22,25 @@ public class KitchenGameMultiplayer : NetworkBehaviour
kitchenObjectNetworkObject.Spawn(true); kitchenObjectNetworkObject.Spawn(true);
KitchenObject kitchenObject = kitchenObjectTransform.GetComponent<KitchenObject>(); KitchenObject kitchenObject = kitchenObjectTransform.GetComponent<KitchenObject>();
kitchenObjectParentNetworkObjectReference.TryGet(out NetworkObject kitchenObjectParentNetworkObject); kitchenObjectParentNetworkObjectReference.TryGet(out NetworkObject kitchenObjectParentNetworkObject);
IKitchenObjectParent kitchenObjectParent = kitchenObjectParentNetworkObject.GetComponent<IKitchenObjectParent>();
IKitchenObjectParent kitchenObjectParent = kitchenObjectParentNetworkObject.GetComponent<IKitchenObjectParent>();
kitchenObject.SetKitchenObjectParent(kitchenObjectParent); kitchenObject.SetKitchenObjectParent(kitchenObjectParent);
kitchenObjectTransform.gameObject.SetActive(true); Debug.Log($"Will spawn {kitchenObjectSO.objectName} on {kitchenObjectParent}...");
} }
private int GetKitchenObjectSOIndex(KitchenObjectSO kitchenObjectSO) => kitchenObjectListSO.kitchenObjectSOList.IndexOf(kitchenObjectSO); private int GetKitchenObjectSOIndex(KitchenObjectSO kitchenObjectSO) =>
kitchenObjectListSO.kitchenObjectSOList.IndexOf(kitchenObjectSO);
private KitchenObjectSO GetKitchenObjectSOFromIndex(int kitchenObjectSOIndex) => kitchenObjectListSO.kitchenObjectSOList[kitchenObjectSOIndex]; private KitchenObjectSO GetKitchenObjectSOFromIndex(int kitchenObjectSOIndex) =>
kitchenObjectListSO.kitchenObjectSOList[kitchenObjectSOIndex];
public void DestroyKitchenObject(KitchenObject kitchenObject) => DestroyKitchenObjectServerRpc(kitchenObject.NetworkObject); public void DestroyKitchenObject(KitchenObject kitchenObject) {
DestroyKitchenObjectServerRpc(kitchenObject.NetworkObject);
}
[ServerRpc(RequireOwnership = false)] [ServerRpc(RequireOwnership = false)]
public void DestroyKitchenObjectServerRpc(NetworkObjectReference kitchenObjectNetworkObjectReference) public void DestroyKitchenObjectServerRpc(NetworkObjectReference kitchenObjectNetworkObjectReference) {
{
kitchenObjectNetworkObjectReference.TryGet(out NetworkObject kitchenObjectNetworkObject); kitchenObjectNetworkObjectReference.TryGet(out NetworkObject kitchenObjectNetworkObject);
KitchenObject kitchenObject = kitchenObjectNetworkObject.GetComponent<KitchenObject>(); KitchenObject kitchenObject = kitchenObjectNetworkObject.GetComponent<KitchenObject>();
ClearKitchenObjectOnParentClientRpc(kitchenObjectNetworkObjectReference); ClearKitchenObjectOnParentClientRpc(kitchenObjectNetworkObjectReference);
@ -45,10 +48,9 @@ public class KitchenGameMultiplayer : NetworkBehaviour
} }
[ClientRpc] [ClientRpc]
private void ClearKitchenObjectOnParentClientRpc(NetworkObjectReference kitchenObjectNetworkObjectReference) public void ClearKitchenObjectOnParentClientRpc(NetworkObjectReference kitchenObjectNetworkObjectReference) {
{
kitchenObjectNetworkObjectReference.TryGet(out NetworkObject kitchenObjectNetworkObject); kitchenObjectNetworkObjectReference.TryGet(out NetworkObject kitchenObjectNetworkObject);
KitchenObject kitchenObject = kitchenObjectNetworkObject.GetComponent<KitchenObject>(); KitchenObject kitchenObject = kitchenObjectNetworkObject.GetComponent<KitchenObject>();
kitchenObject.ClearKitchenObjectOnParent(); kitchenObject.ClearKitchenObjectOnParent();
} }
} }

@ -1,72 +1,59 @@
using Unity.Netcode; using Unity.Netcode;
using UnityEngine; using UnityEngine;
public class KitchenObject : NetworkBehaviour public class KitchenObject : NetworkBehaviour {
{
public KitchenObjectSO KitchenObjectSO; public KitchenObjectSO KitchenObjectSO;
private IKitchenObjectParent kitchenObjectParent;
private FollowTransform followTransform; private FollowTransform followTransform;
private IKitchenObjectParent kitchenObjectParent;
protected virtual void Awake() protected virtual void Awake() => followTransform = GetComponent<FollowTransform>();
{
followTransform = GetComponent<FollowTransform>();
}
public void SetKitchenObjectParent(IKitchenObjectParent kitchenObjectParent) // ReSharper disable Unity.PerformanceAnalysis
{ public void SetKitchenObjectParent(IKitchenObjectParent kOP) => SetKitchenObjectParentServerRpc(kOP.GetNetworkObject());
SetKitchenObjectParentServerRpc(kitchenObjectParent.GetNetworkObject());
}
[ServerRpc(RequireOwnership = false)] [ServerRpc(RequireOwnership = false)]
private void SetKitchenObjectParentServerRpc(NetworkObjectReference kitchenObjectParentNetworkObjectReference) public void SetKitchenObjectParentServerRpc(NetworkObjectReference kitchenObjectParentNetworkObjectReference)
{ => SetKitchenObjectParentClientRpc(kitchenObjectParentNetworkObjectReference);
SetKitchenObjectParentClientRpc(kitchenObjectParentNetworkObjectReference);
}
[ClientRpc] [ClientRpc]
private void SetKitchenObjectParentClientRpc(NetworkObjectReference kitchenObjectParentNetworkObjectReference) public void SetKitchenObjectParentClientRpc(NetworkObjectReference kitchenObjectParentNetworkObjectReference) {
{
kitchenObjectParentNetworkObjectReference.TryGet(out NetworkObject kitchenObjectParentNetworkObject); kitchenObjectParentNetworkObjectReference.TryGet(out NetworkObject kitchenObjectParentNetworkObject);
IKitchenObjectParent _kitchenObjectParent = kitchenObjectParentNetworkObject.GetComponent<IKitchenObjectParent>(); IKitchenObjectParent kitchenOP = kitchenObjectParentNetworkObject.GetComponent<IKitchenObjectParent>();
if (_kitchenObjectParent.KitchenObject != null) Debug.LogError("IKitchenObjectParent already has a KitchenObject"); // if (kitchenObjectParent != null) kitchenObjectParent = null;
kitchenObjectParent = _kitchenObjectParent; kitchenObjectParent = kitchenOP;
//if (_kitchenObjectParwent.KitchenObject != null) kitchenObjectParent.KitchenObject = null; if (kitchenOP.KitchenObject != null)
Debug.LogError($"IKitchenObjectParent {kitchenOP} already has a KitchenObject {kitchenOP.KitchenObject}");
kitchenObjectParent.KitchenObject = this; kitchenObjectParent.KitchenObject = this;
followTransform.SetTargetTransform(kitchenObjectParent.KitchenObjectHoldPoint); followTransform.TargetTransform = kitchenObjectParent.GetKitchenObjectFollowTransform();
} }
public void DestroySelf() public void DestroySelf() {
{
Destroy(gameObject); Destroy(gameObject);
} }
public void ClearKitchenObjectOnParent() public void ClearKitchenObjectOnParent() {
{
kitchenObjectParent.KitchenObject = null; kitchenObjectParent.KitchenObject = null;
} }
public bool TryGetPlate(out PlateKitchenObject plateKitchenObject) public bool TryGetPlate(out PlateKitchenObject plateKitchenObject) {
{ if (this is PlateKitchenObject) {
if (this is PlateKitchenObject)
{
plateKitchenObject = this as PlateKitchenObject; plateKitchenObject = this as PlateKitchenObject;
return true; return true;
} }
plateKitchenObject = null; plateKitchenObject = null;
return false; return false;
} }
public static void SpawnKitchenObject(KitchenObjectSO kitchenObjectSO, IKitchenObjectParent kitchenObjectParent) // ReSharper disable Unity.PerformanceAnalysis
{ public static void SpawnKitchenObject(KitchenObjectSO kitchenObjectSO, IKitchenObjectParent kitchenObjectParent) {
KitchenGameMultiplayer.Instance.SpawnKitchenObject(kitchenObjectSO, kitchenObjectParent); KitchenGameMultiplayer.Instance.SpawnKitchenObject(kitchenObjectSO, kitchenObjectParent);
} }
public static void DestroyKitchenObject(KitchenObject kitchenObject) public static void DestroyKitchenObject(KitchenObject kitchenObject) {
{
KitchenGameMultiplayer.Instance.DestroyKitchenObject(kitchenObject); KitchenGameMultiplayer.Instance.DestroyKitchenObject(kitchenObject);
} }
} }

@ -1,9 +1,7 @@
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
public static class Loader public static class Loader {
{ public enum Scene {
public enum Scene
{
MainMenuScene, MainMenuScene,
GameScene, GameScene,
LoadingScene LoadingScene
@ -11,14 +9,12 @@ public static class Loader
private static Scene targetScene; private static Scene targetScene;
public static void Load(Scene target) public static void Load(Scene target) {
{
targetScene = target; targetScene = target;
SceneManager.LoadScene(Scene.LoadingScene.ToString()); SceneManager.LoadScene(Scene.LoadingScene.ToString());
} }
public static void LoaderCallback() public static void LoaderCallback() {
{
SceneManager.LoadScene(targetScene.ToString()); SceneManager.LoadScene(targetScene.ToString());
} }
} }

@ -8,4 +8,4 @@ public class LoaderCallback : MonoBehaviour {
isFirstUpdate = false; isFirstUpdate = false;
Loader.LoaderCallback(); Loader.LoaderCallback();
} }
} }

@ -1,30 +1,30 @@
using UnityEngine; using UnityEngine;
public class LookAtCamera : MonoBehaviour { public class LookAtCamera : MonoBehaviour {
[SerializeField] private Mode mode; [SerializeField] private Mode mode;
private void LateUpdate() { private void LateUpdate() {
switch (mode) { switch (mode) {
case Mode.LookAt: case Mode.LookAt:
transform.LookAt(Camera.main.transform); transform.LookAt(Camera.main.transform);
break; break;
case Mode.LookAtInverted: case Mode.LookAtInverted:
Vector3 dirFromCamera = transform.position - Camera.main.transform.position; Vector3 dirFromCamera = transform.position - Camera.main.transform.position;
transform.LookAt(transform.position + dirFromCamera); transform.LookAt(transform.position + dirFromCamera);
break; break;
case Mode.CameraForward: case Mode.CameraForward:
transform.forward = Camera.main.transform.forward; transform.forward = Camera.main.transform.forward;
break; break;
case Mode.CameraForwardInverted: case Mode.CameraForwardInverted:
transform.forward = -Camera.main.transform.forward; transform.forward = -Camera.main.transform.forward;
break; break;
} }
} }
private enum Mode { private enum Mode {
LookAt, LookAt,
LookAtInverted, LookAtInverted,
CameraForward, CameraForward,
CameraForwardInverted CameraForwardInverted
} }
} }

@ -1,13 +1,12 @@
using UnityEngine; using UnityEngine;
public class MusicManager : MonoBehaviour public class MusicManager : MonoBehaviour {
{ private const string PlayerPrefsMusicVolume = "MusicVolume";
public static MusicManager Instance { get; private set; }
private AudioSource audioSource; private AudioSource audioSource;
private float volume; private float volume;
private const string PlayerPrefsMusicVolume = "MusicVolume"; public static MusicManager Instance { get; private set; }
private void Awake() { private void Awake() {
Instance = this; Instance = this;
audioSource = GetComponent<AudioSource>(); audioSource = GetComponent<AudioSource>();
@ -23,5 +22,7 @@ public class MusicManager : MonoBehaviour
PlayerPrefs.Save(); PlayerPrefs.Save();
} }
public float GetVolume() => volume; public float GetVolume() {
} return volume;
}
}

@ -1,5 +1,7 @@
using Unity.Netcode.Components; using Unity.Netcode.Components;
public class OwnerNetworkAnimator : NetworkAnimator { public class OwnerNetworkAnimator : NetworkAnimator {
protected override bool OnIsServerAuthoritative() => false; protected override bool OnIsServerAuthoritative() {
return false;
}
} }

@ -2,38 +2,27 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
public class PlateCompleteVisual : MonoBehaviour public class PlateCompleteVisual : MonoBehaviour {
{ [SerializeField] private PlateKitchenObject plateKitchenObject;
[SerializeField] private List<KitchenObjectSOGameObject> kitchenObjectSOGameObjectList;
[Serializable] private void Start() {
public struct KitchenObjectSOGameObject plateKitchenObject.OnIngredientAdded += PlateKitchenObject_OnIngredientAdded;
{ foreach (KitchenObjectSOGameObject kitchenObjectSOGameObject in kitchenObjectSOGameObjectList)
public KitchenObjectSO kitchenObjectSO; kitchenObjectSOGameObject.gameObject.SetActive(false);
public GameObject gameObject; }
}
[SerializeField] private PlateKitchenObject plateKitchenObject; private void PlateKitchenObject_OnIngredientAdded(object sender, PlateKitchenObject.IngredientAddedEventArgs e) {
[SerializeField] private List<KitchenObjectSOGameObject> kitchenObjectSOGameObjectList; foreach (KitchenObjectSOGameObject gO in kitchenObjectSOGameObjectList)
// Debug.Log($"{gO.kitchenObjectSO} wird mit {e.KitchenObjectSO} verglichen... {gO.kitchenObjectSO == e.KitchenObjectSO}");
if (gO.kitchenObjectSO == e.KitchenObjectSO)
// Debug.Log($"{gO.gameObject} wird aktiviert!");
gO.gameObject.SetActive(true);
}
private void Start() [Serializable]
{ public struct KitchenObjectSOGameObject {
plateKitchenObject.OnIngredientAdded += PlateKitchenObject_OnIngredientAdded; public KitchenObjectSO kitchenObjectSO;
foreach (KitchenObjectSOGameObject kitchenObjectSOGameObject in kitchenObjectSOGameObjectList) public GameObject gameObject;
{ }
kitchenObjectSOGameObject.gameObject.SetActive(false); }
}
}
private void PlateKitchenObject_OnIngredientAdded(object sender, PlateKitchenObject.IngredientAddedEventArgs e)
{
foreach (KitchenObjectSOGameObject gO in kitchenObjectSOGameObjectList)
{
// Debug.Log($"{gO.kitchenObjectSO} wird mit {e.KitchenObjectSO} verglichen... {gO.kitchenObjectSO == e.KitchenObjectSO}");
if (gO.kitchenObjectSO == e.KitchenObjectSO)
{
// Debug.Log($"{gO.gameObject} wird aktiviert!");
gO.gameObject.SetActive(true);
}
}
}
}

@ -2,45 +2,35 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
public class PlateKitchenObject : KitchenObject public class PlateKitchenObject : KitchenObject {
{
public event EventHandler<IngredientAddedEventArgs> OnIngredientAdded;
public class IngredientAddedEventArgs : System.EventArgs
{
public KitchenObjectSO KitchenObjectSO;
}
[SerializeField] private List<KitchenObjectSO> validKitchenObjectSOList; [SerializeField] private List<KitchenObjectSO> validKitchenObjectSOList;
private List<KitchenObjectSO> kitchenObjectSOList; private List<KitchenObjectSO> kitchenObjectSOList;
protected override void Awake() protected override void Awake() {
{
base.Awake(); base.Awake();
kitchenObjectSOList = new(); kitchenObjectSOList = new List<KitchenObjectSO>();
} }
public bool TryAddIngredient(KitchenObjectSO kitchenObjectSO) public event EventHandler<IngredientAddedEventArgs> OnIngredientAdded;
{
public bool TryAddIngredient(KitchenObjectSO kitchenObjectSO) {
if (!validKitchenObjectSOList.Contains(kitchenObjectSO)) if (!validKitchenObjectSOList.Contains(kitchenObjectSO))
{
// Debug.Log("Not a valid ingredient"); // Debug.Log("Not a valid ingredient");
return false; return false;
}
if (kitchenObjectSOList.Contains(kitchenObjectSO)) if (kitchenObjectSOList.Contains(kitchenObjectSO))
{
// Debug.Log("Ingredient is already on the plate!"); // Debug.Log("Ingredient is already on the plate!");
return false; return false;
}
kitchenObjectSOList.Add(kitchenObjectSO); kitchenObjectSOList.Add(kitchenObjectSO);
// Debug.Log("Ingredient is added to the plate."); // Debug.Log("Ingredient is added to the plate.");
OnIngredientAdded?.Invoke(this, new() { KitchenObjectSO = kitchenObjectSO }); OnIngredientAdded?.Invoke(this, new IngredientAddedEventArgs{ KitchenObjectSO = kitchenObjectSO });
return true; return true;
} }
public List<KitchenObjectSO> GetKitchenObjectSOList() public List<KitchenObjectSO> GetKitchenObjectSOList() {
{
return kitchenObjectSOList; return kitchenObjectSOList;
} }
}
public class IngredientAddedEventArgs : EventArgs {
public KitchenObjectSO KitchenObjectSO;
}
}

@ -2,70 +2,65 @@ using System;
using Unity.Netcode; using Unity.Netcode;
using UnityEngine; using UnityEngine;
public class Player : NetworkBehaviour, IKitchenObjectParent public class Player : NetworkBehaviour, IKitchenObjectParent {
{ private Transform KitchenObjectHoldPoint { get; set; }
public static Player LocalInstance { get; private set; }
public static event EventHandler OnAnyPlayerSpawned;
public static event EventHandler OnAnyPickedSomething; public KitchenObject KitchenObject {
get => kitchenObject;
public static void ResetStaticData() set {
{ kitchenObject = value;
OnAnyPlayerSpawned = null; if (value == null) return;
OnAnyPickedSomething = null;
OnPickedSomething?.Invoke(this, EventArgs.Empty);
OnAnyPickedSomething?.Invoke(this, EventArgs.Empty);
}
} }
public static Player LocalInstance { get; private set; } [SerializeField] private float moveSpeed = 7f;
public Transform KitchenObjectHoldPoint { get; set; } [SerializeField] private float rotateSpeed = 10f;
[SerializeField] private float interactDistance = 2f;
[SerializeField] private LayerMask countersLayerMask;
private const float playerRadius = .7f; private const float playerRadius = .7f;
private const float playerHeight = 2f; private const float playerHeight = 2f;
public event EventHandler OnPickedSomething;
[SerializeField]
private float moveSpeed = 7f;
[SerializeField]
private float rotateSpeed = 10f;
[SerializeField]
private float interactDistance = 2f;
[SerializeField]
private LayerMask countersLayerMask;
private Vector2 inputVector; private Vector2 inputVector;
private bool isWalking; private bool isWalking;
private KitchenObject kitchenObject; private KitchenObject kitchenObject;
public KitchenObject KitchenObject
{
get => kitchenObject;
set
{
kitchenObject = value;
if (value != null)
{
OnPickedSomething?.Invoke(this, EventArgs.Empty);
OnAnyPickedSomething?.Invoke(this, EventArgs.Empty);
}
}
}
private Vector3 lastInteractDir; private Vector3 lastInteractDir;
private Vector3 moveDir; private Vector3 moveDir;
private BaseCounter selectedCounter; private BaseCounter selectedCounter;
private void Start() private void Start() {
{
GameInput.Instance.OnInteractAction += GameInput_OnInteractAction; GameInput.Instance.OnInteractAction += GameInput_OnInteractAction;
GameInput.Instance.OnInteractAlternateAction += GameInput_OnInteractAlternateAction; GameInput.Instance.OnInteractAlternateAction += GameInput_OnInteractAlternateAction;
} }
public override void OnNetworkSpawn() private void Update() {
{ if (!IsOwner) return;
if (IsOwner)
{ HandleMovement();
HandleInteractions();
}
public Transform GetKitchenObjectFollowTransform() => KitchenObjectHoldPoint;
public NetworkObject GetNetworkObject() => NetworkObject;
public static event EventHandler OnAnyPlayerSpawned;
public static event EventHandler OnAnyPickedSomething;
public static void ResetStaticData() {
OnAnyPlayerSpawned = null;
OnAnyPickedSomething = null;
}
public event EventHandler OnPickedSomething;
public override void OnNetworkSpawn() {
if (IsOwner) {
LocalInstance = this; LocalInstance = this;
KitchenObjectHoldPoint = transform.Find("KitchenObjectHoldPoint"); KitchenObjectHoldPoint = transform.Find("KitchenObjectHoldPoint");
} }
@ -73,111 +68,85 @@ public class Player : NetworkBehaviour, IKitchenObjectParent
OnAnyPlayerSpawned?.Invoke(this, EventArgs.Empty); OnAnyPlayerSpawned?.Invoke(this, EventArgs.Empty);
} }
private void Update()
{
if (!IsOwner) return;
HandleMovement();
HandleInteractions();
}
public event EventHandler<SelectedCounterChangedEventArgs> OnSelectedCounterChanged; public event EventHandler<SelectedCounterChangedEventArgs> OnSelectedCounterChanged;
private void GameInput_OnInteractAlternateAction(object sender, System.EventArgs e) private void GameInput_OnInteractAlternateAction(object sender, EventArgs e) {
{
if (!KitchenGameManager.Instance.IsGamePlaying()) return; if (!KitchenGameManager.Instance.IsGamePlaying()) return;
selectedCounter?.InteractAlternate(this); selectedCounter?.InteractAlternate(this);
} }
private void GameInput_OnInteractAction(object sender, System.EventArgs e) private void GameInput_OnInteractAction(object sender, EventArgs e) {
{
if (!KitchenGameManager.Instance.IsGamePlaying()) return; if (!KitchenGameManager.Instance.IsGamePlaying()) return;
selectedCounter?.Interact(this); selectedCounter?.Interact(this);
} }
private void HandleInteractions() private void HandleInteractions() {
{ inputVector = GameInput.GetMovementVectorNormalized();
inputVector = GameInput.Instance.GetMovementVectorNormalized();
if (moveDir != Vector3.zero) lastInteractDir = moveDir; if (moveDir != Vector3.zero) lastInteractDir = moveDir;
if (Physics.Raycast(transform.position, lastInteractDir, out RaycastHit raycastHit, interactDistance, countersLayerMask)) if (Physics.Raycast(transform.position, lastInteractDir, out RaycastHit raycastHit, interactDistance,
{ countersLayerMask)) {
if (raycastHit.transform.TryGetComponent(out BaseCounter baseCounter)) if (raycastHit.transform.TryGetComponent(out BaseCounter baseCounter)) {
{ if (baseCounter != selectedCounter) SetSelectedCounter(baseCounter);
if (baseCounter != selectedCounter)
{
SetSelectedCounter(baseCounter);
}
} }
else else {
{
SetSelectedCounter(null); SetSelectedCounter(null);
} }
} }
else else {
{
SetSelectedCounter(null); SetSelectedCounter(null);
} }
} }
private void SetSelectedCounter(BaseCounter _selectedCounter) private void SetSelectedCounter(BaseCounter _selectedCounter) {
{
selectedCounter = _selectedCounter; selectedCounter = _selectedCounter;
OnSelectedCounterChanged?.Invoke(this, new() { SelectedCounter = _selectedCounter }); OnSelectedCounterChanged?.Invoke(this,
new SelectedCounterChangedEventArgs{ SelectedCounter = _selectedCounter });
} }
public bool IsWalking() public bool IsWalking() => isWalking;
{
return isWalking;
}
private void HandleMovement() private void HandleMovement() {
{ inputVector = GameInput.GetMovementVectorNormalized();
inputVector = GameInput.Instance.GetMovementVectorNormalized(); moveDir = new Vector3(inputVector.x, 0, inputVector.y);
moveDir = new(inputVector.x, 0, inputVector.y);
float moveDistance = moveSpeed * Time.deltaTime; float moveDistance = moveSpeed * Time.deltaTime;
bool canMove = !Physics.CapsuleCast(transform.position, transform.position + (Vector3.up * playerHeight), playerRadius, moveDir, moveDistance); bool canMove = !Physics.CapsuleCast(transform.position, transform.position + Vector3.up * playerHeight,
if (!canMove) playerRadius, moveDir, moveDistance);
{ if (!canMove) {
// Cannot move towars moveDir // Cannot move towars moveDir
//Attempt only x movement //Attempt only x movement
Vector3 moveDirX = new Vector3(moveDir.x, 0, 0).normalized; Vector3 moveDirX = new Vector3(moveDir.x, 0, 0).normalized;
canMove = canMove =
(moveDir.x < -.5f || moveDir.x > +.5f) (moveDir.x < -.5f || moveDir.x > +.5f)
&& !Physics.CapsuleCast(transform.position, transform.position + (Vector3.up * playerHeight), playerRadius, moveDir, moveDistance); && !Physics.CapsuleCast(transform.position, transform.position + Vector3.up * playerHeight,
if (canMove) playerRadius, moveDir, moveDistance);
{ if (canMove) {
//Can move only on the X //Can move only on the X
MovePlayer(moveDirX, moveDistance); MovePlayer(moveDirX, moveDistance);
} }
else else {
{
//Cannot move only on the X //Cannot move only on the X
//Attempt only Z movement //Attempt only Z movement
Vector3 moveDirZ = new Vector3(0, 0, moveDir.z).normalized; Vector3 moveDirZ = new Vector3(0, 0, moveDir.z).normalized;
canMove = canMove =
(moveDir.z < -.5f || moveDir.z > +.5f) (moveDir.z < -.5f || moveDir.z > +.5f)
&& !Physics.CapsuleCast(transform.position, transform.position + (Vector3.up * playerHeight), playerRadius, moveDir, moveDistance); && !Physics.CapsuleCast(transform.position, transform.position + Vector3.up * playerHeight,
playerRadius, moveDir, moveDistance);
if (canMove) if (canMove) {
{
//Can move only on the Z //Can move only on the Z
MovePlayer(moveDirZ, moveDistance); MovePlayer(moveDirZ, moveDistance);
} }
else //Cannot move in any direction
{
//Cannot move in any direction
}
} }
} }
else else {
{
MovePlayer(moveDir, moveDistance); MovePlayer(moveDir, moveDistance);
} }
@ -185,10 +154,5 @@ public class Player : NetworkBehaviour, IKitchenObjectParent
transform.forward = Vector3.Slerp(transform.forward, moveDir, Time.deltaTime * rotateSpeed); transform.forward = Vector3.Slerp(transform.forward, moveDir, Time.deltaTime * rotateSpeed);
} }
private void MovePlayer(Vector3 moveDirection, float moveDistance) private void MovePlayer(Vector3 moveDirection, float moveDistance) => transform.position += moveDirection * moveDistance;
{
transform.position += moveDirection * moveDistance;
}
public NetworkObject GetNetworkObject() => NetworkObject;
} }

@ -1,20 +1,17 @@
using Unity.Netcode; using Unity.Netcode;
using UnityEngine; using UnityEngine;
public class PlayerAnimator : NetworkBehaviour public class PlayerAnimator : NetworkBehaviour {
{
private static readonly int isWalking = Animator.StringToHash("IsWalking"); private static readonly int isWalking = Animator.StringToHash("IsWalking");
[SerializeField] private Player player; [SerializeField] private Player player;
private Animator animator; private Animator animator;
private void Awake() private void Awake() {
{
animator = GetComponent<Animator>(); animator = GetComponent<Animator>();
} }
private void Update() private void Update() {
{
if (!IsOwner) return; if (!IsOwner) return;
animator.SetBool(isWalking, player.IsWalking()); animator.SetBool(isWalking, player.IsWalking());
} }

@ -1,12 +1,13 @@
using System;
using UnityEngine; using UnityEngine;
public class PlayerSounds : MonoBehaviour { public class PlayerSounds : MonoBehaviour {
private Player player;
private float footstepTimer;
private const float footstepTimerMax = .2f; private const float footstepTimerMax = .2f;
private float footstepTimer;
private Player player;
private void Awake() => player = GetComponent<Player>(); private void Awake() {
player = GetComponent<Player>();
}
private void Update() { private void Update() {
footstepTimer -= Time.deltaTime; footstepTimer -= Time.deltaTime;
@ -16,4 +17,4 @@ public class PlayerSounds : MonoBehaviour {
const float volume = 1f; const float volume = 1f;
SoundManager.Instance.PlayFootstepsSound(player.transform.position, volume); SoundManager.Instance.PlayFootstepsSound(player.transform.position, volume);
} }
} }

@ -1,12 +1,10 @@
using UnityEngine; using UnityEngine;
public class ResetStaticDataManager : MonoBehaviour public class ResetStaticDataManager : MonoBehaviour {
{ private void Awake() {
private void Awake()
{
// CuttingCounter.ResetStaticData(); // CuttingCounter.ResetStaticData();
// BaseCounter.ResetStaticData(); // BaseCounter.ResetStaticData();
// TrashCounter.ResetStaticData(); // TrashCounter.ResetStaticData();
Player.ResetStaticData(); Player.ResetStaticData();
} }
} }

@ -1,10 +1,8 @@
using UnityEngine; using UnityEngine;
[CreateAssetMenu] [CreateAssetMenu]
public class BurningRecipeSO : ScriptableObject public class BurningRecipeSO : ScriptableObject {
{ public KitchenObjectSO input;
public KitchenObjectSO input; public KitchenObjectSO output;
public KitchenObjectSO output; public float burningTimerMax;
public float burningTimerMax; }
}

@ -1,10 +1,8 @@
using UnityEngine; using UnityEngine;
[CreateAssetMenu] [CreateAssetMenu]
public class CuttingRecipeSO : ScriptableObject public class CuttingRecipeSO : ScriptableObject {
{ public KitchenObjectSO input;
public KitchenObjectSO input; public KitchenObjectSO output;
public KitchenObjectSO output; public int cuttingProgressMax;
public int cuttingProgressMax; }
}

@ -1,10 +1,8 @@
using UnityEngine; using UnityEngine;
[CreateAssetMenu] [CreateAssetMenu]
public class FryingRecipeSO : ScriptableObject public class FryingRecipeSO : ScriptableObject {
{ public KitchenObjectSO input;
public KitchenObjectSO input; public KitchenObjectSO output;
public KitchenObjectSO output; public float fryingTimerMax;
public float fryingTimerMax; }
}

@ -4,4 +4,4 @@ using UnityEngine;
//[CreateAssetMenu()] //[CreateAssetMenu()]
public class KitchenObjectListSO : ScriptableObject { public class KitchenObjectListSO : ScriptableObject {
public List<KitchenObjectSO> kitchenObjectSOList; public List<KitchenObjectSO> kitchenObjectSOList;
} }

@ -1,10 +1,8 @@
using UnityEngine; using UnityEngine;
[CreateAssetMenu] [CreateAssetMenu]
public class KitchenObjectSO : ScriptableObject public class KitchenObjectSO : ScriptableObject {
{ public Transform prefab;
public Transform prefab; public Sprite sprite;
public Sprite sprite; public string objectName;
public string objectName; }
}

@ -3,7 +3,6 @@ using UnityEngine;
// [CreateAssetMenu] // [CreateAssetMenu]
public class RecipeListSO : ScriptableObject public class RecipeListSO : ScriptableObject {
{ public List<RecipeSO> recipeSOList;
public List<RecipeSO> recipeSOList; }
}

@ -1,11 +1,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
[CreateAssetMenu] [CreateAssetMenu]
public class RecipeSO : ScriptableObject public class RecipeSO : ScriptableObject {
{ public List<KitchenObjectSO> KitchenObjectSOList;
public List<KitchenObjectSO> KitchenObjectSOList; public string RecipeName;
public string RecipeName; public int Points;
public int Points; }
}

@ -1,57 +1,36 @@
using System; using System;
using UnityEngine; using UnityEngine;
public class SelectedCounterVisual : MonoBehaviour public class SelectedCounterVisual : MonoBehaviour {
{
[SerializeField] private BaseCounter baseCounter; [SerializeField] private BaseCounter baseCounter;
[SerializeField] private GameObject[] visualGameObjectArray; [SerializeField] private GameObject[] visualGameObjectArray;
private void Start() private void Start() {
{
if (Player.LocalInstance != null) if (Player.LocalInstance != null)
{
Player.LocalInstance.OnSelectedCounterChanged += Player_OnSelectedCounterChanged; Player.LocalInstance.OnSelectedCounterChanged += Player_OnSelectedCounterChanged;
}
else else
{
Player.OnAnyPlayerSpawned += Player_OnyAnyPlayerSpawned; Player.OnAnyPlayerSpawned += Player_OnyAnyPlayerSpawned;
}
} }
private void Player_OnyAnyPlayerSpawned(object sender, EventArgs e) private void Player_OnyAnyPlayerSpawned(object sender, EventArgs e) {
{ if (Player.LocalInstance != null) {
if (Player.LocalInstance != null)
{
Player.LocalInstance.OnSelectedCounterChanged -= Player_OnSelectedCounterChanged; Player.LocalInstance.OnSelectedCounterChanged -= Player_OnSelectedCounterChanged;
Player.LocalInstance.OnSelectedCounterChanged += Player_OnSelectedCounterChanged; Player.LocalInstance.OnSelectedCounterChanged += Player_OnSelectedCounterChanged;
} }
} }
private void Player_OnSelectedCounterChanged(object sender, SelectedCounterChangedEventArgs e) private void Player_OnSelectedCounterChanged(object sender, SelectedCounterChangedEventArgs e) {
{
if (e.SelectedCounter == baseCounter) if (e.SelectedCounter == baseCounter)
{
Show(); Show();
}
else else
{
Hide(); Hide();
}
} }
private void Show() private void Show() {
{ foreach (GameObject visualGameObject in visualGameObjectArray) visualGameObject.SetActive(true);
foreach (GameObject visualGameObject in visualGameObjectArray)
{
visualGameObject.SetActive(true);
}
} }
private void Hide() private void Hide() {
{ foreach (GameObject visualGameObject in visualGameObjectArray) visualGameObject.SetActive(false);
foreach (GameObject visualGameObject in visualGameObjectArray)
{
visualGameObject.SetActive(false);
}
} }
} }

@ -3,13 +3,18 @@ using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using Random = UnityEngine.Random; using Random = UnityEngine.Random;
public class SoundManager : MonoBehaviour public class SoundManager : MonoBehaviour {
{ private const string PlayerPrefsSoundEffectsVolume = "SoundEffectsVolume";
public static SoundManager Instance { get; private set; }
[SerializeField] private AudioClipRefsSO audioClipRefsSO; [SerializeField] private AudioClipRefsSO audioClipRefsSO;
private float volume;
public static SoundManager Instance { get; private set; }
private void Awake() {
Instance = this;
volume = PlayerPrefs.GetFloat(PlayerPrefsSoundEffectsVolume, 1f);
}
private void Start() private void Start() {
{
DeliveryManager.Instance.OnRecipeSuccess += DeliveryManager_OnRecipeSuccess; DeliveryManager.Instance.OnRecipeSuccess += DeliveryManager_OnRecipeSuccess;
DeliveryManager.Instance.OnRecipeFailed += DeliveryManager_OnRecipeFailed; DeliveryManager.Instance.OnRecipeFailed += DeliveryManager_OnRecipeFailed;
CuttingCounter.OnAnyCut += CuttingCounter_OnAnyCut; CuttingCounter.OnAnyCut += CuttingCounter_OnAnyCut;
@ -18,84 +23,59 @@ public class SoundManager : MonoBehaviour
TrashCounter.OnAnyObjectTrashed += TrashCounter_OnAnyObjectTrashed; TrashCounter.OnAnyObjectTrashed += TrashCounter_OnAnyObjectTrashed;
} }
private const string PlayerPrefsSoundEffectsVolume = "SoundEffectsVolume"; private void TrashCounter_OnAnyObjectTrashed(object sender, EventArgs e) {
private float volume;
private void Awake()
{
Instance = this;
volume = PlayerPrefs.GetFloat(PlayerPrefsSoundEffectsVolume, 1f);
}
private void TrashCounter_OnAnyObjectTrashed(object sender, System.EventArgs e)
{
PlaySound(audioClipRefsSO.trash, (sender as TrashCounter).transform.position); PlaySound(audioClipRefsSO.trash, (sender as TrashCounter).transform.position);
} }
private void BaseCounter_OnAnyObjectPlacedHere(object sender, System.EventArgs e) private void BaseCounter_OnAnyObjectPlacedHere(object sender, EventArgs e) {
{
PlaySound(audioClipRefsSO.objectDrop, (sender as BaseCounter).transform.position); PlaySound(audioClipRefsSO.objectDrop, (sender as BaseCounter).transform.position);
} }
private void Player_OnAnyPickedSomething(object sender, System.EventArgs e) private void Player_OnAnyPickedSomething(object sender, EventArgs e) {
{
PlaySound(audioClipRefsSO.objectPickup, (sender as Player).transform.position); PlaySound(audioClipRefsSO.objectPickup, (sender as Player).transform.position);
} }
private void CuttingCounter_OnAnyCut(object sender, System.EventArgs e) private void CuttingCounter_OnAnyCut(object sender, EventArgs e) {
{
PlaySound(audioClipRefsSO.chop, (sender as CuttingCounter).transform.position); PlaySound(audioClipRefsSO.chop, (sender as CuttingCounter).transform.position);
} }
private void DeliveryManager_OnRecipeFailed(object sender, System.EventArgs e) private void DeliveryManager_OnRecipeFailed(object sender, EventArgs e) {
{
PlaySound(audioClipRefsSO.deliveryFail, DeliveryCounter.Instance.transform.position); PlaySound(audioClipRefsSO.deliveryFail, DeliveryCounter.Instance.transform.position);
} }
private void DeliveryManager_OnRecipeSuccess(object sender, System.EventArgs e) private void DeliveryManager_OnRecipeSuccess(object sender, EventArgs e) {
{
PlaySound(audioClipRefsSO.deliverySuccess, DeliveryCounter.Instance.transform.position); PlaySound(audioClipRefsSO.deliverySuccess, DeliveryCounter.Instance.transform.position);
} }
private void PlaySound(AudioClip audioClip, Vector3 position, float volumeMultiplier = 1f) private void PlaySound(AudioClip audioClip, Vector3 position, float volumeMultiplier = 1f) {
{
AudioSource.PlayClipAtPoint(audioClip, position, volumeMultiplier * volume); AudioSource.PlayClipAtPoint(audioClip, position, volumeMultiplier * volume);
} }
private void PlaySound(IReadOnlyList<AudioClip> audioClipArray, Vector3 position, float volumeMultiplier = 1f) private void PlaySound(IReadOnlyList<AudioClip> audioClipArray, Vector3 position, float volumeMultiplier = 1f) {
{
PlaySound(audioClipArray[Random.Range(0, audioClipArray.Count)], position, volumeMultiplier * volume); PlaySound(audioClipArray[Random.Range(0, audioClipArray.Count)], position, volumeMultiplier * volume);
} }
public void PlayFootstepsSound(Vector3 position, float volumeMultiplier) public void PlayFootstepsSound(Vector3 position, float volumeMultiplier) {
{
PlaySound(audioClipRefsSO.footstep, position, volumeMultiplier * volume); PlaySound(audioClipRefsSO.footstep, position, volumeMultiplier * volume);
} }
public void PlayCountdownSound() public void PlayCountdownSound() {
{
PlaySound(audioClipRefsSO.warning, Vector3.zero); PlaySound(audioClipRefsSO.warning, Vector3.zero);
} }
public void PlayWarningSound(Vector3 position) public void PlayWarningSound(Vector3 position) {
{
PlaySound(audioClipRefsSO.warning, position); PlaySound(audioClipRefsSO.warning, position);
} }
public void ChangeVolume() public void ChangeVolume() {
{
volume += .1f; volume += .1f;
if (volume > 1.1f) if (volume > 1.1f) volume = 0f;
{
volume = 0f;
}
PlayerPrefs.SetFloat(PlayerPrefsSoundEffectsVolume, volume); PlayerPrefs.SetFloat(PlayerPrefsSoundEffectsVolume, volume);
PlayerPrefs.Save(); PlayerPrefs.Save();
} }
public float GetVolume() public float GetVolume() {
{
return volume; return volume;
} }
} }

@ -2,35 +2,27 @@ using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
public class DeliveryManagerSingleUI : MonoBehaviour public class DeliveryManagerSingleUI : MonoBehaviour {
{ [SerializeField] private TextMeshProUGUI recipeNameText;
[SerializeField] private TextMeshProUGUI recipeNameText; [SerializeField] private Transform iconContainer;
[SerializeField] private Transform iconContainer; [SerializeField] private Transform iconTemplate;
[SerializeField] private Transform iconTemplate;
private void Awake() private void Awake() {
{ iconTemplate.gameObject.SetActive(false);
iconTemplate.gameObject.SetActive(false); }
}
public void SetRecipeSO(RecipeSO recipeSO) public void SetRecipeSO(RecipeSO recipeSO) {
{ recipeNameText.text = $"{recipeSO.RecipeName} ({recipeSO.Points})";
recipeNameText.text = $"{recipeSO.RecipeName} ({recipeSO.Points})"; foreach (Transform child in iconContainer) {
foreach (Transform child in iconContainer) if (child == iconTemplate) continue;
{
if (child == iconTemplate)
{
continue;
}
Destroy(child.gameObject); Destroy(child.gameObject);
} }
foreach (KitchenObjectSO kitchenObjectSO in recipeSO.KitchenObjectSOList) foreach (KitchenObjectSO kitchenObjectSO in recipeSO.KitchenObjectSOList) {
{ Transform iconTransform = Instantiate(iconTemplate, iconContainer);
Transform iconTransform = Instantiate(iconTemplate, iconContainer); iconTransform.gameObject.SetActive(true);
iconTransform.gameObject.SetActive(true); iconTransform.GetComponent<Image>().sprite = kitchenObjectSO.sprite;
iconTransform.GetComponent<Image>().sprite = kitchenObjectSO.sprite; }
} }
}
} }

@ -1,63 +1,50 @@
using System; using System;
using UnityEngine; using UnityEngine;
public class DeliveryManagerUI : MonoBehaviour public class DeliveryManagerUI : MonoBehaviour {
{ [SerializeField] private Transform container;
public static DeliveryManagerUI Instance { get; private set; } [SerializeField] private Transform recipeTemplate;
[SerializeField] private Transform container; public static DeliveryManagerUI Instance { get; private set; }
[SerializeField] private Transform recipeTemplate;
private void Awake() {
private void Awake() recipeTemplate.gameObject.SetActive(false);
{ Instance = this;
recipeTemplate.gameObject.SetActive(false); }
Instance = this;
} private void Start() {
DeliveryManager.Instance.OnRecipeSpawned += DeliveryManager_OnRecipeSpawned;
private void Start() DeliveryManager.Instance.OnRecipeCompleted += DeliveryManager_OnRecipeCompleted;
{ Show();
DeliveryManager.Instance.OnRecipeSpawned += DeliveryManager_OnRecipeSpawned; UpdateVisual();
DeliveryManager.Instance.OnRecipeCompleted += DeliveryManager_OnRecipeCompleted; }
Show();
UpdateVisual(); private void DeliveryManager_OnRecipeCompleted(object sender, EventArgs e) {
} UpdateVisual();
}
private void DeliveryManager_OnRecipeCompleted(object sender, System.EventArgs e)
{ private void DeliveryManager_OnRecipeSpawned(object sender, EventArgs e) {
UpdateVisual(); UpdateVisual();
} }
private void DeliveryManager_OnRecipeSpawned(object sender, System.EventArgs e) private void UpdateVisual() {
{ foreach (Transform child in container) {
UpdateVisual(); if (child == recipeTemplate) continue;
}
Destroy(child.gameObject);
private void UpdateVisual() }
{
foreach (Transform child in container) foreach (RecipeSO recipeSO in DeliveryManager.Instance.WaitingRecipeSOList) {
{ Transform recipeTransform = Instantiate(recipeTemplate, container);
if (child == recipeTemplate) recipeTransform.gameObject.SetActive(true);
{ recipeTransform.GetComponent<DeliveryManagerSingleUI>().SetRecipeSO(recipeSO);
continue; }
} }
Destroy(child.gameObject); public void Hide() {
} gameObject.SetActive(false);
}
foreach (RecipeSO recipeSO in DeliveryManager.Instance.WaitingRecipeSOList)
{ public void Show() {
Transform recipeTransform = Instantiate(recipeTemplate, container); gameObject.SetActive(true);
recipeTransform.gameObject.SetActive(true); }
recipeTransform.GetComponent<DeliveryManagerSingleUI>().SetRecipeSO(recipeSO);
}
}
public void Hide()
{
gameObject.SetActive(false);
}
public void Show()
{
gameObject.SetActive(true);
}
} }

@ -3,8 +3,7 @@ using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
public class DeliveryResultUI : MonoBehaviour public class DeliveryResultUI : MonoBehaviour {
{
private const string Popup = "Popup"; private const string Popup = "Popup";
[SerializeField] private Image backgroundImage; [SerializeField] private Image backgroundImage;
[SerializeField] private Image iconImage; [SerializeField] private Image iconImage;
@ -16,26 +15,25 @@ public class DeliveryResultUI : MonoBehaviour
private Animator animator; private Animator animator;
private void Awake() => animator = GetComponent<Animator>(); private void Awake() {
animator = GetComponent<Animator>();
}
private void Start() private void Start() {
{
DeliveryManager.Instance.OnRecipeSuccess += DeliveryManager_OnRecipeSuccess; DeliveryManager.Instance.OnRecipeSuccess += DeliveryManager_OnRecipeSuccess;
DeliveryManager.Instance.OnRecipeFailed += DeliveryManager_OnRecipeFailed; DeliveryManager.Instance.OnRecipeFailed += DeliveryManager_OnRecipeFailed;
gameObject.SetActive(false); gameObject.SetActive(false);
} }
private void DeliveryManager_OnRecipeFailed(object sender, System.EventArgs e) private void DeliveryManager_OnRecipeFailed(object sender, EventArgs e) {
{
gameObject.SetActive(true); gameObject.SetActive(true);
animator.SetTrigger(Popup); animator.SetTrigger(Popup);
backgroundImage.color = failedColor; backgroundImage.color = failedColor;
iconImage.sprite = failedSprite; iconImage.sprite = failedSprite;
messageText.text = $"Delivery\nfailed!\n-1"; messageText.text = "Delivery\nfailed!\n-1";
} }
private void DeliveryManager_OnRecipeSuccess(object sender, RecipeEventArgs e) private void DeliveryManager_OnRecipeSuccess(object sender, RecipeEventArgs e) {
{
gameObject.SetActive(true); gameObject.SetActive(true);
animator.SetTrigger(Popup); animator.SetTrigger(Popup);
backgroundImage.color = successColor; backgroundImage.color = successColor;

@ -3,34 +3,28 @@ using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
public class GameOverUI : MonoBehaviour public class GameOverUI : MonoBehaviour {
{
public static GameOverUI Instance { get; private set; }
[SerializeField] private TextMeshProUGUI recipeDeliveredText; [SerializeField] private TextMeshProUGUI recipeDeliveredText;
[SerializeField] private TextMeshProUGUI pointsText; [SerializeField] private TextMeshProUGUI pointsText;
[SerializeField] private Button restartButton; [SerializeField] private Button restartButton;
[SerializeField] private Button mainMenuButton; [SerializeField] private Button mainMenuButton;
public static GameOverUI Instance { get; private set; }
private void Awake() private void Awake() {
{
Instance = this; Instance = this;
restartButton.onClick.AddListener(() => Loader.Load(Loader.Scene.GameScene)); restartButton.onClick.AddListener(() => Loader.Load(Loader.Scene.GameScene));
mainMenuButton.onClick.AddListener(() => Loader.Load(Loader.Scene.MainMenuScene)); mainMenuButton.onClick.AddListener(() => Loader.Load(Loader.Scene.MainMenuScene));
} }
private void Start() private void Start() {
{
KitchenGameManager.Instance.OnStateChanged += KitchenGameManager_OnStateChanged; KitchenGameManager.Instance.OnStateChanged += KitchenGameManager_OnStateChanged;
Hide(); Hide();
restartButton.Select(); restartButton.Select();
} }
private void KitchenGameManager_OnStateChanged(object sender, System.EventArgs e) private void KitchenGameManager_OnStateChanged(object sender, EventArgs e) {
{ if (KitchenGameManager.Instance.IsGameOver()) {
if (KitchenGameManager.Instance.IsGameOver())
{
recipeDeliveredText.text = DeliveryManager.Instance.SuccessfulRecipes.ToString(); recipeDeliveredText.text = DeliveryManager.Instance.SuccessfulRecipes.ToString();
pointsText.text = DeliveryManager.Instance.Points.ToString(); pointsText.text = DeliveryManager.Instance.Points.ToString();
Show(); Show();
@ -38,8 +32,7 @@ public class GameOverUI : MonoBehaviour
DeliveryManagerUI.Instance.Hide(); DeliveryManagerUI.Instance.Hide();
PointsUI.Instance.Hide(); PointsUI.Instance.Hide();
} }
else else {
{
Hide(); Hide();
GamePlayingClockUI.Instance.Show(); GamePlayingClockUI.Instance.Show();
DeliveryManagerUI.Instance.Show(); DeliveryManagerUI.Instance.Show();
@ -47,7 +40,11 @@ public class GameOverUI : MonoBehaviour
} }
} }
public void Hide() => gameObject.SetActive(false); public void Hide() {
gameObject.SetActive(false);
}
public void Show() => gameObject.SetActive(true); public void Show() {
gameObject.SetActive(true);
}
} }

@ -2,43 +2,44 @@ using System;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
namespace UI namespace UI {
{ public class GamePauseUI : MonoBehaviour {
public class GamePauseUI : MonoBehaviour
{
[SerializeField] private Button resumeButton; [SerializeField] private Button resumeButton;
//TODO: Restart Button //TODO: Restart Button
[SerializeField] private Button optionsButton; [SerializeField] private Button optionsButton;
[SerializeField] private Button mainMenuButton; [SerializeField] private Button mainMenuButton;
private void Awake() private void Awake() {
{
resumeButton.onClick.AddListener(() => KitchenGameManager.Instance.TogglePauseGame()); resumeButton.onClick.AddListener(() => KitchenGameManager.Instance.TogglePauseGame());
optionsButton.onClick.AddListener(() => optionsButton.onClick.AddListener(() => {
{
Hide(); Hide();
OptionsUI.Instance.Show(Show); OptionsUI.Instance.Show(Show);
}); });
mainMenuButton.onClick.AddListener(() => Loader.Load(Loader.Scene.MainMenuScene)); mainMenuButton.onClick.AddListener(() => Loader.Load(Loader.Scene.MainMenuScene));
} }
private void Start() private void Start() {
{
KitchenGameManager.Instance.OnGamePaused += KitchenGameManager_OnGamePaused; KitchenGameManager.Instance.OnGamePaused += KitchenGameManager_OnGamePaused;
KitchenGameManager.Instance.OnGameUnpaused += KitchenGameManager_OnGameUnpaused; KitchenGameManager.Instance.OnGameUnpaused += KitchenGameManager_OnGameUnpaused;
Hide(); Hide();
} }
private void KitchenGameManager_OnGameUnpaused(object sender, System.EventArgs e) => Hide(); private void KitchenGameManager_OnGameUnpaused(object sender, EventArgs e) {
Hide();
}
private void KitchenGameManager_OnGamePaused(object sender, System.EventArgs e) => Show(); private void KitchenGameManager_OnGamePaused(object sender, EventArgs e) {
Show();
}
private void Show() private void Show() {
{
gameObject.SetActive(true); gameObject.SetActive(true);
resumeButton.Select(); resumeButton.Select();
} }
private void Hide() => gameObject.SetActive(false); private void Hide() {
gameObject.SetActive(false);
}
} }
} }

@ -2,22 +2,20 @@ using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
public class GamePlayingClockUI : MonoBehaviour public class GamePlayingClockUI : MonoBehaviour {
{
public static GamePlayingClockUI Instance { get; private set; }
[SerializeField] private Image timerImage; [SerializeField] private Image timerImage;
[SerializeField] private TextMeshProUGUI timerText; [SerializeField] private TextMeshProUGUI timerText;
public static GamePlayingClockUI Instance { get; private set; }
private void Awake() => Instance = this; private void Awake() {
Instance = this;
}
private void Start() private void Start() {
{
Show(); Show();
} }
private void Update() private void Update() {
{
float playingTimeNormalized = KitchenGameManager.Instance.GetGamePlayingTimerNormalized(); float playingTimeNormalized = KitchenGameManager.Instance.GetGamePlayingTimerNormalized();
timerImage.fillAmount = playingTimeNormalized; timerImage.fillAmount = playingTimeNormalized;
timerImage.color = Color.Lerp(Color.green, Color.red, playingTimeNormalized); timerImage.color = Color.Lerp(Color.green, Color.red, playingTimeNormalized);
@ -25,7 +23,11 @@ public class GamePlayingClockUI : MonoBehaviour
timerText.text = timeLeft.ToString(); timerText.text = timeLeft.ToString();
} }
public void Hide() => gameObject.SetActive(false); public void Hide() {
public void Show() => gameObject.SetActive(true); gameObject.SetActive(false);
}
public void Show() {
gameObject.SetActive(true);
}
} }

@ -2,43 +2,44 @@ using System;
using TMPro; using TMPro;
using UnityEngine; using UnityEngine;
public class GameStartCountdownUI : MonoBehaviour public class GameStartCountdownUI : MonoBehaviour {
{
private const string NumberPopup = "NumberPopup"; private const string NumberPopup = "NumberPopup";
[SerializeField] private TextMeshProUGUI countdownText; [SerializeField] private TextMeshProUGUI countdownText;
private Animator animator; private Animator animator;
private int previousCountdownNumber; private int previousCountdownNumber;
private void Awake() => animator = GetComponent<Animator>(); private void Awake() {
animator = GetComponent<Animator>();
}
private void Start() private void Start() {
{
KitchenGameManager.Instance.OnStateChanged += KitchenGameManager_OnStateChanged; KitchenGameManager.Instance.OnStateChanged += KitchenGameManager_OnStateChanged;
Hide(); Hide();
} }
private void KitchenGameManager_OnStateChanged(object sender, System.EventArgs e) private void Update() {
{
if (KitchenGameManager.Instance.IsCountToStartActive())
Show();
else
Hide();
}
private void Update()
{
int countdownNumber = Mathf.CeilToInt(KitchenGameManager.Instance.CountdownToStartTimer); int countdownNumber = Mathf.CeilToInt(KitchenGameManager.Instance.CountdownToStartTimer);
countdownText.text = countdownNumber.ToString(); countdownText.text = countdownNumber.ToString();
if (previousCountdownNumber != countdownNumber) if (previousCountdownNumber != countdownNumber) {
{
previousCountdownNumber = countdownNumber; previousCountdownNumber = countdownNumber;
animator.SetTrigger(NumberPopup); animator.SetTrigger(NumberPopup);
SoundManager.Instance.PlayCountdownSound(); SoundManager.Instance.PlayCountdownSound();
} }
} }
private void Hide() => gameObject.SetActive(false); private void KitchenGameManager_OnStateChanged(object sender, EventArgs e) {
if (KitchenGameManager.Instance.IsCountToStartActive())
Show();
else
Hide();
}
private void Show() => gameObject.SetActive(true); private void Hide() {
gameObject.SetActive(false);
}
private void Show() {
gameObject.SetActive(true);
}
} }

@ -1,21 +1,18 @@
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
namespace UI namespace UI {
{ public class MainMenuUI : MonoBehaviour {
public class MainMenuUI : MonoBehaviour
{
[SerializeField] private Button playButton; [SerializeField] private Button playButton;
[SerializeField] private Button quitButton; [SerializeField] private Button quitButton;
private void Start() private void Awake() {
{
playButton.Select();
}
private void Awake()
{
playButton.onClick.AddListener(() => Loader.Load(Loader.Scene.GameScene)); playButton.onClick.AddListener(() => Loader.Load(Loader.Scene.GameScene));
quitButton.onClick.AddListener(Application.Quit); quitButton.onClick.AddListener(Application.Quit);
} }
private void Start() {
playButton.Select();
}
} }
} }

@ -3,10 +3,7 @@ using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
public class OptionsUI : MonoBehaviour public class OptionsUI : MonoBehaviour {
{
public static OptionsUI Instance { get; private set; }
[SerializeField] private Button soundEffectsButton; [SerializeField] private Button soundEffectsButton;
[SerializeField] private Button musicButton; [SerializeField] private Button musicButton;
[SerializeField] private Button closeButton; [SerializeField] private Button closeButton;
@ -35,22 +32,19 @@ public class OptionsUI : MonoBehaviour
[SerializeField] private Transform pressToRebindKeyTransform; [SerializeField] private Transform pressToRebindKeyTransform;
private Action onCloseButtonAction; private Action onCloseButtonAction;
public static OptionsUI Instance { get; private set; }
private void Awake() private void Awake() {
{
Instance = this; Instance = this;
soundEffectsButton.onClick.AddListener(() => soundEffectsButton.onClick.AddListener(() => {
{
SoundManager.Instance.ChangeVolume(); SoundManager.Instance.ChangeVolume();
UpdateVisual(); UpdateVisual();
}); });
musicButton.onClick.AddListener(() => musicButton.onClick.AddListener(() => {
{
MusicManager.Instance.ChangeVolume(); MusicManager.Instance.ChangeVolume();
UpdateVisual(); UpdateVisual();
}); });
closeButton.onClick.AddListener(() => closeButton.onClick.AddListener(() => {
{
Hide(); Hide();
onCloseButtonAction(); onCloseButtonAction();
}); });
@ -62,55 +56,61 @@ public class OptionsUI : MonoBehaviour
interactButton.onClick.AddListener(() => RebindBinding(GameInput.Binding.Interact)); interactButton.onClick.AddListener(() => RebindBinding(GameInput.Binding.Interact));
interactAlternateButton.onClick.AddListener(() => RebindBinding(GameInput.Binding.InteractAlternate)); interactAlternateButton.onClick.AddListener(() => RebindBinding(GameInput.Binding.InteractAlternate));
pauseButton.onClick.AddListener(() => RebindBinding(GameInput.Binding.Pause)); pauseButton.onClick.AddListener(() => RebindBinding(GameInput.Binding.Pause));
gamepadInteractButton.onClick.AddListener(() => RebindBinding(GameInput.Binding.Gamepad_Interact)); gamepadInteractButton.onClick.AddListener(() => RebindBinding(GameInput.Binding.GamepadInteract));
gamepadInteractAlternateButton.onClick.AddListener(() => RebindBinding(GameInput.Binding.Gamepad_InteractAlternate)); gamepadInteractAlternateButton.onClick.AddListener(() =>
gamepadPauseButton.onClick.AddListener(() => RebindBinding(GameInput.Binding.Gamepad_Pause)); RebindBinding(GameInput.Binding.GamepadInteractAlternate));
gamepadPauseButton.onClick.AddListener(() => RebindBinding(GameInput.Binding.GamepadPause));
} }
private void Start() private void Start() {
{
KitchenGameManager.Instance.OnGameUnpaused += KitchenGameManager_OnGameUnpaused; KitchenGameManager.Instance.OnGameUnpaused += KitchenGameManager_OnGameUnpaused;
UpdateVisual(); UpdateVisual();
Hide(); Hide();
HidePressToRebindKey(); HidePressToRebindKey();
} }
private void KitchenGameManager_OnGameUnpaused(object sender, System.EventArgs e) => Hide(); private void KitchenGameManager_OnGameUnpaused(object sender, EventArgs e) {
Hide();
}
private void UpdateVisual() private void UpdateVisual() {
{
soundEffectsText.text = $"Sound Effects: {Mathf.Round(SoundManager.Instance.GetVolume() * 10f)}"; soundEffectsText.text = $"Sound Effects: {Mathf.Round(SoundManager.Instance.GetVolume() * 10f)}";
musicText.text = $"Music: {Mathf.Round(MusicManager.Instance.GetVolume() * 10f)}"; musicText.text = $"Music: {Mathf.Round(MusicManager.Instance.GetVolume() * 10f)}";
moveUpText.text = GameInput.Instance.GetBindingText(GameInput.Binding.MoveUp); moveUpText.text = GameInput.GetBindingText(GameInput.Binding.MoveUp);
moveDownText.text = GameInput.Instance.GetBindingText(GameInput.Binding.MoveDown); moveDownText.text = GameInput.GetBindingText(GameInput.Binding.MoveDown);
moveLeftText.text = GameInput.Instance.GetBindingText(GameInput.Binding.MoveLeft); moveLeftText.text = GameInput.GetBindingText(GameInput.Binding.MoveLeft);
moveRightText.text = GameInput.Instance.GetBindingText(GameInput.Binding.MoveRight); moveRightText.text = GameInput.GetBindingText(GameInput.Binding.MoveRight);
interactText.text = GameInput.Instance.GetBindingText(GameInput.Binding.Interact); interactText.text = GameInput.GetBindingText(GameInput.Binding.Interact);
interactAlternateText.text = GameInput.Instance.GetBindingText(GameInput.Binding.InteractAlternate); interactAlternateText.text = GameInput.GetBindingText(GameInput.Binding.InteractAlternate);
pauseText.text = GameInput.Instance.GetBindingText(GameInput.Binding.Pause); pauseText.text = GameInput.GetBindingText(GameInput.Binding.Pause);
gamepadInteractText.text = GameInput.Instance.GetBindingText(GameInput.Binding.Gamepad_Interact); gamepadInteractText.text = GameInput.GetBindingText(GameInput.Binding.GamepadInteract);
gamepadInteractAlternateText.text = GameInput.Instance.GetBindingText(GameInput.Binding.Gamepad_InteractAlternate); gamepadInteractAlternateText.text =
gamepadPauseText.text = GameInput.Instance.GetBindingText(GameInput.Binding.Gamepad_Pause); GameInput.GetBindingText(GameInput.Binding.GamepadInteractAlternate);
gamepadPauseText.text = GameInput.GetBindingText(GameInput.Binding.GamepadPause);
} }
public void Show(Action onCloseButton) public void Show(Action onCloseButton) {
{
onCloseButtonAction = onCloseButton; onCloseButtonAction = onCloseButton;
gameObject.SetActive(true); gameObject.SetActive(true);
closeButton.Select(); closeButton.Select();
} }
private void Hide() => gameObject.SetActive(false); private void Hide() {
gameObject.SetActive(false);
}
private void ShowPressToRebindKey() {
pressToRebindKeyTransform.gameObject.SetActive(true);
}
private void ShowPressToRebindKey() => pressToRebindKeyTransform.gameObject.SetActive(true); private void HidePressToRebindKey() {
private void HidePressToRebindKey() => pressToRebindKeyTransform.gameObject.SetActive(false); pressToRebindKeyTransform.gameObject.SetActive(false);
}
private void RebindBinding(GameInput.Binding binding) private void RebindBinding(GameInput.Binding binding) {
{
ShowPressToRebindKey(); ShowPressToRebindKey();
GameInput.Instance.RebindBinding(binding, () => GameInput.Instance.RebindBinding(binding, () => {
{
HidePressToRebindKey(); HidePressToRebindKey();
UpdateVisual(); UpdateVisual();
}); });

@ -1,12 +1,10 @@
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
public class PlateIconSingleUI : MonoBehaviour public class PlateIconSingleUI : MonoBehaviour {
{
[SerializeField] private Image image; [SerializeField] private Image image;
public void SetKitchenObjectSO(KitchenObjectSO kitchenObjectOS) public void SetKitchenObjectSO(KitchenObjectSO kitchenObjectOS) {
{
image.sprite = kitchenObjectOS.sprite; image.sprite = kitchenObjectOS.sprite;
} }
} }

@ -1,25 +1,28 @@
using UnityEngine; using UnityEngine;
public class PlateIconsUI : MonoBehaviour public class PlateIconsUI : MonoBehaviour {
{
[SerializeField] private PlateKitchenObject plateKitchenObject; [SerializeField] private PlateKitchenObject plateKitchenObject;
[SerializeField] private Transform iconTemplate; [SerializeField] private Transform iconTemplate;
private void Awake() => iconTemplate.gameObject.SetActive(false); private void Awake() {
iconTemplate.gameObject.SetActive(false);
}
private void Start() => plateKitchenObject.OnIngredientAdded += PlateKitchenObject_OnIngredientAdded; private void Start() {
plateKitchenObject.OnIngredientAdded += PlateKitchenObject_OnIngredientAdded;
}
private void PlateKitchenObject_OnIngredientAdded(object sender, PlateKitchenObject.IngredientAddedEventArgs e) => UpdateVisual(); private void PlateKitchenObject_OnIngredientAdded(object sender, PlateKitchenObject.IngredientAddedEventArgs e) {
UpdateVisual();
}
private void UpdateVisual() private void UpdateVisual() {
{ foreach (Transform child in transform) {
foreach (Transform child in transform)
{
if (child == iconTemplate) continue; if (child == iconTemplate) continue;
Destroy(child.gameObject); Destroy(child.gameObject);
} }
foreach (KitchenObjectSO kitchenObjectSO in plateKitchenObject.GetKitchenObjectSOList())
{ foreach (KitchenObjectSO kitchenObjectSO in plateKitchenObject.GetKitchenObjectSOList()) {
Transform iconTransform = Instantiate(iconTemplate, transform); Transform iconTransform = Instantiate(iconTemplate, transform);
iconTransform.gameObject.SetActive(true); iconTransform.gameObject.SetActive(true);
iconTransform.GetComponent<PlateIconSingleUI>().SetKitchenObjectSO(kitchenObjectSO); iconTransform.GetComponent<PlateIconSingleUI>().SetKitchenObjectSO(kitchenObjectSO);

@ -1,25 +1,29 @@
using System; using System;
using TMPro; using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.UI;
public class PointsUI : MonoBehaviour
{
public static PointsUI Instance { get; private set; }
public class PointsUI : MonoBehaviour {
[SerializeField] private TextMeshProUGUI pointsText; [SerializeField] private TextMeshProUGUI pointsText;
public static PointsUI Instance { get; private set; }
private void Awake() => Instance = this; private void Awake() {
Instance = this;
}
private void Start() private void Start() {
{
DeliveryManager.Instance.OnRecipeCompleted += DeliveryManager_OnRecipeCompleted; DeliveryManager.Instance.OnRecipeCompleted += DeliveryManager_OnRecipeCompleted;
Show(); Show();
} }
private void DeliveryManager_OnRecipeCompleted(object sender, System.EventArgs e) => private void DeliveryManager_OnRecipeCompleted(object sender, EventArgs e) {
pointsText.text = $"Points: {DeliveryManager.Instance.Points}"; pointsText.text = $"Points: {DeliveryManager.Instance.Points}";
}
public void Hide() {
gameObject.SetActive(false);
}
public void Hide() => gameObject.SetActive(false); public void Show() {
public void Show() => gameObject.SetActive(true); gameObject.SetActive(true);
} }
}

@ -2,56 +2,43 @@ using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using static StoveCounter; using static StoveCounter;
namespace UI namespace UI {
{ public class ProgressBarUI : MonoBehaviour {
public class ProgressBarUI : MonoBehaviour [SerializeField] private GameObject hasProgressGameObject;
{ [SerializeField] private Image barImage;
[SerializeField] private GameObject hasProgressGameObject;
[SerializeField] private Image barImage; private IHasProgress hasProgress;
private IHasProgress hasProgress; private void Start() {
if (!hasProgressGameObject.TryGetComponent(out hasProgress))
private void Start() Debug.LogError(
{ $"Game Object {hasProgressGameObject} does not have a component that implements IHasProgress!");
if (!hasProgressGameObject.TryGetComponent<IHasProgress>(out hasProgress)) else
{ hasProgress.OnProgressChanged += HasProgress_OnProgressChanged;
Debug.LogError($"Game Object {hasProgressGameObject} does not have a component that implements IHasProgress!");
} barImage.fillAmount = 0f;
else Hide();
{ }
hasProgress.OnProgressChanged += HasProgress_OnProgressChanged;
} private void HasProgress_OnProgressChanged(object sender, IHasProgress.ProgressChangedEventArgs e) {
barImage.fillAmount = e.ProgressNormalized;
barImage.fillAmount = 0f;
Hide(); barImage.color = e.State == State.Fried
} ? Color.Lerp(Color.green, Color.red, e.ProgressNormalized)
: Color.Lerp(Color.blue, Color.green, e.ProgressNormalized);
private void HasProgress_OnProgressChanged(object sender, IHasProgress.ProgressChangedEventArgs e)
{ if (e.ProgressNormalized is 0f or 1f)
barImage.fillAmount = e.ProgressNormalized; Hide();
else
barImage.color = e.State == State.Fried Show();
? Color.Lerp(Color.green, Color.red, t: e.ProgressNormalized) }
: Color.Lerp(Color.blue, Color.green, t: e.ProgressNormalized);
private void Show() {
if (e.ProgressNormalized is 0f or 1f) gameObject.SetActive(true);
{ }
Hide();
} private void Hide() {
else gameObject.SetActive(false);
{ }
Show(); }
}
}
private void Show()
{
gameObject.SetActive(true);
}
private void Hide()
{
gameObject.SetActive(false);
}
}
} }

@ -1,25 +1,21 @@
using UnityEngine; using UnityEngine;
public class StoveBurnFlashingBarUI : MonoBehaviour public class StoveBurnFlashingBarUI : MonoBehaviour {
{ private const string IsFlashing = "IsFlashing";
private const string IsFlashing = "IsFlashing"; [SerializeField] private StoveCounter stoveCounter;
[SerializeField] private StoveCounter stoveCounter; private Animator animator;
private Animator animator;
private void Awake() private void Awake() {
{ animator = GetComponent<Animator>();
animator = GetComponent<Animator>(); }
}
private void Start() private void Start() {
{ stoveCounter.OnProgressChanged += StoveCounter_OnProgressChanged;
stoveCounter.OnProgressChanged += StoveCounter_OnProgressChanged; animator.SetBool(IsFlashing, false);
animator.SetBool(IsFlashing, false); }
}
private void StoveCounter_OnProgressChanged(object sender, IHasProgress.ProgressChangedEventArgs e) private void StoveCounter_OnProgressChanged(object sender, IHasProgress.ProgressChangedEventArgs e) {
{ float burnShowProgressAmount = .5f;
float burnShowProgressAmount = .5f; animator.SetBool(IsFlashing, stoveCounter.IsFried() && e.ProgressNormalized >= burnShowProgressAmount);
animator.SetBool(IsFlashing, stoveCounter.IsFried() && e.ProgressNormalized >= burnShowProgressAmount); }
}
} }

@ -1,35 +1,26 @@
using UnityEngine; using UnityEngine;
public class StoveBurnWarningUI : MonoBehaviour public class StoveBurnWarningUI : MonoBehaviour {
{ [SerializeField] private StoveCounter stoveCounter;
[SerializeField] private StoveCounter stoveCounter;
private void Start() private void Start() {
{ stoveCounter.OnProgressChanged += StoveCounter_OnProgressChanged;
stoveCounter.OnProgressChanged += StoveCounter_OnProgressChanged; Hide();
Hide(); }
}
private void StoveCounter_OnProgressChanged(object sender, IHasProgress.ProgressChangedEventArgs e) private void StoveCounter_OnProgressChanged(object sender, IHasProgress.ProgressChangedEventArgs e) {
{ float burnShowProgressAmount = .5f;
float burnShowProgressAmount = .5f; if (stoveCounter.IsFried() && e.ProgressNormalized >= burnShowProgressAmount)
if (stoveCounter.IsFried() && e.ProgressNormalized >= burnShowProgressAmount) Show();
{ else
Show(); Hide();
} }
else
{
Hide();
}
}
private void Show() private void Show() {
{ gameObject.SetActive(true);
gameObject.SetActive(true); }
}
private void Hide() private void Hide() {
{ gameObject.SetActive(false);
gameObject.SetActive(false); }
}
} }

@ -2,29 +2,24 @@ using Unity.Netcode;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
public class TestingNetcodeUI : MonoBehaviour public class TestingNetcodeUI : MonoBehaviour {
{
[SerializeField] private Button startHostButton; [SerializeField] private Button startHostButton;
[SerializeField] private Button startClientButton; [SerializeField] private Button startClientButton;
private void Awake() private void Awake() {
{ startHostButton.onClick.AddListener(() => {
startHostButton.onClick.AddListener(() =>
{
Debug.Log("HOST"); Debug.Log("HOST");
NetworkManager.Singleton.StartHost(); NetworkManager.Singleton.StartHost();
Hide(); Hide();
}); });
startClientButton.onClick.AddListener(() => startClientButton.onClick.AddListener(() => {
{
Debug.Log("CLIENT"); Debug.Log("CLIENT");
NetworkManager.Singleton.StartClient(); NetworkManager.Singleton.StartClient();
Hide(); Hide();
}); });
} }
private void Hide() private void Hide() {
{
gameObject.SetActive(false); gameObject.SetActive(false);
} }
} }

@ -1,10 +1,8 @@
using System; using System;
using TMPro; using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.UI;
public class TutorialUI : MonoBehaviour public class TutorialUI : MonoBehaviour {
{
[SerializeField] private TextMeshProUGUI keyMoveUpText; [SerializeField] private TextMeshProUGUI keyMoveUpText;
[SerializeField] private TextMeshProUGUI keyMoveDownText; [SerializeField] private TextMeshProUGUI keyMoveDownText;
[SerializeField] private TextMeshProUGUI keyMoveLeftText; [SerializeField] private TextMeshProUGUI keyMoveLeftText;
@ -16,45 +14,40 @@ public class TutorialUI : MonoBehaviour
[SerializeField] private TextMeshProUGUI keyMoveGamepadInteractAlternateText; [SerializeField] private TextMeshProUGUI keyMoveGamepadInteractAlternateText;
[SerializeField] private TextMeshProUGUI keyMoveGamepadPauseText; [SerializeField] private TextMeshProUGUI keyMoveGamepadPauseText;
private void Start() private void Start() {
{
GameInput.Instance.OnBindingRebind += GameInput_OnBindingRebind; GameInput.Instance.OnBindingRebind += GameInput_OnBindingRebind;
KitchenGameManager.Instance.OnStateChanged += KitchenGameManager_OnStateChanged; KitchenGameManager.Instance.OnStateChanged += KitchenGameManager_OnStateChanged;
UpdateVisual(); UpdateVisual();
Show(); Show();
} }
private void GameInput_OnBindingRebind(object sender, System.EventArgs e) => UpdateVisual(); private void GameInput_OnBindingRebind(object sender, EventArgs e) {
UpdateVisual();
}
private void KitchenGameManager_OnStateChanged(object sender, System.EventArgs e) private void KitchenGameManager_OnStateChanged(object sender, EventArgs e) {
{ if (KitchenGameManager.Instance.IsCountToStartActive()) Hide();
if (KitchenGameManager.Instance.IsCountToStartActive())
{
Hide();
}
} }
private void UpdateVisual() private void UpdateVisual() {
{ keyMoveUpText.text = GameInput.GetBindingText(GameInput.Binding.MoveUp);
keyMoveUpText.text = GameInput.Instance.GetBindingText(GameInput.Binding.MoveUp); keyMoveDownText.text = GameInput.GetBindingText(GameInput.Binding.MoveDown);
keyMoveDownText.text = GameInput.Instance.GetBindingText(GameInput.Binding.MoveDown); keyMoveLeftText.text = GameInput.GetBindingText(GameInput.Binding.MoveLeft);
keyMoveLeftText.text = GameInput.Instance.GetBindingText(GameInput.Binding.MoveLeft); keyMoveRightText.text = GameInput.GetBindingText(GameInput.Binding.MoveRight);
keyMoveRightText.text = GameInput.Instance.GetBindingText(GameInput.Binding.MoveRight); keyMoveInteractText.text = GameInput.GetBindingText(GameInput.Binding.Interact);
keyMoveInteractText.text = GameInput.Instance.GetBindingText(GameInput.Binding.Interact); keyMoveInteractAlternateText.text = GameInput.GetBindingText(GameInput.Binding.InteractAlternate);
keyMoveInteractAlternateText.text = GameInput.Instance.GetBindingText(GameInput.Binding.InteractAlternate); keyMovePauseText.text = GameInput.GetBindingText(GameInput.Binding.Pause);
keyMovePauseText.text = GameInput.Instance.GetBindingText(GameInput.Binding.Pause); keyMoveGamepadInteractText.text = GameInput.GetBindingText(GameInput.Binding.GamepadInteract);
keyMoveGamepadInteractText.text = GameInput.Instance.GetBindingText(GameInput.Binding.Gamepad_Interact); keyMoveGamepadInteractAlternateText.text =
keyMoveGamepadInteractAlternateText.text = GameInput.Instance.GetBindingText(GameInput.Binding.Gamepad_InteractAlternate); GameInput.GetBindingText(GameInput.Binding.GamepadInteractAlternate);
keyMoveGamepadPauseText.text = GameInput.Instance.GetBindingText(GameInput.Binding.Gamepad_Pause); keyMoveGamepadPauseText.text = GameInput.GetBindingText(GameInput.Binding.GamepadPause);
} }
private void Show() private void Show() {
{
gameObject.SetActive(true); gameObject.SetActive(true);
} }
private void Hide() private void Hide() {
{
gameObject.SetActive(false); gameObject.SetActive(false);
} }
} }

@ -1,38 +1,28 @@
using UnityEngine;
using System.Collections; using System.Collections;
using UnityEngine;
namespace TMPro.Examples {
public class Benchmark01 : MonoBehaviour {
private const string label01 = "The <#0050FF>count is: </color>{0}";
private const string label02 = "The <color=#0050FF>count is: </color>";
namespace TMPro.Examples public int BenchmarkType;
{
public class Benchmark01 : MonoBehaviour
{
public int BenchmarkType = 0;
public TMP_FontAsset TMProFont; public TMP_FontAsset TMProFont;
public Font TextMeshFont; public Font TextMeshFont;
private TextMeshPro m_textMeshPro;
private TextContainer m_textContainer;
private TextMesh m_textMesh;
private const string label01 = "The <#0050FF>count is: </color>{0}";
private const string label02 = "The <color=#0050FF>count is: </color>";
//private string m_string; //private string m_string;
//private int m_frame; //private int m_frame;
private Material m_material01; private Material m_material01;
private Material m_material02; private Material m_material02;
private TextContainer m_textContainer;
private TextMesh m_textMesh;
private TextMeshPro m_textMeshPro;
IEnumerator Start() private IEnumerator Start() {
{
if (BenchmarkType == 0) // TextMesh Pro Component if (BenchmarkType == 0) // TextMesh Pro Component
{ {
m_textMeshPro = gameObject.AddComponent<TextMeshPro>(); m_textMeshPro = gameObject.AddComponent<TextMeshPro>();
@ -54,27 +44,25 @@ namespace TMPro.Examples
//m_textMeshPro.fontSharedMaterial.SetFloat("_OutlineWidth", 0.2f); //m_textMeshPro.fontSharedMaterial.SetFloat("_OutlineWidth", 0.2f);
//m_textMeshPro.fontSharedMaterial.EnableKeyword("UNDERLAY_ON"); //m_textMeshPro.fontSharedMaterial.EnableKeyword("UNDERLAY_ON");
//m_textMeshPro.lineJustification = LineJustificationTypes.Center; //m_textMeshPro.lineJustification = LineJustificationTypes.Center;
m_textMeshPro.enableWordWrapping = false; m_textMeshPro.enableWordWrapping = false;
//m_textMeshPro.lineLength = 60; //m_textMeshPro.lineLength = 60;
//m_textMeshPro.characterSpacing = 0.2f; //m_textMeshPro.characterSpacing = 0.2f;
//m_textMeshPro.fontColor = new Color32(255, 255, 255, 255); //m_textMeshPro.fontColor = new Color32(255, 255, 255, 255);
m_material01 = m_textMeshPro.font.material; m_material01 = m_textMeshPro.font.material;
m_material02 = Resources.Load<Material>("Fonts & Materials/LiberationSans SDF - Drop Shadow"); // Make sure the LiberationSans SDF exists before calling this... m_material02 =
Resources.Load<Material>(
"Fonts & Materials/LiberationSans SDF - Drop Shadow"); // Make sure the LiberationSans SDF exists before calling this...
} }
else if (BenchmarkType == 1) // TextMesh else if (BenchmarkType == 1) // TextMesh
{ {
m_textMesh = gameObject.AddComponent<TextMesh>(); m_textMesh = gameObject.AddComponent<TextMesh>();
if (TextMeshFont != null) if (TextMeshFont != null) {
{
m_textMesh.font = TextMeshFont; m_textMesh.font = TextMeshFont;
m_textMesh.GetComponent<Renderer>().sharedMaterial = m_textMesh.font.material; m_textMesh.GetComponent<Renderer>().sharedMaterial = m_textMesh.font.material;
} }
else else {
{
m_textMesh.font = Resources.Load("Fonts/ARIAL", typeof(Font)) as Font; m_textMesh.font = Resources.Load("Fonts/ARIAL", typeof(Font)) as Font;
m_textMesh.GetComponent<Renderer>().sharedMaterial = m_textMesh.font.material; m_textMesh.GetComponent<Renderer>().sharedMaterial = m_textMesh.font.material;
} }
@ -86,20 +74,17 @@ namespace TMPro.Examples
} }
for (int i = 0; i <= 1000000; i++) {
for (int i = 0; i <= 1000000; i++) if (BenchmarkType == 0) {
{
if (BenchmarkType == 0)
{
m_textMeshPro.SetText(label01, i % 1000); m_textMeshPro.SetText(label01, i % 1000);
if (i % 1000 == 999) if (i % 1000 == 999)
m_textMeshPro.fontSharedMaterial = m_textMeshPro.fontSharedMaterial == m_material01 ? m_textMeshPro.fontSharedMaterial = m_material02 : m_textMeshPro.fontSharedMaterial = m_material01; m_textMeshPro.fontSharedMaterial = m_textMeshPro.fontSharedMaterial == m_material01
? m_textMeshPro.fontSharedMaterial = m_material02
: m_textMeshPro.fontSharedMaterial = m_material01;
}
else if (BenchmarkType == 1) {
m_textMesh.text = label02 + (i % 1000);
} }
else if (BenchmarkType == 1)
m_textMesh.text = label02 + (i % 1000).ToString();
yield return null; yield return null;
} }
@ -125,4 +110,4 @@ namespace TMPro.Examples
} }
*/ */
} }
} }

@ -1,27 +1,18 @@
using UnityEngine;
using System.Collections; using System.Collections;
using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
namespace TMPro.Examples {
public class Benchmark01_UGUI : MonoBehaviour {
private const string label01 = "The <#0050FF>count is: </color>";
private const string label02 = "The <color=#0050FF>count is: </color>";
namespace TMPro.Examples public int BenchmarkType;
{
public class Benchmark01_UGUI : MonoBehaviour
{
public int BenchmarkType = 0;
public Canvas canvas; public Canvas canvas;
public TMP_FontAsset TMProFont; public TMP_FontAsset TMProFont;
public Font TextMeshFont; public Font TextMeshFont;
private TextMeshProUGUI m_textMeshPro;
//private TextContainer m_textContainer;
private Text m_textMesh;
private const string label01 = "The <#0050FF>count is: </color>";
private const string label02 = "The <color=#0050FF>count is: </color>";
//private const string label01 = "TextMesh <#0050FF>Pro!</color> The count is: {0}"; //private const string label01 = "TextMesh <#0050FF>Pro!</color> The count is: {0}";
//private const string label02 = "Text Mesh<color=#0050FF> The count is: </color>"; //private const string label02 = "Text Mesh<color=#0050FF> The count is: </color>";
@ -29,15 +20,16 @@ namespace TMPro.Examples
//private int m_frame; //private int m_frame;
private Material m_material01; private Material m_material01;
private Material m_material02;
private Material m_material02;
IEnumerator Start() //private TextContainer m_textContainer;
{ private Text m_textMesh;
private TextMeshProUGUI m_textMeshPro;
private IEnumerator Start() {
if (BenchmarkType == 0) // TextMesh Pro Component if (BenchmarkType == 0) // TextMesh Pro Component
{ {
m_textMeshPro = gameObject.AddComponent<TextMeshProUGUI>(); m_textMeshPro = gameObject.AddComponent<TextMeshProUGUI>();
@ -66,25 +58,21 @@ namespace TMPro.Examples
//m_textMeshPro.fontColor = new Color32(255, 255, 255, 255); //m_textMeshPro.fontColor = new Color32(255, 255, 255, 255);
m_material01 = m_textMeshPro.font.material; m_material01 = m_textMeshPro.font.material;
m_material02 = Resources.Load<Material>("Fonts & Materials/LiberationSans SDF - BEVEL"); // Make sure the LiberationSans SDF exists before calling this... m_material02 =
Resources.Load<Material>(
"Fonts & Materials/LiberationSans SDF - BEVEL"); // Make sure the LiberationSans SDF exists before calling this...
} }
else if (BenchmarkType == 1) // TextMesh else if (BenchmarkType == 1) // TextMesh
{ {
m_textMesh = gameObject.AddComponent<Text>(); m_textMesh = gameObject.AddComponent<Text>();
if (TextMeshFont != null) if (TextMeshFont != null) {
{
m_textMesh.font = TextMeshFont; m_textMesh.font = TextMeshFont;
//m_textMesh.renderer.sharedMaterial = m_textMesh.font.material; //m_textMesh.renderer.sharedMaterial = m_textMesh.font.material;
} }
else
{
//m_textMesh.font = Resources.Load("Fonts/ARIAL", typeof(Font)) as Font;
//m_textMesh.renderer.sharedMaterial = m_textMesh.font.material;
}
//m_textMesh.font = Resources.Load("Fonts/ARIAL", typeof(Font)) as Font;
//m_textMesh.renderer.sharedMaterial = m_textMesh.font.material;
m_textMesh.fontSize = 48; m_textMesh.fontSize = 48;
m_textMesh.alignment = TextAnchor.MiddleCenter; m_textMesh.alignment = TextAnchor.MiddleCenter;
@ -92,20 +80,17 @@ namespace TMPro.Examples
} }
for (int i = 0; i <= 1000000; i++) {
for (int i = 0; i <= 1000000; i++) if (BenchmarkType == 0) {
{ m_textMeshPro.text = label01 + i % 1000;
if (BenchmarkType == 0)
{
m_textMeshPro.text = label01 + (i % 1000);
if (i % 1000 == 999) if (i % 1000 == 999)
m_textMeshPro.fontSharedMaterial = m_textMeshPro.fontSharedMaterial == m_material01 ? m_textMeshPro.fontSharedMaterial = m_material02 : m_textMeshPro.fontSharedMaterial = m_material01; m_textMeshPro.fontSharedMaterial = m_textMeshPro.fontSharedMaterial == m_material01
? m_textMeshPro.fontSharedMaterial = m_material02
: m_textMeshPro.fontSharedMaterial = m_material01;
}
else if (BenchmarkType == 1) {
m_textMesh.text = label02 + (i % 1000);
} }
else if (BenchmarkType == 1)
m_textMesh.text = label02 + (i % 1000).ToString();
yield return null; yield return null;
} }
@ -131,5 +116,4 @@ namespace TMPro.Examples
} }
*/ */
} }
}
}

@ -1,31 +1,19 @@
using UnityEngine; using UnityEngine;
using System.Collections;
namespace TMPro.Examples {
namespace TMPro.Examples public class Benchmark02 : MonoBehaviour {
{ public int SpawnType;
public class Benchmark02 : MonoBehaviour
{
public int SpawnType = 0;
public int NumberOfNPC = 12; public int NumberOfNPC = 12;
public bool IsTextObjectScaleStatic; public bool IsTextObjectScaleStatic;
private TextMeshProFloatingText floatingText_Script; private TextMeshProFloatingText floatingText_Script;
void Start() private void Start() {
{
for (int i = 0; i < NumberOfNPC; i++) for (int i = 0; i < NumberOfNPC; i++)
{ if (SpawnType == 0) {
if (SpawnType == 0)
{
// TextMesh Pro Implementation // TextMesh Pro Implementation
GameObject go = new GameObject(); GameObject go = new();
go.transform.position = new Vector3(Random.Range(-95f, 95f), 0.25f, Random.Range(-95f, 95f)); go.transform.position = new Vector3(Random.Range(-95f, 95f), 0.25f, Random.Range(-95f, 95f));
TextMeshPro textMeshPro = go.AddComponent<TextMeshPro>(); TextMeshPro textMeshPro = go.AddComponent<TextMeshPro>();
@ -46,10 +34,9 @@ namespace TMPro.Examples
floatingText_Script.SpawnType = 0; floatingText_Script.SpawnType = 0;
floatingText_Script.IsTextObjectScaleStatic = IsTextObjectScaleStatic; floatingText_Script.IsTextObjectScaleStatic = IsTextObjectScaleStatic;
} }
else if (SpawnType == 1) else if (SpawnType == 1) {
{
// TextMesh Implementation // TextMesh Implementation
GameObject go = new GameObject(); GameObject go = new();
go.transform.position = new Vector3(Random.Range(-95f, 95f), 0.25f, Random.Range(-95f, 95f)); go.transform.position = new Vector3(Random.Range(-95f, 95f), 0.25f, Random.Range(-95f, 95f));
TextMesh textMesh = go.AddComponent<TextMesh>(); TextMesh textMesh = go.AddComponent<TextMesh>();
@ -66,10 +53,9 @@ namespace TMPro.Examples
floatingText_Script = go.AddComponent<TextMeshProFloatingText>(); floatingText_Script = go.AddComponent<TextMeshProFloatingText>();
floatingText_Script.SpawnType = 1; floatingText_Script.SpawnType = 1;
} }
else if (SpawnType == 2) else if (SpawnType == 2) {
{
// Canvas WorldSpace Camera // Canvas WorldSpace Camera
GameObject go = new GameObject(); GameObject go = new();
Canvas canvas = go.AddComponent<Canvas>(); Canvas canvas = go.AddComponent<Canvas>();
canvas.worldCamera = Camera.main; canvas.worldCamera = Camera.main;
@ -88,10 +74,6 @@ namespace TMPro.Examples
floatingText_Script = go.AddComponent<TextMeshProFloatingText>(); floatingText_Script = go.AddComponent<TextMeshProFloatingText>();
floatingText_Script.SpawnType = 0; floatingText_Script.SpawnType = 0;
} }
}
} }
} }
} }

@ -1,14 +1,15 @@
using UnityEngine; using UnityEngine;
using System.Collections;
using UnityEngine.TextCore.LowLevel; using UnityEngine.TextCore.LowLevel;
namespace TMPro.Examples {
namespace TMPro.Examples public class Benchmark03 : MonoBehaviour {
{ public enum BenchmarkType {
TMP_SDF_MOBILE = 0,
public class Benchmark03 : MonoBehaviour TMP_SDF__MOBILE_SSD = 1,
{ TMP_SDF = 2,
public enum BenchmarkType { TMP_SDF_MOBILE = 0, TMP_SDF__MOBILE_SSD = 1, TMP_SDF = 2, TMP_BITMAP_MOBILE = 3, TEXTMESH_BITMAP = 4 } TMP_BITMAP_MOBILE = 3,
TEXTMESH_BITMAP = 4
}
public int NumberOfSamples = 100; public int NumberOfSamples = 100;
public BenchmarkType Benchmark; public BenchmarkType Benchmark;
@ -16,77 +17,65 @@ namespace TMPro.Examples
public Font SourceFont; public Font SourceFont;
void Awake() private void Awake() { }
{
}
void Start() private void Start() {
{
TMP_FontAsset fontAsset = null; TMP_FontAsset fontAsset = null;
// Create Dynamic Font Asset for the given font file. // Create Dynamic Font Asset for the given font file.
switch (Benchmark) switch (Benchmark) {
{
case BenchmarkType.TMP_SDF_MOBILE: case BenchmarkType.TMP_SDF_MOBILE:
fontAsset = TMP_FontAsset.CreateFontAsset(SourceFont, 90, 9, GlyphRenderMode.SDFAA, 256, 256, AtlasPopulationMode.Dynamic); fontAsset = TMP_FontAsset.CreateFontAsset(SourceFont, 90, 9, GlyphRenderMode.SDFAA, 256, 256);
break; break;
case BenchmarkType.TMP_SDF__MOBILE_SSD: case BenchmarkType.TMP_SDF__MOBILE_SSD:
fontAsset = TMP_FontAsset.CreateFontAsset(SourceFont, 90, 9, GlyphRenderMode.SDFAA, 256, 256, AtlasPopulationMode.Dynamic); fontAsset = TMP_FontAsset.CreateFontAsset(SourceFont, 90, 9, GlyphRenderMode.SDFAA, 256, 256);
fontAsset.material.shader = Shader.Find("TextMeshPro/Mobile/Distance Field SSD"); fontAsset.material.shader = Shader.Find("TextMeshPro/Mobile/Distance Field SSD");
break; break;
case BenchmarkType.TMP_SDF: case BenchmarkType.TMP_SDF:
fontAsset = TMP_FontAsset.CreateFontAsset(SourceFont, 90, 9, GlyphRenderMode.SDFAA, 256, 256, AtlasPopulationMode.Dynamic); fontAsset = TMP_FontAsset.CreateFontAsset(SourceFont, 90, 9, GlyphRenderMode.SDFAA, 256, 256);
fontAsset.material.shader = Shader.Find("TextMeshPro/Distance Field"); fontAsset.material.shader = Shader.Find("TextMeshPro/Distance Field");
break; break;
case BenchmarkType.TMP_BITMAP_MOBILE: case BenchmarkType.TMP_BITMAP_MOBILE:
fontAsset = TMP_FontAsset.CreateFontAsset(SourceFont, 90, 9, GlyphRenderMode.SMOOTH, 256, 256, AtlasPopulationMode.Dynamic); fontAsset = TMP_FontAsset.CreateFontAsset(SourceFont, 90, 9, GlyphRenderMode.SMOOTH, 256, 256);
break; break;
} }
for (int i = 0; i < NumberOfSamples; i++) for (int i = 0; i < NumberOfSamples; i++)
{ switch (Benchmark) {
switch (Benchmark)
{
case BenchmarkType.TMP_SDF_MOBILE: case BenchmarkType.TMP_SDF_MOBILE:
case BenchmarkType.TMP_SDF__MOBILE_SSD: case BenchmarkType.TMP_SDF__MOBILE_SSD:
case BenchmarkType.TMP_SDF: case BenchmarkType.TMP_SDF:
case BenchmarkType.TMP_BITMAP_MOBILE: case BenchmarkType.TMP_BITMAP_MOBILE: {
{ GameObject go = new();
GameObject go = new GameObject(); go.transform.position = new Vector3(0, 1.2f, 0);
go.transform.position = new Vector3(0, 1.2f, 0);
TextMeshPro textComponent = go.AddComponent<TextMeshPro>();
TextMeshPro textComponent = go.AddComponent<TextMeshPro>(); textComponent.font = fontAsset;
textComponent.font = fontAsset; textComponent.fontSize = 128;
textComponent.fontSize = 128; textComponent.text = "@";
textComponent.text = "@"; textComponent.alignment = TextAlignmentOptions.Center;
textComponent.alignment = TextAlignmentOptions.Center; textComponent.color = new Color32(255, 255, 0, 255);
textComponent.color = new Color32(255, 255, 0, 255);
if (Benchmark == BenchmarkType.TMP_BITMAP_MOBILE)
if (Benchmark == BenchmarkType.TMP_BITMAP_MOBILE) textComponent.fontSize = 132;
textComponent.fontSize = 132; }
}
break; break;
case BenchmarkType.TEXTMESH_BITMAP: case BenchmarkType.TEXTMESH_BITMAP: {
{ GameObject go = new();
GameObject go = new GameObject(); go.transform.position = new Vector3(0, 1.2f, 0);
go.transform.position = new Vector3(0, 1.2f, 0);
TextMesh textMesh = go.AddComponent<TextMesh>();
TextMesh textMesh = go.AddComponent<TextMesh>(); textMesh.GetComponent<Renderer>().sharedMaterial = SourceFont.material;
textMesh.GetComponent<Renderer>().sharedMaterial = SourceFont.material; textMesh.font = SourceFont;
textMesh.font = SourceFont; textMesh.anchor = TextAnchor.MiddleCenter;
textMesh.anchor = TextAnchor.MiddleCenter; textMesh.fontSize = 130;
textMesh.fontSize = 130;
textMesh.color = new Color32(255, 255, 0, 255);
textMesh.color = new Color32(255, 255, 0, 255); textMesh.text = "@";
textMesh.text = "@"; }
}
break; break;
} }
}
} }
} }
} }

@ -1,14 +1,8 @@
using UnityEngine; using UnityEngine;
using System.Collections;
namespace TMPro.Examples {
namespace TMPro.Examples public class Benchmark04 : MonoBehaviour {
{ public int SpawnType;
public class Benchmark04 : MonoBehaviour
{
public int SpawnType = 0;
public int MinPointSize = 12; public int MinPointSize = 12;
public int MaxPointSize = 64; public int MaxPointSize = 64;
@ -19,8 +13,7 @@ namespace TMPro.Examples
//public Material material; //public Material material;
void Start() private void Start() {
{
m_Transform = transform; m_Transform = transform;
float lineHeight = 0; float lineHeight = 0;
@ -28,15 +21,15 @@ namespace TMPro.Examples
float ratio = (float)Screen.width / Screen.height; float ratio = (float)Screen.width / Screen.height;
for (int i = MinPointSize; i <= MaxPointSize; i += Steps) for (int i = MinPointSize; i <= MaxPointSize; i += Steps)
{ if (SpawnType == 0) {
if (SpawnType == 0)
{
// TextMesh Pro Implementation // TextMesh Pro Implementation
GameObject go = new GameObject("Text - " + i + " Pts"); GameObject go = new("Text - " + i + " Pts");
if (lineHeight > orthoSize * 2) return; if (lineHeight > orthoSize * 2) return;
go.transform.position = m_Transform.position + new Vector3(ratio * -orthoSize * 0.975f, orthoSize * 0.975f - lineHeight, 0); go.transform.position = m_Transform.position +
new Vector3(ratio * -orthoSize * 0.975f, orthoSize * 0.975f - lineHeight,
0);
TextMeshPro textMeshPro = go.AddComponent<TextMeshPro>(); TextMeshPro textMeshPro = go.AddComponent<TextMeshPro>();
@ -55,11 +48,9 @@ namespace TMPro.Examples
lineHeight += i; lineHeight += i;
} }
else // TextMesh Implementation
{ // Causes crashes since atlas needed exceeds 4096 X 4096
// TextMesh Implementation /*
// Causes crashes since atlas needed exceeds 4096 X 4096
/*
GameObject go = new GameObject("Arial " + i); GameObject go = new GameObject("Arial " + i);
//if (lineHeight > orthoSize * 2 * 0.9f) return; //if (lineHeight > orthoSize * 2 * 0.9f) return;
@ -77,9 +68,6 @@ namespace TMPro.Examples
lineHeight += i; lineHeight += i;
*/ */
}
}
} }
} }
} }

@ -1,16 +1,19 @@
using UnityEngine; using UnityEngine;
using System.Collections;
namespace TMPro.Examples {
public class CameraController : MonoBehaviour {
public enum CameraModes {
Follow,
Isometric,
Free
}
namespace TMPro.Examples // Controls for Touches on Mobile devices
{ //private float prev_ZoomDelta;
public class CameraController : MonoBehaviour
{
public enum CameraModes { Follow, Isometric, Free }
private Transform cameraTransform;
private Transform dummyTarget; private const string event_SmoothingValue = "Slider - Smoothing Value";
private const string event_FollowDistance = "Slider - Camera Zoom";
public Transform CameraTarget; public Transform CameraTarget;
@ -20,38 +23,33 @@ namespace TMPro.Examples
public float ElevationAngle = 30.0f; public float ElevationAngle = 30.0f;
public float MaxElevationAngle = 85.0f; public float MaxElevationAngle = 85.0f;
public float MinElevationAngle = 0f; public float MinElevationAngle;
public float OrbitalAngle = 0f; public float OrbitalAngle;
public CameraModes CameraMode = CameraModes.Follow; public CameraModes CameraMode = CameraModes.Follow;
public bool MovementSmoothing = true; public bool MovementSmoothing = true;
public bool RotationSmoothing = false; public bool RotationSmoothing;
private bool previousSmoothing;
public float MovementSmoothingValue = 25f; public float MovementSmoothingValue = 25f;
public float RotationSmoothingValue = 5.0f; public float RotationSmoothingValue = 5.0f;
public float MoveSensitivity = 2.0f; public float MoveSensitivity = 2.0f;
private Transform cameraTransform;
private Vector3 currentVelocity = Vector3.zero; private Vector3 currentVelocity = Vector3.zero;
private Vector3 desiredPosition; private Vector3 desiredPosition;
private Transform dummyTarget;
private float mouseWheel;
private float mouseX; private float mouseX;
private float mouseY; private float mouseY;
private Vector3 moveVector; private Vector3 moveVector;
private float mouseWheel; private bool previousSmoothing;
// Controls for Touches on Mobile devices
//private float prev_ZoomDelta;
private const string event_SmoothingValue = "Slider - Smoothing Value";
private const string event_FollowDistance = "Slider - Camera Zoom";
void Awake() private void Awake() {
{
if (QualitySettings.vSyncCount > 0) if (QualitySettings.vSyncCount > 0)
Application.targetFrameRate = 60; Application.targetFrameRate = 60;
else else
@ -66,10 +64,8 @@ namespace TMPro.Examples
// Use this for initialization // Use this for initialization
void Start() private void Start() {
{ if (CameraTarget == null) {
if (CameraTarget == null)
{
// If we don't have a target (assigned by the player, create a dummy in the center of the scene). // If we don't have a target (assigned by the player, create a dummy in the center of the scene).
dummyTarget = new GameObject("Camera Target").transform; dummyTarget = new GameObject("Camera Target").transform;
CameraTarget = dummyTarget; CameraTarget = dummyTarget;
@ -77,54 +73,42 @@ namespace TMPro.Examples
} }
// Update is called once per frame // Update is called once per frame
void LateUpdate() private void LateUpdate() {
{
GetPlayerInput(); GetPlayerInput();
// Check if we still have a valid target // Check if we still have a valid target
if (CameraTarget != null) if (CameraTarget != null) {
{ if (CameraMode == CameraModes.Isometric) {
if (CameraMode == CameraModes.Isometric) desiredPosition = CameraTarget.position + Quaternion.Euler(ElevationAngle, OrbitalAngle, 0f) *
{ new Vector3(0, 0, -FollowDistance);
desiredPosition = CameraTarget.position + Quaternion.Euler(ElevationAngle, OrbitalAngle, 0f) * new Vector3(0, 0, -FollowDistance);
} }
else if (CameraMode == CameraModes.Follow) else if (CameraMode == CameraModes.Follow) {
{ desiredPosition = CameraTarget.position + CameraTarget.TransformDirection(
desiredPosition = CameraTarget.position + CameraTarget.TransformDirection(Quaternion.Euler(ElevationAngle, OrbitalAngle, 0f) * (new Vector3(0, 0, -FollowDistance))); Quaternion.Euler(ElevationAngle, OrbitalAngle, 0f) * new Vector3(0, 0, -FollowDistance));
}
else
{
// Free Camera implementation
} }
if (MovementSmoothing == true) // Free Camera implementation
{ if (MovementSmoothing)
// Using Smoothing // Using Smoothing
cameraTransform.position = Vector3.SmoothDamp(cameraTransform.position, desiredPosition, ref currentVelocity, MovementSmoothingValue * Time.fixedDeltaTime); cameraTransform.position = Vector3.SmoothDamp(cameraTransform.position, desiredPosition,
//cameraTransform.position = Vector3.Lerp(cameraTransform.position, desiredPosition, Time.deltaTime * 5.0f); ref currentVelocity, MovementSmoothingValue * Time.fixedDeltaTime);
} //cameraTransform.position = Vector3.Lerp(cameraTransform.position, desiredPosition, Time.deltaTime * 5.0f);
else else
{
// Not using Smoothing // Not using Smoothing
cameraTransform.position = desiredPosition; cameraTransform.position = desiredPosition;
}
if (RotationSmoothing == true) if (RotationSmoothing)
cameraTransform.rotation = Quaternion.Lerp(cameraTransform.rotation, Quaternion.LookRotation(CameraTarget.position - cameraTransform.position), RotationSmoothingValue * Time.deltaTime); cameraTransform.rotation = Quaternion.Lerp(cameraTransform.rotation,
Quaternion.LookRotation(CameraTarget.position - cameraTransform.position),
RotationSmoothingValue * Time.deltaTime);
else else
{
cameraTransform.LookAt(CameraTarget); cameraTransform.LookAt(CameraTarget);
}
} }
} }
private void GetPlayerInput() {
void GetPlayerInput()
{
moveVector = Vector3.zero; moveVector = Vector3.zero;
// Check Mouse Wheel Input prior to Shift Key so we can apply multiplier on Shift for Scrolling // Check Mouse Wheel Input prior to Shift Key so we can apply multiplier on Shift for Scrolling
@ -132,8 +116,7 @@ namespace TMPro.Examples
float touchCount = Input.touchCount; float touchCount = Input.touchCount;
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift) || touchCount > 0) if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift) || touchCount > 0) {
{
mouseWheel *= 10; mouseWheel *= 10;
if (Input.GetKeyDown(KeyCode.I)) if (Input.GetKeyDown(KeyCode.I))
@ -147,20 +130,17 @@ namespace TMPro.Examples
// Check for right mouse button to change camera follow and elevation angle // Check for right mouse button to change camera follow and elevation angle
if (Input.GetMouseButton(1)) if (Input.GetMouseButton(1)) {
{
mouseY = Input.GetAxis("Mouse Y"); mouseY = Input.GetAxis("Mouse Y");
mouseX = Input.GetAxis("Mouse X"); mouseX = Input.GetAxis("Mouse X");
if (mouseY > 0.01f || mouseY < -0.01f) if (mouseY > 0.01f || mouseY < -0.01f) {
{
ElevationAngle -= mouseY * MoveSensitivity; ElevationAngle -= mouseY * MoveSensitivity;
// Limit Elevation angle between min & max values. // Limit Elevation angle between min & max values.
ElevationAngle = Mathf.Clamp(ElevationAngle, MinElevationAngle, MaxElevationAngle); ElevationAngle = Mathf.Clamp(ElevationAngle, MinElevationAngle, MaxElevationAngle);
} }
if (mouseX > 0.01f || mouseX < -0.01f) if (mouseX > 0.01f || mouseX < -0.01f) {
{
OrbitalAngle += mouseX * MoveSensitivity; OrbitalAngle += mouseX * MoveSensitivity;
if (OrbitalAngle > 360) if (OrbitalAngle > 360)
OrbitalAngle -= 360; OrbitalAngle -= 360;
@ -170,13 +150,11 @@ namespace TMPro.Examples
} }
// Get Input from Mobile Device // Get Input from Mobile Device
if (touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved) if (touchCount == 1 && Input.GetTouch(0).phase == TouchPhase.Moved) {
{
Vector2 deltaPosition = Input.GetTouch(0).deltaPosition; Vector2 deltaPosition = Input.GetTouch(0).deltaPosition;
// Handle elevation changes // Handle elevation changes
if (deltaPosition.y > 0.01f || deltaPosition.y < -0.01f) if (deltaPosition.y > 0.01f || deltaPosition.y < -0.01f) {
{
ElevationAngle -= deltaPosition.y * 0.1f; ElevationAngle -= deltaPosition.y * 0.1f;
// Limit Elevation angle between min & max values. // Limit Elevation angle between min & max values.
ElevationAngle = Mathf.Clamp(ElevationAngle, MinElevationAngle, MaxElevationAngle); ElevationAngle = Mathf.Clamp(ElevationAngle, MinElevationAngle, MaxElevationAngle);
@ -184,45 +162,36 @@ namespace TMPro.Examples
// Handle left & right // Handle left & right
if (deltaPosition.x > 0.01f || deltaPosition.x < -0.01f) if (deltaPosition.x > 0.01f || deltaPosition.x < -0.01f) {
{
OrbitalAngle += deltaPosition.x * 0.1f; OrbitalAngle += deltaPosition.x * 0.1f;
if (OrbitalAngle > 360) if (OrbitalAngle > 360)
OrbitalAngle -= 360; OrbitalAngle -= 360;
if (OrbitalAngle < 0) if (OrbitalAngle < 0)
OrbitalAngle += 360; OrbitalAngle += 360;
} }
} }
// Check for left mouse button to select a new CameraTarget or to reset Follow position // Check for left mouse button to select a new CameraTarget or to reset Follow position
if (Input.GetMouseButton(0)) if (Input.GetMouseButton(0)) {
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit; RaycastHit hit;
if (Physics.Raycast(ray, out hit, 300, 1 << 10 | 1 << 11 | 1 << 12 | 1 << 14)) if (Physics.Raycast(ray, out hit, 300, (1 << 10) | (1 << 11) | (1 << 12) | (1 << 14))) {
{ if (hit.transform == CameraTarget) {
if (hit.transform == CameraTarget)
{
// Reset Follow Position // Reset Follow Position
OrbitalAngle = 0; OrbitalAngle = 0;
} }
else else {
{
CameraTarget = hit.transform; CameraTarget = hit.transform;
OrbitalAngle = 0; OrbitalAngle = 0;
MovementSmoothing = previousSmoothing; MovementSmoothing = previousSmoothing;
} }
} }
} }
if (Input.GetMouseButton(2)) if (Input.GetMouseButton(2)) {
{ if (dummyTarget == null) {
if (dummyTarget == null)
{
// We need a Dummy Target to anchor the Camera // We need a Dummy Target to anchor the Camera
dummyTarget = new GameObject("Camera Target").transform; dummyTarget = new GameObject("Camera Target").transform;
dummyTarget.position = CameraTarget.position; dummyTarget.position = CameraTarget.position;
@ -231,8 +200,7 @@ namespace TMPro.Examples
previousSmoothing = MovementSmoothing; previousSmoothing = MovementSmoothing;
MovementSmoothing = false; MovementSmoothing = false;
} }
else if (dummyTarget != CameraTarget) else if (dummyTarget != CameraTarget) {
{
// Move DummyTarget to CameraTarget // Move DummyTarget to CameraTarget
dummyTarget.position = CameraTarget.position; dummyTarget.position = CameraTarget.position;
dummyTarget.rotation = CameraTarget.rotation; dummyTarget.rotation = CameraTarget.rotation;
@ -248,14 +216,11 @@ namespace TMPro.Examples
moveVector = cameraTransform.TransformDirection(mouseX, mouseY, 0); moveVector = cameraTransform.TransformDirection(mouseX, mouseY, 0);
dummyTarget.Translate(-moveVector, Space.World); dummyTarget.Translate(-moveVector, Space.World);
} }
} }
// Check Pinching to Zoom in - out on Mobile device // Check Pinching to Zoom in - out on Mobile device
if (touchCount == 2) if (touchCount == 2) {
{
Touch touch0 = Input.GetTouch(0); Touch touch0 = Input.GetTouch(0);
Touch touch1 = Input.GetTouch(1); Touch touch1 = Input.GetTouch(1);
@ -267,26 +232,19 @@ namespace TMPro.Examples
float zoomDelta = prevTouchDelta - touchDelta; float zoomDelta = prevTouchDelta - touchDelta;
if (zoomDelta > 0.01f || zoomDelta < -0.01f) if (zoomDelta > 0.01f || zoomDelta < -0.01f) {
{
FollowDistance += zoomDelta * 0.25f; FollowDistance += zoomDelta * 0.25f;
// Limit FollowDistance between min & max values. // Limit FollowDistance between min & max values.
FollowDistance = Mathf.Clamp(FollowDistance, MinFollowDistance, MaxFollowDistance); FollowDistance = Mathf.Clamp(FollowDistance, MinFollowDistance, MaxFollowDistance);
} }
} }
// Check MouseWheel to Zoom in-out // Check MouseWheel to Zoom in-out
if (mouseWheel < -0.01f || mouseWheel > 0.01f) if (mouseWheel < -0.01f || mouseWheel > 0.01f) {
{
FollowDistance -= mouseWheel * 5.0f; FollowDistance -= mouseWheel * 5.0f;
// Limit FollowDistance between min & max values. // Limit FollowDistance between min & max values.
FollowDistance = Mathf.Clamp(FollowDistance, MinFollowDistance, MaxFollowDistance); FollowDistance = Mathf.Clamp(FollowDistance, MinFollowDistance, MaxFollowDistance);
} }
} }
} }
} }

@ -1,38 +1,34 @@
using UnityEngine; using System;
using UnityEngine.UI;
using TMPro; using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class ChatController : MonoBehaviour { public class ChatController : MonoBehaviour {
public TMP_InputField ChatInputField; public TMP_InputField ChatInputField;
public TMP_Text ChatDisplayOutput; public TMP_Text ChatDisplayOutput;
public Scrollbar ChatScrollbar; public Scrollbar ChatScrollbar;
void OnEnable() private void OnEnable() {
{
ChatInputField.onSubmit.AddListener(AddToChatOutput); ChatInputField.onSubmit.AddListener(AddToChatOutput);
} }
void OnDisable() private void OnDisable() {
{
ChatInputField.onSubmit.RemoveListener(AddToChatOutput); ChatInputField.onSubmit.RemoveListener(AddToChatOutput);
} }
void AddToChatOutput(string newText) private void AddToChatOutput(string newText) {
{
// Clear Input Field // Clear Input Field
ChatInputField.text = string.Empty; ChatInputField.text = string.Empty;
var timeNow = System.DateTime.Now; DateTime timeNow = DateTime.Now;
string formattedInput = "[<#FFFF80>" + timeNow.Hour.ToString("d2") + ":" + timeNow.Minute.ToString("d2") + ":" + timeNow.Second.ToString("d2") + "</color>] " + newText; string formattedInput = "[<#FFFF80>" + timeNow.Hour.ToString("d2") + ":" + timeNow.Minute.ToString("d2") + ":" +
timeNow.Second.ToString("d2") + "</color>] " + newText;
if (ChatDisplayOutput != null) if (ChatDisplayOutput != null) {
{
// No special formatting for first entry // No special formatting for first entry
// Add line feed before each subsequent entries // Add line feed before each subsequent entries
if (ChatDisplayOutput.text == string.Empty) if (ChatDisplayOutput.text == string.Empty)
@ -47,5 +43,4 @@ public class ChatController : MonoBehaviour {
// Set the scrollbar to the bottom when next text is submitted. // Set the scrollbar to the bottom when next text is submitted.
ChatScrollbar.value = 0; ChatScrollbar.value = 0;
} }
}
}

@ -1,19 +1,16 @@
using TMPro; using TMPro;
using UnityEngine; using UnityEngine;
public class DropdownSample: MonoBehaviour public class DropdownSample : MonoBehaviour {
{ [SerializeField] private TextMeshProUGUI text;
[SerializeField]
private TextMeshProUGUI text = null;
[SerializeField] [SerializeField] private TMP_Dropdown dropdownWithoutPlaceholder;
private TMP_Dropdown dropdownWithoutPlaceholder = null;
[SerializeField] [SerializeField] private TMP_Dropdown dropdownWithPlaceholder;
private TMP_Dropdown dropdownWithPlaceholder = null;
public void OnButtonClick() public void OnButtonClick() {
{ text.text = dropdownWithPlaceholder.value > -1
text.text = dropdownWithPlaceholder.value > -1 ? "Selected values:\n" + dropdownWithoutPlaceholder.value + " - " + dropdownWithPlaceholder.value : "Error: Please make a selection"; ? "Selected values:\n" + dropdownWithoutPlaceholder.value + " - " + dropdownWithPlaceholder.value
} : "Error: Please make a selection";
} }
}

@ -1,35 +1,33 @@
using UnityEngine; using System.Collections;
using System.Collections;
using TMPro; using TMPro;
using UnityEngine;
public class EnvMapAnimator : MonoBehaviour { public class EnvMapAnimator : MonoBehaviour {
//private Vector3 TranslationSpeeds; //private Vector3 TranslationSpeeds;
public Vector3 RotationSpeeds; public Vector3 RotationSpeeds;
private TMP_Text m_textMeshPro;
private Material m_material; private Material m_material;
private TMP_Text m_textMeshPro;
void Awake()
{ private void Awake() {
//Debug.Log("Awake() on Script called."); //Debug.Log("Awake() on Script called.");
m_textMeshPro = GetComponent<TMP_Text>(); m_textMeshPro = GetComponent<TMP_Text>();
m_material = m_textMeshPro.fontSharedMaterial; m_material = m_textMeshPro.fontSharedMaterial;
} }
// Use this for initialization // Use this for initialization
IEnumerator Start () private IEnumerator Start() {
{ Matrix4x4 matrix = new();
Matrix4x4 matrix = new Matrix4x4();
while (true) {
while (true)
{
//matrix.SetTRS(new Vector3 (Time.time * TranslationSpeeds.x, Time.time * TranslationSpeeds.y, Time.time * TranslationSpeeds.z), Quaternion.Euler(Time.time * RotationSpeeds.x, Time.time * RotationSpeeds.y , Time.time * RotationSpeeds.z), Vector3.one); //matrix.SetTRS(new Vector3 (Time.time * TranslationSpeeds.x, Time.time * TranslationSpeeds.y, Time.time * TranslationSpeeds.z), Quaternion.Euler(Time.time * RotationSpeeds.x, Time.time * RotationSpeeds.y , Time.time * RotationSpeeds.z), Vector3.one);
matrix.SetTRS(Vector3.zero, Quaternion.Euler(Time.time * RotationSpeeds.x, Time.time * RotationSpeeds.y , Time.time * RotationSpeeds.z), Vector3.one); matrix.SetTRS(Vector3.zero,
Quaternion.Euler(Time.time * RotationSpeeds.x, Time.time * RotationSpeeds.y,
Time.time * RotationSpeeds.z), Vector3.one);
m_material.SetMatrix("_EnvMatrix", matrix); m_material.SetMatrix("_EnvMatrix", matrix);
yield return null; yield return null;
} }
} }
} }

@ -1,13 +1,7 @@
using UnityEngine; using UnityEngine;
using System.Collections;
namespace TMPro.Examples
{
public class ObjectSpin : MonoBehaviour
{
namespace TMPro.Examples {
public class ObjectSpin : MonoBehaviour {
#pragma warning disable 0414 #pragma warning disable 0414
public float SpinSpeed = 5; public float SpinSpeed = 5;
@ -19,13 +13,17 @@ namespace TMPro.Examples
private Vector3 m_initial_Rotation; private Vector3 m_initial_Rotation;
private Vector3 m_initial_Position; private Vector3 m_initial_Position;
private Color32 m_lightColor; private Color32 m_lightColor;
private int frames = 0; private int frames;
public enum MotionType {
Rotation,
BackAndForth,
Translation
}
public enum MotionType { Rotation, BackAndForth, Translation };
public MotionType Motion; public MotionType Motion;
void Awake() private void Awake() {
{
m_transform = transform; m_transform = transform;
m_initial_Rotation = m_transform.rotation.eulerAngles; m_initial_Rotation = m_transform.rotation.eulerAngles;
m_initial_Position = m_transform.position; m_initial_Position = m_transform.position;
@ -36,19 +34,16 @@ namespace TMPro.Examples
// Update is called once per frame // Update is called once per frame
void Update() private void Update() {
{ if (Motion == MotionType.Rotation) {
if (Motion == MotionType.Rotation)
{
m_transform.Rotate(0, SpinSpeed * Time.deltaTime, 0); m_transform.Rotate(0, SpinSpeed * Time.deltaTime, 0);
} }
else if (Motion == MotionType.BackAndForth) else if (Motion == MotionType.BackAndForth) {
{
m_time += SpinSpeed * Time.deltaTime; m_time += SpinSpeed * Time.deltaTime;
m_transform.rotation = Quaternion.Euler(m_initial_Rotation.x, Mathf.Sin(m_time) * RotationRange + m_initial_Rotation.y, m_initial_Rotation.z); m_transform.rotation = Quaternion.Euler(m_initial_Rotation.x,
Mathf.Sin(m_time) * RotationRange + m_initial_Rotation.y, m_initial_Rotation.z);
} }
else else {
{
m_time += SpinSpeed * Time.deltaTime; m_time += SpinSpeed * Time.deltaTime;
float x = 15 * Mathf.Cos(m_time * .95f); float x = 15 * Mathf.Cos(m_time * .95f);

@ -1,22 +1,16 @@
using UnityEngine; using System.Collections;
using System.Collections; using UnityEngine;
namespace TMPro.Examples
{
public class ShaderPropAnimator : MonoBehaviour
{
private Renderer m_Renderer;
private Material m_Material;
namespace TMPro.Examples {
public class ShaderPropAnimator : MonoBehaviour {
public AnimationCurve GlowCurve; public AnimationCurve GlowCurve;
public float m_frame; public float m_frame;
private Material m_Material;
private Renderer m_Renderer;
void Awake() private void Awake() {
{
// Cache a reference to object's renderer // Cache a reference to object's renderer
m_Renderer = GetComponent<Renderer>(); m_Renderer = GetComponent<Renderer>();
@ -24,19 +18,16 @@ namespace TMPro.Examples
m_Material = m_Renderer.material; m_Material = m_Renderer.material;
} }
void Start() private void Start() {
{
StartCoroutine(AnimateProperties()); StartCoroutine(AnimateProperties());
} }
IEnumerator AnimateProperties() private IEnumerator AnimateProperties() {
{
//float lightAngle; //float lightAngle;
float glowPower; float glowPower;
m_frame = Random.Range(0f, 1f); m_frame = Random.Range(0f, 1f);
while (true) while (true) {
{
//lightAngle = (m_Material.GetFloat(ShaderPropertyIDs.ID_LightAngle) + Time.deltaTime) % 6.2831853f; //lightAngle = (m_Material.GetFloat(ShaderPropertyIDs.ID_LightAngle) + Time.deltaTime) % 6.2831853f;
//m_Material.SetFloat(ShaderPropertyIDs.ID_LightAngle, lightAngle); //m_Material.SetFloat(ShaderPropertyIDs.ID_LightAngle, lightAngle);
@ -48,4 +39,4 @@ namespace TMPro.Examples
} }
} }
} }
} }

@ -1,22 +1,16 @@
using UnityEngine; using UnityEngine;
using System.Collections;
namespace TMPro.Examples {
namespace TMPro.Examples public class SimpleScript : MonoBehaviour {
{
public class SimpleScript : MonoBehaviour
{
private TextMeshPro m_textMeshPro;
//private TMP_FontAsset m_FontAsset; //private TMP_FontAsset m_FontAsset;
private const string label = "The <#0050FF>count is: </color>{0:2}"; private const string label = "The <#0050FF>count is: </color>{0:2}";
private float m_frame; private float m_frame;
private TextMeshPro m_textMeshPro;
void Start() private void Start() {
{
// Add new TextMesh Pro Component // Add new TextMesh Pro Component
m_textMeshPro = gameObject.AddComponent<TextMeshPro>(); m_textMeshPro = gameObject.AddComponent<TextMeshPro>();
@ -29,12 +23,12 @@ namespace TMPro.Examples
// Assign Material to TextMesh Pro Component // Assign Material to TextMesh Pro Component
//m_textMeshPro.fontSharedMaterial = Resources.Load("Fonts & Materials/LiberationSans SDF - Bevel", typeof(Material)) as Material; //m_textMeshPro.fontSharedMaterial = Resources.Load("Fonts & Materials/LiberationSans SDF - Bevel", typeof(Material)) as Material;
//m_textMeshPro.fontSharedMaterial.EnableKeyword("BEVEL_ON"); //m_textMeshPro.fontSharedMaterial.EnableKeyword("BEVEL_ON");
// Set various font settings. // Set various font settings.
m_textMeshPro.fontSize = 48; m_textMeshPro.fontSize = 48;
m_textMeshPro.alignment = TextAlignmentOptions.Center; m_textMeshPro.alignment = TextAlignmentOptions.Center;
//m_textMeshPro.anchorDampening = true; // Has been deprecated but under consideration for re-implementation. //m_textMeshPro.anchorDampening = true; // Has been deprecated but under consideration for re-implementation.
//m_textMeshPro.enableAutoSizing = true; //m_textMeshPro.enableAutoSizing = true;
@ -48,11 +42,9 @@ namespace TMPro.Examples
} }
void Update() private void Update() {
{
m_textMeshPro.SetText(label, m_frame % 1000); m_textMeshPro.SetText(label, m_frame % 1000);
m_frame += 1 * Time.deltaTime; m_frame += 1 * Time.deltaTime;
} }
} }
} }

@ -1,36 +1,30 @@
using UnityEngine; using System.Collections;
using System.Collections; using UnityEngine;
namespace TMPro.Examples {
public class SkewTextExample : MonoBehaviour {
public AnimationCurve VertexCurve = new(new Keyframe(0, 0), new Keyframe(0.25f, 2.0f), new Keyframe(0.5f, 0),
new Keyframe(0.75f, 2.0f), new Keyframe(1, 0f));
namespace TMPro.Examples
{
public class SkewTextExample : MonoBehaviour
{
private TMP_Text m_TextComponent;
public AnimationCurve VertexCurve = new AnimationCurve(new Keyframe(0, 0), new Keyframe(0.25f, 2.0f), new Keyframe(0.5f, 0), new Keyframe(0.75f, 2.0f), new Keyframe(1, 0f));
//public float AngleMultiplier = 1.0f; //public float AngleMultiplier = 1.0f;
//public float SpeedMultiplier = 1.0f; //public float SpeedMultiplier = 1.0f;
public float CurveScale = 1.0f; public float CurveScale = 1.0f;
public float ShearAmount = 1.0f; public float ShearAmount = 1.0f;
void Awake() private TMP_Text m_TextComponent;
{
private void Awake() {
m_TextComponent = gameObject.GetComponent<TMP_Text>(); m_TextComponent = gameObject.GetComponent<TMP_Text>();
} }
void Start() private void Start() {
{
StartCoroutine(WarpText()); StartCoroutine(WarpText());
} }
private AnimationCurve CopyAnimationCurve(AnimationCurve curve) private AnimationCurve CopyAnimationCurve(AnimationCurve curve) {
{ AnimationCurve newCurve = new();
AnimationCurve newCurve = new AnimationCurve();
newCurve.keys = curve.keys; newCurve.keys = curve.keys;
@ -39,12 +33,11 @@ namespace TMPro.Examples
/// <summary> /// <summary>
/// Method to curve text along a Unity animation curve. /// Method to curve text along a Unity animation curve.
/// </summary> /// </summary>
/// <param name="textComponent"></param> /// <param name="textComponent"></param>
/// <returns></returns> /// <returns></returns>
IEnumerator WarpText() private IEnumerator WarpText() {
{
VertexCurve.preWrapMode = WrapMode.Clamp; VertexCurve.preWrapMode = WrapMode.Clamp;
VertexCurve.postWrapMode = WrapMode.Clamp; VertexCurve.postWrapMode = WrapMode.Clamp;
@ -59,10 +52,9 @@ namespace TMPro.Examples
float old_ShearValue = ShearAmount; float old_ShearValue = ShearAmount;
AnimationCurve old_curve = CopyAnimationCurve(VertexCurve); AnimationCurve old_curve = CopyAnimationCurve(VertexCurve);
while (true) while (true) {
{ if (!m_TextComponent.havePropertiesChanged && old_CurveScale == CurveScale &&
if (!m_TextComponent.havePropertiesChanged && old_CurveScale == CurveScale && old_curve.keys[1].value == VertexCurve.keys[1].value && old_ShearValue == ShearAmount) old_curve.keys[1].value == VertexCurve.keys[1].value && old_ShearValue == ShearAmount) {
{
yield return null; yield return null;
continue; continue;
} }
@ -71,7 +63,8 @@ namespace TMPro.Examples
old_curve = CopyAnimationCurve(VertexCurve); old_curve = CopyAnimationCurve(VertexCurve);
old_ShearValue = ShearAmount; old_ShearValue = ShearAmount;
m_TextComponent.ForceMeshUpdate(); // Generate the mesh and populate the textInfo with data we can use and manipulate. m_TextComponent
.ForceMeshUpdate(); // Generate the mesh and populate the textInfo with data we can use and manipulate.
TMP_TextInfo textInfo = m_TextComponent.textInfo; TMP_TextInfo textInfo = m_TextComponent.textInfo;
int characterCount = textInfo.characterCount; int characterCount = textInfo.characterCount;
@ -82,13 +75,11 @@ namespace TMPro.Examples
//vertices = textInfo.meshInfo[0].vertices; //vertices = textInfo.meshInfo[0].vertices;
//int lastVertexIndex = textInfo.characterInfo[characterCount - 1].vertexIndex; //int lastVertexIndex = textInfo.characterInfo[characterCount - 1].vertexIndex;
float boundsMinX = m_TextComponent.bounds.min.x; //textInfo.meshInfo[0].mesh.bounds.min.x; float boundsMinX = m_TextComponent.bounds.min.x; //textInfo.meshInfo[0].mesh.bounds.min.x;
float boundsMaxX = m_TextComponent.bounds.max.x; //textInfo.meshInfo[0].mesh.bounds.max.x; float boundsMaxX = m_TextComponent.bounds.max.x; //textInfo.meshInfo[0].mesh.bounds.max.x;
for (int i = 0; i < characterCount; i++) for (int i = 0; i < characterCount; i++) {
{
if (!textInfo.characterInfo[i].isVisible) if (!textInfo.characterInfo[i].isVisible)
continue; continue;
@ -100,7 +91,9 @@ namespace TMPro.Examples
vertices = textInfo.meshInfo[materialIndex].vertices; vertices = textInfo.meshInfo[materialIndex].vertices;
// Compute the baseline mid point for each character // Compute the baseline mid point for each character
Vector3 offsetToMidBaseline = new Vector2((vertices[vertexIndex + 0].x + vertices[vertexIndex + 2].x) / 2, textInfo.characterInfo[i].baseLine); Vector3 offsetToMidBaseline =
new Vector2((vertices[vertexIndex + 0].x + vertices[vertexIndex + 2].x) / 2,
textInfo.characterInfo[i].baseLine);
//float offsetY = VertexCurve.Evaluate((float)i / characterCount + loopCount / 50f); // Random.Range(-0.25f, 0.25f); //float offsetY = VertexCurve.Evaluate((float)i / characterCount + loopCount / 50f); // Random.Range(-0.25f, 0.25f);
// Apply offset to adjust our pivot point. // Apply offset to adjust our pivot point.
@ -111,8 +104,13 @@ namespace TMPro.Examples
// Apply the Shearing FX // Apply the Shearing FX
float shear_value = ShearAmount * 0.01f; float shear_value = ShearAmount * 0.01f;
Vector3 topShear = new Vector3(shear_value * (textInfo.characterInfo[i].topRight.y - textInfo.characterInfo[i].baseLine), 0, 0); Vector3 topShear =
Vector3 bottomShear = new Vector3(shear_value * (textInfo.characterInfo[i].baseLine - textInfo.characterInfo[i].bottomRight.y), 0, 0); new(shear_value * (textInfo.characterInfo[i].topRight.y - textInfo.characterInfo[i].baseLine),
0, 0);
Vector3 bottomShear =
new(
shear_value * (textInfo.characterInfo[i].baseLine -
textInfo.characterInfo[i].bottomRight.y), 0, 0);
vertices[vertexIndex + 0] += -bottomShear; vertices[vertexIndex + 0] += -bottomShear;
vertices[vertexIndex + 1] += topShear; vertices[vertexIndex + 1] += topShear;
@ -121,14 +119,16 @@ namespace TMPro.Examples
// Compute the angle of rotation for each character based on the animation curve // Compute the angle of rotation for each character based on the animation curve
float x0 = (offsetToMidBaseline.x - boundsMinX) / (boundsMaxX - boundsMinX); // Character's position relative to the bounds of the mesh. float x0 = (offsetToMidBaseline.x - boundsMinX) /
(boundsMaxX - boundsMinX); // Character's position relative to the bounds of the mesh.
float x1 = x0 + 0.0001f; float x1 = x0 + 0.0001f;
float y0 = VertexCurve.Evaluate(x0) * CurveScale; float y0 = VertexCurve.Evaluate(x0) * CurveScale;
float y1 = VertexCurve.Evaluate(x1) * CurveScale; float y1 = VertexCurve.Evaluate(x1) * CurveScale;
Vector3 horizontal = new Vector3(1, 0, 0); Vector3 horizontal = new(1, 0, 0);
//Vector3 normal = new Vector3(-(y1 - y0), (x1 * (boundsMaxX - boundsMinX) + boundsMinX) - offsetToMidBaseline.x, 0); //Vector3 normal = new Vector3(-(y1 - y0), (x1 * (boundsMaxX - boundsMinX) + boundsMinX) - offsetToMidBaseline.x, 0);
Vector3 tangent = new Vector3(x1 * (boundsMaxX - boundsMinX) + boundsMinX, y1) - new Vector3(offsetToMidBaseline.x, y0); Vector3 tangent = new Vector3(x1 * (boundsMaxX - boundsMinX) + boundsMinX, y1) -
new Vector3(offsetToMidBaseline.x, y0);
float dot = Mathf.Acos(Vector3.Dot(horizontal, tangent.normalized)) * 57.2957795f; float dot = Mathf.Acos(Vector3.Dot(horizontal, tangent.normalized)) * 57.2957795f;
Vector3 cross = Vector3.Cross(horizontal, tangent); Vector3 cross = Vector3.Cross(horizontal, tangent);
@ -155,4 +155,4 @@ namespace TMPro.Examples
} }
} }
} }
} }

@ -1,21 +1,15 @@
using UnityEngine; using System;
using System;
namespace TMPro {
namespace TMPro
{
/// <summary> /// <summary>
/// EXample of a Custom Character Input Validator to only allow digits from 0 to 9. /// EXample of a Custom Character Input Validator to only allow digits from 0 to 9.
/// </summary> /// </summary>
[Serializable] [Serializable]
//[CreateAssetMenu(fileName = "InputValidator - Digits.asset", menuName = "TextMeshPro/Input Validators/Digits", order = 100)] //[CreateAssetMenu(fileName = "InputValidator - Digits.asset", menuName = "TextMeshPro/Input Validators/Digits", order = 100)]
public class TMP_DigitValidator : TMP_InputValidator public class TMP_DigitValidator : TMP_InputValidator {
{
// Custom text input validation function // Custom text input validation function
public override char Validate(ref string text, ref int pos, char ch) public override char Validate(ref string text, ref int pos, char ch) {
{ if (ch >= '0' && ch <= '9') {
if (ch >= '0' && ch <= '9')
{
text += ch; text += ch;
pos += 1; pos += 1;
return ch; return ch;
@ -24,4 +18,4 @@ namespace TMPro
return (char)0; return (char)0;
} }
} }
} }

@ -1,29 +1,24 @@
using UnityEngine; using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using TMPro;
namespace TMPro.Examples {
public class TMP_ExampleScript_01 : MonoBehaviour {
public enum objectType {
TextMeshPro = 0,
TextMeshProUGUI = 1
}
//private TMP_InputField m_inputfield;
namespace TMPro.Examples
{
public class TMP_ExampleScript_01 : MonoBehaviour private const string k_label = "The count is <#0080ff>{0}</color>";
{
public enum objectType { TextMeshPro = 0, TextMeshProUGUI = 1 };
public objectType ObjectType; public objectType ObjectType;
public bool isStatic; public bool isStatic;
private int count;
private TMP_Text m_text; private TMP_Text m_text;
//private TMP_InputField m_inputfield; private void Awake() {
private const string k_label = "The count is <#0080ff>{0}</color>";
private int count;
void Awake()
{
// Get a reference to the TMP text component if one already exists otherwise add one. // Get a reference to the TMP text component if one already exists otherwise add one.
// This example show the convenience of having both TMP components derive from TMP_Text. // This example show the convenience of having both TMP components derive from TMP_Text.
if (ObjectType == 0) if (ObjectType == 0)
@ -51,14 +46,11 @@ namespace TMPro.Examples
} }
void Update() private void Update() {
{ if (!isStatic) {
if (!isStatic)
{
m_text.SetText(k_label, count % 1000); m_text.SetText(k_label, count % 1000);
count += 1; count += 1;
} }
} }
} }
} }

@ -1,42 +1,42 @@
using UnityEngine; using UnityEngine;
using System.Collections;
namespace TMPro.Examples {
public class TMP_FrameRateCounter : MonoBehaviour {
public enum FpsCounterAnchorPositions {
TopLeft,
BottomLeft,
TopRight,
BottomRight
}
namespace TMPro.Examples private const string fpsLabel = "{0:2}</color> <#8080ff>FPS \n<#FF8000>{1:2} <#8080ff>MS";
{
public class TMP_FrameRateCounter : MonoBehaviour
{
public float UpdateInterval = 5.0f; public float UpdateInterval = 5.0f;
private float m_LastInterval = 0;
private int m_Frames = 0;
public enum FpsCounterAnchorPositions { TopLeft, BottomLeft, TopRight, BottomRight };
public FpsCounterAnchorPositions AnchorPosition = FpsCounterAnchorPositions.TopRight; public FpsCounterAnchorPositions AnchorPosition = FpsCounterAnchorPositions.TopRight;
private string htmlColorTag; private string htmlColorTag;
private const string fpsLabel = "{0:2}</color> <#8080ff>FPS \n<#FF8000>{1:2} <#8080ff>MS";
private TextMeshPro m_TextMeshPro; private FpsCounterAnchorPositions last_AnchorPosition;
private Transform m_frameCounter_transform;
private Camera m_camera; private Camera m_camera;
private Transform m_frameCounter_transform;
private int m_Frames;
private float m_LastInterval;
private FpsCounterAnchorPositions last_AnchorPosition; private TextMeshPro m_TextMeshPro;
void Awake() private void Awake() {
{
if (!enabled) if (!enabled)
return; return;
m_camera = Camera.main; m_camera = Camera.main;
Application.targetFrameRate = 9999; Application.targetFrameRate = 9999;
GameObject frameCounter = new GameObject("Frame Counter"); GameObject frameCounter = new("Frame Counter");
m_TextMeshPro = frameCounter.AddComponent<TextMeshPro>(); m_TextMeshPro = frameCounter.AddComponent<TextMeshPro>();
m_TextMeshPro.font = Resources.Load<TMP_FontAsset>("Fonts & Materials/LiberationSans SDF"); m_TextMeshPro.font = Resources.Load<TMP_FontAsset>("Fonts & Materials/LiberationSans SDF");
m_TextMeshPro.fontSharedMaterial = Resources.Load<Material>("Fonts & Materials/LiberationSans SDF - Overlay"); m_TextMeshPro.fontSharedMaterial =
Resources.Load<Material>("Fonts & Materials/LiberationSans SDF - Overlay");
m_frameCounter_transform = frameCounter.transform; m_frameCounter_transform = frameCounter.transform;
@ -57,18 +57,14 @@ namespace TMPro.Examples
Set_FrameCounter_Position(AnchorPosition); Set_FrameCounter_Position(AnchorPosition);
last_AnchorPosition = AnchorPosition; last_AnchorPosition = AnchorPosition;
} }
void Start() private void Start() {
{
m_LastInterval = Time.realtimeSinceStartup; m_LastInterval = Time.realtimeSinceStartup;
m_Frames = 0; m_Frames = 0;
} }
void Update() private void Update() {
{
if (AnchorPosition != last_AnchorPosition) if (AnchorPosition != last_AnchorPosition)
Set_FrameCounter_Position(AnchorPosition); Set_FrameCounter_Position(AnchorPosition);
@ -77,8 +73,7 @@ namespace TMPro.Examples
m_Frames += 1; m_Frames += 1;
float timeNow = Time.realtimeSinceStartup; float timeNow = Time.realtimeSinceStartup;
if (timeNow > m_LastInterval + UpdateInterval) if (timeNow > m_LastInterval + UpdateInterval) {
{
// display two fractional digits (f2 format) // display two fractional digits (f2 format)
float fps = m_Frames / (timeNow - m_LastInterval); float fps = m_Frames / (timeNow - m_LastInterval);
float ms = 1000.0f / Mathf.Max(fps, 0.00001f); float ms = 1000.0f / Mathf.Max(fps, 0.00001f);
@ -101,13 +96,11 @@ namespace TMPro.Examples
} }
void Set_FrameCounter_Position(FpsCounterAnchorPositions anchor_position) private void Set_FrameCounter_Position(FpsCounterAnchorPositions anchor_position) {
{
//Debug.Log("Changing frame counter anchor position."); //Debug.Log("Changing frame counter anchor position.");
m_TextMeshPro.margin = new Vector4(1f, 1f, 1f, 1f); m_TextMeshPro.margin = new Vector4(1f, 1f, 1f, 1f);
switch (anchor_position) switch (anchor_position) {
{
case FpsCounterAnchorPositions.TopLeft: case FpsCounterAnchorPositions.TopLeft:
m_TextMeshPro.alignment = TextAlignmentOptions.TopLeft; m_TextMeshPro.alignment = TextAlignmentOptions.TopLeft;
m_TextMeshPro.rectTransform.pivot = new Vector2(0, 1); m_TextMeshPro.rectTransform.pivot = new Vector2(0, 1);
@ -131,4 +124,4 @@ namespace TMPro.Examples
} }
} }
} }
} }

@ -1,21 +1,17 @@
using UnityEngine; using System;
using System.Collections; using UnityEngine;
using System;
namespace TMPro namespace TMPro {
{
/// <summary> /// <summary>
/// Example of a Custom Character Input Validator to only allow phone number in the (800) 555-1212 format. /// Example of a Custom Character Input Validator to only allow phone number in the (800) 555-1212 format.
/// </summary> /// </summary>
[Serializable] [Serializable]
//[CreateAssetMenu(fileName = "InputValidator - Phone Numbers.asset", menuName = "TextMeshPro/Input Validators/Phone Numbers")] //[CreateAssetMenu(fileName = "InputValidator - Phone Numbers.asset", menuName = "TextMeshPro/Input Validators/Phone Numbers")]
public class TMP_PhoneNumberValidator : TMP_InputValidator public class TMP_PhoneNumberValidator : TMP_InputValidator {
{
// Custom text input validation function // Custom text input validation function
public override char Validate(ref string text, ref int pos, char ch) public override char Validate(ref string text, ref int pos, char ch) {
{
Debug.Log("Trying to validate..."); Debug.Log("Trying to validate...");
// Return unless the character is a valid digit // Return unless the character is a valid digit
if (ch < '0' && ch > '9') return (char)0; if (ch < '0' && ch > '9') return (char)0;
@ -23,9 +19,7 @@ namespace TMPro
// Enforce Phone Number format for every character input. // Enforce Phone Number format for every character input.
for (int i = 0; i < length + 1; i++) for (int i = 0; i < length + 1; i++)
{ switch (i) {
switch (i)
{
case 0: case 0:
if (i == length) if (i == length)
text = "(" + ch; text = "(" + ch;
@ -97,9 +91,8 @@ namespace TMPro
pos = 14; pos = 14;
break; break;
} }
}
return ch; return ch;
} }
} }
} }

@ -1,22 +1,16 @@
using UnityEngine; using UnityEngine;
namespace TMPro.Examples {
namespace TMPro.Examples public class TMP_TextEventCheck : MonoBehaviour {
{
public class TMP_TextEventCheck : MonoBehaviour
{
public TMP_TextEventHandler TextEventHandler; public TMP_TextEventHandler TextEventHandler;
private TMP_Text m_TextComponent; private TMP_Text m_TextComponent;
void OnEnable() private void OnEnable() {
{ if (TextEventHandler != null) {
if (TextEventHandler != null)
{
// Get a reference to the text component // Get a reference to the text component
m_TextComponent = TextEventHandler.GetComponent<TMP_Text>(); m_TextComponent = TextEventHandler.GetComponent<TMP_Text>();
TextEventHandler.onCharacterSelection.AddListener(OnCharacterSelection); TextEventHandler.onCharacterSelection.AddListener(OnCharacterSelection);
TextEventHandler.onSpriteSelection.AddListener(OnSpriteSelection); TextEventHandler.onSpriteSelection.AddListener(OnSpriteSelection);
TextEventHandler.onWordSelection.AddListener(OnWordSelection); TextEventHandler.onWordSelection.AddListener(OnWordSelection);
@ -26,10 +20,8 @@ namespace TMPro.Examples
} }
void OnDisable() private void OnDisable() {
{ if (TextEventHandler != null) {
if (TextEventHandler != null)
{
TextEventHandler.onCharacterSelection.RemoveListener(OnCharacterSelection); TextEventHandler.onCharacterSelection.RemoveListener(OnCharacterSelection);
TextEventHandler.onSpriteSelection.RemoveListener(OnSpriteSelection); TextEventHandler.onSpriteSelection.RemoveListener(OnSpriteSelection);
TextEventHandler.onWordSelection.RemoveListener(OnWordSelection); TextEventHandler.onWordSelection.RemoveListener(OnWordSelection);
@ -39,35 +31,31 @@ namespace TMPro.Examples
} }
void OnCharacterSelection(char c, int index) private void OnCharacterSelection(char c, int index) {
{
Debug.Log("Character [" + c + "] at Index: " + index + " has been selected."); Debug.Log("Character [" + c + "] at Index: " + index + " has been selected.");
} }
void OnSpriteSelection(char c, int index) private void OnSpriteSelection(char c, int index) {
{
Debug.Log("Sprite [" + c + "] at Index: " + index + " has been selected."); Debug.Log("Sprite [" + c + "] at Index: " + index + " has been selected.");
} }
void OnWordSelection(string word, int firstCharacterIndex, int length) private void OnWordSelection(string word, int firstCharacterIndex, int length) {
{ Debug.Log("Word [" + word + "] with first character index of " + firstCharacterIndex + " and length of " +
Debug.Log("Word [" + word + "] with first character index of " + firstCharacterIndex + " and length of " + length + " has been selected."); length + " has been selected.");
} }
void OnLineSelection(string lineText, int firstCharacterIndex, int length) private void OnLineSelection(string lineText, int firstCharacterIndex, int length) {
{ Debug.Log("Line [" + lineText + "] with first character index of " + firstCharacterIndex +
Debug.Log("Line [" + lineText + "] with first character index of " + firstCharacterIndex + " and length of " + length + " has been selected."); " and length of " + length + " has been selected.");
} }
void OnLinkSelection(string linkID, string linkText, int linkIndex) private void OnLinkSelection(string linkID, string linkText, int linkIndex) {
{ if (m_TextComponent != null) {
if (m_TextComponent != null)
{
TMP_LinkInfo linkInfo = m_TextComponent.textInfo.linkInfo[linkIndex]; TMP_LinkInfo linkInfo = m_TextComponent.textInfo.linkInfo[linkIndex];
} }
Debug.Log("Link Index: " + linkIndex + " with ID [" + linkID + "] and Text \"" + linkText + "\" has been selected.");
}
Debug.Log("Link Index: " + linkIndex + " with ID [" + linkID + "] and Text \"" + linkText +
"\" has been selected.");
}
} }
} }

@ -1,151 +1,124 @@
using UnityEngine; using System;
using UnityEngine;
using UnityEngine.Events; using UnityEngine.Events;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using System;
namespace TMPro {
public class TMP_TextEventHandler : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler {
[SerializeField] private CharacterSelectionEvent m_OnCharacterSelection = new();
namespace TMPro [SerializeField] private SpriteSelectionEvent m_OnSpriteSelection = new();
{
public class TMP_TextEventHandler : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler [SerializeField] private WordSelectionEvent m_OnWordSelection = new();
{
[Serializable]
public class CharacterSelectionEvent : UnityEvent<char, int> { }
[Serializable] [SerializeField] private LineSelectionEvent m_OnLineSelection = new();
public class SpriteSelectionEvent : UnityEvent<char, int> { }
[Serializable] [SerializeField] private LinkSelectionEvent m_OnLinkSelection = new();
public class WordSelectionEvent : UnityEvent<string, int, int> { }
[Serializable] private Camera m_Camera;
public class LineSelectionEvent : UnityEvent<string, int, int> { } private Canvas m_Canvas;
private int m_lastCharIndex = -1;
private int m_lastLineIndex = -1;
private int m_lastWordIndex = -1;
[Serializable] private int m_selectedLink = -1;
public class LinkSelectionEvent : UnityEvent<string, string, int> { }
private TMP_Text m_TextComponent;
/// <summary> /// <summary>
/// Event delegate triggered when pointer is over a character. /// Event delegate triggered when pointer is over a character.
/// </summary> /// </summary>
public CharacterSelectionEvent onCharacterSelection public CharacterSelectionEvent onCharacterSelection {
{ get => m_OnCharacterSelection;
get { return m_OnCharacterSelection; } set => m_OnCharacterSelection = value;
set { m_OnCharacterSelection = value; }
} }
[SerializeField]
private CharacterSelectionEvent m_OnCharacterSelection = new CharacterSelectionEvent();
/// <summary> /// <summary>
/// Event delegate triggered when pointer is over a sprite. /// Event delegate triggered when pointer is over a sprite.
/// </summary> /// </summary>
public SpriteSelectionEvent onSpriteSelection public SpriteSelectionEvent onSpriteSelection {
{ get => m_OnSpriteSelection;
get { return m_OnSpriteSelection; } set => m_OnSpriteSelection = value;
set { m_OnSpriteSelection = value; }
} }
[SerializeField]
private SpriteSelectionEvent m_OnSpriteSelection = new SpriteSelectionEvent();
/// <summary> /// <summary>
/// Event delegate triggered when pointer is over a word. /// Event delegate triggered when pointer is over a word.
/// </summary> /// </summary>
public WordSelectionEvent onWordSelection public WordSelectionEvent onWordSelection {
{ get => m_OnWordSelection;
get { return m_OnWordSelection; } set => m_OnWordSelection = value;
set { m_OnWordSelection = value; }
} }
[SerializeField]
private WordSelectionEvent m_OnWordSelection = new WordSelectionEvent();
/// <summary> /// <summary>
/// Event delegate triggered when pointer is over a line. /// Event delegate triggered when pointer is over a line.
/// </summary> /// </summary>
public LineSelectionEvent onLineSelection public LineSelectionEvent onLineSelection {
{ get => m_OnLineSelection;
get { return m_OnLineSelection; } set => m_OnLineSelection = value;
set { m_OnLineSelection = value; }
} }
[SerializeField]
private LineSelectionEvent m_OnLineSelection = new LineSelectionEvent();
/// <summary> /// <summary>
/// Event delegate triggered when pointer is over a link. /// Event delegate triggered when pointer is over a link.
/// </summary> /// </summary>
public LinkSelectionEvent onLinkSelection public LinkSelectionEvent onLinkSelection {
{ get => m_OnLinkSelection;
get { return m_OnLinkSelection; } set => m_OnLinkSelection = value;
set { m_OnLinkSelection = value; }
} }
[SerializeField]
private LinkSelectionEvent m_OnLinkSelection = new LinkSelectionEvent();
private TMP_Text m_TextComponent; private void Awake() {
private Camera m_Camera;
private Canvas m_Canvas;
private int m_selectedLink = -1;
private int m_lastCharIndex = -1;
private int m_lastWordIndex = -1;
private int m_lastLineIndex = -1;
void Awake()
{
// Get a reference to the text component. // Get a reference to the text component.
m_TextComponent = gameObject.GetComponent<TMP_Text>(); m_TextComponent = gameObject.GetComponent<TMP_Text>();
// Get a reference to the camera rendering the text taking into consideration the text component type. // Get a reference to the camera rendering the text taking into consideration the text component type.
if (m_TextComponent.GetType() == typeof(TextMeshProUGUI)) if (m_TextComponent.GetType() == typeof(TextMeshProUGUI)) {
{
m_Canvas = gameObject.GetComponentInParent<Canvas>(); m_Canvas = gameObject.GetComponentInParent<Canvas>();
if (m_Canvas != null) if (m_Canvas != null) {
{
if (m_Canvas.renderMode == RenderMode.ScreenSpaceOverlay) if (m_Canvas.renderMode == RenderMode.ScreenSpaceOverlay)
m_Camera = null; m_Camera = null;
else else
m_Camera = m_Canvas.worldCamera; m_Camera = m_Canvas.worldCamera;
} }
} }
else else {
{
m_Camera = Camera.main; m_Camera = Camera.main;
} }
} }
void LateUpdate() private void LateUpdate() {
{ if (TMP_TextUtilities.IsIntersectingRectTransform(m_TextComponent.rectTransform, Input.mousePosition,
if (TMP_TextUtilities.IsIntersectingRectTransform(m_TextComponent.rectTransform, Input.mousePosition, m_Camera)) m_Camera)) {
{
#region Example of Character or Sprite Selection #region Example of Character or Sprite Selection
int charIndex = TMP_TextUtilities.FindIntersectingCharacter(m_TextComponent, Input.mousePosition, m_Camera, true);
if (charIndex != -1 && charIndex != m_lastCharIndex) int charIndex =
{ TMP_TextUtilities.FindIntersectingCharacter(m_TextComponent, Input.mousePosition, m_Camera, true);
if (charIndex != -1 && charIndex != m_lastCharIndex) {
m_lastCharIndex = charIndex; m_lastCharIndex = charIndex;
TMP_TextElementType elementType = m_TextComponent.textInfo.characterInfo[charIndex].elementType; TMP_TextElementType elementType = m_TextComponent.textInfo.characterInfo[charIndex].elementType;
// Send event to any event listeners depending on whether it is a character or sprite. // Send event to any event listeners depending on whether it is a character or sprite.
if (elementType == TMP_TextElementType.Character) if (elementType == TMP_TextElementType.Character)
SendOnCharacterSelection(m_TextComponent.textInfo.characterInfo[charIndex].character, charIndex); SendOnCharacterSelection(m_TextComponent.textInfo.characterInfo[charIndex].character,
charIndex);
else if (elementType == TMP_TextElementType.Sprite) else if (elementType == TMP_TextElementType.Sprite)
SendOnSpriteSelection(m_TextComponent.textInfo.characterInfo[charIndex].character, charIndex); SendOnSpriteSelection(m_TextComponent.textInfo.characterInfo[charIndex].character, charIndex);
} }
#endregion #endregion
#region Example of Word Selection #region Example of Word Selection
// Check if Mouse intersects any words and if so assign a random color to that word. // Check if Mouse intersects any words and if so assign a random color to that word.
int wordIndex = TMP_TextUtilities.FindIntersectingWord(m_TextComponent, Input.mousePosition, m_Camera); int wordIndex = TMP_TextUtilities.FindIntersectingWord(m_TextComponent, Input.mousePosition, m_Camera);
if (wordIndex != -1 && wordIndex != m_lastWordIndex) if (wordIndex != -1 && wordIndex != m_lastWordIndex) {
{
m_lastWordIndex = wordIndex; m_lastWordIndex = wordIndex;
// Get the information about the selected word. // Get the information about the selected word.
@ -154,14 +127,15 @@ namespace TMPro
// Send the event to any listeners. // Send the event to any listeners.
SendOnWordSelection(wInfo.GetWord(), wInfo.firstCharacterIndex, wInfo.characterCount); SendOnWordSelection(wInfo.GetWord(), wInfo.firstCharacterIndex, wInfo.characterCount);
} }
#endregion #endregion
#region Example of Line Selection #region Example of Line Selection
// Check if Mouse intersects any words and if so assign a random color to that word. // Check if Mouse intersects any words and if so assign a random color to that word.
int lineIndex = TMP_TextUtilities.FindIntersectingLine(m_TextComponent, Input.mousePosition, m_Camera); int lineIndex = TMP_TextUtilities.FindIntersectingLine(m_TextComponent, Input.mousePosition, m_Camera);
if (lineIndex != -1 && lineIndex != m_lastLineIndex) if (lineIndex != -1 && lineIndex != m_lastLineIndex) {
{
m_lastLineIndex = lineIndex; m_lastLineIndex = lineIndex;
// Get the information about the selected word. // Get the information about the selected word.
@ -169,24 +143,25 @@ namespace TMPro
// Send the event to any listeners. // Send the event to any listeners.
char[] buffer = new char[lineInfo.characterCount]; char[] buffer = new char[lineInfo.characterCount];
for (int i = 0; i < lineInfo.characterCount && i < m_TextComponent.textInfo.characterInfo.Length; i++) for (int i = 0;
{ i < lineInfo.characterCount && i < m_TextComponent.textInfo.characterInfo.Length;
i++)
buffer[i] = m_TextComponent.textInfo.characterInfo[i + lineInfo.firstCharacterIndex].character; buffer[i] = m_TextComponent.textInfo.characterInfo[i + lineInfo.firstCharacterIndex].character;
}
string lineText = new string(buffer); string lineText = new(buffer);
SendOnLineSelection(lineText, lineInfo.firstCharacterIndex, lineInfo.characterCount); SendOnLineSelection(lineText, lineInfo.firstCharacterIndex, lineInfo.characterCount);
} }
#endregion #endregion
#region Example of Link Handling #region Example of Link Handling
// Check if mouse intersects with any links. // Check if mouse intersects with any links.
int linkIndex = TMP_TextUtilities.FindIntersectingLink(m_TextComponent, Input.mousePosition, m_Camera); int linkIndex = TMP_TextUtilities.FindIntersectingLink(m_TextComponent, Input.mousePosition, m_Camera);
// Handle new Link selection. // Handle new Link selection.
if (linkIndex != -1 && linkIndex != m_selectedLink) if (linkIndex != -1 && linkIndex != m_selectedLink) {
{
m_selectedLink = linkIndex; m_selectedLink = linkIndex;
// Get information about the link. // Get information about the link.
@ -195,10 +170,10 @@ namespace TMPro
// Send the event to any listeners. // Send the event to any listeners.
SendOnLinkSelection(linkInfo.GetLinkID(), linkInfo.GetLinkText(), linkIndex); SendOnLinkSelection(linkInfo.GetLinkID(), linkInfo.GetLinkText(), linkIndex);
} }
#endregion #endregion
} }
else else {
{
// Reset all selections given we are hovering outside the text container bounds. // Reset all selections given we are hovering outside the text container bounds.
m_selectedLink = -1; m_selectedLink = -1;
m_lastCharIndex = -1; m_lastCharIndex = -1;
@ -208,47 +183,54 @@ namespace TMPro
} }
public void OnPointerEnter(PointerEventData eventData) public void OnPointerEnter(PointerEventData eventData) {
{
//Debug.Log("OnPointerEnter()"); //Debug.Log("OnPointerEnter()");
} }
public void OnPointerExit(PointerEventData eventData) public void OnPointerExit(PointerEventData eventData) {
{
//Debug.Log("OnPointerExit()"); //Debug.Log("OnPointerExit()");
} }
private void SendOnCharacterSelection(char character, int characterIndex) private void SendOnCharacterSelection(char character, int characterIndex) {
{
if (onCharacterSelection != null) if (onCharacterSelection != null)
onCharacterSelection.Invoke(character, characterIndex); onCharacterSelection.Invoke(character, characterIndex);
} }
private void SendOnSpriteSelection(char character, int characterIndex) private void SendOnSpriteSelection(char character, int characterIndex) {
{
if (onSpriteSelection != null) if (onSpriteSelection != null)
onSpriteSelection.Invoke(character, characterIndex); onSpriteSelection.Invoke(character, characterIndex);
} }
private void SendOnWordSelection(string word, int charIndex, int length) private void SendOnWordSelection(string word, int charIndex, int length) {
{
if (onWordSelection != null) if (onWordSelection != null)
onWordSelection.Invoke(word, charIndex, length); onWordSelection.Invoke(word, charIndex, length);
} }
private void SendOnLineSelection(string line, int charIndex, int length) private void SendOnLineSelection(string line, int charIndex, int length) {
{
if (onLineSelection != null) if (onLineSelection != null)
onLineSelection.Invoke(line, charIndex, length); onLineSelection.Invoke(line, charIndex, length);
} }
private void SendOnLinkSelection(string linkID, string linkText, int linkIndex) private void SendOnLinkSelection(string linkID, string linkText, int linkIndex) {
{
if (onLinkSelection != null) if (onLinkSelection != null)
onLinkSelection.Invoke(linkID, linkText, linkIndex); onLinkSelection.Invoke(linkID, linkText, linkIndex);
} }
[Serializable]
public class CharacterSelectionEvent : UnityEvent<char, int> { }
[Serializable]
public class SpriteSelectionEvent : UnityEvent<char, int> { }
[Serializable]
public class WordSelectionEvent : UnityEvent<string, int, int> { }
[Serializable]
public class LineSelectionEvent : UnityEvent<string, int, int> { }
[Serializable]
public class LinkSelectionEvent : UnityEvent<string, string, int> { }
} }
} }

@ -1,29 +1,22 @@
using System; using System;
using UnityEngine;
using System.Collections;
using UnityEditor; using UnityEditor;
using UnityEngine;
namespace TMPro.Examples {
namespace TMPro.Examples public class TMP_TextInfoDebugTool : MonoBehaviour {
{
public class TMP_TextInfoDebugTool : MonoBehaviour
{
// Since this script is used for debugging, we exclude it from builds. // Since this script is used for debugging, we exclude it from builds.
// TODO: Rework this script to make it into an editor utility. // TODO: Rework this script to make it into an editor utility.
#if UNITY_EDITOR #if UNITY_EDITOR
public bool ShowCharacters; public bool ShowCharacters;
public bool ShowWords; public bool ShowWords;
public bool ShowLinks; public bool ShowLinks;
public bool ShowLines; public bool ShowLines;
public bool ShowMeshBounds; public bool ShowMeshBounds;
public bool ShowTextBounds; public bool ShowTextBounds;
[Space(10)]
[TextArea(2, 2)]
public string ObjectStats;
[SerializeField] [Space(10)] [TextArea(2, 2)] public string ObjectStats;
private TMP_Text m_TextComponent;
[SerializeField] private TMP_Text m_TextComponent;
private Transform m_Transform; private Transform m_Transform;
private TMP_TextInfo m_TextInfo; private TMP_TextInfo m_TextInfo;
@ -32,10 +25,8 @@ namespace TMPro.Examples
private float m_HandleSize; private float m_HandleSize;
void OnDrawGizmos() private void OnDrawGizmos() {
{ if (m_TextComponent == null) {
if (m_TextComponent == null)
{
m_TextComponent = GetComponent<TMP_Text>(); m_TextComponent = GetComponent<TMP_Text>();
if (m_TextComponent == null) if (m_TextComponent == null)
@ -48,7 +39,9 @@ namespace TMPro.Examples
m_TextInfo = m_TextComponent.textInfo; m_TextInfo = m_TextComponent.textInfo;
// Update Text Statistics // Update Text Statistics
ObjectStats = "Characters: " + m_TextInfo.characterCount + " Words: " + m_TextInfo.wordCount + " Spaces: " + m_TextInfo.spaceCount + " Sprites: " + m_TextInfo.spriteCount + " Links: " + m_TextInfo.linkCount ObjectStats = "Characters: " + m_TextInfo.characterCount + " Words: " + m_TextInfo.wordCount +
" Spaces: " + m_TextInfo.spaceCount + " Sprites: " + m_TextInfo.spriteCount +
" Links: " + m_TextInfo.linkCount
+ "\nLines: " + m_TextInfo.lineCount + " Pages: " + m_TextInfo.pageCount; + "\nLines: " + m_TextInfo.lineCount + " Pages: " + m_TextInfo.pageCount;
// Get the handle size for drawing the various // Get the handle size for drawing the various
@ -56,53 +49,69 @@ namespace TMPro.Examples
m_HandleSize = HandleUtility.GetHandleSize(m_Transform.position) * m_ScaleMultiplier; m_HandleSize = HandleUtility.GetHandleSize(m_Transform.position) * m_ScaleMultiplier;
// Draw line metrics // Draw line metrics
#region Draw Lines #region Draw Lines
if (ShowLines) if (ShowLines)
DrawLineBounds(); DrawLineBounds();
#endregion #endregion
// Draw word metrics // Draw word metrics
#region Draw Words #region Draw Words
if (ShowWords) if (ShowWords)
DrawWordBounds(); DrawWordBounds();
#endregion #endregion
// Draw character metrics // Draw character metrics
#region Draw Characters #region Draw Characters
if (ShowCharacters) if (ShowCharacters)
DrawCharactersBounds(); DrawCharactersBounds();
#endregion #endregion
// Draw Quads around each of the words // Draw Quads around each of the words
#region Draw Links #region Draw Links
if (ShowLinks) if (ShowLinks)
DrawLinkBounds(); DrawLinkBounds();
#endregion #endregion
// Draw Quad around the bounds of the text // Draw Quad around the bounds of the text
#region Draw Bounds #region Draw Bounds
if (ShowMeshBounds) if (ShowMeshBounds)
DrawBounds(); DrawBounds();
#endregion #endregion
// Draw Quad around the rendered region of the text. // Draw Quad around the rendered region of the text.
#region Draw Text Bounds #region Draw Text Bounds
if (ShowTextBounds) if (ShowTextBounds)
DrawTextBounds(); DrawTextBounds();
#endregion #endregion
} }
/// <summary> /// <summary>
/// Method to draw a rectangle around each character. /// Method to draw a rectangle around each character.
/// </summary> /// </summary>
/// <param name="text"></param> /// <param name="text"></param>
void DrawCharactersBounds() private void DrawCharactersBounds() {
{
int characterCount = m_TextInfo.characterCount; int characterCount = m_TextInfo.characterCount;
for (int i = 0; i < characterCount; i++) for (int i = 0; i < characterCount; i++) {
{
// Draw visible as well as invisible characters // Draw visible as well as invisible characters
TMP_CharacterInfo characterInfo = m_TextInfo.characterInfo[i]; TMP_CharacterInfo characterInfo = m_TextInfo.characterInfo[i];
@ -111,7 +120,8 @@ namespace TMPro.Examples
i >= m_TextComponent.firstVisibleCharacter; i >= m_TextComponent.firstVisibleCharacter;
if (m_TextComponent.overflowMode == TextOverflowModes.Page) if (m_TextComponent.overflowMode == TextOverflowModes.Page)
isCharacterVisible = isCharacterVisible && characterInfo.pageNumber + 1 == m_TextComponent.pageToDisplay; isCharacterVisible = isCharacterVisible &&
characterInfo.pageNumber + 1 == m_TextComponent.pageToDisplay;
if (!isCharacterVisible) if (!isCharacterVisible)
continue; continue;
@ -120,22 +130,28 @@ namespace TMPro.Examples
// Get Bottom Left and Top Right position of the current character // Get Bottom Left and Top Right position of the current character
Vector3 bottomLeft = m_Transform.TransformPoint(characterInfo.bottomLeft); Vector3 bottomLeft = m_Transform.TransformPoint(characterInfo.bottomLeft);
Vector3 topLeft = m_Transform.TransformPoint(new Vector3(characterInfo.topLeft.x, characterInfo.topLeft.y, 0)); Vector3 topLeft =
m_Transform.TransformPoint(new Vector3(characterInfo.topLeft.x, characterInfo.topLeft.y, 0));
Vector3 topRight = m_Transform.TransformPoint(characterInfo.topRight); Vector3 topRight = m_Transform.TransformPoint(characterInfo.topRight);
Vector3 bottomRight = m_Transform.TransformPoint(new Vector3(characterInfo.bottomRight.x, characterInfo.bottomRight.y, 0)); Vector3 bottomRight =
m_Transform.TransformPoint(new Vector3(characterInfo.bottomRight.x, characterInfo.bottomRight.y,
0));
// Draw character bounds // Draw character bounds
if (characterInfo.isVisible) if (characterInfo.isVisible) {
{
Color color = Color.green; Color color = Color.green;
DrawDottedRectangle(bottomLeft, topRight, color); DrawDottedRectangle(bottomLeft, topRight, color);
} }
else else {
{
Color color = Color.grey; Color color = Color.grey;
float whiteSpaceAdvance = Math.Abs(characterInfo.origin - characterInfo.xAdvance) > 0.01f ? characterInfo.xAdvance : characterInfo.origin + (characterInfo.ascender - characterInfo.descender) * 0.03f; float whiteSpaceAdvance = Math.Abs(characterInfo.origin - characterInfo.xAdvance) > 0.01f
DrawDottedRectangle(m_Transform.TransformPoint(new Vector3(characterInfo.origin, characterInfo.descender, 0)), m_Transform.TransformPoint(new Vector3(whiteSpaceAdvance, characterInfo.ascender, 0)), color, 4); ? characterInfo.xAdvance
: characterInfo.origin + (characterInfo.ascender - characterInfo.descender) * 0.03f;
DrawDottedRectangle(
m_Transform.TransformPoint(new Vector3(characterInfo.origin, characterInfo.descender, 0)),
m_Transform.TransformPoint(new Vector3(whiteSpaceAdvance, characterInfo.ascender, 0)), color,
4);
} }
float origin = characterInfo.origin; float origin = characterInfo.origin;
@ -152,16 +168,19 @@ namespace TMPro.Examples
Handles.DrawDottedLine(ascentlineStart, ascentlineEnd, dottedLineSize); Handles.DrawDottedLine(ascentlineStart, ascentlineEnd, dottedLineSize);
// Draw Cap Height & Mean line // Draw Cap Height & Mean line
float capline = characterInfo.fontAsset == null ? 0 : baseline + characterInfo.fontAsset.faceInfo.capLine * characterInfo.scale; float capline = characterInfo.fontAsset == null
Vector3 capHeightStart = new Vector3(topLeft.x, m_Transform.TransformPoint(new Vector3(0, capline, 0)).y, 0); ? 0
Vector3 capHeightEnd = new Vector3(topRight.x, m_Transform.TransformPoint(new Vector3(0, capline, 0)).y, 0); : baseline + characterInfo.fontAsset.faceInfo.capLine * characterInfo.scale;
Vector3 capHeightStart = new(topLeft.x, m_Transform.TransformPoint(new Vector3(0, capline, 0)).y, 0);
float meanline = characterInfo.fontAsset == null ? 0 : baseline + characterInfo.fontAsset.faceInfo.meanLine * characterInfo.scale; Vector3 capHeightEnd = new(topRight.x, m_Transform.TransformPoint(new Vector3(0, capline, 0)).y, 0);
Vector3 meanlineStart = new Vector3(topLeft.x, m_Transform.TransformPoint(new Vector3(0, meanline, 0)).y, 0);
Vector3 meanlineEnd = new Vector3(topRight.x, m_Transform.TransformPoint(new Vector3(0, meanline, 0)).y, 0); float meanline = characterInfo.fontAsset == null
? 0
if (characterInfo.isVisible) : baseline + characterInfo.fontAsset.faceInfo.meanLine * characterInfo.scale;
{ Vector3 meanlineStart = new(topLeft.x, m_Transform.TransformPoint(new Vector3(0, meanline, 0)).y, 0);
Vector3 meanlineEnd = new(topRight.x, m_Transform.TransformPoint(new Vector3(0, meanline, 0)).y, 0);
if (characterInfo.isVisible) {
// Cap line // Cap line
Handles.color = Color.cyan; Handles.color = Color.cyan;
Handles.DrawDottedLine(capHeightStart, capHeightEnd, dottedLineSize); Handles.DrawDottedLine(capHeightStart, capHeightEnd, dottedLineSize);
@ -195,76 +214,72 @@ namespace TMPro.Examples
DrawCrosshair(advancePosition, 0.0125f / m_ScaleMultiplier, Color.yellow); DrawCrosshair(advancePosition, 0.0125f / m_ScaleMultiplier, Color.yellow);
// Draw text labels for metrics // Draw text labels for metrics
if (m_HandleSize < 0.5f) if (m_HandleSize < 0.5f) {
{ GUIStyle style = new(GUI.skin.GetStyle("Label"));
GUIStyle style = new GUIStyle(GUI.skin.GetStyle("Label")); style.normal.textColor = new Color(0.6f, 0.6f, 0.6f, 1.0f);
style.normal.textColor = new Color(0.6f, 0.6f, 0.6f, 1.0f); style.fontSize = 12;
style.fontSize = 12; style.fixedWidth = 200;
style.fixedWidth = 200; style.fixedHeight = 20;
style.fixedHeight = 20;
Vector3 labelPosition;
Vector3 labelPosition; float center = (origin + advance) / 2;
float center = (origin + advance) / 2;
//float baselineMetrics = 0;
//float baselineMetrics = 0; //float ascentlineMetrics = ascentline - baseline;
//float ascentlineMetrics = ascentline - baseline; //float caplineMetrics = capline - baseline;
//float caplineMetrics = capline - baseline; //float meanlineMetrics = meanline - baseline;
//float meanlineMetrics = meanline - baseline; //float descentlineMetrics = descentline - baseline;
//float descentlineMetrics = descentline - baseline;
// Ascent Line
// Ascent Line labelPosition = m_Transform.TransformPoint(new Vector3(center, ascentline, 0));
labelPosition = m_Transform.TransformPoint(new Vector3(center, ascentline, 0)); style.alignment = TextAnchor.UpperCenter;
style.alignment = TextAnchor.UpperCenter; Handles.Label(labelPosition, "Ascent Line", style);
Handles.Label(labelPosition, "Ascent Line", style); //Handles.Label(labelPosition, "Ascent Line (" + ascentlineMetrics.ToString("f3") + ")" , style);
//Handles.Label(labelPosition, "Ascent Line (" + ascentlineMetrics.ToString("f3") + ")" , style);
// Base Line
// Base Line labelPosition = m_Transform.TransformPoint(new Vector3(center, baseline, 0));
labelPosition = m_Transform.TransformPoint(new Vector3(center, baseline, 0)); Handles.Label(labelPosition, "Base Line", style);
Handles.Label(labelPosition, "Base Line", style); //Handles.Label(labelPosition, "Base Line (" + baselineMetrics.ToString("f3") + ")" , style);
//Handles.Label(labelPosition, "Base Line (" + baselineMetrics.ToString("f3") + ")" , style);
// Descent line
// Descent line labelPosition = m_Transform.TransformPoint(new Vector3(center, descentline, 0));
labelPosition = m_Transform.TransformPoint(new Vector3(center, descentline, 0)); Handles.Label(labelPosition, "Descent Line", style);
Handles.Label(labelPosition, "Descent Line", style); //Handles.Label(labelPosition, "Descent Line (" + descentlineMetrics.ToString("f3") + ")" , style);
//Handles.Label(labelPosition, "Descent Line (" + descentlineMetrics.ToString("f3") + ")" , style);
if (characterInfo.isVisible) {
if (characterInfo.isVisible) // Cap Line
{ labelPosition = m_Transform.TransformPoint(new Vector3(center, capline, 0));
// Cap Line style.alignment = TextAnchor.UpperCenter;
labelPosition = m_Transform.TransformPoint(new Vector3(center, capline, 0)); Handles.Label(labelPosition, "Cap Line", style);
style.alignment = TextAnchor.UpperCenter; //Handles.Label(labelPosition, "Cap Line (" + caplineMetrics.ToString("f3") + ")" , style);
Handles.Label(labelPosition, "Cap Line", style);
//Handles.Label(labelPosition, "Cap Line (" + caplineMetrics.ToString("f3") + ")" , style); // Mean Line
labelPosition = m_Transform.TransformPoint(new Vector3(center, meanline, 0));
// Mean Line style.alignment = TextAnchor.UpperCenter;
labelPosition = m_Transform.TransformPoint(new Vector3(center, meanline, 0)); Handles.Label(labelPosition, "Mean Line", style);
style.alignment = TextAnchor.UpperCenter; //Handles.Label(labelPosition, "Mean Line (" + ascentlineMetrics.ToString("f3") + ")" , style);
Handles.Label(labelPosition, "Mean Line", style);
//Handles.Label(labelPosition, "Mean Line (" + ascentlineMetrics.ToString("f3") + ")" , style); // Origin
labelPosition = m_Transform.TransformPoint(new Vector3(origin, baseline, 0));
// Origin style.alignment = TextAnchor.UpperRight;
labelPosition = m_Transform.TransformPoint(new Vector3(origin, baseline, 0)); Handles.Label(labelPosition, "Origin ", style);
style.alignment = TextAnchor.UpperRight;
Handles.Label(labelPosition, "Origin ", style); // Advance
labelPosition = m_Transform.TransformPoint(new Vector3(advance, baseline, 0));
// Advance style.alignment = TextAnchor.UpperLeft;
labelPosition = m_Transform.TransformPoint(new Vector3(advance, baseline, 0)); Handles.Label(labelPosition, " Advance", style);
style.alignment = TextAnchor.UpperLeft; }
Handles.Label(labelPosition, " Advance", style); }
}
}
} }
} }
/// <summary> /// <summary>
/// Method to draw rectangles around each word of the text. /// Method to draw rectangles around each word of the text.
/// </summary> /// </summary>
/// <param name="text"></param> /// <param name="text"></param>
void DrawWordBounds() private void DrawWordBounds() {
{ for (int i = 0; i < m_TextInfo.wordCount; i++) {
for (int i = 0; i < m_TextInfo.wordCount; i++)
{
TMP_WordInfo wInfo = m_TextInfo.wordInfo[i]; TMP_WordInfo wInfo = m_TextInfo.wordInfo[i];
bool isBeginRegion = false; bool isBeginRegion = false;
@ -280,22 +295,23 @@ namespace TMPro.Examples
Color wordColor = Color.green; Color wordColor = Color.green;
// Iterate through each character of the word // Iterate through each character of the word
for (int j = 0; j < wInfo.characterCount; j++) for (int j = 0; j < wInfo.characterCount; j++) {
{
int characterIndex = wInfo.firstCharacterIndex + j; int characterIndex = wInfo.firstCharacterIndex + j;
TMP_CharacterInfo currentCharInfo = m_TextInfo.characterInfo[characterIndex]; TMP_CharacterInfo currentCharInfo = m_TextInfo.characterInfo[characterIndex];
int currentLine = currentCharInfo.lineNumber; int currentLine = currentCharInfo.lineNumber;
bool isCharacterVisible = characterIndex > m_TextComponent.maxVisibleCharacters || bool isCharacterVisible = characterIndex > m_TextComponent.maxVisibleCharacters ||
currentCharInfo.lineNumber > m_TextComponent.maxVisibleLines || currentCharInfo.lineNumber > m_TextComponent.maxVisibleLines ||
(m_TextComponent.overflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != m_TextComponent.pageToDisplay) ? false : true; (m_TextComponent.overflowMode == TextOverflowModes.Page &&
currentCharInfo.pageNumber + 1 != m_TextComponent.pageToDisplay)
? false
: true;
// Track Max Ascender and Min Descender // Track Max Ascender and Min Descender
maxAscender = Mathf.Max(maxAscender, currentCharInfo.ascender); maxAscender = Mathf.Max(maxAscender, currentCharInfo.ascender);
minDescender = Mathf.Min(minDescender, currentCharInfo.descender); minDescender = Mathf.Min(minDescender, currentCharInfo.descender);
if (isBeginRegion == false && isCharacterVisible) if (isBeginRegion == false && isCharacterVisible) {
{
isBeginRegion = true; isBeginRegion = true;
bottomLeft = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0); bottomLeft = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0);
@ -304,14 +320,15 @@ namespace TMPro.Examples
//Debug.Log("Start Word Region at [" + currentCharInfo.character + "]"); //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
// If Word is one character // If Word is one character
if (wInfo.characterCount == 1) if (wInfo.characterCount == 1) {
{
isBeginRegion = false; isBeginRegion = false;
topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0)); topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0));
bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0)); bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0));
bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0)); bottomRight =
topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0)); m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0));
topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender,
0));
// Draw Region // Draw Region
DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, wordColor); DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, wordColor);
@ -321,13 +338,13 @@ namespace TMPro.Examples
} }
// Last Character of Word // Last Character of Word
if (isBeginRegion && j == wInfo.characterCount - 1) if (isBeginRegion && j == wInfo.characterCount - 1) {
{
isBeginRegion = false; isBeginRegion = false;
topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0)); topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0));
bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0)); bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0));
bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0)); bottomRight =
m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0));
topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0)); topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0));
// Draw Region // Draw Region
@ -336,13 +353,13 @@ namespace TMPro.Examples
//Debug.Log("End Word Region at [" + currentCharInfo.character + "]"); //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
} }
// If Word is split on more than one line. // If Word is split on more than one line.
else if (isBeginRegion && currentLine != m_TextInfo.characterInfo[characterIndex + 1].lineNumber) else if (isBeginRegion && currentLine != m_TextInfo.characterInfo[characterIndex + 1].lineNumber) {
{
isBeginRegion = false; isBeginRegion = false;
topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0)); topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0));
bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0)); bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0));
bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0)); bottomRight =
m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0));
topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0)); topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0));
// Draw Region // Draw Region
@ -350,27 +367,22 @@ namespace TMPro.Examples
//Debug.Log("End Word Region at [" + currentCharInfo.character + "]"); //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
maxAscender = -Mathf.Infinity; maxAscender = -Mathf.Infinity;
minDescender = Mathf.Infinity; minDescender = Mathf.Infinity;
} }
} }
//Debug.Log(wInfo.GetWord(m_TextMeshPro.textInfo.characterInfo)); //Debug.Log(wInfo.GetWord(m_TextMeshPro.textInfo.characterInfo));
} }
} }
/// <summary> /// <summary>
/// Draw rectangle around each of the links contained in the text. /// Draw rectangle around each of the links contained in the text.
/// </summary> /// </summary>
/// <param name="text"></param> /// <param name="text"></param>
void DrawLinkBounds() private void DrawLinkBounds() {
{
TMP_TextInfo textInfo = m_TextComponent.textInfo; TMP_TextInfo textInfo = m_TextComponent.textInfo;
for (int i = 0; i < textInfo.linkCount; i++) for (int i = 0; i < textInfo.linkCount; i++) {
{
TMP_LinkInfo linkInfo = textInfo.linkInfo[i]; TMP_LinkInfo linkInfo = textInfo.linkInfo[i];
bool isBeginRegion = false; bool isBeginRegion = false;
@ -386,22 +398,23 @@ namespace TMPro.Examples
Color32 linkColor = Color.cyan; Color32 linkColor = Color.cyan;
// Iterate through each character of the link text // Iterate through each character of the link text
for (int j = 0; j < linkInfo.linkTextLength; j++) for (int j = 0; j < linkInfo.linkTextLength; j++) {
{
int characterIndex = linkInfo.linkTextfirstCharacterIndex + j; int characterIndex = linkInfo.linkTextfirstCharacterIndex + j;
TMP_CharacterInfo currentCharInfo = textInfo.characterInfo[characterIndex]; TMP_CharacterInfo currentCharInfo = textInfo.characterInfo[characterIndex];
int currentLine = currentCharInfo.lineNumber; int currentLine = currentCharInfo.lineNumber;
bool isCharacterVisible = characterIndex > m_TextComponent.maxVisibleCharacters || bool isCharacterVisible = characterIndex > m_TextComponent.maxVisibleCharacters ||
currentCharInfo.lineNumber > m_TextComponent.maxVisibleLines || currentCharInfo.lineNumber > m_TextComponent.maxVisibleLines ||
(m_TextComponent.overflowMode == TextOverflowModes.Page && currentCharInfo.pageNumber + 1 != m_TextComponent.pageToDisplay) ? false : true; (m_TextComponent.overflowMode == TextOverflowModes.Page &&
currentCharInfo.pageNumber + 1 != m_TextComponent.pageToDisplay)
? false
: true;
// Track Max Ascender and Min Descender // Track Max Ascender and Min Descender
maxAscender = Mathf.Max(maxAscender, currentCharInfo.ascender); maxAscender = Mathf.Max(maxAscender, currentCharInfo.ascender);
minDescender = Mathf.Min(minDescender, currentCharInfo.descender); minDescender = Mathf.Min(minDescender, currentCharInfo.descender);
if (isBeginRegion == false && isCharacterVisible) if (isBeginRegion == false && isCharacterVisible) {
{
isBeginRegion = true; isBeginRegion = true;
bottomLeft = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0); bottomLeft = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0);
@ -410,14 +423,15 @@ namespace TMPro.Examples
//Debug.Log("Start Word Region at [" + currentCharInfo.character + "]"); //Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
// If Link is one character // If Link is one character
if (linkInfo.linkTextLength == 1) if (linkInfo.linkTextLength == 1) {
{
isBeginRegion = false; isBeginRegion = false;
topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0)); topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0));
bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0)); bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0));
bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0)); bottomRight =
topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0)); m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0));
topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender,
0));
// Draw Region // Draw Region
DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, linkColor); DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, linkColor);
@ -427,13 +441,13 @@ namespace TMPro.Examples
} }
// Last Character of Link // Last Character of Link
if (isBeginRegion && j == linkInfo.linkTextLength - 1) if (isBeginRegion && j == linkInfo.linkTextLength - 1) {
{
isBeginRegion = false; isBeginRegion = false;
topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0)); topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0));
bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0)); bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0));
bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0)); bottomRight =
m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0));
topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0)); topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0));
// Draw Region // Draw Region
@ -442,13 +456,13 @@ namespace TMPro.Examples
//Debug.Log("End Word Region at [" + currentCharInfo.character + "]"); //Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
} }
// If Link is split on more than one line. // If Link is split on more than one line.
else if (isBeginRegion && currentLine != textInfo.characterInfo[characterIndex + 1].lineNumber) else if (isBeginRegion && currentLine != textInfo.characterInfo[characterIndex + 1].lineNumber) {
{
isBeginRegion = false; isBeginRegion = false;
topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0)); topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0));
bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0)); bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.x, minDescender, 0));
bottomRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0)); bottomRight =
m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0));
topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0)); topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender, 0));
// Draw Region // Draw Region
@ -466,22 +480,26 @@ namespace TMPro.Examples
/// <summary> /// <summary>
/// Draw Rectangles around each lines of the text. /// Draw Rectangles around each lines of the text.
/// </summary> /// </summary>
/// <param name="text"></param> /// <param name="text"></param>
void DrawLineBounds() private void DrawLineBounds() {
{
int lineCount = m_TextInfo.lineCount; int lineCount = m_TextInfo.lineCount;
for (int i = 0; i < lineCount; i++) for (int i = 0; i < lineCount; i++) {
{
TMP_LineInfo lineInfo = m_TextInfo.lineInfo[i]; TMP_LineInfo lineInfo = m_TextInfo.lineInfo[i];
TMP_CharacterInfo firstCharacterInfo = m_TextInfo.characterInfo[lineInfo.firstCharacterIndex]; TMP_CharacterInfo firstCharacterInfo = m_TextInfo.characterInfo[lineInfo.firstCharacterIndex];
TMP_CharacterInfo lastCharacterInfo = m_TextInfo.characterInfo[lineInfo.lastCharacterIndex]; TMP_CharacterInfo lastCharacterInfo = m_TextInfo.characterInfo[lineInfo.lastCharacterIndex];
bool isLineVisible = (lineInfo.characterCount == 1 && (firstCharacterInfo.character == 10 || firstCharacterInfo.character == 11 || firstCharacterInfo.character == 0x2028 || firstCharacterInfo.character == 0x2029)) || bool isLineVisible = (lineInfo.characterCount == 1 && (firstCharacterInfo.character == 10 ||
i > m_TextComponent.maxVisibleLines || firstCharacterInfo.character == 11 ||
(m_TextComponent.overflowMode == TextOverflowModes.Page && firstCharacterInfo.pageNumber + 1 != m_TextComponent.pageToDisplay) ? false : true; firstCharacterInfo.character == 0x2028 ||
firstCharacterInfo.character == 0x2029)) ||
i > m_TextComponent.maxVisibleLines ||
(m_TextComponent.overflowMode == TextOverflowModes.Page &&
firstCharacterInfo.pageNumber + 1 != m_TextComponent.pageToDisplay)
? false
: true;
if (!isLineVisible) continue; if (!isLineVisible) continue;
@ -495,7 +513,8 @@ namespace TMPro.Examples
float dottedLineSize = 12; float dottedLineSize = 12;
// Draw line extents // Draw line extents
DrawDottedRectangle(m_Transform.TransformPoint(lineInfo.lineExtents.min), m_Transform.TransformPoint(lineInfo.lineExtents.max), Color.green, 4); DrawDottedRectangle(m_Transform.TransformPoint(lineInfo.lineExtents.min),
m_Transform.TransformPoint(lineInfo.lineExtents.max), Color.green, 4);
// Draw Ascent line // Draw Ascent line
Vector3 ascentlineStart = m_Transform.TransformPoint(new Vector3(lineBottomLeft, ascentline, 0)); Vector3 ascentlineStart = m_Transform.TransformPoint(new Vector3(lineBottomLeft, ascentline, 0));
@ -519,9 +538,8 @@ namespace TMPro.Examples
Handles.DrawDottedLine(descentLineStart, descentLineEnd, dottedLineSize); Handles.DrawDottedLine(descentLineStart, descentLineEnd, dottedLineSize);
// Draw text labels for metrics // Draw text labels for metrics
if (m_HandleSize < 1.0f) if (m_HandleSize < 1.0f) {
{ GUIStyle style = new();
GUIStyle style = new GUIStyle();
style.normal.textColor = new Color(0.8f, 0.8f, 0.8f, 1.0f); style.normal.textColor = new Color(0.8f, 0.8f, 0.8f, 1.0f);
style.fontSize = 12; style.fontSize = 12;
style.fixedWidth = 200; style.fixedWidth = 200;
@ -547,10 +565,9 @@ namespace TMPro.Examples
/// <summary> /// <summary>
/// Draw Rectangle around the bounds of the text object. /// Draw Rectangle around the bounds of the text object.
/// </summary> /// </summary>
void DrawBounds() private void DrawBounds() {
{
Bounds meshBounds = m_TextComponent.bounds; Bounds meshBounds = m_TextComponent.bounds;
// Get Bottom Left and Top Right position of each word // Get Bottom Left and Top Right position of each word
@ -561,8 +578,7 @@ namespace TMPro.Examples
} }
void DrawTextBounds() private void DrawTextBounds() {
{
Bounds textBounds = m_TextComponent.textBounds; Bounds textBounds = m_TextComponent.textBounds;
Vector3 bottomLeft = m_TextComponent.transform.position + (textBounds.center - textBounds.extents); Vector3 bottomLeft = m_TextComponent.transform.position + (textBounds.center - textBounds.extents);
@ -573,8 +589,7 @@ namespace TMPro.Examples
// Draw Rectangles // Draw Rectangles
void DrawRectangle(Vector3 BL, Vector3 TR, Color color) private void DrawRectangle(Vector3 BL, Vector3 TR, Color color) {
{
Gizmos.color = color; Gizmos.color = color;
Gizmos.DrawLine(new Vector3(BL.x, BL.y, 0), new Vector3(BL.x, TR.y, 0)); Gizmos.DrawLine(new Vector3(BL.x, BL.y, 0), new Vector3(BL.x, TR.y, 0));
@ -583,8 +598,7 @@ namespace TMPro.Examples
Gizmos.DrawLine(new Vector3(TR.x, BL.y, 0), new Vector3(BL.x, BL.y, 0)); Gizmos.DrawLine(new Vector3(TR.x, BL.y, 0), new Vector3(BL.x, BL.y, 0));
} }
void DrawDottedRectangle(Vector3 bottomLeft, Vector3 topRight, Color color, float size = 5.0f) private void DrawDottedRectangle(Vector3 bottomLeft, Vector3 topRight, Color color, float size = 5.0f) {
{
Handles.color = color; Handles.color = color;
Handles.DrawDottedLine(bottomLeft, new Vector3(bottomLeft.x, topRight.y, bottomLeft.z), size); Handles.DrawDottedLine(bottomLeft, new Vector3(bottomLeft.x, topRight.y, bottomLeft.z), size);
Handles.DrawDottedLine(new Vector3(bottomLeft.x, topRight.y, bottomLeft.z), topRight, size); Handles.DrawDottedLine(new Vector3(bottomLeft.x, topRight.y, bottomLeft.z), topRight, size);
@ -592,20 +606,18 @@ namespace TMPro.Examples
Handles.DrawDottedLine(new Vector3(topRight.x, bottomLeft.y, bottomLeft.z), bottomLeft, size); Handles.DrawDottedLine(new Vector3(topRight.x, bottomLeft.y, bottomLeft.z), bottomLeft, size);
} }
void DrawSolidRectangle(Vector3 bottomLeft, Vector3 topRight, Color color, float size = 5.0f) private void DrawSolidRectangle(Vector3 bottomLeft, Vector3 topRight, Color color, float size = 5.0f) {
{
Handles.color = color; Handles.color = color;
Rect rect = new Rect(bottomLeft, topRight - bottomLeft); Rect rect = new(bottomLeft, topRight - bottomLeft);
Handles.DrawSolidRectangleWithOutline(rect, color, Color.black); Handles.DrawSolidRectangleWithOutline(rect, color, Color.black);
} }
void DrawSquare(Vector3 position, float size, Color color) private void DrawSquare(Vector3 position, float size, Color color) {
{
Handles.color = color; Handles.color = color;
Vector3 bottomLeft = new Vector3(position.x - size, position.y - size, position.z); Vector3 bottomLeft = new(position.x - size, position.y - size, position.z);
Vector3 topLeft = new Vector3(position.x - size, position.y + size, position.z); Vector3 topLeft = new(position.x - size, position.y + size, position.z);
Vector3 topRight = new Vector3(position.x + size, position.y + size, position.z); Vector3 topRight = new(position.x + size, position.y + size, position.z);
Vector3 bottomRight = new Vector3(position.x + size, position.y - size, position.z); Vector3 bottomRight = new(position.x + size, position.y - size, position.z);
Handles.DrawLine(bottomLeft, topLeft); Handles.DrawLine(bottomLeft, topLeft);
Handles.DrawLine(topLeft, topRight); Handles.DrawLine(topLeft, topRight);
@ -613,18 +625,18 @@ namespace TMPro.Examples
Handles.DrawLine(bottomRight, bottomLeft); Handles.DrawLine(bottomRight, bottomLeft);
} }
void DrawCrosshair(Vector3 position, float size, Color color) private void DrawCrosshair(Vector3 position, float size, Color color) {
{
Handles.color = color; Handles.color = color;
Handles.DrawLine(new Vector3(position.x - size, position.y, position.z), new Vector3(position.x + size, position.y, position.z)); Handles.DrawLine(new Vector3(position.x - size, position.y, position.z),
Handles.DrawLine(new Vector3(position.x, position.y - size, position.z), new Vector3(position.x, position.y + size, position.z)); new Vector3(position.x + size, position.y, position.z));
Handles.DrawLine(new Vector3(position.x, position.y - size, position.z),
new Vector3(position.x, position.y + size, position.z));
} }
// Draw Rectangles // Draw Rectangles
void DrawRectangle(Vector3 bl, Vector3 tl, Vector3 tr, Vector3 br, Color color) private void DrawRectangle(Vector3 bl, Vector3 tl, Vector3 tr, Vector3 br, Color color) {
{
Gizmos.color = color; Gizmos.color = color;
Gizmos.DrawLine(bl, tl); Gizmos.DrawLine(bl, tl);
@ -635,18 +647,16 @@ namespace TMPro.Examples
// Draw Rectangles // Draw Rectangles
void DrawDottedRectangle(Vector3 bl, Vector3 tl, Vector3 tr, Vector3 br, Color color) private void DrawDottedRectangle(Vector3 bl, Vector3 tl, Vector3 tr, Vector3 br, Color color) {
{ Camera cam = Camera.current;
var cam = Camera.current;
float dotSpacing = (cam.WorldToScreenPoint(br).x - cam.WorldToScreenPoint(bl).x) / 75f; float dotSpacing = (cam.WorldToScreenPoint(br).x - cam.WorldToScreenPoint(bl).x) / 75f;
UnityEditor.Handles.color = color; Handles.color = color;
UnityEditor.Handles.DrawDottedLine(bl, tl, dotSpacing); Handles.DrawDottedLine(bl, tl, dotSpacing);
UnityEditor.Handles.DrawDottedLine(tl, tr, dotSpacing); Handles.DrawDottedLine(tl, tr, dotSpacing);
UnityEditor.Handles.DrawDottedLine(tr, br, dotSpacing); Handles.DrawDottedLine(tr, br, dotSpacing);
UnityEditor.Handles.DrawDottedLine(br, bl, dotSpacing); Handles.DrawDottedLine(br, bl, dotSpacing);
} }
#endif #endif
} }
} }

@ -1,24 +1,17 @@
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using System.Collections;
namespace TMPro.Examples
{
public class TMP_TextSelector_A : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
private TextMeshPro m_TextMeshPro;
namespace TMPro.Examples {
public class TMP_TextSelector_A : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler {
private Camera m_Camera; private Camera m_Camera;
private bool m_isHoveringObject; private bool m_isHoveringObject;
private int m_selectedLink = -1;
private int m_lastCharIndex = -1; private int m_lastCharIndex = -1;
private int m_lastWordIndex = -1; private int m_lastWordIndex = -1;
private int m_selectedLink = -1;
private TextMeshPro m_TextMeshPro;
void Awake() private void Awake() {
{
m_TextMeshPro = gameObject.GetComponent<TextMeshPro>(); m_TextMeshPro = gameObject.GetComponent<TextMeshPro>();
m_Camera = Camera.main; m_Camera = Camera.main;
@ -27,21 +20,19 @@ namespace TMPro.Examples
} }
void LateUpdate() private void LateUpdate() {
{
m_isHoveringObject = false; m_isHoveringObject = false;
if (TMP_TextUtilities.IsIntersectingRectTransform(m_TextMeshPro.rectTransform, Input.mousePosition, Camera.main)) if (TMP_TextUtilities.IsIntersectingRectTransform(m_TextMeshPro.rectTransform, Input.mousePosition,
{ Camera.main)) m_isHoveringObject = true;
m_isHoveringObject = true;
}
if (m_isHoveringObject) if (m_isHoveringObject) {
{
#region Example of Character Selection #region Example of Character Selection
int charIndex = TMP_TextUtilities.FindIntersectingCharacter(m_TextMeshPro, Input.mousePosition, Camera.main, true);
if (charIndex != -1 && charIndex != m_lastCharIndex && (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))) int charIndex =
{ TMP_TextUtilities.FindIntersectingCharacter(m_TextMeshPro, Input.mousePosition, Camera.main, true);
if (charIndex != -1 && charIndex != m_lastCharIndex &&
(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))) {
//Debug.Log("[" + m_TextMeshPro.textInfo.characterInfo[charIndex].character + "] has been selected."); //Debug.Log("[" + m_TextMeshPro.textInfo.characterInfo[charIndex].character + "] has been selected.");
m_lastCharIndex = charIndex; m_lastCharIndex = charIndex;
@ -50,7 +41,8 @@ namespace TMPro.Examples
int vertexIndex = m_TextMeshPro.textInfo.characterInfo[charIndex].vertexIndex; int vertexIndex = m_TextMeshPro.textInfo.characterInfo[charIndex].vertexIndex;
Color32 c = new Color32((byte)Random.Range(0, 255), (byte)Random.Range(0, 255), (byte)Random.Range(0, 255), 255); Color32 c = new((byte)Random.Range(0, 255), (byte)Random.Range(0, 255), (byte)Random.Range(0, 255),
255);
Color32[] vertexColors = m_TextMeshPro.textInfo.meshInfo[meshIndex].colors32; Color32[] vertexColors = m_TextMeshPro.textInfo.meshInfo[meshIndex].colors32;
@ -62,22 +54,21 @@ namespace TMPro.Examples
//m_TextMeshPro.mesh.colors32 = vertexColors; //m_TextMeshPro.mesh.colors32 = vertexColors;
m_TextMeshPro.textInfo.meshInfo[meshIndex].mesh.colors32 = vertexColors; m_TextMeshPro.textInfo.meshInfo[meshIndex].mesh.colors32 = vertexColors;
} }
#endregion #endregion
#region Example of Link Handling #region Example of Link Handling
// Check if mouse intersects with any links. // Check if mouse intersects with any links.
int linkIndex = TMP_TextUtilities.FindIntersectingLink(m_TextMeshPro, Input.mousePosition, m_Camera); int linkIndex = TMP_TextUtilities.FindIntersectingLink(m_TextMeshPro, Input.mousePosition, m_Camera);
// Clear previous link selection if one existed. // Clear previous link selection if one existed.
if ((linkIndex == -1 && m_selectedLink != -1) || linkIndex != m_selectedLink) if ((linkIndex == -1 && m_selectedLink != -1) || linkIndex != m_selectedLink)
{
//m_TextPopup_RectTransform.gameObject.SetActive(false); //m_TextPopup_RectTransform.gameObject.SetActive(false);
m_selectedLink = -1; m_selectedLink = -1;
}
// Handle new Link selection. // Handle new Link selection.
if (linkIndex != -1 && linkIndex != m_selectedLink) if (linkIndex != -1 && linkIndex != m_selectedLink) {
{
m_selectedLink = linkIndex; m_selectedLink = linkIndex;
TMP_LinkInfo linkInfo = m_TextMeshPro.textInfo.linkInfo[linkIndex]; TMP_LinkInfo linkInfo = m_TextMeshPro.textInfo.linkInfo[linkIndex];
@ -87,45 +78,48 @@ namespace TMPro.Examples
Vector3 worldPointInRectangle; Vector3 worldPointInRectangle;
RectTransformUtility.ScreenPointToWorldPointInRectangle(m_TextMeshPro.rectTransform, Input.mousePosition, m_Camera, out worldPointInRectangle); RectTransformUtility.ScreenPointToWorldPointInRectangle(m_TextMeshPro.rectTransform,
Input.mousePosition, m_Camera, out worldPointInRectangle);
switch (linkInfo.GetLinkID()) switch (linkInfo.GetLinkID()) {
{
case "id_01": // 100041637: // id_01 case "id_01": // 100041637: // id_01
//m_TextPopup_RectTransform.position = worldPointInRectangle; //m_TextPopup_RectTransform.position = worldPointInRectangle;
//m_TextPopup_RectTransform.gameObject.SetActive(true); //m_TextPopup_RectTransform.gameObject.SetActive(true);
//m_TextPopup_TMPComponent.text = k_LinkText + " ID 01"; //m_TextPopup_TMPComponent.text = k_LinkText + " ID 01";
break; break;
case "id_02": // 100041638: // id_02 case "id_02": // 100041638: // id_02
//m_TextPopup_RectTransform.position = worldPointInRectangle; //m_TextPopup_RectTransform.position = worldPointInRectangle;
//m_TextPopup_RectTransform.gameObject.SetActive(true); //m_TextPopup_RectTransform.gameObject.SetActive(true);
//m_TextPopup_TMPComponent.text = k_LinkText + " ID 02"; //m_TextPopup_TMPComponent.text = k_LinkText + " ID 02";
break; break;
} }
} }
#endregion #endregion
#region Example of Word Selection #region Example of Word Selection
// Check if Mouse intersects any words and if so assign a random color to that word. // Check if Mouse intersects any words and if so assign a random color to that word.
int wordIndex = TMP_TextUtilities.FindIntersectingWord(m_TextMeshPro, Input.mousePosition, Camera.main); int wordIndex = TMP_TextUtilities.FindIntersectingWord(m_TextMeshPro, Input.mousePosition, Camera.main);
if (wordIndex != -1 && wordIndex != m_lastWordIndex) if (wordIndex != -1 && wordIndex != m_lastWordIndex) {
{
m_lastWordIndex = wordIndex; m_lastWordIndex = wordIndex;
TMP_WordInfo wInfo = m_TextMeshPro.textInfo.wordInfo[wordIndex]; TMP_WordInfo wInfo = m_TextMeshPro.textInfo.wordInfo[wordIndex];
Vector3 wordPOS = m_TextMeshPro.transform.TransformPoint(m_TextMeshPro.textInfo.characterInfo[wInfo.firstCharacterIndex].bottomLeft); Vector3 wordPOS = m_TextMeshPro.transform.TransformPoint(m_TextMeshPro.textInfo
.characterInfo[wInfo.firstCharacterIndex].bottomLeft);
wordPOS = Camera.main.WorldToScreenPoint(wordPOS); wordPOS = Camera.main.WorldToScreenPoint(wordPOS);
//Debug.Log("Mouse Position: " + Input.mousePosition.ToString("f3") + " Word Position: " + wordPOS.ToString("f3")); //Debug.Log("Mouse Position: " + Input.mousePosition.ToString("f3") + " Word Position: " + wordPOS.ToString("f3"));
Color32[] vertexColors = m_TextMeshPro.textInfo.meshInfo[0].colors32; Color32[] vertexColors = m_TextMeshPro.textInfo.meshInfo[0].colors32;
Color32 c = new Color32((byte)Random.Range(0, 255), (byte)Random.Range(0, 255), (byte)Random.Range(0, 255), 255); Color32 c = new((byte)Random.Range(0, 255), (byte)Random.Range(0, 255), (byte)Random.Range(0, 255),
for (int i = 0; i < wInfo.characterCount; i++) 255);
{ for (int i = 0; i < wInfo.characterCount; i++) {
int vertexIndex = m_TextMeshPro.textInfo.characterInfo[wInfo.firstCharacterIndex + i].vertexIndex; int vertexIndex = m_TextMeshPro.textInfo.characterInfo[wInfo.firstCharacterIndex + i]
.vertexIndex;
vertexColors[vertexIndex + 0] = c; vertexColors[vertexIndex + 0] = c;
vertexColors[vertexIndex + 1] = c; vertexColors[vertexIndex + 1] = c;
@ -135,23 +129,21 @@ namespace TMPro.Examples
m_TextMeshPro.mesh.colors32 = vertexColors; m_TextMeshPro.mesh.colors32 = vertexColors;
} }
#endregion #endregion
} }
} }
public void OnPointerEnter(PointerEventData eventData) public void OnPointerEnter(PointerEventData eventData) {
{
Debug.Log("OnPointerEnter()"); Debug.Log("OnPointerEnter()");
m_isHoveringObject = true; m_isHoveringObject = true;
} }
public void OnPointerExit(PointerEventData eventData) public void OnPointerExit(PointerEventData eventData) {
{
Debug.Log("OnPointerExit()"); Debug.Log("OnPointerExit()");
m_isHoveringObject = false; m_isHoveringObject = false;
} }
} }
} }

@ -1,41 +1,34 @@
using UnityEngine; using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using System.Collections;
using System.Collections.Generic;
#pragma warning disable 0618 // Disabled warning due to SetVertices being deprecated until new release with SetMesh() is available. #pragma warning disable 0618 // Disabled warning due to SetVertices being deprecated until new release with SetMesh() is available.
namespace TMPro.Examples namespace TMPro.Examples {
{ public class TMP_TextSelector_B : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler,
IPointerUpHandler {
public class TMP_TextSelector_B : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler, IPointerUpHandler
{
public RectTransform TextPopup_Prefab_01;
private RectTransform m_TextPopup_RectTransform;
private TextMeshProUGUI m_TextPopup_TMPComponent;
private const string k_LinkText = "You have selected link <#ffff00>"; private const string k_LinkText = "You have selected link <#ffff00>";
private const string k_WordText = "Word Index: <#ffff00>"; private const string k_WordText = "Word Index: <#ffff00>";
public RectTransform TextPopup_Prefab_01;
private TextMeshProUGUI m_TextMeshPro;
private Canvas m_Canvas;
private Camera m_Camera;
// Flags // Flags
private bool isHoveringObject; private bool isHoveringObject;
private int m_selectedWord = -1;
private int m_selectedLink = -1; private TMP_MeshInfo[] m_cachedMeshInfoVertexData;
private Camera m_Camera;
private Canvas m_Canvas;
private int m_lastIndex = -1; private int m_lastIndex = -1;
private Matrix4x4 m_matrix; private Matrix4x4 m_matrix;
private int m_selectedLink = -1;
private int m_selectedWord = -1;
private TMP_MeshInfo[] m_cachedMeshInfoVertexData;
void Awake() private TextMeshProUGUI m_TextMeshPro;
{
private RectTransform m_TextPopup_RectTransform;
private TextMeshProUGUI m_TextPopup_TMPComponent;
private void Awake() {
m_TextMeshPro = gameObject.GetComponent<TextMeshProUGUI>(); m_TextMeshPro = gameObject.GetComponent<TextMeshProUGUI>();
@ -48,53 +41,30 @@ namespace TMPro.Examples
m_Camera = m_Canvas.worldCamera; m_Camera = m_Canvas.worldCamera;
// Create pop-up text object which is used to show the link information. // Create pop-up text object which is used to show the link information.
m_TextPopup_RectTransform = Instantiate(TextPopup_Prefab_01) as RectTransform; m_TextPopup_RectTransform = Instantiate(TextPopup_Prefab_01);
m_TextPopup_RectTransform.SetParent(m_Canvas.transform, false); m_TextPopup_RectTransform.SetParent(m_Canvas.transform, false);
m_TextPopup_TMPComponent = m_TextPopup_RectTransform.GetComponentInChildren<TextMeshProUGUI>(); m_TextPopup_TMPComponent = m_TextPopup_RectTransform.GetComponentInChildren<TextMeshProUGUI>();
m_TextPopup_RectTransform.gameObject.SetActive(false); m_TextPopup_RectTransform.gameObject.SetActive(false);
} }
void OnEnable() private void LateUpdate() {
{ if (isHoveringObject) {
// Subscribe to event fired when text object has been regenerated.
TMPro_EventManager.TEXT_CHANGED_EVENT.Add(ON_TEXT_CHANGED);
}
void OnDisable()
{
// UnSubscribe to event fired when text object has been regenerated.
TMPro_EventManager.TEXT_CHANGED_EVENT.Remove(ON_TEXT_CHANGED);
}
void ON_TEXT_CHANGED(Object obj)
{
if (obj == m_TextMeshPro)
{
// Update cached vertex data.
m_cachedMeshInfoVertexData = m_TextMeshPro.textInfo.CopyMeshInfoVertexData();
}
}
void LateUpdate()
{
if (isHoveringObject)
{
// Check if Mouse Intersects any of the characters. If so, assign a random color. // Check if Mouse Intersects any of the characters. If so, assign a random color.
#region Handle Character Selection #region Handle Character Selection
int charIndex = TMP_TextUtilities.FindIntersectingCharacter(m_TextMeshPro, Input.mousePosition, m_Camera, true);
int charIndex =
TMP_TextUtilities.FindIntersectingCharacter(m_TextMeshPro, Input.mousePosition, m_Camera, true);
// Undo Swap and Vertex Attribute changes. // Undo Swap and Vertex Attribute changes.
if (charIndex == -1 || charIndex != m_lastIndex) if (charIndex == -1 || charIndex != m_lastIndex) {
{
RestoreCachedVertexAttributes(m_lastIndex); RestoreCachedVertexAttributes(m_lastIndex);
m_lastIndex = -1; m_lastIndex = -1;
} }
if (charIndex != -1 && charIndex != m_lastIndex && (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))) if (charIndex != -1 && charIndex != m_lastIndex &&
{ (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))) {
m_lastIndex = charIndex; m_lastIndex = charIndex;
// Get the index of the material / sub text object used by this character. // Get the index of the material / sub text object used by this character.
@ -137,7 +107,7 @@ namespace TMPro.Examples
vertices[vertexIndex + 3] = vertices[vertexIndex + 3] + offset; vertices[vertexIndex + 3] = vertices[vertexIndex + 3] + offset;
// Change Vertex Colors of the highlighted character // Change Vertex Colors of the highlighted character
Color32 c = new Color32(255, 255, 192, 255); Color32 c = new(255, 255, 192, 255);
// Get a reference to the vertex color // Get a reference to the vertex color
Color32[] vertexColors = m_TextMeshPro.textInfo.meshInfo[materialIndex].colors32; Color32[] vertexColors = m_TextMeshPro.textInfo.meshInfo[materialIndex].colors32;
@ -161,21 +131,22 @@ namespace TMPro.Examples
// Need to update the appropriate // Need to update the appropriate
m_TextMeshPro.UpdateVertexData(TMP_VertexDataUpdateFlags.All); m_TextMeshPro.UpdateVertexData(TMP_VertexDataUpdateFlags.All);
} }
#endregion #endregion
#region Word Selection Handling #region Word Selection Handling
//Check if Mouse intersects any words and if so assign a random color to that word. //Check if Mouse intersects any words and if so assign a random color to that word.
int wordIndex = TMP_TextUtilities.FindIntersectingWord(m_TextMeshPro, Input.mousePosition, m_Camera); int wordIndex = TMP_TextUtilities.FindIntersectingWord(m_TextMeshPro, Input.mousePosition, m_Camera);
// Clear previous word selection. // Clear previous word selection.
if (m_TextPopup_RectTransform != null && m_selectedWord != -1 && (wordIndex == -1 || wordIndex != m_selectedWord)) if (m_TextPopup_RectTransform != null && m_selectedWord != -1 &&
{ (wordIndex == -1 || wordIndex != m_selectedWord)) {
TMP_WordInfo wInfo = m_TextMeshPro.textInfo.wordInfo[m_selectedWord]; TMP_WordInfo wInfo = m_TextMeshPro.textInfo.wordInfo[m_selectedWord];
// Iterate through each of the characters of the word. // Iterate through each of the characters of the word.
for (int i = 0; i < wInfo.characterCount; i++) for (int i = 0; i < wInfo.characterCount; i++) {
{
int characterIndex = wInfo.firstCharacterIndex + i; int characterIndex = wInfo.firstCharacterIndex + i;
// Get the index of the material / sub text object used by this character. // Get the index of the material / sub text object used by this character.
@ -203,15 +174,14 @@ namespace TMPro.Examples
// Word Selection Handling // Word Selection Handling
if (wordIndex != -1 && wordIndex != m_selectedWord && !(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))) if (wordIndex != -1 && wordIndex != m_selectedWord &&
{ !(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))) {
m_selectedWord = wordIndex; m_selectedWord = wordIndex;
TMP_WordInfo wInfo = m_TextMeshPro.textInfo.wordInfo[wordIndex]; TMP_WordInfo wInfo = m_TextMeshPro.textInfo.wordInfo[wordIndex];
// Iterate through each of the characters of the word. // Iterate through each of the characters of the word.
for (int i = 0; i < wInfo.characterCount; i++) for (int i = 0; i < wInfo.characterCount; i++) {
{
int characterIndex = wInfo.firstCharacterIndex + i; int characterIndex = wInfo.firstCharacterIndex + i;
// Get the index of the material / sub text object used by this character. // Get the index of the material / sub text object used by this character.
@ -232,25 +202,24 @@ namespace TMPro.Examples
// Update Geometry // Update Geometry
m_TextMeshPro.UpdateVertexData(TMP_VertexDataUpdateFlags.All); m_TextMeshPro.UpdateVertexData(TMP_VertexDataUpdateFlags.All);
} }
#endregion #endregion
#region Example of Link Handling #region Example of Link Handling
// Check if mouse intersects with any links. // Check if mouse intersects with any links.
int linkIndex = TMP_TextUtilities.FindIntersectingLink(m_TextMeshPro, Input.mousePosition, m_Camera); int linkIndex = TMP_TextUtilities.FindIntersectingLink(m_TextMeshPro, Input.mousePosition, m_Camera);
// Clear previous link selection if one existed. // Clear previous link selection if one existed.
if ((linkIndex == -1 && m_selectedLink != -1) || linkIndex != m_selectedLink) if ((linkIndex == -1 && m_selectedLink != -1) || linkIndex != m_selectedLink) {
{
m_TextPopup_RectTransform.gameObject.SetActive(false); m_TextPopup_RectTransform.gameObject.SetActive(false);
m_selectedLink = -1; m_selectedLink = -1;
} }
// Handle new Link selection. // Handle new Link selection.
if (linkIndex != -1 && linkIndex != m_selectedLink) if (linkIndex != -1 && linkIndex != m_selectedLink) {
{
m_selectedLink = linkIndex; m_selectedLink = linkIndex;
TMP_LinkInfo linkInfo = m_TextMeshPro.textInfo.linkInfo[linkIndex]; TMP_LinkInfo linkInfo = m_TextMeshPro.textInfo.linkInfo[linkIndex];
@ -258,10 +227,10 @@ namespace TMPro.Examples
// Debug.Log("Link ID: \"" + linkInfo.GetLinkID() + "\" Link Text: \"" + linkInfo.GetLinkText() + "\""); // Example of how to retrieve the Link ID and Link Text. // Debug.Log("Link ID: \"" + linkInfo.GetLinkID() + "\" Link Text: \"" + linkInfo.GetLinkText() + "\""); // Example of how to retrieve the Link ID and Link Text.
Vector3 worldPointInRectangle; Vector3 worldPointInRectangle;
RectTransformUtility.ScreenPointToWorldPointInRectangle(m_TextMeshPro.rectTransform, Input.mousePosition, m_Camera, out worldPointInRectangle); RectTransformUtility.ScreenPointToWorldPointInRectangle(m_TextMeshPro.rectTransform,
Input.mousePosition, m_Camera, out worldPointInRectangle);
switch (linkInfo.GetLinkID()) switch (linkInfo.GetLinkID()) {
{
case "id_01": // 100041637: // id_01 case "id_01": // 100041637: // id_01
m_TextPopup_RectTransform.position = worldPointInRectangle; m_TextPopup_RectTransform.position = worldPointInRectangle;
m_TextPopup_RectTransform.gameObject.SetActive(true); m_TextPopup_RectTransform.gameObject.SetActive(true);
@ -274,42 +243,37 @@ namespace TMPro.Examples
break; break;
} }
} }
#endregion
#endregion
} }
else else {
{
// Restore any character that may have been modified // Restore any character that may have been modified
if (m_lastIndex != -1) if (m_lastIndex != -1) {
{
RestoreCachedVertexAttributes(m_lastIndex); RestoreCachedVertexAttributes(m_lastIndex);
m_lastIndex = -1; m_lastIndex = -1;
} }
} }
} }
public void OnPointerEnter(PointerEventData eventData) private void OnEnable() {
{ // Subscribe to event fired when text object has been regenerated.
//Debug.Log("OnPointerEnter()"); TMPro_EventManager.TEXT_CHANGED_EVENT.Add(ON_TEXT_CHANGED);
isHoveringObject = true;
} }
private void OnDisable() {
public void OnPointerExit(PointerEventData eventData) // UnSubscribe to event fired when text object has been regenerated.
{ TMPro_EventManager.TEXT_CHANGED_EVENT.Remove(ON_TEXT_CHANGED);
//Debug.Log("OnPointerExit()");
isHoveringObject = false;
} }
public void OnPointerClick(PointerEventData eventData) public void OnPointerClick(PointerEventData eventData) {
{
//Debug.Log("Click at POS: " + eventData.position + " World POS: " + eventData.worldPosition); //Debug.Log("Click at POS: " + eventData.position + " World POS: " + eventData.worldPosition);
// Check if Mouse Intersects any of the characters. If so, assign a random color. // Check if Mouse Intersects any of the characters. If so, assign a random color.
#region Character Selection Handling #region Character Selection Handling
/* /*
int charIndex = TMP_TextUtilities.FindIntersectingCharacter(m_TextMeshPro, Input.mousePosition, m_Camera, true); int charIndex = TMP_TextUtilities.FindIntersectingCharacter(m_TextMeshPro, Input.mousePosition, m_Camera, true);
if (charIndex != -1 && charIndex != m_lastIndex) if (charIndex != -1 && charIndex != m_lastIndex)
@ -330,10 +294,12 @@ namespace TMPro.Examples
m_TextMeshPro.canvasRenderer.SetVertices(uiVertices, uiVertices.Length); m_TextMeshPro.canvasRenderer.SetVertices(uiVertices, uiVertices.Length);
} }
*/ */
#endregion #endregion
#region Word Selection Handling #region Word Selection Handling
//Check if Mouse intersects any words and if so assign a random color to that word. //Check if Mouse intersects any words and if so assign a random color to that word.
/* /*
int wordIndex = TMP_TextUtilities.FindIntersectingWord(m_TextMeshPro, Input.mousePosition, m_Camera); int wordIndex = TMP_TextUtilities.FindIntersectingWord(m_TextMeshPro, Input.mousePosition, m_Camera);
@ -390,10 +356,12 @@ namespace TMPro.Examples
m_TextMeshPro.canvasRenderer.SetVertices(uiVertices, uiVertices.Length); m_TextMeshPro.canvasRenderer.SetVertices(uiVertices, uiVertices.Length);
} }
*/ */
#endregion #endregion
#region Link Selection Handling #region Link Selection Handling
/* /*
// Check if Mouse intersects any words and if so assign a random color to that word. // Check if Mouse intersects any words and if so assign a random color to that word.
int linkIndex = TMP_TextUtilities.FindIntersectingLink(m_TextMeshPro, Input.mousePosition, m_Camera); int linkIndex = TMP_TextUtilities.FindIntersectingLink(m_TextMeshPro, Input.mousePosition, m_Camera);
@ -443,18 +411,36 @@ namespace TMPro.Examples
#endregion #endregion
} }
*/ */
#endregion #endregion
} }
public void OnPointerUp(PointerEventData eventData) public void OnPointerEnter(PointerEventData eventData) {
{ //Debug.Log("OnPointerEnter()");
isHoveringObject = true;
}
public void OnPointerExit(PointerEventData eventData) {
//Debug.Log("OnPointerExit()");
isHoveringObject = false;
}
public void OnPointerUp(PointerEventData eventData) {
//Debug.Log("OnPointerUp()"); //Debug.Log("OnPointerUp()");
} }
void RestoreCachedVertexAttributes(int index) private void ON_TEXT_CHANGED(Object obj) {
{ if (obj == m_TextMeshPro)
// Update cached vertex data.
m_cachedMeshInfoVertexData = m_TextMeshPro.textInfo.CopyMeshInfoVertexData();
}
private void RestoreCachedVertexAttributes(int index) {
if (index == -1 || index > m_TextMeshPro.textInfo.characterCount - 1) return; if (index == -1 || index > m_TextMeshPro.textInfo.characterCount - 1) return;
// Get the index of the material / sub text object used by this character. // Get the index of the material / sub text object used by this character.
@ -544,4 +530,4 @@ namespace TMPro.Examples
m_TextMeshPro.UpdateVertexData(TMP_VertexDataUpdateFlags.All); m_TextMeshPro.UpdateVertexData(TMP_VertexDataUpdateFlags.All);
} }
} }
} }

@ -1,43 +1,43 @@
using UnityEngine; using UnityEngine;
using System.Collections;
namespace TMPro.Examples {
public class TMP_UiFrameRateCounter : MonoBehaviour {
public enum FpsCounterAnchorPositions {
TopLeft,
BottomLeft,
TopRight,
BottomRight
}
namespace TMPro.Examples private const string fpsLabel = "{0:2}</color> <#8080ff>FPS \n<#FF8000>{1:2} <#8080ff>MS";
{
public class TMP_UiFrameRateCounter : MonoBehaviour
{
public float UpdateInterval = 5.0f; public float UpdateInterval = 5.0f;
private float m_LastInterval = 0;
private int m_Frames = 0;
public enum FpsCounterAnchorPositions { TopLeft, BottomLeft, TopRight, BottomRight };
public FpsCounterAnchorPositions AnchorPosition = FpsCounterAnchorPositions.TopRight; public FpsCounterAnchorPositions AnchorPosition = FpsCounterAnchorPositions.TopRight;
private string htmlColorTag; private string htmlColorTag;
private const string fpsLabel = "{0:2}</color> <#8080ff>FPS \n<#FF8000>{1:2} <#8080ff>MS";
private TextMeshProUGUI m_TextMeshPro; private FpsCounterAnchorPositions last_AnchorPosition;
private RectTransform m_frameCounter_transform; private RectTransform m_frameCounter_transform;
private int m_Frames;
private float m_LastInterval;
private FpsCounterAnchorPositions last_AnchorPosition; private TextMeshProUGUI m_TextMeshPro;
void Awake() private void Awake() {
{
if (!enabled) if (!enabled)
return; return;
Application.targetFrameRate = 1000; Application.targetFrameRate = 1000;
GameObject frameCounter = new GameObject("Frame Counter"); GameObject frameCounter = new("Frame Counter");
m_frameCounter_transform = frameCounter.AddComponent<RectTransform>(); m_frameCounter_transform = frameCounter.AddComponent<RectTransform>();
m_frameCounter_transform.SetParent(this.transform, false); m_frameCounter_transform.SetParent(transform, false);
m_TextMeshPro = frameCounter.AddComponent<TextMeshProUGUI>(); m_TextMeshPro = frameCounter.AddComponent<TextMeshProUGUI>();
m_TextMeshPro.font = Resources.Load<TMP_FontAsset>("Fonts & Materials/LiberationSans SDF"); m_TextMeshPro.font = Resources.Load<TMP_FontAsset>("Fonts & Materials/LiberationSans SDF");
m_TextMeshPro.fontSharedMaterial = Resources.Load<Material>("Fonts & Materials/LiberationSans SDF - Overlay"); m_TextMeshPro.fontSharedMaterial =
Resources.Load<Material>("Fonts & Materials/LiberationSans SDF - Overlay");
m_TextMeshPro.enableWordWrapping = false; m_TextMeshPro.enableWordWrapping = false;
m_TextMeshPro.fontSize = 36; m_TextMeshPro.fontSize = 36;
@ -49,15 +49,13 @@ namespace TMPro.Examples
} }
void Start() private void Start() {
{
m_LastInterval = Time.realtimeSinceStartup; m_LastInterval = Time.realtimeSinceStartup;
m_Frames = 0; m_Frames = 0;
} }
void Update() private void Update() {
{
if (AnchorPosition != last_AnchorPosition) if (AnchorPosition != last_AnchorPosition)
Set_FrameCounter_Position(AnchorPosition); Set_FrameCounter_Position(AnchorPosition);
@ -66,8 +64,7 @@ namespace TMPro.Examples
m_Frames += 1; m_Frames += 1;
float timeNow = Time.realtimeSinceStartup; float timeNow = Time.realtimeSinceStartup;
if (timeNow > m_LastInterval + UpdateInterval) if (timeNow > m_LastInterval + UpdateInterval) {
{
// display two fractional digits (f2 format) // display two fractional digits (f2 format)
float fps = m_Frames / (timeNow - m_LastInterval); float fps = m_Frames / (timeNow - m_LastInterval);
float ms = 1000.0f / Mathf.Max(fps, 0.00001f); float ms = 1000.0f / Mathf.Max(fps, 0.00001f);
@ -87,10 +84,8 @@ namespace TMPro.Examples
} }
void Set_FrameCounter_Position(FpsCounterAnchorPositions anchor_position) private void Set_FrameCounter_Position(FpsCounterAnchorPositions anchor_position) {
{ switch (anchor_position) {
switch (anchor_position)
{
case FpsCounterAnchorPositions.TopLeft: case FpsCounterAnchorPositions.TopLeft:
m_TextMeshPro.alignment = TextAlignmentOptions.TopLeft; m_TextMeshPro.alignment = TextAlignmentOptions.TopLeft;
m_frameCounter_transform.pivot = new Vector2(0, 1); m_frameCounter_transform.pivot = new Vector2(0, 1);

Some files were not shown because too many files have changed in this diff Show More