from github
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90dc410c9b2947049b024fe156c18f0c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2bb3facba82353f4a8665dccdd335105
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
@@ -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:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5341123b4bc035641902a29d071b85b5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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:
|
||||
@@ -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 �berpr�fe 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:
|
||||
Reference in New Issue
Block a user