I am making a simple LAN game to just get my bearings in Unity Networking. All it is supposed to do is when a player clicks on a square in a grid, it changes it blue. My issue is when the LAN Host clicks on a square, it only updates locally and doesn't update the clients. When the client clicks on a square, it updates locally and the LAN host gets updated, but other clients do not get updated. All of my grid pieces have a network identity attached to them
Any ideas?
Heres the code:
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
public class Player_Paint : NetworkBehaviour {
[SyncVar]GameObject syncGridPiece;
GameObject gridPiece;
void Update () {
Paint();
TransmitGridColours();
}
void Paint(){
if(isLocalPlayer && Input.GetMouseButtonDown(0)){
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
if(hit.collider != null){
print(GameObject.Find (hit.transform.name));
gridPiece = hit.collider.transform.gameObject;
gridPiece.GetComponent<SpriteRenderer>().color = Color.blue;
}
}
}
[Command]
void CmdProvideGridColourToServer(GameObject gridPiece){
if(gridPiece){
syncGridPiece = gridPiece;
syncGridPiece.GetComponent<SpriteRenderer>().color = Color.blue;
}
}
[Client]
void TransmitGridColours(){
if(isLocalPlayer){
CmdProvideGridColourToServer(gridPiece);
}
}
}
You need to add a hook to your syncGridPiece syncvar.
In the hook function set the color to blue, like you did in the Command.
The hook will be called on remote players.
Also I don't think you can use a GameOject as a syncvar.
You just need to send it's ID.
[SyncVar(hook=UpdateGridPiece)] int syncGridPiece
Maybe an RPC would be more adapted to what you want to do.
From what I understand syncGridPiece could be any square so you shouldn't pass all updates in the same message.
Just send that X players clicked X square with an RPC.
Look at the damage example : https://docs.unity3d.com/Manual/UNetActions.html
Figured it out. Had to use ClientRpc. Code below.
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
public class Player_Paint : NetworkBehaviour {
GameObject gridPiece;
void Start () {
}
// Update is called once per frame
void Update () {
Paint();
}
void Paint(){
if(isLocalPlayer && Input.GetMouseButtonDown(0)){
RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
if(hit.collider != null){
print(GameObject.Find (hit.transform.name));
gridPiece = hit.collider.transform.gameObject;
print("SettingColor");
CmdProvideGridColourToServer(gridPiece,Color.blue);
}
}
}
[Command]
void CmdProvideGridColourToServer(GameObject gridPiece,Color color){
RpcTransmitGridColours(gridPiece,color);
}
[ClientRpc]
void RpcTransmitGridColours(GameObject gridPiece, Color color){
gridPiece.GetComponent<SpriteRenderer>().color = color;
}
}
Related
I am trying to play and stop a particle system which is a child of a player prefab. So far I have gotten the host client's thrust particle effect to update across all clients but I haven't been able to figure out a non-host client. I would like it if every time a player presses the thruster button the game would display the thruster effect across all clients and when they stop the particle system stops and other clients would see it as well.
So far my code is as follows:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
public class PlayerMovement : NetworkBehaviour
{
public float thrustPower = 300f;
Rigidbody2D m_playerRb;
bool m_thrustOn = false;
public ParticleSystem m_playerPs;
[SyncVar(hook = nameof(SetThrusters))]
bool m_thrusterState = false;
private void Start()
{
m_playerRb = GetComponent<Rigidbody2D>();
m_playerPs.Stop();
}
private void Update()
{
if (isLocalPlayer)
{
MovePlayer();
m_thrusterState = m_thrustOn;
}
}
private void FixedUpdate()
{
if (m_thrustOn)
{
m_playerRb.AddForce(gameObject.transform.up * thrustPower * Time.fixedDeltaTime, ForceMode2D.Force);
}
}
private void SetThrusters(bool oldVal, bool newVal)
{
if (newVal) m_playerPs.Play();
else if (newVal == false) m_playerPs.Stop();
}
private void MovePlayer()
{
m_thrustOn = Input.GetKey(KeyCode.W) ? true : false;
float horizontalInput = Input.GetAxisRaw("Horizontal");
transform.Rotate(new Vector3(0, 0, horizontalInput * -180 * Time.deltaTime));
}
}
I cant really find anything online about Mirror in terms of particle systems. Let me know if you need any more info.
Thanks!
SyncVar always only go in the direction HOST -> CLIENT !
For the other way round you would need to use a Command
like e.g.
[Command]
void CmdSetSetThrusters(bool newVal)
{
// the hook would not be executed on the host => do it manually now
SetThrusters(m_thrusterState, newVal);
// set it on the host -> now via synVar automatically forwarded to others
m_thrusterState = newVal;
}
And then you will have to add a check whether you are the host or not like e.g.
if (isLocalPlayer)
{
MovePlayer();
if(isServer)
{
// afaik this you still have to call manually for yourself
// not sure here how SyncVar Hooks are handled on the host itself
SetThrusters(m_thrusterState, m_thrustOn);
// this is now synced to others
m_thrusterState = m_thrustOn;
}
else
{
// if your not Host send the command
CmdSetSetThrusters(m_thrustOn);
}
}
Personally for this reason for me the SyncVar with Hooks was always a little bit confusing and suspect.
You could as well simply implement both directions yourself using e.g.
[Command]
void CmdSetSetThrusters(bool newVal)
{
// set the value on yourself
SetThrusters(newVal);
// Then directly forward it to all clients
RpcSetThrusters(newVal);
}
[ClientRpc]
private void RpcSetThrusters(bool newValue)
{
// set the value on all clients
SetThrusters(newVal);
}
private void SetThrusters(bool newVal)
{
if (newVal)
{
m_playerPs.Play();
}
else
{
m_playerPs.Stop();
}
// if you are the HOST forward to all clients
if(isServer)
{
RpcSetThrusters(newVal);
}
}
and then
if (isLocalPlayer)
{
MovePlayer();
if(isServer)
{
SetThrusters(m_thrusterState);
}
else
{
// if your not Host send the command
CmdSetSetThrusters(m_thrustOn);
}
}
I need help with my project in unity
I want to stop each object by clicking on it.
What I did so far:
all my objects rotate but when I click anywhere they all stop, I need them to stop only if I click on each one.
This is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EarthScript : MonoBehaviour
{
public bool rotateObject = true;
public GameObject MyCenter;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
if(rotateObject == true)
{
rotateObject = false;
}
else
{
rotateObject = true;
}
}
if(rotateObject == true)
{
Vector3 axisofRotation = new Vector3(0,1,0);
transform.RotateAround(MyCenter.transform.position,axisofRotation, 30*Time.deltaTime);
transform.Rotate(0,Time.deltaTime*30,0,Space.Self);
}
}
}
Theres two good ways to achieve this. Both ways require you to have a collider attatched to your object.
One is to raycast from the camera, through the cursor, into the scene, to check which object is currently under the cursor.
The second way is using unity's EventSystem. You will need to attach a PhysicsRaycaster on your camera, but then you get callbacks from the event system which simplifies detection (it is handled by Unity so there is less to write)
using UnityEngine;
using UnityEngine.EventSystems;
public class myClass: MonoBehaviour, IPointerClickHandler
{
public GameObject MyCenter;
public void OnPointerClick (PointerEventData e)
{
rotateObject=!rotateObject;
}
void Update()
{
if(rotateObject == true)
{
Vector3 axisofRotation = new Vector3(0,1,0);
transform.RotateAround(MyCenter.transform.position,axisofRotation, 30*Time.deltaTime);
transform.Rotate(0,Time.deltaTime*30,0,Space.Self);
}
}
I was learning multiplayer game implementation through Unity Multiplayer system.
So I come across this really good tutorials written for beginners:
Introduction to a Simple Multiplayer Example
From this tutorial, I can't able to understand this page content:
Death and Respawning
Through this code, in tutorial they are talking about our player will respawn (player will transform at 0 position and health will be 100) and player can start fighting again.
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.Collections;
public class Health : NetworkBehaviour {
public const int maxHealth = 100;
[SyncVar(hook = "OnChangeHealth")]
public int currentHealth = maxHealth;
public RectTransform healthBar;
public void TakeDamage(int amount)
{
if (!isServer)
return;
currentHealth -= amount;
if (currentHealth <= 0)
{
currentHealth = maxHealth;
// called on the Server, but invoked on the Clients
RpcRespawn();
}
}
void OnChangeHealth (int currentHealth )
{
healthBar.sizeDelta = new Vector2(currentHealth , healthBar.sizeDelta.y);
}
[ClientRpc]
void RpcRespawn()
{
if (isLocalPlayer)
{
// move back to zero location
transform.position = Vector3.zero;
}
}
}
As per my thinking -> All clients are executing ClientRPC so all devices local players will move at the spawn position and health get full.
As per this tutorial -> only own player's spawn position and health get updated.
So why this thing is happening that I can't able to understand?
Actually RPC get called on all clients then all clients should require to move at start position and get full health.
Please give me some explanation in this.
As you can see on this image, you need to think that network systems aren't a "shared" room, they actually works as copying everything about the others on your own room.
Knowing that, now you can understand that if you send the Rpc from your Player 1, that Rpc will be executed on your Player1, and the Player 1- Copy on Player's 2 room.
Cause as you can read on the docs, the [ClientRpc] attribute:
is an attribute that can be put on methods of NetworkBehaviour
classes to allow them to be invoked on clients from a server.
So as Player 1 room is the host (server+client) an Player 2 is client, it will be executed on 2 room's, but evaluated on the first one.
Edit: That means that when, for example, Player 1 will die, it will call the RPC function, and (cause it's an RPC) his copy (Player1-Copy) on Room 2, will do the same.
As per my understanding, respawnprefab has to be instantiated on ServerRpc and the actual respawning logic (changing the transform of the player) should be given in ClientRpc.
private void Die()
{
isDead = true;
//Disable Components
for (int i = 0; i < disableOnDeath.Length; i++)
{
disableOnDeath[i].enabled = false;
}
Collider _col = GetComponent<Collider>();
if (_col != null)
{
_col.enabled = false;
}
Debug.Log(transform.name + " is Dead");
//call respawn method
StartCoroutine(Respawn(transform.name));
}
private IEnumerator Respawn(string _playerID)
{
yield return new WaitForSeconds(GameManager.instance.matchSettings.respawnDelay);
SpawningServerRpc(_playerID);
}
[ServerRpc(RequireOwnership = false)]
void SpawningServerRpc(string _playerID)
{
Transform spawnedPointTransform = Instantiate(spawnPoint);
spawnedPointTransform.GetComponent<NetworkObject>().Spawn(true);
SpawnClientRpc(_playerID);
}
[ClientRpc]
void SpawnClientRpc(string _playerID)
{
Player _player = GameManager.GetPlayer(_playerID);
Debug.Log("The player who dies is:" + _player.transform.name);
_player.transform.position = spawnPoint.position;
Debug.Log("I am Respawned" + _player.name + "Position:" + _player.transform.position);
SetDefaults();
}
Hope this helps you. Cheers👍
I'm a complete unity novice. I want to make a simple scene where you have three lives and you lose a live if you collide with a cube. This is my script:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class Lives : MonoBehaviour {
public Transform player;
public static int lives;
public Image live1;
public Image live2;
public Image live3;
// Use this for initialization
void Start () {
lives = 3;
live1.enabled = true;
live2.enabled = true;
live3.enabled = true;
}
void Update () {
DisplayOfHearts();
}
public static void Damage() {
lives -= 1;
}
public void OnCollisionEnter(Collision col) {
if (col.gameObject.tag == "cube") {
Lives.Damage();
}
}
public void DisplayOfHearts() {
if (lives == 2) {
live3.enabled = false;
}
else if (lives == 1) {
live2.enabled = false;
}
else if (lives == 0) {
live1.enabled = false;
}
}
}
What happens is the player can't move through the cube but the amount of lives stays three. Is there something I'm missing?
The problem is you have attached the script to a wrong game object. The script and the collider must be attached to the same game object.
Unity methods inside a MonoBehaviour script (such as OnEnable, Update, FixedUpdate, Awake, Start, OnTriggerEnter, OnCollisionStay, etc..) only work for the game object which the script is attached to.
If you attach the script to another game object don't expect any of those to work. Update only works while that game object is active. OnCollisionEnter only works when a collision occurs on a collider which is attached directly to that game object. (it doesn't even work when a child has the collider instead of the actual game object where script is attached to)
I am writting an multiplayer game but I am stuck with building system - I mean I've created a code to place blocks it works perfectly fine on host-side (when you run this script on host the block spawn for every client) but it isn't working on client side ( blocks spawn only for local client but not for server and other clients ).
I've seen many simillar problems but no answer was found this day. Maybe you guys could give me a hand.
Here's my code:
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
public class Builder : NetworkBehaviour {
public GameObject preview;
public Transform currentPreview;
bool isPreviewing = false;
GameObject buildingPreview;
private NetworkIdentity networkId;
// Use this for initialization
void Start ()
{
networkId = GetComponent<NetworkIdentity>();
}
// Update is called once per frame
void ViewPreview()
{
buildingPreview = Instantiate(preview, transform.position, transform.rotation) as GameObject;
currentPreview = buildingPreview.transform;
isPreviewing = true;
}
void Update ()
{
CmdBuild();
}
void CmdBuild()
{
if (networkId.isLocalPlayer)
{
}
else
{ return; }
if (Input.GetKeyDown(KeyCode.E))
{
if (!isPreviewing)
ViewPreview();
else
{
Destroy(buildingPreview);
isPreviewing = false;
}
}
if (isPreviewing)
{
Preview();
}
}
[Command]
void CmdSpawnBuilding()
{
GameObject buildingPlaced = Instantiate(preview, currentPreview.position, currentPreview.rotation) as GameObject;
NetworkServer.Spawn(buildingPlaced);
}
void Preview()
{
currentPreview.position = transform.position + transform.forward * 3f;
currentPreview.rotation = transform.rotation;
if (Input.GetButtonDown("Fire1"))
{
CmdSpawnBuilding();
isPreviewing = false;
}
}
}
P.S: I have network identity script assigned to prefab I am creating network aware and I have that prefab in my Network Manager as Registred Spawnable Prefabs.