I want to create a class named Enemy, which should be used in a programmed rpg-themed-battlesystem. The problem is that I would want to create multiple monster types in the Enemy class, but then I would have to create a possibility for the battlesystem with every enemy class for example Enemy.Goblin or Enemy.Golem.
Question:
How could I achieve this by using only one parameter in the battlesystem function? I wanted to use
public static void InitiateBattle ( Player player, Enemy enemy )
but now I cannot use the Enemy.Goblin instance, because it cant implicitly convert Enemy.Goblin to Enemy. How could I most easily and with minimal code fix this?
You need to use inheritance.
public class Enemy
{
// put all properties and methods common to all here
}
public class Goblin: Enemy
{
// goblin specific stuff here
}
you will then be able to pass in a goblin as an enemy.
It sounds like you want to use inheritance?
public class Enemy {}
public class Goblin : Enemy {}
public class Golem : Enemy {}
You can then pass in an instance of Goblin or Golem to your method and the statement will be valid because the compiler will 'box' your object into an instance of the parent type.
Then, if you want to use a member from the Goblin or Golem subclasses, you would need to 'cast' the enemy parameter variable back into the appropriate type using as:
public static void InitiateBattle (Player player, Enemy enemy)
{
var golem = enemy as Golem;
var goblin = enemy as Goblin;
}
Make sure you check for null after the cast!
Bear in mind that C# does not allow multiple-inheritance; each class can inherit from only one parent.
I think it would be best to use interface.
public interface IEnemy
{
//e.g.
public void Attack();
}
public class Goblin : IEnemy
{
public void Attack()
{
throw new System.NotImplementedException();
}
}
public class Battle
{
public static void InitiateBattle(Player player, IEnemy enemy);
}
Related
TLDR: How can I have a script that inherits from a public abstract class have access to an often changing Enemy gameObject variable (so it can't be static) without passing it through several other scripts first?
In my game, I have a battle system where a different "Battle Event" gets loaded for each battle. Each "Battle Event" gets its own script, and each of those events inherits from the same BattleEvent parent (which is public abstract).
The code structure basically goes:
BattleSystem (main brain of battles which holds the Enemy
gameObject) ->
BattleEventsManager (handles both which BattleEvent to load, and which methods to run on that BattleEvent) ->
a random BattleEvent (BattleEventOne or BattleEventTwo etc)
public class BattleSystem : MonoBehaviour
{
BattleEventsManager battleEventsManager;
public Enemy currentEnemy;
// the Enemy data is passed when the battle starts
public void Start(Enemy enemyToLoad)
{
battleEventsManager = GetComponent<BattleEventsManager>();
currentEnemy = enemyToLoad;
}
public void BeginPlayerTurn()
{
battleEventsManager.SetupEvent(currentEnemy);
}
}
public class BattleEventsManager : MonoBehaviour
{
BattleEvent currentBattleEvent;
private void Awake()
{
// define this battleEvent
currentBattleEvent = GetComponent<BattleEventOne>();
}
public void SetupEvent(Enemy currentEnemy)
{
// start the battleEvent with its Setup function
currentBattleEvent.Setup(currentEnemy);
}
}
// inherits from `BattleEvent` parent class, shown below
public class BattleEventOne : BattleEvent
{
// override the method from the parent
public override void Setup(Enemy currentEnemy) {
// we can now use the data we need in `currentEnemy`
// all I wanted was to get access to `BattleSystem.currentEnemy`
// but i had to pass it down all the way here. Is there a better way?
}
}
// parent of all `BattleEvents`
public abstract class BattleEvent : MonoBehaviour
{
public abstract void Setup(Enemy currentEnemy);
} // end BattleEvent class
As you can see, the the currentEnemy variable needs to be passed down through 2 classes in order to get to where it needs to be: BattleEventOne.Setup().
Furthermore, I needed to add the Enemy currentEnemy param to the parent BattleEvent, which is problematic because not all BattleEvents will need this information.
I originally wanted to just call BattleSystem.currentEnemy from BattleEventOne (using a property or something), but because the BattleSystem is abstract/static, it can't access it. And because currentEnemy contains new data each battle, I can't make that a static variable.
So, how can I have BattleEventOne here access BattleSystem.currentEnemy without having to pass it down as I've done above?
(I still struggle a lot with passing information between scripts, so any help here is really appreciated!)
I have an EnemyData ScriptableObject that holds data about enemies. I'd like to have a field on EnemyData that references some logic about how this enemy behaves on its turn (in turn-based card game).
My current attempt at this is to structure that as a ScriptableObject too, basically like this:
public class EnemyData : ScriptableObject
{
public int health;
public EnemyAIBase enemyAI;
}
public abstract class EnemyAIBase : ScriptableObject
{
public abstract void PlayTurn(Enemy thisEnemy);
}
public class PirateShipAI : EnemyAIBase
{
public override void PlayTurn(Enemy thisEnemy)
{
thisEnemy.Heal();
AttackPlayer();
}
}
So as an example, I've got a "PirateShip" asset of type EnemyData, whose enemyAI field points to a "PirateShipAI" asset of type PirateShipAI.
But this feels wrong, every time I code up a new enemy's AI I have to also instantiate an asset just so it can be referenced by an EnemyData. I feel like EnemyAIBase shouldn't even be an SO, it's not like it has any variables that different assets will override. There will be a 1-to-1 mapping between EnemyData assets and custom AI for that enemy. So this SO is just a container for some logic, which doesn't feel right. But I don't know any other way to reference that logic in my EnemyData SO.
I guess I wish an SO could reference a C# class directly, but I don't think this is possible.
One option is that I could build an Editor that hides the EnemyAI asset as a sub-asset of the EnemyData asset, kinda like I did over here: Building an Editor for nested ScriptableObjects to compose abilities in a card game
But that feels really wrong here, because I don't intend to make any of this AI generic.
How can I attach behavior/logic to a ScriptableObject?
You can indeed simply make it not a ScriptableObject. To define different behaviors I would use a generic here:
public abstract class EnemyData<T> : ScriptableObject where T : EnemyAIBase
{
public int health;
public T enemyAI;
}
public abstract class EnemyAIBase
{
public abstract void PlayTurn(Enemy thisEnemy);
}
And then from these create your actual implementations
[CreateAssetMenu]
public class PirateShip : EnemyData<PirateShipAI>{ }
public class PirateShipAI : EnemyAIBase
{
public override void PlayTurn(Enemy thisEnemy)
{
thisEnemy.Heal();
AttackPlayer();
}
}
Say I have a base class called Enemy and a derived class called Ogre.
What is the difference between creating an instance these two ways:
Enemy newOgre = new Ogre();
Ogre newOgre = new Ogre();
Actually, the piece of code that is creating the instance is only new Ogre(). What is in the left side of the equal sign has nothing to do with creating the instance.
The first statement is simply assigning the created instance to a variable of type Enemy. The second one is assigning it to a variable of type Ogre.
So you have two variables of different types pointing to objects of the same type, i.e. Ogre.
The variable (what is on the left side of the equal sign), only determines what you can access from the object. For example, if the Ogre class has a method that is not inherited from Enemy, then using the Enemy variable, you will not be able to access it.
Please note that the variable does not effect how the object behave. For example, if Ogre overrides a method defined in Enemy that does something different. Calling this method on an instance of Ogre using a variable of type Enemy would cause the overridden method in Ogre to be invoked, not the one in Enemy,
For example, consider these classes:
public class Enemy
{
public virtual void Test()
{
Console.WriteLine("enemy");
}
}
public class Ogre: Enemy
{
public override void Test()
{
Console.WriteLine("ogre");
}
}
Now if you do this:
Orge enemy = new Orge();
enemy.Test();
The console would print "ogre".
And if you do this:
Enemy enemy = new Ogre();
enemy.Test();
The console would still print "orge".
In addition to Yacoub's answer, in this case, Enemy would not contain the properties, and methods that Ogre has.
public class Enemy
{
public int Property1 { get; set; }
public int Property2 { get; set; }
}
public class Ogre : Enemy
{
public int Property3 { get; set; }
}
Let's say you inherit Enemy in your Ogre class. This mean that your Ogre will effectively contain 3 properties: 1,2 and 3.
In your example you're assigning an Ogre to an Enemy type.
The Enemy type doesn't contain a "Property3" and therefor you won't be able to work with the extended class "Ogre" in an Enemy cast object.
//This will work
Ogre newOgre = new Ogre();
int newInt = newOgre.Property3;
//This wont.
Enemy newOgre = new Ogre();
int newInt = newOgre.Property3;
One declares a variable of type Enemy and references a new Ogre object. The other declares a variable of type Ogre and references a new Ogre object.
Some differences (not an exhaustive list):
You can't call non-inherited methods of Ogre on the variable of type Enemy.
Any virtual methods of Enemy that are overridden in Ogre will use Ogre's implementation when called on either variable.
Assume your Enemy class looks like this:
public class Enemy
{
public void Attack() { }
public void Move() { }
}
and your Ogre class like this:
public class Ogre : Enemy
{
public void OgreSmash() { }
}
With the Enemy variable you would only have access to Attack() and Move() but not to the OgreSmash() with the Ogre variable you will have access to the methods of the base and derived class.
Here
Enemy newOgre = new Ogre();
you can't call method using newOgre that was later added to the class Ogre for example and was not in the base class, whereas using the other variable you can call those methods.
I'm stuck in this situation where:
I have an abstract class called Ammo, with AmmoBox and Clip as children.
I have an abstract class called Weapon, with Firearm and Melee as children.
Firearm is abstract, with ClipWeapon and ShellWeapon as children.
Inside Firearm, there's a void Reload(Ammo ammo);
The problem is that, a ClipWeapon could use both a Clip and an AmmoBox to reload:
public override void Reload(Ammo ammo)
{
if (ammo is Clip)
{
SwapClips(ammo as Clip);
}
else if (ammo is AmmoBox)
{
var ammoBox = ammo as AmmoBox;
// AddBullets returns how many bullets has left from its parameter
ammoBox.Set(clip.AddBullets(ammoBox.nBullets));
}
}
But a ShellWeapon, could only use an AmmoBox to reload. I could do this:
public override void Reload(Ammo ammo)
{
if (ammo is AmmoBox)
{
// reload...
}
}
But this is bad because, even though I'm checking to make sure it's of type AmmoBox, from the outside, it appears like a ShellWeapon could take a Clip as well, since a Clip is Ammo as well.
Or, I could remove Reload from Firearm, and put it both ClipWeapon and ShellWeapon with the specific params I need, but doing so I will lose the benefits of Polymorphism, which is not what I want to.
Wouldn't it be optimal, if I could override Reload inside ShellWeapon like this:
public override void Reload(AmmoBox ammoBox)
{
// reload ...
}
Of course I tried it, and it didn't work, I got an error saying the signature must match or something, but shouldn't this be valid 'logically'? since AmmoBox is a Ammo?
How should I get around this? And in general, is my design correct?
(Note I was using interfaces IClipWeapon and IShellWeapon but I ran into trouble, so I moved to using classes instead)
Thanks in advance.
but shouldn't this be valid 'logically'?
No. Your interface says that the caller can pass in any Ammo - where you're restricting it to require an AmmoBox, which is more specific.
What would you expect to happen if someone were to write:
Firearm firearm = new ShellWeapon();
firearm.Reload(new Ammo());
? That should be entirely valid code - so do you want it to blow up at execution time? Half the point of static typing is to avoid that sort of problem.
You could make Firearm generic in the type of ammo is uses:
public abstract class Firearm<TAmmo> : Weapon where TAmmo : Ammo
{
public abstract void Reload(TAmmo ammo);
}
Then:
public class ShellWeapon : Firearm<AmmoBox>
That may or may not be a useful way of doing things, but it's at least worth considering.
You can use composition with interface extensions instead of multiple-inheritance:
class Ammo {}
class Clip : Ammo {}
class AmmoBox : Ammo {}
class Firearm {}
interface IClipReloadable {}
interface IAmmoBoxReloadable {}
class ClipWeapon : Firearm, IClipReloadable, IAmmoBoxReloadable {}
class AmmoBoxWeapon : Firearm, IAmmoBoxReloadable {}
static class IClipReloadExtension {
public static void Reload(this IClipReloadable firearm, Clip ammo) {}
}
static class IAmmoBoxReloadExtension {
public static void Reload(this IAmmoBoxReloadable firearm, AmmoBox ammo) {}
}
So that you will have 2 definitions of Reload() method with Clip and AmmoBox as arguments in ClipWeapon and only 1 Reload() method in AmmoBoxWeapon class with AmmoBox argument.
var ammoBox = new AmmoBox();
var clip = new Clip();
var clipWeapon = new ClipWeapon();
clipWeapon.Reload(ammoBox);
clipWeapon.Reload(clip);
var ammoBoxWeapon = new AmmoBoxWeapon();
ammoBoxWeapon.Reload(ammoBox);
And if you try pass Clip to AmmoBoxWeapon.Reload you will get an error:
ammoBoxWeapon.Reload(clip); // <- ERROR at compile time
The problem with which you are wrestling comes from the need to call a different implementation based on the run-time types of both the ammo and the weapon. Essentially, the action of reloading needs to be "virtual" with respect to two, not one, object. This problem is called double dispatch.
One way to address it would be creating a visitor-like construct:
abstract class Ammo {
public virtual void AddToShellWeapon(ShellWeapon weapon) {
throw new ApplicationException("Ammo cannot be added to shell weapon.");
}
public virtual void AddToClipWeapon(ClipWeapon weapon) {
throw new ApplicationException("Ammo cannot be added to clip weapon.");
}
}
class AmmoBox : Ammo {
public override void AddToShellWeapon(ShellWeapon weapon) {
...
}
public override void AddToClipWeapon(ClipWeapon weapon) {
...
}
}
class Clip : Ammo {
public override void AddToClipWeapon(ClipWeapon weapon) {
...
}
}
abstract class Weapon {
public abstract void Reload(Ammo ammo);
}
class ShellWeapon : Weapon {
public void Reload(Ammo ammo) {
ammo.AddToShellWeapon(this);
}
}
class ClipWeapon : Weapon {
public void Reload(Ammo ammo) {
ammo.AddToClipWeapon(this);
}
}
"The magic" happens in the implementations of Reload of the weapon subclasses: rather than deciding what kind of ammo they get, they let the ammo itself do "the second leg" of double dispatch, and call whatever method is appropriate, because their AddTo...Weapon methods know both their own type, and the type of the weapon into which they are being reloaded.
I think, that it's perfectly fine to check, whether passed Ammo is of valid type. The similar situation is, when function accepts a Stream, but internally checks, whether it is seekable or writeable - depending on its requirements.
I'm working on a game for WP7 with XNA. Here is my structure:
public abstract class enemy
{}
Child elements:
public class genericEnemy : enemy{}
...
public class snake : enemy {}
etc...
In WP7, a lot of things have been moved around and/or removed (especially with Serialization) it seems. Despite much searching, I haven't been able to find a solution. I'm trying to duplicate the child elements.
For example: On loading a level, I pass an array of three different enemies into the loading phase. During loading, I need to duplicate each of those enemies so that 20 of each are flying around doing their own thing during gameplay.
All the solutions I've seen refer to things that are not present in the WP7 library.
There's no "library" way of doing this as far as I know. One solution would be:
1) Declare a Clone() method in enemy that returns a copy of that enemy.
abstract class Enemy {
public abstract Enemy Clone();
}
2) Implement it in every concrete type, so a Snake creates a new Snake, etc. Example:
class Snake : Enemy {
int speed;
public override void Enemy Clone() {
var clone = new Snake();
clone.speed = speed;
return clone;
}
}
3) Now any object of a concrete type knows how to clone itself, so if you have an array of Enemies, you can call Clone() on each and it will create the proper concrete type in the proper way.
Create an enemy factory that can create enemies from an id of some sorts. While loading your level, you can then call the factory when you need to create an enemy:
class EnemyFactory
{
Enemy CreateEnemy(int id)
{
if (id == 0)
return new Snake();
return new GenericEnemy();
}
}
void LoadLevel()
{
// bla bla
Level level = new Level();
int enemyId = LoadFromFile();
level.AddEnemy(EnemyFactory.CreateEnemy(enemyId));
}
This way you get rid of the nasty cloning code, and you can control all enemy instantiation in the factory class.
use an abstract method that calls a copy constructor:
public abstract class Enemy
{
private readonly int mEnemyData;
protected Enemy(Enemy pEnemy)
{
mEnemyData = pEnemy.mEnemyData;
}
public abstract Enemy Clone();
}
public sealed class GenericEnemy : Enemy
{
private readonly double mGenericEnemyData;
private GenericEnemy(GenericEnemy pGenericEnemy)
: base(pGenericEnemy)
{
mGenericEnemyData = pGenericEnemy.mGenericEnemyData;
}
public override Enemy Clone()
{
return new GenericEnemy(this);
}
}