Trying to make a Game randomizer while it picks out 1 player

I had made i post before which was on probably set under the wrong tag (since its under worlds While its an udon problem)

I am trying to create an randomizer similar to what we see on the murder, among us & freeze tag worlds. where you have 1 team of hunters and 1 team of bystanders/runners, only problem is that i cant find any help with the problem regarding it.

the only thing that i did found was a Random spawning function with timer (see picture below)

this ish does have some things i am happy to use of course but sadly its not what i am currently looking for. since that would be something like this.

I have made an attempt to make something like it but it ends up being somewhat odd. setting the player tag up was probably the most simplistic part of it.

i know that the function “get players with tag” isn’t functional yet.(sorta wish it was) so instead i thought of maybe finding some ways around it. which ended up with the following few things.

First was to try and make all players with the tag show up so you could see how many people are able/ready to play but since it only wants to detect the local player when i tested it and non of the others it became hard to figure it out from that. (it did show a 1 for the local player)

After that a friend of mine had an idea of maybe making some form of player array which… well you could sorta see the mess that it become for yourself i guess.

and after trying it twice i am at a loss of what to do now, of course i could probably do some attempts with U# (C#) but since i am far from knowing what i am doing on there i felt like first making it in Udon graph and then making it in U# with the visual references for it.

With all of that now out of the way… what’s the problem?

ish there a way where i can create it like picture number 2, if so that would help me out so much and i can finally continue on working further with the world.

So, text only since I can’t dig up the project at the moment.

But, for my “know your friends in space” world.
The way I randomized and kept track of players is just using arrays.

When the player joins you add them to the array.
When they leave you remove them from the array.
You use a separate integer to keep track of the last element that was added.

My world only allows for x players, so the array can be a fixed size.

If I need to pick a random player, I simply do a random range between 0 and the integer I used to keep track of the number of players, and the resulting number will be the entry in the array.

For example:
Int arrLen = 3;

[1] Bob
[2] Charley
[3] Dave
[4] 
[5] 

Will give me a random range between 0 and 3.
If the random roll ends up 2 I’ll pick Charley and add whatever logic I need to add to him.

As a failsafe I can check if the entry I picked is larger than 1 (Don’t think vrc allows for 1 character usernames) and if it isn’t I can simply call the random function until I get a valid number.

If I want to make it automatically scale, I can simply use rounding and division to scale the amount of “enemies” For example, if I want 1 “enemy” for every 5 players, I can simply use (arrLen/5) and round it down to the nearest integer

So for the array since i had a little trouble since i forgot that how to get the For command some how.

would it end up looking along the lines of this? (didn’t know where to put the PlayerAPI for the on player joined atm hence why its not linked “yet”)

or would there be steps/things missing. (also don’t worry take the time, i don’t want to rush this thing) probably will on it further after i get back from work though.

In my opinion UdonSharp will make this much much easier. Is UdonSharp an option for you?

Would you want to just select players from a certain area, and exclude players that are not in that area?

This should get you what you want.

public class test : UdonSharpBehaviour {

    public Collider GameAreaBox;
    
    public VRCPlayerApi GetRandomPlayer() {
        VRCPlayerApi[] ActivelyPlayingPlayers = GetPlayersInGameArea();//this gets a list of all the players in the game area;
        if (ActivelyPlayingPlayers.Length < 1) {//this checks to make sure there are more than 1 player. If there is only one player it returns null
            return null;
        }
        int RandomlyGeneratedNumber = Random.Range(0, ActivelyPlayingPlayers.Length);//get a random index of the array
        VRCPlayerApi Player = ActivelyPlayingPlayers[RandomlyGeneratedNumber];//pull the player assosiated with the random index of the array
        Debug.Log(Player.displayName);//print the name of the player in the console
        return Player;//return the player
    }
    public VRCPlayerApi[] GetPlayersInGameArea() {
        var TempArray = new VRCPlayerApi[VRCPlayerApi.GetPlayerCount()]; //create an array that is the size of the number of players in the world
        VRCPlayerApi.GetPlayers(TempArray); //fill the array with the players in the world
        
        int TotalNumberOfPlayersInGameArea = 0;

        for (int i = 0; i < TempArray.Length; i++) { //this finds the number of players in the game area
            var ThePlayer = TempArray[i];
            if (GameAreaBox.bounds.Contains(ThePlayer.GetPosition())) {
                TotalNumberOfPlayersInGameArea++; //it's worth noting that the ++ after the integer simpely sets the value of the interger equal to it's current value plus 1
            }
        }
        VRCPlayerApi[] ActivelyPlayingPlayers = new VRCPlayerApi[TotalNumberOfPlayersInGameArea];//this creates an array the size of the number of players in the game area
        int a = 0;
        for (int i = 0; i < TempArray.Length; i++) { //fills the array the players in the players in the game area
            var ThePlayer = TempArray[i];
            if (GameAreaBox.bounds.Contains(ThePlayer.GetPosition())) {
                ActivelyPlayingPlayers[a] = ThePlayer;
                a++;
            }
        }
        return ActivelyPlayingPlayers;// this returns the final array of the players in the game area
    }
}
2 Likes

