I'm making a script in Unity using C#. I'm trying to use the Update() method to detect once the Camera position is past a certain point and then Instantiate an object into the scene and overwrite the variable "x" to something else so this only happens once.
The problem is I cant overwrite this "x" variable.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour{
public GameObject GroundSprite;
public int x = 1;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (((Camera.main.transform.position.x) < -4) && ( x == 1))
{
Instantiate(GroundSprite, transform.position, Quaternion.identity);
int x = 2;
}
}
}
Please remove int from below code, your value will be overwritten.
// Update is called once per frame
void Update()
{
if (((Camera.main.transform.position.x) < -4) && ( x == 1))
{
Instantiate(GroundSprite, transform.position, Quaternion.identity);
//int x = 2;
x=2;
}
}
Related
Alright, so here's what's happening: When I hit play and left click to shoot, unity editor freezes and I have to do the old Ctrl + Alt + Del, now, I am almost certain this script is the source of the issue, because when a bullet is shot, this script is immediately added to it, so here's the script(It's called BulletLife.cs, just letting you know)
using System.Timers;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
public class BulletLife : MonoBehaviour
{
public GameObject bullet;
public double bulletLifeSpan = 3;
bool bulletLifeEnded;
public LayerMask targetMask;
bool hasHitTarget;
// Start is called before the first frame update
void Start()
{
var bulletAge = new System.Timers.Timer(bulletLifeSpan * 1000);
bulletAge.Elapsed += OnTimedEvent;
bulletAge.AutoReset = false;
while(hasHitTarget == false && bulletLifeEnded == false) {
hasHitTarget = Physics.CheckSphere(bullet.transform.position, bullet.transform.localScale.y, targetMask);
}
Destroy(bullet);
Debug.Log("Finish");
}
private void OnTimedEvent(System.Object Source, ElapsedEventArgs e) {
bulletLifeEnded = true;
}
}
Also, here's the Shoot.cs script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shoot : MonoBehaviour
{
public Transform gun;
public GameObject bullet;
public LayerMask targetMask;
public float bulletSpeed = 1000f;
bool hasHitTarget = false;
// Update is called once per frame
void Update()
{
if(Input.GetButtonDown("LeftClick")) {
GameObject bulletInstance;
bulletInstance = Instantiate(bullet, gun.position, new Quaternion(gun.rotation.w, gun.rotation.x, gun.forward.y, gun.rotation.z));
bulletInstance.AddComponent<Rigidbody>();
bulletInstance.GetComponent<Rigidbody>().useGravity = false;
bulletInstance.GetComponent<Rigidbody>().AddForce(gun.up * bulletSpeed);
bulletInstance.AddComponent<BulletLife>();
bulletInstance.GetComponent<BulletLife>().bullet = bulletInstance;
}
}
}
NOTE: I am using Unity 2019.4.15f1
Well everytime you instantiate a bullet in Start you do
while(hasHitTarget == false && bulletLifeEnded == false)
{
hasHitTarget = Physics.CheckSphere(bullet.transform.position, bullet.transform.localScale.y, targetMask);
}
this loop will never finish since none of the conditions is changed inside the loop. There either is a hit or not .. but then the parameters for the raycast are never changed, the position isn't updated since you are still in the same frame => endless loop => freeze the main thread completely.
What you rather wanted to do is move that thing to Update which is called once a frame like e.g.
//public GameObject bullet; // not needed
public double bulletLifeSpan = 3;
//bool bulletLifeEnded; // not needed
public LayerMask targetMask;
//bool hasHitTarget; // not needed
void Start()
{
var bulletAge = new System.Timers.Timer(bulletLifeSpan * 1000);
bulletAge.Elapsed += OnTimedEvent;
bulletAge.AutoReset = false;
}
private void Update()
{
if(Physics.CheckSphere(transform.position, transform.localScale.y, targetMask))
{
Destroy(gameObject);
Debug.Log("Finish");
}
}
private void OnTimedEvent(System.Object Source, ElapsedEventArgs e)
{
Destroy(gameObject);
Debug.Log("Finish");
}
Or make it a single Coroutine
// If Start returns IEnumerator it is automatically started as Coroutine
// So no need to start an extra routine
private IEnumerator Start()
{
// Keeps track of how long your bullet exists already
var bulletAge = 0f;
while(bulletAge < bulletLifeSpan && !Physics.CheckSphere(transform.position, transform.localScale.y, targetMask))
{
// Increase by the time passed since last frame
bulletAge += Time.deltaTime;
// "Pause" this routine, render this frame
// and continue from here in the next frame
yield return null;
}
Destroy(gameObject);
Debug.Log("Finish");
}
Btw note that in Shoot you can shorten this a lot
void Update()
{
if(Input.GetButtonDown("LeftClick"))
{
// Note that your quaternion made no sense -> simply pass in the gun.rotation
var bulletInstance = Instantiate(bullet, gun.position, gun.rotation);
var rb = bulletInstance.AddComponent<Rigidbody>();
rb.useGravity = false;
rb.AddForce(gun.up * bulletSpeed);
var life = bulletInstance.AddComponent<BulletLife>();
// Assigning the gameObject reference is completely unnecessary
// within BulletLife simply use "gameObject" as show before
}
}
You could shorten this even more by making sure these components already exist on your prefab object and are configured correctly. Then you wouldn't need any of these line but just Instantiate it.
And finally you shouldn't use thisCheckSphere at all but rather let Unity handle its Collision detection itself and use OnCollisionEnter and configure your Collision Layers according to your needs!
The issue with your solution is: If your bullet moves fast it might simply pass a target without your CheckSphere noting it namely if its velocity is higher then localScale.y * 2.
Your Start method is blocking, thus freezing your game.
You'll have to use Update or a Coroutine to make your hit tests.
public class BulletLife : MonoBehaviour
{
public GameObject bullet;
public double bulletLifeSpan = 3;
bool bulletLifeEnded;
public LayerMask targetMask;
bool hasHitTarget;
// Start is called before the first frame update
void Start()
{
StartCoroutine(CheckHit(0, bulletLifeSpan));
}
private IEnumerator CheckHit(float interval, float lifetime){
bool checkEveryFrame = interval <= 0;
WaitForSeconds wait = checkEveryFrame ? null : new WaitForSeconds(interval);
while(lifetime > 0){
yield return wait;
lifetime = lifetime - (checkEveryFrame ? Time.deltaTime : interval);
hasHitTarget = Physics.CheckSphere(bullet.transform.position, bullet.transform.localScale.y, targetMask);
}
bulletLifeEnded = true;
Destroy(bullet);
Debug.Log("Finish");
}
}
in the problem of today is about unity
well i 'm always in the beginnings so i just took a full asset from the store
so while applying some changes i just get a crazy error saying that no cameras rendering
in the start of the project everything work smoothly
this is the script linked to the camera
this script just trying to make a map generator not completed yet
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MapGen : MonoBehaviour
{
public GameObject[] flats=new GameObject[4];
public GameObject flat;
public int x;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Vector3 pos = flat.transform.position;
x = pos.x;
Debug.Log("position :: "+ x );
if(x%26 == 0)
{
Destroy(flats[0]);
for(int i = 0; i < 3; i++)
{
flats[i] = flats[i + 1];
}
flats[3] = flat;
Debug.Log("position "+x);
}
}
}
all works great but when trying this
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MapGen : MonoBehaviour
{
public GameObject[] flats=new GameObject[4];
public GameObject flat;
public int x;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Vector3 pos = flat.transform.position ;
x=(int) pos.x;
Debug.Log("position :: "+ x );
if(x%26 == 0)
{
Destroy(flats[0]);
for(int i = 0; i < 3; i++)
{
flats[i] = flats[i + 1];
}
flats[3] = flat;
Debug.Log("position "+x);
}
}
}
it just give me the error after running the game just like if the camera was destroyed and as you can see it is just this expression that was changed
x=pos.x;
to
x=(int) pos.x;
by the way the variable flat is referring to the main camera
you end up assigning your camera to the array and end up moving it up to [0] position and destroying it
flats[3] = flat;
flats[i] = flats[i + 1];
Destroy(flats[0]);
EDIT: As I've found out finger ID gets the same number as the touch's original index value, I made a method resetting the saved finger ID from a discarded touch to -1. A complete answer is posted.
I'm trying to implement 2 finger multitouch control for my simple shooting game, where one finger shot continuously affect the rotation of the player if first held down at the defined area lower at the screen, and the other controls the shooting button which can also be loaded to shoot more powerful bullets.
At first I tried storing the touch position when TouchPhase = Began in a Vector3 array with two places, with the other classes checking if and which one of the values in the array comply with the rule for starting their activity. The issue was that the finger index seems to drop down if an earlier finger is lifted, so if I pressed for example aiming -> shot button and then lifted aiming the aiming button became stuck in loading as it was trying to track touch(1) which actually became (0).
The solution it seemed should be using fingerId, which produces a consistent number for a touch regardless of its index. The thing is - what I'm writing works even worse than before. It activates functions even when I'm pressing at the wrong places, so it seems the if statement complies even when it shouldn't. What's wrong with the code?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TouchControls : MonoBehaviour
{
public static int aimID;
public static int shootID;
// Start is called before the first frame update
void Start()
{
aimID = 0;
shootID = 0;
}
// Update is called once per frame
void Update()
{
for (int i = 0; i < Input.touchCount && i < 2; i++)
{
if (Input.GetTouch(i).phase == TouchPhase.Began)
{
Vector2 pos = Camera.main.ScreenToWorldPoint(Input.GetTouch(i).position);
if (pos.x == Mathf.Clamp(pos.x, -2.5f * SceneScale.ratio, 2.5f * SceneScale.ratio) &&
pos.y <= Camera.main.ScreenToWorldPoint(new Vector3(0, 0, 0)).y + (2 * SceneScale.ratio))
{
aimID = Input.GetTouch(i).fingerId;
}
else if (Vector2.Distance(pos, ShotButton.position) <= ShotButton.scale.x)
{
shootID = Input.GetTouch(i).fingerId;
}
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Android;
public class ShotButton : MonoBehaviour
{
public static bool[] time;
static public Vector3 scale;
static public Vector2 position;
// Start is called before the first frame update
void Start()
{
scale = gameObject.transform.localScale;
position = new Vector2(gameObject.transform.position.x, gameObject.transform.position.y);
time = new bool[2];
time[0] = false;
time[1] = false;
}
// Update is called once per frame
public void FixedUpdate()
{
for (int i = 0; i < Input.touchCount && i < 2; i++)
{
time[i] = false;
if (Input.GetTouch(i).fingerId == TouchControls.shootID)
{
time[i] = true;
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public GameObject player;
public static bool isDead = false;
public float rot;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
public void FixedUpdate()
{
for (int i = 0; i < Input.touchCount && i < 2; i++)
{
if (Input.GetTouch(i).fingerId == TouchControls.aimID)
{
rot = Camera.main.ScreenToWorldPoint(Input.GetTouch(i).position).x * 80 / (2.5f * SceneScale.ratio);
}
}
rot = Mathf.Clamp(rot, -80, 80);
player.transform.rotation = Quaternion.Euler(0, 0, rot);
}
}
As this has received no answers and I haven't found a different method, I'll post what I've found to work — even though there might be other more effective solutions.
Finger ID matches the original index of the touch, which could be 0 or above. So I saved a unique integer type for for each controlling method and initialized it with the value -1. When touch with TouchPhase.Began falls within the boundaries defined for one of the buttons or controlling areas, I give the unique integer type the recorded finger ID value, which is then used by the respective method to find the index at which the touch is during each frame, and when the touch with this finger ID returns TouchPhase.Ended it gives the saved integer the value of -1 once again, which can't be found in any touch and thus it halts the controlling method.
I'm making a 3D Unity game. The red cube deletes every element with tag='Enemy' to which it touches during the playtime. The problem occurs when the script tries to count the number of objects with this tag at first. How to fix this problem?
The error:
FindGameObjectsWithTag is not allowed to be called from a MonoBehaviour constructor (or instance field initializer), call it in Awake or Start instead. Called from MonoBehaviour 'Collide'.
See "Script Serialization" page in the Unity Manual for further details.
Collide..ctor () (at Assets/Scripts/Collide.cs:9)
The script Collide.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Collide : MonoBehaviour
{
public Text txt;
public int obji = GameObject.FindGameObjectsWithTag("Enemy").Length;
void OnCollisionEnter(Collision collision)
{
if (collision.collider.gameObject.tag == "Enemy")
{
transform.localScale -= new Vector3(0.03F, 0.03F, 0.03F);
Destroy(collision.collider.gameObject);
obji = obji - 1;
Debug.Log(obji);
if ((obji) > 0)
{
txt.text = (obji).ToString();
}
else {
txt.text = "You win!";
}
}
}
}
Like Piflik said try this
public int obji = -1; //this is an example, I always try to initialize my variables.
void Start(){ //or Awake
obji = GameObject.FindGameObjectsWithTag("Enemy").Length;
}
I want some enemies to spawn in 4 different locations for a survival-type game. The problem is, they all spawn in the same place. Why is this? This is in Unity by the way.
C# script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spawner : MonoBehaviour {
public int spawnHere = 0;
public int spawnTimer = 0;
public int spawnRate = 1;
public Transform spawner1;
public Transform spawner2;
public Transform spawner3;
public Transform spawner4;
public GameObject melee1;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
spawnTimer = spawnTimer + spawnRate;
spawnHere = Random.Range (1, 5);
if(spawnTimer >= 120) {
if(spawnHere == 1) {
Instantiate (melee1, spawner1);
}
if(spawnHere == 2) {
Instantiate (melee1, spawner2);
}
if(spawnHere == 3) {
Instantiate (melee1, spawner3);
}
if(spawnHere == 4) {
Instantiate (melee1, spawner3);
}
spawnTimer = 0;
}
}
}
Have you connected the spawners correctly in the UI?
Watch this: Unity - Survival Shooter - Spawning Enemies
This worked pretty good for my project.
You should also use Time.deltaTime for timing the spawns. Not every system outputs the same amount of frames / second.
Unity - docs - Time.deltaTime
BTW:
Random.rand(min, max)
Unity - docs - Random.Rand
includes max as a possible value.