How to destroy and randomly generate pipes of different size? - c#

So this is the code which I use to generate the pipe thingies, but after it generates it I want to destroy it after its out of screen or any other possible way, maybe after 3 seconds or any way possible and I want this pipe thingies to generate randomly in different sizes you get me ryt just like flappy bird.
using UnityEngine;
using System.Collections;
public class Generate : MonoBehaviour {
public GameObject lightnest;
public float initialDelay;
public float finalDelay;
public float rampDuration;
protected float _delay;
protected float _runTime;
protected float _timeSinceSpawn;
// Use this for initialization
void Start () {
_delay = initialDelay;
_runTime = _timeSinceSpawn = 0.0f;
}
// Update is called once per frame
void Update() {
_runTime += Time.deltaTime;
_timeSinceSpawn += Time.deltaTime;
_delay = Mathf.Lerp(initialDelay, finalDelay, _timeSinceSpawn / rampDuration);
if (_timeSinceSpawn > _delay) Spawn();
}
protected void Spawn()
{
_timeSinceSpawn = 0.0f;
Instantiate(lightnest);
}
}

Use gameobject.transform.localScale to change the size on Start. And use gameobject.transform.position to check if the object is out of the screen. And use Destroy(gameobject) to destroy the object.
https://docs.unity3d.com/ScriptReference/Transform.html
Hope this helps!

I recommend you to watch this tutorial.
Object pooling is the most efficient way handle this kind of situtation.

Related

Unity 2D - Attack system only works while moving

I've recently started coding on Unity, trying to make a game. So long it's been fine, but I faced a problem.
I've implemented a script for the Attack System:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AttackDamage : MonoBehaviour
{
private float attackDamage = 20;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.GetComponent<Health>() != null)
{
Health health = other.GetComponent<Health>();
health.TakeDamage(attackDamage);
}
}
}
And I also implemented one for the Health System:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class Health : MonoBehaviour
{
public Image healthBar;
public float healthAmount = 100;
private void Update()
{
if (healthAmount <= 0)
{
SceneManager.LoadScene(0);
}
}
public void TakeDamage(float Damage)
{
healthAmount -= Damage;
healthBar.fillAmount = healthAmount / 100;
}
public void Healing(float healPoints)
{
healthAmount += healPoints;
healthAmount = Mathf.Clamp(healthAmount, 0, 100);
healthBar.fillAmount = healthAmount / 100;
}
}
And it works prety well.
But as you read on the title, the attack only actually works right after I move. If I try to attack while I'm not moving, the attackArea appears on the scene, but doesn't deal damage. And i can't figure out why.
Do you have any idea on what could be the problem?
Here there's also a video of what actually happens, in the game:
https://drive.google.com/drive/folders/1BTYTNz_yzus-eRLnjsgB0hsYLU5sQm2k?usp=sharing
I have no idea on how to solve this problem, since I've copied the code from a source online, which actually works prorperly.
The code is exactly the same, apart form the script for the Health System, which is not shown on the video, but which also shouldn't make that much of a difference.
So i really don't know how to handle this.
Thanks for the help :)
If you are looking to deal continuous damage while any health-character is in the trigger, use OnTriggerStay2d instead.
Otherwise, if you are looking to deal damage when the user presses a button, you can do the following:
Have a hit-box collider that stores all enemy in range. (By adding enemy to a list when it enters the hit-box, and removing them from list when they exit.)
When the attack is triggered, fetch all enemy in the list from 1. and deal damage to all.
Code-wise, looks something like this:
public class AttackDamage : MonoBehaviour
{
private HashSet<Health> inRange = new HashSet<Health>();
private float attackDamage = 20;
private void OnTriggerEnter2D(Collider2D other)
{
if (other.GetComponent<Health>() != null)
{
// Add to set of characters that are in range of attack
inRange.Add(other.GetComponent<Health>());
}
}
private void OnTriggerExit2D(Collider2D other)
{
var charHealth = other.GetComponent<Health>();
if (charHealth != null) {
// Remove, since it exit the range of the attacking character.
inRange.Remove(charHealth);
}
}
// Call this function whenever you want to do damage to all characters in range.
public void Attack(){
foreach(var character in inRange){
character.TakeDamage(attackDamage);
}
}
}
Then somewhere else... (example, in your player.)
public class YourPlayer {
// ...
// Inspector reference to the hitbox
[SerializeField]
private AttackDamage attackHitbox;
private void Update(){
// Attack when button is pressed (or something).
if (Input.GetKeyDown(KeyCode.A)) {
attackHitbox.Attack();
}
}
// ...
}
Finally, if you are looking to deal damage at specific points in an animation, the term you should search for is Key-Frame. Look for a tutorial on Animation, then Key-Frame.
Once you learn about it, you can use the above mentioned method, but call the damage script on your desired key-frames.

