How to call [Command] on Client in UNet(Unity) - c#

I'm working on a Unity 2D multiplayer game using UNet. My problem is that the Client cant send the [Command] to the server. Im debugging on UnityEditor and a built apk for my android phone.
First I used UnityEditor as Host and the phone as Client, the Debug.Log(SkinName) APPEARS on the console.
Then I used UnityEditor as the Client and the phone as Host, the Debug.Log(SkinName) DOES NOT APPEAR.
I tried to use [ClientRpc] instead of [Client] but it just made it worse, both client and host wont sync. I dont know if I excuted the [ClientRpc] in the right way.(im a noob at UNet)
I've visited other threads/forums and other UNet tutorials to look for solutions but this is what I came up with.
Note: On the other threads that I visited, people mostly suggest that Local Player Authority is unchecked and that is what causing the problem but in this case it is CHECKED.
Code:
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
using Spine;
using Spine.Unity;
using Spine.Unity.Modules;
class SetupLocalPLayer : NetworkBehaviour {
[SyncVar]
public string SkinName;
public string[] CharNames;
public string[] SlotNames;
public string[] AttachmentSuffix;
void Start (){
TransmitSkins ();
SyncSkin ();
if (isLocalPlayer) {
var skeletonrenderer = GetComponent<SkeletonRenderer>();
for(int z=0;z<SlotNames.Length;z++){
skeletonrenderer.skeleton.SetAttachment(SlotNames[z],GameController.control.skinName+AttachmentSuffix[z]);
}
GetComponent<PlayerManager> ().enabled = true;
GetComponent<FollowCam> ().enabled = true;
}
}
void Update () {
}
void SyncSkin(){
if (!isLocalPlayer) {
var skeletonrenderer = GetComponent<SkeletonRenderer>();
for(int z=0;z<SlotNames.Length;z++){
skeletonrenderer.skeleton.SetAttachment(SlotNames[z],SkinName+AttachmentSuffix[z]);
}
}
}
[Command]
void CmdSetSkin(){
SkinName = GameController.control.skinName;
Debug.Log (SkinName);
}
[Client]
void TransmitSkins(){
if (isLocalPlayer) {
CmdSetSkin ();
}
}
}

Ok, so from what i can see you are trying to sync the skins being used by each player so that each player has the correct skin on every client.
There are a couple of problems with your code, in both syntax and structure.
First lets look at the syntax.
You should absolutely use [ClientRpc] instead of [Client] and the method should be named RpcTransmitSkins. If the script is attached to the player prefab and Local Player Authority is Checked on the network identity then it will work. [Client] belongs to a different networking API entirely.
Now lets look at the structure.
The basic logic is correct, however, in reality the SyncSkin method will be called long before the Transmission is received.
The Start method will not wait for the transmission, so why not move the skin assignment to the Rpc, that way the other versions of the player will only try to assign their skin once the message is received.
You also have certain actions split up that all clients need to do, getting the SkeletonRenderer for example
Here is how I would restructure your script.
void Start (){
//first, grab the SkeletonRenderer on every version of the player.
//note that it should not be a local var now, but a field in the script
skeletonrenderer = GetComponent<SkeletonRenderer>();
//now do the local player specific stuff
if (isLocalPlayer) {
//transmit the skin name to the other versions of the player
CmdSendSkinRpc(GameController.control.skinName);
//then assign the skin to the local version of the player
for(int z=0;z<SlotNames.Length;z++){
skeletonrenderer.skeleton.SetAttachment(SlotNames[z],GameController.control.skinName+AttachmentSuffix[z]);
}
GetComponent<PlayerManager> ().enabled = true;
GetComponent<FollowCam> ().enabled = true;
}
}
[Command]
void CmdSendSkinRpc(string skin){
RpcTransmitSkins(skin);
Debug.Log("Transmitting Skin Named: " + skin);
}
[ClientRpc]
void RpcTransmitSkins(string TransmittedSkinName){
if (!isLocalPlayer) {
Debug.Log("Receiving Skin Named: " + TransmittedSkinName);
for(int z=0;z<SlotNames.Length;z++){
skeletonrenderer.skeleton.SetAttachment(SlotNames[z],TransmittedSkinName+AttachmentSuffix[z]);
}
}
}
Don't be disheartened by the learning curve of the UNET system. It's super confusing to think in multi client and server terms. Even this simple action had me scratching my head for a few minutes :D

Related

I'm really struggling to pass the Oculus VRC TestSubmitWhenNotVisible test

