Changing card position - unity3d

I am creating a card game in unity. I have 4 cards and instantiated them randomly 16 times on 4 zones when a button is clicked. Each zone contains 4 random cards. I want to know how can I know which card is in which zone and then change the position of that particular card to another zone.
This is the script attached to each of my card:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Click : MonoBehaviour
{
public GameObject Canvas;
public GameObject MyArea;
private bool isDragging=false;
private bool isOverBottomArea=false;
private GameObject BottomArea;
private GameObject startParent;
private Vector2 startPosition;
private void Awake()
{
MyArea=GameObject.Find("MyArea");
Canvas=GameObject.Find("Canvas");
}
// Update is called once per frame
void Update()
{
if (isDragging)
{
transform.position= new Vector2(Input.mousePosition.x,Input.mousePosition.y);
transform.SetParent(Canvas.transform, true);
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
isOverBottomArea=true;
BottomArea=collision.gameObject;
}
private void OnCollisionExit2D(Collision2D collision)
{
isOverBottomArea=false;
BottomArea=null;
}
public void StartDrag()
{
startParent= transform.parent.gameObject;
startPosition=transform.position;
if(startParent != MyArea)
{
isDragging=false;
}
else
{
isDragging=true;
}
}
public void EndDrag()
{
isDragging=false;
if (isOverBottomArea && startParent==MyArea )
{
transform.SetParent(BottomArea.transform, false);
}
else
{
transform.position=startPosition;
transform.SetParent(startParent.transform, false);
}
}
}
And this is the script attached to the button which on clicking instantiate the 16 cards:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DrawCards : MonoBehaviour
{
public GameObject Card1;
public GameObject Card2;
public GameObject Card3;
public GameObject Card4;
public GameObject MyArea;
public GameObject LeftArea;
public GameObject RightArea;
public GameObject BottomArea;
List<GameObject> cards = new List<GameObject>();
void Start()
{
cards.Add(Card1);
cards.Add(Card2);
cards.Add(Card3);
cards.Add(Card4);
}
int count1 = 0;
public void OnClick()
{
count1++;
if (count1 == 1)
{
for (var i=0; i<4; i++)
{
GameObject playerCard = Instantiate(cards[Random.Range(0, cards.Count)], new Vector3(0,0,0), Quaternion.identity);
playerCard.transform.SetParent(MyArea.transform, false);
GameObject leftCard = Instantiate(cards[Random.Range(0, cards.Count)], new Vector3(0,0,0), Quaternion.identity);
leftCard.transform.SetParent(LeftArea.transform, false);
GameObject rightCard = Instantiate(cards[Random.Range(0, cards.Count)], new Vector3(0,0,0), Quaternion.identity);
rightCard.transform.SetParent(RightArea.transform, false);
GameObject bottomCard = Instantiate(cards[Random.Range(0, cards.Count)], new Vector3(0,0,0), Quaternion.identity);
bottomCard.transform.SetParent(BottomArea.transform, false);
}
}
}
}

I would consider storing the values to each of your 4 zones with card data to be utilized later. I do not exactly know what a zone is or what it means if there can be more than 4 cards to a zone, etc.
Here is a very general approach to having zones, cards and a zone manager. I can help clarify any details as to what the code does but I commented it quite extensively. I did not integrate it into your code but rather it should be used as a template by you to do so. The code I am providing is untested and should not just be copy-pasted as your question is still rather general. I can explain anything further if you need.
Card class on each moveable card
// this class is on each of your cards
public class Card : MonoBehaviour,
{
// current index of this card
private int cardIndex = -1;
// stores the current zone this card is in
private Zone currentZone = null;
// init this object with the zone it is in and the card index it has
public void InitCard(Zone zone, int idx)
{
cardIndex = idx;
currentZone = zone;
}
// I am using the same EndDrag - I am assuming you are using an editor component of EventTrigger with an
// OnBeginDrag and OnEndDrag instead of implementing the IHandlers in code
public void EndDrag()
{
// determine if we are over a zone - I am going to use the MouseInput as with a drag,
// you would be dragging the object using the cursor or a finger
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
// only look for the layer of the Zone objects
// make sure to add a layer to just the zones and make sure
// it is spelled / case-sensative so exactly the same
int layer_mask = LayerMask.GetMask("ZoneLayer");
// if we hit a zone, determine if it is a new zone and to move the card
if(Physics.Raycast (ray, out hit, Mathf.Infinity, layer_mask))
{
Zone newZone = hit.gameObject.GetComponent<Zone>();
// we hit a zone, is it the same zone?
if(currentZone == newZone)
{
// handle however you want to do a case where it is the same zone
// reset the card position possibly
return;
}
// we are in a new zone, so need to remove this card from the old zone and add it to the new one
// I am not sure how you are handling max card counts in each zone, etc. so this is very basic implementation
currentZone.RemoveCard(this);
newZone.AddCard(this);
// update our local zone reference
currentZone = newZone;
}
}
// setters / getters
public int CardIndex
{
set{cardIndex = value;}
get{return cardIndex;}
}
public Zone CurrentZone
{
set{currentZone = value;}
get{return currentZone;}
}
}
ZoneManager should be on an object that manages your individual zones, so possibly the Canvas object that all of this data is on
// this class will manage each one of your zones
public class ZoneManager : MonoBehaviour
{
// list of our prefabs
[SerializeField] private List<GameObjects> allCardsPrefabList = new List<GameObjects>();
// create a list of zone data of size 4
[SerializeField] private Zone[] myZoneData = new Zone[MAX_ZONE_COUNT];
// max number of zones we can have
private const int MAX_ZONE_COUNT = 4;
// max number of cards we can have on Init
private const int MAX_INIT_CARD_COUNT = 4;
// init our zones in Start() - as I do not know if you are working with UI
// and UI components are only set before Start but not Awake
private void Start()
{
// iterate over each zone
for(int x = 0; x < MAX_ZONE_COUNT; ++x)
{
// iterate over 4 new cards
for(int y = 0; y < MAX_INIT_CARD_COUNT; ++y)
{
// I am using this overload of the Instantiate
// public static Object Instantiate(Object original, Transform parent, bool instantiateInWorldSpace);
// grab our new idx
int newCardIdx = Random.Range(0, cards.Count);
// spawn a new card childed to our zone at the new card idx
Card newCard = Instantiate(allCardsPrefabList[newCardIdx], myZoneData[x].transform, false).GetComponent<Card>();
// init our card zone / idx
newCard.Init(myZoneData[x], newCardIdx);
// add this card to a zone
myZoneData[x].AddNewCard(newCard);
}
}
}
}
The Zone class should be on each of your individual zones
// this is a script that will house each of your zones and will be used to add cards to your zone
public class Zone : MonoBehaviour
{
private const int MAX_INIT_CARDS = 4;
// list of our current cards of this zone
public List<CardData> zoneCards = new List<CardData>();
// called from when a cast is detected on a drop
public void AddNewCard(Card newCard)
{
zoneCards.Add(newCard);
}
// remove a card instance from our list as it is no longer apart of this zone
public void RemoveCard(Card oldCard)
{
zoneCards.Remove(oldCard);
}
}
Again, this is a general idea, not a straightforward answer. Use it as a template to add to your existing code.

Related

How do I save the score every time the scene resets and make it my new highscore when the score is higher than the highscore?

I'm currently recreating Jetpack Joyride and I'm having trouble with adding a highscore. I currently track my score on where my player is on the Y position and placing the score on a canvas. So I was wondering how do I save the score every time the score is higher that the highscore and when the scene resets.
This is what I currently use to track Y position of my player:
public class Score : MonoBehaviour
{
public Transform player;
public Text scoreText;
void Update()
{
scoreText.text = player.position.x.ToString("0" + "M");
}
}
There is multiple ways to implement high score.
You can use PlayerPrefs to store data in each scene and then load the data from storage and save again if the latest score is higher than the previous one.
You can create a global object which is not destroyed when new scenes load. In that object, you can attach a high score script that will keep track of the score.
Example Script for the 2nd Option
using UnityEngine;
using System.Collections;
public class MyCustomScript : MonoBehaviour {
public int score = 0;
void Awake()
{
GameObject[] objs = GameObject.FindGameObjectsWithTag("global");
if (objs.Length > 1)
{
Destroy(this.gameObject);
}
DontDestroyOnLoad(this.gameObject);
}
// Update is called once per frame
void Update () {
if( score < **getScore()** ){
score = getScore();
}
}
You should be saving that value when your player lose the game look at the comments I added to understand.
public class Score : MonoBehaviour
{
public Transform player;
public Text scoreText;
public Text highScoreText;
public float score;
bool lost;
private void Start()
{
HighScoreCheck();
}
void Update()
{
score = player.position.x;
//this is storing the score
scoreText.text = score.ToString("0" + "M");
highScoreText.text = PlayerPrefs.GetFloat("HighScore").ToString();
//this is showing the highest score recorded
LoseCheck();
}
private void HighScoreCheck()
{
if (!PlayerPrefs.HasKey("HighScore"))
//checking if this key has any value saved to it
{
Debug.Log("No High Score recorded Yet");
}
else
{
Debug.Log("HighScore is : " + PlayerPrefs.GetFloat("HighScore"));
}
}
private void LoseCheck()
{
if (lost)
{
if (score> PlayerPrefs.GetFloat("HighScore"))
{
PlayerPrefs.SetFloat("HighScore", score);
//this is how you save a float/int into a key that is stored in the device
}
else
{
Debug.Log("No new high score");
}
}
}
}

Character selection and uploading characters to the scene with photon pun2

I'm trying to develop a 3D multiplayer game with Unity. I don't know much about Photon, there are similar questions to the one I'm going to ask, but I still haven't found a solution. I will be glad if you help.
I have two scenes named "menu" and "game". In the menu scene, users make character selection after authenticating with playfab. After completing the selection, they connect to the lobby and set up a room and load the game scene. So far everything is successful. However, when the game scene is loaded, I have difficulty loading the characters selected by the users into the scene.
Here is the code file where I make the users choose their characters:
public class choose : MonoBehaviour {
private GameObject[] characterList;
private int index;
PhotonView PV;
private void Awake()
{
PV = GetComponent<PhotonView>();
}
private void Start()
{
index = PlayerPrefs.GetInt("CharacterSelected");
characterList = new GameObject[transform.childCount];
for (int i=0; i< transform.childCount; i++)
characterList[i] = transform.GetChild(i).gameObject;
foreach (GameObject go in characterList)
go.SetActive (false);
if (characterList [index])
characterList [index].SetActive (true);
}
public void ToggleLeft(){
characterList [index].SetActive (false);
index--;
if (index < 0)
index = characterList.Length - 1;
characterList [index].SetActive (true);
}
public void ToggleRight(){
characterList [index].SetActive (false);
index++;
if (index == characterList.Length)
index = 0;
characterList [index].SetActive (true);
}
public void kaydetbuton() {
PlayerPrefs.SetInt ("CharacterSelected", index);
} }
Here is the code file where I make the characters in the game move:
public class control : MonoBehaviour {
public FixedJoystick LeftJoystick;
private GameObject leftjoystick;
public FixedButton Button;
private GameObject button;
public FixedTouchField TouchField;
private GameObject touchField;
protected ThirdPersonUserControl Control;
protected float CameraAngle;
protected float CameraAngleSpeed = 0.2f;
PhotonView PV;
void Awake()
{
PV = GetComponent<PhotonView>();
}
void Start()
{
if (!PV.IsMine)
return;
Control = GetComponent<ThirdPersonUserControl>();
leftjoystick = GameObject.Find("Fixed Joystick");
if (leftjoystick != null)
{
LeftJoystick = leftjoystick.GetComponent<FixedJoystick>();
}
button = GameObject.Find("Handle (1)");
if (button != null)
{
Button = button.GetComponent<FixedButton>();
}
touchField = GameObject.Find("tfield");
if (touchField != null)
{
TouchField = touchField.GetComponent<FixedTouchField>();
}
}
void FixedUpdate() {
if (PV.IsMine)
{
Control.m_Jump = Button.Pressed;
Control.Hinput = LeftJoystick.Direction.x;
Control.Vinput = LeftJoystick.Direction.y;
CameraAngle += TouchField.TouchDist.x * CameraAngleSpeed;
Camera.main.transform.position = transform.position + Quaternion.AngleAxis(CameraAngle, Vector3.up) * new Vector3(1, 2, 3);
Camera.main.transform.rotation = Quaternion.LookRotation(transform.position + Vector3.up * 2f - Camera.main.transform.position, Vector3.up);
}
} }
There is a game object named "karakteryükle" in the menu scene. The code file named "choose" is in this object. There are 4 characters in this game object. Each character has a code file named "control", photon view, photon transform view, animator view component. And the game object named "karakteryükle" is also available as a prefab in the Resources folder.
I am sharing the picture of the components loaded on each character
I shared a picture of the game object named "karakter yükle"
I'm trying to load "karakter yükle" when the scene named game is loaded
PhotonNetwork.Instantiate("karakteryükle", new Vector3((float)-0.43, (float)1.1, (float)-25.84), Quaternion.identity, 0, null);
Result: The "karakteryükle" is loaded onto the stage, but the same character is loaded for each player, the character chosen by each player is not loaded.
I need your opinion on this.
Each player only knows their own setting for the index, because they use the value set in PlayerPrefs.
private void Start()
{
index = PlayerPrefs.GetInt("CharacterSelected");
}
This works for our local player, no problem there. But what happens when a different player enters the scene.
The playerObject is spawned on each client.
Each client handles that playerObject locally (this is the reason IsMine exist).
Player1 executes index = PlayerPrefs.GetInt(..) on their copy of Player2.
What you can do is send a buffered RPC to set the selected character on those remote copies. We want to buffer the rpc so new players change their remote copies of everyone to the appropriate character.
myPhotonView.RPC("SetCharacterIndex", RpcTarget.OthersBuffered, index);
and the corresponding RPC method
[PunRPC]
private void SetCharacterIndex(int index)
{
// Disable other characters and enable the one at this index
}
In the end you end up with something like
void Start()
{
characterList = new GameObject[transform.childCount];
for (int i=0; i< transform.childCount; i++)
{
characterList[i] = transform.GetChild(i).gameObject;
characterList[i].SetActive(false);
}
if (isMine)
{
index = PlayerPrefs.GetInt("CharacterSelected");
// Notify all remote copies of us to change their index
//
photonView.RPC("SetCharacterIndex", RpcTarget.OthersBuffered, index);
// Set the index locally
//
SetCharacterIndex(index);
}
}
[PunRPC]
private void SetCharacterIndex(int index)
{
if (characterList [index])
characterList [index].SetActive (true);
}
Hopefully that helps clear up the reason this happens (networking can be confusing at times).

Using a timer in conjunction with 2 push buttons from arduino

So I am using two push buttons (connected to an Arduino Uno) as an input to my game. The player has to push down both buttons at the same time for the character to move in the game. I want the player to hold down the buttons for a different amount of time in each level. I have a working Arduino and a working Unity timer and player script, but am not able to get the code to do what I want. What I basically want is that only when the player presses the buttons down, does the timer start counting down. Right now, the timer starts as soon as the scene begins. I know that I somehow have to reference the timer script to the button object, I have tried this but it still doesn't work. Note that the timer UI does have a Timer tag on it. I have also referenced the Player Controller script in the Timer script. Right now, Its giving me a range of errors. I have attached an image depicting these errors.error image
The Timer script:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class Timer : MonoBehaviour
{
//int startTime = 0;
public bool buttonPressed = false;
public int timeLeft;
public Text countdownText;
GameObject Character;
void Awake()
{
Character = GameObject.FindWithTag("Player");
}
public void Start()
{
//StartCoroutine("LoseTime");
BeginTimer();
}
void Update()
{
countdownText.text = ("Time Left = " + timeLeft);
if (timeLeft <= 0)
{
//StopCoroutine("LoseTime");
//countdownText.text = "Times Up!";
Invoke("ChangeLevel", 0.1f);
}
}
public void BeginTimer()
{
Character.GetComponent<PlayerController>().Update();
//gameObject.GetComponent<MyScript2>().MyFunction();
if (buttonPressed == true )
{
StartCoroutine("LoseTime");
}
else if (buttonPressed == false)
{
StopCoroutine("LoseTime");
}
}
IEnumerator LoseTime()
{
while (true)
{
yield return new WaitForSeconds(1);
timeLeft--;
}
}
void ChangeLevel()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}
The Player Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO.Ports;
public class PlayerController : MonoBehaviour
{
SerialPort sp = new SerialPort("\\\\.\\COM4", 9600);
//player == GameObject.FindWithTag("Player").GetComponent<>();
public float Speed;
public Vector2 height;
public float xMin, xMax, yMin, yMax;
public bool buttonPressed = false;
GameObject Character;
public void Awake()
{
Character = GameObject.FindWithTag("Player");
}
public void Start()
{
if (!sp.IsOpen)
{ // If the erial port is not open
sp.Open(); // Open
}
sp.ReadTimeout = 1; // Timeout for reading
}
public void Update()
{
if (sp.IsOpen)
{ // Check to see if the serial port is open
try
{
string value = sp.ReadLine();//To("Button"); //Read the information
int button = int.Parse(value);
//float amount = float.Parse(value);
//transform.Translate(Speed * Time.deltaTime, 0f, 0f); //walk
if (button == 0) //*Input.GetKeyDown(KeyCode.Space*/) //jump
{
buttonPressed = true;
Character.GetComponent<Rigidbody2D>().AddForce(height, ForceMode2D.Impulse);
Character.GetComponent<Rigidbody2D>().position = new Vector3
(
Mathf.Clamp(GetComponent<Rigidbody2D>().position.x, xMin, xMax),
Mathf.Clamp(GetComponent<Rigidbody2D>().position.y, yMin, yMax)
);
Timer tmr = GameObject.Find("Timer").GetComponent<Timer>();
tmr.BeginTimer();
}
}
catch (System.Exception)
{
}
}
void ApplicationQuit()
{
if (sp != null)
{
{
sp.Close();
}
}
}
}
}
I think the problem may be with how I am referencing the scripts in each other.
In your timer you have a quite strange mixup of Update and Coroutine. Also note that BeginTimer is called exactly once! You also shouldn't "manually" call Update of another component.
I wouldn't use Update at all here. Simply start and stop a Coroutine.
The Timer script should only do the countdown. It doesn't have to know more:
public class Timer : MonoBehaviour
{
public int timeLeft;
public Text countdownText;
private bool timerStarted;
public void BeginTimer(int seconds)
{
// Here you have to decide whether you want to restart a timer
timeLeft = seconds;
// or if you rather want to continue counting down
//if(!timerStarted) timeLeft = seconds;
StartCoroutine(LoseTime());
}
public void StopTimer()
{
StopAllCoroutines();
}
private IEnumerator LoseTime()
{
timerStarted = true;
while (timeLeft > 0)
{
yield return new WaitForSeconds(1);
timeLeft --;
countdownText.text = $"Time Left = {timeLeft}";
}
// Only reached after the timer finished and wasn't interrupted meanwhile
// Using Invoke here is a very good idea since we don't want to interrupt anymore
// if the user lets go of the button(s) now
Invoke(nameof(ChangeLevel), 0.1f);
}
void ChangeLevel()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);
}
}
In general avoid to use Find at all. If anyhow possible already reference things in the Inspector! If needed you can use Find but only once! What you never want to do is use any of the Find and GetComponent variants repeatedly - rather store the reference the first time and re-use it - and especially not in Update no a per frame basis. They are very expensive calls!
public class PlayerController : MonoBehaviour
{
public float Speed;
public Vector2 height;
// I prefer to use Vector2 for such things
public Vector2 Min;
public Vector2 Max;
public bool buttonPressed = false;
// Already reference these via the Inspector if possible!
public Rigidbody2D Character;
public Timer timer;
public Rigidbody2D _rigidbody;
private SerialPort sp = new SerialPort("\\\\.\\COM4", 9600);
private void Awake()
{
FetchReferences();
}
// This is my "secret" tip for you! Go to the component in the Inspector
// open the ContextMenu and hit FetchReferences
// This already stores the references in the according fields ;)
[ContextMenu("FetchReferences")]
private void FetchReferences()
{
if(!Character)Character = GameObject.FindWithTag("Player"). GetComponent<Rigidbody2D>();
if(!timer) timer = GameObject.Find("Timer").GetComponent<Timer>();
}
private void Start()
{
if (!sp.IsOpen)
{
sp.Open(); // Open
}
sp.ReadTimeout = 1;
}
private void Update()
{
// I wouldn't do the serialport open check here
// your if block simply silently hides the fact that your application
// doesn't work correctly! Rather throw an error!
try
{
string value = sp.ReadLine(); //Read the information
int button = int.Parse(value);
//TODO: Since it isn't clear in your question how you get TWO buttons
//TODO: You will have to change this condition in order to only fire if both
//TODO: buttons are currently pressed!
buttonPressed = button == 0;
if (buttonPressed)
{
Character.AddForce(height, ForceMode2D.Impulse);
// The clamping of a rigidbody should always be done ine FixedUpdate!
// Pass in how many seconds as parameter or make the method
// parameterless and configure a fixed duration via the Inspector of the Timer
timer.BeginTimer(3.0f);
}
else
{
// Maybe stop the timer if condition is not fulfilled ?
timer.StopTimer();
}
}
catch (System.Exception)
{
// You should do something here! At least a Log ...
}
}
private void FixedUpdate()
{
// Here I wasn't sure: Are there actually two different
// Rigidbody2D involved? I would assume you rather wanted to use the Character rigidbody again!
Character.position = new Vector3(Mathf.Clamp(Character.position.x, Min.x, Max.x), Mathf.Clamp(Character.position.y, Min.y, Max.y));
}
// Did you mean OnApplicationQuit here?
private void ApplicationQuit()
{
if (sp != null)
{
{
sp.Close();
}
}
}
}
Typed on smartphone but I hope the idea gets clear

