I'm learning C# and I'm trying to make a game and I have a problem. I have two classes that I call Item and Weapon, Weapon looks something like this:
class Weapon : Item
{
int damage;
int durability;
public void asd()
{
Weapon shortSword = new Weapon();
shortSword.dmg = 5;
shortSword.durability = 20;
Weapon woodenBow = new Weapon();
woodenBow.dmg = 3;
woodenBow.durability = 15;
}
}
Then I have another class containing different methods, one of them is called when the player walks on an item and its supposed to randomize that item. But I can't reach the objects in Weapon from this other class. How do I reach them, or solve this problem?
There is a problem with your design. The method that creates weapons shouldn't be an instance method on the Weapon class. It could perhaps be a static method, or perhaps even a method on some other class.
Your method should return the weapons in a collection or as an IEnumerable.
public class Weapon : Item
{
public int Damage { get; set; }
public int Durability { get; set; }
// Consider moving this method to another class.
public static IEnumerable<Weapon> CreateWeapons()
{
List<Weapon> weapons = new List<Weapon>();
Weapon shortSword = new Weapon { Damage = 5, Durability = 20 };
weapons.Add(shortSword);
Weapon woodenBow = new Weapon { Damage = 3, Durability = 15 };
weapons.Add(woodenBow);
return weapons;
}
}
I also used the object initializer syntax to make the code more concise, and tidied up some of your method/property naming.
Related
I have a class in a very basic C# console game (text adventure) that I would like to modify a variable elsewhere in the code. I understand that C# does not allow global variables, but that is essentially what I am looking for.
I am a beginner at programming. I am stumped and I've been unable to find an answer to this question. It's really a question of scope that I'm after; what variables can "talk" to each other. I'll include a simple example below.
All of these fields in the class will
be modified elsewhere within various methods.
class Player
{
public int health = 100;
public string armor = "none";
public string gun = "none";
public int money = 1000;
}
public static void AddMoney()
{
var NewPlayer = new Player();
NewPlayer.money += 1500;
}
So I want to basically know what is expected of me to do if global variables aren't a thing in C#.
There's a few ways of updating instance variables in C#: Using properties or setters like in Java or instance methods. Following code shows you a few of those.
Ultimately, what you want to do is pass a reference to the instance of the player to different methods and let them use the properties/methods to update the instance variables:
namespace Test
{
class Program
{
static void Main(string[] args)
{
var p1 = new Player();
p1.Health = 5000;
p1.Armor = "Helmet";
p1.AddMoney(200);
Console.WriteLine($"Health: { p1.Health }");
var enemy = new Enemy();
enemy.Shoot(p1);
Console.WriteLine($"Health after being shot: {p1.Health}");
}
}
class Player
{
private string armor;
private int money;
public int Health { get; set; }
public string Armor
{
get => armor;
set
{
Console.WriteLine("Updating armor.");
armor = value;
}
}
public int Money { get; private set; }
public void AddMoney(int money)
{
// More complex logic here, example: synchronization.
this.money = money;
}
}
class Enemy
{
public void Shoot(Player p)
{
p.Health -= 500;
}
}
}
This is the object-oriented way of doing things. As for global variables in the sense of application-wide variables, you can refer to this.
You can also make the Player a singleton. This is more a design choice than actual C# stuff. I'm not going to go into singleton pattern here. If you search singletons and their pros and cons, you will get an earful of that :)
Disclaimer: you could make Player a static class, which would probably behave how you want, but doing so is ill-advised as it limits you to a single player object and can't easily be unit tested, amongst other problems. See When to use static classes in C#.
You have to understand that you Player is a class. Think of it as a blueprint for how a Player object should look. I usually go to a car analogy for this: you buy two Toyota Priuses. Identical in every way. You take one to a spray shop and have a design painted on it. Has the other Prius changed? No. It's the same with var player1 = new Player(); var player2 = new Player(); - they are both of type "Player", but they are not the same player.
Now consider your AddMoney method:
public static void AddMoney()
{
var NewPlayer = new Player();
NewPlayer.money += 1500;
}
You're creating a player that only exists within the AddMoney method. Once outside, it effectively doesn't exist anymore (stuff isn't deleted from memory immediately, but garbage collection isn't a topic you should be concerned with just yet).
Since Player is a class, it's a reference type, which means that passing it to a method will pass a reference to the same object in memory. This means that we can change the object in AddMoney and it will be reflected in the caller. Note that we can't replace the object - for that you would need the ref keyword (but that's another topic too).
public static void AddMoney(Player player)
{
player.money += 1500;
}
Example:
var player = new Player();
player.money = 8500;
Console.WriteLine(player.money); // 8 500
AddMoney(player);
Console.WriteLine(player.money); // 10 000
Try it online
Alternatively, if you want to create a player, you could create a factory method:
public static Player CreatePlayer()
{
return new Player
{
money = 1500
};
}
First I want to give some context. I got two different classes (class Player and class Enemy), each class contains different data, but they both hold the value "int Initiative".
public class Enemy : MonoBehaviour
{
public int initiative;
//More code
}
public class Player : MonoBehaviour
{
public int initiative;
//More code
}
On my project I have several units from both classes and they are stored on 2 different lists of objects
private List<Player> players;
private List<Enemy> enemies;
void Awake()
{
players = new List<Player>();
enemies = new List<Enemy>();
}
Is not shown in the code, but each unit is being sent and storaged on those list depending on their class.
Now my question:
Is there any way of combining both list into a single list keeping all the different objects? (I tried to do this, but didn't get far)
If there is no way of combining both lists because they contain different types of objects, could I create a single list that only storage the int initiative, type of object as well as the position on their previous list? (so I can refer to the lists (players and enemies) when needed. Please explain me how I could achieve this (if possible with some code).
With this I am trying to create some sort of system that will look at the initiative of each unit and call them in order starting for the one that has the highest.
Please I am pretty new in C# and coding in general, so excuse me if the question is too simple, or doesn't make sense.
Thanks in advance :)
You can do something like this:
public class MonoBehaviour
{
public int initiative;//Since you are using inheritance this can be set here
}
public class Enemy : MonoBehaviour
{
//More code
}
public class Player : MonoBehaviour
{
//More code
}
Here is the code to merge them into one list:
List<MonoBehaviour> list = new List<MonoBehaviour>()
list.Add(new Enemy());
list.Add(new Player());
When you want to process them differently somewhere for example you create a method as below:
void ProcessList(List<MonoBehaviour> list)
{
foreach(var l in list)
{
if(l is Enemy)
{
var enemy = (Enemy) l;
//process the enemy
}
else
{
var player = (Player) l;
//process as a player
}
}
}
You can use inheritance. Having base for both Enemy and Player.
public class AliveEntity
{
public string Name {get;set;}
public double HP {get;set;}
}
public class Player : AliveEntity
{ /*...*/ }
public class Enemy : AliveEntity
{ /*...*/ }
And then the list could be List<AliveEntity>.
I'm currently working on a school project that's due to friday & I've got a whole lot to do.
The assignment is to make a video game with XNA framework using Monogame. I'm currently working with collisions.
The structure for gameobjects looks somewhat like this:
For collisions, I have a simple collision class
class Collision{
public GameObject Other;
public GameObject Obj1;
public Collision(GameObject obj1, GameObject other)
{
Other = other;
Obj1 = obj1;
}}
The collisions are handled in a static method in the GameObject class:
public static void UpdateCollisions()
{
//Empty the list
AllCollisions.Clear();
for (int a = 0; a < AllGameObjectsWithCollision.Count; a++)
{
GameObject obja = AllGameObjectsWithCollision[a];
for (int b = 0; b < AllGameObjectsWithCollision.Count; b++)
{
GameObject objb = AllGameObjectsWithCollision[b];
if (obja.Mask != null & objb.Mask!= null && obja != objb)
{
if (obja.Mask.CollisionRectangle.Intersects(objb.Mask.CollisionRectangle))
AllCollisions.Add(new Collision(obja, objb));
}
}
}
}
This far it's working, the game is finding all collisions like it should. However now I need to let my objects know that they're colliding, and tell them what to do.
For this, I made the entity class abstract to be able to declare an abstract method called "OnCollision(Collision collision)"
abstract class Entity : GameObject
{
public float Health;
public float MaxHealth;
public bool Alive;
public float OriginalDmg;
public float Dmg;
public abstract void OnCollision(Collision collision);
}
Then I'm overriding the method in the classes that inherit the Entity class
Ex. Projectile
class Projectile : Entity
{
Entity Owner;
ProjectileType pType;
public Projectile(Texture2D image, float maxSpeed, Entity owner, float dmg, ProjectileType type)
{
Image = image;
MaxSpeed = maxSpeed;
AccelerationSpeed = MaxSpeed;
Owner = owner;
Dmg = dmg;
pType = type;
}
public override void OnCollision(Collision collision)
{
#region If this projectile friendly
if (pType == ProjectileType.Friendly)
{
//If colliding with an enemy
if (collision.Other.GetType() == typeof(Enemy))
{
var enemy = (Enemy)collision.Other;
enemy.Health -= Dmg;
Destroy(this);
}
}
#endregion
#region If this projectile is hostile
if (pType == ProjectileType.Hostile)
{
}
#endregion
}
}
Then I'm trying to call the OnCollision method from my Update in the GameObject class.
This is how I try to inform my objects if they are collding and who they're colliding with:
if (GetType().IsAssignableFrom(typeof(Entity)))
{
Entity entity = (Entity)this;
if (GetType() == typeof(Player))
entity = (Player)this;
if (GetType() == typeof(Enemy))
entity = (Enemy)this;
if (GetType() == typeof(Projectile))
entity = (Projectile)this;
var entityCol = FindCollision(entity);
if (entityCol != null)
entity.OnCollision(entityCol);
}
I'm new to abstract classes & overriding, so I might have gotten the whole idea wrong.
But it seems the OnCollision method isn't reached as I've tried to Debug.WriteLine stuff but nothing shows up in the output window.
Thanks for reading & perhaps trying to help me out :)
Mediafire link to download the project in case you want to see all the code.
You should read up on interfaces. An interface provides a contract (a bunch of methods and properties) that deriving classes must implement. Abstract classes are more concrete than interfaces in that they can also provide a base implementation for deriving classes. You can only derive from one abstract class whereas you can derive from multiple interfaces. From the code in your post it looks like you are using an abstract class like an interface.
You are using reflection to do type checking. There's the is keyword for testing type compatibility. For example:
if(entity is Player)
{
var player = (Player)entity;
// player specific code
}
Finally; from what I can gather from your post it looks like you aren't quite using inheritance correctly. It looks like you are correctly using inheritance to build a type hierarchy but then putting all the logic in a base class.
Inheritance is meant to let you put the specialized logic in the appropriate class.
public interface IGameObject
{
void OnCollision(IGameObject target);
}
public class Player : IGameObject
{
public void OnCollision(IGameObject target)
{
Console.WriteLine("Player collision");
}
}
public class Projectile : IGameObject
{
public void OnCollision(IGameObject target)
{
Console.WriteLine("Projectile collision");
}
}
When we then have a reference to a IGameObject and call OnCollision the appropriate OnCollision function will automatically be called. For example:
IGameObject player = new Player();
IGameObject projectile = new Projectile();
player.OnCollision(projectile);
projectile.OnCollision(player);
Output:
Player collision
Projectile collision
Eh, wrong if statement...
Using
if (GetType().IsSubclassOf(typeof(Entity)))
fixed it.
Silly mistake, my bad.
Basically I have 3 classes: Game, Level and Player (which is a GameObject). Stripped to the bare minimum it looks something like this:
class Game
{
private Level[] levels;
private Player player;
public Game()
{
levels = new []{new Level(player)};
player = new Player(levels[0]);
}
}
class Level
{
private List<GameObject> gameObjects;
public Level(Player player)
{
gameObjects.Add(player);
}
public void DoSomething() {}
}
class Player : GameObject
{
private Level level;
public Player(Level level)
{
this.level = level;
level.DoSomething();
}
}
abstract class GameObject {}
Is it possible to make this work somehow? player must be created inside Game.
Fix your design. There is no "has-a" relation between player and level, in neither direction (or at least not in both). Or if you think there is, explain why tou think so.
As you found out, using your current design you can't instantiate the one without the other, creating a circular dependency. Of course, to "just make it work" you can create a property or setter method:
pubic class Player
{
private Level _level;
public Level Level
{
get { return _level; }
set { _level = value; }
}
// Or auto-implemented property
public Level Level { get; set; }
public Player()
{
}
}
(Or the same, but then for the Level).
Now you can instantiate a player without requiring a level:
var player = new Player();
var level = new Level(player);
player.Level = level;
As stated before, you can't instantiate one object without already having the other. If you want to keep the design you have now a solution would be to have a Game reference in each other class, like this:
class Level
{
private Game game;
public Level(Game game)
{
this.game = game;
}
}
class Player
{
private Game game;
public Player(Game game)
{
this.game = game;
}
}
You would construct them inside the Game class like this:
levels = { new Level(this) } // (this refers to the instance of Game)
player = new Player(this);
since the constructors accept an instance of Game
Then to access the levels or player object, you would do this inside Level or Player:
this.game.levels
or
this.game.player
Well, next time add Unity tag to your question.
As it is now it is not going to work because currently Player class requires instance of Level class (in constructor) and Level class requires instance of Player class - this makes circular dependence and neither object can be instantiated. So first of all we should break it by removing Player to Level aggregation. Because of condition that we have (Player must be instantiated only inside Game class) we should mark it as abstract :
abstract class AbstractPlayer : GameObject
{
public Level level { get; set; }
}
Now we can modify Game class with new logic and add nested concrete Player class that inherits from AbstractPlayer :
class Game
{
private List<Level> levels;
private Player player;
public Game()
{
player = new Player();
Levels.Add(player);
}
// uncomment this method if you need it
//public Player CreatePlayer()
//{
// return new Player();
//}
private class Player : AbstractPlayer
{
public Player()
{
}
}
}
class Level
{
private List<GameObject> gameObjects;
public Level(Player player)
{
gameObjects.Add(player);
player.Level = this;
}
public void DoSomething() {}
}
I have a declared entity of a class, and want to assign different pre-made templates to it without the templates ever changing. Using a const doesn't seem to do the trick.
Example:
Weapon w1;
w1 = Sword; // premade weapon.
w1.reducedamage(1); // for example a debuff
In this case the premade weapon's damage would be decreased, and it would no longer be available as a template. This problem becomes more profound with enemies.
Example:
Enemy enemy;
enemy = enemies[r] // r being a randomly generated integer and enemies a list of enemy templates
Fight(player,enemy); // this method would resolve a fight between the two entities of the type Character.
This problem would not be visible in the player class, since player is a single reference being passed along all the game methods - because there is only one player. Every time the player fights, an enemy template would be "corrupted".
How would I create templates or classes/structs in general that always pass by value, meaning that the properties of a first class would have the same values as a second, without any relationship between the two classes?
The only success I've gotten with this is to create a method that manually copies each attribute of every class that has a template onto another entity of the same class; but this is extremely unpractical since it needs constant upgrading whenever a new class is added, or an old one changed.
I must be missing something. This seems like a reasonably simple issue that is easily solved by inheritance, perhaps in conjunction with some sort of Factory. First, you don't want to use a reference to a single instance, you want to create a new instance each time so it is a unique object. I prefer classes over structs, but you could easily create a new struct as well. You could use a Factory to create various pre-configured instances of the objects that have pre-defined values. For example, the Sword of Damocles or the Sword of Destiny.
public static class WeaponFactory
{
public static Weapon CreateSword(SwordType type)
{
var sword = new Sword(); // plain, old default sword
// override properties based on type
switch (type)
{
case SwordType.SwordOfDamocles:
sword.FallTime = GetRandomFutureTime();
break;
case SwordType.SwordOfDestiny:
sword.Invincible = true;
break;
...
}
return sword;
}
...
}
Alternative using Actions
public static class WeaponFactory
{
public static Weapon Create<T>(Action<T> decorator) where T : IWeapon, new()
{
var weapon = new T();
decorator(weapon);
return weapon;
}
public static void SwordOfDamocles(Sword sword)
{
sword.FallTime = GetRandomFallTime();
}
public static void SwordOfDestiny(Sword sword)
{
sword.Invincible = true;
}
}
var weapon = WeaponFactory.Create(WeaponFactory.SwordOfDamocles);
What you want is object cloning. You can implement it via the ICloneable interface[1]. That requires that you implement your own cloning mechanism though--you have to do the heavy lifting.
However, what you probably should do instead is just have the constructor take a parameter that represents the template you want, and then fill the properties of the object in question based on that template. That's the direction I go when I want to make duplicate things with a base set of values.
You could do actual copying (e.g. provide a copy constructor as in http://msdn.microsoft.com/en-us/library/ms173116(v=vs.80).aspx ), but what I've seen most often in such cases is a factory pattern, e.g. Weapon w1 = Weapon.CreateSword(); or Enemy e=Enemy.CreateEnemyOfType(r);
you could build a method to return multiple enemies in either a generic collection or array into your enemy class. Something like:
public shared function getEnemies(num as integer, type as string) as list(of clsEnemy)
dim enemyGroup as list(of clsEnemy)
for i = 0 to num - 1
dim thisEnemy as new clsEnemy(type)
enemyGroup.add(thisEnemy)
next
return enemyGroup
end function
Contrary to copying objects to implement some kind of "applied object" pattern, it's good to keep in mind it's not the sword "base item" that is being altered, but the item your player is carrying.
For example, a given sword, say "rusty old sword", will always have a base damage of 50. Now if someone applies "old stuff gets better magic" to it, it's not the "rusty old sword" that gets more damage: if some other player that hasn't got that kind of magic picks up the item, it's back to its base damage of 50.
So if you implement some kind of EquippedWeapon (or even EquippedItem) class, you can let your player equip weapons and give it extended properties. Something like this to declare a Sword:
interface IWeapon
{
int Damage { get; }
}
class Sword : IWeapon
{
public int Damage { get; private set; }
public Sword()
{
this.Damage = 50;
}
}
Now we have a sword with a base damage of 50. Now to let the player carry this sword:
interface IDamageModifier
{
int Damage { get; set; }
}
class EquippedWeapon : IWeapon
{
public int Damage
{
get
{
return CalculateActualDamage();
}
}
public List<IDamageModifier> DamageModifiers { get; set; }
private IWeapon _baseWeapon = null;
public EquippedWeapon(IWeapon weapon)
{
_baseWeapon = weapon;
}
private int CalulcateActualDamage()
{
int baseDamage = _baseWeapon.Damage;
foreach (var modifier in this.DamageModifiers)
{
baseDamage += modifier.Damage;
}
return baseDamage;
}
}
A weapon contains a list of active modifiers, that affect the damage of the carried item, but not the base item. This way you can share one Sword instance with many (non-)playable characters.
Now if the player gets attacked and that attack has a damage effect, you simply apply that to the item(s) the player is carrying, so each successive attack from that player will have those effects applied:
class Player
{
public EquippedWeapon PrimaryWeapon { get; set; }
public Player()
{
this.PrimaryWeapon = new EquippedWeapon(new Sword());
}
public void UnderAttack(Attack attack)
{
// TODO: implement
if (attack.Buffs...)
{
this.EquippedWeapon.DamageModifiers.Add(attack.Buffs);
}
}
}
I wrote an answer answering your question directly. But now I see that all you want is to create items that are the same but not linked.
That's what happens anyway when you create an instance. You don’t have to do anything.
If you have:
class Class1
{
public int i;
}
Then:
Class1 c1 = new Class1() { i = 1 };
Class1 c2 = new Class1() { i = 2 };
Text = c1.i.ToString();
Prints "1", not "2".
And if you mean you want a "Player" class with sub-classes "Friend" and "Foe" - That's what inheritance is for:
class Player
{
}
class Friend : Player
{
}
class Foe : Player
{
}
EDIT:
Perhaps this will make the task easier: (The "Duplicate" method)
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Class1 c1 = new Class1() { i = 1, j = 2 };
Class1 c2 = Duplicate(c1);
c1.i = 3;
Text = c2.i.ToString();//Prints "1";
}
public Class1 Duplicate(Class1 c)//Duplicates all public properties.
{
Class1 result = new Class1();
PropertyInfo[] infos = typeof(Class1).GetProperties();
foreach (PropertyInfo info in infos)
info.SetValue(result, info.GetValue(c, null), null);
return result;
}
}
public class Class1
{
public int i { get; set; }
public int j { get; set; }
}