Overriding Static Constants in Derived Classes in C# - c#

First off, I know that you can't override static properties or functions in C#.
Here's what I need.
public abstract class Effect
{
public virtual const float duration = 1.0f;
public void boo() {
//do some stuff with duration
}
public void foo() {
//do some other stuff with duration
}
}
public class EffectA : Effect
{
public override const float duration = 3.0f;
}
There's some other stuff in the base and derived class, but the part that I'm having trouble with is this static constant. I need to refer it from other sections of code WITHOUT an instance, e.g. EffectA.duration. A function/property would also be fine, but those also can not be overriden if static.
I've seen similar questions, but all the answers seem to involve non-static solutions, i.e. instead of making it a static function, make it an instance function. In my case, in the places where I want to query EffectA.duration, it would not be desirable to create an instance. Also, I'd prefer not to have an instance variable since in reality it's these classes are Unity3D Monobehaviours, and it wouldn't make much sense to have a static instance since there are many being created and destroyed at various points.
What is the ideal work around if I want to be able to share the code in boo and still have access to the static duration for each derived class.
Here's a modified version of the relevant portion of the current code:
public abstract class Effect : MonoBehaviour
{
public virtual float kDuration { get { return 1.0f; }}
public float elapsed = 0.0f;
// Use this for initialization
protected virtual void Start()
{
elapsed = kDuration;
}
// Update is called once per frame
protected virtual void FixedUpdate()
{
elapsed -= Time.fixedDeltaTime;
if(elapsed <= 0) {
Destroy(this);
}
_doEffect();
}
protected abstract void _doEffect();
}
public class EffectA : Effect
{
public override float kDuration { get {return 3.0f; } }
protected override void _doEffect()
{
//Some behavior
}
}
The problem with this code is there's no static way to access kDuration for an Effect, and there are many portions in the code where I need to be able to get EffectA.kDuration or something similar without an instance.

What you're looking for is a bit strange, but one option to keep them static and provide new value/implementation is to just hide the inherited members, by simply using the same name:
public abstract class Effect
{
public const float duration = 1.0f;
public static void boo()
{
// this prints 1.0f
Console.WriteLine(duration);
}
}
public class EffectA : Effect
{
public new const float duration = 3.0f;
public new static void boo()
{
// this prints 3.0f
Console.WriteLine(duration);
}
}
Note the new keyword just gets rid of the compiler warning for you, using the same name is what results in hiding.
Update
Based on your comment, here's how you can avoid duplication and just call the shared implementation:
public abstract class Effect
{
public const float duration = 1.0f;
public static void boo(float val = duration)
{
// your complex shared implementation
Console.WriteLine(val);
}
}
public class EffectA : Effect
{
public new const float duration = 3.0f;
public new static void boo()
{
Effect.boo(duration);
}
}

Could try this but may not be the right approach:
class Program
{
static void Main(string[] args)
{
Console.WriteLine(" duration from Effect " + Effect.duration);
Console.WriteLine(" duration from EffectA " + EffectA.duration);
Console.WriteLine(" duration from EffectB " + EffectB.duration);
Console.Read();
}
}
class Effect
{
public const float duration = 1.0f;
}
class EffectA : Effect
{
public const float duration = 3.0f;
}
class EffectB : Effect
{
}
The result should be :
duration from Effect 1
duration from EffectA 3
duration from EffectB 1
There will be a warning in Visual Studio 2015 but can be by passed.
Not sure whether can work with Unity3D MonoDevelop.

Related

Unity code cleaning: writing general shooting script implementing same methods from different bullet classes

