Monogame Sprite Class - c#

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.

Related

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

Xna - Objects sharing the same texture

In my game, I have an Ai class which is basically just a linked list of every ai in my game. this same class holds the default textures for every ai, and all of my ai's seperate classes inherit from this class, that way they all can inherit the default textures that were already loaded by the ai class. However, I seem to be having problems with this. My game never loads up the gui when ran, and through debugging, it seems like the game has problems with the textures I am passing. Are you not able to load a single texture and pass that same texture for another object to use?
AI class:
class AIs
{
private GraphicsDevice graphics;
private ContentManager content;
private SpriteBatch spriteBatch;
private LinkedList<object> ais;
private LinkNode<object> current;
//Default Textures
private Texture2D robotTexture
// Default Color Datas
private Color[] robotColorData;
public AIs()
{
}
public void Load(ContentManager content, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
this.spriteBatch = spriteBatch;
this.graphics = graphics;
this.content = content;
// Loading Default Textures
robotTexture = content.Load<Texture2D>("robot");
// Loading Default Color Data
robotColorData = new Color[robotTexture.Width * robotTexture.Height];
robotTexture.GetData(robotColorData);
}
public void Update()
{
current = ais.getHead();
while (current.getNext() != null)
{
if (current.getData() is Robot)
{
((Robot)current.getData()).Update();
}
}
}
public void Draw()
{
current = ais.getHead();
while (current.getNext() != null)
{
if (current.getData() is Robot)
{
((Robot)current.getData()).Draw();
}
}
}
public addRobot(float spawnX, float spawnY)
{
Robot temp = new Robot(spawnX, spawnY);
temp.Load(content, graphics, spriteBatch);
ais.add(temp);
}
public Texture2D getRobotTexture()
{
return robotTexture;
}
public Color[] getRobotColorData()
{
return robotColorData;
}
}
Robot Class:
class Robot : AIs
{
private GraphicsDevice graphics;
private ContentManager content;
private SpriteBatch spriteBatch;
private Texture2D robotTexture;
private Color[] robotColorData;
private Rectangle robotRectangle;
private Vector2 robotPosition = Vector2.Zero;
public Robot(float spawnX, float spawnY)
{
robotPosition = new Vector2(spawnX, spawnY);
}
new public void Load(ContentManager content, GraphicsDevice graphics, SpriteBatch spriteBatch)
{
this.spriteBatch = spriteBatch;
this.graphics = graphics;
this.content = content;
robotTexture = getRobotTexture();
robotColorData = getRobotColorData();
}
new public void Update()
{
robotRectangle = new Rectangle((int)robotPosition.X, (int)robotPosition.Y, robotTexture.Width, robotTexture.Height);
}
new public void Draw()
{
spriteBatch.Draw(robotTexture, robotPosition, Color.White);
}
}
Turns out, all you have to do is add the keyword "static" next to the texture and color data.
If you don't put static, then when the class is created, it will inherit the methods and variables, but the variables will be new, null, because it is a new instance of the class. So putting static next to it makes the value stay the same for all instances.
In the AI class:
//Default Textures
private static Texture2D robotTexture
// Default Color Datas
private static Color[] robotColorData;
It seems the problem here is in your use of inheritance.
What happens is that you run your robots Load-method, which fetches robotTexture from itself through AIs getRobotTexture-method. That method just returns robotTexture, so you might as well write robotTexture = robotTexture.
But since this instance has not ran AIs.Load, robotTexture is null.
To be brutally honest; Read up on inheritance!
To be more helpful;
It seems that what you are really after is having a robotmanager that simplifies the spawning of robots. For this, inheritance is not the answer. Try something like this instead:
public class RobotManager
{
private SpriteBatch spriteBatch;
private Texture2D robotTexture;
private List<Robot> robots;
public RobotManager(SpriteBatch spriteBatch, Texture2D texture)
{
this.spriteBatch = spriteBatch;
this.robotTexture = texture;
robots = new List<Robot>();
}
public void Update()
{
foreach (var robot in robots)
robot.Update();
}
public void Draw()
{
foreach (var robot in robots)
robot.Draw();
}
public void AddRobot(Vector2 position, Texture2D customTexture = null)
{
//Creates a new robot with position set and custom texture if specified
var newRobot = new Robot(spriteBatch, position, (customTexture == null) ? robotTexture : customTexture);
robots.Add(newRobot);
}
}

How do I use the variable in this accessor?

