I am doing an exercise on making a small console program which shows how fast a car is moving and a function to make it go either faster or slower.
So here is my class called car, it contains a constructor.
There is a get and set accessor for the private int speed.
There are 2 methods called accelerate and break.
class car
{
private int speed;
public car (int initialSpeed) //initializes car speed
{
Speed = initialSpeed;
}
public void accelerate() //increase Speed
{
Speed = Speed + 10;
}
public void brake() //decrease Speed
{
Speed = Speed - 10;
}
public int Speed //access private int speed
{
get { return speed; }
set { speed = Speed; }
}
}
Now here is the problem:
class program
{
static void Main(string[] args)
{
var Car = new car(5);
Console.WriteLine(Car.Speed);
}
}
When I run the program the console just displays 0 even though initialized with 5. Why is this happening and what am I doing wrong?
Your Speed property is the problem. Here's the code:
public int Speed //access private int speed
{
get { return speed; }
set { speed = Speed; }
}
The getter is fine - it returns the value of the private variable.
The setter, however, sets the value of variable to the result of evaluating the getter - ignoring the value passed into the setter entirely (available via the value variable). It should be:
public int Speed
{
get { return speed; }
set { speed = value; }
}
Or better yet, use an automatically implemented property - remove the speed field, and just have:
public int Speed { get; set; }
At the same time, I'd suggest learning about compound assignment operators and .NET naming conventions. If you're using C# 6 you can also use expression-bodied members, leaving a really small amount of code:
class Car
{
public int Speed { get; set; }
public Car(int initialSpeed)
{
Speed = initialSpeed;
}
public void Accelerate() => Speed += 10;
public void Brake() => Speed -= 10;
}
Change this code:
public int Speed //access private int speed
{
get { return speed; }
set { speed = Speed; }
}
to
public int Speed //access private int speed
{
get { return speed; }
set { speed = value; }
}
In C# properties, the setter uses the value keyword to set values to the underlying field.
Your set assessor should be
set { speed = value; }
So your property definition becomes
public int Speed //access private int speed
{
get { return speed; }
set { speed = value; }
}
The problem is in the set, change this
set { speed = Speed; }
for this
set { speed = value; }
Good luck!
#Jay is correct but the preferred syntax in this case is
public int Speed { get; set;}
and remove
private int speed;
Let the compiler handle the member details for you since you are not running any actual logic in the get/set properties
Related
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class MoveAbleButtonSystem : MonoBehaviour
{
public Transform ObjectToMove;
public bool moveLeft = true;
public bool moveRight = false;
public float SpeedToMove = 1f;
private bool pressed = false;
public float minZ = 0f;
public float maxZ = 0f;
void OnMouseDown()
{
pressed = true;
}
void OnMouseUp()
{
pressed = false;
}
void Update()
{
Transform T = ObjectToMove.transform;
if (pressed && moveLeft)
{
T.Translate(Vector3.forward * SpeedToMove * Time.deltaTime, Space.World);
}else
if(pressed && moveRight)
{
T.Translate(Vector3.back * SpeedToMove * Time.deltaTime, Space.World);
}
if(T.position.z > maxZ)
{
T.position.z = maxZ;
}
}
}
Assets\Scripts\MainMenu\MoveAbleButtonSystem.cs(42,13): error CS1612: Cannot modify the return value of 'Transform.position' because it is not a variable
I dont get why I cannot change the position. All I want to do is for the ObjectToMove to move until it passes a certain point, after what it is not supposed to move anymore. Using Clamp provides the same error.
The following simplified example demonstrates the same compile error:
public class Program
{
public static void Main()
{
var container = new Container();
container.Property.Value = 1; // Error CS1612 - Cannot modify the return value of 'Container.Property' because it is not a variable
}
}
public struct Item
{
public int Value;
}
public class Container
{
public Item Property { get; set; }
}
This is the issue you're seeing. It's happening because the Container.Property property is returning a copy of a value type, in this case struct Item.
Because this is a copy of the item stored in Container and not the item itself, changing the copy's properties cannot have any visible effect because the value returned from the property is not stored anywhere - no variable is assigned from it.
Therefore the compiler gives an error, to avoid misleading and useless code.
The documentation for Error CS1612 explains all this in detail.
Others - and the error documentation - already explained the issue and the why in detail.
But in terms of a solution for your issue you would go e.g.
// Get copy of current position
var position = T.position;
if(position.z > maxZ)
{
// modify the copy
position.z = maxZ;
}
// assign back / apply modified value
T.position = position;
You could even combine and simplify it a lot by doing e.g.
public enum MoveDirection
{
Left,
Right
}
public MoveDirection direction;
void Update()
{
if(pressed)
{
var T = ObjectToMove.transform;
var position = T.position;
var multiplier = direction == MoveDirection.Left ? 1 : -1;
position.z = Mathf.Clamp(position.z + SpeedToMove * Time.deltaTime * multiplier);
T.position = position;
}
}
As pointed in the comments from Mathieu and Matthew, position is a struct field of the calss Transform and due to this reason you can not change its fields like this. If you want to understand why this is not working imagine that Unity's Transform and position objects look something like this:
struct Position
{
public int x;
public int y;
public int z;
}
class Transform
{
public Position _position;
public Position position
{
get
{
return _position;
}
set
{
_position = value;
}
}
}
When you write T.position.z the position part is actually a value copy of the original T's position variable returned by the property getter. So by changing the copy of position you will not get the desired effect of updating the T.position's z field. Due to this reason C# it trying to help you by throwing an error in order to prevent a bug that is very hard to identify.
You can not change the individual components of a Vector3. To do this, you will have to change all the components. i.e :
if (T.position.z > maxZ)
{
T.position = new Vector3(T.position.x, T.position.y, maxZ)
}
I am trying to access value in my code. I have two classes, carex and engine.
The engine class has the following code:
public class Engine
{
public string Size;
public int HorsePower;
public float FuelConsumtionRate;
public Engine()
{
}
public Engine(string cylinder, int hp, float fuelRate)
{
Size = cylinder;
Console.WriteLine($"Engine type: {cylinder}");
HorsePower = hp;
Console.WriteLine($"Horse power: {hp} hp");
FuelConsumtionRate = fuelRate;
Console.WriteLine($"Fuel consumption: {fuelRate} l/h");
}
}
The carex class has the following code:
public class CarEx
{
string Manfacturer;
string RegistrationNr;
float Fuel;
float Speed;
bool IsRunning;
public CarEx(string manuf, float fuel, string regNr)
{
Manfacturer = manuf;
this.Fuel = fuel;
RegistrationNr = regNr;
Console.WriteLine("_____________________________________________________");
Console.WriteLine($"Manufacturer; {manuf}, Fuel amount: {fuel}l, License: {regNr}");
Console.WriteLine("______________________________________________________");
}
public void ChooseEngineType()
{
Engine v4 = new Engine("v4", 200, 0.7f);
}
public void FillFuel(float amount)
{
Fuel += amount;
/*
Fuel -= 0.7f;
*/
}
public static void RunCar()
{
CarEx car1 = new CarEx("Saab", 10, "1234DD");
car1.CallCustomer();
Console.WriteLine($"Maker: {car1.Manfacturer}");
Console.WriteLine($"Registration nr: {car1.RegistrationNr}");
}
}
I would like to replace the hard coded fuel consumption rate
Fuel -= 0.7f;
which I commented out with the value set in the engine constructor so its not hard coded any more.
What am I missing out on?
Your ChooseEngineType method creates an Engine object, but it is stored in a local variable which ceases to exist as soon as the method ends.
Change that variable to a field or property Engine engine at class level, and then you can use engine and its property engine.FuelConsumtionRate in all methods.
(PS, the proper spelling of that property would be FuelConsumptionRate)
Assuming that the fuel consumption rate is constant, you can use a constant that is privately accessible in the class you've declared by doing this:
private const float fuelConsumptionRate = 0.7f;
and then use it in your code so that you do not hard-code the changes. Otherwise, if the rate is bound to change, you'll need to store either the Engine itself (most recommended), or just the consumption rate in a private field like this:
private Engine engine;
// or
private float fuelConsumptionRate;
// the rest of your class' code
public void ChooseEngineType()
{
float rate = 0.7f;
Engine v4 = new Engine("v4", 200, rate);
engine = v4;
// or
fuelConsumptionRate = rate;
}
Added a new constructor for fuel rate in Engine class
public class Engine
{
public static string Size;
public static int HorsePower;
public static float FuelConsumtionRate;
//Constructor to set fuel reate
public Engine(float fuelRate)
{
FuelConsumtionRate = fuelRate;
}
public Engine(string cylinder, int hp, float fuelRate)
{
Size = cylinder;
Console.WriteLine($"Engine type: {cylinder}");
HorsePower = hp;
Console.WriteLine($"Horse power: {hp} hp");
FuelConsumtionRate = fuelRate;
Console.WriteLine($"Fuel consumption: {fuelRate} l/h");
}
And in the carex class:
public class CarEx
{
string Manfacturer { get; set; }
string RegistrationNr { get; set; }
float Fuel;
float Speed;
bool IsRunning;
public CarEx(string manuf, float fuel, string regNr)
{
Manfacturer = manuf;
this.Fuel = fuel;
RegistrationNr = regNr;
Console.WriteLine("_____________________________________________________");
Console.WriteLine($"Manufacturer; {manuf}, Fuel amount: {fuel}l, License: {regNr}");
Console.WriteLine("______________________________________________________");
}
public void ChooseEngineType()
{
Engine v4 = new Engine("v4", 200, 0.7f);
}
public void Accelerate()
{
Speed += 6.0f;
//Fuel rate set for v4 object
Fuel -= Engine.FuelConsumtionRate;
}
public class EnemyBehaviour : MonoBehaviour
{
public float speed; // or the speed of rotation.
public bool randomSpeed = false;
public float speedRange = 4;
private float originalSpeed;
private void Start() { originalSpeed = speed; }
private void Update()
{
if (!randomSpeed)
{
speedRange = 0;
speed = originalSpeed;
}
else speed = Random.Range(1, speedRange);
}
}
The problem is in Update at this part:
if (!randomSpeed)
{
speedRange = 0;
speed = originalSpeed;
}
If for example when running the game the speed was 3 now when I set it to false the speed value will be 3 all the time I can't change it now. But I want to be able to change the speed value to any value. I want that it will get back to the originalSpeed if I set to false but also to be able to change for a new speed and then update the originalSpeed to the new one.
The way I did it now I locked the speed when it's false.
Surely it will lock the speed as you assign the value of speed by originalSpeed every frame.
To solve it, you should not put things inside the Update(). Also, try to use 'get set' syntax for those protected public variable.
Lets say you want to assign a value to the 'speed' only one time, when you click the randomSpeed checkbox.
private float randomSpeed
public float RandomSpeed
{
get {return randomSpeed;}
set {
randomSpeed = RandomSpeed;
if (!randomSpeed){
speed = originalSpeed;
} else {
speed = Random.Range(1, speedRange);
}
}
}
Im fairly new to programming and am making a rpg battle simulator for practice. My problem is that I can't seem to make my attack method work. Heres the classes I have:
class Person
{
protected int attack;
protected int health;
public Person(int _attack, int _health)
{
attack = _attack;
health = _health;
}
public int GetAttack()
{
return attack;
}
public int GetHealth()
{
return health;
}
public int Attack(int _health)
{
_health -= attack;
return _health;
}
}
class Hero : Person
{
public Hero(int _attack, int _health)
:base (_attack , _health)
{
}
}
class Enemy : Person
{
public Enemy(int _attack, int _health)
:base (_attack , _health)
{
}
}
and heres the main:
class Program
{
static void Main(string[] args)
{
Hero Joe = new Hero(4, 10);
Enemy Tim = new Enemy(5, 20);
Joe.Attack(Tim.GetHealth());
Console.WriteLine(Tim.GetHealth());
Console.WriteLine(Tim.GetAttack());
Console.ReadLine();
}
}
My guess is that the attack method is doing the math, but is never changing the health that is passed into it. Or maybe it has something to do with the fact that their protected. Another thought of mine is that it doesn't need to return anything.
How would I go about making my attack method work? I just want it to take in somethings health value, subtract the attacking things attack value, and save the calculated value as the health? Thank you for reading this !
When you pass around an int, you are making copies of the number, not passing references to the same number in memory.
When you pass around an instance of a class, you are passing around references to the same object in memory.
Therefore, I suggest changing your design to something like this:
public void Attack(Person target)
{
target.health -= this.attack;
}
...
Joe.Attack(Jim);
You got a couple things you can improve here. First thing is naming conventions, I recommend reading the design guidelines.
First, If you change your Attack and Health to properties instead of protected fields, you expose getter and setter methods for it. Obviously you only want to set form the controller so make the set a private set:
public class Person
{
public int Attack { get; private set; }
public int Health { get; private set; }
public Person(int attack, int health)
{
Attack = attack;
Health = health;
}
// Rest of code
}
When you do it like this you eliminate the need for your individual GetAttack() and GetHealth() methods.
Next, the names of your parameter in Attack() is misleading. I assume you want the parameter to be "attack" and not "health" right? Since our setter is private this method allows us to only access health modifications inside the class. Since we already changed Health to be a property, we don't need to return it anymore so this method can now be void:
//Our property is named Attack so this has to be AttackAction or something different
public void AttackAction(int attack)
{
Health -= attack;
}
And if we put it all together:
public class Person
{
public int Attack { get; private set; }
public int Health { get; private set; }
public Person(int attack, int health)
{
Attack = attack;
Health = health;
}
public void AttackAction(int attack)
{
Health -= attack;
}
}
public class Hero : Person
{
public Hero(int attack, int health)
:base (attack , health)
{
}
}
public class Enemy : Person
{
public Enemy(int attack, int health)
:base (attack , health)
{
}
}
I made a fiddle here that shows this new code in action.
You are calling Attack() but are never saving the value returned by that method. You need to add a Setter for the health field, then set that value to the method's returned value. Something like
Health Property
public int Health
{
get { return health; }
set { health = value; }
}
Setting the Value
Tim.Health = Joe.Attack(Tim.Health);
If you want to keep the design pattern the same (you don't, see Blorgbeard's answer) you could add a SetHealth() method to Person and do something like this:
Tim.SetHealth(Joe.Attack(Tim.GetHealth());
This gets Tim's health total, passes that to Joe's attack method, which returns a value (what Tim's new health total should be) and then Tim's health is set to this value.
I want to make spell damage over time. So here is my code:
public class Spell : MonoBehaviour
{
public float damage = 1.0f;
public bool ignoreCaster = true;
public float delayBeforeCasting = 0.4f;
public float applyEveryNSeconds = 1.0f;
public int applyDamageNTimes = 5;
private bool delied = false;
private int appliedTimes = 0;
void OnTriggerStay(Collider other)
{
IDamageable takeDamage = other.gameObject.GetComponent<IDamageable>();
if(takeDamage != null)
{
StartCoroutine(CastDamage(takeDamage));
}
}
IEnumerator CastDamage(IDamageable damageable)
{
if(!delied)
{
yield return new WaitForSeconds(delayBeforeCasting);
delied = true;
}
while(appliedTimes < applyDamageNTimes)
{
damageable.TakeDamage(damage);
yield return new WaitForSeconds(applyEveryNSeconds);
appliedTimes++;
}
}
}
Problem is where while starts. I want to check if appliedTimes < applyDamageNTimes, then if it is true to do damage, wait for delay (applyEveryNSeconds) and then check again but i am not handy with coroutine and for some reason it is not doing that.
Here is working code. Also look for other answers if someone need!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spell : MonoBehaviour
{
public float damage = 1.0f;
public bool ignoreCaster = true;
public float delayBeforeCasting = 0.0f;
public float applyEveryNSeconds = 1.0f;
public int applyDamageNTimes = 1;
private bool delied = false;
private int appliedTimes = 0;
private bool test = false;
void OnTriggerStay(Collider other)
{
IDamageable takeDamage = other.gameObject.GetComponent<IDamageable>();
if(takeDamage != null)
{
StartCoroutine(CastDamage(takeDamage));
}
}
IEnumerator CastDamage(IDamageable damageable)
{
if(!test && appliedTimes <= applyDamageNTimes || !test && applyEveryNSeconds == 0)
{
test = true;
if(!delied)
{
yield return new WaitForSeconds(delayBeforeCasting);
delied = true;
}
else
{
yield return new WaitForSeconds(applyEveryNSeconds);
}
damageable.TakeDamage(damage);
appliedTimes++;
test = false;
}
}
}
OnTriggerStay is called every frame 2 objects are colliding. This means that an asynchronous instance of CastDamage coroutine is called every frame. So what happens is that you're getting a whole lot of damage per seconds, which simulates not any damage per second, hehe.
So change OnTriggerStay to OnTriggerEnter.
Depending on the kind of spell it is, I'd rather create a DPS script and apply that to the game object so...
Spell hits Object
AddComponent< MyDamageSpell>();
And then the MyDpsSpell does damage every N seconds to the Object it's on, and removes itself when it is done:
// Spell.cs
void OnTriggerEnter(Collider col) {
// if you don't want more than 1 dps instance on an object, otherwise remove if
if (col.GetComponent<MyDpsAbility>() == null) {
var dps = col.AddComponent<MyDpsAbility>();
dps.Damage = 10f;
dps.ApplyEveryNSeconds(1);
// and set the rest of the public variables
}
}
// MyDpsAbility.cs
public float Damage { get; set; }
public float Seconds { get; set; }
public float Delay { get; set; }
public float ApplyDamageNTimes { get; set; }
public float ApplyEveryNSeconds { get; set; }
private int appliedTimes = 0;
void Start() {
StartCoroutine(Dps());
}
IEnumerator Dps() {
yield return new WaitForSeconds(Delay);
while(appliedTimes < ApplyDamageNTimes)
{
damageable.TakeDamage(damage);
yield return new WaitForSeconds(ApplyEveryNSeconds);
appliedTimes++;
}
Destroy(this);
}
If it's an aura spell where units take damage every second they're in range you could do something like:
float radius = 10f;
float damage = 10f;
void Start() {
InvokeRepeating("Dps", 1);
}
void Dps() {
// QueryTriggerInteraction.Collide might be needed
Collider[] hitColliders = Physics.OverlapSphere(gameObject.position, radius);
foreach(Collider col in hitColliders) {
col.getComponent<IDamagable>().TakeDamage(10);
}
}
https://docs.unity3d.com/ScriptReference/Physics.OverlapSphere.html
Why not create, instead a system that applies auras to in-game characters? For example, if a spell like this adds a damage over time debuff it could instead add the debuff-aura script to the game object and then remove itself once its conditions are met.