I have two players and these players are hitting a ball. Whenever they hit, there is a chance that ball's color may change.( green or red ). But when a player hit a ball it changes ball's color for only himself, other player's ball is the same color. I want all players to see the same color. Here is the code:
using UnityEngine;
using UnityEngine.Networking;
public class ColorGreen : NetworkBehaviour
{
// Use this for initialization
[SyncVar]
int temp = 0;
void Start () {
transform.GetComponent<Renderer>().material.color = Color.green;
}
// Update is called once per frame
void Update () {
if (!isServer)
return;
RpcCHANGE();
}
[ClientRpc]
void RpcCHANGE()
{
temp = Random.Range(0, 2);
if (temp == 0)
{
transform.GetComponent<Renderer>().material.color = Color.green;
}
else if(temp == 1)
{
transform.GetComponent<Renderer>().material.color = Color.red;
}
}
}
Ball Object has network identity checked "Local Player Authority". Can you guys tell me what I did wrong?
Thank you.
I think that when you call RpcCHANGE, it is called on each network instance, running the code within the method separately. This means that the result of Random.Range(0, 2); is different for each client.
Instead, you could generate the random color in the update method, and pass the color to RpcCHANGE, causing each network instance to be changed to the same color.
Related
Inspector (for ball sprite)Trying to add the Ball script to "Ball"I am creating a BlockBreaker game 2D and whenever I try to connect a script to a sprite it gives me the error: "Can't add script behaviour VisualContainerAsset.The script needs to derive from MonoBehaviour."
I have 6 scripts on total and when I try to attach one to a sprite they each give me the same error. Can someone tell me why?
SCREENSHOT OF ERROR
This is the ball script :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ball : MonoBehaviour {
public Paddle paddle;
bool hasStarted = false;
Vector3 paddleToBallVector;
void Start () {
paddleToBallVector = this.transform.position - paddle.transform.position;
}
// Update is called once per frame
void Update () {
if(hasStarted == false)
{
this.transform.position = paddle.transform.position + paddleToBallVector;
if (Input.GetMouseButtonDown(0))
{
hasStarted = true;
this.GetComponent<Rigidbody2D>().velocity = new
Vector2(Random.Range(-1f, 3f), 10f); //This is for the ball to bounce randomly
}
}
}
void OnCollisionEnter2D(Collision2D myColl)
{
if (myColl.gameObject.name == "Rightborder" || myColl.gameObject.name == "Leftborder") //If the ball hits either the left border or the right border
{
this.GetComponent<Rigidbody2D>().velocity += new Vector2(0f, 1f); //When the ball hits one of the borders, the speed will not be reduced
}
}
}
The name of the file and the class need to be consistent. I.e. if the class is called Ball, the file should be called Ball.cs or Ball.script, I forget what they're saved as.
If that doesn't work, I found this on the unity forums:
Apparently you need to reset the script?
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 want my enemy to move back to starting position. He follows me until I get out of his range and then he just stops.
Also i want my skeleton to stop for like 5 sec, and then go back to starting point, any ideas ? I never did anything involving time, exept stopping it.
Here is my script for enemy:
Also here is a screenshoot of inspector on the skeleton: enemy
Here is my script for enemy:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class chase : MonoBehaviour
{
public Transform player;
private Animator anim;
public float LookRadius = 15f;
public Transform enemyStartPos;
// Use this for initialization
void Start()
{
anim = GetComponent<Animator>();
this.enemyStartPos.position = this.transform.position;
}
// Update is called once per frame
void Update()
{
if (!PauseMenu.GameIsPaused)
{
if (Vector3.Distance(player.position, this.transform.position) < 15)
{
Vector3 direction = player.position - this.transform.position;
direction.y = 0;
this.transform.rotation = Quaternion.Slerp(this.transform.rotation, Quaternion.LookRotation(direction), 0.1f);
anim.SetBool("isIdle", false);
if (direction.magnitude > 3)
{
this.transform.Translate(0, 0, 0.05f);
anim.SetBool("isWalking", true);
anim.SetBool("isAttacking", false);
}
else
{
anim.SetBool("isAttacking", true);
anim.SetBool("isWalking", false);
}
}
else
{
if (Vector3.Distance(this.enemyStartPos.position, this.transform.position) >1)
{
Vector3 direction = this.enemyStartPos.position - this.transform.position;
direction.y = 0;
this.transform.rotation = Quaternion.Slerp(this.transform.rotation, Quaternion.LookRotation(direction), 0.1f);
anim.SetBool("isIdle", false);
if (direction.magnitude > 1)
{
this.transform.Translate(0, 0, 0.05f);
anim.SetBool("isWalking", true);
anim.SetBool("isAttacking", false);
}
}
else
anim.SetBool("isIdle", true);
anim.SetBool("isAttacking", false);
anim.SetBool("isWalking", false);
}
}
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, LookRadius);
}
}
Before you try to "code" this, think about it in terms of your program design. You have designed and implemented behavior that says "if the bot's position is within X units of the player's position, turn the bot and move it toward the player."
What you describe wanting to do next can be thought of in the same way (which should lead to similar code).. "else, if the bot's position is NOT within X units of the player's position, turn the bot and move it toward [origin]." Note, this means you need to define what [origin] is for the bot. Point being, it makes no difference in the code whether you are moving toward a player or some arbitrary fixed point. The code to move one transform toward another transform is the same.
For "wandering" it's essentially the same thought process: If bot is within X units of [origin] and not following player, pick a random direction and move that way. (better yet, pick a random direction and move that way for some amount of time so your bot doesn't just jitter around origin).
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;
}
}
So I'm making a puzzle based game in which the player must use Trampolines which detect when the player jumps on top of it and bounces them up x amount of height.
However currently at the moment my code isn't working and stating error that
"Cannot modify the return value of 'UnityEngine.Rigidbody.velocity' because it is not a variable."
Wondering if any you guys know what could be done to fix this code and allow it to work to the purpose its needed for.
using UnityEngine;
using System.Collections;
public class small_trampoline_bounce : MonoBehaviour {
bool willBounce = false;
float bounceHeight = 10;
public Transform Player;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
if (willBounce)
{
Player.rigidbody.velocity.y = 0;
Player.rigidbody.AddForce (0, bounceHeight, 0, ForceMode.Impulse);
willBounce = false;
}
}
void OnCollisionEnter (Collision other)
{
if (other.gameObject.name == "Player")
{
willBounce = true;
}
}
}
To address the specific error you are getting - since velocity is a vector, you can't modify the returned vector and expect the stored vector to change. If you really want to set the velocity directly, you need to set the whole vector, not just a portion:
Player.rigidbody.velocity = new Vector3(0, 0, 0);
If you want to maintain previous x and z velocity, you can do this:
vector3 velocity = Player.rigidbody.velocity;
Player.rigidbody.velocity = new Vector3(velocity.x, 0, velocity.z);