Yes, this ish what i am looking for, though i do have to figure out how to add an Set Text for the ActivelyPlayingPlayers. so that everyone can see how many people are inside the area.
I would also take that i could adjust the length (for the Minimal amount of people) to a different number without causing any major problems correct?

and I probably will need to figure out how to get an teleport event for both teams after the single player is picked out.

Have no problem with the use of UDONsharp its more that i have a visual for when i want to turn most of the existing things from udongraph into udonsharp.

(so that i can learn from it better, at least that how i thought of doing it)

Here is a bit of an expansion to the previous script that should handle those requirements. Of course this may not be the best way to do everything but should act as something usable to build off of.

Hope this is helpful!

(untested)


using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;
using UnityEngine.UI;

public class test : UdonSharpBehaviour {

    public Transform HunterSpawn;// game object transform where the hunter should spawn
    public Transform HuntedSpawn;//game object transform where the hunted players should spawn

    public Collider GameAreaBox;
    public Text PlayerCountTextBox;

    public int RequiredNumberOfPlayers = 3;//amount of players required to start the game

    [UdonSynced]public int SelectedPlayerID;//the ID of the hunter (since you can't sync the VRCPlayerApi variable currently)




    public void StartGame() {
        if (!Networking.LocalPlayer.IsOwner(gameObject)) { Networking.SetOwner(Networking.LocalPlayer, gameObject); }// the player the calls this function needs to own the object this script is on in order to update the synced variable
        SelectedPlayerID = GetRandomPlayer().playerId;// get a random player and set the synced SelectedPlayer variable to the randomly slected players ID
        RequestSerialization();//sync the randomly selected player's name to everyone in the world
    }
    public override void OnDeserialization() {
        SendCustomEventDelayedSeconds("TeleportPlayer", .1f);//this runs when they receive the synced data (but I learned that teleporting players directly from this function doesn't work reliably so I delay it for .1 seconds)
    }
    public void TeleportPlayer() {
        VRCPlayerApi PlayerLocal = Networking.LocalPlayer;//set a variable of the local player (each player in the world will run this function localy)
        if (!GameAreaBox.bounds.Contains(PlayerLocal.GetPosition())) { return; }//if the player is not in the game are the script ends here
        if(PlayerLocal.playerId == SelectedPlayerID) {//if the player's ID matches the selected player they will teleported to the hunter location
            PlayerLocal.TeleportTo(HunterSpawn.position, HunterSpawn.rotation);
        } else {//if their name does not match the selected players ID they will be teleported to the other area
            PlayerLocal.TeleportTo(HuntedSpawn.position, HuntedSpawn.rotation);
        }
    }



