I want to ask how to create a room with a minimum player, for example if there is 1 player in the room then the player cannot start the game, if there are 2 players in the room then the game can start. I've tried searching on photon websites but only get Max Player. I try to search in the photon website but just max plyaer in there, can anyone help me.Please help.
This is My Code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
using TMPro;
using Photon.Realtime;
using System.Linq;
public class Launcher : MonoBehaviourPunCallbacks
{
public static Launcher Instance;
[SerializeField] TMP_InputField roomNameInputField;
[SerializeField] TMP_Text errorText;
[SerializeField] TMP_Text roomNameText;
[SerializeField] Transform roomListContent;
[SerializeField] GameObject roomListItemPrefab;
[SerializeField] GameObject PlayerListItemPrefab;
[SerializeField] Transform playerListContent;
[SerializeField] GameObject startGameButton;
[SerializeField] private byte maxPlayersPerRoom = 4;
void Awake(){
Instance = this;
}
// Start is called before the first frame update
void Start()
{
Debug.Log("Connecting to Master");
PhotonNetwork.ConnectUsingSettings();
}
public override void OnConnectedToMaster()
{
Debug.Log("Connected to Master");
PhotonNetwork.JoinLobby();
PhotonNetwork.AutomaticallySyncScene = true;
}
public override void OnJoinedLobby()
{
MenuSetting.Instance.OpenMenu("firstmenu");
Debug.Log("Joined Lobby");
}
public void CreateRoom(){
if(string.IsNullOrEmpty(roomNameInputField.text)){
return;
}
PhotonNetwork.CreateRoom(roomNameInputField.text, new RoomOptions() {MaxPlayers = maxPlayersPerRoom}, null);
MenuSetting.Instance.OpenMenu("Loading");
}
public override void OnJoinedRoom()
{
MenuSetting.Instance.OpenMenu("room");
roomNameText.text = PhotonNetwork.CurrentRoom.Name;
Player[] players = PhotonNetwork.PlayerList;
foreach(Transform child in playerListContent)
{
Destroy(child.gameObject);
}
for(int i = 0; i < players.Count(); i++){
Instantiate(PlayerListItemPrefab, playerListContent).GetComponent<PlayerListItem>().SetUp(players[i]);
}
startGameButton.SetActive(PhotonNetwork.IsMasterClient);
}
public override void OnMasterClientSwitched(Player newMasterClient)
{
startGameButton.SetActive(PhotonNetwork.IsMasterClient);
}
public override void OnCreateRoomFailed(short returnCode, string message)
{
errorText.text = "Room Creation Failed: " + message;
MenuSetting.Instance.OpenMenu("error");
}
public void StartGame()
{
PhotonNetwork.LoadLevel(1);
}
public void LeaveRoom()
{
PhotonNetwork.LeaveRoom();
MenuSetting.Instance.OpenMenu("main_menu");
}
public void JoinRoom(RoomInfo info){
PhotonNetwork.JoinRoom(info.Name);
MenuSetting.Instance.OpenMenu("loading");
}
public override void OnLeftRoom()
{
MenuSetting.Instance.OpenMenu("loading");
}
public override void OnRoomListUpdate(List<RoomInfo> roomList)
{
foreach(Transform trans in roomListContent){
Destroy(trans.gameObject);
}
for(int i = 0; i < roomList.Count; i++){
if(roomList[i].RemovedFromList)
continue;
Instantiate(roomListItemPrefab, roomListContent).GetComponent<RoomListItem>().SetUp(roomList[i]);
}
}
public override void OnPlayerEnteredRoom(Player newPlayer)
{
Instantiate(PlayerListItemPrefab, playerListContent).GetComponent<PlayerListItem>().SetUp(newPlayer);
}
}
Sounds like you just need to check the player count in the OnJoinedRoom method.
Modify the OnJoinedRoom method to check the number of players in the room before allowing the game to start. Here's an example:
public override void OnJoinedRoom()
{
MenuSetting.Instance.OpenMenu("room");
roomNameText.text = PhotonNetwork.CurrentRoom.Name;
Player[] players = PhotonNetwork.PlayerList; //get all the players
foreach(Transform child in playerListContent)
{
Destroy(child.gameObject);
}
for(int i = 0; i < players.Count(); i++){
Instantiate(PlayerListItemPrefab, playerListContent).GetComponent<PlayerListItem>().SetUp(players[i]);
}
// Check the number of players in the room
if (players.Count() >= 2)
{
startGameButton.SetActive(PhotonNetwork.IsMasterClient); //only show the button if there are more than 2 players
}
else
{
startGameButton.SetActive(false); // hide the button if there are less than 2 players
}
}
Hope this helps!
Related
Hi I'm a student in game development, making a game where players can grab certain amounts of coins on each maps, every maps has different type of coins data, like a highscore. But it only saves 1 playerprefs only. Why is that happening?
This is my Scene Management Script;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using TMPro;
public class changeScene : MonoBehaviour
{
public TextMeshProUGUI desertCoinAmount;
public TextMeshProUGUI plainsCoinAmount;
void Update()
{
desertCoinAmount.text = PlayerPrefs.GetInt("DesertCoins").ToString();
plainsCoinAmount.text = PlayerPrefs.GetInt("PlainsCoins").ToString();
}
public void mainMenu()
{
SceneManager.LoadScene("mainMenu");
PlayerPrefs.Save();
}
public void desertLevel()
{
SceneManager.LoadScene("ancientDesertLEVEL");
Time.timeScale = 1f;
PlayerPrefs.Save();
}
public void plainsLevel()
{
SceneManager.LoadScene("Plain Biome");
Time.timeScale = 1f;
PlayerPrefs.Save();
}
public void jungleLevel()
{
SceneManager.LoadScene("Jungle Biome");
Time.timeScale = 1f;
}
}
And This is my PlayerController;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
public class PlayerController : MonoBehaviour
{
public GameObject winPopup, losePopup;
public GameObject heart1, heart2, heart3;
public float gravityScale = 10f;
private Rigidbody rb;
public TextMeshProUGUI coinText;
public AudioSource coinSound;
int coin_sumDesert;
int coin_sumPlains;
int life_sum = 3;
void Start()
{
rb = GetComponent<Rigidbody>();
}
void FixedUpdate()
{
GetComponent<Rigidbody>().AddForce(Physics.gravity * gravityScale, ForceMode.Force);
}
void Update()
{
PlayerPrefs.SetInt("DesertCoins", coin_sumDesert);
PlayerPrefs.SetInt("PlainsCoins", coin_sumPlains);
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Coins")
{
coin_sumDesert++;
coinText.text = coin_sumDesert.ToString();
Destroy(other.gameObject);
coinSound.Play();
}
if (other.gameObject.tag == "PlainsCoins")
{
coin_sumPlains++;
coinText.text = coin_sumPlains.ToString();
Destroy(other.gameObject);
coinSound.Play();
}
if (other.gameObject.tag == "finishLine")
{
winPopup.SetActive(true);
}
if (other.gameObject.tag == "obstacles")
{
Debug.Log("Collide Detected");
life_sum--;
if (life_sum == 2)
{
heart1.SetActive(false);
}
else if (life_sum == 1)
{
heart2.SetActive(false);
}
else if (life_sum == 0)
{
heart3.SetActive(false);
losePopup.SetActive(true);
Time.timeScale = 0.0f;
}
}
}
}
I would appreciate the reply (this is my first time using StackOverflow xD)
If you have multiple PlayerController then obviously they write to the same PlayerPrefs keys.
Whenever you save a player's data, make sure you differentiate them e.g. Score1, Score2, etc.
This is a way among many others to achieve it:
The player, very simple, append index to player pref to differentiate among many:
public sealed class Player : MonoBehaviour
{
private const string ChocolateBarsKey = "ChocolateBars";
[SerializeField]
[HideInInspector]
private int Index;
private int ChocolateBars
{
get => GetInt(ChocolateBarsKey);
set => SetInt(ChocolateBarsKey, value);
}
private int GetInt([NotNull] string key, int defaultValue = default)
{
if (key == null)
throw new ArgumentNullException(nameof(key));
return PlayerPrefs.GetInt($"{key}{Index}", defaultValue);
}
private void SetInt([NotNull] string key, int value)
{
PlayerPrefs.SetInt($"{key}{Index}", value);
}
[NotNull]
internal static Player Create([NotNull] GameObject parent, int index)
{
if (parent == null)
throw new ArgumentNullException(nameof(parent));
var controller = parent.AddComponent<Player>();
controller.name = $"{nameof(Player)} {index}";
controller.Index = index;
return controller;
}
}
The factory, scriptable singleton won't lose state on assembly reload, whereas if you'd used a static int for player count, it would reset itself to zero at assembly reload because static fields are not serialized by Unity.
public sealed class PlayerFactory : ScriptableSingleton<PlayerFactory>
{
[SerializeField]
private int PlayerCount;
[NotNull]
public Player Create(GameObject parent)
{
return Player.Create(parent, ++PlayerCount);
}
}
Now if you don't want to store score data within Player, it'll be another pattern. I leave that to you as an exercise.
using UnityEngine;
using UnityEngine.UI;
public class HealthBarController : MonoBehaviour
{
private GameObject[] heartContainers;
private Image[] heartFills;
public Transform heartsParent;
public GameObject heartContainerPrefab;
private void Start()
{
// Should I use lists? Maybe :)
heartContainers = new GameObject[(int)PlayerStats.Instance.MaxTotalHealth];
heartFills = new Image[(int)PlayerStats.Instance.MaxTotalHealth];
PlayerStats.Instance.onHealthChangedCallback += UpdateHeartsHUD;
InstantiateHeartContainers();
UpdateHeartsHUD();
}
public void UpdateHeartsHUD()
{
SetHeartContainers();
SetFilledHearts();
}
void SetHeartContainers()
{
for (int i = 0; i < heartContainers.Length; i++)
{
if (i < PlayerStats.Instance.MaxHealth)
{
heartContainers[i].SetActive(true);
}
else
{
heartContainers[i].SetActive(false);
}
}
}
void SetFilledHearts()
{
for (int i = 0; i < heartFills.Length; i++)
{
if (i < PlayerStats.Instance.Health)
{
heartFills[i].fillAmount = 1;
}
else
{
heartFills[i].fillAmount = 0;
}
}
if (PlayerStats.Instance.Health % 1 != 0)
{
int lastPos = Mathf.FloorToInt(PlayerStats.Instance.Health);
heartFills[lastPos].fillAmount = PlayerStats.Instance.Health % 1;
}
}
void InstantiateHeartContainers()
{
for (int i = 0; i < PlayerStats.Instance.MaxTotalHealth; i++)
{
GameObject temp = Instantiate(heartContainerPrefab);
temp.transform.SetParent(heartsParent, false);
heartContainers[i] = temp;
heartFills[i] = temp.transform.Find("HeartFill").GetComponent<Image>();
}
}
}
This is the HealthbarController and it works when the damage is a button. Same with heal and addheart but I can't make my enemies do the damage.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DamageScript : MonoBehaviour
{
public void Hurt(float dmg)
{
PlayerStats.Instance.TakeDamage(dmg);
}
}
and this is the damage script for my enemies. How can I make enemy do damage to the player when colliding with the player? I tried with OnCollisionEnter but whenever I put it into the code I only got a bunch of errors.
What if you tried giving your enemy the tag "Enemy" and your player a sphere collider (isTrigger checked).
And then make a take damage script for example:
public class takeDamage : MonoBehaviour
{
private int PlayerHp = 10f;
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag( "Enemy")) {
PlayerHp -= 1;
Debug.Log("Hit, HP: " + PlayerHp);
//Add GUI update function here
}
}
}
and attach it to the player.
Don't forget to scale the sphere collider to the appropriate size.
I am working on a 2D platformer and I am using cinemachine to follow my player.
When a player drops off a platform under -20 y, the player is destroyed and instantiated as a clone to the spawn point as expected, but the camera is not following, as the original player is destroyed: it says missing on the "Follow" Slot.
Is there any way to solve it? I prefer using Destroy and Instantiate as respawning instead of teleporting the original player to the respawn point.
This is the respawn script (GameMaster)
public class GameMaster : MonoBehaviour
{
public static GameMaster gm;
void Start()
{
if (gm == null)
{
gm = GameObject.FindGameObjectWithTag("GM").GetComponent<GameMaster>();
}
}
public Transform playerPrefab, spawnPoint;
public int spawnDelay = 2;
public void RespawnPlayer() {
//yield return new WaitForSeconds(spawnDelay);
Instantiate(playerPrefab, spawnPoint.position, spawnPoint.rotation);
Debug.Log("ADD SPAWN PARITCAL");
}
public static void Killplayer(Player player) {
Destroy(player.gameObject);
gm.RespawnPlayer();
}
}
here is the player script if it is needed
public class Player : MonoBehaviour
{
[System.Serializable]
public class PlayerStats
{
public int Health = 100;
}
public PlayerStats playerStats = new PlayerStats();
public int FallBoundary = -20;
void FixedUpdate()
{
if (transform.position.y <= FallBoundary)
{
DamagePlayer(1000);
}
}
public void DamagePlayer(int damage) {
playerStats.Health -= damage;
if (playerStats.Health<=0)
{
Debug.Log("Kill Player");
GameMaster.Killplayer(this);
}
}
}
You can cache the cinemachine inside your start method and then assign to follow the player at respawn.
Your code will become
using Cinemachine;
public class GameMaster : MonoBehaviour
{
public static GameMaster gm;
public CinemachineVirtualCamera myCinemachine;
void Start()
{
if (gm == null)
{
gm = GameObject.FindGameObjectWithTag("GM").GetComponent<GameMaster>();
}
myCinemachine = GetComponent<CinemachineVirtualCamera>();
}
public Transform playerPrefab, spawnPoint;
public int spawnDelay = 2;
public void RespawnPlayer() {
//yield return new WaitForSeconds(spawnDelay);
var newPlayer = Instantiate(playerPrefab, spawnPoint.position, spawnPoint.rotation);
Debug.Log("ADD SPAWN PARITCAL");
myCinemachine.m_Follow = newPlayer;
}
public static void Killplayer(Player player) {
Destroy(player.gameObject);
gm.RespawnPlayer();
}
}
You must assign the new object to follow like this:
myCinemachine.m_Follow = spawnedPlayer;
I've never done this before and my true understanding of classes are not that good. However, I plan on mastering it after this project! What I'd like to do is create a class to determine enemy type by TAG: Enemy1 or Boss. (I've already designed a system to randomize the Enemy1 stats so no two will be the same. However, here, I just want to learn how to properly setup enemies stats so here's my code)
using System.Collections;
public class Enemies : MonoBehaviour {
public float MaxHp;
public static float Hp;
GameObject enemy = GameObject.Find("Enemy1");
GameObject boss = GameObject.Find("Boss");
void Awake()
{
AssignStats(enemy, MaxHp);
}
public static void AssignStats (GameObject en, float MaxHp)
{
if (en.tag == "Enemy1")
{
MaxHp = 50;
Hp = MaxHp;
Debug.Log(Hp);
}
if (en.tag == "Boss")
{
MaxHp = 500;
Hp = MaxHp;
Debug.Log(Hp);
}
}
}
This code doesn't seem to work. Why?
Enemy Class : Enemy.cs (not Monobehavior)
using UnityEngine;
[System.Serializable]
public class Enemy
{
public EnemyType EnemyType;
public GameObject EnemyPrefab;
public string EnemyTag;
public int MaxHealth;
public int EnemyDamage;
public Vector3 SpawnPos;
private int _currentHealth;
public void Init()
{
_currentHealth = MaxHealth;
}
public void UpdateHealth(int newHealthValue)
{
_currentHealth = newHealthValue;
}
public void ReceiveDamage(int damage)
{
var updatedHealth = _currentHealth - damage;
UpdateHealth(updatedHealth > 0 ? updatedHealth : 0);
}
}
Enemies Class : Enemies.cs that manage all enemies, randomize between enemies
using UnityEngine;
public enum EnemyType
{
Enemy1,
Enemy2,
Enemy3,
Enemy4,
Enemy5,
Boss
}
public class Enemies : MonoBehaviour
{
public Enemy[] AllEnemies;
//Initial Value
public int NumberOfEnemies = 3;
private void Start()
{
InitEnemies(NumberOfEnemies);
}
public void InitEnemies(int howManyEnemies)
{
for(int i= 0; i < howManyEnemies; i++)
{
var randomIndex = Random.Range(0, AllEnemies.Length - 1);
SpawnEnemy(AllEnemies[randomIndex]);
}
}
public void SpawnEnemy(Enemy enemy)
{
Instantiate(enemy.EnemyPrefab, enemy.SpawnPos, Quaternion.identity);
enemy.Init();
}
}
You can see that I assigned all enemies data in the inspector that come up from the array of Enemy in enemies class, it has Enemy Prefab, position, damage etc.
If you have any question, feel free to ask :)
Cheers!
if I understand corrlcy.
you don't need to pass parameters to AssignStats method because the all you need property in the class.
I would use gameObject.tag to get the current append object tag.
if you append to Enemy1 component you will do gameObject.tag == "Enemy1" condition.
if you append to Boss component you will do gameObject.tag == "Boss" condition.
you just append the script to your role component and tag right tag.
using System.Collections;
public class Enemies : MonoBehaviour {
public float MaxHp;
public float Hp;
void Awake()
{
AssignStats();
}
public void AssignStats ()
{
if (gameObject.tag == "Enemy1")
{
MaxHp = 50;
Hp = MaxHp;
Debug.Log(Hp);
}
if (gameObject.tag== "Boss")
{
MaxHp = 500;
Hp = MaxHp;
Debug.Log(Hp);
}
}
}
I would do it like that :
//enum contains all your enemies
public enum EnemyType
{
Enemy1,
Boss
}
public class Enemies : MonoBehaviour
{
//This will be assigned in the inspector
public EnemyType CurrentEnemyType;
//You don't need them to be public since you are hardcoding them.
private float MaxHp;
private float Hp;
void Awake()
{
AssignStats();
}
public void AssignStats()
{
if (gameObject.CompareTag(CurrentEnemyType.ToString()))
{
if (CurrentEnemyType == EnemyType.Enemy1)
{
MaxHp = 50;
Hp = MaxHp;
Debug.Log(Hp);
}
// instead of doing separated if blocks, you need to do if else for less code execution
else if (CurrentEnemyType == EnemyType.Boss)
{
MaxHp = 500;
Hp = MaxHp;
Debug.Log(Hp);
}
/*
More simplified way instead of the if else, if you assume that all your enemies except the boss have 50 hp.
MaxHp = CurrentEnemyType == EnemyType.Boss ? 500 : 50;
Hp = MaxHp;
Debug.Log(Hp);
*/
}
}
}
Cheers!
I have been working on a minecraft clone game in Unity 2017.3 and C# and i started all right but when i worked on the super flat world generation system there began a problem. Inside the World mono behaviour, in the Generate void, there are three nested for loops which should generate a new dirt block on every possible position between 0 and 5
But it only makes a line of dirt block stretching along the Z axis.
Here is the code for PlaceableItem(attached to dirtPrefab) and World(attached to World)
Placeable Item class
using UnityEngine;
using System.Collections;
public class PlaceableItem : MonoBehaviour
{
public string nameInInventory = "Unnamed Block";
public int maxStack = 64;
public bool destructible = true;
public bool explodesOnX = false;
public bool abidesGravity = false;
public bool isContainer = false;
public bool canBeSleptOn = false;
public bool dropsSelf = false;
private Rigidbody rb;
// Use this for initialization
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
if (abidesGravity) {
rb.useGravity = true;
} else {
rb.useGravity = false;
}
}
private void OnDestroy()
{
if (dropsSelf) {
//Drop this gameObject
}
}
public void Explode() {
if (!explodesOnX)
return;
}
public void OnMouseDown()
{
if (isContainer && !canBeSleptOn) {
//Open the container inventory
} else if (canBeSleptOn && !isContainer) {
//Make the character sleep on the item
}
}
private void OnMouseOver()
{
if (Input.GetMouseButtonDown(1) && destructible) {
Destroy(gameObject);
}
}
}
World class
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class World : MonoBehaviour {
public GameObject dirtPrefab;
public bool generateAutomatically = true;
// Use this for initialization
void Start () {
if (generateAutomatically) {
Generate();
}
}
// Update is called once per frame
void Update () {
}
void Generate() {
for (int x = 0; x <= 5; x++) {
for (int y = 0; y <= 5; y++) {
for (int z = 0; z <= 5; z++) {
Instantiate(dirtPrefab, new Vector3(x, y, z), Quaternion.identity);
}
}
}
}
void RemoveAllBlocks() {
foreach (PlaceableItem placeableItem in GetComponentsInChildren<PlaceableItem>()) {
Destroy(placeableItem.gameObject);
}
}
}
Thanks in advance, fellow developers!
Hope this is not a stupid question!
[SOLVED] I realised i had a recttransform somehow attached to my dirt prefab. Removing it and adding a transform instead helped.