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
#if UNITY_EDITOR
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace ParrelSync.Example
{
public class CustomArgumentExample : MonoBehaviour
{
namespace ParrelSync.Example {
public class CustomArgumentExample : MonoBehaviour {
// Start is called before the first frame update
void Start()
{
private void Start() {
// Is this editor instance running a clone project?
if (ClonesManager.IsClone())
{
if (ClonesManager.IsClone()) {
Debug.Log("This is a clone project.");
//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);
// Do what ever you need with the argument string.
}
else
{
else {
Debug.Log("This is the original project.");
}
}

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

@ -1,18 +1,14 @@
using UnityEditor;
using UnityEngine;
namespace ParrelSync
{
namespace ParrelSync {
/// <summary>
/// For preventing assets being modified from the clone instance.
/// For preventing assets being modified from the clone instance.
/// </summary>
public class ParrelSyncAssetModificationProcessor : UnityEditor.AssetModificationProcessor
{
public static string[] OnWillSaveAssets(string[] paths)
{
if (ClonesManager.IsClone() && Preferences.AssetModPref.Value)
{
if (paths != null && paths.Length > 0 && !EditorQuit.IsQuiting)
{
public class ParrelSyncAssetModificationProcessor : AssetModificationProcessor {
public static string[] OnWillSaveAssets(string[] paths) {
if (ClonesManager.IsClone() && Preferences.AssetModPref.Value) {
if (paths != null && paths.Length > 0 && !EditorQuit.IsQuiting) {
EditorUtility.DisplayDialog(
ClonesManager.ProjectName + ": Asset modifications saving detected and blocked",
"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.",
"ok"
);
foreach (var path in paths)
{
Debug.Log("Attempting to save " + path + " are blocked.");
}
foreach (string path in paths) Debug.Log("Attempting to save " + path + " are blocked.");
}
return new string[0] { };
return new string[0]{ };
}
return paths;
}
}

@ -1,138 +1,131 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using UnityEngine;
using UnityEditor;
using System.Linq;
using System.IO;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;
namespace ParrelSync
{
namespace ParrelSync {
/// <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>
public class ClonesManager
{
public class ClonesManager {
/// <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>
/// <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>
public const string CloneFileName = ".clone";
/// <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>
/// <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>
public const string CloneNameSuffix = "_clone";
public const string ProjectName = "ParrelSync";
/// <summary>
/// The maximum number of clones
/// The maximum number of clones
/// </summary>
public const int MaxCloneProjectCount = 10;
/// <summary>
/// Name of the file for storing clone's argument.
/// Name of the file for storing clone's argument.
/// </summary>
public const string ArgumentFileName = ".parrelsyncarg";
/// <summary>
/// Default argument of the new clone
/// Default argument of the new clone
/// </summary>
public const string DefaultArgument = "client";
#region Managing clones
/// <summary>
/// Creates clone from the project currently open in Unity Editor.
/// Creates clone from the project currently open in Unity Editor.
/// </summary>
/// <returns></returns>
public static Project CreateCloneFromCurrent()
{
if (IsClone())
{
public static Project CreateCloneFromCurrent() {
if (IsClone()) {
Debug.LogError("This project is already a clone. Cannot clone it.");
return null;
}
string currentProjectPath = ClonesManager.GetCurrentProjectPath();
return ClonesManager.CreateCloneFromPath(currentProjectPath);
string currentProjectPath = GetCurrentProjectPath();
return CreateCloneFromPath(currentProjectPath);
}
/// <summary>
/// Creates clone of the project located at the given path.
/// Creates clone of the project located at the given path.
/// </summary>
/// <param name="sourceProjectPath"></param>
/// <returns></returns>
public static Project CreateCloneFromPath(string sourceProjectPath)
{
Project sourceProject = new Project(sourceProjectPath);
public static Project CreateCloneFromPath(string sourceProjectPath) {
Project sourceProject = new(sourceProjectPath);
string cloneProjectPath = null;
//Find available clone suffix id
for (int i = 0; i < MaxCloneProjectCount; i++)
{
string originalProjectPath = ClonesManager.GetCurrentProject().projectPath;
string possibleCloneProjectPath = originalProjectPath + ClonesManager.CloneNameSuffix + "_" + i;
for (int i = 0; i < MaxCloneProjectCount; i++) {
string originalProjectPath = GetCurrentProject().projectPath;
string possibleCloneProjectPath = originalProjectPath + CloneNameSuffix + "_" + i;
if (!Directory.Exists(possibleCloneProjectPath))
{
if (!Directory.Exists(possibleCloneProjectPath)) {
cloneProjectPath = possibleCloneProjectPath;
break;
}
}
if (string.IsNullOrEmpty(cloneProjectPath))
{
if (string.IsNullOrEmpty(cloneProjectPath)) {
Debug.LogError("The number of cloned projects has reach its limit. Limit: " + MaxCloneProjectCount);
return null;
}
Project cloneProject = new Project(cloneProjectPath);
Project cloneProject = new(cloneProjectPath);
Debug.Log("Start cloning project, original project: " + sourceProject + ", clone project: " + cloneProject);
ClonesManager.CreateProjectFolder(cloneProject);
CreateProjectFolder(cloneProject);
//Copy Folders
Debug.Log("Library copy: " + cloneProject.libraryPath);
ClonesManager.CopyDirectoryWithProgressBar(sourceProject.libraryPath, cloneProject.libraryPath,
CopyDirectoryWithProgressBar(sourceProject.libraryPath, cloneProject.libraryPath,
"Cloning Project Library '" + sourceProject.name + "'. ");
Debug.Log("Packages copy: " + cloneProject.libraryPath);
ClonesManager.CopyDirectoryWithProgressBar(sourceProject.packagesPath, cloneProject.packagesPath,
"Cloning Project Packages '" + sourceProject.name + "'. ");
CopyDirectoryWithProgressBar(sourceProject.packagesPath, cloneProject.packagesPath,
"Cloning Project Packages '" + sourceProject.name + "'. ");
//Link Folders
ClonesManager.LinkFolders(sourceProject.assetPath, cloneProject.assetPath);
ClonesManager.LinkFolders(sourceProject.projectSettingsPath, cloneProject.projectSettingsPath);
ClonesManager.LinkFolders(sourceProject.autoBuildPath, cloneProject.autoBuildPath);
ClonesManager.LinkFolders(sourceProject.localPackages, cloneProject.localPackages);
LinkFolders(sourceProject.assetPath, cloneProject.assetPath);
LinkFolders(sourceProject.projectSettingsPath, cloneProject.projectSettingsPath);
LinkFolders(sourceProject.autoBuildPath, cloneProject.autoBuildPath);
LinkFolders(sourceProject.localPackages, cloneProject.localPackages);
ClonesManager.RegisterClone(cloneProject);
RegisterClone(cloneProject);
return cloneProject;
}
/// <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>
/// <param name="cloneProject"></param>
private static void RegisterClone(Project cloneProject)
{
private static void RegisterClone(Project cloneProject) {
/// Add clone identifier file.
string identifierFile = Path.Combine(cloneProject.projectPath, ClonesManager.CloneFileName);
string identifierFile = Path.Combine(cloneProject.projectPath, CloneFileName);
File.Create(identifierFile).Dispose();
//Add argument file with default argument
string argumentFilePath = Path.Combine(cloneProject.projectPath, ClonesManager.ArgumentFileName);
File.WriteAllText(argumentFilePath, DefaultArgument, System.Text.Encoding.UTF8);
string argumentFilePath = Path.Combine(cloneProject.projectPath, ArgumentFileName);
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.
string collabignoreFile = Path.Combine(cloneProject.projectPath, "collabignore.txt");
@ -140,19 +133,16 @@ namespace ParrelSync
}
/// <summary>
/// Opens a project located at the given path (if one exists).
/// Opens a project located at the given path (if one exists).
/// </summary>
/// <param name="projectPath"></param>
public static void OpenProject(string projectPath)
{
if (!Directory.Exists(projectPath))
{
public static void OpenProject(string projectPath) {
if (!Directory.Exists(projectPath)) {
Debug.LogError("Cannot open the project - provided folder (" + projectPath + ") does not exist.");
return;
}
if (projectPath == ClonesManager.GetCurrentProjectPath())
{
if (projectPath == GetCurrentProjectPath()) {
Debug.LogError("Cannot open the project - it is already open.");
return;
}
@ -164,13 +154,11 @@ namespace ParrelSync
string fileName = GetApplicationPath();
string args = "-projectPath \"" + projectPath + "\"";
Debug.Log("Opening project \"" + fileName + " " + args + "\"");
ClonesManager.StartHiddenConsoleProcess(fileName, args);
StartHiddenConsoleProcess(fileName, args);
}
private static string GetApplicationPath()
{
switch (Application.platform)
{
private static string GetApplicationPath() {
switch (Application.platform) {
case RuntimePlatform.WindowsEditor:
return EditorApplication.applicationPath;
case RuntimePlatform.OSXEditor:
@ -178,87 +166,82 @@ namespace ParrelSync
case RuntimePlatform.LinuxEditor:
return EditorApplication.applicationPath;
default:
throw new System.NotImplementedException("Platform has not supported yet ;(");
throw new NotImplementedException("Platform has not supported yet ;(");
}
}
/// <summary>
/// Is this project being opened by an Unity editor?
/// Is this project being opened by an Unity editor?
/// </summary>
/// <param name="projectPath"></param>
/// <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
string UnityLockFilePath = new string[] { projectPath, "Temp", "UnityLockfile" }
string UnityLockFilePath = new[]{ projectPath, "Temp", "UnityLockfile" }
.Aggregate(Path.Combine);
switch (Application.platform)
{
case (RuntimePlatform.WindowsEditor):
switch (Application.platform) {
case RuntimePlatform.WindowsEditor:
//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
//isn't being opened, so a check to the "UnityLockfile" lock status may be necessary.
if (Preferences.AlsoCheckUnityLockFileStaPref.Value)
return File.Exists(UnityLockFilePath) && FileUtilities.IsFileLocked(UnityLockFilePath);
else
return File.Exists(UnityLockFilePath);
case (RuntimePlatform.OSXEditor):
return File.Exists(UnityLockFilePath);
case RuntimePlatform.OSXEditor:
//Mac editor won't lock "UnityLockfile" file when project is being opened
return File.Exists(UnityLockFilePath);
case (RuntimePlatform.LinuxEditor):
case RuntimePlatform.LinuxEditor:
return File.Exists(UnityLockFilePath);
default:
throw new System.NotImplementedException("IsCloneProjectRunning: Unsupport Platfrom: " + Application.platform);
throw new NotImplementedException("IsCloneProjectRunning: Unsupport Platfrom: " +
Application.platform);
}
}
/// <summary>
/// Deletes the clone of the currently open project, if such exists.
/// Deletes the clone of the currently open project, if such exists.
/// </summary>
public static void DeleteClone(string cloneProjectPath)
{
public static void DeleteClone(string cloneProjectPath) {
/// Clone won't be able to delete itself.
if (ClonesManager.IsClone()) return;
if (IsClone()) return;
///Extra precautions.
if (cloneProjectPath == string.Empty) return;
if (cloneProjectPath == ClonesManager.GetOriginalProjectPath()) return;
if (cloneProjectPath == GetOriginalProjectPath()) return;
//Check what OS is
string identifierFile;
string args;
switch (Application.platform)
{
case (RuntimePlatform.WindowsEditor):
switch (Application.platform) {
case RuntimePlatform.WindowsEditor:
Debug.Log("Attempting to delete folder \"" + cloneProjectPath + "\"");
//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.)
//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);
args = "/c " + @"rmdir /s/q " + string.Format("\"{0}\"", cloneProjectPath);
StartHiddenConsoleProcess("cmd.exe", args);
break;
case (RuntimePlatform.OSXEditor):
case RuntimePlatform.OSXEditor:
Debug.Log("Attempting to delete folder \"" + cloneProjectPath + "\"");
//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.)
//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);
FileUtil.DeleteFileOrDirectory(cloneProjectPath);
break;
case (RuntimePlatform.LinuxEditor):
case RuntimePlatform.LinuxEditor:
Debug.Log("Attempting to delete folder \"" + cloneProjectPath + "\"");
identifierFile = Path.Combine(cloneProjectPath, ClonesManager.ArgumentFileName);
identifierFile = Path.Combine(cloneProjectPath, ArgumentFileName);
File.Delete(identifierFile);
FileUtil.DeleteFileOrDirectory(cloneProjectPath);
@ -275,32 +258,30 @@ namespace ParrelSync
#region Creating project folders
/// <summary>
/// Creates an empty folder using data in the given Project object
/// Creates an empty folder using data in the given Project object
/// </summary>
/// <param name="project"></param>
public static void CreateProjectFolder(Project project)
{
public static void CreateProjectFolder(Project project) {
string path = project.projectPath;
Debug.Log("Creating new empty folder at: " + path);
Directory.CreateDirectory(path);
}
/// <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>
/// <param name="sourceProject"></param>
/// <param name="destinationProject"></param>
[System.Obsolete]
public static void CopyLibraryFolder(Project sourceProject, Project destinationProject)
{
if (Directory.Exists(destinationProject.libraryPath))
{
[Obsolete]
public static void CopyLibraryFolder(Project sourceProject, Project destinationProject) {
if (Directory.Exists(destinationProject.libraryPath)) {
Debug.LogWarning("Library copy: destination path already exists! ");
return;
}
Debug.Log("Library copy: " + destinationProject.libraryPath);
ClonesManager.CopyDirectoryWithProgressBar(sourceProject.libraryPath, destinationProject.libraryPath,
CopyDirectoryWithProgressBar(sourceProject.libraryPath, destinationProject.libraryPath,
"Cloning project '" + sourceProject.name + "'. ");
}
@ -309,47 +290,44 @@ namespace ParrelSync
#region Creating symlinks
/// <summary>
/// Creates a symlink between destinationPath and sourcePath (Mac version).
/// Creates a symlink between destinationPath and sourcePath (Mac version).
/// </summary>
/// <param name="sourcePath"></param>
/// <param name="destinationPath"></param>
private static void CreateLinkMac(string sourcePath, string destinationPath)
{
private static void CreateLinkMac(string sourcePath, string destinationPath) {
sourcePath = sourcePath.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);
ClonesManager.ExecuteBashCommand(command);
ExecuteBashCommand(command);
}
/// <summary>
/// Creates a symlink between destinationPath and sourcePath (Linux version).
/// Creates a symlink between destinationPath and sourcePath (Linux version).
/// </summary>
/// <param name="sourcePath"></param>
/// <param name="destinationPath"></param>
private static void CreateLinkLinux(string sourcePath, string destinationPath)
{
private static void CreateLinkLinux(string sourcePath, string destinationPath) {
sourcePath = sourcePath.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);
ClonesManager.ExecuteBashCommand(command);
ExecuteBashCommand(command);
}
/// <summary>
/// Creates a symlink between destinationPath and sourcePath (Windows version).
/// Creates a symlink between destinationPath and sourcePath (Windows version).
/// </summary>
/// <param name="sourcePath"></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);
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!
@ -361,52 +339,44 @@ namespace ParrelSync
// out int pBytesReturned, System.IntPtr lpOverlapped);
/// <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>
/// <param name="sourcePath"></param>
/// <param name="destinationPath"></param>
public static void LinkFolders(string sourcePath, string destinationPath)
{
if ((Directory.Exists(destinationPath) == false) && (Directory.Exists(sourcePath) == true))
{
switch (Application.platform)
{
case (RuntimePlatform.WindowsEditor):
public static void LinkFolders(string sourcePath, string destinationPath) {
if (Directory.Exists(destinationPath) == false && Directory.Exists(sourcePath))
switch (Application.platform) {
case RuntimePlatform.WindowsEditor:
CreateLinkWin(sourcePath, destinationPath);
break;
case (RuntimePlatform.OSXEditor):
case RuntimePlatform.OSXEditor:
CreateLinkMac(sourcePath, destinationPath);
break;
case (RuntimePlatform.LinuxEditor):
case RuntimePlatform.LinuxEditor:
CreateLinkLinux(sourcePath, destinationPath);
break;
default:
Debug.LogWarning("Not in a known editor. Application.platform: " + Application.platform);
break;
}
}
else
{
Debug.LogWarning("Skipping Asset link, it already exists: " + destinationPath);
}
}
#endregion
#region Utility methods
private static bool? isCloneFileExistCache = null;
private static bool? isCloneFileExistCache;
/// <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>
/// <returns></returns>
public static bool IsClone()
{
if (isCloneFileExistCache == null)
{
public static bool IsClone() {
if (isCloneFileExistCache == null) {
/// 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);
}
@ -414,85 +384,71 @@ namespace ParrelSync
}
/// <summary>
/// Get the path to the current unityEditor project folder's info
/// Get the path to the current unityEditor project folder's info
/// </summary>
/// <returns></returns>
public static string GetCurrentProjectPath()
{
public static string GetCurrentProjectPath() {
return Application.dataPath.Replace("/Assets", "");
}
/// <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>
/// <returns></returns>
public static Project GetCurrentProject()
{
string pathString = ClonesManager.GetCurrentProjectPath();
public static Project GetCurrentProject() {
string pathString = GetCurrentProjectPath();
return new Project(pathString);
}
/// <summary>
/// Get the argument of this clone project.
/// If this is the original project, will return an empty string.
/// Get the argument of this clone project.
/// If this is the original project, will return an empty string.
/// </summary>
/// <returns></returns>
public static string GetArgument()
{
public static string GetArgument() {
string argument = "";
if (IsClone())
{
string argumentFilePath = Path.Combine(GetCurrentProjectPath(), ClonesManager.ArgumentFileName);
if (File.Exists(argumentFilePath))
{
argument = File.ReadAllText(argumentFilePath, System.Text.Encoding.UTF8);
}
if (IsClone()) {
string argumentFilePath = Path.Combine(GetCurrentProjectPath(), ArgumentFileName);
if (File.Exists(argumentFilePath)) argument = File.ReadAllText(argumentFilePath, Encoding.UTF8);
}
return argument;
}
/// <summary>
/// Returns the path to the original project.
/// If currently open project is the original, returns its own path.
/// If the original project folder cannot be found, retuns an empty string.
/// Returns the path to the original project.
/// If currently open project is the original, returns its own path.
/// If the original project folder cannot be found, retuns an empty string.
/// </summary>
/// <returns></returns>
public static string GetOriginalProjectPath()
{
if (IsClone())
{
public static string GetOriginalProjectPath() {
if (IsClone()) {
/// If this is a clone...
/// 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);
if (index > 0)
{
int index = cloneProjectPath.LastIndexOf(CloneNameSuffix);
if (index > 0) {
string originalProjectPath = cloneProjectPath.Substring(0, index);
if (Directory.Exists(originalProjectPath)) return originalProjectPath;
}
return string.Empty;
}
else
{
/// If this is the original, we return its own path.
return ClonesManager.GetCurrentProjectPath();
}
/// If this is the original, we return its own path.
return GetCurrentProjectPath();
}
/// <summary>
/// Returns all clone projects path.
/// Returns all clone projects path.
/// </summary>
/// <returns></returns>
public static List<string> GetCloneProjectsPath()
{
List<string> projectsPath = new List<string>();
for (int i = 0; i < MaxCloneProjectCount; i++)
{
string originalProjectPath = ClonesManager.GetCurrentProject().projectPath;
string cloneProjectPath = originalProjectPath + ClonesManager.CloneNameSuffix + "_" + i;
public static List<string> GetCloneProjectsPath() {
List<string> projectsPath = new();
for (int i = 0; i < MaxCloneProjectCount; i++) {
string originalProjectPath = GetCurrentProject().projectPath;
string cloneProjectPath = originalProjectPath + CloneNameSuffix + "_" + i;
if (Directory.Exists(cloneProjectPath))
projectsPath.Add(cloneProjectPath);
@ -502,28 +458,27 @@ namespace ParrelSync
}
/// <summary>
/// Copies directory located at sourcePath to destinationPath. Displays a progress bar.
/// Copies directory located at sourcePath to destinationPath. Displays a progress bar.
/// </summary>
/// <param name="source">Directory to be copied.</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>
public static void CopyDirectoryWithProgressBar(string sourcePath, string destinationPath,
string progressBarPrefix = "")
{
var source = new DirectoryInfo(sourcePath);
var destination = new DirectoryInfo(destinationPath);
string progressBarPrefix = "") {
DirectoryInfo source = new(sourcePath);
DirectoryInfo destination = new(destinationPath);
long totalBytes = 0;
long copiedBytes = 0;
ClonesManager.CopyDirectoryWithProgressBarRecursive(source, destination, ref totalBytes, ref copiedBytes,
CopyDirectoryWithProgressBarRecursive(source, destination, ref totalBytes, ref copiedBytes,
progressBarPrefix);
EditorUtility.ClearProgressBar();
}
/// <summary>
/// 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.
/// 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.
/// </summary>
/// <param name="source">Directory to be copied.</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="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param>
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.
if (source.FullName.ToLower() == destination.FullName.ToLower())
{
if (source.FullName.ToLower() == destination.FullName.ToLower()) {
Debug.LogError("Cannot copy directory into itself.");
return;
}
/// Calculate total bytes, if required.
if (totalBytes == 0)
{
totalBytes = ClonesManager.GetDirectorySize(source, true, progressBarPrefix);
}
if (totalBytes == 0) totalBytes = GetDirectorySize(source, true, progressBarPrefix);
/// Create destination directory, if required.
if (!Directory.Exists(destination.FullName))
{
Directory.CreateDirectory(destination.FullName);
}
if (!Directory.Exists(destination.FullName)) Directory.CreateDirectory(destination.FullName);
/// Copy all files from the source.
foreach (FileInfo file in source.GetFiles())
{
try
{
foreach (FileInfo file in source.GetFiles()) {
try {
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.
/// Just ignore them in such case.
}
@ -569,7 +513,7 @@ namespace ParrelSync
copiedBytes += file.Length;
/// Display the progress bar.
float progress = (float)copiedBytes / (float)totalBytes;
float progress = copiedBytes / (float)totalBytes;
bool cancelCopy = EditorUtility.DisplayCancelableProgressBar(
progressBarPrefix + "Copying '" + source.FullName + "' to '" + destination.FullName + "'...",
"(" + (progress * 100f).ToString("F2") + "%) Copying file '" + file.Name + "'...",
@ -578,66 +522,57 @@ namespace ParrelSync
}
/// 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);
ClonesManager.CopyDirectoryWithProgressBarRecursive(sourceNestedDir, nextDestingationNestedDir,
CopyDirectoryWithProgressBarRecursive(sourceNestedDir, nextDestingationNestedDir,
ref totalBytes, ref copiedBytes, progressBarPrefix);
}
}
/// <summary>
/// Calculates the size of the given directory. Displays a progress bar.
/// Calculates the size of the given directory. Displays a progress bar.
/// </summary>
/// <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="progressBarPrefix">Optional string added to the beginning of the progress bar window header.</param>
/// <returns>Size of the directory in bytes.</returns>
private static long GetDirectorySize(DirectoryInfo directory, bool includeNested = false,
string progressBarPrefix = "")
{
string progressBarPrefix = "") {
EditorUtility.DisplayProgressBar(progressBarPrefix + "Calculating size of directories...",
"Scanning '" + directory.FullName + "'...", 0f);
/// 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.
long directoriesSize = 0;
if (includeNested)
{
if (includeNested) {
IEnumerable<DirectoryInfo> nestedDirectories = directory.GetDirectories();
foreach (DirectoryInfo nestedDir in nestedDirectories)
{
directoriesSize += ClonesManager.GetDirectorySize(nestedDir, true, progressBarPrefix);
}
directoriesSize += GetDirectorySize(nestedDir, true, progressBarPrefix);
}
return filesSize + directoriesSize;
}
/// <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>
/// <param name="fileName"></param>
/// <param name="args"></param>
private static void StartHiddenConsoleProcess(string fileName, string args)
{
System.Diagnostics.Process.Start(fileName, args);
private static void StartHiddenConsoleProcess(string fileName, string args) {
Process.Start(fileName, args);
}
/// <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>
/// <param name="command"></param>
private static void ExecuteBashCommand(string command)
{
private static void ExecuteBashCommand(string command) {
command = command.Replace("\"", "\"\"");
var proc = new Process()
{
StartInfo = new ProcessStartInfo
{
Process proc = new(){
StartInfo = new ProcessStartInfo{
FileName = "/bin/bash",
Arguments = "-c \"" + command + "\"",
UseShellExecute = false,
@ -647,22 +582,18 @@ namespace ParrelSync
}
};
using (proc)
{
using (proc) {
proc.Start();
proc.WaitForExit();
if (!proc.StandardError.EndOfStream)
{
UnityEngine.Debug.LogError(proc.StandardError.ReadToEnd());
}
if (!proc.StandardError.EndOfStream) Debug.LogError(proc.StandardError.ReadToEnd());
}
}
public static void OpenProjectInFileExplorer(string path)
{
System.Diagnostics.Process.Start(@path);
public static void OpenProjectInFileExplorer(string path) {
Process.Start(path);
}
#endregion
}
}
}

@ -1,106 +1,84 @@
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
namespace ParrelSync
{
namespace ParrelSync {
/// <summary>
///Clones manager Unity editor window
/// Clones manager Unity editor window
/// </summary>
public class ClonesManagerWindow : EditorWindow
{
public class ClonesManagerWindow : EditorWindow {
/// <summary>
/// Returns true if project clone exists.
/// For storing the scroll position of clones list
/// </summary>
public bool isCloneCreated
{
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();
}
private Vector2 clonesScrollPos;
/// <summary>
/// For storing the scroll position of clones list
/// Returns true if project clone exists.
/// </summary>
Vector2 clonesScrollPos;
public bool isCloneCreated => ClonesManager.GetCloneProjectsPath().Count >= 1;
private void OnGUI()
{
private void OnGUI() {
/// If it is a clone project...
if (ClonesManager.IsClone())
{
if (ClonesManager.IsClone()) {
//Find out the original project name and show the help box
string originalProjectPath = ClonesManager.GetOriginalProjectPath();
if (originalProjectPath == string.Empty)
{
/// If original project cannot be found, display warning message.
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",
MessageType.Warning);
}
else
{
/// If original project is present, display some usage info.
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);
}
//Clone project custom argument.
GUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Arguments", GUILayout.Width(70));
if (GUILayout.Button("?", GUILayout.Width(20)))
{
Application.OpenURL(ExternalLinks.CustomArgumentHelpLink);
}
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
// 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
//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.
if (File.Exists(argumentFilePath))
{
string argument = File.ReadAllText(argumentFilePath, System.Text.Encoding.UTF8);
if (File.Exists(argumentFilePath)) {
string argument = File.ReadAllText(argumentFilePath, Encoding.UTF8);
string argumentTextAreaInput = EditorGUILayout.TextArea(argument,
GUILayout.Height(50),
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.");
}
}
else// If it is an original project...
else // If it is an original project...
{
if (isCloneCreated)
{
if (isCloneCreated) {
GUILayout.BeginVertical("HelpBox");
GUILayout.Label("Clones of this Project");
//List all clones
clonesScrollPos =
EditorGUILayout.BeginScrollView(clonesScrollPos);
var cloneProjectsPath = ClonesManager.GetCloneProjectsPath();
for (int i = 0; i < cloneProjectsPath.Count; i++)
{
EditorGUILayout.BeginScrollView(clonesScrollPos);
List<string> cloneProjectsPath = ClonesManager.GetCloneProjectsPath();
for (int i = 0; i < cloneProjectsPath.Count; i++) {
GUILayout.BeginVertical("GroupBox");
string cloneProjectPath = cloneProjectsPath[i];
bool isOpenInAnotherInstance = ClonesManager.IsCloneProjectRunning(cloneProjectPath);
if (isOpenInAnotherInstance == true)
if (isOpenInAnotherInstance)
EditorGUILayout.LabelField("Clone " + i + " (Running)", EditorStyles.boldLabel);
else
EditorGUILayout.LabelField("Clone " + i);
@ -109,17 +87,13 @@ namespace ParrelSync
GUILayout.BeginHorizontal();
EditorGUILayout.TextField("Clone project path", cloneProjectPath, EditorStyles.textField);
if (GUILayout.Button("View Folder", GUILayout.Width(80)))
{
ClonesManager.OpenProjectInFileExplorer(cloneProjectPath);
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Arguments", GUILayout.Width(70));
if (GUILayout.Button("?", GUILayout.Width(20)))
{
Application.OpenURL(ExternalLinks.CustomArgumentHelpLink);
}
GUILayout.EndHorizontal();
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
//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.
if (File.Exists(argumentFilePath))
{
string argument = File.ReadAllText(argumentFilePath, System.Text.Encoding.UTF8);
if (File.Exists(argumentFilePath)) {
string argument = File.ReadAllText(argumentFilePath, Encoding.UTF8);
string argumentTextAreaInput = EditorGUILayout.TextArea(argument,
GUILayout.Height(50),
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.");
}
@ -149,50 +121,44 @@ namespace ParrelSync
EditorGUI.BeginDisabledGroup(isOpenInAnotherInstance);
if (GUILayout.Button("Open in New Editor"))
{
ClonesManager.OpenProject(cloneProjectPath);
}
if (GUILayout.Button("Open in New Editor")) ClonesManager.OpenProject(cloneProjectPath);
GUILayout.BeginHorizontal();
if (GUILayout.Button("Delete"))
{
if (GUILayout.Button("Delete")) {
bool delete = EditorUtility.DisplayDialog(
"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",
"Cancel");
if (delete)
{
ClonesManager.DeleteClone(cloneProjectPath);
}
if (delete) ClonesManager.DeleteClone(cloneProjectPath);
}
GUILayout.EndHorizontal();
EditorGUI.EndDisabledGroup();
GUILayout.EndVertical();
}
EditorGUILayout.EndScrollView();
if (GUILayout.Button("Add new clone"))
{
ClonesManager.CreateCloneFromCurrent();
}
if (GUILayout.Button("Add new clone")) ClonesManager.CreateCloneFromCurrent();
GUILayout.EndVertical();
GUILayout.FlexibleSpace();
}
else
{
else {
/// If no clone created yet, we must create it.
EditorGUILayout.HelpBox("No project clones found. Create a new one!", MessageType.Info);
if (GUILayout.Button("Create new clone"))
{
ClonesManager.CreateCloneFromCurrent();
}
if (GUILayout.Button("Create new clone")) 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
{
public class ExternalLinks
{
public const string RemoteVersionURL = "https://raw.githubusercontent.com/VeriorPies/ParrelSync/master/VERSION.txt";
namespace ParrelSync {
public class ExternalLinks {
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 CustomArgumentHelpLink = "https://github.com/VeriorPies/ParrelSync/wiki/Argument";

@ -1,22 +1,16 @@
using System.IO;
using UnityEngine;
namespace ParrelSync
{
public class FileUtilities : MonoBehaviour
{
public static bool IsFileLocked(string path)
{
FileInfo file = new FileInfo(path);
try
{
using (FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None))
{
namespace ParrelSync {
public class FileUtilities : MonoBehaviour {
public static bool IsFileLocked(string path) {
FileInfo file = new(path);
try {
using (FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None)) {
stream.Close();
}
}
catch (IOException)
{
catch (IOException) {
//the file is unavailable because it is:
//still being written to
//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>
/// 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.
/// Removing this file from project wont effect any other functions.
/// 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.
/// Removing this file from project wont effect any other functions.
/// </summary>
[InitializeOnLoad]
public class AskFeedbackDialog
{
const string InitializeOnLoadCountKey = "ParrelSync_InitOnLoadCount", StopShowingKey = "ParrelSync_StopShowFeedBack";
static AskFeedbackDialog()
{
if (EditorPrefs.HasKey(StopShowingKey)) { return; }
public class AskFeedbackDialog {
private const string InitializeOnLoadCountKey = "ParrelSync_InitOnLoadCount",
StopShowingKey = "ParrelSync_StopShowFeedBack";
static AskFeedbackDialog() {
if (EditorPrefs.HasKey(StopShowingKey)) return;
int InitializeOnLoadCount = EditorPrefs.GetInt(InitializeOnLoadCountKey, 0);
if (InitializeOnLoadCount > 20)
{
ShowDialog();
}
else
{
EditorPrefs.SetInt(InitializeOnLoadCountKey, InitializeOnLoadCount + 1);
}
}
//[MenuItem("ParrelSync/(Debug)Show AskFeedbackDialog ")]
private static void ShowDialog()
{
int option = EditorUtility.DisplayDialogComplex("Do you like " + ParrelSync.ClonesManager.ProjectName + "?",
"Do you like " + ParrelSync.ClonesManager.ProjectName + "?\n" +
"If so, please don't hesitate to star it on GitHub and contribute to the project!",
"Star on GitHub",
"Close",
"Remind me next time"
);
private static void ShowDialog() {
int option = EditorUtility.DisplayDialogComplex("Do you like " + ClonesManager.ProjectName + "?",
"Do you like " + ClonesManager.ProjectName + "?\n" +
"If so, please don't hesitate to star it on GitHub and contribute to the project!",
"Star on GitHub",
"Close",
"Remind me next time"
);
switch (option)
{
switch (option) {
// First parameter.
case 0:
Debug.Log("AskFeedbackDialog: Star on GitHub selected");
@ -58,9 +51,6 @@
Debug.Log("AskFeedbackDialog: Remind me next time");
EditorPrefs.SetInt(InitializeOnLoadCountKey, 0);
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)]
private static void OpenGitHub()
{
private static void OpenGitHub() {
Application.OpenURL(ExternalLinks.GitHubHome);
}
[MenuItem("ParrelSync/GitHub/View FAQ", priority = 11)]
private static void OpenFAQ()
{
private static void OpenFAQ() {
Application.OpenURL(ExternalLinks.FAQ);
}
[MenuItem("ParrelSync/GitHub/View Issues", priority = 12)]
private static void OpenGitHubIssues()
{
private static void OpenGitHubIssues() {
Application.OpenURL(ExternalLinks.GitHubIssue);
}
}

@ -1,34 +1,29 @@
using UnityEngine;
using UnityEditor;
using UnityEditor;
using UnityEngine;
namespace ParrelSync
{
namespace ParrelSync {
/// <summary>
/// To add value caching for <see cref="EditorPrefs"/> functions
/// To add value caching for <see cref="EditorPrefs" /> functions
/// </summary>
public class BoolPreference
{
public string key { get; private set; }
public bool defaultValue { get; private set; }
public BoolPreference(string key, bool defaultValue)
{
public class BoolPreference {
private bool? valueCache;
public BoolPreference(string key, bool defaultValue) {
this.key = key;
this.defaultValue = defaultValue;
}
private bool? valueCache = null;
public string key { get; }
public bool defaultValue { get; }
public bool Value
{
get
{
public bool Value {
get {
if (valueCache == null)
valueCache = EditorPrefs.GetBool(key, defaultValue);
return (bool)valueCache;
}
set
{
set {
if (valueCache == value)
return;
@ -38,41 +33,30 @@ namespace ParrelSync
}
}
public void ClearValue()
{
public void ClearValue() {
EditorPrefs.DeleteKey(key);
valueCache = null;
}
}
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();
}
public class Preferences : EditorWindow {
/// <summary>
/// Disable asset saving in clone editors?
/// Disable asset saving in clone editors?
/// </summary>
public static BoolPreference AssetModPref = new BoolPreference("ParrelSync_DisableClonesAssetSaving", true);
public static BoolPreference AssetModPref = new("ParrelSync_DisableClonesAssetSaving", true);
/// <summary>
/// In addition of checking the existence of UnityLockFile,
/// also check is the is the UnityLockFile being opened.
/// In addition of checking the existence of UnityLockFile,
/// also check is the is the UnityLockFile being opened.
/// </summary>
public static BoolPreference AlsoCheckUnityLockFileStaPref = new BoolPreference("ParrelSync_CheckUnityLockFileOpenStatus", true);
public static BoolPreference AlsoCheckUnityLockFileStaPref =
new("ParrelSync_CheckUnityLockFileOpenStatus", true);
private void OnGUI()
{
if (ClonesManager.IsClone())
{
private void OnGUI() {
if (ClonesManager.IsClone()) {
EditorGUILayout.HelpBox(
"This is a clone project. Please use the original project editor to change preferences.",
MessageType.Info);
"This is a clone project. Please use the original project editor to change preferences.",
MessageType.Info);
return;
}
@ -88,7 +72,6 @@ namespace ParrelSync
AssetModPref.Value);
if (Application.platform == RuntimePlatform.WindowsEditor)
{
AlsoCheckUnityLockFileStaPref.Value = EditorGUILayout.ToggleLeft(
new GUIContent(
"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"
),
AlsoCheckUnityLockFileStaPref.Value);
}
GUILayout.EndVertical();
if (GUILayout.Button("Reset to default"))
{
if (GUILayout.Button("Reset to default")) {
AssetModPref.ClearValue();
AlsoCheckUnityLockFileStaPref.ClearValue();
Debug.Log("Editor preferences cleared");
}
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;
namespace ParrelSync
{
public class Project : System.ICloneable
{
public string name;
public string projectPath;
string rootPath;
namespace ParrelSync {
public class Project : ICloneable {
public string assetPath;
public string projectSettingsPath;
public string libraryPath;
public string packagesPath;
public string autoBuildPath;
public string libraryPath;
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>
/// Default constructor
/// Default constructor
/// </summary>
public Project()
{
}
public Project() { }
/// <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>
/// <param name="path"></param>
public Project(string path)
{
public Project(string path) {
ParsePath(path);
}
/// <summary>
/// Create a new object with the same settings
/// Create a new object with the same settings
/// </summary>
/// <returns></returns>
public object Clone()
{
Project newProject = new Project();
public object Clone() {
Project newProject = new();
newProject.rootPath = rootPath;
newProject.projectPath = projectPath;
newProject.assetPath = assetPath;
@ -61,22 +56,21 @@ namespace ParrelSync
/// <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>
/// <param name="name"></param>
public void updateNewName(string newName)
{
public void updateNewName(string newName) {
name = newName;
ParsePath(rootPath + "/" + name + "/Assets");
}
/// <summary>
/// Debug override so we can quickly print out the project info.
/// Debug override so we can quickly print out the project info.
/// </summary>
/// <returns></returns>
public override string ToString()
{
public override string ToString() {
string printString = name + "\n" +
rootPath + "\n" +
projectPath + "\n" +
@ -86,16 +80,15 @@ namespace ParrelSync
autoBuildPath + "\n" +
localPackages + "\n" +
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.
projectPath = 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();
pathArray.RemoveAt(pathArray.Count() - 1);

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

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

@ -1,10 +1,15 @@
{
"name": "com.veriorpies.parrelsync",
"displayName": "ParrelSync",
"version": "1.5.1",
"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.",
"license": "MIT",
"keywords": [ "Networking", "Utils", "Editor", "Extensions" ],
"dependencies": {}
"name": "com.veriorpies.parrelsync",
"displayName": "ParrelSync",
"version": "1.5.1",
"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.",
"license": "MIT",
"keywords": [
"Networking",
"Utils",
"Editor",
"Extensions"
],
"dependencies": {}
}

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

@ -1,7 +1,7 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was auto-generated by com.unity.inputsystem:InputActionCodeGenerator
// version 1.5.0
// version 1.5.1
// from Assets/PlayerInputActions.inputactions
//
// 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_Name:
m_EditorClassIdentifier:
cuttingRecipeSOArray:
CuttingRecipeSOArray:
- {fileID: 11400000, guid: 7099fe20df5fcf646869c078799be4e6, type: 2}
- {fileID: 11400000, guid: 15b9c81d8c4442547ab5bc3aa788ecc8, type: 2}
- {fileID: 11400000, guid: 1f51b2ff38b5b7b498f16d7ea781b45a, type: 2}
- {fileID: 11400000, guid: 15b9c81d8c4442547ab5bc3aa788ecc8, type: 2}

@ -1,21 +1,20 @@
using Unity.Netcode.Components;
using UnityEngine;
namespace Unity.Multiplayer.Samples.Utilities.ClientAuthority
{
namespace Unity.Multiplayer.Samples.Utilities.ClientAuthority {
/// <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
/// for transforms that'll always be owned by the server.
/// Used for syncing a transform with client side changes. This includes host. Pure server as owner isn't supported by
/// this. Please use NetworkTransform
/// for transforms that'll always be owned by the server.
/// </summary>
[DisallowMultipleComponent]
public class ClientNetworkTransform : NetworkTransform
{
public class ClientNetworkTransform : NetworkTransform {
/// <summary>
/// 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.
/// 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.
/// </summary>
protected override bool OnIsServerAuthoritative()
{
protected override bool OnIsServerAuthoritative() {
return false;
}
}

@ -3,33 +3,28 @@ using Unity.Netcode;
using UnityEngine;
public class BaseCounter : NetworkBehaviour, IKitchenObjectParent {
public static event EventHandler OnAnyObjectPlacedHere;
public Transform CounterTopPoint { get; set; }
public Transform KitchenObjectHoldPoint { get; set; }
private KitchenObject kitchenObject;
public KitchenObject kitchenObject;
private Transform CounterTopPoint { get; set; }
public Transform GetKitchenObjectFollowTransform() => CounterTopPoint;
public void Awake() {
CounterTopPoint = transform.Find("CounterTopPoint");
}
public KitchenObject KitchenObject {
get => kitchenObject;
set {
kitchenObject = value;
if (value != null) {
OnAnyObjectPlacedHere?.Invoke(this, EventArgs.Empty);
}
if (value is not null) OnAnyObjectPlacedHere?.Invoke(this, EventArgs.Empty);
}
}
public void Awake() {
CounterTopPoint = transform.Find("CounterTopPoint");
KitchenObjectHoldPoint = CounterTopPoint;
}
public NetworkObject GetNetworkObject() => NetworkObject;
public virtual void Interact(Player player) {
Debug.LogError("Something went wrong, Interaction with BaseCounter");
}
public static event EventHandler OnAnyObjectPlacedHere;
public virtual void InteractAlternate(Player player) {
Debug.LogError("Something went wrong, Interaction with BaseCounter");
}
public virtual void Interact(Player player) => 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;
public class ClearCounter : BaseCounter, IKitchenObjectParent
{
public KitchenObjectSO KitchenObjectSO { get; set; }
public class ClearCounter : BaseCounter, IKitchenObjectParent {
public KitchenObjectSO KitchenObjectSO { get; set; }
public override void Interact(Player player)
{
if (KitchenObject == null)
{
// Debug.Log("There is no KitchenObject here");
if (player.KitchenObject != null)
{
//Debug.Log($"Player is putting {player.KitchenObject.KitchenObjectSO.objectName} to ClearCounter");
player.KitchenObject.SetKitchenObjectParent(this);
player.KitchenObject = null;
}
else
{
Debug.Log("Player not carrying anything");
}
}
else
{
// Debug.Log("There is a KitchenObject");
if (player.KitchenObject != null)
{
if (player.KitchenObject.TryGetPlate(out PlateKitchenObject plateKitchenObject))
{
// Debug.Log("Player is holding a plate");
if (plateKitchenObject.TryAddIngredient(KitchenObject.KitchenObjectSO))
{
KitchenObject.DestroySelf();
}
}
else
{
// 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;
}
}
}
public override void Interact(Player player) {
if (KitchenObject == null) {
// Debug.Log("There is no KitchenObject here");
if (player.KitchenObject != null) {
//Debug.Log($"Player is putting {player.KitchenObject.KitchenObjectSO.objectName} to ClearCounter");
player.KitchenObject.SetKitchenObjectParent(this);
player.KitchenObject = null;
}
else {
Debug.Log("Player not carrying anything");
}
}
else {
// Debug.Log("There is a KitchenObject");
if (player.KitchenObject != null) {
if (player.KitchenObject.TryGetPlate(out PlateKitchenObject plateKitchenObject)) {
// Debug.Log("Player is holding a plate");
if (plateKitchenObject.TryAddIngredient(KitchenObject.KitchenObjectSO)) KitchenObject.DestroySelf();
}
else {
// 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 UnityEngine;
public class ContainerCounter : BaseCounter
{
public class ContainerCounter : BaseCounter {
[SerializeField] private KitchenObjectSO kitchenObjectSO;
public event EventHandler OnPlayerGrabbedObject;
public override void Interact(Player player)
{
if (kitchenObjectSO != null && player.KitchenObject == null)
{
public override void Interact(Player player) {
if (kitchenObjectSO != null && player.KitchenObject == null) {
KitchenObject.SpawnKitchenObject(kitchenObjectSO, player);
InteractLogicServerRpc();
}
}
[ServerRpc(RequireOwnership = false)]
private void InteractLogicServerRpc() => InteractLogicClientRpc();
public void InteractLogicServerRpc() {
InteractLogicClientRpc();
}
[ClientRpc]
private void InteractLogicClientRpc() => OnPlayerGrabbedObject?.Invoke(this, EventArgs.Empty);
}
public void InteractLogicClientRpc() {
OnPlayerGrabbedObject?.Invoke(this, EventArgs.Empty);
}
}

@ -1,22 +1,20 @@
using System;
using UnityEngine;
public class ContainerCounterVisual : MonoBehaviour
{
public class ContainerCounterVisual : MonoBehaviour {
private static readonly int openClose = Animator.StringToHash("OpenClose");
[SerializeField] private ContainerCounter containerCounter;
private Animator animator;
private void Awake()
{
private void Awake() {
animator = GetComponent<Animator>();
}
private void Start()
{
private void Start() {
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 UnityEngine;
public class CuttingCounter : BaseCounter, IHasProgress, IKitchenObjectParent
{
[SerializeField] private CuttingRecipeSO[] cuttingRecipeSOArray;
public class CuttingCounter : BaseCounter, IHasProgress {
[SerializeField] private CuttingRecipeSO[] CuttingRecipeSOArray;
private int cuttingProgress;
public static event EventHandler OnAnyCut;
public event EventHandler<IHasProgress.ProgressChangedEventArgs> OnProgressChanged;
public event EventHandler OnCut;
public override void Interact(Player player)
{
if (KitchenObject == null)
{
if (player.KitchenObject == null) return;
public override void Interact(Player player) {
if (KitchenObject == null) {
if (player.KitchenObject is null) return;
if (!HasRecipeWithInput(player.KitchenObject.KitchenObjectSO)) return;
if (HasRecipeWithInput(player.KitchenObject.KitchenObjectSO))
{
KitchenObject kitchenObject = player.KitchenObject;
kitchenObject.SetKitchenObjectParent(this);
player.KitchenObject = null;
InteractLogicPlaceObjectOnCounterServerRpc();
}
KitchenObject kitchenObject = player.KitchenObject;
kitchenObject.SetKitchenObjectParent(this);
player.KitchenObject = null;
InteractLogicPlaceObjectOnCounterServerRpc();
}
else
{
else {
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();
}
else
{
else {
KitchenObject.SetKitchenObjectParent(player);
KitchenObject = null;
}
@ -44,70 +35,59 @@ public class CuttingCounter : BaseCounter, IHasProgress, IKitchenObjectParent
}
[ServerRpc(RequireOwnership = false)]
private void InteractLogicPlaceObjectOnCounterServerRpc() => InteractLogicPlaceObjectOnCounterClientRpc();
public void InteractLogicPlaceObjectOnCounterServerRpc() => InteractLogicPlaceObjectOnCounterClientRpc();
[ClientRpc]
private void InteractLogicPlaceObjectOnCounterClientRpc()
{
public void InteractLogicPlaceObjectOnCounterClientRpc() {
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 (!HasRecipeWithInput(KitchenObject.KitchenObjectSO)) return;
if (HasRecipeWithInput(KitchenObject.KitchenObjectSO))
{
CutObjectServerRpc();
TestCuttingProgressDoneServerRpc();
}
CutObjectServerRpc();
TestCuttingProgressDoneServerRpc();
}
[ServerRpc(RequireOwnership = false)]
private void CutObjectServerRpc() => CutObjectClientRpc();
public void CutObjectServerRpc() => CutObjectClientRpc();
[ClientRpc]
private void CutObjectClientRpc()
{
public void CutObjectClientRpc() {
cuttingProgress++;
OnCut?.Invoke(this, EventArgs.Empty);
OnAnyCut?.Invoke(this, EventArgs.Empty);
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)]
private void TestCuttingProgressDoneServerRpc()
{
public void TestCuttingProgressDoneServerRpc() {
CuttingRecipeSO cuttingRecipeSO = GetCuttingRecipeSOWithInput(KitchenObject.KitchenObjectSO);
if (cuttingRecipeSO is null) return;
if (cuttingProgress < cuttingRecipeSO.cuttingProgressMax) return;
KitchenObjectSO outputKitchenObjectSO = GetOutputForInput(KitchenObject.KitchenObjectSO);
if (outputKitchenObjectSO == null) return;
if (outputKitchenObjectSO is null) return;
KitchenObject.DestroyKitchenObject(KitchenObject);
KitchenObject = null;
KitchenObject.SpawnKitchenObject(outputKitchenObjectSO, this);
}
private bool HasRecipeWithInput(KitchenObjectSO inputKitchenObjectSO)
{
return GetCuttingRecipeSOWithInput(inputKitchenObjectSO) != null;
}
private bool HasRecipeWithInput(KitchenObjectSO inputKitchenObjectSO) => GetCuttingRecipeSOWithInput(inputKitchenObjectSO) is not null;
private KitchenObjectSO GetOutputForInput(KitchenObjectSO inputKitchenObjectSO)
{
private KitchenObjectSO GetOutputForInput(KitchenObjectSO inputKitchenObjectSO) {
CuttingRecipeSO cuttingRecipeSO = GetCuttingRecipeSOWithInput(inputKitchenObjectSO);
return cuttingRecipeSO ? cuttingRecipeSO.output : null;
}
private CuttingRecipeSO GetCuttingRecipeSOWithInput(KitchenObjectSO inputKitchenObjectSO)
{
return cuttingRecipeSOArray.FirstOrDefault(cuttingRecipeSO => cuttingRecipeSO.input == inputKitchenObjectSO);
}
private CuttingRecipeSO GetCuttingRecipeSOWithInput(KitchenObjectSO inputKitchenObjectSO)
=> CuttingRecipeSOArray.FirstOrDefault(cuttingRecipeSO => cuttingRecipeSO.input == inputKitchenObjectSO);
public static void ResetStaticData() => OnAnyCut = null;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -1,19 +1,20 @@
using Unity.Netcode;
using UnityEngine;
public class KitchenGameMultiplayer : NetworkBehaviour
{
public static KitchenGameMultiplayer Instance { get; private set; }
public class KitchenGameMultiplayer : NetworkBehaviour {
[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)]
private void SpawnKitchenObjectServerRpc(int kitchenObjectSOIndex, NetworkObjectReference kitchenObjectParentNetworkObjectReference)
{
public void SpawnKitchenObjectServerRpc(int kitchenObjectSOIndex, NetworkObjectReference kitchenObjectParentNetworkObjectReference) {
KitchenObjectSO kitchenObjectSO = GetKitchenObjectSOFromIndex(kitchenObjectSOIndex);
Transform kitchenObjectTransform = Instantiate(kitchenObjectSO.prefab);
@ -21,23 +22,25 @@ public class KitchenGameMultiplayer : NetworkBehaviour
kitchenObjectNetworkObject.Spawn(true);
KitchenObject kitchenObject = kitchenObjectTransform.GetComponent<KitchenObject>();
kitchenObjectParentNetworkObjectReference.TryGet(out NetworkObject kitchenObjectParentNetworkObject);
IKitchenObjectParent kitchenObjectParent = kitchenObjectParentNetworkObject.GetComponent<IKitchenObjectParent>();
IKitchenObjectParent kitchenObjectParent = kitchenObjectParentNetworkObject.GetComponent<IKitchenObjectParent>();
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)]
public void DestroyKitchenObjectServerRpc(NetworkObjectReference kitchenObjectNetworkObjectReference)
{
public void DestroyKitchenObjectServerRpc(NetworkObjectReference kitchenObjectNetworkObjectReference) {
kitchenObjectNetworkObjectReference.TryGet(out NetworkObject kitchenObjectNetworkObject);
KitchenObject kitchenObject = kitchenObjectNetworkObject.GetComponent<KitchenObject>();
ClearKitchenObjectOnParentClientRpc(kitchenObjectNetworkObjectReference);
@ -45,10 +48,9 @@ public class KitchenGameMultiplayer : NetworkBehaviour
}
[ClientRpc]
private void ClearKitchenObjectOnParentClientRpc(NetworkObjectReference kitchenObjectNetworkObjectReference)
{
public void ClearKitchenObjectOnParentClientRpc(NetworkObjectReference kitchenObjectNetworkObjectReference) {
kitchenObjectNetworkObjectReference.TryGet(out NetworkObject kitchenObjectNetworkObject);
KitchenObject kitchenObject = kitchenObjectNetworkObject.GetComponent<KitchenObject>();
kitchenObject.ClearKitchenObjectOnParent();
}
}
}

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

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

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

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

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

@ -1,5 +1,7 @@
using Unity.Netcode.Components;
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 UnityEngine;
public class PlateCompleteVisual : MonoBehaviour
{
public class PlateCompleteVisual : MonoBehaviour {
[SerializeField] private PlateKitchenObject plateKitchenObject;
[SerializeField] private List<KitchenObjectSOGameObject> kitchenObjectSOGameObjectList;
[Serializable]
public struct KitchenObjectSOGameObject
{
public KitchenObjectSO kitchenObjectSO;
public GameObject gameObject;
}
private void Start() {
plateKitchenObject.OnIngredientAdded += PlateKitchenObject_OnIngredientAdded;
foreach (KitchenObjectSOGameObject kitchenObjectSOGameObject in kitchenObjectSOGameObjectList)
kitchenObjectSOGameObject.gameObject.SetActive(false);
}
[SerializeField] private PlateKitchenObject plateKitchenObject;
[SerializeField] private List<KitchenObjectSOGameObject> kitchenObjectSOGameObjectList;
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);
}
private void Start()
{
plateKitchenObject.OnIngredientAdded += PlateKitchenObject_OnIngredientAdded;
foreach (KitchenObjectSOGameObject kitchenObjectSOGameObject in kitchenObjectSOGameObjectList)
{
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);
}
}
}
}
[Serializable]
public struct KitchenObjectSOGameObject {
public KitchenObjectSO kitchenObjectSO;
public GameObject gameObject;
}
}

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

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

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

@ -1,12 +1,13 @@
using System;
using UnityEngine;
public class PlayerSounds : MonoBehaviour {
private Player player;
private float footstepTimer;
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() {
footstepTimer -= Time.deltaTime;
@ -16,4 +17,4 @@ public class PlayerSounds : MonoBehaviour {
const float volume = 1f;
SoundManager.Instance.PlayFootstepsSound(player.transform.position, volume);
}
}
}

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

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

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

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

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

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

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

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

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

@ -3,13 +3,18 @@ using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class SoundManager : MonoBehaviour
{
public static SoundManager Instance { get; private set; }
public class SoundManager : MonoBehaviour {
private const string PlayerPrefsSoundEffectsVolume = "SoundEffectsVolume";
[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.OnRecipeFailed += DeliveryManager_OnRecipeFailed;
CuttingCounter.OnAnyCut += CuttingCounter_OnAnyCut;
@ -18,84 +23,59 @@ public class SoundManager : MonoBehaviour
TrashCounter.OnAnyObjectTrashed += TrashCounter_OnAnyObjectTrashed;
}
private const string PlayerPrefsSoundEffectsVolume = "SoundEffectsVolume";
private float volume;
private void Awake()
{
Instance = this;
volume = PlayerPrefs.GetFloat(PlayerPrefsSoundEffectsVolume, 1f);
}
private void TrashCounter_OnAnyObjectTrashed(object sender, System.EventArgs e)
{
private void TrashCounter_OnAnyObjectTrashed(object sender, EventArgs e) {
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);
}
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);
}
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);
}
private void DeliveryManager_OnRecipeFailed(object sender, System.EventArgs e)
{
private void DeliveryManager_OnRecipeFailed(object sender, EventArgs e) {
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);
}
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);
}
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);
}
public void PlayFootstepsSound(Vector3 position, float volumeMultiplier)
{
public void PlayFootstepsSound(Vector3 position, float volumeMultiplier) {
PlaySound(audioClipRefsSO.footstep, position, volumeMultiplier * volume);
}
public void PlayCountdownSound()
{
public void PlayCountdownSound() {
PlaySound(audioClipRefsSO.warning, Vector3.zero);
}
public void PlayWarningSound(Vector3 position)
{
public void PlayWarningSound(Vector3 position) {
PlaySound(audioClipRefsSO.warning, position);
}
public void ChangeVolume()
{
public void ChangeVolume() {
volume += .1f;
if (volume > 1.1f)
{
volume = 0f;
}
if (volume > 1.1f) volume = 0f;
PlayerPrefs.SetFloat(PlayerPrefsSoundEffectsVolume, volume);
PlayerPrefs.Save();
}
public float GetVolume()
{
public float GetVolume() {
return volume;
}
}

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

@ -1,63 +1,50 @@
using System;
using UnityEngine;
public class DeliveryManagerUI : MonoBehaviour
{
public static DeliveryManagerUI Instance { get; private set; }
[SerializeField] private Transform container;
[SerializeField] private Transform recipeTemplate;
private void Awake()
{
recipeTemplate.gameObject.SetActive(false);
Instance = this;
}
private void Start()
{
DeliveryManager.Instance.OnRecipeSpawned += DeliveryManager_OnRecipeSpawned;
DeliveryManager.Instance.OnRecipeCompleted += DeliveryManager_OnRecipeCompleted;
Show();
UpdateVisual();
}
private void DeliveryManager_OnRecipeCompleted(object sender, System.EventArgs e)
{
UpdateVisual();
}
private void DeliveryManager_OnRecipeSpawned(object sender, System.EventArgs e)
{
UpdateVisual();
}
private void UpdateVisual()
{
foreach (Transform child in container)
{
if (child == recipeTemplate)
{
continue;
}
Destroy(child.gameObject);
}
foreach (RecipeSO recipeSO in DeliveryManager.Instance.WaitingRecipeSOList)
{
Transform recipeTransform = Instantiate(recipeTemplate, container);
recipeTransform.gameObject.SetActive(true);
recipeTransform.GetComponent<DeliveryManagerSingleUI>().SetRecipeSO(recipeSO);
}
}
public void Hide()
{
gameObject.SetActive(false);
}
public void Show()
{
gameObject.SetActive(true);
}
public class DeliveryManagerUI : MonoBehaviour {
[SerializeField] private Transform container;
[SerializeField] private Transform recipeTemplate;
public static DeliveryManagerUI Instance { get; private set; }
private void Awake() {
recipeTemplate.gameObject.SetActive(false);
Instance = this;
}
private void Start() {
DeliveryManager.Instance.OnRecipeSpawned += DeliveryManager_OnRecipeSpawned;
DeliveryManager.Instance.OnRecipeCompleted += DeliveryManager_OnRecipeCompleted;
Show();
UpdateVisual();
}
private void DeliveryManager_OnRecipeCompleted(object sender, EventArgs e) {
UpdateVisual();
}
private void DeliveryManager_OnRecipeSpawned(object sender, EventArgs e) {
UpdateVisual();
}
private void UpdateVisual() {
foreach (Transform child in container) {
if (child == recipeTemplate) continue;
Destroy(child.gameObject);
}
foreach (RecipeSO recipeSO in DeliveryManager.Instance.WaitingRecipeSOList) {
Transform recipeTransform = Instantiate(recipeTemplate, container);
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.UI;
public class DeliveryResultUI : MonoBehaviour
{
public class DeliveryResultUI : MonoBehaviour {
private const string Popup = "Popup";
[SerializeField] private Image backgroundImage;
[SerializeField] private Image iconImage;
@ -16,26 +15,25 @@ public class DeliveryResultUI : MonoBehaviour
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.OnRecipeFailed += DeliveryManager_OnRecipeFailed;
gameObject.SetActive(false);
}
private void DeliveryManager_OnRecipeFailed(object sender, System.EventArgs e)
{
private void DeliveryManager_OnRecipeFailed(object sender, EventArgs e) {
gameObject.SetActive(true);
animator.SetTrigger(Popup);
backgroundImage.color = failedColor;
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);
animator.SetTrigger(Popup);
backgroundImage.color = successColor;

@ -3,34 +3,28 @@ using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class GameOverUI : MonoBehaviour
{
public static GameOverUI Instance { get; private set; }
public class GameOverUI : MonoBehaviour {
[SerializeField] private TextMeshProUGUI recipeDeliveredText;
[SerializeField] private TextMeshProUGUI pointsText;
[SerializeField] private Button restartButton;
[SerializeField] private Button mainMenuButton;
public static GameOverUI Instance { get; private set; }
private void Awake()
{
private void Awake() {
Instance = this;
restartButton.onClick.AddListener(() => Loader.Load(Loader.Scene.GameScene));
mainMenuButton.onClick.AddListener(() => Loader.Load(Loader.Scene.MainMenuScene));
}
private void Start()
{
private void Start() {
KitchenGameManager.Instance.OnStateChanged += KitchenGameManager_OnStateChanged;
Hide();
restartButton.Select();
}
private void KitchenGameManager_OnStateChanged(object sender, System.EventArgs e)
{
if (KitchenGameManager.Instance.IsGameOver())
{
private void KitchenGameManager_OnStateChanged(object sender, EventArgs e) {
if (KitchenGameManager.Instance.IsGameOver()) {
recipeDeliveredText.text = DeliveryManager.Instance.SuccessfulRecipes.ToString();
pointsText.text = DeliveryManager.Instance.Points.ToString();
Show();
@ -38,8 +32,7 @@ public class GameOverUI : MonoBehaviour
DeliveryManagerUI.Instance.Hide();
PointsUI.Instance.Hide();
}
else
{
else {
Hide();
GamePlayingClockUI.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.UI;
namespace UI
{
public class GamePauseUI : MonoBehaviour
{
namespace UI {
public class GamePauseUI : MonoBehaviour {
[SerializeField] private Button resumeButton;
//TODO: Restart Button
[SerializeField] private Button optionsButton;
[SerializeField] private Button mainMenuButton;
private void Awake()
{
private void Awake() {
resumeButton.onClick.AddListener(() => KitchenGameManager.Instance.TogglePauseGame());
optionsButton.onClick.AddListener(() =>
{
optionsButton.onClick.AddListener(() => {
Hide();
OptionsUI.Instance.Show(Show);
});
mainMenuButton.onClick.AddListener(() => Loader.Load(Loader.Scene.MainMenuScene));
}
private void Start()
{
private void Start() {
KitchenGameManager.Instance.OnGamePaused += KitchenGameManager_OnGamePaused;
KitchenGameManager.Instance.OnGameUnpaused += KitchenGameManager_OnGameUnpaused;
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);
resumeButton.Select();
}
private void Hide() => gameObject.SetActive(false);
private void Hide() {
gameObject.SetActive(false);
}
}
}

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

@ -2,43 +2,44 @@ using System;
using TMPro;
using UnityEngine;
public class GameStartCountdownUI : MonoBehaviour
{
public class GameStartCountdownUI : MonoBehaviour {
private const string NumberPopup = "NumberPopup";
[SerializeField] private TextMeshProUGUI countdownText;
private Animator animator;
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;
Hide();
}
private void KitchenGameManager_OnStateChanged(object sender, System.EventArgs e)
{
if (KitchenGameManager.Instance.IsCountToStartActive())
Show();
else
Hide();
}
private void Update()
{
private void Update() {
int countdownNumber = Mathf.CeilToInt(KitchenGameManager.Instance.CountdownToStartTimer);
countdownText.text = countdownNumber.ToString();
if (previousCountdownNumber != countdownNumber)
{
if (previousCountdownNumber != countdownNumber) {
previousCountdownNumber = countdownNumber;
animator.SetTrigger(NumberPopup);
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.UI;
namespace UI
{
public class MainMenuUI : MonoBehaviour
{
namespace UI {
public class MainMenuUI : MonoBehaviour {
[SerializeField] private Button playButton;
[SerializeField] private Button quitButton;
private void Start()
{
playButton.Select();
}
private void Awake()
{
private void Awake() {
playButton.onClick.AddListener(() => Loader.Load(Loader.Scene.GameScene));
quitButton.onClick.AddListener(Application.Quit);
}
private void Start() {
playButton.Select();
}
}
}

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

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

@ -1,25 +1,28 @@
using UnityEngine;
public class PlateIconsUI : MonoBehaviour
{
public class PlateIconsUI : MonoBehaviour {
[SerializeField] private PlateKitchenObject plateKitchenObject;
[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()
{
foreach (Transform child in transform)
{
private void UpdateVisual() {
foreach (Transform child in transform) {
if (child == iconTemplate) continue;
Destroy(child.gameObject);
}
foreach (KitchenObjectSO kitchenObjectSO in plateKitchenObject.GetKitchenObjectSOList())
{
foreach (KitchenObjectSO kitchenObjectSO in plateKitchenObject.GetKitchenObjectSOList()) {
Transform iconTransform = Instantiate(iconTemplate, transform);
iconTransform.gameObject.SetActive(true);
iconTransform.GetComponent<PlateIconSingleUI>().SetKitchenObjectSO(kitchenObjectSO);

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

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

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

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

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

@ -1,38 +1,28 @@
using UnityEngine;
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 class Benchmark01 : MonoBehaviour
{
public int BenchmarkType = 0;
public int BenchmarkType;
public TMP_FontAsset TMProFont;
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 int m_frame;
private Material m_material01;
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
{
m_textMeshPro = gameObject.AddComponent<TextMeshPro>();
@ -54,27 +44,25 @@ namespace TMPro.Examples
//m_textMeshPro.fontSharedMaterial.SetFloat("_OutlineWidth", 0.2f);
//m_textMeshPro.fontSharedMaterial.EnableKeyword("UNDERLAY_ON");
//m_textMeshPro.lineJustification = LineJustificationTypes.Center;
m_textMeshPro.enableWordWrapping = false;
m_textMeshPro.enableWordWrapping = false;
//m_textMeshPro.lineLength = 60;
//m_textMeshPro.characterSpacing = 0.2f;
//m_textMeshPro.fontColor = new Color32(255, 255, 255, 255);
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
{
m_textMesh = gameObject.AddComponent<TextMesh>();
if (TextMeshFont != null)
{
if (TextMeshFont != null) {
m_textMesh.font = TextMeshFont;
m_textMesh.GetComponent<Renderer>().sharedMaterial = m_textMesh.font.material;
}
else
{
else {
m_textMesh.font = Resources.Load("Fonts/ARIAL", typeof(Font)) as Font;
m_textMesh.GetComponent<Renderer>().sharedMaterial = m_textMesh.font.material;
}
@ -86,20 +74,17 @@ namespace TMPro.Examples
}
for (int i = 0; i <= 1000000; i++)
{
if (BenchmarkType == 0)
{
for (int i = 0; i <= 1000000; i++) {
if (BenchmarkType == 0) {
m_textMeshPro.SetText(label01, i % 1000);
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;
}
@ -125,4 +110,4 @@ namespace TMPro.Examples
}
*/
}
}
}

@ -1,27 +1,18 @@
using UnityEngine;
using System.Collections;
using UnityEngine;
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 class Benchmark01_UGUI : MonoBehaviour
{
public int BenchmarkType = 0;
public int BenchmarkType;
public Canvas canvas;
public TMP_FontAsset TMProFont;
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 label02 = "Text Mesh<color=#0050FF> The count is: </color>";
@ -29,15 +20,16 @@ namespace TMPro.Examples
//private int m_frame;
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
{
m_textMeshPro = gameObject.AddComponent<TextMeshProUGUI>();
@ -66,25 +58,21 @@ namespace TMPro.Examples
//m_textMeshPro.fontColor = new Color32(255, 255, 255, 255);
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
{
m_textMesh = gameObject.AddComponent<Text>();
if (TextMeshFont != null)
{
if (TextMeshFont != null) {
m_textMesh.font = TextMeshFont;
//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.alignment = TextAnchor.MiddleCenter;
@ -92,20 +80,17 @@ namespace TMPro.Examples
}
for (int i = 0; i <= 1000000; i++)
{
if (BenchmarkType == 0)
{
m_textMeshPro.text = label01 + (i % 1000);
for (int i = 0; i <= 1000000; i++) {
if (BenchmarkType == 0) {
m_textMeshPro.text = label01 + i % 1000;
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;
}
@ -131,5 +116,4 @@ namespace TMPro.Examples
}
*/
}
}
}

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

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

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

@ -1,16 +1,19 @@
using UnityEngine;
using System.Collections;
namespace TMPro.Examples {
public class CameraController : MonoBehaviour {
public enum CameraModes {
Follow,
Isometric,
Free
}
namespace TMPro.Examples
{
public class CameraController : MonoBehaviour
{
public enum CameraModes { Follow, Isometric, Free }
// Controls for Touches on Mobile devices
//private float prev_ZoomDelta;
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;
@ -20,38 +23,33 @@ namespace TMPro.Examples
public float ElevationAngle = 30.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 bool MovementSmoothing = true;
public bool RotationSmoothing = false;
private bool previousSmoothing;
public bool RotationSmoothing;
public float MovementSmoothingValue = 25f;
public float RotationSmoothingValue = 5.0f;
public float MoveSensitivity = 2.0f;
private Transform cameraTransform;
private Vector3 currentVelocity = Vector3.zero;
private Vector3 desiredPosition;
private Transform dummyTarget;
private float mouseWheel;
private float mouseX;
private float mouseY;
private Vector3 moveVector;
private float mouseWheel;
// 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";
private bool previousSmoothing;
void Awake()
{
private void Awake() {
if (QualitySettings.vSyncCount > 0)
Application.targetFrameRate = 60;
else
@ -66,10 +64,8 @@ namespace TMPro.Examples
// Use this for initialization
void Start()
{
if (CameraTarget == null)
{
private void Start() {
if (CameraTarget == null) {
// 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;
CameraTarget = dummyTarget;
@ -77,54 +73,42 @@ namespace TMPro.Examples
}
// Update is called once per frame
void LateUpdate()
{
private void LateUpdate() {
GetPlayerInput();
// Check if we still have a valid target
if (CameraTarget != null)
{
if (CameraMode == CameraModes.Isometric)
{
desiredPosition = CameraTarget.position + Quaternion.Euler(ElevationAngle, OrbitalAngle, 0f) * new Vector3(0, 0, -FollowDistance);
if (CameraTarget != null) {
if (CameraMode == CameraModes.Isometric) {
desiredPosition = CameraTarget.position + Quaternion.Euler(ElevationAngle, OrbitalAngle, 0f) *
new Vector3(0, 0, -FollowDistance);
}
else if (CameraMode == CameraModes.Follow)
{
desiredPosition = CameraTarget.position + CameraTarget.TransformDirection(Quaternion.Euler(ElevationAngle, OrbitalAngle, 0f) * (new Vector3(0, 0, -FollowDistance)));
}
else
{
// Free Camera implementation
else if (CameraMode == CameraModes.Follow) {
desiredPosition = CameraTarget.position + CameraTarget.TransformDirection(
Quaternion.Euler(ElevationAngle, OrbitalAngle, 0f) * new Vector3(0, 0, -FollowDistance));
}
if (MovementSmoothing == true)
{
// Free Camera implementation
if (MovementSmoothing)
// Using Smoothing
cameraTransform.position = Vector3.SmoothDamp(cameraTransform.position, desiredPosition, ref currentVelocity, MovementSmoothingValue * Time.fixedDeltaTime);
//cameraTransform.position = Vector3.Lerp(cameraTransform.position, desiredPosition, Time.deltaTime * 5.0f);
}
cameraTransform.position = Vector3.SmoothDamp(cameraTransform.position, desiredPosition,
ref currentVelocity, MovementSmoothingValue * Time.fixedDeltaTime);
//cameraTransform.position = Vector3.Lerp(cameraTransform.position, desiredPosition, Time.deltaTime * 5.0f);
else
{
// Not using Smoothing
cameraTransform.position = desiredPosition;
}
if (RotationSmoothing == true)
cameraTransform.rotation = Quaternion.Lerp(cameraTransform.rotation, Quaternion.LookRotation(CameraTarget.position - cameraTransform.position), RotationSmoothingValue * Time.deltaTime);
if (RotationSmoothing)
cameraTransform.rotation = Quaternion.Lerp(cameraTransform.rotation,
Quaternion.LookRotation(CameraTarget.position - cameraTransform.position),
RotationSmoothingValue * Time.deltaTime);
else
{
cameraTransform.LookAt(CameraTarget);
}
}
}
void GetPlayerInput()
{
private void GetPlayerInput() {
moveVector = Vector3.zero;
// 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;
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift) || touchCount > 0)
{
if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift) || touchCount > 0) {
mouseWheel *= 10;
if (Input.GetKeyDown(KeyCode.I))
@ -147,20 +130,17 @@ namespace TMPro.Examples
// 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");
mouseX = Input.GetAxis("Mouse X");
if (mouseY > 0.01f || mouseY < -0.01f)
{
if (mouseY > 0.01f || mouseY < -0.01f) {
ElevationAngle -= mouseY * MoveSensitivity;
// Limit Elevation angle between min & max values.
ElevationAngle = Mathf.Clamp(ElevationAngle, MinElevationAngle, MaxElevationAngle);
}
if (mouseX > 0.01f || mouseX < -0.01f)
{
if (mouseX > 0.01f || mouseX < -0.01f) {
OrbitalAngle += mouseX * MoveSensitivity;
if (OrbitalAngle > 360)
OrbitalAngle -= 360;
@ -170,13 +150,11 @@ namespace TMPro.Examples
}
// 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;
// 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;
// Limit Elevation angle between min & max values.
ElevationAngle = Mathf.Clamp(ElevationAngle, MinElevationAngle, MaxElevationAngle);
@ -184,45 +162,36 @@ namespace TMPro.Examples
// 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;
if (OrbitalAngle > 360)
OrbitalAngle -= 360;
if (OrbitalAngle < 0)
OrbitalAngle += 360;
}
}
// 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);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 300, 1 << 10 | 1 << 11 | 1 << 12 | 1 << 14))
{
if (hit.transform == CameraTarget)
{
if (Physics.Raycast(ray, out hit, 300, (1 << 10) | (1 << 11) | (1 << 12) | (1 << 14))) {
if (hit.transform == CameraTarget) {
// Reset Follow Position
OrbitalAngle = 0;
}
else
{
else {
CameraTarget = hit.transform;
OrbitalAngle = 0;
MovementSmoothing = previousSmoothing;
}
}
}
if (Input.GetMouseButton(2))
{
if (dummyTarget == null)
{
if (Input.GetMouseButton(2)) {
if (dummyTarget == null) {
// We need a Dummy Target to anchor the Camera
dummyTarget = new GameObject("Camera Target").transform;
dummyTarget.position = CameraTarget.position;
@ -231,8 +200,7 @@ namespace TMPro.Examples
previousSmoothing = MovementSmoothing;
MovementSmoothing = false;
}
else if (dummyTarget != CameraTarget)
{
else if (dummyTarget != CameraTarget) {
// Move DummyTarget to CameraTarget
dummyTarget.position = CameraTarget.position;
dummyTarget.rotation = CameraTarget.rotation;
@ -248,14 +216,11 @@ namespace TMPro.Examples
moveVector = cameraTransform.TransformDirection(mouseX, mouseY, 0);
dummyTarget.Translate(-moveVector, Space.World);
}
}
// Check Pinching to Zoom in - out on Mobile device
if (touchCount == 2)
{
if (touchCount == 2) {
Touch touch0 = Input.GetTouch(0);
Touch touch1 = Input.GetTouch(1);
@ -267,26 +232,19 @@ namespace TMPro.Examples
float zoomDelta = prevTouchDelta - touchDelta;
if (zoomDelta > 0.01f || zoomDelta < -0.01f)
{
if (zoomDelta > 0.01f || zoomDelta < -0.01f) {
FollowDistance += zoomDelta * 0.25f;
// Limit FollowDistance between min & max values.
FollowDistance = Mathf.Clamp(FollowDistance, MinFollowDistance, MaxFollowDistance);
}
}
// Check MouseWheel to Zoom in-out
if (mouseWheel < -0.01f || mouseWheel > 0.01f)
{
if (mouseWheel < -0.01f || mouseWheel > 0.01f) {
FollowDistance -= mouseWheel * 5.0f;
// Limit FollowDistance between min & max values.
FollowDistance = Mathf.Clamp(FollowDistance, MinFollowDistance, MaxFollowDistance);
}
}
}
}

@ -1,38 +1,34 @@
using UnityEngine;
using UnityEngine.UI;
using System;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class ChatController : MonoBehaviour {
public TMP_InputField ChatInputField;
public TMP_Text ChatDisplayOutput;
public Scrollbar ChatScrollbar;
void OnEnable()
{
private void OnEnable() {
ChatInputField.onSubmit.AddListener(AddToChatOutput);
}
void OnDisable()
{
private void OnDisable() {
ChatInputField.onSubmit.RemoveListener(AddToChatOutput);
}
void AddToChatOutput(string newText)
{
private void AddToChatOutput(string newText) {
// Clear Input Field
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
// Add line feed before each subsequent entries
if (ChatDisplayOutput.text == string.Empty)
@ -47,5 +43,4 @@ public class ChatController : MonoBehaviour {
// Set the scrollbar to the bottom when next text is submitted.
ChatScrollbar.value = 0;
}
}
}

@ -1,19 +1,16 @@
using TMPro;
using UnityEngine;
public class DropdownSample: MonoBehaviour
{
[SerializeField]
private TextMeshProUGUI text = null;
public class DropdownSample : MonoBehaviour {
[SerializeField] private TextMeshProUGUI text;
[SerializeField]
private TMP_Dropdown dropdownWithoutPlaceholder = null;
[SerializeField] private TMP_Dropdown dropdownWithoutPlaceholder;
[SerializeField]
private TMP_Dropdown dropdownWithPlaceholder = null;
[SerializeField] private TMP_Dropdown dropdownWithPlaceholder;
public void OnButtonClick()
{
text.text = dropdownWithPlaceholder.value > -1 ? "Selected values:\n" + dropdownWithoutPlaceholder.value + " - " + dropdownWithPlaceholder.value : "Error: Please make a selection";
}
}
public void OnButtonClick() {
text.text = dropdownWithPlaceholder.value > -1
? "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 UnityEngine;
public class EnvMapAnimator : MonoBehaviour {
//private Vector3 TranslationSpeeds;
public Vector3 RotationSpeeds;
private TMP_Text m_textMeshPro;
private Material m_material;
private TMP_Text m_textMeshPro;
void Awake()
{
private void Awake() {
//Debug.Log("Awake() on Script called.");
m_textMeshPro = GetComponent<TMP_Text>();
m_material = m_textMeshPro.fontSharedMaterial;
}
// Use this for initialization
IEnumerator Start ()
{
Matrix4x4 matrix = new Matrix4x4();
while (true)
{
private IEnumerator Start() {
Matrix4x4 matrix = new();
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(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);
yield return null;
}
}
}
}
}

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

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

@ -1,22 +1,16 @@
using UnityEngine;
using System.Collections;
namespace TMPro.Examples
{
public class SimpleScript : MonoBehaviour
{
private TextMeshPro m_textMeshPro;
namespace TMPro.Examples {
public class SimpleScript : MonoBehaviour {
//private TMP_FontAsset m_FontAsset;
private const string label = "The <#0050FF>count is: </color>{0:2}";
private float m_frame;
private TextMeshPro m_textMeshPro;
void Start()
{
private void Start() {
// Add new TextMesh Pro Component
m_textMeshPro = gameObject.AddComponent<TextMeshPro>();
@ -29,12 +23,12 @@ namespace TMPro.Examples
// Assign Material to TextMesh Pro Component
//m_textMeshPro.fontSharedMaterial = Resources.Load("Fonts & Materials/LiberationSans SDF - Bevel", typeof(Material)) as Material;
//m_textMeshPro.fontSharedMaterial.EnableKeyword("BEVEL_ON");
// Set various font settings.
m_textMeshPro.fontSize = 48;
m_textMeshPro.alignment = TextAlignmentOptions.Center;
//m_textMeshPro.anchorDampening = true; // Has been deprecated but under consideration for re-implementation.
//m_textMeshPro.enableAutoSizing = true;
@ -48,11 +42,9 @@ namespace TMPro.Examples
}
void Update()
{
private void Update() {
m_textMeshPro.SetText(label, m_frame % 1000);
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 SpeedMultiplier = 1.0f;
public float CurveScale = 1.0f;
public float ShearAmount = 1.0f;
void Awake()
{
private TMP_Text m_TextComponent;
private void Awake() {
m_TextComponent = gameObject.GetComponent<TMP_Text>();
}
void Start()
{
private void Start() {
StartCoroutine(WarpText());
}
private AnimationCurve CopyAnimationCurve(AnimationCurve curve)
{
AnimationCurve newCurve = new AnimationCurve();
private AnimationCurve CopyAnimationCurve(AnimationCurve curve) {
AnimationCurve newCurve = new();
newCurve.keys = curve.keys;
@ -39,12 +33,11 @@ namespace TMPro.Examples
/// <summary>
/// Method to curve text along a Unity animation curve.
/// Method to curve text along a Unity animation curve.
/// </summary>
/// <param name="textComponent"></param>
/// <returns></returns>
IEnumerator WarpText()
{
private IEnumerator WarpText() {
VertexCurve.preWrapMode = WrapMode.Clamp;
VertexCurve.postWrapMode = WrapMode.Clamp;
@ -59,10 +52,9 @@ namespace TMPro.Examples
float old_ShearValue = ShearAmount;
AnimationCurve old_curve = CopyAnimationCurve(VertexCurve);
while (true)
{
if (!m_TextComponent.havePropertiesChanged && old_CurveScale == CurveScale && old_curve.keys[1].value == VertexCurve.keys[1].value && old_ShearValue == ShearAmount)
{
while (true) {
if (!m_TextComponent.havePropertiesChanged && old_CurveScale == CurveScale &&
old_curve.keys[1].value == VertexCurve.keys[1].value && old_ShearValue == ShearAmount) {
yield return null;
continue;
}
@ -71,7 +63,8 @@ namespace TMPro.Examples
old_curve = CopyAnimationCurve(VertexCurve);
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;
int characterCount = textInfo.characterCount;
@ -82,13 +75,11 @@ namespace TMPro.Examples
//vertices = textInfo.meshInfo[0].vertices;
//int lastVertexIndex = textInfo.characterInfo[characterCount - 1].vertexIndex;
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 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;
for (int i = 0; i < characterCount; i++)
{
for (int i = 0; i < characterCount; i++) {
if (!textInfo.characterInfo[i].isVisible)
continue;
@ -100,7 +91,9 @@ namespace TMPro.Examples
vertices = textInfo.meshInfo[materialIndex].vertices;
// 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);
// Apply offset to adjust our pivot point.
@ -111,8 +104,13 @@ namespace TMPro.Examples
// Apply the Shearing FX
float shear_value = ShearAmount * 0.01f;
Vector3 topShear = new Vector3(shear_value * (textInfo.characterInfo[i].topRight.y - textInfo.characterInfo[i].baseLine), 0, 0);
Vector3 bottomShear = new Vector3(shear_value * (textInfo.characterInfo[i].baseLine - textInfo.characterInfo[i].bottomRight.y), 0, 0);
Vector3 topShear =
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 + 1] += topShear;
@ -121,14 +119,16 @@ namespace TMPro.Examples
// 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 y0 = VertexCurve.Evaluate(x0) * 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 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;
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>
/// 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>
[Serializable]
//[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
public override char Validate(ref string text, ref int pos, char ch)
{
if (ch >= '0' && ch <= '9')
{
public override char Validate(ref string text, ref int pos, char ch) {
if (ch >= '0' && ch <= '9') {
text += ch;
pos += 1;
return ch;
@ -24,4 +18,4 @@ namespace TMPro
return (char)0;
}
}
}
}

@ -1,29 +1,24 @@
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
{
public enum objectType { TextMeshPro = 0, TextMeshProUGUI = 1 };
private const string k_label = "The count is <#0080ff>{0}</color>";
public objectType ObjectType;
public bool isStatic;
private int count;
private TMP_Text m_text;
//private TMP_InputField m_inputfield;
private const string k_label = "The count is <#0080ff>{0}</color>";
private int count;
void Awake()
{
private void Awake() {
// 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.
if (ObjectType == 0)
@ -51,14 +46,11 @@ namespace TMPro.Examples
}
void Update()
{
if (!isStatic)
{
private void Update() {
if (!isStatic) {
m_text.SetText(k_label, count % 1000);
count += 1;
}
}
}
}
}

@ -1,42 +1,42 @@
using UnityEngine;
using System.Collections;
namespace TMPro.Examples {
public class TMP_FrameRateCounter : MonoBehaviour {
public enum FpsCounterAnchorPositions {
TopLeft,
BottomLeft,
TopRight,
BottomRight
}
namespace TMPro.Examples
{
public class TMP_FrameRateCounter : MonoBehaviour
{
private const string fpsLabel = "{0:2}</color> <#8080ff>FPS \n<#FF8000>{1:2} <#8080ff>MS";
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;
private string htmlColorTag;
private const string fpsLabel = "{0:2}</color> <#8080ff>FPS \n<#FF8000>{1:2} <#8080ff>MS";
private TextMeshPro m_TextMeshPro;
private Transform m_frameCounter_transform;
private FpsCounterAnchorPositions last_AnchorPosition;
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)
return;
m_camera = Camera.main;
Application.targetFrameRate = 9999;
GameObject frameCounter = new GameObject("Frame Counter");
GameObject frameCounter = new("Frame Counter");
m_TextMeshPro = frameCounter.AddComponent<TextMeshPro>();
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;
@ -57,18 +57,14 @@ namespace TMPro.Examples
Set_FrameCounter_Position(AnchorPosition);
last_AnchorPosition = AnchorPosition;
}
void Start()
{
private void Start() {
m_LastInterval = Time.realtimeSinceStartup;
m_Frames = 0;
}
void Update()
{
private void Update() {
if (AnchorPosition != last_AnchorPosition)
Set_FrameCounter_Position(AnchorPosition);
@ -77,8 +73,7 @@ namespace TMPro.Examples
m_Frames += 1;
float timeNow = Time.realtimeSinceStartup;
if (timeNow > m_LastInterval + UpdateInterval)
{
if (timeNow > m_LastInterval + UpdateInterval) {
// display two fractional digits (f2 format)
float fps = m_Frames / (timeNow - m_LastInterval);
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.");
m_TextMeshPro.margin = new Vector4(1f, 1f, 1f, 1f);
switch (anchor_position)
{
switch (anchor_position) {
case FpsCounterAnchorPositions.TopLeft:
m_TextMeshPro.alignment = TextAlignmentOptions.TopLeft;
m_TextMeshPro.rectTransform.pivot = new Vector2(0, 1);
@ -131,4 +124,4 @@ namespace TMPro.Examples
}
}
}
}
}

@ -1,21 +1,17 @@
using UnityEngine;
using System.Collections;
using System;
using System;
using UnityEngine;
namespace TMPro
{
namespace TMPro {
/// <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>
[Serializable]
//[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
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...");
// Return unless the character is a valid digit
if (ch < '0' && ch > '9') return (char)0;
@ -23,9 +19,7 @@ namespace TMPro
// Enforce Phone Number format for every character input.
for (int i = 0; i < length + 1; i++)
{
switch (i)
{
switch (i) {
case 0:
if (i == length)
text = "(" + ch;
@ -97,9 +91,8 @@ namespace TMPro
pos = 14;
break;
}
}
return ch;
}
}
}
}

@ -1,22 +1,16 @@
using UnityEngine;
namespace TMPro.Examples
{
public class TMP_TextEventCheck : MonoBehaviour
{
namespace TMPro.Examples {
public class TMP_TextEventCheck : MonoBehaviour {
public TMP_TextEventHandler TextEventHandler;
private TMP_Text m_TextComponent;
void OnEnable()
{
if (TextEventHandler != null)
{
private void OnEnable() {
if (TextEventHandler != null) {
// Get a reference to the text component
m_TextComponent = TextEventHandler.GetComponent<TMP_Text>();
TextEventHandler.onCharacterSelection.AddListener(OnCharacterSelection);
TextEventHandler.onSpriteSelection.AddListener(OnSpriteSelection);
TextEventHandler.onWordSelection.AddListener(OnWordSelection);
@ -26,10 +20,8 @@ namespace TMPro.Examples
}
void OnDisable()
{
if (TextEventHandler != null)
{
private void OnDisable() {
if (TextEventHandler != null) {
TextEventHandler.onCharacterSelection.RemoveListener(OnCharacterSelection);
TextEventHandler.onSpriteSelection.RemoveListener(OnSpriteSelection);
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.");
}
void OnSpriteSelection(char c, int index)
{
private void OnSpriteSelection(char c, int index) {
Debug.Log("Sprite [" + c + "] at Index: " + index + " has been selected.");
}
void OnWordSelection(string word, int firstCharacterIndex, int length)
{
Debug.Log("Word [" + word + "] with first character index of " + firstCharacterIndex + " and length of " + length + " has been selected.");
private void OnWordSelection(string word, int firstCharacterIndex, int length) {
Debug.Log("Word [" + word + "] with first character index of " + firstCharacterIndex + " and length of " +
length + " has been selected.");
}
void OnLineSelection(string lineText, int firstCharacterIndex, int length)
{
Debug.Log("Line [" + lineText + "] with first character index of " + firstCharacterIndex + " and length of " + length + " has been selected.");
private void OnLineSelection(string lineText, int firstCharacterIndex, int length) {
Debug.Log("Line [" + lineText + "] with first character index of " + firstCharacterIndex +
" and length of " + length + " has been selected.");
}
void OnLinkSelection(string linkID, string linkText, int linkIndex)
{
if (m_TextComponent != null)
{
private void OnLinkSelection(string linkID, string linkText, int linkIndex) {
if (m_TextComponent != null) {
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.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
{
[Serializable]
public class CharacterSelectionEvent : UnityEvent<char, int> { }
[SerializeField] private WordSelectionEvent m_OnWordSelection = new();
[Serializable]
public class SpriteSelectionEvent : UnityEvent<char, int> { }
[SerializeField] private LineSelectionEvent m_OnLineSelection = new();
[Serializable]
public class WordSelectionEvent : UnityEvent<string, int, int> { }
[SerializeField] private LinkSelectionEvent m_OnLinkSelection = new();
[Serializable]
public class LineSelectionEvent : UnityEvent<string, int, int> { }
private Camera m_Camera;
private Canvas m_Canvas;
private int m_lastCharIndex = -1;
private int m_lastLineIndex = -1;
private int m_lastWordIndex = -1;
[Serializable]
public class LinkSelectionEvent : UnityEvent<string, string, int> { }
private int m_selectedLink = -1;
private TMP_Text m_TextComponent;
/// <summary>
/// Event delegate triggered when pointer is over a character.
/// Event delegate triggered when pointer is over a character.
/// </summary>
public CharacterSelectionEvent onCharacterSelection
{
get { return m_OnCharacterSelection; }
set { m_OnCharacterSelection = value; }
public CharacterSelectionEvent onCharacterSelection {
get => m_OnCharacterSelection;
set => m_OnCharacterSelection = value;
}
[SerializeField]
private CharacterSelectionEvent m_OnCharacterSelection = new CharacterSelectionEvent();
/// <summary>
/// Event delegate triggered when pointer is over a sprite.
/// Event delegate triggered when pointer is over a sprite.
/// </summary>
public SpriteSelectionEvent onSpriteSelection
{
get { return m_OnSpriteSelection; }
set { m_OnSpriteSelection = value; }
public SpriteSelectionEvent onSpriteSelection {
get => m_OnSpriteSelection;
set => m_OnSpriteSelection = value;
}
[SerializeField]
private SpriteSelectionEvent m_OnSpriteSelection = new SpriteSelectionEvent();
/// <summary>
/// Event delegate triggered when pointer is over a word.
/// Event delegate triggered when pointer is over a word.
/// </summary>
public WordSelectionEvent onWordSelection
{
get { return m_OnWordSelection; }
set { m_OnWordSelection = value; }
public WordSelectionEvent onWordSelection {
get => m_OnWordSelection;
set => m_OnWordSelection = value;
}
[SerializeField]
private WordSelectionEvent m_OnWordSelection = new WordSelectionEvent();
/// <summary>
/// Event delegate triggered when pointer is over a line.
/// Event delegate triggered when pointer is over a line.
/// </summary>
public LineSelectionEvent onLineSelection
{
get { return m_OnLineSelection; }
set { m_OnLineSelection = value; }
public LineSelectionEvent onLineSelection {
get => m_OnLineSelection;
set => m_OnLineSelection = value;
}
[SerializeField]
private LineSelectionEvent m_OnLineSelection = new LineSelectionEvent();
/// <summary>
/// Event delegate triggered when pointer is over a link.
/// Event delegate triggered when pointer is over a link.
/// </summary>
public LinkSelectionEvent onLinkSelection
{
get { return m_OnLinkSelection; }
set { m_OnLinkSelection = value; }
public LinkSelectionEvent onLinkSelection {
get => m_OnLinkSelection;
set => m_OnLinkSelection = value;
}
[SerializeField]
private LinkSelectionEvent m_OnLinkSelection = new LinkSelectionEvent();
private TMP_Text m_TextComponent;
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()
{
private void Awake() {
// Get a reference to the text component.
m_TextComponent = gameObject.GetComponent<TMP_Text>();
// 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>();
if (m_Canvas != null)
{
if (m_Canvas != null) {
if (m_Canvas.renderMode == RenderMode.ScreenSpaceOverlay)
m_Camera = null;
else
m_Camera = m_Canvas.worldCamera;
}
}
else
{
else {
m_Camera = Camera.main;
}
}
void LateUpdate()
{
if (TMP_TextUtilities.IsIntersectingRectTransform(m_TextComponent.rectTransform, Input.mousePosition, m_Camera))
{
private void LateUpdate() {
if (TMP_TextUtilities.IsIntersectingRectTransform(m_TextComponent.rectTransform, Input.mousePosition,
m_Camera)) {
#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;
TMP_TextElementType elementType = m_TextComponent.textInfo.characterInfo[charIndex].elementType;
// Send event to any event listeners depending on whether it is a character or sprite.
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)
SendOnSpriteSelection(m_TextComponent.textInfo.characterInfo[charIndex].character, charIndex);
}
#endregion
#region Example of Word Selection
// 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);
if (wordIndex != -1 && wordIndex != m_lastWordIndex)
{
if (wordIndex != -1 && wordIndex != m_lastWordIndex) {
m_lastWordIndex = wordIndex;
// Get the information about the selected word.
@ -154,14 +127,15 @@ namespace TMPro
// Send the event to any listeners.
SendOnWordSelection(wInfo.GetWord(), wInfo.firstCharacterIndex, wInfo.characterCount);
}
#endregion
#region Example of Line Selection
// 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);
if (lineIndex != -1 && lineIndex != m_lastLineIndex)
{
if (lineIndex != -1 && lineIndex != m_lastLineIndex) {
m_lastLineIndex = lineIndex;
// Get the information about the selected word.
@ -169,24 +143,25 @@ namespace TMPro
// Send the event to any listeners.
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;
}
string lineText = new string(buffer);
string lineText = new(buffer);
SendOnLineSelection(lineText, lineInfo.firstCharacterIndex, lineInfo.characterCount);
}
#endregion
#region Example of Link Handling
// Check if mouse intersects with any links.
int linkIndex = TMP_TextUtilities.FindIntersectingLink(m_TextComponent, Input.mousePosition, m_Camera);
// Handle new Link selection.
if (linkIndex != -1 && linkIndex != m_selectedLink)
{
if (linkIndex != -1 && linkIndex != m_selectedLink) {
m_selectedLink = linkIndex;
// Get information about the link.
@ -195,10 +170,10 @@ namespace TMPro
// Send the event to any listeners.
SendOnLinkSelection(linkInfo.GetLinkID(), linkInfo.GetLinkText(), linkIndex);
}
#endregion
}
else
{
else {
// Reset all selections given we are hovering outside the text container bounds.
m_selectedLink = -1;
m_lastCharIndex = -1;
@ -208,47 +183,54 @@ namespace TMPro
}
public void OnPointerEnter(PointerEventData eventData)
{
public void OnPointerEnter(PointerEventData eventData) {
//Debug.Log("OnPointerEnter()");
}
public void OnPointerExit(PointerEventData eventData)
{
public void OnPointerExit(PointerEventData eventData) {
//Debug.Log("OnPointerExit()");
}
private void SendOnCharacterSelection(char character, int characterIndex)
{
private void SendOnCharacterSelection(char character, int characterIndex) {
if (onCharacterSelection != null)
onCharacterSelection.Invoke(character, characterIndex);
}
private void SendOnSpriteSelection(char character, int characterIndex)
{
private void SendOnSpriteSelection(char character, int characterIndex) {
if (onSpriteSelection != null)
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)
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)
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)
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 UnityEngine;
using System.Collections;
using UnityEditor;
using UnityEngine;
namespace TMPro.Examples
{
public class TMP_TextInfoDebugTool : MonoBehaviour
{
namespace TMPro.Examples {
public class TMP_TextInfoDebugTool : MonoBehaviour {
// Since this script is used for debugging, we exclude it from builds.
// TODO: Rework this script to make it into an editor utility.
#if UNITY_EDITOR
#if UNITY_EDITOR
public bool ShowCharacters;
public bool ShowWords;
public bool ShowLinks;
public bool ShowLines;
public bool ShowMeshBounds;
public bool ShowTextBounds;
[Space(10)]
[TextArea(2, 2)]
public string ObjectStats;
[SerializeField]
private TMP_Text m_TextComponent;
[Space(10)] [TextArea(2, 2)] public string ObjectStats;
[SerializeField] private TMP_Text m_TextComponent;
private Transform m_Transform;
private TMP_TextInfo m_TextInfo;
@ -32,10 +25,8 @@ namespace TMPro.Examples
private float m_HandleSize;
void OnDrawGizmos()
{
if (m_TextComponent == null)
{
private void OnDrawGizmos() {
if (m_TextComponent == null) {
m_TextComponent = GetComponent<TMP_Text>();
if (m_TextComponent == null)
@ -48,7 +39,9 @@ namespace TMPro.Examples
m_TextInfo = m_TextComponent.textInfo;
// 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;
// Get the handle size for drawing the various
@ -56,53 +49,69 @@ namespace TMPro.Examples
m_HandleSize = HandleUtility.GetHandleSize(m_Transform.position) * m_ScaleMultiplier;
// Draw line metrics
#region Draw Lines
if (ShowLines)
DrawLineBounds();
#endregion
// Draw word metrics
#region Draw Words
if (ShowWords)
DrawWordBounds();
#endregion
// Draw character metrics
#region Draw Characters
if (ShowCharacters)
DrawCharactersBounds();
#endregion
// Draw Quads around each of the words
#region Draw Links
if (ShowLinks)
DrawLinkBounds();
#endregion
// Draw Quad around the bounds of the text
#region Draw Bounds
if (ShowMeshBounds)
DrawBounds();
#endregion
// Draw Quad around the rendered region of the text.
#region Draw Text Bounds
if (ShowTextBounds)
DrawTextBounds();
#endregion
}
/// <summary>
/// Method to draw a rectangle around each character.
/// Method to draw a rectangle around each character.
/// </summary>
/// <param name="text"></param>
void DrawCharactersBounds()
{
private void DrawCharactersBounds() {
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
TMP_CharacterInfo characterInfo = m_TextInfo.characterInfo[i];
@ -111,7 +120,8 @@ namespace TMPro.Examples
i >= m_TextComponent.firstVisibleCharacter;
if (m_TextComponent.overflowMode == TextOverflowModes.Page)
isCharacterVisible = isCharacterVisible && characterInfo.pageNumber + 1 == m_TextComponent.pageToDisplay;
isCharacterVisible = isCharacterVisible &&
characterInfo.pageNumber + 1 == m_TextComponent.pageToDisplay;
if (!isCharacterVisible)
continue;
@ -120,22 +130,28 @@ namespace TMPro.Examples
// Get Bottom Left and Top Right position of the current character
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 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
if (characterInfo.isVisible)
{
if (characterInfo.isVisible) {
Color color = Color.green;
DrawDottedRectangle(bottomLeft, topRight, color);
}
else
{
else {
Color color = Color.grey;
float whiteSpaceAdvance = Math.Abs(characterInfo.origin - characterInfo.xAdvance) > 0.01f ? 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 whiteSpaceAdvance = Math.Abs(characterInfo.origin - characterInfo.xAdvance) > 0.01f
? 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;
@ -152,16 +168,19 @@ namespace TMPro.Examples
Handles.DrawDottedLine(ascentlineStart, ascentlineEnd, dottedLineSize);
// Draw Cap Height & Mean line
float capline = characterInfo.fontAsset == null ? 0 : baseline + characterInfo.fontAsset.faceInfo.capLine * characterInfo.scale;
Vector3 capHeightStart = new Vector3(topLeft.x, m_Transform.TransformPoint(new Vector3(0, capline, 0)).y, 0);
Vector3 capHeightEnd = new Vector3(topRight.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 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);
if (characterInfo.isVisible)
{
float capline = characterInfo.fontAsset == null
? 0
: baseline + characterInfo.fontAsset.faceInfo.capLine * characterInfo.scale;
Vector3 capHeightStart = new(topLeft.x, m_Transform.TransformPoint(new Vector3(0, capline, 0)).y, 0);
Vector3 capHeightEnd = new(topRight.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 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
Handles.color = Color.cyan;
Handles.DrawDottedLine(capHeightStart, capHeightEnd, dottedLineSize);
@ -195,76 +214,72 @@ namespace TMPro.Examples
DrawCrosshair(advancePosition, 0.0125f / m_ScaleMultiplier, Color.yellow);
// Draw text labels for metrics
if (m_HandleSize < 0.5f)
{
GUIStyle style = new GUIStyle(GUI.skin.GetStyle("Label"));
style.normal.textColor = new Color(0.6f, 0.6f, 0.6f, 1.0f);
style.fontSize = 12;
style.fixedWidth = 200;
style.fixedHeight = 20;
Vector3 labelPosition;
float center = (origin + advance) / 2;
//float baselineMetrics = 0;
//float ascentlineMetrics = ascentline - baseline;
//float caplineMetrics = capline - baseline;
//float meanlineMetrics = meanline - baseline;
//float descentlineMetrics = descentline - baseline;
// Ascent Line
labelPosition = m_Transform.TransformPoint(new Vector3(center, ascentline, 0));
style.alignment = TextAnchor.UpperCenter;
Handles.Label(labelPosition, "Ascent Line", style);
//Handles.Label(labelPosition, "Ascent Line (" + ascentlineMetrics.ToString("f3") + ")" , style);
// Base Line
labelPosition = m_Transform.TransformPoint(new Vector3(center, baseline, 0));
Handles.Label(labelPosition, "Base Line", style);
//Handles.Label(labelPosition, "Base Line (" + baselineMetrics.ToString("f3") + ")" , style);
// Descent line
labelPosition = m_Transform.TransformPoint(new Vector3(center, descentline, 0));
Handles.Label(labelPosition, "Descent Line", style);
//Handles.Label(labelPosition, "Descent Line (" + descentlineMetrics.ToString("f3") + ")" , style);
if (characterInfo.isVisible)
{
// Cap Line
labelPosition = m_Transform.TransformPoint(new Vector3(center, capline, 0));
style.alignment = TextAnchor.UpperCenter;
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));
style.alignment = TextAnchor.UpperCenter;
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));
style.alignment = TextAnchor.UpperRight;
Handles.Label(labelPosition, "Origin ", style);
// Advance
labelPosition = m_Transform.TransformPoint(new Vector3(advance, baseline, 0));
style.alignment = TextAnchor.UpperLeft;
Handles.Label(labelPosition, " Advance", style);
}
}
if (m_HandleSize < 0.5f) {
GUIStyle style = new(GUI.skin.GetStyle("Label"));
style.normal.textColor = new Color(0.6f, 0.6f, 0.6f, 1.0f);
style.fontSize = 12;
style.fixedWidth = 200;
style.fixedHeight = 20;
Vector3 labelPosition;
float center = (origin + advance) / 2;
//float baselineMetrics = 0;
//float ascentlineMetrics = ascentline - baseline;
//float caplineMetrics = capline - baseline;
//float meanlineMetrics = meanline - baseline;
//float descentlineMetrics = descentline - baseline;
// Ascent Line
labelPosition = m_Transform.TransformPoint(new Vector3(center, ascentline, 0));
style.alignment = TextAnchor.UpperCenter;
Handles.Label(labelPosition, "Ascent Line", style);
//Handles.Label(labelPosition, "Ascent Line (" + ascentlineMetrics.ToString("f3") + ")" , style);
// Base Line
labelPosition = m_Transform.TransformPoint(new Vector3(center, baseline, 0));
Handles.Label(labelPosition, "Base Line", style);
//Handles.Label(labelPosition, "Base Line (" + baselineMetrics.ToString("f3") + ")" , style);
// Descent line
labelPosition = m_Transform.TransformPoint(new Vector3(center, descentline, 0));
Handles.Label(labelPosition, "Descent Line", style);
//Handles.Label(labelPosition, "Descent Line (" + descentlineMetrics.ToString("f3") + ")" , style);
if (characterInfo.isVisible) {
// Cap Line
labelPosition = m_Transform.TransformPoint(new Vector3(center, capline, 0));
style.alignment = TextAnchor.UpperCenter;
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));
style.alignment = TextAnchor.UpperCenter;
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));
style.alignment = TextAnchor.UpperRight;
Handles.Label(labelPosition, "Origin ", style);
// Advance
labelPosition = m_Transform.TransformPoint(new Vector3(advance, baseline, 0));
style.alignment = TextAnchor.UpperLeft;
Handles.Label(labelPosition, " Advance", style);
}
}
}
}
/// <summary>
/// Method to draw rectangles around each word of the text.
/// Method to draw rectangles around each word of the text.
/// </summary>
/// <param name="text"></param>
void DrawWordBounds()
{
for (int i = 0; i < m_TextInfo.wordCount; i++)
{
private void DrawWordBounds() {
for (int i = 0; i < m_TextInfo.wordCount; i++) {
TMP_WordInfo wInfo = m_TextInfo.wordInfo[i];
bool isBeginRegion = false;
@ -280,22 +295,23 @@ namespace TMPro.Examples
Color wordColor = Color.green;
// 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;
TMP_CharacterInfo currentCharInfo = m_TextInfo.characterInfo[characterIndex];
int currentLine = currentCharInfo.lineNumber;
bool isCharacterVisible = characterIndex > m_TextComponent.maxVisibleCharacters ||
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
maxAscender = Mathf.Max(maxAscender, currentCharInfo.ascender);
minDescender = Mathf.Min(minDescender, currentCharInfo.descender);
if (isBeginRegion == false && isCharacterVisible)
{
if (isBeginRegion == false && isCharacterVisible) {
isBeginRegion = true;
bottomLeft = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0);
@ -304,14 +320,15 @@ namespace TMPro.Examples
//Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
// If Word is one character
if (wInfo.characterCount == 1)
{
if (wInfo.characterCount == 1) {
isBeginRegion = false;
topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0));
bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.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));
bottomRight =
m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0));
topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender,
0));
// Draw Region
DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, wordColor);
@ -321,13 +338,13 @@ namespace TMPro.Examples
}
// Last Character of Word
if (isBeginRegion && j == wInfo.characterCount - 1)
{
if (isBeginRegion && j == wInfo.characterCount - 1) {
isBeginRegion = false;
topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 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));
// Draw Region
@ -336,13 +353,13 @@ namespace TMPro.Examples
//Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
}
// 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;
topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 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));
// Draw Region
@ -350,27 +367,22 @@ namespace TMPro.Examples
//Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
maxAscender = -Mathf.Infinity;
minDescender = Mathf.Infinity;
}
}
//Debug.Log(wInfo.GetWord(m_TextMeshPro.textInfo.characterInfo));
}
}
/// <summary>
/// Draw rectangle around each of the links contained in the text.
/// Draw rectangle around each of the links contained in the text.
/// </summary>
/// <param name="text"></param>
void DrawLinkBounds()
{
private void DrawLinkBounds() {
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];
bool isBeginRegion = false;
@ -386,22 +398,23 @@ namespace TMPro.Examples
Color32 linkColor = Color.cyan;
// 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;
TMP_CharacterInfo currentCharInfo = textInfo.characterInfo[characterIndex];
int currentLine = currentCharInfo.lineNumber;
bool isCharacterVisible = characterIndex > m_TextComponent.maxVisibleCharacters ||
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
maxAscender = Mathf.Max(maxAscender, currentCharInfo.ascender);
minDescender = Mathf.Min(minDescender, currentCharInfo.descender);
if (isBeginRegion == false && isCharacterVisible)
{
if (isBeginRegion == false && isCharacterVisible) {
isBeginRegion = true;
bottomLeft = new Vector3(currentCharInfo.bottomLeft.x, currentCharInfo.descender, 0);
@ -410,14 +423,15 @@ namespace TMPro.Examples
//Debug.Log("Start Word Region at [" + currentCharInfo.character + "]");
// If Link is one character
if (linkInfo.linkTextLength == 1)
{
if (linkInfo.linkTextLength == 1) {
isBeginRegion = false;
topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 0));
bottomLeft = m_Transform.TransformPoint(new Vector3(bottomLeft.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));
bottomRight =
m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, minDescender, 0));
topRight = m_Transform.TransformPoint(new Vector3(currentCharInfo.topRight.x, maxAscender,
0));
// Draw Region
DrawRectangle(bottomLeft, topLeft, topRight, bottomRight, linkColor);
@ -427,13 +441,13 @@ namespace TMPro.Examples
}
// Last Character of Link
if (isBeginRegion && j == linkInfo.linkTextLength - 1)
{
if (isBeginRegion && j == linkInfo.linkTextLength - 1) {
isBeginRegion = false;
topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 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));
// Draw Region
@ -442,13 +456,13 @@ namespace TMPro.Examples
//Debug.Log("End Word Region at [" + currentCharInfo.character + "]");
}
// 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;
topLeft = m_Transform.TransformPoint(new Vector3(topLeft.x, maxAscender, 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));
// Draw Region
@ -466,22 +480,26 @@ namespace TMPro.Examples
/// <summary>
/// Draw Rectangles around each lines of the text.
/// Draw Rectangles around each lines of the text.
/// </summary>
/// <param name="text"></param>
void DrawLineBounds()
{
private void DrawLineBounds() {
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_CharacterInfo firstCharacterInfo = m_TextInfo.characterInfo[lineInfo.firstCharacterIndex];
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)) ||
i > m_TextComponent.maxVisibleLines ||
(m_TextComponent.overflowMode == TextOverflowModes.Page && firstCharacterInfo.pageNumber + 1 != m_TextComponent.pageToDisplay) ? false : true;
bool isLineVisible = (lineInfo.characterCount == 1 && (firstCharacterInfo.character == 10 ||
firstCharacterInfo.character == 11 ||
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;
@ -495,7 +513,8 @@ namespace TMPro.Examples
float dottedLineSize = 12;
// 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
Vector3 ascentlineStart = m_Transform.TransformPoint(new Vector3(lineBottomLeft, ascentline, 0));
@ -519,9 +538,8 @@ namespace TMPro.Examples
Handles.DrawDottedLine(descentLineStart, descentLineEnd, dottedLineSize);
// Draw text labels for metrics
if (m_HandleSize < 1.0f)
{
GUIStyle style = new GUIStyle();
if (m_HandleSize < 1.0f) {
GUIStyle style = new();
style.normal.textColor = new Color(0.8f, 0.8f, 0.8f, 1.0f);
style.fontSize = 12;
style.fixedWidth = 200;
@ -547,10 +565,9 @@ namespace TMPro.Examples
/// <summary>
/// Draw Rectangle around the bounds of the text object.
/// Draw Rectangle around the bounds of the text object.
/// </summary>
void DrawBounds()
{
private void DrawBounds() {
Bounds meshBounds = m_TextComponent.bounds;
// 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;
Vector3 bottomLeft = m_TextComponent.transform.position + (textBounds.center - textBounds.extents);
@ -573,8 +589,7 @@ namespace TMPro.Examples
// Draw Rectangles
void DrawRectangle(Vector3 BL, Vector3 TR, Color color)
{
private void DrawRectangle(Vector3 BL, Vector3 TR, Color color) {
Gizmos.color = color;
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));
}
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.DrawDottedLine(bottomLeft, new Vector3(bottomLeft.x, topRight.y, bottomLeft.z), 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);
}
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;
Rect rect = new Rect(bottomLeft, topRight - bottomLeft);
Rect rect = new(bottomLeft, topRight - bottomLeft);
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;
Vector3 bottomLeft = new Vector3(position.x - size, position.y - size, position.z);
Vector3 topLeft = new Vector3(position.x - size, position.y + size, position.z);
Vector3 topRight = new Vector3(position.x + size, position.y + size, position.z);
Vector3 bottomRight = new Vector3(position.x + size, position.y - size, position.z);
Vector3 bottomLeft = new(position.x - size, position.y - size, position.z);
Vector3 topLeft = new(position.x - size, position.y + size, position.z);
Vector3 topRight = new(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(topLeft, topRight);
@ -613,18 +625,18 @@ namespace TMPro.Examples
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.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, position.y - size, position.z), new Vector3(position.x, position.y + size, position.z));
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, position.y - size, position.z),
new Vector3(position.x, position.y + size, position.z));
}
// 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.DrawLine(bl, tl);
@ -635,18 +647,16 @@ namespace TMPro.Examples
// Draw Rectangles
void DrawDottedRectangle(Vector3 bl, Vector3 tl, Vector3 tr, Vector3 br, Color color)
{
var cam = Camera.current;
private void DrawDottedRectangle(Vector3 bl, Vector3 tl, Vector3 tr, Vector3 br, Color color) {
Camera cam = Camera.current;
float dotSpacing = (cam.WorldToScreenPoint(br).x - cam.WorldToScreenPoint(bl).x) / 75f;
UnityEditor.Handles.color = color;
Handles.color = color;
UnityEditor.Handles.DrawDottedLine(bl, tl, dotSpacing);
UnityEditor.Handles.DrawDottedLine(tl, tr, dotSpacing);
UnityEditor.Handles.DrawDottedLine(tr, br, dotSpacing);
UnityEditor.Handles.DrawDottedLine(br, bl, dotSpacing);
Handles.DrawDottedLine(bl, tl, dotSpacing);
Handles.DrawDottedLine(tl, tr, dotSpacing);
Handles.DrawDottedLine(tr, br, dotSpacing);
Handles.DrawDottedLine(br, bl, dotSpacing);
}
#endif
#endif
}
}
}

@ -1,24 +1,17 @@
using UnityEngine;
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 bool m_isHoveringObject;
private int m_selectedLink = -1;
private int m_lastCharIndex = -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_Camera = Camera.main;
@ -27,21 +20,19 @@ namespace TMPro.Examples
}
void LateUpdate()
{
private void LateUpdate() {
m_isHoveringObject = false;
if (TMP_TextUtilities.IsIntersectingRectTransform(m_TextMeshPro.rectTransform, Input.mousePosition, Camera.main))
{
m_isHoveringObject = true;
}
if (TMP_TextUtilities.IsIntersectingRectTransform(m_TextMeshPro.rectTransform, Input.mousePosition,
Camera.main)) m_isHoveringObject = true;
if (m_isHoveringObject)
{
if (m_isHoveringObject) {
#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.");
m_lastCharIndex = charIndex;
@ -50,7 +41,8 @@ namespace TMPro.Examples
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;
@ -62,22 +54,21 @@ namespace TMPro.Examples
//m_TextMeshPro.mesh.colors32 = vertexColors;
m_TextMeshPro.textInfo.meshInfo[meshIndex].mesh.colors32 = vertexColors;
}
#endregion
#region Example of Link Handling
// Check if mouse intersects with any links.
int linkIndex = TMP_TextUtilities.FindIntersectingLink(m_TextMeshPro, Input.mousePosition, m_Camera);
// Clear previous link selection if one existed.
if ((linkIndex == -1 && m_selectedLink != -1) || linkIndex != m_selectedLink)
{
//m_TextPopup_RectTransform.gameObject.SetActive(false);
m_selectedLink = -1;
}
// Handle new Link selection.
if (linkIndex != -1 && linkIndex != m_selectedLink)
{
if (linkIndex != -1 && linkIndex != m_selectedLink) {
m_selectedLink = linkIndex;
TMP_LinkInfo linkInfo = m_TextMeshPro.textInfo.linkInfo[linkIndex];
@ -87,45 +78,48 @@ namespace TMPro.Examples
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
//m_TextPopup_RectTransform.position = worldPointInRectangle;
//m_TextPopup_RectTransform.gameObject.SetActive(true);
//m_TextPopup_TMPComponent.text = k_LinkText + " ID 01";
//m_TextPopup_RectTransform.position = worldPointInRectangle;
//m_TextPopup_RectTransform.gameObject.SetActive(true);
//m_TextPopup_TMPComponent.text = k_LinkText + " ID 01";
break;
case "id_02": // 100041638: // id_02
//m_TextPopup_RectTransform.position = worldPointInRectangle;
//m_TextPopup_RectTransform.gameObject.SetActive(true);
//m_TextPopup_TMPComponent.text = k_LinkText + " ID 02";
//m_TextPopup_RectTransform.position = worldPointInRectangle;
//m_TextPopup_RectTransform.gameObject.SetActive(true);
//m_TextPopup_TMPComponent.text = k_LinkText + " ID 02";
break;
}
}
#endregion
#region Example of Word Selection
// 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);
if (wordIndex != -1 && wordIndex != m_lastWordIndex)
{
if (wordIndex != -1 && wordIndex != m_lastWordIndex) {
m_lastWordIndex = 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);
//Debug.Log("Mouse Position: " + Input.mousePosition.ToString("f3") + " Word Position: " + wordPOS.ToString("f3"));
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);
for (int i = 0; i < wInfo.characterCount; i++)
{
int vertexIndex = m_TextMeshPro.textInfo.characterInfo[wInfo.firstCharacterIndex + i].vertexIndex;
Color32 c = new((byte)Random.Range(0, 255), (byte)Random.Range(0, 255), (byte)Random.Range(0, 255),
255);
for (int i = 0; i < wInfo.characterCount; i++) {
int vertexIndex = m_TextMeshPro.textInfo.characterInfo[wInfo.firstCharacterIndex + i]
.vertexIndex;
vertexColors[vertexIndex + 0] = c;
vertexColors[vertexIndex + 1] = c;
@ -135,23 +129,21 @@ namespace TMPro.Examples
m_TextMeshPro.mesh.colors32 = vertexColors;
}
#endregion
}
}
public void OnPointerEnter(PointerEventData eventData)
{
public void OnPointerEnter(PointerEventData eventData) {
Debug.Log("OnPointerEnter()");
m_isHoveringObject = true;
}
public void OnPointerExit(PointerEventData eventData)
{
public void OnPointerExit(PointerEventData eventData) {
Debug.Log("OnPointerExit()");
m_isHoveringObject = false;
}
}
}
}

@ -1,41 +1,34 @@
using UnityEngine;
using UnityEngine.Events;
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.
namespace TMPro.Examples
{
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;
namespace TMPro.Examples {
public class TMP_TextSelector_B : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler,
IPointerUpHandler {
private const string k_LinkText = "You have selected link <#ffff00>";
private const string k_WordText = "Word Index: <#ffff00>";
private TextMeshProUGUI m_TextMeshPro;
private Canvas m_Canvas;
private Camera m_Camera;
public RectTransform TextPopup_Prefab_01;
// Flags
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 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>();
@ -48,53 +41,30 @@ namespace TMPro.Examples
m_Camera = m_Canvas.worldCamera;
// 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_TMPComponent = m_TextPopup_RectTransform.GetComponentInChildren<TextMeshProUGUI>();
m_TextPopup_RectTransform.gameObject.SetActive(false);
}
void OnEnable()
{
// 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)
{
private void LateUpdate() {
if (isHoveringObject) {
// Check if Mouse Intersects any of the characters. If so, assign a random color.
#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.
if (charIndex == -1 || charIndex != m_lastIndex)
{
if (charIndex == -1 || charIndex != m_lastIndex) {
RestoreCachedVertexAttributes(m_lastIndex);
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;
// 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;
// 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
Color32[] vertexColors = m_TextMeshPro.textInfo.meshInfo[materialIndex].colors32;
@ -161,21 +131,22 @@ namespace TMPro.Examples
// Need to update the appropriate
m_TextMeshPro.UpdateVertexData(TMP_VertexDataUpdateFlags.All);
}
#endregion
#region Word Selection Handling
//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);
// 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];
// 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;
// Get the index of the material / sub text object used by this character.
@ -203,15 +174,14 @@ namespace TMPro.Examples
// 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;
TMP_WordInfo wInfo = m_TextMeshPro.textInfo.wordInfo[wordIndex];
// 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;
// Get the index of the material / sub text object used by this character.
@ -232,25 +202,24 @@ namespace TMPro.Examples
// Update Geometry
m_TextMeshPro.UpdateVertexData(TMP_VertexDataUpdateFlags.All);
}
#endregion
#region Example of Link Handling
// Check if mouse intersects with any links.
int linkIndex = TMP_TextUtilities.FindIntersectingLink(m_TextMeshPro, Input.mousePosition, m_Camera);
// 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_selectedLink = -1;
}
// Handle new Link selection.
if (linkIndex != -1 && linkIndex != m_selectedLink)
{
if (linkIndex != -1 && linkIndex != m_selectedLink) {
m_selectedLink = 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.
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
m_TextPopup_RectTransform.position = worldPointInRectangle;
m_TextPopup_RectTransform.gameObject.SetActive(true);
@ -274,42 +243,37 @@ namespace TMPro.Examples
break;
}
}
#endregion
#endregion
}
else
{
else {
// Restore any character that may have been modified
if (m_lastIndex != -1)
{
if (m_lastIndex != -1) {
RestoreCachedVertexAttributes(m_lastIndex);
m_lastIndex = -1;
}
}
}
public void OnPointerEnter(PointerEventData eventData)
{
//Debug.Log("OnPointerEnter()");
isHoveringObject = true;
private void OnEnable() {
// Subscribe to event fired when text object has been regenerated.
TMPro_EventManager.TEXT_CHANGED_EVENT.Add(ON_TEXT_CHANGED);
}
public void OnPointerExit(PointerEventData eventData)
{
//Debug.Log("OnPointerExit()");
isHoveringObject = false;
private void OnDisable() {
// UnSubscribe to event fired when text object has been regenerated.
TMPro_EventManager.TEXT_CHANGED_EVENT.Remove(ON_TEXT_CHANGED);
}
public void OnPointerClick(PointerEventData eventData)
{
public void OnPointerClick(PointerEventData eventData) {
//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.
#region Character Selection Handling
/*
int charIndex = TMP_TextUtilities.FindIntersectingCharacter(m_TextMeshPro, Input.mousePosition, m_Camera, true);
if (charIndex != -1 && charIndex != m_lastIndex)
@ -330,10 +294,12 @@ namespace TMPro.Examples
m_TextMeshPro.canvasRenderer.SetVertices(uiVertices, uiVertices.Length);
}
*/
#endregion
#region Word Selection Handling
//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);
@ -390,10 +356,12 @@ namespace TMPro.Examples
m_TextMeshPro.canvasRenderer.SetVertices(uiVertices, uiVertices.Length);
}
*/
#endregion
#region Link Selection Handling
/*
// 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);
@ -443,18 +411,36 @@ namespace TMPro.Examples
#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()");
}
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;
// 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);
}
}
}
}

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

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