Unable to add a number to a variable (New to Unity) - c#

still reasonably new to Unity and c# and so this may be a small and insignificant question to some but it has stumped me. I'm trying to add a number to my score when a game object is destroyed, I have a target script attached to a target prefab.
using UnityEngine;
public class Target : MonoBehaviour
{
public float health = 50f;
private float TotalCubesHit = 0;
public void TakeDamage (float amount){
health = health - (amount);
if (health <= 0f) {
TotalCubesHit += 1;
Die();
}
}
public void Die()
{
Destroy(gameObject);
Debug.Log(TotalCubesHit);
}
}
For some reason, it never gets above one and I'm unsure as to why? any help would be appreciated

Simplest way would be to use a static counter. This makes it a class field which is "shared" among all instances or better said is simply not bound to any instance:
public class Target : MonoBehaviour
{
public float health = 50f;
private static float totalCubesHit = 0;
// Optionally add a public ReadOnly property
// for allowing external access
public static float TotalCubesHit => totalCubesHit;
public void TakeDamage (float amount)
{
health -= amount;
if (health <= 0f)
{
totalCubesHit++;
Die();
}
}
public void Die()
{
Destroy(gameObject);
Debug.Log(totalCubesHit);
}
}
A useful side-effect (and therefore I added the public ReadOnly property): You can now also access it from any other script by simply using
Target.TotalCubeHits
while being sure it can not be changed by other scripts.

It is because it is incremented only once .. thats right before it dies.
public void TakeDamage (float amount){
health = health - (amount);
TotalCubesHit += 1; // <-- Move that outside of the if statement
if (health <= 0f) {
Die();
}
}
**EDIT: I have been re-reading your question and one thing i dont quite follow is: "I'm trying to add a number to my score when a game object is destroyed". If your intent is to keep track of the totalCubeHits from different Target objects that are destroyed, this wont work unless you are re-using the same Target object. If you create a new instance each time, TotalCubeHits will always start from 0.
Store the TotalCubeHits outside or initialize it with a number from previous object.

Related

How to Instantiate an object onto a path in Unity

I am recently new to c# and I need some help.
Essentially I have two scripts, one for spawning objects and one for moving an object along a path. I need a way to combine these two mechanics so that when a new object is instantiated it automatically joins the path and follows it.
The path is made using iTween.
![The objects the scripts are attached to] (https://i.stack.imgur.com/QPQn2.png)
I've tried changing the variable m_PlayerObj to the cube prefab and I've tried adding the Path script to the instantiation script but nothing seems to work.
The scripts attached do nkt include these attempts I made as I wanted to make the code very clear.
Spawner script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnerScript : MonoBehaviour
{
public GameObject cubeprefab;
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Instantiate(cubeprefab, transform.position, Quaternion.identity);
}
}
}
Path script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Path : MonoBehaviour
{
public GameObject m_PlayerObj;
public Transform[] positionPoint;
[Range(0, 1)]
public float value;
// Start is called before the first frame update
void Start()
{
Debug.Log(iTween.PathLength(positionPoint));
}
float tempTime;
// Update is called once per frame
void Update()
{
if (value < 1)
{
value += Time.deltaTime / 10;
}
iTween.PutOnPath(m_PlayerObj, positionPoint, value);
}
private void OnDrawGizmos()
{
iTween.DrawPath(positionPoint,Color.green);
}
}
As stated above, any help would be greatly appreciated as I am really stuck on this conceot and since I am new to Unity I really can’t see a way around it // how to fix it.
Instead of only storing the player object in the Path script, store a collection of objects. That way, the path can keep track of more than one object.
//public GameObject m_PlayerObj; // Get rid of this
public List<GameObject> followers; // Add this
Then, in your Update loop, you can loop through all of them.
void Update()
{
for (var i = 0; i < followers.Length; ++i)
{
if (value < 1)
{
value += Time.deltaTime / 10;
}
iTween.PutOnPath(m_PlayerObj, positionPoint, value);
}
}
Of course, now, you need to make sure you pass your cube instance to the Path GameObject when you spawn it, so the path knows about the cube follower. That means your spawner also needs to know about the path.
public class SpawnerScript : MonoBehaviour
{
public GameObject cubeprefab;
public Path path; // Need to populate this in the Editor, or fetch it during Awake()
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var cubeInst = Instantiate(cubeprefab, transform.position, Quaternion.identity);
path.followers.Add(cubeInst);
}
}
}
Now a new problem is going to be that each object is going to be at the same position on the path, because the path only stores one value - a better term might be progress. So if they're all the same, like the cube, you won't be able to tell because they'd overlap.
So you have to decide what you want to do instead. Evenly space them? You could do that with some math. Or have them all start from the beginning and keep track of their progress separately? Then you'd need to store progress for each of them. A better place to do that is probably on the cube object, which means you need to add a new script to your cube prefab:
public class PathFollower : MonoBehaviour
{
[Range(0, 1)]
public float pathProgress;
}
And, you need to start referring to the prefab by this script, instead of just GameObject:
public class SpawnerScript : MonoBehaviour
{
public PathFollower pathFollower;
public Path path; // Need to populate this in the Editor, or fetch it during Awake()
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var followerInst = Instantiate(pathFollower, transform.position, Quaternion.identity);
path.followers.Add(followerInst);
}
}
}
public class Path : MonoBehaviour
{
//public GameObject m_PlayerObj; // Get rid of this
public List<PathFollower> followers; // Add this
//...
Finally, you need to make sure to use the individual progress for each path follower, rather than a single progress value like your old Path script did:
for (var i = 0; i < followers.Count; ++i)
{
if (followers[i].pathProgress < 1)
{
followers[i].pathProgress += Time.deltaTime / 10;
}
iTween.PutOnPath(followers[i].gameObject, positionPoint, followers[i].pathProgress);
}
Putting it all together (separate files of course, with their own includes!):
public class SpawnerScript : MonoBehaviour
{
public PathFollower pathFollower;
public Path path; // Need to populate this in the Editor, or fetch it during Awake()
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
var followerInst = Instantiate(pathFollower, transform.position, Quaternion.identity);
path.followers.Add(followerInst);
}
}
}
public class Path : MonoBehaviour
{
//public GameObject m_PlayerObj; // Get rid of this
public List<PathFollower> followers; // Add this
public Transform[] positionPoint;
//[Range(0, 1)]
//public float value; // Don't need this anymore either
// Start is called before the first frame update
void Start()
{
Debug.Log(iTween.PathLength(positionPoint));
}
// Update is called once per frame
void Update()
{
for (var i = 0; i < followers.Count; ++i)
{
if (followers[i].pathProgress < 1)
{
followers[i].pathProgress += Time.deltaTime / 10;
}
iTween.PutOnPath(followers[i].gameObject, positionPoint, followers[i].pathProgress);
}
}
private void OnDrawGizmos()
{
iTween.DrawPath(positionPoint,Color.green);
}
}
public class PathFollower : MonoBehaviour
{
[Range(0, 1)]
public float pathProgress;
}

An object reference is required for the non-static field, method, or property 'PlayerMovement.activeMoveSpeed'

