Spawn in 2 different prefabs over a network Unity PUN 2 - c#

I am making an online multiplayer game. Once both players are ready, 2 DIFFERENT character prefabs are spawned into a room. The problem is that each player's device can't seem to read information about the other player's character.
They're spawned using the following code:
public class GameplayManager : MonoBehaviour
{
public PhotonView view;
public string BlueName;
public string GreenName;
public GameObject BluePlayer;
public GameObject GreenPlayer;
public Vector2 BlueSpawnPos = new Vector2(4.07f, 0.02f);
public Vector2 GreenSpawnPos = new Vector2(3.97f, -0.02f);
public void Start()
{
InstantiatePlayers();
BluePlayer = GameObject.Find("blue soldier(Clone)");
BlueName = PhotonNetwork.LocalPlayer.NickName;
GreenPlayer = GameObject.Find("green soldier(Clone)");
GreenName = PhotonNetwork.LocalPlayer.NickName;
VictoryText = GameObject.Find("VictoryText").GetComponent<Text>();
}
public void InstantiatePlayers()
{
//If player 1:
if (PhotonNetwork.LocalPlayer.ActorNumber == 1)
{
BluePlayer = PhotonNetwork.Instantiate(BlueSoldier.name, BlueSpawnPos, Quaternion.identity);
Debug.Log("bname" + BlueName);
}
//if player 2:
if (PhotonNetwork.LocalPlayer.ActorNumber == 2)
{
GreenPlayer = PhotonNetwork.Instantiate(GreenSoldier.name, GreenSpawnPos, Quaternion.identity);
Debug.Log("gname" + GreenName);
}
}
I run this using the Unity Editor and a build to test. When I check the inspector in the editor, the 'Player' and 'Name' variables for the colour character in the editor have been updated correctly, but that of the other copy of the game are empty (i.e. if the editor is blue, 'BlueName' and 'BluePlayer' will be updated but 'GreenName' will be empty and 'GreenPlayer' will be 'None').
I suspect it's to do with my using if statements to spawn in characters, but I don't know if there's another way of spawning in multiple prefabs for multiple players.

Related

Unity Card Game Change Player Perspective

I am new to C# and Unity.. I am trying to make a simple trading card game (tcg/ccg). For this first phase, I would like to make it to be played locally eg. own-self versus own-self. Would like to know how do we usually do for changing the perspective?
Eg: From the Player1's perspective as below image, once I ended my turn and pass it to player2, I would like to make it remain the same view but by showing player2's Hand card and Field card (also at the same time hide the Player1 hand card).
Below is just part of my code :
UiController.cs
[Header("General")]
[SerializeField] private GameObject BattleCardPrefab;
[Header("Player Detail")]
[SerializeField] private GameObject PlayerDeck;
[SerializeField] private GameObject PlayerHand;
[Header("Enemy Detail")]
[SerializeField] private GameObject EnemyDeck;
[SerializeField] private GameObject EnemyHand;
private void GetUserObject(int _player)
{
UserDeck = _player == 0 ? PlayerDeck.transform : EnemyDeck.transform;
UserHand = _player == 0 ? PlayerHand.transform : EnemyHand.transform;
}
public void InitDeck(int _player, List<Card> _deck)
{
foreach (var card in _deck)
{
GameObject resultCards = Instantiate(BattleCardPrefab, UserDeck);
resultCards.GetComponent<CardDetail>().card = card;
resultCards.GetComponent<CardDetail>().ShowCardBack(_player, resultCards, 0);
}
}
Player.cs
public Player(int _playerIndex, string _deckName)
{
playerIndex = _playerIndex;
deckName = _deckName;
}
public void GameStartSetUp(string _deckName){
//initDeck and extra
//blablabla
}
BattleManager.cs
Player player = new Player(0, "testdeck1");
Player enemy = new Player(1, "testdeck2");
void Start()
{
StartGame();
}
public void StartGame()
{
player.GameStartSetUp();
enemy.GameStartSetUp();
}
Please let me know if need more information from my side, mercy and thanks!
Scene Setup
Create 2 empty game objects, "Player 1 Main View" and "Player 2 Main View".
Use the following steps for both objects:
Parent your camera to the object and zero out the cameras position and rotation.
Move that object (with the camera as child) to where the camera should be.
Rotate the object so the camera view looks correct.
Unparent the camera.
Repeat for other views.
Code
From code we parent the camera to the appropriate view object and zero out the cameras local position and local rotation.
public Camera viewingCamera; // <- assign your camera in inspector
public Transform player1MainView; // <- assign "Player 1 Main View" object in inspector
public Transform player2MainView; // <- assign "Player 2 Main View" object in inspector
public void EnablePlayer1View()
{
EnableView(player1MainView);
// Hide player 2 canvas, flip over cards, etc.
// Show player 1 canvas, show cards, etc.
}
public void EnablePlayer2View()
{
EnableView(player2MainView);
// Hide player 1 canvas, flip over cards, etc.
// Show player 2 canvas, show cards, etc.
}
private void EnableView(Transform viewObject)
{
viewingCamera.transform.SetParent(viewObject);
viewingCamera.transform.localPosition = Vector3.zero;
viewingCamera.transform.localRotation = Quaternion.identity;
}
To switch the view to "Player 1", call EnablePlayer1View(). For "Player 2" call EnablePlayer2View().

Unity - Cant access reference to another script

I am trying to learn how does Unity work and I now struggle with problem that I cannot access script from another script. I was searching on Interner for couple hours, I have tried many options but nothing helped.
I have 2 scripts.
CoinSpawn.cs - attached to Player (I would change it to other object but I dont know yet to which one, because its something that runs in background so it really dont need to be on player)
CollectingCoin.cs - attached to Coin (Coin is object, that its not on game scene on the start, it spawns randomly)
CoinSpawn is script that randomly spawn Instantiate of object Coin. I want to change value of CoinSpawn.currentCoinOnScreen from CollectingCoin. I ve tried
CoinSpawn test = GameObject.Find("CoinSpawn").GetComponent<CoinSpawn>();
and it doesnt work. I also have my both scripts in the same asset folder. What am I doing wrong? Thank you
CoinSpawn.cs
public class CoinSpawn : MonoBehaviour
{
public GameObject coin;
public int maximumCoinPerScreen = 10;
public int currentCoinOnScreen = 0;
private int randomNumber;
private Vector2 spawnPosition;
private void Update()
{
randomNumber = Random.Range(1, 1000);
if(randomNumber >= 0 && randomNumber <= 1 && currentCoinOnScreen != maximumCoinPerScreen)
{
currentCoinOnScreen++;
float spawnY = Random.Range
(Camera.main.ScreenToWorldPoint(new Vector2(0, 0)).y, Camera.main.ScreenToWorldPoint(new Vector2(0, Screen.height)).y);
float spawnX = Random.Range
(Camera.main.ScreenToWorldPoint(new Vector2(0, 0)).x, Camera.main.ScreenToWorldPoint(new Vector2(Screen.width, 0)).x);
spawnPosition = new Vector2(spawnX, spawnY);
GameObject coinObject = Instantiate(coin, spawnPosition, Quaternion.identity);
}
}
}
CollectingCoin.cs
public class CollectingCoin : MonoBehaviour
{
UnityEngine.UI.Text Coins;
public static int totalCoins = 0;
private void Start()
{
Coins = GameObject.Find("Score").GetComponent<UnityEngine.UI.Text>();
}
void OnTriggerEnter2D(Collider2D c2d)
{
if (c2d.CompareTag("Player"))
{
totalCoins++;
Destroy(gameObject);
Coins.text = "COINS: " + totalCoins.ToString();
// TESTING
CoinSpawn test = GameObject.Find("CoinSpawn").GetComponent<CoinSpawn>();
CoinSpawn test2 = GetComponent<CoinSpawn>();
}
}
}
GameObject.Find("CoinSpawn").GetComponent<CoinSpawn>();
Searches for a GameObject with the name CoinSpawn. Since you told us this component is rather attached to the player object it makes sense that it isn't found.
GetComponent<CoinSpawn>();
searches for a CoinSpawn component on the very same object your CollectingCoin is attached to. From your description this clearly also isn't the case.
Since you say the CoinSpawn is attached to the player then you probably rather want to get the component from
void OnTriggerEnter2D(Collider2D c2d)
{
if (c2d.CompareTag("Player"))
{
...
// rather get the component on the player object you collided with
CoinSpawn test = c2d.GetComponent<CoinSpawn>();
}
}
Alternatively assuming there is only one single instance of CoinSpawn in your scene anyway and not necessarily on your player you could use FindObjectOfType
CoinSpawn test = FindObjectOfType<CoinSpawn>();
First of all, Do not ever use GameObject.Find(), its very expensive as it will go through all game objects in your scene to find the object. and this not a performance wise.
There are many ways to do so.
Easyest one:
Add both script to same gameobject as component.
Make a global variable CoinSpawn inside CollectingCoin script and then use [serializedFiled] tag on top of it, by this way, you can drag and drop the reference in the editor before you start play. and you can access it the way you want.
2nd way:
Is same as first one, but instead of serializedFiled, just cache it at the beginning by using GetComponent.
Just make sure you have both scripts attached to the same gameobject.
public class CollectingCoin : MonoBehaviour
{
UnityEngine.UI.Text Coins;
public static int totalCoins = 0;
CoinSpawn coinSpawn;
private void Start()
{
coinSpawn = GetComponent<CoinSpawn>();
Coins = GameObject.Find("Score").GetComponent<UnityEngine.UI.Text>();
}
void OnTriggerEnter2D(Collider2D c2d)
{
if (c2d.CompareTag("Player"))
{
totalCoins++;
Destroy(gameObject);
Coins.text = "COINS: " + totalCoins.ToString();
// DO Whaterver you want with coinSpawn here
}
}
}

Different game objects with their respective collider that comes with different score

I have a Game Manager script with a Ball Class attached in it that consists of different information of balls and another script that attached to the ball (game object) to detect collision. (Two scripts in total) So when the player collides with the balls, their respective scores will be counted.
public string[] ballColor;
public int[] score;
Assume there are three balls at the beginning and here are the inputs:
Green Ball - 2 pts
Red Ball - 3 pts
Yellow Ball - 5 pts
Here is the collision script attached to the game object:
public void Start()
{
GameObject gameManager = GameObject.Find("GameManager");
GameManager Ball = gameManager.GetComponent<GameManager>();
ball_color = Ball.ballColor[count];
ball_score = Ball.score[count];
}
public void OnPointerClick(PointerEventData eventData)
{
if (ball_color.Contains(gameObject.tag))
{
//Debug.Log("Yes");
score = ball_score;
GameManager.instance.PlayerScore += score;
Destroy(gameObject);
count = 0;
}
else
{
//Debug.Log("No");
count++;
}
}
Game Manager script:
private int playerScore;
public string[] animalName;
public int[] animalScore;
public int PlayerScore
{
get
{
return playerScore;
}
set
{
playerScore = value;
scoreText.SetText("Score: " + playerScore);
}
}
I realize there is a communication problem between these two scripts.
The game manager script is only passing the first value in the string array to the collision script and not updating afterwards, which means the point is always stuck on the first one.
The same goes for the ball_color information, only the first value in the string array is passed to the collision script and not updating afterwards, which means the color is always stuck on the first one.
As I would like to keep changing the points and colors of the balls whenever I want in the Unity editor without entering the visual studio afterwards.
How do I make the collision script gets the correct points of their respective balls without attaching individual scoring script onto each single game object and also without repeating the same if(gameobject.tag == "Green") comparison?

Shooting laser continuously in Unity

I'm trying to create a falling word game like z-type. Once the game starts a few words are displayed on the screen. When the user types a letter and if that matches with the first letter of any of the words displayed, an activeWord tag is added to the word. I have also created a laser script that checks if the tag is active and when that happens, it shoots the laser.
What's happening right now is that the laser is shot only once i.e when the first letter matches but doesn't shoot a laser when the remaining words are typed.
This is the laser script:
using UnityEngine;
using UnityEngine.UI;
public class Laser : MonoBehaviour {
public float speed = 10.0f;
private Vector3 laserTarget;
private void Update () {
GameObject activeWord = GameObject.FindGameObjectWithTag("activeWord");
if (activeWord && activeWord.GetComponent<Text>().text.Length > 0) {
laserTarget = activeWord.transform.position; // find position of word
transform.Translate(laserTarget * speed * Time.deltaTime); // shoot the laser
}
}
}
I'm also adding the code that I use in the display/UI field.
public void RemoveLetter() {
/* remove the first letter if its correct and so
on for the remaining letters. change the color of the word to red and add
the "activeddWord" tag.*/
text.text = text.text.Remove(0, 1);
text.color = Color.red;
text.gameObject.tag = "activeWord";
}
public void RemoveWord() { // destroy the word once all the letters match
Destroy(gameObject);
}
Can someone please have a look at the code and tell me where I'm making a mistake.
I think you have to reset the position of your laser if it reaches the target:
public float speed = 10.0f;
private Vector3 laserTarget;
private Vector3 laserOrigin;
private void Start () {
// save laser's origin position
laserOrigin = transform.position;
}
private void Update () {
GameObject activeWord = GameObject.FindGameObjectWithTag("activeWord");
if (activeWord && activeWord.GetComponent<Text>().text.Length > 0)
{
laserTarget = activeWord.transform.position; // find position of word
transform.Translate(laserTarget * speed * Time.deltaTime); // shoot the laser
float distance = Vector3.Distance(laserTarget , transform.position);
if(distance < 0.05f){ // I don't know your scaling, perhaps change the limit here!
transform.position = laserOrigin;
}
}
}
Here is one way you can do this using Instantiate() and prefabs. The benefit of this method is that it is scalable. You can create multiple lasers with minimal tweaking. Please note that to use multiple lasers you will have to remove WaitForThisLaserDestroyed;.
To get this to work you will have to start by changing your laser gameObject into a prefab and adding this script onto it:
https://docs.unity3d.com/Manual/Prefabs.html
public class Laser : MonoBehaviour
{
public float speed = 10.0f;
public Vector3 laserTarget;
public float destroyLaserAfterTime = 3f;
private void Update ()
{
transform.Translate(laserTarget * speed * Time.deltaTime);
}
}
And then on some arbitrary other object. For example an empty game object in the same scene:
public class LaserInitializer : MonoBehaviour
{
public GameObject laserPrefab;
public GameObject laserOrigin;
private GameObject WaitForThisLaserDestroyed;
private void Update ()
{
GameObject activeWord = GameObject.FindGameObjectWithTag("activeWord");
if (WaitForThisLaserDestroyed == null && activeWord && activeWord.GetComponent<Text>().text.Length > 0)
{
CreateLaser(activeWord);
}
}
private void CreateLaser(GameObject activeWord)
{
GameObject activeLaser = Instantiate(laserPrefab, laserOrigin.Transform.Position, Quaternion.identity) as GameObject;
Laser laserScript = activeLaser.GetComponent<Laser>();
laserScript.laserTarget = activeWord.transform.position;
WaitForLaserDestroyed = activeLaser;
Destroy(activeLaser, destroyLaserAfterTime);
}
}
To explain the code:
The Laser prefab has its own script for moving towards the word, and as soon as it exists and has the target passed to it, it will move towards the active word.
Somewhere else in the scene you have a game object that exists to hold the second script. Lets call it the "controller game object". It checks to see if the words are "active", as per the earlier design. When a word is active this script creates exactly one laser, and tells it to target the active word.
You have another gameobject (this can be the same one as the controller game object), that marks the origin of the laser. You can do this in other ways, but I thought that using a game object to mark the start point would be an easy way for beginners.

How do I access an updated variable between two scripts in Unity with C#?

Hopefully this isn't too much detail, I'm not used to asking programming questions.
I'm attempting to do the 3D Video Game Development with Unity 3D course that's on Udemy, though using C# instead of Javascript. I just finished up the tutorial that involves creating a space shooter game.
In it, a shield is created by the user when pressing a button. The shield has a "number of uses" variable that does not actually get used by the time the tutorial has finished. I'm trying to add it in, and have successfully managed to implement it so that with each use, we decrease the number of uses remaining, and no longer are able to instantiate the shield once that number is <=0.
This variable is stored on the player, and if I print it from the player, it returns the value I would expect.
However, I'm using a separate SceneManager.cs (this is where the tutorial placed the lives, and score, and timer variables ) where I print numbers into the GUI. My problem is that I cannot get my number of uses variable to stay current when I try to print it from the scene manager... it registers the initial value, but doesn't update after that.
Here is the Player Script
using UnityEngine;
using System.Collections;
public class player_script : MonoBehaviour {
// Inspector Variables
public int numberOfShields = 2; // The number of times the user can create a shield
public Transform shieldMesh; // path to the shield
public KeyCode shieldKeyInput; // the key to activate the shield
public static bool shieldOff = true; // initialize the shield to an "off" state
public int NumberOfShields
{
get{return numberOfShields;}
set{numberOfShields = value;}
}
// Update is called once per frame
void Update()
{
// create a shield when shieldKey has been pressed by player
if (Input.GetKeyDown (shieldKeyInput)) {
if(shieldOff && numberOfShields>0)
{
// creates an instance of the shield
Transform clone;
clone = Instantiate (shieldMesh, transform.position, transform.rotation) as Transform;
// transforms the instance of the shield
clone.transform.parent = gameObject.transform;
// set the shield to an on position
shieldOff = false;
// reduce the numberOfShields left
numberOfShields -=1;
}
}
print ("NumberOfShields = " + NumberOfShields);
}
public void turnShieldOff()
{
shieldOff = true;
}
}
when I run "print ("NumberOfShields = " + NumberOfShields);" I get the value I expect. (astroids trigger the turnShieldOff() when they collide with a shield.
Over in my Scene Manager however... this is the code I'm running:
using UnityEngine;
using System.Collections;
public class SceneManager_script : MonoBehaviour {
// Inspector Variables
public GameObject playerCharacter;
private player_script player_Script;
private int shields = 0;
// Use this for initialization
void Start ()
{
player_Script = playerCharacter.GetComponent<player_script>();
}
// Update is called once per frame
void Update ()
{
shields = player_Script.NumberOfShields;
print(shields);
}
// GUI
void OnGUI()
{
GUI.Label (new Rect (10, 40, 100, 20), "Shields: " + shields);
}
}
Any idea what I'm doing wrong that prevents shields in my SceneManager script from updating when NumberOfShields in my player_script updates?
I think you might have assigned a prefab into playerCharacter GameObject variable instead of an actual in game unit. In this case it will always print the default shield value of prefab. Instead of assigning that variable via inspector try to find player GameObject in Start function. You can for example give your player object a tag and then:
void Start() {
playerCharacter = GameObject.FindGameObjectWithTag("Player");
}

Categories