I picked up unity and C# a month ago so I'm still a noobie.
So far I managed to build a simple space-based arcade shooter (i have my ship, i have a way to shoot bullets). What I'm trying to achieve is a way to keep the script that takes my keyboard input to shoot separate from the possible bullet types.
The way my bullet types are currently implemented is by having a gameobject for each with its own scripts for a) taking keyboard input and b) instancing a prefab with different properties to shoot. Currently i have 2 shooting modes, and a separate script lets me swap between them with the spacebar by enabling disabling the gameobjects. An example of the scripts I'm using for one bullet type:
Script for instantiating bullet. One method simply shoots every time a button is pressed, the other "charges" an array of bullets, accompanied in the second script by a "growing aura" signifing the power increase. These two methods have the same name across different bullet classes, but are implemented differently.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BulletA_Basic : MonoBehaviour
{
public GameObject bulletPrefab;
public GameObject aura;
public Transform firingPoint;
public Transform chargingPoint;
public float bulletForce = 20f;
public float altCooldown = 1f;
public float fireRate = 1f;
public float altFirePowerMultiplier = 1f;
private void Update()
{
}
public void Shoot()
{
GameObject bullet = Instantiate(bulletPrefab, firingPoint);
Rigidbody2D rb = bullet.GetComponent<Rigidbody2D>();
rb.AddForce(firingPoint.up * bulletForce, ForceMode2D.Impulse);
}
public void SpecialShoot(int n)
{
StartCoroutine(Special(n));
}
public IEnumerator Special(int n)
{
for (int i = 0; i < n; i++)
{
Shoot();
yield return new WaitForSeconds(0.1f);
}
}
}
Script for taking keyboard input
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shooting_A : MonoBehaviour //This needs to be copied across all firing types
{
private BulletA_Basic bulletScript; //Change the class to make new projectile types with different firing modes
public Vector3 scaleChange;
private GameObject auraInstance;
private float timePassed = 0f;
private float timePassedMain = 0f;
public float timeToDetonation = 3f;
private void Start()
{
bulletScript = GetComponent<BulletA_Basic>();
}
// Update is called once per frame
void Update()
{
bool isFiring = Input.GetButtonDown("Main Cannon");
bool alternateFire = Input.GetButton("Main Cannon");
timePassedMain += Time.deltaTime;
if (isFiring && timePassedMain > bulletScript.fireRate)
{
bulletScript.Shoot();
timePassedMain = 0;
}
if (alternateFire)
{
timePassed += Time.deltaTime;
if (!auraInstance && timePassed >= bulletScript.altCooldown)
{
auraInstance = Instantiate(bulletScript.aura, bulletScript.chargingPoint);
}
if (alternateFire && auraInstance && timePassed < timeToDetonation)
{
Charge();
//Will need to add shader here
}
else if (timePassed >= timeToDetonation)
{
Destroy(auraInstance);
timePassed = 0;
}
}
else
{
if (auraInstance)
{
Destroy(auraInstance);
int powerAltFire = (int)(bulletScript.altFirePowerMultiplier * (Mathf.Pow(2 , timePassed))); //Equation returns a number of projectiles based on how long charge was held
bulletScript.SpecialShoot(powerAltFire);
}
timePassed = 0;
}
}
void Charge()
{
auraInstance.transform.localScale += scaleChange;
}
}
The key here is the bulletScript field.
Basically i'd like to make the second script general so that i don't have to implement it in a different way and copy-pasting it again and again for each type of bullet I'm going to make, and changing the bulletScript field type each time.
I tried doing it with interfaces but I'm not sure how to implement it in the general script since I need to access each field of the subclasses, which have each their own references (like bulletPrefab, or aura). In general i feel interfaces are not well integrated into unity but that might just be me.
I also tried with delegates, but i had similar problems. I simply changed the type of bulletScript to my delegate type (ShootingDelegate bulletScript), and wrote this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public delegate void ShootDelegate();
public delegate void SpecialShootDelegate(int n);
public class ShootingDelegate : MonoBehaviour
{
public ShootDelegate delShoot;
public SpecialShootDelegate delSpecialShoot;
private int weaponIndex;
public GameObject bulletPrefab;
public GameObject aura;
public Transform firingPoint;
public Transform chargingPoint;
public float bulletForce;
public float altCooldown;
public float fireRate;
public float altFirePowerMultiplier;
// Start is called before the first frame update
void Start()
{
WeaponSwap weapon = GetComponent<WeaponSwap>();
weaponIndex = weapon.weaponIndex;
switch (weaponIndex)
{
case 1:
BulletB_Fan bulletB = GetComponent<BulletB_Fan>();
delShoot = bulletB.Shoot;
delSpecialShoot = bulletB.SpecialShoot;
bulletPrefab = bulletB.bulletPrefab;
aura = bulletB.aura;
firingPoint = bulletB.firingPoint;
chargingPoint = bulletB.chargingPoint;
bulletForce = bulletB.bulletForce;
altCooldown = bulletB.altCooldown;
fireRate = bulletB.fireRate;
altFirePowerMultiplier = bulletB.altFirePowerMultiplier;
break;
case 0:
BulletA_Basic bullet = GetComponent<BulletA_Basic>();
delShoot = bullet.Shoot;
delSpecialShoot = bullet.SpecialShoot;
bulletPrefab = bullet.bulletPrefab;
aura = bullet.aura;
firingPoint = bullet.firingPoint;
chargingPoint = bullet.chargingPoint;
bulletForce = bullet.bulletForce;
altCooldown = bullet.altCooldown;
fireRate = bullet.fireRate;
altFirePowerMultiplier = bullet.altFirePowerMultiplier;
break;
}
}
// Update is called once per frame
void Update()
{
}
}
This is the error it throws:
ArgumentException: Value does not fall within the expected range.
ShootingDelegate.Start () (at Assets/Scripts/ShootingDelegate.cs:54)
which corresponds to this line
delShoot = bullet.Shoot;
I don't really care if a solution employs either interfaces or delegates, those were just things I tried. Any thoughts?
I created a sample just for you.
Here I am creating BulletType parent class for all bullet types. And atting some variable and method for all bullets.
public class BulletType : MonoBehaviour
{
// Protected is less stric than private, more stric than public
// Protected variables only accesible to this class and its child.
protected string name;
protected int bulletDamage;
protected int bulletSpeed;
protected virtual void Start()
{
}
// Virtual means you can override this method in child classes
protected virtual void Damage() { }
public virtual void PlaySound() { }
protected virtual void ShowEffect() { }
}
Now I will add child class and inherit from parent BulletType class. Everything write in comment lines.
// This class inherits from BulletType class.
public class FireBullet : BulletType
{
// If you want to completely ignore parent class variable to by adding new keyword.
new int bulletSpeed = 3;
protected override void Start()
{
// when overriding a method automatically adds this method.
// base means parent class. with base you can access parent methods.
base.Start();
// If you remove base method, parent method won't be called in this class.
// Here I accessed parent class variable and set its value.
// This doesn't effect parent or other child classes. That's the beauty.
name = "Fire Bullet";
}
void StopSound()
{
}
protected override void PlaySound()
{
// Sound played from parent.
base.PlaySound();
// You can add your own variable and methods in parent method.
StopSound();
}
}
// FireBullet inherited from BulletType, and LavaBullet inherited from FireBullet.
// You can do this as much as you want.
public class LavaBullet : FireBullet
{
protected override void PlaySound()
{
// Here base will be FireBullet
base.PlaySound();
}
}
public class IceBullet : BulletType
{
// Add as much as you thing.
}
And for using BulletType script in your player just add this line
public class Player
{
public BulletType currentBulletType;
// You can get child from main class.
FireBullet fireBullet = currentBulletType.GetComponent<FireBullet>();
// Now you can access and use child class methods.
fireBullet.PlaySound();
}
and when you want to change bullet type, just assign new bullettype. Because all bullet type inherit from this class. You can assign every bullet type.
I don't know if it's the answer but,
delShoot = bulletB.Shoot;
delShoot is a reference to a script.
bulletB.Shoot is a function.
I don't think you can make one equal to the other.
If I understand your question, you want to make one script for two type of bullets shoot. You can create a scipts Shoot.cs who is instantiate with a value (1 for the default fire 2 for the second fire) and other scrits defaultFire.cs / secondFire.cs with they own properties.
Like this you'll juste instantiate once your Bullet like this :
public void Start(){
case 1 : Shoot shoot = new Shoot(1)
case 2 : Shoot shoot = new Shoot(2)
}
Hope this help a little..