I'm stuck on the last test for my app before submission to the Oculus store. I've tried all sorts with no avail. I need to do is pass the frames when not visible test.
Effectively, the app needs to go into pause mode when the user clicks the menu button on the oculus touch controller.
I need to stop all frames submitting from Unity. For example, things I've tried, turn off cameras, audio, ovrplayercontroller etc but frames being submitted when the menu button is pressed on the rift so it would appear to freeze the application.
I've tried disabling cameras in a foreach loop, disabling the player gameobject, ovr controllers all sorts.
I have a gameobject with a script attached to try and detect when the test will fire based on the HMD losing tracking.
Here's where I'm currently at (gone back to the basics again), any help would be greatly appreciated.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HMDCheck : MonoBehaviour
{
public GameObject OVRCameraRig;
private void Update()
{
if (!OVRManager.isHmdPresent)
{
OVRCameraRig.SetActive(false);
Time.timeScale = 0f;
}
else
{
OVRCameraRig.SetActive(true);
Time.timeScale = 1f;
}
}
}
Additionally their docs say the test performs this action:
TestSubmitFramesWhenNotVisible
Tests if your app stops submitting frames when the Universal Menu is open.
Note :
My most recent command line response for the test is the following output :
Starting TestSubmitFramesWhenNotVisible
Waiting for the application to run for 5 seconds before testing begins...
Starting test...
Requesting the void...
Number of texture swap chains committed when visible 68
Number of texture swap chains committed when not visible 4
ERROR: Committed a texture swap chain (called ovr_CommitTextureSwapChain) when application not visible
Please refer to VRC Guidelines:
https://developer.oculus.com/distribute/latest/concepts/vrc-pc-input-1/
Cleaning up...
Test FAILED
To get around it, I created a script with public gameobjects. I then dragged my player into the slots inside Unity but left the camera on the scene alone. Here's a screenshot and the code. I initially added the camera to turn off frames sending but Oculus rejected it as it froze in the background upon pressing the menu button.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HMDCheck : MonoBehaviour
{
public GameObject target, scripts, LocalAvatar;
private void Update()
{
if (!OVRManager.hasVrFocus || !OVRManager.isHmdPresent || !OVRManager.hasInputFocus)
{
//target.SetActive(false);
scripts.SetActive(false);
LocalAvatar.SetActive(false);
Time.timeScale = 0f;
AudioListener.pause = true;
}
else
{
//target.SetActive(true);
scripts.SetActive(true);
LocalAvatar.SetActive(true);
Time.timeScale = 1f;
AudioListener.pause = false;
}
}
}
I have almost the same code, but this detects the Universal Menu correctly and does freeze the output, but it still won't pass the test. I think it may pass if you disable splash screens, but can't know since I have the free version of Unity.
Edit: I saw your answer Diego, but cannot comment. Did your action resolve your original question? And what license do you have for Unity?
Camera cam;
bool bPause = false;
void Update()
{
//install 'Oculus Integration' for this to work
bool bPauseNow = !(OVRManager.hasInputFocus && OVRManager.hasVrFocus);
//pause state change
if (Camera.main != null) cam = Camera.main;
if (bPause != bPauseNow)
{
bPause = bPauseNow;
if (bPauseNow)
{
Time.timeScale = 0.0f; //stops FixedUpdate
//Update keeps running, but
// rendering must also be paused to pass vrc
cam.enabled = false;
}
else
{
Time.timeScale = 1.0f;
//
cam.enabled = true;
}
}
//...
}
I am using the "Oculus XR Plugin", and got this issue,
but I found that the results of the "VRC Validator" are not so accurate,
then I submitted the App for review and passed the test.

Sync ParticleSystem across Clients and server, Unity3D

