have been having a problem while working in Unity. I simply want to increase a score every time a collision is made, but my code just isn't helping. I know it sounds basic but I've been looking around in the site for quite some time now and I haven't found an answer yet.
Here's the script:
I have already checked and the "VictoryScreen" is already made and built.
public class PaintScriptGreen : MonoBehaviour {
public GameObject CylGreen;
private int score = 0;
private Vector3 tempPos;
private Quaternion tempRot;
private GameObject tempCyl;
void Awake ()
{
}
public void AddScore (int scoreValue)
{
score += scoreValue;
}
void OnTriggerEnter(Collider col)
{
if (col.gameObject.tag == "Cylinder" && col.gameObject.tag != "TreeHolder")
{
tempPos = col.gameObject.transform.position;
tempRot = col.gameObject.transform.rotation;
Destroy (col.gameObject);
tempCyl = Instantiate(CylGreen, tempPos, tempRot) as GameObject;
AddScore (1);
if (score >= 4)
{
Application.LoadLevel ("VictoryScreen");
}
}
if ((col.gameObject.tag != "Player")&&(col.gameObject.tag != "PlayPart"))
{
// destroy self bullet
Destroy (this.gameObject);
}
}
if ((col.gameObject.tag != "Player")&&(col.gameObject.tag != "PlayPart"))
{
// destroy self bullet
Destroy (this.gameObject);
}
This is probably the culprit. You're destroying "this" anytime the above conditions aren't true. Is this script on the bullet? If so, you'll need to move the scoring mechanism to an object that isn't getting destroyed OR make it a static variable. I don't like static variables, but it is a possible solution.
Related
Context
Hello, its me again. I have been playing around with "Object Pool" and it is fascinating the amount of CPU performance can save up. However, I have encountered a new "feature" where it scores once for the object being called and going through the pipe.
In my object pool, I have a pipe GameObject where is being Instantiated and set its method Active(false) until it is spawned. Once spawned, my prefabs will be filled accordingly to the Object Pool's size.
Problem
Once it spawns, it does what it should do, both scoring and the same mechanic as "Flappy Bird". However, once the object gets Active again, it doesn't seem to score anymore because it was passed by a player. What I have done is to have a flag that checks if the player (bird) has passed the pipe or not. However, when I pass through it, it will update it as if it was 6 times (one point per frame). You may ask "have you done another flag for the pipe itself?" then the answer is yes. I tried that way also, but it will only keep the score once and not increase further than 5.
I ran out of ideas with the object pool approach. It seems to work fine if it is WITHOUT object pooling, but the flaw here is that it costs me CPU performance.
It either increases it by just 1, or it increases it by 6 times (because for each frame the object is in the ray, it counts another point).
Attempts
I have browsed on the Unity Learning center to find out how to do the object pooling, and it wasn't too bad as I thought. Later, I found this issue and I thought it was a problem with my logic (which it can be). I have spent several hours already (first mistake) to think it is something easy to fix, but apparently it wasn't due the time I have spent to just figure out why it is not working 😅. I have been fiddling around my RayCastDetection, SpawnManager, ObjectPooling, and PlayerControl logic that interacts accordingly to the way I want, but nada.
Code
ObjectPooling.cs
public static ObjectPooling sharedInstance;
public List<GameObject> pooledObjects;
public GameObject objectToPool;
private PlayerControl playerControllerScript;
public int amountToPool;
void Awake()
{
sharedInstance = this;
}
void Start()
{
playerControllerScript = GameObject.Find("Player").GetComponent<PlayerControl>();
pooledObjects = new List<GameObject>();
GameObject tmp;
for (int i = 0; i < amountToPool; i++) //Add objects to the pool and turns them invisible (false)
{
tmp = Instantiate(objectToPool);
tmp.SetActive(false);
playerControllerScript.passedBeam = false;
pooledObjects.Add(tmp);
}
}
public GameObject GetPooledObject()
{
for (int i = 0; i < amountToPool; i++)
{
if (!pooledObjects[i].activeInHierarchy)
return pooledObjects[i];
}
return null;
}
RayCastDetection.cs
public class RayCastDetection : MonoBehaviour
{
public UIManager UIManagerScript;
public PlayerControl playerControlScript;
private RaycastHit hit;
public bool passed;
void Start()
{
UIManagerScript = GameObject.Find("UI_Manager").GetComponent<UIManager>(); //Used for scoring
playerControlScript = GameObject.Find("Player").GetComponent<PlayerControl>(); //used for player passing through the pipe
passed = false;
playerControlScript.passedBeam = false;
}
void Update()
{
Vector3 beamDown = transform.TransformDirection(Vector3.down);
Ray ray = new Ray(transform.position, beamDown);
if (!passed)
{
if (Physics.Raycast(ray, out hit))
{
if (hit.collider.tag == "Player" && !playerControlScript.passedBeam)
{
playerControlScript.passedBeam = !playerControlScript.passedBeam;
UIManagerScript.score++;
}
Debug.DrawRay(transform.position, hit.point - transform.position);
}
}
else
playerControlScript.passedBeam = false;
}
}
SpawnManager.cs
public class SpawnManager : MonoBehaviour
{
public GameObject[] obstaclesPrefab;
private PlayerControl playerControllerScript;
private float startDelay = 1.69f;
private float repeatRate = 1.1f;
void Start()
{
playerControllerScript = GameObject.Find("Player").GetComponent<PlayerControl>();
InvokeRepeating("SpawnObstacle", startDelay, repeatRate);
}
void Update()
{
}
void SpawnObstacle()
{
// int obstaclesIndex = Random.Range(0, obstaclesPrefab.Length); //This is used only if I don't want to deal with object pooling, but the whole point is to use it. This is just a reference if I want to go back
if (playerControllerScript.gameOver == false)
{
float randomY = Random.Range(-2f, 2f);
Vector3 randomHeight = new Vector3(35, randomY, -7);
GameObject pipe = ObjectPooling.sharedInstance.GetPooledObject();
if (pipe != null)
{
pipe.transform.position = randomHeight;
pipe.SetActive(true);
//My guess is that I want to instantiate the object pipe's beam to false here
}
}
// Instantiate(obstaclesPrefab[obstaclesIndex], randomHeight, obstaclesPrefab[obstaclesIndex].transform.rotation); //This is used only if I don't want to deal with object pooling, but the whole point is to use it. This is just a reference if I want to go back
}
}
Feel free to leave some suggestions in what I have missed out or any questions in regards to fill in. Thank you for your time!
I wanted to share some good news. My problems has been fixed!
Before I step into the solution, big thanks to rotgers and sharing me the approach he used. I actually was stubborn and wanted to feel unique in some way when it was coming to a solution. I had to step back a little bit and look at the big picture.
Reflection
If you ever struggle and spent like 8-12hrs like me for some logic nonsense and you feel it in the tip of the tongue, it is time to step a little bit back when you are not going anywhere after 1 hour.
Few tips to do (which I didn't do but I wish I do at the moment) was have drawings in how you are picturing it out mentally. Not drawing for the first time was my first mistake. I felt challenged by my own mind and sabotaging my own work to continuing being stubborn.
Code Solution
Now, the solution is that I added my UIManager script with my SpawnManager and RayCastDetection in order to make it work. Here is how I did it:
UIManager.cs
public class UIManager : MonoBehaviour
{
public Text scoreBoardDisplay;
public Text scoreBoardDisplayShadowed;
public AudioSource sound_score;
public string scoreText;
public int score;
public int oldScore;
public bool success;
public bool incrementScore;
void Start()
{
success = false; //NEW
incrementScore = false; //NEW
}
void Update()
{
if (score != oldScore)
{
sound_score.Play();
scoreText = score.ToString();
scoreBoardDisplay.text = scoreText;
scoreBoardDisplayShadowed.text = scoreText;
oldScore = score;
}
}
}
SpawnManager.cs
public class SpawnManager : MonoBehaviour
{
public GameObject[] obstaclesPrefab;
private PlayerControl playerControllerScript;
public UIManager UIManagerScript; //NEW
private float startDelay = 1.69f;
private float repeatRate = 1.1f;
void Start()
{
playerControllerScript = GameObject.Find("Player").GetComponent<PlayerControl>();
UIManagerScript = GameObject.Find("UI_Manager").GetComponent<UIManager>();
InvokeRepeating("SpawnObstacle", startDelay, repeatRate);
}
void SpawnObstacle()
{
if (playerControllerScript.gameOver == false)
{
float randomY = Random.Range(-2f, 2f);
Vector3 randomHeight = new Vector3(35, randomY, -7);
GameObject pipe = ObjectPooling.sharedInstance.GetPooledObject();
if (pipe != null)
{
pipe.transform.position = randomHeight;
pipe.SetActive(true);
UIManagerScript.incrementScore = false; //NEW, where my problem happens to be solved
UIManagerScript.success = false; //NEW, where my problems happens to be solved
}
}
}
}
RayCastDetection.cs
public class RayCastDetection : MonoBehaviour
{
public UIManager UIManagerScript;
public PlayerControl playerControlScript;
private RaycastHit hit;
public bool passed;
void Start()
{
UIManagerScript = GameObject.Find("UI_Manager").GetComponent<UIManager>();
playerControlScript = GameObject.Find("Tomato").GetComponent<PlayerControl>();
passed = false;
}
void Update()
{
Vector3 beamDown = transform.TransformDirection(Vector3.down);
Ray ray = new Ray(transform.position, beamDown);
if (!passed)
{
if (Physics.Raycast(ray, out hit))
{
if (hit.collider.tag == "Player" && !UIManagerScript.success) //Setting a condition to check if the beam hasn't been touched YET
{
//Once touched, turn this true and increment
UIManagerScript.incrementScore = true;
UIManagerScript.score++;
}
//If I already incremented my score, then there is no need to re-increment it again from the same ray. Thus, a flag for success is handled
if (UIManagerScript.incrementScore)
UIManagerScript.success = true;
Debug.DrawRay(transform.position, hit.point - transform.position);
}
}
}
}
Again, thank you very much for guiding me to the right track :). If I think in something to add, I will update it under my Reflection header.
If you want to play around or use the code, feel free and play with it in my Git
So, i have a script, and two objects, one copy of the another, exactly what one does, the other does too, but in other position. The script do the following:
get the actual value of the heat variable of the object and use this for the second part;
the second part is get the heat value and check if it's higher or equal than 850,
if it is, check if the player pressed the button to transform the object mesh in another mesh
but, when the button is pressed, only the first object has it mesh changed, i already tried raycast, a lot of additional code, ineficient code and don't worked. I know i can just make two similar scripts, but i have plans to create more objects when the player wants, so, it's not going to work...
the script:
using UnityEngine;
using DG.Tweening;
public class Iron : MonoBehaviour
{
private float Heat;
private bool Heating;
[Header("Game Objects")]
[SerializeField] private GameObject WeaponCanvas;
[Header("Materials")]
[SerializeField] private Material HotIron;
[SerializeField] private Material MediumIron;
[SerializeField] private Material CoolIron;
[Space]
[Header("Meshs")]
[SerializeField] private Mesh SimpleSwordMaterial;
[Space]
[Header("Text Mesh Pro")]
[SerializeField] private TMPro.TMP_Text TemperatureText;
private bool Hot;
void Update()
{
if (Heating && Heat <= 1500)
{
Heat += 1.5f;
}
if (Heat >= 850)
{
GetComponent<Renderer>().material = HotIron;
Hot = true;
}
if (Heat >= 600 && Heat <= 849)
{
GetComponent<Renderer>().material = MediumIron;
Hot = false;
}
if (Heat <= 400)
{
GetComponent<Renderer>().material = CoolIron;
Hot = false;
}
}
void OnCollisionStay(Collision other)
{
if (other.gameObject.tag == "HeatSource")
{
Heating = true;
}
if (!(other.gameObject.tag == "HeatSource"))
{
Heating = false;
}
}
public void SimpleSword()
{
var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 3.5f))
{
hit.transform.gameObject.GetComponent<MeshFilter>().mesh = SimpleSwordMaterial; // i have problems from here (i guess ;-;)
hit.transform.gameObject.GetComponent<MeshCollider>().sharedMesh = SimpleSwordMaterial;
if (hit.transform.gameObject.GetComponent<BoxCollider>() != null)
{
Destroy(hit.transform.gameObject.GetComponent<BoxCollider>());
}
if (hit.transform.gameObject.GetComponent<SphereCollider>() != null)
{
Destroy(hit.transform.gameObject.GetComponent<SphereCollider>());
}
if (hit.transform.gameObject.GetComponent<CapsuleCollider>() != null)
{
Destroy(hit.transform.gameObject.GetComponent<CapsuleCollider>());
}
transform.localScale = new Vector3(1, 1, 1);
}
WeaponCanvas.SetActive(false);
Player.onMenu = false;
Cursor.lockState = CursorLockMode.Locked;
}
void OnMouseOver()
{
if (Input.GetMouseButtonDown(1) && Hot)
{
WeaponCanvas.SetActive(true);
Player.onMenu = true;
Cursor.lockState = CursorLockMode.None;
}
TemperatureText.text = ((int)Heat).ToString() + "°";
TemperatureText.DOFade(1, 0.4f);
}
void OnMouseExit()
{
TemperatureText.DOFade(0, 0.4f);
}
}
Similar to sharedMaterial
sharedMesh is not what you want to use...
It is recommended to use this function only for reading mesh data and not for writing, since you might modify imported assets and all objects that use this mesh will be affected.
Also, be aware that is not possible to undo the changes done to this mesh.
I'm having a bit of trouble understanding the problem... are all objects with the Iron.cs getting a new mesh? if yes, it's the .sharedmesh =.
Furthermore, it's worth noting that each monobehaviour in Unity has a .GetInstanceId() method. You can compare the id of the instance you hit with your raycast and that of the one with the correct temporature.
You should also consider simply instantiating a prefab of a sword instead.
I accessing another script but her value is by default null.
I want to increase a score when colliding with the obstacle(+5).
Code:
playerscore.cs
public static class playerscore
{
public static int Score = 0; //static variable
}
TouchControll.cs
public Text scoretext;
void Awake()
{
//when I game quit and reenter the game then display the last score
PlayerPrefs.GetInt("score", playerscore.Score);
//this data on my ram not in harddisk
playerscore.Score = PlayerPrefs.GetInt("score", playerscore.Score);
//last score text update when I Reenter the game
scoretext.text = PlayerPrefs.GetInt("score").ToString();
print(scoretext.text);
//pass a score one scene to another scene
PlayerPrefs.SetInt("score", playerscore.Score);
scoretext.text = ("" + playerscore.Score);
PlayerPrefs.SetInt("score", playerscore.Score);
}
void OnCollisionEnter2D(Collision2D col)
{
//Debug.Log("oncollisionenter");
//when my player reaches the finish line then the score add=100; //work fine
if (col.gameObject.tag == "successfinishtag")
{
playerscore.Score += 100;
PlayerPrefs.SetInt("score", playerscore.Score);
scoretext.text = ("" + playerscore.Score);
//Debug.Log("scoreadd:" + playerscore.Score);
}
}
problem is here
I want the when my player collided with the obstacle then increment my score(+5) but the issue is not increasing my score
obstacle.cs
int incrementscore = 5;
TouchControll touchcontroll;
void Start()
{
GetComponent<TouchControll>();
}
//here player colliding with the obstacle
void OnTriggerEnter2D(Collider2D other)
{
foreach (Transform child in transform)
{
//Debug.Log("Inside foreach");
if (child.tag == "obstacleobject")
{
Animator anim = gameObject.GetComponent<Animator>();
anim.Play("animone");
}
}
playerscore.Score = PlayerPrefs.GetInt("score", playerscore.Score);
print(playerscore.Score);
Debug.Log(touchcontroll); //null
if (touchcontroll!= null)//null by default if(null != null) condition false
{
IncrementScore();
touchcontroll.scoretext.text = playerscore.Score.ToString();
}
}
void IncrementScore()
{
//Debug.Log("Inside Increment score");
playerscore.Score = playerscore.Score + incrementscore;
PlayerPrefs.GetInt("score", playerscore.Score);
//print(PlayerPrefs.GetInt("score", playerscore.Score));
PlayerPrefs.SetInt("score", playerscore.Score);
touchcontroll.scoretext.text = playerscore.Score.ToString();
}
I want the when my player collided with the obstacle then increment my score(+5) but the issue is not increasing my score how to solve the problem plz help.
How to solve this problem??
You're not assigning the result of GetComponent back to anything. Change obstacle.cs:
TouchControll touchcontroll;
void Start()
{
touchcontroll = GetComponent<TouchControll>();
}
However, GetComponent() only returns something if there is an instance of whatever object attached to the current game object. Are obstacle.cs and TouchControll.cs both attached to the current GameObject?
If not GetComponent is not going to work, use FindObjectOfType() instead:
void Start()
{
touchcontroll = FindObjectOfType<TouchControll>();
}
Here are the relevant pages from Unity scripting API for more info:
https://docs.unity3d.com/ScriptReference/GameObject.GetComponent.html
https://docs.unity3d.com/ScriptReference/Object.FindObjectOfType.html
I have used in my game this scenario, where Player collide with coin and it increment coin value smoothly in my case. And I have also noticed there is simple difference with your script from me. U have to used FindObjectOfType<> and also have to called public int incrementscore = 5; Where U didn't use any access modifier. Below also include my code, U can take a look and get the idea. Hopefully this will work for U. thanks
public class CoinScript : MonoBehaviour
{
public int coinValue;
private LevelManager gameLevelManager;
// Start is called before the first frame update
void Start()
{
gameLevelManager = FindObjectOfType<LevelManager>();
}
// Update is called once per frame
void Update()
{
}
void OnTriggerEnter2D(Collider2D other) {
Debug.Log("Triggered");
if (other.tag == "Player")
{
gameLevelManager.AddCoins(coinValue);
Destroy(gameObject);
}
}
}
//In LevelManager scripts
public void AddCoins(int numberOfCoins)
{
coins += numberOfCoins;
coinText.text = "Coins : " + coins;
}
Be simply , no use PlayerPrefs to other place
public static class playerscore
{
public static int Score
{
get { return PlayerPrefs.GetInt("score", 0);}
set { PlayerPrefs.SetInt("score", value); }
}
}
First off, I am quite new to scripting so there's probably going to be a few flaws in my script.
So basically, I've made a script for the power up, but once my shot or the player touches the power up coin the fire rate does increase however it won't go back to the normal fire rate after 5 seconds... I have no idea what might be the cause, any advice would be helpful!
using UnityEngine;
using System.Collections;
public class FireRatePowerUp : MonoBehaviour {
private bool isPowerUp = false;
private float powerUpTime = 5.0f;
private PlayerShoot playerShoot;
private void Start()
{
playerShoot = PlayerShoot.FindObjectOfType<PlayerShoot>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "Player" || collision.gameObject.tag == "Projectile")
{
StartCoroutine(PowerUpTime());
isPowerUp = true;
Destroy(gameObject);
if (collision.gameObject.tag == "Projectile")
{
Destroy(collision.gameObject);
}
}
}
IEnumerator PowerUpTime()
{
playerShoot.fireRate -= 0.13f;
yield return new WaitForSeconds(powerUpTime);
playerShoot.fireRate += 0.13f;
}
}
I think the issue here is that you're destroying the gameobject this script is attached to (the coin) and by so doing, the script itself is destroyed, therefor its code, coroutine or otherwise won't execute.
StartCoroutine(PowerUpTime());
isPowerUp = true;
Destroy(gameObject); //oops, our script has been destroyed :(
You would have to do this very differently, basically moving the bulk of the code to the PlayerShoot class.
Something like this (this being in PlayerShoot.cs)
public void ActivatePowerupFireRate(float time, float amt) {
StartCoroutine(DoActivatePowerupFireRate(time, amt));
}
public IEnumerator ActivatePowerupFireRate(float time, float amt) {
fireRate -= amt;
yield return WaitForSeconds(time);
fireRate += amt;
}
IEumerator is definately one of the ways you can solve this issue.
However I'm not a fan of them here's my solution if you have a timer in game.
public int timePassed = 0;
public int gotPowerUp = 0;
void Start(){
InvokeRepeating("Timer", 0f, 1.0f);
//Starting at 0 seconds, every second call Timer function.
}
void Timer(){
timePassed++; // +1 second.
}
That way when you obtained the powerup you can set gotPowerUp = timePassed. So you have the exact time when powerup is activated.
then you do something like
if( (gotPowerUp + 5) == timePassed ){
//5 seconds have passed.
//deactivate powerup here
}
So i am designing a game where once the object collides with the powerup it should wait for 10 seconds and go back to normal. I am using update function to keep count but once OnTriggerEnter function is used the update function stops running every frame. How do i counter it. Please help
public class Powerup_1act : MonoBehaviour {
//This powerup temporarily passes through all lasers
public GameObject EffectiveTo;
private float TimeRemaining;
int flag=0;
float timer=0.0f;
public float alphalevel=.20f;
public GameObject laserfield;
float flag2=0f;
float countdown=0;
void OnTriggerEnter2D(Collider2D other)
{
//Check the provided Collider2D parameter other to see if it is tagged "PickUp", if it is...
if (other.gameObject.CompareTag("Player"))
{
gameObject.SetActive(false);
laserfield.gameObject.tag = "Temp";
flag2 = 1;
changealpha ();
Debug.Log ("Active?"+gameObject.activeInHierarchy);
StartCoroutine(Powerup());
}
}
private IEnumerator Powerup()
{
countdown = 10f;
while (countdown >= 0) {
Debug.Log (timer);
timer += Time.deltaTime;
if (flag2 == 1) {
//changes the alpha level between 0.2f and 0
if (alphalevel > 0 && flag == 0) {
alphalevel -= .05f;
changealpha ();
} else if ((alphalevel == 0 || flag == 1) && alphalevel < .20) {
alphalevel += .05f;
changealpha ();
Debug.Log (alphalevel);
flag = 1;
} else if (alphalevel == .20) {
flag = 0;
}
countdown-=Time.smoothDeltaTime;
yield return null;
}
}
alphalevel = 1f;
changealpha ();
laserfield.gameObject.tag = "Laser";
flag2 = 0;
}
//heres a function to change transparency of player when a powerup is in effect
void changealpha()
{
EffectiveTo.GetComponent<SpriteRenderer> ().color = new Color (1f, 1f, 1f, alphalevel);
}
}
Use Coroutine's for your advantage:
void OnTriggerEnter2D(Collider2D other)
{
StartCoroutine(PowerUp());
}
private IEnumerator PowerUp()
{
countDown = 10f;
while (countDown >= 0)
{
// Logic during the 10 seconds
countDown -= Time.smoothDeltaTime;
yield return null;
}
}
What is happening is that when you disable the gameobject, the script that this gameobject has stop working too.
In your case when the OnTriggerEnter2D is fired you're setting you gameobject to false gameObject.SetActive(false); which means that this script will stop working because you've also placed this script on the gameobject you've just disabled.
You should move the OnTriggerEnter2D event on the other object that dosen't get disabled and you can disable this gameobject from there. What you do in Update would also be moved to that other gameobject.
From Unity documentation:
Making a GameObject inactive will disable every component, turning off any attached renderers, colliders, rigidbodies, scripts, etc... Any scripts that you have attached to the GameObject will no longer have Update() called, for example.