Interactable Objects Unity

First of all, I'm sorry about the lack of information in the title. but I couldn't do better.
The point is I builded an abstract class who checks all my collisions, inside there is a public bool called "isInteracting" to check if I'm inside a collider, if I do "isInteracting" is true, otherwise false. So, classes who inherits from this parent class can use "isInteracting" to knows when I'm inside the collider an do something (ex. display a message, play a sound, etc.), when I created the second class who inherits from the parent class I realized my bool to check if I'm inside the collider didn't work fine because both child classes call it, How can I fix it? make them independents.
Simple Example:
public class Waitress : CollisionBehaviour
{
public override void SayHi()
{
if (isInteracting) Debug.Log("Hi");
else return;
}
public override void Update()
{
SayHi();
base.Update();
}
}
"SayHi()" is an abstract method from the abstract class "CollisionBehaviour", and "Update()" is overridden because it's virtual.
At the request of #Poul Bak:
Parent Class:
public abstract class CollisionBehaviour : MonoBehaviour
{
[HideInInspector] public bool isInteractingByButtonAction;
[HideInInspector] public bool isInteractingByTriggerCollision;
private bool buttonPressed, isInside, a, b;
private void CheckStay()
{
isInteractingByTriggerCollision = true;
// GameInputs.Interact is a mapped button from my controller
if (GameInputs.Interact && !buttonPressed)
{
buttonPressed = true;
isInteractingByButtonAction = true;
}
a = (!buttonPressed) ? true : false;
b = (!buttonPressed) ? true : false;
}
private void CheckExit()
{
isInside = false;
b = false;
isInteractingByButtonAction = false;
isInteractingByTriggerCollision = false;
if (buttonPressed) buttonPressed = false;
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player")) isInside = true;
}
private void OnTriggerExit(Collider other)
{
CheckExit();
}
private void Update()
{
if (isInside) CheckStay();
}
public virtual void LateUpdate()
{
if (a && b) { /* Will display a sprite to indicate which button should be pressed*/ }
}
}
Child class:
public class InteractableObject : CollisionBehaviour
{
#region Exposed Variables
public AnimationCurve ease;
public float smoothTime;
public float minFov = 30;
public float maxFov = 50;
#endregion
private float currentVelocity;
private void InteractionFieldOfView()
{
// Animerp is an animation curve interpolation... Clever name I know
Camera.main.fieldOfView = isInteractingByTriggerCollision.Animerp(maxFov, minFov, ref currentVelocity, smoothTime, ease);
}
public override void LateUpdate()
{
InteractionFieldOfView();
base.LateUpdate();
}
}
Well, camera field of view (isInteractingByTriggerCollision) only works fine when only one class inherit from CollisionBehaviour, as I told you.
BTW, I avoid OntriggerStay because garbage collector is pretty high
Your post is poorly formatted and explained and I found it hard to understand what you want your code to do an the errors being faced, nevertheless I took a shot.
This class should replace your CollisionBehaviour class, it essentially imitates OnTriggerStay by calling a function every frame while the player is within the trigger.
using UnityEngine;
[RequireComponent(typeof(SphereCollider))]
internal abstract class CollisionTrigger : MonoBehaviour
{
private bool _isPlayerInsideTrigger = false;
private void Update()
{
if(_isPlayerInsideTrigger)
{
FakeOnTriggerStay();
}
}
private void OnTriggerEnter(Collider collider)
{
if(!collider.CompareTag("Player")) return;
_isPlayerInsideTrigger = true;
}
public abstract void FakeOnTriggerStay();
private void OnTriggerExit(Collider collider)
{
if(!collider.CompareTag("Player")) return;
_isPlayerInsideTrigger = false;
}
}
This is an example to demonstrate what your Waitress class would look like using the provided class above.
internal class Waitress : CollisionTrigger
{
public override void FakeOnTriggerStay()
{
// Replace this with input system you are using.
if(Input.GetKeyDown(...))
{
Debug.Log("Hi");
}
}
}
Hope this helps.

