I want my player to teleport to a GameObject location When I get 7 points.
When I pick up my item and my points change to 7 I want my player to teleport to GameObject's location (Cube) Here is the script C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FpsScoreScript : MonoBehaviour
{
public int points;
public Transform Destination;
public void Start()
{
}
public void Update()
{
if (points == 7)
{
//teleport code here
}
}
}
How do it get it to work. I want to be teleported to the object that is linked to the "public Transform Destination;" Thanks for the answers.
Its true that your player will not move because of the check you had put in the update() function. Now this is the code that will make your player teleport.
Theory:
The code has a private bool isTeleported and a private function Teleport(). In the update function we will check if the points are equal to 7 and isTeleported is false and then call Teleport() function. And there we will set isTeleported to true so that the check in the update() function becomes false so the player will not teleport.
public class FpsScoreScript : MonoBehaviour
{
public int points;
public Transform Destination;
bool isTeleported = false;
public void Update()
{
if (points == 7 && !isTeleported)
{
Teleport();
}
}
void Teleport(){
isTeleported = true;
player.transform.position = destination.transform.position;
}
Simply set your current object's position to Destination's position.
gameObject.transform.position = Destination.position;
However if you put this directly in:
if (points == 7)
{
gameObject.transform.position = Destination.position;
}
You are going to get teleported to the cube forever since your points have not changed and Update is called every frame. You will need to have something to prevent that, like resetting points to 0, for example.
You Might Not Be Able to Move The Player Since Your Player Is Stuck In The Ground. Is That The Case? BTW, Here's The Final Fixed Code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class FpsScoreScript : MonoBehaviour
{
public int points;
public Transform destination;
public bool teleported;
public void Start()
{
}
public void Update()
{
if (points == 7 && !teleported)
{
gameObject.transform.position = destination.position;
teleported = true;
}
}
}
Related
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.
This is my first time making a 2d game. I'm trying to load a scene if the amount of objects with tag "Enemy" is zero. Line 22 doesn't seem to work. I don't get any errors.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelLoader : MonoBehaviour
{
public Animator transition;
public float transitionTime;
public GameObject enemyPrefab;
public GameObject[] enemy;
// Update is called once per frame
void Update()
{
if (enemy == null)
{
enemy = GameObject.FindGameObjectsWithTag("Enemy");
Debug.Log("null!");
LoadNextLevel();
}
}
public void LoadNextLevel()
{
StartCoroutine(LoadLevel(SceneManager.GetActiveScene().buildIndex + 1));
}
IEnumerator LoadLevel(int levelIndex)
{
transition.SetTrigger("Start");
yield return new WaitForSeconds(transitionTime);
SceneManager.LoadScene(levelIndex);
}
}
enemy = GameObject.FindGameObjectsWithTag("Enemy");
Initially enemy is null.
So, in the first frame (the first time Update() is called) it satisfies the if condition and then assign something to enemy.
Then, in the second and later frames, enemy is not null anymore, so it does not satisfies if (enemy == null) condition and the lines in the if statement does not be executed. It means that, the lines enemy = GameObject.FindGameObjectsWithTag("Enemy") and LoadNextLevel(); will never be executed after the first frame.
My suggestion is that rewrite the Update() just like this:
void Update() {
enemy = GameObject.FindGameObjectsWithTag("Enemy"); // get "Enemies" first
// then do something by using it
if (enemy == null) {
Debug.Log("null!");
LoadNextLevel();
}
}
So, I solved the question. The problem was that the condition was before assigning the variable.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelLoader : MonoBehaviour
{
public Animator transition;
public float transitionTime;
// Update is called once per frame
void Update()
{
GameObject[] enemy = GameObject.FindGameObjectsWithTag("Enemy");
if (enemy == null || enemy.Length == 0)
{
LoadNextLevel();
}
//Debug.Log(GameObject.FindGameObjectsWithTag("Enemy"));
}
public void LoadNextLevel()
{
StartCoroutine(LoadLevel(SceneManager.GetActiveScene().buildIndex + 1));
}
IEnumerator LoadLevel(int levelIndex)
{
transition.SetTrigger("Start");
yield return new WaitForSeconds(transitionTime);
SceneManager.LoadScene(levelIndex);
}
}
I believe that it's because the variable enemy is of type array and not a list. Arrays are more difficult to deal with and are of a fixed size.
Instead, consider using a List:
List<GameObject> enemy = new List<GameObject>();
Here are some additional resources to assist you with understanding the difference between arrays and lists.
https://www.w3schools.com/cs/cs_arrays.php
https://www.c-sharpcorner.com/article/c-sharp-list/
The difference/when to use each
Array versus List<T>: When to use which?
https://csharp-station.com/c-arrays-vs-lists/
https://www.educba.com/c-sharp-array-vs-list/
I'm trying to make a Destroy gameobject, wait x seconds, respawn gameobject system. I have 2 scripts and I'm destorying then instantiating it again. I want to use multiples of the same prefab called "Breakable" but have only the one I'm aiming at being destroyed. Similar to games like Minecraft, aim and only the aimed at the block is destroyed.
BlockBreakItem script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BlockBreakItem : MonoBehaviour
{
RaycastHit hit;
int layerMask = 1;
public GameObject breakableObject;
public bool isObjectDestoryed = false;
public int score = 0;
// Update is called once per frame
void Update()
{
breakableDetection();
}
void breakableDetection()
{
Ray rayLocation = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(rayLocation, out hit, 1000, layerMask))
{
print("Detected");
if (Input.GetKey(KeyCode.Mouse0))
{
breakableObject = GameObject.Find("Breakable");
Destroy(breakableObject);
isObjectDestoryed = true;
score = score +1 ;
}
}
}
}
RespawnBrokenObject script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RespawnBrokenObject : MonoBehaviour
{
private BlockBreakItem BlockBreakItem;
public GameObject breakablePrefab;
// Start is called before the first frame update
void Start()
{
BlockBreakItem = GameObject.FindObjectOfType<BlockBreakItem>();
}
// Update is called once per frame
void Update()
{
if (BlockBreakItem.isObjectDestoryed == true)
{
Invoke("respawnObject", 5.0f);
BlockBreakItem.isObjectDestoryed = false;
}
}
void respawnObject()
{
GameObject tempObjName = Instantiate(breakablePrefab);
tempObjName.name = breakablePrefab.name;
BlockBreakItem.breakableObject = tempObjName;
}
}
I hope the code isn't too messy! Always worried it won't be understood xD
Fortunately it's easy and basic in Unity!
You don't do this:
breakableObject = GameObject.Find("Breakable");
The good news is the cast will tell you what object you hit! Great, eh?
It's basically:
hit.collider.gameObject
You can use hit.collider.gameObject.name in a Debug.Log for convenience.
It's that easy!
Note you then (if you need it) get your component on that object with something like .GetComponent<YourBreakishBlock>()... easy.
Note that you can CHECK if the object hit, is one of the "YourBreakishBlock" objects, by simply checking if that component is present.
Note simply google something like "unity physics raycast out hit, which object was hit ?" for endless examples,
https://forum.unity.com/threads/getting-object-hit-with-raycast.573982/
https://forum.unity.com/threads/changing-properties-of-object-hit-with-raycast.538819/
etc.
--
Make this simple class:
public class ExamplePutThisOnACube: MonoBehavior
{
public void SAYHELLO() { Debug.Log("hello!"); }
}
Now put that on SOME cubes but NOT on OTHER cubes.
In your ray code:
ExamplePutThisOnACube teste =
hit.collider.gameObject.GetComponent<ExamplePutThisOnACube>();
and then check this out:
if (teste != null) { teste.SAYHELLO(); }
Now run the app and try pointing at the various cubes!
I have some functionalities within some GameObjects.
These functionalities need to be changed when an upgrade is purchased within the game. The problem is that each function is set on its own object.
The first problem is that the variables don't change when I click on the button. As you can see I have added an onclick value to the button, stating that when the button is clicked. The value should change.
The problem here is that "Object reference not set to an instance"
The second problem I face is that each projectile is fired independently. So if I change the static damage of 1 it won't be transferred to other projectiles.
UpgradeMenu
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UpgradeMenu : MonoBehaviour
{
[SerializeField]
private Text accuracyText;
[SerializeField]
private Text speedText;
[SerializeField]
private Text damageText;
[SerializeField]
private float accuracyMultiplier = 0.7f;
private Weapon weapon;
private Projectile projectile;
private Player player;
void OnEnable()
{
UpdateValues();
}
void UpdateValues ()
{
accuracyText.text = weapon.randomAngle.ToString();
damageText.text = projectile.DamageOnHit.ToString();
speedText.text = player.MaxRun.ToString();
}
public void UpgradeAccuracy ()
{
weapon.randomAngle = (int)weapon.randomAngle * accuracyMultiplier;
UpdateValues();
}
public void UpgradeDamage ()
{
projectile.DamageOnHit = (int)projectile.DamageOnHit + 1;
UpdateValues();
}
}
Projectile (DamageScript)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//[RequireComponent (typeof(Rigidbody2D))]
public class Projectile : MonoBehaviour {
[Header ("Speed")]
public float baseSpeed;
public float randomSpeed;
public Vector2 SpeedV2;
public Vector2 Direction;
[Header ("Damage")]
public int DamageOnHit;
[Header ("Layers")]
public LayerMask solid_layer;
public LayerMask entities_layer;
[Header ("OnHit FX")]
public GameObject HitFxPrefab;
public GameObject DustFxPrefab;
[Header ("Bounce")]
public bool BounceOnCollide = false;
public int bouncesLeft = 0;
[HideInInspector]
public Health owner; // owner of the projectile
private Vector2 Position; // Current position
private Vector2 movementCounter = Vector2.zero; // Counter for subpixel movement
public BoxCollider2D myCollider;
List<Health> healthsDamaged = new List<Health>(); // List to store healths damaged
void OnCollideWith (Collider2D col, bool horizontalCol = true) {
var component = col.GetComponent<Health> ();
// If the target the hitbox collided with has a health component and it is not our owner and it is not on the already on the list of healths damaged by the current hitbox
if (component != null && component != owner && !healthsDamaged.Contains(component)) {
// Add the health component to the list of damaged healths
healthsDamaged.Add (component);
// Apply the damage
var didDamage = component.TakeDamage (DamageOnHit);
// Destroy the projectile after applying damage
if (didDamage) {
DestroyMe ();
return;
}
}
// if the projectile hit's a solid object, destroy it
if (col.gameObject.layer == (int)Mathf.Log(solid_layer.value, 2)) {
DestroyMeWall ();
return;
}
}
void OnCollideWithEntity(Collider2D col) {
var component = col.GetComponent<Health> ();
// If the target the hitbox collided with has a health component and it is not our owner and it is not on the already on the list of healths damaged by the current hitbox
if (component != null && component != owner && !healthsDamaged.Contains(component)) {
// Add the health component to the list of damaged healths
healthsDamaged.Add (component);
// Apply the damage
var didDamage = component.TakeDamage (DamageOnHit);
// Destroy the projectile after applying damage
if (didDamage) {
DestroyMe ();
}
}
}
First of all, change
[Header ("Damage")]
public int DamageOnHit;
to static
public static int DamageOnHit = /*your starting value*/;
This ensures that all projectiles will share the same damage it deals on a hit.
For instance, if you currently have 10 projectiles in a scene, and DamageOnHit is 2, they all will deal 2 damage.
Without the static, each of the projectile will have it's own DamageOnHit. This brings us to the next case too:
If each projectile had it's own DamageOnHit, and we want to modify DamageOnHit, we need to specify which projectile's damage to modify.
But if it's static, it becomes much simpler as ALL of the projectile shares the same DamageOnHit.
Now, if you wanted to change the DamageOnHit for ALL projectiles, just do
Projectile.DamageOnHit = /*Your new damage value*/
Also, your null reference exception occured due to the fact that you never did assign your projectile in UpgradeMenu.
(Notice how you never did projectile = /*your projectile*/ in UpgradeMenu.cs?)
By default, that will make the variable null. And trying to do null.DamageOnHit += 1 would make no sense.
Small Edit: Making a variable static would also mean that you can't expose it to the inspector. But you can assign a starting value like the code shown initially.
I have created a game where when the user breaks all the blocks he is taken to the next scene but this is not happening despite adding all of the scenes I have in the build settings. I have no errors whatsoever and the scene is written correctly. Can someone help me resolve this, please?
This is the build settings
Bricks script : (where the scene is called)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bricks : MonoBehaviour {
public LevelManager myLevelManager;
public static int brickCount = 0;
public int maxNumberOfHits = 0;
int timesHit;
public AudioClip BlockBreaking;
// Use this for initialization
void Start () {
timesHit = 0;
if(this.gameObject.tag == "BrickHit")
{
brickCount++;
}
if(this.gameObject.tag == "BrickHitTwice")
{
brickCount++;
}
}
void OnCollisionEnter2D()
{
timesHit++;
if (timesHit == maxNumberOfHits)
{
brickCount--;
Destroy(this.gameObject);
}
if(brickCount == 0)
{
myLevelManager.LoadLevel("Level1.2"); //THIS SCENE IS NOT LOADING
}
if(this.gameObject.tag == "BrickHit") //If the gameObject (Block One Point) with the tag "BrickHit" is hit
{
Scores.scoreValue += 1;//The user will be rewarded 1 point
AudioSource.PlayClipAtPoint(BlockBreaking, transform.position);
}
if(this.gameObject.tag == "BrickHitTwice") //If the gameObject (Block Two Points) with the tag "BrickHitTwice" is hit
{
Scores.scoreValue += 2; //The user will be rewarded 2 points
AudioSource.PlayClipAtPoint(BlockBreaking, transform.position);
}
}
LevelManager Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class LevelManager : MonoBehaviour {
public void LoadLevel(string name)
{
print("Level loading requested for" + name);
SceneManager.LoadScene(name);
}
I suspect that your bug may lie in the fact that you're Destroy()ing the gameObject before it can load the next scene; you get a race condition on what will finish first; LoadScene or Destroy() - which would explain why it sometimes work. You should never assume it is a bug in the framework before understanding your problem.
Try putting the Destroy() after the LoadScene() or with a delay to understand if this is your issue.
Also, your LevelManager can be made static and doesn't need to inherit from MonoBehaviour since it doesn't use gameObject functionality.
public static class LevelManager {
public static void LoadLevel(string name)
{
print("Level loading requested for" + name);
SceneManager.LoadScene(name);
}
}
Used by doing LevelManager.LoadLevel("MyLevel");, but then you may question what is more effective, doing LevelManager.LoadLevel or SceneManager.LoadLevel, as they will do the exact same thing.
The main issue that you're having is not having a single source to check brickCount instead each individual brick is maintaining its own count. I would recommend moving the brick counting logic into a separate class. It would seem like LevelManager would a good place for it. So in LevelManager add:
private int brickCount = 0;
public void AddBrick()
{
brickCount++;
}
public void RemoveBrick()
{
brickCount--;
// Check if all bricks are destroyed
if (brickCount == 0)
{
LoadLevel("Level1.2");
}
}
And then in your brick script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bricks : MonoBehaviour {
public LevelManager myLevelManager;
public int maxNumberOfHits = 0;
int timesHit;
public AudioClip BlockBreaking;
// Use this for initialization
void Start () {
timesHit = 0;
myLevelManager.AddBrick();
// I'm not sure why you were checking the tag here, since the result was the same
}
void OnCollisionEnter2D()
{
timesHit++;
if (timesHit == maxNumberOfHits)
{
myLevelManager.RemoveBrick();
Destroy(this.gameObject);
}
/* This looks like the player is getting score whether the brick is destroyed or not. Also, it would appear the player won't get scored on the final brick */
if(this.gameObject.tag == "BrickHit") //If the gameObject (Block One Point) with the tag "BrickHit" is hit
{
Scores.scoreValue += 1;//The user will be rewarded 1 point
AudioSource.PlayClipAtPoint(BlockBreaking, transform.position);
}
if(this.gameObject.tag == "BrickHitTwice") //If the gameObject (Block Two Points) with the tag "BrickHitTwice" is hit
{
Scores.scoreValue += 2; //The user will be rewarded 2 points
AudioSource.PlayClipAtPoint(BlockBreaking, transform.position);
}
}
}