question about unity c# script update function - c#

i'm trying to make a timer to control gameObject display,i found t doesn't change when it become zero,
how can i fix it or another better solution?
public class randomGopher : MonoBehaviour
{
// Use this for initialization
public int t;
public bool active;
void Start()
{
t = Random.Range(30, 150);
}
// Update is called once per frame
void Update()
{
Debug.Log(t);
if (t == 0)
{
active = this.gameObject.activeSelf;
this.gameObject.SetActive(!active);
t = Random.Range(30, 150);
}
else
{
t = t - 1;
}
}
}

So your problem is that Update is not called anymore if the GameObject your script is attached to is inactive after
gameObject.SetActive(false);
(Not 100% sure and can't test it right now as typing on smartphone.)
Afaik Invoke is still working also on a disabled MonoBehaviour or inactive GameObject (at least I know that it is the case for InvokeRepeating) so you could probably simply use something like
public class randomGopher : MonoBehaviour
{
// Adjust these in seconds
[SerializeField] private float minDelay = 1f;
[SerializeField] private float maxDelay = 3f;
private void Start()
{
Invoke(nameof(SwitchActiveState), Random.Range(minDelay, maxDelay);
}
private void SwitchActiveState ()
{
gameObject.SetActive(! gameObject.activeSelf);
Invoke(nameof(SwitchActiveState), Random.Range(minDelay, maxDelay);
}
}

The best way is executing your script in a separated GameObject, and only activate or deactivate the TargetObject.
public class randomGopher : MonoBehaviour
{
// Use this for initialization
public int t;
public bool active;
public GameObject TargetObj;
void Start()
{
t = Random.Range(30, 150);
}
// Update is called once per frame
void Update()
{
Debug.Log(t);
if (t == 0)
{
active = TargetObj.activeSelf;
TargetObj.SetActive(!active);
t = Random.Range(30, 150);
}
else
{
t = t - 1;
}
}
}

Related

Assigning multiple prefabs to a script which only allows one to be added

I have a script where it puts an object (premade) onto a premade path using LeanTween which works fine.
The way this works is you can assign one object to the "path adder" (MoveController) that has the Moveable script attached to it.
However, I need to be able to add new prefabs made during runtime to the MoveController so they follow the path.
So how would I go about making instantiated objects attach to the MoveController during runtime.
Thank you.
Moveable script, also instantiates the prefabs
public class Moveable : MonoBehaviour
{
[SerializeField] private float _speedMetersPerSecond = 25f;
private Vector3? _destination;
private Vector3 _startPosition;
private float _totalLerpDuration;
private float _elapsedLerpDuration;
private Action _onCompleteCallback;
public GameObject Electron;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var NextOnPath = Instantiate(Electron, transform.position, Quaternion.identity);
NextOnPath.AddComponent<Moveable>();
}
if (_destination.HasValue == false)
return;
if (_elapsedLerpDuration >= _totalLerpDuration && _totalLerpDuration > 0)
return;
_elapsedLerpDuration += Time.deltaTime;
float percent = (_elapsedLerpDuration / _totalLerpDuration);
Debug.Log($"{percent} = {_elapsedLerpDuration} / {_totalLerpDuration}");
transform.position = Vector3.Lerp(_startPosition, _destination.Value, percent);
}
public void MoveTo(Vector3 destination, Action onComplete = null)
{
var distanceToNextWaypoint = Vector3.Distance(transform.position, destination);
_totalLerpDuration = distanceToNextWaypoint / _speedMetersPerSecond;
_startPosition = transform.position;
_destination = destination;
_elapsedLerpDuration = 0f;
_onCompleteCallback = onComplete;
}
}
MoveController script
using System.Linq;
public class MoverController : MonoBehaviour
{
[SerializeField] private Moveable target;
private List<Transform> _waypoints;
private int _nextWaypointIndex;
private void OnEnable()
{
_waypoints = GetComponentsInChildren<Transform>().ToList();
_waypoints.RemoveAt(0);
MoveToNextWaypoint();
}
private void MoveToNextWaypoint()
{
var targetWaypointTransform = _waypoints[_nextWaypointIndex];
target.MoveTo(targetWaypointTransform.position, MoveToNextWaypoint);
target.transform.LookAt(_waypoints[_nextWaypointIndex].position);
_nextWaypointIndex++;
if (_nextWaypointIndex >= _waypoints.Count)
_nextWaypointIndex = 0;
}
}
Let me know if I need to clarify anything.
I understand this is quite a loaded question but I would greatly appreciate any help!
Solution
Moveable:
public class Moveable : MonoBehaviour
{
[SerializeField] private float _speedMetersPerSecond = 25f;
public List<Moveable> moveables = new List<Moveable>();
private Vector3? _destination;
private Vector3 _startPosition;
private float _totalLerpDuration;
private float _elapsedLerpDuration;
private Action _onCompleteCallback;
public GameObject Electron;
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Moveable NextOnPath = Instantiate(Electron, transform.position, Quaternion.identity).GetComponent<Moveable>();
moveables.Add(NextOnPath.GetComponent<Moveable>());
MoverController.instance.targets.Add(NextOnPath.GetComponent<Moveable>());
}
}
}
MoveController:
public List<Moveable> targets;
[SerializeField] private Moveable target;
private List<Transform> _waypoints;
private int _nextWaypointIndex;
public static MoverController instance;
private void MoveToNextWaypoint()
{
for (int i = 0; i < targets.Count; i++)
{
var targetWaypointTransform = _waypoints[_nextWaypointIndex];
targets[i].MoveTo(targetWaypointTransform.position, MoveToNextWaypoint);
targets[i].transform.LookAt(_waypoints[_nextWaypointIndex].position);
_nextWaypointIndex++;
if (_nextWaypointIndex >= _waypoints.Count)
_nextWaypointIndex = 0;
}
}
Note: Only the changed parts are displayed in the solution
Yes, if you use an Array then you can add as many prefabs as you want. You will still need to set the code in the script to handle multiple prefabs.
Although, it sounds like you might not understand the basics for C# and Unity and you might like to take a class, read a tutorial or watch a Youtube video.
Update
Because your question is so broad, I cannot just give you the code. That's something you should pay a coder for. But I will tell you the general ideas of what will need to be done.
In the Moveable class, you need to track your var NextOnPath after instantiating them. A great way to do this (As long as you don't plan to convert anything to JSON) would be with a List<>.
For example:
List<Moveable> moveables = new List<Moveable>();
void Update()
{
//Some code before - with instantiation
//It is best to track items by the script that you will use since you can just use moveables[i].gameObject to access the gameObject and moveables[i].transform to access transform
moveables.Add(NextOnPath.GetComponent<Moveable>());
//Some code after
}
You might save computing power by adding the Moveable script to your Electron prefab so you use:
Moveable nextOnPath = Instantiate(electron, transform.position, Quaternion.identity).GetComponent<Moveable>();
Look up Singleton, you can use this to assign values to your MoverController class easily:
public static MoverController instance;
void Awake()
{
if (instance == null)
{
instance = this;
}
else
{
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
}
Then in the Moveable class simply add it to MoverController
//Requires changing the above code
void Update()
{
//Some code before - with instantiation
MoverController.instance.targets.Add(NextOnPath.GetComponent<Moveable>());
//Some code after
}

Proper way to "spawn" game objects. (I want to spawn projectiles)

Hey all I made the following class to act as "bullets" in my game and I don't know how to construct these bullet objects with the params that I want (determined at runtime) and spawn them in the game.
My first attempt looked like this:
if (canShoot())
{
shotCoolDown = FRAMES_BETWEEN_SHOTS;
Bullet bullet = new Bullet().setLoft(LOFT).setWobble(WOBBLE).setInitialVel(INITIAL_VEL).setDirectionOffset(internalRecoil.getRecoil());
}
but i get the following warnign from the unity editor:
You are trying to create a MonoBehaviour using the 'new' keyword. This is not allowed. MonoBehaviours can only be added using AddComponent(). Alternatively, your script can inherit from ScriptableObject or no base class at all
What is the proper way to spawn these bullets and to set the fields as I want them?
For more info here is the bullet class
public class Bullet : MonoBehaviour
{
private Rigidbody rigidbody;
private float wobble;
private float loft;
private float initialVel;
private Vector2 initialDirectionOffset;
private bool wobbleDirection = false;
private void Awake()
{
this.rigidbody = this.GetComponent<Rigidbody>();
}
private void Start()
{
transform.Rotate(initialDirectionOffset);
rigidbody.AddForce(initialVel*transform.forward);
rigidbody.AddForce(loft*transform.up);
}
private void FixedUpdate()
{
if (wobbleDirection)
{
rigidbody.AddForce(transform.right * wobble);
wobbleDirection = false;
}
else
{
rigidbody.AddForce(-transform.right * wobble);
wobbleDirection = true;
}
}
public Bullet setWobble(float wobble)
{
this.wobble = wobble;
return this;
}
public Bullet setLoft(float loft)
{
this.loft = loft;
return this;
}
public Bullet setInitialVel(float initialVel)
{
this.initialVel = initialVel;
return this;
}
public Bullet setDirectionOffset(Vector2 offset)
{
this.initialDirectionOffset = offset;
return this;
}
}
For more info here is the full "gun" class that spawns the bullets.
public class Gun : MonoBehaviour
{
private int FRAMES_BETWEEN_SHOTS = 10;
private int shotCoolDown = 0;
private const float LOFT = 100F;
private const float WOBBLE = 100F;
private const float INITIAL_VEL = 100F;
private Vector3 directionOffset;
private Recoil internalRecoil;
private void Awake()
{
internalRecoil = this.GetComponent<Recoil>();
if (internalRecoil == null)
{
throw new Exception("Could not find recoil component");
}
}
private void FixedUpdate()
{
if (shotCoolDown > 0)
{
shotCoolDown--;
}
}
private bool canShoot()
{
return shotCoolDown == 0;
}
public void performShoot()
{
if (canShoot())
{
//need to have a recoil component that reacts to bullet fire. then adjusts back to 0,0,0
shotCoolDown = FRAMES_BETWEEN_SHOTS;
Bullet bullet = new Bullet().setLoft(LOFT).setWobble(WOBBLE).setInitialVel(INITIAL_VEL).setDirectionOffset(internalRecoil.getRecoil());
}
}
}
My second attempt looks like:
GameObject.Instantiate(new Bullet().setLoft(LOFT).setWobble(WOBBLE).setInitialVel(INITIAL_VEL)
.setDirectionOffset(internalRecoil.getRecoil()));
is this the right way to do it???
As 3Dave mentioned, the best way to do this would be to instantiate a prefab. Assuming you will be dealing with multiple projectiles, I like to separate this into a utility function for instantiating prefabs at a given position.
public static class UnityUtil {
public static GameObject instantiatePrefab(
Object prefab,
Vector3 position,
Transform? parent = null
){
// Create an instance of the prefab
GameObject instance = Object.Instantiate(prefab, position, Quaternion.identity) as GameObject;
// Set the parent
if(parent != null){
instance.transform.parent = parent;
}
return instance;
}
}
Then you could have code like
Transform projectileHolder;
GameObject myBulletPrefab;
Vector3 bulletPosition;
...setup variables...
var BulletGameObj = UnityUtil.instantiatePrefab(myBulletPrefab, bulletPosition, projectileHolder);
Bullet bullet = BulletGameObj.GetComponent<Bullet>();
I keep some other functions in my util for dealing with generic gameobject cases, you can see my full UnityUtil class here

How do I make my Score timer stop on death?

I'm making a bullet hell game where the longer you survive, the more score you get. The score timer starts at 1 and goes up when playing. However, I want it to stop when the player dies, but I can't get it quite right.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ScorePerSecond : MonoBehaviour
{
public Text scoreText;
public float scoreAmount;
public float pointIncreasedPerSecond;
private string ENEMY_TAG = "Enemy";
private string FOLLOWENEMY_TAG = "Followenemy";
private string PLAYER_TAG = "Player";
private bool isFinished = false;
// Start is called before the first frame update
void Start()
{
isFinished = true;
scoreAmount = 0f;
pointIncreasedPerSecond = 5f;
}
// Update is called once per frame
void Update()
{
if (isFinished == true)
{ scoreText.text = "Survived time " + (int)scoreAmount;
scoreAmount += pointIncreasedPerSecond * Time.deltaTime;
isFinished = true;
}
else
{
}
}
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag(PLAYER_TAG))
Destroy(gameObject);
{
isFinished = false;
}
}
//if (collision.gameObject.CompareTag(FOLLOWENEMY_TAG))
// Destroy(gameObject);
}
So there is a lot of strange things going on in your code!
First of all you probably do not want to set
isFinished = true;
right from the beginning. Otherwise you want to change the naming ...
Then note that in
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag(PLAYER_TAG))
Destroy(gameObject);
{
isFinished = false;
}
}
What you actually do is
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.CompareTag(PLAYER_TAG))
{
Destroy(gameObject);
}
isFinished = false;
}
so no matter what you collide with you change the state of isFinished. You probably rather wanted to make the brackets wrap both lines.
And then checking and setting
if(isFinished == true)
{
...
isFinished = true;
}
is quite redundant.
And last but not least: You don't need the isFinished at all!
Since you do
Destroy(gameObject);
this Update method anyway will not be executed anymore. So unless there are not multiple instances of this script writing into the same Text it should stop as soon as the object was destroyed anyway.
public class ScorePerSecond : MonoBehaviour
{
private const string ENEMY_TAG = "Enemy";
private const string FOLLOWENEMY_TAG = "Followenemy";
private const string PLAYER_TAG = "Player";
public Text scoreText;
public float scoreAmount;
// Instead of overwriting this hardcoded allow to set it via the Inspector
public float pointIncreasedPerSecond = 5f;
private void Start()
{
scoreAmount = 0f;
}
private void Update()
{
// you probably want to increase the value first and then update the display
// otherwise you always only see the vlaue of the previous frame
scoreAmount += pointIncreasedPerSecond * Time.deltaTime;
scoreText.text = "Survived time " + (int)scoreAmount;
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag(PLAYER_TAG))
{
Destroy(gameObject);
}
}
}
What stays quite unclear to me:
Here no player is dying. You rather check if you collide with a player and kill this object itself.
Did you maybe mean to rather do
Destroy(other.gameObject);
-> In that case, yes you would need the isFinished again.
public class ScorePerSecond : MonoBehaviour
{
private const string ENEMY_TAG = "Enemy";
private const string FOLLOWENEMY_TAG = "Followenemy";
private const string PLAYER_TAG = "Player";
public Text scoreText;
public float scoreAmount;
// Instead of overwriting this hardcoded allow to set it via the Inspector
public float pointIncreasedPerSecond = 5f;
private bool isFinished;
private void Start()
{
scoreAmount = 0f;
}
private void Update()
{
if(isFinished) return;
// you probably want to increase the value first and then update the display
// otherwise you always only see the vlaue of the previous frame
scoreAmount += pointIncreasedPerSecond * Time.deltaTime;
scoreText.text = "Survived time " + (int)scoreAmount;
}
private void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag(PLAYER_TAG))
{
Destroy(other.gameObject);
isFinished = true;
}
}
}

Object jumps into the upper right corner after the start

I hope you could help me. I want my enemy object to stop after a few seconds, and then move on after another few seconds. So far everything works, but the objects jump into the top right corner a few seconds after they are stopped. Can you give me some tips on what I did wrong and how I can do it better.
Ball Control script :
public class Ball_Controller : MonoBehaviour
{
public Rigidbody2D rb;
public float ballForce;
public Rigidbody2D rbtwo;
public loselevel lose;
public float moving;
public startMovement startMove;
private bool isMoving = false;
private void Start()
{
lose = GameObject.FindObjectOfType<loselevel>();
rb = GetComponent<Rigidbody2D>();
moving = transform.position.x;
startMove = GameObject.FindObjectOfType<startMovement>();
}
public void Update()
{
if (moving < transform.position.x)
{
StartCoroutine(stopMovement());
isMoving = true;
}
if (isMoving == true )
{
isMoving = false;
startMove.beginnMovement();
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag.Equals("Player"))
{
Destroy(gameObject);
Destroy(GameObject.FindWithTag("Player"));
lose.startFinish();
}
}
public void BallControll()
{
rb.AddForce(new Vector2(ballForce, ballForce));
rbtwo.AddForce(new Vector2(ballForce, ballForce));
}
public IEnumerator stopMovement()
{
yield return new WaitForSeconds(4f);
rb.velocity = Vector2.zero;
Debug.Log("Stop moving");
}
}
Script to start the enemy after stop:
public class startMovement : MonoBehaviour
{
public Ball_Controller bc;
// Start is called before the first frame update
void Start()
{
bc = GameObject.FindObjectOfType<Ball_Controller>();
}
// Update is called once per frame
void Update()
{
}
public IEnumerator beginMovement()
{
yield return new WaitForSeconds(10f);
bc.BallControll();
Debug.Log("start moving");
}
public void beginnMovement()
{
StartCoroutine(beginMovement());
}
}

How to change the scene after gaze Timer done?

Now I am doing a project for my final term in university and my area changed the scene by gazing(google VR SDK).
At first, I just put a code to change the scene. It worked. But I want some enhance, so I used gaze timer which I watched at youtube (link: https://www.youtube.com/watch?v=bmMaVTV8UqY)
Now, the gaze Timer animation is working but the SceneSwitcher function in NexScene1 code isn't working.
I've done 'build' after adding the new scene that I add gaze timer function.
Also, I used tagged which shown in the youtube.
That's all I did.
public class VRGaze : MonoBehaviour {
public Image imgGaze;
public float totalTime = 2;
bool gvrStatus;
float gvrTimer;
public int distanceOfRay = 10;
private RaycastHit _hit;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (gvrStatus)
{
gvrTimer += Time.deltaTime;
imgGaze.fillAmount = gvrTimer / totalTime;
}
Ray ray = Camera.main.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0.5f));
if(Physics.Raycast(ray, out _hit, distanceOfRay))
{
if(imgGaze.fillAmount == 1 && _hit.transform.CompareTag("NexScene1"))
{
_hit.transform.gameObject.GetComponent<NexScene1>().SceneSwitcher();
}
}
}
public void GVROn() {
gvrStatus = true;
}
public void GVROff() {
gvrStatus = false;
gvrTimer = 0;
imgGaze.fillAmount = 0;
}
}
public class NexScene1 : MonoBehaviour {
public UnityEngine.UI.Scrollbar obj_scroll_;
public GameObject player;
public void PointEnter()
{
StartCoroutine(TimeToAction());
}
public void PointExit()
{
StopAllCoroutines();
}
public void SceneSwitcher()
{
SceneManager.LoadScene("Page2");
}
void Start () {
}
// Update is called once per frame
void Update () {
}
}
Not an actual error but just the SwitchScene function is not called at VRGaze
Not an error but the console says:
It is recommended to use GvrPointerPhysicsRaycaster or
GvrPointerGrahpicRaycaster with GvrPointerInputModule.

Categories

Resources