Skip to content

Instantly share code, notes, and snippets.

@planaria
Created August 18, 2023 19:03
Show Gist options
  • Save planaria/421994e86af949bd61f9db56f041e857 to your computer and use it in GitHub Desktop.
Save planaria/421994e86af949bd61f9db56f041e857 to your computer and use it in GitHub Desktop.
using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
using VRC.Udon;
public class ColliderDisabler : UdonSharpBehaviour
{
public UdonBehaviour portal1;
public UdonBehaviour portal2;
private MeshCollider lastCollider1;
private MeshCollider lastCollider2;
public Text debug;
private void UpdateWallCollider(UdonBehaviour portal, MeshCollider collider)
{
var localPlayer = Networking.LocalPlayer;
var p1 = localPlayer.GetPosition();
var p2 = localPlayer.GetBonePosition(UnityEngine.HumanBodyBones.Head);
var p = (p1 + p2) * 0.5f;
var v = p - portal.transform.position;
var n = portal.transform.rotation * new Vector3(0.0f, 1.0f, 0.0f);
var dot = Vector3.Dot(n, v);
var proj = v - n * dot;
if (Mathf.Abs(dot) > 2.0f)
{
return;
}
var threshold = 1.0f;
if (proj.sqrMagnitude > threshold * threshold)
{
return;
}
collider.enabled = false;
}
void LateUpdate()
{
if (lastCollider1 != null)
{
lastCollider1.enabled = true;
}
if (lastCollider2 != null)
{
lastCollider2.enabled = true;
}
var wallGameObject1 = (GameObject)portal1.GetProgramVariable("wallGameObject");
var wallGameObject2 = (GameObject)portal2.GetProgramVariable("wallGameObject");
MeshCollider collider1 = null;
MeshCollider collider2 = null;
if (wallGameObject1 != null && wallGameObject2 != null)
{
collider1 = (MeshCollider)wallGameObject1.GetComponent(typeof(MeshCollider));
if (collider1 != null)
{
UpdateWallCollider(portal1, collider1);
}
collider2 = (MeshCollider)wallGameObject2.GetComponent(typeof(MeshCollider));
if (collider2 != null)
{
UpdateWallCollider(portal2, collider2);
}
}
lastCollider1 = collider1;
lastCollider2 = collider2;
}
}
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class Gun : UdonSharpBehaviour
{
public GameObject portal;
public GameObject anotherPortal;
public Vector3 offset;
public string button;
public GameObject moon;
public GameObject moonSurface;
public AudioSource audioSource;
public AudioClip shootEnable;
public AudioClip shootDisable;
private bool pickup;
private float dropTime;
private const int targetLayer = 25;
private const int noTargetLayer = 0;
void Update()
{
var localPlayer = Networking.LocalPlayer;
if (localPlayer != null)
{
var now = Time.time;
if (pickup)
{
dropTime = now;
}
if (now - dropTime > 5.0f)
{
var head = localPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
transform.position = head.position + head.rotation * offset;
transform.rotation = head.rotation;
}
if (!localPlayer.IsUserInVR() && Input.GetButtonDown(button))
{
var head = localPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
var origin = head.position;
var direction = head.rotation * new Vector3(0.0f, 0.0f, 1.0f);
ShootAndSound(origin, direction);
}
}
}
public override void OnPickup()
{
pickup = true;
}
public override void OnDrop()
{
pickup = false;
}
private bool CheckGameObject(Vector3 origin, Vector3 normal, GameObject obj)
{
RaycastHit hit;
var layerMask = 1 << targetLayer;
if (!Physics.Raycast(origin + normal * 0.01f, -normal, out hit, 0.02f, layerMask))
{
return false;
}
if (hit.transform.gameObject != obj)
{
return false;
}
if (Vector3.Dot(hit.normal, normal) < Mathf.Cos(1.0f * Mathf.Deg2Rad))
{
return false;
}
if (Mathf.Abs(Vector3.Dot(hit.point - origin, normal)) > 0.01f)
{
return false;
}
return true;
}
private float Bisect(Vector3 origin, Vector3 normal, GameObject obj, Vector3 v, float range)
{
var min = 0.0f;
var max = range;
while (max - min > 0.01)
{
var mid = (min + max) * 0.5f;
if (CheckGameObject(origin + v * mid, normal, obj))
{
min = mid;
}
else
{
max = mid;
}
}
return min;
}
private bool Shoot(Vector3 origin, Vector3 direction)
{
RaycastHit hit;
var layerMask = (1 << targetLayer) | (1 << noTargetLayer);
if (!Physics.Raycast(origin, direction, out hit, Mathf.Infinity, layerMask))
{
return false;
}
if (hit.transform.gameObject == moon)
{
var portalUdon = (UdonBehaviour)portal.GetComponent(typeof(UdonBehaviour));
portalUdon.SetProgramVariable("wallGameObject", moonSurface);
portalUdon.SetProgramVariable("relativePosition", new Vector3());
portalUdon.SetProgramVariable("relativeRotation", Quaternion.identity);
return true;
}
else if (hit.transform.gameObject.layer == targetLayer)
{
var q = Quaternion.FromToRotation(new Vector3(0.0f, 1.0f, 0.0f), hit.normal);
var v1 = q * new Vector3(1.0f, 0.0f, 0.0f);
var v2 = q * new Vector3(0.0f, 0.0f, 1.0f);
var minMargin = 1.1f;
var p = hit.point;
for (int i = 0; i < 180; i += 45)
{
var angle = (float)i * Mathf.Deg2Rad;
var sin = Mathf.Sin(angle);
var cos = Mathf.Cos(angle);
var v = v1 * cos + v2 * sin;
var margin1 = Bisect(p, hit.normal, hit.transform.gameObject, v, minMargin * 2.0f);
var margin2 = Bisect(p, hit.normal, hit.transform.gameObject, -v, minMargin * 2.0f);
float d = 0.0f;
if (margin1 + margin2 < 2.0f * minMargin)
{
return false;
}
if (margin1 < minMargin)
{
d = margin1 - minMargin;
}
else if (margin2 < minMargin)
{
d = minMargin - margin2;
}
p += v * d;
}
for (int i = 0; i < 180; i += 10)
{
var angle = (float)i * Mathf.Deg2Rad;
var sin = Mathf.Sin(angle);
var cos = Mathf.Cos(angle);
var v = v1 * cos + v2 * sin;
if (!CheckGameObject(p + v, hit.normal, hit.transform.gameObject))
{
return false;
}
}
var anotherUdon = (UdonBehaviour)anotherPortal.GetComponent(typeof(UdonBehaviour));
var anotherWall = (GameObject)anotherUdon.GetProgramVariable("wallGameObject");
if (anotherWall == hit.transform.gameObject)
{
if (Vector3.Distance(anotherPortal.transform.position, p) < 2.2f)
{
return false;
}
}
var invRot = Quaternion.Inverse(hit.transform.rotation);
var relativePosition = invRot * (p - hit.transform.position);
var relativeRotation = invRot * q;
var portalUdon = (UdonBehaviour)portal.GetComponent(typeof(UdonBehaviour));
portalUdon.SetProgramVariable("wallGameObject", hit.transform.gameObject);
portalUdon.SetProgramVariable("relativePosition", relativePosition);
portalUdon.SetProgramVariable("relativeRotation", relativeRotation);
return true;
}
return false;
}
private bool ShootAndSound(Vector3 origin, Vector3 direction)
{
if (Shoot(origin, direction))
{
audioSource.PlayOneShot(shootEnable);
return true;
}
else
{
audioSource.PlayOneShot(shootDisable);
return false;
}
}
public override void OnPickupUseDown()
{
var direction = transform.rotation * new Vector3(0.0f, 1.0f, 0.0f);
var origin = transform.position + direction * 0.1f;
ShootAndSound(origin, direction);
}
}
using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
using VRC.Udon;
public class Portal : UdonSharpBehaviour
{
public GameObject wallGameObject;
public GameObject visual;
public Vector3 relativePosition;
public Quaternion relativeRotation;
void Start()
{
}
void Update()
{
if (wallGameObject != null)
{
transform.position = wallGameObject.transform.position + wallGameObject.transform.rotation * relativePosition;
transform.rotation = wallGameObject.transform.rotation * relativeRotation;
visual.SetActive(true);
}
else
{
visual.SetActive(false);
}
}
}
using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
using VRC.Udon;
public class Tracker : UdonSharpBehaviour
{
public Camera refCamera;
public Camera camera;
public GameObject portal1;
public GameObject portal2;
void Start()
{
var localPlayer = Networking.LocalPlayer;
if (localPlayer != null)
{
camera.enabled = true;
camera.nearClipPlane = refCamera.nearClipPlane;
camera.farClipPlane = refCamera.farClipPlane;
}
}
private bool IsActive(GameObject portal)
{
var udon = (UdonBehaviour)portal.GetComponent(typeof(UdonBehaviour));
var wall = (GameObject)udon.GetProgramVariable("wallGameObject");
return wall != null;
}
private Quaternion CalculateRotation(Quaternion r1, Quaternion r2)
{
var v1 = r1 * new Vector3(0.0f, -1.0f, 0.0f);
var v2 = r2 * new Vector3(0.0f, 1.0f, 0.0f);
Quaternion q = Quaternion.identity;
if (v1.y < -0.99)
{
q = Quaternion.Euler(180.0f, 0.0f, 0.0f) * q;
}
else if (v1.y < 0.99)
{
var rotary = Mathf.Atan2(v1.z, v1.x) * Mathf.Rad2Deg;
q = Quaternion.Euler(0.0f, rotary, 0.0f) * q;
var tilt = Mathf.Acos(v1.y) * Mathf.Rad2Deg;
q = Quaternion.Euler(0.0f, 0.0f, tilt) * q;
}
if (v2.y < -0.99)
{
q = Quaternion.Euler(180.0f, 0.0f, 0.0f) * q;
}
else if (v2.y < 0.99)
{
var tilt = Mathf.Acos(v2.y) * Mathf.Rad2Deg;
q = Quaternion.Euler(0.0f, 0.0f, -tilt) * q;
var rotary = Mathf.Atan2(v2.z, v2.x) * Mathf.Rad2Deg;
q = Quaternion.Euler(0.0f, -rotary, 0.0f) * q;
}
return q;
}
void LateUpdate()
{
var localPlayer = Networking.LocalPlayer;
if (localPlayer != null)
{
camera.enabled = IsActive(portal1) && IsActive(portal2);
var rot = CalculateRotation(portal1.transform.rotation, portal2.transform.rotation);
var head = localPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
var pos = portal2.transform.position + rot * (head.position - portal1.transform.position);
var invQ = Quaternion.Inverse(camera.transform.localRotation);
var q = rot * head.rotation * invQ;
var scale = 1.0f / (camera.transform.localScale.x * camera.transform.localScale.x);
transform.localScale = Vector3.one * scale;
transform.position = pos - q * camera.transform.localPosition * scale;
transform.rotation = q;
}
}
}
using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
using VRC.Udon;
public class Warp : UdonSharpBehaviour
{
public GameObject portal1;
public GameObject portal2;
private float lastWarpTime;
public AudioSource audioSource;
public AudioClip[] warpAudioClips;
public Text debug;
private bool IsActive(GameObject portal)
{
var udon = (UdonBehaviour)portal.GetComponent(typeof(UdonBehaviour));
var wall = (GameObject)udon.GetProgramVariable("wallGameObject");
return wall != null;
}
private Quaternion CalculateRotation(Quaternion r1, Quaternion r2)
{
var v1 = r1 * new Vector3(0.0f, -1.0f, 0.0f);
var v2 = r2 * new Vector3(0.0f, 1.0f, 0.0f);
Quaternion q = Quaternion.identity;
if (v1.y < -0.99)
{
q = Quaternion.Euler(180.0f, 0.0f, 0.0f) * q;
}
else if (v1.y < 0.99)
{
var rotary = Mathf.Atan2(v1.z, v1.x) * Mathf.Rad2Deg;
q = Quaternion.Euler(0.0f, rotary, 0.0f) * q;
var tilt = Mathf.Acos(v1.y) * Mathf.Rad2Deg;
q = Quaternion.Euler(0.0f, 0.0f, tilt) * q;
}
if (v2.y < -0.99)
{
q = Quaternion.Euler(180.0f, 0.0f, 0.0f) * q;
}
else if (v2.y < 0.99)
{
var tilt = Mathf.Acos(v2.y) * Mathf.Rad2Deg;
q = Quaternion.Euler(0.0f, 0.0f, -tilt) * q;
var rotary = Mathf.Atan2(v2.z, v2.x) * Mathf.Rad2Deg;
q = Quaternion.Euler(0.0f, -rotary, 0.0f) * q;
}
return q;
}
private bool Check(GameObject portalA, GameObject portalB)
{
var now = Time.time;
if (now - lastWarpTime < 0.1)
{
return false;
}
var localPlayer = Networking.LocalPlayer;
var p1 = localPlayer.GetPosition();
var p2 = localPlayer.GetBonePosition(UnityEngine.HumanBodyBones.Head);
var p = (p1 + p2) * 0.5f;
var v = p - portalA.transform.position;
var n = portalA.transform.rotation * new Vector3(0.0f, 1.0f, 0.0f);
var dot = Vector3.Dot(v, n);
if (dot >= -0.01 || dot < -5.0f)
{
return false;
}
var proj = v - n * dot;
if (proj.magnitude > 1.5f)
{
return false;
}
var rot = CalculateRotation(portalA.transform.rotation, portalB.transform.rotation);
var pos = portalB.transform.position + rot * (p - portalA.transform.position) + (localPlayer.GetPosition() - p);
localPlayer.TeleportTo(pos, rot * localPlayer.GetRotation(), VRC_SceneDescriptor.SpawnOrientation.AlignPlayerWithSpawnPoint, false);
localPlayer.SetVelocity(rot * localPlayer.GetVelocity());
if (warpAudioClips.Length != 0)
{
audioSource.PlayOneShot(warpAudioClips[Random.Range(0, warpAudioClips.Length)]);
}
lastWarpTime = now;
return true;
}
void Update()
{
if (IsActive(portal1) && IsActive(portal2))
{
if (!Check(portal1, portal2))
{
Check(portal2, portal1);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment