from github

This commit is contained in:
jonas
2024-10-29 17:16:17 +01:00
commit 537f1d77fb
1074 changed files with 303521 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 90dc410c9b2947049b024fe156c18f0c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+46
View File
@@ -0,0 +1,46 @@
using Unity.Netcode;
using UnityEngine;
//this scripts provides movement functions for the player avatar
[System.Serializable]
public class MapTransform
{
public Transform vrTarget;
public Transform IKTarget;
public Vector3 trackingPositionOffset;
public Vector3 trackingRotationOffset;
public void MapVRAvatar()
{
//map position and rotation of inverse kinematics target
IKTarget.position = vrTarget.TransformPoint(trackingPositionOffset);
IKTarget.rotation = vrTarget.rotation * Quaternion.Euler(trackingRotationOffset);
}
}
public class AvatarController : NetworkBehaviour
{
[SerializeField] private MapTransform head;
[SerializeField] private MapTransform leftHand;
[SerializeField] private MapTransform rightHand;
[SerializeField] private float turnSmoothness;
[SerializeField] private Transform IKHead;
[SerializeField] private Vector3 headBodyOffset;
private void LateUpdate()
{
if (IsOwner)
{
//map position and rotation of VR-Target to the avatar
transform.position = IKHead.position + headBodyOffset;
transform.forward = Vector3.Lerp(transform.forward, Vector3.ProjectOnPlane(IKHead.forward, Vector3.up).normalized, Time.deltaTime * turnSmoothness); ;
head.MapVRAvatar();
leftHand.MapVRAvatar();
rightHand.MapVRAvatar();
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: abc1b0f06c98c604ca6ad6e205da3f7a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 74a775786c7067b4099a8284a7e773ce
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,67 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
//this script does block rotation and movement if a object is grabbed (BUGGY)
public class BlockRotAndMove : MonoBehaviour
{
private ActionBasedContinuousMoveProvider moveProvider;
private ActionBasedContinuousTurnProvider turnProvider;
private XRGrabInteractable grabInteractable;
//variable that ensures, that the move and turn provider must not be searched every time an object is grabbed
private bool moveProviderFound = false;
//block rotation and movement if an object is grabed
private void OnGrab(XRBaseInteractor interactor)
{
if (!moveProviderFound)
{
FindMoveProvider();
moveProviderFound = true;
}
moveProvider.enabled = false;
turnProvider.enabled = false;
}
private void OnRelease(XRBaseInteractor interactor)
{
if (!moveProviderFound)
{
FindMoveProvider();
moveProviderFound = true;
}
moveProvider.enabled = true;
turnProvider.enabled = true;
}
//find the move provider of the current player, currently: get the move provider of the first player found
//TODO: fix to only get move provider of current player
private void FindMoveProvider()
{
moveProvider = GameObject.Find("Move").GetComponent<ActionBasedContinuousMoveProvider>();
turnProvider = GameObject.Find("Turn").GetComponent<ActionBasedContinuousTurnProvider>();
}
private void Start()
{
try
{
FindMoveProvider();
moveProviderFound = true;
}
catch
{
moveProviderFound = false;
}
//add listener functions to the object the script is attached to
grabInteractable = GetComponent<XRGrabInteractable>();
grabInteractable.onSelectEntered.AddListener(OnGrab);
grabInteractable.onSelectExited.AddListener(OnRelease);
grabInteractable.onSelectCanceled.AddListener(OnRelease);
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ada7667e6c79def4f934db4615354acb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+58
View File
@@ -0,0 +1,58 @@
using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEditor.SearchService;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
//this script handles functionality if leaving the classroom scene
public class ExitFunction : NetworkBehaviour
{
UnityEngine.SceneManagement.Scene curScene;
private string sceneName;
void Start()
{
curScene = SceneManager.GetActiveScene();
sceneName = curScene.name;
GetComponent<Button>().onClick.AddListener(() =>
{
LeaveTheWorld();
});
}
//the rpcs are called if the host tries to leave the scene (try to kick every client out of the scene)
//clients need to be kicked because without a host the gameplay would not work anymore (work very limited)
[ServerRpc]
public void TryKickUserServerRPC()
{
Debug.Log("Server Site");
KickMeClientRPC();
}
[ClientRpc]
public void KickMeClientRPC()
{
Debug.Log("Client Site");
//NetworkManager.Singleton.Shutdown();
SceneManager.LoadScene("StartMenu");
}
private void LeaveTheWorld()
{
//check if "leaver" is a host --> clients must be also kicked
//or is a client --> can just leave without any impact to others
if(sceneName.Equals("ClassRoom") && PlayerPrefs.GetString("status").Equals("host"))
{
TryKickUserServerRPC();
NetworkManager.Singleton.Shutdown();
}
else if (sceneName.Equals("ClassRoom"))
{
NetworkManager.Singleton.Shutdown();
SceneManager.LoadScene("StartMenu");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a122d1279e489144ab13d3c16ad4ece2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,74 @@
using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.XR;
//this scripts handels the laser pointer (also the network sync for it)
public class LineRendererActivator : NetworkBehaviour
{
[SerializeField] public LineRenderer lineRenderer;
[SerializeField] public List<InputDevice> mDevice = new List<InputDevice>();
//network variable to perform the laser pointers sync
private NetworkVariable<bool> isRayActive = new NetworkVariable<bool> (writePerm: NetworkVariableWritePermission.Owner);
void Start()
{
}
void Update()
{
//if the script is attached to the owners player object
if (IsOwner)
{
mDevice.Clear();
InputDevices.GetDevicesWithCharacteristics(InputDeviceCharacteristics.Right | InputDeviceCharacteristics.Controller, mDevice);
// check if any physical controller is connected
if (mDevice.Count > 0)
{
//try to get the input of the right controllers secondary button (B)
InputDevice rightController = mDevice[0];
rightController.TryGetFeatureValue(CommonUsages.secondaryButton, out bool secondaryDown);
if (secondaryDown || Input.GetKey(KeyCode.O))
{
// activate the line renderer while the button is pressed
lineRenderer.enabled = true;
isRayActive.Value = true;
}
else
{
// deactivate the line renderer if the button is released
lineRenderer.enabled = false;
isRayActive.Value = false;
}
}
//for test case with keyboard inputs only
else
{
if (Input.GetKey(KeyCode.O))
{
// activate the line renderer while the button is pressed
lineRenderer.enabled = true;
isRayActive.Value = true;
}
else
{
// deactivate the line renderer if the button is released
lineRenderer.enabled = false;
isRayActive.Value = false;
}
}
}
else
{
//read the activation variable if not owner
lineRenderer.enabled = isRayActive.Value;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e45e046223a654948a6964242d1c32e1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+40
View File
@@ -0,0 +1,40 @@
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
//this script provides part of functionallity for the menu bar
public class MenuBar : MonoBehaviour
{
[SerializeField] private GameObject MenuCanvas;
[SerializeField] private GameObject OBJCanvas;
[SerializeField] private GameObject MainCamera;
private void Update()
{
// calc the position of the menubar (offset to the player itself)
Vector3 newPosition = MainCamera.transform.position + MainCamera.transform.forward * 0.7f;
newPosition.y = MainCamera.transform.position.y - 0.45f; // always keep up y-value
// apply the calculated position
MenuCanvas.transform.position = newPosition;
// rotate the menu canvas with the player
MenuCanvas.transform.rotation = Quaternion.Euler(15, MainCamera.transform.rotation.eulerAngles.y, 0);
}
// change menu bar to the object loader, scaler etc. menu (this menu bar part must deactivated)
public void changeToOBJMenu()
{
MenuCanvas.SetActive(false);
OBJCanvas.SetActive(true);
}
// button function binding to call the savescene function
public void SaveScene()
{
GameObject.Find("ObjectSpawner").GetComponent<RoomSaver>().SaveScene();
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2bb3facba82353f4a8665dccdd335105
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+25
View File
@@ -0,0 +1,25 @@
using System.Collections;
using System.Collections.Generic;
using TMPro;
using Unity.Netcode;
using UnityEngine;
// this script moves the name tag above the head
public class NameLabel : NetworkBehaviour
{
[SerializeField] private GameObject NameCanvas;
[SerializeField] private GameObject MainCamera;
private void Update()
{
//if you are owner of the player object this script is attached to, move the name tag relative to your position
// sync handled by PlayerComponent...Sync
if (IsOwner)
{
NameCanvas.transform.position = MainCamera.transform.position + Vector3.up * 0.625f;
NameCanvas.transform.rotation = Quaternion.EulerRotation(0, Quaternion.ToEulerAngles(MainCamera.transform.rotation).y + 135, 0);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a5e3f133be27b344185c0b2fa4acd1b5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2fe4fbbe7c5d59c47adc99fb65e626a5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,55 @@
using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
//this script is the base of location and rotation sync of spawned 3D objects
public class ObjectGrabListener : MonoBehaviour
{
//stores if the object is grabbed by local player
private bool isGrabed = false;
//counts the frames
private int count = 0;
private ObjectSync objectSync;
//index of the object in objectsyncs lists
public int index;
void Start()
{
//add listener functions to the grabinteractor
objectSync = GameObject.Find("ObjectSpawner").GetComponent<ObjectSync>();
XRGrabInteractable grabinteract = GetComponent<XRGrabInteractable>();
grabinteract.onSelectEntered.AddListener(OnGrab);
grabinteract.onSelectExited.AddListener(OnRelease);
grabinteract.onSelectCanceled.AddListener(OnRelease);
}
public void OnGrab(XRBaseInteractor interactor)
{
isGrabed = true;
count = 0;
}
public void OnRelease(XRBaseInteractor interactor)
{
isGrabed = false;
//on release of the object the position and rotation should be synced one last time
objectSync.syncLocationServerRPC(index, transform.position, transform.rotation);
}
public void Update()
{
//if the object is grabbed sync its position and rotation are syned every 10 frames (performance)
if (isGrabed)
{
if (count >= 10)
{
objectSync.syncLocationServerRPC(index, transform.position, transform.rotation);
count = 0;
}
count++;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 913bdcbc8641e0c4e8e31c8bcee85749
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,100 @@
using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
//this scripts syncs the object movement etc.
public class ObjectSync : NetworkBehaviour
{
[SerializeField] private OBJLoader objLoader;
//a struct that can been sent over network containing the data of an object
public struct OBJData : INetworkSerializable
{
public Vector3[] vertices;
public int[] triangles;
public Vector3 positionVector;
public Quaternion rotation;
public Vector3 scale;
//seriealizer method for network sync
public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
{
serializer.SerializeValue(ref vertices);
serializer.SerializeValue(ref triangles);
serializer.SerializeValue(ref positionVector);
serializer.SerializeValue(ref rotation);
serializer.SerializeValue(ref scale);
}
}
//a list of the objects spawned on the local client NOT SYNCED!
public List<GameObject> spawnedObjects = new List<GameObject>();
//a list of the object data on the server NOT DIRECTLY SYNED!
public List<OBJData> objDataList = new List<OBJData>();
//the clientside function to scale an object
[ClientRpc]
public void syncScaleClientRPC(int i, Vector3 scale)
{
spawnedObjects[i].transform.localScale = scale;
}
//the server side function to scale an object
[ServerRpc(RequireOwnership = false)]
public void syncScaleServerRPC(int i, Vector3 scale)
{
syncScaleClientRPC(i, scale);
//keep track of the scale on the servers list
OBJData localCopy = objDataList[i];
localCopy.scale = scale;
objDataList[i] = localCopy;
}
//the client side function to sync the position and rotation of an object
[ClientRpc]
public void syncLocationClientRPC(int i, Vector3 pos, Quaternion rot)
{
spawnedObjects[i].transform.position = pos;
spawnedObjects[i].transform.rotation = rot;
//Debug.Log(NetworkManager.Singleton.LocalClientId);
}
[ServerRpc(RequireOwnership = false)]
public void syncLocationServerRPC(int i, Vector3 pos, Quaternion rot)
{
syncLocationClientRPC(i, pos, rot);
OBJData localCopy = objDataList[i];
localCopy.positionVector = pos;
localCopy.rotation = rot;
objDataList[i] = localCopy;
}
//the next 2 functions provide functionality to sync every object from the servers list to the client
//for the case that a player joins after some objects are already in the scene
[ClientRpc]
public void syncListClientRPC(ulong id, OBJData objDat)
{
//only the client with the correct id (the receiving client) must create the objects (the server got the id from initial clients request)
if (id == NetworkManager.Singleton.LocalClientId)
{
//create a mesh via OBJLoader width the data in objDat
GameObject obj = objLoader.CreateMesh(objLoader.buildObjData(objDat.vertices, objDat.triangles), objDat.positionVector);
//apply scale and rotation (maybe further parameters) to the object, that the OBJLoader is not capable to load
obj.transform.rotation = objDat.rotation;
obj.transform.localScale = objDat.scale;
}
}
[ServerRpc(RequireOwnership = false)]
public void syncListServerRPC(ulong id)
{
//send each object in a seperate RPC (network buffer limit only per object, NOT FOR ALL OBJECTS)
foreach (OBJData obj in objDataList)
{
syncListClientRPC(id, obj);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 96baf24ed4dfa0e4585d05667895523b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,29 @@
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using Unity.Netcode;
using UnityEngine;
//this script syncs player components position and rotation in a local space
public class PlayerComponentNetworkSync : NetworkBehaviour
{
private NetworkVariable<Vector3> pos = new NetworkVariable<Vector3>(writePerm: NetworkVariableWritePermission.Owner);
private NetworkVariable<Quaternion> rot = new NetworkVariable<Quaternion>(writePerm:NetworkVariableWritePermission.Owner);
private void Update()
{
//the owner of the player object should write to the network variable, all others should read it
if (IsOwner)
{
pos.Value = transform.localPosition;
rot.Value = transform.localRotation;
}
else
{
transform.localPosition = pos.Value;
transform.localRotation = rot.Value;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 88889d527159cdb4385f38abe64ef5db
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,28 @@
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using Unity.Netcode;
using UnityEngine;
//this script syncs player components position and rotation in a global space
public class PlayerGlobalCompontentSync : NetworkBehaviour
{
//the owner of the player object should write to the network variable, all others should read it
private NetworkVariable<Vector3> pos = new NetworkVariable<Vector3>(writePerm: NetworkVariableWritePermission.Owner);
private NetworkVariable<Quaternion> rot = new NetworkVariable<Quaternion>(writePerm: NetworkVariableWritePermission.Owner);
private void Update()
{
if (IsOwner)
{
pos.Value = transform.position;
rot.Value = transform.rotation;
}
else
{
transform.position = pos.Value;
transform.rotation = rot.Value;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4f502141805ac51439b33dde1e80a539
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,58 @@
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
//this script syncs the name and color of a player
public class PlayerNameAndColor : NetworkBehaviour
{
[SerializeField] public TMP_Text playerText;
[SerializeField] public GameObject dummyMesh;
[SerializeField] private Material DummyHandMaterial;
[SerializeField] public GameObject handLeft, handRight;
private NetworkVariable<FixedString64Bytes> playername = new NetworkVariable<FixedString64Bytes>(writePerm: NetworkVariableWritePermission.Owner);
private NetworkVariable<float> colorR = new NetworkVariable<float>(writePerm: NetworkVariableWritePermission.Owner);
private NetworkVariable<float> colorG = new NetworkVariable<float>(writePerm: NetworkVariableWritePermission.Owner);
private NetworkVariable<float> colorB = new NetworkVariable<float>(writePerm: NetworkVariableWritePermission.Owner);
public void userDataSync()
{
//set the name tag of each individual player
playerText.text = playername.Value.ToString();
//set the color of each individual player to the value received from network
dummyMesh.GetComponent<Renderer>().material.color = new Color(colorR.Value, colorG.Value, colorB.Value);
//apply color to the hands (the standard hands material is for every players hands --> create material for each individual player)
Material newHandMaterial = new Material(handLeft.GetComponent<Renderer>().material.shader);
newHandMaterial.color = new Color(colorR.Value, colorG.Value, colorB.Value);
handLeft.GetComponent<SkinnedMeshRenderer>().material = newHandMaterial;
handRight.GetComponent<SkinnedMeshRenderer>().material = newHandMaterial;
}
public override void OnNetworkSpawn()
{
base.OnNetworkSpawn();
if(IsOwner)
{
//write all parameters from the playerprefs to the networkvariable if you are owner of the player object
playerText.text = PlayerPrefs.GetString("playerName");
playername.Value = PlayerPrefs.GetString("playerName");
colorR.Value = PlayerPrefs.GetFloat("playerColorR");
colorG.Value = PlayerPrefs.GetFloat("playerColorG");
colorB.Value = PlayerPrefs.GetFloat("playerColorB");
dummyMesh.GetComponent<Renderer>().material.color = new Color(colorR.Value, colorG.Value, colorB.Value);
DummyHandMaterial.color = new Color(colorR.Value, colorG.Value, colorB.Value);
}
else
{
//read the player value with a little delay so the players client had enough time to set the network variables
Invoke("userDataSync", 3);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4c250757fc7603b4f8d7cf466013c58f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,68 @@
using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using Unity.XR.CoreUtils;
using UnityEngine;
using UnityEngine.InputSystem.XR;
using UnityEngine.Rendering.Universal;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.XR.Interaction.Toolkit.Inputs;
//this script enables important components for the owners player
//the components listed here should be active for others player objects
//f.e.: unity will switch to other joined players camera if not handled
public class PlayerRemoveNetworkComps : NetworkBehaviour
{
[SerializeField] private InputActionManager inputActionManager;
[SerializeField] private XROrigin xrOrigin;
[SerializeField] private Camera cam;
[SerializeField] private AudioListener audioListener;
[SerializeField] private ActionBasedController leftController;
[SerializeField] private ActionBasedController rightController;
[SerializeField] private XRRayInteractor leftRayInteractor;
[SerializeField] private XRRayInteractor rightRayInteractor;
[SerializeField] private XRInteractorLineVisual leftLineVisual;
[SerializeField] private XRInteractorLineVisual rightLineVisual;
[SerializeField] private Animator rightHandAnimator;
[SerializeField] private Animator leftHandAnimator;
[SerializeField] private SkinnedMeshRenderer leftHandRenderer;
[SerializeField] private SkinnedMeshRenderer rightHandRenderer;
[SerializeField] private LocomotionSystem locomotionSystem;
[SerializeField] private ActionBasedContinuousMoveProvider moveProvider;
[SerializeField] private ActionBasedContinuousTurnProvider turnProvider;
[SerializeField] private TrackedPoseDriver trackedPoseDriver;
[SerializeField] private GameObject menuBar;
public override void OnNetworkSpawn()
{
if (IsOwner)
{
inputActionManager.enabled = true;
xrOrigin.enabled = true;
cam.enabled = true;
audioListener.enabled = true;
leftController.enabled = true;
rightController.enabled = true;
leftRayInteractor.enabled = true;
rightRayInteractor.enabled = true;
leftLineVisual.enabled = true;
rightLineVisual.enabled = true;
leftHandAnimator.enabled = true;
rightHandAnimator.enabled = true;
leftHandRenderer.enabled = true;
rightHandRenderer.enabled = true;
locomotionSystem.enabled = true;
moveProvider.enabled = true;
turnProvider.enabled = true;
trackedPoseDriver.enabled = true;
menuBar.SetActive(true);
//request all objects that have been already loaded from the server / host
ObjectSync objSync = GameObject.Find("ObjectSpawner").GetComponent<ObjectSync>();
objSync.syncListServerRPC(NetworkManager.Singleton.LocalClientId);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3a05404a0dffca541898079702b8b9d1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,60 @@
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using Unity.Netcode;
using UnityEngine.SceneManagement;
using Unity.Netcode.Transports.UTP;
using UnityEngine;
using System;
using System.Net.Sockets;
//this scripts is the startpoint of the classroom scene, it starts the host or creates the connection to it
public class onStart : NetworkBehaviour
{
// Start is called before the first frame update
private float starttime;
[SerializeField] private UnityTransport hostIPAdress;
//stores if the connection establishment check has already been performed
private bool roomCheck = true;
void Start()
{
//value to measure the time since connection attempt (for timeout)
starttime = Time.time;
if (PlayerPrefs.GetString("status") == "client")
{
try
{
hostIPAdress.ConnectionData.Address = PlayerPrefs.GetString("hostadress");
NetworkManager.Singleton.StartClient();
}
catch (NullReferenceException)
{
//on error load the room creattion scene
SceneManager.LoadScene("RoomCreation");
}
}
else if (PlayerPrefs.GetString("status") == "host")
{
//set address to 0.0.0.0 to accept connection from all networks
hostIPAdress.ConnectionData.Address = "0.0.0.0";
NetworkManager.Singleton.StartHost();
}
}
public void Update()
{
//UnityEngine.Debug.Log((Time.time - starttime));
if ((Time.time - starttime) > 15 && roomCheck)
{
roomCheck = false;
//if there is no camera in the scene, the network join was not successfull XD
if ( !(GameObject.Find("Main Camera")))
{
SceneManager.LoadScene("RoomCreation");
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f01e5b523c3afed4586251bd16692442
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+71
View File
@@ -0,0 +1,71 @@
using System.Collections;
using System.Collections.Generic;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
//this script is the second part of the menu bar
public class OBJBar : MonoBehaviour
{
[SerializeField] private GameObject OBJCanvas;
[SerializeField] private GameObject MenuCanvas;
[SerializeField] private GameObject MainCamera;
private void Update()
{
// see menu bar
Vector3 newPosition = MainCamera.transform.position + MainCamera.transform.forward * 0.7f;
newPosition.y = MainCamera.transform.position.y - 0.45f;
OBJCanvas.transform.position = newPosition;
OBJCanvas.transform.rotation = Quaternion.Euler(15, MainCamera.transform.rotation.eulerAngles.y, 0);
}
//change back to the menu bar default
public void changeToMenu()
{
OBJCanvas.SetActive(false);
MenuCanvas.SetActive(true);
}
//open the file explorer provided by OBJLoader
public void openOBJLoader() {
GameObject.Find("ObjectSpawner").GetComponent<OBJLoader>().OpenObjLoadingDialog();
}
//scale the last selected object
public void scaleUP()
{
GameObject lastTouchesObj = GameObject.Find("ObjectSpawner").GetComponent<OBJGrabListener>().lastTouchedOBJ;
lastTouchesObj.transform.localScale += new Vector3(0.1f, 0.1f, 0.1f);
ObjectSync objSyn = GameObject.Find("ObjectSpawner").GetComponent<ObjectSync>();
//get the index of the scaled object in object lists (for sync)
int index = objSyn.spawnedObjects.IndexOf(lastTouchesObj);
//try to sync the new location to the server (all clients too)
objSyn.syncScaleServerRPC(index, lastTouchesObj.transform.localScale);
}
//same as scale up
public void scaleDown()
{
GameObject lastTouchesObj = GameObject.Find("ObjectSpawner").GetComponent<OBJGrabListener>().lastTouchedOBJ;
lastTouchesObj.transform.localScale -= new Vector3(0.1f, 0.1f, 0.1f);
ObjectSync objSyn = GameObject.Find("ObjectSpawner").GetComponent<ObjectSync>();
int index = objSyn.spawnedObjects.IndexOf(lastTouchesObj);
objSyn.syncScaleServerRPC(index, lastTouchesObj.transform.localScale);
}
//same as scale up BUT the location is set to 1, 1, 1 instead of adding or substring
public void resetScale()
{
GameObject lastTouchesObj = GameObject.Find("ObjectSpawner").GetComponent<OBJGrabListener>().lastTouchedOBJ;
lastTouchesObj.transform.localScale = new Vector3(1, 1, 1);
ObjectSync objSyn = GameObject.Find("ObjectSpawner").GetComponent<ObjectSync>();
int index = objSyn.spawnedObjects.IndexOf(lastTouchesObj);
objSyn.syncScaleServerRPC(index, lastTouchesObj.transform.localScale);
}
}
+11
View File
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1a6397586b0a13f43af45e1db070f389
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,15 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
//this script provides a function to keep track of the last touched object (for scale functions)
public class OBJGrabListener : MonoBehaviour
{
public GameObject lastTouchedOBJ;
public void onGrab(GameObject grabbedObject)
{
lastTouchedOBJ = grabbedObject;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6b5316dfb67ebef40bddf081bd5e5dd8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+284
View File
@@ -0,0 +1,284 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System;
using System.Globalization;
using SimpleFileBrowser;
using UnityEngine.XR;
using UnityEngine.XR.Interaction.Toolkit;
using System.Reflection;
using UnityEngine.Android;
using Unity.Netcode;
using System.Linq;
using Unity.VisualScripting;
//this script provides functions to load obj files, build 3D-Meshes from vertices and triangles etc.
//all objects do only exist on the local client, not in the network!!!
//network behaviour is "simulated" by other script like ObjectSync
//BECAUSE: netcode for gameobjects needs each network object as prefab...
//!!!!-----> these objects can not be made prefabs before runtime because they are loaded at runtime XD
public class OBJLoader : NetworkBehaviour
{
//reference to the current spawned gameobjet
private GameObject spawnedObject;
//culture info (for float parse)
private NumberFormatInfo ci = CultureInfo.InvariantCulture.NumberFormat;
[SerializeField] private Material defaultMat;
[SerializeField] private ObjectSync objectSync;
//data struct to store object data in one place
public struct OBJData
{
public string objectName;
public List<Vector3> vertices;
public List<int> triangles;
}
//convert a vector string to a vector3 object (originally made for obj fileformat, might be extended)
private Vector3 ObjVector3StringToVector3(string vecString)
{
string[] lnSplit = vecString.Trim().Split(" ");
return new Vector3(float.Parse(lnSplit[1], ci), float.Parse(lnSplit[2], ci), float.Parse(lnSplit[3], ci));
}
//this function is a litte tricky
//each face for rendering must be a tringle shape (3 vertices)
//sometimes (because of some 3D software like blender) faces have more than 3 vertices
//this functions cuts each complex shape (vertices count > 3) into nice and clean triangles
private int[] GetIndexScheme(string[] lnSplit)
{
//create an index scheme (triangles) to spawn new vertices later
int faceTrianglesCount = (lnSplit.Length - 1) - 2;
int[] indexScheme = new int[((lnSplit.Length - 1) - 2)*3];
int arrIdx = 0;
for (int i = 0; i < faceTrianglesCount; i++)
{
indexScheme[arrIdx++] = 0;
indexScheme[arrIdx++] = i + 1;
indexScheme[arrIdx++] = i + 2;
}
return indexScheme;
}
//this function can parse a triangle define string from obj file format und adds it to the triangle index list
private void AddTrianglesLineToList(string line, ref List<int> ls)
{
string[] lnSplit = line.Trim().Split(" ");
int[] indexScheme = GetIndexScheme(lnSplit);
foreach (int i in indexScheme)
{
ls.Add(int.Parse(lnSplit[i + 1].Trim().Split("/")[0]) - 1);
}
}
//this function generates a perfect triangulated mesh data as output
public OBJData buildObjData(Vector3[] vertices, int[] triangles)
{
OBJData objData = new OBJData();
objData.vertices = new List<Vector3>();
objData.triangles = new List<int>();
Vector3[] objVertices = vertices.ToArray();
//vertices have been ordered to fit triangle index so the triangle index must just count up here (fixes normal calculation problem)
int triangleIndex = 0;
foreach (int i in triangles)
{
objData.vertices.Add(objVertices[i]);
objData.triangles.Add(triangleIndex++);
}
return objData;
}
//this function reads the vertices and triangles from an obj file
private void ReadObjDataFromFile(string path)
{
//handles SAF stuff on android (works on windows too of course)
string[] lines = FileBrowserHelpers.ReadTextFromFile(path).Split("\n");
//try to get permission to access file on android
Permission.RequestUserPermission(Permission.ExternalStorageRead);
Permission.RequestUserPermission(Permission.ExternalStorageWrite);
//list to temp store obj files vertex and triangle data
List<Vector3> vertices = new List<Vector3>();
List<int> triangles = new List<int>();
//cut the obj file into pieces
//string line = "";
foreach(string line in lines)
{
if (!(line.StartsWith("vt") || line.StartsWith("s") || line.StartsWith("use") || line.StartsWith("vn")))
{
if (line.StartsWith("f"))
{
AddTrianglesLineToList(line, ref triangles);
}
else if (line.StartsWith("v"))
{
vertices.Add(ObjVector3StringToVector3(line));
}
}
}
//calculate position to spawn object in front of the player
GameObject mainCameraObj = Camera.main.gameObject;
Vector3 positionVector = mainCameraObj.transform.position + mainCameraObj.transform.forward;
positionVector.y = 1.5f;
//to actually spawn the object ask the server to spawn it (of course spawn it on all other clients too)
SpawnObjServerRPC(vertices.ToArray(), triangles.ToArray(), positionVector, new Vector3(1, 1, 1));
}
//this function creates a unity mesh from objdata struct
private Mesh CreateMeshFromObjData(OBJData objData)
{
Mesh mesh = new Mesh();
//set the vertices and triangles of the unity mesh
mesh.vertices = objData.vertices.ToArray();
mesh.triangles = objData.triangles.ToArray();
//recalucate the normals of the mesh to provide correct lighting
mesh.RecalculateNormals();
//for some reason inverted normals sometimes generate weird effects, so invert them again --> works XD
for (int i = 0; i < mesh.normals.Length; i++)
{
mesh.normals[i] = -mesh.normals[i];
}
return mesh;
}
//this function generates the final gameobject (should be only accessible by servers clientrpc call!!!)
public GameObject CreateMesh(OBJData objData, Vector3 positionVector)
{
//call the function above to get a unity mesh object
Mesh mesh = CreateMeshFromObjData(objData);
//crate a new gameobject in the scene
spawnedObject = new GameObject(objData.objectName);
//can maybe set a parent of the spawned object (creates issues in position sync, so just put in the blank scene)
//spawnedObject.transform.parent = gameObject.transform;
//spawnedObject.transform.localPosition = Vector3.zero;
//set the requested position to the gameobject
spawnedObject.transform.position = positionVector;
//create a meshfilter that will store the mesh object / data and material info
MeshFilter meshFilter = spawnedObject.AddComponent<MeshFilter>();
//create a renderer object to will render the data of the meshfilter
MeshRenderer meshRenderer = spawnedObject.AddComponent<MeshRenderer>();
//set the meshfilters mesh to the generated mesh
meshFilter.sharedMesh = mesh;
//set the meshfilters material to a a default white material passed by serializefield
meshRenderer.material = defaultMat;
//add a collider to the object that uses the generated mesh as collission shape
MeshCollider coll = spawnedObject.AddComponent<MeshCollider>();
coll.sharedMesh = mesh;
coll.convex = true;
//add a rigidbody to the object so that raycast etc. can collide with it
Rigidbody rigid = spawnedObject.AddComponent<Rigidbody>();
rigid.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
rigid.useGravity = false;
rigid.isKinematic = true;
//add scripts to make the object draggable later
XRGrabInteractable interactableGrab = spawnedObject.AddComponent<XRGrabInteractable>();
InteractionLayerMask interactLayerMask = InteractionLayerMask.GetMask("Interactable");
interactableGrab.interactionLayers = interactLayerMask;
interactableGrab.movementType = XRGrabInteractable.MovementType.VelocityTracking;
//add object grab listener to the object (for the scale etc.)
ObjectGrabListener objectGrabListener = spawnedObject.AddComponent<ObjectGrabListener>();
//add the object to the list of spawned objects to keep track of it (for other scripts)
objectSync.spawnedObjects.Add(spawnedObject);
//get the index of the spawed objects (important for network syncs)
objectGrabListener.index = objectSync.spawnedObjects.IndexOf(spawnedObject);
BlockRotAndMove rotMoveBlocker = spawnedObject.AddComponent<BlockRotAndMove>();
OBJScaler oBJScaler = spawnedObject.AddComponent<OBJScaler>();
return spawnedObject;
}
//will be called from the server to create the object on the local client
//keep in mind that this function will only run on the client and does not have to any server ressources !!!
//even if the variables are availabe --> the will be empty !!!!!!!!
[ClientRpc]
private void SpawnObjClientRPC(Vector3[] vertices, int[] triangles, Vector3 positionVector)
{
CreateMesh(buildObjData(vertices, triangles), positionVector);
}
//if you are the one that has loaded a object from a obj file, this function can be called to spawn the object
//on your own and all others clients
//all raw object data like vertices and triangles MUST be passed!
//keep in mind that this function will only run on the server and will not have any access to clients ressources
[ServerRpc(RequireOwnership = false)]
private void SpawnObjServerRPC(Vector3[] vertices, int[] triangles, Vector3 positionVector, Vector3 scaleVector)
{
ObjectSync.OBJData backupObjData = new ObjectSync.OBJData();
backupObjData.vertices = vertices;
backupObjData.triangles = triangles;
backupObjData.positionVector = positionVector;
backupObjData.rotation = new Quaternion(0, 0, 0, 0);
backupObjData.scale = new Vector3(1, 1, 1);
objectSync.objDataList.Add(backupObjData);
//tell each client to spawn the object
SpawnObjClientRPC(vertices, triangles, positionVector);
}
//this function opens the simple file browser
public void OpenObjLoadingDialog()
{
//only allow opening obj files
SimpleFileBrowser.FileBrowser.SetFilters(false, new FileBrowser.Filter("OBJ", ".obj"));
//attach loader function to the "open" button of the file browser
//place the filebrowser in the scene correctly
SimpleFileBrowser.FileBrowser.ShowLoadDialog((path) => { ReadObjDataFromFile(path[0]); }, () => { }, FileBrowser.PickMode.Files, allowMultiSelection: false);
GameObject fileExplorerObject = GameObject.Find("SimpleFileBrowserCanvas(Clone)");
GameObject mainCameraObj = Camera.main.gameObject;
fileExplorerObject.transform.rotation = mainCameraObj.transform.rotation;
Vector3 localRot = Quaternion.ToEulerAngles(fileExplorerObject.transform.localRotation);
localRot.z = 0;
localRot.x = 0;
fileExplorerObject.transform.localRotation = Quaternion.EulerAngles(localRot);
Vector3 positionVector = mainCameraObj.transform.position + mainCameraObj.transform.forward * 2 - fileExplorerObject.transform.right * 1.25f;
positionVector.y = 0;
fileExplorerObject.transform.position = positionVector;
}
private void Awake()
{
}
private List<InputDevice> mDevice = new List<InputDevice>();
private bool lastDown = false;
//depracted method to open the file browser (well still works, but is replaced by menu bars button) nice for test use
private void Update()
{
mDevice.Clear();
InputDevices.GetDevicesWithCharacteristics(InputDeviceCharacteristics.Left, mDevice);
bool primaryDown = false;
if (mDevice.Count == 1)
{
InputDevice leftController = mDevice[0];
leftController.TryGetFeatureValue(CommonUsages.primaryButton, out primaryDown);
}
if (primaryDown && !lastDown || Input.GetKeyDown(KeyCode.P))
{
OpenObjLoadingDialog();
}
lastDown = primaryDown;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 54faa82e113978d47b008620ab751a63
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+17
View File
@@ -0,0 +1,17 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
//this script attaches a listener function to a object to keep track of scaling functionallity
public class OBJScaler : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
XRGrabInteractable xRGrabInteractable = GetComponent<XRGrabInteractable>();
xRGrabInteractable.onSelectEntered.AddListener((baseInteractor) => { GameObject.Find("ObjectSpawner").GetComponent<OBJGrabListener>().onGrab(gameObject); });
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8c4188223c084b844a847c2442f7cf16
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+238
View File
@@ -0,0 +1,238 @@
using SimpleFileBrowser;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
//this script implements save logic for a room on any client
public class RoomSaver : MonoBehaviour
{
//culture info for float parsing
private CultureInfo ci = CultureInfo.InvariantCulture;
[SerializeField] private ObjectSync objSyn;
[SerializeField] private OBJLoader objLoader;
private GameObject spawnedObject;
[SerializeField] private Material defaultMat;
private string osPath;
// a class that can store all object data in one place (keep in mind DIFFERENT TO OTHER OBJData structs in other structs !!!)
public class OBJData
{
public string objectName;
public List<Vector3> vertices;
public List<int> triangles;
public Vector3 position;
public Quaternion rotation;
public Vector3 scale;
}
//if a clients requests to create a save file, the server will call this function on the client
[ClientRpc]
public void GetAllRoomDataClientRPC(ulong clientID, ObjectSync.OBJData[] objData)
{
//only the requesting client should save the file
if (clientID != NetworkManager.Singleton.LocalClientId)
{
return;
}
//generate a random string for save (save file can be renamed by user in their system file explorer)
string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
char[] stringChars = new char[8];
System.Random random = new System.Random();
for (int i = 0; i < stringChars.Length; i++)
{
stringChars[i] = chars[random.Next(chars.Length)];
}
//add the current date to the save file
string curDate = new String(DateTime.Today.ToString("d"));
string finalString = new String(stringChars) + "_" + curDate;
//check if the saves directory does already existist -not-> create it
if (!Directory.Exists(osPath + "vrcsaves"))
{
Directory.CreateDirectory(osPath + "vrcsaves");
}
//the final path to the saves destination (please keep os specific file paths in mind "c:" for windows "/ etc." for unix based systems)
string filePath = osPath + "vrcsaves/" + finalString.Replace("/", "_") + ".vrc";
StreamWriter writer = new StreamWriter(filePath);
//enumerate all objects sent by the server and write into the savefile
foreach (ObjectSync.OBJData o in objData)
{
writer.WriteLine("o");
foreach (Vector3 v in o.vertices)
{
writer.WriteLine("v$" + v.x + "$" + v.y + "$" + v.z);
}
foreach (int t in o.triangles)
{
writer.WriteLine("t$" + t);
}
writer.WriteLine("p$" + o.positionVector.x + "$" + o.positionVector.y + "$" + o.positionVector.z);
writer.WriteLine("r$" + o.rotation.x + "$" + o.rotation.y + "$" + o.rotation.z + "$" + o.rotation.w);
writer.WriteLine("s$" + o.scale.x + "$" + o.scale.y + "$" + o.scale.z);
}
//"save file changes"
writer.Flush();
}
//a client requesting to save will call this function with its id
[ServerRpc]
public void GetAllRoomDataServerRPC(ulong clientID)
{
GetAllRoomDataClientRPC(clientID, objSyn.objDataList.ToArray());
}
//this function will be called from the menu bar if a player wants to save
public void SaveScene()
{
//ask the server for the current data in the room
GetAllRoomDataServerRPC(NetworkManager.Singleton.LocalClientId);
}
private void Start()
{
//set default save paths on windows and linux
if (Application.platform == RuntimePlatform.LinuxEditor || Application.platform == RuntimePlatform.LinuxServer || Application.platform == RuntimePlatform.LinuxPlayer)
{
osPath = "/home/" + Environment.UserName + "/";
}
else
{
osPath = "c:/";
}
try
{
//try to get a filepath to load a a save file
//if there is no save file a blank classroom will be created
if (PlayerPrefs.GetString("path") != "")
{
string path = PlayerPrefs.GetString("path");
ReadEvenMoreObjDataFromFile(path);
PlayerPrefs.SetString("path", "");
}
}
catch
{
Debug.Log("Fehler beim Lesen der Datei");
}
}
//This is a meme <-- XD
//this function does read and load a save file
private void ReadEvenMoreObjDataFromFile(string path)
{
List<OBJData> objDataList = new List<OBJData>();
//read the file data and split each line
string[] lines = FileBrowserHelpers.ReadTextFromFile(path).Split("\n");
OBJData dataStruct = new OBJData();
string curString;
foreach (string line in lines)
{
line.Trim();
curString = line.Replace("$", " ");
//if there is a "o" the data block of a new object will begin
if (curString.StartsWith("o"))
{
dataStruct = new OBJData();
dataStruct.vertices = new List<Vector3>();
dataStruct.triangles = new List<int>();
objDataList.Add(dataStruct);
}
//if there is a "v" at the start of the line there is a vertex stored
else if (curString.StartsWith("v"))
{
dataStruct.vertices.Add(ComeBackOfTheObjVector3StringToVector3(curString));
}
//if there is a "t" at the start of the line there is a triangle index stored
else if (curString.StartsWith("t"))
{
int curInt = int.Parse(curString.Trim().Replace("t", ""));
dataStruct.triangles.Add((curInt));
}
//if there is a "p" at the start of the line position of the gameobject is stored here
else if (curString.StartsWith("p"))
{
dataStruct.position = ComeBackOfTheObjVector3StringToVector3(curString);
}
//if there is a "r" at the start of the line rotation of the gameobject is stored here
else if (curString.StartsWith("r"))
{
dataStruct.rotation = ObjQuanternion(curString);
}
//if there is a "s" at the start of the line scale of the gameobject is stored here
else if (curString.StartsWith("s"))
{
dataStruct.scale = ComeBackOfTheObjVector3StringToVector3(curString);
}
}
//create a mesh/gameobject of each loaded object data
for (int i = 0; i < objDataList.Count; i++)
{
GameObject finishedObj = objLoader.CreateMesh(objLoader.buildObjData(objDataList[i].vertices.ToArray(), objDataList[i].triangles.ToArray()), objDataList[i].position);
//apply rotation and scale to the gameobject
finishedObj.transform.rotation = objDataList[i].rotation;
finishedObj.transform.localScale = objDataList[i].scale;
//create info to keep track of the object in servers object list etc.
ObjectSync.OBJData objsyncData = new ObjectSync.OBJData();
objsyncData.vertices = objDataList[i].vertices.ToArray();
objsyncData.triangles = objDataList[i].triangles.ToArray();
objsyncData.positionVector = objDataList[i].position;
objsyncData.rotation = objDataList[i].rotation;
Debug.Log(objDataList[i].scale);
//add the object to the sync
objsyncData.scale = objDataList[i].scale;
objSyn.objDataList.Add(objsyncData);
objSyn.spawnedObjects.Add(finishedObj);
}
}
//This is another meme
//this function creates a vector3 object from a vector stored in vrc file
private Vector3 ComeBackOfTheObjVector3StringToVector3(string vecString)
{
vecString = vecString.Replace(",", ".");
string[] lnSplit = vecString.Trim().Split(" ");
return new Vector3(float.Parse(lnSplit[1], NumberStyles.Float, ci),
float.Parse(lnSplit[2], NumberStyles.Float, ci),
float.Parse(lnSplit[3], NumberStyles.Float, ci));
}
//this function creates a quaternion object from a quaternion stored in vrc file
private Quaternion ObjQuanternion(string quanString)
{
quanString = quanString.Replace(",", ".");
string[] lnSplit = quanString.Trim().Split(" ");
return new Quaternion(float.Parse(lnSplit[1], NumberStyles.Float, ci),
float.Parse(lnSplit[2], NumberStyles.Float, ci),
float.Parse(lnSplit[3], NumberStyles.Float, ci),
float.Parse(lnSplit[4], NumberStyles.Float, ci));
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c1825b2a99ca3834a81d5318722cfe99
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5341123b4bc035641902a29d071b85b5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+37
View File
@@ -0,0 +1,37 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
//this script stors the selected color to the playerprefs
public class ColorButton : MonoBehaviour
{
[SerializeField] Color buttonColor;
[SerializeField] GameObject nameInput;
private void Start()
{
GetComponent<Image>().color = buttonColor;
GetComponent<Button>().onClick.AddListener(() =>
{
PlayerPrefs.SetFloat("playerColorR", buttonColor.r);
PlayerPrefs.SetFloat("playerColorG", buttonColor.g);
PlayerPrefs.SetFloat("playerColorB", buttonColor.b);
PlayerPrefs.Save();
transform.parent.parent.gameObject.SetActive(false);
changeScene();
});
}
public void changeScene()
{
//if the name has already been set, change the scene
if (nameInput.activeInHierarchy == false)
{
SceneManager.LoadScene("RoomCreation");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 97f3ea8b8c8a14e4183b81adaafdf9e6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,41 @@
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
//this script stores the input name to the playerprefs
public class NameInputKeyboard : MonoBehaviour
{
[SerializeField] private Button enterButton;
[SerializeField] private TMP_Text inputText;
[SerializeField] private GameObject keyboardObject;
[SerializeField] private GameObject colorInput;
private void Start()
{
enterButton.onClick.AddListener( () =>
{
SetName(inputText.text);
keyboardObject.SetActive(false);
changeScene();
});
}
public void SetName(string name)
{
PlayerPrefs.SetString("playerName", name);
PlayerPrefs.Save();
}
public void changeScene()
{
//if the color has already been select, change the scene
if (colorInput.activeInHierarchy == false)
{
SceneManager.LoadScene("RoomCreation");
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 71d94d1bf314c324cacae87c9b4333f4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
+8
View File
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d59ebf279daff5f47bcf554e7c430c50
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,59 @@
using SimpleFileBrowser;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
//this script is in the room creation scene and provides functions to load a vrc save files
public class LoadSafedFiles : MonoBehaviour
{
[SerializeField] private TMP_Text infoText;
private void Start()
{
try
{
//add click listener to the button
GetComponent<Button>().onClick.AddListener(() =>
{
OpenObjLoadingDialog();
});
}
catch
{
}
}
//if a save file is loaded the current client be made a host (server + client)
private void InitializeNetworkData(string path)
{
PlayerPrefs.SetString("path", path);
PlayerPrefs.SetString("status", "host");
PlayerPrefs.Save();
SceneManager.LoadScene("ClassRoom");
}
// see OBJLoader (same logic)
private void OpenObjLoadingDialog()
{
SimpleFileBrowser.FileBrowser.SetFilters(false, new FileBrowser.Filter("VRC", ".vrc"));
SimpleFileBrowser.FileBrowser.ShowLoadDialog((path) => { InitializeNetworkData(path[0]); }, () => { }, FileBrowser.PickMode.Files, allowMultiSelection: false, initialPath: "C:\\vrcsaves");
GameObject fileExplorerObject = GameObject.Find("SimpleFileBrowserCanvas(Clone)");
GameObject mainCameraObj = Camera.main.gameObject;
fileExplorerObject.transform.rotation = mainCameraObj.transform.rotation;
Vector3 localRot = Quaternion.ToEulerAngles(fileExplorerObject.transform.localRotation);
localRot.z = 0;
localRot.x = 0;
fileExplorerObject.transform.localRotation = Quaternion.EulerAngles(localRot);
Vector3 positionVector = mainCameraObj.transform.position + mainCameraObj.transform.forward * 2 - fileExplorerObject.transform.right * 1.25f;
positionVector.y = 0;
fileExplorerObject.transform.position = positionVector;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 773e43e630b734a4997e658c059bcef7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,29 @@
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using System.Net;
using System.Net.Sockets;
public class NetworkHostFunction : MonoBehaviour
{
// Start is called before the first frame update
public void Start()
{
GetComponent<Button>().onClick.AddListener(() =>
{
setNetworkData();
SceneManager.LoadScene("ClassRoom");
});
}
//make the current client host (client + server at the same time)
public void setNetworkData()
{
PlayerPrefs.SetString("status", "host");
PlayerPrefs.Save();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6b045e4b35c7bdf409d9d593275cce02
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,56 @@
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.Text.RegularExpressions;
using System.Net.Sockets;
public class NetworkJoinFunction : MonoBehaviour
{
[SerializeField] private Button enterButton;
[SerializeField] private TMP_Text inputText;
[SerializeField] private TMP_Text infoText;
// Start is called before the first frame update
public void Start()
{
enterButton.onClick.AddListener(() =>
{
//check if there ip entered is valid
//for any reason the keyboard of blackwhale studios creates zero width whitespaces, this must be cleared!
//https://en.wikipedia.org/wiki/Zero-width_space
if (IsValidIPv4(inputText.text.Replace("\u200B", "")))
{
setNetworkData(inputText.text);
SceneManager.LoadScene("ClassRoom");
}
else
{
infoText.text = "Die Eingabe scheint keine valide IPv4-Adresse zu sein.\n Bitte berprfe deine Eingabe und achte drauf, die Zieladresse wie folgt anzugeben \n 127.0.0.1";
}
});
}
public bool IsValidIPv4(string ipAddress)
{
//the pattern that a valid ipv4 must match
string pattern = @"^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
Regex regex = new Regex(pattern);
return regex.IsMatch(ipAddress);
}
//just make the current client a client (no server/host etc.)
public void setNetworkData(string hostadress)
{
PlayerPrefs.SetString("hostadress", hostadress.Replace("\u200B", ""));
PlayerPrefs.SetString("status", "client");
PlayerPrefs.Save();
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9e2d50b0ef5577f46abdc76e3cda587c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: