Utilising interfaces for polymorphism in C# - c#

By way of an intro, I'm creating a basic Quadtree engine for personal learning purposes. I'm wanting this engine to have the capability of working with many different types of shapes (at the moment I'm going with circles and squares) that will all move around in a window and perform some sort of action when collision occurs.
After asking a question on the topic of generic lists earlier, I have decided on using an interface for polymorphism. The best interface for this would be an interface utilising Vector2 due to the fact that every object that appears in my Quadtree will have an x,y position and Vector2 covers that nicely. Here is my code as it currently stands:
public interface ISpatialNode {
Vector2 position { get; set; }
}
public class QShape {
public string colour { get; set; }
}
public class QCircle : QShape, ISpatialNode {
public int radius;
public Vector2 position {
get { return position; }
set { position = value; }
}
public QCircle(int theRadius, float theX, float theY, string theColour) {
this.radius = theRadius;
this.position = new Vector2(theX, theY);
this.colour = theColour;
}
}
public class QSquare : QShape, ISpatialNode {
public int sideLength;
public Vector2 position {
get { return position; }
set { position = value; }
}
public QSquare(int theSideLength, float theX, float theY, string theColour) {
this.sideLength = theSideLength;
this.position = new Vector2(theX, theY);
this.colour = theColour;
}
}
So I'll be eventually wanting to have an interface that works to the point that I can use the generic list List<ISpatialNode> QObjectList = new List<ISpatialNode>(); and I can add shapes to it using the code QObjectList.Add(new QCircle(50, 400, 300, "Red")); or QObjectList.Add(new QSquare(100, 400, 300, "Blue")); or something along those lines (keep in mind that I'll be wanting to add different shapes later along the line).
Problem is, this code doesn't seem to work when I call it from here (Initialize() is the XNA method):
protected override void Initialize() {
QObjectList.Add(new QCircle(5, 10, 10, "Red"));
base.Initialize();
}
So my question has two parts:
1. Why does this code give me a stackoverflow error at the set {
position = value; } part of my QCircle and QSquare classes?
2. Would this be an efficient/effective way of utilising interfaces for
polymorphism?

The problem is in your property it is setting itself in circular loop
public Vector2 position { get ; set ; }
Or declare a private field
private Vector2 _position;
public Vector2 position {
get { return _position; }
set { _position = value; }
}

Stack overflow is because:
public Vector2 position {
get { return position; }
set { position = value; }
}
the set actually sets the same again. You may want this:
private Vector2 _position;
public Vector2 position {
get { return _position; }
set { _position = value; }
}
or its short version:
public Vector2 position { get; set; } //BTW, the c# standard is to use upper camel case property names
Regarding the use of polymorphism, it seems right in this scenario.

Related

Why cant I modify Transform.position?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class MoveAbleButtonSystem : MonoBehaviour
{
public Transform ObjectToMove;
public bool moveLeft = true;
public bool moveRight = false;
public float SpeedToMove = 1f;
private bool pressed = false;
public float minZ = 0f;
public float maxZ = 0f;
void OnMouseDown()
{
pressed = true;
}
void OnMouseUp()
{
pressed = false;
}
void Update()
{
Transform T = ObjectToMove.transform;
if (pressed && moveLeft)
{
T.Translate(Vector3.forward * SpeedToMove * Time.deltaTime, Space.World);
}else
if(pressed && moveRight)
{
T.Translate(Vector3.back * SpeedToMove * Time.deltaTime, Space.World);
}
if(T.position.z > maxZ)
{
T.position.z = maxZ;
}
}
}
Assets\Scripts\MainMenu\MoveAbleButtonSystem.cs(42,13): error CS1612: Cannot modify the return value of 'Transform.position' because it is not a variable
I dont get why I cannot change the position. All I want to do is for the ObjectToMove to move until it passes a certain point, after what it is not supposed to move anymore. Using Clamp provides the same error.
The following simplified example demonstrates the same compile error:
public class Program
{
public static void Main()
{
var container = new Container();
container.Property.Value = 1; // Error CS1612 - Cannot modify the return value of 'Container.Property' because it is not a variable
}
}
public struct Item
{
public int Value;
}
public class Container
{
public Item Property { get; set; }
}
This is the issue you're seeing. It's happening because the Container.Property property is returning a copy of a value type, in this case struct Item.
Because this is a copy of the item stored in Container and not the item itself, changing the copy's properties cannot have any visible effect because the value returned from the property is not stored anywhere - no variable is assigned from it.
Therefore the compiler gives an error, to avoid misleading and useless code.
The documentation for Error CS1612 explains all this in detail.
Others - and the error documentation - already explained the issue and the why in detail.
But in terms of a solution for your issue you would go e.g.
// Get copy of current position
var position = T.position;
if(position.z > maxZ)
{
// modify the copy
position.z = maxZ;
}
// assign back / apply modified value
T.position = position;
You could even combine and simplify it a lot by doing e.g.
public enum MoveDirection
{
Left,
Right
}
public MoveDirection direction;
void Update()
{
if(pressed)
{
var T = ObjectToMove.transform;
var position = T.position;
var multiplier = direction == MoveDirection.Left ? 1 : -1;
position.z = Mathf.Clamp(position.z + SpeedToMove * Time.deltaTime * multiplier);
T.position = position;
}
}
As pointed in the comments from Mathieu and Matthew, position is a struct field of the calss Transform and due to this reason you can not change its fields like this. If you want to understand why this is not working imagine that Unity's Transform and position objects look something like this:
struct Position
{
public int x;
public int y;
public int z;
}
class Transform
{
public Position _position;
public Position position
{
get
{
return _position;
}
set
{
_position = value;
}
}
}
When you write T.position.z the position part is actually a value copy of the original T's position variable returned by the property getter. So by changing the copy of position you will not get the desired effect of updating the T.position's z field. Due to this reason C# it trying to help you by throwing an error in order to prevent a bug that is very hard to identify.
You can not change the individual components of a Vector3. To do this, you will have to change all the components. i.e :
if (T.position.z > maxZ)
{
T.position = new Vector3(T.position.x, T.position.y, maxZ)
}

Nvidia Flex: Why can't I set particle count in FlexArray Actor? (Unity)

Im new to coding in C# and I'm not able to set the particle count in the Nvidia FlexArray script, the particle count public class appears grey and can't be edited. It is somehow permanently set to 0.
Ive attached an image and the script that comes with the package. Id greatly appreciate any help. The image for the inspector view on array actor
using UnityEngine;
namespace NVIDIA.Flex
{
[ExecuteInEditMode]
public class FlexArrayAsset : FlexAsset
{
#region Properties
public Mesh boundaryMesh
{
get { return m_boundaryMesh; }
set { m_boundaryMesh = value; }
}
public Vector3 meshLocalScale
{
get { return m_meshLocalScale; }
set { m_meshLocalScale = value; }
}
public float meshExpansion
{
get { return m_meshExpansion; }
set { m_meshExpansion = value; }
}
public float particleSpacing
{
get { return m_particleSpacing; }
set { m_particleSpacing = Mathf.Max(value, 0.01f); }
}
public float particleCount
{
get { return m_particleCount; }
set { m_particleCount = Mathf.Max(value, 516f); }
}
#endregion
#region Methods
#endregion
#region Messages
#endregion
#region Protected
protected override void ValidateFields()
{
base.ValidateFields();
m_particleSpacing = Mathf.Max(m_particleSpacing, 0.01f);
}
protected override void RebuildAsset()
{
BuildFromMesh();
base.RebuildAsset();
}
#endregion
#region Private
void BuildFromMesh()
{
if (m_boundaryMesh)
{
Vector3[] vertices = m_boundaryMesh.vertices;
if (vertices != null && vertices.Length > 0)
{
for (int i = 0; i < vertices.Length; ++i)
{
Vector3 v = vertices[i];
vertices[i] = new Vector3(v.x * m_meshLocalScale.x, v.y * m_meshLocalScale.y, v.z * m_meshLocalScale.z);
}
int[] indices = m_boundaryMesh.triangles;
if (indices != null && indices.Length > 0)
{
FlexExt.Asset.Handle assetHandle = FlexExt.CreateRigidFromMesh(ref vertices[0], vertices.Length, ref indices[0], indices.Length, m_particleSpacing, m_meshExpansion);
if (assetHandle)
{
FlexExt.Asset asset = assetHandle.asset;
FlexExt.Asset particlesOnly = new FlexExt.Asset();
particlesOnly.numParticles = asset.numParticles;
particlesOnly.maxParticles = asset.numParticles;
particlesOnly.particles = asset.particles;
StoreAsset(particlesOnly);
FlexExt.DestroyAsset(assetHandle);
}
}
}
}
}
[SerializeField]
Mesh m_boundaryMesh = null;
[SerializeField]
Vector3 m_meshLocalScale = Vector3.one;
[SerializeField, Tooltip("Particles will be moved inwards (if negative) or outwards (if positive) from the surface of the mesh according to this factor")]
float m_meshExpansion = 0.0f;
[SerializeField, Tooltip("The spacing used for voxelization, note that the number of voxels grows proportional to the inverse cube of radius, currently this method limits construction to resolutions < 64^3")]
float m_particleSpacing = 0.1f;
#endregion
}
}
Properties can't be serialized directly in Unity.
Recommended flow would be to mark m_particleCount as a [SerializeField], which exposes it to the editor even if it's private.
It looks like it might be buried in the Flex API though, so I'm not sure if you can edit it directly. A more complex editor script could do the job in such a case, I'd recommend looking at trying to hack the value with your own value, then using OnValidate() to write that value to the real one.
That said, is this value really what you think it is? I haven't used the API but 'particleCount' is usually the number of live particles in the system, naturally at edit time it would be 0, but at runtime it could be 10 or 20 or 1000 depending on what the particle system is doing.

