#udon-networking
1 messages · Page 17 of 1
i thought it would only follow locally?
Object Sync is needed for pickups.
With creator companion, use the quick launcher w/ 2 diff accounts and test ur code out
i did it with build and test and see that
im just surprised
because all this time i thought you needed object sync to do it-
you are technically updating locally. But you're telling the local client to make this object follow its owner, which can and will be a remote player instead of the local player
i see hmm
you're using the player's networked position to "network" the object instead
alright
Yeah think of players already having ObjectSync on them
Since we all need to see when other move etc
that makes senseish
only thing I'm worried abt is starting the following OnEnable; I've noticed in my testing that PlayerObjects get added to the world a little bit before the player is actually in the scene, so it might not be able to find the player's position yet
oh..?
oh and it stores the owner twice in two different variables, but uses only one of them to just print their name to the console....?
Make your start to the onplayerrestored
should only have to do that once, and print out player's displayName instead, the result will be the same
yea that will be better
alright 1 sec
anything persistence related, you want to wait for OnPlayerRestored just in case
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class FollowPlayer : UdonSharpBehaviour
{
VRCPlayerApi player;
public GameObject hitbox;
public override void OnPlayerRespawn(VRCPlayerApi player)
{
hitbox = gameObject;
}
public void OnEnable()
{
player = Networking.GetOwner(gameObject);
Debug.Log("Owner = " + player.displayName);
}
private void LateUpdate()
{
TrackPlayer();
}
public void TrackPlayer()
{
VRCPlayerApi.TrackingData headData = player.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
hitbox.transform.position = headData.position;
hitbox.transform.rotation = headData.rotation;
}
public override void OnOwnershipTransferred(VRCPlayerApi newOwner)
{
player = newOwner;
}
}
oops
i did onplayerespawn
by accident
i dont think its working now
oh, i took that out before i even tested
lol
but the issue is i changes start to restored but now the game object doesnt get set
oh wait a minute
That's respawn
this has no persistent data on it
I don't actually know if OnPlayerRestored even fires if the object isn't set to be persistent
it doesnt seem like it did
he doesnt have onplayeresroted in his script
u dont need persistence
example of my footstep script
yeah my aim right now is have these cubes act as hitboxes for later when i make thing hit them to do damage
trying to make a super basic combat system then build off it
since my favorite system iv been using has stopped being updated and has issues
also when u send ur scripts, after the first 3 `'s put C#
honestly, this is like, way out of my league but i think if i take it slow, ask questions and take my time i can get somewhere since you guys are very helpful
lemme try rq
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class FollowPlayer : UdonSharpBehaviour
{
VRCPlayerApi player;
public GameObject hitbox;
void Start()
{
hitbox = gameObject;
}
public void OnEnable()
{
player = Networking.GetOwner(gameObject);
Debug.Log("Owner = " + player.displayName);
}
private void LateUpdate()
{
TrackPlayer();
}
public void TrackPlayer()
{
VRCPlayerApi.TrackingData headData = player.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
hitbox.transform.position = headData.position;
hitbox.transform.rotation = headData.rotation;
}
public override void OnOwnershipTransferred(VRCPlayerApi newOwner)
{
player = newOwner;
}
}
thats cool
oh yeah imma remove ownership thingy too
I think that ownership thing is in there because the creator of that tutorial said they "don't trust VRChat to not screw it up", which I feel is a bad precedent to code around, as ownership of PlayerObjects are pretty rock fuckin' solid in my experiments
now... the harder part raycasts and damage detection.. don't suppose you guys have any decent tutorials in mind for that?
ahh fair enough
So for that, youll need a collider on your playerobject and have it be a trigger
and whenever the right thing triggers it, u can decrement their health or whatever
I'd start slow. Make like an object that ticks your health down (you could check out the Example Central Health bar)
you may not even need raycasts, if your PVP system will have guns or whatnot, just detect the particles hitting the collider
would just having it set as a cube and change the box collider to istrigger work?
yeah it should...
from what iv seen, and i could be wrong, particles tend to have like big big latency in pvp
Cause my footsteps have the ability to read what u step on
should be isTrigger. or else the player will collide with the box and you'll fly off into the sky lol
xd
i dont believe the health state needs to be synced does it?
not unless you want other players to see it
Unless you wanna convey it to everyone
i dont think it will need to be seen by everyone, for the sake of keeping it simple id prefer to have it handled locally
yeah then you could just throw an int onto the player object for health
would a float or int be better for this situation?
IDK, depends on what u wanna do
If you care about people being at like 12.5 health over 12
¯_(ツ)_/¯
prob gonna stick to float
It would be easy to change in the future. regardless of the route u take
yeah
now the question
particles vs raycasts
which is more performant, and easier to implement
as well as taking latency into consideration
let's start with that then
I wouldnt worry about it until you get ur health sorted out
Again, make an object that ticks your health down when u are colliding w/ it
if you make hit detection client-sided then latency won't be much of a problem
alright ill start with that
I also believe there is a prefab for raycasts
huh? didnt know that
Yeah... ill look for it while u work on ur stuff
oki
one of these. IDK if the first one is for ur use case BUT i imagine you could pull it apart to make it work.
Having guns and crap fire is a bit more complicated which is why I suggest getting ur other stuff sorted
mhm yeah
IDK the "best" way to do it but I will probably have something figured out in a few weeks when I get to it for my world...
just trying to remember how to correctly get a component in a ontriggerenter
i thought i was doing it right 😅
you doing something similar?
one more tutorial for ya comin right up
Well, I am making a bar and I want a "drink" to be a squirt gun that on click will make the collided user tipsy
thats cool
A simple tutorial to make a script to turn on a mirror when you walk in front of it. You could do a simple LOD component and base the activation on the distance instead, but that wouldn't give as much control
Hope this helps!
Come join the discord! https://discord.gg/jjdnCWVvKg
Wanna help support me in making more vrchat stuff? Support me on p...
the concept is similar...
So on triggerenter over onplayertrigerenter
that will give you the thing it collided with, so for your example. You have a lava floor with a collider. When you touch it from your hitbox, you can check
What am I colliding with?
Maybe u will have it go off names or a literal damage script attached to whatever ur touchign which stores all that crap
really u can do whatever
remember if you are using particles it's OnParticleCollision
Does prison escape use particles?
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class PlayerHealth : UdonSharpBehaviour
{
public float playerHealth = 100f;
public float respawnHealth = 100f;
void Start()
{
}
public void TakeDamage(float damage)
{
playerHealth = playerHealth - damage;
}
}
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class Projectile : UdonSharpBehaviour
{
private PlayerHealth targetPlayerHealth;
public float projectileDamage = 10;
public void OnTriggerEnter(Collider other)
{
if (other.gameObject.GetComponent<PlayerHealth>())
{
targetPlayerHealth = other.gameObject.GetComponent<PlayerHealth>();
}
}
public void DamageTarget()
{
targetPlayerHealth.TakeDamage(projectileDamage);
}
}
this is what i did so far to test but i dont think i did it right
I think this would work for rigidbodies hitting the collider
like a sword or something
Lowkey I don't see a point in abstracting all this stuff out to seperate scripts
I would just tack it into your player object and call it a day
Especially if you aren't the "best" coder
and what ur doign is calling the collder and checking it has a PlayerHealth Script
oh wait
I read it wrong
you aren't ever calling ur DamageTarget
so after u assigned ur targetPlayerHealth in ur OnTriggerEnter, u should call damage target
Yeah I forgot that bit
Yeah that's what I think i should do too
Although, I'm not sure how I'd work particle collision into this as damage
Since I'd like there to be different damage amounts
Well when someone shoots a gun, you can call a global network event to instantiate those particles. THey will move in whatever direction.
The particles sent could have a damage script associated to them. So when the particles collide with the hitbox, the hitbox will just grab the damage script and take the values from it
I am not familiar with using them beyond playing them but I am sure we can figure it out
I may want to do that too ngl
Maybe some kind of tutorial is out there
Maybe, I am sure we could also figure it out either through trial and error or pulling apart a public prefab
(also I bet Legos may know)
xD
Big brain
So a basic script that will fire a particle globally and damage a player if it hits their hit box
Question, if it's like an automatic weapon firing a lot will it screw up the networking?
Well your gun will need a like ontriggerdown or whatever, that will spawn a particle which will go flying.
Collide with a hitbox and ur hitbox will grab all the damage data it needs
Particles youve got the right idea, either you look for the name or as Sondly said a script on it (need to filter it somehow, as OnParticleCollision fires for ANY particle that is set to send collision events)
Oh boy
lowkey to test that out, you could just make a cube that fires every second.
and u can walk into the particle and see
True
Just need to figure out how to fire it and find a particle that will work with it
the only gun I've made is a sort of sniper rifle, so all I did was configure the particle system not to emit on its own, then used the Emit() function on the particle to emit 1 particle
for automatic hmm. You can use Stop() and Play() to turn the system on and off, or put the particle system on a GameObject and toggle it on and off
oh and footsteps I think are commonly done by getting the player's velocity, then increasing the volume of the sound based on their speed
and maybe also the speed at which the sound plays?
I don't do that personally but yeah.
Interesting
okay guys i got a gun now and firing off particles which is great, now i just need it to apply damage, ill show what i have so far
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using VRC.Udon.Common.Interfaces;
public class Gun : UdonSharpBehaviour
{
public ParticleSystem bullet;
public override void OnPickupUseDown()
{
SendCustomNetworkEvent(NetworkEventTarget.All, "Fire");
}
public void Fire()
{
bullet.Play();
}
}
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class Projectile : UdonSharpBehaviour
{
private PlayerHealth targetPlayerHealth;
public float projectileDamage = 10;
public ParticleSystem bullet;
public void OnParticleCollision(GameObject other)
{
if (other.gameObject.GetComponent<PlayerHealth>())
{
targetPlayerHealth = other.gameObject.GetComponent<PlayerHealth>();
Debug.Log("Collision Detected" + other.name);
}
}
public void DamageTarget()
{
targetPlayerHealth.TakeDamage(projectileDamage);
}
}
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class PlayerHealth : UdonSharpBehaviour
{
public float playerHealth = 100f;
public float respawnHealth = 100f;
public void TakeDamage(float damage)
{
playerHealth = playerHealth - damage;
}
}
currently, when the particle hits the cube it doesnt apply any damage
the detection is working when it hits the cube at least
thats good
oh right xd
yup it works now
🫡 nice work tho. It isn't too bad once you get the hang of it
Yeah, so you could do something like prison escape where you can toggle everyones collider to be visible
and technically, you could even change the shade of the material on the collider like prison escape does
and that could technically represent health
but IK you said earlier it isn't important
hmmm i just need to ensure it works now
Make an alt and dual boot into your vr world to test it out
It might be beneifical to have a ui in your world that shows the local players health
doing a build and testw ith 2 clients
yeah thats what i intend to do late
You got it boss 🫡
there is some unexpected behavior rn
uhh ill try to explain
unless i figured it out checking rq
Screw with it. It may be time to work on a UI to debug crap
well one thing is, if the player is walking forward while firing it will show it exploding in the player's face for player 2 but appear to be firing normally for player 1 who is firing it
i know i need to make a layer to make the cube not collide with the player too
since istrigger cant be off it seems
I imagine the particle spawns for everyone and is colliding with the persons hitbox who is shooting it
yes
So if you want the user to be able to shoot themselves (low-key kinda funny) , you may need to tune where the particle is relative to the gun
as for the layer, how should i make it so the cube doesnt collide with the player but can still be it by particles?
yeah, its pretty far ahead of the gun already so its kinda weird
Then it's network latency
You can scale the hitbox down a bit
Also the local player doesn't technically need to have their hitbox active on their end if you don't want them to be able to hurt themselves
yeah that would help to have it off
So you could just turn off the collider for the local player
how would you approach that?
Drop your hitbox here again
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class PlayerHealth : UdonSharpBehaviour
{
public float playerHealth = 100f;
public float respawnHealth = 100f;
public void TakeDamage(float damage)
{
playerHealth = playerHealth - damage;
if (playerHealth <= 0)
{
gameObject.SetActive(false);
}
}
}```
This isn't the hitbox
this is the script attached to the hitbox?
where's the collision event(s)
Cause it doesn't track on the player
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class FollowPlayer : UdonSharpBehaviour
{
VRCPlayerApi player;
public GameObject hitbox;
void Start()
{
hitbox = gameObject;
}
public void OnEnable()
{
player = Networking.GetOwner(gameObject);
Debug.Log("Owner = " + player.displayName);
}
private void LateUpdate()
{
TrackPlayer();
}
public void TrackPlayer()
{
VRCPlayerApi.TrackingData headData = player.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
hitbox.transform.position = headData.position;
hitbox.transform.rotation = headData.rotation;
}
}
So after you assign the hitbox in start, you can check the local player, if they are local, find the collider component and disable it
alrighty
How you view code here is everything is ran on everyone's machine (very important on start and update functions)
oh..
void Start()
{
hitbox = gameObject;
targetCollider = gameObject.GetComponent<Collider>();
if (player.isLocal)
{
targetCollider.enabled = false;
}
}
like that?
or wait, that may not work because OnEnable is called after start right?
OnEnable is first I believe
oh
okay, so i that works and i made a layer for it so it doesnt collide with players
now there is some odd behavior
so, lets say player 2 is firing at player 1, both players see the impact and after 10 shots (amount needed to reach dead state) the cube turns off, but it only turns off for the shooter.
is it tied in with how the damage logic works?
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class Projectile : UdonSharpBehaviour
{
private PlayerHealth targetPlayerHealth;
public float projectileDamage = 10;
public ParticleSystem bullet;
public void OnParticleCollision(GameObject other)
{
if (other.gameObject.GetComponent<PlayerHealth>())
{
targetPlayerHealth = other.gameObject.GetComponent<PlayerHealth>();
Debug.Log("Collision Detected" + other.name);
DamageTarget();
}
}
public void DamageTarget()
{
targetPlayerHealth.TakeDamage(projectileDamage);
}
}
well now its because the users local collider is never triggering for their health since its off
that makes sense
So on the 2nd players POV
The collision code is being ran and its working.
1st player POV will never know they got shot
and since the players health isnt synced, they have no way to know they've been hit
idk lowkey. Legos would know better
isnt there a chance that they make take damage twice or multiple times if multiple players see it happen and its synced?
thasts true yeah
ideally, i think for the best experience the shooter should be reporting the damage
agreed
this should make the latency less, annoying?
I guess when the collision is called, you can check the local player and if it is the local player, modify their health
will that still cause the self damage?
so collision calls would only register for the guy who fired the gun
No since the local players collider is always off
hmm alright
so they have no chance of shooting themselves
so where do you think i should place the local check?
But IDK. this is getting into networking game design I am not familiar with just yet
networking is a nightmare-
I agree but I think my brain is just bad at understanding it. I am still a noob.
you could technically call a network event when the collision happens. It would be ran on all players computers to decrement the shot players health
Or well, when the damage is called. Not on collision
honestly same man my networking understanding is sooo bad rn
i have so many wrong things memorized
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class Projectile : UdonSharpBehaviour
{
private PlayerHealth targetPlayerHealth;
public float projectileDamage = 10;
public ParticleSystem bullet;
public void OnParticleCollision(GameObject other)
{
if (other.gameObject.GetComponent<PlayerHealth>())
{
targetPlayerHealth = other.gameObject.GetComponent<PlayerHealth>();
Debug.Log("Collision Detected" + other.name);
DamageTarget();
}
}
public void DamageTarget()
{
targetPlayerHealth.TakeDamage(projectileDamage);
}
}
in the Damage Target method?
Actuyally ugh. Maybe we can sync the health instead
the healthbar?
Yeah
It has like a lava thing
I'd reckon they may just sync the variable. You could see how it interprets damage
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
public class DamageOnTouch : UdonSharpBehaviour
{
// The amount of damage to apply per second
[SerializeField]
private float _damagePerSecond = 10f;
// Reference to the HealthBar component
private HealthBar _healthBar;
public void Start()
{
// Get the HealthBar component from the local player
_healthBar = PersistenceUtilities.GetPlayerObjectComponent<HealthBar>(Networking.LocalPlayer);
}
public override void OnPlayerTriggerStay(VRCPlayerApi player)
{
// If the player is local, apply damage to the health bar
if (player.isLocal)
{
_healthBar.TakeDamage(_damagePerSecond * Time.deltaTime);
}
}
}
using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using VRC.SDKBase;
public class HealthBar : UdonSharpBehaviour
{
// The health of the player
[UdonSynced]
private float health = 100f;
// The maximum health of the player
[SerializeField]
private float _maxHealth = 100f;
// The offset of the healthbar above the player's head
[SerializeField]
private Vector3 _offsetAboaveHead = new Vector3(0, 0.5f, 0);
// The healthbar slider for visual representation
[SerializeField]
private Slider _healthBarSlider;
// The owner of the healthbar
private VRCPlayerApi _owner;
public void Start()
{
_owner = Networking.GetOwner(gameObject);
_healthBarSlider.minValue = 0;
_healthBarSlider.maxValue = 1;
UpdateHealth();
}
yeah see, the health is synced
public void Update()
{
if (!Utilities.IsValid(_owner))
{
return;
}
if(!Utilities.IsValid(Networking.LocalPlayer))
{
return;
}
// Get the head tracking data of the owner and the local player
VRCPlayerApi.TrackingData headReference = _owner.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
VRCPlayerApi.TrackingData localHeadReference = Networking.LocalPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
// Place the healthbar above the head of the owner
transform.position = headReference.position + _offsetAboaveHead;
transform.rotation = Quaternion.LookRotation(_owner.isLocal ? Vector3.down :localHeadReference.position - headReference.position, _owner.isLocal ? localHeadReference.rotation * Vector3.forward : Vector3.up);
}
public override void OnDeserialization()
{
UpdateHealth();
}
private void UpdateHealth()
{
// Update the healthbar slider
_healthBarSlider.value = health / _maxHealth;
}
public void TakeDamage(float damage)
{
// Apply damage to the health
health -= damage;
// Make sure the health is within the bounds of 0 and the max health
health = Mathf.Clamp(health, 0, _maxHealth);
// If the health is 0 or below, die
if(health <= 0)
{
Die();
return;
}
// Request serialization to update the health on all clients
RequestSerialization();
UpdateHealth();
}
// Respawn the player and reset the health
private void Die()
{
_owner.Respawn();
health = _maxHealth;
RequestSerialization();
UpdateHealth();
}
}````
big script lol
That may honestly be the thing we need to mess with
and see, after everytime the user takes damage, we request serialization which basically syncs your synced crap for everyone else
[UdonSynced] on health
Request serialization in your playerhealths takedamage AFTER you've modified health
yeah
lets see what happens now
I'm tryna catch up
all good
so if you disable the local player's collider, they won't be able to respond to themselves taking damage
and if they are the owner of this collider, then another player hitting it won't be able to RequestSerialization
ooh true
ah.. yeah i see it not working xd
just it being disabled for shooter again
hard part is there's a lotta ways to do it... these colliders are PlayerObjects right?
yes
alotta ways to tie a knot
so taking ownership of them wont work right?
that's right
since its a player object, it cant be owned by someone else
so instead of the player who hit them attempting to network it, they need to tell the owner to make changes when they register a hit
yeah.. but how xd
you can SendCustomNetworkEvent to just the owner, and have them make the health changes and serialize it
with networking you always gotta visualize the relationship between local & remote players, and owner vs. non-owner
so, when I, the local player, detect that I hit another player's collider > SendCustomNetworkEvent to the owner and tell them to take damage
that's the part im still trying to learn
it clicks eventually
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class Projectile : UdonSharpBehaviour
{
private PlayerHealth targetPlayerHealth;
public float projectileDamage = 10;
public ParticleSystem bullet;
public void OnParticleCollision(GameObject other)
{
if (other.gameObject.GetComponent<PlayerHealth>())
{
targetPlayerHealth = other.gameObject.GetComponent<PlayerHealth>();
Debug.Log("Collision Detected" + other.name);
DamageTarget();
}
}
public void DamageTarget()
{
targetPlayerHealth.TakeDamage(projectileDamage);
}
}
so if im understanding correctly, i should call the damage target method through a network event targetted towards the owner?
it's good that you already have TakeDamage as its own function, so it should be easy to adapt
yep all you should have to change is the DamageTarget call to SendCustomNetworkEvent([the big long thing that targets Owner], nameof(DamageTarget));
lets try it
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using VRC.Udon.Common.Interfaces;
public class Projectile : UdonSharpBehaviour
{
private PlayerHealth targetPlayerHealth;
public float projectileDamage = 10;
public ParticleSystem bullet;
public void OnParticleCollision(GameObject other)
{
if (other.gameObject.GetComponent<PlayerHealth>())
{
targetPlayerHealth = other.gameObject.GetComponent<PlayerHealth>();
Debug.Log("Collision Detected" + other.name);
SendCustomNetworkEvent(NetworkEventTarget.Owner, "DamageTarget");
}
}
public void DamageTarget()
{
targetPlayerHealth.TakeDamage(projectileDamage);
}
}
The biggest problem with using a particle system for favor the shooter will be that everybody sees the particle hit the player, and you don't want everybody sending a message to tell the person to take damage. If you do that, they would take damage multiplied by the number of users in the instance.
It's important to make sure you filter it out so that only the person who shot the particle tells the receiver to take damage, and nobody else. If this is a particle created by the world, then you can modify it so that only the owner of it has SendCollisionMessages enabled.
If this is a particle created by the avatar, you can't manipulate it or identify who it came from, and that's the core problem which prevents you from doing favor the shooter with avatar particles
yeah, then it's easy
how would you filter it?
oh yeah forgot to consider for everyone seeing the collision
he's cooking :3
*she
You can do that either by enabling sendcollisionmessages only on the owner's gun, or by reading the message and checking who the gun belongs to
hmm which would be more logical? there will be multiple guns and multiple players firing at different times
whichever is easier for you, both are valid. Though in either case, it would be important to check that the source particle is valid or else you'll get avatar particles which don't have this same filtering
yeah, i got no idea how to do that 😅 but im open to learn if you're willing
it would be something like
OnParticleCollision(GameObject other)
if (!utilities.isvalid(other)) return;
if (!networking.isowner(other)) return;```
the gameobject other doesn't inherently have an owner because it probably doesn't have a networked behavior, but if you ask for the owner of an object like that it will traverse up the hierarchy until it finds a parent that does have an owner. So as long as the particle system is the child of something that you own, it will work
okay, so like this?
public void OnParticleCollision(GameObject other)
{
if (!Utilities.IsValid(other)) return;
if (!Networking.IsOwner(other)) return;
if (other.gameObject.GetComponent<PlayerHealth>())
{
targetPlayerHealth = other.gameObject.GetComponent<PlayerHealth>();
Debug.Log("Collision Detected" + other.name);
SendCustomNetworkEvent(NetworkEventTarget.Owner, "DamageTarget");
}
}```
the particle system is a child of the gun
oh hang on, is this script on the collider of the person being hit, or is it on the particle system of the gun?
uhh here lemme send each thing and explain them
When a particle collision happens, both sides get OnParticleCollision. So if this script is on the particle system, then "GameObject other" represents the collider being hit. In that case, we want to check the owner of the particle system not the collider that was hit
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using VRC.Udon.Common.Interfaces;
public class Gun : UdonSharpBehaviour
{
public ParticleSystem bullet;
public override void OnPickupUseDown()
{
SendCustomNetworkEvent(NetworkEventTarget.All, "Fire");
}
public void Fire()
{
bullet.Play();
}
}
^^ This script fires the particle system
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using VRC.Udon.Common.Interfaces;
public class Projectile : UdonSharpBehaviour
{
private PlayerHealth targetPlayerHealth;
public float projectileDamage = 10;
public ParticleSystem bullet;
public void OnParticleCollision(GameObject other)
{
if (!Utilities.IsValid(other)) return;
if (!Networking.IsOwner(other)) return;
if (other.gameObject.GetComponent<PlayerHealth>())
{
targetPlayerHealth = other.gameObject.GetComponent<PlayerHealth>();
Debug.Log("Collision Detected" + other.name);
SendCustomNetworkEvent(NetworkEventTarget.Owner, "DamageTarget");
}
}
public void DamageTarget()
{
targetPlayerHealth.TakeDamage(projectileDamage);
}
}
This one is attached to the actual particle being fired
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class PlayerHealth : UdonSharpBehaviour
{
[UdonSynced] public float playerHealth = 100f;
public float respawnHealth = 100f;
public void TakeDamage(float damage)
{
playerHealth = playerHealth - damage;
if (playerHealth <= 0)
{
gameObject.SetActive(false);
}
}
}
This is attached to the hitbox
if (!Networking.IsOwner(other)) return; this needs to be a reference back to the gun
ohh
could just have like public GameObject gun and then if (!Networking.IsOwner(gun)) return;
oki lemme do it rq
oh we got an issue
oh
wait
im not supposed to reference the Gun script
supposed to reference the Gun Game Object
my bad
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using VRC.Udon.Common.Interfaces;
public class Projectile : UdonSharpBehaviour
{
private PlayerHealth targetPlayerHealth;
public float projectileDamage = 10;
public ParticleSystem bullet;
public GameObject gunRef;
public void OnParticleCollision(GameObject other)
{
if (!Utilities.IsValid(other)) return;
if (!Networking.IsOwner(gunRef)) return;
if (other.gameObject.GetComponent<PlayerHealth>())
{
targetPlayerHealth = other.gameObject.GetComponent<PlayerHealth>();
Debug.Log("Collision Detected" + other.name);
SendCustomNetworkEvent(NetworkEventTarget.Owner, "DamageTarget");
}
}
public void DamageTarget()
{
targetPlayerHealth.TakeDamage(projectileDamage);
}
}
how does that look?
alright ill give it a shot
I'd replace if (other.gameObject.GetComponent<PlayerHealth>()) { targetPlayerHealth = other.gameObject.GetComponent<PlayerHealth>();
with
targetPlayerHealth = other.gameObject.GetComponent<PlayerHealth>();
if (Utilities.IsValid(targetPlayerHealth))
{```
just for code cleanliness though
dang, no luck. nothing happened, it collided but no damage applied
oh, actually I see a problem. You're calling the network event on the particle system, which means you lose out on the reference. The targetPlayerHealth isn't synced so when they receive the network event they won't know which playerhealth was supposed to be hit.
You can fix that by moving the DamageTarget function to playerhealth script, and calling the network event on that directly
okay, uhh lemme try rq
The tricky part is that without network events with parameters, this won't be able to communicate the amount of damage of the particle system
But worry about that later, start with getting something to work, even if it's always 1 damage
oh okay
add this to PlayerHealth
{
targetPlayerHealth.TakeDamage(1);
}```
Then inside of the particle collision, you'd do `targetPlayerHealth.SendCustomNetworkEvent(NetworkEventTarget.Owner, "DamageTarget");`
okay, lemme try im just piecing it together in my head
how does this look?
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using VRC.Udon.Common.Interfaces;
public class Projectile : UdonSharpBehaviour
{
private PlayerHealth targetPlayerHealth;
public float projectileDamage = 10;
public ParticleSystem bullet;
public GameObject gunRef;
public void OnParticleCollision(GameObject other)
{
if (!Utilities.IsValid(other)) return;
if (!Networking.IsOwner(gunRef)) return;
targetPlayerHealth = other.gameObject.GetComponent<PlayerHealth>();
if (Utilities.IsValid(targetPlayerHealth))
{
Debug.Log("Collision Detected" + other.name);
targetPlayerHealth.SendCustomNetworkEvent(NetworkEventTarget.Owner, "DamageTarget");
}
}
}
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class PlayerHealth : UdonSharpBehaviour
{
[UdonSynced] public float playerHealth = 100f;
public float respawnHealth = 100f;
public void TakeDamage(float damage)
{
playerHealth = playerHealth - damage;
if (playerHealth <= 0)
{
gameObject.SetActive(false);
}
}
public void DamageTarget()
{
TakeDamage(1);
}
}
just tested it like this but no damage applied
Is PlayerHealth on a PlayerObject?
Do you get the Collision Detected log?
the follow player script doesn't have the gameobject defined, could that break something with that script?
it gets defined at start
also
ah
i found issue
i think
i didnt assign gun ref
okay now ill check for collision
yes there is a collision log
but player health doesnt go down sadly
testing in client sim rn
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using VRC.Udon.Common.Interfaces;
public class Projectile : UdonSharpBehaviour
{
private PlayerHealth targetPlayerHealth;
public float projectileDamage = 10;
public ParticleSystem bullet;
public GameObject gunRef;
public void OnParticleCollision(GameObject other)
{
if (!Utilities.IsValid(other)) return;
if (!Networking.IsOwner(gunRef)) return;
targetPlayerHealth = other.gameObject.GetComponent<PlayerHealth>();
if (Utilities.IsValid(targetPlayerHealth))
{
Debug.Log("Collision Detected" + other.name);
targetPlayerHealth.SendCustomNetworkEvent(NetworkEventTarget.Owner, "DamageTarget");
}
}
}
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class PlayerHealth : UdonSharpBehaviour
{
[UdonSynced] public float playerHealth = 100f;
public float respawnHealth = 100f;
public void TakeDamage(float damage)
{
playerHealth = playerHealth - damage;
if (playerHealth <= 0)
{
gameObject.SetActive(false);
}
}
public void DamageTarget()
{
TakeDamage(1);
}
}
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class FollowPlayer : UdonSharpBehaviour
{
VRCPlayerApi player;
public GameObject hitbox;
private Collider targetCollider;
void Start()
{
hitbox = gameObject;
targetCollider = gameObject.GetComponent<Collider>();
if (player.isLocal)
{
targetCollider.enabled = false;
}
}
public void OnEnable()
{
player = Networking.GetOwner(gameObject);
Debug.Log("Owner = " + player.displayName);
}
private void LateUpdate()
{
TrackPlayer();
}
public void TrackPlayer()
{
VRCPlayerApi.TrackingData headData = player.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
hitbox.transform.position = headData.position;
hitbox.transform.rotation = headData.rotation;
}
}
these are all the current versions of the scripts
ah
i found my issue
Sync method was set to none on player health
wait...oh man
it seems like its.. delayed?
the syncing of playerhealth is definitely not set up right but you shouldn't need that to test basic functionality
delay of what?
i was watching the health go down in the inspector and it only updated in the inspector when i put my mouse over it
ah... shouldnt have tested in client sim
because in vrc build and test it doesnt appear to be taking damage
oh yeah, build and test remote clients aren't real, so networking isn't going to work between them
oh... boy..
but.. oddly enough the "networking" worked in client sim but not build and test
that's because there is only one owner of the objects in clientsim
ah
okay, so the current issue appears to be no damage is being applied
the scripts i sent earlier are still accurate to their current versions
did you set the sync to Manual or Continuous?
Cont
TakeDamage doesn't RequestSerialization, and you don't have any field change callbacks setup
and you don't have the health printing anywhere no?
Correct
Yes, I dont have any logs
do you want players to see each other's health? Or do you want people to only see their own health?
Then there is no need to [udonsynced] the playerhealth or requestserialization or anything
but yeah, get some logging in there, do a respawn on kill, increase the damage, whatever you need to make it more visible when taking damage just to rule out the possibility that you are taking damage but just can't see it
yea do some loggin' like just Debug.Log the health after the damage is taken
after its taken, when you are about to call the event etc.
just so its easier to trace crap
half my code ends up being Debug.Logs sometimes
yeah onky their own
yea then it doesn't need UdonSynced
ill change it to respawn and add logging
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class PlayerHealth : UdonSharpBehaviour
{
[UdonSynced] public float playerHealth = 10f;
public float respawnHealth = 100f;
public void TakeDamage(float damage)
{
playerHealth = playerHealth - damage;
Debug.Log("Current Health = " + playerHealth);
if (playerHealth <= 0)
{
Networking.LocalPlayer.Respawn();
}
}
public void DamageTarget()
{
TakeDamage(1);
}
}
hows that
change TakeDamage(1); to TakeDamage(10); so that it's a one shot kill
and remove udonsynced on the playerhealth
right
i forgot how to view logs in vrc
rshift, tilde, 3
also, it worked it seems
woo
yesss progresssssssssssss
now that we're good on that part, we need to figure out how to do custom dmg
So, for that you could either build a whole complicated manual event buffer on the particle system script, or you can just have network events with DamageTarget1, DamageTarget2, DamageTarget3, DamageTarget4 etc
{
TakeDamage(1);
}
public void DamageTarget2()
{
TakeDamage(2);
}
public void DamageTarget3()
{
TakeDamage(3);
}
public void DamageTarget4()
{
TakeDamage(4);
}```
Like DamageTarget2 could deal more damage and you could have your sniper rifles use that one
Or a diff gun
wouldnt that get messy if you have like 20 guns?
eh it's only 20 lines
true
and are they all going to do different damage?
Uh yeah but you should honestly just have a few types of gun damage and just have diff skins and shooting sounds
Like I think Ostinyos TTT world has like 3 different skins for each gun type.
yeah i think i should just have a select few weapons
Don't have the damage function be tied to the gun type, just do this:
targetPlayerHealth.SendCustomNetworkEvent(NetworkEventTarget.Owner, "DamageTarget" + projectileDamage);
then you can have the same gun script and just change projectileDamage
though projectileDamage should be an int, not a float
yeah thats what i would love ^^
it works p well
Gigabrain
ill try to write that rq
it's dumb but it works
if spacing the brackets like that feels like too much space, you can put the function on one line each like this
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
public class PlayerHealth : UdonSharpBehaviour
{
public int playerHealth = 10;
public int respawnHealth = 100;
public void TakeDamage(int damage)
{
playerHealth = playerHealth - damage;
Debug.Log("Current Health = " + playerHealth);
if (playerHealth <= 0)
{
Networking.LocalPlayer.Respawn();
}
}
public void DamageTarget(int damage)
{
TakeDamage(damage);
}
}
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using VRC.Udon.Common.Interfaces;
public class Projectile : UdonSharpBehaviour
{
private PlayerHealth targetPlayerHealth;
public int projectileDamage = 10;
public ParticleSystem bullet;
public GameObject gunRef;
public void OnParticleCollision(GameObject other)
{
if (!Utilities.IsValid(other)) return;
if (!Networking.IsOwner(gunRef)) return;
targetPlayerHealth = other.gameObject.GetComponent<PlayerHealth>();
if (Utilities.IsValid(targetPlayerHealth))
{
Debug.Log("Collision Detected" + other.name);
targetPlayerHealth.SendCustomNetworkEvent(NetworkEventTarget.Owner, "DamageTarget" + projectileDamage);
}
}
}
like this?
that plus the functions Phased posted, where DamageTarget is
what you're doing is essentially making a new function for every possible argument to the original function
and all these new functions do, is call the original with the argument
almost. You need to make DamageTarget -> DamageTarget1 and hardcode the damage value ur gunna deal instead of passing a variable
and then dupe that function while changing the number and the damage dealt
I thought it was like this?
yeah that part is correc
but you also need this in order for that to work
can you tell what your SendCustomNetworkEvent is calling now?
Where will those be coded into? The player health?
in the current state? without thefunction?
it's gonna smush "DamageTarget" and "10"(projectileDamage), creating "DamageTarget10", and then will call the function DamageTarget10 instead of DamageTarget
oh i see
so for every "projectileDamage" value that you expect, you need a DamageTarget1, DamageTarget2, etc. function, or nothing will happen because the function doesn't exist
where the num is projectileDamage
then as you can see all the function does is call TakeDamage with the value you want
okay so im trying to wrap my head around it
you're essentially faking doing a function call with parameters, since you can't normally do that with events
so how do i change this part to make the number do the projectile damage
or am i misunderstanding 😅
that's what "DamageTarget" + projectileDamage is for
the number after "DamageTarget", THAT'S the "input" so to speak
okay so, delete damage target and leave the rest like this?
yez
you could if you wanted
no
No
o
that's what + projectileDamge does
OH
it will add the number to it
You are concatinating a string
aw..
and just calling that string. its like a psuedo prop or passing a variable
No, you can't
so maybe i should change the name from projectiledamage to damagetype maybe?
since you're basically selecting a type
You could have classes of damage types
Yeah so your projectiles could be typed OR you can just give the projectiles a damage
So say instead of DT1 (DamageTarget), we could have a DT10 which deals 10.
It really just depends on how you wanna do it at this point
¯_(ツ)_/¯
the world is ur oyster
yeah you're in "do whatever you want that works" territory now
im cuious of the second part of this
if idid DT10
Yeah that works man
how would the second one play into this?
it works but then that means when you put in projectileDamage 4 on the gun, it actually does 50 which is just mildly confusing
they are the same thing just diff smell
yeah thats what i was thinking
whats the other weay of approaching this?
Yeah so you can either interpret DT1 as deal type one damage
OR deal one damage
I mean it's down to what you want, what will "projectileDamage" actually represent
I'd personally do DamageTarget50 = TakeDamage(50) so that when you put in projectileDamage 50, it still does 50. And like, you don't need to make DamageTarget43 if you're not gonna have a gun that does specifically 43 damage
the raw number of damage taken, or the "type" of damage?
i would still need to hardcode it regardless right>?
if you ever make a gun that does 75 damage, then you can just make sure you have a DamageTarget75 function
I think raw damage is probably easier and we just end up abstracting it further
With the other method
And it'll be easier to read
Like say you had 20 types, now your like " damn, what was type 17 again?'
alternatively, you can run a little script that autogenerates 1 through 100 so you never have to think about this again
yeah I'd go with that
guaranteed this is just gonna confuse you in the future
mm? what would that look like?
just spit out a string to the console that loops 100 times and puts that in the text
go to here https://www.programiz.com/csharp-programming/online-compiler/
and then run this:
// Write, Edit and Run your C# code using C# Online Compiler
using System;
public class HelloWorld
{
public static void Main(string[] args)
{
for (int i = 1; i <= 100; i++){
Console.WriteLine(" public void DamageTarget" + i + "() { TakeDamage(" + i + ");");
}
Console.WriteLine ("Try programiz.pro");
}
}```
This is just a random website where you can run C# code. You can prototype a quick little script to output what you need, and then take that output and plug it into your script
with this method, you could theoretically generate a damage (within a small range) when u fire a projectile
forgot the closing bracket!
// Online C# Editor for free
// Write, Edit and Run your C# code using C# Online Compiler
using System;
public class HelloWorld
{
public static void Main(string[] args)
{
for (int i = 1; i <= 100; i++){
Console.WriteLine(" public void DamageTarget" + i + "() { TakeDamage(" + i + "); }");
}
}
}```
trying to understand sorry xd
^just run that code on the webbed site, copy the output and slap it into your script
oh so it makes it 100 times
yea
I swear the more I work on networking the less stuff works...
maybe you need to rework the logic @finite sequoia
yes that's what I was trying to do for the last 7 hours... 🤣
I'm remaking from scratch at this point to un-confuse myself
only one hiccup away from finishing...
Everything seems to work on the generic doors.
One thing is weird but only on the "whitelisted user" door
- when entering a world ID, portal don't update for the owner
- when entering a world ID, portal do update for everyone else normally
- when owner rejoins the world, portal updates for the owner
(even tho it works fine on the generic version)
is the owner also applying the change to themselves?
remember they don't get OnDeserialization
yes I'm following the tutorial you sent this morning
that structure
should I use "SendCustomNetworkedEvent" targeted to "Owner" instead?
yeah that's why I'm confused since the generic version do work
and the OnPlayerRestored do work so it's set way before I input a new link manually
it shouldn't fail this late
what's happening before this Send group
you're presumably setting ownership after writing to the synced variable
which I don't think will work, you need to be owner first
uh, it seems weird to me as well but it was the order given in the video you sent
set ownership right before you write to the ID_current variable, instead of after
granted I didn't watch the videos
this part of the lesson
Still behaves the same (for the non generic one it was more of a failsafe, since the username whitelist is supposed to set owner already on start)
Do you think it's because of the second request when I save the persisted data?
would it work to just put the request as a third branch of the block so it's called once?
I guess this order would make more sense since the request is after "save" but before "confirm"
(dunno if I even need the block anymore here)
looks alright to me
acutally yeah, looks like it makes more sense this way
wait, no...the whitelist version is always "save" and "network" it doesn't even use these variables anymore
Yeah no I don't see why this don't work...
wait, hold on...what did I just do? Why does it work now?
I didn't change anything in the menu itself...
Maybe it was something inside the whitelister crashing the menu all along... 🐸
I want to make a pickup wish is local for everyone, but a hologram copy appears for remote
This is possible???
Player Objects are the best way to do that. Everybody is assigned their own and you can just apply different settings depending on if they're remote or not, so that remote ones are technically the same thing but with a different material and pickup disabled
you would do that by adding a single pickup to your world, then the VRCPlayerObject script, and then an additional udon script on that object to manage the functionality of the pickup with whatever scripting you want to write.
By putting PlayerObject on it, this one object in editor will be duplicated once per player in client. And the script you put on it will be able to figure out the context of where it's running by comparing the owner of itself to the localplayer. If the localplayer is not the owner, then that means it's a remote playerobject
Interesting interesting, muchos gracias
One more thing, with this method, can I have a variable that's a different value for every copy, like an index or reference to the remote player who owns it?
there is nothing that inherently links together different player objects except for the fact that they are all instantiated from the same template. Once they are spun up and running, they are completely independent and so you can set variables however you like
Something tells me I'm gonna have to redo the whole thing if I ever want my taxis to be sync... 🤣
VRCObjectSync?
that made things worse somehow, things didn't float as much when it was only on the parent
To be fair it wouldn't be nearly as bad if it was a propper model and not some cubes thrown around the main object
I wonder if it's a case where parent constraints would make more sense than trying to sync all the parts..
even tho no parts should be "moving" relative to each others, it's only the parent who drive the whole thing
idk what you doing but why not send network event to tell it where to go, then it will go locally for everyone
That's what it was supposed to do yeah, start the machine-state then it just goes.
But it's an old thing from before I learned about the propper order to send the events yesterday. Hence the "I will need to redo everything" the pic being super obvious was a joke...Because of course I need to if it's this bad xD
oh wait, I tried just for the heck of it but it looks like parent contraints do work pretty good compared to the "sync all parts" attempt
It just lag behind because don't explode
Meh, it's already more acceptable. good enough xD
I can do VRC.Udon.Common.Interfaces.NetworkEventTarget.All and .Owner but how can I send an event to everyone EXCEPT the owner?
send it to All, but then do an ownership check, and skip executing the rest if it's the Owner
Basic question about syncing, if i want to display something correctly, can I not just get GetServerTimeInSeconds every frame?
display correctly what, animator state? anyway, if were talking optimised and proper way, the amount of update* events and code in them should be minimal, you should always think of ways of NOT doing something every frame first.
say a moving cube, that needs to be the same position for everyone
also for late joiner
isn't that what ObjectSync is for?
what if i dont want any networking, can i not GetServerTimeInSeconds and use it to calculate the position?
and then what like, mod it by the animation time?
say rotate a clock hand to a correct spot
oh like not using an animation
yeah, you can just translate the servertime into the output display of the clock, essentially?
yeah
so wether or not you're a late joiner, or been there, you'll be doing the same math
That's quite the roundabout way to do it but I guess if a cycle rounds up exactly to be loopable by minutes it would work
the server time will be the same for anyone anyway, if I'm thinking it's the right thing
an actual animation sync would be neat to make though. it's on my to-do list
@fallow mountain theres already network-less prefab for looped animators https://github.com/Pokeyi/VRC-Animation-Sync otherwise master stores start time, everyone syncs it on join once (or on demand) and jump to % of animation. done. no update event.
If I request ownership just before serialization, does it create more lag than say if I requested ownership a few seconds ago?
changing ownership shouldn't affect performance, you just don't want to request too quickly or the networking will fight itself
Interestingly enough I currently have behavior that is instant when you claim ownership while running it, but if you do that again and are already owner it takes 2-5 seconds to serialize before remote players catch it
So I guess for some reason taking ownership forces serialization to happen sooner than if you were already the owner
That is interesting, did you check whether local is owner before requesting ownership?
^ I always do that
Also idk if it matters. I'm not a networking expert but I believe if you are ever running serialization on a script, you should have manual sync mode? (Berate me if I'm wrong)
Yes that is my understanding
I didn’t think this was necessary since it just ignores the trysetowner internally when they match?
Just wondering if the delay has anything to do with it
Ohh 🤔 I never imagined setting owner when you already are could trigger a delay but I’ll try that and see if it’s any different!
I recall phasedragon mentioning that "it doesn't cause any unnecessary traffic" to call setowner when you're already owner. #udon-networking message
Alright, so Im having issues with Networking>Getplayerobjects, In editor if I search for what player object 1 is it will be A, but if I publish and go in game and search for player object 1, it will be B, this is seriously messy and is breaking everything, whats going on?
The order is never the same
what defines it?
Idk, I grab playerobjects like this but IK there is a better way
the order is not guaranteed, so you'll have to account for it
Is there a way I can define an object as Object 1,2,3,etc?
idk, I just do it like the screenshot brutha. Never need to care about order
If only I could read/write code dead
oh shit ur graph gang
yeah my homies don't fw that on hood
You would basically
GetPlayerObjects
Loop through them (for each)
Check the name and compare
BUT IK there is a better more clean way
honestly name is probably the easiest
if ClientSim operates the same as PlayerObjects do in-game, they basically keep the same name when it duplicates the object, and just adds a number to it (the one in brackets)
so if you have a playerobject named "Cube", and another "Sphere", the function GameObject.name.Contains will be able to differentiate between each (since every player will only have one)
as long as the PlayerObjects are not named the same, this method works
don't need the brackets
the original in my example is "Cube (1)"
kinda cut it off a little, it's auto-hidden
which ones are the PlayerObjects
Persistence object is what the guns/customisation data etc are inside, the hitbox is... well the hotbox as well as some other stuff that follows their player around
so
Those 2 objects have persistence enabled
you don't necessarily need to change the names
...
unless you just want to look for "1" and "2"
Its an issue because in editor I have to use this device to force the PLAYER PERSISTENCE OBJECT as the target for teleporting the players gun to the player instead of the hitbox
In game it works fine but in editor I have to set its value differently for testing
right I get that
So can I have a script run oon start that marks each object as 1,2,3 etc?
no just check the name of the object when you're accessing them via GetPlayerObjects
Not really sure what you mean, theres no node for getting an objects name
Right... and how do I use that as part of networking>getplayer objects?
you need to loop the GameObject[] array that you get out of GetPlayerObjects, and only continue if the name matches
Ah... thats new to me
loops are a crucial thing to know about when you're working with arrays; you can create a for loop by holding SHIFT + F, then clicking on the GetPlayerObjects node
Confusing :L Ok so I got that, not sure how to put it all together
ya like videos by chance?
https://www.youtube.com/watch?v=wxds6MAtUQ0
Loops are a fundamental concept in computer science. Here's an explainer on how they work, with the help of our favorite dessert.
-
-
-
- -
Join the millions learning to code with Codecademy.
Learn to code: https://j.mp/334JBBi
Check out our full course catalog: https://j.mp/3aTMMQz
- -
-
-
Hmmm
I always found programming tutorials are taught by people who arnt really good at speaking at the learners level
cupcakes? whah???
this was like the shortest example that was also not specific to a language
Body is the part that will be repeated, Exit is the code that will run after the loop is finished
Makes sense
so body is essentially plugged into "For"'s own input invisibly
where can I find "For" without having to remember Shift+D
Wait was it even Shift D?
I forgot already lol
Shift F but yeah
just search "For" and you'll get the For node by itself
this quick method of adding it does kinda add a few nodes you don't really need
Nice
but it automatically defines the loop to loop for as long as the array is (that's what Get Length is for)
From that, Im guessing this would be correct?
that looks alright
Yey now I can fix the rest of the issues lol
If something dont work, hit it harder, if it still dont work... Hit it even harder!, thats my philosophy 😛
hey, as long as it works
but sometimes you're also banging your head through the wall, when there was a door there the whole time
Yep
Now the bruised healing can begin
I cant belive VRChat devs didnt just make a integar option in the VRC player object component :L hmmm
Maybe down the line
IDK why it isn't in the same order each time but what happens if you remove one or add a new one? Your scripts will break
this is way safer
Safer... at the cost of having to cycle the code for as long as the list is, I guess its fine a small amount of objects with little need to search, but what if its a massive array and you need to search often...
There is a way to just grab it
I just don't use it. As I said, my method is NOT the best
and often you shouldn't loop through them each time
I'm assuming GetPlayerObjects works similar to some other Unity functions where you get an array of things, that often the order isn't guaranteed by Unity
the PlayerObjects aren't going to change, so you can just cache them all into variables during OnPlayerRestored
Yeah, was just a thought
Im already using a bunch of arrays, each 100 long, not had to use FOR til this point
what. what were you doing to go through them
Theyre just lists, so I have 60 planned guns in my game and 40 gadget items, each gun has a value for damage, weight, range etc
So i just been using []get for refering to a stat in a database
well, if you can work an array without looping it, it's technically more efficient
I was worried you were doing array[0] = whatever, array[1] = whatever.... array[100], and just never thinking "surely there is an easier way to do this"
There is actually a case where I had to make a debug cheat to Unresearch all the guns that I had previously researched just unticking all 100 bools to false using a manual system i made lol
I probably invented the FOR before even knowing it even existed :p
Wait no thats not the one
thats just the debug
Where was it...
There :p
If you think that's slow you should see mines, I remember one that wait 30 seconds before checking again... 🤣
is it possible to detect weather a player is on mobile or pc?
I think something like OnInputMethodChanged you can check
https://creators.vrchat.com/worlds/udon/input-events/#oninputmethodchanged
You can read the input of a player's controller in a unified way across all platforms by using Udon Input Events. These events will work correctly even when the player has remapped their controls.
and you don't have to wait for it to actually change, it runs once at the start
hmm do you think it would be the getjoysticknames one? Ik i can use the anykey for desktop users.
or does inputmethod.quest or something like that exist
it doesn't help that the docs don't have the latest entries of the enum
there isn't one quest specific, maybe it's VRCInputMethod.Oculus?
there's also QuestHands which I assume is hand tracking
wouldn't that include rift users though?
would assume so
😭 fml
you can check by doing something like this
then run on Quest, and see what it spits out in the logs
or just display this in the world somewhere
ill have to give it a try sometime thank you
here's all the possible options
ooohhhh baller thank you
if by mobile u mean android
you can use "Easy Quest Switch" and turn a gameobject on for quest builds. This will signify you are on android vs PC and you can code accordingly off of it
Ok, this is my set up.
I have stick. If I grab that stick and touch object2(bowl), it would make a sound and make other 55 objects (stations) to appear.
Then, I have second set of sticks. If I touch them together, they would play a sound and then make those 55 objects move in rectangular path one by one (each of 55 objects has script attached to it).
If I touch second set of sticks with each other it would make those 55 objects to move 2x faster and stop at their original position. If I touch these sticks again, all 55 objects will disappear.
Now, if I want to repeat this cycle, I would get the stick from beginning and touch object2(bowl) again it would make those 55 objects appear again which will allow me to repeat this cycle.
I made it work perfectly fine for me.
But whenever I tried to test with second player, he would not see a consequence of me touching those sticks -> he doesn’t see those objects staying/moving or he sees it with glitches.
I added obj sync to each and tried to add some networking addition to code (like stabilization) but it didn’t help.
Does anyone have a suggestion how to make it work? I actually need it to work with up to 55 people in the game.
I know it might be some read, but any help or suggestion would be appreciated!
have a stick*
outside of having two different versions ie one per platform, you can use conditional compilation just fine with u#
https://docs.unity3d.com/2019.4/Documentation/Manual/PlatformDependentCompilation.html
no need to add object sync, just setowner (of the gameobject with your script storing the touch count int)->request serialization........and ondeserialization do stuff based on the touch count
You just need to sync the least amount of information, in your situation, it's literally just one integer (touch count) to tell the others (including late joiners) what state the chairs are in
I've been making a wave based zombie survival game world,
i'm a game developer / modder, and used my pre-existing c# knowledge and networking knowledge from various mediums like gmod and unity mirror networking,
but i'm having really inconsistent desync issues with my udonsharp networking code.
is there a way to pass in arguments into a method running in SendCustomNetworkEvent? i am having Udonsynced desync issues,
and is there any weird gimmicks to how udonsharp networks stuff?
this whole owner / world master / client setup, is a nightmare for me, and randomly stuff won't set, even when its running on the owner?
I feel like there is lacking documentation for the quirks of udonsharp's networking, and its making my knowledge hard to apply / adapt
To be honest, using this structure instead of "objectsync" and "sendsetworkedcustomevent" is quite the big step forward to take, having to de-learn what looked like the method VRc wanted us to use for so long...
Udon is sadly pretty poorly made when it comes to documentation. and it lacks aloot of what to do and what not to do etc. you have two ways to Sync things and that is using Continous Sync and Manual. Manual requires the owner of the object to request Serialization and all other people then receive that data that is set on that object. and we have no ways to pass Arguments. and also using SendCustomNetworkEvent is not realiable and is not used to sync things.
setting ownership also has a pretty big delay and can cause major issues if to many calls has been done.
all your prio knowledge does not apply to UdonSharp or UdonNetworking sadly. and i can confidently say this since i have been not only using unity for a very long time but also been a c# developer, and game developer for a very long but udon made the most simple things complex due to its inherit limits and lack of documentation.
and not to mention lack of passing arguments along to sync to other players.
All supported attributes in UdonSharp
this is the doc i've been using, simply relinking the udonsharp doc doesn't help my issues smh.
so you're saying i should go with Manual instead of Continous syncing? and run all my major operations on the owner of the object- and then sync it to clients 2ndarily?
what if a client wants to send data to the owner of the object? what if the ownership of the object changes?? would it change the data to reflect that client's now inaccurate data? thats such a dumb setup
in an ideal scenario, i would have everything run on the "server" / master, and make the world master the owner, but then it randomly changes ownership based on contact, and its such a nightmare for me to manage.
would i even be able to apply changes to udonsynced variables then?
First of all. we have a hard limit of 11 kb/s of total Data out per user. and Continous Sync should not be used that often unless its something that changes very often.
since Continous sync also has a 200 bytes limit per Serialization.
cant really send data to the owner.
also ownership never changes unless you allow it too.
in udon you kinda need every user to have their own object that cannot be stolen. aka Vrc Player Objects.
Master is just whoever owns data that hasn't been explicitly taken ownership of. And the mental model of server, client, or master might be unhelpful. Mostly it's just owner and non-owner. And a player has to take ownership to modify variables
in an ideal scenario you wouldn't have a player be the owner/master. it would be the server it self acting as a Master.. but also in an ideal world you would have the ablity to send pass arguments etc but since we cant we have to work around it with what we have.
whats so horrible though, surely there's a way??
like imagine a non-owner of an object, wanted to do something like send 7 damage, or something like that, in my systems of my guns and npc's i've made
imagine an npc is shot with a gun, by a client-
the owner of the npc object would get that data, of the shot vector and its damage 'n such,
and rn in my systems- it would verify the legitimacy of the shot bullet, from the owner's perspective, with the data sent from another client,
and then it would act on that damage, in the npc on the owner, and then sync that with all the clients, for the damage event visually and locally
surely there is a way to send this data to the owner in some way?
there is two ways. one is to create 100s of methods for each point of damage. which i wouldnt do
the second method is whoever shoots. has to have two UdonSynced variables. one that tells who they shot. and the other is the damage they did. sadly doing it this way requires you to check a collection of weapons that are in play and check their stats etc. but even with that you would have to check if its valid as well and so onn
and there is no way to send data to a owner. from a non owner. you can only tell them to run a method or do something.
just detect shots locally and if it is hit, SendNetworkCustomEvent to owner to damage the zombie, then you are sending minimal amount of information, they can then calculate what the damage is, based on what they know about your weapon (information you synced to them before)
in fact, SendNetworkCustomEvent to everyone, they all render locally the shot
i dont actually know but there is already a zombie game, so surely there is some way
that is not a good thing to do lol. Sending that many network events will cause it to throttle hard.
SendCustomNetworkEvent is notoriously slow to send. And no, there are no parameterized version of it. Someone made an asset that mimics the SendCustomNetworkEvent with parameters: https://github.com/Miner28/NetworkedEventCaller
Although I keep suggesting it to people, I've yet to stress test it myself.
I personally usually tend to use synced variables for everything (that asset I linked uses them under the hood btw).
i have stress tested it heavily. and it starts to slow down heavily with around 30 people. doing very little. so its not really recommended.
At this point nothing is a solution "good enough to be recommanded" but people still have to make things work somehow... xD
Even without the zombies, if someone fires a gun, they need to send a network event, that is the minimum, so that other people can at least know to play a fire animation and make the gun noise... now imagine the zombies are all run locally, all their damage and dying are local... you don't even need extra networking. There will be desync, but that is I guess a matter of game design to make it not matter so much, which idk the answer. Maybe that can be your strategy.
The ideal way currently is to use PlayerObjects as channels to sync everything (can't be taken ownership from so safe against cheating too). So I'd have a script on the PlayerObject that syncs the damage event with all the required data. Everyone else knows who is doing damage to what and can change visuals appropriately. But that involes a bit of overhead.
Yep, the best networking "code" is just smart game design..
when it comes to udon there isn't really anything i would consider smart.
like making any fps in udon is not going to be something that is possible. and its not ment for it either. as it is just to slow and we dont have the required networking for it.
Ownership doesn't change randomly. I think only the VRCObjectSync component can do that when you have physics object that collide (it's a setting you can disable too). It only changes when you call SetOwner or if someone leaves. But then you can consider the new owner to be the "server".
The simplest stupid method to get decent networking is to just take ownership every time you shoot something and then sync the decreased HP. Yes, you will get conflicts if something is being shot at the same time, but it's simple with little overhead.
If you want something more like a "server" concept, then you'd need to have "player owned objects". The new PlayerObjects make it a lot easier, you should use them. Those can then be used to send the damage events to the "server" who then syncs the health of the npc, which is only ever owned by the "server".
yeah but how would this work if the non-owner cannot update the udonsynced variables for the owner??
changing overship every shot is not going not work. to many requests and a server concept is not possible either since you cant send data to the owner and even then if the owner has to do 100 of requests it will also cause issues
I guess you just want the "action" be as deterministic as possible, and can rely on slowly synced data to get very similar or indistinguishable results. Now from observation remote players only sync their movement/aim angle every second or so (if you ever nodded to someone quickly, it won't show up on their end), so your game need to be take into account this fuzzyness... there is no twitch action ever, forget about it, unless your "quick time event" is done locally and you only send the result, so you can still have some element of twitch action, but you just gotta do it locally.
the non owner will receive the update when the owner does a request.
Player A Shoots Player B. in this case Player A hits Player B. and Player A request a serialize that then tells everyone else it has updated its data. then Player B puts in those data that comes from Player A and Requests it.
then Player A receives the new data on the one they shot.
this does mean you have two requests per shot.
alternatively is to have fixed data that all people have locally. and then you just need to check what they have. and calculate it based off that
Nah it will work unless you want to have 5+ people shooting the same target, then yeah, but that's where smart game design comes in. That's why it's the simple stupid solution. It will work in a lot of cases. Just not when everyone has to focus a huge boss or something.
Also, you seem to not understand how networking works. It doesn't matter who syncs it, all people are going to get all data anyways. Player owner objects means there will be no ownership transfer which makes sending data a lot cheaper and faster.
then you just need to sync what weapon, and what modifiers that person has.
it wont. lol as soon as you have two people hitting the second person will be the owner.
and it will fail
its not recommended to change owner often.
Okay, you can save yourself 90% of the work and have a shootable NPC that has conflicts with 2 or more people. As I said, that works for a lot of cases. Especially if you're hiding the sync conflicts.
wouldn't most player stats be able to be sync beforehand so it's safe to access for calculations and don't need to be sync all the time? Like...sync it on weapon change but that's it, the value stay the same otherwise.
thats why i mentioned fixed data
ye
so everyone knows what each weapon and modifier does
and all you need to do then is just when something is shot. update it based on that
it's the basics of any equippement system. same goes for protective gear (even if it only exist in a menu because avatar shapes differ)
mhm. atm having dynamic data is not really possible.
it all gotta be fixed.
unless you want to go through the hassel of changing owner and then setting the data and then verifying it etc
which would ruin any fast phased games.
and also heavily limit the amount of people.
to possible 8-16 only
to be fair there's quite a few zombie maps on VRc and, as imperfect as they are, they're still fairly playable. Most restrict the players to not be more than a party (which stays wy below the 30 players thresold you mentionned before) so there's ways to work with the limitations rather than against them
a map that's 4-12 players should be fine
12 is already A LOT of people when you have to shoot stuff...
unless it's PvP I mean, but PvE it's a lot
But yeah, "fixed" stats you only sync when changing, then use "as is" for calculations. Only need to sync the calculation result
then play the visual "hit animation" which isn't too much of a problem if it's slightly delayed, since it's just flavor/feedback
I wonder if you can even package "firing events" into two-second chucks, so while there is two seconds lag but the shots timing will be consistent internally
so, basically a combo system?
could add a multiplier depending on number of shot registered during the delay xD
combined with getservertime... and deterministic "battle" that have the zombie play an animation that is synced, like a synced video player, we can sort of know the exact timing someone made a shot at what time into the animation
...maybe too complicated
that seems a bit too much now.. ._.
but the "combo" chunk system could be cool for more action oriented map and less horror
yeah combo is a good game design
"go ham or go home" damages xD
to make the events as slow as possible
it's funny to me how people who actually knows what they're doing complain about "when you have more than 30 players" and there I am worlds crashing if there's 3+ in mines... 🤣
if you get a party of 4-8 to run without problems you're fiiiine
like, really xD
the world i have created for a community reached 57 people with no sync issues.
btw the zombie map can have like 100+ zombies at one time
the wave based zombie survival world i'm working on,
can handle up to 64 waves, with a total of 128 undead, of type 0 and type 1, if i consider other possible variations, the amount gets cascadingly larger,
but their processed in bulk by the wave manager, so their simply just representations of the data of what undead are where and doing what
I was hoping to simulate the undead per client as-is, and then only network position data every now 'n again, their agro status and damage events 'n such,
but im having to rewrite a lot of it anyway
the harder thing to network is what undead are spawned where, when a wave starts,
and also what is damaged / repair status,
as they can break walls, doors, windows and such.
that isn't so much of an issue luckly. having 128 objects at any given time makes it very easy. and you prob knonw about it already. but make sure to have instantiated all 128 Zombies immediately. aka make them in the unity editor. since its more costly to instantiate objects in runtime.
each zombie should have a ID that is fixed. so a number of 1 to 128 for that is fine.
each zombie should have a float or short for health. depending on if you want decimals or not. make sure that its UdonSynced. andn each zombie must have the UdonSyncBeaviour be manual.
and each zombie should also have anonther int that is used to check which user its aggroed to at the given moment. again make sure its UdonSynced
same goes for the Wave count etc also needs to be UdonSynced. but only needs to be a byte.
all in all a zombie only needs 3 or 4 viables that are UdonSynced and should in theory only require 20-28 bytes per serialize.
i made my own system of object caching, and they all exist in a sorta ghost state until in their spawned state,
all their colliders, renderers, operations and children that be cosmetic, are hidden away until spawned, and then hidden again when they are deloaded for the next wave.
they are pulled and put back into the undead cache of the wave handler, with no definition of what one is what- as its irrelevant,
all their data is reset per wave, and there are 64 of each undead type, currently 2, so there are 128
even in the unity editor- they hide according to their spawned status
i hope you refer to a an object pool. cause there is no thing such as a custom object caching system.
If she made it herself, then surely it exists, no?
what do you mean?
the objects exist ambiently already on world load,
they are pulled from the children of the wave handler object and also put in an array, and then put back as children and removed from the array,
that's how my system works. idk what you mean by "there is no such thing", what do you not know how to do stuff yourself?
i figure it works the same way anyway
just my system involves them never being disabled, meanwhile the other method for object pooling results in them being disabled when not active
semantics 😄 a Object Cache system is just a Object Pool.
so i just made my own object pool then? figured, i just looked at what was obvious and made one myself for my use case
sounds like it
anyway the navmesh agents glitch out if i disable them on load
so it was just my solution to it- to keep them enabled but forcibly make my own loading and unloading system, where they are never disabled as objects
i would port over my own pathing algorithm from one of my other projects, but the main issue with such an idea is the networking, and i dont wanna mess with trying to rewrite one of my old algorithms to work with this scuffed networking- that i have yet to even sort out for just basic stuff yet
having it entirely client-side isn't a good solution forever, but atleast the agents are semi predictable if you put them in the same position and velocity
I hope it's alright for me to ask questions about networking and trying to understand how it all works
I remember talking here earlier about shooting someone's hit box and then telling them report the damage you did.
I'm wanting to understand the concept of why and how things need to be done in general and the cases.
I know that's pretty vague or way too general so I'll talk about the thoughts that appear in my mind.
The first thing that appears in my mind is shooting a target.
Let's say this target has 100 health.
Does it need to contain a udon synced variable for its health in order for it to be networked?
If a player shoots this target with their gun, do they need to own that target in order to change its health?
If so, how would you become the owner of it?
If you are shooting particles from a VRC pick up gun you are holding, are you the owner of that gun and the particles fired from it?
Does ownership of particles matter when it comes to interacting with player own objects such as hitboxes and host own objects such as a target?
These are just random thoughts and examples I had in my mind. I'm trying to understand how the networking works.
If anyone has any better examples or is willing to explain it to me with the examples I provided I would appreciate it
Networking has always been a challenging thing for me and I wanna improve my knowledge of it
The entire concept of ownership simply means the person who has authority over the current state of the synced variables. If something does not have synced variables, then its ownership does not matter.
VRCObjectSync goes through the same architecture, so its position is analogous to
Likewise, if you want to directly change the synced variables but do not currently have ownership, then the first step is to take ownership. After that, you can set variables and requestserialization (if it's manual)
Consider a couple of factors though. What happens if two people try to take ownership and set variables at once? We don't know the future and latency is a thing, so if two people take ownership of the same thing at the same time, they'll fight over it. Perhaps the health is 10, and one person has a gun that does 2 damage while the other person has a gun that does 3 damage.
If the first person shoots it does 10 - 2, take ownership, set new health to 8. Then if there's enough time between that and the second person shooting it, then the second person will do 8 - 3, take ownership, set new health to 5.
However, if they shoot it at the exact same time, then one person will do 10 - 2, and the other person will do 10 - 3. Neither of them knows the other person shot at that point, and so they won't be able to add the damage on top of each other.
That said, that's not the only way of doing that. Instead of taking ownership of the target, you could sync everything through a script on the gun. If one gun says "I shot this target and did 2 damage" and then another gun says "I shot this target and did 3 damage" then it doesn't matter which order they did that in or which order the events are received, everybody can see both of the messages.
This doesn't mean that either way is particularly bad, it just means that they fit different use cases. Consider a simple toggle door - if two people try to open it at the same time, you don't want to open and then immediately close it, right? If both people are trying to open it, then all that matters is that it's open. You don't care that one of those messages was ignored.
Different methods for different use cases.
What is the best way to make one gun say "I did 2 damage"? Do I set a variable "lastHitTarget" = MonsterA and "last hit damage" = 2 and then request serialization and then (immediately?) send networked custom event "ShotFired"?
First, thank you taking the time to write that out, it does help me understand especially with edge cases.
I remember in a previous conversation we had the situation I had going was using player object as the hit box which can't be owned by anyone else so I was wondering if you could potentially dive deeper into that concept of how you can shoot someone and have them handle the health changes.
I believe we had a gun script going which has a set amount of damage and we somehow communicated that damage to the owner of the hitbox and had them handle it.
Now i also remember that the health state was handled locally since there was no need to network it for other players
I'm curious of how that concept works
Interestingly enough i had some help with this awhile back, I can try to link the conversation do you can view it
that's a really interesting way to do weapon damage
Somewhere around here is the middle of the conversation
Send networked event "Damage1" "Damage2" "Damage3" .....
lol
hmm that doesn't look very flexible
oh that's not what I got from that
what if I for loop counts for "damage" times and fire a number of "do1damage" (im joking)
I think you'd calculate the boss health by doing "TotalHealth - gun1Damage - gun2Damage... gunNDamage"
each player is networking the damage they deal, do so with Continous sync so it's pretty fast, and so it doesn't matter if player 1's damage or player 2's damage gets there first, AND you don't have to fight over ownership of a variable
Hm continuous can miss updates though, right?
well that's the point, it doesn't matter if it's missed
if local player's gun did 1 damage, then 2, then 3, but network only synced 1 and 3, it's fine, you only need the latest state
yeah but if I take a bunch of shots quickly? Wouldn't some of them possibly get dropped?
you wouldn't be caring about detecting other players, hit, only your own gun hitting locally
it's not the bullets hitting that sync, it's just the damage that player is reporting they've dealt
That's an interesting approach I have no seen yet. But does that work if you shoot two targets? I think you need some kind of history of who is being shot too. I personally would do this by syncing an array of damage and who it's hitting.
yeah you'd have to expand it for each enemy
yeah I was thinking some kind of most recent shots queue or something would be needed. But I haven't actually done anything action-oriented, so idk what the best solutions are
Forgot to mention, but I mean it would require manual sync. Honestly, I literally never use continuous, so I have no clue what else you could do with it.
But yeah, been a while since I tried anything that requires that much information. Not sure myself atm
It is almost we need something other than variables and events, like an event with input nodes to plug in a variable to be sent one-time......
manual usually ends up being slower
most cases manual works best, but there are times where you need things to update quickly, and manual won't cut it
Is there really such a difference? I always assumed they are about equal. Is that mentioned anywhere?
Continuous synchronization is intended for data that changes frequently and where intermediary values don't matter
https://creators.vrchat.com/worlds/udon/networking/network-details/
Networking in Udon can be challenging! Try to keep things simple until you're more experienced.
continous has the advantage that it will interpolate values inbetween, so the value will seemingly update faster than it's actually getting updates
Both say that 🤔
yeeah the one for manual I believe is a typo
Manual also mentions doing it quickly
the same sentence contradicts itself
There are many issues with the docs :))
a few weeks ago I submitted a request to edit this very article (as it still has the old Manual sync limit too), but I guess nobody checks them
I see what you mean. Still, it mentions doing so quickly. Frequently just means it's not meant do something every frame and stuff
manual definitely has the chance to not be quick at all
it purely depends on the package size
if you try to sync around the limit (280kb), it can take 30-35 seconds for other players to receive it
100kb is around 10 seconds
I assume that's because of other stuff related to player position, ik stuff etc? If you only sync small amounts of data, relatively speaking, then it should be pretty fast if not instant.
Of course, I'm just guessing here. But I heard this said multiple times that the limit isn't just for manual but includes other things too.
this is true, the limit for manual syncing, 280,496 bytes, is the absolute maximum limit. There's a little bit of overhead. In my experiments a more realistic limit was 280kb that still managed to work
so if your world is very full it will push down that limit even more
Yeah, that's my understanding too
Theoretically, if I'm currently having no issues saving/syncing an array of 1764 strings, would combining them and syncing a single string be less easy to work with?
Syncing arrays is annoyin because I often need an arbitrary length change, but if that limitation doesn't exist with strings I'd rather just locally parse the synced string into an array as-needed instead
I'll be investigating this soon so if you have any worries after reading this, please lmk 🙏
Only thing I can think of is I think Udon limits how long a single string can be
I don't feel like I'm remembering right but I think it's 100k characters
If it is that’s totally fine! I’m thinking at most it would be ~10.5k characters or so?
You'd have to test I don't think this is documented anywhere
Just keep making a string longer until the script crashes
remember thats only if there isn't anything else going on. it could very well take 60 + seconds
Ok
also yea i can't understannd why its not updated. countless people have prob suggest changes. that are very valid. as atm its just confusing
and has alot of contradicting explanations
Is there documentation for network get server time? And time get UTC time? I want to know if there is any network traffic generated, or if UTC time is from a time server therefore more accurate or something
And I wonder which is better for syncing purposes
@fallow mountain utc time is your local pc time. but given youre connected to the net, it should be well within 1 second for everyone.
You mean it is just UTC calculated from time zone and local clock? ok that cannot be reliable then, and can be spoofed
Hello everyone, I need help. I am making my Bachelors Dissertation project in AI using VRChat. I am analyzing a songs emotional content over time using a neural network and mapping that to corresponding character dance animations. I need to connect my VRChat world to a server so that when a user selects a song using the in-world UI, it sends a request, the server processes the song using the trained neural network and sends back a JSON file which can then be used to play the corresponding character animations along with song playback. I have decent experience with Unity, however, have no experience with connecting a world to an external server and I do not know the VRChat (Udon) specifics. If anyone knows how to do that, please give me some hints, refer me to a place where I can get more info, or best of all, get in touch with me. Much appreciated.
Due to VRChat having a very high priority on security and privacy, they limit your ability to send data out of VRChat heavily. You can generally only make requests via a URL that was manually entered (or at least pasted) in by a user directly, or hardcoded into the world at upload. Furthermore VRChat does not allow arbitrary HTTP requests to be made, but specifically only via the VideoDownloader, StringDownloader and ImageDownloader APIs. You can also only make one HTTP request every 5 seconds. No matter what.
All of these restrictions make it very hard to dynamically make requests out of and deliver content back into VRChat.
Woah, that’s super helpful, but a bummer. Thankfully in this setup, I think, everything can be encoded into a URL and request are only made every so often. I’m actually under a lot of time pressure right now and figuring all these caveats by myself will be borderline impossible. Can I message you directly for some further help @lone zealot ?
I mean you can store their movements I guess and have them export it out as a string?
or nvm
idk what ur trynna do. Good luck tho.
sounds hard
Sure
I only need to send to the server the song that was selected, so it can process it and send back an encoded array something like [0-15:sad, 15-25:neutral, 25-50:excited]
But you can also just ask your questions here and I will try to answer them
Yay ^^
You can hard code strings on github for each song with the data you need
What I would do is host a Webserver which provides an endpoint where one of the URL parameters is the song that was selected.
Then inside the VRC client use the StringDownloader API to make the request to your server.
Your server then processes whatever youre doing and replies with a JSON string, which you can then parse using VRCJson back on the client.
My brain is going brrr. Thank you so much for the responses, I have hope that this will be possible, my bachelors depends on it 😅. I have to sleep now and will continue figuring it out tmr.
Like HeplfulHelper said, VRc is very strict on data collection.
What you're trying to do however, only "which song have been selected" is needed, right? Everything else is external and related to the AI, not any players from what I understand?
Maybe you can get away with using OSC to drive the responses (but compute everything externally) and play the song outside of VRchat "for the AI to hear it" but players don't have to know about that little cheat. Send the play request in-world and separately to whatever the AI is actually listening to and...it should be ok?
It feel to me the VRchat portion is just flavor in this explaination, and everything could be done externally aside of the call to tell which song needs to start (and the returning OSC commands to drive the virtual-player AI)
Trying to write a comparatively simple rideable vehicle script.
On local side, its smooth, works great.
On remote site, motion is jittery.
I tried a few methods to script interpolating the recieved position for non-owners. I think I am fundamentally misunderstanding something though.
When I try to change the code to smoothly interpolate the position, I somehow always end up breaking the network logic? And then, only the first person to join the instance is able to ride any of the rafts.
But, once that person leaves a raft, suddenly other people can ride that specific one.
Wondering if anyone has any ideas on what I am misunderstanding. I'm sure it's something dumb. TLDR, That script is working as expected, but I am having a hard time implementing smoothed motion.
Here is an example of me trying to Lerp-N'-Slerp the non-owner motion. The smoothing worked quite well, but now for some reason, the rafts (stations) are locked out for anyone but the instance owner
Any help is so appreciated, X_X
I wonder if syncing the movement should just leave it to VRC Object Sync
Manual sync is not continuous and probably not suitable for movement syncing
Upon further testing, it seems the motion on the raft itself in the first script (with VRCObjectSync component on the station as well, not sure if thats best practice) is actually pretty smoothed. But, the player model is jittering like crazy
In OnStationEntered I think the ownership part might not be right. Networking.IsOwner is same as Networking.LocalPlayer.IsOwner().
Maybe something like this?
public override void OnStationEntered(VRCPlayerApi player)
{
if (player.isLocal && !player.IsOwner(gameObject))
{
Networking.SetOwner(player, gameObject);
}
controllingPlayer = player;
}
I'll try that and report back thanks :3
I wonder though - is this being called when the player clicks on the station? Or only once they actually enter the station? Because, in the first script, anyone can join any station as expected. But in the second script, only the instance owner can use the stations at first. For others, they highlight, but dont get put in the station when they click
Once they actually enter
Ok. I wonder why then, though the networking logic isnt really changed between the two scripts (besides non-owners interpolating the synced position), suddenly they can't enter the station.
TLDR, First script is working as expected, Raft itself is being smoothly synced (I suppose for no other reason than VRCObjectSync being on the station) - but the player model of the riders, is jittery and choppy when they're driving the raft
Yes. I started with the VRCChair3 prefab, and put this script as well as a VRCObjectSync on the station object.
Before, I tried having the station as a child, but then OnStationEntered/Exited wasn't popping.
I think your manual calculation and VRC Object Sync might conflict each other, I would just let it do its thing and not touch the transform at all
Yeah OnStationEnter/Exit needs to be on the VRC Station object
Just worry about control, and don't worry about syncing at all, leave it to VRC Object Sync
Ok
So, do you think, effectively, commenting out / removing the entire "OnDeserialization" block would be in line with that approach?
yeah, and the FixedUpdate
Thank you for your help!
Occams Razor really applied here.
Gosh, I was entering so many layers of convolution, last night I even had an entire "Raft Manager" trying to juggle who owned what. It was such a mess.
Here is the working final script for the rafts now found in my world **Furry Grotto **(At least, before I add more functionality). Hopefully this can help someone in the future. The setup is, this U# script is attached to a station which also has a VRCObjectSync component on it. Not sure if it's relevant but I have the station set to "Immobilize for Vehicle" and the ObjectSync has "collision transfer ownership" off
nice
Yes yes, all the processing will be done on the server. I just need to send an identifier to the server, so it knows which song to analyze and send back a JSON String that will be used to map animations.
Two questions:
- When you set up an external server, which hosting service do you use
- How does setup in VRChat look like? Do you simply need a script? If yes, can you send me an example script that handles sending and receiving requests.
Looks like my reply was blocked, guess it's because of the links to documentation... ^^"
do you mind if I re-send it in DMs?
But yeah VRchat does have documentation about OSC and a list of ressource (aka, known projects and modules the users tend to trust) you can search for it
your best bet is to contact said ressources devs and community servers, they're specialists and will have actual answers on what they experimented with and is actually possible or not
Please do, that will be very helpful 🙏🏼
That a great idea. Will definitely try to do that
i am in need of help getting the networking and object sync playable for a new game world im making. the world has pickupable gameobjects (playing cards) and im finding that the draw mechanic for the cards leaves then un-interactable, unvisible, or simply their transform isnt the same as others. it works perfectly fine with a single person in the world but ya cant really play a board game alone
Are you talking arbitrary songs or a preset list? The later seems like you could pre bake it into the world.
But the former you’re probably better at looking into the YouTube search prefabs and how they work. That’s a more complex setup where everything is actually handled externally, and it has an array of URLs pre baked into the world. The server sends back multiple options, each with a specific url assigned . Since they’re pre baked the URLs themselves aren’t dynamic — you’re instead communicating via url selection.
Alternatively you do a very simple, build the url and present it to the user in a text box to copy from, and then have them paste it into a url entry box. A couple of vket’s have used this approach for cross-world persistence.
Exfiltration of data is possible, though you have to be careful to stay in vrc rules and it can be slow, but by minimizing the data to the absolute minimum needed you can make it work.
Even if they make the music using in-world instruments you could use the copy+paste method to send larger amounts of data.
I will be making a preset list for now
Thanks. I understand like 70%, will have to look into it