Logic error adding 2 variables together in Unity Engine - c#

This sounds dumb, but I can't get 2 simple variables to add to each other, I have set points to add to 100 in the inspector. Everything would seem to work fine but when I all AddPoints from another script I Always get a debug log of 5. No matter what I set pointsToAdd to it always comes out as 5. I've tried score++; and that comes out just fine, I also tried to write a Debug.log just to make sure pointsToAdd wasn't set to something weird and it always returned 0! I don't know what in the world is happening. I must have screwed something up somehow.
private float score = 0;
public float pointsToAdd; // Setting in the editor
public void AddPoints()
{
score += pointsToAdd;
Debug.Log(score);
}
And the script that's calling AddPoints contains:
public LevelManager levelManager; // I'm setting in the editor
void OnCollisionEnter2D(Collision2D other)
{
if (other.transform.CompareTag("Wall"))
{
levelManager.AddPoints();
}
Edit: I've been testing for a few days now, with every chance I get completely unpredictable results until I set my score and Text UI to static variables, what about static variables changes the way that works?

Going to need a little more information on this one.
However I don't know how you are doing it in the other script, I am assuming its not this script causing the problem, most likely the other. I would personally do:
OtherScript:
pS.AddPoints(100f);
PointsScript
private float score;
public void AddPoints(float pointsToAdd)
{
score += pointsToAdd;
}

Related

C# - Unable to increment an int beyond 1. Console prints 1 and it does not increase, despite triggering multiple times

The game is simple, your Player has a running animation and must jump to dodge obstacles, but isn't moving. The obstacles and background are.
My gut instinct is that it is out of scope somehow or is perhaps overflowing - the score increment was increasing far beyond what I had anticipated when I had the AddScore() inside of the Destroy(gameObject); if condition instead of its own function.
However, at this point I am very confused why it isn't working. As a bonus, I cannot get Audio to play from the second commented bit (Value cannot be null.) As for why that happens, no idea. I definitely have the source I have it attached to to the prefab that is spawned, and said spawn should trigger that sound to play when it passes under the player when they jump, I originally thought that there was an issue where the object was deleted before it could reference its audio source but I am unsure.
Edit: I am going to leave the 'bonus' issue above even though I literally fixed it as I typed this up. (I had to add an Audio Source component to the prefab I was spawning.)
I still, for the life of me, cannot get an integer to go above 1 and print to the console. It might be driving me a little insane. Please help friends. I have googled a ton and none of the suggestions from other comments fixed my issue.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveLeft : MonoBehaviour
{
private float speed = 30;
private PlayerController playerControllerScript;
private float leftBound = -15;
private float scoreZone = -3;
public AudioClip scoreSound;
private int score = 0;
private AudioSource playerAudio;
// Start is called before the first frame update
void Start()
{
playerControllerScript = GameObject.Find("Player").GetComponent<PlayerController>();
playerAudio = GetComponent<AudioSource>();
}
// Update is called once per frame
void Update()
{
// Moves objects as long as it is not game over.
if (playerControllerScript.gameOver == false)
{
transform.Translate(Vector3.left * Time.deltaTime * speed);
}
//Assigns scoring system, buggy. Commented out sound because of errors.
if (transform.position.x < scoreZone && gameObject.CompareTag("Obstacle"))
{
//playerAudio.PlayOneShot(scoreSound, 1);
}
//Destroy object out of bounds
if (transform.position.x < leftBound && gameObject.CompareTag("Obstacle"))
{
Destroy(gameObject);
AddScore();
}
}
void AddScore()
{
//Score goes to 1.
score++;
//prints 1 in console but does not increase over 1.
Debug.Log("Score! Your Score is" + score);
Debug.Log(score);
}
}
Tried: Numerous changes to configuration of ++, x = x +1, x++, x +=, etc. No idea. Am lost.
This is an issue caused by the local member int. You have an object, which has been created and this MoveLeft component is attached to it. You then destroy this object on collision, and therefore this component as well. You’ll have added one to the local instance int score, but then you destroy the component and lose this local instance.
I suspect what you thought was happening is that all the scripts/components shared the same variable values. That would only be true if you if you made the int score member a static int score, which means the member is the same shared amongst all instances of this component.
Instead of making score static, you might want to have a “Game Manager” that exposes public functions to allow you to increment and retrieve the current score.

InvalidOperationException: Cannot read value of type 'Vector2' from composite 'UnityEngine.InputSystem.Composites.Vector2Composite'

So I've been tinkering with the Unity input system (the one you install as a package), trying to create a simple movement script, but immediately ran into a problem. The way I did it was create an action of the Value type, then bound WASD to a 2D Composite and then created a Unity event calling on the "Movement" function inside the player script.
public void Movement (InputAction.CallbackContext context)
{
Debug.Log("!!!!!!");
Vector2 moveVal = context.ReadValue<Vector2>();
playerContainer.transform.Translate(new Vector3(moveVal.X, moveVal.Y, 0) * moveSpeed * Time.deltaTime);
}
Anyway, up to this point it all works fine: the function is called on, and the button presses are registered, but any time I press them, it throws two exceptions. "InvalidOperationException: Cannot read value of type 'Vector2' from composite 'UnityEngine.InputSystem.Composites.Vector2Composite'" and "InvalidOperationException while executing 'performed' callbacks".
The problem seems to be this line:
Vector2 moveVal = context.ReadValue<Vector2>();
but thing is, every single tutorial and solution out there uses the same piece of code, so it should work fine, I guess? What could be causing the problem on my side?
[Disclaimer, this is not an answer but I needed to include the picture/code]
Everything looks OK so I guess the error is in the Input Actions control. Compared to this example, do you see any differences?
To use the Input Actions above I use code like this:
public class PlayWithCube : MonoBehaviour
{
NewControls newControls;
private void Awake()
{
newControls = new NewControls();
newControls.Game.Movement.performed += Movement_performed;
}
private void Movement_performed(UnityEngine.InputSystem.InputAction.CallbackContext obj)
{
var v = obj.ReadValue<Vector2>();
Debug.Log(v);
}
private void OnEnable()
{
newControls.Enable();
}
private void OnDisable()
{
newControls.Disable();
}
}
For me, the reason for not being able to read input as Vector2 was that I accidentally used System.Numerics.Vector2 instead of UnityEngine.Vector2.

Unity 3D C# Script - my private member seems to just be alternating between 0 an the correct value

I have a script in my unity application (called cubemove.cs).
It has the private member:
private float z_ref;
Inside my void Awake() I have a line for initialization: z_ref = (float) 0.0;
I have a button in my application set to run the following function onclick from cubemove.cs
public void zeroZ()
{
z_ref = (float) 45.0;
}
My void Update() contains Debug.Log("z_ref: " + z_ref.ToString());
When I run the application and view the debugging output, first it's only printing z_ref: 0. This is expected as that is what I initialized the value to. Then when I hit the button in the application (which results in zeroZ() being executed), I expect it to now print 45. Instead it alternates between 45 and 0.
Here is the image of the debug output after the button press
Prior to pressing the button it was all 0's.
My original program actually had more code when I first encountered this issue with my z_ref randomly changing (it wasn't fixed to 45, zeroZ() was a more complex function etc). However even after I changed my cubemove.cs file to the following code below, I still see this exact same behavior of z_ref alternating between 0 and the value it's supposed to be.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class cubemove : MonoBehaviour
{
private float z_ref;
void Awake()
{
z_ref = (float) 0.0;
}
void Start()
{
}
void Update()
{
Debug.Log("z_ref: " + z_ref.ToString());
}
public void zeroZ()
{
z_ref = (float) 45.0;
}
}
After zeroZ() is called there should no reason z_ref should ever show up as any value other then 45. What exactly is going on here?
In case it is relevant, my build target is android (the debug output image is from logcat).
As pointed out in the comment, the issue was due to multiple instances of cubemove (it was associated with more then one gameobject, but I hadn't realized at the time).

null reference exception in level manager script

So I'm basically brand new to unity and C# and have been following along with tutorials online (N3K 2D Platformer, YouTube), I'm trying to create a basic UI to display score etc and I seem to have come across a null pointer exception which I can't seem to figure out as I have referenced the two objects that are causing this error, namely my scoreText object and my hitPointText object.
As I've said I did reference those very objects by dragging them from my hierarchy and into the fields I had created in my level manager script in the inspector and further to that I am simply following along with a tutorial and have done exactly as the video has instructed, but yet on the video it seems to work fine.
The offending lines of code are:
scoreText.text = score.ToString();
hitPointText.text = hitPoints.ToString();
This tutorial is now over 1 year old, is it possible that a unity update has changed that way things NEED to be referenced?
I'll post my level manager code below in the hopes that someone may be able to point out the error that I seem to be missing.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class LevelManager : MonoBehaviour
{
public static LevelManager Instance { set; get; }
public Transform spawnPosition;
public Transform playerTransform;
private int hitPoints = 3;
private int score = 0;
public Text scoreText;
public Text hitPointText;
private void Awake()
{
Instance = this;
scoreText.text = score.ToString();
hitPointText.text = hitPoints.ToString();
}
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
private void Update ()
{
if(playerTransform.position.y < (-10))
{
playerTransform.position = spawnPosition.position;
hitPoints--;
if(hitPoints <= 0)
{
Debug.Log("Your Dead!");
}
}
}
public void Win()
{
Debug.Log("Victory");
}
}
Snippets of screens below:
Scene view of unity engine
Game view of unity engine, with game running
So here is a snippet of code from my player class which uses Instance on the LevelManager script in order that it can have access to the win() method as can be seen in the last case of the switch "WinPost", not sure if that is what you are referring to when your mentioning singleton, other than that never is the term singleton used in any of the scripts I have.
switch (hit.gameObject.tag)
{
case "Coin":
Destroy(hit.gameObject);
break;
case "JumpPad":
verticalVelocity = jumpForce * 2;
break;
case "Teleporter_1":
controller.enabled = false;
transform.position = hit.transform.GetChild(0).position;
controller.enabled = true;
Debug.Log("This works!");
break;
case "Teleporter_2":
controller.enabled = false;
transform.position = hit.transform.GetChild(0).position;
controller.enabled = true;
Debug.Log("This works!");
break;
case "WinPost":
LevelManager.Instance.Win();
break;
default:
break;
}
My guess would be that the components aren't initialized when you call Awake. Awake gets called as a constructor-kind-of method as soon as the object is created. When it is called, you can't be sure if the other components got initialized already.
I would suggest you copy the assignments you make in Awake into Start and come back to see if it works. Start gets called after the GameObjects have their components initialized.
private void Awake()
{
Instance = this;
}
// Use this for initialization
void Start ()
{
scoreText.text = score.ToString();
hitPointText.text = hitPoints.ToString();
}
Thanks to everyone for trying to help and all the great suggestions.
Ultimately I ended up breaking my game in the process of trying to recreate the same UI in a new blank scene, I did manage to recreate the same error before breaking my game which at the time then left me none the wiser. However due to the fact I broke my game I had to step back at least two tutorials and recreate the level manager object and the empty child spawnPosition object, (the level manager script was ok, it was just the level manager object and its child that I broke), anyway in having to recreate both of those objects again everything now seems to work as intended and so this leads me to the conclusion that the problem was not the code but the objects themselves???
Thanks again to everyone that tried to help, another day another learning experience.
D.

Unity3d: Adding gameobject to List from an array

Thanks for help in advance. Here is a short snippet of the code that I am having an issue with.
GameObject[] allMotor_array;
public List<GameObject> BrokenMotor_list = new List<GameObject>();
void Start()
{
allMotor_array = GameObject.FindGameObjectsWithTag ("Motors");
}
void Update()
{
foreach (GameObject motor in allMotor_array)
{
if(motor.GetComponent<Pump_event>().damaged)
{
BrokenMotor_list.Add(motor);
}
}
}
I have an array of Gameobjects that is created on Start, each of the gameobjects in the array have a script called Pump_event. What I want to do is add the gameobject with a true boolean (damaged) to the list so that I can create a GUI list of all the motors that are damaged (and then take further action on those motors).
With the current code it instantiates the array fine, but when One of the motors boolean changes to true the list tends to continuously add the motor gameobject to the list on each update cycle. So what I want is to figure out a way of adding the gameobject to the list ONCE.
Having it in the update() is probably not the best method but I really am stuck on how to approach this.
G
The Solution to my problem
Thanks for your answers, you all had well thought out responses. I appreciate it. I didn't go with 1 persons method but instead adapted logical approaches found here to work with my script/s.
Here is what I did.
In my pump_event script the events are sorted in a Case and switch as damage increased on the pump the event would escalate. So I added in a section to that script to include "reporting" the damage.
public class Pump_event : MonoBehaviour
//The damage has taken place and event_category=0\\
switch (event_category)
{
case 0:
Master_script.GetComponent<Control_room>().AddtoList (gameObject);
event_category = 1;
break;
I took advice not to insert these types of programing and placed it into its separate class which works out well.
public class Master_script: MonoBehaviour
public void AddtoList(GameObject motor_tobadded)
{
BrokenMotor_list.Add(motor_tobadded);
}
This also eliminated the need on having an array holding all of the pump event controllers as well.
Now the script all works fine. It may not be most efficient but it is doing its job.
Thank you again to all that helped.
In your Pump_event Script you can have a event Action which you register in this snippet and whenever damaged is set true you need to fire the event.
Example:
// in Pump_event Class
public static event Action<GameObject> OnDamagedValueChanged;
private bool _damaged;
public bool Damaged
{
get { return _damaged;}
set
{
_damaged = value;
if(_damaged)
{
if(OnDamagedValueChanged != null)
OnDamagedValueChanged(gameObject);
}
}
}
In your Current Class where you have array of GameObjects:
void OnEnable()
{
Pump_event.OnDamagedValueChanged += HandleOnDamagedValueChanged;
}
void OnDisable()
{
Pump_event.OnDamagedValueChanged -= HandleOnDamagedValueChanged;
}
void HandleOnDamagedValueChanged(GameObject obj)
{
if (!BrokenMotor_list.Contains (obj))
{
BrokenMotor_list.Add (obj);
}
}
Using Actions is a better approach than doing it in Update Method. It is not good for performance to keep checking for a bool in iteration in update method. and try to avoid GetComponent and Find/FindObjectWithTag Methods in Update. It is not good practice. I hope this is helpful.
According to the code you have posted, the problem lies within the fact that the damaged property is never reset. One solution would be to reset this property once you add it to the list, like so:
if(motor.GetComponent<Pump_event>().damaged)
{
motor.GetComponent<Pump_event>().damaged = false;
BrokenMotor_list.Add(motor);
}
However, multiple copies of the same object could still be added to your list if the motor is damaged again.
To go around this, you could use a HashSet. The hash set will allow only one copy of an object to exist within it, thus, if an object is already present is will not be added again.
The catch is that you will need to override the GetHashCode and Equals methods for your GameObject class since these will be used internally by the hash set to place items within itself and identify duplicates.
check if list already contains motor.
if(motor.GetComponent<Pump_event>().damaged)
{
if(BrokenMotor_list.Contains(motor))
{
BrokenMotor_list.Add(motor);
}
}
although on msdn describes how to implement IEquatable in case if you want compare different objects(with different references) https://msdn.microsoft.com/ru-ru/library/vstudio/bhkz42b3%28v=vs.100%29.aspx
if (!BrokenMotor_list.Contains (motor)) {
BrokenMotor_list.Add (motor);
}
You'd better do this after damage event occur by add a delegate.

Categories