Invoke a method when the underlying value of a ref returning property changes

Consider the following code:
public struct Vector2
{
public float X;
public float Y;
}
public class Sprite
{
private Vector2 position;
public ref Vector2 Position => ref position;
private void DoStuffWhenPositionChanges() { /*...code...*/ }
}
The ref return allows me to do the following set operations:
someSprite.Position.X++;
someSprite.Position.Y = 42;
I would like to invoke DoStuffWhenPositionChanges whenever either the X or Y components of the Position struct are set. Note, Vector2 is a library-level struct, and cannot be changed in my code.
I am looking for a solution similar to:
public class Sprite
{
private Vector2 position;
public Vector2 Position
{
get => position; //ref return not possible!
set
{
position = value;
DoStuffWhenPositionChanges();
}
}
private void DoStuffWhenPositionChanges() { /*...code...*/ }
}
...but with a ref return, to avoid having to do the following in the calling code:
someSprite.Position = new Vector2(someSprite.Position.X, someSprite.Position.Y + 1);
I've considered INotifyPropertyChanged, but as I cannot modify the library-level Vector2 struct, I need an alternative solution. I also considered a proxy type, implicitly convertible to Vector2, that would implement INotifyPropertyChanged, but that seems ...cumbersome; perhaps there's a cleaner solution I am not aware of.
Is it possible to know when the underlying value of the ref return property changes, given the above setup?
One way is to create a wrapper for Struct and then implement RaisePropertyChanged method or INotifyPropertyChanged:
public struct Vector2
{
public float X;
public float Y;
}
public class VectorWrapper
{
private Vector2 thing;
public var X
{
get { return thing.X; }
set { thing.X = value; RaisePropertyChanged(SomePropertyName); }
}
public var Y
{
get { return thing.Y; }
set { thing.Y = value; RaisePropertyChanged(SomePropertyName); }
}
}
It's a bit of a hack but this will get the job done.
static void Main(string[] args)
{
var s = new Sprite();
s.DoStuffWhenPositionChanges(s.Position.X++);
}
public struct Vector2
{
public float X;
public float Y;
}
public class Sprite
{
private Vector2 position;
public ref Vector2 Position => ref position;
public void DoStuffWhenPositionChanges(float f = default)
{
}
}

fixed values for property c#