Updating Enemy Health bar in unity 2d

Why is this connecting all the health bars of my enemies together, even though their actual health is decreasing at its specified rate?
public class FillHealth : MonoBehaviour
{
Image HealthBar;
private NormalMonster normalMonster;
// Start is called before the first frame update
void Start()
{
HealthBar = GetComponent<Image>();
normalMonster = GameObject.FindGameObjectWithTag("Normal Monster").GetComponent<NormalMonster>();
}
// Update is called once per frame
void Update()
{
UpdateHealthLeft();
}
public void UpdateHealthLeft()
{
if (normalMonster.healthLeft > 0)
{
HealthBar.fillAmount = normalMonster.healthLeft / normalMonster.setHealth;
}
}
}
This is the script that is being referenced in FillHealth. As far as I understand it, since the variable isn't static, then the values should not be shared. It should find fill the health bar for each individual enemy.
public class NormalMonster : MonoBehaviour
{
private float _normalSpeed = 2f;
private float _BaseHealth = 20f;
private float _HealthModifier;
public float setHealth;
public float healthLeft;
// Start is called before the first frame update
void Start()
{
UpdateHealth();
}
// Update is called once per frame
void Update()
{
NormMonMov();
}
public void OnTriggerEnter2D(Collider2D other)
{
if (other.tag == "Arrows")
{
healthLeft -= Arrows.Damage;
Destroy(other.gameObject);
if (healthLeft <= 0f)
{
Destroy(this.gameObject);
EarnedGold.earnedGold += 7;
Spawn_Manager.enemyCount--;
}
}
}
public void UpdateHealth()
{
if (StageMode.StageLvl > 5)
{
_HealthModifier = (StageMode.StageLvl * 0.01f) * _BaseHealth;
setHealth = Mathf.Round(_BaseHealth + _HealthModifier);
}
else
{
setHealth = _BaseHealth;
}
healthLeft = setHealth;
}
public void NormMonMov()
{
transform.Translate(Vector3.left * _normalSpeed * Time.deltaTime);
transform.position = new Vector3(Mathf.Clamp(transform.position.x, -7.0f, 10), transform.position.y, 0);
}
}
Any help would be greatly appreciated for this guy who just start playing with unity this weekend.
I believe the issue is with normalMonster = GameObject.FindGameObjectWithTag("Normal Monster").GetComponent<NormalMonster>();
If you have two Monsters
Both have two scripts attached, FillHealth and NormalMonster
Both the "FillHealth" scripts look for the FIRST gameobject in the scene that has a script with tag NormalMonster so both monsters are pointing to the exact same NormalMonster script (the first in the list)
Change "GameObject" capital G to "gameObject" lower case g
Still not the best way to code this, but that may work I think
Instead of getting the image, get the rect transform, like this
public RectTransform healthBar;
and change the length with:
healthBar.sizeDelta = new Vector2(normalMonster.healthLeft,healthBar.sizeDelta.y);

Spawn objects don't sync

I am trying to do a multiplayer game using photon and when I try to spawn objects near my player, they don't sync the same for both players. What I should add in the inspector (Already both objects that need to be spawn have PhotonView and PhotonView Transform). What I should add to my script?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Photon.Pun;
public class SpawnFix : MonoBehaviour
{
public GameObject[] powersPrefab;
public Transform[] points;
public float beat = (60 / 130) * 2;
private float timer;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
[PunRPC]
void Update()
{
if (timer > beat)
{
GameObject powers = Instantiate(powersPrefab[Random.Range(0, 2)]);
powers.transform.localPosition = points[Random.Range(0, points.Length)].position;
timer -= beat;
}
timer += Time.deltaTime;
}
}
Given the similarity of your code:
Was exactly this question not already asked and answered here? (Only that there you accepted the stupid answer ^^)
Anyway
well ... that's not how [PunRPC] works. It would need to be invoked via the network somewhere e.g. via
var photonView = PhotonView.Get(this);
photonView.RPC("Update", RpcTarget.All);
HOWEVER, you definitely wouldn't want to synchronize the Update method itself every frame but rather only the spawning.
You probably should rather use PhotonNetwork.Instantiate
public class SpawnFix : MonoBehaviour
{
public GameObject[] powersPrefab;
public Transform[] points;
public float beat = (60 / 130) * 2;
private float timer;
// Update is called once per frame
void Update()
{
// only run on the master client
if(!PhotonNetwork.isMasterClient) return;
if (timer > beat)
{
PhotonNetwork.Instantiate(
powersPrefab[Random.Range(0, 2)].name,
points[Random.Range(0, points.Length)].position,
Quaternion.idendity,
0);
timer -= beat;
}
timer += Time.deltaTime;
}
}
For this the prefabs need to be registered in the Photon as spawnable objects.

Unity/C# How to run Script continuously on User Interface Select Event Trigger

Complete beginner here, I'm looking to smoothly rotate my sphere continuously while the button it is a child of is selected in the UI.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ButtonHandler : MonoBehaviour
{
private Vector3 rotationDirection = new Vector3(0, .25f, 0);
private float smoothTime = 1;
private float convertedTime = 200;
private float smooth;
public void SetText()
{
Transform target = transform.Find("Globe");
smooth = Time.deltaTime * smoothTime * convertedTime;
target.Rotate(rotationDirection * smooth);
}
}
The issue is that when I go to select my button, the function is only ran a single time, whereas I want it to run continuously until deselected.
** Note this is using the Select Event Trigger
Any help would be greatly appreciated!
Please let me know if you need additional information to answer
Thanks!
The way I see it, you could use the Update() function (native to MonoBehaviour) to run the few lines of code that you have and rotate your sphere.
Then set up functions for when the button is clicked and when it is released to change a boolean variable that is evaluated in the Update() function...
Breif example below...
private bool rotate = false;
void Update()
{
if (rotate)
{
Transform target = transform.Find("Globe");
smooth = Time.deltaTime * smoothTime * convertedTime;
target.Rotate(rotationDirection * smooth);
}
}
public void StartRotation() //on click
{
rotate = true;
}
public void StopRotation() //on release
{
rotate = false;
}
Further improvements might include Finding the "Globe" object in the constructor and storing a reference to it for future use during rotation.
Another method involves setting the rotation speed of the sphere rather than directly transforming (rotating) it constantly (make the Physics Enginge do the work) but try this first to get it working.
Extending Nicholas Sims' answer a slightly better option would be a Coroutine. Coroutines can be seen as temporary Update method as by default each iteration step is Don right after the Update call.
This way you don't waste resources to an Update method when the trigger is not activated anyway.
Also note that Find in general is a very expensive call and should be avoided wherever possible! The least you want to do is using it in Update every frame! Instead store the reference as early as possible .. maybe even already via a field in the Inspector!
// For storing the reference
// If possible already reference this via the Inspector
// by drag&drop to avoid Find entirely!
[SerializeField] private Transform target;
private bool rotate;
private void Start()
{
if(!target) target = GameObject.Find("Globe");
}
public void ToggleRoutine()
{
rotate = !rotate;
if(!rotate)
{
StopAllCoroutines();
}
else
{
StartCoroutine (RotateContinuously());
}
}
private IEnumerator RotateContinuously()
{
// Whut? o.O
// No worries, this is fine in a Coroutine as long as you yield somewhere inside
while (true)
{
// You should use Find only once .. if at all. You should avoid it wherever possible!
if(!target) target = transform.Find("Globe");
smooth = Time.deltaTime * smoothTime * convertedTime;
target.Rotate(rotationDirection * smooth);
// Very important for not freezing the editor completely!
// This tells unity to "pause" the routine here
// render the current frame and continue
// from this point on the next frame
yield return null;
}
}
If you rather want a "While Pressed" behaviour, so you want the rotation going on while the user holds the mouse/button pressed and stop as soon as he released it, you can use the IPointerXYHandler interfaces like
public class WhilePressedRotation : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler
{
[SerializeField] private Transform target;
private void Start ()
{
if(!target) target = GameObject.Find("Globe");
}
public void OnPointerDown(PointerEventData pointerEventData)
{
StartCoroutine(RotateContinuously());
}
public void OnPointerUp(PointerEventData pointerEventData)
{
StopAllCoroutines();
}
public void OnPointerEnter(PointerEventData pointerEventData)
{
// Actually not really needed
// but not sure if maybe required for having OnPointerExit work
}
public void OnPointerExit(PointerEventData pointerEventData)
{
StopAllCoroutines();
}
private IEnumerator RotateContinuously()
{
// Whut? o.O
// No worries, this is fine in a Coroutine as long as you yield somewhere inside
while (true)
{
// You should use Find only once .. if at all. You should avoid it wherever possible!
if(!target) target = transform.Find("Globe");
smooth = Time.deltaTime * smoothTime * convertedTime;
target.Rotate(rotationDirection * smooth);
// Very important for not freezing the editor completely!
// This tells unity to "pause" the routine here
// render the current frame and continue
// from this point on the next frame
yield return null;
}
}
}
Or a more general code
public class WhilePressedButton : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler
{
[SerializeField] private UnityEvent whilePressed;
public void OnPointerDown(PointerEventData pointerEventData)
{
StartCoroutine(ContinuousButton());
}
public void OnPointerUp(PointerEventData pointerEventData)
{
StopAllCoroutines();
}
public void OnPointerEnter(PointerEventData pointerEventData)
{
// Actually not really needed
// but not sure if maybe required for having OnPointerExit work
}
public void OnPointerExit(PointerEventData pointerEventData)
{
StopAllCoroutines();
}
private IEnumerator ContinuousButton()
{
// Whut? o.O
// No worries, this is fine in a Coroutine as long as you yield somewhere inside
while (true)
{
whilePressed.Invoke();
// Very important for not freezing the editor completely!
// This tells unity to "pause" the routine here
// render the current frame and continue
// from this point on the next frame
yield return null;
}
}
}
And reference a method in whilePressed like usually in a button onClick via Inspector or code.

Give every prefab his own speed

Is was building a star background simulation for a spaceshooter game in UNITY3D.
In c# i built The Instantitiation of prefabs which look like dots. While the generation of multiple dots on random spots on the x-axis go perfectly i wanted to add one thing.
Random speed. The problem is that i don't know how to give every prefab his own speed en keep it instead of getting overwritten by the next randomnization.
backgroundloop.cs
using UnityEngine;
using System.Collections;
public class backgroundloop : MonoBehaviour {
public GameObject star;
public float spawnTime;
private GameObject starPrefab;
private float timestamp;
//public float hoogte = Random.Range(-1.01f,1.1f);
//public float rotationPrefab = transform.localEulerAngles.z;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if (Time.time > timestamp) {
starPrefab = Instantiate
(star, new Vector3(7,Random.Range(-5.0f,5.0f),0),
Quaternion.Euler(0, 0, 0)) as GameObject;
timestamp = Time.time + spawnTime;
}
}
}
And the problem is in this script:
prefabMovement.cs
using UnityEngine;
using System.Collections;
public class prefabMovement : MonoBehaviour {
float speed = Random.Range (-3f, -0.1f);
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
transform.Translate (new Vector3(-1f,0,0) * Time.deltaTime);
if (transform.position.x < -6.8) {
Destroy (this.gameObject);
}
}
}
Huh? So the speed is overwritten and eventually ends up being the same on all instantiated prefabs?
Maybe it helps if initialize the speed variable inside the Start method.

Categories