    public VRCPlayerApi GetRandomPlayer() {
        VRCPlayerApi[] ActivelyPlayingPlayers = GetPlayersInGameArea();//this gets a list of all the players in the game area;
        if (ActivelyPlayingPlayers.Length < 1) {//this checks to make sure there are more than 1 player. If there is only one player it returns null
            return null;
        }
        int RandomlyGeneratedNumber = Random.Range(0, ActivelyPlayingPlayers.Length);//get a random index of the array
        VRCPlayerApi Player = ActivelyPlayingPlayers[RandomlyGeneratedNumber];//pull the player assosiated with the random index of the array
        Debug.Log(Player.displayName);//print the name of the player in the console
        return Player;//return the player
    }
    public VRCPlayerApi[] GetPlayersInGameArea() {
        var TempArray = new VRCPlayerApi[VRCPlayerApi.GetPlayerCount()]; //create an array that is the size of the number of players in the world
        VRCPlayerApi.GetPlayers(TempArray); //fill the array with the players in the world
        
        int TotalNumberOfPlayersInGameArea = 0;

        for (int i = 0; i < TempArray.Length; i++) { //this finds the number of players in the game area
            var ThePlayer = TempArray[i];
            if (GameAreaBox.bounds.Contains(ThePlayer.GetPosition())) {
                TotalNumberOfPlayersInGameArea++; //it's worth noting that the ++ after the integer simpely sets the value of the interger equal to it's current value plus 1
            }
        }
        VRCPlayerApi[] ActivelyPlayingPlayers = new VRCPlayerApi[TotalNumberOfPlayersInGameArea];//this creates an array the size of the number of players in the game area
        int a = 0;
        for (int i = 0; i < TempArray.Length; i++) { //fills the array the players in the players in the game area
            var ThePlayer = TempArray[i];
            if (GameAreaBox.bounds.Contains(ThePlayer.GetPosition())) {
                ActivelyPlayingPlayers[a] = ThePlayer;
                a++;
            }
        }
        return ActivelyPlayingPlayers;// this returns the final array of the players in the game area
    }




    public void UpdatePlayerCountTextBox() {
        VRCPlayerApi[] ActivelyPlayingPlayers = GetPlayersInGameArea();//get the array of players in the game area
        int NumberOfPlayers = ActivelyPlayingPlayers.Length;//put the length of the array into a veriable

        if (NumberOfPlayers < RequiredNumberOfPlayers) {//check if the number of players in the area is less than the required number of players
            PlayerCountTextBox.text = "Not Enough Players :(";
        } else {
            PlayerCountTextBox.text = NumberOfPlayers.ToString() + " players are ready to play! :)";
        }
    }
    private void Update() {
        UpdatePlayerCountTextBox();//this updates the player count text box every frame. probably not the most efficient way to do it
    }
}

So, this ish closer to what i want yes, but the thing ish that as soon as you walk into the game area you get teleported but 1 player. which ish odd since you did add the teleport for that for said player.

also there isn’t a interact button within it, which probably would make it better for both playing & testing, wouldn’t it? (white cube in the picture below, code is set to that)

lastly ish the Text… It doesn’t show up… like … at all, i have used the Textmeshpro around my world which works but the normal text doesn’t (it probably a weird unity thing which is rather annoying but i wouldn’t know, or me making a dumb mistake idk) the last one i should be able to adjust myself without to much trouble… i hope.

though the other ones i probably cant do much about unless i learn lots of C#/Udonsharp coding some how…

thanks again for helping me with this problem

Forgot to add a picture to the text is supposed to be set (for testing of course)

Ah that makes sense.

You could try adding another synced bool like “[udonsynced] public bool GameIsActive” set it to false by default, then add a line to the StartGame() function that sets it to true, and on the TeleportPlayer() function put a line at the beginning that returns if it’s false, or you could also put the SendCustomEventDelayedInSeconds in an if statement, so it only runs if GameIsActive is true.

if (GameIsActive == false){return;}
or
if (GameIsActive == true){
SendCustomeEventDelayedSeconds(“TeleportPlayer”, .1f);
}

Regarding the textbox, yes TextMeshPro is why it isn’t working, though you might still be able to use it if you want. At the top I added “using UnityEngine.UI” which imports functions that allow you to manipulate normal Unity UI elements, but not TextMeshPro UI elements. You would just need to import TextMeshPro functions (google “unity csharp textmeshpro”). Though I’m not certain if Udon supports TextMeshPro.

Regarding the interact you could either add an override public void OnInteract() function that calls the StartGame() function (and make sure there is a mesh and collider on the gameobject this udonbehavior is on), or you could call StartGame() from a ui button.

Hope that’s helpful.