Just picked up Unity about a week ago, I have really been struggling to fix this problem and referencing overall, I'm well aware that I need to read and watch more tutorials, I just want to get this fixed so I can continue working on my game.
An object reference is required for the non-static field, method, or property PlayerMovement.activeMoveSpeed
The first problem was referencing the other Game Object and script, I not sure if its completely fixed now but
at least that not the error that its giving me anymore, I've tried doing what this link says
https://answers.unity.com/questions/1119537/help-how-do-i-referenceaccess-another-script-in-un.html
but as you might see it hasn't worked out, every single time I get the compile errors fixed the script doesn't work because the object reference is not set to an instance of an object, any help would be extremely appreciated (the script I'm trying to reference will be at the end)
Thanks
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ShieldAttack : MonoBehaviour
{
public GameObject shield;
private Vector3 mousePos;
private Camera mainCam;
public bool isDashing;
private Rigidbody2D rb;
GameObject Player;
PlayerMovement playerMovement;
Shooting shooting;
// Start is called before the first frame update
void Start()
{
GameObject.FindGameObjectWithTag("EnemyMelee");
GameObject.FindGameObjectWithTag("Enemy2");
mainCam = GameObject.FindGameObjectWithTag("MainCamera").GetComponent<Camera>();
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
mousePos = mainCam.ScreenToWorldPoint(Input.mousePosition);
Vector3 rotation = mousePos - transform.position;
float rotZ = Mathf.Atan2(rotation.y, rotation.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0, 0, rotZ);
if (Input.GetKeyDown(KeyCode.Space) && playerMovement.dashCoolCounter <= 0 && playerMovement.dashCounter <= 0)
{
isDashing = true;
Instantiate(shield, shooting.bulletTransform.position, Quaternion.identity);
if (PlayerMovement.activeMoveSpeed == 5)
{
DestroyShield();
isDashing = false;
}
}
}
void DestroyShield()
{
Destroy(gameObject);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float moveSpeed;
public Rigidbody2D rb2d;
private Vector2 moveInput;
public float activeMoveSpeed;
public float dashSpeed;
public float dashLength = 0.5f, dashCooldown = 1f;
public float dashCounter;
public float dashCoolCounter;
[SerializeField] private TrailRenderer tr;
//Start is called before the first frame update
void Start()
{
activeMoveSpeed = moveSpeed;
}
//Update is called once per frame
void Update()
{
if(dashCounter > 0)
{
tr.emitting = true;
}
if(dashCounter < 0)
{
tr.emitting = false;
}
moveInput.x = Input.GetAxisRaw("Horizontal");
moveInput.y = Input.GetAxisRaw("Vertical");
moveInput.Normalize();
rb2d.velocity = moveInput * activeMoveSpeed;
if (Input.GetKeyDown(KeyCode.Space))
{
if (dashCoolCounter <=0 && dashCounter <=0)
{
activeMoveSpeed = dashSpeed;
dashCounter = dashLength;
}
}
if (dashCounter > 0)
{
dashCounter -= Time.deltaTime;
if (dashCounter <= 0)
{
activeMoveSpeed = moveSpeed;
dashCoolCounter = dashCooldown;
}
}
if (dashCoolCounter > 0)
{
dashCoolCounter -= Time.deltaTime;
}
}
}
You are using class name PlayerMovement.activeMoveSpeed instead of object name playerMovement.activeMoveSpeed in this part of code:
if (PlayerMovement.activeMoveSpeed == 5)
{
DestroyShield();
isDashing = false;
}
Also I cannot see playerMovement variable initialization in the ShieldAttack script. You need to mark it with the [SerializeField] attribute or make it public to be able to assign it in the inspector. Or initialize it in your Start() method along with the other fields.
When you create a variable, you do <type> <name>, where <type> is the kind of thing you want to create and <name> is how you want to reference the instance you created.
We as programmers are generally lazy, so if we wanted to create a pineapple we would create an instance of the Pineapple class. What do we call it? Typically just pineapple, and then you wind up with code like
Pineapple pineapple;
The first term means you're wanting to create a Pineapple, and the second term means you're going to refer to that instance as pineapple. Note the capitalization differences there.
The issue with your code is that you've created an instance of PlayerMovement:
PlayerMovement playerMovement;
but when you're trying to use the instance in your code, you are using the class name PlayerMovement and not the instance name playerMovement. Again, note the capitalization difference.
The particular line that you've got:
if (PlayerMovement.activeMoveSpeed == 5)
should instead be:
if (playerMovement.activeMoveSpeed == 5)
to refer to the instance and not the class.
It is possible to have things that are common to all instances of a class. Those are static items and you would access them by referencing the class name instead of the instance name.
The error message you got:
An object reference is required for the non-static field, method, or property PlayerMovement.activeMoveSpeed
was telling you that you were trying to access a part of the class, but it wasn't static, so you needed to reference the object (instance).

My value keeps on getting set to 1 in unity

So I have been trying to get a difficulty system to work in my game but I have encountered a problem, my float keeps on getting reset to 1, I have the larger script on a prefab that is in another prefab and the small script is one I have on the "DifficultyController". I have tried looking through the files but I cannot find any instance of the number 1 I have also tried making a new prefab to see if that was the problem but still the number 1!
The script in the prefab:
public float health;
public GameObject deathEffect;
public float difficulty;
private float destroy;
private void Update()
{
if (health <= 0)
{
//Instantiate(deathEffect, transform.position, Quaternion.identity);
Destroy(gameObject);
}
}
private void Start()
{
EnemyDifficulty(difficulty);
}
public void TakeDamage (float damage)
{
health -= damage;
}
public void EnemyDifficulty(float chance)
{
Debug.Log(chance);
destroy = Random.Range(0, 50);
if (destroy <= 3) { Destroy(gameObject); }
chance = Random.Range(chance / 2, chance * 2);
Debug.Log(chance);
if (chance <= 8 && chance >= 3)
{
BasicEnemy();
}
if (chance <= 20 && chance >= 8)
{
MediumEnemy();
}
if(chance <= 3)
{
Destroy(gameObject);
Debug.Log("Destroeyed");
}
}
public void BasicEnemy()
{
Debug.Log("basic");
}
public void MediumEnemy()
{
Debug.Log("medium");
}
}
Code in the "DifficultyController"
public Enemy enemy;
public float difficultye = 5;
// Update is called once per frame
void Update()
{
enemy.difficulty = difficultye;
}
You haven't specified which variable keeps getting set to one, but I assume it's either health, difficulty, or difficultye. This is because they are public, meaning that they can be edited in the inspector. If the inspector value differs from the code value, then the inspector value will override that of our code.
You can fix this by changing the variables in editor, marking them as [System.NonSerialized], or setting them to private, depending on which best fits your needs.

How to push enemy back with a force when it's hitted?

im working on simple battle system in Unity, I have already added damage system but I want to upgrade it and push enemy back whenever he gets damage. I have made some tries but nothing worked correctly. Here is a litte bit of my code. I already appreciate for any suggestions.
public class EnemyTakeDmg : MonoBehaviour
{
public int health = 8;
private Rigidbody myBody;
public float knockBackForce = 5f;
public event System.Action OnDeath;
private void Start()
{
myBody = GetComponent<Rigidbody>();
}
public void TakeDamage(int damage)
{
health -= damage; // TAKING DAMAGE
// PUSHING ENEMY BACK ???
if(health <= 0)
{
Die();
}
}
public void Die()
{
if(OnDeath != null)
{
OnDeath();
}
Destroy(gameObject);
}
}
I'd suggest to somehow get a reference to the object that made the shot. This is necessary to calculate direction of the knockback.
After that, it is pretty easy!
public void TakeDamage(int damage, Transform shooter)
{
//Take damage
health -= damage;
//Knockback
Vector3 direction = (transform.position - shooter.position).normalized;
myBody.AddForce(direction * knockBackForce);
}
The direction might be the opposite, so you may need to switch shooter.position and transform.position.

Unity code cleaning: writing general shooting script implementing same methods from different bullet classes

I picked up unity and C# a month ago so I'm still a noobie.
So far I managed to build a simple space-based arcade shooter (i have my ship, i have a way to shoot bullets). What I'm trying to achieve is a way to keep the script that takes my keyboard input to shoot separate from the possible bullet types.
The way my bullet types are currently implemented is by having a gameobject for each with its own scripts for a) taking keyboard input and b) instancing a prefab with different properties to shoot. Currently i have 2 shooting modes, and a separate script lets me swap between them with the spacebar by enabling disabling the gameobjects. An example of the scripts I'm using for one bullet type:
Script for instantiating bullet. One method simply shoots every time a button is pressed, the other "charges" an array of bullets, accompanied in the second script by a "growing aura" signifing the power increase. These two methods have the same name across different bullet classes, but are implemented differently.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletA_Basic : MonoBehaviour
{
public GameObject bulletPrefab;
public GameObject aura;
public Transform firingPoint;
public Transform chargingPoint;
public float bulletForce = 20f;
public float altCooldown = 1f;
public float fireRate = 1f;
public float altFirePowerMultiplier = 1f;
private void Update()
{
}
public void Shoot()
{
GameObject bullet = Instantiate(bulletPrefab, firingPoint);
Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
rb.AddForce(firingPoint.up * bulletForce, ForceMode2D.Impulse);
}
public void SpecialShoot(int n)
{
StartCoroutine(Special(n));
}
public IEnumerator Special(int n)
{
for (int i = 0; i < n; i++)
{
Shoot();
yield return new WaitForSeconds(0.1f);
}
}
}
Script for taking keyboard input
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shooting_A : MonoBehaviour //This needs to be copied across all firing types
{
private BulletA_Basic bulletScript; //Change the class to make new projectile types with different firing modes
public Vector3 scaleChange;
private GameObject auraInstance;
private float timePassed = 0f;
private float timePassedMain = 0f;
public float timeToDetonation = 3f;
private void Start()
{
bulletScript = GetComponent<BulletA_Basic>();
}
// Update is called once per frame
void Update()
{
bool isFiring = Input.GetButtonDown("Main Cannon");
bool alternateFire = Input.GetButton("Main Cannon");
timePassedMain += Time.deltaTime;
if (isFiring && timePassedMain > bulletScript.fireRate)
{
bulletScript.Shoot();
timePassedMain = 0;
}
if (alternateFire)
{
timePassed += Time.deltaTime;
if (!auraInstance && timePassed >= bulletScript.altCooldown)
{
auraInstance = Instantiate(bulletScript.aura, bulletScript.chargingPoint);
}
if (alternateFire && auraInstance && timePassed < timeToDetonation)
{
Charge();
//Will need to add shader here
}
else if (timePassed >= timeToDetonation)
{
Destroy(auraInstance);
timePassed = 0;
}
}
else
{
if (auraInstance)
{
Destroy(auraInstance);
int powerAltFire = (int)(bulletScript.altFirePowerMultiplier * (Mathf.Pow(2 , timePassed))); //Equation returns a number of projectiles based on how long charge was held
bulletScript.SpecialShoot(powerAltFire);
}
timePassed = 0;
}
}
void Charge()
{
auraInstance.transform.localScale += scaleChange;
}
}
The key here is the bulletScript field.
Basically i'd like to make the second script general so that i don't have to implement it in a different way and copy-pasting it again and again for each type of bullet I'm going to make, and changing the bulletScript field type each time.
I tried doing it with interfaces but I'm not sure how to implement it in the general script since I need to access each field of the subclasses, which have each their own references (like bulletPrefab, or aura). In general i feel interfaces are not well integrated into unity but that might just be me.
I also tried with delegates, but i had similar problems. I simply changed the type of bulletScript to my delegate type (ShootingDelegate bulletScript), and wrote this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public delegate void ShootDelegate();
public delegate void SpecialShootDelegate(int n);
public class ShootingDelegate : MonoBehaviour
{
public ShootDelegate delShoot;
public SpecialShootDelegate delSpecialShoot;
private int weaponIndex;
public GameObject bulletPrefab;
public GameObject aura;
public Transform firingPoint;
public Transform chargingPoint;
public float bulletForce;
public float altCooldown;
public float fireRate;
public float altFirePowerMultiplier;
// Start is called before the first frame update
void Start()
{
WeaponSwap weapon = GetComponent<WeaponSwap>();
weaponIndex = weapon.weaponIndex;
switch (weaponIndex)
{
case 1:
BulletB_Fan bulletB = GetComponent<BulletB_Fan>();
delShoot = bulletB.Shoot;
delSpecialShoot = bulletB.SpecialShoot;
bulletPrefab = bulletB.bulletPrefab;
aura = bulletB.aura;
firingPoint = bulletB.firingPoint;
chargingPoint = bulletB.chargingPoint;
bulletForce = bulletB.bulletForce;
altCooldown = bulletB.altCooldown;
fireRate = bulletB.fireRate;
altFirePowerMultiplier = bulletB.altFirePowerMultiplier;
break;
case 0:
BulletA_Basic bullet = GetComponent<BulletA_Basic>();
delShoot = bullet.Shoot;
delSpecialShoot = bullet.SpecialShoot;
bulletPrefab = bullet.bulletPrefab;
aura = bullet.aura;
firingPoint = bullet.firingPoint;
chargingPoint = bullet.chargingPoint;
bulletForce = bullet.bulletForce;
altCooldown = bullet.altCooldown;
fireRate = bullet.fireRate;
altFirePowerMultiplier = bullet.altFirePowerMultiplier;
break;
}
}
// Update is called once per frame
void Update()
{
}
}
This is the error it throws:
ArgumentException: Value does not fall within the expected range.
ShootingDelegate.Start () (at Assets/Scripts/ShootingDelegate.cs:54)
which corresponds to this line
delShoot = bullet.Shoot;
I don't really care if a solution employs either interfaces or delegates, those were just things I tried. Any thoughts?
I created a sample just for you.
Here I am creating BulletType parent class for all bullet types. And atting some variable and method for all bullets.
public class BulletType : MonoBehaviour
{
// Protected is less stric than private, more stric than public
// Protected variables only accesible to this class and its child.
protected string name;
protected int bulletDamage;
protected int bulletSpeed;
protected virtual void Start()
{
}
// Virtual means you can override this method in child classes
protected virtual void Damage() { }
public virtual void PlaySound() { }
protected virtual void ShowEffect() { }
}
Now I will add child class and inherit from parent BulletType class. Everything write in comment lines.
// This class inherits from BulletType class.
public class FireBullet : BulletType
{
// If you want to completely ignore parent class variable to by adding new keyword.
new int bulletSpeed = 3;
protected override void Start()
{
// when overriding a method automatically adds this method.
// base means parent class. with base you can access parent methods.
base.Start();
// If you remove base method, parent method won't be called in this class.
// Here I accessed parent class variable and set its value.
// This doesn't effect parent or other child classes. That's the beauty.
name = "Fire Bullet";
}
void StopSound()
{
}
protected override void PlaySound()
{
// Sound played from parent.
base.PlaySound();
// You can add your own variable and methods in parent method.
StopSound();
}
}
// FireBullet inherited from BulletType, and LavaBullet inherited from FireBullet.
// You can do this as much as you want.
public class LavaBullet : FireBullet
{
protected override void PlaySound()
{
// Here base will be FireBullet
base.PlaySound();
}
}
public class IceBullet : BulletType
{
// Add as much as you thing.
}
And for using BulletType script in your player just add this line
public class Player
{
public BulletType currentBulletType;
// You can get child from main class.
FireBullet fireBullet = currentBulletType.GetComponent<FireBullet>();
// Now you can access and use child class methods.
fireBullet.PlaySound();
}
and when you want to change bullet type, just assign new bullettype. Because all bullet type inherit from this class. You can assign every bullet type.
I don't know if it's the answer but,
delShoot = bulletB.Shoot;
delShoot is a reference to a script.
bulletB.Shoot is a function.
I don't think you can make one equal to the other.
If I understand your question, you want to make one script for two type of bullets shoot. You can create a scipts Shoot.cs who is instantiate with a value (1 for the default fire 2 for the second fire) and other scrits defaultFire.cs / secondFire.cs with they own properties.
Like this you'll juste instantiate once your Bullet like this :
public void Start(){
case 1 : Shoot shoot = new Shoot(1)
case 2 : Shoot shoot = new Shoot(2)
}
Hope this help a little..

Categories

Resources