I've currently only been learning C# for a week so apologies for any stupid errors but I'm trying to call a method within a switch statement to take away an integer value that is declared within a list from the argument given to the method, the argument in this case is the health of the currEnemy object, but when the currEnemy.health value is printed out to the console it's value is unchanged and I can't figure out why.
The list that stores the integer value that the currEnemy.health is taken away by a long with the health variable which is set to an integer value in the Enemy class:
public List<Weapon> myWepList = new List<Weapon>(){
new Weapon {name = "Dagger", dmg = 10, stamDrain = 5},
new Weapon {name = "Sword", dmg = 20, stamDrain = 20},
new Weapon {name = "Halberd", dmg = 40, stamDrain = 20}
};
public int health{ get; set; }
The method within the player class that should take away the 'dmg' value from the enemy.health value and set enemy.health to a new value:
public void charAttack(int enemyHealth)
{
enemyHealth -= equippedWep[0].dmg;
}
Then the code that calls the above method to display the currEnemy.health's new value:
enum PlayerInput
{
Attack,
Block,
}
while (gameStart == true) //this part onwards is stored in the main method
{
string playerInput = Console.ReadLine().ToUpper();
PlayerInput inputChoice;
Console.WriteLine("Type 'attack' to attack the enemy");
if (Enum.TryParse<PlayerInput>(playerInput, true, out inputChoice))
{
switch (inputChoice)
{
case PlayerInput.Attack:
currPlayer.charAttack(currEnemy.health);
Console.WriteLine("Enemy Health is {0}", currEnemy.health);
break;
default:
break;
}
}
}
I'd also appreciate if any general advice is just given to me about my code considering I'm completely new to C# so any constructive advice would be great, thank you.
This method:
public void charAttack(int enemyHealth)
{
enemyHealth -= equippedWep[0].dmg;
}
... is basically pointless. The statement enemyHealth -= ... only affects the parameter called enemyHealth. It won't change currEnemy.Health at all - because the method argument is passed by value. In other words, the process is:
currEnemy.health is evaluated
The value is used as the initial value for enemyHealth
The method executes
The final value of enemyHealth isn't used at all
See my article on parameter passing for more details.
There are various ways you could tackle this - for example, you might want:
currPlayer.Attack(currEnemy);
where the Attack method would look something like:
public void Attack(Enemy enemy)
{
Weapon weapon = equippedWep[0];
enemy.Health -= weapon.Damage;
}
Note that the last line is very different to your previous code, because it would set the value of enemy.Health afterwards... you wouldn't just be changing a parameter, but the state of an object.
You can't change a value that is passing to a function. It is the same in all langages, not only C#.
int a = 0;
function changeA(int a)
{
a = 1;
}
print a; // show 0, not 1
You can change a value that is contained into an object:
public class Obj
{
public int health{ get; set; }
}
var o = new Obj();
function changeHealth(Obj o)
{
o.health = 1;
}
print o.health; // show 1
C# or whatever, that is the first thing you need to understand.
For your actual problem, here is a proposal: pass the actual object representing the enemy, not just his health. Then deduce the health of the enemy object.
public void charAttack(Player enemy)
{
enemy.health -= equippedWep[0].dmg;
}
while (gameStart == true) //this part onwards is stored in the main method
{
string playerInput = Console.ReadLine().ToUpper();
PlayerInput inputChoice;
Console.WriteLine("Type 'attack' to attack the enemy");
if (Enum.TryParse<PlayerInput>(playerInput, true, out inputChoice))
{
switch (inputChoice)
{
case PlayerInput.Attack:
currPlayer.charAttack(currEnemy);
Console.WriteLine("Enemy Health is {0}", currEnemy.health);
break;
default: break;
}
}
}
C# passes ints by value which means that you only get a copy of the value in the method. To solve it you can parse the enemy object instead like this:
public void charAttack(Enemy enemy)
{
enemy.health -= equippedWep[0].dmg;
}
This would affect the health on the enemy object.
The problem lies in your charAttack method declaration. The enemyHealth parameter is not being passed by reference, so you are actually modifying a copy of the variable not the original (this is call passing by value).
You can either pass the parameter by reference, which will mean that you modify the original:
public void charAttack(ref int enemyHealth)
{
enemyHealth -= equippedWep[0].dmg;
}
Or you could return the modified result and set your original value to it:
public int charAttack()
{
return equippedWep[0].dmg;
}
i = charAttack();
You need to pass your enemyHealth value by reference. C# defaults to passing by value which means the method gets it own copy of the value. To pass the actual int object pass by reference.
public void charAttack(int enemyHealth)
{
enemyHealth -= equippedWep[0].dmg;
}
Based on the above method, the behavior you're experiencing is expected. You are not actually stating "change the calling currEnemy.health" when calling this method, but change enemyHealth. The problem is currEnemy.health is being passed by value (as in the literal value is passed, not the "object" that represents that value) not by reference.
There are two (or more) ways to fix this:
one would be use a ref to the enemy health:
public void charAttack(ref int enemyHealth)
{
enemyHealth -= equippedWep[0].dmg;
}
and call your method like:
currPlayer.charAttack(ref currEnemy.health);
or return a new enemy health from your method:
public int charAttack(int enemyHealth)
{
return enemyHealth - equippedWep[0].dmg;
}
and call like:
currEnemy.heatlh = currPlayer.charAttack(currEnemy.health);
Related
I study C# from a book called C# player's guide this book is good very simple but I found myself stuck at certain chapter which is completely frustrating to me the author wants me to write a tic tac toe
console program (I did that but with procedural programming ) the author solution is very frustrating and vague using many classes and using fields of type Class in another which is new to me here is the code
this is the full solution of the author http://starboundsoftware.com/books/c-sharp/try-it-out/tic-tac-toe
ill just write down the codes where my confusion lies, I will write 3 Classes under the same namespace
public class Board
{
private State[,] state;
public State NextTurn { get; private set; }
public Board()
{
state = new State[3, 3];
NextTurn = State.X;
}
public State GetState(Position position)
{
return state[position.Row, position.Column];
}
public bool SetState(Position position, State newState)
{
if (newState != NextTurn) return false;
if (state[position.Row, position.Column] != State.Undecided) return false;
state[position.Row, position.Column] = newState;
SwitchNextTurn();
return true;
}
private void SwitchNextTurn()
{
if (NextTurn == State.X) NextTurn = State.O;
else NextTurn = State.X;
}
}
public class Position
{
public int Row { get; }
public int Column { get; }
public Position(int row, int column)
{
Row = row;
Column = column;
}
}
public class Player
{
public Position GetPosition(Board board)
{
int position = Convert.ToInt32(Console.ReadLine());
Position desiredCoordinate = PositionForNumber(position);
return desiredCoordinate;
}
private Position PositionForNumber(int position)
{
switch (position)
{
case 1: return new Position(2, 0); // Bottom Left
case 2: return new Position(2, 1); // Bottom Middle
case 3: return new Position(2, 2); // Bottom Right
case 4: return new Position(1, 0); // Middle Left
case 5: return new Position(1, 1); // Middle Middle
case 6: return new Position(1, 2); // Middle Right
case 7: return new Position(0, 0); // Top Left
case 8: return new Position(0, 1); // Top Middle
case 9: return new Position(0, 2); // Top Right
default: return null;
}
}
}
I understand most of the logic here what I don't understand is this line of code
public Position GetPosition(Board board)
I think the author used a method of the type Position which is a class I don't understand that and why did he use that also fields of type Class like in this example I am very frustrated searched the internet to see something like that and I did not find anything helpful please if this Question is long and frustrating at least give me something to help me THANK YOU
Sounds like you're having trouble understanding the syntax itself, so I'll break it down into its constituent concepts (italics) which can be further researched by name. See also the official docs.
public Position GetPosition(Board board)
This declares a method named GetPosition (within the Player class):
public is a C# language keyword used to tell the compiler that this method should be accessible from outside the class (i.e. given a Player object, anyone will be able to call GetPosition on it). Other access modifier keywords that could be used are private, protected, or even none at all (the default is private).
Position is the type that GetPosition is declared to return (it must therefore return only object instances of that type, or of a derived type).
In parentheses is the parameter list of the method. Parameter declarations in this list are separated by commas (,), but here there is only one parameter.
The first (and only) parameter is declared to be of type Board. This means only object instances of that type (or of a derived type) can be passed as the first argument to this method when calling it.
The first (and only) parameter is named board. The name can be arbitrary, but is used within the method's code to refer to the Board object instance that was passed in as an argument by the caller.
I have this problem in Unity (or maybe C#) that's very weird to me. Here is a virtual class:
public abstract class ActionTaken : MonoBehaviour {
protected char type;
protected Transform minionTakingAction;
public abstract void activate();
}
And this virtual class is a parent to the one that interests me:
public class AbilityTaken : ActionTaken
{
public int index;
List<Transform> selectedFriendlyMinions;
List<Transform> selectedEnemyMinions;
List<Transform> selectedTiles;
public override void activate()
{
//The value here is 0 !!! And it should be 1...
Debug.Log(selectedEnemyMinions.Count);
if (selectedFriendlyMinions.Count == 0 && selectedEnemyMinions.Count == 0 && selectedTiles.Count == 0 )
{
minionTakingAction.GetComponentInParent<AbilitiesActivation>().activateAbility(index);
}
else
{
minionTakingAction.GetComponentInParent<AbilitiesActivation>().activateAbility(index, selectedFriendlyMinions, selectedEnemyMinions, selectedTiles);
}
}
public AbilityTaken(Transform _minionTakingAction, int abilityIndex, List<Transform> _selectedFriendlyMinions, List<Transform> _selectedEnemyMinions, List<Transform> _selectedTiles)
{
type = 'S';
minionTakingAction = _minionTakingAction;
index = abilityIndex;
selectedEnemyMinions = _selectedEnemyMinions;
selectedFriendlyMinions = _selectedFriendlyMinions;
selectedTiles = _selectedTiles;
//The value here is 1 !!!
Debug.Log(selectedEnemyMinions.Count);
}
public AbilityTaken(Transform _minionTakingAction, int abilityIndex)
{
type = 'S';
minionTakingAction = _minionTakingAction;
index = abilityIndex;
selectedFriendlyMinions = new List<Transform>();
selectedEnemyMinions = new List<Transform>();
selectedTiles = new List<Transform>();
}
}
As you can see in the comments, the value of selectedEnemyMinions list of Transforms changes from the constructor (count value: 1) to the "activate()" function (count value: 0), without me making any changes to it. All I do is:
1. I create a new instance of AbilityTaken giving to the constructor an enemyMinionSelection list with 1 element
2. I add abilityTaken to a list
3. I call activate() from LateUpdate()
AbilityTaken abilityTaken = new AbilityTaken(minionTakingAction, gameMaster.abilitySelected,
// value of enemyMinionSelected.Count here is 1
new List<Transform>(), gameMaster.enemyMinionsSelected, new List<Transform>());
List<ActionsTaken> actionsTaken = new List<ActionTaken>();
actionsTaken.Add(abilityTaken);
private void LateUpdate()
{
if (!actionInProgress &&
actionsTaken.Count>0)
{
ActionTaken currentAction = actionsTaken[0];
currentAction.activate();
}
}
If you can tell me why adding the class instance to List and accessing this instance would cause a member List of Transforms to change their value, that would be great. On the other hand the value of member variable "index" doesn't change (it's always 1). I tried changing Lists to public but that doesn't help as expected.
If you can tell me why adding the class instance to List and accessing this instance would cause a member List of Transforms to change their value, that would be great.
It's not. There's nothing wrong with doing that, it won't change the current values of AbilityTaken.
There is something else going on, that is, the problem is somewhere else. For example, Debug.Log(selectedEnemyMinions); is not printing the Count.
I thoroughly debugged my code and found that the problem here is that I am giving the
constructor of ActionTaken parameters (Namely List) that get changed after assignment in ActionTaken constructor. Then I execute some code that changes the first list of transforms, but due to C# not copying the value, but referencing the assigned value the List of Transforms changes when the firstly assigned value changes.
void function()
{
//Some code here
AbilityTaken abilityTaken = new AbilityTaken(minionTakingAction, gameMaster.abilitySelected, new List<Transform>(), gameMaster.enemyMinionsSelected, new List<Transform>())
//Here we go in the constructor and everything seems fine
// ! Some code here that changes gameMaster.enemyMinionsSelected. The class object doesn't copy the List of Transforms, but references it's address and when it changes, the reference in ActionTaken also changes.
}
I have a class Star that inherits from Object. It has a GameObject field.
Here is code:
public class Star : Object {
public GameObject starObject;
// code omitted
}
It has a method that I use for selection.
public void selectStar () {
Debug.Log ("Inside star selection");
mapManager.selectionStarDidChange(this);
selected = true;
}
I have another script from which I call that method, it detects input.
public class StarObjectHandler : MonoBehaviour {
public Star starScript;
void OnMouseOver()
{
if (Input.GetMouseButtonDown(0)) {
Debug.Log ("Mouse registered star selection");
starScript.selectStar();
}
}
}
But when I call the first method, somehow this becomes null. How can I even call methods on null and they work?
What is wrong here? I checked the starScript in second class, it is not null.
In the log I get "Mouse registered.." and then "Inside star selection" as expected.
EDIT1:
Here is the method that is getting invoked from this:
public void selectionStarDidChange(Star newlySelected)
{
if (newlySelected) {
starSelector.transform.position = newlySelected.position;
} else {
starSelector.transform.position = new Vector2(-10, -10);
}
if (lastSelectedStar) {
lastSelectedStar.deselectStar();
}
if (lastSelectedShip) {
lastSelectedShip.deselectShip();
}
lastSelectedStar = newlySelected;
}
It always goes the else route, and logging here says that newlySelected is null.
EDIT2:
Here is the project if anyone feels interested to see what is wrong:
https://www.dropbox.com/s/napsqknlw3hjuku/Orion2.zip?dl=0
EDIT3:
I managed to localize the error to this code:
Star star = new Star();
Debug.Log ("Is star null? __" + (star == null) + "__");
Somehow this returns true, I will have to look up object instantiation.
Yes looks like you have declared variable without assigning it
Star star = new Star();
Will solve the issue
Apparently the problem was that this code returned null instead of an object. I have no idea how the rest of the program worked with this being null, and I can't understand why it returns null.
It is a subclass of Object class. I changed it to no superclass and it worked.
Star star = new Star();
I am trying to make a game where my background scrolls depending on how fast I want the player to be going.
I have tried creating a non-static function, that accesses BackgroundScroller.speed as a simple way to pass the value.
.
(PlayerController.cs)
void Setspeed(float setSpeed){
BackgroundScroller.speed = setSpeed;
}
BackgroundScroller.cs looks like this:
using UnityEngine;
using System.Collections;
public class BackgroundScroller : MonoBehaviour {
public float speed = 0;
public static BackgroundScroller current;
float pos = 0;
void Start () {
current = this;
}
public void Go () {
pos += speed;
if (pos > 1.0f)
pos-= 1.0f;
renderer.material.mainTextureOffset = new Vector2 (pos, 0);
}
}
.
The error I get when I try and access BackgroundScroller.speed from PlayerController.cs is: "An object reference is required to access non-static member "BackgroundScroller.speed".
I don't understand how to access the value of BackgroundScroller.speed from PlayerController.cs essentially. I don't want to create an object reference, I just want to simply change the value in the other class.
Cheers
Lucio
You cannot statically access speed because it is not a static member. It is an instance variable that can only be accessed through an instantiated BackgroundScroller.
Assuming that Start has already been called somewhere ensuring that BackgroundScroller.current is not null, the following line will give you access to the speed to use the existing static reference for the current scroller.
BackgroundScroller.current.speed = setSpeed;
Because the speed is not static type and you can fix this by adding static in speed variable.
Try to change your speed type to static float, for example
public static float speed;
then you can finally set the value of speed
void Setspeed(float setSpeed){
BackgroundScroller.speed = setSpeed;
}
I declared two variables in Main and changed both of them in a non static Method which receives a few variables(including the two I changed) and returns another, but Main doesn't recognize the change to the other variables after I use return();, and the values keep resetting. How can I make it recognize the changes?
string Attack(int Mhp, int Chp, int Cdmg, int Mdmg, string charname)
{
string res;
Mhp -= Cdmg;
Chp -= Mdmg;
Console.WriteLine();
res=charname + "'s Health has been reduced to " + Chp +", and Monster's Health reduced to " + Mhp;
return(res);
}
Mhp and Chp remain the same as I declared them in Main();, making my code pointless if the characters' health keeps resetting.
Thanks in advance!
Use
string Attack(ref int Mhp, ref int Chp, int Cdmg, int Mdmg, string charname)
When you want to do change in value type inside a procedure, it has to be passed by reference. This is C# trivia - read something on it :)
Integer is a value type, which passed to method by copy, not by reference. You are changing copy and original value stays same. Either pass value types by reference, or create reference type Character which will hold integer values, and pass character to this method.
public class Character
{
private int health;
public event Action<Character> HealthChanged;
public Character(string name, int hp, int cdmg)
{
Name = name;
health = hp;
Damage = cdmg;
}
public string Name { get; private set; }
public int Damage { get; private set; }
public bool IsAlive { get { return Health > 0; } }
public int Health
{
get { return health; }
private set
{
if (!IsAlive)
return;
health = value;
if (HealthChanged != null)
HealthChanged(this);
}
}
public void Attack(Character target)
{
if (IsAlive)
target.Health -= Damage;
}
}
This class has event to notify game core about character health changed (consider to notify about healing also). In Main create both characters and subscribe to health changing event. Then start fight:
var character = new Character("Bob", 100, 70);
character.HealthChanged += CharacterHealthChanged;
var monster = new Character("Monster", 200, 10);
monster.HealthChanged += CharacterHealthChanged;
while (character.IsAlive && monster.IsAlive)
{
character.Attack(monster);
monster.Attack(character);
}
Event handler:
static void CharacterHealthChanged(Character characer)
{
if (!characer.IsAlive)
Console.WriteLine("{0} was killed", characer.Name);
else
Console.WriteLine("{0}'s health reduced to {1}",
characer.Name, characer.Health);
}
Output
Monster's health reduced to 130
Bob's health reduced to 90
Monster's health reduced to 60
Bob's health reduced to 80
Monster is dead
primitives are handed over to a function by value, not by reference ... so you are changing a copy of your variable ... the original never sees that change ...
You have to explicitly reference them:
int something = 5;
CallMethod(ref something);
private void CallMethod(ref int x) {
x += 10;
}
Console.WriteLine(something); // Output: 15
The way you are doing it now will only change the values inside your method but will not reflect these changes to the variable outside of it. Integers are value types which means they will be sent by value. You have to explicitly define them to be sent as a reference.
In my opinion it would be better to declare Mhp and Chp (or the structure/class containing them) globally in the class.
If you really want to have them declared in your main, use the ref keyword so that the program will pass to the attack function a reference to your main's var, and not a shallow copy containing the values taken from them at the time the call to attack is being issued
string Attack(ref int Mhp, ref int Chp, int Cdmg, int Mdmg, string charname)
Note that the first approach I suggested is a lot easier (declaring class-level vars) because that way they will be accessible directly (and not by reference) by any member method of your class.
Cheers