Why isn't the value of class member changing?

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.

How can I use const fields of a child class in inherited methods where the parent class has the same const fields?

I have a base class called Projectile and a child class called SaiBlast. In my SaiBlast class, I want to use methods inherited from Projectile, but still use const variables belonging to SaiBlast in these inherited methods.
Here's a minimal example.
base class:
class Projectile
{
protected const float defaultSpeed = 50;
public void Shoot( float speed = defaultSpeed ) //optional parameter
{
//code
}
}
child class:
class SaiBlast : Projectile
{
protected new const float defaultSpeed = 100;
}
Now if I say:
SaiBlast saiBlast = new SaiBlast();
saiBlast.Shoot();
Shoot() should use a value of 100 since that is the default speed for sai blasts. Right now it uses the default speed for Projectiles in general which is 50.
I was half expecting this to work because of polymorphism, but I figured I'd run into this problem because the compiler fills in the hard values for constants at compile time.
How can I accomplish this?
class Projectile
{
protected virtual float DefaultSpeed { get { return 50; } }
public void Shoot(float? speed = null)
{
float actualSpeed = speed ?? DefaultSpeed;
//Do stuff
}
}
class SaiBlast : Projectile
{
protected override float DefaultSpeed { get { return 100; } }
}

Functions that call functions and/or delegates

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
}
}

Categories

Resources