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.
Related
Say I have a class with multiple variables that I want to check via OnValidate, it doesn't seem to make much sense to check every variable in said class every time I change one variable through the Unity Inspector. So is there a way to just check the variable that was changed?
[System.Serializable]
public class Stat
{
[SerializeField]
private float _value;
Value { get; set; }
}
public class TestClass : MonoBehaviour
{
public Stat stat1;
public Stat stat2;
public Stat stat3;
void Start(){}
private void OnValidate()
{
if (stat1.Value < 0)
{
throw new ArgumentException("Error with stat1");
}
else if (stat2.Value < 0)
{
throw new ArgumentException("Error with stat2");
}
else if (stat3.Value < 0)
{
throw new ArgumentException("Error with stat3");
}
}
}
So in this case, everytime I change either stat1, stat2, or stat3 in Unity Inspector. OnValidate will go through and check every variable which doesn't seem very efficient. I'm wondering if there is a way to only get the value that was changed so my code can look something like this:
public class TestClass : MonoBehaviour
{
public Stat stat1;
public Stat stat2;
public Stat stat3;
void Start(){}
private void OnValidate()
{
if (value < 0) // value being the value that was changed
{
throw new ArgumentException("Error with {value}");
}
}
}
This shouldn't be too relevant but in case anyone is wondering why I have a Stat class, I am following a tutorial on creating character stats that have a SerializeField.
Edit: Added code examples for clarification
My variable in my parameter method is not being called in another class.
I tried telling it that we are getting the PlayerdmgAmount from the playerlivesdisplayed class. I get an error that says that PlayerLivesDisplay can not be converted to an int.
So I comment that out and wrote in the int value again. The code runs, but it is not doing what I want it to do.
public class PlayerLivesDisplay : MonoBehaviour
{
public void takeLives(int PlayerdmgAmount)
{
playerLives -= PlayerdmgAmount;
displayUpdate();
if (playerLives <= 0)
{
//TODO load mainMenu
}
}
}//playerLives
public class DamgePlayer : MonoBehaviour
{
private void OnTriggerEnter2D(Collider2D othercollision)
{
//PlayerLivesDisplay PlayerdmgAmount = GetComponent<PlayerLivesDisplay>()
int PlayerdmgAmount = 1;
FindObjectOfType<PlayerLivesDisplay>().takeLives(PlayerdmgAmount);
}
}
public class Attacker : MonoBehaviour
{
[Range(0f, 10f)] [SerializeField] float walkSpeed = 1f;
[SerializeField] int PlayerdmgAmount = 1;
GameObject currentTarget;
public void hurtplayer(int PlayerdmgAmount)
{
FindObjectOfType<PlayerLivesDisplay>().takeLives(PlayerdmgAmount);
}
}
What I am trying to achieve:
Have the attacker script have Player dmg amount on them.
Golem1 = take 5 lives away
Fox: takes 2 lives away
Pass these variables (when collided) to the players health damage (DamagePlayer script)
Then go to the player lives display class takeLives method and input the damage variables into the parameters that was initiated from the attackers script.
If your takeLives method takes an int variable as argument, you cannot pass your PlayerLivesDisplay object, you need to pass an int instead (that's what the error is about). PlayerLivesDisplay may contain the PlayerdmgAmount (so the int) but is not in itself the PlayerdmgAmount.
Depending on what you are trying to accomplish, you could do something like:
In your PlayerLivesDisplay add a property and use it to store the value that you need to get later on:
public class PlayerLivesDisplay : MonoBehaviour
{
...
public int PlayerdmgAmount { get; set; }
...
public void takeLives(int playerdmgAmount)
{
...
this.PlayerdmgAmount = playerdmgAmount;
}
}
Now you can access the value in other classes:
public class DamgePlayer : MonoBehaviour
{
private void OnTriggerEnter2D(Collider2D othercollision)
{
int playerdmgAmount = GetComponent<PlayerLivesDisplay>().PlayerdmgAmount;
...
}
}
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
I want to create some game in Unity and I've started by creating a class hierarchy in order to be able to use polymorphism. So I've created some interfaces with both methods and also variables.
As said in the C# interfaces documentation, I've created my interface with variables like this
public interface IUnit : ISelectable {
int healthPoint { get; set; }
bool isIndestructible { get; set; }
/******************************/
void takeDamage(int dmg);
void die();
}
Now, I'm implementing my interface in a class:
[System.Serializable]
public class BasicUnit : MonoBehaviour, IUnit {
private int _healthPoint;
public int HealthPoint { get { return (_healthPoint); } set { _healthPoint = value; } }
private bool _isIndestructible;
public bool isIndestructible { get { return (_isIndestructible); } set { _isIndestructible = value; } }
public void takeDamage (int dmg)
{
if (this.isIndestructible == false) {
this.HealthPoint -= dmg;
if (this.HealthPoint <= 0) {
die();
}
}
}
public void die()
{
Destroy(gameObject);
}
}
My problem is that my variables, healthPoint and isIndestructible are not shown in Unity's inspector despite being public variables. I've tried using [System.Serializable] but it doesn't work.
Well my question is quite simple, how should I do to show my inherited variables in Unity's inspector ?
Note: I'm trying to have nice and readable code, so if possible I would like to keep my IUnit class as an interface and my variables inside my IUnit.
It's not your inherited code that's hidden. Inheritance has no effect whatsoever in the display of the fields.
Rather, what's really happening is that you have two fields that are serializeable, but marked private (_healthPoint & _isIndestructible),
and two public Properties. Unfortunately, Unity can't process and display properties out of the box.
Fortunately, here's a simple solution. I found this on Unity Wiki, and saved it for a situation like this :)
Expose Proerties in Inspector from Unity Wiki
How it works
Basically, any Monobehavior that you want the properties exposed on should inherit from ExposableMonoBehaviour and also
1. private fields (like your _healthPoint) should have the [SerializeField, HideInInspector] attributes
2. public properties (like HealthPoint) should have the [ExposeProperty] attribute
Partial example
public class BasicUnit : ExposableMonoBehaviour, IUnit {
[SerializeField, HideInInspector]
private int _healthPoint;
[ExposeProperty]
public int HealthPoint {
get { return (_healthPoint); }
set { _healthPoint = value; }
}
}
If still anyone have problem with setting value of the variables inherited from an interface in inspector, Thanks to Jetbrains Rider , I found a solution. just use [field: SerializeField] before introducing the variable in the child script.
example :
public interface IAlive
{
float HealthPoint { get; set;}
}
public class Cat : MonoBehaviour , IAlive
{
[field: SerializeField] float HealthPoint { get; set;}
}
I found it, you can use [SerializeField] on any field you want to show in the inspector. However, it have to be used on the private variable you want to serialize, not the public one.
public class BasicUnit : MonoBehaviour, IUnit {
[SerializeField]
private int _healthPoint;
public int HealthPoint { get { return (_healthPoint); } set { _healthPoint = value; } }
[SerializeField]
private bool _isIndestructible;
public bool isIndestructible { get { return (_isIndestructible); } set { _isIndestructible = value; } }
}
Let's say I have an Enemy class with a couple properties and a method AddPoints which will add experience points to an Enemy object. After a certain amount of experience points the Level property of the Enemy object will increase.
Initially I thought 'how can I make the program update the Level property when I don't know when the correct amount of experience points will be reached?. This made me think of events (have to listen for the event to occur, in this case the outcome of the LevelUp() method) so I decided to do something like
private void LevelUp()
{
if (ExperiencePoints > (5 * Level))
{
Level++;
}
}
public void AddPoints(int points)
{
this.ExperiencePoints += points;
LevelUp();
}
This way every time there are points added to the Enemy object the method will check whether or not the Level property needs to be incremented. Having one method call another method made think about containment/delegation (one method is 'nested' inside another). In this way, my AddPoints function sort of acts like a function pointer (at least in my mind).
Does anyone with a knowledge of language design or a good historical knowledge of C++/C# find this a helpful way of thinking about delegates? With the following code is there any way that a delegate can improve the program, or is it too simple?
full Enemy class
class Enemy
{
public int ExperiencePoints { get; set; }
public string Name { get; set; }
private int level;
public int Level
{
get { return level; }
private set { level = value; }
}
private void LevelUp()
{
if (ExperiencePoints > (5 * Level))
{
Level++;
}
}
public void AddPoints(int points)
{
this.ExperiencePoints += points;
LevelUp();
}
public Enemy()
{
ExperiencePoints = 1;
Level = 1;
}
}
testing
delegate void myDelegate(int x);
class Program
{
static void Main(string[] args)
{
Enemy e = new Enemy();
myDelegate del = e.AddPoints;
e.AddPoints(10); //Level =1 at runtime, after this call Level=2
del(20);//now Level=3
Console.WriteLine(e.Level);//output = 3
}
}