Unity - NullReferenceException in my GameOverManager

Iam trying to load my "PlayerHealth" Script to my "GameOverManager" to check my currentHealth from the PlayerHealth-Script.
If the current health is "0" - I want to trigger an animation.
The problem is, that Unity gives me an error with following message:
"NullReferenceException: Object reference not set to an instance of an object
GameOverManager.Update () (at Assets/GameOverManager.cs:32)"
Here is the piece of Code of my GameOverManager:
public class GameOverManager : MonoBehaviour {
public PlayerHealth playerHealthScript;
public float restartDelay = 5f;
Animator anim;
float restartTimer;
private void Awake()
{
anim = GetComponent<Animator>();
}
private void Update()
{
playerHealthScript = GetComponent<PlayerHealth>();
if (playerHealthScript.currentHealth <= 0) {
anim.SetTrigger("GamerOver");
restartTimer += Time.deltaTime;
if (restartTimer >= restartDelay) {
SceneManager.LoadScene(2);
}
}
}
}
The error is triggered on the following line:
if (playerHealthScript.currentHealth <= 0)
Here is the hierarchy - FPSController holds "PlayerHealth" - HUDCanvas holds "GameOverManager:
Here are the inspectors:
Here is the code of "PlayerHealth":
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using UnityEngine.SceneManagement;
public class PlayerHealth : MonoBehaviour
{
public int startingHealth = 100; // The amount of health the player starts the game with.
public int currentHealth; // The current health the player has.
public Slider healthSlider; // Reference to the UI's health bar.
public Image damageImage; // Reference to an image to flash on the screen on being hurt.
public AudioClip deathClip; // The audio clip to play when the player dies.
public float flashSpeed = 5f; // The speed the damageImage will fade at.
public Color flashColour = new Color(1f, 0f, 0f, 0.1f); // The colour the damageImage is set to, to flash.
public float restartDelay = 5f;
//Animator anim; // Reference to the Animator component.
public AudioSource playerAudio; // Reference to the AudioSource component.
// PlayerMovement playerMovement; // Reference to the player's movement.
// PlayerShooting playerShooting; // Reference to the PlayerShooting script.
bool isDead; // Whether the player is dead.
bool damaged; // True when the player gets damaged.
void Awake()
{
// Setting up the references.
// anim = GetComponent<Animator>();
// playerAudio = GetComponent<AudioSource>();
// playerMovement = GetComponent<PlayerMovement>();
// playerShooting = GetComponentInChildren<PlayerShooting>();
// Set the initial health of the player.
currentHealth = startingHealth;
}
void Update()
{
// If the player has just been damaged...
if (damaged)
{
// ... set the colour of the damageImage to the flash colour.
damageImage.color = flashColour;
}
// Otherwise...
else
{
// ... transition the colour back to clear.
damageImage.color = Color.Lerp(damageImage.color, Color.clear, flashSpeed * Time.deltaTime);
}
// Reset the damaged flag.
damaged = false;
}
public void TakeDamage(int amount)
{
// Set the damaged flag so the screen will flash.
damaged = true;
// Reduce the current health by the damage amount.
currentHealth -= amount;
playerAudio.Play();
Debug.Log("PLayer Health: " + currentHealth);
// Set the health bar's value to the current health.
healthSlider.value = currentHealth;
// Play the hurt sound
playerAudio.Play();
// If the player has lost all it's health and the death flag hasn't been set yet...
if (currentHealth <= 0 && !isDead)
{
// ... it should die.
Death();
}
}
void Death()
{
// Set the death flag so this function won't be called again.
isDead = true;
Debug.Log("In der Death Funktion");
First of all, you don't need, or better, you SHOULD NOT, use GetComponent inside Update, it's a very slow method and it impacts a lot the performance.
So, change your code to this:
public class GameOverManager : MonoBehaviour {
public PlayerHealth playerHealthScript;
public float restartDelay = 5f;
private Animator anim;
private float restartTimer;
private void Awake() {
anim = GetComponent<Animator>();
//No need for GetComponent<PlayerHealth>() if you assign it in the Inspector
//playerHealthScript = GetComponent<PlayerHealth>();
}
private void Update() {
if (playerHealthScript.currentHealth <= 0) {
anim.SetTrigger("GamerOver");
restartTimer += Time.deltaTime;
if (restartTimer >= restartDelay) {
SceneManager.LoadScene(2);
}
}
}
}
Moreover, your bug happens most probably because in the Inspector you assigned to the playerHealthScript variable the game object containing the PlayerHealth script. But, since you try in Update to get the script component again but this time from the game object that has the GameOverManager script (and I assume it doesn't have the PlayerHealth script), you get the NullReference since that script can't be found on this game object.
So, as you can see from the two lines commented out in my code, you actually don't need to get that component from script, just assign it via Inspector and you're good to go.

Reference to a particular Prefab clone

I am developing a simple 2D game. In game, I've created a prefab for charcaters. and I am changing sprite of prefab runtime. This all execute fine. Now I want to apply click event on a particular prefab clone and want to increase scale of prefab. I am attaching a c# script what I have did till now.
public class ShoppingManager : MonoBehaviour {
public static ShoppingManager instance;
[System.Serializable]
public class Shopping
{
public string CharacterName;
public Sprite CharacterSprite;
}
public GameObject CharacterPrefab;
public Transform CharacterSpacer;
public List<Shopping> ShoppingList;
private CharacterScript NewCharacterScript;
/*********************************************Awake()******************************************/
void Awake()
{
MakeSingleton ();
}
/******************************Create object of the script**********************************/
void MakeSingleton()
{
instance = this;
DontDestroyOnLoad (gameObject);
}
// Use this for initialization
void Start () {
LoadCharacters ();
}
void LoadCharacters()
{
foreach (var characters in ShoppingList) {
GameObject NewCharacter = Instantiate (CharacterPrefab) as GameObject;
NewCharacterScript = NewCharacter.GetComponent<CharacterScript> ();
NewCharacterScript.CharacterName = characters.CharacterName;
NewCharacterScript.Charcater.GetComponent<Image> ().sprite = characters.CharacterSprite;
NewCharacterScript.GetComponent<Button> ().onClick.AddListener (() => CharacterClicked (NewCharacterScript.CharacterName, NewCharacterScript.Charcater));
NewCharacter.transform.SetParent (CharacterSpacer, false);
}
}
void CharacterClicked(string CharacterName, GameObject Char)
{
StartCoroutine (IncreaseScale (Char));
}
IEnumerator IncreaseScale(GameObject TempCharacter)
{
int i = 5;
while (i > 0) {
yield return new WaitForSeconds (0.1f);
Vector3 TempVector = TempCharacter.GetComponent<RectTransform> ().localScale;
TempVector.x = TempVector.x + 0.2f;
TempVector.y = TempVector.y + 0.2f;
TempCharacter.GetComponent<RectTransform> ().localScale = TempVector;
i--;
}
}
}
This code triggers click event and also it increases scale but of last clone, not of clicked prefab clone. What I am missing, I can't understand. What should I correct in this. and Yeah! I am also attaching code of a script that I've added on prefab.
public class CharacterScript : MonoBehaviour {
public string CharacterName;
public GameObject Charcater;
}
create collider for your object attach the script below to it this way each object is responsible for handling its own functionalities like increasing its own size
public class characterFunctionalities: MonoBehaviour{
void OnMouseDown()
{
StartCoroutine (IncreaseScale (this.gameobject));
}
IEnumerator IncreaseScale(GameObject TempCharacter)
{
int i = 5;
while (i > 0) {
yield return new WaitForSeconds (0.1f);
Vector3 TempVector = TempCharacter.GetComponent<RectTransform> ().localScale;
TempVector.x = TempVector.x + 0.2f;
TempVector.y = TempVector.y + 0.2f;
TempCharacter.GetComponent<RectTransform> ().localScale = TempVector;
i--;
}
}
}