I am trying to switch a single player game into multiplayer using unity 5.6.4p2 and c#.
I have two particle systems:
"enginePS" set to play on-wake which appears on all sides (cliens + server).
"engineSpeedPS" which set to play when player moving forward.
The second particle system does not appear only for localplayer the one moving forward, so others does not see the particle system.
I am new to UNet so I would really appreciate your help. please check my code below:
using UnityEngine;
using UnityEngine.Networking;
public class MP_PlayerController : NetworkBehaviour
{
public ParticleSystem enginePS1, enginePS2, engineSpeedPS1, engineSpeedPS2,
engineSpeedPS3;
private float verticalCurrentValue;
private float movingX, movingZ;
private bool zAxis;
public bool isMovingForward = false;
public bool isMovingBackward;
public float currentPossition;
public GameObject player;
void Start()
{
engineSpeedPS1.Stop();
engineSpeedPS2.Stop();
engineSpeedPS3.Stop();
verticalCurrentValue = movingZ;
currentPossition = transform.position.z;
}
void Update()
{
if (!isLocalPlayer)
{
return;
}
movingX = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;
movingZ = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;
transform.Rotate(0, movingX, 0);
transform.Translate(0, 0, movingZ);
ChangeEngineParticle();
}
void ChangeEngineParticle()
{
if(!isLocalPlayer)
{
return;
}
if (Input.GetKey("up"))
{
if (isMovingForward == false)
isMovingForward = true;
}
else
{
if (isMovingForward == true)
isMovingForward = false;
}
if (isMovingForward)
{
if (!engineSpeedPS1.isPlaying)
{
engineSpeedPS1.Play();
engineSpeedPS2.Play();
engineSpeedPS3.Play();
}
}
else if (isMovingForward == false)
{
if (engineSpeedPS1.isPlaying)
{
engineSpeedPS1.Stop();
engineSpeedPS2.Stop();
engineSpeedPS3.Stop();
}
}
}
I tried using the [Command] attribute before CmdChangeEngineParticle(), but that made the engineSpeedPS to work at the server end only without being detected at the client end.
Any ideas??
OK so your second engineSpeedPS can be ignored in the networking code - just play that locally on the client using isClient or similar. It doesn't need to go over the network.
I'll talk just about setting the other ParticleEffect, which you want to play everywhere. It is on a Client object, meaning that object is controlled by the client. The client should tell the server that it's time to play the particles, and the server then tells all other connected clients.
The particles probably shouldn't play on awake, as everyone will awake at different times.
So you should have your own code that decides it's time to play the particles. When it does it tells the server with a Command. Note, this should be on the Player object - if not you'll need to Assign Client Authority.
[Command]
public void CmdStartParticles()
{
}
So the client in charge calls CmdStartParticles(). The [Command] tag means that it doesn't get executed locally, but on the server instead.
So what do we want the server to do? It should tell all Clients to play the particles, and also play them itself.
[ClientRpc]
public void RpcStartParticles()
{
DoStartParticles();
}
public void DoStartParticles()
{
particles.Play();
}
The first function can only be called by a server, and it acts on all the connected clients.
The second function is just a handy way to actually do the action
In the CmddStartParticles function, you would just call RpcStartParticles() and also DoStartParticles() (because you want the server to also play them, and RpcStartParticles only affects the clients ).
Rip off the Tanks Demo, which I believe has moving things with engines which play Particle Effects. It might be a bitt outdated, but should help still.

null reference exception in level manager script

So I'm basically brand new to unity and C# and have been following along with tutorials online (N3K 2D Platformer, YouTube), I'm trying to create a basic UI to display score etc and I seem to have come across a null pointer exception which I can't seem to figure out as I have referenced the two objects that are causing this error, namely my scoreText object and my hitPointText object.
As I've said I did reference those very objects by dragging them from my hierarchy and into the fields I had created in my level manager script in the inspector and further to that I am simply following along with a tutorial and have done exactly as the video has instructed, but yet on the video it seems to work fine.
The offending lines of code are:
scoreText.text = score.ToString();
hitPointText.text = hitPoints.ToString();
This tutorial is now over 1 year old, is it possible that a unity update has changed that way things NEED to be referenced?
I'll post my level manager code below in the hopes that someone may be able to point out the error that I seem to be missing.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class LevelManager : MonoBehaviour
{
public static LevelManager Instance { set; get; }
public Transform spawnPosition;
public Transform playerTransform;
private int hitPoints = 3;
private int score = 0;
public Text scoreText;
public Text hitPointText;
private void Awake()
{
Instance = this;
scoreText.text = score.ToString();
hitPointText.text = hitPoints.ToString();
}
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
private void Update ()
{
if(playerTransform.position.y < (-10))
{
playerTransform.position = spawnPosition.position;
hitPoints--;
if(hitPoints <= 0)
{
Debug.Log("Your Dead!");
}
}
}
public void Win()
{
Debug.Log("Victory");
}
}
Snippets of screens below:
Scene view of unity engine
Game view of unity engine, with game running
So here is a snippet of code from my player class which uses Instance on the LevelManager script in order that it can have access to the win() method as can be seen in the last case of the switch "WinPost", not sure if that is what you are referring to when your mentioning singleton, other than that never is the term singleton used in any of the scripts I have.
switch (hit.gameObject.tag)
{
case "Coin":
Destroy(hit.gameObject);
break;
case "JumpPad":
verticalVelocity = jumpForce * 2;
break;
case "Teleporter_1":
controller.enabled = false;
transform.position = hit.transform.GetChild(0).position;
controller.enabled = true;
Debug.Log("This works!");
break;
case "Teleporter_2":
controller.enabled = false;
transform.position = hit.transform.GetChild(0).position;
controller.enabled = true;
Debug.Log("This works!");
break;
case "WinPost":
LevelManager.Instance.Win();
break;
default:
break;
}
My guess would be that the components aren't initialized when you call Awake. Awake gets called as a constructor-kind-of method as soon as the object is created. When it is called, you can't be sure if the other components got initialized already.
I would suggest you copy the assignments you make in Awake into Start and come back to see if it works. Start gets called after the GameObjects have their components initialized.
private void Awake()
{
Instance = this;
}
// Use this for initialization
void Start ()
{
scoreText.text = score.ToString();
hitPointText.text = hitPoints.ToString();
}
Thanks to everyone for trying to help and all the great suggestions.
Ultimately I ended up breaking my game in the process of trying to recreate the same UI in a new blank scene, I did manage to recreate the same error before breaking my game which at the time then left me none the wiser. However due to the fact I broke my game I had to step back at least two tutorials and recreate the level manager object and the empty child spawnPosition object, (the level manager script was ok, it was just the level manager object and its child that I broke), anyway in having to recreate both of those objects again everything now seems to work as intended and so this leads me to the conclusion that the problem was not the code but the objects themselves???
Thanks again to everyone that tried to help, another day another learning experience.
D.

Send player position to another player to move him in multi player game using Unity Photon

I follow the steps of PUN basic tutorial.
For now I reach to a part of what I want Send my position in the current time to another player with me in the room to move him. I can print my position each time I update it , what I need is to know how I can send position to another player to move him.
Let's say I have a desktop player and when I move him this translation moving the player on mobile.
And how I stop instantiate object on mobile, I just want to deal with the instantiated object on desktop.
I am using unity and Photon Network SDK.
Here is the code I used
using UnityEngine;
using System.Collections;
public class NetworkCharacter : Photon.PunBehaviour {
private Vector3 correctPlayerPos;
void Update()
{
if (!photonView.isMine)
transform.position = Vector3.Lerp(transform.position, this.correctPlayerPos, Time.deltaTime * 5);
photonView.RPC ("PrintPosition", PhotonTargets.All, transform.position);
}
void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
{
if (stream.isWriting)
{
// We own this player: send the others our data
stream.SendNext(transform.position);
}
else
{
// Network player, receive data
this.correctPlayerPos = (Vector3)stream.ReceiveNext();
}
}
[PunRPC]
void PrintPosition(Vector3 pos)
{
Debug.Log (pos);
//I need to send position coordinates to the other device
}
}
The other class of establish multiplayer environment:
using UnityEngine;
using System.Collections;
public class NetworkManager : Photon.PunBehaviour {
// Use this for initialization
void Start () {
PhotonNetwork.ConnectUsingSettings ("0.1");
//PhotonNetwork.logLevel = PhotonLogLevel.Full;
}
void Update () {
}
void OnGUI()
{
GUILayout.Label(PhotonNetwork.connectionStateDetailed.ToString());
}
public override void OnJoinedLobby ()
{
Debug.Log("join lobby!");
PhotonNetwork.JoinRandomRoom ();
}
void OnPhotonRandomJoinFailed()
{
Debug.Log("Can't join random room!");
PhotonNetwork.CreateRoom(null);
}
void OnJoinedRoom()
{
Debug.Log("join random room!");
GameObject monster = PhotonNetwork.Instantiate("monsterprefab", Vector3.zero, Quaternion.identity, 0);
}
}
PUN has some new components since their new update.
I'd recommend you to use those components because it is really user friendly towards new users.
Some components you could use:
PhotonTransformView
PhotonRigidbodyView
PhotonAnimatorView
These three components will help you sync your position, physics and animations on the GameObject it is attached to.
If you do not want to use these components i suggest you search up: interpolation and extrapolation for Unity PUN.
Some good starting tutorials:
SkyArena PUN Tutorial This is a really good video tutorial[in parts] on PUN so you should definitely check that out.
Exit Games Marco Polo This tutorial is created by someone from PUN also really good to read even if you don't need it.
Hope this will help you.
-Menno
I found the answer in Demo Synchronization that provided in PUN plugin, it is really helpful.

Unity Networking: can't spawn scene objects on all clients

I can't really figure out how to solve my problem. I have been looking for an answer but I couldn't find anything.
I have a button in my scene that can be pressed both by client and host. When the button is pressed, it creates a cube in the scene. The problem is that: the cube can be created only by the host and the host is the only user that can see it and manipulate it.
My code is:
public class CreateCube : NetworkBehaviour {
GameObject cubo;
float lastCollisionTime=0;
float collisionTime=0;
void OnCollisionExit(Collision other) {
collisionTime = Time.time;
if (collisionTime - lastCollisionTime >1.5) {
CmdCreaCubo ();
lastCollisionTime = collisionTime;
}
}
}
}
[Command]
void CmdCreaCubo(){
GameObject cubo=Instantiate(Resources.Load("MyPrefabs\\Oggetti\\CubeGrasp")) as GameObject;
cubo.transform.position = new Vector3 (-5.88f, 7.51f, -19f);
cubo.name = "CubeGrasp";
NetworkServer.Spawn (cubo);
}
}
Could anyone help me please?
Thank you so much
Instead using simple Instantiate you should need to use Network.Instantiate
The given prefab will be instanted on all clients in the game.
Synchronization is automatically set up so there is no extra work
involved.

Categories