I have an Movement class as a property for my objects.
What I want to do is only be able to set determined values for this property, ex:
class Movement
{
public Direction Moving;
public Direction Facing;
}
Movement movement = new Movement():
movement.Moving = Direction.Up;
movement.Facing = Direction.Down;
I could make Direction.Up/Down/Left/Right constant strings, but i dont want to be able to set anything that's not Direction.Up/Down/Left/Right' to the property.
but i don't know how to make the Direction class.
One way is to use an Enum
public enum Direction
{
Up,
Down,
Left,
Right
}
Then if you need to make sure the stored int value is a valid value for your enum: Is there a way to check if int is legal enum in C#?
You can use an enum and use it like this:
public enum Direction {
1 = Up,
2 = Right,
3 = Down,
4 = Left
}
public class UseDirection {
public Direction Dir { public get; private set; }
public void SetDirection(Direction dir) {
Dir = dir;
}
}
However, now if you want to do some manipulations on Directions, or do some calculation, you cannot implement it in Direction since it is an enum.
You could also make an abstract class Direction, and make the actual directions derive from Direction, like this:
public abstract class Direction {
private class DirectionUp : Direction { }
private class DirectionDown : Direction { }
private class DirectionLeft : Direction { }
private class DirectionRight : Direction { }
public static Direction Up => new DirectionUp();
public static Direction Down => new DirectionDown();
public static Direction Left => new DirectionLeft();
public static Direction Right => new DirectionRight();
}
Now you can use Direction like this:
public class AggregatesDirection {
public Direction Dir { public get; private set; }
public AggregatesDirection() {
Dir = Direction.Up; //uses the static property in Direction that will use the private constructor of the private class in Direction.
}
}
This pattern is nice, because you can now just use the Direction interface to do all the stuff relevant to direction, and still have different implementations for when you would, say, relocate something for a given direction in the different direction classes.
You could also just make the classes public and outside of the abstract Direction class, but you would expose the types to the namespace then, which is probably not necessary.
i figured it out, here's what did the trick for me:
public class Direction
{
private string value;
private Direction(string value)
{
switch (value)
{
case "left":
{
this.value = value;
break;
}
case "right":
{
this.value = value;
break;
}
case "up":
{
this.value = value;
break;
}
case "down":
{
this.value = value;
break;
}
default:
throw new Exception("Valor invalido");
}
}
public static class Directions
{
public static Direction Up = new Direction("up");
public static Direction Down = new Direction("down");
public static Direction Left = new Direction("left");
public static Direction Right = new Direction("right");
}
}
i put the class 'Direction' as public, but it constructor as private, so it can only be instantiated inside itself and its child's, and then made the static class 'Directions' inside it.
so i can easily:
this.directionMoving = Direction.Directions.Down;
this.directionLooking = Direction.Directions.Down;
anywhere in my code, but can't:
Direction move = new Direction();
because it's constructor is only accessible inside itself.

How to make a correct abstract entity class

I'm looking to make an abstract class Entity which then has few classes that derive from it like Enemy, Friendly and Player. The reason I do this is because the classes have a lot of properties/fields that are alike. I also have 2 methods: updateEntity and drawEntity. The reason I have the update and draw entity is that drawEntity & updateEntity is the same for most of the classes that inherit from it. This is the code of my entity class:
public abstract class Entity
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private Texture2D texture;
public Texture2D Texture
{
get { return texture; }
set { texture = value; }
}
private Vector2 position;
public Vector2 Position
{
get { return position; }
set { position = value; }
}
private int health;
public int Health
{
get { return health; }
set { health = value; }
}
private Color entColor;
public Color EntColor
{
get { return entColor; }
set { entColor = value; }
}
public Entity(string name, Texture2D texture, Vector2 position, int health, Color entColor)
{
this.name = name;
this.texture = texture;
this.position = position;
this.health = health;
this.entColor = entColor;
}
public virtual void updateEntity(GameTime gameTime)
{
//update stuff here
}
public virtual void drawEntity(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, new Rectangle((int)position.X, (int)position.Y, texture.Width, texture.Height), entColor);
}
}
And this is how I envision my Enemy class :
public Enemy(string name, Texture2D texture, Vector2 position, int health, Color entColor)
{
Name = name;
Texture = texture;
Position = position;
Health = health;
EntColor = entColor;
}
Can anybody tell me if this is a good use of abstract class or if I'm doing something completely wrong in game design/architecture way?
You usually use an abstract class when its implementation is not complete to work with but it contains properties and/or methods that are common for other types that are derived from it or it provides an interface that should be shared by derived types but cannot be implemented at this lever of abstraction and thus it is not possible to instantiate it.
Such an example could be an abstract class Fruit that has a Color property that is common to all fruits and doesn't have to be implemented by each one of them. It can also have a method Grow() without implementation. This class alone makes no sense yet. You need to implement a concrete fruit like an Apple type and implement the Grow() method for this particular fruit.
In your case the Entity would be such a fruit and the apple could be a rectangle or a circle that implement their own drawing logic.
Base entity:
public abstract class Entity
{
public abstract void Draw(); // no implementation here
public virtual void UpdateEntity(GameTime gameTime)
{
// default update
}
}
Rectangle:
public class Rectangle : Entity
{
public override void Draw()
{
// draw a rectangle here
}
}
Circle which uses a different logic for UpdateEntity:
public class Circle : Entity
{
public override void Draw()
{
// draw a circle here
}
public override void UpdateEntity(GameTime gameTime)
{
// custom update for circles
}
}

Categories

Resources