I have one base class:
class Tile{}
And few others which extends tile
class Free : Tile{}
class Wall : Tile{}
Each tile have its own texture, it isn't string but its Texture2D which has to be loaded upon initialization. I imagine code will look similar to this but I'm not sure how to create this properly:
class Tile{
static Texture2D texture; //Static will use less ram because it will be same for inherited class?
static string texture_path; //This is set by inherited class
public Tile(){
if(texture==null)
texture = LoadTexture(texture_path);
}
}
class Free : Tile{
static string texture_path = "Content/wall.png";
}
With other words, all Free tiles have same texture and all Wall tiles have same textures - that is why in my opinion I should use static.
How to do this properly?
What you need to do is declare the property in base class and provide an option for the child class to override it. This will allow you to also provide a default value if you want to.
Some thing like this:
public class Tile
{
private string _texturePath = String.Empty;
private Texture2D _texture;
protected virtual string TexturePath { private get { return _texturePath; } set { _texturePath = value; } }
public Tile()
{
if (!string.IsNullOrWhiteSpace(TexturePath))
_texture = LoadTexture(TexturePath);
}
private Texture2D LoadTexture(string texturePath)
{
throw new NotImplementedException();
}
}
internal class Texture2D
{
}
public sealed class Free:Tile
{
protected override string TexturePath
{
set
{
if (value == null) throw new ArgumentNullException("value");
base.TexturePath = "Content/wall.png";
}
}
}
In case you do not want to provide a default texture path, you can plan to make the property and the base class abstract.
If you want your base class to have access to texture_path you should declare it in your base class.
A baseclass doesn't know anything about fields, properties or methods declared in its subclasses. This is by design BTW...
According to your question, you want all instances of Free to share a texture and all instances of Wall to share a texture. This means that you want the static fields texture and texture_path to be in the child classes, not the parent.
Ex:
public class Tile { }
public class Free : Tile
{
private static Texture2D texture;
private static string texture_path;
}
public class Wall : Tile
{
private static Texture2D texture;
private static string texture_path;
}
If you want Tile references to have texture and texture_path properties so that you can access the shared texture or texture_path from an instance, you need a virtual or abstract property.
Ex:
public abstract class Tile
{
public abstract Texture2D Texture { get; }
public abstract string TexturePath { get; }
}
public class Free : Tile
{
private static Texture2D texture;
private static string texture_path;
public override Texture2D Texture { get { return texture; } }
public override string TexturePath { get { return texture_path; } }
}
// and similarly for Wall
Related
Im working on a level editor in monogame for my engine.
I want to make a class where i can call a simple function and it will draw a sprite.
This is the function i want to call - and as you may know you have to be able to load and unload content and use the draw method.
Question: How would I make this be able to use those so that all I have to do is call this function and it works?
Here is the function:
public static void DrawSprite(Texture2D Texture, string Path, Vector2 Position, Color Color)
{
}
If you are going to leave the drawing to a single static method then you would be restricting what you are able to draw. I suggest creating an interface and do some abstraction.
Interface
public interface IGameObject
{
void Update(GameTime gameTime);
void Draw();
}
Utility Class
public sealed class GameUtility
{
private static GameUtility instance = null;
private static readonly object _lock = new object();
public ContentManager ContentManager { get; private set; }
public SpriteBatch SpriteBatch { get; private set; }
public static GameUtility Instance
{
get
{
lock (_lock)
{
if (instance == null)
{
instance = new GameUtility();
}
return instance;
}
}
}
public void SetContentManager(ContentManager contentManager)
{
this.ContentManager = contentManager;
}
public void SetSpriteBatch(SpriteBatch spriteBatch)
{
this.SpriteBatch = spriteBatch;
}
public GameUtility(ContentManager contentManager, SpriteBatch spriteBatch)
{
this.contentManager = contentManager;
this.spriteBatch = spriteBatch;
}
}
Game Objects
public class Hero : IGameObject
{
private Texture2D texture;
private Vector2 position;
private Color color;
public Hero(string path)
{
texture = GameUtility.Instance.ContentManager.Load<Texture2D>(path);
}
public void Update(GameTime gameTime)
{
// Do game update logic
}
public void Draw()
{
GameUtility.Instance.SpriteBatch.Begin();
GameUtility.Instance.SpriteBatch.Draw(texture, position, color);
GameUtility.Instance.SpriteBatch.End();
}
}
Game Class
Initialize the GameUtility
GameUtility.Instance.SetContentManager(contentManager);
GameUtility.Instance.SetSpriteBatch(spriteBatch);
Create the game objects
gameObects = new List<IGameObject>();
gameObjects.Add(new Hero("some path"));
Utilize the interface
protected override void Draw(GameTime gameTime)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
foreach (IGameObject gameObject in gameObjects)
{
gameObject.Draw();
}
base.Draw(gameTime);
}
The beauty of this approach is you can perform different drawings based on your needs. For example, you could use a Rectangle instead of Vector2 based on different scenarios. You can also draw a sprite font or something else.
For unloading content, there is only one option which is
GameUtility.Instance.ContentManager.Unload();
You better unload content during your transition to the next level as calling ContentManager.Unload() will dispose all resources. As to why it disposes everything in one go, I don't really understand but that is the design.
Hope this answer give you some insight. I would not suggest creating this public static void DrawSprite.
Trying to make a Tile system in Unity. I have my tile class done but when I create a list in the TileManager class it gives me the option to change the size of the list but gives me an empty element with no changeable variables.
Tile Class:
public class Tile : MonoBehaviour
{
public Sprite tileSprite;
public string tileName;
public int tileID;
public Tile(Sprite newTileSprite, string newTileName, int newTileID)
{
tileSprite = newTileSprite;
tileName = newTileName;
tileID = newTileID;
}
void Start()
{
SpriteRenderer spriteRenderer = gameObject.GetComponent(typeof(SpriteRenderer)) as SpriteRenderer;
if (spriteRenderer != null) spriteRenderer.sprite = tileSprite;
}
TileManager Class:
public class TileManager : MonoBehaviour {
public List<Tile> Tiles;
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update () {
}
}
Use the System.Serializable attribute on Tile class. Also be sure that the Tile class does not inherit from MonoBehaviour, otherwise it will still not appear in the inspector list.
[System.Serializable]
public class Tile
{
public Sprite tileSprite;
public string tileName;
public int tileID;
public Tile(Sprite newTileSprite, string newTileName, int newTileID)
{
tileSprite = newTileSprite;
tileName = newTileName;
tileID = newTileID;
}
}
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
}
}
I want to create some game in Unity and I've started by creating a class hierarchy in order to be able to use polymorphism. So I've created some interfaces with both methods and also variables.
As said in the C# interfaces documentation, I've created my interface with variables like this
public interface IUnit : ISelectable {
int healthPoint { get; set; }
bool isIndestructible { get; set; }
/******************************/
void takeDamage(int dmg);
void die();
}
Now, I'm implementing my interface in a class:
[System.Serializable]
public class BasicUnit : MonoBehaviour, IUnit {
private int _healthPoint;
public int HealthPoint { get { return (_healthPoint); } set { _healthPoint = value; } }
private bool _isIndestructible;
public bool isIndestructible { get { return (_isIndestructible); } set { _isIndestructible = value; } }
public void takeDamage (int dmg)
{
if (this.isIndestructible == false) {
this.HealthPoint -= dmg;
if (this.HealthPoint <= 0) {
die();
}
}
}
public void die()
{
Destroy(gameObject);
}
}
My problem is that my variables, healthPoint and isIndestructible are not shown in Unity's inspector despite being public variables. I've tried using [System.Serializable] but it doesn't work.
Well my question is quite simple, how should I do to show my inherited variables in Unity's inspector ?
Note: I'm trying to have nice and readable code, so if possible I would like to keep my IUnit class as an interface and my variables inside my IUnit.
It's not your inherited code that's hidden. Inheritance has no effect whatsoever in the display of the fields.
Rather, what's really happening is that you have two fields that are serializeable, but marked private (_healthPoint & _isIndestructible),
and two public Properties. Unfortunately, Unity can't process and display properties out of the box.
Fortunately, here's a simple solution. I found this on Unity Wiki, and saved it for a situation like this :)
Expose Proerties in Inspector from Unity Wiki
How it works
Basically, any Monobehavior that you want the properties exposed on should inherit from ExposableMonoBehaviour and also
1. private fields (like your _healthPoint) should have the [SerializeField, HideInInspector] attributes
2. public properties (like HealthPoint) should have the [ExposeProperty] attribute
Partial example
public class BasicUnit : ExposableMonoBehaviour, IUnit {
[SerializeField, HideInInspector]
private int _healthPoint;
[ExposeProperty]
public int HealthPoint {
get { return (_healthPoint); }
set { _healthPoint = value; }
}
}
If still anyone have problem with setting value of the variables inherited from an interface in inspector, Thanks to Jetbrains Rider , I found a solution. just use [field: SerializeField] before introducing the variable in the child script.
example :
public interface IAlive
{
float HealthPoint { get; set;}
}
public class Cat : MonoBehaviour , IAlive
{
[field: SerializeField] float HealthPoint { get; set;}
}
I found it, you can use [SerializeField] on any field you want to show in the inspector. However, it have to be used on the private variable you want to serialize, not the public one.
public class BasicUnit : MonoBehaviour, IUnit {
[SerializeField]
private int _healthPoint;
public int HealthPoint { get { return (_healthPoint); } set { _healthPoint = value; } }
[SerializeField]
private bool _isIndestructible;
public bool isIndestructible { get { return (_isIndestructible); } set { _isIndestructible = value; } }
}
I want to use a Texture2D for a base enum. Similar to the way Color Works. ie. Color.Black
This doesn't compile, because you can't use Texture2D as a base, I am using this code to demonstrate what I want.
public class Content
{
public Dictionary<string,Texture2D> Textures =new Dictionary<string, Texture2D>();
}
public enum Texture:Texture2D
{
Player = Content.Textures["Player"],
BackGround = Content.Textures["BackGround"],
SelectedBox = Content.Textures["SelectedBox"],
Border = Content.Textures["Border"],
HostButton = Content.Textures["HostButton"]
}
Which could then be used like
Texture2D myTexture= Content.Texture.Player;
You cannot use objects as a base for enums. What you can do is add your different textures as static properties to a class:
public static class Texture
{
public static Texture2D Player { get; private set; }
public static Texture2D BackGround { get; private set; }
...
static Texture()
{
Player = Content.Textures["Player"];
BackGround = Content.Textures["BackGround"];
...
}
}
That way you can use them like you want:
Texture2D myTexture = Texture.Player;