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().
Related
I've started learning 3D game development and now I've reached the scenes changing.
I looked over the internet and I couldn't find a solution that I can implement in my project.
At this point, I've two scenes, the street which is the main scene, and a bar scene.
I've created a sliding door that has a collider called portal that triggers the scene switch.
I've managed to swap the scene but when I'm trying to go back to my main scene I respawn and the starting point and not at the bar exit. I've tried to save the player position in a temp variable but that didn't work well. what should I do to make my player start at the store exit ?
public class PlayerMotion : MonoBehaviour
{
//...
public static Vector3 playerPosition; // for respawning use
public static bool respawnNeeded = false;
CharacterController cController;
void Update()
{
if(respawnNeeded)
{
cController.transform.position = new Vector3(StreetToBarFFPortal.tempPosition.x,
StreetToBarFFPortal.tempPosition.y,
StreetToBarFFPortal.tempPosition.z);
respawnNeeded = false;
}
//..... some movement code
playerPosition = new Vector3(cController.transform.position.x, cController.transform.position.y, cController.transform.position.z);
}
public class StreetToBarFFPortal : MonoBehaviour
{
public static Vector3 tempPosition;
private void OnTriggerEnter(Collider other)
{
if(other.CompareTag("Player"))
{
int index = SceneManager.GetActiveScene().buildIndex;
if(index==0)
{
PlayerMotion.respawnNeeded = true;
tempPosition = new Vector3(PlayerMotion.playerPosition.x,
PlayerMotion.playerPosition.y,
PlayerMotion.playerPosition.z-4);
}
index = 1 - index;// 1 transfers to 0 and 0 transfers to 1
SceneManager.LoadScene(index);
}
}
}
I also have a GlobalManeger script with a singeltone design pattern if that helps.
You have two options:
Store it in the DontDestroyOnLoad()
Store it in playerprefs before you load the other scene and load it back when the scene is loaded again. Since you're storing a Vector3 then store the x, y and z like this:
PlayerPrefs.SetFloat("X", playerPosition.x);
PlayerPrefs.SetFloat("Y", playerPosition.y);
PlayerPrefs.SetFloat("Z", playerPosition.z);
and load is like this:
float xpos = PlayerPrefs.GetFloat("X");
float ypos = PlayerPrefs.GetFloat("Y");
float zpos = PlayerPrefs.GetFloat("Z");
playerPosition = new Vector3(xpos, ypos, zpos);
You have tempPosition which as I can see used to store player position before teleportation, am I right?
So I can't see where this tempPosition variable came from. Is it static or from a singletone script? Anyway you need to have this variable through scenes. So it should be in the script that has DontDestroyOnLoad() method;
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.
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?
so the player hits a button to create a box. I need this box to be randomly varying between a few colors. I also need this box to have a tag corresponding to said color. Green box - "greenBlock" tag etc.
I have instantiated the box and then try to change it's material with material.color. It doesn't do anything. I've seen suggestions of sharedMaterial but having tried that found it just ends up changing the color of every game object in the scene. I think I'm fetching the box prefabs renderer correctly? Any help would be appreciated!
Here's what I have so far:
public class ButtonScript : MonoBehaviour
{
public GameObject Box;
public Transform spawnpoint1;
public Transform spawnpoint2;
public Rigidbody2D player;
public Renderer boxRenderer;
[SerializeField]
private Color boxColor;
[SerializeField]
private AudioSource actionSound;
// Update is called once per frame
private void Start()
{
//boxRenderer = Box.gameObject.GetComponent<Renderer>();
boxRenderer = GameObject.Find("Box").GetComponent<Renderer>(); // Find the renderer of the box prefab
}
public void OnTriggerStay2D(Collider2D col)
{
if (player) // If it's the player in the collider trigger
{
if (Input.GetKeyDown(KeyCode.E))
{
Instantiate(Box, spawnpoint1.position, Quaternion.identity);
boxRenderer.material.color = boxColor; // change the color after it is instantiated
actionSound.Play();
}
}
}
}
boxRenderer.material.SetColor("_Color", boxColor);
or
boxRenderer.material.color = new Color(0.5, 0.5, 0.0, 1.0);
And when you instantiate the box, you need to get the render for the box at this point as it is a new object. So:
if (Input.GetKeyDown(KeyCode.E))
{
Box = Instantiate(Box, spawnpoint1.position, Quaternion.identity);
boxRenderer = Box.transform.GetComponent<Renderer>();
boxRenderer.material.color = boxColor; // change the color after it is instantiated
actionSound.Play();
}
Note you have creating a New color and assigning it, you can't modify the color there as it may be used on other objects that are using the same material.
Check out SetColor in the docs, that is setting the shader property called _Color which is the default color element in shaders, you can of course have more depending on the shader.
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.