I am trying to use an accessor, as it seems to me that that is the only way to accomplish what I want to do. Here is my code:
Game1.cs
public class GroundTexture
{
private Texture2D dirt;
public Texture2D Dirt
{
get
{
return dirt;
}
set
{
dirt = value;
}
}
}
public class Main : Game
{
public static Texture2D texture = tile.Texture;
GroundTexture groundTexture = new GroundTexture();
public static Texture2D dirt;
protected override void LoadContent()
{
Tile tile = (Tile)currentLevel.GetTile(20, 20);
dirt = Content.Load<Texture2D>("Dirt");
groundTexture.Dirt = dirt;
Texture2D texture = tile.Texture;
}
protected override void Update(GameTime gameTime)
{
if (texture == groundTexture.Dirt)
{
player.TileCollision(groundBounds);
}
base.Update(gameTime);
}
}
I removed irrelevant information from the LoadContent and Update functions.
On the following line:
if (texture == groundTexture.Dirt)
I am getting the error
Operator '==' cannot be applied to operands of type 'Microsoft.Xna.Framework.Graphics.Texture2D' and 'Game1.GroundTexture'
Am I using the accessor correctly? And why do I get this error? "Dirt" is Texture2D, so they should be comparable.
This using a few functions from a program called Realm Factory, which is a tile editor. The numbers "20, 20" are just a sample of the level I made below:
http://i.stack.imgur.com/d8cO3.png
tile.Texture returns the sprite, which here is the content item Dirt.png
Thank you very much!

'GameStates': cannot reference a type through an expression; try 'menuinterface.Game1.GameStates' instead

I changed my code but it still doesn't work. I get this error message in the Intro class:
'GameStates': cannot reference a type through an expression; try 'menuinterface.Game1.GameStates' instead
What is wrong? I want to set the gamestate to MenuState if the player presses the Space Key.
Game1 class:
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
private IState currentState;
public enum GameStates
{
IntroState = 0,
MenuState = 1,
MaingameState = 2,
}
public GameStates CurrentState
{
get { return currentGameState; }
set { currentGameState = value; }
}
private GameStates currentGameState = GameStates.IntroState;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
currentState = new Intro(this);
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
currentState.Load(Content);
}
protected override void Update(GameTime gameTime)
{
currentState.Update(gameTime);
KeyboardState kbState = Keyboard.GetState();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
currentState.Render(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
}
Interface:
public interface IState
{
void Load(ContentManager content);
void Update(GameTime gametime);
void Render(SpriteBatch batch);
}
Intro class:
public class Intro : IState
{
private IState currentState;
Texture2D Menuscreen;
private Game1 game1;
public Intro(Game1 game)
{
game1 = game;
}
public void Load(ContentManager content)
{
Menuscreen = content.Load<Texture2D>("menu");
}
public void Update(GameTime gametime)
{
KeyboardState kbState = Keyboard.GetState();
if (kbState.IsKeyDown(Keys.Space))
game1.CurrentState = game1.GameStates.IntroState;
currentState = new Menu(game1);
}
public void Render(SpriteBatch batch)
{
batch.Draw(Menuscreen, new Rectangle(0, 0, 1280, 720), Color.White);
}
}
Your problem is not understanding how enumerative types work. Specifically, this line:
game1.GameStates = IntroState;
First, let's discuss a little of what an enum actually is. It's a structure that simply assigns names to integer values, and can then refer to each value by name, and thus make code more readable (as it's far easier to understand direction = Dir.Up than direction = 1).
Notice how I use those two example statements, though. In the code, you treat the enum as a type not as a variable, and this is the problem you're encountering. In fact, your problem is twofold. The first issue id that you're trying to assign a value to a structure, which is similar to writing int = 4 - i.e. it doesn't make sense. Your second issue is that enums are not global, so IntroState has no meaning outside game1.
Interestingly enough, you've set up the system correctly, as you have the currentGameState variable, which is what you actually intend to change. However, it is a private variable, disallowing access to it from outside the game1 class. You can either make the variable public, or create a property to access it. The latter is good practice, as it allows you to control how the variable is set. To create this, you can use something like this:
public GameStates CurrentState
{
get { return currentGameState; }
set { currentGameState = value; }
}
Once you have this in your game1 class, you can set the game state like so:
game1.CurrentState = Game1.GameStates.IntroState;
Notice how this code tells the program where to look for what you want. IntroState is part of a structure (GameStates) within game1, and so needs to be accessed explicitly.
Hopefully, this has helped you to understand how enumerative types work, and why the code you wrote doesn't make sense, and thus why it breaks.
Do what the error message says:
game1.GameStates = Game1.GameStates.IntroState;
You need to fully qualify that enum value.

Inheritance - change variable upon initialization based on child value

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

Categories

Resources